clarity-pattern-parser 6.0.2 → 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.
Files changed (88) hide show
  1. package/TODO.md +1 -78
  2. package/dist/ast/Node.d.ts +1 -0
  3. package/dist/grammar/Grammar.d.ts +17 -0
  4. package/dist/grammar/patterns/andLiteral.d.ts +2 -0
  5. package/dist/grammar/patterns/comment.d.ts +2 -0
  6. package/dist/grammar/patterns/grammar.d.ts +2 -0
  7. package/dist/grammar/patterns/literal.d.ts +2 -0
  8. package/dist/grammar/patterns/name.d.ts +2 -0
  9. package/dist/grammar/patterns/orLiteral.d.ts +2 -0
  10. package/dist/grammar/patterns/pattern.d.ts +2 -0
  11. package/dist/grammar/patterns/regexLiteral.d.ts +2 -0
  12. package/dist/grammar/patterns/repeatLiteral.d.ts +3 -0
  13. package/dist/grammar/patterns/spaces.d.ts +2 -0
  14. package/dist/grammar/patterns/statement.d.ts +2 -0
  15. package/dist/index.browser.js +1161 -556
  16. package/dist/index.browser.js.map +1 -1
  17. package/dist/index.d.ts +5 -4
  18. package/dist/index.esm.js +1159 -555
  19. package/dist/index.esm.js.map +1 -1
  20. package/dist/index.js +1159 -554
  21. package/dist/index.js.map +1 -1
  22. package/dist/intellisense/AutoComplete.d.ts +2 -6
  23. package/dist/patterns/And.d.ts +1 -1
  24. package/dist/patterns/Cursor.d.ts +1 -0
  25. package/dist/patterns/CursorHistory.d.ts +2 -1
  26. package/dist/patterns/FiniteRepeat.d.ts +39 -0
  27. package/dist/patterns/InfiniteRepeat.d.ts +47 -0
  28. package/dist/patterns/Literal.d.ts +1 -1
  29. package/dist/patterns/Not.d.ts +1 -1
  30. package/dist/patterns/Or.d.ts +1 -1
  31. package/dist/patterns/Pattern.d.ts +1 -1
  32. package/dist/patterns/Reference.d.ts +1 -1
  33. package/dist/patterns/Regex.d.ts +1 -1
  34. package/dist/patterns/Repeat.d.ts +18 -22
  35. package/jest.config.js +0 -1
  36. package/jest.coverage.config.js +13 -0
  37. package/package.json +3 -3
  38. package/src/ast/Node.test.ts +15 -0
  39. package/src/ast/Node.ts +12 -6
  40. package/src/grammar/Grammar.test.ts +288 -0
  41. package/src/grammar/Grammar.ts +234 -0
  42. package/src/grammar/patterns/andLiteral.ts +8 -0
  43. package/src/grammar/patterns/comment.ts +3 -0
  44. package/src/grammar/patterns/grammar.ts +19 -0
  45. package/src/grammar/patterns/literal.ts +5 -0
  46. package/src/grammar/patterns/name.ts +3 -0
  47. package/src/grammar/patterns/orLiteral.ts +8 -0
  48. package/src/grammar/patterns/pattern.ts +13 -0
  49. package/src/grammar/patterns/regexLiteral.ts +4 -0
  50. package/src/grammar/patterns/repeatLiteral.ts +72 -0
  51. package/src/grammar/patterns/spaces.ts +4 -0
  52. package/src/grammar/patterns/statement.ts +35 -0
  53. package/src/grammar/spec.md +142 -0
  54. package/src/index.ts +6 -3
  55. package/src/intellisense/AutoComplete.test.ts +21 -2
  56. package/src/intellisense/AutoComplete.ts +14 -25
  57. package/src/intellisense/Suggestion.ts +0 -1
  58. package/src/intellisense/css/cssValue.ts +1 -1
  59. package/src/intellisense/css/method.ts +1 -1
  60. package/src/intellisense/css/values.ts +1 -1
  61. package/src/intellisense/javascript/Javascript.test.ts +0 -1
  62. package/src/intellisense/javascript/arrayLiteral.ts +1 -1
  63. package/src/intellisense/javascript/expression.ts +1 -1
  64. package/src/intellisense/javascript/invocation.ts +1 -1
  65. package/src/intellisense/javascript/objectLiteral.ts +1 -1
  66. package/src/intellisense/javascript/parameters.ts +1 -1
  67. package/src/intellisense/javascript/stringLiteral.ts +2 -4
  68. package/src/patterns/And.test.ts +5 -5
  69. package/src/patterns/And.ts +11 -17
  70. package/src/patterns/Cursor.ts +4 -0
  71. package/src/patterns/CursorHistory.ts +34 -5
  72. package/src/patterns/FiniteRepeat.test.ts +481 -0
  73. package/src/patterns/FiniteRepeat.ts +231 -0
  74. package/src/patterns/InfiniteRepeat.test.ts +296 -0
  75. package/src/patterns/InfiniteRepeat.ts +329 -0
  76. package/src/patterns/Literal.test.ts +4 -4
  77. package/src/patterns/Literal.ts +1 -1
  78. package/src/patterns/Not.test.ts +11 -11
  79. package/src/patterns/Not.ts +1 -1
  80. package/src/patterns/Or.test.ts +9 -9
  81. package/src/patterns/Or.ts +1 -1
  82. package/src/patterns/Pattern.ts +1 -1
  83. package/src/patterns/Reference.test.ts +8 -8
  84. package/src/patterns/Reference.ts +1 -1
  85. package/src/patterns/Regex.test.ts +4 -4
  86. package/src/patterns/Regex.ts +1 -1
  87. package/src/patterns/Repeat.test.ts +160 -165
  88. package/src/patterns/Repeat.ts +95 -230
