clarity-pattern-parser 8.4.14 → 9.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 (100) hide show
  1. package/TODO.md +4 -1
  2. package/dist/grammar/Grammar.d.ts +18 -10
  3. package/dist/grammar/patterns/andLiteral.d.ts +2 -0
  4. package/dist/grammar/patterns/anonymousPattern.d.ts +2 -0
  5. package/dist/grammar/patterns/inlinePattern.d.ts +1 -0
  6. package/dist/grammar/patterns/literals.d.ts +3 -0
  7. package/dist/grammar/patterns/pattern.d.ts +2 -2
  8. package/dist/grammar/patterns.d.ts +2 -0
  9. package/dist/index.browser.js +472 -185
  10. package/dist/index.browser.js.map +1 -1
  11. package/dist/index.d.ts +3 -1
  12. package/dist/index.esm.js +471 -186
  13. package/dist/index.esm.js.map +1 -1
  14. package/dist/index.js +472 -185
  15. package/dist/index.js.map +1 -1
  16. package/dist/patterns/And.d.ts +4 -1
  17. package/dist/patterns/Cursor.d.ts +5 -0
  18. package/dist/patterns/CursorHistory.d.ts +7 -0
  19. package/dist/patterns/FiniteRepeat.d.ts +4 -1
  20. package/dist/patterns/InfiniteRepeat.d.ts +5 -4
  21. package/dist/patterns/Literal.d.ts +6 -5
  22. package/dist/patterns/Not.d.ts +5 -4
  23. package/dist/patterns/Or.d.ts +5 -4
  24. package/dist/patterns/Pattern.d.ts +4 -2
  25. package/dist/patterns/Reference.d.ts +5 -4
  26. package/dist/patterns/Regex.d.ts +5 -4
  27. package/dist/patterns/Repeat.d.ts +3 -0
  28. package/dist/patterns/arePatternsEqual.d.ts +2 -0
  29. package/package.json +1 -1
  30. package/src/grammar/Grammar.test.ts +126 -72
  31. package/src/grammar/Grammar.ts +241 -158
  32. package/src/grammar/patterns/anonymousPattern.ts +23 -0
  33. package/src/grammar/patterns/body.ts +9 -6
  34. package/src/grammar/patterns/comment.ts +3 -2
  35. package/src/grammar/patterns/grammar.ts +15 -12
  36. package/src/grammar/patterns/import.ts +18 -12
  37. package/src/grammar/patterns/literal.ts +2 -3
  38. package/src/grammar/patterns/literals.ts +20 -0
  39. package/src/grammar/patterns/optionsLiteral.ts +19 -0
  40. package/src/grammar/patterns/pattern.ts +23 -9
  41. package/src/grammar/patterns/regexLiteral.ts +1 -0
  42. package/src/grammar/patterns/repeatLiteral.ts +30 -25
  43. package/src/grammar/patterns/sequenceLiteral.ts +24 -0
  44. package/src/grammar/patterns/spaces.ts +8 -6
  45. package/src/grammar/patterns/statement.ts +8 -20
  46. package/src/grammar/patterns.test.ts +38 -0
  47. package/src/grammar/patterns.ts +24 -0
  48. package/src/grammar/spec.md +4 -12
  49. package/src/index.ts +11 -5
  50. package/src/intellisense/AutoComplete.test.ts +41 -40
  51. package/src/intellisense/css/method.ts +2 -2
  52. package/src/intellisense/css/unit.ts +2 -2
  53. package/src/intellisense/css/value.ts +1 -1
  54. package/src/intellisense/javascript/Javascript.test.ts +31 -32
  55. package/src/intellisense/javascript/arrayLiteral.ts +7 -6
  56. package/src/intellisense/javascript/assignment.ts +6 -6
  57. package/src/intellisense/javascript/deleteStatement.ts +2 -2
  58. package/src/intellisense/javascript/escapedCharacter.ts +6 -6
  59. package/src/intellisense/javascript/exponent.ts +6 -6
  60. package/src/intellisense/javascript/expression.ts +18 -17
  61. package/src/intellisense/javascript/fraction.ts +3 -3
  62. package/src/intellisense/javascript/infixOperator.ts +10 -10
  63. package/src/intellisense/javascript/integer.ts +1 -1
  64. package/src/intellisense/javascript/invocation.ts +8 -7
  65. package/src/intellisense/javascript/literal.ts +3 -3
  66. package/src/intellisense/javascript/numberLiteral.ts +5 -4
  67. package/src/intellisense/javascript/objectAccess.ts +2 -3
  68. package/src/intellisense/javascript/objectLiteral.ts +8 -7
  69. package/src/intellisense/javascript/optionalSpaces.ts +2 -1
  70. package/src/intellisense/javascript/parameters.ts +5 -5
  71. package/src/intellisense/javascript/prefixOperator.ts +3 -4
  72. package/src/intellisense/javascript/propertyAccess.ts +9 -8
  73. package/src/intellisense/javascript/stringLiteral.ts +14 -15
  74. package/src/patterns/Cursor.ts +42 -4
  75. package/src/patterns/CursorHistory.ts +20 -4
  76. package/src/patterns/FiniteRepeat.test.ts +52 -51
  77. package/src/patterns/FiniteRepeat.ts +60 -38
  78. package/src/patterns/InfiniteRepeat.test.ts +36 -49
  79. package/src/patterns/InfiniteRepeat.ts +70 -37
  80. package/src/patterns/Literal.test.ts +16 -27
  81. package/src/patterns/Literal.ts +34 -27
  82. package/src/patterns/Not.test.ts +7 -7
  83. package/src/patterns/Not.ts +24 -6
  84. package/src/patterns/Optional.test.ts +164 -0
  85. package/src/patterns/Optional.ts +143 -0
  86. package/src/patterns/{Or.test.ts → Options.test.ts} +51 -49
  87. package/src/patterns/{Or.ts → Options.ts} +32 -23
  88. package/src/patterns/Pattern.ts +6 -5
  89. package/src/patterns/Reference.test.ts +21 -22
  90. package/src/patterns/Reference.ts +26 -15
  91. package/src/patterns/Regex.test.ts +15 -15
  92. package/src/patterns/Regex.ts +29 -19
  93. package/src/patterns/Repeat.test.ts +12 -22
  94. package/src/patterns/Repeat.ts +22 -21
  95. package/src/patterns/{And.test.ts → Sequence.test.ts} +78 -78
  96. package/src/patterns/{And.ts → Sequence.ts} +40 -29
  97. package/src/patterns/arePatternsEqual.ts +12 -0
  98. package/src/patterns/clonePatterns.ts +2 -2
  99. package/src/grammar/patterns/andLiteral.ts +0 -8
  100. package/src/grammar/patterns/orLiteral.ts +0 -8
