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.
Files changed (94) hide show
  1. package/TODO.md +1 -76
  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 +1205 -550
  16. package/dist/index.browser.js.map +1 -1
  17. package/dist/index.d.ts +5 -4
  18. package/dist/index.esm.js +1203 -549
  19. package/dist/index.esm.js.map +1 -1
  20. package/dist/index.js +1203 -548
  21. package/dist/index.js.map +1 -1
  22. package/dist/intellisense/AutoComplete.d.ts +9 -7
  23. package/dist/intellisense/Suggestion.d.ts +1 -2
  24. package/dist/patterns/And.d.ts +2 -1
  25. package/dist/patterns/Cursor.d.ts +1 -0
  26. package/dist/patterns/CursorHistory.d.ts +2 -1
  27. package/dist/patterns/FiniteRepeat.d.ts +39 -0
  28. package/dist/patterns/InfiniteRepeat.d.ts +47 -0
  29. package/dist/patterns/Literal.d.ts +2 -1
  30. package/dist/patterns/Not.d.ts +2 -1
  31. package/dist/patterns/Or.d.ts +2 -1
  32. package/dist/patterns/Pattern.d.ts +3 -2
  33. package/dist/patterns/Reference.d.ts +2 -1
  34. package/dist/patterns/Regex.d.ts +2 -1
  35. package/dist/patterns/Repeat.d.ts +19 -22
  36. package/jest.config.js +0 -1
  37. package/jest.coverage.config.js +13 -0
  38. package/package.json +3 -3
  39. package/src/ast/Node.test.ts +21 -0
  40. package/src/ast/Node.ts +12 -6
  41. package/src/grammar/Grammar.test.ts +288 -0
  42. package/src/grammar/Grammar.ts +234 -0
  43. package/src/grammar/patterns/andLiteral.ts +8 -0
  44. package/src/grammar/patterns/comment.ts +3 -0
  45. package/src/grammar/patterns/grammar.ts +19 -0
  46. package/src/grammar/patterns/literal.ts +5 -0
  47. package/src/grammar/patterns/name.ts +3 -0
  48. package/src/grammar/patterns/orLiteral.ts +8 -0
  49. package/src/grammar/patterns/pattern.ts +13 -0
  50. package/src/grammar/patterns/regexLiteral.ts +4 -0
  51. package/src/grammar/patterns/repeatLiteral.ts +72 -0
  52. package/src/grammar/patterns/spaces.ts +4 -0
  53. package/src/grammar/patterns/statement.ts +35 -0
  54. package/src/grammar/spec.md +142 -0
  55. package/src/index.ts +6 -3
  56. package/src/intellisense/AutoComplete.test.ts +125 -39
  57. package/src/intellisense/AutoComplete.ts +52 -36
  58. package/src/intellisense/Suggestion.ts +1 -2
  59. package/src/intellisense/css/cssValue.ts +1 -1
  60. package/src/intellisense/css/method.ts +1 -1
  61. package/src/intellisense/css/values.ts +1 -1
  62. package/src/intellisense/javascript/Javascript.test.ts +34 -11
  63. package/src/intellisense/javascript/arrayLiteral.ts +1 -1
  64. package/src/intellisense/javascript/{expressionStatement.ts → assignment.ts} +7 -8
  65. package/src/intellisense/javascript/expression.ts +45 -27
  66. package/src/intellisense/javascript/infixOperator.ts +6 -2
  67. package/src/intellisense/javascript/invocation.ts +1 -1
  68. package/src/intellisense/javascript/keywords.ts +3 -0
  69. package/src/intellisense/javascript/objectAccess.ts +9 -0
  70. package/src/intellisense/javascript/objectLiteral.ts +3 -3
  71. package/src/intellisense/javascript/parameters.ts +1 -1
  72. package/src/intellisense/javascript/propertyAccess.ts +8 -3
  73. package/src/intellisense/javascript/stringLiteral.ts +14 -8
  74. package/src/patterns/And.test.ts +16 -3
  75. package/src/patterns/And.ts +25 -17
  76. package/src/patterns/Cursor.ts +4 -0
  77. package/src/patterns/CursorHistory.ts +34 -5
  78. package/src/patterns/FiniteRepeat.test.ts +481 -0
  79. package/src/patterns/FiniteRepeat.ts +231 -0
  80. package/src/patterns/InfiniteRepeat.test.ts +296 -0
  81. package/src/patterns/InfiniteRepeat.ts +329 -0
  82. package/src/patterns/Literal.test.ts +13 -4
  83. package/src/patterns/Literal.ts +5 -1
  84. package/src/patterns/Not.test.ts +20 -9
  85. package/src/patterns/Not.ts +5 -1
  86. package/src/patterns/Or.test.ts +18 -7
  87. package/src/patterns/Or.ts +11 -1
  88. package/src/patterns/Pattern.ts +3 -2
  89. package/src/patterns/Reference.test.ts +18 -8
  90. package/src/patterns/Reference.ts +5 -1
  91. package/src/patterns/Regex.test.ts +13 -4
  92. package/src/patterns/Regex.ts +5 -1
  93. package/src/patterns/Repeat.test.ts +162 -158
  94. 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.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");
