Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
NFAtoDFA.h
Go to the documentation of this file.
1/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#ifndef NFAtoDFA_DEFINED
8#define NFAtoDFA_DEFINED
9
10#include "src/sksl/lex/DFA.h"
12#include "src/sksl/lex/NFA.h"
14
15#include <algorithm>
16#include <climits>
17#include <memory>
18#include <unordered_map>
19#include <set>
20#include <vector>
21
22/**
23 * Converts a nondeterministic finite automaton to a deterministic finite automaton. Since NFAs and
24 * DFAs differ only in that an NFA allows multiple states at the same time, we can find each
25 * possible combination of simultaneous NFA states and give this combination a label. These labelled
26 * nodes are our DFA nodes, since we can only be in one such unique set of NFA states at a time.
27 *
28 * As an NFA can end up in multiple accept states at the same time (for instance, the token "while"
29 * is valid for both WHILE and IDENTIFIER), we disambiguate by preferring the first matching regex
30 * (in terms of the order in which they were added to the NFA).
31 */
32class NFAtoDFA {
33public:
34 inline static constexpr char START_CHAR = 9;
35 inline static constexpr char END_CHAR = 126;
36
38 : fNFA(*nfa) {}
39
40 /**
41 * Returns a DFA created from the NFA.
42 */
44 // create state 0, the "reject" state
45 getState(DFAState::Label({}));
46 // create a state representing being in all of the NFA's start states at once
47 std::vector<int> startStates = fNFA.fStartStates;
48 std::sort(startStates.begin(), startStates.end());
49 // this becomes state 1, our start state
50 DFAState* start = getState(DFAState::Label(startStates));
51 this->scanState(start);
52
53 this->computeMappings();
54
55 int stateCount = 0;
56 for (const auto& row : fTransitions) {
57 stateCount = std::max(stateCount, (int) row.size());
58 }
59 return DFA(fCharMappings, fTransitions, fAccepts);
60 }
61
62private:
63 /**
64 * Returns an existing state with the given label, or creates a new one and returns it.
65 */
66 DFAState* getState(DFAState::Label label) {
67 auto found = fStates.find(label);
68 if (found == fStates.end()) {
69 int id = fStates.size();
70 fStates[label] = std::unique_ptr<DFAState>(new DFAState(id, label));
71 return fStates[label].get();
72 }
73 return found->second.get();
74 }
75
76 void add(int nfaState, std::vector<int>* states) {
77 NFAState state = fNFA.fStates[nfaState];
78 if (state.fKind == NFAState::kRemapped_Kind) {
79 for (int next : state.fData) {
80 this->add(next, states);
81 }
82 } else {
83 for (int entry : *states) {
84 if (nfaState == entry) {
85 return;
86 }
87 }
88 states->push_back(nfaState);
89 }
90 }
91
92 void addTransition(char c, int start, int next) {
93 while (fTransitions.size() <= (size_t) c) {
94 fTransitions.push_back(std::vector<int>());
95 }
96 std::vector<int>& row = fTransitions[c];
97 while (row.size() <= (size_t) start) {
98 row.push_back(INVALID);
99 }
100 row[start] = next;
101 }
102
103 void scanState(DFAState* state) {
104 state->fIsScanned = true;
105 for (char c = START_CHAR; c <= END_CHAR; ++c) {
106 std::vector<int> next;
107 int bestAccept = INT_MAX;
108 for (int idx : state->fLabel.fStates) {
109 const NFAState& nfaState = fNFA.fStates[idx];
110 if (nfaState.accept(c)) {
111 for (int nextState : nfaState.fNext) {
112 if (fNFA.fStates[nextState].fKind == NFAState::kAccept_Kind) {
113 bestAccept = std::min(bestAccept, fNFA.fStates[nextState].fData[0]);
114 }
115 this->add(nextState, &next);
116 }
117 }
118 }
119 std::sort(next.begin(), next.end());
120 DFAState* nextState = this->getState(DFAState::Label(next));
121 this->addTransition(c, state->fId, nextState->fId);
122 if (bestAccept != INT_MAX) {
123 while (fAccepts.size() <= (size_t) nextState->fId) {
124 fAccepts.push_back(INVALID);
125 }
126 fAccepts[nextState->fId] = bestAccept;
127 }
128 if (!nextState->fIsScanned) {
129 this->scanState(nextState);
130 }
131 }
132 }
133
134 // collapse rows with the same transitions to a single row. This is common, as each row
135 // represents a character and often there are many characters for which all transitions are
136 // identical (e.g. [0-9] are treated the same way by all lexer rules)
137 void computeMappings() {
138 // mappings[<input row>] = <output row>
139 std::vector<std::vector<int>*> uniques;
140 // this could be done more efficiently, but O(n^2) is plenty fast for our purposes
141 for (size_t i = 0; i < fTransitions.size(); ++i) {
142 int found = -1;
143 for (size_t j = 0; j < uniques.size(); ++j) {
144 if (*uniques[j] == fTransitions[i]) {
145 found = j;
146 break;
147 }
148 }
149 if (found == -1) {
150 found = (int) uniques.size();
151 uniques.push_back(&fTransitions[i]);
152 }
153 fCharMappings.push_back(found);
154 }
155 std::vector<std::vector<int>> newTransitions;
156 for (std::vector<int>* row : uniques) {
157 newTransitions.push_back(*row);
158 }
159 fTransitions = newTransitions;
160 }
161
162 const NFA& fNFA;
163 std::unordered_map<DFAState::Label, std::unique_ptr<DFAState>> fStates;
164 std::vector<std::vector<int>> fTransitions;
165 std::vector<int> fCharMappings;
166 std::vector<int> fAccepts;
167};
168#endif // NFAtoDFA_DEFINED
Instance * fNext
#define INVALID
Definition LexUtil.h:13
static float next(float f)
Type::kYUV Type::kRGBA() int(0.7 *637)
DFA convert()
Definition NFAtoDFA.h:43
NFAtoDFA(NFA *nfa)
Definition NFAtoDFA.h:37
static constexpr char START_CHAR
Definition NFAtoDFA.h:34
static constexpr char END_CHAR
Definition NFAtoDFA.h:35
AtkStateType state
bool fIsScanned
Definition DFAState.h:60
int fId
Definition DFAState.h:56
Definition DFA.h:17
@ kRemapped_Kind
Definition NFAState.h:27
@ kAccept_Kind
Definition NFAState.h:20
bool accept(char c) const
Definition NFAState.h:60
Definition NFA.h:22
std::vector< int > fStartStates
Definition NFA.h:55
std::vector< NFAState > fStates
Definition NFA.h:53