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
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { Cursor } from "./Cursor";
|
|
3
|
+
import { Pattern } from "./Pattern";
|
|
4
|
+
import { clonePatterns } from "./clonePatterns";
|
|
5
|
+
import { findPattern } from "./findPattern";
|
|
6
|
+
|
|
7
|
+
export interface InfiniteRepeatOptions {
|
|
8
|
+
divider?: Pattern;
|
|
9
|
+
min?: number;
|
|
10
|
+
trimDivider?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class InfiniteRepeat implements Pattern {
|
|
14
|
+
private _type: string;
|
|
15
|
+
private _name: string;
|
|
16
|
+
private _parent: Pattern | null;
|
|
17
|
+
private _children: Pattern[];
|
|
18
|
+
private _pattern: Pattern;
|
|
19
|
+
private _divider: Pattern | null;
|
|
20
|
+
private _nodes: Node[];
|
|
21
|
+
private _firstIndex: number;
|
|
22
|
+
private _min: number;
|
|
23
|
+
private _trimDivider: boolean;
|
|
24
|
+
|
|
25
|
+
get type(): string {
|
|
26
|
+
return this._type;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get name(): string {
|
|
30
|
+
return this._name;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get parent(): Pattern | null {
|
|
34
|
+
return this._parent;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
set parent(pattern: Pattern | null) {
|
|
38
|
+
this._parent = pattern;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get children(): Pattern[] {
|
|
42
|
+
return this._children;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get isOptional(): boolean {
|
|
46
|
+
return this._min === 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get min(): number {
|
|
50
|
+
return this._min;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
constructor(name: string, pattern: Pattern, options: InfiniteRepeatOptions = {}) {
|
|
54
|
+
const min = options.min != null ? options.min : 1;
|
|
55
|
+
const divider = options.divider;
|
|
56
|
+
let children: Pattern[];
|
|
57
|
+
|
|
58
|
+
if (divider != null) {
|
|
59
|
+
children = [pattern.clone(), divider.clone(divider.name, false)]
|
|
60
|
+
} else {
|
|
61
|
+
children = [pattern.clone()]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this._assignChildrenToParent(children);
|
|
65
|
+
|
|
66
|
+
this._type = "infinite-repeat";
|
|
67
|
+
this._name = name;
|
|
68
|
+
this._min = min;
|
|
69
|
+
this._parent = null;
|
|
70
|
+
this._children = children;
|
|
71
|
+
this._pattern = children[0];
|
|
72
|
+
this._divider = children[1];
|
|
73
|
+
this._firstIndex = -1
|
|
74
|
+
this._nodes = [];
|
|
75
|
+
this._trimDivider = options.trimDivider == null ? false : options.trimDivider;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private _assignChildrenToParent(children: Pattern[]): void {
|
|
79
|
+
for (const child of children) {
|
|
80
|
+
child.parent = this;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
test(text: string) {
|
|
85
|
+
const cursor = new Cursor(text);
|
|
86
|
+
const ast = this.parse(cursor);
|
|
87
|
+
|
|
88
|
+
return ast?.value === text;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
exec(text: string) {
|
|
92
|
+
const cursor = new Cursor(text);
|
|
93
|
+
const ast = this.parse(cursor);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
ast: ast?.value === text ? ast : null,
|
|
97
|
+
cursor
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
parse(cursor: Cursor): Node | null {
|
|
102
|
+
this._firstIndex = cursor.index;
|
|
103
|
+
this._nodes = [];
|
|
104
|
+
|
|
105
|
+
const passed = this._tryToParse(cursor);
|
|
106
|
+
|
|
107
|
+
if (passed) {
|
|
108
|
+
cursor.resolveError();
|
|
109
|
+
const node = this._createNode(cursor);
|
|
110
|
+
|
|
111
|
+
if (node != null) {
|
|
112
|
+
cursor.moveTo(node.lastIndex);
|
|
113
|
+
cursor.recordMatch(this, node);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return node;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (this._min > 0) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
cursor.resolveError();
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private _meetsMin() {
|
|
128
|
+
if (this._divider != null) {
|
|
129
|
+
return Math.ceil(this._nodes.length / 2) >= this._min;
|
|
130
|
+
}
|
|
131
|
+
return this._nodes.length >= this._min;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private _tryToParse(cursor: Cursor): boolean {
|
|
135
|
+
let passed = false;
|
|
136
|
+
|
|
137
|
+
while (true) {
|
|
138
|
+
const runningCursorIndex = cursor.index;
|
|
139
|
+
const repeatedNode = this._pattern.parse(cursor);
|
|
140
|
+
|
|
141
|
+
if (cursor.hasError) {
|
|
142
|
+
const lastValidNode = this._getLastValidNode();
|
|
143
|
+
|
|
144
|
+
if (lastValidNode != null) {
|
|
145
|
+
passed = true;
|
|
146
|
+
} else {
|
|
147
|
+
cursor.moveTo(runningCursorIndex);
|
|
148
|
+
cursor.recordErrorAt(runningCursorIndex, this._pattern);
|
|
149
|
+
passed = false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
break;
|
|
153
|
+
} else {
|
|
154
|
+
if (repeatedNode != null) {
|
|
155
|
+
this._nodes.push(repeatedNode);
|
|
156
|
+
|
|
157
|
+
if (!cursor.hasNext()) {
|
|
158
|
+
passed = true;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
cursor.next();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (this._divider != null) {
|
|
166
|
+
const dividerNode = this._divider.parse(cursor);
|
|
167
|
+
|
|
168
|
+
if (cursor.hasError) {
|
|
169
|
+
passed = true;
|
|
170
|
+
break;
|
|
171
|
+
} else if (dividerNode != null) {
|
|
172
|
+
this._nodes.push(dividerNode);
|
|
173
|
+
|
|
174
|
+
if (!cursor.hasNext()) {
|
|
175
|
+
passed = true;
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
cursor.next();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const hasMinimum = this._meetsMin();
|
|
186
|
+
|
|
187
|
+
if (hasMinimum) {
|
|
188
|
+
return passed;
|
|
189
|
+
} else if (!hasMinimum && passed) {
|
|
190
|
+
cursor.recordErrorAt(cursor.index, this);
|
|
191
|
+
cursor.moveTo(this._firstIndex);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return passed;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private _createNode(cursor: Cursor): Node | null {
|
|
199
|
+
const hasDivider = this._divider != null;
|
|
200
|
+
|
|
201
|
+
if (
|
|
202
|
+
hasDivider &&
|
|
203
|
+
this._trimDivider &&
|
|
204
|
+
cursor.leafMatch.pattern === this._divider
|
|
205
|
+
) {
|
|
206
|
+
const dividerNode = this._nodes.pop() as Node;
|
|
207
|
+
cursor.moveTo(dividerNode.firstIndex);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const lastIndex = this._nodes[this._nodes.length - 1].lastIndex;
|
|
211
|
+
cursor.moveTo(lastIndex);
|
|
212
|
+
|
|
213
|
+
return new Node(
|
|
214
|
+
this._type,
|
|
215
|
+
this._name,
|
|
216
|
+
this._firstIndex,
|
|
217
|
+
lastIndex,
|
|
218
|
+
this._nodes
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private _getLastValidNode(): Node | null {
|
|
223
|
+
const nodes = this._nodes.filter((node) => node !== null);
|
|
224
|
+
|
|
225
|
+
if (nodes.length === 0) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return nodes[nodes.length - 1];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
getTokens(): string[] {
|
|
233
|
+
return this._pattern.getTokens();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
getTokensAfter(childReference: Pattern): string[] {
|
|
237
|
+
const patterns = this.getPatternsAfter(childReference);
|
|
238
|
+
const tokens: string[] = [];
|
|
239
|
+
|
|
240
|
+
patterns.forEach(p => tokens.push(...p.getTokens()));
|
|
241
|
+
|
|
242
|
+
return tokens;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
getNextTokens(): string[] {
|
|
246
|
+
if (this._parent == null) {
|
|
247
|
+
return []
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return this._parent.getTokensAfter(this);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
getPatterns(): Pattern[] {
|
|
254
|
+
return this._pattern.getPatterns();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
getPatternsAfter(childReference: Pattern): Pattern[] {
|
|
258
|
+
let index = -1;
|
|
259
|
+
const patterns: Pattern[] = [];
|
|
260
|
+
|
|
261
|
+
for (let i = 0; i < this._children.length; i++) {
|
|
262
|
+
if (this._children[i] === childReference) {
|
|
263
|
+
index = i;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// If the last match isn't a child of this pattern.
|
|
268
|
+
if (index === -1) {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// If the last match was the repeated patterns, then suggest the divider.
|
|
273
|
+
if (index === 0 && this._divider) {
|
|
274
|
+
patterns.push(this._children[1]);
|
|
275
|
+
|
|
276
|
+
if (this._parent) {
|
|
277
|
+
patterns.push(...this._parent.getPatternsAfter(this));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Suggest the pattern because the divider was the last match.
|
|
282
|
+
if (index === 1) {
|
|
283
|
+
patterns.push(this._children[0]);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// If there is no divider then suggest the repeating pattern and the next pattern after.
|
|
287
|
+
if (index === 0 && !this._divider && this._parent) {
|
|
288
|
+
patterns.push(this._children[0]);
|
|
289
|
+
patterns.push(...this._parent.getPatternsAfter(this));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return patterns;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
getNextPatterns(): Pattern[] {
|
|
296
|
+
if (this._parent == null) {
|
|
297
|
+
return [];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return this._parent.getPatternsAfter(this)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
find(predicate: (p: Pattern) => boolean): Pattern | null {
|
|
304
|
+
return findPattern(this, predicate);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
clone(name = this._name, isOptional?: boolean): Pattern {
|
|
308
|
+
let min = this._min;
|
|
309
|
+
|
|
310
|
+
if (isOptional != null) {
|
|
311
|
+
if (isOptional) {
|
|
312
|
+
min = 0
|
|
313
|
+
} else {
|
|
314
|
+
min = Math.max(this._min, 1);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return new InfiniteRepeat(
|
|
319
|
+
name,
|
|
320
|
+
this._pattern,
|
|
321
|
+
{
|
|
322
|
+
divider: this._divider == null ? undefined : this._divider,
|
|
323
|
+
min: min,
|
|
324
|
+
trimDivider: this._trimDivider
|
|
325
|
+
}
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
@@ -119,7 +119,7 @@ describe("Literal", () => {
|
|
|
119
119
|
const sequence = new And("sequence", [new Literal("a", "A")]);
|
|
120
120
|
const parent = new And("parent", [sequence, new Literal("b", "B")]);
|
|
121
121
|
|
|
122
|
-
const a = parent.
|
|
122
|
+
const a = parent.find(p => p.name === "a");
|
|
123
123
|
const tokens = a?.getNextTokens() || [];
|
|
124
124
|
|
|
125
125
|
expect(tokens[0]).toBe("B");
|
|
@@ -132,13 +132,22 @@ describe("Literal", () => {
|
|
|
132
132
|
expect(tokens.length).toBe(0);
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
+
test("Get Patterns", () => {
|
|
136
|
+
const a = new Literal("a", "A");
|
|
137
|
+
|
|
138
|
+
const tokens = a.getPatterns();
|
|
139
|
+
const expectedTokens = [a];
|
|
140
|
+
|
|
141
|
+
expect(tokens).toEqual(expectedTokens);
|
|
142
|
+
});
|
|
143
|
+
|
|
135
144
|
test("Get Next Patterns", () => {
|
|
136
145
|
const sequence = new And("sequence", [new Literal("a", "A")]);
|
|
137
146
|
const parent = new And("parent", [sequence, new Literal("b", "B")]);
|
|
138
147
|
|
|
139
|
-
const a = parent.
|
|
148
|
+
const a = parent.find(p => p.name === "a");
|
|
140
149
|
const nextPatterns = a?.getNextPatterns() || [];
|
|
141
|
-
const b = parent.
|
|
150
|
+
const b = parent.find(p => p.name === "b")
|
|
142
151
|
|
|
143
152
|
expect(nextPatterns[0]).toBe(b);
|
|
144
153
|
});
|
|
@@ -159,7 +168,7 @@ describe("Literal", () => {
|
|
|
159
168
|
|
|
160
169
|
test("Find Pattern", () => {
|
|
161
170
|
const a = new Literal("a", "A");
|
|
162
|
-
const pattern = a.
|
|
171
|
+
const pattern = a.find(p => p.name === "nada");
|
|
163
172
|
|
|
164
173
|
expect(pattern).toBeNull();
|
|
165
174
|
});
|
package/src/patterns/Literal.ts
CHANGED
|
@@ -151,6 +151,10 @@ export class Literal implements Pattern {
|
|
|
151
151
|
return this.parent.getTokensAfter(this);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
getPatterns(): Pattern[] {
|
|
155
|
+
return [this];
|
|
156
|
+
}
|
|
157
|
+
|
|
154
158
|
getPatternsAfter(): Pattern[] {
|
|
155
159
|
return []
|
|
156
160
|
}
|
|
@@ -163,7 +167,7 @@ export class Literal implements Pattern {
|
|
|
163
167
|
return this.parent.getPatternsAfter(this)
|
|
164
168
|
}
|
|
165
169
|
|
|
166
|
-
|
|
170
|
+
find(_predicate: (p: Pattern) => boolean): Pattern | null {
|
|
167
171
|
return null;
|
|
168
172
|
}
|
|
169
173
|
|
package/src/patterns/Not.test.ts
CHANGED
|
@@ -88,7 +88,7 @@ describe("Not", () => {
|
|
|
88
88
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
89
89
|
const sequence = new And("sequence", [notAboutUs, new Literal("about-them", "About Them")]);
|
|
90
90
|
|
|
91
|
-
const cloneNotAboutUs = sequence.
|
|
91
|
+
const cloneNotAboutUs = sequence.find(p => p.name === "not-about-us") as Pattern;
|
|
92
92
|
const nextTokens = cloneNotAboutUs.getNextTokens() || [];
|
|
93
93
|
|
|
94
94
|
expect(nextTokens[0]).toBe("About Them");
|
|
@@ -105,7 +105,7 @@ describe("Not", () => {
|
|
|
105
105
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
106
106
|
const sequence = new And("sequence", [notAboutUs, new Literal("about-them", "About Them")]);
|
|
107
107
|
|
|
108
|
-
const cloneNotAboutUs = sequence.
|
|
108
|
+
const cloneNotAboutUs = sequence.find(p => p.name === "not-about-us") as Pattern;
|
|
109
109
|
const nextTokens = cloneNotAboutUs.getTokens() || [];
|
|
110
110
|
|
|
111
111
|
expect(nextTokens[0]).toBe("About Them");
|
|
@@ -114,8 +114,8 @@ describe("Not", () => {
|
|
|
114
114
|
test("Get Tokens After", () => {
|
|
115
115
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
116
116
|
const sequence = new And("sequence", [notAboutUs, new Literal("about-them", "About Them")]);
|
|
117
|
-
const notAboutUsClone = sequence.
|
|
118
|
-
const aboutUsClone = sequence.
|
|
117
|
+
const notAboutUsClone = sequence.find(p => p.name === "not-about-us") as Pattern;
|
|
118
|
+
const aboutUsClone = sequence.find(p => p.name === "about-us") as Pattern;
|
|
119
119
|
const nextTokens = notAboutUsClone.getTokensAfter(aboutUsClone) || [];
|
|
120
120
|
|
|
121
121
|
expect(nextTokens[0]).toBe("About Them");
|
|
@@ -123,16 +123,27 @@ describe("Not", () => {
|
|
|
123
123
|
|
|
124
124
|
test("Find Pattern", () => {
|
|
125
125
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
126
|
-
const child = notAboutUs.
|
|
126
|
+
const child = notAboutUs.find(p => p.name === "about-us")
|
|
127
127
|
|
|
128
128
|
expect(child).not.toBeNull();
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
+
test("Get Patterns", () => {
|
|
132
|
+
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
133
|
+
const sequence = new And("sequence", [notAboutUs, new Literal("about-them", "About Them")]);
|
|
134
|
+
|
|
135
|
+
const cloneNotAboutUs = sequence.find(p => p.name === "not-about-us") as Pattern;
|
|
136
|
+
const nextPatterns = cloneNotAboutUs.getPatterns();
|
|
137
|
+
const expected = [sequence.find(p=>p.name === "about-them")];
|
|
138
|
+
|
|
139
|
+
expect(nextPatterns).toEqual(expected);
|
|
140
|
+
});
|
|
141
|
+
|
|
131
142
|
test("Get Next Patterns", () => {
|
|
132
143
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
133
144
|
const sequence = new And("sequence", [notAboutUs, new Literal("about-them", "About Them")]);
|
|
134
145
|
|
|
135
|
-
const cloneNotAboutUs = sequence.
|
|
146
|
+
const cloneNotAboutUs = sequence.find(p => p.name === "not-about-us") as Pattern;
|
|
136
147
|
const patterns = cloneNotAboutUs.getNextPatterns() || [];
|
|
137
148
|
|
|
138
149
|
expect(patterns.length).toBe(1);
|
|
@@ -149,8 +160,8 @@ describe("Not", () => {
|
|
|
149
160
|
test("Get Patterns After", () => {
|
|
150
161
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
151
162
|
const sequence = new And("sequence", [notAboutUs, new Literal("about-them", "About Them")]);
|
|
152
|
-
const notAboutUsClone = sequence.
|
|
153
|
-
const aboutUsClone = sequence.
|
|
163
|
+
const notAboutUsClone = sequence.find(p => p.name === "not-about-us") as Pattern;
|
|
164
|
+
const aboutUsClone = sequence.find(p => p.name === "about-us") as Pattern;
|
|
154
165
|
const patterns = notAboutUsClone.getPatternsAfter(aboutUsClone) || [];
|
|
155
166
|
|
|
156
167
|
expect(patterns.length).toBe(1);
|
|
@@ -159,7 +170,7 @@ describe("Not", () => {
|
|
|
159
170
|
|
|
160
171
|
test("Get Patterns After With Null Parent", () => {
|
|
161
172
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
162
|
-
const aboutUsClone = notAboutUs.
|
|
173
|
+
const aboutUsClone = notAboutUs.find(p => p.name === "about-us") as Pattern;
|
|
163
174
|
const patterns = notAboutUs.getPatternsAfter(aboutUsClone) || [];
|
|
164
175
|
|
|
165
176
|
expect(patterns.length).toBe(0);
|
package/src/patterns/Not.ts
CHANGED
|
@@ -106,6 +106,10 @@ export class Not implements Pattern {
|
|
|
106
106
|
return this.parent.getTokensAfter(this);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
getPatterns(): Pattern[] {
|
|
110
|
+
return [...this.getNextPatterns().map(p => p.getPatterns()).flat()];
|
|
111
|
+
}
|
|
112
|
+
|
|
109
113
|
getPatternsAfter(_childReference: Pattern): Pattern[] {
|
|
110
114
|
const parent = this._parent;
|
|
111
115
|
|
|
@@ -124,7 +128,7 @@ export class Not implements Pattern {
|
|
|
124
128
|
return this.parent.getPatternsAfter(this)
|
|
125
129
|
}
|
|
126
130
|
|
|
127
|
-
|
|
131
|
+
find(predicate: (p: Pattern) => boolean): Pattern | null {
|
|
128
132
|
return predicate(this._children[0]) ? this._children[0] : null;
|
|
129
133
|
}
|
|
130
134
|
|
package/src/patterns/Or.test.ts
CHANGED
|
@@ -118,7 +118,7 @@ describe("Or", () => {
|
|
|
118
118
|
new Literal("c", "C")
|
|
119
119
|
]);
|
|
120
120
|
|
|
121
|
-
const orClone = sequence.
|
|
121
|
+
const orClone = sequence.find(p => p.name === "a-or-b") as Pattern;
|
|
122
122
|
const tokens = orClone.getNextTokens();
|
|
123
123
|
|
|
124
124
|
expect(tokens.length).toBe(1);
|
|
@@ -145,14 +145,25 @@ describe("Or", () => {
|
|
|
145
145
|
new Literal("c", "C")
|
|
146
146
|
]);
|
|
147
147
|
|
|
148
|
-
const aClone = sequence.
|
|
149
|
-
const orClone = sequence.
|
|
148
|
+
const aClone = sequence.find(p => p.name === "a") as Pattern;
|
|
149
|
+
const orClone = sequence.find(p => p.name === "a-or-b") as Pattern;
|
|
150
150
|
const tokens = orClone.getTokensAfter(aClone);
|
|
151
151
|
|
|
152
152
|
expect(tokens.length).toBe(1);
|
|
153
153
|
expect(tokens[0]).toBe("C");
|
|
154
154
|
});
|
|
155
155
|
|
|
156
|
+
test("Get Patterns", () => {
|
|
157
|
+
const aOrB = new Or("a-b", [new Literal("a", "A"), new Literal("b", "B")]);
|
|
158
|
+
const patterns = aOrB.getPatterns();
|
|
159
|
+
const expected = [
|
|
160
|
+
aOrB.find(p => p.name === "a"),
|
|
161
|
+
aOrB.find(p => p.name === "b")
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
expect(patterns).toEqual(expected);
|
|
165
|
+
});
|
|
166
|
+
|
|
156
167
|
test("Get Patterns After", () => {
|
|
157
168
|
const sequence = new And("sequence", [
|
|
158
169
|
new Or("a-or-b", [
|
|
@@ -162,8 +173,8 @@ describe("Or", () => {
|
|
|
162
173
|
new Literal("c", "C")
|
|
163
174
|
]);
|
|
164
175
|
|
|
165
|
-
const aClone = sequence.
|
|
166
|
-
const orClone = sequence.
|
|
176
|
+
const aClone = sequence.find(p => p.name === "a") as Pattern;
|
|
177
|
+
const orClone = sequence.find(p => p.name === "a-or-b") as Pattern;
|
|
167
178
|
const patterns = orClone.getPatternsAfter(aClone);
|
|
168
179
|
|
|
169
180
|
expect(patterns.length).toBe(1);
|
|
@@ -175,7 +186,7 @@ describe("Or", () => {
|
|
|
175
186
|
new Literal("a", "A"),
|
|
176
187
|
new Literal("b", "B")
|
|
177
188
|
])
|
|
178
|
-
const aClone = or.
|
|
189
|
+
const aClone = or.find(p => p.name === "a") as Pattern;
|
|
179
190
|
const patterns = or.getPatternsAfter(aClone);
|
|
180
191
|
|
|
181
192
|
expect(patterns.length).toBe(0);
|
|
@@ -190,7 +201,7 @@ describe("Or", () => {
|
|
|
190
201
|
new Literal("c", "C")
|
|
191
202
|
]);
|
|
192
203
|
|
|
193
|
-
const orClone = sequence.
|
|
204
|
+
const orClone = sequence.find(p => p.name === "a-or-b") as Pattern;
|
|
194
205
|
const patterns = orClone.getNextPatterns();
|
|
195
206
|
|
|
196
207
|
expect(patterns.length).toBe(1);
|
package/src/patterns/Or.ts
CHANGED
|
@@ -136,6 +136,16 @@ export class Or implements Pattern {
|
|
|
136
136
|
return this._parent.getTokensAfter(this);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
getPatterns(): Pattern[] {
|
|
140
|
+
const patterns: Pattern[] = [];
|
|
141
|
+
|
|
142
|
+
for (const pattern of this._children) {
|
|
143
|
+
patterns.push(...pattern.getPatterns());
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return patterns;
|
|
147
|
+
}
|
|
148
|
+
|
|
139
149
|
getPatternsAfter(_childReference: Pattern): Pattern[] {
|
|
140
150
|
if (this._parent === null) {
|
|
141
151
|
return [];
|
|
@@ -152,7 +162,7 @@ export class Or implements Pattern {
|
|
|
152
162
|
return this.parent.getPatternsAfter(this)
|
|
153
163
|
}
|
|
154
164
|
|
|
155
|
-
|
|
165
|
+
find(predicate: (p: Pattern) => boolean): Pattern | null {
|
|
156
166
|
return findPattern(this, predicate);
|
|
157
167
|
}
|
|
158
168
|
|
package/src/patterns/Pattern.ts
CHANGED
|
@@ -15,8 +15,9 @@ export interface Pattern {
|
|
|
15
15
|
clone(name?: string, isOptional?: boolean): Pattern;
|
|
16
16
|
getTokens(): string[];
|
|
17
17
|
getTokensAfter(childReference: Pattern): string[];
|
|
18
|
+
getNextTokens(): string[];
|
|
19
|
+
getPatterns(): Pattern[];
|
|
18
20
|
getPatternsAfter(childReference: Pattern): Pattern[];
|
|
19
21
|
getNextPatterns(): Pattern[];
|
|
20
|
-
|
|
21
|
-
findPattern(predicate: (p: Pattern) => boolean): Pattern | null;
|
|
22
|
+
find(predicate: (p: Pattern) => boolean): Pattern | null;
|
|
22
23
|
}
|
|
@@ -19,7 +19,7 @@ function createValuePattern() {
|
|
|
19
19
|
divider.setTokens([", "]);
|
|
20
20
|
|
|
21
21
|
const valueRef = new Reference("value");
|
|
22
|
-
const values = new Repeat("values", valueRef, divider);
|
|
22
|
+
const values = new Repeat("values", valueRef, { divider });
|
|
23
23
|
const array = new And("array", [openBracket, values, closeBracket]);
|
|
24
24
|
const value = new Or("value", [number, array]);
|
|
25
25
|
|
|
@@ -34,7 +34,7 @@ describe("Reference", () => {
|
|
|
34
34
|
|
|
35
35
|
const expected = new Node("and", "array", 0, 5, [
|
|
36
36
|
new Node("literal", "open-bracket", 0, 0, [], "["),
|
|
37
|
-
new Node("repeat", "values", 1, 4, [
|
|
37
|
+
new Node("infinite-repeat", "values", 1, 4, [
|
|
38
38
|
new Node("regex", "number", 1, 1, [], "1"),
|
|
39
39
|
new Node("regex", "divider", 2, 3, [], ", "),
|
|
40
40
|
new Node("regex", "number", 4, 4, [], "2")
|
|
@@ -114,9 +114,9 @@ describe("Reference", () => {
|
|
|
114
114
|
|
|
115
115
|
test("Find Pattern", () => {
|
|
116
116
|
const value = createValuePattern();
|
|
117
|
-
const reference = value.
|
|
117
|
+
const reference = value.find(p => p.type === "reference") as Pattern;
|
|
118
118
|
|
|
119
|
-
const pattern = reference?.
|
|
119
|
+
const pattern = reference?.find(p => p.name === "Nada");
|
|
120
120
|
|
|
121
121
|
expect(pattern).toBe(null);
|
|
122
122
|
});
|
|
@@ -124,7 +124,7 @@ describe("Reference", () => {
|
|
|
124
124
|
|
|
125
125
|
test("Get Next Tokens", () => {
|
|
126
126
|
const value = createValuePattern();
|
|
127
|
-
const reference = value.
|
|
127
|
+
const reference = value.find(p => p.type === "reference") as Pattern;
|
|
128
128
|
const tokens = reference.getNextTokens();
|
|
129
129
|
|
|
130
130
|
expect(tokens).toEqual([", ", "]"]);
|
|
@@ -139,7 +139,7 @@ describe("Reference", () => {
|
|
|
139
139
|
|
|
140
140
|
test("Get Tokens After", () => {
|
|
141
141
|
const value = createValuePattern();
|
|
142
|
-
const reference = value.
|
|
142
|
+
const reference = value.find(p => p.type === "reference") as Pattern;
|
|
143
143
|
const tokens = reference.getTokensAfter(new Literal("bogus", "Bogus"));
|
|
144
144
|
|
|
145
145
|
expect(tokens).toEqual([", ", "]"]);
|
|
@@ -152,9 +152,19 @@ describe("Reference", () => {
|
|
|
152
152
|
expect(tokens).toEqual([])
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
+
test("Get Patterns", () => {
|
|
156
|
+
const value = createValuePattern();
|
|
157
|
+
const ref = findPattern(value, (p) => p.type === "reference");
|
|
158
|
+
const patterns = ref?.getPatterns() || [];
|
|
159
|
+
|
|
160
|
+
expect(patterns.length).toBe(2);
|
|
161
|
+
expect(patterns[0].name).toBe("number");
|
|
162
|
+
expect(patterns[1].name).toBe("open-bracket");
|
|
163
|
+
});
|
|
164
|
+
|
|
155
165
|
test("Get Patterns After", () => {
|
|
156
166
|
const value = createValuePattern();
|
|
157
|
-
const reference = value.
|
|
167
|
+
const reference = value.find(p => p.type === "reference") as Pattern;
|
|
158
168
|
const patterns = reference.getPatternsAfter(new Literal("bogus", "Bogus"));
|
|
159
169
|
|
|
160
170
|
expect(patterns.length).toEqual(2);
|
|
@@ -173,7 +183,7 @@ describe("Reference", () => {
|
|
|
173
183
|
|
|
174
184
|
test("Get Next Patterns", () => {
|
|
175
185
|
const value = createValuePattern();
|
|
176
|
-
const reference = value.
|
|
186
|
+
const reference = value.find(p => p.type === "reference") as Pattern;
|
|
177
187
|
const patterns = reference.getNextPatterns();
|
|
178
188
|
|
|
179
189
|
expect(patterns.length).toEqual(2);
|
|
@@ -127,6 +127,10 @@ export class Reference implements Pattern {
|
|
|
127
127
|
return this.parent.getTokensAfter(this);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
getPatterns(): Pattern[] {
|
|
131
|
+
return this._getPatternSafely().getPatterns();
|
|
132
|
+
}
|
|
133
|
+
|
|
130
134
|
getPatternsAfter(_childReference: Pattern): Pattern[] {
|
|
131
135
|
if (this._parent == null) {
|
|
132
136
|
return [];
|
|
@@ -143,7 +147,7 @@ export class Reference implements Pattern {
|
|
|
143
147
|
return this.parent.getPatternsAfter(this)
|
|
144
148
|
}
|
|
145
149
|
|
|
146
|
-
|
|
150
|
+
find(_predicate: (p: Pattern) => boolean): Pattern | null {
|
|
147
151
|
return null;
|
|
148
152
|
}
|
|
149
153
|
|