clarity-pattern-parser 8.4.14 → 9.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/TODO.md +4 -1
- package/dist/grammar/Grammar.d.ts +18 -10
- package/dist/grammar/patterns/andLiteral.d.ts +2 -0
- package/dist/grammar/patterns/anonymousPattern.d.ts +2 -0
- package/dist/grammar/patterns/inlinePattern.d.ts +1 -0
- package/dist/grammar/patterns/literals.d.ts +3 -0
- package/dist/grammar/patterns/pattern.d.ts +2 -2
- package/dist/grammar/patterns.d.ts +2 -0
- package/dist/index.browser.js +472 -185
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.esm.js +471 -186
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +472 -185
- package/dist/index.js.map +1 -1
- package/dist/patterns/And.d.ts +4 -1
- package/dist/patterns/Cursor.d.ts +5 -0
- package/dist/patterns/CursorHistory.d.ts +7 -0
- package/dist/patterns/FiniteRepeat.d.ts +4 -1
- package/dist/patterns/InfiniteRepeat.d.ts +5 -4
- package/dist/patterns/Literal.d.ts +6 -5
- package/dist/patterns/Not.d.ts +5 -4
- package/dist/patterns/Or.d.ts +5 -4
- package/dist/patterns/Pattern.d.ts +4 -2
- package/dist/patterns/Reference.d.ts +5 -4
- package/dist/patterns/Regex.d.ts +5 -4
- package/dist/patterns/Repeat.d.ts +3 -0
- package/dist/patterns/arePatternsEqual.d.ts +2 -0
- package/package.json +1 -1
- package/src/grammar/Grammar.test.ts +126 -72
- package/src/grammar/Grammar.ts +241 -158
- package/src/grammar/patterns/anonymousPattern.ts +23 -0
- package/src/grammar/patterns/body.ts +9 -6
- package/src/grammar/patterns/comment.ts +3 -2
- package/src/grammar/patterns/grammar.ts +15 -12
- package/src/grammar/patterns/import.ts +18 -12
- package/src/grammar/patterns/literal.ts +2 -3
- package/src/grammar/patterns/literals.ts +20 -0
- package/src/grammar/patterns/optionsLiteral.ts +19 -0
- package/src/grammar/patterns/pattern.ts +23 -9
- package/src/grammar/patterns/regexLiteral.ts +1 -0
- package/src/grammar/patterns/repeatLiteral.ts +30 -25
- package/src/grammar/patterns/sequenceLiteral.ts +24 -0
- package/src/grammar/patterns/spaces.ts +8 -6
- package/src/grammar/patterns/statement.ts +8 -20
- package/src/grammar/patterns.test.ts +38 -0
- package/src/grammar/patterns.ts +24 -0
- package/src/grammar/spec.md +4 -12
- package/src/index.ts +11 -5
- package/src/intellisense/AutoComplete.test.ts +41 -40
- package/src/intellisense/css/method.ts +2 -2
- package/src/intellisense/css/unit.ts +2 -2
- package/src/intellisense/css/value.ts +1 -1
- package/src/intellisense/javascript/Javascript.test.ts +31 -32
- package/src/intellisense/javascript/arrayLiteral.ts +7 -6
- package/src/intellisense/javascript/assignment.ts +6 -6
- package/src/intellisense/javascript/deleteStatement.ts +2 -2
- package/src/intellisense/javascript/escapedCharacter.ts +6 -6
- package/src/intellisense/javascript/exponent.ts +6 -6
- package/src/intellisense/javascript/expression.ts +18 -17
- package/src/intellisense/javascript/fraction.ts +3 -3
- package/src/intellisense/javascript/infixOperator.ts +10 -10
- package/src/intellisense/javascript/integer.ts +1 -1
- package/src/intellisense/javascript/invocation.ts +8 -7
- package/src/intellisense/javascript/literal.ts +3 -3
- package/src/intellisense/javascript/numberLiteral.ts +5 -4
- package/src/intellisense/javascript/objectAccess.ts +2 -3
- package/src/intellisense/javascript/objectLiteral.ts +8 -7
- package/src/intellisense/javascript/optionalSpaces.ts +2 -1
- package/src/intellisense/javascript/parameters.ts +5 -5
- package/src/intellisense/javascript/prefixOperator.ts +3 -4
- package/src/intellisense/javascript/propertyAccess.ts +9 -8
- package/src/intellisense/javascript/stringLiteral.ts +14 -15
- package/src/patterns/Cursor.ts +42 -4
- package/src/patterns/CursorHistory.ts +20 -4
- package/src/patterns/FiniteRepeat.test.ts +52 -51
- package/src/patterns/FiniteRepeat.ts +60 -38
- package/src/patterns/InfiniteRepeat.test.ts +36 -49
- package/src/patterns/InfiniteRepeat.ts +70 -37
- package/src/patterns/Literal.test.ts +16 -27
- package/src/patterns/Literal.ts +34 -27
- package/src/patterns/Not.test.ts +7 -7
- package/src/patterns/Not.ts +24 -6
- package/src/patterns/Optional.test.ts +164 -0
- package/src/patterns/Optional.ts +143 -0
- package/src/patterns/{Or.test.ts → Options.test.ts} +51 -49
- package/src/patterns/{Or.ts → Options.ts} +32 -23
- package/src/patterns/Pattern.ts +6 -5
- package/src/patterns/Reference.test.ts +21 -22
- package/src/patterns/Reference.ts +26 -15
- package/src/patterns/Regex.test.ts +15 -15
- package/src/patterns/Regex.ts +29 -19
- package/src/patterns/Repeat.test.ts +12 -22
- package/src/patterns/Repeat.ts +22 -21
- package/src/patterns/{And.test.ts → Sequence.test.ts} +78 -78
- package/src/patterns/{And.ts → Sequence.ts} +40 -29
- package/src/patterns/arePatternsEqual.ts +12 -0
- package/src/patterns/clonePatterns.ts +2 -2
- package/src/grammar/patterns/andLiteral.ts +0 -8
- package/src/grammar/patterns/orLiteral.ts +0 -8
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Node } from "../ast/Node";
|
|
2
|
-
import {
|
|
2
|
+
import { Sequence } from "./Sequence";
|
|
3
3
|
import { Cursor } from "./Cursor";
|
|
4
4
|
import { findPattern } from "./findPattern";
|
|
5
5
|
import { Literal } from "./Literal";
|
|
6
6
|
import { Pattern } from "./Pattern";
|
|
7
7
|
import { Regex } from "./Regex";
|
|
8
8
|
import { InfiniteRepeat } from "./InfiniteRepeat";
|
|
9
|
+
import { arePatternsEqual } from "./arePatternsEqual";
|
|
10
|
+
import { Optional } from "./Optional";
|
|
9
11
|
|
|
10
12
|
describe("InfiniteRepeat", () => {
|
|
11
13
|
test("Successful Parse", () => {
|
|
@@ -19,8 +21,8 @@ describe("InfiniteRepeat", () => {
|
|
|
19
21
|
new Node("regex", "digit", 2, 2, [], "7"),
|
|
20
22
|
]);
|
|
21
23
|
|
|
22
|
-
expect(result).toEqual(expected)
|
|
23
|
-
expect(cursor.hasError).toBeFalsy()
|
|
24
|
+
expect(result).toEqual(expected);
|
|
25
|
+
expect(cursor.hasError).toBeFalsy();
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
test("Bounds", () => {
|
|
@@ -51,8 +53,8 @@ describe("InfiniteRepeat", () => {
|
|
|
51
53
|
const cursor = new Cursor("John");
|
|
52
54
|
const result = integer.parse(cursor);
|
|
53
55
|
|
|
54
|
-
expect(result).toBeNull()
|
|
55
|
-
expect(cursor.hasError).toBeTruthy()
|
|
56
|
+
expect(result).toBeNull();
|
|
57
|
+
expect(cursor.hasError).toBeTruthy();
|
|
56
58
|
});
|
|
57
59
|
|
|
58
60
|
test("Successful Parse With Divider", () => {
|
|
@@ -69,8 +71,8 @@ describe("InfiniteRepeat", () => {
|
|
|
69
71
|
new Node("regex", "digit", 4, 4, [], "7"),
|
|
70
72
|
]);
|
|
71
73
|
|
|
72
|
-
expect(result).toEqual(expected)
|
|
73
|
-
expect(cursor.hasError).toBeFalsy()
|
|
74
|
+
expect(result).toEqual(expected);
|
|
75
|
+
expect(cursor.hasError).toBeFalsy();
|
|
74
76
|
});
|
|
75
77
|
|
|
76
78
|
test("Successful Parse Text Ends With Divider", () => {
|
|
@@ -87,8 +89,8 @@ describe("InfiniteRepeat", () => {
|
|
|
87
89
|
new Node("regex", "digit", 4, 4, [], "7"),
|
|
88
90
|
]);
|
|
89
91
|
|
|
90
|
-
expect(result).toEqual(expected)
|
|
91
|
-
expect(cursor.hasError).toBeFalsy()
|
|
92
|
+
expect(result).toEqual(expected);
|
|
93
|
+
expect(cursor.hasError).toBeFalsy();
|
|
92
94
|
});
|
|
93
95
|
|
|
94
96
|
test("Successful Parse Trailing Comma", () => {
|
|
@@ -105,18 +107,18 @@ describe("InfiniteRepeat", () => {
|
|
|
105
107
|
new Node("regex", "digit", 4, 4, [], "7"),
|
|
106
108
|
]);
|
|
107
109
|
|
|
108
|
-
expect(result).toEqual(expected)
|
|
109
|
-
expect(cursor.hasError).toBeFalsy()
|
|
110
|
+
expect(result).toEqual(expected);
|
|
111
|
+
expect(cursor.hasError).toBeFalsy();
|
|
110
112
|
});
|
|
111
113
|
|
|
112
114
|
test("Failed (Optional)", () => {
|
|
113
|
-
const digit = new Regex("digit", "\\d");
|
|
115
|
+
const digit = new Optional("optional-digit", new Regex("digit", "\\d"));
|
|
114
116
|
const integer = new InfiniteRepeat("number", digit, { min: 0 });
|
|
115
117
|
const cursor = new Cursor("John");
|
|
116
118
|
const result = integer.parse(cursor);
|
|
117
119
|
|
|
118
|
-
expect(result).toBeNull()
|
|
119
|
-
expect(cursor.hasError).toBeFalsy()
|
|
120
|
+
expect(result).toBeNull();
|
|
121
|
+
expect(cursor.hasError).toBeFalsy();
|
|
120
122
|
});
|
|
121
123
|
|
|
122
124
|
test("Get Tokens", () => {
|
|
@@ -125,7 +127,7 @@ describe("InfiniteRepeat", () => {
|
|
|
125
127
|
const tokens = manyA.getTokens();
|
|
126
128
|
const expected = ["A"];
|
|
127
129
|
|
|
128
|
-
expect(tokens).toEqual(expected)
|
|
130
|
+
expect(tokens).toEqual(expected);
|
|
129
131
|
});
|
|
130
132
|
|
|
131
133
|
test("Get Tokens After With Bogus Pattern", () => {
|
|
@@ -134,7 +136,7 @@ describe("InfiniteRepeat", () => {
|
|
|
134
136
|
const tokens = manyA.getTokensAfter(new Literal("bogus", "bogus"));
|
|
135
137
|
const expected: string[] = [];
|
|
136
138
|
|
|
137
|
-
expect(tokens).toEqual(expected)
|
|
139
|
+
expect(tokens).toEqual(expected);
|
|
138
140
|
});
|
|
139
141
|
|
|
140
142
|
test("Get Tokens After With Divider", () => {
|
|
@@ -142,31 +144,31 @@ describe("InfiniteRepeat", () => {
|
|
|
142
144
|
const b = new Literal("b", "B");
|
|
143
145
|
const divider = new Literal("divider", ",");
|
|
144
146
|
const manyA = new InfiniteRepeat("many-a", a, { divider });
|
|
145
|
-
const parent = new
|
|
147
|
+
const parent = new Sequence("parent", [manyA, b]);
|
|
146
148
|
|
|
147
|
-
const clonedManyA = findPattern(parent, p => p.name
|
|
149
|
+
const clonedManyA = findPattern(parent, p => p.name === "many-a");
|
|
148
150
|
let tokens = clonedManyA?.getTokensAfter(clonedManyA.children[0]);
|
|
149
151
|
let expected = [",", "B"];
|
|
150
152
|
|
|
151
|
-
expect(tokens).toEqual(expected)
|
|
153
|
+
expect(tokens).toEqual(expected);
|
|
152
154
|
|
|
153
155
|
tokens = clonedManyA?.getTokensAfter(clonedManyA.children[1]);
|
|
154
156
|
expected = ["A"];
|
|
155
157
|
|
|
156
|
-
expect(tokens).toEqual(expected)
|
|
158
|
+
expect(tokens).toEqual(expected);
|
|
157
159
|
});
|
|
158
160
|
|
|
159
161
|
test("Get Tokens After Without Divider", () => {
|
|
160
162
|
const a = new Literal("a", "A");
|
|
161
163
|
const b = new Literal("b", "B");
|
|
162
164
|
const manyA = new InfiniteRepeat("many-a", a);
|
|
163
|
-
const parent = new
|
|
165
|
+
const parent = new Sequence("parent", [manyA, b]);
|
|
164
166
|
|
|
165
|
-
const clonedManyA = findPattern(parent, p => p.name
|
|
167
|
+
const clonedManyA = findPattern(parent, p => p.name === "many-a");
|
|
166
168
|
const tokens = clonedManyA?.getTokensAfter(clonedManyA.children[0]);
|
|
167
169
|
const expected = ["A", "B"];
|
|
168
170
|
|
|
169
|
-
expect(tokens).toEqual(expected)
|
|
171
|
+
expect(tokens).toEqual(expected);
|
|
170
172
|
});
|
|
171
173
|
|
|
172
174
|
test("Properties", () => {
|
|
@@ -175,7 +177,6 @@ describe("InfiniteRepeat", () => {
|
|
|
175
177
|
expect(integer.type).toBe("infinite-repeat");
|
|
176
178
|
expect(integer.name).toBe("integer");
|
|
177
179
|
expect(integer.min).toBe(1);
|
|
178
|
-
expect(integer.isOptional).toBeFalsy()
|
|
179
180
|
expect(integer.parent).toBeNull();
|
|
180
181
|
expect(integer.children[0].name).toBe("digit");
|
|
181
182
|
});
|
|
@@ -183,28 +184,28 @@ describe("InfiniteRepeat", () => {
|
|
|
183
184
|
test("Exec", () => {
|
|
184
185
|
const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
|
|
185
186
|
const { ast: result } = integer.exec("B");
|
|
186
|
-
expect(result).toBeNull()
|
|
187
|
+
expect(result).toBeNull();
|
|
187
188
|
});
|
|
188
189
|
|
|
189
190
|
test("Test With Match", () => {
|
|
190
191
|
const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
|
|
191
192
|
const result = integer.test("1");
|
|
192
|
-
expect(result).toBeTruthy()
|
|
193
|
+
expect(result).toBeTruthy();
|
|
193
194
|
});
|
|
194
195
|
|
|
195
196
|
test("Test With No Match", () => {
|
|
196
197
|
const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
|
|
197
198
|
const result = integer.test("b");
|
|
198
|
-
expect(result).toBeFalsy()
|
|
199
|
+
expect(result).toBeFalsy();
|
|
199
200
|
});
|
|
200
201
|
|
|
201
202
|
test("Get Next Tokens", () => {
|
|
202
203
|
const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
|
|
203
|
-
const parent = new
|
|
204
|
+
const parent = new Sequence("parent", [integer, new Literal("pow", "!")]);
|
|
204
205
|
const integerClone = parent.find(p => p.name === "integer") as Pattern;
|
|
205
206
|
const tokens = integerClone.getNextTokens();
|
|
206
207
|
|
|
207
|
-
expect(tokens).toEqual(["!"])
|
|
208
|
+
expect(tokens).toEqual(["!"]);
|
|
208
209
|
});
|
|
209
210
|
|
|
210
211
|
test("Get Next Tokens With Null Parents", () => {
|
|
@@ -227,12 +228,12 @@ describe("InfiniteRepeat", () => {
|
|
|
227
228
|
const patterns = manyA.getPatterns();
|
|
228
229
|
const expected = [manyA.find(p => p.name === "a")];
|
|
229
230
|
|
|
230
|
-
expect(patterns).toEqual(expected)
|
|
231
|
+
expect(patterns).toEqual(expected);
|
|
231
232
|
});
|
|
232
233
|
|
|
233
234
|
test("Get Next Patterns", () => {
|
|
234
235
|
const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
|
|
235
|
-
const parent = new
|
|
236
|
+
const parent = new Sequence("parent", [integer, new Literal("pow", "!")]);
|
|
236
237
|
const integerClone = parent.find(p => p.name === "integer") as Pattern;
|
|
237
238
|
const powClone = parent.find(p => p.name === "pow") as Pattern;
|
|
238
239
|
const patterns = integerClone.getNextPatterns();
|
|
@@ -249,26 +250,11 @@ describe("InfiniteRepeat", () => {
|
|
|
249
250
|
});
|
|
250
251
|
|
|
251
252
|
test("Clone With Custom Overrides", () => {
|
|
252
|
-
const numbers = new InfiniteRepeat("numbers", new Regex("number", "\\d"), { min:
|
|
253
|
+
const numbers = new InfiniteRepeat("numbers", new Regex("number", "\\d"), { min: 3, divider: new Literal("divider", "divider"), trimDivider: true });
|
|
253
254
|
let clone = numbers.clone();
|
|
254
|
-
let expected = new InfiniteRepeat("numbers", new Regex("number", "\\d"), { min:
|
|
255
|
-
|
|
256
|
-
expect(clone).toEqual(expected);
|
|
257
|
-
|
|
258
|
-
clone = numbers.clone("cloned-numbers");
|
|
259
|
-
expected = new InfiniteRepeat("cloned-numbers", new Regex("number", "\\d"), { min: 0 });
|
|
260
|
-
|
|
261
|
-
expect(clone).toEqual(expected);
|
|
262
|
-
|
|
263
|
-
clone = numbers.clone("cloned-numbers", true);
|
|
264
|
-
expected = new InfiniteRepeat("cloned-numbers", new Regex("number", "\\d"), { min: 0 });
|
|
265
|
-
|
|
266
|
-
expect(clone).toEqual(expected);
|
|
267
|
-
|
|
268
|
-
clone = numbers.clone("cloned-numbers", false);
|
|
269
|
-
expected = new InfiniteRepeat("cloned-numbers", new Regex("number", "\\d"), { min: 1 });
|
|
255
|
+
let expected = new InfiniteRepeat("numbers", new Regex("number", "\\d"), { min: 3, divider: new Literal("divider", "divider"), trimDivider: true });
|
|
270
256
|
|
|
271
|
-
expect(clone).
|
|
257
|
+
expect(arePatternsEqual(clone, expected)).toBeTruthy();
|
|
272
258
|
});
|
|
273
259
|
|
|
274
260
|
test("No Results, min is 0", () => {
|
|
@@ -276,6 +262,7 @@ describe("InfiniteRepeat", () => {
|
|
|
276
262
|
const result = numbers.exec("j");
|
|
277
263
|
expect(result.ast).toBeNull();
|
|
278
264
|
expect(result.cursor.index).toBe(0);
|
|
265
|
+
expect(result.cursor.hasError).toBeTruthy();
|
|
279
266
|
});
|
|
280
267
|
|
|
281
268
|
|
|
@@ -3,6 +3,9 @@ import { Cursor } from "./Cursor";
|
|
|
3
3
|
import { Pattern } from "./Pattern";
|
|
4
4
|
import { clonePatterns } from "./clonePatterns";
|
|
5
5
|
import { findPattern } from "./findPattern";
|
|
6
|
+
import { ParseResult } from "./ParseResult";
|
|
7
|
+
|
|
8
|
+
let idIndex = 0;
|
|
6
9
|
|
|
7
10
|
export interface InfiniteRepeatOptions {
|
|
8
11
|
divider?: Pattern;
|
|
@@ -11,6 +14,7 @@ export interface InfiniteRepeatOptions {
|
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
export class InfiniteRepeat implements Pattern {
|
|
17
|
+
private _id: string;
|
|
14
18
|
private _type: string;
|
|
15
19
|
private _name: string;
|
|
16
20
|
private _parent: Pattern | null;
|
|
@@ -22,6 +26,10 @@ export class InfiniteRepeat implements Pattern {
|
|
|
22
26
|
private _min: number;
|
|
23
27
|
private _trimDivider: boolean;
|
|
24
28
|
|
|
29
|
+
get id(): string {
|
|
30
|
+
return this._id;
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
get type(): string {
|
|
26
34
|
return this._type;
|
|
27
35
|
}
|
|
@@ -42,27 +50,24 @@ export class InfiniteRepeat implements Pattern {
|
|
|
42
50
|
return this._children;
|
|
43
51
|
}
|
|
44
52
|
|
|
45
|
-
get isOptional(): boolean {
|
|
46
|
-
return this._min === 0;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
53
|
get min(): number {
|
|
50
54
|
return this._min;
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
constructor(name: string, pattern: Pattern, options: InfiniteRepeatOptions = {}) {
|
|
54
|
-
const min = options.min != null ? options.min : 1;
|
|
58
|
+
const min = options.min != null ? Math.max(options.min, 1) : 1;
|
|
55
59
|
const divider = options.divider;
|
|
56
60
|
let children: Pattern[];
|
|
57
61
|
|
|
58
62
|
if (divider != null) {
|
|
59
|
-
children = [pattern.clone(
|
|
63
|
+
children = [pattern.clone(), divider.clone()];
|
|
60
64
|
} else {
|
|
61
|
-
children = [pattern.clone(
|
|
65
|
+
children = [pattern.clone()];
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
this._assignChildrenToParent(children);
|
|
65
69
|
|
|
70
|
+
this._id = `infinite-repeat-${idIndex++}`;
|
|
66
71
|
this._type = "infinite-repeat";
|
|
67
72
|
this._name = name;
|
|
68
73
|
this._min = min;
|
|
@@ -70,7 +75,7 @@ export class InfiniteRepeat implements Pattern {
|
|
|
70
75
|
this._children = children;
|
|
71
76
|
this._pattern = children[0];
|
|
72
77
|
this._divider = children[1];
|
|
73
|
-
this._firstIndex = -1
|
|
78
|
+
this._firstIndex = -1;
|
|
74
79
|
this._nodes = [];
|
|
75
80
|
this._trimDivider = options.trimDivider == null ? false : options.trimDivider;
|
|
76
81
|
}
|
|
@@ -88,8 +93,10 @@ export class InfiniteRepeat implements Pattern {
|
|
|
88
93
|
return ast?.value === text;
|
|
89
94
|
}
|
|
90
95
|
|
|
91
|
-
exec(text: string) {
|
|
96
|
+
exec(text: string, record = false): ParseResult {
|
|
92
97
|
const cursor = new Cursor(text);
|
|
98
|
+
record && cursor.startRecording();
|
|
99
|
+
|
|
93
100
|
const ast = this.parse(cursor);
|
|
94
101
|
|
|
95
102
|
return {
|
|
@@ -99,6 +106,8 @@ export class InfiniteRepeat implements Pattern {
|
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
parse(cursor: Cursor): Node | null {
|
|
109
|
+
cursor.startParseWith(this);
|
|
110
|
+
|
|
102
111
|
this._firstIndex = cursor.index;
|
|
103
112
|
this._nodes = [];
|
|
104
113
|
|
|
@@ -113,14 +122,17 @@ export class InfiniteRepeat implements Pattern {
|
|
|
113
122
|
cursor.recordMatch(this, node);
|
|
114
123
|
}
|
|
115
124
|
|
|
125
|
+
cursor.endParse();
|
|
116
126
|
return node;
|
|
117
127
|
}
|
|
118
128
|
|
|
119
129
|
if (this._min > 0) {
|
|
130
|
+
cursor.endParse();
|
|
120
131
|
return null;
|
|
121
132
|
}
|
|
122
133
|
|
|
123
134
|
cursor.resolveError();
|
|
135
|
+
cursor.endParse();
|
|
124
136
|
return null;
|
|
125
137
|
}
|
|
126
138
|
|
|
@@ -137,9 +149,14 @@ export class InfiniteRepeat implements Pattern {
|
|
|
137
149
|
|
|
138
150
|
while (true) {
|
|
139
151
|
const runningCursorIndex = cursor.index;
|
|
140
|
-
const
|
|
152
|
+
const repeatNode = this._pattern.parse(cursor);
|
|
153
|
+
|
|
154
|
+
const hasError = cursor.hasError;
|
|
155
|
+
const hasNoErrorAndNoResult = !cursor.hasError && repeatNode == null;
|
|
156
|
+
const hasDivider = this._divider != null;
|
|
157
|
+
const hasNoDivider = !hasDivider;
|
|
141
158
|
|
|
142
|
-
if (
|
|
159
|
+
if (hasError) {
|
|
143
160
|
const lastValidNode = this._getLastValidNode();
|
|
144
161
|
|
|
145
162
|
if (lastValidNode != null) {
|
|
@@ -152,8 +169,13 @@ export class InfiniteRepeat implements Pattern {
|
|
|
152
169
|
|
|
153
170
|
break;
|
|
154
171
|
} else {
|
|
155
|
-
if (
|
|
156
|
-
|
|
172
|
+
if (hasNoErrorAndNoResult && hasNoDivider) {
|
|
173
|
+
// If we didn't match and didn't error we need to get out. Nothing different will happen.
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (repeatNode != null) {
|
|
178
|
+
this._nodes.push(repeatNode);
|
|
157
179
|
|
|
158
180
|
if (!cursor.hasNext()) {
|
|
159
181
|
passed = true;
|
|
@@ -164,20 +186,31 @@ export class InfiniteRepeat implements Pattern {
|
|
|
164
186
|
}
|
|
165
187
|
|
|
166
188
|
if (this._divider != null) {
|
|
189
|
+
const dividerStartIndex = cursor.index;
|
|
167
190
|
const dividerNode = this._divider.parse(cursor);
|
|
168
191
|
|
|
169
192
|
if (cursor.hasError) {
|
|
170
193
|
passed = true;
|
|
171
194
|
break;
|
|
172
|
-
} else
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
195
|
+
} else {
|
|
196
|
+
if (dividerNode == null) {
|
|
197
|
+
cursor.moveTo(dividerStartIndex);
|
|
198
|
+
|
|
199
|
+
if (dividerNode == null && repeatNode == null) {
|
|
200
|
+
// If neither the repeat pattern or divider pattern matched get out.
|
|
201
|
+
passed = true;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
this._nodes.push(dividerNode);
|
|
206
|
+
|
|
207
|
+
if (!cursor.hasNext()) {
|
|
208
|
+
passed = true;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
cursor.next();
|
|
178
213
|
}
|
|
179
|
-
|
|
180
|
-
cursor.next();
|
|
181
214
|
}
|
|
182
215
|
}
|
|
183
216
|
}
|
|
@@ -208,10 +241,10 @@ export class InfiniteRepeat implements Pattern {
|
|
|
208
241
|
cursor.moveTo(dividerNode.firstIndex);
|
|
209
242
|
}
|
|
210
243
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
244
|
+
if (this._nodes.length === 0) {
|
|
245
|
+
cursor.moveTo(this._firstIndex);
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
215
248
|
|
|
216
249
|
const lastIndex = this._nodes[this._nodes.length - 1].lastIndex;
|
|
217
250
|
cursor.moveTo(lastIndex);
|
|
@@ -250,7 +283,7 @@ export class InfiniteRepeat implements Pattern {
|
|
|
250
283
|
|
|
251
284
|
getNextTokens(): string[] {
|
|
252
285
|
if (this._parent == null) {
|
|
253
|
-
return []
|
|
286
|
+
return [];
|
|
254
287
|
}
|
|
255
288
|
|
|
256
289
|
return this._parent.getTokensAfter(this);
|
|
@@ -303,25 +336,17 @@ export class InfiniteRepeat implements Pattern {
|
|
|
303
336
|
return [];
|
|
304
337
|
}
|
|
305
338
|
|
|
306
|
-
return this._parent.getPatternsAfter(this)
|
|
339
|
+
return this._parent.getPatternsAfter(this);
|
|
307
340
|
}
|
|
308
341
|
|
|
309
342
|
find(predicate: (p: Pattern) => boolean): Pattern | null {
|
|
310
343
|
return findPattern(this, predicate);
|
|
311
344
|
}
|
|
312
345
|
|
|
313
|
-
clone(name = this._name
|
|
346
|
+
clone(name = this._name): Pattern {
|
|
314
347
|
let min = this._min;
|
|
315
348
|
|
|
316
|
-
|
|
317
|
-
if (isOptional) {
|
|
318
|
-
min = 0
|
|
319
|
-
} else {
|
|
320
|
-
min = Math.max(this._min, 1);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return new InfiniteRepeat(
|
|
349
|
+
const clone = new InfiniteRepeat(
|
|
325
350
|
name,
|
|
326
351
|
this._pattern,
|
|
327
352
|
{
|
|
@@ -330,6 +355,14 @@ export class InfiniteRepeat implements Pattern {
|
|
|
330
355
|
trimDivider: this._trimDivider
|
|
331
356
|
}
|
|
332
357
|
);
|
|
358
|
+
|
|
359
|
+
clone._id = this._id;
|
|
360
|
+
|
|
361
|
+
return clone;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
isEqual(pattern: InfiniteRepeat): boolean {
|
|
365
|
+
return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index]));
|
|
333
366
|
}
|
|
334
367
|
}
|
|
335
368
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Node } from "../ast/Node";
|
|
2
|
-
import {
|
|
2
|
+
import { Sequence } from "./Sequence";
|
|
3
3
|
import { Cursor } from "./Cursor";
|
|
4
4
|
import { Literal } from "./Literal"
|
|
5
5
|
|
|
6
6
|
describe("Literal", () => {
|
|
7
7
|
test("Empty Value", () => {
|
|
8
8
|
expect(() => {
|
|
9
|
-
new Literal("empty", "")
|
|
10
|
-
}).toThrowError()
|
|
9
|
+
new Literal("empty", "");
|
|
10
|
+
}).toThrowError();
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
test("Successful Parse", () => {
|
|
@@ -20,10 +20,10 @@ describe("Literal", () => {
|
|
|
20
20
|
expect(result).toEqual(expected);
|
|
21
21
|
expect(cursor.index).toBe(11);
|
|
22
22
|
expect(cursor.error).toBeNull();
|
|
23
|
-
expect(cursor.leafMatch.node).toEqual(expected)
|
|
24
|
-
expect(cursor.leafMatch.pattern).toBe(literal)
|
|
25
|
-
expect(cursor.rootMatch.node).toEqual(expected)
|
|
26
|
-
expect(cursor.rootMatch.pattern).toBe(literal)
|
|
23
|
+
expect(cursor.leafMatch.node).toEqual(expected);
|
|
24
|
+
expect(cursor.leafMatch.pattern).toBe(literal);
|
|
25
|
+
expect(cursor.rootMatch.node).toEqual(expected);
|
|
26
|
+
expect(cursor.rootMatch.pattern).toBe(literal);
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
test("Failed Parse", () => {
|
|
@@ -36,7 +36,7 @@ describe("Literal", () => {
|
|
|
36
36
|
expect(cursor.index).toBe(6);
|
|
37
37
|
expect(cursor.error?.startIndex).toBe(0);
|
|
38
38
|
expect(cursor.error?.endIndex).toBe(6);
|
|
39
|
-
expect(cursor.error?.pattern).toBe(literal)
|
|
39
|
+
expect(cursor.error?.pattern).toBe(literal);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
test("Failed Parse Because End Of Text", () => {
|
|
@@ -49,18 +49,7 @@ describe("Literal", () => {
|
|
|
49
49
|
expect(cursor.index).toBe(10);
|
|
50
50
|
expect(cursor.error?.startIndex).toBe(0);
|
|
51
51
|
expect(cursor.error?.endIndex).toBe(11);
|
|
52
|
-
expect(cursor.error?.pattern).toBe(literal)
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test("Failed Parse (Optional)", () => {
|
|
56
|
-
const literal = new Literal("greeting", "Hello World!", true);
|
|
57
|
-
|
|
58
|
-
const cursor = new Cursor("Hello Saturn!");
|
|
59
|
-
const result = literal.parse(cursor);
|
|
60
|
-
|
|
61
|
-
expect(result).toEqual(null);
|
|
62
|
-
expect(cursor.index).toBe(0);
|
|
63
|
-
expect(cursor.error).toBeNull();
|
|
52
|
+
expect(cursor.error?.pattern).toBe(literal);
|
|
64
53
|
});
|
|
65
54
|
|
|
66
55
|
test("Clone", () => {
|
|
@@ -85,7 +74,7 @@ describe("Literal", () => {
|
|
|
85
74
|
const tokens = literal.getTokensAfter(new Literal("bogus", "bogus"));
|
|
86
75
|
const expected: string[] = [];
|
|
87
76
|
|
|
88
|
-
expect(tokens).toEqual(expected)
|
|
77
|
+
expect(tokens).toEqual(expected);
|
|
89
78
|
});
|
|
90
79
|
|
|
91
80
|
test("Properties", () => {
|
|
@@ -100,7 +89,7 @@ describe("Literal", () => {
|
|
|
100
89
|
test("Exec", () => {
|
|
101
90
|
const literal = new Literal("a", "A");
|
|
102
91
|
const { ast: result } = literal.exec("B");
|
|
103
|
-
expect(result).toBeNull()
|
|
92
|
+
expect(result).toBeNull();
|
|
104
93
|
});
|
|
105
94
|
|
|
106
95
|
test("Test With No Match", () => {
|
|
@@ -118,8 +107,8 @@ describe("Literal", () => {
|
|
|
118
107
|
});
|
|
119
108
|
|
|
120
109
|
test("Get Next Tokens", () => {
|
|
121
|
-
const sequence = new
|
|
122
|
-
const parent = new
|
|
110
|
+
const sequence = new Sequence("sequence", [new Literal("a", "A")]);
|
|
111
|
+
const parent = new Sequence("parent", [sequence, new Literal("b", "B")]);
|
|
123
112
|
|
|
124
113
|
const a = parent.find(p => p.name === "a");
|
|
125
114
|
const tokens = a?.getNextTokens() || [];
|
|
@@ -144,12 +133,12 @@ describe("Literal", () => {
|
|
|
144
133
|
});
|
|
145
134
|
|
|
146
135
|
test("Get Next Patterns", () => {
|
|
147
|
-
const sequence = new
|
|
148
|
-
const parent = new
|
|
136
|
+
const sequence = new Sequence("sequence", [new Literal("a", "A")]);
|
|
137
|
+
const parent = new Sequence("parent", [sequence, new Literal("b", "B")]);
|
|
149
138
|
|
|
150
139
|
const a = parent.find(p => p.name === "a");
|
|
151
140
|
const nextPatterns = a?.getNextPatterns() || [];
|
|
152
|
-
const b = parent.find(p => p.name === "b")
|
|
141
|
+
const b = parent.find(p => p.name === "b");
|
|
153
142
|
|
|
154
143
|
expect(nextPatterns[0]).toBe(b);
|
|
155
144
|
});
|