clarity-pattern-parser 8.2.0 → 8.3.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/.vscode/settings.json +5 -1
- package/README.md +2 -2
- package/dist/grammar/Grammar.d.ts +22 -3
- package/dist/grammar/patterns/import.d.ts +2 -0
- package/dist/index.browser.js +325 -193
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +323 -191
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +323 -191
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/ast/Node.ts +1 -1
- package/src/grammar/Grammar.test.ts +67 -21
- package/src/grammar/Grammar.ts +120 -13
- package/src/grammar/patterns/grammar.ts +9 -6
- package/src/grammar/patterns/import.ts +29 -0
- package/src/grammar/spec.md +26 -2
- package/src/patterns/And.ts +1 -5
- package/src/patterns/FiniteRepeat.ts +2 -1
- package/src/patterns/InfiniteRepeat.test.ts +9 -23
- package/src/patterns/InfiniteRepeat.ts +7 -2
- package/src/patterns/Or.ts +2 -1
package/dist/index.js
CHANGED
|
@@ -3,16 +3,6 @@
|
|
|
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
|
-
}
|
|
16
6
|
get type() {
|
|
17
7
|
return this._type;
|
|
18
8
|
}
|
|
@@ -43,6 +33,16 @@ class Node {
|
|
|
43
33
|
get value() {
|
|
44
34
|
return this.toString();
|
|
45
35
|
}
|
|
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) {
|
|
@@ -180,6 +180,36 @@ class Node {
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
/******************************************************************************
|
|
184
|
+
Copyright (c) Microsoft Corporation.
|
|
185
|
+
|
|
186
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
187
|
+
purpose with or without fee is hereby granted.
|
|
188
|
+
|
|
189
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
190
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
191
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
192
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
193
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
194
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
195
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
196
|
+
***************************************************************************** */
|
|
197
|
+
|
|
198
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
199
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
200
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
201
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
202
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
203
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
204
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
209
|
+
var e = new Error(message);
|
|
210
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
211
|
+
};
|
|
212
|
+
|
|
183
213
|
class ParseError {
|
|
184
214
|
constructor(startIndex, endIndex, pattern) {
|
|
185
215
|
this.startIndex = startIndex;
|
|
@@ -283,12 +313,6 @@ class CursorHistory {
|
|
|
283
313
|
}
|
|
284
314
|
|
|
285
315
|
class Cursor {
|
|
286
|
-
constructor(text) {
|
|
287
|
-
this._text = text;
|
|
288
|
-
this._index = 0;
|
|
289
|
-
this._length = text.length;
|
|
290
|
-
this._history = new CursorHistory();
|
|
291
|
-
}
|
|
292
316
|
get text() {
|
|
293
317
|
return this._text;
|
|
294
318
|
}
|
|
@@ -331,6 +355,12 @@ class Cursor {
|
|
|
331
355
|
get currentChar() {
|
|
332
356
|
return this._text[this._index];
|
|
333
357
|
}
|
|
358
|
+
constructor(text) {
|
|
359
|
+
this._text = text;
|
|
360
|
+
this._index = 0;
|
|
361
|
+
this._length = text.length;
|
|
362
|
+
this._history = new CursorHistory();
|
|
363
|
+
}
|
|
334
364
|
hasNext() {
|
|
335
365
|
return this._index + 1 < this._length;
|
|
336
366
|
}
|
|
@@ -382,20 +412,6 @@ class Cursor {
|
|
|
382
412
|
}
|
|
383
413
|
|
|
384
414
|
class Literal {
|
|
385
|
-
constructor(name, value, isOptional = false) {
|
|
386
|
-
if (value.length === 0) {
|
|
387
|
-
throw new Error("Value Cannot be empty.");
|
|
388
|
-
}
|
|
389
|
-
this._type = "literal";
|
|
390
|
-
this._name = name;
|
|
391
|
-
this._literal = value;
|
|
392
|
-
this._runes = Array.from(value);
|
|
393
|
-
this._isOptional = isOptional;
|
|
394
|
-
this._parent = null;
|
|
395
|
-
this._firstIndex = 0;
|
|
396
|
-
this._lastIndex = 0;
|
|
397
|
-
this._endIndex = 0;
|
|
398
|
-
}
|
|
399
415
|
get type() {
|
|
400
416
|
return this._type;
|
|
401
417
|
}
|
|
@@ -414,6 +430,20 @@ class Literal {
|
|
|
414
430
|
get isOptional() {
|
|
415
431
|
return this._isOptional;
|
|
416
432
|
}
|
|
433
|
+
constructor(name, value, isOptional = false) {
|
|
434
|
+
if (value.length === 0) {
|
|
435
|
+
throw new Error("Value Cannot be empty.");
|
|
436
|
+
}
|
|
437
|
+
this._type = "literal";
|
|
438
|
+
this._name = name;
|
|
439
|
+
this._literal = value;
|
|
440
|
+
this._runes = Array.from(value);
|
|
441
|
+
this._isOptional = isOptional;
|
|
442
|
+
this._parent = null;
|
|
443
|
+
this._firstIndex = 0;
|
|
444
|
+
this._lastIndex = 0;
|
|
445
|
+
this._endIndex = 0;
|
|
446
|
+
}
|
|
417
447
|
test(text) {
|
|
418
448
|
const cursor = new Cursor(text);
|
|
419
449
|
const ast = this.parse(cursor);
|
|
@@ -504,20 +534,6 @@ class Literal {
|
|
|
504
534
|
}
|
|
505
535
|
|
|
506
536
|
class Regex {
|
|
507
|
-
constructor(name, regex, isOptional = false) {
|
|
508
|
-
this._node = null;
|
|
509
|
-
this._cursor = null;
|
|
510
|
-
this._firstIndex = -1;
|
|
511
|
-
this._substring = "";
|
|
512
|
-
this._tokens = [];
|
|
513
|
-
this._type = "regex";
|
|
514
|
-
this._name = name;
|
|
515
|
-
this._isOptional = isOptional;
|
|
516
|
-
this._parent = null;
|
|
517
|
-
this._originalRegexString = regex;
|
|
518
|
-
this._regex = new RegExp(`^${regex}`, "g");
|
|
519
|
-
this.assertArguments();
|
|
520
|
-
}
|
|
521
537
|
get type() {
|
|
522
538
|
return this._type;
|
|
523
539
|
}
|
|
@@ -536,6 +552,20 @@ class Regex {
|
|
|
536
552
|
get isOptional() {
|
|
537
553
|
return this._isOptional;
|
|
538
554
|
}
|
|
555
|
+
constructor(name, regex, isOptional = false) {
|
|
556
|
+
this._node = null;
|
|
557
|
+
this._cursor = null;
|
|
558
|
+
this._firstIndex = -1;
|
|
559
|
+
this._substring = "";
|
|
560
|
+
this._tokens = [];
|
|
561
|
+
this._type = "regex";
|
|
562
|
+
this._name = name;
|
|
563
|
+
this._isOptional = isOptional;
|
|
564
|
+
this._parent = null;
|
|
565
|
+
this._originalRegexString = regex;
|
|
566
|
+
this._regex = new RegExp(`^${regex}`, "g");
|
|
567
|
+
this.assertArguments();
|
|
568
|
+
}
|
|
539
569
|
assertArguments() {
|
|
540
570
|
if (this._originalRegexString.length < 1) {
|
|
541
571
|
throw new Error("Invalid Arguments: The regex string argument needs to be at least one character long.");
|
|
@@ -654,14 +684,6 @@ function findPattern(pattern, predicate) {
|
|
|
654
684
|
}
|
|
655
685
|
|
|
656
686
|
class Reference {
|
|
657
|
-
constructor(name, isOptional = false) {
|
|
658
|
-
this._type = "reference";
|
|
659
|
-
this._name = name;
|
|
660
|
-
this._parent = null;
|
|
661
|
-
this._isOptional = isOptional;
|
|
662
|
-
this._pattern = null;
|
|
663
|
-
this._children = [];
|
|
664
|
-
}
|
|
665
687
|
get type() {
|
|
666
688
|
return this._type;
|
|
667
689
|
}
|
|
@@ -680,6 +702,14 @@ class Reference {
|
|
|
680
702
|
get isOptional() {
|
|
681
703
|
return this._isOptional;
|
|
682
704
|
}
|
|
705
|
+
constructor(name, isOptional = false) {
|
|
706
|
+
this._type = "reference";
|
|
707
|
+
this._name = name;
|
|
708
|
+
this._parent = null;
|
|
709
|
+
this._isOptional = isOptional;
|
|
710
|
+
this._pattern = null;
|
|
711
|
+
this._children = [];
|
|
712
|
+
}
|
|
683
713
|
test(text) {
|
|
684
714
|
const cursor = new Cursor(text);
|
|
685
715
|
const ast = this.parse(cursor);
|
|
@@ -771,20 +801,6 @@ function clonePatterns(patterns, isOptional) {
|
|
|
771
801
|
}
|
|
772
802
|
|
|
773
803
|
class Or {
|
|
774
|
-
constructor(name, options, isOptional = false, isGreedy = false) {
|
|
775
|
-
if (options.length === 0) {
|
|
776
|
-
throw new Error("Need at least one pattern with an 'or' pattern.");
|
|
777
|
-
}
|
|
778
|
-
const children = clonePatterns(options, false);
|
|
779
|
-
this._assignChildrenToParent(children);
|
|
780
|
-
this._type = "or";
|
|
781
|
-
this._name = name;
|
|
782
|
-
this._parent = null;
|
|
783
|
-
this._children = children;
|
|
784
|
-
this._isOptional = isOptional;
|
|
785
|
-
this._firstIndex = 0;
|
|
786
|
-
this._isGreedy = isGreedy;
|
|
787
|
-
}
|
|
788
804
|
get type() {
|
|
789
805
|
return this._type;
|
|
790
806
|
}
|
|
@@ -803,6 +819,20 @@ class Or {
|
|
|
803
819
|
get isOptional() {
|
|
804
820
|
return this._isOptional;
|
|
805
821
|
}
|
|
822
|
+
constructor(name, options, isOptional = false, isGreedy = false) {
|
|
823
|
+
if (options.length === 0) {
|
|
824
|
+
throw new Error("Need at least one pattern with an 'or' pattern.");
|
|
825
|
+
}
|
|
826
|
+
const children = clonePatterns(options, false);
|
|
827
|
+
this._assignChildrenToParent(children);
|
|
828
|
+
this._type = "or";
|
|
829
|
+
this._name = name;
|
|
830
|
+
this._parent = null;
|
|
831
|
+
this._children = children;
|
|
832
|
+
this._isOptional = isOptional;
|
|
833
|
+
this._firstIndex = 0;
|
|
834
|
+
this._isGreedy = isGreedy;
|
|
835
|
+
}
|
|
806
836
|
_assignChildrenToParent(children) {
|
|
807
837
|
for (const child of children) {
|
|
808
838
|
child.parent = this;
|
|
@@ -825,6 +855,7 @@ class Or {
|
|
|
825
855
|
this._firstIndex = cursor.index;
|
|
826
856
|
const node = this._tryToParse(cursor);
|
|
827
857
|
if (node != null) {
|
|
858
|
+
cursor.moveTo(node.lastIndex);
|
|
828
859
|
cursor.resolveError();
|
|
829
860
|
return node;
|
|
830
861
|
}
|
|
@@ -895,28 +926,12 @@ class Or {
|
|
|
895
926
|
return findPattern(this, predicate);
|
|
896
927
|
}
|
|
897
928
|
clone(name = this._name, isOptional = this._isOptional) {
|
|
898
|
-
const or = new Or(name, this._children, isOptional);
|
|
929
|
+
const or = new Or(name, this._children, isOptional, this._isGreedy);
|
|
899
930
|
return or;
|
|
900
931
|
}
|
|
901
932
|
}
|
|
902
933
|
|
|
903
934
|
class FiniteRepeat {
|
|
904
|
-
constructor(name, pattern, repeatAmount, options = {}) {
|
|
905
|
-
this._type = "finite-repeat";
|
|
906
|
-
this._name = name;
|
|
907
|
-
this._parent = null;
|
|
908
|
-
this._children = [];
|
|
909
|
-
this._hasDivider = options.divider != null;
|
|
910
|
-
this._min = options.min != null ? options.min : 1;
|
|
911
|
-
this._max = repeatAmount;
|
|
912
|
-
this._trimDivider = options.trimDivider == null ? false : options.trimDivider;
|
|
913
|
-
for (let i = 0; i < repeatAmount; i++) {
|
|
914
|
-
this._children.push(pattern.clone(pattern.name));
|
|
915
|
-
if (options.divider != null && (i < repeatAmount - 1 || !this._trimDivider)) {
|
|
916
|
-
this._children.push(options.divider.clone(options.divider.name, false));
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
935
|
get type() {
|
|
921
936
|
return this._type;
|
|
922
937
|
}
|
|
@@ -941,6 +956,22 @@ class FiniteRepeat {
|
|
|
941
956
|
get max() {
|
|
942
957
|
return this._max;
|
|
943
958
|
}
|
|
959
|
+
constructor(name, pattern, repeatAmount, options = {}) {
|
|
960
|
+
this._type = "finite-repeat";
|
|
961
|
+
this._name = name;
|
|
962
|
+
this._parent = null;
|
|
963
|
+
this._children = [];
|
|
964
|
+
this._hasDivider = options.divider != null;
|
|
965
|
+
this._min = options.min != null ? options.min : 1;
|
|
966
|
+
this._max = repeatAmount;
|
|
967
|
+
this._trimDivider = options.trimDivider == null ? false : options.trimDivider;
|
|
968
|
+
for (let i = 0; i < repeatAmount; i++) {
|
|
969
|
+
this._children.push(pattern.clone(pattern.name));
|
|
970
|
+
if (options.divider != null && (i < repeatAmount - 1 || !this._trimDivider)) {
|
|
971
|
+
this._children.push(options.divider.clone(options.divider.name, false));
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
944
975
|
parse(cursor) {
|
|
945
976
|
const startIndex = cursor.index;
|
|
946
977
|
const nodes = [];
|
|
@@ -973,8 +1004,9 @@ class FiniteRepeat {
|
|
|
973
1004
|
}
|
|
974
1005
|
}
|
|
975
1006
|
if (matchCount < this._min) {
|
|
1007
|
+
const lastIndex = cursor.index;
|
|
976
1008
|
cursor.moveTo(startIndex);
|
|
977
|
-
cursor.recordErrorAt(startIndex,
|
|
1009
|
+
cursor.recordErrorAt(startIndex, lastIndex, this);
|
|
978
1010
|
return null;
|
|
979
1011
|
}
|
|
980
1012
|
else if (nodes.length === 0) {
|
|
@@ -1065,28 +1097,6 @@ class FiniteRepeat {
|
|
|
1065
1097
|
}
|
|
1066
1098
|
|
|
1067
1099
|
class InfiniteRepeat {
|
|
1068
|
-
constructor(name, pattern, options = {}) {
|
|
1069
|
-
const min = options.min != null ? options.min : 1;
|
|
1070
|
-
const divider = options.divider;
|
|
1071
|
-
let children;
|
|
1072
|
-
if (divider != null) {
|
|
1073
|
-
children = [pattern.clone(), divider.clone(divider.name, false)];
|
|
1074
|
-
}
|
|
1075
|
-
else {
|
|
1076
|
-
children = [pattern.clone()];
|
|
1077
|
-
}
|
|
1078
|
-
this._assignChildrenToParent(children);
|
|
1079
|
-
this._type = "infinite-repeat";
|
|
1080
|
-
this._name = name;
|
|
1081
|
-
this._min = min;
|
|
1082
|
-
this._parent = null;
|
|
1083
|
-
this._children = children;
|
|
1084
|
-
this._pattern = children[0];
|
|
1085
|
-
this._divider = children[1];
|
|
1086
|
-
this._firstIndex = -1;
|
|
1087
|
-
this._nodes = [];
|
|
1088
|
-
this._trimDivider = options.trimDivider == null ? false : options.trimDivider;
|
|
1089
|
-
}
|
|
1090
1100
|
get type() {
|
|
1091
1101
|
return this._type;
|
|
1092
1102
|
}
|
|
@@ -1108,6 +1118,28 @@ class InfiniteRepeat {
|
|
|
1108
1118
|
get min() {
|
|
1109
1119
|
return this._min;
|
|
1110
1120
|
}
|
|
1121
|
+
constructor(name, pattern, options = {}) {
|
|
1122
|
+
const min = options.min != null ? options.min : 1;
|
|
1123
|
+
const divider = options.divider;
|
|
1124
|
+
let children;
|
|
1125
|
+
if (divider != null) {
|
|
1126
|
+
children = [pattern.clone(pattern.name, false), divider.clone(divider.name, false)];
|
|
1127
|
+
}
|
|
1128
|
+
else {
|
|
1129
|
+
children = [pattern.clone(pattern.name, false)];
|
|
1130
|
+
}
|
|
1131
|
+
this._assignChildrenToParent(children);
|
|
1132
|
+
this._type = "infinite-repeat";
|
|
1133
|
+
this._name = name;
|
|
1134
|
+
this._min = min;
|
|
1135
|
+
this._parent = null;
|
|
1136
|
+
this._children = children;
|
|
1137
|
+
this._pattern = children[0];
|
|
1138
|
+
this._divider = children[1];
|
|
1139
|
+
this._firstIndex = -1;
|
|
1140
|
+
this._nodes = [];
|
|
1141
|
+
this._trimDivider = options.trimDivider == null ? false : options.trimDivider;
|
|
1142
|
+
}
|
|
1111
1143
|
_assignChildrenToParent(children) {
|
|
1112
1144
|
for (const child of children) {
|
|
1113
1145
|
child.parent = this;
|
|
@@ -1214,6 +1246,10 @@ class InfiniteRepeat {
|
|
|
1214
1246
|
const dividerNode = this._nodes.pop();
|
|
1215
1247
|
cursor.moveTo(dividerNode.firstIndex);
|
|
1216
1248
|
}
|
|
1249
|
+
// if (this._nodes.length === 0) {
|
|
1250
|
+
// cursor.moveTo(this._firstIndex);
|
|
1251
|
+
// return null;
|
|
1252
|
+
// }
|
|
1217
1253
|
const lastIndex = this._nodes[this._nodes.length - 1].lastIndex;
|
|
1218
1254
|
cursor.moveTo(lastIndex);
|
|
1219
1255
|
return new Node(this._type, this._name, this._firstIndex, lastIndex, this._nodes);
|
|
@@ -1301,19 +1337,6 @@ class InfiniteRepeat {
|
|
|
1301
1337
|
}
|
|
1302
1338
|
|
|
1303
1339
|
class Repeat {
|
|
1304
|
-
constructor(name, pattern, options = {}) {
|
|
1305
|
-
this._pattern = pattern;
|
|
1306
|
-
this._parent = null;
|
|
1307
|
-
this._options = Object.assign(Object.assign({}, options), { min: options.min == null ? 1 : options.min, max: options.max == null ? Infinity : options.max });
|
|
1308
|
-
if (this._options.max !== Infinity) {
|
|
1309
|
-
this._repeatPattern = new FiniteRepeat(name, pattern, this._options.max, this._options);
|
|
1310
|
-
}
|
|
1311
|
-
else {
|
|
1312
|
-
this._repeatPattern = new InfiniteRepeat(name, pattern, this._options);
|
|
1313
|
-
}
|
|
1314
|
-
this._children = [this._repeatPattern];
|
|
1315
|
-
this._repeatPattern.parent = this;
|
|
1316
|
-
}
|
|
1317
1340
|
get type() {
|
|
1318
1341
|
return this._repeatPattern.type;
|
|
1319
1342
|
}
|
|
@@ -1332,6 +1355,19 @@ class Repeat {
|
|
|
1332
1355
|
get isOptional() {
|
|
1333
1356
|
return this._repeatPattern.isOptional;
|
|
1334
1357
|
}
|
|
1358
|
+
constructor(name, pattern, options = {}) {
|
|
1359
|
+
this._pattern = pattern;
|
|
1360
|
+
this._parent = null;
|
|
1361
|
+
this._options = Object.assign(Object.assign({}, options), { min: options.min == null ? 1 : options.min, max: options.max == null ? Infinity : options.max });
|
|
1362
|
+
if (this._options.max !== Infinity) {
|
|
1363
|
+
this._repeatPattern = new FiniteRepeat(name, pattern, this._options.max, this._options);
|
|
1364
|
+
}
|
|
1365
|
+
else {
|
|
1366
|
+
this._repeatPattern = new InfiniteRepeat(name, pattern, this._options);
|
|
1367
|
+
}
|
|
1368
|
+
this._children = [this._repeatPattern];
|
|
1369
|
+
this._repeatPattern.parent = this;
|
|
1370
|
+
}
|
|
1335
1371
|
parse(cursor) {
|
|
1336
1372
|
return this._repeatPattern.parse(cursor);
|
|
1337
1373
|
}
|
|
@@ -1401,20 +1437,6 @@ function filterOutNull(nodes) {
|
|
|
1401
1437
|
}
|
|
1402
1438
|
|
|
1403
1439
|
class And {
|
|
1404
|
-
constructor(name, sequence, isOptional = false) {
|
|
1405
|
-
if (sequence.length === 0) {
|
|
1406
|
-
throw new Error("Need at least one pattern with an 'and' pattern.");
|
|
1407
|
-
}
|
|
1408
|
-
const children = clonePatterns(sequence);
|
|
1409
|
-
this._assignChildrenToParent(children);
|
|
1410
|
-
this._type = "and";
|
|
1411
|
-
this._name = name;
|
|
1412
|
-
this._isOptional = isOptional;
|
|
1413
|
-
this._parent = null;
|
|
1414
|
-
this._children = children;
|
|
1415
|
-
this._firstIndex = -1;
|
|
1416
|
-
this._nodes = [];
|
|
1417
|
-
}
|
|
1418
1440
|
get type() {
|
|
1419
1441
|
return this._type;
|
|
1420
1442
|
}
|
|
@@ -1433,6 +1455,20 @@ class And {
|
|
|
1433
1455
|
get isOptional() {
|
|
1434
1456
|
return this._isOptional;
|
|
1435
1457
|
}
|
|
1458
|
+
constructor(name, sequence, isOptional = false) {
|
|
1459
|
+
if (sequence.length === 0) {
|
|
1460
|
+
throw new Error("Need at least one pattern with an 'and' pattern.");
|
|
1461
|
+
}
|
|
1462
|
+
const children = clonePatterns(sequence);
|
|
1463
|
+
this._assignChildrenToParent(children);
|
|
1464
|
+
this._type = "and";
|
|
1465
|
+
this._name = name;
|
|
1466
|
+
this._isOptional = isOptional;
|
|
1467
|
+
this._parent = null;
|
|
1468
|
+
this._children = children;
|
|
1469
|
+
this._firstIndex = -1;
|
|
1470
|
+
this._nodes = [];
|
|
1471
|
+
}
|
|
1436
1472
|
_assignChildrenToParent(children) {
|
|
1437
1473
|
for (const child of children) {
|
|
1438
1474
|
child.parent = this;
|
|
@@ -1545,7 +1581,6 @@ class And {
|
|
|
1545
1581
|
createNode(cursor) {
|
|
1546
1582
|
const children = filterOutNull(this._nodes);
|
|
1547
1583
|
const lastIndex = children[children.length - 1].lastIndex;
|
|
1548
|
-
cursor.getChars(this._firstIndex, lastIndex);
|
|
1549
1584
|
cursor.moveTo(lastIndex);
|
|
1550
1585
|
return new Node("and", this._name, this._firstIndex, lastIndex, children);
|
|
1551
1586
|
}
|
|
@@ -1587,9 +1622,6 @@ class And {
|
|
|
1587
1622
|
let index = -1;
|
|
1588
1623
|
for (let i = 0; i < this._children.length; i++) {
|
|
1589
1624
|
if (this._children[i] === childReference) {
|
|
1590
|
-
if (i + 1 < this._children.length) {
|
|
1591
|
-
this._children[i + 1];
|
|
1592
|
-
}
|
|
1593
1625
|
nextSiblingIndex = i + 1;
|
|
1594
1626
|
index = i;
|
|
1595
1627
|
break;
|
|
@@ -1631,11 +1663,11 @@ class And {
|
|
|
1631
1663
|
}
|
|
1632
1664
|
}
|
|
1633
1665
|
|
|
1634
|
-
const name = new Regex("name", "[a-zA-Z_-]+[a-zA-Z0-9_-]*");
|
|
1666
|
+
const name$1 = new Regex("name", "[a-zA-Z_-]+[a-zA-Z0-9_-]*");
|
|
1635
1667
|
|
|
1636
1668
|
const optionalNot = new Literal("not", "!", true);
|
|
1637
1669
|
const optionalIsOptional$1 = new Literal("is-optional", "?", true);
|
|
1638
|
-
const patternName$1 = name.clone("pattern-name");
|
|
1670
|
+
const patternName$1 = name$1.clone("pattern-name");
|
|
1639
1671
|
const pattern$1 = new And("pattern", [
|
|
1640
1672
|
optionalNot,
|
|
1641
1673
|
patternName$1,
|
|
@@ -1648,44 +1680,44 @@ const andLiteral = new Repeat("and-literal", pattern$1, { divider: divider$1, mi
|
|
|
1648
1680
|
|
|
1649
1681
|
const divider = new Regex("or-divider", "\\s*[|]\\s*");
|
|
1650
1682
|
divider.setTokens([" | "]);
|
|
1651
|
-
const orLiteral = new Repeat("or-literal", name.clone("pattern-name"), { divider, min: 2 });
|
|
1683
|
+
const orLiteral = new Repeat("or-literal", name$1.clone("pattern-name"), { divider, min: 2 });
|
|
1652
1684
|
|
|
1653
1685
|
const regexLiteral = new Regex("regex-literal", "/(\\\\/|[^/\\n\\r])*/");
|
|
1654
1686
|
|
|
1655
|
-
const spaces = new Regex("spaces", "[ \\t]+");
|
|
1656
|
-
spaces.setTokens([" "]);
|
|
1687
|
+
const spaces$1 = new Regex("spaces", "[ \\t]+");
|
|
1688
|
+
spaces$1.setTokens([" "]);
|
|
1657
1689
|
|
|
1658
1690
|
const optionalIsOptional = new Literal("is-optional", "?", true);
|
|
1659
|
-
const patternName = name.clone("pattern-name");
|
|
1691
|
+
const patternName = name$1.clone("pattern-name");
|
|
1660
1692
|
const pattern = new And("pattern", [
|
|
1661
1693
|
patternName,
|
|
1662
1694
|
optionalIsOptional,
|
|
1663
1695
|
]);
|
|
1664
|
-
const optionalSpaces$
|
|
1665
|
-
const dividerPattern = name.clone("divider-pattern");
|
|
1666
|
-
const openBracket = new Literal("open-bracket", "{");
|
|
1667
|
-
const closeBracket = new Literal("close-bracket", "}");
|
|
1696
|
+
const optionalSpaces$2 = spaces$1.clone("optional-spaces", true);
|
|
1697
|
+
const dividerPattern = name$1.clone("divider-pattern");
|
|
1698
|
+
const openBracket$1 = new Literal("open-bracket", "{");
|
|
1699
|
+
const closeBracket$1 = new Literal("close-bracket", "}");
|
|
1668
1700
|
const comma = new Literal("comma", ",");
|
|
1669
1701
|
const integer = new Regex("integer", "([1-9][0-9]*)|0");
|
|
1670
1702
|
integer.setTokens(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]);
|
|
1671
1703
|
const optionalInteger = integer.clone("integer", true);
|
|
1672
1704
|
const bounds = new And("bounds", [
|
|
1673
|
-
openBracket,
|
|
1674
|
-
optionalSpaces$
|
|
1705
|
+
openBracket$1,
|
|
1706
|
+
optionalSpaces$2,
|
|
1675
1707
|
optionalInteger.clone("min"),
|
|
1676
|
-
optionalSpaces$
|
|
1708
|
+
optionalSpaces$2,
|
|
1677
1709
|
comma,
|
|
1678
|
-
optionalSpaces$
|
|
1710
|
+
optionalSpaces$2,
|
|
1679
1711
|
optionalInteger.clone("max"),
|
|
1680
|
-
optionalSpaces$
|
|
1681
|
-
closeBracket
|
|
1712
|
+
optionalSpaces$2,
|
|
1713
|
+
closeBracket$1
|
|
1682
1714
|
]);
|
|
1683
1715
|
const exactCount = new And("exact-count", [
|
|
1684
|
-
openBracket,
|
|
1685
|
-
optionalSpaces$
|
|
1716
|
+
openBracket$1,
|
|
1717
|
+
optionalSpaces$2,
|
|
1686
1718
|
integer,
|
|
1687
|
-
optionalSpaces$
|
|
1688
|
-
closeBracket,
|
|
1719
|
+
optionalSpaces$2,
|
|
1720
|
+
closeBracket$1,
|
|
1689
1721
|
]);
|
|
1690
1722
|
const quantifierShorthand = new Regex("quantifier-shorthand", "\\*|\\+");
|
|
1691
1723
|
quantifierShorthand.setTokens(["*", "+"]);
|
|
@@ -1702,19 +1734,19 @@ const dividerComma = new Regex("divider-comma", "\\s*,\\s*");
|
|
|
1702
1734
|
dividerComma.setTokens([", "]);
|
|
1703
1735
|
const repeatLiteral = new And("repeat-literal", [
|
|
1704
1736
|
openParen,
|
|
1705
|
-
optionalSpaces$
|
|
1737
|
+
optionalSpaces$2,
|
|
1706
1738
|
pattern,
|
|
1707
1739
|
optional,
|
|
1708
1740
|
new And("optional-divider-section", [dividerComma, dividerPattern], true),
|
|
1709
|
-
optionalSpaces$
|
|
1741
|
+
optionalSpaces$2,
|
|
1710
1742
|
closeParen,
|
|
1711
|
-
new And("quantifier-section", [optionalSpaces$
|
|
1712
|
-
new And("optional-trim-divider-section", [spaces, trimDivider], true)
|
|
1743
|
+
new And("quantifier-section", [optionalSpaces$2, quantifier]),
|
|
1744
|
+
new And("optional-trim-divider-section", [spaces$1, trimDivider], true)
|
|
1713
1745
|
]);
|
|
1714
1746
|
|
|
1715
1747
|
const literal = new Regex("literal", "\"(?:\\\\[\"\\\\]|[^\n\"\\\\])*\"");
|
|
1716
1748
|
|
|
1717
|
-
const optionalSpaces = spaces.clone("optional-spaces", true);
|
|
1749
|
+
const optionalSpaces$1 = spaces$1.clone("optional-spaces", true);
|
|
1718
1750
|
const assignOperator = new Literal("assign-operator", "=");
|
|
1719
1751
|
const optionalComment = comment.clone("inline-comment", true);
|
|
1720
1752
|
const statements = new Or("statements", [
|
|
@@ -1723,39 +1755,57 @@ const statements = new Or("statements", [
|
|
|
1723
1755
|
orLiteral,
|
|
1724
1756
|
andLiteral,
|
|
1725
1757
|
repeatLiteral,
|
|
1726
|
-
name.clone("alias-literal"),
|
|
1758
|
+
name$1.clone("alias-literal"),
|
|
1727
1759
|
]);
|
|
1728
1760
|
const statement = new And("statement", [
|
|
1729
|
-
optionalSpaces,
|
|
1730
|
-
name,
|
|
1731
|
-
optionalSpaces,
|
|
1761
|
+
optionalSpaces$1,
|
|
1762
|
+
name$1,
|
|
1763
|
+
optionalSpaces$1,
|
|
1732
1764
|
assignOperator,
|
|
1733
|
-
optionalSpaces,
|
|
1765
|
+
optionalSpaces$1,
|
|
1734
1766
|
statements,
|
|
1735
|
-
optionalSpaces,
|
|
1767
|
+
optionalSpaces$1,
|
|
1736
1768
|
optionalComment,
|
|
1769
|
+
optionalSpaces$1,
|
|
1770
|
+
]);
|
|
1771
|
+
|
|
1772
|
+
const spaces = new Regex("spaces", "\\s+", true);
|
|
1773
|
+
const importNameDivider = new Regex("import-name-divider", "(\\s+)?,(\\s+)?");
|
|
1774
|
+
const importKeyword = new Literal("import", "import");
|
|
1775
|
+
const fromKeyword = new Literal("from", "from");
|
|
1776
|
+
const openBracket = new Literal("open-bracket", "{");
|
|
1777
|
+
const closeBracket = new Literal("close-bracket", "}");
|
|
1778
|
+
const name = new Regex("import-name", "[^}\\s,]+");
|
|
1779
|
+
const importedNames = new Repeat("imported-names", name, { divider: importNameDivider });
|
|
1780
|
+
const optionalSpaces = spaces.clone("optional-spaces", true);
|
|
1781
|
+
const importStatement = new And("import-statement", [
|
|
1782
|
+
importKeyword,
|
|
1783
|
+
optionalSpaces,
|
|
1784
|
+
openBracket,
|
|
1785
|
+
optionalSpaces,
|
|
1786
|
+
importedNames,
|
|
1737
1787
|
optionalSpaces,
|
|
1788
|
+
closeBracket,
|
|
1789
|
+
optionalSpaces,
|
|
1790
|
+
fromKeyword,
|
|
1791
|
+
spaces,
|
|
1792
|
+
literal.clone("url"),
|
|
1738
1793
|
]);
|
|
1739
1794
|
|
|
1740
|
-
const whitespace = new Regex("whitespace", "[ \\t]+");
|
|
1795
|
+
const whitespace = new Regex("whitespace", "[ \\t]+((\\r?\\n)+)?");
|
|
1741
1796
|
const newLine = new Regex("new-line", "(\\r?\\n)+");
|
|
1742
1797
|
whitespace.setTokens([" "]);
|
|
1743
1798
|
newLine.setTokens(["\n"]);
|
|
1744
1799
|
const line = new Or("line", [
|
|
1800
|
+
newLine,
|
|
1801
|
+
whitespace,
|
|
1745
1802
|
comment,
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
]
|
|
1749
|
-
const grammar = new Repeat("
|
|
1803
|
+
importStatement,
|
|
1804
|
+
statement
|
|
1805
|
+
]);
|
|
1806
|
+
const grammar = new Repeat("grammer", line);
|
|
1750
1807
|
|
|
1751
1808
|
class Not {
|
|
1752
|
-
constructor(name, pattern) {
|
|
1753
|
-
this._type = "not";
|
|
1754
|
-
this._name = name;
|
|
1755
|
-
this._parent = null;
|
|
1756
|
-
this._children = [pattern.clone(pattern.name, false)];
|
|
1757
|
-
this._children[0].parent = this;
|
|
1758
|
-
}
|
|
1759
1809
|
get type() {
|
|
1760
1810
|
return this._type;
|
|
1761
1811
|
}
|
|
@@ -1774,6 +1824,13 @@ class Not {
|
|
|
1774
1824
|
get isOptional() {
|
|
1775
1825
|
return false;
|
|
1776
1826
|
}
|
|
1827
|
+
constructor(name, pattern) {
|
|
1828
|
+
this._type = "not";
|
|
1829
|
+
this._name = name;
|
|
1830
|
+
this._parent = null;
|
|
1831
|
+
this._children = [pattern.clone(pattern.name, false)];
|
|
1832
|
+
this._children[0].parent = this;
|
|
1833
|
+
}
|
|
1777
1834
|
test(text) {
|
|
1778
1835
|
const cursor = new Cursor(text);
|
|
1779
1836
|
this.parse(cursor);
|
|
@@ -2043,10 +2100,16 @@ function getFurthestOptions(options) {
|
|
|
2043
2100
|
class ParseContext {
|
|
2044
2101
|
constructor() {
|
|
2045
2102
|
this.patternsByName = new Map();
|
|
2103
|
+
this.importedPatternsByName = new Map();
|
|
2046
2104
|
}
|
|
2047
2105
|
}
|
|
2106
|
+
function defaultImportResolver(_path, _basePath) {
|
|
2107
|
+
throw new Error("No import resolver supplied.");
|
|
2108
|
+
}
|
|
2048
2109
|
class Grammar {
|
|
2049
|
-
constructor() {
|
|
2110
|
+
constructor(options = {}) {
|
|
2111
|
+
this._meta = options.meta == null ? null : options.meta;
|
|
2112
|
+
this._resolveImport = options.resolveImport == null ? defaultImportResolver : options.resolveImport;
|
|
2050
2113
|
this._parseContext = new ParseContext();
|
|
2051
2114
|
this._autoComplete = new AutoComplete(grammar, {
|
|
2052
2115
|
greedyPatternNames: ["spaces", "optional-spaces", "whitespace", "new-line"],
|
|
@@ -2058,9 +2121,31 @@ class Grammar {
|
|
|
2058
2121
|
}
|
|
2059
2122
|
});
|
|
2060
2123
|
}
|
|
2124
|
+
import(path) {
|
|
2125
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2126
|
+
const grammarFile = yield this._resolveImport(path, null);
|
|
2127
|
+
const grammar = new Grammar({ resolveImport: this._resolveImport, meta: { originPath: grammarFile.path } });
|
|
2128
|
+
return grammar.parse(grammarFile.expression);
|
|
2129
|
+
});
|
|
2130
|
+
}
|
|
2061
2131
|
parse(expression) {
|
|
2132
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2133
|
+
this._parseContext = new ParseContext();
|
|
2134
|
+
const ast = this._tryToParse(expression);
|
|
2135
|
+
yield this._resolveImports(ast);
|
|
2136
|
+
this._buildPatterns(ast);
|
|
2137
|
+
this._cleanAst(ast);
|
|
2138
|
+
return this._parseContext.patternsByName;
|
|
2139
|
+
});
|
|
2140
|
+
}
|
|
2141
|
+
parseString(expression) {
|
|
2062
2142
|
this._parseContext = new ParseContext();
|
|
2063
|
-
this._tryToParse(expression);
|
|
2143
|
+
const ast = this._tryToParse(expression);
|
|
2144
|
+
if (this._hasImports(ast)) {
|
|
2145
|
+
throw new Error("Cannot use imports on parseString, use parse instead.");
|
|
2146
|
+
}
|
|
2147
|
+
this._buildPatterns(ast);
|
|
2148
|
+
this._cleanAst(ast);
|
|
2064
2149
|
return this._parseContext.patternsByName;
|
|
2065
2150
|
}
|
|
2066
2151
|
_tryToParse(expression) {
|
|
@@ -2077,8 +2162,14 @@ class Grammar {
|
|
|
2077
2162
|
throw new Error(message);
|
|
2078
2163
|
}
|
|
2079
2164
|
// If it is complete it will always have a node. So we have to cast it.
|
|
2080
|
-
|
|
2081
|
-
|
|
2165
|
+
return ast;
|
|
2166
|
+
}
|
|
2167
|
+
_hasImports(ast) {
|
|
2168
|
+
const importBlock = ast.find(n => n.name === "import-block");
|
|
2169
|
+
if (importBlock == null) {
|
|
2170
|
+
return false;
|
|
2171
|
+
}
|
|
2172
|
+
return importBlock && importBlock.children.length > 0;
|
|
2082
2173
|
}
|
|
2083
2174
|
_cleanAst(ast) {
|
|
2084
2175
|
ast.findAll(n => n.name === "spaces" ||
|
|
@@ -2119,6 +2210,36 @@ class Grammar {
|
|
|
2119
2210
|
}
|
|
2120
2211
|
});
|
|
2121
2212
|
}
|
|
2213
|
+
_resolveImports(ast) {
|
|
2214
|
+
var _a;
|
|
2215
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2216
|
+
const parseContext = this._parseContext;
|
|
2217
|
+
const importStatements = ast.findAll(n => n.name === "import-statement");
|
|
2218
|
+
for (const importStatement of importStatements) {
|
|
2219
|
+
const urlNode = importStatement.find(n => n.name === "url");
|
|
2220
|
+
const url = urlNode.value.slice(1, -1);
|
|
2221
|
+
const grammarFile = yield this._resolveImport(url, ((_a = this._meta) === null || _a === void 0 ? void 0 : _a.originPath) || null);
|
|
2222
|
+
const grammar = new Grammar({ resolveImport: this._resolveImport, meta: { originPath: grammarFile.path } });
|
|
2223
|
+
try {
|
|
2224
|
+
const patterns = yield grammar.parse(grammarFile.expression);
|
|
2225
|
+
const importNames = importStatement.findAll(n => n.name === "import-name").map(n => n.value);
|
|
2226
|
+
importNames.forEach((importName) => {
|
|
2227
|
+
if (parseContext.importedPatternsByName.has(importName)) {
|
|
2228
|
+
throw new Error(`'${importName}' was already used within another import.`);
|
|
2229
|
+
}
|
|
2230
|
+
const pattern = patterns.get(importName);
|
|
2231
|
+
if (pattern == null) {
|
|
2232
|
+
throw new Error(`Couldn't find pattern with name: ${importName}, from import: ${url}.`);
|
|
2233
|
+
}
|
|
2234
|
+
parseContext.importedPatternsByName.set(importName, pattern);
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
2237
|
+
catch (e) {
|
|
2238
|
+
throw new Error(`Failed loading expression from: "${url}". Error details: "${e.message}"`);
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2122
2243
|
_buildLiteral(statementNode) {
|
|
2123
2244
|
const nameNode = statementNode.find(n => n.name === "name");
|
|
2124
2245
|
const literalNode = statementNode.find(n => n.name === "literal");
|
|
@@ -2138,14 +2259,17 @@ class Grammar {
|
|
|
2138
2259
|
_buildOr(statementNode) {
|
|
2139
2260
|
const nameNode = statementNode.find(n => n.name === "name");
|
|
2140
2261
|
const orNode = statementNode.find(n => n.name === "or-literal");
|
|
2141
|
-
const patternNodes = orNode.children.filter(n => n.name
|
|
2262
|
+
const patternNodes = orNode.children.filter(n => n.name === "pattern-name");
|
|
2142
2263
|
const name = nameNode.value;
|
|
2143
2264
|
const patterns = patternNodes.map(n => this._getPattern(n.value));
|
|
2144
|
-
const or = new Or(name, patterns);
|
|
2265
|
+
const or = new Or(name, patterns, false, true);
|
|
2145
2266
|
this._parseContext.patternsByName.set(name, or);
|
|
2146
2267
|
}
|
|
2147
2268
|
_getPattern(name) {
|
|
2148
|
-
|
|
2269
|
+
let pattern = this._parseContext.patternsByName.get(name);
|
|
2270
|
+
if (pattern == null) {
|
|
2271
|
+
pattern = this._parseContext.importedPatternsByName.get(name);
|
|
2272
|
+
}
|
|
2149
2273
|
if (pattern == null) {
|
|
2150
2274
|
return new Reference(name);
|
|
2151
2275
|
}
|
|
@@ -2154,7 +2278,7 @@ class Grammar {
|
|
|
2154
2278
|
_buildAnd(statementNode) {
|
|
2155
2279
|
const nameNode = statementNode.find(n => n.name === "name");
|
|
2156
2280
|
const andNode = statementNode.find(n => n.name === "and-literal");
|
|
2157
|
-
const patternNodes = andNode.children.filter(n => n.name
|
|
2281
|
+
const patternNodes = andNode.children.filter(n => n.name === "pattern");
|
|
2158
2282
|
const name = nameNode.value;
|
|
2159
2283
|
const patterns = patternNodes.map(n => {
|
|
2160
2284
|
const nameNode = n.find(n => n.name === "pattern-name");
|
|
@@ -2173,7 +2297,7 @@ class Grammar {
|
|
|
2173
2297
|
_buildRepeat(statementNode) {
|
|
2174
2298
|
const nameNode = statementNode.find(n => n.name === "name");
|
|
2175
2299
|
const repeatNode = statementNode.find(n => n.name === "repeat-literal");
|
|
2176
|
-
const patternNode = repeatNode.find(n => n.name
|
|
2300
|
+
const patternNode = repeatNode.find(n => n.name === "pattern");
|
|
2177
2301
|
const patternNameNode = patternNode.find(n => n.name === "pattern-name");
|
|
2178
2302
|
const dividerNode = repeatNode.find(n => n.name === "divider-pattern");
|
|
2179
2303
|
const bounds = repeatNode.find(n => n.name === "bounds");
|
|
@@ -2230,10 +2354,18 @@ class Grammar {
|
|
|
2230
2354
|
const alias = pattern.clone(name);
|
|
2231
2355
|
this._parseContext.patternsByName.set(name, alias);
|
|
2232
2356
|
}
|
|
2233
|
-
static parse(expression) {
|
|
2234
|
-
const grammar = new Grammar();
|
|
2357
|
+
static parse(expression, options) {
|
|
2358
|
+
const grammar = new Grammar(options);
|
|
2235
2359
|
return grammar.parse(expression);
|
|
2236
2360
|
}
|
|
2361
|
+
static import(path, options) {
|
|
2362
|
+
const grammar = new Grammar(options);
|
|
2363
|
+
return grammar.import(path);
|
|
2364
|
+
}
|
|
2365
|
+
static parseString(expression) {
|
|
2366
|
+
const grammar = new Grammar();
|
|
2367
|
+
return grammar.parseString(expression);
|
|
2368
|
+
}
|
|
2237
2369
|
}
|
|
2238
2370
|
|
|
2239
2371
|
exports.And = And;
|