grammatik 0.1.0 → 0.1.2
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/README.md +16 -0
- package/bin/grammatik.js +8 -0
- package/dist/cjs/index.js +1637 -139
- package/dist/cjs/runtime.js +642 -34
- package/dist/esm/index.js +1637 -139
- package/dist/esm/runtime.js +642 -34
- package/dist/types/index.d.ts +36 -0
- package/dist/types/runtime.d.ts +4 -3
- package/package.json +3 -2
- package/src/analyze.js +225 -18
- package/src/codegen.js +218 -27
- package/src/definition.js +5 -0
- package/src/emit.js +41 -7
- package/src/lexer.d.ts +43 -2
- package/src/lexer.js +573 -44
- package/src/lexgen.js +451 -0
- package/src/memoplan.js +220 -0
- package/src/methods.js +1 -1
- package/src/parser.js +38 -3
- package/src/plan.js +142 -30
- package/src/runtime.js +8 -6
- package/src/scan.js +422 -0
- package/src/state.js +33 -1
package/dist/cjs/runtime.js
CHANGED
|
@@ -33,6 +33,12 @@ module.exports = __toCommonJS(runtime_exports);
|
|
|
33
33
|
|
|
34
34
|
// src/state.js
|
|
35
35
|
var FAIL = /* @__PURE__ */ Symbol("grammatik.fail");
|
|
36
|
+
var memoTables = (slots, length) => {
|
|
37
|
+
if (!slots) return null;
|
|
38
|
+
const tables = new Array(slots);
|
|
39
|
+
for (let i = 0; i < slots; i++) tables[i] = { ends: new Int32Array(length + 1), vals: new Array(length + 1) };
|
|
40
|
+
return tables;
|
|
41
|
+
};
|
|
36
42
|
var createState = (tokens, grammar, options = {}) => ({
|
|
37
43
|
tokens,
|
|
38
44
|
idx: 0,
|
|
@@ -40,6 +46,7 @@ var createState = (tokens, grammar, options = {}) => ({
|
|
|
40
46
|
expected: [],
|
|
41
47
|
grammar,
|
|
42
48
|
memo: options.memo ? /* @__PURE__ */ new Map() : null,
|
|
49
|
+
rm: memoTables(options.memoSlots, tokens.length),
|
|
43
50
|
// Entering a rule is one array store rather than an object allocation, and
|
|
44
51
|
// the stack is snapshotted only on a furthest failure — by the time an error
|
|
45
52
|
// is built it would otherwise have unwound to just the start rule.
|
|
@@ -60,6 +67,19 @@ function expect(state, expectation) {
|
|
|
60
67
|
if (!state.track || state.idx !== state.maxIdx) return;
|
|
61
68
|
if (!state.expected.includes(expectation)) state.expected.push(expectation);
|
|
62
69
|
}
|
|
70
|
+
var LexError = class extends Error {
|
|
71
|
+
/**
|
|
72
|
+
* @param {string} message
|
|
73
|
+
* @param {{ offset: number, line: number, col: number }} position
|
|
74
|
+
*/
|
|
75
|
+
constructor(message, position) {
|
|
76
|
+
super(message);
|
|
77
|
+
this.name = "LexError";
|
|
78
|
+
this.offset = position.offset;
|
|
79
|
+
this.line = position.line;
|
|
80
|
+
this.col = position.col;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
63
83
|
var ParseError = class extends Error {
|
|
64
84
|
/**
|
|
65
85
|
* @param {string} message
|
|
@@ -400,20 +420,303 @@ function firstCharSet(source, ignoreCase) {
|
|
|
400
420
|
return found;
|
|
401
421
|
}
|
|
402
422
|
|
|
403
|
-
// src/
|
|
404
|
-
var
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
423
|
+
// src/scan.js
|
|
424
|
+
var DEFER = -2;
|
|
425
|
+
var CLASS = 0;
|
|
426
|
+
var SEQ2 = 1;
|
|
427
|
+
var KEYWORD = 2;
|
|
428
|
+
var LINE = 3;
|
|
429
|
+
var QUOTED = 4;
|
|
430
|
+
var NUMBER = 5;
|
|
431
|
+
var STRING = 6;
|
|
432
|
+
var SCANNER_KINDS = Object.freeze({ CLASS, SEQ2, KEYWORD, LINE, QUOTED, NUMBER, STRING });
|
|
433
|
+
var NONE = new Uint8Array(128);
|
|
434
|
+
function make(kind, fields) {
|
|
435
|
+
return {
|
|
436
|
+
kind,
|
|
437
|
+
wide: false,
|
|
438
|
+
first: NONE,
|
|
439
|
+
rest: NONE,
|
|
440
|
+
second: NONE,
|
|
441
|
+
look: NONE,
|
|
442
|
+
lower: [],
|
|
443
|
+
upper: [],
|
|
444
|
+
prefix: "",
|
|
445
|
+
quote: -1,
|
|
446
|
+
sign: false,
|
|
447
|
+
jsonInt: false,
|
|
448
|
+
frac: false,
|
|
449
|
+
exp: false,
|
|
450
|
+
ctrl: false,
|
|
451
|
+
uni: false,
|
|
452
|
+
escAny: false,
|
|
453
|
+
esc: NONE,
|
|
454
|
+
...fields
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
var SHORTHAND = {
|
|
458
|
+
d: (t) => {
|
|
459
|
+
for (let k = 48; k < 58; k++) t[k] = 1;
|
|
460
|
+
},
|
|
461
|
+
w: (t) => {
|
|
462
|
+
for (let k = 0; k < 128; k++) if (/\w/.test(String.fromCharCode(k))) t[k] = 1;
|
|
463
|
+
},
|
|
464
|
+
s: (t) => {
|
|
465
|
+
for (const k of [9, 10, 11, 12, 13, 32]) t[k] = 1;
|
|
415
466
|
}
|
|
416
467
|
};
|
|
468
|
+
var SIMPLE_ESCAPES2 = { t: " ", r: "\r", n: "\n", f: "\f", v: "\v" };
|
|
469
|
+
var ALNUM = /[a-zA-Z0-9]/;
|
|
470
|
+
function parseClass(body) {
|
|
471
|
+
if (body.length === 0 || body[0] === "^") return null;
|
|
472
|
+
const table = new Uint8Array(128);
|
|
473
|
+
for (let i = 0; i < body.length; i++) {
|
|
474
|
+
let c = body[i];
|
|
475
|
+
if (c === "\\") {
|
|
476
|
+
const n = body[++i];
|
|
477
|
+
if (n === "d" || n === "w") {
|
|
478
|
+
SHORTHAND[n](table);
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
if (n === void 0) return null;
|
|
482
|
+
if (ALNUM.test(n) && !(n in SIMPLE_ESCAPES2)) return null;
|
|
483
|
+
c = SIMPLE_ESCAPES2[n] ?? n;
|
|
484
|
+
}
|
|
485
|
+
if (body[i + 1] === "-" && i + 2 < body.length) {
|
|
486
|
+
let hi = body[i + 2];
|
|
487
|
+
let advance = 2;
|
|
488
|
+
if (hi === "\\") {
|
|
489
|
+
hi = body[i + 3];
|
|
490
|
+
advance = 3;
|
|
491
|
+
if (hi === void 0 || ALNUM.test(hi)) return null;
|
|
492
|
+
}
|
|
493
|
+
const from = c.charCodeAt(0);
|
|
494
|
+
const to = hi.charCodeAt(0);
|
|
495
|
+
if (from > to || to >= 128) return null;
|
|
496
|
+
for (let k = from; k <= to; k++) table[k] = 1;
|
|
497
|
+
i += advance;
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
const code = c.charCodeAt(0);
|
|
501
|
+
if (code >= 128) return null;
|
|
502
|
+
table[code] = 1;
|
|
503
|
+
}
|
|
504
|
+
return table;
|
|
505
|
+
}
|
|
506
|
+
var BODY = String.raw`((?:[^\]\\]|\\.)*)`;
|
|
507
|
+
var RE_CLASS_RUN = new RegExp(String.raw`^\[${BODY}\]([+*])$`);
|
|
508
|
+
var RE_CLASS_PAIR = new RegExp(String.raw`^\[${BODY}\]\[${BODY}\]\*$`);
|
|
509
|
+
var RE_CLASS_SEQ = new RegExp(String.raw`^\[${BODY}\]\+\[${BODY}\]\+$`);
|
|
510
|
+
var RE_SHORTHAND = /^\\([sdw])[+*]$/;
|
|
511
|
+
var RE_KEYWORD = new RegExp(String.raw`^([A-Za-z0-9_]+)\(\?!\[${BODY}\]\)$`);
|
|
512
|
+
var RE_LINE = new RegExp(String.raw`^([^\\\[\]()*+?.|^$]+)\[\^${BODY}\]\*$`);
|
|
513
|
+
var RE_QUOTED = /^(['"])\(\?:\1\1\|\[\^\1\]\)\*\1$/;
|
|
514
|
+
var RE_NUMBER = /^(-\?)?(?:(\(\?:0\|\[1-9\]\\d\*\))|\\d\+)(\(\?:\\\.\\d\+\)\?)?(\(\?:\[eE\]\[\+-\]\?\\d\+\)\?)?$/;
|
|
515
|
+
var RE_STRING_ESC = new RegExp(String.raw`^(["'])\(\?:\[\^\1\\\\(\\u0000-\\u001f|\\x00-\\x1f)?\]\|\\\\\(\?:\[${BODY}\](\|u\[0-9a-fA-F\]\{4\})?\)\)\*\1$`);
|
|
516
|
+
var RE_STRING_ANY = new RegExp(String.raw`^(["'])\(\?:\[\^\1\\\\\]\|\\\\\.\)\*\1$`);
|
|
517
|
+
function buildScanner(source, ignoreCase) {
|
|
518
|
+
let m = RE_KEYWORD.exec(source);
|
|
519
|
+
if (m) {
|
|
520
|
+
const look = parseClass(m[2]);
|
|
521
|
+
if (look === null) return null;
|
|
522
|
+
const word = m[1];
|
|
523
|
+
const lower = [];
|
|
524
|
+
const upper = [];
|
|
525
|
+
for (const ch of word) {
|
|
526
|
+
lower.push((ignoreCase ? ch.toLowerCase() : ch).charCodeAt(0));
|
|
527
|
+
upper.push((ignoreCase ? ch.toUpperCase() : ch).charCodeAt(0));
|
|
528
|
+
}
|
|
529
|
+
return make(KEYWORD, { look, lower, upper });
|
|
530
|
+
}
|
|
531
|
+
if (ignoreCase) return null;
|
|
532
|
+
m = RE_STRING_ESC.exec(source);
|
|
533
|
+
if (m) {
|
|
534
|
+
const esc = parseClass(m[3]);
|
|
535
|
+
if (esc === null) return null;
|
|
536
|
+
return make(STRING, { quote: m[1].charCodeAt(0), ctrl: m[2] !== void 0, uni: m[4] !== void 0, esc });
|
|
537
|
+
}
|
|
538
|
+
m = RE_STRING_ANY.exec(source);
|
|
539
|
+
if (m) return make(STRING, { quote: m[1].charCodeAt(0), escAny: true });
|
|
540
|
+
m = RE_NUMBER.exec(source.replace(/\[0-9\]/g, "\\d"));
|
|
541
|
+
if (m && (m[1] || m[2] || m[3] || m[4])) {
|
|
542
|
+
return make(NUMBER, { sign: m[1] !== void 0, jsonInt: m[2] !== void 0, frac: m[3] !== void 0, exp: m[4] !== void 0 });
|
|
543
|
+
}
|
|
544
|
+
m = RE_CLASS_RUN.exec(source);
|
|
545
|
+
if (m) {
|
|
546
|
+
const table = parseClass(m[1]);
|
|
547
|
+
return table && make(CLASS, { first: table, rest: table });
|
|
548
|
+
}
|
|
549
|
+
m = RE_CLASS_PAIR.exec(source);
|
|
550
|
+
if (m) {
|
|
551
|
+
const first = parseClass(m[1]);
|
|
552
|
+
const rest = parseClass(m[2]);
|
|
553
|
+
return first && rest && make(CLASS, { first, rest });
|
|
554
|
+
}
|
|
555
|
+
m = RE_CLASS_SEQ.exec(source);
|
|
556
|
+
if (m) {
|
|
557
|
+
const first = parseClass(m[1]);
|
|
558
|
+
const second = parseClass(m[2]);
|
|
559
|
+
if (first && second && first.every((v, i) => !(v && second[i]))) {
|
|
560
|
+
return make(SEQ2, { first, rest: first, second });
|
|
561
|
+
}
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
m = RE_SHORTHAND.exec(source);
|
|
565
|
+
if (m) {
|
|
566
|
+
const table = new Uint8Array(128);
|
|
567
|
+
SHORTHAND[m[1]](table);
|
|
568
|
+
return make(CLASS, { first: table, rest: table, wide: m[1] === "s" });
|
|
569
|
+
}
|
|
570
|
+
m = RE_QUOTED.exec(source);
|
|
571
|
+
if (m) return make(QUOTED, { quote: m[1].charCodeAt(0) });
|
|
572
|
+
m = RE_LINE.exec(source);
|
|
573
|
+
if (m) {
|
|
574
|
+
const stop = parseClass(m[2]);
|
|
575
|
+
return stop && make(LINE, { prefix: m[1], look: stop });
|
|
576
|
+
}
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
var skipDigits = (text, i, length) => {
|
|
580
|
+
while (i < length) {
|
|
581
|
+
const c = text.charCodeAt(i);
|
|
582
|
+
if (c < 48 || c > 57) break;
|
|
583
|
+
i++;
|
|
584
|
+
}
|
|
585
|
+
return i;
|
|
586
|
+
};
|
|
587
|
+
var isHex = (c) => c >= 48 && c <= 57 || c >= 65 && c <= 70 || c >= 97 && c <= 102;
|
|
588
|
+
function scanEnd(sc, text, offset, length) {
|
|
589
|
+
switch (sc.kind) {
|
|
590
|
+
case CLASS: {
|
|
591
|
+
const code = text.charCodeAt(offset);
|
|
592
|
+
if (code >= 128) return sc.wide ? DEFER : -1;
|
|
593
|
+
if (sc.first[code] !== 1) return -1;
|
|
594
|
+
const rest = sc.rest;
|
|
595
|
+
let j = offset + 1;
|
|
596
|
+
while (j < length) {
|
|
597
|
+
const c = text.charCodeAt(j);
|
|
598
|
+
if (c >= 128) return sc.wide ? DEFER : j;
|
|
599
|
+
if (rest[c] !== 1) break;
|
|
600
|
+
j++;
|
|
601
|
+
}
|
|
602
|
+
return j;
|
|
603
|
+
}
|
|
604
|
+
case SEQ2: {
|
|
605
|
+
const c0 = text.charCodeAt(offset);
|
|
606
|
+
if (c0 >= 128 || sc.first[c0] !== 1) return -1;
|
|
607
|
+
const { first, second } = sc;
|
|
608
|
+
let j = offset + 1;
|
|
609
|
+
while (j < length) {
|
|
610
|
+
const c = text.charCodeAt(j);
|
|
611
|
+
if (c < 128 && first[c] === 1) j++;
|
|
612
|
+
else break;
|
|
613
|
+
}
|
|
614
|
+
const mid = j;
|
|
615
|
+
while (j < length) {
|
|
616
|
+
const c = text.charCodeAt(j);
|
|
617
|
+
if (c < 128 && second[c] === 1) j++;
|
|
618
|
+
else break;
|
|
619
|
+
}
|
|
620
|
+
return j > mid ? j : -1;
|
|
621
|
+
}
|
|
622
|
+
case KEYWORD: {
|
|
623
|
+
const { lower, upper } = sc;
|
|
624
|
+
const n = lower.length;
|
|
625
|
+
if (offset + n > length) return -1;
|
|
626
|
+
for (let i = 0; i < n; i++) {
|
|
627
|
+
const c = text.charCodeAt(offset + i);
|
|
628
|
+
if (c !== lower[i] && c !== upper[i]) return -1;
|
|
629
|
+
}
|
|
630
|
+
const end = offset + n;
|
|
631
|
+
if (end < length) {
|
|
632
|
+
const c = text.charCodeAt(end);
|
|
633
|
+
if (c < 128 && sc.look[c] === 1) return -1;
|
|
634
|
+
}
|
|
635
|
+
return end;
|
|
636
|
+
}
|
|
637
|
+
case LINE: {
|
|
638
|
+
if (!text.startsWith(sc.prefix, offset)) return -1;
|
|
639
|
+
let j = offset + sc.prefix.length;
|
|
640
|
+
const stop = sc.look;
|
|
641
|
+
while (j < length) {
|
|
642
|
+
const c = text.charCodeAt(j);
|
|
643
|
+
if (c < 128 && stop[c] === 1) break;
|
|
644
|
+
j++;
|
|
645
|
+
}
|
|
646
|
+
return j;
|
|
647
|
+
}
|
|
648
|
+
case NUMBER: {
|
|
649
|
+
let j = offset;
|
|
650
|
+
if (sc.sign && text.charCodeAt(j) === 45) j++;
|
|
651
|
+
const c = text.charCodeAt(j);
|
|
652
|
+
if (sc.jsonInt) {
|
|
653
|
+
if (c === 48) j++;
|
|
654
|
+
else if (c >= 49 && c <= 57) j = skipDigits(text, j + 1, length);
|
|
655
|
+
else return -1;
|
|
656
|
+
} else {
|
|
657
|
+
if (!(c >= 48 && c <= 57)) return -1;
|
|
658
|
+
j = skipDigits(text, j + 1, length);
|
|
659
|
+
}
|
|
660
|
+
if (sc.frac && text.charCodeAt(j) === 46) {
|
|
661
|
+
const k = skipDigits(text, j + 1, length);
|
|
662
|
+
if (k > j + 1) j = k;
|
|
663
|
+
}
|
|
664
|
+
if (sc.exp) {
|
|
665
|
+
const e = text.charCodeAt(j);
|
|
666
|
+
if (e === 101 || e === 69) {
|
|
667
|
+
let k = j + 1;
|
|
668
|
+
const s = text.charCodeAt(k);
|
|
669
|
+
if (s === 43 || s === 45) k++;
|
|
670
|
+
const end = skipDigits(text, k, length);
|
|
671
|
+
if (end > k) j = end;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return j;
|
|
675
|
+
}
|
|
676
|
+
case STRING: {
|
|
677
|
+
const q = sc.quote;
|
|
678
|
+
if (text.charCodeAt(offset) !== q) return -1;
|
|
679
|
+
let j = offset + 1;
|
|
680
|
+
for (; ; ) {
|
|
681
|
+
if (j >= length) return -1;
|
|
682
|
+
const c = text.charCodeAt(j);
|
|
683
|
+
if (c === q) return j + 1;
|
|
684
|
+
if (c === 92) {
|
|
685
|
+
const d = text.charCodeAt(j + 1);
|
|
686
|
+
if (sc.escAny) {
|
|
687
|
+
if (!(d >= 0) || d === 10 || d === 13 || d === 8232 || d === 8233) return -1;
|
|
688
|
+
j += 2;
|
|
689
|
+
} else if (d < 128 && sc.esc[d] === 1) {
|
|
690
|
+
j += 2;
|
|
691
|
+
} else if (sc.uni && d === 117 && isHex(text.charCodeAt(j + 2)) && isHex(text.charCodeAt(j + 3)) && isHex(text.charCodeAt(j + 4)) && isHex(text.charCodeAt(j + 5))) {
|
|
692
|
+
j += 6;
|
|
693
|
+
} else {
|
|
694
|
+
return -1;
|
|
695
|
+
}
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
if (sc.ctrl && c < 32) return -1;
|
|
699
|
+
j++;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
default: {
|
|
703
|
+
const q = sc.quote;
|
|
704
|
+
if (text.charCodeAt(offset) !== q) return -1;
|
|
705
|
+
const quote = String.fromCharCode(q);
|
|
706
|
+
let fallback = -1;
|
|
707
|
+
let j = offset + 1;
|
|
708
|
+
for (; ; ) {
|
|
709
|
+
j = text.indexOf(quote, j);
|
|
710
|
+
if (j === -1) return fallback;
|
|
711
|
+
if (text.charCodeAt(j + 1) !== q) return j + 1;
|
|
712
|
+
fallback = j + 1;
|
|
713
|
+
j += 2;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// src/lexer.js
|
|
417
720
|
var DEFAULT_MODE = "default";
|
|
418
721
|
function modesOf(def) {
|
|
419
722
|
if (Array.isArray(def.modes) && def.modes.length > 0) return def.modes;
|
|
@@ -423,6 +726,8 @@ function modesOf(def) {
|
|
|
423
726
|
function createLexer(defs, options = {}) {
|
|
424
727
|
const positions = options.positions === "offset" ? "offset" : "full";
|
|
425
728
|
const trackLines = positions === "full";
|
|
729
|
+
const useScanners = options.scanners !== false;
|
|
730
|
+
const useFuse = options.fuse !== false;
|
|
426
731
|
if (!Array.isArray(defs) || defs.length === 0) throw new Error("createLexer: expected a non-empty array of token definitions");
|
|
427
732
|
const tokenNames = defs.map((d) => d.name);
|
|
428
733
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -442,7 +747,7 @@ function createLexer(defs, options = {}) {
|
|
|
442
747
|
return id;
|
|
443
748
|
};
|
|
444
749
|
for (const def of defs) for (const mode of modesOf(def)) idOf(mode);
|
|
445
|
-
const compiled = defs.map((def) => {
|
|
750
|
+
const compiled = defs.map((def, id) => {
|
|
446
751
|
if (!def.name) throw new Error("createLexer: every token definition needs a name");
|
|
447
752
|
if (!def.pattern && !def.literal) throw new Error(`createLexer: token '${def.name}' needs a pattern or a literal`);
|
|
448
753
|
if (def.pattern && def.literal) throw new Error(`createLexer: token '${def.name}' has both a pattern and a literal; pick one`);
|
|
@@ -457,13 +762,24 @@ function createLexer(defs, options = {}) {
|
|
|
457
762
|
}
|
|
458
763
|
return {
|
|
459
764
|
name: def.name,
|
|
765
|
+
// The declaration index. `createParser` derives the identical mapping
|
|
766
|
+
// from `tokenNames` independently (same array, same order), so a
|
|
767
|
+
// combinator that resolves a `consume` target's id at build time agrees
|
|
768
|
+
// with what a token actually carries without the two ever comparing notes.
|
|
769
|
+
id,
|
|
460
770
|
skip: !!def.skip,
|
|
461
771
|
literal: def.literal,
|
|
462
772
|
longerAlt: def.longerAlt || null,
|
|
463
773
|
regex: def.pattern ? new RegExp(def.pattern, def.ignoreCase ? "iy" : "y") : null,
|
|
774
|
+
// A table-driven stand-in for the regex where its shape is recognised;
|
|
775
|
+
// null otherwise (and always, with `scanners: false`). It is a field on
|
|
776
|
+
// every definition so they all keep one shape. See scan.js.
|
|
777
|
+
scanner: useScanners && def.pattern ? buildScanner(def.pattern, !!def.ignoreCase) : null,
|
|
464
778
|
modes: modesOf(def),
|
|
465
779
|
pushMode: def.pushMode ? idOf(def.pushMode) : -1,
|
|
466
|
-
popMode: !!def.popMode
|
|
780
|
+
popMode: !!def.popMode,
|
|
781
|
+
// Set only on the stand-in `fuse` builds for a run of regexes; see there.
|
|
782
|
+
members: null
|
|
467
783
|
};
|
|
468
784
|
});
|
|
469
785
|
const byName = new Map(compiled.map((c) => [c.name, c]));
|
|
@@ -480,14 +796,47 @@ function createLexer(defs, options = {}) {
|
|
|
480
796
|
for (const c of compiled) {
|
|
481
797
|
c.firstChars = c.literal !== void 0 ? c.literal.length > 0 ? /* @__PURE__ */ new Set([c.literal.charCodeAt(0)]) : null : firstCharSet(c.regex.source, !!c.regex.ignoreCase);
|
|
482
798
|
}
|
|
483
|
-
const
|
|
799
|
+
const FUSE_MIN = 3;
|
|
800
|
+
const fusible = (def) => def.literal === void 0 && def.scanner === null && def.regex !== null && !/\\[1-9]|\\k</.test(def.regex.source) && // `exec` of an always-matching `|` alternative returns one slot per capture group.
|
|
801
|
+
new RegExp(def.regex.source + "|").exec("").length === 1;
|
|
802
|
+
function fuse(list) {
|
|
803
|
+
if (!useFuse) return list;
|
|
804
|
+
const out = [];
|
|
805
|
+
for (let i = 0; i < list.length; ) {
|
|
806
|
+
let j = i;
|
|
807
|
+
while (j < list.length && fusible(list[j]) && list[j].regex.flags === list[i].regex.flags) j++;
|
|
808
|
+
if (j - i < FUSE_MIN) {
|
|
809
|
+
out.push(list[i++]);
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
const run = list.slice(i, j);
|
|
813
|
+
out.push({
|
|
814
|
+
name: run.map((d) => d.name).join("|"),
|
|
815
|
+
id: -1,
|
|
816
|
+
skip: false,
|
|
817
|
+
literal: void 0,
|
|
818
|
+
longerAlt: null,
|
|
819
|
+
longerAltDef: void 0,
|
|
820
|
+
regex: new RegExp(run.map((d) => `(${d.regex.source})`).join("|"), run[0].regex.flags),
|
|
821
|
+
scanner: null,
|
|
822
|
+
modes: run[0].modes,
|
|
823
|
+
pushMode: -1,
|
|
824
|
+
popMode: false,
|
|
825
|
+
members: run
|
|
826
|
+
});
|
|
827
|
+
i = j;
|
|
828
|
+
}
|
|
829
|
+
return out;
|
|
830
|
+
}
|
|
831
|
+
const modes = modeNames.map((name, id) => {
|
|
484
832
|
const members = compiled.filter((c) => c.modes.includes(name));
|
|
485
833
|
const alwaysTry = members.filter((c) => c.firstChars === null);
|
|
486
|
-
const candidatesFor = (code) => members.filter((c) => c.firstChars === null || c.firstChars.has(code));
|
|
834
|
+
const candidatesFor = (code) => fuse(members.filter((c) => c.firstChars === null || c.firstChars.has(code)));
|
|
487
835
|
const ascii = new Array(128);
|
|
488
836
|
for (let code = 0; code < 128; code++) ascii[code] = candidatesFor(code);
|
|
489
837
|
return {
|
|
490
838
|
name,
|
|
839
|
+
id,
|
|
491
840
|
members,
|
|
492
841
|
ascii,
|
|
493
842
|
wide: /* @__PURE__ */ new Map(),
|
|
@@ -511,6 +860,30 @@ function createLexer(defs, options = {}) {
|
|
|
511
860
|
const multiMode = modes.length > 1 || compiled.some((c) => c.popMode || c.pushMode !== -1);
|
|
512
861
|
const namesModes = modes.length > 1;
|
|
513
862
|
const initial = modes[0];
|
|
863
|
+
let fusedMember = null;
|
|
864
|
+
let fusedText = "";
|
|
865
|
+
function fusedEnd(run, text, offset) {
|
|
866
|
+
run.regex.lastIndex = offset;
|
|
867
|
+
const match = run.regex.exec(text);
|
|
868
|
+
if (match === null) return -1;
|
|
869
|
+
if (match[0].length === 0) {
|
|
870
|
+
for (const member of run.members) {
|
|
871
|
+
member.regex.lastIndex = offset;
|
|
872
|
+
const one = member.regex.exec(text);
|
|
873
|
+
if (one !== null && one[0].length > 0) {
|
|
874
|
+
fusedMember = member;
|
|
875
|
+
fusedText = one[0];
|
|
876
|
+
return offset + fusedText.length;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return -1;
|
|
880
|
+
}
|
|
881
|
+
let group = 1;
|
|
882
|
+
while (match[group] === void 0) group++;
|
|
883
|
+
fusedMember = run.members[group - 1];
|
|
884
|
+
fusedText = match[0];
|
|
885
|
+
return offset + fusedText.length;
|
|
886
|
+
}
|
|
514
887
|
function longerAltEnd(def, text, offset) {
|
|
515
888
|
const alt = def.longerAltDef;
|
|
516
889
|
if (alt.literal !== void 0) return text.startsWith(alt.literal, offset) ? offset + alt.literal.length : -1;
|
|
@@ -530,19 +903,30 @@ function createLexer(defs, options = {}) {
|
|
|
530
903
|
outer: while (offset < length) {
|
|
531
904
|
const applicable = mode.dispatch ? candidates(mode, text.charCodeAt(offset)) : mode.members;
|
|
532
905
|
for (let i = 0; i < applicable.length; i++) {
|
|
533
|
-
|
|
906
|
+
let def = applicable[i];
|
|
534
907
|
let end = -1;
|
|
535
908
|
let matched = null;
|
|
536
|
-
if (def.
|
|
909
|
+
if (def.members !== null) {
|
|
910
|
+
end = fusedEnd(def, text, offset);
|
|
911
|
+
if (end === -1) continue;
|
|
912
|
+
def = fusedMember;
|
|
913
|
+
matched = fusedText;
|
|
914
|
+
} else if (def.literal !== void 0) {
|
|
537
915
|
if (text.startsWith(def.literal, offset)) end = offset + def.literal.length;
|
|
538
|
-
} else if (def.skip) {
|
|
539
|
-
def.regex.lastIndex = offset;
|
|
540
|
-
if (def.regex.test(text) && def.regex.lastIndex > offset) end = def.regex.lastIndex;
|
|
541
916
|
} else {
|
|
542
|
-
def.
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
917
|
+
end = def.scanner === null ? DEFER : scanEnd(def.scanner, text, offset, length);
|
|
918
|
+
if (end === DEFER) {
|
|
919
|
+
end = -1;
|
|
920
|
+
if (def.skip) {
|
|
921
|
+
def.regex.lastIndex = offset;
|
|
922
|
+
if (def.regex.test(text) && def.regex.lastIndex > offset) end = def.regex.lastIndex;
|
|
923
|
+
} else {
|
|
924
|
+
def.regex.lastIndex = offset;
|
|
925
|
+
const match = def.regex.exec(text);
|
|
926
|
+
if (match !== null && match[0].length > 0) matched = match[0];
|
|
927
|
+
if (matched !== null) end = offset + matched.length;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
546
930
|
}
|
|
547
931
|
if (end === -1) continue;
|
|
548
932
|
let winner = def;
|
|
@@ -555,14 +939,7 @@ function createLexer(defs, options = {}) {
|
|
|
555
939
|
}
|
|
556
940
|
if (!winner.skip) {
|
|
557
941
|
const image = winner.literal !== void 0 ? winner.literal : winner === def && matched !== null ? matched : text.slice(offset, end);
|
|
558
|
-
tokens.push({
|
|
559
|
-
type: winner.name,
|
|
560
|
-
image,
|
|
561
|
-
start: offset,
|
|
562
|
-
end,
|
|
563
|
-
line: trackLines ? line : 0,
|
|
564
|
-
col: trackLines ? offset - lineStart + 1 : 0
|
|
565
|
-
});
|
|
942
|
+
tokens.push(trackLines ? { type: winner.name, image, start: offset, end, line, col: offset - lineStart + 1 } : { type: winner.name, id: winner.id, image, start: offset, end });
|
|
566
943
|
}
|
|
567
944
|
if (trackLines) {
|
|
568
945
|
for (let j = offset; j < end; j++) {
|
|
@@ -598,7 +975,238 @@ function createLexer(defs, options = {}) {
|
|
|
598
975
|
}
|
|
599
976
|
const inMode = (mode) => namesModes ? ` (in lexer mode '${mode.name}')` : "";
|
|
600
977
|
const describe = (offset, line, lineStart) => trackLines ? `line ${line} column ${offset - lineStart + 1}` : `offset ${offset}`;
|
|
601
|
-
return { tokenize, tokenNames, positions, modeNames };
|
|
978
|
+
if (!options.incremental) return { tokenize, tokenNames, positions, modeNames };
|
|
979
|
+
const recover = !!options.recover;
|
|
980
|
+
const errorTokenName = options.errorToken || "Error";
|
|
981
|
+
function snapshotStack(stack, depth) {
|
|
982
|
+
const ids = new Array(depth);
|
|
983
|
+
for (let i = 0; i < depth; i++) ids[i] = stack[i].id;
|
|
984
|
+
return ids;
|
|
985
|
+
}
|
|
986
|
+
function sameStack(a, b) {
|
|
987
|
+
if (a.length !== b.length) return false;
|
|
988
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
989
|
+
return true;
|
|
990
|
+
}
|
|
991
|
+
function scan(text, offset, mode, stack, depth, line, lineStart) {
|
|
992
|
+
const tokens = [];
|
|
993
|
+
const checkpoints = [];
|
|
994
|
+
const length = text.length;
|
|
995
|
+
outer: while (offset < length) {
|
|
996
|
+
checkpoints.push({ modeId: mode.id, stack: snapshotStack(stack, depth), line, lineStart, offset });
|
|
997
|
+
const applicable = mode.dispatch ? candidates(mode, text.charCodeAt(offset)) : mode.members;
|
|
998
|
+
for (let i = 0; i < applicable.length; i++) {
|
|
999
|
+
let def = applicable[i];
|
|
1000
|
+
let end = -1;
|
|
1001
|
+
let matched = null;
|
|
1002
|
+
if (def.members !== null) {
|
|
1003
|
+
end = fusedEnd(def, text, offset);
|
|
1004
|
+
if (end === -1) continue;
|
|
1005
|
+
def = fusedMember;
|
|
1006
|
+
matched = fusedText;
|
|
1007
|
+
} else if (def.literal !== void 0) {
|
|
1008
|
+
if (text.startsWith(def.literal, offset)) end = offset + def.literal.length;
|
|
1009
|
+
} else {
|
|
1010
|
+
end = def.scanner === null ? DEFER : scanEnd(def.scanner, text, offset, length);
|
|
1011
|
+
if (end === DEFER) {
|
|
1012
|
+
end = -1;
|
|
1013
|
+
if (def.skip) {
|
|
1014
|
+
def.regex.lastIndex = offset;
|
|
1015
|
+
if (def.regex.test(text) && def.regex.lastIndex > offset) end = def.regex.lastIndex;
|
|
1016
|
+
} else {
|
|
1017
|
+
def.regex.lastIndex = offset;
|
|
1018
|
+
const match = def.regex.exec(text);
|
|
1019
|
+
if (match !== null && match[0].length > 0) matched = match[0];
|
|
1020
|
+
if (matched !== null) end = offset + matched.length;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
if (end === -1) continue;
|
|
1025
|
+
let winner = def;
|
|
1026
|
+
if (def.longerAltDef) {
|
|
1027
|
+
const altEnd = longerAltEnd(def, text, offset);
|
|
1028
|
+
if (altEnd > end) {
|
|
1029
|
+
end = altEnd;
|
|
1030
|
+
winner = def.longerAltDef;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
if (!winner.skip) {
|
|
1034
|
+
const image = winner.literal !== void 0 ? winner.literal : winner === def && matched !== null ? matched : text.slice(offset, end);
|
|
1035
|
+
tokens.push(trackLines ? { type: winner.name, image, start: offset, end, line, col: offset - lineStart + 1 } : { type: winner.name, id: winner.id, image, start: offset, end });
|
|
1036
|
+
}
|
|
1037
|
+
if (trackLines) {
|
|
1038
|
+
for (let j = offset; j < end; j++) {
|
|
1039
|
+
if (text.charCodeAt(j) !== 10) continue;
|
|
1040
|
+
line++;
|
|
1041
|
+
lineStart = j + 1;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
if (multiMode) {
|
|
1045
|
+
if (winner.popMode) {
|
|
1046
|
+
if (depth === 0) {
|
|
1047
|
+
if (!recover) {
|
|
1048
|
+
throw new LexError(
|
|
1049
|
+
`Token '${winner.name}' popped the initial lexer mode at ${describe(offset, line, lineStart)}`,
|
|
1050
|
+
{ offset, line: trackLines ? line : 0, col: trackLines ? offset - lineStart + 1 : 0 }
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
} else {
|
|
1054
|
+
mode = stack[--depth];
|
|
1055
|
+
}
|
|
1056
|
+
} else if (winner.pushMode !== -1) {
|
|
1057
|
+
stack[depth++] = mode;
|
|
1058
|
+
mode = modes[winner.pushMode];
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
offset = end;
|
|
1062
|
+
continue outer;
|
|
1063
|
+
}
|
|
1064
|
+
if (recover) {
|
|
1065
|
+
tokens.push(trackLines ? { type: errorTokenName, image: text[offset], start: offset, end: offset + 1, line, col: offset - lineStart + 1 } : { type: errorTokenName, id: -1, image: text[offset], start: offset, end: offset + 1 });
|
|
1066
|
+
if (trackLines && text.charCodeAt(offset) === 10) {
|
|
1067
|
+
line++;
|
|
1068
|
+
lineStart = offset + 1;
|
|
1069
|
+
}
|
|
1070
|
+
offset++;
|
|
1071
|
+
continue outer;
|
|
1072
|
+
}
|
|
1073
|
+
const column = offset - lineStart + 1;
|
|
1074
|
+
throw new LexError(
|
|
1075
|
+
trackLines ? `Unexpected character '${text[offset]}' at line ${line} column ${column}${inMode(mode)}` : `Unexpected character '${text[offset]}' at offset ${offset}${inMode(mode)}`,
|
|
1076
|
+
{ offset, line: trackLines ? line : 0, col: trackLines ? column : 0 }
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
return { tokens, checkpoints, mode, stack, depth, line, lineStart };
|
|
1080
|
+
}
|
|
1081
|
+
function tokenizeIncremental(text) {
|
|
1082
|
+
if (typeof text !== "string") throw new TypeError("tokenizeIncremental: expected a string");
|
|
1083
|
+
const result = scan(text, 0, initial, multiMode ? [] : [], 0, 1, 0);
|
|
1084
|
+
return { text, tokens: result.tokens, checkpoints: result.checkpoints, eof: { modeId: result.mode.id, stack: snapshotStack(result.stack, result.depth), line: result.line, lineStart: result.lineStart, offset: text.length } };
|
|
1085
|
+
}
|
|
1086
|
+
function tokenFloor(tokens, offset) {
|
|
1087
|
+
let lo = 0;
|
|
1088
|
+
let hi = tokens.length;
|
|
1089
|
+
while (lo < hi) {
|
|
1090
|
+
const mid = lo + hi >>> 1;
|
|
1091
|
+
if (tokens[mid].start < offset) lo = mid + 1;
|
|
1092
|
+
else hi = mid;
|
|
1093
|
+
}
|
|
1094
|
+
return lo;
|
|
1095
|
+
}
|
|
1096
|
+
function checkpointFloor(checkpoints, eof, offset) {
|
|
1097
|
+
let lo = 0;
|
|
1098
|
+
let hi = checkpoints.length + 1;
|
|
1099
|
+
while (lo < hi) {
|
|
1100
|
+
const mid = lo + hi >>> 1;
|
|
1101
|
+
const at = mid < checkpoints.length ? checkpoints[mid].offset : eof.offset;
|
|
1102
|
+
if (at <= offset) lo = mid + 1;
|
|
1103
|
+
else hi = mid;
|
|
1104
|
+
}
|
|
1105
|
+
return lo - 1;
|
|
1106
|
+
}
|
|
1107
|
+
function checkpointAt(checkpoints, eof, offset) {
|
|
1108
|
+
if (eof.offset === offset) return eof;
|
|
1109
|
+
let lo = 0;
|
|
1110
|
+
let hi = checkpoints.length;
|
|
1111
|
+
while (lo < hi) {
|
|
1112
|
+
const mid = lo + hi >>> 1;
|
|
1113
|
+
if (checkpoints[mid].offset < offset) lo = mid + 1;
|
|
1114
|
+
else hi = mid;
|
|
1115
|
+
}
|
|
1116
|
+
return lo < checkpoints.length && checkpoints[lo].offset === offset ? checkpoints[lo] : null;
|
|
1117
|
+
}
|
|
1118
|
+
function relex(newText, prev, edit) {
|
|
1119
|
+
if (typeof newText !== "string") throw new TypeError("relex: expected a string");
|
|
1120
|
+
const tokens = prev.tokens;
|
|
1121
|
+
const checkpoints = prev.checkpoints;
|
|
1122
|
+
const delta = edit.newEnd - edit.oldEnd;
|
|
1123
|
+
let resumeIndex = checkpointFloor(checkpoints, prev.eof, edit.start);
|
|
1124
|
+
if (resumeIndex > 0) resumeIndex--;
|
|
1125
|
+
let boundary = resumeIndex < checkpoints.length ? checkpoints[resumeIndex].offset : prev.eof.offset;
|
|
1126
|
+
let ti = tokenFloor(tokens, boundary);
|
|
1127
|
+
while (ti > 0 && tokens[ti - 1].end === boundary) {
|
|
1128
|
+
ti--;
|
|
1129
|
+
boundary = tokens[ti].start;
|
|
1130
|
+
}
|
|
1131
|
+
if (boundary < (resumeIndex < checkpoints.length ? checkpoints[resumeIndex].offset : prev.eof.offset)) {
|
|
1132
|
+
resumeIndex = checkpointFloor(checkpoints, prev.eof, boundary);
|
|
1133
|
+
}
|
|
1134
|
+
const resume = resumeIndex < checkpoints.length ? checkpoints[resumeIndex] : prev.eof;
|
|
1135
|
+
const resumeMode = modes[resume.modeId];
|
|
1136
|
+
const resumeStack = resume.stack.map((id) => modes[id]);
|
|
1137
|
+
const scanned = scan(newText, resume.offset, resumeMode, resumeStack, resumeStack.length, resume.line, resume.lineStart);
|
|
1138
|
+
const scannedEof = { modeId: scanned.mode.id, stack: snapshotStack(scanned.stack, scanned.depth), line: scanned.line, lineStart: scanned.lineStart, offset: newText.length };
|
|
1139
|
+
const VERIFY_WINDOW = 4;
|
|
1140
|
+
function convergenceVerified(oldOffset, newOffset) {
|
|
1141
|
+
const oldStart = tokenFloor(tokens, oldOffset);
|
|
1142
|
+
const freshStart = tokenFloor(scanned.tokens, newOffset);
|
|
1143
|
+
for (let k = 0; k < VERIFY_WINDOW; k++) {
|
|
1144
|
+
const oldTok = tokens[oldStart + k];
|
|
1145
|
+
const freshTok = scanned.tokens[freshStart + k];
|
|
1146
|
+
if (oldTok === void 0 && freshTok === void 0) return true;
|
|
1147
|
+
if (oldTok === void 0 || freshTok === void 0) return false;
|
|
1148
|
+
if (oldTok.type !== freshTok.type || oldTok.image !== freshTok.image) return false;
|
|
1149
|
+
}
|
|
1150
|
+
return true;
|
|
1151
|
+
}
|
|
1152
|
+
let convergeNewOffset = -1;
|
|
1153
|
+
let convergeOldOffset = -1;
|
|
1154
|
+
let convergeOldCheckpoint = null;
|
|
1155
|
+
let freshCheckpointCount = 0;
|
|
1156
|
+
for (let s = 0; s <= scanned.checkpoints.length; s++) {
|
|
1157
|
+
const cp = s < scanned.checkpoints.length ? scanned.checkpoints[s] : scannedEof;
|
|
1158
|
+
if (cp.offset < edit.newEnd) continue;
|
|
1159
|
+
const oldOffset = cp.offset - delta;
|
|
1160
|
+
const oldCp = checkpointAt(checkpoints, prev.eof, oldOffset);
|
|
1161
|
+
if (oldCp && oldCp.modeId === cp.modeId && sameStack(oldCp.stack, cp.stack) && convergenceVerified(oldOffset, cp.offset)) {
|
|
1162
|
+
convergeNewOffset = cp.offset;
|
|
1163
|
+
convergeOldOffset = oldOffset;
|
|
1164
|
+
convergeOldCheckpoint = oldCp;
|
|
1165
|
+
freshCheckpointCount = s;
|
|
1166
|
+
break;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
const tokenFrom = tokenFloor(tokens, resume.offset);
|
|
1170
|
+
const checkpointFrom = resumeIndex;
|
|
1171
|
+
if (convergeNewOffset === -1) {
|
|
1172
|
+
tokens.splice(tokenFrom, tokens.length - tokenFrom, ...scanned.tokens);
|
|
1173
|
+
checkpoints.splice(checkpointFrom, checkpoints.length - checkpointFrom, ...scanned.checkpoints);
|
|
1174
|
+
prev.text = newText;
|
|
1175
|
+
prev.eof = scannedEof;
|
|
1176
|
+
return prev;
|
|
1177
|
+
}
|
|
1178
|
+
const freshTokenCount = tokenFloor(scanned.tokens, convergeNewOffset);
|
|
1179
|
+
const lineDelta = trackLines ? scanned.checkpoints.length > freshCheckpointCount ? scanned.checkpoints[freshCheckpointCount].line - convergeOldCheckpoint.line : scannedEof.line - convergeOldCheckpoint.line : 0;
|
|
1180
|
+
const boundaryOldLineStart = convergeOldCheckpoint.lineStart;
|
|
1181
|
+
const boundaryNewLineStart = (freshCheckpointCount < scanned.checkpoints.length ? scanned.checkpoints[freshCheckpointCount] : scannedEof).lineStart;
|
|
1182
|
+
const shiftedLineStart = (oldLineStart) => oldLineStart === boundaryOldLineStart ? boundaryNewLineStart : oldLineStart + delta;
|
|
1183
|
+
const tailTokenFrom = tokenFloor(tokens, convergeOldOffset);
|
|
1184
|
+
for (let i = tailTokenFrom; i < tokens.length; i++) {
|
|
1185
|
+
const t = tokens[i];
|
|
1186
|
+
if (trackLines) {
|
|
1187
|
+
const oldLineStart = t.start - (t.col - 1);
|
|
1188
|
+
t.col = t.start + delta - shiftedLineStart(oldLineStart) + 1;
|
|
1189
|
+
t.line += lineDelta;
|
|
1190
|
+
}
|
|
1191
|
+
t.start += delta;
|
|
1192
|
+
t.end += delta;
|
|
1193
|
+
}
|
|
1194
|
+
tokens.splice(tokenFrom, tailTokenFrom - tokenFrom, ...scanned.tokens.slice(0, freshTokenCount));
|
|
1195
|
+
const tailCheckpointFrom = checkpointFloor(checkpoints, prev.eof, convergeOldOffset - 1) + 1;
|
|
1196
|
+
for (let i = tailCheckpointFrom; i < checkpoints.length; i++) {
|
|
1197
|
+
const cp = checkpoints[i];
|
|
1198
|
+
cp.lineStart = shiftedLineStart(cp.lineStart);
|
|
1199
|
+
cp.line += lineDelta;
|
|
1200
|
+
cp.offset += delta;
|
|
1201
|
+
}
|
|
1202
|
+
checkpoints.splice(checkpointFrom, tailCheckpointFrom - checkpointFrom, ...scanned.checkpoints.slice(0, freshCheckpointCount));
|
|
1203
|
+
prev.text = newText;
|
|
1204
|
+
prev.eof.lineStart = shiftedLineStart(prev.eof.lineStart);
|
|
1205
|
+
prev.eof.line += lineDelta;
|
|
1206
|
+
prev.eof.offset += delta;
|
|
1207
|
+
return prev;
|
|
1208
|
+
}
|
|
1209
|
+
return { tokenize, tokenizeIncremental, relex, tokenNames, positions, modeNames };
|
|
602
1210
|
}
|
|
603
1211
|
|
|
604
1212
|
// src/values.js
|