clarity-pattern-parser 6.0.0 → 7.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 (94) hide show
  1. package/TODO.md +1 -76
  2. package/dist/ast/Node.d.ts +1 -0
  3. package/dist/grammar/Grammar.d.ts +17 -0
  4. package/dist/grammar/patterns/andLiteral.d.ts +2 -0
  5. package/dist/grammar/patterns/comment.d.ts +2 -0
  6. package/dist/grammar/patterns/grammar.d.ts +2 -0
  7. package/dist/grammar/patterns/literal.d.ts +2 -0
  8. package/dist/grammar/patterns/name.d.ts +2 -0
  9. package/dist/grammar/patterns/orLiteral.d.ts +2 -0
  10. package/dist/grammar/patterns/pattern.d.ts +2 -0
  11. package/dist/grammar/patterns/regexLiteral.d.ts +2 -0
  12. package/dist/grammar/patterns/repeatLiteral.d.ts +3 -0
  13. package/dist/grammar/patterns/spaces.d.ts +2 -0
  14. package/dist/grammar/patterns/statement.d.ts +2 -0
  15. package/dist/index.browser.js +1205 -550
  16. package/dist/index.browser.js.map +1 -1
  17. package/dist/index.d.ts +5 -4
  18. package/dist/index.esm.js +1203 -549
  19. package/dist/index.esm.js.map +1 -1
  20. package/dist/index.js +1203 -548
  21. package/dist/index.js.map +1 -1
  22. package/dist/intellisense/AutoComplete.d.ts +9 -7
  23. package/dist/intellisense/Suggestion.d.ts +1 -2
  24. package/dist/patterns/And.d.ts +2 -1
  25. package/dist/patterns/Cursor.d.ts +1 -0
  26. package/dist/patterns/CursorHistory.d.ts +2 -1
  27. package/dist/patterns/FiniteRepeat.d.ts +39 -0
  28. package/dist/patterns/InfiniteRepeat.d.ts +47 -0
  29. package/dist/patterns/Literal.d.ts +2 -1
  30. package/dist/patterns/Not.d.ts +2 -1
  31. package/dist/patterns/Or.d.ts +2 -1
  32. package/dist/patterns/Pattern.d.ts +3 -2
  33. package/dist/patterns/Reference.d.ts +2 -1
  34. package/dist/patterns/Regex.d.ts +2 -1
  35. package/dist/patterns/Repeat.d.ts +19 -22
  36. package/jest.config.js +0 -1
  37. package/jest.coverage.config.js +13 -0
  38. package/package.json +3 -3
  39. package/src/ast/Node.test.ts +21 -0
  40. package/src/ast/Node.ts +12 -6
  41. package/src/grammar/Grammar.test.ts +288 -0
  42. package/src/grammar/Grammar.ts +234 -0
  43. package/src/grammar/patterns/andLiteral.ts +8 -0
  44. package/src/grammar/patterns/comment.ts +3 -0
  45. package/src/grammar/patterns/grammar.ts +19 -0
  46. package/src/grammar/patterns/literal.ts +5 -0
  47. package/src/grammar/patterns/name.ts +3 -0
  48. package/src/grammar/patterns/orLiteral.ts +8 -0
  49. package/src/grammar/patterns/pattern.ts +13 -0
  50. package/src/grammar/patterns/regexLiteral.ts +4 -0
  51. package/src/grammar/patterns/repeatLiteral.ts +72 -0
  52. package/src/grammar/patterns/spaces.ts +4 -0
  53. package/src/grammar/patterns/statement.ts +35 -0
  54. package/src/grammar/spec.md +142 -0
  55. package/src/index.ts +6 -3
  56. package/src/intellisense/AutoComplete.test.ts +125 -39
  57. package/src/intellisense/AutoComplete.ts +52 -36
  58. package/src/intellisense/Suggestion.ts +1 -2
  59. package/src/intellisense/css/cssValue.ts +1 -1
  60. package/src/intellisense/css/method.ts +1 -1
  61. package/src/intellisense/css/values.ts +1 -1
  62. package/src/intellisense/javascript/Javascript.test.ts +34 -11
  63. package/src/intellisense/javascript/arrayLiteral.ts +1 -1
  64. package/src/intellisense/javascript/{expressionStatement.ts → assignment.ts} +7 -8
  65. package/src/intellisense/javascript/expression.ts +45 -27
  66. package/src/intellisense/javascript/infixOperator.ts +6 -2
  67. package/src/intellisense/javascript/invocation.ts +1 -1
  68. package/src/intellisense/javascript/keywords.ts +3 -0
  69. package/src/intellisense/javascript/objectAccess.ts +9 -0
  70. package/src/intellisense/javascript/objectLiteral.ts +3 -3
  71. package/src/intellisense/javascript/parameters.ts +1 -1
  72. package/src/intellisense/javascript/propertyAccess.ts +8 -3
  73. package/src/intellisense/javascript/stringLiteral.ts +14 -8
  74. package/src/patterns/And.test.ts +16 -3
  75. package/src/patterns/And.ts +25 -17
  76. package/src/patterns/Cursor.ts +4 -0
  77. package/src/patterns/CursorHistory.ts +34 -5
  78. package/src/patterns/FiniteRepeat.test.ts +481 -0
  79. package/src/patterns/FiniteRepeat.ts +231 -0
  80. package/src/patterns/InfiniteRepeat.test.ts +296 -0
  81. package/src/patterns/InfiniteRepeat.ts +329 -0
  82. package/src/patterns/Literal.test.ts +13 -4
  83. package/src/patterns/Literal.ts +5 -1
  84. package/src/patterns/Not.test.ts +20 -9
  85. package/src/patterns/Not.ts +5 -1
  86. package/src/patterns/Or.test.ts +18 -7
  87. package/src/patterns/Or.ts +11 -1
  88. package/src/patterns/Pattern.ts +3 -2
  89. package/src/patterns/Reference.test.ts +18 -8
  90. package/src/patterns/Reference.ts +5 -1
  91. package/src/patterns/Regex.test.ts +13 -4
  92. package/src/patterns/Regex.ts +5 -1
  93. package/src/patterns/Repeat.test.ts +162 -158
  94. package/src/patterns/Repeat.ts +95 -226