@@ -3,12 +3,15 @@ import { Cursor } from "./Cursor";
3
3
  import { Regex } from "./Regex";
4
4
  import { Node } from "../ast/Node";
5
5
  import { Literal } from "./Literal";
6
- import { And } from "./And";
6
+ import { Sequence } from "./Sequence";
7
+ import { arePatternsEqual } from "./arePatternsEqual";
8
+ import { Optional } from "./Optional";
7
9
 
8
10
  describe("BoundedRepeat", () => {
9
11
  test("Bounds Without Divider", () => {
10
- const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, {
11
- min: 2
12
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), {
13
+ min: 2,
14
+ max: 3
12
15
  });
13
16
 
14
17
  let cursor = new Cursor("1");
@@ -62,7 +65,7 @@ describe("BoundedRepeat", () => {
62
65
  });
63
66
 
64
67
  test("Bounds Are Equal Without Divider", () => {
65
- const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 3});
68
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), { min: 3, max: 3 });
66
69
 
67
70
  let cursor = new Cursor("1");
68
71
  let result = numbers.parse(cursor);
@@ -104,7 +107,7 @@ describe("BoundedRepeat", () => {
104
107
 
105
108
  test("Bounds With Divider", () => {
106
109
  const divider = new Literal("comma", ",");
107
- const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { divider, min: 2, trimDivider: true });
110
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), { divider, min: 2, max: 3, trimDivider: true });
108
111
 
109
112
  let cursor = new Cursor("1,");
110
113
  let result = numbers.parse(cursor);
@@ -151,9 +154,9 @@ describe("BoundedRepeat", () => {
151
154
  });
152
155
 
153
156
  test("Optional Repeating Pattern", () => {
154
- const digit = new Regex("digit", "\\d+", true);
157
+ const digit = new Optional("optional-digit", new Regex("digit", "\\d+"));
155
158
  const divider = new Regex("divider", "\\s");
156
- const integer = new FiniteRepeat("number", digit, 4, { divider });
159
+ const integer = new FiniteRepeat("number", digit, { divider, max: 4 });
157
160
  const cursor = new Cursor(
158
161
  "\n" +
159
162
  "3\n" +
@@ -169,13 +172,13 @@ describe("BoundedRepeat", () => {
169
172
  new Node("regex", "divider", 4, 4, [], "\n"),
170
173
  ]);
171
174
 
172
- expect(result).toEqual(expected)
173
- expect(cursor.hasError).toBeFalsy()
175
+ expect(result).toEqual(expected);
176
+ expect(cursor.hasError).toBeFalsy();
174
177
  });
175
178
 
176
179
  test("Bounds Are Equal With Divider", () => {
177
180
  const divider = new Literal("comma", ",");
178
- const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { divider, min: 3, trimDivider: true });
181
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), { divider, min: 3, max: 3, trimDivider: true });
179
182
 
180
183
  let cursor = new Cursor("1,");
181
184
  let result = numbers.parse(cursor);
@@ -218,14 +221,14 @@ describe("BoundedRepeat", () => {
218
221
  });
219
222
 
220
223
  test("Test", () => {
221
- const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3);
224
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), { max: 3 });
222
225
  const result = numbers.test("1");
223
226
 
224
227
  expect(result).toBeTruthy();
225
228
  });
