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.
- package/dist/index.browser.js +54 -6
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +54 -6
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +54 -6
- package/dist/index.js.map +1 -1
- package/dist/patterns/ExpressionPattern.d.ts +2 -0
- package/dist/patterns/Repeat.d.ts +3 -1
- package/package.json +1 -1
- package/src/ast/Node.ts +1 -2
- package/src/grammar/Grammar.test.ts +53 -3
- package/src/grammar/Grammar.ts +20 -0
- package/src/grammar/patterns/statement.ts +7 -2
- package/src/patterns/ExpressionPattern.test.ts +38 -0
- package/src/patterns/ExpressionPattern.ts +35 -2
- package/src/patterns/Repeat.ts +10 -2
|
@@ -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
|
-
|
|
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
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
|
-
|
|
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 {
|
|
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
|
});
|
package/src/grammar/Grammar.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
package/src/patterns/Repeat.ts
CHANGED
|
@@ -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
|
|
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
|
}
|