@@ -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.findPattern(p => p.name === "a");
148
+ const a = parent.find(p => p.name === "a");
140
149
  const nextPatterns = a?.getNextPatterns() || [];
141
- const b = parent.findPattern(p => p.name === "b")
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.findPattern(p => p.name === "nada");
171
+ const pattern = a.find(p => p.name === "nada");
163
172
 
164
173
  expect(pattern).toBeNull();
165
174
  });
@@ -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
- findPattern(_predicate: (p: Pattern) => boolean): Pattern | null {
170
+ find(_predicate: (p: Pattern) => boolean): Pattern | null {
167
171
  return null;
168
172
  }
169
173
 
@@ -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.findPattern(p => p.name === "not-about-us") as Pattern;
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.findPattern(p => p.name === "not-about-us") as Pattern;
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.findPattern(p => p.name === "not-about-us") as Pattern;
118
- const aboutUsClone = sequence.findPattern(p => p.name === "about-us") as Pattern;
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.findPattern(p => p.name === "about-us")
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.findPattern(p => p.name === "not-about-us") as Pattern;
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.findPattern(p => p.name === "not-about-us") as Pattern;
153
- const aboutUsClone = sequence.findPattern(p => p.name === "about-us") as Pattern;
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.findPattern(p => p.name === "about-us") as Pattern;
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);
@@ -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
- findPattern(predicate: (p: Pattern) => boolean): Pattern | null {
131
+ find(predicate: (p: Pattern) => boolean): Pattern | null {
128
132
  return predicate(this._children[0]) ? this._children[0] : null;
129
133
  }
130
134
 
@@ -118,7 +118,7 @@ describe("Or", () => {
118
118
  new Literal("c", "C")
119
119
  ]);
120
120
 
121
- const orClone = sequence.findPattern(p => p.name === "a-or-b") as Pattern;
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.findPattern(p => p.name === "a") as Pattern;
149
- const orClone = sequence.findPattern(p => p.name === "a-or-b") as Pattern;
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.findPattern(p => p.name === "a") as Pattern;
166
- const orClone = sequence.findPattern(p => p.name === "a-or-b") as Pattern;
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.findPattern(p => p.name === "a") as Pattern;
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.findPattern(p => p.name === "a-or-b") as Pattern;
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);
@@ -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
- findPattern(predicate: (p: Pattern) => boolean): Pattern | null {
165
+ find(predicate: (p: Pattern) => boolean): Pattern | null {
156
166
  return findPattern(this, predicate);
157
167
  }
158
168
 
@@ -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
- getNextTokens(): string[];
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.findPattern(p => p.type === "reference") as Pattern;
117
+ const reference = value.find(p => p.type === "reference") as Pattern;
118
118
 
119
- const pattern = reference?.findPattern(p => p.name === "Nada");
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.findPattern(p => p.type === "reference") as Pattern;
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.findPattern(p => p.type === "reference") as Pattern;
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.findPattern(p => p.type === "reference") as Pattern;
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.findPattern(p => p.type === "reference") as Pattern;
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
- findPattern(_predicate: (p: Pattern) => boolean): Pattern | null {
150
+ find(_predicate: (p: Pattern) => boolean): Pattern | null {
147
151
  return null;
148
152
  }
149
153