226
229
 
227
230
  test("Exec", () => {
228
- const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3);
231
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), { max: 3 });
229
232
  const result = numbers.exec("1");
230
233
 
231
234
  expect(result.ast).not.toBeNull();
@@ -233,7 +236,7 @@ describe("BoundedRepeat", () => {
233
236
  });
234
237
 
235
238
  test("Fail", () => {
236
- const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3);
239
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), { max: 3 });
237
240
  const result = numbers.exec("f");
238
241
 
239
242
  expect(result.ast).toBeNull();
@@ -241,7 +244,7 @@ describe("BoundedRepeat", () => {
241
244
  });
242
245
 
243
246
  test("Optional", () => {
244
- const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
247
+ const numbers = new FiniteRepeat("numbers", new Optional("optional-number", new Regex("number", "\\d")), { min: 0, max: 3 });
245
248
  const result = numbers.exec("f");
246
249
 
247
250
  expect(result.ast).toBeNull();
@@ -249,7 +252,7 @@ describe("BoundedRepeat", () => {
249
252
  });
250
253
 
251
254
  test("Optional With Multiple Matches But Still Below Min", () => {
252
- const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
255
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), { min: 0, max: 3 });
253
256
  const result = numbers.exec("12f");
254
257
 
255
258
  expect(result.ast).toBeNull();
@@ -257,19 +260,18 @@ describe("BoundedRepeat", () => {
257
260
  });
258
261
 
259
262
  test("Properties", () => {
260
- const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
263
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), { min: 1, max: 3 });
261
264
 
262
265
  expect(numbers.type).toBe("finite-repeat");
263
266
  expect(numbers.name).toBe("numbers");
264
267
  expect(numbers.parent).toBeNull();
265
268
  expect(numbers.children.length).toBe(3);
266
- expect(numbers.min).toBe(0);
269
+ expect(numbers.min).toBe(1);
267
270
  expect(numbers.max).toBe(3);
268
- expect(numbers.isOptional).toBeTruthy();
269
271
  });