@@ -0,0 +1,296 @@
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 { Pattern } from "./Pattern";
7
+ import { Regex } from "./Regex";
8
+ import { InfiniteRepeat } from "./InfiniteRepeat";
9
+
10
+ describe("InfiniteRepeat", () => {
11
+ test("Successful Parse", () => {
12
+ const digit = new Regex("digit", "\\d");
13
+ const integer = new InfiniteRepeat("number", digit);
14
+ const cursor = new Cursor("337");
15
+ const result = integer.parse(cursor);
16
+ const expected = new Node("infinite-repeat", "number", 0, 2, [
17
+ new Node("regex", "digit", 0, 0, [], "3"),
18
+ new Node("regex", "digit", 1, 1, [], "3"),
19
+ new Node("regex", "digit", 2, 2, [], "7"),
20
+ ]);
21
+
22
+ expect(result).toEqual(expected)
23
+ expect(cursor.hasError).toBeFalsy()
24
+ });
25
+
26
+ test("Bounds", () => {
27
+ const digit = new Regex("digit", "\\d");
28
+ const integer = new InfiniteRepeat("number", digit, { min: 2 });
29
+
30
+ let cursor = new Cursor("3");
31
+ let result = integer.parse(cursor);
32
+ let expected: Node | null = null;
33
+
34
+ expect(result).toEqual(expected);
35
+ expect(cursor.hasError).toBeTruthy();
36
+
37
+ cursor = new Cursor("33");
38
+ result = integer.parse(cursor);
39
+ expected = new Node("infinite-repeat", "number", 0, 1, [
40
+ new Node("regex", "digit", 0, 0, [], "3"),
41
+ new Node("regex", "digit", 1, 1, [], "3")
42
+ ]);
43
+
44
+ expect(result).toEqual(expected);
45
+ expect(cursor.hasError).toBeFalsy();
46
+ });
47
+
48
+ test("Failed Parse", () => {
49
+ const digit = new Regex("digit", "\\d");
50
+ const integer = new InfiniteRepeat("number", digit);
51
+ const cursor = new Cursor("John");
52
+ const result = integer.parse(cursor);
53
+
54
+ expect(result).toBeNull()
55
+ expect(cursor.hasError).toBeTruthy()
56
+ });
57
+
58
+ test("Successful Parse With Divider", () => {
59
+ const digit = new Regex("digit", "\\d");
60
+ const divider = new Literal("divider", ",");
61
+ const integer = new InfiniteRepeat("number", digit, { divider });
62
+ const cursor = new Cursor("3,3,7");
63
+ const result = integer.parse(cursor);
64
+ const expected = new Node("infinite-repeat", "number", 0, 4, [
65
+ new Node("regex", "digit", 0, 0, [], "3"),
66
+ new Node("literal", "divider", 1, 1, [], ","),
67
+ new Node("regex", "digit", 2, 2, [], "3"),
68
+ new Node("literal", "divider", 3, 3, [], ","),
69
+ new Node("regex", "digit", 4, 4, [], "7"),
70
+ ]);
71
+
72
+ expect(result).toEqual(expected)
73
+ expect(cursor.hasError).toBeFalsy()
74
+ });
75
+
76
+ test("Successful Parse Text Ends With Divider", () => {
77
+ const digit = new Regex("digit", "\\d");
78
+ const divider = new Literal("divider", ",");
79
+ const integer = new InfiniteRepeat("number", digit, { divider, trimDivider: true });
80
+ const cursor = new Cursor("3,3,7,");
81
+ const result = integer.parse(cursor);
82
+ const expected = new Node("infinite-repeat", "number", 0, 4, [
83
+ new Node("regex", "digit", 0, 0, [], "3"),
84
+ new Node("literal", "divider", 1, 1, [], ","),
85
+ new Node("regex", "digit", 2, 2, [], "3"),
86
+ new Node("literal", "divider", 3, 3, [], ","),
87
+ new Node("regex", "digit", 4, 4, [], "7"),
88
+ ]);
89
+
90
+ expect(result).toEqual(expected)
91
+ expect(cursor.hasError).toBeFalsy()
92
+ });
93
+
94
+ test("Successful Parse Trailing Comma", () => {
95
+ const digit = new Regex("digit", "\\d");
96
+ const divider = new Literal("divider", ",");
97
+ const integer = new InfiniteRepeat("number", digit, { divider, trimDivider: true });
98
+ const cursor = new Cursor("3,3,7,t");
99
+ const result = integer.parse(cursor);
100
+ const expected = new Node("infinite-repeat", "number", 0, 4, [
101
+ new Node("regex", "digit", 0, 0, [], "3"),
102
+ new Node("literal", "divider", 1, 1, [], ","),
103
+ new Node("regex", "digit", 2, 2, [], "3"),
104
+ new Node("literal", "divider", 3, 3, [], ","),
105
+ new Node("regex", "digit", 4, 4, [], "7"),
106
+ ]);
107
+
108
+ expect(result).toEqual(expected)
109
+ expect(cursor.hasError).toBeFalsy()
110
+ });
111
+
112
+ test("Optional Repeating Pattern", () => {
113
+ const digit = new Regex("digit", "\\d+", true);
114
+ const divider = new Regex("divider", "\\s");
115
+ const integer = new InfiniteRepeat("number", digit, { divider });
116
+ const cursor = new Cursor(
117
+ "\n" +
118
+ "3\n" +
119
+ "\n" +
120
+ "\n"
121
+ );
122
+ const result = integer.parse(cursor);
123
+ const expected = new Node("infinite-repeat", "number", 0, 4, [
124
+ new Node("regex", "divider", 0, 0, [], "\n"),
125
+ new Node("regex", "digit", 1, 1, [], "3"),
126
+ new Node("regex", "divider", 2, 2, [], "\n"),
127
+ new Node("regex", "divider", 3, 3, [], "\n"),
128
+ new Node("regex", "divider", 4, 4, [], "\n"),
129
+ ]);
130
+
131
+ expect(result).toEqual(expected)
132
+ expect(cursor.hasError).toBeFalsy()
133
+ });
134
+
135
+ test("Failed (Optional)", () => {
136
+ const digit = new Regex("digit", "\\d");
137
+ const integer = new InfiniteRepeat("number", digit, { min: 0 });
138
+ const cursor = new Cursor("John");
139
+ const result = integer.parse(cursor);
140
+
141
+ expect(result).toBeNull()
142
+ expect(cursor.hasError).toBeFalsy()
143
+ });
144
+
145
+ test("Get Tokens", () => {
146
+ const a = new Literal("a", "A");
147
+ const manyA = new InfiniteRepeat("number", a);
148
+ const tokens = manyA.getTokens();
149
+ const expected = ["A"];
150
+
151
+ expect(tokens).toEqual(expected)
152
+ });
153
+
154
+ test("Get Tokens After With Bogus Pattern", () => {
155
+ const a = new Literal("a", "A");
156
+ const manyA = new InfiniteRepeat("many-a", a);
157
+ const tokens = manyA.getTokensAfter(new Literal("bogus", "bogus"));
158
+ const expected: string[] = [];
159
+
160
+ expect(tokens).toEqual(expected)
161
+ });
162
+
163
+ test("Get Tokens After With Divider", () => {
164
+ const a = new Literal("a", "A");
165
+ const b = new Literal("b", "B");
166
+ const divider = new Literal("divider", ",");
167
+ const manyA = new InfiniteRepeat("many-a", a, { divider });
168
+ const parent = new And("parent", [manyA, b]);
169
+
170
+ const clonedManyA = findPattern(parent, p => p.name == "many-a");
171
+ let tokens = clonedManyA?.getTokensAfter(clonedManyA.children[0]);
172
+ let expected = [",", "B"];
173
+
174
+ expect(tokens).toEqual(expected)
175
+
176
+ tokens = clonedManyA?.getTokensAfter(clonedManyA.children[1]);
177
+ expected = ["A"];
178
+
179
+ expect(tokens).toEqual(expected)
180
+ });
181
+
182
+ test("Get Tokens After Without Divider", () => {
183
+ const a = new Literal("a", "A");
184
+ const b = new Literal("b", "B");
185
+ const manyA = new InfiniteRepeat("many-a", a);
186
+ const parent = new And("parent", [manyA, b]);
187
+
188
+ const clonedManyA = findPattern(parent, p => p.name == "many-a");
189
+ const tokens = clonedManyA?.getTokensAfter(clonedManyA.children[0]);
190
+ const expected = ["A", "B"];
191
+
192
+ expect(tokens).toEqual(expected)
193
+ });
194
+
195
+ test("Properties", () => {
196
+ const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
197
+
198
+ expect(integer.type).toBe("infinite-repeat");
199
+ expect(integer.name).toBe("integer");
200
+ expect(integer.min).toBe(1);
201
+ expect(integer.isOptional).toBeFalsy()
202
+ expect(integer.parent).toBeNull();
203
+ expect(integer.children[0].name).toBe("digit");
204
+ });
205
+
206
+ test("Exec", () => {
207
+ const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
208
+ const { ast: result } = integer.exec("B");
209
+ expect(result).toBeNull()
210
+ });
211
+
212
+ test("Test With Match", () => {
213
+ const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
214
+ const result = integer.test("1");
215
+ expect(result).toBeTruthy()
216
+ });
217
+
218
+ test("Test With No Match", () => {
219
+ const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
220
+ const result = integer.test("b");
221
+ expect(result).toBeFalsy()
222
+ });
223
+
224
+ test("Get Next Tokens", () => {
225
+ const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
226
+ const parent = new And("parent", [integer, new Literal("pow", "!")]);
227
+ const integerClone = parent.find(p => p.name === "integer") as Pattern;
228
+ const tokens = integerClone.getNextTokens();
229
+
230
+ expect(tokens).toEqual(["!"])
231
+ });
232
+
233
+ test("Get Next Tokens With Null Parents", () => {
234
+ const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
235
+ const tokens = integer.getNextTokens();
236
+
237
+ expect(tokens.length).toBe(0);
238
+ });
239
+
240
+ test("Find Pattern", () => {
241
+ const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
242
+ const digitClone = integer.find(p => p.name === "digit") as Pattern;
243
+
244
+ expect(digitClone).not.toBeNull();
245
+ });
246
+
247
+ test("Get Patterns", () => {
248
+ const a = new Literal("a", "A");
249
+ const manyA = new InfiniteRepeat("number", a);
250
+ const patterns = manyA.getPatterns();
251
+ const expected = [manyA.find(p => p.name === "a")];
252
+
253
+ expect(patterns).toEqual(expected)
254
+ });
255
+
256
+ test("Get Next Patterns", () => {
257
+ const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
258
+ const parent = new And("parent", [integer, new Literal("pow", "!")]);
259
+ const integerClone = parent.find(p => p.name === "integer") as Pattern;
260
+ const powClone = parent.find(p => p.name === "pow") as Pattern;
261
+ const patterns = integerClone.getNextPatterns();
262
+
263
+ expect(patterns.length).toBe(1);
264
+ expect(patterns[0]).toBe(powClone);
265
+ });
266
+
267
+ test("Get Next Patterns With Null Parents", () => {
268
+ const integer = new InfiniteRepeat("integer", new Regex("digit", "\\d"));
269
+ const patterns = integer.getNextPatterns();
270
+
271
+ expect(patterns.length).toBe(0);
272
+ });
273
+
274
+ test("Clone With Custom Overrides", () => {
275
+ const numbers = new InfiniteRepeat("numbers", new Regex("number", "\\d"), { min: 0 });
276
+ let clone = numbers.clone();
277
+ let expected = new InfiniteRepeat("numbers", new Regex("number", "\\d"), { min: 0 });
278
+
279
+ expect(clone).toEqual(expected);
280
+
281
+ clone = numbers.clone("cloned-numbers");
282
+ expected = new InfiniteRepeat("cloned-numbers", new Regex("number", "\\d"), { min: 0 });
283
+
284
+ expect(clone).toEqual(expected);
285
+
286
+ clone = numbers.clone("cloned-numbers", true);
287
+ expected = new InfiniteRepeat("cloned-numbers", new Regex("number", "\\d"), { min: 0 });
288
+
289
+ expect(clone).toEqual(expected);
290
+
291
+ clone = numbers.clone("cloned-numbers", false);
292
+ expected = new InfiniteRepeat("cloned-numbers", new Regex("number", "\\d"), { min: 1 });
293
+
294
+ expect(clone).toEqual(expected);
295
+ });
296
+ });
@@ -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.findPattern(p => p.name === "a");
122
+ const a = parent.find(p => p.name === "a");
123
123
  const tokens = a?.getNextTokens() || [];
