clarity-pattern-parser 6.0.0 → 7.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 +1 -76
- package/dist/ast/Node.d.ts +1 -0
- package/dist/grammar/Grammar.d.ts +17 -0
- package/dist/grammar/patterns/andLiteral.d.ts +2 -0
- package/dist/grammar/patterns/comment.d.ts +2 -0
- package/dist/grammar/patterns/grammar.d.ts +2 -0
- package/dist/grammar/patterns/literal.d.ts +2 -0
- package/dist/grammar/patterns/name.d.ts +2 -0
- package/dist/grammar/patterns/orLiteral.d.ts +2 -0
- package/dist/grammar/patterns/pattern.d.ts +2 -0
- package/dist/grammar/patterns/regexLiteral.d.ts +2 -0
- package/dist/grammar/patterns/repeatLiteral.d.ts +3 -0
- package/dist/grammar/patterns/spaces.d.ts +2 -0
- package/dist/grammar/patterns/statement.d.ts +2 -0
- package/dist/index.browser.js +1205 -550
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.esm.js +1203 -549
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1203 -548
- package/dist/index.js.map +1 -1
- package/dist/intellisense/AutoComplete.d.ts +9 -7
- package/dist/intellisense/Suggestion.d.ts +1 -2
- package/dist/patterns/And.d.ts +2 -1
- package/dist/patterns/Cursor.d.ts +1 -0
- package/dist/patterns/CursorHistory.d.ts +2 -1
- package/dist/patterns/FiniteRepeat.d.ts +39 -0
- package/dist/patterns/InfiniteRepeat.d.ts +47 -0
- package/dist/patterns/Literal.d.ts +2 -1
- package/dist/patterns/Not.d.ts +2 -1
- package/dist/patterns/Or.d.ts +2 -1
- package/dist/patterns/Pattern.d.ts +3 -2
- package/dist/patterns/Reference.d.ts +2 -1
- package/dist/patterns/Regex.d.ts +2 -1
- package/dist/patterns/Repeat.d.ts +19 -22
- package/jest.config.js +0 -1
- package/jest.coverage.config.js +13 -0
- package/package.json +3 -3
- package/src/ast/Node.test.ts +21 -0
- package/src/ast/Node.ts +12 -6
- package/src/grammar/Grammar.test.ts +288 -0
- package/src/grammar/Grammar.ts +234 -0
- package/src/grammar/patterns/andLiteral.ts +8 -0
- package/src/grammar/patterns/comment.ts +3 -0
- package/src/grammar/patterns/grammar.ts +19 -0
- package/src/grammar/patterns/literal.ts +5 -0
- package/src/grammar/patterns/name.ts +3 -0
- package/src/grammar/patterns/orLiteral.ts +8 -0
- package/src/grammar/patterns/pattern.ts +13 -0
- package/src/grammar/patterns/regexLiteral.ts +4 -0
- package/src/grammar/patterns/repeatLiteral.ts +72 -0
- package/src/grammar/patterns/spaces.ts +4 -0
- package/src/grammar/patterns/statement.ts +35 -0
- package/src/grammar/spec.md +142 -0
- package/src/index.ts +6 -3
- package/src/intellisense/AutoComplete.test.ts +125 -39
- package/src/intellisense/AutoComplete.ts +52 -36
- package/src/intellisense/Suggestion.ts +1 -2
- package/src/intellisense/css/cssValue.ts +1 -1
- package/src/intellisense/css/method.ts +1 -1
- package/src/intellisense/css/values.ts +1 -1
- package/src/intellisense/javascript/Javascript.test.ts +34 -11
- package/src/intellisense/javascript/arrayLiteral.ts +1 -1
- package/src/intellisense/javascript/{expressionStatement.ts → assignment.ts} +7 -8
- package/src/intellisense/javascript/expression.ts +45 -27
- package/src/intellisense/javascript/infixOperator.ts +6 -2
- package/src/intellisense/javascript/invocation.ts +1 -1
- package/src/intellisense/javascript/keywords.ts +3 -0
- package/src/intellisense/javascript/objectAccess.ts +9 -0
- package/src/intellisense/javascript/objectLiteral.ts +3 -3
- package/src/intellisense/javascript/parameters.ts +1 -1
- package/src/intellisense/javascript/propertyAccess.ts +8 -3
- package/src/intellisense/javascript/stringLiteral.ts +14 -8
- package/src/patterns/And.test.ts +16 -3
- package/src/patterns/And.ts +25 -17
- package/src/patterns/Cursor.ts +4 -0
- package/src/patterns/CursorHistory.ts +34 -5
- package/src/patterns/FiniteRepeat.test.ts +481 -0
- package/src/patterns/FiniteRepeat.ts +231 -0
- package/src/patterns/InfiniteRepeat.test.ts +296 -0
- package/src/patterns/InfiniteRepeat.ts +329 -0
- package/src/patterns/Literal.test.ts +13 -4
- package/src/patterns/Literal.ts +5 -1
- package/src/patterns/Not.test.ts +20 -9
- package/src/patterns/Not.ts +5 -1
- package/src/patterns/Or.test.ts +18 -7
- package/src/patterns/Or.ts +11 -1
- package/src/patterns/Pattern.ts +3 -2
- package/src/patterns/Reference.test.ts +18 -8
- package/src/patterns/Reference.ts +5 -1
- package/src/patterns/Regex.test.ts +13 -4
- package/src/patterns/Regex.ts +5 -1
- package/src/patterns/Repeat.test.ts +162 -158
- package/src/patterns/Repeat.ts +95 -226
package/src/patterns/And.ts
CHANGED
|
@@ -234,11 +234,25 @@ export class And implements Pattern {
|
|
|
234
234
|
return this.parent.getTokensAfter(this);
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
getPatterns(): Pattern[] {
|
|
238
|
+
const patterns: Pattern[] = [];
|
|
239
|
+
|
|
240
|
+
for (const pattern of this._children) {
|
|
241
|
+
patterns.push(...pattern.getPatterns());
|
|
242
|
+
|
|
243
|
+
if (!pattern.isOptional) {
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return patterns;
|
|
249
|
+
}
|
|
250
|
+
|
|
237
251
|
getPatternsAfter(childReference: Pattern): Pattern[] {
|
|
252
|
+
const patterns: Pattern[] = [];
|
|
238
253
|
let nextSibling: Pattern | null = null;
|
|
239
254
|
let nextSiblingIndex = -1;
|
|
240
255
|
let index = -1;
|
|
241
|
-
const patterns: Pattern[] = [];
|
|
242
256
|
|
|
243
257
|
for (let i = 0; i < this._children.length; i++) {
|
|
244
258
|
if (this._children[i] === childReference) {
|
|
@@ -261,24 +275,18 @@ export class And implements Pattern {
|
|
|
261
275
|
return this._parent.getPatternsAfter(this);
|
|
262
276
|
}
|
|
263
277
|
|
|
264
|
-
// Next pattern isn't optional so send it back as the next patterns.
|
|
265
|
-
if (nextSibling !== null && !nextSibling.isOptional) {
|
|
266
|
-
return [nextSibling];
|
|
267
|
-
}
|
|
268
|
-
|
|
269
278
|
// Send back as many optional patterns as possible.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
patterns.push(child);
|
|
279
|
+
for (let i = nextSiblingIndex; i < this._children.length; i++) {
|
|
280
|
+
const child = this._children[i];
|
|
281
|
+
patterns.push(child);
|
|
274
282
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
283
|
+
if (!child.isOptional) {
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
278
286
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
287
|
+
// If we are on the last child and its options then ask for the next pattern from the parent.
|
|
288
|
+
if (i === this._children.length - 1 && this._parent !== null) {
|
|
289
|
+
patterns.push(...this._parent.getPatternsAfter(this));
|
|
282
290
|
}
|
|
283
291
|
}
|
|
284
292
|
|
|
@@ -293,7 +301,7 @@ export class And implements Pattern {
|
|
|
293
301
|
return this.parent.getPatternsAfter(this)
|
|
294
302
|
}
|
|
295
303
|
|
|
296
|
-
|
|
304
|
+
find(predicate: (p: Pattern) => boolean): Pattern | null {
|
|
297
305
|
return findPattern(this, predicate);
|
|
298
306
|
}
|
|
299
307
|
|
package/src/patterns/Cursor.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface Match {
|
|
|
9
9
|
|
|
10
10
|
export class CursorHistory {
|
|
11
11
|
private _isRecording: boolean = false;
|
|
12
|
-
private
|
|
12
|
+
private _leafMatches: Match[] = [{ pattern: null, node: null }];
|
|
13
13
|
private _furthestError: ParseError | null = null;
|
|
14
14
|
private _currentError: ParseError | null = null;
|
|
15
15
|
private _rootMatch: Match = { pattern: null, node: null };
|
|
@@ -26,7 +26,11 @@ export class CursorHistory {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
get leafMatch(): Match {
|
|
29
|
-
return this.
|
|
29
|
+
return this._leafMatches[this._leafMatches.length - 1];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get leafMatches() {
|
|
33
|
+
return this._leafMatches;
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
get furthestError(): ParseError | null {
|
|
@@ -57,13 +61,38 @@ export class CursorHistory {
|
|
|
57
61
|
|
|
58
62
|
this._rootMatch.pattern = pattern;
|
|
59
63
|
this._rootMatch.node = node;
|
|
64
|
+
const leafMatch = this._leafMatches[this._leafMatches.length - 1];
|
|
60
65
|
|
|
61
66
|
const isFurthestMatch =
|
|
62
|
-
|
|
67
|
+
leafMatch.node === null || node.lastIndex > leafMatch.node.lastIndex;
|
|
68
|
+
|
|
69
|
+
const isSameIndexMatch =
|
|
70
|
+
leafMatch.node === null || node.lastIndex === leafMatch.node.lastIndex
|
|
63
71
|
|
|
64
72
|
if (isFurthestMatch) {
|
|
65
|
-
|
|
66
|
-
this.
|
|
73
|
+
// This is to save on GC churn.
|
|
74
|
+
const match = this._leafMatches.pop() as Match;
|
|
75
|
+
match.pattern = pattern;
|
|
76
|
+
match.node = node;
|
|
77
|
+
|
|
78
|
+
this._leafMatches.length = 0;
|
|
79
|
+
this._leafMatches.push(match);
|
|
80
|
+
} else if (isSameIndexMatch) {
|
|
81
|
+
const isAncestor = this._leafMatches.some((m) => {
|
|
82
|
+
let parent = m.pattern?.parent;
|
|
83
|
+
|
|
84
|
+
while (parent != null) {
|
|
85
|
+
if (parent == pattern.parent) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
parent = parent.parent
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
if (!isAncestor) {
|
|
94
|
+
this._leafMatches.unshift({ pattern, node });
|
|
95
|
+
}
|
|
67
96
|
}
|
|
68
97
|
}
|
|
69
98
|
|
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import { FiniteRepeat } from "./FiniteRepeat";
|
|
2
|
+
import { Cursor } from "./Cursor";
|
|
3
|
+
import { Regex } from "./Regex";
|
|
4
|
+
import { Node } from "../ast/Node";
|
|
5
|
+
import { Literal } from "./Literal";
|
|
6
|
+
import { And } from "./And";
|
|
7
|
+
|
|
8
|
+
describe("BoundedRepeat", () => {
|
|
9
|
+
test("Bounds Without Divider", () => {
|
|
10
|
+
const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, {
|
|
11
|
+
min: 2
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
let cursor = new Cursor("1");
|
|
15
|
+
let result = numbers.parse(cursor);
|
|
16
|
+
let expected: Node | null = null;
|
|
17
|
+
expect(result).toBeNull();
|
|
18
|
+
expect(cursor.hasError).toBeTruthy();
|
|
19
|
+
|
|
20
|
+
cursor = new Cursor("12");
|
|
21
|
+
result = numbers.parse(cursor);
|
|
22
|
+
expected = new Node("finite-repeat", "numbers", 0, 1, [
|
|
23
|
+
new Node("regex", "number", 0, 0, [], "1"),
|
|
24
|
+
new Node("regex", "number", 1, 1, [], "2"),
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
expect(result).toEqual(expected);
|
|
28
|
+
expect(cursor.hasError).toBeFalsy();
|
|
29
|
+
|
|
30
|
+
cursor = new Cursor("123");
|
|
31
|
+
result = numbers.parse(cursor);
|
|
32
|
+
expected = new Node("finite-repeat", "numbers", 0, 2, [
|
|
33
|
+
new Node("regex", "number", 0, 0, [], "1"),
|
|
34
|
+
new Node("regex", "number", 1, 1, [], "2"),
|
|
35
|
+
new Node("regex", "number", 2, 2, [], "3"),
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
expect(result).toEqual(expected);
|
|
39
|
+
expect(cursor.hasError).toBeFalsy();
|
|
40
|
+
|
|
41
|
+
cursor = new Cursor("1234");
|
|
42
|
+
result = numbers.parse(cursor);
|
|
43
|
+
expected = new Node("finite-repeat", "numbers", 0, 2, [
|
|
44
|
+
new Node("regex", "number", 0, 0, [], "1"),
|
|
45
|
+
new Node("regex", "number", 1, 1, [], "2"),
|
|
46
|
+
new Node("regex", "number", 2, 2, [], "3"),
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
expect(result).toEqual(expected);
|
|
50
|
+
expect(cursor.hasError).toBeFalsy();
|
|
51
|
+
expect(cursor.index).toBe(2);
|
|
52
|
+
|
|
53
|
+
cursor = new Cursor("12f");
|
|
54
|
+
result = numbers.parse(cursor);
|
|
55
|
+
expected = new Node("finite-repeat", "numbers", 0, 1, [
|
|
56
|
+
new Node("regex", "number", 0, 0, [], "1"),
|
|
57
|
+
new Node("regex", "number", 1, 1, [], "2"),
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
expect(result).toEqual(expected);
|
|
61
|
+
expect(cursor.hasError).toBeFalsy();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("Bounds Are Equal Without Divider", () => {
|
|
65
|
+
const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 3});
|
|
66
|
+
|
|
67
|
+
let cursor = new Cursor("1");
|
|
68
|
+
let result = numbers.parse(cursor);
|
|
69
|
+
let expected: Node | null = null;
|
|
70
|
+
expect(result).toBeNull();
|
|
71
|
+
expect(cursor.hasError).toBeTruthy();
|
|
72
|
+
|
|
73
|
+
cursor = new Cursor("12");
|
|
74
|
+
result = numbers.parse(cursor);
|
|
75
|
+
expected = null;
|
|
76
|
+
|
|
77
|
+
expect(result).toBeNull();
|
|
78
|
+
expect(cursor.hasError).toBeTruthy();
|
|
79
|
+
|
|
80
|
+
cursor = new Cursor("123");
|
|
81
|
+
result = numbers.parse(cursor);
|
|
82
|
+
expected = new Node("finite-repeat", "numbers", 0, 2, [
|
|
83
|
+
new Node("regex", "number", 0, 0, [], "1"),
|
|
84
|
+
new Node("regex", "number", 1, 1, [], "2"),
|
|
85
|
+
new Node("regex", "number", 2, 2, [], "3"),
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
expect(result).toEqual(expected);
|
|
89
|
+
expect(cursor.hasError).toBeFalsy();
|
|
90
|
+
|
|
91
|
+
cursor = new Cursor("1234");
|
|
92
|
+
result = numbers.parse(cursor);
|
|
93
|
+
expected = new Node("finite-repeat", "numbers", 0, 2, [
|
|
94
|
+
new Node("regex", "number", 0, 0, [], "1"),
|
|
95
|
+
new Node("regex", "number", 1, 1, [], "2"),
|
|
96
|
+
new Node("regex", "number", 2, 2, [], "3"),
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
expect(result).toEqual(expected);
|
|
100
|
+
expect(cursor.hasError).toBeFalsy();
|
|
101
|
+
expect(cursor.index).toBe(2);
|
|
102
|
+
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("Bounds With Divider", () => {
|
|
106
|
+
const divider = new Literal("comma", ",");
|
|
107
|
+
const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { divider, min: 2, trimDivider: true });
|
|
108
|
+
|
|
109
|
+
let cursor = new Cursor("1,");
|
|
110
|
+
let result = numbers.parse(cursor);
|
|
111
|
+
let expected: Node | null = null;
|
|
112
|
+
expect(result).toBeNull();
|
|
113
|
+
expect(cursor.hasError).toBeTruthy();
|
|
114
|
+
|
|
115
|
+
cursor = new Cursor("1,2");
|
|
116
|
+
result = numbers.parse(cursor);
|
|
117
|
+
expected = new Node("finite-repeat", "numbers", 0, 2, [
|
|
118
|
+
new Node("regex", "number", 0, 0, [], "1"),
|
|
119
|
+
new Node("literal", "comma", 1, 1, [], ","),
|
|
120
|
+
new Node("regex", "number", 2, 2, [], "2"),
|
|
121
|
+
]);
|
|
122
|
+
|
|
123
|
+
expect(result).toEqual(expected);
|
|
124
|
+
expect(cursor.hasError).toBeFalsy();
|
|
125
|
+
|
|
126
|
+
cursor = new Cursor("1,2,3,");
|
|
127
|
+
result = numbers.parse(cursor);
|
|
128
|
+
expected = new Node("finite-repeat", "numbers", 0, 4, [
|
|
129
|
+
new Node("regex", "number", 0, 0, [], "1"),
|
|
130
|
+
new Node("literal", "comma", 1, 1, [], ","),
|
|
131
|
+
new Node("regex", "number", 2, 2, [], "2"),
|
|
132
|
+
new Node("literal", "comma", 3, 3, [], ","),
|
|
133
|
+
new Node("regex", "number", 4, 4, [], "3"),
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
expect(result).toEqual(expected);
|
|
137
|
+
expect(cursor.hasError).toBeFalsy();
|
|
138
|
+
|
|
139
|
+
cursor = new Cursor("1,2,3,4");
|
|
140
|
+
result = numbers.parse(cursor);
|
|
141
|
+
expected = new Node("finite-repeat", "numbers", 0, 4, [
|
|
142
|
+
new Node("regex", "number", 0, 0, [], "1"),
|
|
143
|
+
new Node("literal", "comma", 1, 1, [], ","),
|
|
144
|
+
new Node("regex", "number", 2, 2, [], "2"),
|
|
145
|
+
new Node("literal", "comma", 3, 3, [], ","),
|
|
146
|
+
new Node("regex", "number", 4, 4, [], "3"),
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
expect(result).toEqual(expected);
|
|
150
|
+
expect(cursor.hasError).toBeFalsy();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("Optional Repeating Pattern", () => {
|
|
154
|
+
const digit = new Regex("digit", "\\d+", true);
|
|
155
|
+
const divider = new Regex("divider", "\\s");
|
|
156
|
+
const integer = new FiniteRepeat("number", digit, 4, { divider });
|
|
157
|
+
const cursor = new Cursor(
|
|
158
|
+
"\n" +
|
|
159
|
+
"3\n" +
|
|
160
|
+
"\n" +
|
|
161
|
+
"\n"
|
|
162
|
+
);
|
|
163
|
+
const result = integer.parse(cursor);
|
|
164
|
+
const expected = new Node("finite-repeat", "number", 0, 4, [
|
|
165
|
+
new Node("regex", "divider", 0, 0, [], "\n"),
|
|
166
|
+
new Node("regex", "digit", 1, 1, [], "3"),
|
|
167
|
+
new Node("regex", "divider", 2, 2, [], "\n"),
|
|
168
|
+
new Node("regex", "divider", 3, 3, [], "\n"),
|
|
169
|
+
new Node("regex", "divider", 4, 4, [], "\n"),
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
expect(result).toEqual(expected)
|
|
173
|
+
expect(cursor.hasError).toBeFalsy()
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("Bounds Are Equal With Divider", () => {
|
|
177
|
+
const divider = new Literal("comma", ",");
|
|
178
|
+
const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { divider, min: 3, trimDivider: true });
|
|
179
|
+
|
|
180
|
+
let cursor = new Cursor("1,");
|
|
181
|
+
let result = numbers.parse(cursor);
|
|
182
|
+
let expected: Node | null = null;
|
|
183
|
+
expect(result).toBeNull();
|
|
184
|
+
expect(cursor.hasError).toBeTruthy();
|
|
185
|
+
|
|
186
|
+
cursor = new Cursor("1,2");
|
|
187
|
+
result = numbers.parse(cursor);
|
|
188
|
+
expected = null;
|
|
189
|
+
|
|
190
|
+
expect(result).toEqual(expected);
|
|
191
|
+
expect(cursor.hasError).toBeTruthy();
|
|
192
|
+
|
|
193
|
+
cursor = new Cursor("1,2,3,");
|
|
194
|
+
result = numbers.parse(cursor);
|
|
195
|
+
expected = new Node("finite-repeat", "numbers", 0, 4, [
|
|
196
|
+
new Node("regex", "number", 0, 0, [], "1"),
|
|
197
|
+
new Node("literal", "comma", 1, 1, [], ","),
|
|
198
|
+
new Node("regex", "number", 2, 2, [], "2"),
|
|
199
|
+
new Node("literal", "comma", 3, 3, [], ","),
|
|
200
|
+
new Node("regex", "number", 4, 4, [], "3"),
|
|
201
|
+
]);
|
|
202
|
+
|
|
203
|
+
expect(result).toEqual(expected);
|
|
204
|
+
expect(cursor.hasError).toBeFalsy();
|
|
205
|
+
|
|
206
|
+
cursor = new Cursor("1,2,3,4");
|
|
207
|
+
result = numbers.parse(cursor);
|
|
208
|
+
expected = new Node("finite-repeat", "numbers", 0, 4, [
|
|
209
|
+
new Node("regex", "number", 0, 0, [], "1"),
|
|
210
|
+
new Node("literal", "comma", 1, 1, [], ","),
|
|
211
|
+
new Node("regex", "number", 2, 2, [], "2"),
|
|
212
|
+
new Node("literal", "comma", 3, 3, [], ","),
|
|
213
|
+
new Node("regex", "number", 4, 4, [], "3"),
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
expect(result).toEqual(expected);
|
|
217
|
+
expect(cursor.hasError).toBeFalsy();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("Test", () => {
|
|
221
|
+
const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3);
|
|
222
|
+
const result = numbers.test("1");
|
|
223
|
+
|
|
224
|
+
expect(result).toBeTruthy();
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("Exec", () => {
|
|
228
|
+
const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3);
|
|
229
|
+
const result = numbers.exec("1");
|
|
230
|
+
|
|
231
|
+
expect(result.ast).not.toBeNull();
|
|
232
|
+
expect(result.cursor.hasError).toBeFalsy();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("Fail", () => {
|
|
236
|
+
const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3);
|
|
237
|
+
const result = numbers.exec("f");
|
|
238
|
+
|
|
239
|
+
expect(result.ast).toBeNull();
|
|
240
|
+
expect(result.cursor.hasError).toBeTruthy();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("Optional", () => {
|
|
244
|
+
const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
|
|
245
|
+
const result = numbers.exec("f");
|
|
246
|
+
|
|
247
|
+
expect(result.ast).toBeNull();
|
|
248
|
+
expect(result.cursor.hasError).toBeFalsy();
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("Optional With Multiple Matches But Still Below Min", () => {
|
|
252
|
+
const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
|
|
253
|
+
const result = numbers.exec("12f");
|
|
254
|
+
|
|
255
|
+
expect(result.ast).toBeNull();
|
|
256
|
+
expect(result.cursor.hasError).toBeFalsy();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("Properties", () => {
|
|
260
|
+
const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
|
|
261
|
+
|
|
262
|
+
expect(numbers.type).toBe("finite-repeat");
|
|
263
|
+
expect(numbers.name).toBe("numbers");
|
|
264
|
+
expect(numbers.parent).toBeNull();
|
|
265
|
+
expect(numbers.children.length).toBe(3);
|
|
266
|
+
expect(numbers.min).toBe(0);
|
|
267
|
+
expect(numbers.max).toBe(3);
|
|
268
|
+
expect(numbers.isOptional).toBeTruthy();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("Clone", () => {
|
|
272
|
+
const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
|
|
273
|
+
const clone = numbers.clone() as FiniteRepeat;
|
|
274
|
+
|
|
275
|
+
expect(clone.type).toBe(numbers.type);
|
|
276
|
+
expect(clone.name).toBe(numbers.name);
|
|
277
|
+
expect(clone.parent).toBeNull();
|
|
278
|
+
expect(clone.children.length).toBe(numbers.children.length);
|
|
279
|
+
expect(clone.min).toBe(numbers.min);
|
|
280
|
+
expect(clone.max).toBe(numbers.max);
|
|
281
|
+
expect(clone.isOptional).toBe(numbers.isOptional);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test("Clone With Custom Overrides", () => {
|
|
285
|
+
const numbers = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
|
|
286
|
+
let clone = numbers.clone();
|
|
287
|
+
let expected = new FiniteRepeat("numbers", new Regex("number", "\\d"), 3, { min: 0 });
|
|
288
|
+
|
|
289
|
+
expect(clone).toEqual(expected);
|
|
290
|
+
|
|
291
|
+
clone = numbers.clone("cloned-numbers");
|
|
292
|
+
expected = new FiniteRepeat("cloned-numbers", new Regex("number", "\\d"), 3, { min: 0 });
|
|
293
|
+
|
|
294
|
+
expect(clone).toEqual(expected);
|
|
295
|
+
|
|
296
|
+
clone = numbers.clone("cloned-numbers", true);
|
|
297
|
+
expected = new FiniteRepeat("cloned-numbers", new Regex("number", "\\d"), 3, { min: 0 });
|
|
298
|
+
|
|
299
|
+
expect(clone).toEqual(expected);
|
|
300
|
+
|
|
301
|
+
clone = numbers.clone("cloned-numbers", false);
|
|
302
|
+
expected = new FiniteRepeat("cloned-numbers", new Regex("number", "\\d"), 3, { min: 1 });
|
|
303
|
+
|
|
304
|
+
expect(clone).toEqual(expected);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("Get Tokens", () => {
|
|
308
|
+
const numbers = new FiniteRepeat(
|
|
309
|
+
"numbers",
|
|
310
|
+
new Literal("one", "1"),
|
|
311
|
+
3,
|
|
312
|
+
{
|
|
313
|
+
divider: new Literal("comma", ","),
|
|
314
|
+
min: 0
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const tokens = numbers.getTokens();
|
|
318
|
+
|
|
319
|
+
expect(tokens).toEqual(["1"]);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test("Get Tokens After Without Parent", () => {
|
|
323
|
+
const numbers = new FiniteRepeat(
|
|
324
|
+
"numbers",
|
|
325
|
+
new Literal("one", "1"),
|
|
326
|
+
2,
|
|
327
|
+
{
|
|
328
|
+
divider: new Literal("comma", ","),
|
|
329
|
+
min: 0,
|
|
330
|
+
trimDivider: true
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
let child = numbers.children[0];
|
|
334
|
+
let tokens = numbers.getTokensAfter(child);
|
|
335
|
+
|
|
336
|
+
expect(tokens).toEqual([","]);
|
|
337
|
+
|
|
338
|
+
child = numbers.children[2];
|
|
339
|
+
tokens = numbers.getTokensAfter(child);
|
|
340
|
+
|
|
341
|
+
expect(tokens).toEqual([]);
|
|
342
|
+
|
|
343
|
+
child = numbers.children[3];
|
|
344
|
+
tokens = numbers.getTokensAfter(child);
|
|
345
|
+
|
|
346
|
+
expect(tokens).toEqual([]);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("Get Tokens After With Parent", () => {
|
|
350
|
+
const numbers = new FiniteRepeat(
|
|
351
|
+
"numbers",
|
|
352
|
+
new Literal("one", "1"),
|
|
353
|
+
2,
|
|
354
|
+
{
|
|
355
|
+
divider: new Literal("comma", ","),
|
|
356
|
+
trimDivider: true,
|
|
357
|
+
min: 0
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const parent = new And("parent", [numbers, new Literal("b", "B")]);
|
|
361
|
+
const numbersClone = parent.children[0];
|
|
362
|
+
let child = numbersClone.children[0];
|
|
363
|
+
let tokens = numbersClone.getTokensAfter(child);
|
|
364
|
+
|
|
365
|
+
expect(tokens).toEqual([","]);
|
|
366
|
+
|
|
367
|
+
child = numbersClone.children[2];
|
|
368
|
+
tokens = numbersClone.getTokensAfter(child);
|
|
369
|
+
|
|
370
|
+
expect(tokens).toEqual(["B"]);
|
|
371
|
+
|
|
372
|
+
child = numbersClone.children[3];
|
|
373
|
+
tokens = numbersClone.getTokensAfter(child);
|
|
374
|
+
|
|
375
|
+
expect(tokens).toEqual([]);
|
|
376
|
+
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test("Get Next Tokens", () => {
|
|
380
|
+
const numbers = new FiniteRepeat(
|
|
381
|
+
"numbers",
|
|
382
|
+
new Literal("one", "1"),
|
|
383
|
+
2,
|
|
384
|
+
{
|
|
385
|
+
divider: new Literal("comma", ","),
|
|
386
|
+
min: 0
|
|
387
|
+
}
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
const parent = new And("parent", [numbers, new Literal("b", "B")]);
|
|
391
|
+
const numbersClone = parent.children[0];
|
|
392
|
+
const tokens = numbersClone.getNextTokens();
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
expect(tokens).toEqual(["B"]);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test("Get Next Tokens Without Parent", () => {
|
|
399
|
+
const numbers = new FiniteRepeat(
|
|
400
|
+
"numbers",
|
|
401
|
+
new Literal("one", "1"),
|
|
402
|
+
2,
|
|
403
|
+
{
|
|
404
|
+
divider: new Literal("comma", ","),
|
|
405
|
+
min: 0
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
const tokens = numbers.getNextTokens();
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
expect(tokens).toEqual([]);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test("Get Patterns", () => {
|
|
416
|
+
const numbers = new FiniteRepeat(
|
|
417
|
+
"numbers",
|
|
418
|
+
new Literal("one", "1"),
|
|
419
|
+
2,
|
|
420
|
+
{
|
|
421
|
+
divider: new Literal("comma", ","),
|
|
422
|
+
min: 0
|
|
423
|
+
}
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
const patterns = numbers.getPatterns();
|
|
427
|
+
|
|
428
|
+
expect(patterns).toEqual([numbers.children[0]]);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test("Get Next Patterns Without Parent", () => {
|
|
432
|
+
const numbers = new FiniteRepeat(
|
|
433
|
+
"numbers",
|
|
434
|
+
new Literal("one", "1"),
|
|
435
|
+
2,
|
|
436
|
+
{
|
|
437
|
+
divider: new Literal("comma", ","),
|
|
438
|
+
min: 0
|
|
439
|
+
}
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
const patterns = numbers.getNextPatterns();
|
|
443
|
+
|
|
444
|
+
expect(patterns).toEqual([]);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
test("Get Next Patterns With Parent", () => {
|
|
448
|
+
const numbers = new FiniteRepeat(
|
|
449
|
+
"numbers",
|
|
450
|
+
new Literal("one", "1"),
|
|
451
|
+
2,
|
|
452
|
+
{
|
|
453
|
+
divider: new Literal("comma", ","),
|
|
454
|
+
min: 0
|
|
455
|
+
}
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
const parent = new And("parent", [numbers, new Literal("b", "B")]);
|
|
459
|
+
const numbersClone = parent.children[0];
|
|
460
|
+
|
|
461
|
+
const patterns = numbersClone.getNextPatterns();
|
|
462
|
+
|
|
463
|
+
expect(patterns).toEqual([parent.children[1]]);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
test("Find Pattern", () => {
|
|
467
|
+
const numbers = new FiniteRepeat(
|
|
468
|
+
"numbers",
|
|
469
|
+
new Literal("one", "1"),
|
|
470
|
+
2,
|
|
471
|
+
{
|
|
472
|
+
divider: new Literal("comma", ","),
|
|
473
|
+
min: 0
|
|
474
|
+
}
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
const comma = numbers.find(p => p.name === "comma");
|
|
478
|
+
expect(comma).toBe(numbers.children[1]);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
});
|