builders.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /**
  2. * The MIT License (MIT)
  3. * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
  4. */
  5. 'use strict';
  6. var NFA = require('./nfa');
  7. var NFAState = require('./nfa-state');
  8. var _require = require('../special-symbols'),
  9. EPSILON = _require.EPSILON;
  10. // -----------------------------------------------------------------------------
  11. // Char NFA fragment: `c`
  12. /**
  13. * Char factory.
  14. *
  15. * Creates an NFA fragment for a single char.
  16. *
  17. * [in] --c--> [out]
  18. */
  19. function char(c) {
  20. var inState = new NFAState();
  21. var outState = new NFAState({
  22. accepting: true
  23. });
  24. return new NFA(inState.addTransition(c, outState), outState);
  25. }
  26. // -----------------------------------------------------------------------------
  27. // Epsilon NFA fragment
  28. /**
  29. * Epsilon factory.
  30. *
  31. * Creates an NFA fragment for ε (recognizes an empty string).
  32. *
  33. * [in] --ε--> [out]
  34. */
  35. function e() {
  36. return char(EPSILON);
  37. }
  38. // -----------------------------------------------------------------------------
  39. // Alteration NFA fragment: `abc`
  40. /**
  41. * Creates a connection between two NFA fragments on epsilon transition.
  42. *
  43. * [in-a] --a--> [out-a] --ε--> [in-b] --b--> [out-b]
  44. */
  45. function altPair(first, second) {
  46. first.out.accepting = false;
  47. second.out.accepting = true;
  48. first.out.addTransition(EPSILON, second.in);
  49. return new NFA(first.in, second.out);
  50. }
  51. /**
  52. * Alteration factory.
  53. *
  54. * Creates a alteration NFA for (at least) two NFA-fragments.
  55. */
  56. function alt(first) {
  57. for (var _len = arguments.length, fragments = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  58. fragments[_key - 1] = arguments[_key];
  59. }
  60. var _iteratorNormalCompletion = true;
  61. var _didIteratorError = false;
  62. var _iteratorError = undefined;
  63. try {
  64. for (var _iterator = fragments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
  65. var fragment = _step.value;
  66. first = altPair(first, fragment);
  67. }
  68. } catch (err) {
  69. _didIteratorError = true;
  70. _iteratorError = err;
  71. } finally {
  72. try {
  73. if (!_iteratorNormalCompletion && _iterator.return) {
  74. _iterator.return();
  75. }
  76. } finally {
  77. if (_didIteratorError) {
  78. throw _iteratorError;
  79. }
  80. }
  81. }
  82. return first;
  83. }
  84. // -----------------------------------------------------------------------------
  85. // Disjunction NFA fragment: `a|b`
  86. /**
  87. * Creates a disjunction choice between two fragments.
  88. */
  89. function orPair(first, second) {
  90. var inState = new NFAState();
  91. var outState = new NFAState();
  92. inState.addTransition(EPSILON, first.in);
  93. inState.addTransition(EPSILON, second.in);
  94. outState.accepting = true;
  95. first.out.accepting = false;
  96. second.out.accepting = false;
  97. first.out.addTransition(EPSILON, outState);
  98. second.out.addTransition(EPSILON, outState);
  99. return new NFA(inState, outState);
  100. }
  101. /**
  102. * Disjunction factory.
  103. *
  104. * Creates a disjunction NFA for (at least) two NFA-fragments.
  105. */
  106. function or(first) {
  107. for (var _len2 = arguments.length, fragments = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
  108. fragments[_key2 - 1] = arguments[_key2];
  109. }
  110. var _iteratorNormalCompletion2 = true;
  111. var _didIteratorError2 = false;
  112. var _iteratorError2 = undefined;
  113. try {
  114. for (var _iterator2 = fragments[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
  115. var fragment = _step2.value;
  116. first = orPair(first, fragment);
  117. }
  118. } catch (err) {
  119. _didIteratorError2 = true;
  120. _iteratorError2 = err;
  121. } finally {
  122. try {
  123. if (!_iteratorNormalCompletion2 && _iterator2.return) {
  124. _iterator2.return();
  125. }
  126. } finally {
  127. if (_didIteratorError2) {
  128. throw _iteratorError2;
  129. }
  130. }
  131. }
  132. return first;
  133. }
  134. // -----------------------------------------------------------------------------
  135. // Kleene-closure
  136. /**
  137. * Kleene star/closure.
  138. *
  139. * a*
  140. */
  141. function repExplicit(fragment) {
  142. var inState = new NFAState();
  143. var outState = new NFAState({
  144. accepting: true
  145. });
  146. // 0 or more.
  147. inState.addTransition(EPSILON, fragment.in);
  148. inState.addTransition(EPSILON, outState);
  149. fragment.out.accepting = false;
  150. fragment.out.addTransition(EPSILON, outState);
  151. outState.addTransition(EPSILON, fragment.in);
  152. return new NFA(inState, outState);
  153. }
  154. /**
  155. * Optimized Kleene-star: just adds ε-transitions from
  156. * input to the output, and back.
  157. */
  158. function rep(fragment) {
  159. fragment.in.addTransition(EPSILON, fragment.out);
  160. fragment.out.addTransition(EPSILON, fragment.in);
  161. return fragment;
  162. }
  163. /**
  164. * Optimized Plus: just adds ε-transitions from
  165. * the output to the input.
  166. */
  167. function plusRep(fragment) {
  168. fragment.out.addTransition(EPSILON, fragment.in);
  169. return fragment;
  170. }
  171. /**
  172. * Optimized ? repetition: just adds ε-transitions from
  173. * the input to the output.
  174. */
  175. function questionRep(fragment) {
  176. fragment.in.addTransition(EPSILON, fragment.out);
  177. return fragment;
  178. }
  179. module.exports = {
  180. alt: alt,
  181. char: char,
  182. e: e,
  183. or: or,
  184. rep: rep,
  185. repExplicit: repExplicit,
  186. plusRep: plusRep,
  187. questionRep: questionRep
  188. };