reglibcpp  1.3.0
(Naïve) C++ implementation of models for regular languages
gnfa.cpp
Go to the documentation of this file.
1 #include "gnfa.h"
3 using std::vector;
4 using std::pair;
5 using std::string;
6 
7 #include <unordered_map>
8 using std::unordered_map;
9 
10 #include <functional>
11 using std::reference_wrapper;
12 
13 #include <forward_list>
14 using std::forward_list;
15 
16 #include "nfa.h"
17 
18 namespace reg {
19 
21 struct gnfa::pImpl {
22  unordered_map<string,unordered_map<string,expression::exptr>> transitions;
24  string initial;
26  string accepting;
28 
30  pImpl() : initial("p0"), accepting("pF") {
33  }
34 
36  pImpl(unordered_map<string,unordered_map<string,expression::exptr>>& transitionMap,
37  string const& initialState,
38  forward_list<reference_wrapper<string const>>& acceptingStates) : transitions(move(transitionMap)) {
39  initial = generateState('p', '0');
40  auto const& epsilon = expression::spawnEmptyString();
41  transitions[initial][initialState] = epsilon;
42  accepting = generateState('p', 'F');
43  for (string const& a : acceptingStates) {
44  transitions[a][accepting] = epsilon;
45  }
46  }
47 
49 
54  expression::exptr const& getTransition(string const& qLabel, string const& pLabel) const {
55  auto const& from = transitions.at(qLabel);
56  auto reIt = from.find(pLabel);
57  if (reIt == from.end() || !reIt->second) {
59  }
60  return reIt->second;
61  }
62 
64 
74  void addTransition(string const& qLabel, string const& pLabel, expression::exptr re, bool optimized = true, bool aggressive = false) {
75  transitions[qLabel][pLabel] = expression::spawnAlternation(getTransition(qLabel, pLabel), re, optimized, aggressive);
76  }
77 
79 
89  string const& generateState(char prefix = 'q', char suffix = '\0') {
90  string state;
91  if (suffix == '\0') {
92  state = string(1, prefix).append(std::to_string(0));
93  for (size_t q(1); transitions.count(state) != 0; q++) {
94  state = string(1, prefix).append(std::to_string(q));
95  }
96  } else {
97  for (state = string(1, prefix).append(1, suffix);
98  transitions.count(state) != 0;
99  state.append(1, suffix)
100  ) {}
101  }
102  return transitions.insert(std::make_pair(state, unordered_map<string, expression::exptr>())).first->first;
103  }
104 };
105 
107 
116 gnfa::gnfa(nfa const& n) {
117  unordered_map<string,unordered_map<string,expression::exptr>> transitions(n.getNumberOfStates()+2);
118  forward_list<reference_wrapper<string const>> acceptingStates;
119  for (size_t qi(0); qi < n.getNumberOfStates(); qi++) {
120  auto const& qLabel = n.getLabelOf(qi);
121  auto& entry = transitions[qLabel];
122  if (n.isAccepting(qi)) {
123  acceptingStates.push_front(qLabel);
124  }
125  for (auto const& symbol : n.getAlphabet()) {
126  auto& pSet = n.delta(qi, symbol);
127  for (size_t pi(0); pi < pSet.size(); pi++) {
128  if (pSet[pi]) {
129  auto& dest = entry[n.getLabelOf(pi)];
130  if (!dest) {
131  dest = expression::spawnSymbol(symbol);
132  } else {
134  }
135  }
136  }
137  }
138  }
139  p = std::make_unique<pImpl>(transitions, n.getLabelOf(0), acceptingStates);
140 }
141 
143 
149  p->transitions[p->initial][p->accepting] = r;
150 }
151 
152 gnfa::gnfa(expression const& r) : p(new pImpl) {
153  switch (r.getOperation()) {
154  case expression::operation::alternation :
155  p->transitions[p->initial][p->accepting] = expression::spawnAlternation(*r.begin(), *(r.begin()+1));
156  break;
157  case expression::operation::concatenation :
158  p->transitions[p->initial][p->accepting] = expression::spawnConcatenation(*r.begin(), *(r.begin()+1));
159  break;
160  case expression::operation::kleene :
161  p->transitions[p->initial][p->accepting] = expression::spawnKleene(*r.begin());
162  break;
163  case expression::operation::symbol :
164  p->transitions[p->initial][p->accepting] = expression::spawnSymbol(r.extractSymbol());
165  break;
166  case expression::operation::empty :
167  p->transitions[p->initial][p->accepting] = expression::spawnEmptySet();
168  break;
169  }
170 }
171 
173 string const& gnfa::getInitialState() const {
174  return p->initial;
175 }
176 
178 string const& gnfa::getAcceptingState() const {
179  return p->accepting;
180 }
181 
183 vector<reference_wrapper<string const>> gnfa::getActiveStates() const {
184  vector<reference_wrapper<string const>> active;
185  for (auto const& entry : p->transitions) {
186  if (entry.first != p->initial && entry.first != p->accepting) {
187  active.push_back(std::cref(entry.first));
188  }
189  }
190  return active;
191 }
192 
194 expression::exptr gnfa::getTransition(string const& qLabel, string const& pLabel) const {
195  return p->getTransition(qLabel, pLabel);
196 }
197 
199 
202 vector<pair<reference_wrapper<string const>, reference_wrapper<string const>>> gnfa::getSplittableTransitions() const {
203  vector<pair<reference_wrapper<string const>, reference_wrapper<string const>>> splittable;
204  splittable.reserve(p->transitions.size()*p->transitions.size());
205  for (pair<string const, unordered_map<string, expression::exptr>> const& from : p->transitions) {
206  for (pair<string const, expression::exptr> const& to : from.second) {
207  if (to.second && to.second->getOperation() > expression::operation::symbol) {
208  splittable.push_back(std::make_pair(std::ref(from.first), std::ref(to.first)));
209  }
210  }
211  }
212  splittable.shrink_to_fit();
213  return splittable;
214 }
215 
217 
224 vector<reference_wrapper<string const>> gnfa::splitTransition(string const& qLabel, string const& pLabel) {
225  vector<reference_wrapper<string const>> newStates;
226  auto const& re = p->getTransition(qLabel, pLabel);
227  switch (re->getOperation()) {
228  case expression::operation::alternation : {
229  auto sub1 = *(re->begin());
230  auto sub2 = *(re->begin()+1);
231  p->transitions[qLabel][pLabel] = expression::spawnEmptySet();
232  newStates.reserve(4);
233  for (size_t i(0); i < 4; i++) {
234  newStates.push_back(p->generateState());
235  }
236  auto const& epsilon = expression::spawnEmptyString();
237  p->transitions[qLabel][newStates[0]] = epsilon;
238  p->transitions[newStates[0]][newStates[1]] = sub1;
239  p->transitions[newStates[1]][pLabel] = epsilon;
240  p->transitions[qLabel][newStates[2]] = epsilon;
241  p->transitions[newStates[2]][newStates[3]] = sub2;
242  p->transitions[newStates[3]][pLabel] = epsilon;
243  break;
244  }
245  case expression::operation::concatenation : {
246  auto sub1 = *(re->begin());
247  auto sub2 = *(re->begin()+1);
248  p->transitions[qLabel][pLabel] = expression::spawnEmptySet();
249  newStates.reserve(2);
250  for (size_t i(0); i < 2; i++) {
251  newStates.push_back(p->generateState());
252  }
253  p->transitions[qLabel][newStates[0]] = sub1;
254  p->transitions[newStates[0]][newStates[1]] = expression::spawnEmptyString();
255  p->transitions[newStates[1]][pLabel] = sub2;
256  break;
257  }
258  case expression::operation::kleene : {
259  auto sub = *(re->begin());
260  auto const& epsilon = expression::spawnEmptyString();
261  p->transitions[qLabel][pLabel] = epsilon;
262  newStates.reserve(2);
263  for (size_t i(0); i < 2; i++) {
264  newStates.push_back(p->generateState());
265  }
266  p->transitions[qLabel][newStates[0]] = epsilon;
267  p->transitions[newStates[1]][pLabel] = epsilon;
268  p->transitions[newStates[1]][newStates[0]] = epsilon;
269  p->transitions[newStates[0]][newStates[1]] = sub;
270  break;
271  }
272  case expression::operation::symbol :
273  case expression::operation::empty :
274  ;
275  }
276  return newStates;
277 }
278 
280 
288  for (auto splittable = getSplittableTransitions(); !splittable.empty(); splittable = getSplittableTransitions()) {
289  for (auto const& nodes : splittable) {
290  splitTransition(nodes.first, nodes.second);
291  }
292  }
293  nfa::builder b;
294  auto const& empty = expression::spawnEmptySet();
295  for (auto const& from : p->transitions) {
296  for (auto const& to : from.second) {
297  if (to.second != empty) {
298  b.addTransition(from.first, to.first, to.second->extractSymbol());
299  }
300  }
301  }
302  b.makeInitial(p->initial);
303  b.setAccepting(p->accepting, true);
304  return b.build();
305 }
306 
308 void gnfa::bypassTransition(string const& qLabel, string const& pLabel) {
309  auto const& re = p->getTransition(qLabel, pLabel);
310  if (re == expression::spawnEmptySet()) {
311  return;
312  }
313  auto const& selfRe = p->getTransition(qLabel, qLabel);
314  for (auto const& from : p->transitions) {
315  if (from.first != qLabel) {
316  auto to = from.second.find(qLabel);
317  if (to != from.second.end() && to->second) {
318  p->addTransition(
319  from.first,
320  pLabel,
322  to->second,
324  expression::spawnKleene(selfRe),
325  re
326  )
327  )
328  );
329  }
330  }
331  }
332  p->transitions[qLabel].erase(pLabel);
333 }
334 
336 void gnfa::ripState(string const& qLabel) {
337  forward_list<reference_wrapper<string const>> right;
338  for (auto const& to : p->transitions[qLabel]) {
339  right.push_front(to.first);
340  }
341  for (auto const& to : right) {
342  if (to.get() != qLabel) {
343  bypassTransition(qLabel, to);
344  }
345  }
346  for (auto& from : p->transitions) {
347  from.second.erase(qLabel);
348  }
349  p->transitions.erase(qLabel);
350 }
351 
353 
357  for (auto const& rippable : getActiveStates()) {
358  ripState(rippable);
359  }
361 }
362 
364 gnfa::gnfa(gnfa const& n) : p(new pImpl(*(n.p))) {}
365 
367 
368 gnfa::gnfa(gnfa&& n) : p(new pImpl) {p.swap(n.p);}
369 
372  if (this != &n) {
373  p.reset(new pImpl(*(n.p)));
374  }
375  return *this;
376 }
377 
379 
381  if (this != &n) {
382  p.reset(new pImpl);
383  p.swap(n.p);
384  }
385  return *this;
386 }
387 
388 gnfa::~gnfa() = default;
389 }
pImpl()
Constructs private implementation object for a GNFA accepting the empty language ∅.
Definition: gnfa.cpp:30
void addTransition(string const &qLabel, string const &pLabel, expression::exptr re, bool optimized=true, bool aggressive=false)
Safely adds a transition RE between two states.
Definition: gnfa.cpp:74
void ripState(std::string const &qLabel)
Removes a state, bypassing all its outgoing transitions.
Definition: gnfa.cpp:336
std::vector< std::reference_wrapper< std::string const > > getActiveStates() const
Reveals the names of this GNFA's non-initial, non-accepting states.
Definition: gnfa.cpp:183
nfa build()
Builds the NFA, as defined by previous operations.
Definition: nfa.cpp:771
expression::exptr getTransition(std::string const &qLabel, std::string const &pLabel) const
Extracts the RE labelling the transition between two states.
Definition: gnfa.cpp:194
Represents nondeterministic finite automata with ε-moves.
Definition: nfa.h:22
unordered_map< string, unordered_map< string, expression::exptr > > transitions
Holds the transition table viz state × state → regular expression.
Definition: gnfa.cpp:22
std::vector< char32_t > const & getAlphabet() const
Fetches this NFA's set of processable symbols.
Definition: nfa.cpp:298
std::string const & getLabelOf(size_t q) const
Puts a name to an index.
Definition: nfa.cpp:268
std::vector< exptr >::const_iterator begin() const
Returns an iterator pointing to this RE's first subexpression.
Definition: expression.cpp:284
static exptr const & spawnEmptyString()
Gives an RE representing the empty string ε.
Definition: expression.cpp:42
Constructs NFAs step by step.
Definition: nfa.h:62
gnfa(nfa const &n)
Constructs a GNFA with the same states and transitions as a given NFA.
Definition: gnfa.cpp:116
nfa splitAllTransitions()
Splits all transitions until only ∅, ε, and symbol REs remain and builds the resulting NFA...
Definition: gnfa.cpp:287
expression::exptr ripAllStates()
Removes all active states, constructing an RE semantically equivalent to this GNFA.
Definition: gnfa.cpp:356
std::string const & getAcceptingState() const
Reveals the name of this GNFA's accept state.
Definition: gnfa.cpp:178
static exptr spawnAlternation(exptr const &l, exptr const &r, bool optimized=true, bool aggressive=false)
Gives an RE representing the alternation of two given REs.
Definition: expression.cpp:106
bool isAccepting(size_t q) const
Tests whether a state is an accept state within this NFA.
Definition: nfa.cpp:323
Represents formal regular expressions.
Definition: expression.h:27
char32_t extractSymbol() const
Reports this symbol expression's UTF-32-encoded symbol.
Definition: expression.cpp:212
std::string const & getInitialState() const
Reveals the name of this GNFA's initial state.
Definition: gnfa.cpp:173
std::vector< std::pair< std::reference_wrapper< std::string const >, std::reference_wrapper< std::string const > > > getSplittableTransitions() const
Reveals this GNFA's splittable transitions.
Definition: gnfa.cpp:202
gnfa & operator=(gnfa const &n)
Copy-assigns this GNFA by copying another one's private implementation object.
Definition: gnfa.cpp:371
static exptr const & spawnSymbol(char32_t symbol)
Gives an RE representing the given UTF-32-encoded symbol.
Definition: expression.cpp:52
string initial
Holds the name of the initial state.
Definition: gnfa.cpp:24
builder & setAccepting(std::string const &state, bool accept)
Sets whether or not a state will be accepting within the prospective NFA.
Definition: nfa.cpp:545
expression::exptr const & getTransition(string const &qLabel, string const &pLabel) const
Safely fetches a transition RE between two states.
Definition: gnfa.cpp:54
Contains the reg::nfa class definition.
operation getOperation() const
Reports this RE's function.
Definition: expression.cpp:181
Represents generalized nondeterministic finite automata.
Definition: gnfa.h:18
string const & generateState(char prefix='q', char suffix='\0')
Generates a unique new state name with given prefix (and suffix).
Definition: gnfa.cpp:89
builder & makeInitial(std::string const &state)
Resets the initial state for the prospective NFA.
Definition: nfa.cpp:563
std::vector< std::reference_wrapper< std::string const > > splitTransition(std::string const &qLabel, std::string const &pLabel)
Splits a transition between two states, adding new states if needed.
Definition: gnfa.cpp:224
size_t getNumberOfStates() const
Counts this NFA's states.
Definition: nfa.cpp:314
void bypassTransition(std::string const &qLabel, std::string const &pLabel)
Removes a transition between two states and replaces it with equivalent ones, bypassing its beginning...
Definition: gnfa.cpp:308
pImpl(unordered_map< string, unordered_map< string, expression::exptr >> &transitionMap, string const &initialState, forward_list< reference_wrapper< string const >> &acceptingStates)
Constructs private implementation object with provided members.
Definition: gnfa.cpp:36
Contains the reg::gnfa class definition.
Definition: dfa.cpp:29
builder & addTransition(std::string const &from, std::string const &to, char32_t symbol)
Adds a transition for the prospective NFA.
Definition: nfa.cpp:576
std::valarray< bool > const & delta(size_t q, char32_t symbol) const
Computes this NFA's transition function for a state and a symbol.
Definition: nfa.cpp:102
std::shared_ptr< expression const > exptr
This is the type used to handle regular expressions.
Definition: expression.h:39
string accepting
Holds the name of the accept state.
Definition: gnfa.cpp:26
static exptr const & spawnEmptySet()
Gives an RE representing the empty set ∅.
Definition: expression.cpp:33
static exptr spawnKleene(exptr const &b, bool optimized=true, bool aggressive=false)
Gives an RE representing the Kleene closure of a given RE.
Definition: expression.cpp:140
static exptr spawnConcatenation(exptr const &l, exptr const &r, bool optimized=true, bool aggressive=false)
Gives an RE representing the concatenation of two given REs.
Definition: expression.cpp:71
Private implementation details of GNFAs.
Definition: gnfa.cpp:21