clarity-pattern-parser 10.1.26 → 10.2.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/ast/Node.d.ts +1 -0
- package/dist/grammar/Grammar.d.ts +2 -0
- package/dist/index.browser.js +420 -10
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.esm.js +420 -11
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +420 -10
- package/dist/index.js.map +1 -1
- package/dist/patterns/ExpressionPattern.d.ts +51 -0
- package/package.json +1 -1
- package/src/ast/Node.ts +12 -1
- package/src/grammar/Grammar.test.ts +15 -0
- package/src/grammar/Grammar.ts +25 -1
- package/src/index.ts +2 -0
- package/src/patterns/ExpressionPattern.test.ts +132 -0
- package/src/patterns/ExpressionPattern.ts +298 -57
- package/src/patterns/Options.ts +0 -1
- package/src/patterns/RightAssociatedPattern.ts +29 -11
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { Cursor } from "./Cursor";
|
|
3
|
+
import { ParseResult } from "./ParseResult";
|
|
4
|
+
import { Pattern } from "./Pattern";
|
|
5
|
+
export declare class ExpressionPattern implements Pattern {
|
|
6
|
+
private _id;
|
|
7
|
+
private _type;
|
|
8
|
+
private _name;
|
|
9
|
+
private _parent;
|
|
10
|
+
private _firstIndex;
|
|
11
|
+
private _originalPatterns;
|
|
12
|
+
private _patterns;
|
|
13
|
+
private _unaryPatterns;
|
|
14
|
+
private _binaryPatterns;
|
|
15
|
+
private _recursivePatterns;
|
|
16
|
+
private _recursiveNames;
|
|
17
|
+
private _binaryAssociation;
|
|
18
|
+
private _precedenceMap;
|
|
19
|
+
private _binaryNames;
|
|
20
|
+
get id(): string;
|
|
21
|
+
get type(): string;
|
|
22
|
+
get name(): string;
|
|
23
|
+
get parent(): Pattern | null;
|
|
24
|
+
set parent(pattern: Pattern | null);
|
|
25
|
+
get children(): Pattern[];
|
|
26
|
+
get unaryPatterns(): readonly Pattern[];
|
|
27
|
+
get binaryPatterns(): readonly Pattern[];
|
|
28
|
+
get recursivePatterns(): readonly Pattern[];
|
|
29
|
+
constructor(name: string, patterns: Pattern[]);
|
|
30
|
+
private _organizePatterns;
|
|
31
|
+
private _isBinary;
|
|
32
|
+
private _isBinaryPattern;
|
|
33
|
+
private _extractDelimiter;
|
|
34
|
+
private _extractName;
|
|
35
|
+
private _isRecursive;
|
|
36
|
+
private _isRecursivePattern;
|
|
37
|
+
private _extractRecursiveTail;
|
|
38
|
+
parse(cursor: Cursor): Node | null;
|
|
39
|
+
private _tryToParse;
|
|
40
|
+
test(text: string): boolean;
|
|
41
|
+
exec(text: string, record?: boolean): ParseResult;
|
|
42
|
+
getTokens(): string[];
|
|
43
|
+
getTokensAfter(childReference: Pattern): string[];
|
|
44
|
+
getNextTokens(): string[];
|
|
45
|
+
getPatterns(): Pattern[];
|
|
46
|
+
getPatternsAfter(childReference: Pattern): Pattern[];
|
|
47
|
+
getNextPatterns(): Pattern[];
|
|
48
|
+
find(predicate: (p: Pattern) => boolean): Pattern | null;
|
|
49
|
+
clone(name?: string): Pattern;
|
|
50
|
+
isEqual(pattern: ExpressionPattern): boolean;
|
|
51
|
+
}
|
package/package.json
CHANGED
package/src/ast/Node.ts
CHANGED
|
@@ -183,6 +183,17 @@ export class Node {
|
|
|
183
183
|
return this.findAll(predicate)[0] || null;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
findRoot() {
|
|
187
|
+
let pattern: Node | null = this;
|
|
188
|
+
|
|
189
|
+
while (true) {
|
|
190
|
+
if (pattern.parent == null) {
|
|
191
|
+
return pattern;
|
|
192
|
+
}
|
|
193
|
+
pattern = pattern.parent;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
186
197
|
findAll(predicate: (node: Node) => boolean): Node[] {
|
|
187
198
|
const matches: Node[] = [];
|
|
188
199
|
|
|
@@ -284,7 +295,7 @@ export class Node {
|
|
|
284
295
|
if (this.children.length === 0) {
|
|
285
296
|
length = this._value.length;
|
|
286
297
|
} else {
|
|
287
|
-
length = this.children.reduce((acc, c) => acc + c.normalize(acc + startIndex), startIndex);
|
|
298
|
+
length = this.children.reduce((acc, c) => acc + c.normalize(acc + startIndex), startIndex) - startIndex;
|
|
288
299
|
}
|
|
289
300
|
|
|
290
301
|
this._firstIndex = startIndex;
|
|
@@ -9,6 +9,7 @@ import { Repeat } from "../patterns/Repeat";
|
|
|
9
9
|
import { Grammar } from "./Grammar";
|
|
10
10
|
import { Optional } from "../patterns/Optional";
|
|
11
11
|
import { Context } from "../patterns/Context";
|
|
12
|
+
import { patterns } from "./patterns";
|
|
12
13
|
|
|
13
14
|
describe("Grammar", () => {
|
|
14
15
|
test("Literal", () => {
|
|
@@ -569,4 +570,18 @@ describe("Grammar", () => {
|
|
|
569
570
|
const result = fullname.exec("John Doe");
|
|
570
571
|
expect(result?.ast?.value).toBe("John Doe");
|
|
571
572
|
});
|
|
573
|
+
|
|
574
|
+
test("Expression Pattern", ()=>{
|
|
575
|
+
const {variables, expression} = patterns`
|
|
576
|
+
variables = "a" | "b" | "c"
|
|
577
|
+
ternary = expression + " ? " + expression + " : " + expression
|
|
578
|
+
expression = ternary | variables
|
|
579
|
+
|
|
580
|
+
bad-ternary = bad-expression + " ? " + bad-expression + " : " + bad-expression
|
|
581
|
+
bad-expression = bad-ternary | bad-ternary
|
|
582
|
+
`;
|
|
583
|
+
let result = expression.exec("a ? b : c");
|
|
584
|
+
debugger;
|
|
585
|
+
expect(result).toBe(result);
|
|
586
|
+
});
|
|
572
587
|
});
|
package/src/grammar/Grammar.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { Repeat, RepeatOptions } from "../patterns/Repeat";
|
|
|
11
11
|
import { AutoComplete } from "../intellisense/AutoComplete";
|
|
12
12
|
import { Optional } from "../patterns/Optional";
|
|
13
13
|
import { Context } from "../patterns/Context";
|
|
14
|
+
import {ExpressionPattern} from "../patterns/ExpressionPattern";
|
|
14
15
|
|
|
15
16
|
let anonymousIndexId = 0;
|
|
16
17
|
|
|
@@ -248,11 +249,34 @@ export class Grammar {
|
|
|
248
249
|
const patternNodes = node.children.filter(n => n.name !== "default-divider" && n.name !== "greedy-divider");
|
|
249
250
|
const isGreedy = node.find(n => n.name === "greedy-divider") != null;
|
|
250
251
|
const patterns = patternNodes.map(n => this._buildPattern(n));
|
|
251
|
-
const
|
|
252
|
+
const hasRecursivePattern = patterns.some(p=>this._isRecursive(name, p));
|
|
253
|
+
|
|
254
|
+
if (hasRecursivePattern && !isGreedy){
|
|
255
|
+
try {
|
|
256
|
+
const expression = new ExpressionPattern(name, patterns);
|
|
257
|
+
return expression;
|
|
258
|
+
} catch{}
|
|
259
|
+
}
|
|
252
260
|
|
|
261
|
+
const or = new Options(name, patterns, isGreedy);
|
|
253
262
|
return or;
|
|
254
263
|
}
|
|
255
264
|
|
|
265
|
+
private _isRecursive(name: string, pattern: Pattern) {
|
|
266
|
+
if (pattern.type === "right-associated" && this._isRecursivePattern(name, pattern.children[0])) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return this._isRecursivePattern(name, pattern);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private _isRecursivePattern(name: string, pattern: Pattern) {
|
|
274
|
+
return pattern.type === "sequence" &&
|
|
275
|
+
pattern.children[0].type === "reference" &&
|
|
276
|
+
pattern.children[0].name === name &&
|
|
277
|
+
pattern.children.length > 2;
|
|
278
|
+
}
|
|
279
|
+
|
|
256
280
|
private _buildPattern(node: Node): Pattern {
|
|
257
281
|
const type = node.name;
|
|
258
282
|
const name = `anonymous-pattern-${anonymousIndexId++}`;
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { ParseResult } from "./patterns/ParseResult";
|
|
|
19
19
|
import { grammar } from "./grammar/patterns/grammar";
|
|
20
20
|
import { patterns } from "./grammar/patterns";
|
|
21
21
|
import { Context } from "./patterns/Context";
|
|
22
|
+
import { ExpressionPattern } from "./patterns/ExpressionPattern";
|
|
22
23
|
|
|
23
24
|
export {
|
|
24
25
|
Node,
|
|
@@ -32,6 +33,7 @@ export {
|
|
|
32
33
|
CursorHistory,
|
|
33
34
|
Match,
|
|
34
35
|
Context,
|
|
36
|
+
ExpressionPattern,
|
|
35
37
|
Literal,
|
|
36
38
|
Not,
|
|
37
39
|
Options,
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Options } from "./Options";
|
|
2
|
+
import { Literal } from './Literal';
|
|
3
|
+
import { ExpressionPattern } from './ExpressionPattern';
|
|
4
|
+
import { Reference } from "./Reference";
|
|
5
|
+
import { Sequence } from './Sequence';
|
|
6
|
+
import { Regex } from './Regex';
|
|
7
|
+
import { Optional } from "./Optional";
|
|
8
|
+
import { AutoComplete } from "../intellisense/AutoComplete";
|
|
9
|
+
import { RightAssociatedPattern } from "./RightAssociatedPattern";
|
|
10
|
+
|
|
11
|
+
function createExpressionPattern() {
|
|
12
|
+
const spaces = new Regex("spaces", "\\s+");
|
|
13
|
+
spaces.setTokens([" "]);
|
|
14
|
+
|
|
15
|
+
const optionalSpaces = new Optional("optional-spaces", spaces);
|
|
16
|
+
|
|
17
|
+
const variables = new Options("variables", [
|
|
18
|
+
new Literal("a", "a"),
|
|
19
|
+
new Literal("b", "b"),
|
|
20
|
+
new Literal("c", "c"),
|
|
21
|
+
new Literal("d", "d"),
|
|
22
|
+
new Literal("e", "e"),
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
const mulDivOperator = new Options("mult-div", [
|
|
26
|
+
new Literal("mult", " * "),
|
|
27
|
+
new Literal("div", " / "),
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
const addSubOperator = new Options("add-sub", [
|
|
31
|
+
new Literal("add", " + "),
|
|
32
|
+
new Literal("sub", " - "),
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
const boolOperator = new Options("and-or", [
|
|
36
|
+
new Literal("and", " && "),
|
|
37
|
+
new Literal("or", " || "),
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const group = new Sequence("group", [
|
|
41
|
+
new Literal("open-paren", "("),
|
|
42
|
+
optionalSpaces,
|
|
43
|
+
new Reference("expression"),
|
|
44
|
+
optionalSpaces,
|
|
45
|
+
new Literal("close-paren", ")"),
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
const ternary = new Sequence("ternary", [
|
|
49
|
+
new Reference("expression"),
|
|
50
|
+
optionalSpaces,
|
|
51
|
+
new Literal("question-mark", "?"),
|
|
52
|
+
optionalSpaces,
|
|
53
|
+
new Reference("expression"),
|
|
54
|
+
optionalSpaces,
|
|
55
|
+
new Literal("colon", ":"),
|
|
56
|
+
optionalSpaces,
|
|
57
|
+
new Reference("expression"),
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
const multDivExpression = new Sequence("mult-div-expression", [
|
|
61
|
+
new Reference("expression"),
|
|
62
|
+
mulDivOperator,
|
|
63
|
+
new Reference("expression"),
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
const addSubExpression = new Sequence("add-sub-expression", [
|
|
67
|
+
new Reference("expression"),
|
|
68
|
+
addSubOperator,
|
|
69
|
+
new Reference("expression"),
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
const boolExpression = new Sequence("bool-expression", [
|
|
73
|
+
new Reference("expression"),
|
|
74
|
+
boolOperator,
|
|
75
|
+
new Reference("expression"),
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
const expression = new ExpressionPattern("expression", [
|
|
79
|
+
multDivExpression,
|
|
80
|
+
addSubExpression,
|
|
81
|
+
boolExpression,
|
|
82
|
+
ternary,
|
|
83
|
+
group,
|
|
84
|
+
variables,
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
return expression;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function createOptionsExpression() {
|
|
91
|
+
const a = new Literal("a", "a");
|
|
92
|
+
const b = new Literal("b", "b");
|
|
93
|
+
const c = new Literal("c", "c");
|
|
94
|
+
|
|
95
|
+
const expressionPattern = new ExpressionPattern("expression", [a, b, c]);
|
|
96
|
+
return expressionPattern;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
describe("Expression Pattern", () => {
|
|
100
|
+
test("Single Expression", () => {
|
|
101
|
+
const expression = createExpressionPattern();
|
|
102
|
+
let result = expression.exec("a || c || b / c * a + d");
|
|
103
|
+
result = expression.exec("a + b");
|
|
104
|
+
result = expression.exec("a + b * c * d");
|
|
105
|
+
result = expression.exec("a + b * c || d + e");
|
|
106
|
+
result = expression.exec("(a + b) * (c + d)");
|
|
107
|
+
result = expression.exec("(a + b) * c + (d + e)");
|
|
108
|
+
result = expression.exec("a + b * c ? d : e");
|
|
109
|
+
result = expression.exec("a + b * (a + b * c ? d : e) ? d : e");
|
|
110
|
+
result = expression.exec("a + b * a + b * c ? d : e ? d : e");
|
|
111
|
+
result = expression.exec("a + b * ?");
|
|
112
|
+
|
|
113
|
+
expect(result).toBe(result);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("Options like", ()=>{
|
|
117
|
+
const expression = createOptionsExpression();
|
|
118
|
+
const autoComplete = new AutoComplete(expression);
|
|
119
|
+
const suggestion = autoComplete.suggestFor("a");
|
|
120
|
+
|
|
121
|
+
expect(suggestion).toBe(suggestion);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("Suggest", () => {
|
|
125
|
+
const expression = createExpressionPattern();
|
|
126
|
+
|
|
127
|
+
const autoComplete = new AutoComplete(expression);
|
|
128
|
+
const suggestion = autoComplete.suggestFor("a");
|
|
129
|
+
|
|
130
|
+
expect(suggestion).toBe(suggestion);
|
|
131
|
+
});
|
|
132
|
+
});
|