clarity-pattern-parser 4.0.2 → 5.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.
- package/README.md +176 -1
- package/TODO.md +22 -2
- package/dist/ast/Node.d.ts +43 -11
- package/dist/ast/Visitor.d.ts +31 -31
- package/dist/index.browser.js +1248 -1495
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +12 -16
- package/dist/index.esm.js +1218 -1460
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1217 -1464
- package/dist/index.js.map +1 -1
- package/dist/patterns/And.d.ts +37 -24
- package/dist/patterns/Cursor.d.ts +35 -0
- package/dist/patterns/CursorHistory.d.ts +30 -0
- package/dist/patterns/Literal.d.ts +36 -19
- package/dist/patterns/Not.d.ts +26 -11
- package/dist/patterns/Or.d.ts +31 -22
- package/dist/patterns/ParseError.d.ts +6 -8
- package/dist/patterns/ParseResult.d.ts +6 -0
- package/dist/patterns/Pattern.d.ts +17 -26
- package/dist/patterns/Reference.d.ts +31 -12
- package/dist/patterns/Regex.d.ts +42 -21
- package/dist/patterns/Repeat.d.ts +38 -20
- package/dist/patterns/clonePatterns.d.ts +2 -0
- package/dist/patterns/filterOutNull.d.ts +2 -0
- package/dist/patterns/findPattern.d.ts +2 -0
- package/dist/patterns/getNextPattern.d.ts +2 -0
- package/jest.config.js +2 -1
- package/package.json +4 -5
- package/rollup.config.js +1 -1
- package/src/ast/Node.test.ts +254 -0
- package/src/ast/Node.ts +171 -23
- package/src/index.ts +11 -19
- package/src/intellisense/AutoComplete.test.ts +72 -0
- package/src/intellisense/AutoComplete.ts +146 -0
- package/src/intellisense/Suggestion.ts +13 -0
- package/src/intellisense/SuggestionOption.ts +4 -0
- package/src/{tests/cssPatterns → intellisense/css}/cssValue.ts +1 -1
- package/src/{tests/cssPatterns → intellisense/css}/divider.ts +2 -1
- package/src/intellisense/css/hex.ts +6 -0
- package/src/{tests/cssPatterns → intellisense/css}/method.ts +8 -9
- package/src/intellisense/css/name.ts +5 -0
- package/src/{tests/javascriptPatterns → intellisense/css}/number.ts +3 -3
- package/src/intellisense/css/spaces.ts +6 -0
- package/src/intellisense/css/unit.ts +10 -0
- package/src/{tests/cssPatterns → intellisense/css}/value.ts +1 -1
- package/src/{tests/cssPatterns → intellisense/css}/values.ts +1 -1
- package/src/intellisense/javascript/Javascript.test.ts +203 -0
- package/src/intellisense/javascript/arrayLiteral.ts +25 -0
- package/src/intellisense/javascript/deleteStatement.ts +14 -0
- package/src/intellisense/javascript/escapedCharacter.ts +50 -0
- package/src/intellisense/javascript/exponent.ts +26 -0
- package/src/intellisense/javascript/expression.ts +87 -0
- package/src/intellisense/javascript/expressionStatement.ts +29 -0
- package/src/intellisense/javascript/fraction.ts +13 -0
- package/src/intellisense/javascript/infixOperator.ts +36 -0
- package/src/intellisense/javascript/integer.ts +7 -0
- package/src/intellisense/javascript/invocation.ts +28 -0
- package/src/intellisense/javascript/literal.ts +14 -0
- package/src/intellisense/javascript/name.ts +3 -0
- package/src/intellisense/javascript/numberLiteral.ts +10 -0
- package/src/intellisense/javascript/objectLiteral.ts +30 -0
- package/src/intellisense/javascript/optionalSpaces.ts +3 -0
- package/src/intellisense/javascript/parameters.ts +20 -0
- package/src/intellisense/javascript/prefixOperator.ts +13 -0
- package/src/intellisense/javascript/propertyAccess.ts +23 -0
- package/src/intellisense/javascript/stringLiteral.ts +28 -0
- package/src/patterns/And.test.ts +299 -0
- package/src/patterns/And.ts +222 -119
- package/src/patterns/Cursor.test.ts +93 -0
- package/src/patterns/Cursor.ts +130 -0
- package/src/patterns/CursorHistory.test.ts +54 -0
- package/src/patterns/CursorHistory.ts +95 -0
- package/src/patterns/Literal.test.ts +134 -0
- package/src/patterns/Literal.ts +151 -61
- package/src/patterns/Not.test.ts +88 -0
- package/src/patterns/Not.ts +74 -33
- package/src/patterns/Or.test.ts +105 -0
- package/src/patterns/Or.ts +106 -98
- package/src/patterns/ParseError.ts +3 -7
- package/src/patterns/ParseResult.ts +7 -0
- package/src/patterns/Pattern.ts +18 -150
- package/src/patterns/Reference.test.ts +104 -0
- package/src/patterns/Reference.ts +94 -94
- package/src/patterns/Regex.test.ts +101 -0
- package/src/patterns/Regex.ts +129 -60
- package/src/patterns/Repeat.test.ts +196 -0
- package/src/patterns/Repeat.ts +208 -104
- package/src/patterns/clonePatterns.ts +5 -0
- package/src/patterns/filterOutNull.ts +13 -0
- package/src/patterns/findPattern.ts +25 -0
- package/src/patterns/getNextPattern.test.ts +39 -0
- package/src/patterns/getNextPattern.ts +18 -0
- package/src/Cursor.ts +0 -141
- package/src/CursorHistory.ts +0 -146
- package/src/TextSuggester.ts +0 -317
- package/src/ast/Visitor.ts +0 -271
- package/src/patterns/LookAhead.ts +0 -32
- package/src/patterns/Recursive.ts +0 -92
- package/src/tests/And.test.ts +0 -180
- package/src/tests/ComplexExamples.test.ts +0 -86
- package/src/tests/CssPatterns.test.ts +0 -90
- package/src/tests/CursorHistory.test.ts +0 -107
- package/src/tests/Cusor.test.ts +0 -174
- package/src/tests/HtmlPatterns.test.ts +0 -34
- package/src/tests/Literal.test.ts +0 -79
- package/src/tests/LookAhead.test.ts +0 -44
- package/src/tests/Not.test.ts +0 -51
- package/src/tests/Or.test.ts +0 -113
- package/src/tests/Pattern.test.ts +0 -290
- package/src/tests/Recursive.test.ts +0 -64
- package/src/tests/Reference.test.ts +0 -16
- package/src/tests/Repeat.test.ts +0 -75
- package/src/tests/SpeedTest.test.ts +0 -31
- package/src/tests/TextSuggester.test.ts +0 -297
- package/src/tests/Visitor.test.ts +0 -331
- package/src/tests/cssPatterns/hex.ts +0 -5
- package/src/tests/cssPatterns/name.ts +0 -5
- package/src/tests/cssPatterns/number.ts +0 -8
- package/src/tests/cssPatterns/spaces.ts +0 -5
- package/src/tests/cssPatterns/unit.ts +0 -8
- package/src/tests/htmlPatterns/element.ts +0 -49
- package/src/tests/javascriptPatterns/boolean.ts +0 -10
- package/src/tests/javascriptPatterns/json.ts +0 -67
- package/src/tests/javascriptPatterns/name.ts +0 -5
- package/src/tests/javascriptPatterns/objectLiteral.ts +0 -40
- package/src/tests/javascriptPatterns/string.ts +0 -84
- package/src/tests/javascriptPatterns/unit.ts +0 -8
- package/src/tests/javascriptPatterns/whitespace.ts +0 -44
- package/src/tests/naturalLanguage/filter.ts +0 -37
- package/src/tests/patterns/sentence.ts +0 -37
- /package/src/{tests/cssPatterns → intellisense/css}/optionalSpaces.ts +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { And } from "./And";
|
|
3
|
+
import { Cursor } from "./Cursor";
|
|
4
|
+
import { findPattern } from "./findPattern";
|
|
5
|
+
import { Literal } from "./Literal";
|
|
6
|
+
import { Regex } from "./Regex";
|
|
7
|
+
import { Repeat } from "./Repeat";
|
|
8
|
+
|
|
9
|
+
describe("Repeat", () => {
|
|
10
|
+
test("Successful Parse", () => {
|
|
11
|
+
const digit = new Regex("digit", "\\d");
|
|
12
|
+
const integer = new Repeat("number", digit);
|
|
13
|
+
const cursor = new Cursor("337");
|
|
14
|
+
const result = integer.parse(cursor);
|
|
15
|
+
const expected = new Node("repeat", "number", 0, 2, [
|
|
16
|
+
new Node("regex", "digit", 0, 0, [], "3"),
|
|
17
|
+
new Node("regex", "digit", 1, 1, [], "3"),
|
|
18
|
+
new Node("regex", "digit", 2, 2, [], "7"),
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
expect(result).toEqual(expected)
|
|
22
|
+
expect(cursor.hasError).toBeFalsy()
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("Failed Parse", () => {
|
|
26
|
+
const digit = new Regex("digit", "\\d");
|
|
27
|
+
const integer = new Repeat("number", digit);
|
|
28
|
+
const cursor = new Cursor("John");
|
|
29
|
+
const result = integer.parse(cursor);
|
|
30
|
+
|
|
31
|
+
expect(result).toBeNull()
|
|
32
|
+
expect(cursor.hasError).toBeTruthy()
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("Successful Parse With Divider", () => {
|
|
36
|
+
const digit = new Regex("digit", "\\d");
|
|
37
|
+
const divider = new Literal("divider", ",");
|
|
38
|
+
const integer = new Repeat("number", digit, divider);
|
|
39
|
+
const cursor = new Cursor("3,3,7");
|
|
40
|
+
const result = integer.parse(cursor);
|
|
41
|
+
const expected = new Node("repeat", "number", 0, 4, [
|
|
42
|
+
new Node("regex", "digit", 0, 0, [], "3"),
|
|
43
|
+
new Node("literal", "divider", 1, 1, [], ","),
|
|
44
|
+
new Node("regex", "digit", 2, 2, [], "3"),
|
|
45
|
+
new Node("literal", "divider", 3, 3, [], ","),
|
|
46
|
+
new Node("regex", "digit", 4, 4, [], "7"),
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
expect(result).toEqual(expected)
|
|
50
|
+
expect(cursor.hasError).toBeFalsy()
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("Successful Parse Text Ends With Divider", () => {
|
|
54
|
+
const digit = new Regex("digit", "\\d");
|
|
55
|
+
const divider = new Literal("divider", ",");
|
|
56
|
+
const integer = new Repeat("number", digit, divider);
|
|
57
|
+
const cursor = new Cursor("3,3,7,");
|
|
58
|
+
const result = integer.parse(cursor);
|
|
59
|
+
const expected = new Node("repeat", "number", 0, 4, [
|
|
60
|
+
new Node("regex", "digit", 0, 0, [], "3"),
|
|
61
|
+
new Node("literal", "divider", 1, 1, [], ","),
|
|
62
|
+
new Node("regex", "digit", 2, 2, [], "3"),
|
|
63
|
+
new Node("literal", "divider", 3, 3, [], ","),
|
|
64
|
+
new Node("regex", "digit", 4, 4, [], "7"),
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
expect(result).toEqual(expected)
|
|
68
|
+
expect(cursor.hasError).toBeFalsy()
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("Successful Parse Trailing Comma", () => {
|
|
72
|
+
const digit = new Regex("digit", "\\d");
|
|
73
|
+
const divider = new Literal("divider", ",");
|
|
74
|
+
const integer = new Repeat("number", digit, divider);
|
|
75
|
+
const cursor = new Cursor("3,3,7,t");
|
|
76
|
+
const result = integer.parse(cursor);
|
|
77
|
+
const expected = new Node("repeat", "number", 0, 4, [
|
|
78
|
+
new Node("regex", "digit", 0, 0, [], "3"),
|
|
79
|
+
new Node("literal", "divider", 1, 1, [], ","),
|
|
80
|
+
new Node("regex", "digit", 2, 2, [], "3"),
|
|
81
|
+
new Node("literal", "divider", 3, 3, [], ","),
|
|
82
|
+
new Node("regex", "digit", 4, 4, [], "7"),
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
expect(result).toEqual(expected)
|
|
86
|
+
expect(cursor.hasError).toBeFalsy()
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("Failed (Optional)", () => {
|
|
90
|
+
const digit = new Regex("digit", "\\d");
|
|
91
|
+
const integer = new Repeat("number", digit, undefined, true);
|
|
92
|
+
const cursor = new Cursor("John");
|
|
93
|
+
const result = integer.parse(cursor);
|
|
94
|
+
|
|
95
|
+
expect(result).toBeNull()
|
|
96
|
+
expect(cursor.hasError).toBeFalsy()
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("Ast Reduction", () => {
|
|
100
|
+
const digit = new Regex("digit", "\\d");
|
|
101
|
+
const integer = new Repeat("number", digit);
|
|
102
|
+
const cursor = new Cursor("337");
|
|
103
|
+
|
|
104
|
+
integer.enableAstReduction();
|
|
105
|
+
|
|
106
|
+
let result = integer.parse(cursor);
|
|
107
|
+
let expected = new Node("repeat", "number", 0, 2, [], "337");
|
|
108
|
+
|
|
109
|
+
expect(result).toEqual(expected)
|
|
110
|
+
|
|
111
|
+
integer.disableAstReduction()
|
|
112
|
+
cursor.moveTo(0)
|
|
113
|
+
|
|
114
|
+
result = integer.parse(cursor);
|
|
115
|
+
expected = new Node("repeat", "number", 0, 2, [
|
|
116
|
+
new Node("regex", "digit", 0, 0, [], "3"),
|
|
117
|
+
new Node("regex", "digit", 1, 1, [], "3"),
|
|
118
|
+
new Node("regex", "digit", 2, 2, [], "7"),
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
expect(result).toEqual(expected)
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("Get Tokens", () => {
|
|
125
|
+
const a = new Literal("a", "A");
|
|
126
|
+
const manyA = new Repeat("number", a);
|
|
127
|
+
const tokens = manyA.getTokens();
|
|
128
|
+
const expected = ["A"];
|
|
129
|
+
|
|
130
|
+
expect(tokens).toEqual(expected)
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("Get Next Tokens With Bogus Pattern", () => {
|
|
134
|
+
const a = new Literal("a", "A");
|
|
135
|
+
const manyA = new Repeat("many-a", a);
|
|
136
|
+
const tokens = manyA.getNextTokens(new Literal("bogus", "bogus"));
|
|
137
|
+
const expected: string[] = [];
|
|
138
|
+
|
|
139
|
+
expect(tokens).toEqual(expected)
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("Get Next Tokens With Divider", () => {
|
|
143
|
+
const a = new Literal("a", "A");
|
|
144
|
+
const b = new Literal("b", "B");
|
|
145
|
+
const divider = new Literal("divider", ",");
|
|
146
|
+
const manyA = new Repeat("many-a", a, divider);
|
|
147
|
+
const parent = new And("parent", [manyA, b]);
|
|
148
|
+
|
|
149
|
+
const clonedManyA = findPattern(parent, p => p.name == "many-a");
|
|
150
|
+
let tokens = clonedManyA?.getNextTokens(clonedManyA.children[0]);
|
|
151
|
+
let expected = [",", "B"];
|
|
152
|
+
|
|
153
|
+
expect(tokens).toEqual(expected)
|
|
154
|
+
|
|
155
|
+
tokens = clonedManyA?.getNextTokens(clonedManyA.children[1]);
|
|
156
|
+
expected = ["A"];
|
|
157
|
+
|
|
158
|
+
expect(tokens).toEqual(expected)
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("Get Next Tokens Without Divider", () => {
|
|
162
|
+
const a = new Literal("a", "A");
|
|
163
|
+
const b = new Literal("b", "B");
|
|
164
|
+
const manyA = new Repeat("many-a", a);
|
|
165
|
+
const parent = new And("parent", [manyA, b]);
|
|
166
|
+
|
|
167
|
+
const clonedManyA = findPattern(parent, p => p.name == "many-a");
|
|
168
|
+
const tokens = clonedManyA?.getNextTokens(clonedManyA.children[0]);
|
|
169
|
+
const expected = ["A", "B"];
|
|
170
|
+
|
|
171
|
+
expect(tokens).toEqual(expected)
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("Get Next Pattern", () => {
|
|
175
|
+
const repeat = new Repeat("many-a", new Literal("a", "A"));
|
|
176
|
+
const nextPattern = repeat.getNextPattern();
|
|
177
|
+
|
|
178
|
+
expect(nextPattern).toBeNull()
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("Properties", () => {
|
|
182
|
+
const integer = new Repeat("integer", new Regex("digit", "\\d"));
|
|
183
|
+
|
|
184
|
+
expect(integer.type).toBe("repeat");
|
|
185
|
+
expect(integer.name).toBe("integer");
|
|
186
|
+
expect(integer.isOptional).toBeFalsy()
|
|
187
|
+
expect(integer.parent).toBeNull();
|
|
188
|
+
expect(integer.children[0].name).toBe("digit");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("Parse Text", () => {
|
|
192
|
+
const integer = new Repeat("integer", new Regex("digit", "\\d"));
|
|
193
|
+
const { ast: result } = integer.parseText("B");
|
|
194
|
+
expect(result).toBeNull()
|
|
195
|
+
});
|
|
196
|
+
});
|
package/src/patterns/Repeat.ts
CHANGED
|
@@ -1,87 +1,145 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { Cursor } from "./Cursor";
|
|
3
|
+
import { Pattern } from "./Pattern";
|
|
4
|
+
import { clonePatterns } from "./clonePatterns";
|
|
5
|
+
import { getNextPattern } from "./getNextPattern";
|
|
6
|
+
import { findPattern } from "./findPattern";
|
|
7
|
+
|
|
8
|
+
export class Repeat implements Pattern {
|
|
9
|
+
private _type: string;
|
|
10
|
+
private _name: string;
|
|
11
|
+
private _parent: Pattern | null;
|
|
12
|
+
private _children: Pattern[];
|
|
13
|
+
private _pattern: Pattern;
|
|
14
|
+
private _divider: Pattern;
|
|
15
|
+
private _isOptional: boolean;
|
|
16
|
+
private _nodes: Node[];
|
|
17
|
+
private _firstIndex: number;
|
|
18
|
+
private _shouldReduceAst: boolean;
|
|
19
|
+
|
|
20
|
+
get type(): string {
|
|
21
|
+
return this._type;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get name(): string {
|
|
25
|
+
return this._name;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get parent(): Pattern | null {
|
|
29
|
+
return this._parent;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
set parent(pattern: Pattern | null) {
|
|
33
|
+
this._parent = pattern;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get children(): Pattern[] {
|
|
37
|
+
return this._children;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get isOptional(): boolean {
|
|
41
|
+
return this._isOptional;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
constructor(name: string, pattern: Pattern, divider?: Pattern, isOptional = false) {
|
|
45
|
+
const patterns = divider != null ? [pattern, divider] : [pattern];
|
|
46
|
+
const children: Pattern[] = clonePatterns(patterns, false);
|
|
47
|
+
this._assignChildrenToParent(children);
|
|
26
48
|
|
|
27
|
-
this.
|
|
28
|
-
this.
|
|
29
|
-
this.
|
|
49
|
+
this._type = "repeat";
|
|
50
|
+
this._name = name;
|
|
51
|
+
this._isOptional = isOptional;
|
|
52
|
+
this._parent = null;
|
|
53
|
+
this._children = children;
|
|
54
|
+
this._pattern = children[0];
|
|
55
|
+
this._divider = children[1];
|
|
56
|
+
this._firstIndex = -1
|
|
57
|
+
this._shouldReduceAst = false
|
|
58
|
+
this._nodes = [];
|
|
30
59
|
}
|
|
31
60
|
|
|
32
|
-
private
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"Invalid Arguments: The pattern cannot be a optional pattern."
|
|
36
|
-
);
|
|
61
|
+
private _assignChildrenToParent(children: Pattern[]): void {
|
|
62
|
+
for (const child of children) {
|
|
63
|
+
child.parent = this;
|
|
37
64
|
}
|
|
38
65
|
}
|
|
39
66
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
67
|
+
parseText(text: string) {
|
|
68
|
+
const cursor = new Cursor(text);
|
|
69
|
+
const ast = this.parse(cursor)
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
ast,
|
|
73
|
+
cursor
|
|
74
|
+
};
|
|
44
75
|
}
|
|
45
76
|
|
|
46
|
-
parse(cursor: Cursor) {
|
|
47
|
-
this.
|
|
48
|
-
this.
|
|
77
|
+
parse(cursor: Cursor): Node | null {
|
|
78
|
+
this._firstIndex = cursor.index;
|
|
79
|
+
this._nodes = [];
|
|
80
|
+
|
|
81
|
+
const passed = this.tryToParse(cursor);
|
|
82
|
+
|
|
83
|
+
if (passed) {
|
|
84
|
+
cursor.resolveError();
|
|
85
|
+
const node = this.createNode(cursor);
|
|
86
|
+
|
|
87
|
+
if (node) {
|
|
88
|
+
cursor.recordMatch(this, node);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return node;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!this._isOptional) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
49
97
|
|
|
50
|
-
|
|
98
|
+
cursor.resolveError();
|
|
99
|
+
cursor.moveTo(this._firstIndex);
|
|
100
|
+
return null;
|
|
51
101
|
}
|
|
52
102
|
|
|
53
|
-
private tryToParse() {
|
|
54
|
-
|
|
103
|
+
private tryToParse(cursor: Cursor): boolean {
|
|
104
|
+
let passed = false;
|
|
55
105
|
|
|
56
106
|
while (true) {
|
|
57
|
-
const
|
|
107
|
+
const runningCursorIndex = cursor.index;
|
|
108
|
+
const repeatedNode = this._pattern.parse(cursor);
|
|
109
|
+
|
|
110
|
+
if (cursor.hasError) {
|
|
111
|
+
const lastValidNode = this.getLastValidNode();
|
|
112
|
+
|
|
113
|
+
if (lastValidNode) {
|
|
114
|
+
passed = true;
|
|
115
|
+
} else {
|
|
116
|
+
cursor.moveTo(runningCursorIndex);
|
|
117
|
+
cursor.recordErrorAt(runningCursorIndex, this._pattern);
|
|
118
|
+
passed = false;
|
|
119
|
+
}
|
|
58
120
|
|
|
59
|
-
if (cursor.hasUnresolvedError()) {
|
|
60
|
-
this.processResult();
|
|
61
121
|
break;
|
|
62
|
-
} else if (
|
|
63
|
-
this.
|
|
122
|
+
} else if (repeatedNode) {
|
|
123
|
+
this._nodes.push(repeatedNode);
|
|
64
124
|
|
|
65
|
-
if (
|
|
66
|
-
|
|
125
|
+
if (!cursor.hasNext()) {
|
|
126
|
+
passed = true;
|
|
67
127
|
break;
|
|
68
128
|
}
|
|
69
129
|
|
|
70
130
|
cursor.next();
|
|
71
131
|
|
|
72
|
-
if (this._divider
|
|
73
|
-
const
|
|
74
|
-
const node = this._divider.parse(cursor);
|
|
132
|
+
if (this._divider) {
|
|
133
|
+
const dividerNode = this._divider.parse(cursor);
|
|
75
134
|
|
|
76
|
-
if (cursor.
|
|
77
|
-
|
|
78
|
-
this.processResult();
|
|
135
|
+
if (cursor.hasError) {
|
|
136
|
+
passed = true;
|
|
79
137
|
break;
|
|
80
|
-
} else if (
|
|
81
|
-
this.
|
|
138
|
+
} else if (dividerNode) {
|
|
139
|
+
this._nodes.push(dividerNode);
|
|
82
140
|
|
|
83
|
-
if (
|
|
84
|
-
|
|
141
|
+
if (!cursor.hasNext()) {
|
|
142
|
+
passed = true;
|
|
85
143
|
break;
|
|
86
144
|
}
|
|
87
145
|
|
|
@@ -90,66 +148,112 @@ export default class Repeat extends Pattern {
|
|
|
90
148
|
}
|
|
91
149
|
}
|
|
92
150
|
}
|
|
93
|
-
}
|
|
94
151
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const noMatch = this.nodes.length === 0;
|
|
98
|
-
const hasDivider = this._divider != null;
|
|
152
|
+
return passed;
|
|
153
|
+
}
|
|
99
154
|
|
|
100
|
-
|
|
155
|
+
private createNode(cursor: Cursor): Node | null {
|
|
156
|
+
let children: Node[] = [];
|
|
101
157
|
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
this.cursor.moveToMark(this.mark);
|
|
105
|
-
} else {
|
|
106
|
-
const parseError = new ParseError(
|
|
107
|
-
`Did not find a repeating match of ${this.name}.`,
|
|
108
|
-
this.mark,
|
|
109
|
-
this
|
|
110
|
-
);
|
|
111
|
-
this.cursor.throwError(parseError);
|
|
112
|
-
}
|
|
113
|
-
this.node = null;
|
|
158
|
+
if (!this._divider) {
|
|
159
|
+
children = this._nodes;
|
|
114
160
|
} else {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
this.
|
|
121
|
-
|
|
122
|
-
this.nodes,
|
|
123
|
-
value
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
this.cursor.index = this.node.endIndex;
|
|
127
|
-
this.cursor.addMatch(this, this.node);
|
|
161
|
+
if (this._nodes.length % 2 !== 1) {
|
|
162
|
+
const dividerNode = this._nodes[this._nodes.length - 1];
|
|
163
|
+
cursor.moveTo(dividerNode.firstIndex);
|
|
164
|
+
children = this._nodes.slice(0, this._nodes.length - 1);
|
|
165
|
+
} else {
|
|
166
|
+
children = this._nodes;
|
|
167
|
+
}
|
|
128
168
|
}
|
|
169
|
+
|
|
170
|
+
const lastIndex = children[children.length - 1].lastIndex;
|
|
171
|
+
const value = cursor.getChars(this._firstIndex, lastIndex);
|
|
172
|
+
cursor.moveTo(lastIndex);
|
|
173
|
+
|
|
174
|
+
if (this._shouldReduceAst) {
|
|
175
|
+
children = [];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return new Node(
|
|
179
|
+
"repeat",
|
|
180
|
+
this._name,
|
|
181
|
+
this._firstIndex,
|
|
182
|
+
lastIndex,
|
|
183
|
+
children,
|
|
184
|
+
this._shouldReduceAst ? value : undefined
|
|
185
|
+
);
|
|
129
186
|
}
|
|
130
187
|
|
|
131
|
-
private
|
|
132
|
-
const
|
|
188
|
+
private getLastValidNode(): Node | null {
|
|
189
|
+
const nodes = this._nodes.filter((node) => node !== null);
|
|
133
190
|
|
|
134
|
-
if (
|
|
135
|
-
|
|
191
|
+
if (nodes.length === 0) {
|
|
192
|
+
return null;
|
|
136
193
|
}
|
|
137
|
-
|
|
194
|
+
|
|
195
|
+
return nodes[nodes.length - 1];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
enableAstReduction(): void {
|
|
199
|
+
this._shouldReduceAst = true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
disableAstReduction(): void {
|
|
203
|
+
this._shouldReduceAst = false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
getTokens(): string[] {
|
|
207
|
+
return this._pattern.getTokens();
|
|
138
208
|
}
|
|
139
209
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
210
|
+
getNextTokens(lastMatched: Pattern): string[] {
|
|
211
|
+
let index = -1;
|
|
212
|
+
const tokens: string[] = [];
|
|
213
|
+
|
|
214
|
+
for (let i = 0; i < this._children.length; i++) {
|
|
215
|
+
if (this._children[i] === lastMatched) {
|
|
216
|
+
index = i;
|
|
217
|
+
}
|
|
143
218
|
}
|
|
144
219
|
|
|
145
|
-
if (
|
|
146
|
-
|
|
220
|
+
if (index === -1) {
|
|
221
|
+
return [];
|
|
147
222
|
}
|
|
148
223
|
|
|
149
|
-
|
|
224
|
+
if (index === 0 && this._divider) {
|
|
225
|
+
tokens.push(...this._children[1].getTokens());
|
|
226
|
+
|
|
227
|
+
if (this._parent) {
|
|
228
|
+
tokens.push(...this._parent.getNextTokens(this));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (index === 1) {
|
|
233
|
+
tokens.push(...this._children[0].getTokens());
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (index === 0 && !this._divider && this._parent) {
|
|
237
|
+
tokens.push(...this._children[0].getTokens());
|
|
238
|
+
tokens.push(...this._parent.getNextTokens(this));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return tokens;
|
|
150
242
|
}
|
|
151
243
|
|
|
152
|
-
|
|
153
|
-
return this
|
|
244
|
+
getNextPattern(): Pattern | null {
|
|
245
|
+
return getNextPattern(this)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
findPattern(isMatch: (p: Pattern) => boolean): Pattern | null {
|
|
249
|
+
return findPattern(this, isMatch);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
clone(name = this._name, isOptional = this._isOptional): Pattern {
|
|
253
|
+
const repeat = new Repeat(name, this._pattern, this._divider, isOptional);
|
|
254
|
+
repeat._shouldReduceAst = this._shouldReduceAst;
|
|
255
|
+
|
|
256
|
+
return repeat;
|
|
154
257
|
}
|
|
155
258
|
}
|
|
259
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Node } from "../ast/Node"
|
|
2
|
+
|
|
3
|
+
export function filterOutNull(nodes: (Node | null)[]): Node[] {
|
|
4
|
+
const filteredNodes: Node[] = [];
|
|
5
|
+
|
|
6
|
+
for (const node of nodes) {
|
|
7
|
+
if (node !== null) {
|
|
8
|
+
filteredNodes.push(node);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return filteredNodes;
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Pattern } from "./Pattern";
|
|
2
|
+
|
|
3
|
+
export function findPattern(pattern: Pattern, predicate: (pattern: Pattern) => boolean): Pattern | null {
|
|
4
|
+
let children: Pattern[] = [];
|
|
5
|
+
|
|
6
|
+
if (pattern.type === "reference") {
|
|
7
|
+
children = [];
|
|
8
|
+
} else {
|
|
9
|
+
children = pattern.children;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
for (const child of children) {
|
|
13
|
+
const result = findPattern(child, predicate);
|
|
14
|
+
|
|
15
|
+
if (result !== null) {
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (predicate(pattern)) {
|
|
21
|
+
return pattern;
|
|
22
|
+
} else {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { And } from "./And";
|
|
2
|
+
import { findPattern } from "./findPattern";
|
|
3
|
+
import { Literal } from "./Literal";
|
|
4
|
+
|
|
5
|
+
describe("getNextPatter", ()=>{
|
|
6
|
+
test("No Parent", ()=>{
|
|
7
|
+
const a = new Literal("a", "A");
|
|
8
|
+
const nextPattern = a.getNextPattern();
|
|
9
|
+
|
|
10
|
+
expect(nextPattern).toBe(null);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("No Next Sibling", ()=>{
|
|
14
|
+
const a = new Literal("a", "A");
|
|
15
|
+
const parent = new And("parent", [a]);
|
|
16
|
+
const nextPattern = parent.children[0].getNextPattern();
|
|
17
|
+
|
|
18
|
+
expect(nextPattern).toBe(null);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("Get Parents Sibling", ()=>{
|
|
22
|
+
const a = new Literal("a", "A");
|
|
23
|
+
const parent = new And("parent", [a]);
|
|
24
|
+
const grandParent = new And("grand-parent", [parent]);
|
|
25
|
+
const clonedA = findPattern(grandParent, p=>p.name === "a");
|
|
26
|
+
const nextPattern = clonedA?.getNextPattern();
|
|
27
|
+
|
|
28
|
+
expect(nextPattern).toBe(null);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("Get Sibling", ()=>{
|
|
32
|
+
const a = new Literal("a", "A");
|
|
33
|
+
const b = new Literal("b", "B");
|
|
34
|
+
const parent = new And("parent", [a, b]);
|
|
35
|
+
|
|
36
|
+
const nextPattern = parent.children[0].getNextPattern();
|
|
37
|
+
expect(nextPattern?.name).toBe("b");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Pattern } from "./Pattern";
|
|
2
|
+
|
|
3
|
+
export function getNextPattern(pattern: Pattern): Pattern | null {
|
|
4
|
+
const parent = pattern.parent;
|
|
5
|
+
|
|
6
|
+
if (parent == null) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const patternIndex = parent.children.indexOf(pattern);
|
|
11
|
+
const nextPattern = parent.children[patternIndex + 1] || null;
|
|
12
|
+
|
|
13
|
+
if (nextPattern == null) {
|
|
14
|
+
return parent.getNextPattern();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return nextPattern;
|
|
18
|
+
}
|