Skip to content

Commit 6ed113a

Browse files
refactor: Unify all types of NFA transitions into NfaSpontaneousTransition. (#76)
Co-authored-by: Lin Zhihao <59785146+LinZhihao-723@users.noreply.github.com>
1 parent 6904e51 commit 6ed113a

10 files changed

Lines changed: 275 additions & 308 deletions

File tree

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,14 @@ set(SOURCE_FILES
7373
src/log_surgeon/finite_automata/DfaState.hpp
7474
src/log_surgeon/finite_automata/DfaStatePair.hpp
7575
src/log_surgeon/finite_automata/Nfa.hpp
76+
src/log_surgeon/finite_automata/NfaSpontaneousTransition.hpp
7677
src/log_surgeon/finite_automata/NfaState.hpp
7778
src/log_surgeon/finite_automata/PrefixTree.cpp
7879
src/log_surgeon/finite_automata/PrefixTree.hpp
7980
src/log_surgeon/finite_automata/RegexAST.hpp
8081
src/log_surgeon/finite_automata/RegisterHandler.hpp
8182
src/log_surgeon/finite_automata/StateType.hpp
82-
src/log_surgeon/finite_automata/TaggedTransition.hpp
83+
src/log_surgeon/finite_automata/TagOperation.hpp
8384
src/log_surgeon/finite_automata/UnicodeIntervalTree.hpp
8485
src/log_surgeon/finite_automata/UnicodeIntervalTree.tpp
8586
src/log_surgeon/Lalr1Parser.cpp

src/log_surgeon/Lexer.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <log_surgeon/Constants.hpp>
1414
#include <log_surgeon/finite_automata/Dfa.hpp>
1515
#include <log_surgeon/finite_automata/DfaState.hpp>
16+
#include <log_surgeon/finite_automata/NfaState.hpp>
1617
#include <log_surgeon/finite_automata/RegexAST.hpp>
1718
#include <log_surgeon/LexicalRule.hpp>
1819
#include <log_surgeon/ParserInputBuffer.hpp>

src/log_surgeon/finite_automata/Dfa.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <map>
66
#include <memory>
77
#include <set>
8+
#include <stack>
89
#include <vector>
910

1011
#include <log_surgeon/finite_automata/DfaStatePair.hpp>

src/log_surgeon/finite_automata/Nfa.hpp

Lines changed: 42 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <cstdint>
55
#include <memory>
6+
#include <optional>
67
#include <queue>
78
#include <string>
89
#include <unordered_map>
@@ -11,9 +12,12 @@
1112
#include <vector>
1213

1314
#include <fmt/core.h>
15+
#include <fmt/format.h>
1416

1517
#include <log_surgeon/Constants.hpp>
16-
#include <log_surgeon/finite_automata/NfaState.hpp>
18+
#include <log_surgeon/finite_automata/Capture.hpp>
19+
#include <log_surgeon/finite_automata/TagOperation.hpp>
20+
#include <log_surgeon/finite_automata/UnicodeIntervalTree.hpp>
1721
#include <log_surgeon/LexicalRule.hpp>
1822
#include <log_surgeon/types.hpp>
1923
#include <log_surgeon/UniqueIdGenerator.hpp>
@@ -35,32 +39,31 @@ class Nfa {
3539
explicit Nfa(std::vector<LexicalRule<TypedNfaState>> const& rules);
3640

3741
/**
38-
* Creates a unique_ptr for an NFA state with no tagged transitions and adds it to `m_states`.
39-
* @return TypedNfaState*
42+
* @return A pointer to the newly created NFA state with no spontaneous transitions.
4043
*/
4144
[[nodiscard]] auto new_state() -> TypedNfaState*;
4245

4346
/**
44-
* Creates a unique_ptr for an NFA state with a negative tagged transition and adds it to
45-
* `m_states`.
46-
* @param captures
47-
* @param dest_state
48-
* @return TypedNfaState*
47+
* @param captures A vector containing the captures of all alternate paths.
48+
* @param dest_state The destination state to arrive at after negating the captures.
49+
* @return A pointer to the newly created NFA state with a spontaneous transition to
50+
* `dest_state`negating all the tags associated with `captures`.
4951
*/
50-
[[nodiscard]] auto new_state_with_negative_tagged_transition(
52+
[[nodiscard]] auto new_state_from_negative_captures(
5153
std::vector<Capture const*> const& captures,
5254
TypedNfaState const* dest_state
5355
) -> TypedNfaState*;
5456

5557
/**
56-
* Creates the start and end states for a capture group.
57-
* @param capture The capture associated with the capture group.
58-
* @param dest_state
59-
* @return A pair of states:
60-
* - A new state with a positive tagged start transition from `m_root`.
61-
* - A new state with a positive tagged end transition to `dest_state`.
58+
* @param capture The positive capture to be tracked.
59+
* @param dest_state The destination state to arrive at after tracking the capture.
60+
* @return A pair of pointers to the two newly created NFA states:
61+
* - A state arrived at from a spontaneous transition out of `m_root` that sets a tag to track
62+
* the capture's start position.
63+
* - A state with a spontaneous transition to `dest_state` that sets a tag to track the
64+
* capture's end position
6265
*/
63-
[[nodiscard]] auto new_start_and_end_states_with_positive_tagged_transitions(
66+
[[nodiscard]] auto new_start_and_end_states_from_positive_capture(
6467
Capture const* capture,
6568
TypedNfaState const* dest_state
6669
) -> std::pair<TypedNfaState*, TypedNfaState*>;
@@ -72,9 +75,10 @@ class Nfa {
7275
[[nodiscard]] auto get_bfs_traversal_order() const -> std::vector<TypedNfaState const*>;
7376

7477
/**
75-
* @return A string representation of the NFA.
78+
* @return A string representation of the NFA on success.
79+
* @return Forwards `NfaState::serialize`'s return value (`std::nullopt`) on failure.
7680
*/
77-
[[nodiscard]] auto serialize() const -> std::string;
81+
[[nodiscard]] auto serialize() const -> std::optional<std::string>;
7882

7983
auto add_root_interval(Interval interval, TypedNfaState* dest_state) -> void {
8084
m_root->add_interval(interval, dest_state);
@@ -100,18 +104,6 @@ class Nfa {
100104
[[nodiscard]] auto get_or_create_capture_tag_pair(Capture const* capture
101105
) -> std::pair<tag_id_t, tag_id_t>;
102106

103-
/**
104-
* Creates a `unique_ptr` for an NFA state with a positive tagged end transition and adds it to
105-
* `m_states`.
106-
* @param tag_id
107-
* @param dest_state
108-
* @return A new state with a positive tagged end transition to `dest_state`.
109-
*/
110-
[[nodiscard]] auto new_state_with_positive_tagged_end_transition(
111-
tag_id_t tag_id,
112-
TypedNfaState const* dest_state
113-
) -> TypedNfaState*;
114-
115107
std::vector<std::unique_ptr<TypedNfaState>> m_states;
116108
// TODO: Lexer currently enforces unique naming across capture groups. However, this limits use
117109
// cases. Possibly initialize this in the lexer and pass it in during construction.
@@ -146,39 +138,35 @@ auto Nfa<TypedNfaState>::new_state() -> TypedNfaState* {
146138
}
147139

148140
template <typename TypedNfaState>
149-
auto Nfa<TypedNfaState>::new_state_with_positive_tagged_end_transition(
150-
tag_id_t const tag_id,
151-
TypedNfaState const* dest_state
152-
) -> TypedNfaState* {
153-
m_states.emplace_back(std::make_unique<TypedNfaState>(tag_id, dest_state));
154-
return m_states.back().get();
155-
}
156-
157-
template <typename TypedNfaState>
158-
auto Nfa<TypedNfaState>::new_state_with_negative_tagged_transition(
141+
auto Nfa<TypedNfaState>::new_state_from_negative_captures(
159142
std::vector<Capture const*> const& captures,
160143
TypedNfaState const* dest_state
161144
) -> TypedNfaState* {
162145
std::vector<tag_id_t> tags;
163-
for (auto const capture : captures) {
146+
for (auto const* capture : captures) {
164147
auto const [start_tag, end_tag]{get_or_create_capture_tag_pair(capture)};
165148
tags.push_back(start_tag);
166149
tags.push_back(end_tag);
167150
}
168151

169-
m_states.emplace_back(std::make_unique<TypedNfaState>(std::move(tags), dest_state));
152+
m_states.emplace_back(
153+
std::make_unique<TypedNfaState>(TagOperationType::Negate, std::move(tags), dest_state)
154+
);
170155
return m_states.back().get();
171156
}
172157

173158
template <typename TypedNfaState>
174-
auto Nfa<TypedNfaState>::new_start_and_end_states_with_positive_tagged_transitions(
159+
auto Nfa<TypedNfaState>::new_start_and_end_states_from_positive_capture(
175160
Capture const* capture,
176161
TypedNfaState const* dest_state
177162
) -> std::pair<TypedNfaState*, TypedNfaState*> {
178163
auto const [start_tag, end_tag]{get_or_create_capture_tag_pair(capture)};
179-
auto* start_state = new_state();
180-
m_root->add_positive_tagged_start_transition(start_tag, start_state);
181-
auto* end_state{new_state_with_positive_tagged_end_transition(end_tag, dest_state)};
164+
auto* start_state{new_state()};
165+
m_root->add_spontaneous_transition(TagOperationType::Set, {start_tag}, start_state);
166+
m_states.emplace_back(
167+
std::make_unique<TypedNfaState>(TagOperationType::Set, std::vector{end_tag}, dest_state)
168+
);
169+
auto* end_state{m_states.back().get()};
182170
return {start_state, end_state};
183171
}
184172

@@ -208,33 +196,15 @@ auto Nfa<TypedNfaState>::get_bfs_traversal_order() const -> std::vector<TypedNfa
208196
add_to_queue_and_visited(dest_state);
209197
}
210198
}
211-
for (auto const* dest_state : current_state->get_epsilon_transitions()) {
212-
add_to_queue_and_visited(dest_state);
213-
}
214-
for (auto const& positive_tagged_start_transition :
215-
current_state->get_positive_tagged_start_transitions())
216-
{
217-
add_to_queue_and_visited(positive_tagged_start_transition.get_dest_state());
218-
}
219-
220-
auto const& optional_positive_tagged_end_transition
221-
= current_state->get_positive_tagged_end_transition();
222-
if (optional_positive_tagged_end_transition.has_value()) {
223-
add_to_queue_and_visited(optional_positive_tagged_end_transition.value().get_dest_state(
224-
));
225-
}
226-
227-
auto const& optional_negative_tagged_transition
228-
= current_state->get_negative_tagged_transition();
229-
if (optional_negative_tagged_transition.has_value()) {
230-
add_to_queue_and_visited(optional_negative_tagged_transition.value().get_dest_state());
199+
for (auto const& spontaneous_transition : current_state->get_spontaneous_transitions()) {
200+
add_to_queue_and_visited(spontaneous_transition.get_dest_state());
231201
}
232202
}
233203
return visited_order;
234204
}
235205

236206
template <typename TypedNfaState>
237-
auto Nfa<TypedNfaState>::serialize() const -> std::string {
207+
auto Nfa<TypedNfaState>::serialize() const -> std::optional<std::string> {
238208
auto const traversal_order = get_bfs_traversal_order();
239209

240210
std::unordered_map<TypedNfaState const*, uint32_t> state_ids;
@@ -244,10 +214,11 @@ auto Nfa<TypedNfaState>::serialize() const -> std::string {
244214

245215
std::vector<std::string> serialized_states;
246216
for (auto const* state : traversal_order) {
247-
// `state_ids` is well-formed as its generated from `get_bfs_traversal_order` so we can
248-
// safely assume `state->serialize(state_ids)` will return a valid value.
249-
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
250-
serialized_states.emplace_back(state->serialize(state_ids).value());
217+
auto const optional_serialized_state{state->serialize(state_ids)};
218+
if (false == optional_serialized_state.has_value()) {
219+
return std::nullopt;
220+
}
221+
serialized_states.emplace_back(optional_serialized_state.value());
251222
}
252223
return fmt::format("{}\n", fmt::join(serialized_states, "\n"));
253224
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#ifndef LOG_SURGEON_FINITE_AUTOMATA_NFASPONTANEOUSTRANSITION_HPP
2+
#define LOG_SURGEON_FINITE_AUTOMATA_NFASPONTANEOUSTRANSITION_HPP
3+
4+
#include <cstdint>
5+
#include <optional>
6+
#include <ranges>
7+
#include <string>
8+
#include <unordered_map>
9+
#include <utility>
10+
#include <vector>
11+
12+
#include <fmt/format.h>
13+
14+
#include <log_surgeon/finite_automata/TagOperation.hpp>
15+
16+
namespace log_surgeon::finite_automata {
17+
/**
18+
* Represents an NFA transition with a collection of tag operations to be performed during the
19+
* transition.
20+
*
21+
* @tparam TypedNfaState Specifies the type of transition (bytes or UTF-8 characters).
22+
*/
23+
template <typename TypedNfaState>
24+
class NfaSpontaneousTransition {
25+
public:
26+
NfaSpontaneousTransition(std::vector<TagOperation> tag_ops, TypedNfaState const* dest_state)
27+
: m_tag_ops{std::move(tag_ops)},
28+
m_dest_state{dest_state} {}
29+
30+
[[nodiscard]] auto get_tag_ops() const -> std::vector<TagOperation> const& { return m_tag_ops; }
31+
32+
[[nodiscard]] auto get_dest_state() const -> TypedNfaState const* { return m_dest_state; }
33+
34+
/**
35+
* @param state_ids A map of states to their unique identifiers.
36+
* @return A string representation of the spontaneous transition on success.
37+
* @return std::nullopt if `m_dest_state` is not in `state_ids`.
38+
*/
39+
[[nodiscard]] auto serialize(std::unordered_map<TypedNfaState const*, uint32_t> const& state_ids
40+
) const -> std::optional<std::string>;
41+
42+
private:
43+
std::vector<TagOperation> m_tag_ops;
44+
TypedNfaState const* m_dest_state;
45+
};
46+
47+
template <typename TypedNfaState>
48+
auto NfaSpontaneousTransition<TypedNfaState>::serialize(
49+
std::unordered_map<TypedNfaState const*, uint32_t> const& state_ids
50+
) const -> std::optional<std::string> {
51+
if (false == state_ids.contains(m_dest_state)) {
52+
return std::nullopt;
53+
}
54+
auto transformed_operations
55+
= m_tag_ops | std::ranges::views::transform(&TagOperation::serialize);
56+
57+
return fmt::format(
58+
"{}[{}]",
59+
state_ids.at(m_dest_state),
60+
fmt::join(transformed_operations, ",")
61+
);
62+
}
63+
} // namespace log_surgeon::finite_automata
64+
65+
#endif // LOG_SURGEON_FINITE_AUTOMATA_NFASPONTANEOUSTRANSITION_HPP

0 commit comments

Comments
 (0)