124
124
 
125
125
  expect(tokens[0]).toBe("B");
@@ -145,9 +145,9 @@ describe("Literal", () => {
145
145
  const sequence = new And("sequence", [new Literal("a", "A")]);
146
146
  const parent = new And("parent", [sequence, new Literal("b", "B")]);
147
147
 
148
- const a = parent.findPattern(p => p.name === "a");
148
+ const a = parent.find(p => p.name === "a");
149
149
  const nextPatterns = a?.getNextPatterns() || [];
150
- const b = parent.findPattern(p => p.name === "b")
150
+ const b = parent.find(p => p.name === "b")
151
151
 
152
152
  expect(nextPatterns[0]).toBe(b);
153
153
  });
@@ -168,7 +168,7 @@ describe("Literal", () => {
168
168
 
169
169
  test("Find Pattern", () => {
170
170
  const a = new Literal("a", "A");
171
- const pattern = a.findPattern(p => p.name === "nada");
171
+ const pattern = a.find(p => p.name === "nada");
172
172
 
173
173
  expect(pattern).toBeNull();
174
174
  });
@@ -167,7 +167,7 @@ export class Literal implements Pattern {
167
167
  return this.parent.getPatternsAfter(this)
168
168
  }
169
169
 
170
- findPattern(_predicate: (p: Pattern) => boolean): Pattern | null {
170
+ find(_predicate: (p: Pattern) => boolean): Pattern | null {
171
171
  return null;
172
172
  }
173
173