clarity-pattern-parser 3.0.15 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/README.md +0 -191
  2. package/dist/ast/Node.d.ts +4 -5
  3. package/dist/index.browser.js +476 -731
  4. package/dist/index.browser.js.map +1 -1
  5. package/dist/index.d.ts +10 -18
  6. package/dist/index.esm.js +469 -716
  7. package/dist/index.esm.js.map +1 -1
  8. package/dist/index.js +476 -731
  9. package/dist/index.js.map +1 -1
  10. package/dist/patterns/And.d.ts +24 -0
  11. package/dist/patterns/Literal.d.ts +19 -0
  12. package/dist/patterns/LookAhead.d.ts +8 -0
  13. package/dist/patterns/Not.d.ts +11 -0
  14. package/dist/patterns/Or.d.ts +22 -0
  15. package/dist/patterns/Pattern.d.ts +6 -7
  16. package/dist/patterns/{RecursivePattern.d.ts → Recursive.d.ts} +4 -4
  17. package/dist/patterns/Reference.d.ts +14 -0
  18. package/dist/patterns/Regex.d.ts +21 -0
  19. package/dist/patterns/Repeat.d.ts +20 -0
  20. package/package.json +1 -1
  21. package/src/CursorHistory.ts +1 -1
  22. package/src/ast/Node.ts +20 -17
  23. package/src/ast/Visitor.ts +14 -18
  24. package/src/index.ts +17 -33
  25. package/src/patterns/And.ts +178 -0
  26. package/src/patterns/Literal.ts +91 -0
  27. package/src/patterns/LookAhead.ts +32 -0
  28. package/src/patterns/Not.ts +50 -0
  29. package/src/patterns/Or.ts +132 -0
  30. package/src/patterns/Pattern.ts +31 -47
  31. package/src/patterns/Recursive.ts +92 -0
  32. package/src/patterns/Reference.ts +127 -0
  33. package/src/patterns/Regex.ts +123 -0
  34. package/src/patterns/Repeat.ts +155 -0
  35. package/src/tests/{AndValue.test.ts → And.test.ts} +31 -41
  36. package/src/tests/CursorHistory.test.ts +6 -6
  37. package/src/tests/Cusor.test.ts +7 -10
  38. package/src/tests/Literal.test.ts +3 -5
  39. package/src/tests/LookAhead.test.ts +44 -0
  40. package/src/tests/Not.test.ts +51 -0
  41. package/src/tests/Or.test.ts +113 -0
  42. package/src/tests/Pattern.test.ts +41 -141
  43. package/src/tests/{RecursivePattern.test.ts → Recursive.test.ts} +10 -8
  44. package/src/tests/Reference.test.ts +16 -0
  45. package/src/tests/{RepeatValue.test.ts → Repeat.test.ts} +10 -42
  46. package/src/tests/TextSuggester.test.ts +20 -28
  47. package/src/tests/{NodeVisitor.test.ts → Visitor.test.ts} +42 -21
  48. package/src/tests/cssPatterns/cssValue.ts +2 -2
  49. package/src/tests/cssPatterns/divider.ts +2 -2
  50. package/src/tests/cssPatterns/hex.ts +2 -2
  51. package/src/tests/cssPatterns/method.ts +7 -9
  52. package/src/tests/cssPatterns/name.ts +2 -2
  53. package/src/tests/cssPatterns/number.ts +2 -2
  54. package/src/tests/cssPatterns/optionalSpaces.ts +1 -2
  55. package/src/tests/cssPatterns/spaces.ts +2 -2
  56. package/src/tests/cssPatterns/unit.ts +3 -3
  57. package/src/tests/cssPatterns/value.ts +2 -2
  58. package/src/tests/cssPatterns/values.ts +2 -2
  59. package/src/tests/htmlPatterns/element.ts +18 -33
  60. package/src/tests/javascriptPatterns/boolean.ts +2 -3
  61. package/src/tests/javascriptPatterns/json.ts +14 -24
  62. package/src/tests/javascriptPatterns/name.ts +3 -20
  63. package/src/tests/javascriptPatterns/number.ts +2 -2
  64. package/src/tests/javascriptPatterns/objectLiteral.ts +9 -16
  65. package/src/tests/javascriptPatterns/string.ts +26 -24
  66. package/src/tests/javascriptPatterns/unit.ts +3 -6
  67. package/src/tests/javascriptPatterns/whitespace.ts +8 -12
  68. package/src/tests/naturalLanguage/filter.ts +16 -33
  69. package/src/tests/patterns/sentence.ts +8 -8
  70. package/dist/Cursor.js +0 -105
  71. package/dist/Cursor.js.map +0 -1
  72. package/dist/CursorHistory.js +0 -104
  73. package/dist/CursorHistory.js.map +0 -1
  74. package/dist/Permutor.d.ts +0 -13
  75. package/dist/Permutor.js +0 -52
  76. package/dist/Permutor.js.map +0 -1
  77. package/dist/TextSuggester.js +0 -244
  78. package/dist/TextSuggester.js.map +0 -1
  79. package/dist/ast/CompositeNode.d.ts +0 -6
  80. package/dist/ast/CompositeNode.js +0 -17
  81. package/dist/ast/CompositeNode.js.map +0 -1
  82. package/dist/ast/Node.js +0 -16
  83. package/dist/ast/Node.js.map +0 -1
  84. package/dist/ast/NodeVisitor.d.ts +0 -31
  85. package/dist/ast/ValueNode.d.ts +0 -6
  86. package/dist/ast/ValueNode.js +0 -14
  87. package/dist/ast/ValueNode.js.map +0 -1
  88. package/dist/ast/Visitor.js +0 -209
  89. package/dist/ast/Visitor.js.map +0 -1
  90. package/dist/patterns/ParseError.js +0 -9
  91. package/dist/patterns/ParseError.js.map +0 -1
  92. package/dist/patterns/Pattern.js +0 -127
  93. package/dist/patterns/Pattern.js.map +0 -1
  94. package/dist/patterns/RecursivePattern.js +0 -65
  95. package/dist/patterns/RecursivePattern.js.map +0 -1
  96. package/dist/patterns/composite/AndComposite.d.ts +0 -22
  97. package/dist/patterns/composite/AndComposite.js +0 -117
  98. package/dist/patterns/composite/AndComposite.js.map +0 -1
  99. package/dist/patterns/composite/CompositePattern.d.ts +0 -4
  100. package/dist/patterns/composite/CompositePattern.js +0 -7
  101. package/dist/patterns/composite/CompositePattern.js.map +0 -1
  102. package/dist/patterns/composite/OptionalComposite.d.ts +0 -10
  103. package/dist/patterns/composite/OptionalComposite.js +0 -29
  104. package/dist/patterns/composite/OptionalComposite.js.map +0 -1
  105. package/dist/patterns/composite/OrComposite.d.ts +0 -16
  106. package/dist/patterns/composite/OrComposite.js +0 -69
  107. package/dist/patterns/composite/OrComposite.js.map +0 -1
  108. package/dist/patterns/composite/RepeatComposite.d.ts +0 -21
  109. package/dist/patterns/composite/RepeatComposite.js +0 -88
  110. package/dist/patterns/composite/RepeatComposite.js.map +0 -1
  111. package/dist/patterns/value/AndValue.d.ts +0 -21
  112. package/dist/patterns/value/AndValue.js +0 -118
  113. package/dist/patterns/value/AndValue.js.map +0 -1
  114. package/dist/patterns/value/AnyOfThese.d.ts +0 -18
  115. package/dist/patterns/value/AnyOfThese.js +0 -59
  116. package/dist/patterns/value/AnyOfThese.js.map +0 -1
  117. package/dist/patterns/value/Literal.d.ts +0 -20
  118. package/dist/patterns/value/Literal.js +0 -63
  119. package/dist/patterns/value/Literal.js.map +0 -1
  120. package/dist/patterns/value/NotValue.d.ts +0 -17
  121. package/dist/patterns/value/NotValue.js +0 -70
  122. package/dist/patterns/value/NotValue.js.map +0 -1
  123. package/dist/patterns/value/OptionalValue.d.ts +0 -9
  124. package/dist/patterns/value/OptionalValue.js +0 -32
  125. package/dist/patterns/value/OptionalValue.js.map +0 -1
  126. package/dist/patterns/value/OrValue.d.ts +0 -19
  127. package/dist/patterns/value/OrValue.js +0 -73
  128. package/dist/patterns/value/OrValue.js.map +0 -1
  129. package/dist/patterns/value/RegexValue.d.ts +0 -19
  130. package/dist/patterns/value/RegexValue.js +0 -69
  131. package/dist/patterns/value/RegexValue.js.map +0 -1
  132. package/dist/patterns/value/RepeatValue.d.ts +0 -19
  133. package/dist/patterns/value/RepeatValue.js +0 -89
  134. package/dist/patterns/value/RepeatValue.js.map +0 -1
  135. package/dist/patterns/value/ValuePattern.d.ts +0 -5
  136. package/dist/patterns/value/ValuePattern.js +0 -7
  137. package/dist/patterns/value/ValuePattern.js.map +0 -1
  138. package/src/Permutor.ts +0 -64
  139. package/src/ast/CompositeNode.ts +0 -26
  140. package/src/ast/ValueNode.ts +0 -28
  141. package/src/patterns/RecursivePattern.ts +0 -86
  142. package/src/patterns/composite/AndComposite.ts +0 -159
  143. package/src/patterns/composite/CompositePattern.ts +0 -7
  144. package/src/patterns/composite/OptionalComposite.ts +0 -37
  145. package/src/patterns/composite/OrComposite.ts +0 -96
  146. package/src/patterns/composite/RepeatComposite.ts +0 -130
  147. package/src/patterns/value/AndValue.ts +0 -153
  148. package/src/patterns/value/AnyOfThese.ts +0 -81
  149. package/src/patterns/value/Literal.ts +0 -92
  150. package/src/patterns/value/NotValue.ts +0 -95
  151. package/src/patterns/value/OptionalValue.ts +0 -39
  152. package/src/patterns/value/OrValue.ts +0 -103
  153. package/src/patterns/value/RegexValue.ts +0 -103
  154. package/src/patterns/value/RepeatValue.ts +0 -131
  155. package/src/patterns/value/ValuePattern.ts +0 -8
  156. package/src/tests/AndComposite.test.ts +0 -102
  157. package/src/tests/AnyOfThese.test.ts +0 -74
  158. package/src/tests/CompositeNode.test.ts +0 -33
  159. package/src/tests/NotValue.test.ts +0 -69
  160. package/src/tests/OptionalValue.test.ts +0 -50
  161. package/src/tests/OrComposite.test.ts +0 -75
  162. package/src/tests/OrValue.test.ts +0 -171
  163. package/src/tests/Permutor.test.ts +0 -30
  164. package/src/tests/RegexValue.test.ts +0 -22
  165. package/src/tests/RepeatComposite.test.ts +0 -58
  166. package/src/tests/ValueNode.test.ts +0 -24
  167. package/src/tests/javascriptPatterns/varStatement.ts +0 -0
  168. package/src/tests/readmeDemo.test.ts +0 -124