@@ -234,11 +234,25 @@ export class And implements Pattern {
234
234
  return this.parent.getTokensAfter(this);
235
235
  }
236
236
 
237
+ getPatterns(): Pattern[] {
238
+ const patterns: Pattern[] = [];
239
+
240
+ for (const pattern of this._children) {
241
+ patterns.push(...pattern.getPatterns());
242
+
243
+ if (!pattern.isOptional) {
244
+ break;
245
+ }
246
+ }
247
+
248
+ return patterns;
249
+ }
250
+
237
251
  getPatternsAfter(childReference: Pattern): Pattern[] {
252
+ const patterns: Pattern[] = [];
238
253
  let nextSibling: Pattern | null = null;
239
254
  let nextSiblingIndex = -1;
240
255
  let index = -1;
241
- const patterns: Pattern[] = [];
242
256
 
243
257
  for (let i = 0; i < this._children.length; i++) {
244
258
  if (this._children[i] === childReference) {
@@ -261,24 +275,18 @@ export class And implements Pattern {
261
275
  return this._parent.getPatternsAfter(this);
262
276
  }
263
277
 
264
- // Next pattern isn't optional so send it back as the next patterns.
265
- if (nextSibling !== null && !nextSibling.isOptional) {
266
- return [nextSibling];
267
- }
268
-
269
278
  // Send back as many optional patterns as possible.
270
- if (nextSibling !== null && nextSibling.isOptional) {
271
- for (let i = nextSiblingIndex; i < this._children.length; i++) {
272
- const child = this._children[i];
273
- patterns.push(child);
279
+ for (let i = nextSiblingIndex; i < this._children.length; i++) {
280
+ const child = this._children[i];
281
+ patterns.push(child);
274
282
 
275
- if (!child.isOptional) {
276
- break;
277
- }
283
+ if (!child.isOptional) {
284
+ break;
285
+ }
278
286
 
279
- if (i === this._children.length - 1 && this._parent !== null) {
280
- patterns.push(...this._parent.getPatternsAfter(this));
281
- }
287
+ // If we are on the last child and its options then ask for the next pattern from the parent.
288
+ if (i === this._children.length - 1 && this._parent !== null) {
289
+ patterns.push(...this._parent.getPatternsAfter(this));
282
290
  }
283
291
  }
284
292
 
@@ -293,7 +301,7 @@ export class And implements Pattern {
293
301
  return this.parent.getPatternsAfter(this)
294
302
  }
295
303
 
296
- findPattern(predicate: (p: Pattern) => boolean): Pattern | null {
304
+ find(predicate: (p: Pattern) => boolean): Pattern | null {
297
305
  return findPattern(this, predicate);
298
306
  }
299
307
 
@@ -33,6 +33,10 @@ export class Cursor {
33
33
  return this._history.leafMatch;
34
34
  }
35
35
 
36
+ get leafMatches(): Match[] {
37
+ return this._history.leafMatches;
38
+ }
39
+
36
40
  get furthestError(): ParseError | null {
37
41
  return this._history.furthestError;
38
42
  }
@@ -9,7 +9,7 @@ export interface Match {
9
9
 
10
10
  export class CursorHistory {
11
11
  private _isRecording: boolean = false;
12
- private _leafMatch: Match = { pattern: null, node: null };
12
+ private _leafMatches: Match[] = [{ pattern: null, node: null }];
13
13
  private _furthestError: ParseError | null = null;
14
14
  private _currentError: ParseError | null = null;
15
15
  private _rootMatch: Match = { pattern: null, node: null };
@@ -26,7 +26,11 @@ export class CursorHistory {
26
26
  }
27
27
 
28
28
  get leafMatch(): Match {
29
- return this._leafMatch;
29
+ return this._leafMatches[this._leafMatches.length - 1];
30
+ }
31
+
32
+ get leafMatches() {
33
+ return this._leafMatches;
30
34
  }
31
35
 
32
36
  get furthestError(): ParseError | null {
@@ -57,13 +61,38 @@ export class CursorHistory {
57
61
 
58
62
  this._rootMatch.pattern = pattern;
59
63
  this._rootMatch.node = node;
64
+ const leafMatch = this._leafMatches[this._leafMatches.length - 1];
60
65
 
61
66
  const isFurthestMatch =
62
- this._leafMatch.node === null || node.lastIndex > this._leafMatch.node.lastIndex;
67
+ leafMatch.node === null || node.lastIndex > leafMatch.node.lastIndex;
68
+
69
+ const isSameIndexMatch =
70
+ leafMatch.node === null || node.lastIndex === leafMatch.node.lastIndex
63
71
 
64
72
  if (isFurthestMatch) {
65
- this._leafMatch.pattern = pattern;
66
- this._leafMatch.node = node;
73
+ // This is to save on GC churn.
74
+ const match = this._leafMatches.pop() as Match;
75
+ match.pattern = pattern;
76
+ match.node = node;
77
+
78
+ this._leafMatches.length = 0;
79
+ this._leafMatches.push(match);
80
+ } else if (isSameIndexMatch) {
81
+ const isAncestor = this._leafMatches.some((m) => {
82
+ let parent = m.pattern?.parent;
83
+
84
+ while (parent != null) {
85
+ if (parent == pattern.parent) {
86
+ return true;
87
+ }
88
+ parent = parent.parent
89
+ }
90
+ return false;
91
+ })
92
+
93
+ if (!isAncestor) {
94
+ this._leafMatches.unshift({ pattern, node });
95
+ }
67
96
  }
68
97
  }
69
98
 
@@ -0,0 +1,481 @@
1
+ import { FiniteRepeat } from "./FiniteRepeat";
2
+ import { Cursor } from "./Cursor";
3
+ import { Regex } from "./Regex";
4
+ import { Node } from "../ast/Node";
5
+ import { Literal } from "./Literal";
6
+ import { And } from "./And";
7
+
8
+ describe("BoundedRepeat", () => {
9
+ test("Bounds Without Divider", () => {
10
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, {
11
+ min: 2
12
+ });
13
+
14
+ let cursor = new Cursor("1");
15
+ let result = numbers.parse(cursor);
16
+ let expected: Node | null = null;
17
+ expect(result).toBeNull();
18
+ expect(cursor.hasError).toBeTruthy();
19
+
20
+ cursor = new Cursor("12");
21
+ result = numbers.parse(cursor);
22
+ expected = new Node("finite-repeat", "numbers", 0, 1, [
23
+ new Node("regex", "number", 0, 0, [], "1"),
24
+ new Node("regex", "number", 1, 1, [], "2"),
25
+ ]);
26
+
27
+ expect(result).toEqual(expected);
28
+ expect(cursor.hasError).toBeFalsy();
29
+
30
+ cursor = new Cursor("123");
31
+ result = numbers.parse(cursor);
32
+ expected = new Node("finite-repeat", "numbers", 0, 2, [
33
+ new Node("regex", "number", 0, 0, [], "1"),
34
+ new Node("regex", "number", 1, 1, [], "2"),
35
+ new Node("regex", "number", 2, 2, [], "3"),
36
+ ]);
37
+
38
+ expect(result).toEqual(expected);
39
+ expect(cursor.hasError).toBeFalsy();
40
+
41
+ cursor = new Cursor("1234");
42
+ result = numbers.parse(cursor);
43
+ expected = new Node("finite-repeat", "numbers", 0, 2, [
44
+ new Node("regex", "number", 0, 0, [], "1"),
45
+ new Node("regex", "number", 1, 1, [], "2"),
46
+ new Node("regex", "number", 2, 2, [], "3"),
47
+ ]);
48
+
49
+ expect(result).toEqual(expected);
50
+ expect(cursor.hasError).toBeFalsy();
51
+ expect(cursor.index).toBe(2);
52
+
53
+ cursor = new Cursor("12f");
54
+ result = numbers.parse(cursor);
55
+ expected = new Node("finite-repeat", "numbers", 0, 1, [
56
+ new Node("regex", "number", 0, 0, [], "1"),
57
+ new Node("regex", "number", 1, 1, [], "2"),
58
+ ]);
59
+
60
+ expect(result).toEqual(expected);
61
+ expect(cursor.hasError).toBeFalsy();
62
+ });
63
+
64
+ test("Bounds Are Equal Without Divider", () => {
65
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 3});
66
+
67
+ let cursor = new Cursor("1");
68
+ let result = numbers.parse(cursor);
69
+ let expected: Node | null = null;
70
+ expect(result).toBeNull();
71
+ expect(cursor.hasError).toBeTruthy();
72
+
73
+ cursor = new Cursor("12");
74
+ result = numbers.parse(cursor);
75
+ expected = null;
76
+
77
+ expect(result).toBeNull();
78
+ expect(cursor.hasError).toBeTruthy();
79
+
80
+ cursor = new Cursor("123");
81
+ result = numbers.parse(cursor);
82
+ expected = new Node("finite-repeat", "numbers", 0, 2, [
83
+ new Node("regex", "number", 0, 0, [], "1"),
84
+ new Node("regex", "number", 1, 1, [], "2"),
85
+ new Node("regex", "number", 2, 2, [], "3"),
86
+ ]);
87
+
88
+ expect(result).toEqual(expected);
89
+ expect(cursor.hasError).toBeFalsy();
90
+
91
+ cursor = new Cursor("1234");
92
+ result = numbers.parse(cursor);
93
+ expected = new Node("finite-repeat", "numbers", 0, 2, [
94
+ new Node("regex", "number", 0, 0, [], "1"),
95
+ new Node("regex", "number", 1, 1, [], "2"),
96
+ new Node("regex", "number", 2, 2, [], "3"),
97
+ ]);
98
+
99
+ expect(result).toEqual(expected);
100
+ expect(cursor.hasError).toBeFalsy();
101
+ expect(cursor.index).toBe(2);
102
+
103
+ });
104
+
105
+ test("Bounds With Divider", () => {
106
+ const divider = new Literal("comma", ",");
107
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { divider, min: 2, trimDivider: true });
108
+
109
+ let cursor = new Cursor("1,");
110
+ let result = numbers.parse(cursor);
111
+ let expected: Node | null = null;
112
+ expect(result).toBeNull();
113
+ expect(cursor.hasError).toBeTruthy();
114
+
115
+ cursor = new Cursor("1,2");
116
+ result = numbers.parse(cursor);
117
+ expected = new Node("finite-repeat", "numbers", 0, 2, [
118
+ new Node("regex", "number", 0, 0, [], "1"),
119
+ new Node("literal", "comma", 1, 1, [], ","),
120
+ new Node("regex", "number", 2, 2, [], "2"),
121
+ ]);
122
+
123
+ expect(result).toEqual(expected);
124
+ expect(cursor.hasError).toBeFalsy();
125
+
126
+ cursor = new Cursor("1,2,3,");
127
+ result = numbers.parse(cursor);
128
+ expected = new Node("finite-repeat", "numbers", 0, 4, [
129
+ new Node("regex", "number", 0, 0, [], "1"),
130
+ new Node("literal", "comma", 1, 1, [], ","),
131
+ new Node("regex", "number", 2, 2, [], "2"),
132
+ new Node("literal", "comma", 3, 3, [], ","),
133
+ new Node("regex", "number", 4, 4, [], "3"),
134
+ ]);
135
+
136
+ expect(result).toEqual(expected);
137
+ expect(cursor.hasError).toBeFalsy();
138
+
139
+ cursor = new Cursor("1,2,3,4");
140
+ result = numbers.parse(cursor);
141
+ expected = new Node("finite-repeat", "numbers", 0, 4, [
142
+ new Node("regex", "number", 0, 0, [], "1"),
143
+ new Node("literal", "comma", 1, 1, [], ","),
144
+ new Node("regex", "number", 2, 2, [], "2"),
145
+ new Node("literal", "comma", 3, 3, [], ","),
146
+ new Node("regex", "number", 4, 4, [], "3"),
147
+ ]);
148
+
149
+ expect(result).toEqual(expected);
150
+ expect(cursor.hasError).toBeFalsy();
151
+ });
152
+
153
+ test("Optional Repeating Pattern", () => {
154
+ const digit = new Regex("digit", "\\d+", true);
155
+ const divider = new Regex("divider", "\\s");
156
+ const integer = new FiniteRepeat("number", digit, 4, { divider });
157
+ const cursor = new Cursor(
158
+ "\n" +
159
+ "3\n" +
160
+ "\n" +
161
+ "\n"
162
+ );
163
+ const result = integer.parse(cursor);
164
+ const expected = new Node("finite-repeat", "number", 0, 4, [
165
+ new Node("regex", "divider", 0, 0, [], "\n"),
166
+ new Node("regex", "digit", 1, 1, [], "3"),
167
+ new Node("regex", "divider", 2, 2, [], "\n"),
168
+ new Node("regex", "divider", 3, 3, [], "\n"),
169
+ new Node("regex", "divider", 4, 4, [], "\n"),
170
+ ]);
171
+
172
+ expect(result).toEqual(expected)
173
+ expect(cursor.hasError).toBeFalsy()
174
+ });
175
+
176
+ test("Bounds Are Equal With Divider", () => {
177
+ const divider = new Literal("comma", ",");
178
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { divider, min: 3, trimDivider: true });
179
+
180
+ let cursor = new Cursor("1,");
181
+ let result = numbers.parse(cursor);
182
+ let expected: Node | null = null;
183
+ expect(result).toBeNull();
184
+ expect(cursor.hasError).toBeTruthy();
185
+
186
+ cursor = new Cursor("1,2");
187
+ result = numbers.parse(cursor);
188
+ expected = null;
189
+
190
+ expect(result).toEqual(expected);
191
+ expect(cursor.hasError).toBeTruthy();
192
+
193
+ cursor = new Cursor("1,2,3,");
194
+ result = numbers.parse(cursor);
195
+ expected = new Node("finite-repeat", "numbers", 0, 4, [
196
+ new Node("regex", "number", 0, 0, [], "1"),
197
+ new Node("literal", "comma", 1, 1, [], ","),
198
+ new Node("regex", "number", 2, 2, [], "2"),
199
+ new Node("literal", "comma", 3, 3, [], ","),
200
+ new Node("regex", "number", 4, 4, [], "3"),
201
+ ]);
202
+
203
+ expect(result).toEqual(expected);
204
+ expect(cursor.hasError).toBeFalsy();
205
+
206
+ cursor = new Cursor("1,2,3,4");
207
+ result = numbers.parse(cursor);
208
+ expected = new Node("finite-repeat", "numbers", 0, 4, [
209
+ new Node("regex", "number", 0, 0, [], "1"),
210
+ new Node("literal", "comma", 1, 1, [], ","),
211
+ new Node("regex", "number", 2, 2, [], "2"),
212
+ new Node("literal", "comma", 3, 3, [], ","),
213
+ new Node("regex", "number", 4, 4, [], "3"),
214
+ ]);
215
+
216
+ expect(result).toEqual(expected);
217
+ expect(cursor.hasError).toBeFalsy();
218
+ });
219
+
220
+ test("Test", () => {
221
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3);
222
+ const result = numbers.test("1");
223
+
224
+ expect(result).toBeTruthy();
225
+ });
226
+
227
+ test("Exec", () => {
228
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3);
229
+ const result = numbers.exec("1");
230
+
231
+ expect(result.ast).not.toBeNull();
232
+ expect(result.cursor.hasError).toBeFalsy();
233
+ });
234
+
235
+ test("Fail", () => {
236
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3);
237
+ const result = numbers.exec("f");
238
+
239
+ expect(result.ast).toBeNull();
240
+ expect(result.cursor.hasError).toBeTruthy();
241
+ });
242
+
243
+ test("Optional", () => {
244
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
245
+ const result = numbers.exec("f");
246
+
247
+ expect(result.ast).toBeNull();
248
+ expect(result.cursor.hasError).toBeFalsy();
249
+ });
250
+
251
+ test("Optional With Multiple Matches But Still Below Min", () => {
252
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
253
+ const result = numbers.exec("12f");
254
+
255
+ expect(result.ast).toBeNull();
256
+ expect(result.cursor.hasError).toBeFalsy();
257
+ });
258
+
259
+ test("Properties", () => {
260
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
261
+
262
+ expect(numbers.type).toBe("finite-repeat");
263
+ expect(numbers.name).toBe("numbers");
264
+ expect(numbers.parent).toBeNull();
265
+ expect(numbers.children.length).toBe(3);
266
+ expect(numbers.min).toBe(0);
267
+ expect(numbers.max).toBe(3);
268
+ expect(numbers.isOptional).toBeTruthy();
269
+ });
270
+
271
+ test("Clone", () => {
272
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
273
+ const clone = numbers.clone() as FiniteRepeat;
274
+
275
+ expect(clone.type).toBe(numbers.type);
276
+ expect(clone.name).toBe(numbers.name);
277
+ expect(clone.parent).toBeNull();
278
+ expect(clone.children.length).toBe(numbers.children.length);
279
+ expect(clone.min).toBe(numbers.min);
280
+ expect(clone.max).toBe(numbers.max);
281
+ expect(clone.isOptional).toBe(numbers.isOptional);
282
+ });
283
+
284
+ test("Clone With Custom Overrides", () => {
285
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
286
+ let clone = numbers.clone();
287
+ let expected = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
288
+
289
+ expect(clone).toEqual(expected);
290
+
291
+ clone = numbers.clone("cloned-numbers");
292
+ expected = new FiniteRepeat("cloned-numbers", new Regex("number", "\\d"), 3, { min: 0 });
293
+
294
+ expect(clone).toEqual(expected);
295
+
296
+ clone = numbers.clone("cloned-numbers", true);
297
+ expected = new FiniteRepeat("cloned-numbers", new Regex("number", "\\d"), 3, { min: 0 });
298
+
299
+ expect(clone).toEqual(expected);
300
+
301
+ clone = numbers.clone("cloned-numbers", false);
302
+ expected = new FiniteRepeat("cloned-numbers", new Regex("number", "\\d"), 3, { min: 1 });
303
+
304
+ expect(clone).toEqual(expected);
305
+ });
306
+
307
+ test("Get Tokens", () => {
308
+ const numbers = new FiniteRepeat(
309
+ "numbers",
310
+ new Literal("one", "1"),
311
+ 3,
312
+ {
313
+ divider: new Literal("comma", ","),
314
+ min: 0
315
+ });
316
+
317
+ const tokens = numbers.getTokens();
318
+
319
+ expect(tokens).toEqual(["1"]);
320
+ });
321
+
322
+ test("Get Tokens After Without Parent", () => {
323
+ const numbers = new FiniteRepeat(
324
+ "numbers",
325
+ new Literal("one", "1"),
326
+ 2,
327
+ {
328
+ divider: new Literal("comma", ","),
329
+ min: 0,
330
+ trimDivider: true
331
+ });
332
+
333
+ let child = numbers.children[0];
334
+ let tokens = numbers.getTokensAfter(child);
335
+
336
+ expect(tokens).toEqual([","]);
337
+
338
+ child = numbers.children[2];
339
+ tokens = numbers.getTokensAfter(child);
340
+
341
+ expect(tokens).toEqual([]);
342
+
343
+ child = numbers.children[3];
344
+ tokens = numbers.getTokensAfter(child);
345
+
346
+ expect(tokens).toEqual([]);
347
+ });
348
+
349
+ test("Get Tokens After With Parent", () => {
350
+ const numbers = new FiniteRepeat(
351
+ "numbers",
352
+ new Literal("one", "1"),
353
+ 2,
354
+ {
355
+ divider: new Literal("comma", ","),
356
+ trimDivider: true,
357
+ min: 0
358
+ });
359
+
360
+ const parent = new And("parent", [numbers, new Literal("b", "B")]);
361
+ const numbersClone = parent.children[0];
362
+ let child = numbersClone.children[0];
363
+ let tokens = numbersClone.getTokensAfter(child);
364
+
365
+ expect(tokens).toEqual([","]);
366
+
367
+ child = numbersClone.children[2];
368
+ tokens = numbersClone.getTokensAfter(child);
369
+
370
+ expect(tokens).toEqual(["B"]);
371
+
372
+ child = numbersClone.children[3];
373
+ tokens = numbersClone.getTokensAfter(child);
374
+
375
+ expect(tokens).toEqual([]);
376
+
377
+ });
378
+
379
+ test("Get Next Tokens", () => {
380
+ const numbers = new FiniteRepeat(
381
+ "numbers",
382
+ new Literal("one", "1"),
383
+ 2,
384
+ {
385
+ divider: new Literal("comma", ","),
386
+ min: 0
387
+ }
388
+ );
389
+
390
+ const parent = new And("parent", [numbers, new Literal("b", "B")]);
391
+ const numbersClone = parent.children[0];
392
+ const tokens = numbersClone.getNextTokens();
393
+
394
+
395
+ expect(tokens).toEqual(["B"]);
396
+ });
397
+
398
+ test("Get Next Tokens Without Parent", () => {
399
+ const numbers = new FiniteRepeat(
400
+ "numbers",
401
+ new Literal("one", "1"),
402
+ 2,
403
+ {
404
+ divider: new Literal("comma", ","),
405
+ min: 0
406
+ }
407
+ );
408
+
409
+ const tokens = numbers.getNextTokens();
410
+
411
+
412
+ expect(tokens).toEqual([]);
413
+ });
414
+
415
+ test("Get Patterns", () => {
416
+ const numbers = new FiniteRepeat(
417
+ "numbers",
418
+ new Literal("one", "1"),
419
+ 2,
420
+ {
421
+ divider: new Literal("comma", ","),
422
+ min: 0
423
+ }
424
+ );
425
+
426
+ const patterns = numbers.getPatterns();
427
+
428
+ expect(patterns).toEqual([numbers.children[0]]);
429
+ });
430
+
431
+ test("Get Next Patterns Without Parent", () => {
432
+ const numbers = new FiniteRepeat(
433
+ "numbers",
434
+ new Literal("one", "1"),
435
+ 2,
436
+ {
437
+ divider: new Literal("comma", ","),
438
+ min: 0
439
+ }
440
+ );
441
+
442
+ const patterns = numbers.getNextPatterns();
443
+
444
+ expect(patterns).toEqual([]);
445
+ });
446
+
447
+ test("Get Next Patterns With Parent", () => {
448
+ const numbers = new FiniteRepeat(
449
+ "numbers",
450
+ new Literal("one", "1"),
451
+ 2,
452
+ {
453
+ divider: new Literal("comma", ","),
454
+ min: 0
455
+ }
456
+ );
457
+
458
+ const parent = new And("parent", [numbers, new Literal("b", "B")]);
459
+ const numbersClone = parent.children[0];
460
+
461
+ const patterns = numbersClone.getNextPatterns();
462
+
463
+ expect(patterns).toEqual([parent.children[1]]);
464
+ });
465
+
466
+ test("Find Pattern", () => {
467
+ const numbers = new FiniteRepeat(
468
+ "numbers",
469
+ new Literal("one", "1"),
470
+ 2,
471
+ {
472
+ divider: new Literal("comma", ","),
473
+ min: 0
474
+ }
475
+ );
476
+
477
+ const comma = numbers.find(p => p.name === "comma");
478
+ expect(comma).toBe(numbers.children[1]);
479
+ });
480
+
481
+ });