clarity-pattern-parser 6.0.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/TODO.md +1 -76
- package/dist/ast/Node.d.ts +1 -0
- package/dist/grammar/Grammar.d.ts +17 -0
- package/dist/grammar/patterns/andLiteral.d.ts +2 -0
- package/dist/grammar/patterns/comment.d.ts +2 -0
- package/dist/grammar/patterns/grammar.d.ts +2 -0
- package/dist/grammar/patterns/literal.d.ts +2 -0
- package/dist/grammar/patterns/name.d.ts +2 -0
- package/dist/grammar/patterns/orLiteral.d.ts +2 -0
- package/dist/grammar/patterns/pattern.d.ts +2 -0
- package/dist/grammar/patterns/regexLiteral.d.ts +2 -0
- package/dist/grammar/patterns/repeatLiteral.d.ts +3 -0
- package/dist/grammar/patterns/spaces.d.ts +2 -0
- package/dist/grammar/patterns/statement.d.ts +2 -0
- package/dist/index.browser.js +1205 -550
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.esm.js +1203 -549
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1203 -548
- package/dist/index.js.map +1 -1
- package/dist/intellisense/AutoComplete.d.ts +9 -7
- package/dist/intellisense/Suggestion.d.ts +1 -2
- package/dist/patterns/And.d.ts +2 -1
- package/dist/patterns/Cursor.d.ts +1 -0
- package/dist/patterns/CursorHistory.d.ts +2 -1
- package/dist/patterns/FiniteRepeat.d.ts +39 -0
- package/dist/patterns/InfiniteRepeat.d.ts +47 -0
- package/dist/patterns/Literal.d.ts +2 -1
- package/dist/patterns/Not.d.ts +2 -1
- package/dist/patterns/Or.d.ts +2 -1
- package/dist/patterns/Pattern.d.ts +3 -2
- package/dist/patterns/Reference.d.ts +2 -1
- package/dist/patterns/Regex.d.ts +2 -1
- package/dist/patterns/Repeat.d.ts +19 -22
- package/jest.config.js +0 -1
- package/jest.coverage.config.js +13 -0
- package/package.json +3 -3
- package/src/ast/Node.test.ts +21 -0
- package/src/ast/Node.ts +12 -6
- package/src/grammar/Grammar.test.ts +288 -0
- package/src/grammar/Grammar.ts +234 -0
- package/src/grammar/patterns/andLiteral.ts +8 -0
- package/src/grammar/patterns/comment.ts +3 -0
- package/src/grammar/patterns/grammar.ts +19 -0
- package/src/grammar/patterns/literal.ts +5 -0
- package/src/grammar/patterns/name.ts +3 -0
- package/src/grammar/patterns/orLiteral.ts +8 -0
- package/src/grammar/patterns/pattern.ts +13 -0
- package/src/grammar/patterns/regexLiteral.ts +4 -0
- package/src/grammar/patterns/repeatLiteral.ts +72 -0
- package/src/grammar/patterns/spaces.ts +4 -0
- package/src/grammar/patterns/statement.ts +35 -0
- package/src/grammar/spec.md +142 -0
- package/src/index.ts +6 -3
- package/src/intellisense/AutoComplete.test.ts +125 -39
- package/src/intellisense/AutoComplete.ts +52 -36
- package/src/intellisense/Suggestion.ts +1 -2
- package/src/intellisense/css/cssValue.ts +1 -1
- package/src/intellisense/css/method.ts +1 -1
- package/src/intellisense/css/values.ts +1 -1
- package/src/intellisense/javascript/Javascript.test.ts +34 -11
- package/src/intellisense/javascript/arrayLiteral.ts +1 -1
- package/src/intellisense/javascript/{expressionStatement.ts → assignment.ts} +7 -8
- package/src/intellisense/javascript/expression.ts +45 -27
- package/src/intellisense/javascript/infixOperator.ts +6 -2
- package/src/intellisense/javascript/invocation.ts +1 -1
- package/src/intellisense/javascript/keywords.ts +3 -0
- package/src/intellisense/javascript/objectAccess.ts +9 -0
- package/src/intellisense/javascript/objectLiteral.ts +3 -3
- package/src/intellisense/javascript/parameters.ts +1 -1
- package/src/intellisense/javascript/propertyAccess.ts +8 -3
- package/src/intellisense/javascript/stringLiteral.ts +14 -8
- package/src/patterns/And.test.ts +16 -3
- package/src/patterns/And.ts +25 -17
- package/src/patterns/Cursor.ts +4 -0
- package/src/patterns/CursorHistory.ts +34 -5
- package/src/patterns/FiniteRepeat.test.ts +481 -0
- package/src/patterns/FiniteRepeat.ts +231 -0
- package/src/patterns/InfiniteRepeat.test.ts +296 -0
- package/src/patterns/InfiniteRepeat.ts +329 -0
- package/src/patterns/Literal.test.ts +13 -4
- package/src/patterns/Literal.ts +5 -1
- package/src/patterns/Not.test.ts +20 -9
- package/src/patterns/Not.ts +5 -1
- package/src/patterns/Or.test.ts +18 -7
- package/src/patterns/Or.ts +11 -1
- package/src/patterns/Pattern.ts +3 -2
- package/src/patterns/Reference.test.ts +18 -8
- package/src/patterns/Reference.ts +5 -1
- package/src/patterns/Regex.test.ts +13 -4
- package/src/patterns/Regex.ts +5 -1
- package/src/patterns/Repeat.test.ts +162 -158
- package/src/patterns/Repeat.ts +95 -226
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
class Node {
|
|
2
|
+
constructor(type, name, firstIndex, lastIndex, children = [], value = "") {
|
|
3
|
+
this._type = type;
|
|
4
|
+
this._name = name;
|
|
5
|
+
this._firstIndex = firstIndex;
|
|
6
|
+
this._lastIndex = lastIndex;
|
|
7
|
+
this._parent = null;
|
|
8
|
+
this._children = children;
|
|
9
|
+
this._value = value;
|
|
10
|
+
this._children.forEach(c => c._parent = this);
|
|
11
|
+
}
|
|
2
12
|
get type() {
|
|
3
13
|
return this._type;
|
|
4
14
|
}
|
|
@@ -29,16 +39,6 @@ class Node {
|
|
|
29
39
|
get value() {
|
|
30
40
|
return this.toString();
|
|
31
41
|
}
|
|
32
|
-
constructor(type, name, firstIndex, lastIndex, children = [], value = "") {
|
|
33
|
-
this._type = type;
|
|
34
|
-
this._name = name;
|
|
35
|
-
this._firstIndex = firstIndex;
|
|
36
|
-
this._lastIndex = lastIndex;
|
|
37
|
-
this._parent = null;
|
|
38
|
-
this._children = children;
|
|
39
|
-
this._value = value;
|
|
40
|
-
this._children.forEach(c => c._parent = this);
|
|
41
|
-
}
|
|
42
42
|
removeChild(node) {
|
|
43
43
|
const index = this._children.indexOf(node);
|
|
44
44
|
if (index > -1) {
|
|
@@ -145,6 +145,11 @@ class Node {
|
|
|
145
145
|
this.removeAllChildren();
|
|
146
146
|
this._value = value;
|
|
147
147
|
}
|
|
148
|
+
remove() {
|
|
149
|
+
if (this._parent != null) {
|
|
150
|
+
this._parent.removeChild(this);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
148
153
|
clone() {
|
|
149
154
|
return new Node(this._type, this._name, this._firstIndex, this._lastIndex, this._children.map((c) => c.clone()), this._value);
|
|
150
155
|
}
|
|
@@ -181,7 +186,7 @@ class ParseError {
|
|
|
181
186
|
class CursorHistory {
|
|
182
187
|
constructor() {
|
|
183
188
|
this._isRecording = false;
|
|
184
|
-
this.
|
|
189
|
+
this._leafMatches = [{ pattern: null, node: null }];
|
|
185
190
|
this._furthestError = null;
|
|
186
191
|
this._currentError = null;
|
|
187
192
|
this._rootMatch = { pattern: null, node: null };
|
|
@@ -196,7 +201,10 @@ class CursorHistory {
|
|
|
196
201
|
return this._rootMatch;
|
|
197
202
|
}
|
|
198
203
|
get leafMatch() {
|
|
199
|
-
return this.
|
|
204
|
+
return this._leafMatches[this._leafMatches.length - 1];
|
|
205
|
+
}
|
|
206
|
+
get leafMatches() {
|
|
207
|
+
return this._leafMatches;
|
|
200
208
|
}
|
|
201
209
|
get furthestError() {
|
|
202
210
|
return this._furthestError;
|
|
@@ -220,10 +228,32 @@ class CursorHistory {
|
|
|
220
228
|
}
|
|
221
229
|
this._rootMatch.pattern = pattern;
|
|
222
230
|
this._rootMatch.node = node;
|
|
223
|
-
const
|
|
231
|
+
const leafMatch = this._leafMatches[this._leafMatches.length - 1];
|
|
232
|
+
const isFurthestMatch = leafMatch.node === null || node.lastIndex > leafMatch.node.lastIndex;
|
|
233
|
+
const isSameIndexMatch = leafMatch.node === null || node.lastIndex === leafMatch.node.lastIndex;
|
|
224
234
|
if (isFurthestMatch) {
|
|
225
|
-
|
|
226
|
-
this.
|
|
235
|
+
// This is to save on GC churn.
|
|
236
|
+
const match = this._leafMatches.pop();
|
|
237
|
+
match.pattern = pattern;
|
|
238
|
+
match.node = node;
|
|
239
|
+
this._leafMatches.length = 0;
|
|
240
|
+
this._leafMatches.push(match);
|
|
241
|
+
}
|
|
242
|
+
else if (isSameIndexMatch) {
|
|
243
|
+
const isAncestor = this._leafMatches.some((m) => {
|
|
244
|
+
var _a;
|
|
245
|
+
let parent = (_a = m.pattern) === null || _a === void 0 ? void 0 : _a.parent;
|
|
246
|
+
while (parent != null) {
|
|
247
|
+
if (parent == pattern.parent) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
parent = parent.parent;
|
|
251
|
+
}
|
|
252
|
+
return false;
|
|
253
|
+
});
|
|
254
|
+
if (!isAncestor) {
|
|
255
|
+
this._leafMatches.unshift({ pattern, node });
|
|
256
|
+
}
|
|
227
257
|
}
|
|
228
258
|
}
|
|
229
259
|
recordErrorAt(index, pattern) {
|
|
@@ -248,6 +278,15 @@ class CursorHistory {
|
|
|
248
278
|
}
|
|
249
279
|
|
|
250
280
|
class Cursor {
|
|
281
|
+
constructor(text) {
|
|
282
|
+
if (text.length === 0) {
|
|
283
|
+
throw new Error("Cannot have a empty string.");
|
|
284
|
+
}
|
|
285
|
+
this._text = text;
|
|
286
|
+
this._index = 0;
|
|
287
|
+
this._length = text.length;
|
|
288
|
+
this._history = new CursorHistory();
|
|
289
|
+
}
|
|
251
290
|
get text() {
|
|
252
291
|
return this._text;
|
|
253
292
|
}
|
|
@@ -266,6 +305,9 @@ class Cursor {
|
|
|
266
305
|
get leafMatch() {
|
|
267
306
|
return this._history.leafMatch;
|
|
268
307
|
}
|
|
308
|
+
get leafMatches() {
|
|
309
|
+
return this._history.leafMatches;
|
|
310
|
+
}
|
|
269
311
|
get furthestError() {
|
|
270
312
|
return this._history.furthestError;
|
|
271
313
|
}
|
|
@@ -284,15 +326,6 @@ class Cursor {
|
|
|
284
326
|
get currentChar() {
|
|
285
327
|
return this._text[this._index];
|
|
286
328
|
}
|
|
287
|
-
constructor(text) {
|
|
288
|
-
if (text.length === 0) {
|
|
289
|
-
throw new Error("Cannot have a empty string.");
|
|
290
|
-
}
|
|
291
|
-
this._text = text;
|
|
292
|
-
this._index = 0;
|
|
293
|
-
this._length = text.length;
|
|
294
|
-
this._history = new CursorHistory();
|
|
295
|
-
}
|
|
296
329
|
hasNext() {
|
|
297
330
|
return this._index + 1 < this._length;
|
|
298
331
|
}
|
|
@@ -343,7 +376,20 @@ class Cursor {
|
|
|
343
376
|
}
|
|
344
377
|
}
|
|
345
378
|
|
|
346
|
-
class
|
|
379
|
+
class Literal {
|
|
380
|
+
constructor(name, value, isOptional = false) {
|
|
381
|
+
if (value.length === 0) {
|
|
382
|
+
throw new Error("Value Cannot be empty.");
|
|
383
|
+
}
|
|
384
|
+
this._type = "literal";
|
|
385
|
+
this._name = name;
|
|
386
|
+
this._literal = value;
|
|
387
|
+
this._runes = Array.from(value);
|
|
388
|
+
this._isOptional = isOptional;
|
|
389
|
+
this._parent = null;
|
|
390
|
+
this._firstIndex = 0;
|
|
391
|
+
this._lastIndex = 0;
|
|
392
|
+
}
|
|
347
393
|
get type() {
|
|
348
394
|
return this._type;
|
|
349
395
|
}
|
|
@@ -362,6 +408,94 @@ class Regex {
|
|
|
362
408
|
get isOptional() {
|
|
363
409
|
return this._isOptional;
|
|
364
410
|
}
|
|
411
|
+
test(text) {
|
|
412
|
+
const cursor = new Cursor(text);
|
|
413
|
+
const ast = this.parse(cursor);
|
|
414
|
+
return (ast === null || ast === void 0 ? void 0 : ast.value) === text;
|
|
415
|
+
}
|
|
416
|
+
exec(text) {
|
|
417
|
+
const cursor = new Cursor(text);
|
|
418
|
+
const ast = this.parse(cursor);
|
|
419
|
+
return {
|
|
420
|
+
ast: (ast === null || ast === void 0 ? void 0 : ast.value) === text ? ast : null,
|
|
421
|
+
cursor
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
parse(cursor) {
|
|
425
|
+
this._firstIndex = cursor.index;
|
|
426
|
+
const passed = this._tryToParse(cursor);
|
|
427
|
+
if (passed) {
|
|
428
|
+
cursor.resolveError();
|
|
429
|
+
const node = this._createNode();
|
|
430
|
+
cursor.recordMatch(this, node);
|
|
431
|
+
return node;
|
|
432
|
+
}
|
|
433
|
+
if (!this._isOptional) {
|
|
434
|
+
cursor.recordErrorAt(cursor.index, this);
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
cursor.resolveError();
|
|
438
|
+
cursor.moveTo(this._firstIndex);
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
_tryToParse(cursor) {
|
|
442
|
+
let passed = false;
|
|
443
|
+
const literalRuneLength = this._runes.length;
|
|
444
|
+
for (let i = 0; i < literalRuneLength; i++) {
|
|
445
|
+
const literalRune = this._runes[i];
|
|
446
|
+
const cursorRune = cursor.currentChar;
|
|
447
|
+
if (literalRune !== cursorRune) {
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
if (i + 1 === literalRuneLength) {
|
|
451
|
+
this._lastIndex = this._firstIndex + this._literal.length - 1;
|
|
452
|
+
passed = true;
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
if (!cursor.hasNext()) {
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
cursor.next();
|
|
459
|
+
}
|
|
460
|
+
return passed;
|
|
461
|
+
}
|
|
462
|
+
_createNode() {
|
|
463
|
+
return new Node("literal", this._name, this._firstIndex, this._lastIndex, undefined, this._literal);
|
|
464
|
+
}
|
|
465
|
+
clone(name = this._name, isOptional = this._isOptional) {
|
|
466
|
+
const clone = new Literal(name, this._literal, isOptional);
|
|
467
|
+
return clone;
|
|
468
|
+
}
|
|
469
|
+
getTokens() {
|
|
470
|
+
return [this._literal];
|
|
471
|
+
}
|
|
472
|
+
getTokensAfter(_lastMatched) {
|
|
473
|
+
return [];
|
|
474
|
+
}
|
|
475
|
+
getNextTokens() {
|
|
476
|
+
if (this.parent == null) {
|
|
477
|
+
return [];
|
|
478
|
+
}
|
|
479
|
+
return this.parent.getTokensAfter(this);
|
|
480
|
+
}
|
|
481
|
+
getPatterns() {
|
|
482
|
+
return [this];
|
|
483
|
+
}
|
|
484
|
+
getPatternsAfter() {
|
|
485
|
+
return [];
|
|
486
|
+
}
|
|
487
|
+
getNextPatterns() {
|
|
488
|
+
if (this.parent == null) {
|
|
489
|
+
return [];
|
|
490
|
+
}
|
|
491
|
+
return this.parent.getPatternsAfter(this);
|
|
492
|
+
}
|
|
493
|
+
find(_predicate) {
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
class Regex {
|
|
365
499
|
constructor(name, regex, isOptional = false) {
|
|
366
500
|
this._node = null;
|
|
367
501
|
this._cursor = null;
|
|
@@ -375,6 +509,24 @@ class Regex {
|
|
|
375
509
|
this._regex = new RegExp(`^${regex}`, "g");
|
|
376
510
|
this.assertArguments();
|
|
377
511
|
}
|
|
512
|
+
get type() {
|
|
513
|
+
return this._type;
|
|
514
|
+
}
|
|
515
|
+
get name() {
|
|
516
|
+
return this._name;
|
|
517
|
+
}
|
|
518
|
+
get parent() {
|
|
519
|
+
return this._parent;
|
|
520
|
+
}
|
|
521
|
+
set parent(pattern) {
|
|
522
|
+
this._parent = pattern;
|
|
523
|
+
}
|
|
524
|
+
get children() {
|
|
525
|
+
return [];
|
|
526
|
+
}
|
|
527
|
+
get isOptional() {
|
|
528
|
+
return this._isOptional;
|
|
529
|
+
}
|
|
378
530
|
assertArguments() {
|
|
379
531
|
if (this._originalRegexString.length < 1) {
|
|
380
532
|
throw new Error("Invalid Arguments: The regex string argument needs to be at least one character long.");
|
|
@@ -449,6 +601,9 @@ class Regex {
|
|
|
449
601
|
}
|
|
450
602
|
return this.parent.getTokensAfter(this);
|
|
451
603
|
}
|
|
604
|
+
getPatterns() {
|
|
605
|
+
return [this];
|
|
606
|
+
}
|
|
452
607
|
getPatternsAfter(_childReference) {
|
|
453
608
|
return [];
|
|
454
609
|
}
|
|
@@ -458,7 +613,7 @@ class Regex {
|
|
|
458
613
|
}
|
|
459
614
|
return this.parent.getPatternsAfter(this);
|
|
460
615
|
}
|
|
461
|
-
|
|
616
|
+
find(_predicate) {
|
|
462
617
|
return null;
|
|
463
618
|
}
|
|
464
619
|
setTokens(tokens) {
|
|
@@ -466,20 +621,6 @@ class Regex {
|
|
|
466
621
|
}
|
|
467
622
|
}
|
|
468
623
|
|
|
469
|
-
function clonePatterns(patterns, isOptional) {
|
|
470
|
-
return patterns.map(p => p.clone(p.name, isOptional));
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
function filterOutNull(nodes) {
|
|
474
|
-
const filteredNodes = [];
|
|
475
|
-
for (const node of nodes) {
|
|
476
|
-
if (node !== null) {
|
|
477
|
-
filteredNodes.push(node);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
return filteredNodes;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
624
|
function findPattern(pattern, predicate) {
|
|
484
625
|
let children = [];
|
|
485
626
|
if (pattern.type === "reference") {
|
|
@@ -502,7 +643,15 @@ function findPattern(pattern, predicate) {
|
|
|
502
643
|
}
|
|
503
644
|
}
|
|
504
645
|
|
|
505
|
-
class
|
|
646
|
+
class Reference {
|
|
647
|
+
constructor(name, isOptional = false) {
|
|
648
|
+
this._type = "reference";
|
|
649
|
+
this._name = name;
|
|
650
|
+
this._parent = null;
|
|
651
|
+
this._isOptional = isOptional;
|
|
652
|
+
this._pattern = null;
|
|
653
|
+
this._children = [];
|
|
654
|
+
}
|
|
506
655
|
get type() {
|
|
507
656
|
return this._type;
|
|
508
657
|
}
|
|
@@ -521,25 +670,6 @@ class And {
|
|
|
521
670
|
get isOptional() {
|
|
522
671
|
return this._isOptional;
|
|
523
672
|
}
|
|
524
|
-
constructor(name, sequence, isOptional = false) {
|
|
525
|
-
if (sequence.length === 0) {
|
|
526
|
-
throw new Error("Need at least one pattern with an 'and' pattern.");
|
|
527
|
-
}
|
|
528
|
-
const children = clonePatterns(sequence);
|
|
529
|
-
this._assignChildrenToParent(children);
|
|
530
|
-
this._type = "and";
|
|
531
|
-
this._name = name;
|
|
532
|
-
this._isOptional = isOptional;
|
|
533
|
-
this._parent = null;
|
|
534
|
-
this._children = children;
|
|
535
|
-
this._firstIndex = -1;
|
|
536
|
-
this._nodes = [];
|
|
537
|
-
}
|
|
538
|
-
_assignChildrenToParent(children) {
|
|
539
|
-
for (const child of children) {
|
|
540
|
-
child.parent = this;
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
673
|
test(text) {
|
|
544
674
|
const cursor = new Cursor(text);
|
|
545
675
|
const ast = this.parse(cursor);
|
|
@@ -554,118 +684,48 @@ class And {
|
|
|
554
684
|
};
|
|
555
685
|
}
|
|
556
686
|
parse(cursor) {
|
|
557
|
-
this.
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
if (
|
|
561
|
-
const
|
|
562
|
-
if (
|
|
563
|
-
|
|
687
|
+
return this._getPatternSafely().parse(cursor);
|
|
688
|
+
}
|
|
689
|
+
_getPatternSafely() {
|
|
690
|
+
if (this._pattern === null) {
|
|
691
|
+
const pattern = this._findPattern();
|
|
692
|
+
if (pattern === null) {
|
|
693
|
+
throw new Error(`Couldn't find '${this._name}' pattern within tree.`);
|
|
564
694
|
}
|
|
565
|
-
|
|
695
|
+
const clonedPattern = pattern.clone(this._name, this._isOptional);
|
|
696
|
+
clonedPattern.parent = this;
|
|
697
|
+
this._pattern = clonedPattern;
|
|
698
|
+
this._children = [this._pattern];
|
|
566
699
|
}
|
|
567
|
-
|
|
568
|
-
|
|
700
|
+
return this._pattern;
|
|
701
|
+
}
|
|
702
|
+
_findPattern() {
|
|
703
|
+
const root = this._getRoot();
|
|
704
|
+
return findPattern(root, (pattern) => {
|
|
705
|
+
return pattern.name === this._name && pattern.type !== "reference";
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
_getRoot() {
|
|
709
|
+
let node = this;
|
|
710
|
+
while (true) {
|
|
711
|
+
const parent = node.parent;
|
|
712
|
+
if (parent == null) {
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
716
|
+
node = parent;
|
|
717
|
+
}
|
|
569
718
|
}
|
|
570
|
-
return
|
|
571
|
-
}
|
|
572
|
-
tryToParse(cursor) {
|
|
573
|
-
let passed = false;
|
|
574
|
-
for (let i = 0; i < this._children.length; i++) {
|
|
575
|
-
const runningCursorIndex = cursor.index;
|
|
576
|
-
const nextPatternIndex = i + 1;
|
|
577
|
-
const hasMorePatterns = nextPatternIndex < this._children.length;
|
|
578
|
-
const node = this._children[i].parse(cursor);
|
|
579
|
-
const hasNoError = !cursor.hasError;
|
|
580
|
-
const hadMatch = node !== null;
|
|
581
|
-
if (hasNoError) {
|
|
582
|
-
this._nodes.push(node);
|
|
583
|
-
if (hasMorePatterns) {
|
|
584
|
-
if (hadMatch) {
|
|
585
|
-
if (cursor.hasNext()) {
|
|
586
|
-
// We had a match. Increment the cursor and use the next pattern.
|
|
587
|
-
cursor.next();
|
|
588
|
-
continue;
|
|
589
|
-
}
|
|
590
|
-
else {
|
|
591
|
-
// We are at the end of the text, it may still be valid, if all the
|
|
592
|
-
// following patterns are optional.
|
|
593
|
-
if (this.areRemainingPatternsOptional(i)) {
|
|
594
|
-
passed = true;
|
|
595
|
-
break;
|
|
596
|
-
}
|
|
597
|
-
// We didn't finish the parsing sequence.
|
|
598
|
-
cursor.recordErrorAt(cursor.index + 1, this);
|
|
599
|
-
break;
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
else {
|
|
603
|
-
// An optional pattern did not matched, try from the same spot on the next
|
|
604
|
-
// pattern.
|
|
605
|
-
cursor.moveTo(runningCursorIndex);
|
|
606
|
-
continue;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
else {
|
|
610
|
-
// If we don't have any results from what we parsed then record error.
|
|
611
|
-
const lastNode = this.getLastValidNode();
|
|
612
|
-
if (lastNode === null) {
|
|
613
|
-
cursor.recordErrorAt(cursor.index, this);
|
|
614
|
-
break;
|
|
615
|
-
}
|
|
616
|
-
// The sequence was parsed fully.
|
|
617
|
-
passed = true;
|
|
618
|
-
break;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
else {
|
|
622
|
-
// The pattern failed.
|
|
623
|
-
cursor.moveTo(this._firstIndex);
|
|
624
|
-
break;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
return passed;
|
|
628
|
-
}
|
|
629
|
-
getLastValidNode() {
|
|
630
|
-
const nodes = filterOutNull(this._nodes);
|
|
631
|
-
if (nodes.length === 0) {
|
|
632
|
-
return null;
|
|
633
|
-
}
|
|
634
|
-
return nodes[nodes.length - 1];
|
|
635
|
-
}
|
|
636
|
-
areRemainingPatternsOptional(fromIndex) {
|
|
637
|
-
const startOnIndex = fromIndex + 1;
|
|
638
|
-
const length = this._children.length;
|
|
639
|
-
for (let i = startOnIndex; i < length; i++) {
|
|
640
|
-
const pattern = this._children[i];
|
|
641
|
-
if (!pattern.isOptional) {
|
|
642
|
-
return false;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
return true;
|
|
646
|
-
}
|
|
647
|
-
createNode(cursor) {
|
|
648
|
-
const children = filterOutNull(this._nodes);
|
|
649
|
-
const lastIndex = children[children.length - 1].lastIndex;
|
|
650
|
-
cursor.getChars(this._firstIndex, lastIndex);
|
|
651
|
-
cursor.moveTo(lastIndex);
|
|
652
|
-
return new Node("and", this._name, this._firstIndex, lastIndex, children);
|
|
719
|
+
return node;
|
|
653
720
|
}
|
|
654
721
|
getTokens() {
|
|
655
|
-
|
|
656
|
-
for (const child of this._children) {
|
|
657
|
-
tokens.push(...child.getTokens());
|
|
658
|
-
if (!child.isOptional) {
|
|
659
|
-
break;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
return tokens;
|
|
722
|
+
return this._getPatternSafely().getTokens();
|
|
663
723
|
}
|
|
664
|
-
getTokensAfter(
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
return
|
|
724
|
+
getTokensAfter(_lastMatched) {
|
|
725
|
+
if (this._parent == null) {
|
|
726
|
+
return [];
|
|
727
|
+
}
|
|
728
|
+
return this._parent.getTokensAfter(this);
|
|
669
729
|
}
|
|
670
730
|
getNextTokens() {
|
|
671
731
|
if (this.parent == null) {
|
|
@@ -673,47 +733,14 @@ class And {
|
|
|
673
733
|
}
|
|
674
734
|
return this.parent.getTokensAfter(this);
|
|
675
735
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
for (let i = 0; i < this._children.length; i++) {
|
|
682
|
-
if (this._children[i] === childReference) {
|
|
683
|
-
if (i + 1 < this._children.length) {
|
|
684
|
-
nextSibling = this._children[i + 1];
|
|
685
|
-
}
|
|
686
|
-
nextSiblingIndex = i + 1;
|
|
687
|
-
index = i;
|
|
688
|
-
break;
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
// The child reference isn't one of the child patterns.
|
|
692
|
-
if (index === -1) {
|
|
736
|
+
getPatterns() {
|
|
737
|
+
return this._getPatternSafely().getPatterns();
|
|
738
|
+
}
|
|
739
|
+
getPatternsAfter(_childReference) {
|
|
740
|
+
if (this._parent == null) {
|
|
693
741
|
return [];
|
|
694
742
|
}
|
|
695
|
-
|
|
696
|
-
if (nextSiblingIndex === this._children.length && this._parent !== null) {
|
|
697
|
-
return this._parent.getPatternsAfter(this);
|
|
698
|
-
}
|
|
699
|
-
// Next pattern isn't optional so send it back as the next patterns.
|
|
700
|
-
if (nextSibling !== null && !nextSibling.isOptional) {
|
|
701
|
-
return [nextSibling];
|
|
702
|
-
}
|
|
703
|
-
// Send back as many optional patterns as possible.
|
|
704
|
-
if (nextSibling !== null && nextSibling.isOptional) {
|
|
705
|
-
for (let i = nextSiblingIndex; i < this._children.length; i++) {
|
|
706
|
-
const child = this._children[i];
|
|
707
|
-
patterns.push(child);
|
|
708
|
-
if (!child.isOptional) {
|
|
709
|
-
break;
|
|
710
|
-
}
|
|
711
|
-
if (i === this._children.length - 1 && this._parent !== null) {
|
|
712
|
-
patterns.push(...this._parent.getPatternsAfter(this));
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
return patterns;
|
|
743
|
+
return this._parent.getPatternsAfter(this);
|
|
717
744
|
}
|
|
718
745
|
getNextPatterns() {
|
|
719
746
|
if (this.parent == null) {
|
|
@@ -721,15 +748,32 @@ class And {
|
|
|
721
748
|
}
|
|
722
749
|
return this.parent.getPatternsAfter(this);
|
|
723
750
|
}
|
|
724
|
-
|
|
725
|
-
return
|
|
751
|
+
find(_predicate) {
|
|
752
|
+
return null;
|
|
726
753
|
}
|
|
727
754
|
clone(name = this._name, isOptional = this._isOptional) {
|
|
728
|
-
return new
|
|
755
|
+
return new Reference(name, isOptional);
|
|
729
756
|
}
|
|
730
757
|
}
|
|
731
758
|
|
|
732
|
-
|
|
759
|
+
function clonePatterns(patterns, isOptional) {
|
|
760
|
+
return patterns.map(p => p.clone(p.name, isOptional));
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
class Or {
|
|
764
|
+
constructor(name, options, isOptional = false) {
|
|
765
|
+
if (options.length === 0) {
|
|
766
|
+
throw new Error("Need at least one pattern with an 'or' pattern.");
|
|
767
|
+
}
|
|
768
|
+
const children = clonePatterns(options, false);
|
|
769
|
+
this._assignChildrenToParent(children);
|
|
770
|
+
this._type = "or";
|
|
771
|
+
this._name = name;
|
|
772
|
+
this._parent = null;
|
|
773
|
+
this._children = children;
|
|
774
|
+
this._isOptional = isOptional;
|
|
775
|
+
this._firstIndex = 0;
|
|
776
|
+
}
|
|
733
777
|
get type() {
|
|
734
778
|
return this._type;
|
|
735
779
|
}
|
|
@@ -743,23 +787,15 @@ class Literal {
|
|
|
743
787
|
this._parent = pattern;
|
|
744
788
|
}
|
|
745
789
|
get children() {
|
|
746
|
-
return
|
|
790
|
+
return this._children;
|
|
747
791
|
}
|
|
748
792
|
get isOptional() {
|
|
749
793
|
return this._isOptional;
|
|
750
794
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
795
|
+
_assignChildrenToParent(children) {
|
|
796
|
+
for (const child of children) {
|
|
797
|
+
child.parent = this;
|
|
754
798
|
}
|
|
755
|
-
this._type = "literal";
|
|
756
|
-
this._name = name;
|
|
757
|
-
this._literal = value;
|
|
758
|
-
this._runes = Array.from(value);
|
|
759
|
-
this._isOptional = isOptional;
|
|
760
|
-
this._parent = null;
|
|
761
|
-
this._firstIndex = 0;
|
|
762
|
-
this._lastIndex = 0;
|
|
763
799
|
}
|
|
764
800
|
test(text) {
|
|
765
801
|
const cursor = new Cursor(text);
|
|
@@ -776,15 +812,13 @@ class Literal {
|
|
|
776
812
|
}
|
|
777
813
|
parse(cursor) {
|
|
778
814
|
this._firstIndex = cursor.index;
|
|
779
|
-
const
|
|
780
|
-
if (
|
|
815
|
+
const node = this._tryToParse(cursor);
|
|
816
|
+
if (node != null) {
|
|
781
817
|
cursor.resolveError();
|
|
782
|
-
const node = this._createNode();
|
|
783
|
-
cursor.recordMatch(this, node);
|
|
784
818
|
return node;
|
|
785
819
|
}
|
|
786
820
|
if (!this._isOptional) {
|
|
787
|
-
cursor.recordErrorAt(
|
|
821
|
+
cursor.recordErrorAt(this._firstIndex, this);
|
|
788
822
|
return null;
|
|
789
823
|
}
|
|
790
824
|
cursor.resolveError();
|
|
@@ -792,60 +826,250 @@ class Literal {
|
|
|
792
826
|
return null;
|
|
793
827
|
}
|
|
794
828
|
_tryToParse(cursor) {
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
if (literalRune !== cursorRune) {
|
|
801
|
-
break;
|
|
829
|
+
for (const pattern of this._children) {
|
|
830
|
+
cursor.moveTo(this._firstIndex);
|
|
831
|
+
const result = pattern.parse(cursor);
|
|
832
|
+
if (!cursor.hasError) {
|
|
833
|
+
return result;
|
|
802
834
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
835
|
+
cursor.resolveError();
|
|
836
|
+
}
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
839
|
+
getTokens() {
|
|
840
|
+
const tokens = [];
|
|
841
|
+
for (const child of this._children) {
|
|
842
|
+
tokens.push(...child.getTokens());
|
|
843
|
+
}
|
|
844
|
+
return tokens;
|
|
845
|
+
}
|
|
846
|
+
getTokensAfter(_childReference) {
|
|
847
|
+
if (this._parent === null) {
|
|
848
|
+
return [];
|
|
849
|
+
}
|
|
850
|
+
return this._parent.getTokensAfter(this);
|
|
851
|
+
}
|
|
852
|
+
getNextTokens() {
|
|
853
|
+
if (this._parent == null) {
|
|
854
|
+
return [];
|
|
855
|
+
}
|
|
856
|
+
return this._parent.getTokensAfter(this);
|
|
857
|
+
}
|
|
858
|
+
getPatterns() {
|
|
859
|
+
const patterns = [];
|
|
860
|
+
for (const pattern of this._children) {
|
|
861
|
+
patterns.push(...pattern.getPatterns());
|
|
862
|
+
}
|
|
863
|
+
return patterns;
|
|
864
|
+
}
|
|
865
|
+
getPatternsAfter(_childReference) {
|
|
866
|
+
if (this._parent === null) {
|
|
867
|
+
return [];
|
|
868
|
+
}
|
|
869
|
+
return this._parent.getPatternsAfter(this);
|
|
870
|
+
}
|
|
871
|
+
getNextPatterns() {
|
|
872
|
+
if (this.parent == null) {
|
|
873
|
+
return [];
|
|
874
|
+
}
|
|
875
|
+
return this.parent.getPatternsAfter(this);
|
|
876
|
+
}
|
|
877
|
+
find(predicate) {
|
|
878
|
+
return findPattern(this, predicate);
|
|
879
|
+
}
|
|
880
|
+
clone(name = this._name, isOptional = this._isOptional) {
|
|
881
|
+
const or = new Or(name, this._children, isOptional);
|
|
882
|
+
return or;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
class FiniteRepeat {
|
|
887
|
+
constructor(name, pattern, repeatAmount, options = {}) {
|
|
888
|
+
this._type = "finite-repeat";
|
|
889
|
+
this._name = name;
|
|
890
|
+
this._parent = null;
|
|
891
|
+
this._children = [];
|
|
892
|
+
this._hasDivider = options.divider != null;
|
|
893
|
+
this._min = options.min != null ? options.min : 1;
|
|
894
|
+
this._max = repeatAmount;
|
|
895
|
+
this._trimDivider = options.trimDivider == null ? false : options.trimDivider;
|
|
896
|
+
for (let i = 0; i < repeatAmount; i++) {
|
|
897
|
+
this._children.push(pattern.clone(pattern.name));
|
|
898
|
+
if (options.divider != null && (i < repeatAmount - 1 || !this._trimDivider)) {
|
|
899
|
+
this._children.push(options.divider.clone(options.divider.name, false));
|
|
807
900
|
}
|
|
808
|
-
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
get type() {
|
|
904
|
+
return this._type;
|
|
905
|
+
}
|
|
906
|
+
get name() {
|
|
907
|
+
return this._name;
|
|
908
|
+
}
|
|
909
|
+
get parent() {
|
|
910
|
+
return this._parent;
|
|
911
|
+
}
|
|
912
|
+
set parent(value) {
|
|
913
|
+
this._parent = value;
|
|
914
|
+
}
|
|
915
|
+
get children() {
|
|
916
|
+
return this._children;
|
|
917
|
+
}
|
|
918
|
+
get isOptional() {
|
|
919
|
+
return this._min === 0;
|
|
920
|
+
}
|
|
921
|
+
get min() {
|
|
922
|
+
return this._min;
|
|
923
|
+
}
|
|
924
|
+
get max() {
|
|
925
|
+
return this._max;
|
|
926
|
+
}
|
|
927
|
+
parse(cursor) {
|
|
928
|
+
const startIndex = cursor.index;
|
|
929
|
+
const nodes = [];
|
|
930
|
+
const modulo = this._hasDivider ? 2 : 1;
|
|
931
|
+
let matchCount = 0;
|
|
932
|
+
for (let i = 0; i < this._children.length; i++) {
|
|
933
|
+
const childPattern = this._children[i];
|
|
934
|
+
const node = childPattern.parse(cursor);
|
|
935
|
+
if (i % modulo === 0 && !cursor.hasError) {
|
|
936
|
+
matchCount++;
|
|
937
|
+
}
|
|
938
|
+
if (cursor.hasError) {
|
|
939
|
+
cursor.resolveError();
|
|
809
940
|
break;
|
|
810
941
|
}
|
|
811
|
-
|
|
942
|
+
if (node != null) {
|
|
943
|
+
nodes.push(node);
|
|
944
|
+
if (cursor.hasNext()) {
|
|
945
|
+
cursor.next();
|
|
946
|
+
}
|
|
947
|
+
else {
|
|
948
|
+
break;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
812
951
|
}
|
|
813
|
-
|
|
952
|
+
if (this._trimDivider && this._hasDivider) {
|
|
953
|
+
if (cursor.leafMatch.pattern === this.children[1]) {
|
|
954
|
+
const node = nodes.pop();
|
|
955
|
+
cursor.moveTo(node.firstIndex);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
if (matchCount < this._min) {
|
|
959
|
+
cursor.moveTo(startIndex);
|
|
960
|
+
cursor.recordErrorAt(startIndex, this);
|
|
961
|
+
return null;
|
|
962
|
+
}
|
|
963
|
+
else if (nodes.length === 0) {
|
|
964
|
+
cursor.resolveError();
|
|
965
|
+
cursor.moveTo(startIndex);
|
|
966
|
+
return null;
|
|
967
|
+
}
|
|
968
|
+
const firstIndex = nodes[0].firstIndex;
|
|
969
|
+
const lastIndex = nodes[nodes.length - 1].lastIndex;
|
|
970
|
+
cursor.moveTo(lastIndex);
|
|
971
|
+
return new Node(this._type, this.name, firstIndex, lastIndex, nodes);
|
|
814
972
|
}
|
|
815
|
-
|
|
816
|
-
|
|
973
|
+
test(text) {
|
|
974
|
+
const cursor = new Cursor(text);
|
|
975
|
+
const ast = this.parse(cursor);
|
|
976
|
+
return (ast === null || ast === void 0 ? void 0 : ast.value) === text;
|
|
817
977
|
}
|
|
818
|
-
|
|
819
|
-
const
|
|
820
|
-
|
|
978
|
+
exec(text) {
|
|
979
|
+
const cursor = new Cursor(text);
|
|
980
|
+
const ast = this.parse(cursor);
|
|
981
|
+
return {
|
|
982
|
+
ast: (ast === null || ast === void 0 ? void 0 : ast.value) === text ? ast : null,
|
|
983
|
+
cursor
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
clone(name = this._name, isOptional) {
|
|
987
|
+
let min = this._min;
|
|
988
|
+
if (isOptional != null) {
|
|
989
|
+
if (isOptional) {
|
|
990
|
+
min = 0;
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
min = Math.max(this._min, 1);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return new FiniteRepeat(name, this._children[0], this._max, {
|
|
997
|
+
divider: this._hasDivider ? this._children[1] : undefined,
|
|
998
|
+
min,
|
|
999
|
+
trimDivider: this._trimDivider
|
|
1000
|
+
});
|
|
821
1001
|
}
|
|
822
1002
|
getTokens() {
|
|
823
|
-
return
|
|
1003
|
+
return this._children[0].getTokens();
|
|
824
1004
|
}
|
|
825
|
-
getTokensAfter(
|
|
826
|
-
|
|
1005
|
+
getTokensAfter(childReference) {
|
|
1006
|
+
const patterns = this.getPatternsAfter(childReference);
|
|
1007
|
+
const tokens = [];
|
|
1008
|
+
patterns.forEach(p => tokens.push(...p.getTokens()));
|
|
1009
|
+
return tokens;
|
|
827
1010
|
}
|
|
828
1011
|
getNextTokens() {
|
|
829
|
-
if (this.
|
|
1012
|
+
if (this._parent == null) {
|
|
830
1013
|
return [];
|
|
831
1014
|
}
|
|
832
|
-
return this.
|
|
1015
|
+
return this._parent.getTokensAfter(this);
|
|
833
1016
|
}
|
|
834
|
-
|
|
835
|
-
return [];
|
|
1017
|
+
getPatterns() {
|
|
1018
|
+
return this._children[0].getPatterns();
|
|
1019
|
+
}
|
|
1020
|
+
getPatternsAfter(childReference) {
|
|
1021
|
+
const childIndex = this._children.indexOf(childReference);
|
|
1022
|
+
// If Reference Pattern isn't a child.
|
|
1023
|
+
if (childIndex == -1) {
|
|
1024
|
+
return [];
|
|
1025
|
+
}
|
|
1026
|
+
// If Reference Pattern is the last pattern. Ask for the parents next patterns
|
|
1027
|
+
if (childIndex === this._children.length - 1) {
|
|
1028
|
+
if (this._parent == null) {
|
|
1029
|
+
return [];
|
|
1030
|
+
}
|
|
1031
|
+
else {
|
|
1032
|
+
return this._parent.getPatternsAfter(this);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
// Get the next childs patterns.
|
|
1036
|
+
const nextChild = this._children[childIndex + 1];
|
|
1037
|
+
return nextChild.getPatterns();
|
|
836
1038
|
}
|
|
837
1039
|
getNextPatterns() {
|
|
838
|
-
if (this.
|
|
1040
|
+
if (this._parent == null) {
|
|
839
1041
|
return [];
|
|
840
1042
|
}
|
|
841
|
-
return this.
|
|
1043
|
+
return this._parent.getPatternsAfter(this);
|
|
842
1044
|
}
|
|
843
|
-
|
|
844
|
-
return
|
|
1045
|
+
find(predicate) {
|
|
1046
|
+
return findPattern(this, predicate);
|
|
845
1047
|
}
|
|
846
1048
|
}
|
|
847
1049
|
|
|
848
|
-
class
|
|
1050
|
+
class InfiniteRepeat {
|
|
1051
|
+
constructor(name, pattern, options = {}) {
|
|
1052
|
+
const min = options.min != null ? options.min : 1;
|
|
1053
|
+
const divider = options.divider;
|
|
1054
|
+
let children;
|
|
1055
|
+
if (divider != null) {
|
|
1056
|
+
children = [pattern.clone(), divider.clone(divider.name, false)];
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
children = [pattern.clone()];
|
|
1060
|
+
}
|
|
1061
|
+
this._assignChildrenToParent(children);
|
|
1062
|
+
this._type = "infinite-repeat";
|
|
1063
|
+
this._name = name;
|
|
1064
|
+
this._min = min;
|
|
1065
|
+
this._parent = null;
|
|
1066
|
+
this._children = children;
|
|
1067
|
+
this._pattern = children[0];
|
|
1068
|
+
this._divider = children[1];
|
|
1069
|
+
this._firstIndex = -1;
|
|
1070
|
+
this._nodes = [];
|
|
1071
|
+
this._trimDivider = options.trimDivider == null ? false : options.trimDivider;
|
|
1072
|
+
}
|
|
849
1073
|
get type() {
|
|
850
1074
|
return this._type;
|
|
851
1075
|
}
|
|
@@ -862,169 +1086,260 @@ class Not {
|
|
|
862
1086
|
return this._children;
|
|
863
1087
|
}
|
|
864
1088
|
get isOptional() {
|
|
865
|
-
return
|
|
1089
|
+
return this._min === 0;
|
|
866
1090
|
}
|
|
867
|
-
|
|
868
|
-
this.
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1091
|
+
get min() {
|
|
1092
|
+
return this._min;
|
|
1093
|
+
}
|
|
1094
|
+
_assignChildrenToParent(children) {
|
|
1095
|
+
for (const child of children) {
|
|
1096
|
+
child.parent = this;
|
|
1097
|
+
}
|
|
873
1098
|
}
|
|
874
1099
|
test(text) {
|
|
875
1100
|
const cursor = new Cursor(text);
|
|
876
|
-
this.parse(cursor);
|
|
877
|
-
return
|
|
1101
|
+
const ast = this.parse(cursor);
|
|
1102
|
+
return (ast === null || ast === void 0 ? void 0 : ast.value) === text;
|
|
878
1103
|
}
|
|
879
1104
|
exec(text) {
|
|
880
1105
|
const cursor = new Cursor(text);
|
|
881
1106
|
const ast = this.parse(cursor);
|
|
882
1107
|
return {
|
|
883
|
-
ast,
|
|
1108
|
+
ast: (ast === null || ast === void 0 ? void 0 : ast.value) === text ? ast : null,
|
|
884
1109
|
cursor
|
|
885
1110
|
};
|
|
886
1111
|
}
|
|
887
1112
|
parse(cursor) {
|
|
888
|
-
|
|
889
|
-
this.
|
|
890
|
-
|
|
1113
|
+
this._firstIndex = cursor.index;
|
|
1114
|
+
this._nodes = [];
|
|
1115
|
+
const passed = this._tryToParse(cursor);
|
|
1116
|
+
if (passed) {
|
|
891
1117
|
cursor.resolveError();
|
|
892
|
-
|
|
1118
|
+
const node = this._createNode(cursor);
|
|
1119
|
+
if (node != null) {
|
|
1120
|
+
cursor.moveTo(node.lastIndex);
|
|
1121
|
+
cursor.recordMatch(this, node);
|
|
1122
|
+
}
|
|
1123
|
+
return node;
|
|
1124
|
+
}
|
|
1125
|
+
if (this._min > 0) {
|
|
1126
|
+
return null;
|
|
1127
|
+
}
|
|
1128
|
+
cursor.resolveError();
|
|
1129
|
+
return null;
|
|
1130
|
+
}
|
|
1131
|
+
_meetsMin() {
|
|
1132
|
+
if (this._divider != null) {
|
|
1133
|
+
return Math.ceil(this._nodes.length / 2) >= this._min;
|
|
1134
|
+
}
|
|
1135
|
+
return this._nodes.length >= this._min;
|
|
1136
|
+
}
|
|
1137
|
+
_tryToParse(cursor) {
|
|
1138
|
+
let passed = false;
|
|
1139
|
+
while (true) {
|
|
1140
|
+
const runningCursorIndex = cursor.index;
|
|
1141
|
+
const repeatedNode = this._pattern.parse(cursor);
|
|
1142
|
+
if (cursor.hasError) {
|
|
1143
|
+
const lastValidNode = this._getLastValidNode();
|
|
1144
|
+
if (lastValidNode != null) {
|
|
1145
|
+
passed = true;
|
|
1146
|
+
}
|
|
1147
|
+
else {
|
|
1148
|
+
cursor.moveTo(runningCursorIndex);
|
|
1149
|
+
cursor.recordErrorAt(runningCursorIndex, this._pattern);
|
|
1150
|
+
passed = false;
|
|
1151
|
+
}
|
|
1152
|
+
break;
|
|
1153
|
+
}
|
|
1154
|
+
else {
|
|
1155
|
+
if (repeatedNode != null) {
|
|
1156
|
+
this._nodes.push(repeatedNode);
|
|
1157
|
+
if (!cursor.hasNext()) {
|
|
1158
|
+
passed = true;
|
|
1159
|
+
break;
|
|
1160
|
+
}
|
|
1161
|
+
cursor.next();
|
|
1162
|
+
}
|
|
1163
|
+
if (this._divider != null) {
|
|
1164
|
+
const dividerNode = this._divider.parse(cursor);
|
|
1165
|
+
if (cursor.hasError) {
|
|
1166
|
+
passed = true;
|
|
1167
|
+
break;
|
|
1168
|
+
}
|
|
1169
|
+
else if (dividerNode != null) {
|
|
1170
|
+
this._nodes.push(dividerNode);
|
|
1171
|
+
if (!cursor.hasNext()) {
|
|
1172
|
+
passed = true;
|
|
1173
|
+
break;
|
|
1174
|
+
}
|
|
1175
|
+
cursor.next();
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
const hasMinimum = this._meetsMin();
|
|
1181
|
+
if (hasMinimum) {
|
|
1182
|
+
return passed;
|
|
1183
|
+
}
|
|
1184
|
+
else if (!hasMinimum && passed) {
|
|
1185
|
+
cursor.recordErrorAt(cursor.index, this);
|
|
1186
|
+
cursor.moveTo(this._firstIndex);
|
|
1187
|
+
return false;
|
|
893
1188
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
1189
|
+
return passed;
|
|
1190
|
+
}
|
|
1191
|
+
_createNode(cursor) {
|
|
1192
|
+
const hasDivider = this._divider != null;
|
|
1193
|
+
if (hasDivider &&
|
|
1194
|
+
this._trimDivider &&
|
|
1195
|
+
cursor.leafMatch.pattern === this._divider) {
|
|
1196
|
+
const dividerNode = this._nodes.pop();
|
|
1197
|
+
cursor.moveTo(dividerNode.firstIndex);
|
|
898
1198
|
}
|
|
899
|
-
|
|
1199
|
+
const lastIndex = this._nodes[this._nodes.length - 1].lastIndex;
|
|
1200
|
+
cursor.moveTo(lastIndex);
|
|
1201
|
+
return new Node(this._type, this._name, this._firstIndex, lastIndex, this._nodes);
|
|
900
1202
|
}
|
|
901
|
-
|
|
902
|
-
const
|
|
903
|
-
|
|
1203
|
+
_getLastValidNode() {
|
|
1204
|
+
const nodes = this._nodes.filter((node) => node !== null);
|
|
1205
|
+
if (nodes.length === 0) {
|
|
1206
|
+
return null;
|
|
1207
|
+
}
|
|
1208
|
+
return nodes[nodes.length - 1];
|
|
904
1209
|
}
|
|
905
1210
|
getTokens() {
|
|
906
|
-
|
|
907
|
-
if (parent != null) {
|
|
908
|
-
return parent.getTokensAfter(this);
|
|
909
|
-
}
|
|
910
|
-
return [];
|
|
1211
|
+
return this._pattern.getTokens();
|
|
911
1212
|
}
|
|
912
|
-
getTokensAfter(
|
|
913
|
-
const
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
return [];
|
|
1213
|
+
getTokensAfter(childReference) {
|
|
1214
|
+
const patterns = this.getPatternsAfter(childReference);
|
|
1215
|
+
const tokens = [];
|
|
1216
|
+
patterns.forEach(p => tokens.push(...p.getTokens()));
|
|
1217
|
+
return tokens;
|
|
918
1218
|
}
|
|
919
1219
|
getNextTokens() {
|
|
920
|
-
if (this.
|
|
1220
|
+
if (this._parent == null) {
|
|
921
1221
|
return [];
|
|
922
1222
|
}
|
|
923
|
-
return this.
|
|
1223
|
+
return this._parent.getTokensAfter(this);
|
|
924
1224
|
}
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1225
|
+
getPatterns() {
|
|
1226
|
+
return this._pattern.getPatterns();
|
|
1227
|
+
}
|
|
1228
|
+
getPatternsAfter(childReference) {
|
|
1229
|
+
let index = -1;
|
|
1230
|
+
const patterns = [];
|
|
1231
|
+
for (let i = 0; i < this._children.length; i++) {
|
|
1232
|
+
if (this._children[i] === childReference) {
|
|
1233
|
+
index = i;
|
|
1234
|
+
}
|
|
929
1235
|
}
|
|
930
|
-
|
|
1236
|
+
// If the last match isn't a child of this pattern.
|
|
1237
|
+
if (index === -1) {
|
|
1238
|
+
return [];
|
|
1239
|
+
}
|
|
1240
|
+
// If the last match was the repeated patterns, then suggest the divider.
|
|
1241
|
+
if (index === 0 && this._divider) {
|
|
1242
|
+
patterns.push(this._children[1]);
|
|
1243
|
+
if (this._parent) {
|
|
1244
|
+
patterns.push(...this._parent.getPatternsAfter(this));
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
// Suggest the pattern because the divider was the last match.
|
|
1248
|
+
if (index === 1) {
|
|
1249
|
+
patterns.push(this._children[0]);
|
|
1250
|
+
}
|
|
1251
|
+
// If there is no divider then suggest the repeating pattern and the next pattern after.
|
|
1252
|
+
if (index === 0 && !this._divider && this._parent) {
|
|
1253
|
+
patterns.push(this._children[0]);
|
|
1254
|
+
patterns.push(...this._parent.getPatternsAfter(this));
|
|
1255
|
+
}
|
|
1256
|
+
return patterns;
|
|
931
1257
|
}
|
|
932
1258
|
getNextPatterns() {
|
|
933
|
-
if (this.
|
|
1259
|
+
if (this._parent == null) {
|
|
934
1260
|
return [];
|
|
935
1261
|
}
|
|
936
|
-
return this.
|
|
1262
|
+
return this._parent.getPatternsAfter(this);
|
|
937
1263
|
}
|
|
938
|
-
|
|
939
|
-
return
|
|
1264
|
+
find(predicate) {
|
|
1265
|
+
return findPattern(this, predicate);
|
|
1266
|
+
}
|
|
1267
|
+
clone(name = this._name, isOptional) {
|
|
1268
|
+
let min = this._min;
|
|
1269
|
+
if (isOptional != null) {
|
|
1270
|
+
if (isOptional) {
|
|
1271
|
+
min = 0;
|
|
1272
|
+
}
|
|
1273
|
+
else {
|
|
1274
|
+
min = Math.max(this._min, 1);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
return new InfiniteRepeat(name, this._pattern, {
|
|
1278
|
+
divider: this._divider == null ? undefined : this._divider,
|
|
1279
|
+
min: min,
|
|
1280
|
+
trimDivider: this._trimDivider
|
|
1281
|
+
});
|
|
940
1282
|
}
|
|
941
1283
|
}
|
|
942
1284
|
|
|
943
|
-
class
|
|
1285
|
+
class Repeat {
|
|
1286
|
+
constructor(name, pattern, options = {}) {
|
|
1287
|
+
this._pattern = pattern;
|
|
1288
|
+
this._parent = null;
|
|
1289
|
+
this._options = Object.assign(Object.assign({}, options), { min: options.min == null ? 1 : options.min, max: options.max == null ? Infinity : options.max });
|
|
1290
|
+
if (this._options.max != Infinity) {
|
|
1291
|
+
this._repeatPattern = new FiniteRepeat(name, pattern, this._options.max, this._options);
|
|
1292
|
+
}
|
|
1293
|
+
else {
|
|
1294
|
+
this._repeatPattern = new InfiniteRepeat(name, pattern, this._options);
|
|
1295
|
+
}
|
|
1296
|
+
this._children = [this._repeatPattern];
|
|
1297
|
+
this._repeatPattern.parent = this;
|
|
1298
|
+
}
|
|
944
1299
|
get type() {
|
|
945
|
-
return this.
|
|
1300
|
+
return this._repeatPattern.type;
|
|
946
1301
|
}
|
|
947
1302
|
get name() {
|
|
948
|
-
return this.
|
|
1303
|
+
return this._repeatPattern.name;
|
|
949
1304
|
}
|
|
950
1305
|
get parent() {
|
|
951
1306
|
return this._parent;
|
|
952
1307
|
}
|
|
953
|
-
set parent(
|
|
954
|
-
this._parent =
|
|
1308
|
+
set parent(value) {
|
|
1309
|
+
this._parent = value;
|
|
955
1310
|
}
|
|
956
1311
|
get children() {
|
|
957
1312
|
return this._children;
|
|
958
1313
|
}
|
|
959
1314
|
get isOptional() {
|
|
960
|
-
return this.
|
|
1315
|
+
return this._repeatPattern.isOptional;
|
|
961
1316
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
throw new Error("Need at least one pattern with an 'or' pattern.");
|
|
965
|
-
}
|
|
966
|
-
const children = clonePatterns(options, false);
|
|
967
|
-
this._assignChildrenToParent(children);
|
|
968
|
-
this._type = "or";
|
|
969
|
-
this._name = name;
|
|
970
|
-
this._parent = null;
|
|
971
|
-
this._children = children;
|
|
972
|
-
this._isOptional = isOptional;
|
|
973
|
-
this._firstIndex = 0;
|
|
974
|
-
}
|
|
975
|
-
_assignChildrenToParent(children) {
|
|
976
|
-
for (const child of children) {
|
|
977
|
-
child.parent = this;
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
test(text) {
|
|
981
|
-
const cursor = new Cursor(text);
|
|
982
|
-
const ast = this.parse(cursor);
|
|
983
|
-
return (ast === null || ast === void 0 ? void 0 : ast.value) === text;
|
|
1317
|
+
parse(cursor) {
|
|
1318
|
+
return this._repeatPattern.parse(cursor);
|
|
984
1319
|
}
|
|
985
1320
|
exec(text) {
|
|
986
|
-
|
|
987
|
-
const ast = this.parse(cursor);
|
|
988
|
-
return {
|
|
989
|
-
ast: (ast === null || ast === void 0 ? void 0 : ast.value) === text ? ast : null,
|
|
990
|
-
cursor
|
|
991
|
-
};
|
|
1321
|
+
return this._repeatPattern.exec(text);
|
|
992
1322
|
}
|
|
993
|
-
|
|
994
|
-
this.
|
|
995
|
-
const node = this._tryToParse(cursor);
|
|
996
|
-
if (node != null) {
|
|
997
|
-
cursor.resolveError();
|
|
998
|
-
return node;
|
|
999
|
-
}
|
|
1000
|
-
if (!this._isOptional) {
|
|
1001
|
-
cursor.recordErrorAt(this._firstIndex, this);
|
|
1002
|
-
return null;
|
|
1003
|
-
}
|
|
1004
|
-
cursor.resolveError();
|
|
1005
|
-
cursor.moveTo(this._firstIndex);
|
|
1006
|
-
return null;
|
|
1323
|
+
test(text) {
|
|
1324
|
+
return this._repeatPattern.test(text);
|
|
1007
1325
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1326
|
+
clone(name = this.name, isOptional) {
|
|
1327
|
+
let min = this._options.min;
|
|
1328
|
+
if (isOptional != null) {
|
|
1329
|
+
if (isOptional) {
|
|
1330
|
+
min = 0;
|
|
1331
|
+
}
|
|
1332
|
+
else {
|
|
1333
|
+
min = Math.max(this._options.min, 1);
|
|
1014
1334
|
}
|
|
1015
|
-
cursor.resolveError();
|
|
1016
1335
|
}
|
|
1017
|
-
return
|
|
1336
|
+
return new Repeat(name, this._pattern, Object.assign(Object.assign({}, this._options), { min }));
|
|
1018
1337
|
}
|
|
1019
1338
|
getTokens() {
|
|
1020
|
-
|
|
1021
|
-
for (const child of this._children) {
|
|
1022
|
-
tokens.push(...child.getTokens());
|
|
1023
|
-
}
|
|
1024
|
-
return tokens;
|
|
1339
|
+
return this._repeatPattern.getTokens();
|
|
1025
1340
|
}
|
|
1026
1341
|
getTokensAfter(_childReference) {
|
|
1027
|
-
if (this._parent
|
|
1342
|
+
if (this._parent == null) {
|
|
1028
1343
|
return [];
|
|
1029
1344
|
}
|
|
1030
1345
|
return this._parent.getTokensAfter(this);
|
|
@@ -1035,28 +1350,53 @@ class Or {
|
|
|
1035
1350
|
}
|
|
1036
1351
|
return this._parent.getTokensAfter(this);
|
|
1037
1352
|
}
|
|
1353
|
+
getPatterns() {
|
|
1354
|
+
return this._repeatPattern.getPatterns();
|
|
1355
|
+
}
|
|
1038
1356
|
getPatternsAfter(_childReference) {
|
|
1039
|
-
if (this._parent
|
|
1357
|
+
if (this._parent == null) {
|
|
1040
1358
|
return [];
|
|
1041
1359
|
}
|
|
1042
1360
|
return this._parent.getPatternsAfter(this);
|
|
1043
1361
|
}
|
|
1044
1362
|
getNextPatterns() {
|
|
1045
|
-
if (this.
|
|
1363
|
+
if (this._parent == null) {
|
|
1046
1364
|
return [];
|
|
1047
1365
|
}
|
|
1048
|
-
return this.
|
|
1366
|
+
return this._parent.getPatternsAfter(this);
|
|
1049
1367
|
}
|
|
1050
|
-
|
|
1051
|
-
return
|
|
1368
|
+
find(predicate) {
|
|
1369
|
+
return this._repeatPattern.find(predicate);
|
|
1052
1370
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
const comment = new Regex("comment", "#[^\r\n]+");
|
|
1374
|
+
|
|
1375
|
+
function filterOutNull(nodes) {
|
|
1376
|
+
const filteredNodes = [];
|
|
1377
|
+
for (const node of nodes) {
|
|
1378
|
+
if (node !== null) {
|
|
1379
|
+
filteredNodes.push(node);
|
|
1380
|
+
}
|
|
1056
1381
|
}
|
|
1382
|
+
return filteredNodes;
|
|
1057
1383
|
}
|
|
1058
1384
|
|
|
1059
|
-
class
|
|
1385
|
+
class And {
|
|
1386
|
+
constructor(name, sequence, isOptional = false) {
|
|
1387
|
+
if (sequence.length === 0) {
|
|
1388
|
+
throw new Error("Need at least one pattern with an 'and' pattern.");
|
|
1389
|
+
}
|
|
1390
|
+
const children = clonePatterns(sequence);
|
|
1391
|
+
this._assignChildrenToParent(children);
|
|
1392
|
+
this._type = "and";
|
|
1393
|
+
this._name = name;
|
|
1394
|
+
this._isOptional = isOptional;
|
|
1395
|
+
this._parent = null;
|
|
1396
|
+
this._children = children;
|
|
1397
|
+
this._firstIndex = -1;
|
|
1398
|
+
this._nodes = [];
|
|
1399
|
+
}
|
|
1060
1400
|
get type() {
|
|
1061
1401
|
return this._type;
|
|
1062
1402
|
}
|
|
@@ -1075,20 +1415,6 @@ class Repeat {
|
|
|
1075
1415
|
get isOptional() {
|
|
1076
1416
|
return this._isOptional;
|
|
1077
1417
|
}
|
|
1078
|
-
constructor(name, pattern, divider, isOptional = false) {
|
|
1079
|
-
const patterns = divider != null ? [pattern, divider] : [pattern];
|
|
1080
|
-
const children = clonePatterns(patterns, false);
|
|
1081
|
-
this._assignChildrenToParent(children);
|
|
1082
|
-
this._type = "repeat";
|
|
1083
|
-
this._name = name;
|
|
1084
|
-
this._isOptional = isOptional;
|
|
1085
|
-
this._parent = null;
|
|
1086
|
-
this._children = children;
|
|
1087
|
-
this._pattern = children[0];
|
|
1088
|
-
this._divider = children[1];
|
|
1089
|
-
this._firstIndex = -1;
|
|
1090
|
-
this._nodes = [];
|
|
1091
|
-
}
|
|
1092
1418
|
_assignChildrenToParent(children) {
|
|
1093
1419
|
for (const child of children) {
|
|
1094
1420
|
child.parent = this;
|
|
@@ -1112,92 +1438,108 @@ class Repeat {
|
|
|
1112
1438
|
this._nodes = [];
|
|
1113
1439
|
const passed = this.tryToParse(cursor);
|
|
1114
1440
|
if (passed) {
|
|
1115
|
-
cursor.resolveError();
|
|
1116
1441
|
const node = this.createNode(cursor);
|
|
1117
|
-
if (node
|
|
1442
|
+
if (node !== null) {
|
|
1118
1443
|
cursor.recordMatch(this, node);
|
|
1119
1444
|
}
|
|
1120
1445
|
return node;
|
|
1121
1446
|
}
|
|
1122
|
-
if (
|
|
1123
|
-
|
|
1447
|
+
if (this._isOptional) {
|
|
1448
|
+
cursor.resolveError();
|
|
1124
1449
|
}
|
|
1125
|
-
cursor.resolveError();
|
|
1126
|
-
cursor.moveTo(this._firstIndex);
|
|
1127
1450
|
return null;
|
|
1128
1451
|
}
|
|
1129
1452
|
tryToParse(cursor) {
|
|
1130
1453
|
let passed = false;
|
|
1131
|
-
|
|
1454
|
+
for (let i = 0; i < this._children.length; i++) {
|
|
1132
1455
|
const runningCursorIndex = cursor.index;
|
|
1133
|
-
const
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
passed = true;
|
|
1157
|
-
break;
|
|
1158
|
-
}
|
|
1159
|
-
else if (dividerNode != null) {
|
|
1160
|
-
this._nodes.push(dividerNode);
|
|
1161
|
-
if (!cursor.hasNext()) {
|
|
1162
|
-
passed = true;
|
|
1456
|
+
const nextPatternIndex = i + 1;
|
|
1457
|
+
const hasMorePatterns = nextPatternIndex < this._children.length;
|
|
1458
|
+
const node = this._children[i].parse(cursor);
|
|
1459
|
+
const hasNoError = !cursor.hasError;
|
|
1460
|
+
const hadMatch = node !== null;
|
|
1461
|
+
if (hasNoError) {
|
|
1462
|
+
this._nodes.push(node);
|
|
1463
|
+
if (hasMorePatterns) {
|
|
1464
|
+
if (hadMatch) {
|
|
1465
|
+
if (cursor.hasNext()) {
|
|
1466
|
+
// We had a match. Increment the cursor and use the next pattern.
|
|
1467
|
+
cursor.next();
|
|
1468
|
+
continue;
|
|
1469
|
+
}
|
|
1470
|
+
else {
|
|
1471
|
+
// We are at the end of the text, it may still be valid, if all the
|
|
1472
|
+
// following patterns are optional.
|
|
1473
|
+
if (this.areRemainingPatternsOptional(i)) {
|
|
1474
|
+
passed = true;
|
|
1475
|
+
break;
|
|
1476
|
+
}
|
|
1477
|
+
// We didn't finish the parsing sequence.
|
|
1478
|
+
cursor.recordErrorAt(cursor.index + 1, this);
|
|
1163
1479
|
break;
|
|
1164
1480
|
}
|
|
1165
|
-
|
|
1481
|
+
}
|
|
1482
|
+
else {
|
|
1483
|
+
// An optional pattern did not matched, try from the same spot on the next
|
|
1484
|
+
// pattern.
|
|
1485
|
+
cursor.moveTo(runningCursorIndex);
|
|
1486
|
+
continue;
|
|
1166
1487
|
}
|
|
1167
1488
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
const dividerNode = this._nodes[this._nodes.length - 1];
|
|
1180
|
-
cursor.moveTo(dividerNode.firstIndex);
|
|
1181
|
-
children = this._nodes.slice(0, this._nodes.length - 1);
|
|
1489
|
+
else {
|
|
1490
|
+
// If we don't have any results from what we parsed then record error.
|
|
1491
|
+
const lastNode = this.getLastValidNode();
|
|
1492
|
+
if (lastNode === null) {
|
|
1493
|
+
cursor.recordErrorAt(cursor.index, this);
|
|
1494
|
+
break;
|
|
1495
|
+
}
|
|
1496
|
+
// The sequence was parsed fully.
|
|
1497
|
+
passed = true;
|
|
1498
|
+
break;
|
|
1499
|
+
}
|
|
1182
1500
|
}
|
|
1183
1501
|
else {
|
|
1184
|
-
|
|
1502
|
+
// The pattern failed.
|
|
1503
|
+
cursor.moveTo(this._firstIndex);
|
|
1504
|
+
break;
|
|
1185
1505
|
}
|
|
1186
1506
|
}
|
|
1187
|
-
|
|
1188
|
-
cursor.getChars(this._firstIndex, lastIndex);
|
|
1189
|
-
cursor.moveTo(lastIndex);
|
|
1190
|
-
return new Node("repeat", this._name, this._firstIndex, lastIndex, children, undefined);
|
|
1507
|
+
return passed;
|
|
1191
1508
|
}
|
|
1192
1509
|
getLastValidNode() {
|
|
1193
|
-
const nodes = this._nodes
|
|
1510
|
+
const nodes = filterOutNull(this._nodes);
|
|
1194
1511
|
if (nodes.length === 0) {
|
|
1195
1512
|
return null;
|
|
1196
1513
|
}
|
|
1197
1514
|
return nodes[nodes.length - 1];
|
|
1198
1515
|
}
|
|
1516
|
+
areRemainingPatternsOptional(fromIndex) {
|
|
1517
|
+
const startOnIndex = fromIndex + 1;
|
|
1518
|
+
const length = this._children.length;
|
|
1519
|
+
for (let i = startOnIndex; i < length; i++) {
|
|
1520
|
+
const pattern = this._children[i];
|
|
1521
|
+
if (!pattern.isOptional) {
|
|
1522
|
+
return false;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
return true;
|
|
1526
|
+
}
|
|
1527
|
+
createNode(cursor) {
|
|
1528
|
+
const children = filterOutNull(this._nodes);
|
|
1529
|
+
const lastIndex = children[children.length - 1].lastIndex;
|
|
1530
|
+
cursor.getChars(this._firstIndex, lastIndex);
|
|
1531
|
+
cursor.moveTo(lastIndex);
|
|
1532
|
+
return new Node("and", this._name, this._firstIndex, lastIndex, children);
|
|
1533
|
+
}
|
|
1199
1534
|
getTokens() {
|
|
1200
|
-
|
|
1535
|
+
const tokens = [];
|
|
1536
|
+
for (const child of this._children) {
|
|
1537
|
+
tokens.push(...child.getTokens());
|
|
1538
|
+
if (!child.isOptional) {
|
|
1539
|
+
break;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
return tokens;
|
|
1201
1543
|
}
|
|
1202
1544
|
getTokensAfter(childReference) {
|
|
1203
1545
|
const patterns = this.getPatternsAfter(childReference);
|
|
@@ -1211,33 +1553,50 @@ class Repeat {
|
|
|
1211
1553
|
}
|
|
1212
1554
|
return this.parent.getTokensAfter(this);
|
|
1213
1555
|
}
|
|
1556
|
+
getPatterns() {
|
|
1557
|
+
const patterns = [];
|
|
1558
|
+
for (const pattern of this._children) {
|
|
1559
|
+
patterns.push(...pattern.getPatterns());
|
|
1560
|
+
if (!pattern.isOptional) {
|
|
1561
|
+
break;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
return patterns;
|
|
1565
|
+
}
|
|
1214
1566
|
getPatternsAfter(childReference) {
|
|
1215
|
-
let index = -1;
|
|
1216
1567
|
const patterns = [];
|
|
1568
|
+
let nextSiblingIndex = -1;
|
|
1569
|
+
let index = -1;
|
|
1217
1570
|
for (let i = 0; i < this._children.length; i++) {
|
|
1218
1571
|
if (this._children[i] === childReference) {
|
|
1572
|
+
if (i + 1 < this._children.length) {
|
|
1573
|
+
this._children[i + 1];
|
|
1574
|
+
}
|
|
1575
|
+
nextSiblingIndex = i + 1;
|
|
1219
1576
|
index = i;
|
|
1577
|
+
break;
|
|
1220
1578
|
}
|
|
1221
1579
|
}
|
|
1222
|
-
//
|
|
1580
|
+
// The child reference isn't one of the child patterns.
|
|
1223
1581
|
if (index === -1) {
|
|
1224
1582
|
return [];
|
|
1225
1583
|
}
|
|
1226
|
-
//
|
|
1227
|
-
if (
|
|
1228
|
-
|
|
1229
|
-
|
|
1584
|
+
// The reference pattern is the last child. So ask the parent for the next pattern.
|
|
1585
|
+
if (nextSiblingIndex === this._children.length && this._parent !== null) {
|
|
1586
|
+
return this._parent.getPatternsAfter(this);
|
|
1587
|
+
}
|
|
1588
|
+
// Send back as many optional patterns as possible.
|
|
1589
|
+
for (let i = nextSiblingIndex; i < this._children.length; i++) {
|
|
1590
|
+
const child = this._children[i];
|
|
1591
|
+
patterns.push(child);
|
|
1592
|
+
if (!child.isOptional) {
|
|
1593
|
+
break;
|
|
1594
|
+
}
|
|
1595
|
+
// If we are on the last child and its options then ask for the next pattern from the parent.
|
|
1596
|
+
if (i === this._children.length - 1 && this._parent !== null) {
|
|
1230
1597
|
patterns.push(...this._parent.getPatternsAfter(this));
|
|
1231
1598
|
}
|
|
1232
1599
|
}
|
|
1233
|
-
// Suggest the pattern because the divider was the last match.
|
|
1234
|
-
if (index === 1) {
|
|
1235
|
-
patterns.push(this._children[0]);
|
|
1236
|
-
}
|
|
1237
|
-
if (index === 0 && !this._divider && this._parent) {
|
|
1238
|
-
patterns.push(this._children[0]);
|
|
1239
|
-
patterns.push(...this._parent.getPatternsAfter(this));
|
|
1240
|
-
}
|
|
1241
1600
|
return patterns;
|
|
1242
1601
|
}
|
|
1243
1602
|
getNextPatterns() {
|
|
@@ -1246,15 +1605,138 @@ class Repeat {
|
|
|
1246
1605
|
}
|
|
1247
1606
|
return this.parent.getPatternsAfter(this);
|
|
1248
1607
|
}
|
|
1249
|
-
|
|
1608
|
+
find(predicate) {
|
|
1250
1609
|
return findPattern(this, predicate);
|
|
1251
1610
|
}
|
|
1252
1611
|
clone(name = this._name, isOptional = this._isOptional) {
|
|
1253
|
-
return new
|
|
1612
|
+
return new And(name, this._children, isOptional);
|
|
1254
1613
|
}
|
|
1255
1614
|
}
|
|
1256
1615
|
|
|
1257
|
-
|
|
1616
|
+
const name = new Regex("name", "[a-zA-Z_-]+[a-zA-Z0-9_-]*");
|
|
1617
|
+
|
|
1618
|
+
const optionalNot = new Literal("not", "!", true);
|
|
1619
|
+
const optionalIsOptional$1 = new Literal("is-optional", "?", true);
|
|
1620
|
+
const patternName$1 = name.clone("pattern-name");
|
|
1621
|
+
const pattern$1 = new And("pattern", [
|
|
1622
|
+
optionalNot,
|
|
1623
|
+
patternName$1,
|
|
1624
|
+
optionalIsOptional$1,
|
|
1625
|
+
]);
|
|
1626
|
+
|
|
1627
|
+
const divider$1 = new Regex("and-divider", "\\s*[&]\\s*");
|
|
1628
|
+
divider$1.setTokens([" & "]);
|
|
1629
|
+
const andLiteral = new Repeat("and-literal", pattern$1, { divider: divider$1, min: 2 });
|
|
1630
|
+
|
|
1631
|
+
const divider = new Regex("or-divider", "\\s*[|]\\s*");
|
|
1632
|
+
divider.setTokens([" | "]);
|
|
1633
|
+
const orLiteral = new Repeat("or-literal", name.clone("pattern-name"), { divider, min: 2 });
|
|
1634
|
+
|
|
1635
|
+
const regexLiteral = new Regex("regex-literal", "/(\\\\/|[^/\\n\\r])*/");
|
|
1636
|
+
|
|
1637
|
+
const spaces = new Regex("spaces", "[ \\t]+");
|
|
1638
|
+
spaces.setTokens([" "]);
|
|
1639
|
+
|
|
1640
|
+
const optionalIsOptional = new Literal("is-optional", "?", true);
|
|
1641
|
+
const patternName = name.clone("pattern-name");
|
|
1642
|
+
const pattern = new And("pattern", [
|
|
1643
|
+
patternName,
|
|
1644
|
+
optionalIsOptional,
|
|
1645
|
+
]);
|
|
1646
|
+
const optionalSpaces$1 = spaces.clone("optional-spaces", true);
|
|
1647
|
+
const dividerPattern = name.clone("divider-pattern");
|
|
1648
|
+
const openBracket = new Literal("open-bracket", "{");
|
|
1649
|
+
const closeBracket = new Literal("close-bracket", "}");
|
|
1650
|
+
const comma = new Literal("comma", ",");
|
|
1651
|
+
const integer = new Regex("integer", "([1-9][0-9]*)|0");
|
|
1652
|
+
integer.setTokens(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]);
|
|
1653
|
+
const optionalInteger = integer.clone("integer", true);
|
|
1654
|
+
const bounds = new And("bounds", [
|
|
1655
|
+
openBracket,
|
|
1656
|
+
optionalSpaces$1,
|
|
1657
|
+
optionalInteger.clone("min"),
|
|
1658
|
+
optionalSpaces$1,
|
|
1659
|
+
comma,
|
|
1660
|
+
optionalSpaces$1,
|
|
1661
|
+
optionalInteger.clone("max"),
|
|
1662
|
+
optionalSpaces$1,
|
|
1663
|
+
closeBracket
|
|
1664
|
+
]);
|
|
1665
|
+
const exactCount = new And("exact-count", [
|
|
1666
|
+
openBracket,
|
|
1667
|
+
optionalSpaces$1,
|
|
1668
|
+
integer,
|
|
1669
|
+
optionalSpaces$1,
|
|
1670
|
+
closeBracket,
|
|
1671
|
+
]);
|
|
1672
|
+
const quantifierShorthand = new Regex("quantifier-shorthand", "\\*|\\+");
|
|
1673
|
+
quantifierShorthand.setTokens(["*", "+"]);
|
|
1674
|
+
const quantifier = new Or("quantifier", [
|
|
1675
|
+
quantifierShorthand,
|
|
1676
|
+
exactCount,
|
|
1677
|
+
bounds
|
|
1678
|
+
]);
|
|
1679
|
+
const optional = new Literal("is-optional", "?", true);
|
|
1680
|
+
const trimDivider = new Literal("trim-divider", "-t");
|
|
1681
|
+
const openParen = new Literal("open-paren", "(");
|
|
1682
|
+
const closeParen = new Literal("close-paren", ")");
|
|
1683
|
+
const dividerComma = new Regex("divider-comma", "\\s*,\\s*");
|
|
1684
|
+
dividerComma.setTokens([", "]);
|
|
1685
|
+
const repeatLiteral = new And("repeat-literal", [
|
|
1686
|
+
openParen,
|
|
1687
|
+
optionalSpaces$1,
|
|
1688
|
+
pattern,
|
|
1689
|
+
optional,
|
|
1690
|
+
new And("optional-divider-section", [dividerComma, dividerPattern], true),
|
|
1691
|
+
optionalSpaces$1,
|
|
1692
|
+
closeParen,
|
|
1693
|
+
new And("quantifier-section", [optionalSpaces$1, quantifier]),
|
|
1694
|
+
new And("optional-trim-divider-section", [spaces, trimDivider], true)
|
|
1695
|
+
]);
|
|
1696
|
+
|
|
1697
|
+
const literal = new Regex("literal", "\"(?:\\\\[\"\\\\]|[^\n\"\\\\])*\"");
|
|
1698
|
+
|
|
1699
|
+
const optionalSpaces = spaces.clone("optional-spaces", true);
|
|
1700
|
+
const assignOperator = new Literal("assign-operator", "=");
|
|
1701
|
+
const optionalComment = comment.clone("inline-comment", true);
|
|
1702
|
+
const statements = new Or("statements", [
|
|
1703
|
+
literal,
|
|
1704
|
+
regexLiteral,
|
|
1705
|
+
orLiteral,
|
|
1706
|
+
andLiteral,
|
|
1707
|
+
repeatLiteral,
|
|
1708
|
+
]);
|
|
1709
|
+
const statement = new And("statement", [
|
|
1710
|
+
optionalSpaces,
|
|
1711
|
+
name,
|
|
1712
|
+
optionalSpaces,
|
|
1713
|
+
assignOperator,
|
|
1714
|
+
optionalSpaces,
|
|
1715
|
+
statements,
|
|
1716
|
+
optionalSpaces,
|
|
1717
|
+
optionalComment,
|
|
1718
|
+
optionalSpaces,
|
|
1719
|
+
]);
|
|
1720
|
+
|
|
1721
|
+
const whitespace = new Regex("whitespace", "[ \\t]+");
|
|
1722
|
+
const newLine = new Regex("new-line", "(\\r?\\n)+");
|
|
1723
|
+
whitespace.setTokens([" "]);
|
|
1724
|
+
newLine.setTokens(["\n"]);
|
|
1725
|
+
const line = new Or("line", [
|
|
1726
|
+
comment,
|
|
1727
|
+
statement,
|
|
1728
|
+
whitespace
|
|
1729
|
+
], true);
|
|
1730
|
+
const grammar = new Repeat("grammar", line, { divider: newLine });
|
|
1731
|
+
|
|
1732
|
+
class Not {
|
|
1733
|
+
constructor(name, pattern) {
|
|
1734
|
+
this._type = "not";
|
|
1735
|
+
this._name = name;
|
|
1736
|
+
this._parent = null;
|
|
1737
|
+
this._children = [pattern.clone(pattern.name, false)];
|
|
1738
|
+
this._children[0].parent = this;
|
|
1739
|
+
}
|
|
1258
1740
|
get type() {
|
|
1259
1741
|
return this._type;
|
|
1260
1742
|
}
|
|
@@ -1271,72 +1753,52 @@ class Reference {
|
|
|
1271
1753
|
return this._children;
|
|
1272
1754
|
}
|
|
1273
1755
|
get isOptional() {
|
|
1274
|
-
return
|
|
1275
|
-
}
|
|
1276
|
-
constructor(name, isOptional = false) {
|
|
1277
|
-
this._type = "reference";
|
|
1278
|
-
this._name = name;
|
|
1279
|
-
this._parent = null;
|
|
1280
|
-
this._isOptional = isOptional;
|
|
1281
|
-
this._pattern = null;
|
|
1282
|
-
this._children = [];
|
|
1756
|
+
return false;
|
|
1283
1757
|
}
|
|
1284
1758
|
test(text) {
|
|
1285
1759
|
const cursor = new Cursor(text);
|
|
1286
|
-
|
|
1287
|
-
return
|
|
1760
|
+
this.parse(cursor);
|
|
1761
|
+
return !cursor.hasError;
|
|
1288
1762
|
}
|
|
1289
1763
|
exec(text) {
|
|
1290
1764
|
const cursor = new Cursor(text);
|
|
1291
1765
|
const ast = this.parse(cursor);
|
|
1292
1766
|
return {
|
|
1293
|
-
ast
|
|
1767
|
+
ast,
|
|
1294
1768
|
cursor
|
|
1295
1769
|
};
|
|
1296
1770
|
}
|
|
1297
1771
|
parse(cursor) {
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
if (pattern === null) {
|
|
1304
|
-
throw new Error(`Couldn't find '${this._name}' pattern within tree.`);
|
|
1305
|
-
}
|
|
1306
|
-
const clonedPattern = pattern.clone(this._name, this._isOptional);
|
|
1307
|
-
clonedPattern.parent = this;
|
|
1308
|
-
this._pattern = clonedPattern;
|
|
1309
|
-
this._children = [this._pattern];
|
|
1772
|
+
const firstIndex = cursor.index;
|
|
1773
|
+
this._children[0].parse(cursor);
|
|
1774
|
+
if (cursor.hasError) {
|
|
1775
|
+
cursor.resolveError();
|
|
1776
|
+
cursor.moveTo(firstIndex);
|
|
1310
1777
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
return findPattern(root, (pattern) => {
|
|
1316
|
-
return pattern.name === this._name && pattern.type !== "reference";
|
|
1317
|
-
});
|
|
1318
|
-
}
|
|
1319
|
-
_getRoot() {
|
|
1320
|
-
let node = this;
|
|
1321
|
-
while (true) {
|
|
1322
|
-
const parent = node.parent;
|
|
1323
|
-
if (parent == null) {
|
|
1324
|
-
break;
|
|
1325
|
-
}
|
|
1326
|
-
else {
|
|
1327
|
-
node = parent;
|
|
1328
|
-
}
|
|
1778
|
+
else {
|
|
1779
|
+
cursor.moveTo(firstIndex);
|
|
1780
|
+
cursor.resolveError();
|
|
1781
|
+
cursor.recordErrorAt(firstIndex, this);
|
|
1329
1782
|
}
|
|
1330
|
-
return
|
|
1783
|
+
return null;
|
|
1784
|
+
}
|
|
1785
|
+
clone(name = this._name) {
|
|
1786
|
+
const not = new Not(name, this._children[0]);
|
|
1787
|
+
return not;
|
|
1331
1788
|
}
|
|
1332
1789
|
getTokens() {
|
|
1333
|
-
|
|
1790
|
+
const parent = this._parent;
|
|
1791
|
+
if (parent != null) {
|
|
1792
|
+
return parent.getTokensAfter(this);
|
|
1793
|
+
}
|
|
1794
|
+
return [];
|
|
1334
1795
|
}
|
|
1335
|
-
getTokensAfter(
|
|
1336
|
-
|
|
1337
|
-
|
|
1796
|
+
getTokensAfter(_childReference) {
|
|
1797
|
+
const parent = this._parent;
|
|
1798
|
+
if (parent != null) {
|
|
1799
|
+
return parent.getTokensAfter(this);
|
|
1338
1800
|
}
|
|
1339
|
-
return
|
|
1801
|
+
return [];
|
|
1340
1802
|
}
|
|
1341
1803
|
getNextTokens() {
|
|
1342
1804
|
if (this.parent == null) {
|
|
@@ -1344,11 +1806,15 @@ class Reference {
|
|
|
1344
1806
|
}
|
|
1345
1807
|
return this.parent.getTokensAfter(this);
|
|
1346
1808
|
}
|
|
1809
|
+
getPatterns() {
|
|
1810
|
+
return [...this.getNextPatterns().map(p => p.getPatterns()).flat()];
|
|
1811
|
+
}
|
|
1347
1812
|
getPatternsAfter(_childReference) {
|
|
1348
|
-
|
|
1349
|
-
|
|
1813
|
+
const parent = this._parent;
|
|
1814
|
+
if (parent != null) {
|
|
1815
|
+
return parent.getPatternsAfter(this);
|
|
1350
1816
|
}
|
|
1351
|
-
return
|
|
1817
|
+
return [];
|
|
1352
1818
|
}
|
|
1353
1819
|
getNextPatterns() {
|
|
1354
1820
|
if (this.parent == null) {
|
|
@@ -1356,11 +1822,8 @@ class Reference {
|
|
|
1356
1822
|
}
|
|
1357
1823
|
return this.parent.getPatternsAfter(this);
|
|
1358
1824
|
}
|
|
1359
|
-
|
|
1360
|
-
return null;
|
|
1361
|
-
}
|
|
1362
|
-
clone(name = this._name, isOptional = this._isOptional) {
|
|
1363
|
-
return new Reference(name, isOptional);
|
|
1825
|
+
find(predicate) {
|
|
1826
|
+
return predicate(this._children[0]) ? this._children[0] : null;
|
|
1364
1827
|
}
|
|
1365
1828
|
}
|
|
1366
1829
|
|
|
@@ -1371,72 +1834,73 @@ class AutoComplete {
|
|
|
1371
1834
|
this._options = options;
|
|
1372
1835
|
this._text = "";
|
|
1373
1836
|
}
|
|
1374
|
-
|
|
1837
|
+
suggestFor(text) {
|
|
1375
1838
|
if (text.length === 0) {
|
|
1376
1839
|
return {
|
|
1377
1840
|
isComplete: false,
|
|
1378
|
-
options: this.
|
|
1379
|
-
|
|
1841
|
+
options: this._createSuggestionsFromRoot(),
|
|
1842
|
+
errorAtIndex: 0,
|
|
1380
1843
|
cursor: null,
|
|
1381
1844
|
ast: null
|
|
1382
1845
|
};
|
|
1383
1846
|
}
|
|
1384
1847
|
this._text = text;
|
|
1385
1848
|
this._cursor = new Cursor(text);
|
|
1849
|
+
let errorAtIndex = null;
|
|
1386
1850
|
const ast = this._pattern.parse(this._cursor);
|
|
1387
|
-
const leafPattern = this._cursor.leafMatch.pattern;
|
|
1388
1851
|
const isComplete = (ast === null || ast === void 0 ? void 0 : ast.value) === text;
|
|
1389
|
-
const options = this.
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1852
|
+
const options = this._getAllOptions();
|
|
1853
|
+
if (this._cursor.hasError && this._cursor.furthestError != null) {
|
|
1854
|
+
errorAtIndex = this._cursor.furthestError.index;
|
|
1855
|
+
errorAtIndex = options.reduce((errorAtIndex, option) => Math.max(errorAtIndex, option.startIndex), errorAtIndex);
|
|
1393
1856
|
}
|
|
1394
1857
|
return {
|
|
1395
1858
|
isComplete: isComplete,
|
|
1396
1859
|
options: options,
|
|
1397
|
-
|
|
1860
|
+
errorAtIndex,
|
|
1398
1861
|
cursor: this._cursor,
|
|
1399
1862
|
ast,
|
|
1400
1863
|
};
|
|
1401
1864
|
}
|
|
1402
|
-
|
|
1865
|
+
_getAllOptions() {
|
|
1866
|
+
return this._cursor.leafMatches.map((m) => this._createSuggestionsFromMatch(m)).flat();
|
|
1867
|
+
}
|
|
1868
|
+
_createSuggestionsFromRoot() {
|
|
1403
1869
|
const suggestions = [];
|
|
1404
1870
|
const tokens = this._pattern.getTokens();
|
|
1405
1871
|
for (const token of tokens) {
|
|
1406
|
-
suggestions.push(this.
|
|
1872
|
+
suggestions.push(this._createSuggestion("", token));
|
|
1407
1873
|
}
|
|
1408
1874
|
return suggestions;
|
|
1409
1875
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
return this.createSuggestions(-1, this._getTokensForPattern(this._pattern));
|
|
1876
|
+
_createSuggestionsFromMatch(match) {
|
|
1877
|
+
if (!match.pattern) {
|
|
1878
|
+
return this._createSuggestions(-1, this._getTokensForPattern(this._pattern));
|
|
1414
1879
|
}
|
|
1415
|
-
const leafPattern =
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
if (parent !== null && leafMatch.node != null) {
|
|
1880
|
+
const leafPattern = match.pattern;
|
|
1881
|
+
const parent = match.pattern.parent;
|
|
1882
|
+
if (parent !== null && match.node != null) {
|
|
1419
1883
|
const patterns = leafPattern.getNextPatterns();
|
|
1420
1884
|
const tokens = patterns.reduce((acc, pattern) => {
|
|
1421
1885
|
acc.push(...this._getTokensForPattern(pattern));
|
|
1422
1886
|
return acc;
|
|
1423
1887
|
}, []);
|
|
1424
|
-
return this.
|
|
1888
|
+
return this._createSuggestions(match.node.lastIndex, tokens);
|
|
1425
1889
|
}
|
|
1426
1890
|
else {
|
|
1427
1891
|
return [];
|
|
1428
1892
|
}
|
|
1429
1893
|
}
|
|
1430
1894
|
_getTokensForPattern(pattern) {
|
|
1431
|
-
|
|
1432
|
-
|
|
1895
|
+
const augmentedTokens = this._getAugmentedTokens(pattern);
|
|
1896
|
+
if (this._options.greedyPatternNames != null && this._options.greedyPatternNames.includes(pattern.name)) {
|
|
1433
1897
|
const nextPatterns = pattern.getNextPatterns();
|
|
1434
1898
|
const tokens = [];
|
|
1435
1899
|
const nextPatternTokens = nextPatterns.reduce((acc, pattern) => {
|
|
1436
1900
|
acc.push(...this._getTokensForPattern(pattern));
|
|
1437
1901
|
return acc;
|
|
1438
1902
|
}, []);
|
|
1439
|
-
for (let token of
|
|
1903
|
+
for (let token of augmentedTokens) {
|
|
1440
1904
|
for (let nextPatternToken of nextPatternTokens) {
|
|
1441
1905
|
tokens.push(token + nextPatternToken);
|
|
1442
1906
|
}
|
|
@@ -1444,13 +1908,20 @@ class AutoComplete {
|
|
|
1444
1908
|
return tokens;
|
|
1445
1909
|
}
|
|
1446
1910
|
else {
|
|
1447
|
-
|
|
1448
|
-
const customTokens = this._options.customTokens[pattern.name] || [];
|
|
1449
|
-
tokens.push(...customTokens);
|
|
1450
|
-
return tokens;
|
|
1911
|
+
return augmentedTokens;
|
|
1451
1912
|
}
|
|
1452
1913
|
}
|
|
1453
|
-
|
|
1914
|
+
_getAugmentedTokens(pattern) {
|
|
1915
|
+
const customTokensMap = this._options.customTokens || {};
|
|
1916
|
+
const leafPatterns = pattern.getPatterns();
|
|
1917
|
+
const tokens = customTokensMap[pattern.name] || [];
|
|
1918
|
+
leafPatterns.forEach(p => {
|
|
1919
|
+
const augmentedTokens = customTokensMap[p.name] || [];
|
|
1920
|
+
tokens.push(...p.getTokens(), ...augmentedTokens);
|
|
1921
|
+
});
|
|
1922
|
+
return tokens;
|
|
1923
|
+
}
|
|
1924
|
+
_createSuggestions(lastIndex, tokens) {
|
|
1454
1925
|
let substring = lastIndex === -1 ? "" : this._cursor.getChars(0, lastIndex);
|
|
1455
1926
|
const suggestionStrings = [];
|
|
1456
1927
|
const options = [];
|
|
@@ -1461,14 +1932,14 @@ class AutoComplete {
|
|
|
1461
1932
|
const isSameAsText = suggestion === this._text;
|
|
1462
1933
|
if (startsWith && !alreadyExist && !isSameAsText) {
|
|
1463
1934
|
suggestionStrings.push(suggestion);
|
|
1464
|
-
options.push(this.
|
|
1935
|
+
options.push(this._createSuggestion(this._cursor.text, suggestion));
|
|
1465
1936
|
}
|
|
1466
1937
|
}
|
|
1467
1938
|
const reducedOptions = getFurthestOptions(options);
|
|
1468
1939
|
reducedOptions.sort((a, b) => a.text.localeCompare(b.text));
|
|
1469
1940
|
return reducedOptions;
|
|
1470
1941
|
}
|
|
1471
|
-
|
|
1942
|
+
_createSuggestion(fullText, suggestion) {
|
|
1472
1943
|
const furthestMatch = findMatchIndex(suggestion, fullText);
|
|
1473
1944
|
const text = suggestion.slice(furthestMatch);
|
|
1474
1945
|
return {
|
|
@@ -1508,5 +1979,188 @@ function getFurthestOptions(options) {
|
|
|
1508
1979
|
return furthestOptions;
|
|
1509
1980
|
}
|
|
1510
1981
|
|
|
1511
|
-
|
|
1982
|
+
class ParseContext {
|
|
1983
|
+
constructor() {
|
|
1984
|
+
this.patternsByName = new Map();
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
class Grammar {
|
|
1988
|
+
constructor() {
|
|
1989
|
+
this._parseContext = new ParseContext();
|
|
1990
|
+
this._autoComplete = new AutoComplete(grammar, {
|
|
1991
|
+
greedyPatternNames: ["spaces", "optional-spaces", "whitespace", "new-line"],
|
|
1992
|
+
customTokens: {
|
|
1993
|
+
"regex-literal": ["[Regular Expression]"],
|
|
1994
|
+
"literal": ["[String]"],
|
|
1995
|
+
"name": ["[Pattern Name]"],
|
|
1996
|
+
"pattern-name": ["[Pattern Name]"]
|
|
1997
|
+
}
|
|
1998
|
+
});
|
|
1999
|
+
}
|
|
2000
|
+
parse(expression) {
|
|
2001
|
+
this._parseContext = new ParseContext();
|
|
2002
|
+
this._tryToParse(expression);
|
|
2003
|
+
return this._parseContext.patternsByName;
|
|
2004
|
+
}
|
|
2005
|
+
_tryToParse(expression) {
|
|
2006
|
+
const { ast, cursor, options, isComplete } = this._autoComplete.suggestFor(expression);
|
|
2007
|
+
if (!isComplete) {
|
|
2008
|
+
const text = (cursor === null || cursor === void 0 ? void 0 : cursor.text) || "";
|
|
2009
|
+
const index = options.reduce((num, o) => Math.max(o.startIndex, num), 0);
|
|
2010
|
+
const foundText = text.slice(Math.max(index - 10, 0), index + 10);
|
|
2011
|
+
const expectedTexts = "'" + options.map(o => {
|
|
2012
|
+
const startText = text.slice(Math.max(o.startIndex - 10), o.startIndex);
|
|
2013
|
+
return startText + o.text;
|
|
2014
|
+
}).join("' or '") + "'";
|
|
2015
|
+
const message = `[Parse Error] Found: '${foundText}', expected: ${expectedTexts}.`;
|
|
2016
|
+
throw new Error(message);
|
|
2017
|
+
}
|
|
2018
|
+
// If it is complete it will always have a node. So we have to cast it.
|
|
2019
|
+
this._cleanAst(ast);
|
|
2020
|
+
this._buildPatterns(ast);
|
|
2021
|
+
}
|
|
2022
|
+
_cleanAst(ast) {
|
|
2023
|
+
ast.findAll(n => n.name === "spaces" ||
|
|
2024
|
+
n.name === "optional-spaces" ||
|
|
2025
|
+
n.name === "new-line" ||
|
|
2026
|
+
n.name.includes("whitespace") ||
|
|
2027
|
+
n.name.includes("comment")).forEach(n => n.remove());
|
|
2028
|
+
}
|
|
2029
|
+
_buildPatterns(ast) {
|
|
2030
|
+
ast.children.forEach((n) => {
|
|
2031
|
+
const typeNode = n.find(n => n.name.includes("literal"));
|
|
2032
|
+
const type = (typeNode === null || typeNode === void 0 ? void 0 : typeNode.name) || "unknown";
|
|
2033
|
+
switch (type) {
|
|
2034
|
+
case "literal": {
|
|
2035
|
+
this._buildLiteral(n);
|
|
2036
|
+
break;
|
|
2037
|
+
}
|
|
2038
|
+
case "regex-literal": {
|
|
2039
|
+
this._buildRegex(n);
|
|
2040
|
+
break;
|
|
2041
|
+
}
|
|
2042
|
+
case "or-literal": {
|
|
2043
|
+
this._buildOr(n);
|
|
2044
|
+
break;
|
|
2045
|
+
}
|
|
2046
|
+
case "and-literal": {
|
|
2047
|
+
this._buildAnd(n);
|
|
2048
|
+
break;
|
|
2049
|
+
}
|
|
2050
|
+
case "repeat-literal": {
|
|
2051
|
+
this._buildRepeat(n);
|
|
2052
|
+
break;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
_buildLiteral(statementNode) {
|
|
2058
|
+
const nameNode = statementNode.find(n => n.name === "name");
|
|
2059
|
+
const literalNode = statementNode.find(n => n.name === "literal");
|
|
2060
|
+
const value = literalNode.value.slice(1, literalNode.value.length - 1);
|
|
2061
|
+
const name = nameNode.value;
|
|
2062
|
+
const literal = new Literal(name, value);
|
|
2063
|
+
this._parseContext.patternsByName.set(name, literal);
|
|
2064
|
+
}
|
|
2065
|
+
_buildRegex(statementNode) {
|
|
2066
|
+
const nameNode = statementNode.find(n => n.name === "name");
|
|
2067
|
+
const regexNode = statementNode.find(n => n.name === "regex-literal");
|
|
2068
|
+
const value = regexNode.value.slice(1, regexNode.value.length - 1);
|
|
2069
|
+
const name = nameNode.value;
|
|
2070
|
+
const regex = new Regex(name, value);
|
|
2071
|
+
this._parseContext.patternsByName.set(name, regex);
|
|
2072
|
+
}
|
|
2073
|
+
_buildOr(statementNode) {
|
|
2074
|
+
const nameNode = statementNode.find(n => n.name === "name");
|
|
2075
|
+
const orNode = statementNode.find(n => n.name === "or-literal");
|
|
2076
|
+
const patternNodes = orNode.children.filter(n => n.name == "pattern-name");
|
|
2077
|
+
const name = nameNode.value;
|
|
2078
|
+
const patterns = patternNodes.map(n => this._getPattern(n.value));
|
|
2079
|
+
const or = new Or(name, patterns);
|
|
2080
|
+
this._parseContext.patternsByName.set(name, or);
|
|
2081
|
+
}
|
|
2082
|
+
_getPattern(name) {
|
|
2083
|
+
const pattern = this._parseContext.patternsByName.get(name);
|
|
2084
|
+
if (pattern == null) {
|
|
2085
|
+
return new Reference(name);
|
|
2086
|
+
}
|
|
2087
|
+
return pattern;
|
|
2088
|
+
}
|
|
2089
|
+
_buildAnd(statementNode) {
|
|
2090
|
+
const nameNode = statementNode.find(n => n.name === "name");
|
|
2091
|
+
const andNode = statementNode.find(n => n.name === "and-literal");
|
|
2092
|
+
const patternNodes = andNode.children.filter(n => n.name == "pattern");
|
|
2093
|
+
const name = nameNode.value;
|
|
2094
|
+
const patterns = patternNodes.map(n => {
|
|
2095
|
+
const nameNode = n.find(n => n.name === "pattern-name");
|
|
2096
|
+
const isNot = n.find(n => n.name === "not") != null;
|
|
2097
|
+
const isOptional = n.find(n => n.name === "is-optional") != null;
|
|
2098
|
+
const name = nameNode.value;
|
|
2099
|
+
const pattern = this._getPattern(name);
|
|
2100
|
+
if (isNot) {
|
|
2101
|
+
return new Not(`not-${name}`, pattern.clone(name, isOptional));
|
|
2102
|
+
}
|
|
2103
|
+
return pattern.clone(name, isOptional);
|
|
2104
|
+
});
|
|
2105
|
+
const and = new And(name, patterns);
|
|
2106
|
+
this._parseContext.patternsByName.set(name, and);
|
|
2107
|
+
}
|
|
2108
|
+
_buildRepeat(statementNode) {
|
|
2109
|
+
const nameNode = statementNode.find(n => n.name === "name");
|
|
2110
|
+
const repeatNode = statementNode.find(n => n.name === "repeat-literal");
|
|
2111
|
+
const patternNode = repeatNode.find(n => n.name == "pattern");
|
|
2112
|
+
const patternNameNode = patternNode.find(n => n.name === "pattern-name");
|
|
2113
|
+
const dividerNode = repeatNode.find(n => n.name === "divider-pattern");
|
|
2114
|
+
const bounds = repeatNode.find(n => n.name === "bounds");
|
|
2115
|
+
const exactCount = repeatNode.find(n => n.name === "exact-count");
|
|
2116
|
+
const quantifier = repeatNode.find(n => n.name === "quantifier-shorthand");
|
|
2117
|
+
const isPatternOptional = repeatNode.find(n => n.name === "is-optional") != null;
|
|
2118
|
+
const trimDivider = repeatNode.find(n => n.name === "trim-divider") != null;
|
|
2119
|
+
const name = nameNode.value;
|
|
2120
|
+
const pattern = this._getPattern(patternNameNode.value);
|
|
2121
|
+
const options = {
|
|
2122
|
+
min: 1,
|
|
2123
|
+
max: Infinity
|
|
2124
|
+
};
|
|
2125
|
+
if (trimDivider) {
|
|
2126
|
+
options.trimDivider = trimDivider;
|
|
2127
|
+
}
|
|
2128
|
+
if (dividerNode != null) {
|
|
2129
|
+
options.divider = this._getPattern(dividerNode.value);
|
|
2130
|
+
}
|
|
2131
|
+
if (bounds != null) {
|
|
2132
|
+
const minNode = bounds.find(p => p.name === "min");
|
|
2133
|
+
const maxNode = bounds.find(p => p.name === "max");
|
|
2134
|
+
const min = minNode == null ? 0 : Number(minNode.value);
|
|
2135
|
+
const max = maxNode == null ? Infinity : Number(maxNode.value);
|
|
2136
|
+
options.min = min;
|
|
2137
|
+
options.max = max;
|
|
2138
|
+
}
|
|
2139
|
+
else if (exactCount != null) {
|
|
2140
|
+
const integerNode = exactCount.find(p => p.name === "integer");
|
|
2141
|
+
const integer = Number(integerNode.value);
|
|
2142
|
+
options.min = integer;
|
|
2143
|
+
options.max = integer;
|
|
2144
|
+
}
|
|
2145
|
+
else if (quantifier != null) {
|
|
2146
|
+
const type = quantifier.value;
|
|
2147
|
+
if (type === "+") {
|
|
2148
|
+
options.min = 1;
|
|
2149
|
+
options.max = Infinity;
|
|
2150
|
+
}
|
|
2151
|
+
else {
|
|
2152
|
+
options.min = 0;
|
|
2153
|
+
options.max = Infinity;
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
const repeat = new Repeat(name, pattern.clone(pattern.name, isPatternOptional), options);
|
|
2157
|
+
this._parseContext.patternsByName.set(name, repeat);
|
|
2158
|
+
}
|
|
2159
|
+
static parse(expression) {
|
|
2160
|
+
const grammar = new Grammar();
|
|
2161
|
+
return grammar.parse(expression);
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
export { And, AutoComplete, Cursor, CursorHistory, Grammar, Literal, Node, Not, Or, ParseError, Reference, Regex, Repeat };
|
|
1512
2166
|
//# sourceMappingURL=index.esm.js.map
|