@@ -0,0 +1,132 @@
1
+ import Pattern from "./Pattern";
2
+ import ParseError from "./ParseError";
3
+ import Cursor from "../Cursor";
4
+ import Node from "../ast/Node";
5
+
6
+ export default class Or extends Pattern {
7
+ public patternIndex: number = 0;
8
+ public errors: ParseError[] = [];
9
+ public node: Node | null = null;
10
+ public cursor: Cursor | null = null;
11
+ public mark: number = 0;
12
+ public parseError: ParseError | null = null;
13
+
14
+ constructor(name: string, patterns: Pattern[], isOptional = false) {
15
+ super("or", name, patterns, isOptional);
16
+ this.assertArguments();
17
+ }
18
+
19
+ private assertArguments() {
20
+ if (this._children.length < 2) {
21
+ throw new Error(
22
+ "Invalid Argument: OrValue needs to have more than one value pattern."
23
+ );
24
+ }
25
+
26
+ const hasOptionalChildren = this._children.some(
27
+ (pattern) => pattern.isOptional
28
+ );
29
+
30
+ if (hasOptionalChildren) {
31
+ throw new Error("OrValues cannot have optional patterns.");
32
+ }
33
+ }
34
+
35
+ private resetState(cursor: Cursor) {
36
+ this.patternIndex = 0;
37
+ this.errors = [];
38
+ this.node = null;
39
+ this.cursor = cursor;
40
+ this.mark = cursor.mark();
41
+ }
42
+
43
+ private safelyGetCursor() {
44
+ const cursor = this.cursor;
45
+
46
+ if (cursor == null) {
47
+ throw new Error("Couldn't find cursor.");
48
+ }
49
+ return cursor;
50
+ }
51
+
52
+ parse(cursor: Cursor) {
53
+ this.resetState(cursor);
54
+ this.tryToParse();
55
+
56
+ return this.node;
57
+ }
58
+
59
+ private tryToParse() {
60
+ const cursor = this.safelyGetCursor();
61
+
62
+ while (true) {
63
+ const pattern = this._children[this.patternIndex];
64
+ const node = pattern.parse(cursor);
65
+ const hasError = cursor.hasUnresolvedError();
66
+
67
+ if (hasError) {
68
+ const shouldBreak = this.processError();
69
+ if (shouldBreak) {
70
+ break;
71
+ }
72
+ } else if (node != null) {
73
+ this.processResult(node);
74
+ break;
75
+ }
76
+ }
77
+ }
78
+
79
+ private processError() {
80
+ const cursor = this.safelyGetCursor();
81
+ const isLastPattern = this.patternIndex + 1 === this._children.length;
82
+
83
+ if (!isLastPattern) {
84
+ this.patternIndex++;
85
+ cursor.resolveError();
86
+ cursor.moveToMark(this.mark);
87
+ return false;
88
+ } else {
89
+ if (this._isOptional) {
90
+ cursor.resolveError();
91
+ cursor.moveToMark(this.mark);
92
+ }
93
+ this.node = null;
94
+ return true;
95
+ }
96
+ }
97
+
98
+ private processResult(node: Node) {
99
+ const cursor = this.safelyGetCursor();
100
+
101
+ this.node = new Node(
102
+ "or",
103
+ this.name,
104
+ node.startIndex,
105
+ node.endIndex,
106
+ [node],
107
+ node.value
108
+ );
109
+
110
+ cursor.index = this.node.endIndex;
111
+ cursor.addMatch(this, this.node);
112
+ }
113
+
114
+ clone(name?: string, isOptional?: boolean) {
115
+ if (name == null) {
116
+ name = this.name;
117
+ }
118
+
119
+ if (isOptional == null) {
120
+ isOptional = this._isOptional;
121
+ }
122
+
123
+ return new Or(name, this._children, isOptional);
124
+ }
125
+
126
+ getTokens() {
127
+ return this._children.reduce<string[]>(
128
+ (acc, c) => acc.concat(c.getTokens()),
129
+ []
130
+ );
131
+ }
132
+ }
@@ -6,27 +6,26 @@ export default abstract class Pattern {
6
6
  protected _name: string;
7
7
  protected _children: Pattern[];
8
8
  protected _parent: Pattern | null;
9
- public isSequence: boolean;
9
+ protected _isOptional = false;
10
10
 
11
- constructor(type: string = "", name: string, children: Pattern[] = []) {
11
+ get isOptional() {
12
+ return this._isOptional;
13
+ }
14
+
15
+ constructor(
16
+ type: string,
17
+ name: string,
18
+ children: Pattern[] = [],
19
+ isOptional = false
20
+ ) {
12
21
  this._type = type;
13
22
  this._name = name;
14
23
  this._children = [];
15
24
  this._parent = null;
16
- this.isSequence = false;
17
-
18
- this._assertName();
25
+ this._isOptional = isOptional;
19
26
  this.children = children;
20
27
  }
21
28
 
22
- private _assertName() {
23
- if (typeof this.name !== "string") {
24
- throw new Error(
25
- "Invalid Argument: Patterns needs to have a name that's a string."
26
- );
27
- }
28
- }
29
-
30
29
  abstract parse(cursor: Cursor): Node | null;
31
30
 
32
31
  exec(text: string) {
@@ -57,9 +56,7 @@ export default abstract class Pattern {
57
56
  }
58
57
 
59
58
  set parent(value: Pattern | null) {
60
- if (value instanceof Pattern) {
61
- this._parent = value;
62
- }
59
+ this._parent = value;
63
60
  }
64
61
 
65
62
  get children() {
@@ -68,38 +65,17 @@ export default abstract class Pattern {
68
65
 
69
66
  set children(value) {
70
67
  this._children = value;
71
- this._cloneChildren();
72
- this._assertChildren();
73
- this._assignAsParent();
74
- }
75
-
76
- protected _assertChildren() {
77
- // Empty,can be overridden by subclasses.
78
- }
79
-
80
- private _cloneChildren() {
81
- // We need to clone the patterns so nested patterns can be parsed.
82
- this._children = this._children.map((pattern) => {
83
- if (!(pattern instanceof Pattern)) {
84
- throw new Error(
85
- `The ${this.name} pattern has an invalid child pattern.`
86
- );
87
- }
88
- return pattern.clone();
89
- });
90
-
91
- // We need to freeze the children so they aren't modified.
92
- Object.freeze(this._children);
93
- }
94
-
95
- private _assignAsParent() {
96
- this._children.forEach((child) => (child.parent = this));
68
+ this.cloneChildren();
69
+ this.assignAsParent();
97
70
  }
98
71
 
99
72
  abstract clone(name?: string): Pattern;
100
-
101
73
  abstract getTokens(): string[];
102
74
 
75
+ getTokenValue(): string | null {
76
+ return null;
77
+ }
78
+
103
79
  getNextTokens(): string[] {
104
80
  const parent = this._parent;
105
81
 
@@ -124,14 +100,14 @@ export default abstract class Pattern {
124
100
  if (
125
101
  this._parent?.type?.indexOf("and") === 0 &&
126
102
  nextSibling != null &&
127
- nextSibling?.type?.indexOf("optional") === 0
103
+ nextSibling.isOptional
128
104
  ) {
129
105
  let tokens: string[] = [];
130
106
 
131
107
  for (let x = index + 1; x < siblings.length; x++) {
132
108
  const child = siblings[x];
133
109
 
134
- if (child.type.indexOf("optional") === 0) {
110
+ if (child.isOptional) {
135
111
  tokens = tokens.concat(child.getTokens());
136
112
  } else {
137
113
  tokens = tokens.concat(child.getTokens());
@@ -161,7 +137,15 @@ export default abstract class Pattern {
161
137
  return [];
162
138
  }
163
139
 
164
- getTokenValue(): string | null {
165
- return null;
140
+ private cloneChildren() {
141
+ this._children = this._children.map((pattern) => {
142
+ return pattern.clone();
143
+ });
144
+
145
+ Object.freeze(this._children);
146
+ }
147
+
148
+ private assignAsParent() {
149
+ this._children.forEach((child) => (child.parent = this));
166
150
  }
167
151
  }
@@ -0,0 +1,92 @@
1
+ import Pattern from "./Pattern";
2
+ import ParserError from "./ParseError";
3
+ import Cursor from "../Cursor";
4
+
5
+ export default class Recursive extends Pattern {
6
+ public isRecursing: boolean;
7
+ public pattern: Pattern | null = null;
8
+
9
+ constructor(name: string, isOptional = false) {
10
+ super("recursive", name, [], isOptional);
11
+ this.isRecursing = false;
12
+ }
13
+
14
+ getPattern() {
15
+ return this.climb(this.parent, (pattern: Pattern | null) => {
16
+ if (pattern == null) {
17
+ return false;
18
+ }
19
+ return (
20
+ pattern.type !== "recursive" &&
21
+ pattern.name === this.name
22
+ );
23
+ });
24
+ }
25
+
26
+ private climb(
27
+ pattern: Pattern | null,
28
+ isMatch: (pattern: Pattern | null) => boolean
29
+ ): Pattern | null {
30
+ if (isMatch(pattern)) {
31
+ return pattern;
32
+ } else {
33
+ if (pattern && pattern.parent != null) {
34
+ return this.climb(pattern.parent, isMatch);
35
+ }
36
+ return null;
37
+ }
38
+ }
39
+
40
+ parse(cursor: Cursor) {
41
+ if (this.pattern == null) {
42
+ const pattern = this.getPattern();
43
+
44
+ if (pattern == null) {
45
+ if (!this._isOptional) {
46
+ cursor.throwError(
47
+ new ParserError(
48
+ `Couldn't find parent pattern to recursively parse, with the name ${this.name}.`,
49
+ cursor.index,
50
+ this
51
+ )
52
+ );
53
+ }
54
+
55
+ return null;
56
+ }
57
+
58
+ this.pattern = pattern.clone();
59
+ this.pattern.parent = this;
60
+ }
61
+
62
+ const mark = cursor.mark();
63
+ const node = this.pattern.parse(cursor);
64
+
65
+ if (!cursor.hasUnresolvedError() && node != null) {
66
+ cursor.addMatch(this, node);
67
+ }
68
+
69
+ if (cursor.hasUnresolvedError() && this._isOptional) {
70
+ cursor.resolveError();
71
+ cursor.moveToMark(mark);
72
+ }
73
+
74
+ return node;
75
+ }
76
+
77
+ clone(name?: string, isOptional?: boolean) {
78
+ if (name == null) {
79
+ name = this.name;
80
+ }
81
+
82
+ if (isOptional == null) {
83
+ isOptional = this._isOptional;
84
+ }
85
+
86
+ return new Recursive(name, isOptional);
87
+ }
88
+
89
+ getTokens() {
90
+ return this.getPattern()?.getTokens() || [];
91
+ }
92
+ }
@@ -0,0 +1,127 @@
1
+ import Pattern from "./Pattern";
2
+ import ParserError from "./ParseError";
3
+ import Cursor from "../Cursor";
4
+
5
+ export default class Reference extends Pattern {
6
+ constructor(name: string, isOptional = false) {
7
+ super("reference", name, [], isOptional);
8
+ }
9
+
10
+ private getRoot() {
11
+ let node = this.parent;
12
+ while (node != null) {
13
+ if (node.parent == null) {
14
+ return node;
15
+ }
16
+ node = node.parent;
17
+ }
18
+ return node;
19
+ }
20
+
21
+ private findPattern(): Pattern | null {
22
+ const root = this.getRoot();
23
+ let result: Pattern | null = null;
24
+
25
+ if (root == null) {
26
+ return null;
27
+ }
28
+
29
+ this.walkTheTree(root, (pattern) => {
30
+ if (
31
+ pattern.name === this.name &&
32
+ pattern != this &&
33
+ pattern.type != "reference"
34
+ ) {
35
+ result = pattern;
36
+ return false;
37
+ }
38
+ return true;
39
+ });
40
+
41
+ return result;
42
+ }
43
+
44
+ private walkTheTree(
45
+ pattern: Pattern,
46
+ callback: (pattern: Pattern) => boolean
47
+ ) {
48
+ for (let x = 0; x < pattern.children.length; x++) {
49
+ const p = pattern.children[x];
50
+ const continueWalking = this.walkTheTree(p, callback);
51
+
52
+ if (!continueWalking) {
53
+ return false;
54
+ }
55
+ }
56
+
57
+ return callback(pattern);
58
+ }
59
+
60
+ parse(cursor: Cursor) {
61
+ const mark = cursor.mark();
62
+
63
+ try {
64
+ const node = this.safelyGetPattern().parse(cursor);
65
+
66
+ if (!cursor.hasUnresolvedError() && node != null) {
67
+ cursor.addMatch(this, node);
68
+ }
69
+
70
+ if (cursor.hasUnresolvedError() && this._isOptional) {
71
+ cursor.resolveError();
72
+ cursor.moveToMark(mark);
73
+ }
74
+
75
+ return node;
76
+ } catch (error) {
77
+ if (this._isOptional) {
78
+ cursor.moveToMark(mark);
79
+ } else {
80
+ cursor.throwError(
81
+ new ParserError(
82
+ `Couldn't find reference pattern to parse, with the name ${this.name}.`,
83
+ cursor.index,
84
+ this as Pattern
85
+ )
86
+ );
87
+ }
88
+
89
+ return null;
90
+ }
91
+ }
92
+
93
+ clone(name?: string, isOptional?: boolean) {
94
+ if (name == null) {
95
+ name = this.name;
96
+ }
97
+
98
+ if (isOptional == null) {
99
+ isOptional = this._isOptional;
100
+ }
101
+
102
+ return new Reference(name, isOptional);
103
+ }
104
+
105
+ private safelyGetPattern() {
106
+ let pattern = this.children[0];
107
+ const hasNoPattern = pattern == null;
108
+
109
+ if (hasNoPattern) {
110
+ const reference = this.findPattern();
111
+ if (reference == null) {
112
+ throw new Error(
113
+ `Couldn't find reference pattern, with the name ${this.name}.`
114
+ );
115
+ }
116
+
117
+ pattern = reference;
118
+ this.children = [pattern];
119
+ }
120
+
121
+ return pattern;
122
+ }
123
+
124
+ getTokens() {
125
+ return this.safelyGetPattern().getTokens();
126
+ }
127
+ }
@@ -0,0 +1,123 @@
1
+ import ParseError from "./ParseError";
2
+ import Node from "../ast/Node";
3
+ import Pattern from "./Pattern";
4
+ import Cursor from "../Cursor";
5
+
6
+ export default class Regex extends Pattern {
7
+ private regexString: string;
8
+ private regex: RegExp;
9
+ private node: Node | null = null;
10
+ private cursor: Cursor | null = null;
11
+ private substring: string = "";
12
+
13
+ constructor(name: string, regex: string, isOptional = false) {
14
+ super("regex", name, [], isOptional);
15
+ this.regexString = regex;
16
+ this.regex = new RegExp(`^${regex}`, "g");
17
+ this.assertArguments();
18
+ }
19
+
20
+ private assertArguments() {
21
+ if (this.regexString.length < 1) {
22
+ throw new Error(
23
+ "Invalid Arguments: The regex string argument needs to be at least one character long."
24
+ );
25
+ }
26
+
27
+ if (this.regexString.charAt(0) === "^") {
28
+ throw new Error(
29
+ "Invalid Arguments: The regex string cannot start with a '^' because it is expected to be in the middle of a string."
30
+ );
31
+ }
32
+
33
+ if (this.regexString.charAt(this.regexString.length - 1) === "$") {
34
+ throw new Error(
35
+ "Invalid Arguments: The regex string cannot end with a '$' because it is expected to be in the middle of a string."
36
+ );
37
+ }
38
+ }
39
+
40
+ parse(cursor: Cursor) {
41
+ this.resetState(cursor);
42
+ this.tryToParse();
43
+
44
+ return this.node;
45
+ }
46
+
47
+ private resetState(cursor: Cursor) {
48
+ this.cursor = cursor;
49
+ this.regex.lastIndex = 0;
50
+ this.substring = this.cursor.text.substr(this.cursor.getIndex());
51
+ this.node = null;
52
+ }
53
+
54
+ private tryToParse() {
55
+ const result = this.regex.exec(this.substring);
56
+
57
+ if (result != null && result.index === 0) {
58
+ this.processResult(result);
59
+ } else {
60
+ this.processError();
61
+ }
62
+ }
63
+
64
+ private processResult(result: RegExpExecArray) {
65
+ const cursor = this.safelyGetCursor();
66
+ const currentIndex = cursor.getIndex();
67
+ const newIndex = currentIndex + result[0].length - 1;
68
+
69
+ this.node = new Node(
70
+ "regex",
71
+ this.name,
72
+ currentIndex,
73
+ newIndex,
74
+ [],
75
+ result[0]
76
+ );
77
+
78
+ cursor.moveToMark(newIndex);
79
+ cursor.addMatch(this, this.node);
80
+ }
81
+
82
+ private processError() {
83
+ const cursor = this.safelyGetCursor();
84
+
85
+ if (!this._isOptional) {
86
+ const message = `ParseError: Expected regex pattern of '${this.regexString}' but found '${this.substring}'.`;
87
+ const parseError = new ParseError(message, cursor.getIndex(), this);
88
+
89
+ cursor.throwError(parseError);
90
+ }
91
+
92
+ this.node = null;
93
+ }
94
+
95
+ private safelyGetCursor() {
96
+ const cursor = this.cursor;
97
+
98
+ if (cursor == null) {
99
+ throw new Error("Couldn't find cursor.");
100
+ }
101
+ return cursor;
102
+ }
103
+
104
+ clone(name?: string, isOptional?: boolean) {
105
+ if (name == null) {
106
+ name = this.name;
107
+ }
108
+
109
+ if (isOptional == null) {
110
+ isOptional = this._isOptional;
111
+ }
112
+
113
+ return new Regex(name, this.regexString, isOptional);
114
+ }
115
+
116
+ getTokenValue() {
117
+ return this.name;
118
+ }
119
+
120
+ getTokens() {
121
+ return [this.name];
122
+ }
123
+ }