clarity-pattern-parser 10.2.13 → 10.3.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 (39) hide show
  1. package/dist/ast/Node.d.ts +1 -0
  2. package/dist/index.browser.js +100 -9
  3. package/dist/index.browser.js.map +1 -1
  4. package/dist/index.esm.js +100 -9
  5. package/dist/index.esm.js.map +1 -1
  6. package/dist/index.js +100 -9
  7. package/dist/index.js.map +1 -1
  8. package/dist/patterns/Context.d.ts +1 -0
  9. package/dist/patterns/ExpressionPattern.d.ts +3 -0
  10. package/dist/patterns/FiniteRepeat.d.ts +1 -0
  11. package/dist/patterns/InfiniteRepeat.d.ts +1 -0
  12. package/dist/patterns/Literal.d.ts +1 -0
  13. package/dist/patterns/Not.d.ts +1 -0
  14. package/dist/patterns/Optional.d.ts +1 -0
  15. package/dist/patterns/Options.d.ts +1 -0
  16. package/dist/patterns/Pattern.d.ts +1 -0
  17. package/dist/patterns/Reference.d.ts +1 -0
  18. package/dist/patterns/Regex.d.ts +1 -0
  19. package/dist/patterns/Repeat.d.ts +3 -0
  20. package/dist/patterns/Sequence.d.ts +1 -0
  21. package/package.json +1 -1
  22. package/src/ast/Node.ts +6 -2
  23. package/src/grammar/Grammar.test.ts +53 -3
  24. package/src/grammar/Grammar.ts +20 -0
  25. package/src/grammar/patterns/statement.ts +7 -2
  26. package/src/patterns/Context.ts +3 -0
  27. package/src/patterns/ExpressionPattern.ts +35 -0
  28. package/src/patterns/FiniteRepeat.ts +10 -1
  29. package/src/patterns/InfiniteRepeat.ts +7 -0
  30. package/src/patterns/Literal.ts +3 -0
  31. package/src/patterns/Not.ts +2 -0
  32. package/src/patterns/Optional.ts +10 -3
  33. package/src/patterns/Options.ts +11 -3
  34. package/src/patterns/Pattern.ts +1 -0
  35. package/src/patterns/Reference.ts +3 -0
  36. package/src/patterns/Regex.ts +5 -2
  37. package/src/patterns/Repeat.ts +12 -0
  38. package/src/patterns/RightAssociatedPattern.ts +2 -0
  39. package/src/patterns/Sequence.ts +7 -0
