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