clarity-pattern-parser 10.2.14 → 10.3.1

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.
@@ -18,6 +18,7 @@ export declare class ExpressionPattern implements Pattern {
18
18
  private _binaryAssociation;
19
19
  private _precedenceMap;
20
20
  private _binaryNames;
21
+ private _shouldCompactPatternsMap;
21
22
  shouldCompactAst: boolean;
22
23
  get id(): string;
23
24
  get type(): string;
@@ -39,6 +40,7 @@ export declare class ExpressionPattern implements Pattern {
39
40
  private _extractRecursiveTail;
40
41
  private _endsWithRecursion;
41
42
  parse(cursor: Cursor): Node | null;
43
+ private _compactResult;
42
44
  private _tryToParse;
43
45
  test(text: string): boolean;
44
46
  exec(text: string, record?: boolean): ParseResult;
@@ -15,7 +15,9 @@ export declare class Repeat implements Pattern {
15
15
  private _pattern;
16
16
  private _options;
17
17
  private _children;
18
- shouldCompactAst: boolean;
18
+ private _shouldCompactAst;
19
+ get shouldCompactAst(): boolean;
20
+ set shouldCompactAst(value: boolean);
19
21
  get id(): string;
20
22
  get type(): string;
21
23
  get name(): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-pattern-parser",
3
- "version": "10.2.14",
3
+ "version": "10.3.1",
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
  }
@@ -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")]);
@@ -96,6 +96,34 @@ function createOptionsExpression() {
96
96
  return expressionPattern;
97
97
  }
98
98
 
99
+ function createTailExpression() {
100
+ const a = new Literal("a", "a");
101
+ const b = new Literal("b", "b");
102
+ const c = new Literal("c", "c");
103
+ const variable = new Options("variable", [a, b, c]);
104
+ const period = new Literal(".", ".");
105
+
106
+ const refinement = new Sequence("refinement", [period, variable]);
107
+ const refinementExpression = new Sequence("refinement-expression", [
108
+ new Reference("expression"),
109
+ refinement
110
+ ]);
111
+
112
+ const invocation = new Literal("invocation", "()");
113
+ const invocationExpression = new Sequence("invocation-expression", [
114
+ new Reference("expression"),
115
+ invocation
116
+ ]);
117
+
118
+ const expression = new ExpressionPattern("expression", [
119
+ refinementExpression,
120
+ invocationExpression,
121
+ variable
122
+ ]);
123
+
124
+ return expression;
125
+ }
126
+
99
127
  describe("Expression Pattern", () => {
100
128
  test("Single Expression", () => {
101
129
  const expression = createExpressionPattern();
@@ -113,6 +141,16 @@ describe("Expression Pattern", () => {
113
141
  expect(result).toBe(result);
114
142
  });
115
143
 
144
+ test("Tail", () => {
145
+ const expression = createTailExpression();
146
+ let result = expression.exec("a");
147
+ result = expression.exec("a.b");
148
+ result = expression.exec("a.b.c");
149
+ result = expression.exec("a.b.c()()()");
150
+
151
+ expect(result).toBe(result);
152
+ });
153
+
116
154
  test("Options like", () => {
117
155
  const expression = createOptionsExpression();
118
156
  const autoComplete = new AutoComplete(expression);
@@ -34,6 +34,7 @@ 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>;
37
38
 
38
39
  shouldCompactAst = false;
39
40
 
@@ -92,6 +93,7 @@ export class ExpressionPattern implements Pattern {
92
93
  this._binaryAssociation = [];
93
94
  this._precedenceMap = {};
94
95
  this._originalPatterns = patterns;
96
+ this._shouldCompactPatternsMap = {};
95
97
  this._patterns = this._organizePatterns(patterns);
96
98
 
97
99
  if (this._unaryPatterns.length === 0) {
@@ -102,6 +104,8 @@ export class ExpressionPattern implements Pattern {
102
104
  private _organizePatterns(patterns: Pattern[]) {
103
105
  const finalPatterns: Pattern[] = [];
104
106
  patterns.forEach((pattern) => {
107
+ this._shouldCompactPatternsMap[pattern.name] = pattern.shouldCompactAst;
108
+
105
109
  if (this._isBinary(pattern)) {
106
110
  const binaryName = this._extractName(pattern);
107
111
  const clone = this._extractDelimiter(pattern).clone();
@@ -121,6 +125,7 @@ export class ExpressionPattern implements Pattern {
121
125
  } else if (this._isRecursive(pattern)) {
122
126
  const name = this._extractName(pattern);
123
127
  const tail = this._extractRecursiveTail(pattern);
128
+
124
129
  tail.parent = this;
125
130
 
126
131
  this._recursivePatterns.push(tail);
@@ -206,7 +211,6 @@ export class ExpressionPattern implements Pattern {
206
211
  }
207
212
 
208
213
  parse(cursor: Cursor): Node | null {
209
- // This is a cache to help with speed
210
214
  this._firstIndex = cursor.index;
211
215
  depthCache.incrementDepth(this._id, this._firstIndex);
212
216
 
@@ -218,6 +222,7 @@ export class ExpressionPattern implements Pattern {
218
222
  if (node != null) {
219
223
  cursor.moveTo(node.lastIndex);
220
224
  cursor.resolveError();
225
+ this._compactResult(node);
221
226
  return node;
222
227
  }
223
228
 
@@ -225,6 +230,28 @@ export class ExpressionPattern implements Pattern {
225
230
  return null;
226
231
  }
227
232
 
233
+ private _compactResult(node: Node | null) {
234
+ if (node == null) {
235
+ return;
236
+ }
237
+
238
+ if (this.shouldCompactAst) {
239
+ node.compact();
240
+ return;
241
+ }
242
+
243
+ // This could be really expensive with large trees. So we optimize with these checks,
244
+ // as well as use breadth first as to not recompact nodes over and over again.
245
+ const isCompactingNeeded = Object.values(this._shouldCompactPatternsMap).some(p => p);
246
+ if (isCompactingNeeded) {
247
+ node.walkBreadthFirst(n => {
248
+ if (this._shouldCompactPatternsMap[n.name]) {
249
+ n.compact();
250
+ }
251
+ });
252
+ }
253
+ }
254
+
228
255
  private _tryToParse(cursor: Cursor): Node | null {
229
256
  if (depthCache.getDepth(this._id, this._firstIndex) > 2) {
230
257
  cursor.recordErrorAt(this._firstIndex, this._firstIndex, this);
@@ -248,6 +275,7 @@ export class ExpressionPattern implements Pattern {
248
275
 
249
276
  if (node != null) {
250
277
  lastUnaryNode = node;
278
+
251
279
  break;
252
280
  } else {
253
281
  lastUnaryNode = null;
@@ -301,7 +329,9 @@ export class ExpressionPattern implements Pattern {
301
329
  }
302
330
  break outer;
303
331
  }
304
- break;
332
+ onIndex = cursor.index;
333
+ i = -1;
334
+ continue;
305
335
  }
306
336
  }
307
337
 
@@ -340,12 +370,14 @@ export class ExpressionPattern implements Pattern {
340
370
  if (precedence === lastPrecendece && association === Association.right) {
341
371
  const node = createNode(name, [lastUnaryNode, delimiterNode]);
342
372
  lastBinaryNode.appendChild(node);
373
+
343
374
  lastBinaryNode = node;
344
375
  } else if (precedence === lastPrecendece) {
345
376
  const node = createNode(name, []);
346
377
 
347
378
  lastBinaryNode.replaceWith(node);
348
379
  lastBinaryNode.appendChild(lastUnaryNode);
380
+
349
381
  node.append(lastBinaryNode, delimiterNode);
350
382
  lastBinaryNode = node;
351
383
  } else if (precedence > lastPrecendece) {
@@ -378,6 +410,7 @@ export class ExpressionPattern implements Pattern {
378
410
  } else {
379
411
  const node = createNode(name, [lastUnaryNode, delimiterNode]);
380
412
  lastBinaryNode.appendChild(node);
413
+
381
414
  lastBinaryNode = node;
382
415
  }
383
416
 
@@ -27,8 +27,16 @@ export class Repeat implements Pattern {
27
27
  private _pattern: Pattern;
28
28
  private _options: InternalRepeatOptions;
29
29
  private _children: Pattern[];
30
+ private _shouldCompactAst: boolean;
30
31
 
31
- shouldCompactAst = false;
32
+ get shouldCompactAst() {
33
+ return this._shouldCompactAst;
34
+ }
35
+
36
+ set shouldCompactAst(value: boolean) {
37
+ this._shouldCompactAst = value;
38
+ this._repeatPattern.shouldCompactAst = value;
39
+ }
32
40
 
33
41
  get id() {
34
42
  return this._id;
@@ -66,6 +74,7 @@ export class Repeat implements Pattern {
66
74
  this._id = `repeat-${idIndex++}`;
67
75
  this._pattern = pattern;
68
76
  this._parent = null;
77
+ this._shouldCompactAst = false;
69
78
  this._options = {
70
79
  ...options,
71
80
  min: options.min == null ? 1 : options.min,
@@ -78,7 +87,6 @@ export class Repeat implements Pattern {
78
87
  this._repeatPattern = new InfiniteRepeat(name, pattern, this._options);
79
88
  }
80
89
 
81
- this._repeatPattern.shouldCompactAst = this.shouldCompactAst;
82
90
  this._children = [this._repeatPattern];
83
91
  this._repeatPattern.parent = this;
84
92
  }