270
272
 
271
273
  test("Clone", () => {
272
- const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
274
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), { min: 0, max: 3 });
273
275
  const clone = numbers.clone() as FiniteRepeat;
274
276
 
275
277
  expect(clone.type).toBe(numbers.type);
@@ -278,40 +280,39 @@ describe("BoundedRepeat", () => {
278
280
  expect(clone.children.length).toBe(numbers.children.length);
279
281
  expect(clone.min).toBe(numbers.min);
280
282
  expect(clone.max).toBe(numbers.max);
281
- expect(clone.isOptional).toBe(numbers.isOptional);
282
283
  });
283
284
 
284
285
  test("Clone With Custom Overrides", () => {
285
- const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
286
+ const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), { min: 0, max: 3 });
286
287
  let clone = numbers.clone();
287
- let expected = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
288
+ let expected = new FiniteRepeat("numbers", new Regex("number", "\\d"), { min: 0, max: 3 });
288
289
 
289
- expect(clone).toEqual(expected);
290
+ expect(arePatternsEqual(clone, expected)).toBeTruthy();
290
291
 
291
292
  clone = numbers.clone("cloned-numbers");
292
- expected = new FiniteRepeat("cloned-numbers", new Regex("number", "\\d"), 3, { min: 0 });
293
+ expected = new FiniteRepeat("cloned-numbers", new Regex("number", "\\d"), { min: 0, max: 3 });
293
294
 
294
- expect(clone).toEqual(expected);
295
+ expect(arePatternsEqual(clone, expected)).toBeTruthy();
295
296
 
296
- clone = numbers.clone("cloned-numbers", true);
297
- expected = new FiniteRepeat("cloned-numbers", new Regex("number", "\\d"), 3, { min: 0 });
297
+ clone = numbers.clone("cloned-numbers");
298
+ expected = new FiniteRepeat("cloned-numbers", new Regex("number", "\\d"), { min: 0, max: 3 });
298
299
 
299
- expect(clone).toEqual(expected);
300
+ expect(arePatternsEqual(clone, expected)).toBeTruthy();
300
301
 
301
- clone = numbers.clone("cloned-numbers", false);
302
- expected = new FiniteRepeat("cloned-numbers", new Regex("number", "\\d"), 3, { min: 1 });
302
+ clone = numbers.clone("cloned-numbers");
303
+ expected = new FiniteRepeat("cloned-numbers", new Regex("number", "\\d"), { min: 0, max: 3 });
303
304
 
304
- expect(clone).toEqual(expected);
305
+ expect(arePatternsEqual(clone, expected)).toBeTruthy();
305
306
  });
306
307
 