@@ -10,6 +10,7 @@ export declare class Context implements Pattern {
10
10
  private _children;
11
11
  private _pattern;
12
12
  private _patterns;
13
+ shouldCompactAst: boolean;
13
14
  get id(): string;
14
15
  get type(): string;
15
16
  get name(): string;
@@ -18,6 +18,8 @@ export declare class ExpressionPattern implements Pattern {
18
18
  private _binaryAssociation;
19
19
  private _precedenceMap;
20
20
  private _binaryNames;
21
+ private _shouldCompactPatternsMap;
22
+ shouldCompactAst: boolean;
21
23
  get id(): string;
22
24
  get type(): string;
23
25
  get name(): string;
@@ -38,6 +40,7 @@ export declare class ExpressionPattern implements Pattern {
38
40
  private _extractRecursiveTail;
39
41
  private _endsWithRecursion;
40
42
  parse(cursor: Cursor): Node | null;
43
+ private _compactResult;
41
44
  private _tryToParse;
42
45
  test(text: string): boolean;
43
46
  exec(text: string, record?: boolean): ParseResult;
@@ -18,6 +18,7 @@ export declare class FiniteRepeat implements Pattern {
18
18
  private _min;
19
19
  private _max;
20
20
  private _trimDivider;
21
+ shouldCompactAst: boolean;
21
22
  get id(): string;
22
23
  get type(): string;
23
24
  get name(): string;
@@ -19,6 +19,7 @@ export declare class InfiniteRepeat implements Pattern {
19
19
  private _firstIndex;
20
20
  private _min;
21
21
  private _trimDivider;
22
+ shouldCompactAst: boolean;
22
23
  get id(): string;
23
24
  get type(): string;
24
25
  get name(): string;
@@ -12,6 +12,7 @@ export declare class Literal implements Pattern {
12
12
  private _firstIndex;
13
13
  private _lastIndex;
14
14
  private _endIndex;
15
+ shouldCompactAst: boolean;
15
16
  get id(): string;
16
17
  get type(): string;
17
18
  get name(): string;
@@ -8,6 +8,7 @@ export declare class Not implements Pattern {
8
8
  private _name;
9
9
  private _parent;
10
10
  private _children;
11
+ shouldCompactAst: boolean;
11
12
  get id(): string;
12
13
  get type(): string;
13
14
  get name(): string;
@@ -8,6 +8,7 @@ export declare class Optional implements Pattern {
8
8
  private _name;
9
9
  private _parent;
10
10
  private _children;
11
+ shouldCompactAst: boolean;
11
12
  get id(): string;
12
13
  get type(): string;
13
14
  get name(): string;
@@ -10,6 +10,7 @@ export declare class Options implements Pattern {
10
10
  private _children;
11
11
  private _isGreedy;
12
12
  private _firstIndex;
13
+ shouldCompactAst: boolean;
13
14
  get id(): string;
14
15
  get type(): string;
15
16
  get name(): string;
@@ -5,6 +5,7 @@ export interface Pattern {
5
5
  id: string;
6
6
  type: string;
7
7
  name: string;
8
+ shouldCompactAst: boolean;
8
9
  parent: Pattern | null;
9
10
  children: Pattern[];
10
11
  parse(cursor: Cursor): Node | null;
@@ -10,6 +10,7 @@ export declare class Reference implements Pattern {
10
10
  private _cachedPattern;
11
11
  private _pattern;
12
12
  private _children;
13
+ shouldCompactAst: boolean;
13
14
  get id(): string;
14
15
  get type(): string;
15
16
  get name(): string;
@@ -14,6 +14,7 @@ export declare class Regex implements Pattern {
14
14
  private _firstIndex;
15
15
  private _substring;
16
16
  private _tokens;
17
+ shouldCompactAst: boolean;
17
18
  get id(): string;
18
19
  get type(): string;
19
20
  get name(): string;
@@ -15,6 +15,9 @@ export declare class Repeat implements Pattern {
15
15
  private _pattern;
16
16
  private _options;
17
17
  private _children;
18
+ private _shouldCompactAst;
19
+ get shouldCompactAst(): boolean;
20
+ set shouldCompactAst(value: boolean);
18
21
  get id(): string;
19
22
  get type(): string;
20
23
  get name(): string;
@@ -9,6 +9,7 @@ export declare class Sequence implements Pattern {
9
9
  private _children;
10
10
  private _nodes;
11
11
  private _firstIndex;
12
+ shouldCompactAst: boolean;
12
13
  get id(): string;
13
14
  get type(): string;
14
15
  get name(): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-pattern-parser",
3
- "version": "10.2.13",
3
+ "version": "10.3.0",
4
4
  "description": "Parsing Library for Typescript and Javascript.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",
package/src/ast/Node.ts CHANGED
@@ -236,8 +236,7 @@ export class Node {
236
236
  const queue: Node[] = [this];
237
237
 
238
238
  while (queue.length > 0) {
239
- // biome-ignore lint/style/noNonNullAssertion: This will never be undefined.
240
- const current = queue.shift()!;
239
+ const current = queue.shift() as Node;
241
240
  callback(current);
242
241
  queue.push(...current.children);
243
242
  }
@@ -309,6 +308,11 @@ export class Node {
309
308
  return length;
310
309
  }
311
310
 
311
+ compact(){
312
+ this._value = this.toString();
313
+ this._children.length = 0;
314
+ }
315
+
312
316
  toString(): string {
313
317
  if (this._children.length === 0) {
314
318
  return this._value;
@@ -571,8 +571,8 @@ describe("Grammar", () => {
571
571
  expect(result?.ast?.value).toBe("John Doe");
572
572
  });
573
573
 
574
- test("Expression Pattern", ()=>{
575
- const {variables, expression} = patterns`
574
+ test("Expression Pattern", () => {
575
+ const { expression } = patterns`
576
576
  variables = "a" | "b" | "c"
577
577
  ternary = expression + " ? " + expression + " : " + expression
578
578
  expression = ternary | variables
@@ -581,7 +581,57 @@ describe("Grammar", () => {
581
581
  bad-expression = bad-ternary | bad-ternary
582
582
  `;
583
583
  let result = expression.exec("a ? b : c");
584
- debugger;
585
584
  expect(result).toBe(result);
586
585
  });
586
+
587
+ test("Compacted Recursive Sequence", () => {
588
+ const { expression } = patterns`
589
+ variables = "a" | "b" | "c"
590
+ ternary = expression + " ? " + expression + " : " + expression compact
591
+ expression = ternary | variables
592
+ `;
593
+ const result = expression.exec("a ? b : c");
594
+ expect(result.ast?.children.length).toBe(0);
595
+ expect(result.ast?.value).toBe("a ? b : c");
596
+ });
597
+
598
+ test("Compacted Repeat", () => {
599
+ const { variables } = patterns`
600
+ variable = "a" | "b" | "c"
601
+ variables = (variable, /\s*,\s*/)+ compact
602
+ `;
603
+ const result = variables.exec("a,b,c");
604
+ expect(result.ast?.children.length).toBe(0);
605
+ expect(result.ast?.value).toBe("a,b,c");
606
+ });
607
+
608
+ test("Compacted Binary Expression", () => {
609
+ const { expression } = patterns`
610
+ variable = "a" | "b" | "c"
611
+ mult-div-operator = " * " | " / "
612
+ add-sub-operator = " + " | " - "
613
+
614
+ mult-div-expression = expression + mult-div-operator + expression compact
615
+ add-sub-expression = expression + add-sub-operator + expression compact
616
+ expression = mult-div-expression | add-sub-expression | variable
617
+ `;
618
+ const result = expression.exec("a * b + c");
619
+ expect(result.ast?.children.length).toBe(0);
620
+ expect(result.ast?.value).toBe("a * b + c");
621
+ });
622
+
623
+ test("Compacted Expression", () => {
624
+ const { expression } = patterns`
625
+ variable = "a" | "b" | "c"
626
+ mult-div-operator = " * " | " / "
627
+ add-sub-operator = " + " | " - "
628
+
629
+ mult-div-expression = expression + mult-div-operator + expression
630
+ add-sub-expression = expression + add-sub-operator + expression
631
+ expression = mult-div-expression | add-sub-expression | variable compact
632
+ `;
633
+ const result = expression.exec("a * b + c");
634
+ expect(result.ast?.children.length).toBe(0);
635
+ expect(result.ast?.value).toBe("a * b + c");
636
+ });
587
637
  });
@@ -238,10 +238,15 @@ export class Grammar {
238
238
 
239
239
  private _saveOptions(statementNode: Node) {
240
240
  const nameNode = statementNode.find(n => n.name === "name") as Node;
241
+ const shouldCompactAst = statementNode.find(n=>n.name === "compact");
241
242
  const name = nameNode.value;
242
243
  const optionsNode = statementNode.find(n => n.name === "options-literal") as Node;
243
244
  const options = this._buildOptions(name, optionsNode);
244
245
 
246
+ if (shouldCompactAst != null){
247
+ options.shouldCompactAst = true;
248
+ }
249
+
245
250
  this._parseContext.patternsByName.set(name, options);
246
251
  }
247
252
 
@@ -310,10 +315,15 @@ export class Grammar {
310
315
 
311
316
  private _saveSequence(statementNode: Node) {
312
317
  const nameNode = statementNode.find(n => n.name === "name") as Node;
318
+ const shouldCompactAst = statementNode.find(n=>n.name === "compact");
313
319
  const name = nameNode.value;
314
320
  const sequenceNode = statementNode.find(n => n.name === "sequence-literal") as Node;
315
321
  const sequence = this._buildSequence(name, sequenceNode);
316
322
 
323
+ if (shouldCompactAst != null){
324
+ sequence.shouldCompactAst = true;
325
+ }
326
+
317
327
  this._parseContext.patternsByName.set(name, sequence);
318
328
  }
319
329
 
@@ -339,10 +349,15 @@ export class Grammar {
339
349
 
340
350
  private _saveRepeat(statementNode: Node) {
341
351
  const nameNode = statementNode.find(n => n.name === "name") as Node;
352
+ const shouldCompactAst = statementNode.find(n=>n.name === "compact");
342
353
  const name = nameNode.value;
343
354
  const repeatNode = statementNode.find(n => n.name === "repeat-literal") as Node;
344
355
  const repeat = this._buildRepeat(name, repeatNode);
345
356
 
357
+ if (shouldCompactAst != null){
358
+ repeat.shouldCompactAst = true;
359
+ }
360
+
346
361
  this._parseContext.patternsByName.set(name, repeat);
347
362
  }
348
363
 
@@ -527,11 +542,16 @@ export class Grammar {
527
542
 
528
543
  private _saveAlias(statementNode: Node) {
529
544
  const nameNode = statementNode.find(n => n.name === "name") as Node;
545
+ const shouldCompactAst = statementNode.find(n=>n.name === "compact");
530
546
  const aliasNode = statementNode.find(n => n.name === "alias-literal") as Node;
531
547
  const aliasName = aliasNode.value;
532
548
  const name = nameNode.value;
533
549
  const alias = this._getPattern(aliasName).clone(name);
534
550
 
551
+ if (shouldCompactAst != null){
552
+ alias.shouldCompactAst = true;
553
+ }
554
+
535
555
  this._parseContext.patternsByName.set(name, alias);
536
556
  }
537
557
 
@@ -2,20 +2,25 @@ import { Sequence } from "../../patterns/Sequence";
2
2
  import { Literal } from "../../patterns/Literal";
3
3
  import { Options } from "../../patterns/Options";
4
4
  import { name } from "./name";
5
- import { spaces } from "./spaces";
5
+ import { lineSpaces, spaces } from "./spaces";
6
6
  import { pattern } from "./pattern";
7
7
  import { Optional } from "../../patterns/Optional";
8
8
 
9
9
  const optionalSpaces = new Optional("optional-spaces", spaces);
10
10
  const assignOperator = new Literal("assign-operator", "=");
11
11
 
12
+ const compact = new Literal("compact", "compact");
13
+ const compactModifier = new Sequence("compact-modifier", [lineSpaces, compact]);
14
+ const optionalCompactModifier = new Optional("optional-compact-modifier", compactModifier);
15
+
12
16
  const assignStatement = new Sequence("assign-statement", [
13
17
  optionalSpaces,
14
18
  name,
15
19
  optionalSpaces,
16
20
  assignOperator,
17
21
  optionalSpaces,
18
- pattern
22
+ pattern,
23
+ optionalCompactModifier
19
24
  ]);
20
25
 
21
26
  export const statement = new Options("statement", [assignStatement, name.clone("export-name")]);
@@ -14,6 +14,8 @@ export class Context implements Pattern {
14
14
  private _pattern: Pattern;
15
15
  private _patterns: Record<string, Pattern>;
16
16
 
17
+ shouldCompactAst = false;
18
+
17
19
  get id(): string {
18
20
  return this._id;
19
21
  }
@@ -76,6 +78,7 @@ export class Context implements Pattern {
76
78
  clone(name = this._name): Pattern {
77
79
  const clone = new Context(name, this._pattern, Object.values(this._patterns));
78
80
  clone._id = this._id;
81
+ clone.shouldCompactAst = this.shouldCompactAst;
79
82
  return clone;
80
83
  }
81
84
 
@@ -34,6 +34,9 @@ export class ExpressionPattern implements Pattern {
34
34
  private _binaryAssociation: Association[];
35
35
  private _precedenceMap: Record<string, number>;
36
36
  private _binaryNames: string[];
37
+ private _shouldCompactPatternsMap: Record<string, boolean>;
38
+
39
+ shouldCompactAst = false;
37
40
 
38
41
  get id(): string {
39
42
  return this._id;
@@ -90,6 +93,7 @@ export class ExpressionPattern implements Pattern {
90
93
  this._binaryAssociation = [];
91
94
  this._precedenceMap = {};
92
95
  this._originalPatterns = patterns;
96
+ this._shouldCompactPatternsMap = {};
93
97
  this._patterns = this._organizePatterns(patterns);
94
98
 
95
99
  if (this._unaryPatterns.length === 0) {
@@ -100,6 +104,8 @@ export class ExpressionPattern implements Pattern {
100
104
  private _organizePatterns(patterns: Pattern[]) {
101
105
  const finalPatterns: Pattern[] = [];
102
106
  patterns.forEach((pattern) => {
107
+ this._shouldCompactPatternsMap[pattern.name] = pattern.shouldCompactAst;
108
+
103
109
  if (this._isBinary(pattern)) {
104
110
  const binaryName = this._extractName(pattern);
105
111
  const clone = this._extractDelimiter(pattern).clone();
@@ -119,6 +125,7 @@ export class ExpressionPattern implements Pattern {
119
125
  } else if (this._isRecursive(pattern)) {
120
126
  const name = this._extractName(pattern);
121
127
  const tail = this._extractRecursiveTail(pattern);
128
+
122
129
  tail.parent = this;
123
130
 
124
131
  this._recursivePatterns.push(tail);
@@ -216,6 +223,7 @@ export class ExpressionPattern implements Pattern {
216
223
  if (node != null) {
217
224
  cursor.moveTo(node.lastIndex);
218
225
  cursor.resolveError();
226
+ this._compactResult(node);
219
227
  return node;
220
228
  }
221
229
 
@@ -223,6 +231,28 @@ export class ExpressionPattern implements Pattern {
223
231
  return null;
224
232
  }
225
233
 
234
+ private _compactResult(node: Node | null) {
235
+ if (node == null) {
236
+ return;
237
+ }
238
+
239
+ if (this.shouldCompactAst) {
240
+ node.compact();
241
+ return;
242
+ }
243
+
244
+ // This could be really expensive with large trees. So we optimize with these checks,
245
+ // as well as use breadth first as to not recompact nodes over and over again.
246
+ const isCompactingNeeded = Object.values(this._shouldCompactPatternsMap).some(p => p);
247
+ if (isCompactingNeeded) {
248
+ node.walkBreadthFirst(n => {
249
+ if (this._shouldCompactPatternsMap[n.name]) {
250
+ n.compact();
251
+ }
252
+ });
253
+ }
254
+ }
255
+
226
256
  private _tryToParse(cursor: Cursor): Node | null {
227
257
  if (depthCache.getDepth(this._id, this._firstIndex) > 2) {
228
258
  cursor.recordErrorAt(this._firstIndex, this._firstIndex, this);
@@ -246,6 +276,7 @@ export class ExpressionPattern implements Pattern {
246
276
 
247
277
  if (node != null) {
248
278
  lastUnaryNode = node;
279
+
249
280
  break;
250
281
  } else {
251
282
  lastUnaryNode = null;
@@ -338,12 +369,14 @@ export class ExpressionPattern implements Pattern {
338
369
  if (precedence === lastPrecendece && association === Association.right) {
339
370
  const node = createNode(name, [lastUnaryNode, delimiterNode]);
340
371
  lastBinaryNode.appendChild(node);
372
+
341
373
  lastBinaryNode = node;
342
374
  } else if (precedence === lastPrecendece) {
343
375
  const node = createNode(name, []);
344
376
 
345
377
  lastBinaryNode.replaceWith(node);
346
378
  lastBinaryNode.appendChild(lastUnaryNode);
379
+
347
380
  node.append(lastBinaryNode, delimiterNode);
348
381
  lastBinaryNode = node;
349
382
  } else if (precedence > lastPrecendece) {
@@ -376,6 +409,7 @@ export class ExpressionPattern implements Pattern {
376
409
  } else {
377
410
  const node = createNode(name, [lastUnaryNode, delimiterNode]);
378
411
  lastBinaryNode.appendChild(node);
412
+
379
413
  lastBinaryNode = node;
380
414
  }
381
415
 
@@ -514,6 +548,7 @@ export class ExpressionPattern implements Pattern {
514
548
  clone(name = this._name): Pattern {
515
549
  const clone = new ExpressionPattern(name, this._originalPatterns);
516
550
  clone._id = this._id;
551
+ clone.shouldCompactAst = this.shouldCompactAst;
517
552
  return clone;
518
553
  }
519
554
 
@@ -24,6 +24,8 @@ export class FiniteRepeat implements Pattern {
24
24
  private _max: number;
25
25
  private _trimDivider: boolean;
26
26
 
27
+ shouldCompactAst = false;
28
+
27
29
  get id() {
28
30
  return this._id;
29
31
  }
@@ -140,7 +142,13 @@ export class FiniteRepeat implements Pattern {
140
142
  cursor.resolveError();
141
143
  cursor.moveTo(lastIndex);
142
144
 
143
- return new Node(this._type, this.name, firstIndex, lastIndex, nodes);
145
+ const node = new Node(this._type, this.name, firstIndex, lastIndex, nodes);
146
+
147
+ if (this.shouldCompactAst) {
148
+ node.compact();
149
+ }
150
+
151
+ return node;
144
152
  }
145
153
 
146
154
  test(text: string): boolean {
@@ -178,6 +186,7 @@ export class FiniteRepeat implements Pattern {
178
186
  );
179
187
 
180
188
  clone._id = this._id;
189
+ clone.shouldCompactAst = this.shouldCompactAst;
181
190
 
182
191
  return clone;
183
192
  }
@@ -26,6 +26,8 @@ export class InfiniteRepeat implements Pattern {
26
26
  private _min: number;
27
27
  private _trimDivider: boolean;
28
28
 
29
+ shouldCompactAst = false;
30
+
29
31
  get id(): string {
30
32
  return this._id;
31
33
  }
@@ -118,6 +120,10 @@ export class InfiniteRepeat implements Pattern {
118
120
  if (node != null) {
119
121
  cursor.moveTo(node.lastIndex);
120
122
  cursor.recordMatch(this, node);
123
+
124
+ if (this.shouldCompactAst) {
125
+ node.compact();
126
+ }
121
127
  }
122
128
 
123
129
  return node;
@@ -352,6 +358,7 @@ export class InfiniteRepeat implements Pattern {
352
358
  );
353
359
 
354
360
  clone._id = this._id;
361
+ clone.shouldCompactAst = this.shouldCompactAst;
355
362
 
356
363
  return clone;
357
364
  }
@@ -16,6 +16,8 @@ export class Literal implements Pattern {
16
16
  private _lastIndex: number;
17
17
  private _endIndex: number;
18
18
 
19
+ shouldCompactAst = false;
20
+
19
21
  get id(): string {
20
22
  return this._id;
21
23
  }
@@ -141,6 +143,7 @@ export class Literal implements Pattern {
141
143
  clone(name = this._name): Pattern {
142
144
  const clone = new Literal(name, this._token);
143
145
  clone._id = this._id;
146
+ clone.shouldCompactAst = this.shouldCompactAst;
144
147
  return clone;
145
148
  }
146
149
 
@@ -12,6 +12,8 @@ export class Not implements Pattern {
12
12
  private _parent: Pattern | null;
13
13
  private _children: Pattern[];
14
14
 
15
+ shouldCompactAst = false;
16
+
15
17
  get id(): string {
16
18
  return this._id;
17
19
  }
@@ -12,6 +12,8 @@ export class Optional implements Pattern {
12
12
  private _parent: Pattern | null;
13
13
  private _children: Pattern[];
14
14
 
15
+ shouldCompactAst = false;
16
+
15
17
  get id(): string {
16
18
  return this._id;
17
19
  }
@@ -74,15 +76,20 @@ export class Optional implements Pattern {
74
76
 
75
77
  return null;
76
78
  } else {
79
+ if (node != null && this.shouldCompactAst) {
80
+ node.compact();
81
+ }
82
+
77
83
  return node;
78
84
  }
79
85
 
80
86
  }
81
87
 
82
88
  clone(name = this._name): Pattern {
83
- const optional = new Optional(name, this._children[0]);
84
- optional._id = this._id;
85
- return optional;
89
+ const clone = new Optional(name, this._children[0]);
90
+ clone._id = this._id;
91
+ clone.shouldCompactAst = this.shouldCompactAst;
92
+ return clone;
86
93
  }
87
94
 
88
95
  getTokens(): string[] {
@@ -23,6 +23,8 @@ export class Options implements Pattern {
23
23
  private _isGreedy: boolean;
24
24
  private _firstIndex: number;
25
25
 
26
+ shouldCompactAst = false;
27
+
26
28
  get id(): string {
27
29
  return this._id;
28
30
  }
@@ -102,6 +104,11 @@ export class Options implements Pattern {
102
104
  if (node != null) {
103
105
  cursor.moveTo(node.lastIndex);
104
106
  cursor.resolveError();
107
+
108
+ if (this.shouldCompactAst) {
109
+ node.compact();
110
+ }
111
+
105
112
  return node;
106
113
  }
107
114
 
@@ -202,9 +209,10 @@ export class Options implements Pattern {
202
209
  }
203
210
 
204
211
  clone(name = this._name): Pattern {
205
- const or = new Options(name, this._children, this._isGreedy);
206
- or._id = this._id;
207
- return or;
212
+ const clone = new Options(name, this._children, this._isGreedy);
213
+ clone._id = this._id;
214
+ clone.shouldCompactAst = this.shouldCompactAst;
215
+ return clone;
208
216
  }
209
217
 
210
218
  isEqual(pattern: Options): boolean {
@@ -6,6 +6,7 @@ export interface Pattern {
6
6
  id: string;
7
7
  type: string;
8
8
  name: string;
9
+ shouldCompactAst: boolean;
9
10
  parent: Pattern | null;
10
11
  children: Pattern[];
11
12
 
@@ -16,6 +16,8 @@ export class Reference implements Pattern {
16
16
  private _pattern: Pattern | null;
17
17
  private _children: Pattern[];
18
18
 
19
+ shouldCompactAst = false;
20
+
19
21
  get id(): string {
20
22
  return this._id;
21
23
  }
@@ -196,6 +198,7 @@ export class Reference implements Pattern {
196
198
  clone(name = this._name): Pattern {
197
199
  const clone = new Reference(name);
198
200
  clone._id = this._id;
201
+ clone.shouldCompactAst = this.shouldCompactAst;
199
202
 
200
203
  // Optimize future clones, by caching the pattern we already found.
201
204
  if (this._pattern != null) {
@@ -18,6 +18,8 @@ export class Regex implements Pattern {
18
18
  private _substring = "";
19
19
  private _tokens: string[] = [];
20
20
 
21
+ shouldCompactAst = false;
22
+
21
23
  get id(): string {
22
24
  return this._id;
23
25
  }
@@ -30,7 +32,7 @@ export class Regex implements Pattern {
30
32
  return this._name;
31
33
  }
32
34
 
33
- get regex(): string{
35
+ get regex(): string {
34
36
  return this._originalRegexString;
35
37
  }
36
38
 
@@ -145,8 +147,9 @@ export class Regex implements Pattern {
145
147
  clone(name = this._name) {
146
148
  const clone = new Regex(name, this._originalRegexString);
147
149
  clone._tokens = this._tokens.slice();
148
-
149
150
  clone._id = this._id;
151
+ clone.shouldCompactAst = this.shouldCompactAst;
152
+
150
153
  return clone;
151
154
  }
152
155