reglibcpp  1.7.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>
9 
10 #include <functional>
12 
13 #include <forward_list>
14 using std::forward_list;
15 
16 #include "nfa.h"
17 #include "dfa.h"
18 
19 namespace reg {
20 
22 struct gnfa::pImpl {
27  string initial;
29  string accepting;
31 
33  pImpl() : initial("p0"), accepting("pF") {
36  }
37 
40  string const& initialState,
42  nfa const* eq) : equivalent(eq), transitions(move(transitionMap)) {
43  initial = generateState('p', '0');
44  auto const& epsilon = expression::spawnEmptyString();
45  transitions[initial][initialState] = epsilon;
46  accepting = generateState('p', 'F');
47  for (string const& a : acceptingStates) {
48  transitions[a][accepting] = epsilon;
49  }
50  }
51 
53 
58  expression::exptr const& getTransition(string const& qLabel, string const& pLabel) const {
59  auto const& from = transitions.at(qLabel);
60  auto reIt = from.find(pLabel);
61  if (reIt == from.end() || !reIt->second) {
63  }
64  return reIt->second;
65  }
66 
68 
78  void addTransition(string const& qLabel, string const& pLabel, expression::exptr re, bool optimized = true, bool aggressive = false) {
79  transitions[qLabel][pLabel] = expression::spawnAlternation(getTransition(qLabel, pLabel), re, optimized, aggressive);
80  }
81 
83 
93  string const& generateState(char prefix = 'q', char suffix = '\0') {
94  string state;
95  if (suffix == '\0') {
96  state = string(1, prefix).append(std::to_string(0));
97  for (size_t q(1); transitions.count(state) != 0; q++) {
98  state = string(1, prefix).append(std::to_string(q));
99  }
100  } else {
101  for (state = string(1, prefix).append(1, suffix);
102  transitions.count(state) != 0;
103  state.append(1, suffix)
104  ) {}
105  }
106  return transitions.insert(std::make_pair(state, unordered_map<string, expression::exptr>())).first->first;
107  }
108 };
109 
111 
120 gnfa::gnfa(dfa const& d) {
123  for (size_t qi(0); qi < d.getNumberOfStates(); qi++) {
124  auto const& qLabel = d.getLabelOf(qi);
125  auto& entry = transitions[qLabel];
126  if (d.isAccepting(qi)) {
127  acceptingStates.push_front(qLabel);
128  }
129  for (size_t si(0); si < d.getNumberOfSymbols(); si++) {
130  char32_t symbol = d.getAlphabet()[si];
131  size_t pi = d.delta(qi, si);
132  auto& dest = entry[d.getStates()[pi]];
133  if (!dest) {
134  dest = expression::spawnSymbol(symbol);
135  } else {
137  }
138  }
139  }
140  p = std::make_unique<pImpl>(transitions, d.getLabelOf(0), acceptingStates, new nfa(d));
141 }
142 
144 
153 gnfa::gnfa(nfa const& n) {
156  for (size_t qi(0); qi < n.getNumberOfStates(); qi++) {
157  auto const& qLabel = n.getLabelOf(qi);
158  auto& entry = transitions[qLabel];
159  if (n.isAccepting(qi)) {
160  acceptingStates.push_front(qLabel);
161  }
162  for (auto const& symbol : n.getAlphabet()) {
163  auto& pSet = n.delta(qi, symbol);
164  for (size_t pi(0); pi < pSet.size(); pi++) {
165  if (pSet[pi]) {
166  auto& dest = entry[n.getLabelOf(pi)];
167  if (!dest) {
168  dest = expression::spawnSymbol(symbol);
169  } else {
171  }
172  }
173  }
174  }
175  }
176  p = std::make_unique<pImpl>(transitions, n.getLabelOf(0), acceptingStates, new nfa(n));
177 }
178 
180 
186  p->transitions[p->initial][p->accepting] = r;
187 }
188 
189 gnfa::gnfa(expression const& r) : p(new pImpl) {
190  switch (r.getOperation()) {
191  case expression::operation::alternation :
192  p->transitions[p->initial][p->accepting] = expression::spawnAlternation(*r.begin(), *(r.begin()+1));
193  break;
194  case expression::operation::concatenation :
195  p->transitions[p->initial][p->accepting] = expression::spawnConcatenation(*r.begin(), *(r.begin()+1));
196  break;
197  case expression::operation::kleene :
198  p->transitions[p->initial][p->accepting] = expression::spawnKleene(*r.begin());
199  break;
200  case expression::operation::symbol :
201  p->transitions[p->initial][p->accepting] = expression::spawnSymbol(r.extractSymbol());
202  break;
203  case expression::operation::empty :
204  p->transitions[p->initial][p->accepting] = expression::spawnEmptySet();
205  break;
206  }
207 }
208 
210 string const& gnfa::getInitialState() const {
211  return p->initial;
212 }
213 
215 string const& gnfa::getAcceptingState() const {
216  return p->accepting;
217 }
218 
222  for (auto const& entry : p->transitions) {
223  if (entry.first != p->initial && entry.first != p->accepting) {
224  active.push_back(std::cref(entry.first));
225  }
226  }
227  return active;
228 }
229 
231 expression::exptr gnfa::getTransition(string const& qLabel, string const& pLabel) const {
232  return p->getTransition(qLabel, pLabel);
233 }
234 
236 
241  splittable.reserve(p->transitions.size()*p->transitions.size());
242  for (pair<string const, unordered_map<string, expression::exptr>> const& from : p->transitions) {
243  for (pair<string const, expression::exptr> const& to : from.second) {
244  if (to.second && to.second->getOperation() > expression::operation::symbol) {
245  splittable.push_back(std::make_pair(std::ref(from.first), std::ref(to.first)));
246  }
247  }
248  }
249  splittable.shrink_to_fit();
250  return splittable;
251 }
252 
254 
261 vector<reference_wrapper<string const>> gnfa::splitTransition(string const& qLabel, string const& pLabel) {
263  auto const& re = p->getTransition(qLabel, pLabel);
264  switch (re->getOperation()) {
265  case expression::operation::alternation : {
266  auto sub1 = *(re->begin());
267  auto sub2 = *(re->begin()+1);
268  p->transitions[qLabel][pLabel] = expression::spawnEmptySet();
269  newStates.reserve(4);
270  for (size_t i(0); i < 4; i++) {
271  newStates.push_back(p->generateState());
272  }
273  auto const& epsilon = expression::spawnEmptyString();
274  p->transitions[qLabel][newStates[0]] = epsilon;
275  p->transitions[newStates[0]][newStates[1]] = sub1;
276  p->transitions[newStates[1]][pLabel] = epsilon;
277  p->transitions[qLabel][newStates[2]] = epsilon;
278  p->transitions[newStates[2]][newStates[3]] = sub2;
279  p->transitions[newStates[3]][pLabel] = epsilon;
280  break;
281  }
282  case expression::operation::concatenation : {
283  auto sub1 = *(re->begin());
284  auto sub2 = *(re->begin()+1);
285  p->transitions[qLabel][pLabel] = expression::spawnEmptySet();
286  newStates.reserve(2);
287  for (size_t i(0); i < 2; i++) {
288  newStates.push_back(p->generateState());
289  }
290  p->transitions[qLabel][newStates[0]] = sub1;
291  p->transitions[newStates[0]][newStates[1]] = expression::spawnEmptyString();
292  p->transitions[newStates[1]][pLabel] = sub2;
293  break;
294  }
295  case expression::operation::kleene : {
296  auto sub = *(re->begin());
297  auto const& epsilon = expression::spawnEmptyString();
298  p->transitions[qLabel][pLabel] = epsilon;
299  newStates.reserve(2);
300  for (size_t i(0); i < 2; i++) {
301  newStates.push_back(p->generateState());
302  }
303  p->transitions[qLabel][newStates[0]] = epsilon;
304  p->transitions[newStates[1]][pLabel] = epsilon;
305  p->transitions[newStates[1]][newStates[0]] = epsilon;
306  p->transitions[newStates[0]][newStates[1]] = sub;
307  break;
308  }
309  case expression::operation::symbol :
310  case expression::operation::empty :
311  ;
312  }
313  return newStates;
314 }
315 
317 
325  for (auto splittable = getSplittableTransitions(); !splittable.empty(); splittable = getSplittableTransitions()) {
326  for (auto const& nodes : splittable) {
327  splitTransition(nodes.first, nodes.second);
328  }
329  }
330  nfa::builder b;
331  auto const& empty = expression::spawnEmptySet();
332  for (auto const& from : p->transitions) {
333  for (auto const& to : from.second) {
334  if (to.second != empty) {
335  b.addTransition(from.first, to.first, to.second->extractSymbol());
336  }
337  }
338  }
339  b.makeInitial(p->initial);
340  b.setAccepting(p->accepting, true);
341  return b.build();
342 }
343 
345 void gnfa::bypassTransition(string const& qLabel, string const& pLabel) {
346  auto const& re = p->getTransition(qLabel, pLabel);
347  if (re == expression::spawnEmptySet()) {
348  return;
349  }
350  auto const& selfRe = p->getTransition(qLabel, qLabel);
351  for (auto const& from : p->transitions) {
352  if (from.first != qLabel) {
353  auto to = from.second.find(qLabel);
354  if (to != from.second.end() && to->second) {
355  p->addTransition(
356  from.first,
357  pLabel,
359  to->second,
361  expression::spawnKleene(selfRe),
362  re
363  )
364  )
365  );
366  }
367  }
368  }
369  p->transitions[qLabel].erase(pLabel);
370 }
371 
373 void gnfa::ripState(string const& qLabel) {
375  for (auto const& to : p->transitions[qLabel]) {
376  right.push_front(to.first);
377  }
378  for (auto const& to : right) {
379  if (to.get() != qLabel) {
380  bypassTransition(qLabel, to);
381  }
382  }
383  for (auto& from : p->transitions) {
384  from.second.erase(qLabel);
385  }
386  p->transitions.erase(qLabel);
387 }
388 
390 
394  for (auto const& rippable : getActiveStates()) {
395  ripState(rippable);
396  }
398 }
399 
401 gnfa::gnfa(gnfa const& n) : p(new pImpl(*(n.p))) {}
402 
404 
405 gnfa::gnfa(gnfa&& n) : p(new pImpl) {p.swap(n.p);}
406 
407 #ifndef DOXYGEN_SHOULD_SKIP_THIS
408 gnfa::operator nfa const&() const {
409  if (!p->equivalent) {
410  p->equivalent = std::make_shared<nfa const>(gnfa(*this).splitAllTransitions());
411  }
412  return *p->equivalent;
413 }
414 #endif // DOXYGEN_SHOULD_SKIP_THIS
415 
417 
420 bool gnfa::operator==(nfa const& other) const {
421  return other.operator==(*this);
422 }
423 
425 
428 bool gnfa::operator!=(nfa const& other) const {
429  return !operator==(other);
430 }
431 
434  if (this != &n) {
435  p.reset(new pImpl(*(n.p)));
436  }
437  return *this;
438 }
439 
441 
443  if (this != &n) {
444  p.reset(new pImpl);
445  p.swap(n.p);
446  }
447  return *this;
448 }
449 
450 gnfa::~gnfa() = default;
451 }
pImpl()
Constructs private implementation object for a GNFA accepting the empty language ∅.
Definition: gnfa.cpp:33
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:78
void ripState(std::string const &qLabel)
Removes a state, bypassing all its outgoing transitions.
Definition: gnfa.cpp:373
bool isAccepting(size_t qi) const
Tests whether a state is an accept state within this DFA.
Definition: dfa.cpp:391
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:220
nfa build()
Builds the NFA, as defined by previous operations.
Definition: nfa.cpp:1003
expression::exptr getTransition(std::string const &qLabel, std::string const &pLabel) const
Extracts the RE labelling the transition between two states.
Definition: gnfa.cpp:231
Represents nondeterministic finite automata with ε-moves.
Definition: nfa.h:23
unordered_map< string, unordered_map< string, expression::exptr > > transitions
Holds the transition table viz state × state → regular expression.
Definition: gnfa.cpp:25
bool operator!=(nfa const &n) const
Checks whether this RE describes a different regular language than another object.
Definition: gnfa.cpp:428
std::vector< char32_t > const & getAlphabet() const
Fetches this NFA's set of processable symbols.
Definition: nfa.cpp:445
std::string const & getLabelOf(size_t q) const
Definition: nfa.cpp:335
pImpl(unordered_map< string, unordered_map< string, expression::exptr >> &transitionMap, string const &initialState, forward_list< reference_wrapper< string const >> &acceptingStates, nfa const *eq)
Constructs private implementation object with provided members.
Definition: gnfa.cpp:39
Represents deterministic finite automata.
Definition: dfa.h:21
std::vector< exptr >::const_iterator begin() const
Returns an iterator pointing to this RE's first subexpression.
Definition: expression.cpp:315
static exptr const & spawnEmptyString()
Gives an RE representing the empty string ε.
Definition: expression.cpp:59
Constructs NFAs step by step.
Definition: nfa.h:91
nfa splitAllTransitions()
Splits all transitions until only ∅, ε, and symbol REs remain and builds the resulting NFA...
Definition: gnfa.cpp:324
expression::exptr ripAllStates()
Removes all active states, constructing an RE semantically equivalent to this GNFA.
Definition: gnfa.cpp:393
std::string const & getAcceptingState() const
Reveals the name of this GNFA's accept state.
Definition: gnfa.cpp:215
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:123
Represents formal regular expressions.
Definition: expression.h:28
char32_t extractSymbol() const
Reports this symbol expression's UTF-32-encoded symbol.
Definition: expression.cpp:249
std::string const & getInitialState() const
Reveals the name of this GNFA's initial state.
Definition: gnfa.cpp:210
size_t delta(size_t qi, size_t si) const
Computes this DFA's transition function for a state index and a symbol index.
Definition: dfa.cpp:239
std::vector< char32_t > const & getAlphabet() const
Fetches this DFA's set of processable symbols.
Definition: dfa.cpp:363
bool operator==(nfa const &n) const
Checks whether this RE describes the same regular language as another object.
Definition: gnfa.cpp:420
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:239
gnfa & operator=(gnfa const &n)
Copy-assigns this GNFA by copying another one's private implementation object.
Definition: gnfa.cpp:433
static exptr const & spawnSymbol(char32_t symbol)
Gives an RE representing the given UTF-32-encoded symbol.
Definition: expression.cpp:69
size_t getNumberOfSymbols() const
Definition: dfa.cpp:381
string initial
Holds the name of the initial state.
Definition: gnfa.cpp:27
builder & setAccepting(std::string const &state, bool accept)
Sets whether or not a state will be accepting within the prospective NFA.
Definition: nfa.cpp:777
expression::exptr const & getTransition(string const &qLabel, string const &pLabel) const
Safely fetches a transition RE between two states.
Definition: gnfa.cpp:58
Contains the reg::nfa class definition.
operation getOperation() const
Reports this RE's function.
Definition: expression.cpp:205
bool isAccepting(size_t qi) const
Tests whether a state is an accept state within this NFA.
Definition: nfa.cpp:473
Represents generalized nondeterministic finite automata.
Definition: gnfa.h:17
string const & generateState(char prefix='q', char suffix='\0')
Generates a unique new state name with given prefix (and suffix).
Definition: gnfa.cpp:93
std::vector< std::string > const & getStates() const
Fetches this DFA's set of states.
Definition: dfa.cpp:355
size_t getNumberOfStates() const
Definition: dfa.cpp:376
builder & makeInitial(std::string const &state)
Resets the initial state for the prospective NFA.
Definition: nfa.cpp:795
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:261
size_t getNumberOfStates() const
Definition: nfa.cpp:458
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:345
Contains the reg::gnfa class definition.
Where this library lives.
Definition: dfa.cpp:51
std::shared_ptr< nfa const > equivalent
Points to a DFA accepting the same language as the owning GNFA.
Definition: gnfa.cpp:23
builder & addTransition(std::string const &from, std::string const &to, char32_t symbol)
Adds a transition for the prospective NFA.
Definition: nfa.cpp:808
std::valarray< bool > const & delta(size_t qi, size_t si) const
Computes this NFA's transition function for a state index and a symbol index.
Definition: nfa.cpp:108
std::string const & getLabelOf(size_t qi) const
Definition: dfa.cpp:339
string accepting
Holds the name of the accept state.
Definition: gnfa.cpp:29
gnfa(dfa const &d)
Constructs a GNFA with the same states and transitions as a given DFA.
Definition: gnfa.cpp:120
static exptr const & spawnEmptySet()
Gives an RE representing the empty set ∅.
Definition: expression.cpp:50
Contains the reg::dfa class definition.
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:164
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:88
Private implementation details of GNFAs.
Definition: gnfa.cpp:22