307
308
  test("Get Tokens", () => {
308
309
  const numbers = new FiniteRepeat(
309
310
  "numbers",
310
311
  new Literal("one", "1"),
311
- 3,
312
312
  {
313
313
  divider: new Literal("comma", ","),
314
- min: 0
314
+ min: 0,
315
+ max: 3
315
316
  });
316
317
 
317
318
  const tokens = numbers.getTokens();
@@ -323,10 +324,10 @@ describe("BoundedRepeat", () => {
323
324
  const numbers = new FiniteRepeat(
324
325
  "numbers",
325
326
  new Literal("one", "1"),
326
- 2,
327
327
  {
328
328
  divider: new Literal("comma", ","),
329
329
  min: 0,
330
+ max: 2,
330
331
  trimDivider: true
331
332
  });
332
333
 
@@ -350,14 +351,14 @@ describe("BoundedRepeat", () => {
350
351
  const numbers = new FiniteRepeat(
351
352
  "numbers",
352
353
  new Literal("one", "1"),
353
- 2,
354
354
  {
355
355
  divider: new Literal("comma", ","),
356
356
  trimDivider: true,
357
- min: 0
357
+ min: 0,
358
+ max: 2
358
359
  });
359
360
 
360
- const parent = new And("parent", [numbers, new Literal("b", "B")]);
361
+ const parent = new Sequence("parent", [numbers, new Literal("b", "B")]);
361
362
  const numbersClone = parent.children[0];
362
363
  let child = numbersClone.children[0];
363
364
  let tokens = numbersClone.getTokensAfter(child);
@@ -380,14 +381,14 @@ describe("BoundedRepeat", () => {
380
381
  const numbers = new FiniteRepeat(
381
382
  "numbers",
382
383
  new Literal("one", "1"),
383
- 2,
384
384
  {
385
385
  divider: new Literal("comma", ","),
386
- min: 0
386
+ min: 0,
387
+ max: 2
387
388
  }
388
389
  );
389
390
 
390
- const parent = new And("parent", [numbers, new Literal("b", "B")]);
391
+ const parent = new Sequence("parent", [numbers, new Literal("b", "B")]);
391
392
  const numbersClone = parent.children[0];
392
393
  const tokens = numbersClone.getNextTokens();
393
394
 
@@ -399,10 +400,10 @@ describe("BoundedRepeat", () => {
399
400
  const numbers = new FiniteRepeat(
400
401
  "numbers",
401
402
  new Literal("one", "1"),
402
- 2,
403
403
  {
404
404
  divider: new Literal("comma", ","),
405
- min: 0
405
+ min: 0,
406
+ max: 2
406
407
  }
407
408
  );
408
409
 
@@ -416,10 +417,10 @@ describe("BoundedRepeat", () => {
416
417
  const numbers = new FiniteRepeat(
417
418
  "numbers",
418
419
  new Literal("one", "1"),
419
- 2,
420
420
  {
421
421
  divider: new Literal("comma", ","),
422
- min: 0
422
+ min: 0,
423
+ max: 2
423
424
  }
424
425
  );
425
426
 
@@ -432,10 +433,10 @@ describe("BoundedRepeat", () => {
432
433
  const numbers = new FiniteRepeat(
433
434
  "numbers",
434
435
  new Literal("one", "1"),
435
- 2,
436
436
  {
437
437
  divider: new Literal("comma", ","),
438
- min: 0
438
+ min: 0,
439
+ max: 2
439
440
  }
440
441
  );
441
442
 
@@ -448,14 +449,14 @@ describe("BoundedRepeat", () => {
448
449
  const numbers = new FiniteRepeat(
449
450
  "numbers",
450
451
  new Literal("one", "1"),
451
- 2,
452
452
  {
453
453
  divider: new Literal("comma", ","),
454
- min: 0
454
+ min: 0,
455
+ max: 2
455
456
  }
456
457
  );
457
458
 
458
- const parent = new And("parent", [numbers, new Literal("b", "B")]);
459
+ const parent = new Sequence("parent", [numbers, new Literal("b", "B")]);
459
460
  const numbersClone = parent.children[0];
460
461
 
461
462
  const patterns = numbersClone.getNextPatterns();
@@ -467,10 +468,10 @@ describe("BoundedRepeat", () => {
467
468
  const numbers = new FiniteRepeat(
468
469
  "numbers",
469
470
  new Literal("one", "1"),
470
- 2,
471
471
  {
472
472
  divider: new Literal("comma", ","),
473
- min: 0
473
+ min: 0,
474
+ max: 2
474
475
  }
475
476
  );
476
477
 
@@ -4,13 +4,17 @@ import { findPattern } from "./findPattern";
4
4
  import { ParseResult } from "./ParseResult";
5
5
  import { Pattern } from "./Pattern";
6
6
 
7
+ let idIndex = 0;
8
+
7
9
  export interface FiniteRepeatOptions {
8
10
  divider?: Pattern;
9
11
  min?: number;
12
+ max?: number;
10
13
  trimDivider?: boolean;
11
14
  }
12
15
 
13
16
  export class FiniteRepeat implements Pattern {
17
+ private _id: string;
14
18
  private _type: string;
15
19
  private _name: string;
16
20
  private _parent: Pattern | null;
@@ -20,6 +24,10 @@ export class FiniteRepeat implements Pattern {
20
24
  private _max: number;
21
25
  private _trimDivider: boolean;
22
26
 
27
+ get id() {
28
+ return this._id;
29
+ }
30
+
23
31
  get type() {
24
32
  return this._type;
25
33
  }
@@ -40,10 +48,6 @@ export class FiniteRepeat implements Pattern {
40
48
  return this._children;
41
49
  }
42
50
 
43
- get isOptional() {
44
- return this._min === 0;
45
- }
46
-
47
51
  get min() {
48
52
  return this._min;
49
53
  }
@@ -52,46 +56,56 @@ export class FiniteRepeat implements Pattern {
52
56
  return this._max;
53
57
  }
54
58
 
55
- constructor(name: string, pattern: Pattern, repeatAmount: number, options: FiniteRepeatOptions = {}) {
59
+ constructor(name: string, pattern: Pattern, options: FiniteRepeatOptions = {}) {
60
+ this._id = `finite-repeat-${idIndex++}`;
56
61
  this._type = "finite-repeat";
57
62
  this._name = name;
58
63
  this._parent = null;
59
64
  this._children = [];
60
65
  this._hasDivider = options.divider != null;
61
- this._min = options.min != null ? options.min : 1;
62
- this._max = repeatAmount;
66
+ this._min = options.min != null ? Math.max(options.min, 1) : 1;
67
+ this._max = Math.max(this.min, options.max || this.min);
63
68
  this._trimDivider = options.trimDivider == null ? false : options.trimDivider;
64
69
 
65
- for (let i = 0; i < repeatAmount; i++) {
66
- this._children.push(pattern.clone(pattern.name));
70
+ for (let i = 0; i < this._max; i++) {
71
+ const child = pattern.clone();
72
+ child.parent = this;
73
+
74
+ this._children.push(child);
67
75
 
68
- if (options.divider != null && (i < repeatAmount - 1 || !this._trimDivider)) {
69
- this._children.push(options.divider.clone(options.divider.name, false));
76
+ if (options.divider != null && (i < this._max - 1 || !this._trimDivider)) {
77
+ const divider = options.divider.clone();
78
+ divider.parent = this;
79
+ this._children.push(divider);
70
80
  }
71
81
  }
82
+
72
83
  }
73
84
 
74
85
  parse(cursor: Cursor): Node | null {
86
+ cursor.startParseWith(this);
87
+
75
88
  const startIndex = cursor.index;
76
89
  const nodes: Node[] = [];
77
90
  const modulo = this._hasDivider ? 2 : 1;
78
91
  let matchCount = 0;
79
92
 
80
-
81
93
  for (let i = 0; i < this._children.length; i++) {
82
94
  const childPattern = this._children[i];
95
+ const runningIndex = cursor.index;
83
96
  const node = childPattern.parse(cursor);
84
97
 
85
- if (i % modulo === 0 && !cursor.hasError) {
86
- matchCount++
87
- }
88
-
89
98
  if (cursor.hasError) {
90
- cursor.resolveError();
91
99
  break;
92
100
  }
93
101
 
94
- if (node != null) {
102
+ if (i % modulo === 0 && !cursor.hasError) {
103
+ matchCount++;
104
+ }
105
+
106
+ if (node == null) {
107
+ cursor.moveTo(runningIndex);
108
+ } else {
95
109
  nodes.push(node);
96
110
 
97
111
  if (cursor.hasNext()) {
@@ -100,13 +114,13 @@ export class FiniteRepeat implements Pattern {
100
114
  break;
101
115
  }
102
116
  }
103
-
104
117
  }
105
118
 
106
119
  if (this._trimDivider && this._hasDivider) {
107
- if (cursor.leafMatch.pattern === this.children[1]) {
120
+ const isDividerLastMatch = cursor.leafMatch.pattern === this.children[1];
121
+ if (isDividerLastMatch) {
108
122
  const node = nodes.pop() as Node;
109
- cursor.moveTo(node.firstIndex)
123
+ cursor.moveTo(node.firstIndex);
110
124
  }
111
125
  }
112
126
 
@@ -114,17 +128,22 @@ export class FiniteRepeat implements Pattern {
114
128
  const lastIndex = cursor.index;
115
129
  cursor.moveTo(startIndex);
116
130
  cursor.recordErrorAt(startIndex, lastIndex, this);
131
+ cursor.endParse();
117
132
  return null;
118
- } else if (nodes.length === 0) {
119
- cursor.resolveError();
120
- cursor.moveTo(startIndex)
133
+ }
134
+
135
+ if (nodes.length === 0 && !cursor.hasError) {
136
+ cursor.moveTo(startIndex);
137
+ cursor.endParse();
121
138
  return null;
122
139
  }
123
140
 
124
141
  const firstIndex = nodes[0].firstIndex;
125
142
  const lastIndex = nodes[nodes.length - 1].lastIndex;
126
143
 
144
+ cursor.resolveError();
127
145
  cursor.moveTo(lastIndex);
146
+ cursor.endParse();
128
147
 
129
148
  return new Node(this._type, this.name, firstIndex, lastIndex, nodes);
130
149
  }
@@ -136,8 +155,10 @@ export class FiniteRepeat implements Pattern {
136
155
  return ast?.value === text;
137
156
  }
138
157
 
139
- exec(text: string): ParseResult {
158
+ exec(text: string, record = false): ParseResult {
140
159
  const cursor = new Cursor(text);
160
+ record && cursor.startRecording();
161
+
141
162
  const ast = this.parse(cursor);
142
163
 
143
164
  return {
@@ -146,27 +167,24 @@ export class FiniteRepeat implements Pattern {
146
167
  };
147
168
  }
148
169
 
149
- clone(name = this._name, isOptional?: boolean): Pattern {
170
+ clone(name = this._name): Pattern {
150
171
  let min = this._min;
172
+ let max = this._max;
151
173
 
152
- if (isOptional != null) {
153
- if (isOptional) {
154
- min = 0
155
- } else {
156
- min = Math.max(this._min, 1);
157
- }
158
- }
159
-
160
- return new FiniteRepeat(
174
+ const clone = new FiniteRepeat(
161
175
  name,
162
176
  this._children[0],
163
- this._max,
164
177
  {
165
178
  divider: this._hasDivider ? this._children[1] : undefined,
166
179
  min,
180
+ max,
167
181
  trimDivider: this._trimDivider
168
182
  }
169
183
  );
184
+
185
+ clone._id = this._id;
186
+
187
+ return clone;
170
188
  }
171
189
 
172
190
  getTokens(): string[] {
@@ -184,7 +202,7 @@ export class FiniteRepeat implements Pattern {
184
202
 
185
203
  getNextTokens(): string[] {
186
204
  if (this._parent == null) {
187
- return []
205
+ return [];
188
206
  }
189
207
 
190
208
  return this._parent.getTokensAfter(this);
@@ -214,7 +232,7 @@ export class FiniteRepeat implements Pattern {
214
232
  // Get the next childs patterns.
215
233
  const nextChild = this._children[childIndex + 1];
216
234
 
217
- return nextChild.getPatterns()
235
+ return nextChild.getPatterns();
218
236
  }
219
237
 
220
238
  getNextPatterns(): Pattern[] {
@@ -229,4 +247,8 @@ export class FiniteRepeat implements Pattern {
229
247
  return findPattern(this, predicate);
230
248
  }
231
249
 
250
+ isEqual(pattern: FiniteRepeat): boolean {
251
+ return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index]));
252
+ }
253
+
232
254
  }