clarity-pattern-parser 10.1.25 → 10.2.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.
@@ -3,10 +3,16 @@ import { Cursor } from "./Cursor";
3
3
  import { DepthCache } from "./DepthCache";
4
4
  import { ParseResult } from "./ParseResult";
5
5
  import { Pattern } from "./Pattern";
6
+ import { findPattern } from "./findPattern";
7
+ import { Sequence } from "./Sequence";
6
8
 
7
9
  let indexId = 0;
8
10
  const depthCache = new DepthCache();
9
11
 
12
+ function createNode(name: string, children: Node[]) {
13
+ return new Node("expression", name, 0, 0, children, "");
14
+ }
15
+
10
16
  enum Association {
11
17
  left = 0,
12
18
  right = 1,
@@ -17,12 +23,15 @@ export class ExpressionPattern implements Pattern {
17
23
  private _type: string;
18
24
  private _name: string;
19
25
  private _parent: Pattern | null;
20
- private _token: string;
21
26
  private _firstIndex: number;
27
+ private _originalPatterns: Pattern[];
22
28
  private _patterns: Pattern[];
23
29
  private _unaryPatterns: Pattern[];
24
30
  private _binaryPatterns: Pattern[];
31
+ private _recursivePatterns: Pattern[];
32
+ private _recursiveNames: string[];
25
33
  private _binaryAssociation: Association[];
34
+ private _precedenceMap: Record<string, number>;
26
35
  private _binaryNames: string[];
27
36
 
28
37
  get id(): string {
@@ -37,10 +46,6 @@ export class ExpressionPattern implements Pattern {
37
46
  return this._name;
38
47
  }
39
48
 
40
- get token(): string {
41
- return this._token;
42
- }
43
-
44
49
  get parent(): Pattern | null {
45
50
  return this._parent;
46
51
  }
@@ -53,6 +58,18 @@ export class ExpressionPattern implements Pattern {
53
58
  return this._patterns;
54
59
  }
55
60
 
61
+ get unaryPatterns(): readonly Pattern[] {
62
+ return this._unaryPatterns;
63
+ }
64
+
65
+ get binaryPatterns(): readonly Pattern[] {
66
+ return this._binaryPatterns;
67
+ }
68
+
69
+ get recursivePatterns(): readonly Pattern[] {
70
+ return this._recursivePatterns;
71
+ }
72
+
56
73
  constructor(name: string, patterns: Pattern[]) {
57
74
  if (patterns.length === 0) {
58
75
  throw new Error("Need at least one pattern with an 'expression' pattern.");
@@ -61,21 +78,32 @@ export class ExpressionPattern implements Pattern {
61
78
  this._id = `expression-${indexId++}`;
62
79
  this._type = "expression";
63
80
  this._name = name;
81
+ this._parent = null;
82
+ this._firstIndex = -1;
64
83
  this._unaryPatterns = [];
65
84
  this._binaryPatterns = [];
66
-
67
- this._patterns.forEach(p => p.parent = this);
85
+ this._recursivePatterns = [];
86
+ this._recursiveNames = [];
87
+ this._binaryNames = [];
88
+ this._binaryAssociation = [];
89
+ this._precedenceMap = {};
90
+ this._originalPatterns = patterns;
68
91
  this._patterns = this._organizePatterns(patterns);
92
+
93
+ if (this._unaryPatterns.length === 0) {
94
+ throw new Error("Need at least one operand pattern with an 'expression' pattern.");
95
+ }
69
96
  }
70
97
 
71
98
  private _organizePatterns(patterns: Pattern[]) {
72
99
  const finalPatterns: Pattern[] = [];
73
100
  patterns.forEach((pattern) => {
74
101
  if (this._isBinary(pattern)) {
75
- const binaryName = pattern.name;
102
+ const binaryName = this._extractName(pattern);
76
103
  const clone = this._extractDelimiter(pattern).clone();
77
104
  clone.parent = this;
78
105
 
106
+ this._precedenceMap[binaryName] = this._binaryPatterns.length;
79
107
  this._binaryPatterns.push(clone);
80
108
  this._binaryNames.push(binaryName);
81
109
 
@@ -86,6 +114,14 @@ export class ExpressionPattern implements Pattern {
86
114
  }
87
115
 
88
116
  finalPatterns.push(clone);
117
+ } else if (this._isRecursive(pattern)) {
118
+ const name = this._extractName(pattern);
119
+ const tail = this._extractRecursiveTail(pattern);
120
+ tail.parent = this;
121
+
122
+ this._recursivePatterns.push(tail);
123
+ this._recursiveNames.push(name);
124
+ finalPatterns.push(tail);
89
125
  } else {
90
126
  const clone = pattern.clone();
91
127
  clone.parent = this;
@@ -115,10 +151,43 @@ export class ExpressionPattern implements Pattern {
115
151
  pattern.children.length === 3;
116
152
  }
117
153
 
118
- private _extractDelimiter(pattern) {
154
+ private _extractDelimiter(pattern: Pattern) {
155
+ if (pattern.type === "right-associated") {
156
+ return pattern.children[0].children[1];
157
+ }
119
158
  return pattern.children[1];
120
159
  }
121
160
 
161
+ private _extractName(pattern: Pattern) {
162
+ if (pattern.type === "right-associated") {
163
+ return pattern.children[0].name;
164
+ }
165
+
166
+ return pattern.name;
167
+ }
168
+
169
+ private _isRecursive(pattern: Pattern) {
170
+ if (pattern.type === "right-associated" && this._isRecursivePattern(pattern.children[0])) {
171
+ return true;
172
+ }
173
+
174
+ return this._isRecursivePattern(pattern);
175
+ }
176
+
177
+ private _isRecursivePattern(pattern: Pattern) {
178
+ return pattern.type === "sequence" &&
179
+ pattern.children[0].type === "reference" &&
180
+ pattern.children[0].name === this.name &&
181
+ pattern.children.length > 2;
182
+ }
183
+
184
+ private _extractRecursiveTail(pattern: Pattern) {
185
+ if (pattern.type === "right-associated") {
186
+ return new Sequence(`${pattern.children[0].name}-tail`, pattern.children[0].children.slice(1));
187
+ }
188
+ return new Sequence(`${pattern.name}-tail`, pattern.children.slice(1));
189
+ }
190
+
122
191
  parse(cursor: Cursor): Node | null {
123
192
  // This is a cache to help with speed
124
193
  this._firstIndex = cursor.index;
@@ -140,12 +209,16 @@ export class ExpressionPattern implements Pattern {
140
209
  }
141
210
 
142
211
  private _tryToParse(cursor: Cursor): Node | null {
143
- const astStack: Node[] = [];
144
- let binaryIndex: number = -1;
145
- let associationStack: Association[] = [];
212
+ if (depthCache.getDepth(this._id, this._firstIndex) > 2) {
213
+ cursor.recordErrorAt(this._firstIndex, this._firstIndex, this);
214
+ return null;
215
+ }
216
+
217
+ let lastUnaryNode: Node | null = null;
218
+ let lastBinaryNode: Node | null = null;
146
219
  let onIndex = cursor.index;
147
220
 
148
- while (true) {
221
+ outer: while (true) {
149
222
  onIndex = cursor.index;
150
223
 
151
224
  for (let i = 0; i < this._unaryPatterns.length; i++) {
@@ -153,105 +226,246 @@ export class ExpressionPattern implements Pattern {
153
226
 
154
227
  const pattern = this._unaryPatterns[i];
155
228
  const node = pattern.parse(cursor);
229
+
156
230
  if (node != null) {
157
- astStack.push(node);
231
+ lastUnaryNode = node;
158
232
  break;
233
+ } else {
234
+ lastUnaryNode = null;
235
+ cursor.resolveError();
159
236
  }
160
237
  }
161
238
 
162
- if (astStack.length === 0) {
163
- return null;
164
- } else if (astStack.length > 1) {
165
- // if (association === Association.left){
239
+ if (lastUnaryNode == null) {
240
+ break;
241
+ }
166
242
 
167
- // }
168
- } else if (astStack.length === 1) {
169
- return astStack[0]
243
+ if (cursor.hasNext()) {
244
+ cursor.next();
245
+ } else {
246
+ if (lastBinaryNode != null && lastUnaryNode != null) {
247
+ lastBinaryNode.appendChild(lastUnaryNode);
248
+ }
249
+ break;
170
250
  }
171
251
 
172
- const canContinue = cursor.hasNext();
252
+ onIndex = cursor.index;
253
+
254
+ for (let i = 0; i < this._recursivePatterns.length; i++) {
255
+ const pattern = this._recursivePatterns[i];
256
+ const node = pattern.parse(cursor);
173
257
 
174
- if (canContinue) {
175
- cursor.next();
176
- } else if (!canContinue && astStack.length > 1) {
258
+ if (node != null) {
259
+ if (lastBinaryNode != null && lastUnaryNode != null) {
260
+ lastBinaryNode.appendChild(lastUnaryNode);
261
+ }
262
+
263
+ const frontExpression = lastBinaryNode == null ? lastUnaryNode as Node : lastBinaryNode.findRoot();
264
+ const name = this._recursiveNames[i];
265
+ const recursiveNode = createNode(name, [frontExpression, ...node.children]);
266
+
267
+ recursiveNode.normalize(this._firstIndex);
268
+ return recursiveNode;
269
+ }
177
270
 
178
- } else if (!canContinue && astStack.length === 1) {
179
- return astStack[0];
271
+ cursor.moveTo(onIndex);
180
272
  }
181
273
 
182
274
  onIndex = cursor.index;
183
-
184
275
  for (let i = 0; i < this._binaryPatterns.length; i++) {
185
276
  cursor.moveTo(onIndex);
186
- const name = this._binaryNames[i];
187
- const pattern = this._binaryPatterns[i];
188
-
189
- const node = pattern.parse(cursor);
190
277
 
191
- if (node != null) {
192
- binaryIndex = i;
278
+ const pattern = this._binaryPatterns[i];
279
+ const name = this._binaryNames[i];
280
+ const delimiterNode = pattern.parse(cursor);
281
+
282
+ if (delimiterNode == null) {
283
+ if (i === this._binaryPatterns.length - 1) {
284
+ if (lastBinaryNode == null) {
285
+ return lastUnaryNode;
286
+ } else if (lastUnaryNode != null) {
287
+ lastBinaryNode.appendChild(lastUnaryNode);
288
+ }
289
+ }
290
+ continue;
291
+ }
193
292
 
194
- // const binaryNode = Node.createNode(name, []);
195
- // association = this._binaryAssociation[i];
293
+ if (lastBinaryNode == null && lastUnaryNode != null && delimiterNode != null) {
294
+ const node = createNode(name, [lastUnaryNode, delimiterNode]);
295
+ lastBinaryNode = node;
296
+ } else if (lastBinaryNode != null && lastUnaryNode != null && delimiterNode != null) {
297
+ const precedence = this._precedenceMap[name];
298
+ const lastPrecendece = lastBinaryNode == null ? 0 : this._precedenceMap[lastBinaryNode.name];
299
+ const association = this._binaryAssociation[i];
300
+
301
+ if (precedence === lastPrecendece && association === Association.right) {
302
+ const node = createNode(name, [lastUnaryNode, delimiterNode]);
303
+ lastBinaryNode.appendChild(node);
304
+ lastBinaryNode = node;
305
+ } else if (precedence === lastPrecendece) {
306
+ const node = createNode(name, []);
307
+
308
+ lastBinaryNode.replaceWith(node);
309
+ lastBinaryNode.appendChild(lastUnaryNode);
310
+ node.append(lastBinaryNode, delimiterNode);
311
+ lastBinaryNode = node;
312
+ } else if (precedence > lastPrecendece) {
313
+ const root = lastBinaryNode.findRoot();
314
+ lastBinaryNode.appendChild(lastUnaryNode);
315
+
316
+ if (root != null) {
317
+ const node = createNode(name, [root, delimiterNode]);
318
+ lastBinaryNode = node;
319
+ } else {
320
+ const node = createNode(name, [lastUnaryNode, delimiterNode]);
321
+ lastBinaryNode = node;
322
+ }
323
+
324
+ } else {
325
+ const node = createNode(name, [lastUnaryNode, delimiterNode]);
326
+ lastBinaryNode.appendChild(node);
327
+ lastBinaryNode = node;
328
+ }
196
329
 
197
- // if (association === Association.left) {
198
- // if (nodeToAppendTo != null){
199
- // nodeToAppendTo = binaryNode;
200
- // } else {
201
- // nodeToAppendTo.
202
- // }
203
- // } else {
330
+ }
204
331
 
205
- // }
332
+ if (cursor.hasNext()) {
333
+ cursor.next();
334
+ } else {
335
+ break outer;
206
336
  }
207
337
 
338
+ break;
339
+ }
340
+
341
+ if (lastBinaryNode == null){
342
+ break;
208
343
  }
209
344
  }
210
345
 
211
- return null;
212
- }
346
+ if (lastBinaryNode == null) {
347
+ return lastUnaryNode;
348
+ } else {
349
+ const root = lastBinaryNode.findAncestor(n => n.parent == null) as Node || lastBinaryNode;
350
+ if (lastBinaryNode.children.length < 3) {
351
+ lastBinaryNode.remove();
352
+
353
+ if (lastBinaryNode === root) {
354
+ return lastUnaryNode;
355
+ }
356
+ }
213
357
 
214
- exec(text: string, record?: boolean | undefined): ParseResult {
215
- throw new Error("Method not implemented.");
358
+ root.normalize(this._firstIndex);
359
+ return root;
360
+ }
216
361
  }
217
362
 
218
- test(text: string, record?: boolean | undefined): boolean {
219
- throw new Error("Method not implemented.");
363
+ test(text: string) {
364
+ const cursor = new Cursor(text);
365
+ const ast = this.parse(cursor);
366
+
367
+ return ast?.value === text;
220
368
  }
221
369
 
222
- clone(name?: string | undefined): Pattern {
223
- throw new Error("Method not implemented.");
370
+ exec(text: string, record = false): ParseResult {
371
+ const cursor = new Cursor(text);
372
+ record && cursor.startRecording();
373
+
374
+ const ast = this.parse(cursor);
375
+
376
+ return {
377
+ ast: ast?.value === text ? ast : null,
378
+ cursor
379
+ };
224
380
  }
225
381
 
226
382
  getTokens(): string[] {
227
- throw new Error("Method not implemented.");
383
+ return this.unaryPatterns.map(p => p.getTokens()).flat();
228
384
  }
229
385
 
230
386
  getTokensAfter(childReference: Pattern): string[] {
231
- throw new Error("Method not implemented.");
387
+ if (this.unaryPatterns.indexOf(childReference)) {
388
+ const recursiveTokens = this._recursivePatterns.map(p => p.getTokens()).flat();
389
+ const binaryTokens = this._binaryPatterns.map(p => p.getTokens()).flat();
390
+
391
+ return [...recursiveTokens, ...binaryTokens];
392
+ }
393
+
394
+ if (this.recursivePatterns.indexOf(childReference)) {
395
+ return this._binaryPatterns.map(p => p.getTokens()).flat();
396
+ }
397
+
398
+ if (this.binaryPatterns.indexOf(childReference)) {
399
+ const unaryTokens = this._unaryPatterns.map(p => p.getTokens()).flat();
400
+
401
+ if (this._parent != null) {
402
+ const nextTokens = this._parent.getTokensAfter(this);
403
+ return [...unaryTokens, ...nextTokens];
404
+ }
405
+
406
+ return unaryTokens;
407
+ }
408
+
409
+ return [];
232
410
  }
233
411
 
234
412
  getNextTokens(): string[] {
235
- throw new Error("Method not implemented.");
413
+ if (this._parent == null) {
414
+ return [];
415
+ }
416
+
417
+ return this._parent.getTokensAfter(this);
236
418
  }
237
419
 
238
420
  getPatterns(): Pattern[] {
239
- throw new Error("Method not implemented.");
421
+ return this.unaryPatterns.map(p => p.getPatterns()).flat();
240
422
  }
241
423
 
242
424
  getPatternsAfter(childReference: Pattern): Pattern[] {
243
- throw new Error("Method not implemented.");
425
+ if (this.unaryPatterns.indexOf(childReference)) {
426
+ const recursivePatterns = this._recursivePatterns.map(p => p.getPatterns()).flat();
427
+ const binaryPatterns = this._binaryPatterns.map(p => p.getPatterns()).flat();
428
+
429
+ return [...recursivePatterns, ...binaryPatterns];
430
+ }
431
+
432
+ if (this.recursivePatterns.indexOf(childReference)) {
433
+ return this._binaryPatterns.map(p => p.getPatterns()).flat();
434
+ }
435
+
436
+ if (this.binaryPatterns.indexOf(childReference)) {
437
+ const unaryPatterns = this._unaryPatterns.map(p => p.getPatterns()).flat();
438
+
439
+ if (this._parent != null) {
440
+ const nextPatterns = this._parent.getPatternsAfter(this);
441
+ return [...unaryPatterns, ...nextPatterns];
442
+ }
443
+
444
+ return unaryPatterns;
445
+ }
446
+
447
+ return [];
244
448
  }
245
449
 
246
450
  getNextPatterns(): Pattern[] {
247
- throw new Error("Method not implemented.");
451
+ if (this._parent == null) {
452
+ return [];
453
+ }
454
+
455
+ return this._parent.getPatternsAfter(this);
248
456
  }
249
457
 
250
- find(predicate: (pattern: Pattern) => boolean): Pattern | null {
251
- throw new Error("Method not implemented.");
458
+ find(predicate: (p: Pattern) => boolean): Pattern | null {
459
+ return findPattern(this, predicate);
252
460
  }
253
461
 
254
- isEqual(pattern: Pattern): boolean {
255
- throw new Error("Method not implemented.");
462
+ clone(name = this._name): Pattern {
463
+ const clone = new ExpressionPattern(name, this._originalPatterns);
464
+ clone._id = this._id;
465
+ return clone;
256
466
  }
257
- }
467
+
468
+ isEqual(pattern: ExpressionPattern): boolean {
469
+ return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index]));
470
+ }
471
+ }
@@ -115,7 +115,7 @@ export class FiniteRepeat implements Pattern {
115
115
  }
116
116
 
117
117
  if (this._trimDivider && this._hasDivider) {
118
- const isDividerLastMatch = this.children.length > 1 && nodes[nodes.length - 1].name === this.children[1].name;
118
+ const isDividerLastMatch = this.children.length > 1 && nodes.length > 1 && nodes[nodes.length - 1].name === this.children[1].name;
119
119
  if (isDividerLastMatch) {
120
120
  const node = nodes.pop() as Node;
121
121
  cursor.moveTo(node.firstIndex);
@@ -111,7 +111,6 @@ export class Options implements Pattern {
111
111
 
112
112
  private _tryToParse(cursor: Cursor): Node | null {
113
113
  if (depthCache.getDepth(this._id, this._firstIndex) > 2) {
114
- cursor.recordErrorAt(this._firstIndex, this._firstIndex, this);
115
114
  return null;
116
115
  }
117
116
 
@@ -6,13 +6,25 @@ import { Pattern } from "./Pattern";
6
6
  let indexId = 0;
7
7
 
8
8
  export class RightAssociatedPattern implements Pattern {
9
- readonly id: string;
10
- readonly type: string;
11
- readonly name: string;
9
+ private _id: string;
10
+ private _type: string;
11
+ private _name: string;
12
12
  private _parent: Pattern | null;
13
- readonly children: Pattern[];
13
+ private _children: Pattern[];
14
14
 
15
- get parent() {
15
+ get id(): string {
16
+ return this._id;
17
+ }
18
+
19
+ get type(): string {
20
+ return this._type;
21
+ }
22
+
23
+ get name(): string {
24
+ return this._name;
25
+ }
26
+
27
+ get parent(): Pattern | null {
16
28
  return this._parent;
17
29
  }
18
30
 
@@ -20,12 +32,16 @@ export class RightAssociatedPattern implements Pattern {
20
32
  this._parent = pattern;
21
33
  }
22
34
 
35
+ get children(): Pattern[] {
36
+ return this._children;
37
+ }
38
+
23
39
  constructor(pattern: Pattern) {
24
- this.id = `right-associated-${indexId++}`;
25
- this.type = "right-associated";
26
- this.name = "";
27
- this.parent = null;
28
- this.children = [pattern];
40
+ this._id = `right-associated-${indexId++}`;
41
+ this._type = "right-associated";
42
+ this._name = "";
43
+ this._parent = null;
44
+ this._children = [pattern.clone()];
29
45
  }
30
46
 
31
47
  parse(cursor: Cursor): Node | null {
@@ -41,7 +57,9 @@ export class RightAssociatedPattern implements Pattern {
41
57
  }
42
58
 
43
59
  clone(_name?: string | undefined): Pattern {
44
- return new RightAssociatedPattern(this.children[0]);
60
+ const clone = new RightAssociatedPattern(this.children[0]);
61
+ clone._id = this._id;
62
+ return clone;
45
63
  }
46
64
 
47
65
  getTokens(): string[] {