parseman 0.55.4 → 0.56.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/dist/analysis/choice-cost.d.ts.map +1 -1
- package/dist/analysis/commitment.d.ts.map +1 -1
- package/dist/analysis/diagnostics.js +3353 -3095
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/analysis/duplication.d.ts.map +1 -1
- package/dist/analysis/gating.d.ts.map +1 -1
- package/dist/cli/index.js +1813 -1555
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/attempt.d.ts.map +1 -1
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/lines.d.ts +79 -0
- package/dist/combinators/lines.d.ts.map +1 -0
- package/dist/combinators/trivia-skip.d.ts +1 -0
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/compiler/fields.d.ts.map +1 -1
- package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
- package/dist/compiler/ir-serialize.d.ts.map +1 -1
- package/dist/compiler/token-alphabet.d.ts.map +1 -1
- package/dist/compiler/token-capability.js +25 -0
- package/dist/compiler/token-capability.js.map +1 -1
- package/dist/coverage.d.ts.map +1 -1
- package/dist/cst/reflection.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1905 -1625
- package/dist/index.js.map +3 -3
- package/dist/language-service/index.js +2 -0
- package/dist/language-service/index.js.map +1 -1
- package/dist/parse-context.d.ts.map +1 -1
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.js +1318 -1045
- package/dist/plugin/index.js.map +3 -3
- package/dist/plugin/ir-eval.d.ts +0 -4
- package/dist/plugin/ir-eval.d.ts.map +1 -1
- package/dist/run/index.js +2 -0
- package/dist/run/index.js.map +1 -1
- package/dist/spec/index.js +15 -0
- package/dist/spec/index.js.map +1 -1
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/table/assemble.d.ts +0 -65
- package/dist/table/assemble.d.ts.map +1 -1
- package/dist/table/emit.d.ts.map +1 -1
- package/dist/table/encode.d.ts.map +1 -1
- package/dist/table/exec.d.ts.map +1 -1
- package/dist/table/index.js +3442 -3192
- package/dist/table/index.js.map +3 -3
- package/dist/table/line-scan.d.ts +9 -0
- package/dist/table/line-scan.d.ts.map +1 -0
- package/dist/table/program.d.ts +11 -2
- package/dist/table/program.d.ts.map +1 -1
- package/dist/types.d.ts +21 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/analysis/choice-cost.ts +1 -0
- package/src/analysis/commitment.ts +13 -0
- package/src/analysis/duplication.ts +3 -0
- package/src/analysis/gating.ts +5 -0
- package/src/analysis/rebuild.ts +2 -2
- package/src/combinators/attempt.ts +2 -0
- package/src/combinators/choice.ts +3 -0
- package/src/combinators/dispatch.ts +3 -0
- package/src/combinators/expect.ts +5 -0
- package/src/combinators/first-set.ts +9 -1
- package/src/combinators/lines.ts +244 -0
- package/src/combinators/trivia-skip.ts +4 -2
- package/src/compiler/fields.ts +3 -0
- package/src/compiler/grammar-coverage-ids.ts +1 -0
- package/src/compiler/ir-serialize.ts +10 -0
- package/src/compiler/token-alphabet.ts +3 -1
- package/src/coverage.ts +6 -0
- package/src/cst/reflection.ts +3 -0
- package/src/index.ts +2 -0
- package/src/parse-context.ts +2 -0
- package/src/plugin/evaluator.ts +4 -0
- package/src/plugin/ir-eval.ts +3 -0
- package/src/spec/model.ts +15 -0
- package/src/table/assemble.ts +6 -12
- package/src/table/emit.ts +7 -6
- package/src/table/encode.ts +18 -6
- package/src/table/exec.ts +3 -0
- package/src/table/line-scan.ts +19 -0
- package/src/table/program.ts +17 -2
- package/src/types.ts +14 -0
- package/src/version.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -155,6 +155,11 @@ function matchesEmptyBody(p, seen, resolve) {
|
|
|
155
155
|
return true;
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
|
+
// A run consumes at least its first line's prefix, and its body after that.
|
|
159
|
+
case "lines":
|
|
160
|
+
return me(d.first ?? d.prefix) && me(d.body);
|
|
161
|
+
case "columns":
|
|
162
|
+
return d.n === 0;
|
|
158
163
|
default:
|
|
159
164
|
return true;
|
|
160
165
|
}
|
|
@@ -269,6 +274,11 @@ function firstSetBody(p, seen, resolve, activeTrivia) {
|
|
|
269
274
|
resolve,
|
|
270
275
|
d.clearTrivia ? null : d.triviaParser ?? activeTrivia
|
|
271
276
|
);
|
|
277
|
+
// A run starts with its first line's prefix; a nullable prefix starts with anything.
|
|
278
|
+
case "lines": {
|
|
279
|
+
const opening = d.first ?? d.prefix;
|
|
280
|
+
return empties(opening) ? any() : fs(opening);
|
|
281
|
+
}
|
|
272
282
|
default:
|
|
273
283
|
return p._meta.firstSet;
|
|
274
284
|
}
|
|
@@ -1293,6 +1303,8 @@ function createParseContext() {
|
|
|
1293
1303
|
_triviaCaptureMask: void 0,
|
|
1294
1304
|
trackLines: false,
|
|
1295
1305
|
state: void 0,
|
|
1306
|
+
lineView: void 0,
|
|
1307
|
+
_columnsSplit: void 0,
|
|
1296
1308
|
build: void 0,
|
|
1297
1309
|
_errors: void 0,
|
|
1298
1310
|
_tolerant: void 0,
|
|
@@ -2014,11 +2026,12 @@ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
|
|
|
2014
2026
|
return result;
|
|
2015
2027
|
}
|
|
2016
2028
|
function saveLookaheadMark(ctx) {
|
|
2017
|
-
return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
|
|
2029
|
+
return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null, split: ctx._columnsSplit };
|
|
2018
2030
|
}
|
|
2019
2031
|
function rollbackLookahead(ctx, mark) {
|
|
2020
2032
|
rollbackTrivia(ctx, mark);
|
|
2021
2033
|
if (ctx._probe) ctx._probe.best = mark.probeBest;
|
|
2034
|
+
ctx._columnsSplit = mark.split;
|
|
2022
2035
|
}
|
|
2023
2036
|
function scanWithLabels(input, cur, ctx) {
|
|
2024
2037
|
const triviaP = ctx.trivia;
|
|
@@ -2887,6 +2900,12 @@ function derive(c, seen) {
|
|
|
2887
2900
|
return deriveExpected2(def.parser);
|
|
2888
2901
|
case "expect":
|
|
2889
2902
|
return def.expected;
|
|
2903
|
+
case "lines": {
|
|
2904
|
+
const opening = def.first ?? def.prefix;
|
|
2905
|
+
return matchesEmpty(opening) ? [...deriveExpected2(opening), ...deriveExpected2(def.body)] : deriveExpected2(opening);
|
|
2906
|
+
}
|
|
2907
|
+
case "columns":
|
|
2908
|
+
return [`columns(${def.n})`];
|
|
2890
2909
|
case "node":
|
|
2891
2910
|
case "grammar":
|
|
2892
2911
|
case "trivia":
|
|
@@ -3074,9 +3093,11 @@ function choice(...args) {
|
|
|
3074
3093
|
for (let i = 0; i < parsers.length; i++) {
|
|
3075
3094
|
if (gates[i] && !gates[i](ctx.state)) continue;
|
|
3076
3095
|
const mark = saveTriviaMark(ctx);
|
|
3096
|
+
const split = ctx._columnsSplit;
|
|
3077
3097
|
const result = parsers[i].parse(input, pos, ctx);
|
|
3078
3098
|
if (!result.ok) {
|
|
3079
3099
|
rollbackTrivia(ctx, mark);
|
|
3100
|
+
ctx._columnsSplit = split;
|
|
3080
3101
|
const at = result.span.start;
|
|
3081
3102
|
if (at > expectedAt) {
|
|
3082
3103
|
expectedAt = at;
|
|
@@ -3297,6 +3318,9 @@ function parserUsesRouted(parser2, seen = /* @__PURE__ */ new Set()) {
|
|
|
3297
3318
|
return parserUsesRouted(def.sentinel, seen) || def.skip.some((entry) => parserUsesRouted(entry, seen)) || def.recoverAt !== void 0 && parserUsesRouted(def.recoverAt, seen) || def.stopAt !== void 0 && parserUsesRouted(def.stopAt, seen);
|
|
3298
3319
|
case "recover":
|
|
3299
3320
|
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.sentinel, seen);
|
|
3321
|
+
// The body parses its own input, so only the checks on this one can reuse a routed head.
|
|
3322
|
+
case "lines":
|
|
3323
|
+
return [def.prefix, def.first, def.lazy].some((entry) => entry !== void 0 && parserUsesRouted(entry, seen));
|
|
3300
3324
|
case "lazy":
|
|
3301
3325
|
try {
|
|
3302
3326
|
return parserUsesRouted(def.thunk(), seen);
|
|
@@ -3550,9 +3574,11 @@ function attempt(parser2, options) {
|
|
|
3550
3574
|
const mark = saveCstMark(ctx);
|
|
3551
3575
|
const log = ctx._triviaLog?.length;
|
|
3552
3576
|
const rootLog = ctx._rootTriviaLog?.length;
|
|
3577
|
+
const split = ctx._columnsSplit;
|
|
3553
3578
|
const result = parser2.parse(input, pos, ctx);
|
|
3554
3579
|
if (result.ok) return result;
|
|
3555
3580
|
rollbackCstCapture(ctx, mark);
|
|
3581
|
+
ctx._columnsSplit = split;
|
|
3556
3582
|
if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
|
|
3557
3583
|
if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
|
|
3558
3584
|
if (result.committed && !contain) return result;
|
|
@@ -4344,6 +4370,9 @@ function childrenOf(def) {
|
|
|
4344
4370
|
];
|
|
4345
4371
|
case "routed":
|
|
4346
4372
|
return def.fallback ? [def.fallback] : [];
|
|
4373
|
+
case "lines":
|
|
4374
|
+
return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
|
|
4375
|
+
case "columns":
|
|
4347
4376
|
case "literal":
|
|
4348
4377
|
case "regex":
|
|
4349
4378
|
case "keywords":
|
|
@@ -4655,12 +4684,12 @@ function beginCompileDegradationDrain() {
|
|
|
4655
4684
|
return true;
|
|
4656
4685
|
});
|
|
4657
4686
|
if (fresh.length === 0) return;
|
|
4658
|
-
const
|
|
4687
|
+
const lines2 = formatDegradations(fresh);
|
|
4659
4688
|
if (resolveDegradationLevel() === "error") {
|
|
4660
4689
|
throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
|
|
4661
|
-
${
|
|
4690
|
+
${lines2.join("\n")}`);
|
|
4662
4691
|
}
|
|
4663
|
-
for (const l of
|
|
4692
|
+
for (const l of lines2) console.warn(l);
|
|
4664
4693
|
};
|
|
4665
4694
|
}
|
|
4666
4695
|
|
|
@@ -4878,6 +4907,8 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
4878
4907
|
return parserHasOwnFields(d.sentinel, seen) || d.skip.some((x) => parserHasOwnFields(x, seen)) || d.recoverAt !== void 0 && parserHasOwnFields(d.recoverAt, seen) || d.stopAt !== void 0 && parserHasOwnFields(d.stopAt, seen);
|
|
4879
4908
|
case "recover":
|
|
4880
4909
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.sentinel, seen);
|
|
4910
|
+
case "lines":
|
|
4911
|
+
return parserHasOwnFields(d.body, seen);
|
|
4881
4912
|
case "routed":
|
|
4882
4913
|
return d.fallback ? parserHasOwnFields(d.fallback, seen) : false;
|
|
4883
4914
|
case "many":
|
|
@@ -5291,1605 +5322,1633 @@ function sourceLeaf(root, fn) {
|
|
|
5291
5322
|
return makeLeaf(root, fn, true);
|
|
5292
5323
|
}
|
|
5293
5324
|
|
|
5294
|
-
// src/
|
|
5295
|
-
function
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
return d.parsers;
|
|
5300
|
-
case "dispatch":
|
|
5301
|
-
return [
|
|
5302
|
-
d.selector,
|
|
5303
|
-
...d.cases.map((c) => c.parser),
|
|
5304
|
-
...d.matchers ? d.matchers.map((c) => c.parser) : [],
|
|
5305
|
-
...d.otherwise === void 0 ? [] : [d.otherwise]
|
|
5306
|
-
];
|
|
5307
|
-
case "sepBy":
|
|
5308
|
-
return [d.parser, d.separator];
|
|
5309
|
-
case "recover":
|
|
5310
|
-
return [d.parser, d.sentinel];
|
|
5311
|
-
case "scanTo":
|
|
5312
|
-
return [
|
|
5313
|
-
d.sentinel,
|
|
5314
|
-
...d.skip,
|
|
5315
|
-
...d.recoverAt === void 0 ? [] : [d.recoverAt],
|
|
5316
|
-
...d.stopAt === void 0 ? [] : [d.stopAt]
|
|
5317
|
-
];
|
|
5318
|
-
case "grammar":
|
|
5319
|
-
return d.triviaParser ? [d.parser, d.triviaParser] : [d.parser];
|
|
5320
|
-
case "routed":
|
|
5321
|
-
return d.fallback ? [d.fallback] : [];
|
|
5322
|
-
// A `lazy` is a REFERENCE, not a subtree. Descending through it would make
|
|
5323
|
-
// every rule's walk cover the whole reachable grammar — site paths become
|
|
5324
|
-
// nonsense, and a structural hash includes half the grammar. Treated as a
|
|
5325
|
-
// leaf, keyed by the rule name it refers to, which is also the right
|
|
5326
|
-
// semantics: two productions referencing `g.Ident` really do fill that slot
|
|
5327
|
-
// the same way.
|
|
5328
|
-
case "lazy":
|
|
5329
|
-
case "literal":
|
|
5330
|
-
case "regex":
|
|
5331
|
-
case "keywords":
|
|
5332
|
-
case "guard":
|
|
5333
|
-
case "adjacency":
|
|
5334
|
-
case "unknown":
|
|
5335
|
-
return [];
|
|
5336
|
-
default: {
|
|
5337
|
-
const rec = d;
|
|
5338
|
-
return rec.parser ? [rec.parser] : [];
|
|
5339
|
-
}
|
|
5325
|
+
// src/table/lex-body.ts
|
|
5326
|
+
function buildLexBodyRecognizer(spec, constants) {
|
|
5327
|
+
const raw = constants[spec[0]];
|
|
5328
|
+
if (!(raw instanceof RegExp)) {
|
|
5329
|
+
throw new TypeError("table lexical body does not reference a RegExp constant");
|
|
5340
5330
|
}
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
function intersection(a, b) {
|
|
5344
|
-
if (a.kind === "any") return b;
|
|
5345
|
-
if (b.kind === "any") return a;
|
|
5346
|
-
if (a.kind === "empty" || b.kind === "empty") return { kind: "empty" };
|
|
5347
|
-
const ranges = [];
|
|
5348
|
-
for (const ra of a.ranges) for (const rb of b.ranges) {
|
|
5349
|
-
const lo = Math.max(ra.lo, rb.lo);
|
|
5350
|
-
const hi = Math.min(ra.hi, rb.hi);
|
|
5351
|
-
if (lo <= hi) ranges.push({ lo, hi });
|
|
5331
|
+
if (!Number.isInteger(spec[1]) || spec[1] < -1 || spec[1] > 65535) {
|
|
5332
|
+
throw new TypeError("table lexical body suffix is not absent or one UTF-16 code unit");
|
|
5352
5333
|
}
|
|
5353
|
-
|
|
5334
|
+
const re = raw.sticky ? new RegExp(raw.source, raw.flags) : new RegExp(raw.source, `${raw.flags.replace(/[gy]/g, "")}y`);
|
|
5335
|
+
const suffix = spec[1];
|
|
5336
|
+
return (input, pos) => {
|
|
5337
|
+
re.lastIndex = pos;
|
|
5338
|
+
if (!re.test(input)) return -1;
|
|
5339
|
+
const end = re.lastIndex;
|
|
5340
|
+
if (suffix < 0) return end * 2;
|
|
5341
|
+
return input.charCodeAt(end) === suffix ? (end + 1) * 2 + 1 : end * 2;
|
|
5342
|
+
};
|
|
5354
5343
|
}
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5344
|
+
|
|
5345
|
+
// src/table/lex-program.ts
|
|
5346
|
+
var LEX_NODE_TERMINAL = 0;
|
|
5347
|
+
var LEX_NODE_SEQUENCE2 = 1;
|
|
5348
|
+
var LEX_NODE_SEQUENCE3 = 2;
|
|
5349
|
+
var LEX_NODE_SEQUENCE4 = 3;
|
|
5350
|
+
var LEX_NODE_SEQUENCE5 = 4;
|
|
5351
|
+
var LEX_NODE_CHOICE2 = 5;
|
|
5352
|
+
var LEX_NODE_CHOICE4 = 6;
|
|
5353
|
+
var LEX_NODE_CHOICE8 = 7;
|
|
5354
|
+
var LEX_NODE_NOT = 8;
|
|
5355
|
+
var LEX_NODE_OPTIONAL = 9;
|
|
5356
|
+
function lexProgramDigest(words) {
|
|
5357
|
+
let hash = 2166136261;
|
|
5358
|
+
for (const word2 of words) {
|
|
5359
|
+
hash ^= word2 | 0;
|
|
5360
|
+
hash = Math.imul(hash, 16777619);
|
|
5361
5361
|
}
|
|
5362
|
-
return
|
|
5363
|
-
}
|
|
5364
|
-
var ch = (c) => c >= 32 && c < 127 ? `'${String.fromCharCode(c)}'` : `\\u${c}`;
|
|
5365
|
-
function firstSetToString(fs) {
|
|
5366
|
-
if (fs.kind === "any") return "ANY";
|
|
5367
|
-
if (fs.kind === "empty") return "(empty)";
|
|
5368
|
-
return fs.ranges.map((r) => r.lo === r.hi ? ch(r.lo) : `${ch(r.lo)}-${ch(r.hi)}`).join(",");
|
|
5362
|
+
return hash | 0;
|
|
5369
5363
|
}
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
"nullable-prefix": "This arm begins with something optional \u2014 an optional(...) or a repeat that allows zero items \u2014 so the arm can also start with whatever comes AFTER it, and that is broad enough that no character rules the arm out.\nTo fix: give the optional part its own arm so each arm has a definite beginning, or require at least one item. A plain sepBy(item, sep) also matches nothing at all; pass { min: 1 } when the list must not be empty.",
|
|
5376
|
-
"cross-artifact-ref": "This arm hands off to another rule, and that rule has the same problem \u2014 it can begin with any character \u2014 so the cost is inherited rather than caused here.\nTo fix: run this check on the rule it refers to and fix it there. One rule given a definite beginning fixes every choice that uses it.",
|
|
5377
|
-
"opaque-wrapper": "This arm begins with a wrapper that only inspects parser state (a gate/withCtx/recover) and consumes no text, so it says nothing about which character the arm starts with.\nTo fix: put a term that actually matches text in front of the wrapper.",
|
|
5378
|
-
"ref-cycle": "This arm refers back to itself through a cycle of rules, and parseman could not work out a definite first character for any of them.\nTo fix: make sure the base case of the recursion begins with a fixed character or keyword, so the cycle has somewhere definite to start."
|
|
5379
|
-
};
|
|
5380
|
-
function refineSuggestion(cause, arm) {
|
|
5381
|
-
if (cause !== "broad-recognizer") return SUGGESTIONS[cause];
|
|
5382
|
-
const lead2 = peelToLeading(arm);
|
|
5383
|
-
const d = lead2._def;
|
|
5384
|
-
if (d.tag === "regex" && KEYWORD_REGEX_RE.test(d.source)) {
|
|
5385
|
-
return `This arm matches the fixed word \`${d.source}\` with a regular expression, and parseman cannot always tell from a regular expression which character it starts with.
|
|
5386
|
-
To fix: write it as word('\u2026') or keywords([\u2026]) instead. They match exactly the same text and compile to the same character scan, but they also tell parseman the first character, which is what lets the parser skip this arm when it cannot match.`;
|
|
5364
|
+
function classHas(cls, code) {
|
|
5365
|
+
if (code < 0) return false;
|
|
5366
|
+
if (code < 128) return cls.ascii[code] === 1;
|
|
5367
|
+
for (let i = 0; i < cls.hi.length; i += 2) {
|
|
5368
|
+
if (code >= cls.hi[i] && code <= cls.hi[i + 1]) return true;
|
|
5387
5369
|
}
|
|
5388
|
-
|
|
5389
|
-
|
|
5370
|
+
return false;
|
|
5371
|
+
}
|
|
5372
|
+
var committed = (ctx) => ctx._fc === true;
|
|
5373
|
+
function trackLexLines(ctx, input, end) {
|
|
5374
|
+
let at = ctx._lineScannedTo ?? 0;
|
|
5375
|
+
const starts = ctx._lineStarts;
|
|
5376
|
+
if (starts === void 0 || end <= at) return;
|
|
5377
|
+
while (at < end) {
|
|
5378
|
+
if (input.charCodeAt(at) === 10) starts.push(at + 1);
|
|
5379
|
+
at++;
|
|
5390
5380
|
}
|
|
5391
|
-
|
|
5381
|
+
ctx._lineScannedTo = end;
|
|
5392
5382
|
}
|
|
5393
|
-
function
|
|
5394
|
-
const
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
|
|
5398
|
-
seen.add(p);
|
|
5399
|
-
const d = p._def;
|
|
5400
|
-
switch (d.tag) {
|
|
5401
|
-
case "literal":
|
|
5402
|
-
case "regex":
|
|
5403
|
-
case "keywords":
|
|
5404
|
-
return isAny(p._meta.firstSet) ? { cause: "broad-recognizer", detail: `broad recognizer (${d.tag})` } : null;
|
|
5405
|
-
case "not":
|
|
5406
|
-
return { cause: "leading-not", detail: "leading not(...) (first-set ANY)" };
|
|
5407
|
-
// peek() CARRIES its body's first-set, so it is only broad when the body is
|
|
5408
|
-
// (or the body is nullable) — walk in for the real cause.
|
|
5409
|
-
case "peek":
|
|
5410
|
-
return walk(d.parser);
|
|
5411
|
-
case "scanTo":
|
|
5412
|
-
return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
|
|
5413
|
-
// `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
|
|
5414
|
-
// its own — it is the poison. `withCtx`/`recover` FORWARD their inner
|
|
5415
|
-
// first-set (see firstSetOf), so a broad result comes from the inner parser:
|
|
5416
|
-
// walk into it for a precise cause.
|
|
5417
|
-
case "guard":
|
|
5418
|
-
return { cause: "opaque-wrapper", detail: `opaque wrapper (${d.tag})` };
|
|
5419
|
-
// Zero-width and dropped from a sequence's first-set (isZeroWidthAssertion),
|
|
5420
|
-
// so it is never the reason an arm is broad — but it can be reached as the
|
|
5421
|
-
// sole term of a degenerate arm. Report it precisely rather than as a wrapper.
|
|
5422
|
-
case "adjacency":
|
|
5423
|
-
return { cause: "broad-recognizer", detail: `${d.polarity}() (zero-width assertion)` };
|
|
5424
|
-
// attempt/withCtx/recover FORWARD their inner first-set (transparent for
|
|
5425
|
-
// dispatch), so a broad result comes from the inner parser — walk into it.
|
|
5426
|
-
case "attempt":
|
|
5427
|
-
case "withCtx":
|
|
5428
|
-
case "recover":
|
|
5429
|
-
return walk(d.parser);
|
|
5430
|
-
case "lazy": {
|
|
5431
|
-
const name = p._ruleName;
|
|
5432
|
-
let target;
|
|
5433
|
-
try {
|
|
5434
|
-
target = d.thunk();
|
|
5435
|
-
} catch {
|
|
5436
|
-
const bound = name !== void 0 ? resolve?.(name) : void 0;
|
|
5437
|
-
if (bound === void 0)
|
|
5438
|
-
return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
|
|
5439
|
-
target = bound;
|
|
5440
|
-
}
|
|
5441
|
-
const inner = walk(target);
|
|
5442
|
-
if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
|
|
5443
|
-
return inner;
|
|
5444
|
-
}
|
|
5445
|
-
case "optional":
|
|
5446
|
-
case "many": {
|
|
5447
|
-
const inner = walk(d.parser);
|
|
5448
|
-
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
5449
|
-
}
|
|
5450
|
-
case "oneOrMore":
|
|
5451
|
-
case "transform":
|
|
5452
|
-
case "label":
|
|
5453
|
-
case "field":
|
|
5454
|
-
case "trivia":
|
|
5455
|
-
case "token":
|
|
5456
|
-
case "leaf":
|
|
5457
|
-
case "node":
|
|
5458
|
-
case "grammar":
|
|
5459
|
-
case "expect":
|
|
5460
|
-
return walk(d.parser);
|
|
5461
|
-
case "sequence": {
|
|
5462
|
-
let sawNullablePrefix = false;
|
|
5463
|
-
for (const t of d.parsers) {
|
|
5464
|
-
if (t._def.tag === "not") {
|
|
5465
|
-
sawNullablePrefix = true;
|
|
5466
|
-
continue;
|
|
5467
|
-
}
|
|
5468
|
-
if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve))) {
|
|
5469
|
-
const inner = walk(t);
|
|
5470
|
-
if (sawNullablePrefix)
|
|
5471
|
-
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
5472
|
-
return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
|
|
5473
|
-
}
|
|
5474
|
-
if (matchesEmpty(t, /* @__PURE__ */ new Set(), resolve)) {
|
|
5475
|
-
sawNullablePrefix = true;
|
|
5476
|
-
continue;
|
|
5477
|
-
}
|
|
5478
|
-
return null;
|
|
5479
|
-
}
|
|
5480
|
-
return { cause: "broad-recognizer", detail: "sequence of only nullable/zero-width terms" };
|
|
5481
|
-
}
|
|
5482
|
-
case "choice": {
|
|
5483
|
-
for (const a of d.parsers) {
|
|
5484
|
-
const r = walk(a);
|
|
5485
|
-
if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
|
|
5486
|
-
}
|
|
5487
|
-
return null;
|
|
5488
|
-
}
|
|
5489
|
-
case "dispatch":
|
|
5490
|
-
return walk(d.selector);
|
|
5491
|
-
default:
|
|
5492
|
-
return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
|
|
5493
|
-
}
|
|
5383
|
+
function terminalNode(matcher, failure, tracksLines) {
|
|
5384
|
+
const match = (input, pos) => {
|
|
5385
|
+
const result = matcher(input, pos);
|
|
5386
|
+
return result < 0 ? -1 : result / 2;
|
|
5494
5387
|
};
|
|
5495
|
-
return
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
continue;
|
|
5505
|
-
}
|
|
5506
|
-
break;
|
|
5507
|
-
}
|
|
5508
|
-
if (d.tag === "sequence" && d.parsers.length >= 1) return d.parsers[0];
|
|
5509
|
-
return cur;
|
|
5510
|
-
}
|
|
5511
|
-
function detectAntiPatterns(rule, arms) {
|
|
5512
|
-
const out = [];
|
|
5513
|
-
arms.forEach((arm, i) => {
|
|
5514
|
-
const lead2 = peelToLeading(arm);
|
|
5515
|
-
const ld = lead2._def;
|
|
5516
|
-
if (ld.tag === "not") {
|
|
5517
|
-
const inner = ld.parser;
|
|
5518
|
-
if (inner._def.tag === "not") {
|
|
5519
|
-
out.push({
|
|
5520
|
-
kind: "double-not",
|
|
5521
|
-
rule,
|
|
5522
|
-
armIndex: i,
|
|
5523
|
-
message: 'not(not(X)) is a hand-written way of saying "X must come next, but do not consume it". parseman does that for you, and the hand-written form is worse than nothing here: because not(...) reveals no first character, the arm cannot be skipped, and among sibling arms that share a first character it can select the wrong one.\nTo fix: write peek(X). It checks the same thing without consuming, and it also tells parseman that the arm starts with whatever X starts with.'
|
|
5524
|
-
});
|
|
5525
|
-
} else {
|
|
5526
|
-
out.push({
|
|
5527
|
-
kind: "leading-not",
|
|
5528
|
-
rule,
|
|
5529
|
-
armIndex: i,
|
|
5530
|
-
message: "This arm begins with not(...), which matches no text and so reveals no first character. The parser therefore cannot skip the arm and enters it at every position.\nTo fix: put the term that actually consumes text first, and keep not(...) after it as a trailing boundary check."
|
|
5531
|
-
});
|
|
5388
|
+
return {
|
|
5389
|
+
match,
|
|
5390
|
+
run: (input, pos, ctx) => {
|
|
5391
|
+
const end = match(input, pos);
|
|
5392
|
+
if (end < 0) {
|
|
5393
|
+
ctx._fe = pos;
|
|
5394
|
+
ctx._fx = failure;
|
|
5395
|
+
if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
|
|
5396
|
+
return -1;
|
|
5532
5397
|
}
|
|
5398
|
+
if (tracksLines) trackLexLines(ctx, input, end);
|
|
5399
|
+
return end;
|
|
5533
5400
|
}
|
|
5534
|
-
if (ld.tag === "regex" && KEYWORD_REGEX_RE.test(ld.source)) {
|
|
5535
|
-
out.push({
|
|
5536
|
-
kind: "keyword-regex",
|
|
5537
|
-
rule,
|
|
5538
|
-
armIndex: i,
|
|
5539
|
-
message: `This arm starts with the fixed word \`${ld.source}\` written as a regular expression. Writing it as word('\u2026') or keywords([\u2026]) matches exactly the same text and compiles to the same character scan, but also tells parseman the first character, which is what lets the parser skip the arm when it cannot match.`
|
|
5540
|
-
});
|
|
5541
|
-
}
|
|
5542
|
-
});
|
|
5543
|
-
return out;
|
|
5544
|
-
}
|
|
5545
|
-
function analyzeGating(entry, opts) {
|
|
5546
|
-
return analyzeGatingRules([[ruleNameOf2(entry) ?? opts?.entryName ?? "<entry>", entry]], opts);
|
|
5547
|
-
}
|
|
5548
|
-
function analyzeGatingRules(ruleMap, opts) {
|
|
5549
|
-
const raw = [];
|
|
5550
|
-
const antiPatterns = [];
|
|
5551
|
-
const unanalysable = [];
|
|
5552
|
-
const seenUnanalysable = /* @__PURE__ */ new Set();
|
|
5553
|
-
const visited = /* @__PURE__ */ new Set();
|
|
5554
|
-
const noteUnanalysable = (u) => {
|
|
5555
|
-
const key = `${u.rule}\0${u.kind}`;
|
|
5556
|
-
if (seenUnanalysable.has(key)) return;
|
|
5557
|
-
seenUnanalysable.add(key);
|
|
5558
|
-
unanalysable.push(u);
|
|
5559
5401
|
};
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
kind: "not-a-combinator",
|
|
5573
|
-
reason: `value of type ${p === null ? "null" : typeof p} is not a combinator and carries no _def descriptor.`
|
|
5574
|
-
}
|
|
5575
|
-
);
|
|
5576
|
-
return;
|
|
5577
|
-
}
|
|
5578
|
-
const d = def;
|
|
5579
|
-
const rule = ruleNameOf2(p) ?? enclosingRule;
|
|
5580
|
-
if (d.tag === "choice") {
|
|
5581
|
-
raw.push({ g: analyzeChoice(p, d, rule, opts?.resolveRef), rule, arms: d.parsers });
|
|
5582
|
-
antiPatterns.push(...detectAntiPatterns(rule, d.parsers));
|
|
5402
|
+
}
|
|
5403
|
+
function sequence2(a, b) {
|
|
5404
|
+
return {
|
|
5405
|
+
match: (input, pos) => {
|
|
5406
|
+
const e0 = a.match(input, pos);
|
|
5407
|
+
return e0 < 0 ? -1 : b.match(input, e0);
|
|
5408
|
+
},
|
|
5409
|
+
run: (input, pos, ctx) => {
|
|
5410
|
+
const e0 = a.run(input, pos, ctx);
|
|
5411
|
+
if (e0 < 0) return -1;
|
|
5412
|
+
ctx._fc = false;
|
|
5413
|
+
return b.run(input, e0, ctx);
|
|
5583
5414
|
}
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5415
|
+
};
|
|
5416
|
+
}
|
|
5417
|
+
function sequence3(a, b, c) {
|
|
5418
|
+
return {
|
|
5419
|
+
match: (input, pos) => {
|
|
5420
|
+
const e0 = a.match(input, pos);
|
|
5421
|
+
if (e0 < 0) return -1;
|
|
5422
|
+
const e1 = b.match(input, e0);
|
|
5423
|
+
return e1 < 0 ? -1 : c.match(input, e1);
|
|
5424
|
+
},
|
|
5425
|
+
run: (input, pos, ctx) => {
|
|
5426
|
+
const e0 = a.run(input, pos, ctx);
|
|
5427
|
+
if (e0 < 0) return -1;
|
|
5428
|
+
ctx._fc = false;
|
|
5429
|
+
const e1 = b.run(input, e0, ctx);
|
|
5430
|
+
if (e1 < 0) return -1;
|
|
5431
|
+
ctx._fc = false;
|
|
5432
|
+
return c.run(input, e1, ctx);
|
|
5590
5433
|
}
|
|
5591
|
-
for (const k of kids) visit(k, rule);
|
|
5592
5434
|
};
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
const seenInRule = /* @__PURE__ */ new Map();
|
|
5597
|
-
const accept = new Set(opts?.accept ?? []);
|
|
5598
|
-
const usedAccept = /* @__PURE__ */ new Set();
|
|
5599
|
-
const choices = raw.map(({ g, rule, arms }) => {
|
|
5600
|
-
const n = seenInRule.get(rule) ?? 0;
|
|
5601
|
-
seenInRule.set(rule, n + 1);
|
|
5602
|
-
const id = (perRule.get(rule) ?? 1) === 1 ? rule : `${rule}#${n}`;
|
|
5603
|
-
const isAccepted = g.gates === "no" && accept.has(id);
|
|
5604
|
-
if (isAccepted) usedAccept.add(id);
|
|
5605
|
-
const out = { ...g, id, accepted: isAccepted };
|
|
5606
|
-
Object.defineProperty(out, "arms", { value: arms, enumerable: false, writable: false });
|
|
5607
|
-
return out;
|
|
5608
|
-
});
|
|
5609
|
-
const gated = choices.filter((c) => c.gates === "yes").length;
|
|
5610
|
-
const recoverable = choices.filter((c) => c.gates === "recoverable").length;
|
|
5611
|
-
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted && !c.deferred);
|
|
5612
|
-
const accepted = choices.filter((c) => c.gates === "no" && c.accepted);
|
|
5613
|
-
const deferred = choices.filter((c) => c.gates === "no" && !c.accepted && c.deferred);
|
|
5614
|
-
const acceptedUnused = [...accept].filter((id) => !usedAccept.has(id));
|
|
5435
|
+
}
|
|
5436
|
+
function sequence4(a, b, c, d) {
|
|
5437
|
+
const abc = sequence3(a, b, c);
|
|
5615
5438
|
return {
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5439
|
+
match: (input, pos) => {
|
|
5440
|
+
const end = abc.match(input, pos);
|
|
5441
|
+
return end < 0 ? -1 : d.match(input, end);
|
|
5442
|
+
},
|
|
5443
|
+
run: (input, pos, ctx) => {
|
|
5444
|
+
const end = abc.run(input, pos, ctx);
|
|
5445
|
+
if (end < 0) return -1;
|
|
5446
|
+
ctx._fc = false;
|
|
5447
|
+
return d.run(input, end, ctx);
|
|
5448
|
+
}
|
|
5626
5449
|
};
|
|
5627
5450
|
}
|
|
5628
|
-
function
|
|
5629
|
-
const
|
|
5630
|
-
const shallow = arms.map((a) => a._meta.firstSet);
|
|
5631
|
-
const deep = arms.map((a) => firstSetOf(a, /* @__PURE__ */ new Set(), resolve));
|
|
5632
|
-
const anyArms = deep.map((fs, index) => ({ fs, index })).filter((x) => isAny(x.fs)).map(({ index }) => {
|
|
5633
|
-
const { cause, detail, unresolvedExternal } = classifyBroadArm(arms[index], resolve);
|
|
5634
|
-
return {
|
|
5635
|
-
index,
|
|
5636
|
-
cause,
|
|
5637
|
-
detail,
|
|
5638
|
-
shallowAnyOnly: false,
|
|
5639
|
-
unresolvedExternal: unresolvedExternal === true,
|
|
5640
|
-
suggestion: refineSuggestion(cause, arms[index])
|
|
5641
|
-
};
|
|
5642
|
-
});
|
|
5643
|
-
const overlaps = [];
|
|
5644
|
-
for (let i = 0; i < deep.length; i++)
|
|
5645
|
-
for (let j = i + 1; j < deep.length; j++)
|
|
5646
|
-
if (!isAny(deep[i]) && !isAny(deep[j]) && intersects(deep[i], deep[j]))
|
|
5647
|
-
overlaps.push({
|
|
5648
|
-
a: i,
|
|
5649
|
-
b: j,
|
|
5650
|
-
on: intersection(deep[i], deep[j]),
|
|
5651
|
-
suggestion: "Two arms of this choice can begin with the same character, so the parser cannot tell from that character which one to try. It tries them in order and undoes the ones that do not match.\nTo fix: pull the shared beginning out in front of the choice so it is matched once \u2014 sequence(shared, choice(rest\u2026)) \u2014 instead of repeating it inside each arm. parseman recognises that shape automatically and turns it back into a single test."
|
|
5652
|
-
});
|
|
5653
|
-
let gates;
|
|
5654
|
-
if (d.disjoint) gates = "yes";
|
|
5655
|
-
else if (anyArms.length === 0 && overlaps.length === 0) gates = "recoverable";
|
|
5656
|
-
else gates = "no";
|
|
5657
|
-
const deferred = gates === "no" && overlaps.length === 0 && anyArms.length > 0 && anyArms.every((a) => a.unresolvedExternal);
|
|
5451
|
+
function sequence5(a, b, c, d, e) {
|
|
5452
|
+
const abcd = sequence4(a, b, c, d);
|
|
5658
5453
|
return {
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5454
|
+
match: (input, pos) => {
|
|
5455
|
+
const end = abcd.match(input, pos);
|
|
5456
|
+
return end < 0 ? -1 : e.match(input, end);
|
|
5457
|
+
},
|
|
5458
|
+
run: (input, pos, ctx) => {
|
|
5459
|
+
const end = abcd.run(input, pos, ctx);
|
|
5460
|
+
if (end < 0) return -1;
|
|
5461
|
+
ctx._fc = false;
|
|
5462
|
+
return e.run(input, end, ctx);
|
|
5463
|
+
}
|
|
5666
5464
|
};
|
|
5667
5465
|
}
|
|
5668
|
-
function
|
|
5669
|
-
|
|
5670
|
-
if (
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
);
|
|
5674
|
-
for (const u of report.unanalysable)
|
|
5675
|
-
lines.push(` \xB7 ${u.rule} [${u.kind}]: ${u.reason}`);
|
|
5676
|
-
}
|
|
5677
|
-
for (const c of report.ungated) {
|
|
5678
|
-
lines.push(`parseman gating: choice @ ${c.id} is UNGATED [${c.strategy}] \u2014 no first-char dispatch; every position speculatively enters doomed arms.`);
|
|
5679
|
-
for (const a of c.anyArms)
|
|
5680
|
-
lines.push(` \xB7 arm[${a.index}] first-set ANY (${a.cause}): ${a.detail}
|
|
5681
|
-
fix: ${a.suggestion}`);
|
|
5682
|
-
for (const o of c.overlaps)
|
|
5683
|
-
lines.push(` \xB7 arm[${o.a}] \u2229 arm[${o.b}] overlap on ${firstSetToString(o.on)}
|
|
5684
|
-
fix: ${o.suggestion}`);
|
|
5685
|
-
lines.push(` (intentional? accept it in the gating snapshot: { accept: ['${c.id}'] }.)`);
|
|
5686
|
-
}
|
|
5687
|
-
for (const ap of report.antiPatterns)
|
|
5688
|
-
lines.push(`parseman anti-pattern [${ap.kind}] @ ${ap.rule} arm[${ap.armIndex}]: ${ap.message}`);
|
|
5689
|
-
return lines;
|
|
5466
|
+
function appendExpected(acc, values) {
|
|
5467
|
+
if (values === void 0 || values.length === 0) return acc;
|
|
5468
|
+
if (acc === void 0) return values.slice();
|
|
5469
|
+
acc.push(...values);
|
|
5470
|
+
return acc;
|
|
5690
5471
|
}
|
|
5691
|
-
function
|
|
5692
|
-
|
|
5693
|
-
for (
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5472
|
+
function choiceNode(arms, classes, armExpected, choiceExpected) {
|
|
5473
|
+
let matchTail = () => -1;
|
|
5474
|
+
for (let i = arms.length - 1; i >= 0; i--) {
|
|
5475
|
+
const arm = arms[i], next = matchTail;
|
|
5476
|
+
matchTail = (input, pos) => {
|
|
5477
|
+
const end = arm.match(input, pos);
|
|
5478
|
+
return end < 0 ? next(input, pos) : end;
|
|
5479
|
+
};
|
|
5480
|
+
}
|
|
5481
|
+
let tail = (_input, pos, ctx, _lead, acc) => {
|
|
5482
|
+
ctx._fe = pos;
|
|
5483
|
+
ctx._fx = acc ?? choiceExpected;
|
|
5484
|
+
return -1;
|
|
5485
|
+
};
|
|
5486
|
+
for (let i = arms.length - 1; i >= 0; i--) {
|
|
5487
|
+
const arm = arms[i], cls = classes[i], staticExpected = armExpected[i], next = tail;
|
|
5488
|
+
tail = (input, pos, ctx, lead2, acc, best) => {
|
|
5489
|
+
if (cls !== void 0 && !classHas(cls, lead2)) {
|
|
5490
|
+
return next(
|
|
5491
|
+
input,
|
|
5492
|
+
pos,
|
|
5493
|
+
ctx,
|
|
5494
|
+
lead2,
|
|
5495
|
+
best === pos ? appendExpected(acc, staticExpected) : acc,
|
|
5496
|
+
best
|
|
5497
|
+
);
|
|
5716
5498
|
}
|
|
5717
|
-
|
|
5499
|
+
ctx._fc = false;
|
|
5500
|
+
const end = arm.run(input, pos, ctx);
|
|
5501
|
+
if (end >= 0) return end;
|
|
5502
|
+
const at = ctx._fe ?? pos;
|
|
5503
|
+
if (at > best) {
|
|
5504
|
+
best = at;
|
|
5505
|
+
acc = void 0;
|
|
5506
|
+
}
|
|
5507
|
+
const nextAcc = at === best ? appendExpected(acc, ctx._fx) : acc;
|
|
5508
|
+
if (committed(ctx)) {
|
|
5509
|
+
if (nextAcc !== void 0) ctx._fx = nextAcc;
|
|
5510
|
+
return -1;
|
|
5511
|
+
}
|
|
5512
|
+
return next(input, pos, ctx, lead2, nextAcc, best);
|
|
5718
5513
|
};
|
|
5719
|
-
walk(comb, true);
|
|
5720
|
-
deps.set(name, [...found]);
|
|
5721
5514
|
}
|
|
5722
|
-
return
|
|
5515
|
+
return {
|
|
5516
|
+
match: matchTail,
|
|
5517
|
+
run: (input, pos, ctx) => tail(
|
|
5518
|
+
input,
|
|
5519
|
+
pos,
|
|
5520
|
+
ctx,
|
|
5521
|
+
pos < input.length ? input.codePointAt(pos) : -1,
|
|
5522
|
+
void 0,
|
|
5523
|
+
pos
|
|
5524
|
+
)
|
|
5525
|
+
};
|
|
5723
5526
|
}
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
return d.value.length > 0;
|
|
5733
|
-
case "trivia":
|
|
5734
|
-
return false;
|
|
5735
|
-
// `optional`/`many`/a min-0 `sepBy` swallow an ORDINARY failure — zero
|
|
5736
|
-
// repetitions is a match. They do NOT swallow a COMMITTED one: a dispatch
|
|
5737
|
-
// that has selected an arm and then failed inside it breaks out past every
|
|
5738
|
-
// enclosing repeat, straight to the fallible boundary. So these are total
|
|
5739
|
-
// exactly when their body cannot commit. (Measured: treating them as
|
|
5740
|
-
// unconditionally total left an extra zero-width leaf in
|
|
5741
|
-
// `BareInterpolatedSelector` on benchmark.less — a rollback the legacy
|
|
5742
|
-
// build fired and this one had deleted.)
|
|
5743
|
-
case "optional":
|
|
5744
|
-
case "many":
|
|
5745
|
-
return mayCommitFailure(d.parser);
|
|
5746
|
-
case "sepBy":
|
|
5747
|
-
return d.min >= 1 ? mayFail(d.parser, seen) : mayCommitFailure(d.parser) || mayCommitFailure(d.separator);
|
|
5748
|
-
case "oneOrMore":
|
|
5749
|
-
return mayFail(d.parser, seen);
|
|
5750
|
-
case "sequence":
|
|
5751
|
-
return d.parsers.some((x) => mayFail(x, seen));
|
|
5752
|
-
// Delegating wrappers: as fallible as what they wrap.
|
|
5753
|
-
case "node":
|
|
5754
|
-
case "transform":
|
|
5755
|
-
case "label":
|
|
5756
|
-
case "field":
|
|
5757
|
-
case "expect":
|
|
5758
|
-
case "withCtx":
|
|
5759
|
-
case "grammar":
|
|
5760
|
-
case "token":
|
|
5761
|
-
case "leaf":
|
|
5762
|
-
return mayFail(d.parser, seen);
|
|
5763
|
-
case "lazy": {
|
|
5764
|
-
try {
|
|
5765
|
-
return mayFail(d.thunk(), seen);
|
|
5766
|
-
} catch {
|
|
5767
|
-
return true;
|
|
5768
|
-
}
|
|
5527
|
+
function notNode(body, failure) {
|
|
5528
|
+
return {
|
|
5529
|
+
match: (input, pos) => body.match(input, pos) < 0 ? pos : -1,
|
|
5530
|
+
run: (input, pos, ctx) => {
|
|
5531
|
+
if (body.match(input, pos) < 0) return pos;
|
|
5532
|
+
ctx._fe = pos;
|
|
5533
|
+
ctx._fx = failure;
|
|
5534
|
+
return -1;
|
|
5769
5535
|
}
|
|
5770
|
-
|
|
5771
|
-
case "adjacency":
|
|
5772
|
-
return true;
|
|
5773
|
-
default:
|
|
5774
|
-
return true;
|
|
5775
|
-
}
|
|
5536
|
+
};
|
|
5776
5537
|
}
|
|
5777
|
-
function
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
// Adjacency probes with `_cap = 0` / no `commit()`: it cannot push anything.
|
|
5788
|
-
case "adjacency":
|
|
5789
|
-
case "not":
|
|
5790
|
-
// peek(): emitted under a non-capturing probe ctx and zero-width on both
|
|
5791
|
-
// outcomes, so it can never leave a partial capture behind.
|
|
5792
|
-
case "peek":
|
|
5793
|
-
case "trivia":
|
|
5794
|
-
case "token":
|
|
5795
|
-
case "leaf":
|
|
5796
|
-
case "scanTo":
|
|
5797
|
-
case "unknown":
|
|
5798
|
-
return false;
|
|
5799
|
-
// node() captures into its own private buffers and rolls them back on
|
|
5800
|
-
// failure (emitNode restores _ctx.* and never pushes on the !ok path).
|
|
5801
|
-
case "node":
|
|
5802
|
-
return false;
|
|
5803
|
-
// choice/firstMatch already roll back each failed arm; on overall failure
|
|
5804
|
-
// nothing committed remains.
|
|
5805
|
-
case "choice":
|
|
5806
|
-
return false;
|
|
5807
|
-
case "dispatch":
|
|
5808
|
-
return capturesLeaf(d.selector, new Set(seen)) || d.cases.some((x) => mayLeavePartialCapture(x.parser, new Set(seen), triviaActive) || capturesLeaf(x.parser, new Set(seen))) || (d.matchers ? d.matchers.some((x) => mayLeavePartialCapture(x.parser, new Set(seen), triviaActive) || capturesLeaf(x.parser, new Set(seen))) : false) || (d.otherwise ? mayLeavePartialCapture(d.otherwise, new Set(seen), triviaActive) || capturesLeaf(d.otherwise, new Set(seen)) : false);
|
|
5809
|
-
case "attempt":
|
|
5810
|
-
return false;
|
|
5811
|
-
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
5812
|
-
case "optional":
|
|
5813
|
-
case "many":
|
|
5814
|
-
case "oneOrMore":
|
|
5815
|
-
return false;
|
|
5816
|
-
// sepBy emits its own per-iteration rollback in emitSepBy.
|
|
5817
|
-
case "sepBy":
|
|
5818
|
-
return false;
|
|
5819
|
-
// A sequence is the real case: an earlier capturing term followed by a term
|
|
5820
|
-
// that can fail leaves the earlier captures buffered.
|
|
5821
|
-
//
|
|
5822
|
-
// BOTH halves of that sentence are load-bearing, and the second half is what
|
|
5823
|
-
// this walk exists to exploit. "Some earlier term captures" alone was the old
|
|
5824
|
-
// test, and it is true of very nearly every sequence in a real grammar — a
|
|
5825
|
-
// sequence almost always opens with a literal or a node. The rollback is only
|
|
5826
|
-
// reachable if something AFTER the capture can still fail; once the remainder
|
|
5827
|
-
// is total (`optional`/`many`/trivia/a min-0 `sepBy`), the sequence has no
|
|
5828
|
-
// failure path left to roll back from, and the save+restore is dead code.
|
|
5829
|
-
//
|
|
5830
|
-
// `captured` tracks whether anything is in the buffers yet as the scan moves
|
|
5831
|
-
// left to right. Two things put it there:
|
|
5832
|
-
// - a term that captures (`capturesLeaf`/`hasNodeDef`), and
|
|
5833
|
-
// - the inter-term TRIVIA scan, which under `capturing` pushes into
|
|
5834
|
-
// `_cstRawChildren`/`_cstTriviaLog` BEFORE the term at each boundary
|
|
5835
|
-
// i >= 1. `emitSeqValues` only rewinds that push on the term's SUCCESS
|
|
5836
|
-
// path (the "matched empty, so leave the whitespace out of the span"
|
|
5837
|
-
// branch); a term that FAILS breaks straight out with the trivia still
|
|
5838
|
-
// buffered, so from boundary 1 onward the buffers are dirty regardless of
|
|
5839
|
-
// what the earlier terms did.
|
|
5840
|
-
// Each term is also asked about itself: a nested sequence can leave its own
|
|
5841
|
-
// partial capture behind, and its terms are emitted inline into this same
|
|
5842
|
-
// block with no boundary of their own to roll them back.
|
|
5843
|
-
case "sequence": {
|
|
5844
|
-
const parts = d.parsers;
|
|
5845
|
-
let captured = false;
|
|
5846
|
-
for (let i = 0; i < parts.length; i++) {
|
|
5847
|
-
const part = parts[i];
|
|
5848
|
-
if (i > 0 && triviaActive) captured = true;
|
|
5849
|
-
if (captured && mayFail(part)) return true;
|
|
5850
|
-
if (mayLeavePartialCapture(part, seen, triviaActive)) return true;
|
|
5851
|
-
if (capturesLeaf(part) || hasNodeDef(part)) captured = true;
|
|
5852
|
-
}
|
|
5853
|
-
return false;
|
|
5538
|
+
function optionalNode(body) {
|
|
5539
|
+
return {
|
|
5540
|
+
match: (input, pos) => {
|
|
5541
|
+
const end = body.match(input, pos);
|
|
5542
|
+
return end < 0 ? pos : end;
|
|
5543
|
+
},
|
|
5544
|
+
run: (input, pos, ctx) => {
|
|
5545
|
+
ctx._fc = false;
|
|
5546
|
+
const end = body.run(input, pos, ctx);
|
|
5547
|
+
return end < 0 && !committed(ctx) ? pos : end;
|
|
5854
5548
|
}
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5549
|
+
};
|
|
5550
|
+
}
|
|
5551
|
+
function fixedTreeRunner(words, matchers, classes, expected) {
|
|
5552
|
+
const stack = [];
|
|
5553
|
+
const pop = () => {
|
|
5554
|
+
const value = stack.pop();
|
|
5555
|
+
if (value === void 0) throw new TypeError("table lexical tree has an incomplete fixed body");
|
|
5556
|
+
return value;
|
|
5557
|
+
};
|
|
5558
|
+
for (let at = 0; at < words.length; ) {
|
|
5559
|
+
const op = words[at++];
|
|
5560
|
+
if (op === LEX_NODE_TERMINAL) {
|
|
5561
|
+
const matcher = matchers[words[at++]], failure = expected[words[at++]];
|
|
5562
|
+
const tracks = words[at++];
|
|
5563
|
+
if (matcher === void 0 || failure === void 0 || tracks !== 0 && tracks !== 1) {
|
|
5564
|
+
throw new TypeError("table lexical tree terminal references an invalid pool entry");
|
|
5870
5565
|
}
|
|
5566
|
+
stack.push(terminalNode(matcher, failure, tracks === 1));
|
|
5567
|
+
continue;
|
|
5871
5568
|
}
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
case "trivia":
|
|
5895
|
-
case "unknown":
|
|
5896
|
-
return false;
|
|
5897
|
-
case "sequence":
|
|
5898
|
-
case "choice":
|
|
5899
|
-
return d.parsers.some((x) => capturesLeaf(x, seen));
|
|
5900
|
-
case "dispatch":
|
|
5901
|
-
return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => capturesLeaf(entry.parser, seen)) : false) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
|
|
5902
|
-
case "sepBy":
|
|
5903
|
-
return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
|
|
5904
|
-
case "many":
|
|
5905
|
-
case "oneOrMore":
|
|
5906
|
-
case "optional":
|
|
5907
|
-
case "attempt":
|
|
5908
|
-
case "transform":
|
|
5909
|
-
case "label":
|
|
5910
|
-
case "field":
|
|
5911
|
-
case "expect":
|
|
5912
|
-
case "withCtx":
|
|
5913
|
-
case "grammar":
|
|
5914
|
-
case "recover":
|
|
5915
|
-
return capturesLeaf(d.parser, seen);
|
|
5916
|
-
case "scanTo":
|
|
5917
|
-
return true;
|
|
5918
|
-
case "lazy": {
|
|
5919
|
-
try {
|
|
5920
|
-
return capturesLeaf(d.thunk(), seen);
|
|
5921
|
-
} catch {
|
|
5922
|
-
return true;
|
|
5569
|
+
if (op >= LEX_NODE_SEQUENCE2 && op <= LEX_NODE_SEQUENCE5) {
|
|
5570
|
+
const count = op - LEX_NODE_SEQUENCE2 + 2;
|
|
5571
|
+
const nodes = Array.from({ length: count }, pop).reverse();
|
|
5572
|
+
if (count === 2) stack.push(sequence2(nodes[0], nodes[1]));
|
|
5573
|
+
else if (count === 3) stack.push(sequence3(nodes[0], nodes[1], nodes[2]));
|
|
5574
|
+
else if (count === 4) stack.push(sequence4(nodes[0], nodes[1], nodes[2], nodes[3]));
|
|
5575
|
+
else stack.push(sequence5(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4]));
|
|
5576
|
+
continue;
|
|
5577
|
+
}
|
|
5578
|
+
if (op >= LEX_NODE_CHOICE2 && op <= LEX_NODE_CHOICE8) {
|
|
5579
|
+
const count = op === LEX_NODE_CHOICE2 ? 2 : op === LEX_NODE_CHOICE4 ? 4 : 8;
|
|
5580
|
+
const choiceFx = expected[words[at++]];
|
|
5581
|
+
if (choiceFx === void 0) throw new TypeError("table lexical tree choice has an invalid expected set");
|
|
5582
|
+
const armClasses = [];
|
|
5583
|
+
const armFx = [];
|
|
5584
|
+
for (let i = 0; i < count; i++) {
|
|
5585
|
+
const cls = words[at++], fx = expected[words[at++]];
|
|
5586
|
+
if (cls < -1 || cls >= 0 && classes[cls] === void 0 || fx === void 0) {
|
|
5587
|
+
throw new TypeError("table lexical tree choice references an invalid pool entry");
|
|
5588
|
+
}
|
|
5589
|
+
armClasses.push(cls < 0 ? void 0 : classes[cls]);
|
|
5590
|
+
armFx.push(fx);
|
|
5923
5591
|
}
|
|
5592
|
+
const arms = Array.from({ length: count }, pop).reverse();
|
|
5593
|
+
stack.push(choiceNode(arms, armClasses, armFx, choiceFx));
|
|
5594
|
+
continue;
|
|
5924
5595
|
}
|
|
5925
|
-
|
|
5926
|
-
|
|
5596
|
+
if (op === LEX_NODE_NOT) {
|
|
5597
|
+
const failure = expected[words[at++]];
|
|
5598
|
+
if (failure === void 0) throw new TypeError("table lexical tree assertion has an invalid expected set");
|
|
5599
|
+
stack.push(notNode(pop(), failure));
|
|
5600
|
+
continue;
|
|
5601
|
+
}
|
|
5602
|
+
if (op === LEX_NODE_OPTIONAL) {
|
|
5603
|
+
stack.push(optionalNode(pop()));
|
|
5604
|
+
continue;
|
|
5605
|
+
}
|
|
5606
|
+
throw new TypeError("table lexical tree has an unknown fixed body");
|
|
5927
5607
|
}
|
|
5608
|
+
if (stack.length !== 1) throw new TypeError("table lexical tree does not resolve to one root body");
|
|
5609
|
+
return stack[0].run;
|
|
5928
5610
|
}
|
|
5929
|
-
function
|
|
5930
|
-
if (
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
} catch {
|
|
5940
|
-
return false;
|
|
5941
|
-
}
|
|
5611
|
+
function buildLexProgramRunner(spec, matchers, classes, expected) {
|
|
5612
|
+
if (spec[0] !== 0 && spec[0] !== 1 && spec[0] !== 2 && spec[0] !== 3 || spec[0] === 0 && spec.length !== 13 || spec[0] === 1 && spec.length !== 12 || spec[0] === 2 && spec.length < 6 || spec[0] === 3 && spec.length !== 3) {
|
|
5613
|
+
throw new TypeError("table lexical program has an invalid fixed body width");
|
|
5614
|
+
}
|
|
5615
|
+
if (spec[1] !== lexProgramDigest(spec.slice(2))) {
|
|
5616
|
+
throw new TypeError("table lexical program semantic digest is inconsistent");
|
|
5617
|
+
}
|
|
5618
|
+
if (spec[0] === 3) {
|
|
5619
|
+
if (!Number.isInteger(spec[2]) || spec[2] < 0) {
|
|
5620
|
+
throw new TypeError("table balanced lexical program has an invalid scan reference");
|
|
5942
5621
|
}
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
case "transform":
|
|
5955
|
-
return hasNodeDef(d.parser, seen);
|
|
5956
|
-
case "sequence":
|
|
5957
|
-
case "choice":
|
|
5958
|
-
return d.parsers.some((x) => hasNodeDef(x, seen));
|
|
5959
|
-
case "dispatch":
|
|
5960
|
-
return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => hasNodeDef(entry.parser, seen)) : false) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
|
|
5961
|
-
case "sepBy":
|
|
5962
|
-
return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
|
|
5963
|
-
case "scanTo":
|
|
5964
|
-
return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen)) || d.recoverAt !== void 0 && hasNodeDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasNodeDef(d.stopAt, seen);
|
|
5965
|
-
case "recover":
|
|
5966
|
-
return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
|
|
5967
|
-
case "expect":
|
|
5968
|
-
return hasNodeDef(d.parser, seen);
|
|
5969
|
-
case "withCtx":
|
|
5970
|
-
return hasNodeDef(d.parser, seen);
|
|
5971
|
-
default:
|
|
5972
|
-
return false;
|
|
5622
|
+
const run3 = (_input, _pos, ctx, scan) => {
|
|
5623
|
+
if (scan === void 0) throw new TypeError("table balanced lexical program lost its scan body");
|
|
5624
|
+
const result = scan.parse(_input, _pos, ctx);
|
|
5625
|
+
if (!result.ok) {
|
|
5626
|
+
ctx._fe = result.span.start;
|
|
5627
|
+
ctx._fx = result.expected ?? [];
|
|
5628
|
+
return -1;
|
|
5629
|
+
}
|
|
5630
|
+
return result.span.end;
|
|
5631
|
+
};
|
|
5632
|
+
return Object.assign(run3, { scan: spec[2] });
|
|
5973
5633
|
}
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5634
|
+
if (spec[0] === 2) return fixedTreeRunner(spec.slice(2), matchers, classes, expected);
|
|
5635
|
+
if (spec[0] === 1) {
|
|
5636
|
+
const g00 = matchers[spec[2]], g01 = matchers[spec[3]];
|
|
5637
|
+
const g02 = matchers[spec[4]], g03 = matchers[spec[5]];
|
|
5638
|
+
const guard0Fx = expected[spec[6]];
|
|
5639
|
+
const g1 = matchers[spec[7]], guard1Fx = expected[spec[8]];
|
|
5640
|
+
const terminal = matchers[spec[9]], terminalFx = expected[spec[10]];
|
|
5641
|
+
const tracks2 = spec[11];
|
|
5642
|
+
if (g00 === void 0 || g01 === void 0 || g02 === void 0 || g03 === void 0 || guard0Fx === void 0 || g1 === void 0 || guard1Fx === void 0 || terminal === void 0 || terminalFx === void 0 || tracks2 !== 0 && tracks2 !== 1) {
|
|
5643
|
+
throw new TypeError("table lexical program references an invalid pool entry");
|
|
5644
|
+
}
|
|
5645
|
+
return (input, pos, ctx) => {
|
|
5646
|
+
ctx._fc = false;
|
|
5647
|
+
if (g00(input, pos) >= 0 || g01(input, pos) >= 0 || g02(input, pos) >= 0 || g03(input, pos) >= 0) {
|
|
5648
|
+
ctx._fe = pos;
|
|
5649
|
+
ctx._fx = guard0Fx;
|
|
5650
|
+
return -1;
|
|
5651
|
+
}
|
|
5652
|
+
ctx._fc = false;
|
|
5653
|
+
if (g1(input, pos) >= 0) {
|
|
5654
|
+
ctx._fe = pos;
|
|
5655
|
+
ctx._fx = guard1Fx;
|
|
5656
|
+
return -1;
|
|
5987
5657
|
}
|
|
5658
|
+
ctx._fc = false;
|
|
5659
|
+
const result = terminal(input, pos);
|
|
5660
|
+
if (result < 0) {
|
|
5661
|
+
ctx._fe = pos;
|
|
5662
|
+
ctx._fx = terminalFx;
|
|
5663
|
+
if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
|
|
5664
|
+
return -1;
|
|
5665
|
+
}
|
|
5666
|
+
const end = result / 2;
|
|
5667
|
+
if (tracks2 === 1) trackLexLines(ctx, input, end);
|
|
5668
|
+
return end;
|
|
5669
|
+
};
|
|
5670
|
+
}
|
|
5671
|
+
const cls = classes[spec[2]];
|
|
5672
|
+
const choiceFx = expected[spec[3]];
|
|
5673
|
+
const m0 = matchers[spec[4]], x0 = expected[spec[5]];
|
|
5674
|
+
const m1 = matchers[spec[6]], x1 = expected[spec[7]];
|
|
5675
|
+
const m2 = matchers[spec[8]], x2 = expected[spec[9]];
|
|
5676
|
+
const m3 = matchers[spec[10]], x3 = expected[spec[11]];
|
|
5677
|
+
const tracks = spec[12];
|
|
5678
|
+
if (cls === void 0 || choiceFx === void 0 || m0 === void 0 || x0 === void 0 || m1 === void 0 || x1 === void 0 || m2 === void 0 || x2 === void 0 || m3 === void 0 || x3 === void 0 || tracks !== 0 && tracks !== 1) {
|
|
5679
|
+
throw new TypeError("table lexical program references an invalid pool entry");
|
|
5680
|
+
}
|
|
5681
|
+
return (input, pos, ctx) => {
|
|
5682
|
+
const lead2 = pos < input.length ? input.codePointAt(pos) : -1;
|
|
5683
|
+
if (!classHas(cls, lead2)) {
|
|
5684
|
+
ctx._fe = pos;
|
|
5685
|
+
ctx._fx = choiceFx.slice();
|
|
5686
|
+
return -1;
|
|
5988
5687
|
}
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5688
|
+
let acc;
|
|
5689
|
+
const failed = (fx) => {
|
|
5690
|
+
ctx._fe = pos;
|
|
5691
|
+
ctx._fx = fx;
|
|
5692
|
+
if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
|
|
5693
|
+
if (acc === void 0) acc = fx.slice();
|
|
5694
|
+
else acc.push(...fx);
|
|
5695
|
+
};
|
|
5696
|
+
ctx._fc = false;
|
|
5697
|
+
const success = (result2) => {
|
|
5698
|
+
const end = result2 / 2;
|
|
5699
|
+
if (tracks === 1) trackLexLines(ctx, input, end);
|
|
5700
|
+
return end;
|
|
5701
|
+
};
|
|
5702
|
+
let result = m0(input, pos);
|
|
5703
|
+
if (result >= 0) return success(result);
|
|
5704
|
+
failed(x0);
|
|
5705
|
+
ctx._fc = false;
|
|
5706
|
+
result = m1(input, pos);
|
|
5707
|
+
if (result >= 0) return success(result);
|
|
5708
|
+
failed(x1);
|
|
5709
|
+
ctx._fc = false;
|
|
5710
|
+
result = m2(input, pos);
|
|
5711
|
+
if (result >= 0) return success(result);
|
|
5712
|
+
failed(x2);
|
|
5713
|
+
ctx._fc = false;
|
|
5714
|
+
result = m3(input, pos);
|
|
5715
|
+
if (result >= 0) return success(result);
|
|
5716
|
+
failed(x3);
|
|
5717
|
+
ctx._fe = pos;
|
|
5718
|
+
ctx._fx = acc ?? choiceFx;
|
|
5719
|
+
return -1;
|
|
5720
|
+
};
|
|
5721
|
+
}
|
|
5722
|
+
|
|
5723
|
+
// src/table/program.ts
|
|
5724
|
+
function closureArtifact(prog) {
|
|
5725
|
+
return { ...prog, asm: [] };
|
|
5726
|
+
}
|
|
5727
|
+
function covKindCode(kind) {
|
|
5728
|
+
switch (kind) {
|
|
5729
|
+
case "rule":
|
|
5730
|
+
return 0;
|
|
5731
|
+
case "choice-arm":
|
|
5732
|
+
return 1;
|
|
5733
|
+
case "dispatch-arm":
|
|
5734
|
+
return 2;
|
|
5993
5735
|
case "label":
|
|
5994
|
-
|
|
5995
|
-
case "optional":
|
|
5996
|
-
case "many":
|
|
5997
|
-
case "oneOrMore":
|
|
5998
|
-
case "attempt":
|
|
5999
|
-
case "not":
|
|
6000
|
-
case "peek":
|
|
6001
|
-
case "withCtx":
|
|
6002
|
-
case "expect":
|
|
6003
|
-
case "transform":
|
|
6004
|
-
return hasDirectBuildDef(d.parser, seen);
|
|
6005
|
-
case "sequence":
|
|
6006
|
-
case "choice":
|
|
6007
|
-
return d.parsers.some((x) => hasDirectBuildDef(x, seen));
|
|
6008
|
-
case "dispatch":
|
|
6009
|
-
return hasDirectBuildDef(d.selector, seen) || d.cases.some((x) => hasDirectBuildDef(x.parser, seen)) || (d.matchers ? d.matchers.some((x) => hasDirectBuildDef(x.parser, seen)) : false) || (d.otherwise ? hasDirectBuildDef(d.otherwise, seen) : false);
|
|
6010
|
-
case "sepBy":
|
|
6011
|
-
return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.separator, seen);
|
|
6012
|
-
case "scanTo":
|
|
6013
|
-
return hasDirectBuildDef(d.sentinel, seen) || d.skip.some((x) => hasDirectBuildDef(x, seen)) || d.recoverAt !== void 0 && hasDirectBuildDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasDirectBuildDef(d.stopAt, seen);
|
|
6014
|
-
case "recover":
|
|
6015
|
-
return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.sentinel, seen);
|
|
6016
|
-
case "routed":
|
|
6017
|
-
return d.fallback ? hasDirectBuildDef(d.fallback, seen) : false;
|
|
6018
|
-
default:
|
|
6019
|
-
return false;
|
|
5736
|
+
return 3;
|
|
6020
5737
|
}
|
|
6021
5738
|
}
|
|
6022
|
-
function
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
case
|
|
6031
|
-
return
|
|
6032
|
-
case "choice":
|
|
6033
|
-
case "sequence":
|
|
6034
|
-
return d.parsers;
|
|
6035
|
-
// A contained transaction reports every failure uncommitted.
|
|
6036
|
-
case "attempt":
|
|
6037
|
-
return d.contain === true ? NO_EDGES : [d.parser];
|
|
6038
|
-
case "many":
|
|
6039
|
-
case "oneOrMore":
|
|
6040
|
-
case "optional":
|
|
6041
|
-
case "transform":
|
|
6042
|
-
case "label":
|
|
6043
|
-
case "field":
|
|
6044
|
-
case "grammar":
|
|
6045
|
-
case "node":
|
|
6046
|
-
case "token":
|
|
6047
|
-
case "leaf":
|
|
6048
|
-
case "withCtx":
|
|
6049
|
-
return [d.parser];
|
|
6050
|
-
case "sepBy":
|
|
6051
|
-
return [d.parser, d.separator];
|
|
6052
|
-
case "scanTo":
|
|
6053
|
-
return [
|
|
6054
|
-
d.sentinel,
|
|
6055
|
-
...d.skip,
|
|
6056
|
-
...d.recoverAt === void 0 ? NO_EDGES : [d.recoverAt],
|
|
6057
|
-
...d.stopAt === void 0 ? NO_EDGES : [d.stopAt]
|
|
6058
|
-
];
|
|
6059
|
-
case "lazy": {
|
|
6060
|
-
try {
|
|
6061
|
-
return [d.thunk()];
|
|
6062
|
-
} catch {
|
|
6063
|
-
const name = p._ruleName;
|
|
6064
|
-
const target = name === void 0 ? void 0 : resolve?.(name);
|
|
6065
|
-
return target === void 0 ? true : [target];
|
|
6066
|
-
}
|
|
6067
|
-
}
|
|
6068
|
-
default:
|
|
6069
|
-
return NO_EDGES;
|
|
5739
|
+
function covKindName(code) {
|
|
5740
|
+
switch (code) {
|
|
5741
|
+
case 0:
|
|
5742
|
+
return "rule";
|
|
5743
|
+
case 1:
|
|
5744
|
+
return "choice-arm";
|
|
5745
|
+
case 2:
|
|
5746
|
+
return "dispatch-arm";
|
|
5747
|
+
case 3:
|
|
5748
|
+
return "label";
|
|
6070
5749
|
}
|
|
6071
5750
|
}
|
|
6072
|
-
function
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
const
|
|
6077
|
-
const
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
}
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
} while (member !== node2);
|
|
6112
|
-
}
|
|
6113
|
-
return at;
|
|
5751
|
+
function covDefinitions(prog) {
|
|
5752
|
+
return (prog.cov ?? []).map(([id, kind]) => ({ id, kind: covKindName(kind) }));
|
|
5753
|
+
}
|
|
5754
|
+
function scanRefs(s) {
|
|
5755
|
+
const refs = [];
|
|
5756
|
+
for (const r of [s.sentinel, s.recoverAt, s.stopAt, s.prefix, s.body, s.first, s.lazy]) if (r !== void 0) refs.push(r);
|
|
5757
|
+
refs.push(...s.skip);
|
|
5758
|
+
return refs;
|
|
5759
|
+
}
|
|
5760
|
+
function expandCompact(p) {
|
|
5761
|
+
if ("code" in p) return p;
|
|
5762
|
+
return ownTableProgram({
|
|
5763
|
+
code: p.c,
|
|
5764
|
+
k: p.k,
|
|
5765
|
+
cc: p.x,
|
|
5766
|
+
fx: p.e,
|
|
5767
|
+
disp: p.d,
|
|
5768
|
+
rules: p.r,
|
|
5769
|
+
fns: p.f,
|
|
5770
|
+
lines: p.l ?? 0,
|
|
5771
|
+
dsp: p.p ?? [],
|
|
5772
|
+
...p.lx === void 0 ? {} : { lex: p.lx },
|
|
5773
|
+
...p.lp === void 0 ? {} : { lexPrograms: p.lp },
|
|
5774
|
+
...p.lb === void 0 ? {} : { labels: p.lb },
|
|
5775
|
+
...p.rc === void 0 ? {} : { classified: p.rc },
|
|
5776
|
+
...p.h === void 0 ? {} : { hostMode: p.h },
|
|
5777
|
+
...p.tv === void 0 ? {} : { triviaSpecs: p.tv },
|
|
5778
|
+
...p.sc === void 0 ? {} : { scans: p.sc },
|
|
5779
|
+
...p.ss === void 0 ? {} : { scanSkip: p.ss },
|
|
5780
|
+
...p.so === void 0 ? {} : { scanSkipOf: p.so },
|
|
5781
|
+
...p.rv === void 0 ? {} : { rec: p.rv },
|
|
5782
|
+
...p.cv === void 0 ? {} : { cov: p.cv },
|
|
5783
|
+
...p.a === void 0 ? {} : { asm: p.a }
|
|
5784
|
+
});
|
|
5785
|
+
}
|
|
5786
|
+
function validateDispatchSpec(spec, arity, fallbackRouted) {
|
|
5787
|
+
const invalid = (arms) => {
|
|
5788
|
+
for (const arm of arms) if (!Number.isInteger(arm) || arm < 0 || arm >= arity) return true;
|
|
5789
|
+
return false;
|
|
6114
5790
|
};
|
|
6115
|
-
|
|
5791
|
+
if (spec === void 0 || !Number.isInteger(arity) || arity < 0 || fallbackRouted !== 0 && fallbackRouted !== 1 || spec.routed.length !== arity || spec.routed.some((value) => value !== 0 && value !== 1) || invalid(spec.byKey.values()) || invalid(spec.byFold.values()) || spec.match.some((matcher) => invalid([matcher[3]]))) {
|
|
5792
|
+
throw new TypeError("table: malformed dispatch");
|
|
5793
|
+
}
|
|
6116
5794
|
}
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
const
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
if (def.tag === "lazy") {
|
|
6128
|
-
if (externalRefs?.has(parser2)) return false;
|
|
6129
|
-
try {
|
|
6130
|
-
return visit(def.thunk());
|
|
6131
|
-
} catch {
|
|
6132
|
-
return true;
|
|
6133
|
-
}
|
|
5795
|
+
var TABLE_RUNTIME = /* @__PURE__ */ Symbol("parseman.tableRuntime");
|
|
5796
|
+
function ownTableProgram(prog, resolved, caches = {}) {
|
|
5797
|
+
const runtime = prog[TABLE_RUNTIME];
|
|
5798
|
+
return {
|
|
5799
|
+
...prog,
|
|
5800
|
+
[TABLE_RUNTIME]: {
|
|
5801
|
+
resolved,
|
|
5802
|
+
choiceRollbackMasks: caches.choiceRollbackMasks ?? runtime?.choiceRollbackMasks,
|
|
5803
|
+
nonCommittingChoiceSites: caches.nonCommittingChoiceSites ?? runtime?.nonCommittingChoiceSites,
|
|
5804
|
+
failureRollbackCleanSites: caches.failureRollbackCleanSites ?? runtime?.failureRollbackCleanSites
|
|
6134
5805
|
}
|
|
6135
|
-
return childrenOf2(def).some(visit);
|
|
6136
5806
|
};
|
|
6137
|
-
return roots.some(visit);
|
|
6138
5807
|
}
|
|
6139
|
-
function
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
if (p._ruleName) externalRefs.add(p);
|
|
6155
|
-
return;
|
|
6156
|
-
}
|
|
6157
|
-
scanExternal(resolved);
|
|
6158
|
-
return;
|
|
5808
|
+
function choiceRollbackMask(prog, ip) {
|
|
5809
|
+
return prog[TABLE_RUNTIME]?.choiceRollbackMasks?.get(ip);
|
|
5810
|
+
}
|
|
5811
|
+
function choiceCannotCommit(prog, ip) {
|
|
5812
|
+
return prog[TABLE_RUNTIME]?.nonCommittingChoiceSites?.has(ip) === true;
|
|
5813
|
+
}
|
|
5814
|
+
function failureRollbackClean(prog, ip) {
|
|
5815
|
+
return prog[TABLE_RUNTIME]?.failureRollbackCleanSites?.has(ip) === true;
|
|
5816
|
+
}
|
|
5817
|
+
var WIDE_CLASS_PREFIX_LO = 65535;
|
|
5818
|
+
var WIDE_CLASS_PREFIX_HI = 0;
|
|
5819
|
+
function validateClassRange(lo, hi) {
|
|
5820
|
+
for (const cp of [lo, hi]) {
|
|
5821
|
+
if (!Number.isInteger(cp) || cp < 0 || cp > 1114111) {
|
|
5822
|
+
throw new TypeError(`table char-class endpoint is outside Unicode: ${cp}`);
|
|
6159
5823
|
}
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
6165
|
-
isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs)
|
|
6166
|
-
};
|
|
5824
|
+
}
|
|
5825
|
+
if (lo > hi) {
|
|
5826
|
+
throw new TypeError(`table char-class range is descending: ${lo} > ${hi}`);
|
|
5827
|
+
}
|
|
6167
5828
|
}
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
var OP_SEQV = 4;
|
|
6174
|
-
var OP_CHOICE = 5;
|
|
6175
|
-
var OP_REP = 6;
|
|
6176
|
-
var OP_REPV = 7;
|
|
6177
|
-
var OP_OPT = 8;
|
|
6178
|
-
var OP_XFORM = 9;
|
|
6179
|
-
var OP_NODE = 10;
|
|
6180
|
-
var OP_RULE = 11;
|
|
6181
|
-
var OP_GATE = 12;
|
|
6182
|
-
var OP_NOT = 13;
|
|
6183
|
-
var OP_PEEK = 14;
|
|
6184
|
-
var OP_LEAF = 15;
|
|
6185
|
-
var OP_EMPTY = 16;
|
|
6186
|
-
var OP_LIT_TRACK = 17;
|
|
6187
|
-
var OP_RX_TRACK = 18;
|
|
6188
|
-
var OP_NODE_TRACK = 19;
|
|
6189
|
-
var OP_SCOPE = 20;
|
|
6190
|
-
var OP_EXPECT = 21;
|
|
6191
|
-
var OP_SEQX = 22;
|
|
6192
|
-
var OP_SCAN = 23;
|
|
6193
|
-
var OP_FIELD = 24;
|
|
6194
|
-
var OP_LIT_CI = 27;
|
|
6195
|
-
var OP_LIT_CI_TRACK = 28;
|
|
6196
|
-
var OP_TOKEN = 29;
|
|
6197
|
-
var OP_SCOPE_CAP = 30;
|
|
6198
|
-
var OP_WITHCTX = 31;
|
|
6199
|
-
var OP_GUARD = 32;
|
|
6200
|
-
var OP_ADJ = 33;
|
|
6201
|
-
var OP_GREEDY = 34;
|
|
6202
|
-
var OP_REJECT = 35;
|
|
6203
|
-
var OP_ARMGATE = 36;
|
|
6204
|
-
var OP_LIVE = 37;
|
|
6205
|
-
var OP_ATTEMPT = 38;
|
|
6206
|
-
var OP_LABEL = 39;
|
|
6207
|
-
var OP_COV = 40;
|
|
6208
|
-
var OP_SCOPE_PLAIN = 41;
|
|
6209
|
-
var OP_LEX_BODY = 42;
|
|
6210
|
-
var OP_LEX_PROGRAM = 43;
|
|
6211
|
-
var OP_SOURCE_LEAF = 44;
|
|
6212
|
-
var OP_DISPATCH = 25;
|
|
6213
|
-
var OP_ROUTED = 26;
|
|
6214
|
-
var OP_NAMES = {
|
|
6215
|
-
[OP_LIT]: "LIT",
|
|
6216
|
-
[OP_RX]: "RX",
|
|
6217
|
-
[OP_SEQ]: "SEQ",
|
|
6218
|
-
[OP_SEQV]: "SEQV",
|
|
6219
|
-
[OP_CHOICE]: "CHOICE",
|
|
6220
|
-
[OP_REP]: "REP",
|
|
6221
|
-
[OP_REPV]: "REPV",
|
|
6222
|
-
[OP_OPT]: "OPT",
|
|
6223
|
-
[OP_XFORM]: "XFORM",
|
|
6224
|
-
[OP_NODE]: "NODE",
|
|
6225
|
-
[OP_RULE]: "RULE",
|
|
6226
|
-
[OP_GATE]: "GATE",
|
|
6227
|
-
[OP_NOT]: "NOT",
|
|
6228
|
-
[OP_PEEK]: "PEEK",
|
|
6229
|
-
[OP_LEAF]: "LEAF",
|
|
6230
|
-
[OP_SOURCE_LEAF]: "SOURCE_LEAF",
|
|
6231
|
-
[OP_EMPTY]: "EMPTY",
|
|
6232
|
-
[OP_LIT_TRACK]: "LIT_TRACK",
|
|
6233
|
-
[OP_RX_TRACK]: "RX_TRACK",
|
|
6234
|
-
[OP_NODE_TRACK]: "NODE_TRACK",
|
|
6235
|
-
[OP_SCOPE]: "SCOPE",
|
|
6236
|
-
[OP_EXPECT]: "EXPECT",
|
|
6237
|
-
[OP_SEQX]: "SEQX",
|
|
6238
|
-
[OP_SCAN]: "SCAN",
|
|
6239
|
-
[OP_FIELD]: "FIELD",
|
|
6240
|
-
[OP_LIT_CI]: "LIT_CI",
|
|
6241
|
-
[OP_LIT_CI_TRACK]: "LIT_CI_TRACK",
|
|
6242
|
-
[OP_DISPATCH]: "DISPATCH",
|
|
6243
|
-
[OP_ROUTED]: "ROUTED",
|
|
6244
|
-
[OP_TOKEN]: "TOKEN",
|
|
6245
|
-
[OP_SCOPE_CAP]: "SCOPE_CAP",
|
|
6246
|
-
[OP_WITHCTX]: "WITHCTX",
|
|
6247
|
-
[OP_GUARD]: "GUARD",
|
|
6248
|
-
[OP_ADJ]: "ADJ",
|
|
6249
|
-
[OP_GREEDY]: "GREEDY",
|
|
6250
|
-
[OP_REJECT]: "REJECT",
|
|
6251
|
-
[OP_ARMGATE]: "ARMGATE",
|
|
6252
|
-
[OP_LIVE]: "LIVE",
|
|
6253
|
-
[OP_ATTEMPT]: "ATTEMPT",
|
|
6254
|
-
[OP_LABEL]: "LABEL",
|
|
6255
|
-
[OP_COV]: "COV",
|
|
6256
|
-
[OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
|
|
6257
|
-
[OP_LEX_BODY]: "LEX_BODY",
|
|
6258
|
-
[OP_LEX_PROGRAM]: "LEX_PROGRAM"
|
|
6259
|
-
};
|
|
6260
|
-
|
|
6261
|
-
// src/table/lex-program.ts
|
|
6262
|
-
var LEX_NODE_TERMINAL = 0;
|
|
6263
|
-
var LEX_NODE_SEQUENCE2 = 1;
|
|
6264
|
-
var LEX_NODE_SEQUENCE3 = 2;
|
|
6265
|
-
var LEX_NODE_SEQUENCE4 = 3;
|
|
6266
|
-
var LEX_NODE_SEQUENCE5 = 4;
|
|
6267
|
-
var LEX_NODE_CHOICE2 = 5;
|
|
6268
|
-
var LEX_NODE_CHOICE4 = 6;
|
|
6269
|
-
var LEX_NODE_CHOICE8 = 7;
|
|
6270
|
-
var LEX_NODE_NOT = 8;
|
|
6271
|
-
var LEX_NODE_OPTIONAL = 9;
|
|
6272
|
-
function lexProgramDigest(words) {
|
|
6273
|
-
let hash = 2166136261;
|
|
6274
|
-
for (const word2 of words) {
|
|
6275
|
-
hash ^= word2 | 0;
|
|
6276
|
-
hash = Math.imul(hash, 16777619);
|
|
5829
|
+
function encodeClassSpec(ranges) {
|
|
5830
|
+
let wide = false;
|
|
5831
|
+
for (const range of ranges) {
|
|
5832
|
+
validateClassRange(range.lo, range.hi);
|
|
5833
|
+
if (range.hi > 65535) wide = true;
|
|
6277
5834
|
}
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
if (code < 128) return cls.ascii[code] === 1;
|
|
6283
|
-
for (let i = 0; i < cls.hi.length; i += 2) {
|
|
6284
|
-
if (code >= cls.hi[i] && code <= cls.hi[i + 1]) return true;
|
|
5835
|
+
if (!wide) {
|
|
5836
|
+
let spec2 = "";
|
|
5837
|
+
for (const range of ranges) spec2 += String.fromCharCode(range.lo, range.hi);
|
|
5838
|
+
return spec2;
|
|
6285
5839
|
}
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
if (input.charCodeAt(at) === 10) starts.push(at + 1);
|
|
6295
|
-
at++;
|
|
5840
|
+
let spec = String.fromCharCode(WIDE_CLASS_PREFIX_LO, WIDE_CLASS_PREFIX_HI);
|
|
5841
|
+
for (const range of ranges) {
|
|
5842
|
+
spec += String.fromCharCode(
|
|
5843
|
+
range.lo >>> 16,
|
|
5844
|
+
range.lo & 65535,
|
|
5845
|
+
range.hi >>> 16,
|
|
5846
|
+
range.hi & 65535
|
|
5847
|
+
);
|
|
6296
5848
|
}
|
|
6297
|
-
|
|
5849
|
+
return spec;
|
|
6298
5850
|
}
|
|
6299
|
-
function
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
ctx._fe = pos;
|
|
6310
|
-
ctx._fx = failure;
|
|
6311
|
-
if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
|
|
6312
|
-
return -1;
|
|
6313
|
-
}
|
|
6314
|
-
if (tracksLines) trackLexLines(ctx, input, end);
|
|
6315
|
-
return end;
|
|
5851
|
+
function decodeClassSpec(spec) {
|
|
5852
|
+
if (spec.charCodeAt(0) === WIDE_CLASS_PREFIX_LO && spec.charCodeAt(1) === WIDE_CLASS_PREFIX_HI) {
|
|
5853
|
+
if ((spec.length - 2) % 4 !== 0) throw new TypeError("table char-class spec has a truncated wide range");
|
|
5854
|
+
if (spec.length === 2) throw new TypeError("table char-class spec has a wide prefix but no ranges");
|
|
5855
|
+
const ranges2 = [];
|
|
5856
|
+
for (let i = 2; i < spec.length; i += 4) {
|
|
5857
|
+
const lo = spec.charCodeAt(i) * 65536 + spec.charCodeAt(i + 1);
|
|
5858
|
+
const hi = spec.charCodeAt(i + 2) * 65536 + spec.charCodeAt(i + 3);
|
|
5859
|
+
validateClassRange(lo, hi);
|
|
5860
|
+
ranges2.push({ lo, hi });
|
|
6316
5861
|
}
|
|
6317
|
-
|
|
5862
|
+
return ranges2;
|
|
5863
|
+
}
|
|
5864
|
+
if (spec.length % 2 !== 0) throw new TypeError("table char-class spec has an unmatched range endpoint");
|
|
5865
|
+
const ranges = [];
|
|
5866
|
+
for (let i = 0; i < spec.length; i += 2) {
|
|
5867
|
+
const lo = spec.charCodeAt(i);
|
|
5868
|
+
const hi = spec.charCodeAt(i + 1);
|
|
5869
|
+
validateClassRange(lo, hi);
|
|
5870
|
+
ranges.push({ lo, hi });
|
|
5871
|
+
}
|
|
5872
|
+
return ranges;
|
|
6318
5873
|
}
|
|
6319
|
-
function
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
if (e0 < 0) return -1;
|
|
6328
|
-
ctx._fc = false;
|
|
6329
|
-
return b.run(input, e0, ctx);
|
|
6330
|
-
}
|
|
6331
|
-
};
|
|
5874
|
+
function resolveClass(spec) {
|
|
5875
|
+
const ascii = new Uint8Array(128);
|
|
5876
|
+
const hi = [];
|
|
5877
|
+
for (const { lo, hi: up } of decodeClassSpec(spec)) {
|
|
5878
|
+
if (lo < 128) for (let c = lo; c <= Math.min(up, 127); c++) ascii[c] = 1;
|
|
5879
|
+
if (up >= 128) hi.push(Math.max(lo, 128), up);
|
|
5880
|
+
}
|
|
5881
|
+
return { ascii, hi };
|
|
6332
5882
|
}
|
|
6333
|
-
function
|
|
6334
|
-
return
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
return e1 < 0 ? -1 : c.match(input, e1);
|
|
6340
|
-
},
|
|
6341
|
-
run: (input, pos, ctx) => {
|
|
6342
|
-
const e0 = a.run(input, pos, ctx);
|
|
6343
|
-
if (e0 < 0) return -1;
|
|
6344
|
-
ctx._fc = false;
|
|
6345
|
-
const e1 = b.run(input, e0, ctx);
|
|
6346
|
-
if (e1 < 0) return -1;
|
|
6347
|
-
ctx._fc = false;
|
|
6348
|
-
return c.run(input, e1, ctx);
|
|
6349
|
-
}
|
|
6350
|
-
};
|
|
5883
|
+
function classHas2(cls, code) {
|
|
5884
|
+
if (code < 0) return false;
|
|
5885
|
+
if (code < 128) return cls.ascii[code] === 1;
|
|
5886
|
+
const hi = cls.hi;
|
|
5887
|
+
for (let i = 0; i < hi.length; i += 2) if (code >= hi[i] && code <= hi[i + 1]) return true;
|
|
5888
|
+
return false;
|
|
6351
5889
|
}
|
|
6352
|
-
function
|
|
6353
|
-
const
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
5890
|
+
function resolveDispatch(arms, cc) {
|
|
5891
|
+
const ascii = new Uint8Array(128);
|
|
5892
|
+
const hi = [];
|
|
5893
|
+
const open = [];
|
|
5894
|
+
const armCls = [];
|
|
5895
|
+
let exclusive = true;
|
|
5896
|
+
let hiOwners = 0;
|
|
5897
|
+
for (let a = 0; a < arms.length; a++) {
|
|
5898
|
+
const ci = arms[a];
|
|
5899
|
+
if (ci < 0) {
|
|
5900
|
+
open.push(a);
|
|
5901
|
+
armCls.push(null);
|
|
5902
|
+
exclusive = false;
|
|
5903
|
+
continue;
|
|
6364
5904
|
}
|
|
6365
|
-
|
|
5905
|
+
const cls = cc[ci];
|
|
5906
|
+
armCls.push(cls);
|
|
5907
|
+
for (let c = 0; c < 128; c++) {
|
|
5908
|
+
if (cls.ascii[c] !== 1) continue;
|
|
5909
|
+
if (ascii[c] === 0) ascii[c] = a + 1;
|
|
5910
|
+
else exclusive = false;
|
|
5911
|
+
}
|
|
5912
|
+
if (cls.hi.length > 0 && ++hiOwners > 1) exclusive = false;
|
|
5913
|
+
for (let i = 0; i < cls.hi.length; i += 2) hi.push(cls.hi[i], cls.hi[i + 1], a);
|
|
5914
|
+
}
|
|
5915
|
+
return { ascii, hi, open, armCls, exclusive };
|
|
6366
5916
|
}
|
|
6367
|
-
function
|
|
6368
|
-
|
|
6369
|
-
return
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
const end = abcd.run(input, pos, ctx);
|
|
6376
|
-
if (end < 0) return -1;
|
|
6377
|
-
ctx._fc = false;
|
|
6378
|
-
return e.run(input, end, ctx);
|
|
6379
|
-
}
|
|
6380
|
-
};
|
|
6381
|
-
}
|
|
6382
|
-
function appendExpected(acc, values) {
|
|
6383
|
-
if (values === void 0 || values.length === 0) return acc;
|
|
6384
|
-
if (acc === void 0) return values.slice();
|
|
6385
|
-
acc.push(...values);
|
|
6386
|
-
return acc;
|
|
6387
|
-
}
|
|
6388
|
-
function choiceNode(arms, classes, armExpected, choiceExpected) {
|
|
6389
|
-
let matchTail = () => -1;
|
|
6390
|
-
for (let i = arms.length - 1; i >= 0; i--) {
|
|
6391
|
-
const arm = arms[i], next = matchTail;
|
|
6392
|
-
matchTail = (input, pos) => {
|
|
6393
|
-
const end = arm.match(input, pos);
|
|
6394
|
-
return end < 0 ? next(input, pos) : end;
|
|
6395
|
-
};
|
|
6396
|
-
}
|
|
6397
|
-
let tail = (_input, pos, ctx, _lead, acc) => {
|
|
6398
|
-
ctx._fe = pos;
|
|
6399
|
-
ctx._fx = acc ?? choiceExpected;
|
|
6400
|
-
return -1;
|
|
6401
|
-
};
|
|
6402
|
-
for (let i = arms.length - 1; i >= 0; i--) {
|
|
6403
|
-
const arm = arms[i], cls = classes[i], staticExpected = armExpected[i], next = tail;
|
|
6404
|
-
tail = (input, pos, ctx, lead2, acc, best) => {
|
|
6405
|
-
if (cls !== void 0 && !classHas(cls, lead2)) {
|
|
6406
|
-
return next(
|
|
6407
|
-
input,
|
|
6408
|
-
pos,
|
|
6409
|
-
ctx,
|
|
6410
|
-
lead2,
|
|
6411
|
-
best === pos ? appendExpected(acc, staticExpected) : acc,
|
|
6412
|
-
best
|
|
6413
|
-
);
|
|
6414
|
-
}
|
|
6415
|
-
ctx._fc = false;
|
|
6416
|
-
const end = arm.run(input, pos, ctx);
|
|
6417
|
-
if (end >= 0) return end;
|
|
6418
|
-
const at = ctx._fe ?? pos;
|
|
6419
|
-
if (at > best) {
|
|
6420
|
-
best = at;
|
|
6421
|
-
acc = void 0;
|
|
6422
|
-
}
|
|
6423
|
-
const nextAcc = at === best ? appendExpected(acc, ctx._fx) : acc;
|
|
6424
|
-
if (committed(ctx)) {
|
|
6425
|
-
if (nextAcc !== void 0) ctx._fx = nextAcc;
|
|
6426
|
-
return -1;
|
|
6427
|
-
}
|
|
6428
|
-
return next(input, pos, ctx, lead2, nextAcc, best);
|
|
6429
|
-
};
|
|
5917
|
+
function buildTrivia(spec) {
|
|
5918
|
+
if (spec.live !== void 0) return spec.live;
|
|
5919
|
+
if (spec.plain !== void 0) return trivia(regex(new RegExp(spec.plain[0], spec.plain[1])));
|
|
5920
|
+
if (spec.alts !== void 0) {
|
|
5921
|
+
const [head, ...rest] = spec.alts.map(([source, flags]) => regex(new RegExp(source, flags)));
|
|
5922
|
+
if (head === void 0) throw new TypeError("trivia spec has an EMPTY alternation, which matches nothing");
|
|
5923
|
+
const body = rest.length === 0 ? head : choice(head, ...rest);
|
|
5924
|
+
return trivia(many(body, { min: spec.min ?? 1 }));
|
|
6430
5925
|
}
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
input,
|
|
6435
|
-
pos,
|
|
6436
|
-
ctx,
|
|
6437
|
-
pos < input.length ? input.codePointAt(pos) : -1,
|
|
6438
|
-
void 0,
|
|
6439
|
-
pos
|
|
6440
|
-
)
|
|
6441
|
-
};
|
|
5926
|
+
const arms = {};
|
|
5927
|
+
for (const [name, source, flags] of spec.arms) arms[name] = regex(new RegExp(source, flags));
|
|
5928
|
+
return classifiedTrivia(arms);
|
|
6442
5929
|
}
|
|
6443
|
-
function
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
5930
|
+
function resolveTable(prog) {
|
|
5931
|
+
const owner = prog[TABLE_RUNTIME];
|
|
5932
|
+
const hit = owner?.resolved;
|
|
5933
|
+
if (hit !== void 0) return hit;
|
|
5934
|
+
const cc = prog.cc.map(resolveClass);
|
|
5935
|
+
const triviaBuilt = (prog.triviaSpecs ?? []).map(buildTrivia);
|
|
5936
|
+
const lex = (prog.lex ?? []).map((spec) => buildLexBodyRecognizer(spec, prog.k));
|
|
5937
|
+
const built = {
|
|
5938
|
+
prog,
|
|
5939
|
+
code: Int32Array.from(prog.code),
|
|
5940
|
+
k: prog.k,
|
|
5941
|
+
fns: prog.fns,
|
|
5942
|
+
cc,
|
|
5943
|
+
fx: prog.fx,
|
|
5944
|
+
disp: prog.disp.map((d) => resolveDispatch(d, cc)),
|
|
5945
|
+
trivia: triviaBuilt,
|
|
5946
|
+
triviaScan: triviaBuilt.map(fastTriviaScanner),
|
|
5947
|
+
triviaLabelled: triviaBuilt.map((t) => t._meta.triviaKindLabels !== void 0),
|
|
5948
|
+
dsp: prog.dsp.map((d) => ({
|
|
5949
|
+
byKey: new Map(d.key.map((x, i) => [x, d.keyArm[i]])),
|
|
5950
|
+
byFold: new Map(d.fold.map((x, i) => [x, d.foldArm[i]])),
|
|
5951
|
+
match: d.match,
|
|
5952
|
+
routed: d.routed,
|
|
5953
|
+
expected: d.expected
|
|
5954
|
+
})),
|
|
5955
|
+
lex,
|
|
5956
|
+
lexPrograms: (prog.lexPrograms ?? []).map((spec) => buildLexProgramRunner(spec, lex, cc, prog.fx)),
|
|
5957
|
+
rules: prog.rules
|
|
6452
5958
|
};
|
|
5959
|
+
if (owner !== void 0) owner.resolved = built;
|
|
5960
|
+
return built;
|
|
6453
5961
|
}
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
5962
|
+
|
|
5963
|
+
// src/analysis/gating.ts
|
|
5964
|
+
function childrenOf2(d) {
|
|
5965
|
+
switch (d.tag) {
|
|
5966
|
+
case "sequence":
|
|
5967
|
+
case "choice":
|
|
5968
|
+
return d.parsers;
|
|
5969
|
+
case "dispatch":
|
|
5970
|
+
return [
|
|
5971
|
+
d.selector,
|
|
5972
|
+
...d.cases.map((c) => c.parser),
|
|
5973
|
+
...d.matchers ? d.matchers.map((c) => c.parser) : [],
|
|
5974
|
+
...d.otherwise === void 0 ? [] : [d.otherwise]
|
|
5975
|
+
];
|
|
5976
|
+
case "sepBy":
|
|
5977
|
+
return [d.parser, d.separator];
|
|
5978
|
+
case "recover":
|
|
5979
|
+
return [d.parser, d.sentinel];
|
|
5980
|
+
case "scanTo":
|
|
5981
|
+
return [
|
|
5982
|
+
d.sentinel,
|
|
5983
|
+
...d.skip,
|
|
5984
|
+
...d.recoverAt === void 0 ? [] : [d.recoverAt],
|
|
5985
|
+
...d.stopAt === void 0 ? [] : [d.stopAt]
|
|
5986
|
+
];
|
|
5987
|
+
case "grammar":
|
|
5988
|
+
return d.triviaParser ? [d.parser, d.triviaParser] : [d.parser];
|
|
5989
|
+
case "routed":
|
|
5990
|
+
return d.fallback ? [d.fallback] : [];
|
|
5991
|
+
case "lines":
|
|
5992
|
+
return [d.prefix, d.body, ...d.first === void 0 ? [] : [d.first], ...d.lazy === void 0 ? [] : [d.lazy]];
|
|
5993
|
+
case "columns":
|
|
5994
|
+
return [];
|
|
5995
|
+
// A `lazy` is a REFERENCE, not a subtree. Descending through it would make
|
|
5996
|
+
// every rule's walk cover the whole reachable grammar — site paths become
|
|
5997
|
+
// nonsense, and a structural hash includes half the grammar. Treated as a
|
|
5998
|
+
// leaf, keyed by the rule name it refers to, which is also the right
|
|
5999
|
+
// semantics: two productions referencing `g.Ident` really do fill that slot
|
|
6000
|
+
// the same way.
|
|
6001
|
+
case "lazy":
|
|
6002
|
+
case "literal":
|
|
6003
|
+
case "regex":
|
|
6004
|
+
case "keywords":
|
|
6005
|
+
case "guard":
|
|
6006
|
+
case "adjacency":
|
|
6007
|
+
case "unknown":
|
|
6008
|
+
return [];
|
|
6009
|
+
default: {
|
|
6010
|
+
const rec = d;
|
|
6011
|
+
return rec.parser ? [rec.parser] : [];
|
|
6464
6012
|
}
|
|
6465
|
-
}
|
|
6013
|
+
}
|
|
6466
6014
|
}
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
const
|
|
6476
|
-
if (
|
|
6477
|
-
const matcher = matchers[words[at++]], failure = expected[words[at++]];
|
|
6478
|
-
const tracks = words[at++];
|
|
6479
|
-
if (matcher === void 0 || failure === void 0 || tracks !== 0 && tracks !== 1) {
|
|
6480
|
-
throw new TypeError("table lexical tree terminal references an invalid pool entry");
|
|
6481
|
-
}
|
|
6482
|
-
stack.push(terminalNode(matcher, failure, tracks === 1));
|
|
6483
|
-
continue;
|
|
6484
|
-
}
|
|
6485
|
-
if (op >= LEX_NODE_SEQUENCE2 && op <= LEX_NODE_SEQUENCE5) {
|
|
6486
|
-
const count = op - LEX_NODE_SEQUENCE2 + 2;
|
|
6487
|
-
const nodes = Array.from({ length: count }, pop).reverse();
|
|
6488
|
-
if (count === 2) stack.push(sequence2(nodes[0], nodes[1]));
|
|
6489
|
-
else if (count === 3) stack.push(sequence3(nodes[0], nodes[1], nodes[2]));
|
|
6490
|
-
else if (count === 4) stack.push(sequence4(nodes[0], nodes[1], nodes[2], nodes[3]));
|
|
6491
|
-
else stack.push(sequence5(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4]));
|
|
6492
|
-
continue;
|
|
6493
|
-
}
|
|
6494
|
-
if (op >= LEX_NODE_CHOICE2 && op <= LEX_NODE_CHOICE8) {
|
|
6495
|
-
const count = op === LEX_NODE_CHOICE2 ? 2 : op === LEX_NODE_CHOICE4 ? 4 : 8;
|
|
6496
|
-
const choiceFx = expected[words[at++]];
|
|
6497
|
-
if (choiceFx === void 0) throw new TypeError("table lexical tree choice has an invalid expected set");
|
|
6498
|
-
const armClasses = [];
|
|
6499
|
-
const armFx = [];
|
|
6500
|
-
for (let i = 0; i < count; i++) {
|
|
6501
|
-
const cls = words[at++], fx = expected[words[at++]];
|
|
6502
|
-
if (cls < -1 || cls >= 0 && classes[cls] === void 0 || fx === void 0) {
|
|
6503
|
-
throw new TypeError("table lexical tree choice references an invalid pool entry");
|
|
6504
|
-
}
|
|
6505
|
-
armClasses.push(cls < 0 ? void 0 : classes[cls]);
|
|
6506
|
-
armFx.push(fx);
|
|
6507
|
-
}
|
|
6508
|
-
const arms = Array.from({ length: count }, pop).reverse();
|
|
6509
|
-
stack.push(choiceNode(arms, armClasses, armFx, choiceFx));
|
|
6510
|
-
continue;
|
|
6511
|
-
}
|
|
6512
|
-
if (op === LEX_NODE_NOT) {
|
|
6513
|
-
const failure = expected[words[at++]];
|
|
6514
|
-
if (failure === void 0) throw new TypeError("table lexical tree assertion has an invalid expected set");
|
|
6515
|
-
stack.push(notNode(pop(), failure));
|
|
6516
|
-
continue;
|
|
6517
|
-
}
|
|
6518
|
-
if (op === LEX_NODE_OPTIONAL) {
|
|
6519
|
-
stack.push(optionalNode(pop()));
|
|
6520
|
-
continue;
|
|
6521
|
-
}
|
|
6522
|
-
throw new TypeError("table lexical tree has an unknown fixed body");
|
|
6015
|
+
var isAny = (fs) => fs.kind === "any";
|
|
6016
|
+
function intersection(a, b) {
|
|
6017
|
+
if (a.kind === "any") return b;
|
|
6018
|
+
if (b.kind === "any") return a;
|
|
6019
|
+
if (a.kind === "empty" || b.kind === "empty") return { kind: "empty" };
|
|
6020
|
+
const ranges = [];
|
|
6021
|
+
for (const ra of a.ranges) for (const rb of b.ranges) {
|
|
6022
|
+
const lo = Math.max(ra.lo, rb.lo);
|
|
6023
|
+
const hi = Math.min(ra.hi, rb.hi);
|
|
6024
|
+
if (lo <= hi) ranges.push({ lo, hi });
|
|
6523
6025
|
}
|
|
6524
|
-
|
|
6525
|
-
return stack[0].run;
|
|
6026
|
+
return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
|
|
6526
6027
|
}
|
|
6527
|
-
function
|
|
6528
|
-
|
|
6529
|
-
|
|
6028
|
+
function combine(sets) {
|
|
6029
|
+
let acc = { kind: "empty" };
|
|
6030
|
+
for (const s of sets) {
|
|
6031
|
+
if (s.kind === "any") return { kind: "any" };
|
|
6032
|
+
if (s.kind === "empty") continue;
|
|
6033
|
+
acc = acc.kind === "ranges" ? { kind: "ranges", ranges: [...acc.ranges, ...s.ranges] } : s;
|
|
6530
6034
|
}
|
|
6531
|
-
|
|
6532
|
-
|
|
6035
|
+
return acc;
|
|
6036
|
+
}
|
|
6037
|
+
var ch = (c) => c >= 32 && c < 127 ? `'${String.fromCharCode(c)}'` : `\\u${c}`;
|
|
6038
|
+
function firstSetToString(fs) {
|
|
6039
|
+
if (fs.kind === "any") return "ANY";
|
|
6040
|
+
if (fs.kind === "empty") return "(empty)";
|
|
6041
|
+
return fs.ranges.map((r) => r.lo === r.hi ? ch(r.lo) : `${ch(r.lo)}-${ch(r.hi)}`).join(",");
|
|
6042
|
+
}
|
|
6043
|
+
var ruleNameOf2 = (p) => p._ruleName;
|
|
6044
|
+
var KEYWORD_REGEX_RE = /^\^?[@#.-]?[A-Za-z][\w-]*(\(\?![^)]*\))?\$?$/;
|
|
6045
|
+
var SUGGESTIONS = {
|
|
6046
|
+
"broad-recognizer": "This arm can begin with any character, so no single-character test can rule it out. The parser has to enter it \u2014 set up, try, and undo \u2014 at every position it reaches, instead of skipping it for free.\nTo fix: make the arm begin with a fixed character, word or keyword. If it is meant to be a catch-all that matches anything, leave it and add the choice to the accept list at the end.",
|
|
6047
|
+
"leading-not": "This arm begins with a not(...) check, which matches nothing and so says nothing about which character the arm starts with. The parser then cannot rule the arm out and enters it everywhere.\nTo fix: put the term that actually consumes text first and keep not(...) after it as a trailing boundary. If you need to REQUIRE something ahead without consuming it, use peek(X) \u2014 it checks the same thing but still tells parseman that the arm starts with X.",
|
|
6048
|
+
"nullable-prefix": "This arm begins with something optional \u2014 an optional(...) or a repeat that allows zero items \u2014 so the arm can also start with whatever comes AFTER it, and that is broad enough that no character rules the arm out.\nTo fix: give the optional part its own arm so each arm has a definite beginning, or require at least one item. A plain sepBy(item, sep) also matches nothing at all; pass { min: 1 } when the list must not be empty.",
|
|
6049
|
+
"cross-artifact-ref": "This arm hands off to another rule, and that rule has the same problem \u2014 it can begin with any character \u2014 so the cost is inherited rather than caused here.\nTo fix: run this check on the rule it refers to and fix it there. One rule given a definite beginning fixes every choice that uses it.",
|
|
6050
|
+
"opaque-wrapper": "This arm begins with a wrapper that only inspects parser state (a gate/withCtx/recover) and consumes no text, so it says nothing about which character the arm starts with.\nTo fix: put a term that actually matches text in front of the wrapper.",
|
|
6051
|
+
"ref-cycle": "This arm refers back to itself through a cycle of rules, and parseman could not work out a definite first character for any of them.\nTo fix: make sure the base case of the recursion begins with a fixed character or keyword, so the cycle has somewhere definite to start."
|
|
6052
|
+
};
|
|
6053
|
+
function refineSuggestion(cause, arm) {
|
|
6054
|
+
if (cause !== "broad-recognizer") return SUGGESTIONS[cause];
|
|
6055
|
+
const lead2 = peelToLeading(arm);
|
|
6056
|
+
const d = lead2._def;
|
|
6057
|
+
if (d.tag === "regex" && KEYWORD_REGEX_RE.test(d.source)) {
|
|
6058
|
+
return `This arm matches the fixed word \`${d.source}\` with a regular expression, and parseman cannot always tell from a regular expression which character it starts with.
|
|
6059
|
+
To fix: write it as word('\u2026') or keywords([\u2026]) instead. They match exactly the same text and compile to the same character scan, but they also tell parseman the first character, which is what lets the parser skip this arm when it cannot match.`;
|
|
6533
6060
|
}
|
|
6534
|
-
if (
|
|
6535
|
-
|
|
6536
|
-
throw new TypeError("table balanced lexical program has an invalid scan reference");
|
|
6537
|
-
}
|
|
6538
|
-
const run3 = (_input, _pos, ctx, scan) => {
|
|
6539
|
-
if (scan === void 0) throw new TypeError("table balanced lexical program lost its scan body");
|
|
6540
|
-
const result = scan.parse(_input, _pos, ctx);
|
|
6541
|
-
if (!result.ok) {
|
|
6542
|
-
ctx._fe = result.span.start;
|
|
6543
|
-
ctx._fx = result.expected ?? [];
|
|
6544
|
-
return -1;
|
|
6545
|
-
}
|
|
6546
|
-
return result.span.end;
|
|
6547
|
-
};
|
|
6548
|
-
return Object.assign(run3, { scan: spec[2] });
|
|
6061
|
+
if (d.tag === "scanTo") {
|
|
6062
|
+
return "This arm ends in a scanTo(...) catch-all, which reads forward until it finds something and so can begin at any character. The parser can never rule it out. That is usually exactly what you want from a fallback.\nTo fix: usually nothing. Add this choice to the accept list at the end so the check keeps flagging the choices that are real problems.";
|
|
6549
6063
|
}
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6064
|
+
return SUGGESTIONS[cause];
|
|
6065
|
+
}
|
|
6066
|
+
function classifyBroadArm(arm, resolve) {
|
|
6067
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6068
|
+
const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
|
|
6069
|
+
const walk = (p) => {
|
|
6070
|
+
if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
|
|
6071
|
+
seen.add(p);
|
|
6072
|
+
const d = p._def;
|
|
6073
|
+
switch (d.tag) {
|
|
6074
|
+
case "literal":
|
|
6075
|
+
case "regex":
|
|
6076
|
+
case "keywords":
|
|
6077
|
+
return isAny(p._meta.firstSet) ? { cause: "broad-recognizer", detail: `broad recognizer (${d.tag})` } : null;
|
|
6078
|
+
case "not":
|
|
6079
|
+
return { cause: "leading-not", detail: "leading not(...) (first-set ANY)" };
|
|
6080
|
+
// peek() CARRIES its body's first-set, so it is only broad when the body is
|
|
6081
|
+
// (or the body is nullable) — walk in for the real cause.
|
|
6082
|
+
case "peek":
|
|
6083
|
+
return walk(d.parser);
|
|
6084
|
+
case "scanTo":
|
|
6085
|
+
return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
|
|
6086
|
+
// A run opens with its first line's prefix.
|
|
6087
|
+
case "lines":
|
|
6088
|
+
return walk(d.first ?? d.prefix);
|
|
6089
|
+
// `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
|
|
6090
|
+
// its own — it is the poison. `withCtx`/`recover` FORWARD their inner
|
|
6091
|
+
// first-set (see firstSetOf), so a broad result comes from the inner parser:
|
|
6092
|
+
// walk into it for a precise cause.
|
|
6093
|
+
case "guard":
|
|
6094
|
+
return { cause: "opaque-wrapper", detail: `opaque wrapper (${d.tag})` };
|
|
6095
|
+
// Zero-width and dropped from a sequence's first-set (isZeroWidthAssertion),
|
|
6096
|
+
// so it is never the reason an arm is broad — but it can be reached as the
|
|
6097
|
+
// sole term of a degenerate arm. Report it precisely rather than as a wrapper.
|
|
6098
|
+
case "adjacency":
|
|
6099
|
+
return { cause: "broad-recognizer", detail: `${d.polarity}() (zero-width assertion)` };
|
|
6100
|
+
// attempt/withCtx/recover FORWARD their inner first-set (transparent for
|
|
6101
|
+
// dispatch), so a broad result comes from the inner parser — walk into it.
|
|
6102
|
+
case "attempt":
|
|
6103
|
+
case "withCtx":
|
|
6104
|
+
case "recover":
|
|
6105
|
+
return walk(d.parser);
|
|
6106
|
+
case "lazy": {
|
|
6107
|
+
const name = p._ruleName;
|
|
6108
|
+
let target;
|
|
6109
|
+
try {
|
|
6110
|
+
target = d.thunk();
|
|
6111
|
+
} catch {
|
|
6112
|
+
const bound = name !== void 0 ? resolve?.(name) : void 0;
|
|
6113
|
+
if (bound === void 0)
|
|
6114
|
+
return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
|
|
6115
|
+
target = bound;
|
|
6116
|
+
}
|
|
6117
|
+
const inner = walk(target);
|
|
6118
|
+
if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
|
|
6119
|
+
return inner;
|
|
6567
6120
|
}
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
return -1;
|
|
6121
|
+
case "optional":
|
|
6122
|
+
case "many": {
|
|
6123
|
+
const inner = walk(d.parser);
|
|
6124
|
+
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
6573
6125
|
}
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6126
|
+
case "oneOrMore":
|
|
6127
|
+
case "transform":
|
|
6128
|
+
case "label":
|
|
6129
|
+
case "field":
|
|
6130
|
+
case "trivia":
|
|
6131
|
+
case "token":
|
|
6132
|
+
case "leaf":
|
|
6133
|
+
case "node":
|
|
6134
|
+
case "grammar":
|
|
6135
|
+
case "expect":
|
|
6136
|
+
return walk(d.parser);
|
|
6137
|
+
case "sequence": {
|
|
6138
|
+
let sawNullablePrefix = false;
|
|
6139
|
+
for (const t of d.parsers) {
|
|
6140
|
+
if (t._def.tag === "not") {
|
|
6141
|
+
sawNullablePrefix = true;
|
|
6142
|
+
continue;
|
|
6143
|
+
}
|
|
6144
|
+
if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve))) {
|
|
6145
|
+
const inner = walk(t);
|
|
6146
|
+
if (sawNullablePrefix)
|
|
6147
|
+
return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
|
|
6148
|
+
return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
|
|
6149
|
+
}
|
|
6150
|
+
if (matchesEmpty(t, /* @__PURE__ */ new Set(), resolve)) {
|
|
6151
|
+
sawNullablePrefix = true;
|
|
6152
|
+
continue;
|
|
6153
|
+
}
|
|
6154
|
+
return null;
|
|
6155
|
+
}
|
|
6156
|
+
return { cause: "broad-recognizer", detail: "sequence of only nullable/zero-width terms" };
|
|
6581
6157
|
}
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
const tracks = spec[12];
|
|
6594
|
-
if (cls === void 0 || choiceFx === void 0 || m0 === void 0 || x0 === void 0 || m1 === void 0 || x1 === void 0 || m2 === void 0 || x2 === void 0 || m3 === void 0 || x3 === void 0 || tracks !== 0 && tracks !== 1) {
|
|
6595
|
-
throw new TypeError("table lexical program references an invalid pool entry");
|
|
6596
|
-
}
|
|
6597
|
-
return (input, pos, ctx) => {
|
|
6598
|
-
const lead2 = pos < input.length ? input.codePointAt(pos) : -1;
|
|
6599
|
-
if (!classHas(cls, lead2)) {
|
|
6600
|
-
ctx._fe = pos;
|
|
6601
|
-
ctx._fx = choiceFx.slice();
|
|
6602
|
-
return -1;
|
|
6158
|
+
case "choice": {
|
|
6159
|
+
for (const a of d.parsers) {
|
|
6160
|
+
const r = walk(a);
|
|
6161
|
+
if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
|
|
6162
|
+
}
|
|
6163
|
+
return null;
|
|
6164
|
+
}
|
|
6165
|
+
case "dispatch":
|
|
6166
|
+
return walk(d.selector);
|
|
6167
|
+
default:
|
|
6168
|
+
return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
|
|
6603
6169
|
}
|
|
6604
|
-
let acc;
|
|
6605
|
-
const failed = (fx) => {
|
|
6606
|
-
ctx._fe = pos;
|
|
6607
|
-
ctx._fx = fx;
|
|
6608
|
-
if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
|
|
6609
|
-
if (acc === void 0) acc = fx.slice();
|
|
6610
|
-
else acc.push(...fx);
|
|
6611
|
-
};
|
|
6612
|
-
ctx._fc = false;
|
|
6613
|
-
const success = (result2) => {
|
|
6614
|
-
const end = result2 / 2;
|
|
6615
|
-
if (tracks === 1) trackLexLines(ctx, input, end);
|
|
6616
|
-
return end;
|
|
6617
|
-
};
|
|
6618
|
-
let result = m0(input, pos);
|
|
6619
|
-
if (result >= 0) return success(result);
|
|
6620
|
-
failed(x0);
|
|
6621
|
-
ctx._fc = false;
|
|
6622
|
-
result = m1(input, pos);
|
|
6623
|
-
if (result >= 0) return success(result);
|
|
6624
|
-
failed(x1);
|
|
6625
|
-
ctx._fc = false;
|
|
6626
|
-
result = m2(input, pos);
|
|
6627
|
-
if (result >= 0) return success(result);
|
|
6628
|
-
failed(x2);
|
|
6629
|
-
ctx._fc = false;
|
|
6630
|
-
result = m3(input, pos);
|
|
6631
|
-
if (result >= 0) return success(result);
|
|
6632
|
-
failed(x3);
|
|
6633
|
-
ctx._fe = pos;
|
|
6634
|
-
ctx._fx = acc ?? choiceFx;
|
|
6635
|
-
return -1;
|
|
6636
6170
|
};
|
|
6171
|
+
return walk(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
|
|
6637
6172
|
}
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6173
|
+
function peelToLeading(arm) {
|
|
6174
|
+
let d = arm._def;
|
|
6175
|
+
let cur = arm;
|
|
6176
|
+
for (; ; ) {
|
|
6177
|
+
if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label" || d.tag === "attempt") {
|
|
6178
|
+
cur = d.parser;
|
|
6179
|
+
d = cur._def;
|
|
6180
|
+
continue;
|
|
6181
|
+
}
|
|
6182
|
+
break;
|
|
6647
6183
|
}
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
return (input, pos) => {
|
|
6651
|
-
re.lastIndex = pos;
|
|
6652
|
-
if (!re.test(input)) return -1;
|
|
6653
|
-
const end = re.lastIndex;
|
|
6654
|
-
if (suffix < 0) return end * 2;
|
|
6655
|
-
return input.charCodeAt(end) === suffix ? (end + 1) * 2 + 1 : end * 2;
|
|
6656
|
-
};
|
|
6184
|
+
if (d.tag === "sequence" && d.parsers.length >= 1) return d.parsers[0];
|
|
6185
|
+
return cur;
|
|
6657
6186
|
}
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6187
|
+
function detectAntiPatterns(rule, arms) {
|
|
6188
|
+
const out = [];
|
|
6189
|
+
arms.forEach((arm, i) => {
|
|
6190
|
+
const lead2 = peelToLeading(arm);
|
|
6191
|
+
const ld = lead2._def;
|
|
6192
|
+
if (ld.tag === "not") {
|
|
6193
|
+
const inner = ld.parser;
|
|
6194
|
+
if (inner._def.tag === "not") {
|
|
6195
|
+
out.push({
|
|
6196
|
+
kind: "double-not",
|
|
6197
|
+
rule,
|
|
6198
|
+
armIndex: i,
|
|
6199
|
+
message: 'not(not(X)) is a hand-written way of saying "X must come next, but do not consume it". parseman does that for you, and the hand-written form is worse than nothing here: because not(...) reveals no first character, the arm cannot be skipped, and among sibling arms that share a first character it can select the wrong one.\nTo fix: write peek(X). It checks the same thing without consuming, and it also tells parseman that the arm starts with whatever X starts with.'
|
|
6200
|
+
});
|
|
6201
|
+
} else {
|
|
6202
|
+
out.push({
|
|
6203
|
+
kind: "leading-not",
|
|
6204
|
+
rule,
|
|
6205
|
+
armIndex: i,
|
|
6206
|
+
message: "This arm begins with not(...), which matches no text and so reveals no first character. The parser therefore cannot skip the arm and enters it at every position.\nTo fix: put the term that actually consumes text first, and keep not(...) after it as a trailing boundary check."
|
|
6207
|
+
});
|
|
6208
|
+
}
|
|
6209
|
+
}
|
|
6210
|
+
if (ld.tag === "regex" && KEYWORD_REGEX_RE.test(ld.source)) {
|
|
6211
|
+
out.push({
|
|
6212
|
+
kind: "keyword-regex",
|
|
6213
|
+
rule,
|
|
6214
|
+
armIndex: i,
|
|
6215
|
+
message: `This arm starts with the fixed word \`${ld.source}\` written as a regular expression. Writing it as word('\u2026') or keywords([\u2026]) matches exactly the same text and compiles to the same character scan, but also tells parseman the first character, which is what lets the parser skip the arm when it cannot match.`
|
|
6216
|
+
});
|
|
6217
|
+
}
|
|
6218
|
+
});
|
|
6219
|
+
return out;
|
|
6686
6220
|
}
|
|
6687
|
-
function
|
|
6688
|
-
return (
|
|
6221
|
+
function analyzeGating(entry, opts) {
|
|
6222
|
+
return analyzeGatingRules([[ruleNameOf2(entry) ?? opts?.entryName ?? "<entry>", entry]], opts);
|
|
6689
6223
|
}
|
|
6690
|
-
function
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6224
|
+
function analyzeGatingRules(ruleMap, opts) {
|
|
6225
|
+
const raw = [];
|
|
6226
|
+
const antiPatterns = [];
|
|
6227
|
+
const unanalysable = [];
|
|
6228
|
+
const seenUnanalysable = /* @__PURE__ */ new Set();
|
|
6229
|
+
const visited = /* @__PURE__ */ new Set();
|
|
6230
|
+
const noteUnanalysable = (u) => {
|
|
6231
|
+
const key = `${u.rule}\0${u.kind}`;
|
|
6232
|
+
if (seenUnanalysable.has(key)) return;
|
|
6233
|
+
seenUnanalysable.add(key);
|
|
6234
|
+
unanalysable.push(u);
|
|
6235
|
+
};
|
|
6236
|
+
const visit = (p, enclosingRule) => {
|
|
6237
|
+
if (visited.has(p)) return;
|
|
6238
|
+
visited.add(p);
|
|
6239
|
+
const def = p?._def;
|
|
6240
|
+
if (def === null || typeof def !== "object") {
|
|
6241
|
+
noteUnanalysable(
|
|
6242
|
+
typeof p === "function" ? {
|
|
6243
|
+
rule: enclosingRule,
|
|
6244
|
+
kind: "fused-rule",
|
|
6245
|
+
reason: "fused rule function \u2014 a compose()/fuse result has no combinator graph. Analyze the composed grammar itself (analyzeGrammarGating) so the carried IR is re-lowered first."
|
|
6246
|
+
} : {
|
|
6247
|
+
rule: enclosingRule,
|
|
6248
|
+
kind: "not-a-combinator",
|
|
6249
|
+
reason: `value of type ${p === null ? "null" : typeof p} is not a combinator and carries no _def descriptor.`
|
|
6250
|
+
}
|
|
6251
|
+
);
|
|
6252
|
+
return;
|
|
6253
|
+
}
|
|
6254
|
+
const d = def;
|
|
6255
|
+
const rule = ruleNameOf2(p) ?? enclosingRule;
|
|
6256
|
+
if (d.tag === "choice") {
|
|
6257
|
+
raw.push({ g: analyzeChoice(p, d, rule, opts?.resolveRef), rule, arms: d.parsers });
|
|
6258
|
+
antiPatterns.push(...detectAntiPatterns(rule, d.parsers));
|
|
6259
|
+
}
|
|
6260
|
+
const kids = [...childrenOf2(d)];
|
|
6261
|
+
if (d.tag === "lazy") {
|
|
6262
|
+
try {
|
|
6263
|
+
kids.push(d.thunk());
|
|
6264
|
+
} catch {
|
|
6265
|
+
}
|
|
6266
|
+
}
|
|
6267
|
+
for (const k of kids) visit(k, rule);
|
|
6268
|
+
};
|
|
6269
|
+
for (const [name, root] of ruleMap) visit(root, ruleNameOf2(root) ?? name);
|
|
6270
|
+
const perRule = /* @__PURE__ */ new Map();
|
|
6271
|
+
for (const r of raw) perRule.set(r.rule, (perRule.get(r.rule) ?? 0) + 1);
|
|
6272
|
+
const seenInRule = /* @__PURE__ */ new Map();
|
|
6273
|
+
const accept = new Set(opts?.accept ?? []);
|
|
6274
|
+
const usedAccept = /* @__PURE__ */ new Set();
|
|
6275
|
+
const choices = raw.map(({ g, rule, arms }) => {
|
|
6276
|
+
const n = seenInRule.get(rule) ?? 0;
|
|
6277
|
+
seenInRule.set(rule, n + 1);
|
|
6278
|
+
const id = (perRule.get(rule) ?? 1) === 1 ? rule : `${rule}#${n}`;
|
|
6279
|
+
const isAccepted = g.gates === "no" && accept.has(id);
|
|
6280
|
+
if (isAccepted) usedAccept.add(id);
|
|
6281
|
+
const out = { ...g, id, accepted: isAccepted };
|
|
6282
|
+
Object.defineProperty(out, "arms", { value: arms, enumerable: false, writable: false });
|
|
6283
|
+
return out;
|
|
6714
6284
|
});
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
const
|
|
6718
|
-
|
|
6719
|
-
|
|
6285
|
+
const gated = choices.filter((c) => c.gates === "yes").length;
|
|
6286
|
+
const recoverable = choices.filter((c) => c.gates === "recoverable").length;
|
|
6287
|
+
const ungated = choices.filter((c) => c.gates === "no" && !c.accepted && !c.deferred);
|
|
6288
|
+
const accepted = choices.filter((c) => c.gates === "no" && c.accepted);
|
|
6289
|
+
const deferred = choices.filter((c) => c.gates === "no" && !c.accepted && c.deferred);
|
|
6290
|
+
const acceptedUnused = [...accept].filter((id) => !usedAccept.has(id));
|
|
6291
|
+
return {
|
|
6292
|
+
totalChoices: choices.length,
|
|
6293
|
+
gated,
|
|
6294
|
+
recoverable,
|
|
6295
|
+
unanalysable,
|
|
6296
|
+
ungated,
|
|
6297
|
+
accepted,
|
|
6298
|
+
deferred,
|
|
6299
|
+
acceptedUnused,
|
|
6300
|
+
choices,
|
|
6301
|
+
antiPatterns
|
|
6720
6302
|
};
|
|
6721
|
-
if (spec === void 0 || !Number.isInteger(arity) || arity < 0 || fallbackRouted !== 0 && fallbackRouted !== 1 || spec.routed.length !== arity || spec.routed.some((value) => value !== 0 && value !== 1) || invalid(spec.byKey.values()) || invalid(spec.byFold.values()) || spec.match.some((matcher) => invalid([matcher[3]]))) {
|
|
6722
|
-
throw new TypeError("table: malformed dispatch");
|
|
6723
|
-
}
|
|
6724
6303
|
}
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
const
|
|
6304
|
+
function analyzeChoice(p, d, rule, resolve) {
|
|
6305
|
+
const arms = d.parsers;
|
|
6306
|
+
const shallow = arms.map((a) => a._meta.firstSet);
|
|
6307
|
+
const deep = arms.map((a) => firstSetOf(a, /* @__PURE__ */ new Set(), resolve));
|
|
6308
|
+
const anyArms = deep.map((fs, index) => ({ fs, index })).filter((x) => isAny(x.fs)).map(({ index }) => {
|
|
6309
|
+
const { cause, detail, unresolvedExternal } = classifyBroadArm(arms[index], resolve);
|
|
6310
|
+
return {
|
|
6311
|
+
index,
|
|
6312
|
+
cause,
|
|
6313
|
+
detail,
|
|
6314
|
+
shallowAnyOnly: false,
|
|
6315
|
+
unresolvedExternal: unresolvedExternal === true,
|
|
6316
|
+
suggestion: refineSuggestion(cause, arms[index])
|
|
6317
|
+
};
|
|
6318
|
+
});
|
|
6319
|
+
const overlaps = [];
|
|
6320
|
+
for (let i = 0; i < deep.length; i++)
|
|
6321
|
+
for (let j = i + 1; j < deep.length; j++)
|
|
6322
|
+
if (!isAny(deep[i]) && !isAny(deep[j]) && intersects(deep[i], deep[j]))
|
|
6323
|
+
overlaps.push({
|
|
6324
|
+
a: i,
|
|
6325
|
+
b: j,
|
|
6326
|
+
on: intersection(deep[i], deep[j]),
|
|
6327
|
+
suggestion: "Two arms of this choice can begin with the same character, so the parser cannot tell from that character which one to try. It tries them in order and undoes the ones that do not match.\nTo fix: pull the shared beginning out in front of the choice so it is matched once \u2014 sequence(shared, choice(rest\u2026)) \u2014 instead of repeating it inside each arm. parseman recognises that shape automatically and turns it back into a single test."
|
|
6328
|
+
});
|
|
6329
|
+
let gates;
|
|
6330
|
+
if (d.disjoint) gates = "yes";
|
|
6331
|
+
else if (anyArms.length === 0 && overlaps.length === 0) gates = "recoverable";
|
|
6332
|
+
else gates = "no";
|
|
6333
|
+
const deferred = gates === "no" && overlaps.length === 0 && anyArms.length > 0 && anyArms.every((a) => a.unresolvedExternal);
|
|
6728
6334
|
return {
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6335
|
+
rule,
|
|
6336
|
+
strategy: d.strategy?.tag ?? "firstMatch",
|
|
6337
|
+
gates,
|
|
6338
|
+
deferred,
|
|
6339
|
+
combinedFirstSet: { shallow: combine(shallow), deep: combine(deep) },
|
|
6340
|
+
anyArms,
|
|
6341
|
+
overlaps
|
|
6736
6342
|
};
|
|
6737
6343
|
}
|
|
6738
|
-
function
|
|
6739
|
-
|
|
6344
|
+
function formatGatingWarnings(report) {
|
|
6345
|
+
const lines2 = [];
|
|
6346
|
+
if (report.unanalysable.length > 0) {
|
|
6347
|
+
lines2.push(
|
|
6348
|
+
`parseman gating: ${report.unanalysable.length} rule(s) UNANALYSABLE \u2014 this report is PARTIAL (${report.totalChoices} choice(s) examined). An empty finding list below does NOT mean the grammar is clean.`
|
|
6349
|
+
);
|
|
6350
|
+
for (const u of report.unanalysable)
|
|
6351
|
+
lines2.push(` \xB7 ${u.rule} [${u.kind}]: ${u.reason}`);
|
|
6352
|
+
}
|
|
6353
|
+
for (const c of report.ungated) {
|
|
6354
|
+
lines2.push(`parseman gating: choice @ ${c.id} is UNGATED [${c.strategy}] \u2014 no first-char dispatch; every position speculatively enters doomed arms.`);
|
|
6355
|
+
for (const a of c.anyArms)
|
|
6356
|
+
lines2.push(` \xB7 arm[${a.index}] first-set ANY (${a.cause}): ${a.detail}
|
|
6357
|
+
fix: ${a.suggestion}`);
|
|
6358
|
+
for (const o of c.overlaps)
|
|
6359
|
+
lines2.push(` \xB7 arm[${o.a}] \u2229 arm[${o.b}] overlap on ${firstSetToString(o.on)}
|
|
6360
|
+
fix: ${o.suggestion}`);
|
|
6361
|
+
lines2.push(` (intentional? accept it in the gating snapshot: { accept: ['${c.id}'] }.)`);
|
|
6362
|
+
}
|
|
6363
|
+
for (const ap of report.antiPatterns)
|
|
6364
|
+
lines2.push(`parseman anti-pattern [${ap.kind}] @ ${ap.rule} arm[${ap.armIndex}]: ${ap.message}`);
|
|
6365
|
+
return lines2;
|
|
6740
6366
|
}
|
|
6741
|
-
function
|
|
6742
|
-
|
|
6367
|
+
function ruleDependencies(ruleMap) {
|
|
6368
|
+
const nameOf2 = /* @__PURE__ */ new Map();
|
|
6369
|
+
for (const [name, comb] of ruleMap) nameOf2.set(comb, name);
|
|
6370
|
+
const deps = /* @__PURE__ */ new Map();
|
|
6371
|
+
for (const [name, comb] of ruleMap) {
|
|
6372
|
+
const found = /* @__PURE__ */ new Set();
|
|
6373
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6374
|
+
const walk = (p, isRoot) => {
|
|
6375
|
+
const def = p._def;
|
|
6376
|
+
const boundary = def.tag === "lazy" ? nameOf2.get(p) ?? p._ruleName : nameOf2.get(p);
|
|
6377
|
+
if (!isRoot && boundary !== void 0) {
|
|
6378
|
+
found.add(boundary);
|
|
6379
|
+
return;
|
|
6380
|
+
}
|
|
6381
|
+
if (seen.has(p)) return;
|
|
6382
|
+
seen.add(p);
|
|
6383
|
+
if (def.tag === "lazy") {
|
|
6384
|
+
let resolved;
|
|
6385
|
+
try {
|
|
6386
|
+
resolved = def.thunk();
|
|
6387
|
+
} catch {
|
|
6388
|
+
return;
|
|
6389
|
+
}
|
|
6390
|
+
walk(resolved, false);
|
|
6391
|
+
return;
|
|
6392
|
+
}
|
|
6393
|
+
for (const child of childrenOf2(def)) walk(child, false);
|
|
6394
|
+
};
|
|
6395
|
+
walk(comb, true);
|
|
6396
|
+
deps.set(name, [...found]);
|
|
6397
|
+
}
|
|
6398
|
+
return deps;
|
|
6743
6399
|
}
|
|
6744
|
-
|
|
6745
|
-
|
|
6400
|
+
|
|
6401
|
+
// src/analysis/commitment.ts
|
|
6402
|
+
function mayFail(p, seen = /* @__PURE__ */ new Set()) {
|
|
6403
|
+
if (seen.has(p)) return true;
|
|
6404
|
+
seen.add(p);
|
|
6405
|
+
const d = p._def;
|
|
6406
|
+
switch (d.tag) {
|
|
6407
|
+
case "literal":
|
|
6408
|
+
return d.value.length > 0;
|
|
6409
|
+
case "trivia":
|
|
6410
|
+
return false;
|
|
6411
|
+
// `optional`/`many`/a min-0 `sepBy` swallow an ORDINARY failure — zero
|
|
6412
|
+
// repetitions is a match. They do NOT swallow a COMMITTED one: a dispatch
|
|
6413
|
+
// that has selected an arm and then failed inside it breaks out past every
|
|
6414
|
+
// enclosing repeat, straight to the fallible boundary. So these are total
|
|
6415
|
+
// exactly when their body cannot commit. (Measured: treating them as
|
|
6416
|
+
// unconditionally total left an extra zero-width leaf in
|
|
6417
|
+
// `BareInterpolatedSelector` on benchmark.less — a rollback the legacy
|
|
6418
|
+
// build fired and this one had deleted.)
|
|
6419
|
+
case "optional":
|
|
6420
|
+
case "many":
|
|
6421
|
+
return mayCommitFailure(d.parser);
|
|
6422
|
+
case "sepBy":
|
|
6423
|
+
return d.min >= 1 ? mayFail(d.parser, seen) : mayCommitFailure(d.parser) || mayCommitFailure(d.separator);
|
|
6424
|
+
case "oneOrMore":
|
|
6425
|
+
return mayFail(d.parser, seen);
|
|
6426
|
+
case "sequence":
|
|
6427
|
+
return d.parsers.some((x) => mayFail(x, seen));
|
|
6428
|
+
// Delegating wrappers: as fallible as what they wrap.
|
|
6429
|
+
case "node":
|
|
6430
|
+
case "transform":
|
|
6431
|
+
case "label":
|
|
6432
|
+
case "field":
|
|
6433
|
+
case "expect":
|
|
6434
|
+
case "withCtx":
|
|
6435
|
+
case "grammar":
|
|
6436
|
+
case "token":
|
|
6437
|
+
case "leaf":
|
|
6438
|
+
return mayFail(d.parser, seen);
|
|
6439
|
+
case "lazy": {
|
|
6440
|
+
try {
|
|
6441
|
+
return mayFail(d.thunk(), seen);
|
|
6442
|
+
} catch {
|
|
6443
|
+
return true;
|
|
6444
|
+
}
|
|
6445
|
+
}
|
|
6446
|
+
// An adjacency assertion is a TEST — failing is its whole job.
|
|
6447
|
+
case "adjacency":
|
|
6448
|
+
return true;
|
|
6449
|
+
default:
|
|
6450
|
+
return true;
|
|
6451
|
+
}
|
|
6746
6452
|
}
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6453
|
+
function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set(), triviaActive = true) {
|
|
6454
|
+
if (seen.has(p)) return false;
|
|
6455
|
+
seen.add(p);
|
|
6456
|
+
const d = p._def;
|
|
6457
|
+
switch (d.tag) {
|
|
6458
|
+
// Atomic / non-capturing-then-failing: a failure happens before any push.
|
|
6459
|
+
case "literal":
|
|
6460
|
+
case "regex":
|
|
6461
|
+
case "keywords":
|
|
6462
|
+
case "guard":
|
|
6463
|
+
// Adjacency probes with `_cap = 0` / no `commit()`: it cannot push anything.
|
|
6464
|
+
case "adjacency":
|
|
6465
|
+
case "not":
|
|
6466
|
+
// peek(): emitted under a non-capturing probe ctx and zero-width on both
|
|
6467
|
+
// outcomes, so it can never leave a partial capture behind.
|
|
6468
|
+
case "peek":
|
|
6469
|
+
case "trivia":
|
|
6470
|
+
case "token":
|
|
6471
|
+
case "leaf":
|
|
6472
|
+
case "scanTo":
|
|
6473
|
+
case "unknown":
|
|
6474
|
+
case "columns":
|
|
6475
|
+
return false;
|
|
6476
|
+
// A run's prefixes are probed detached; only its body can capture.
|
|
6477
|
+
case "lines":
|
|
6478
|
+
return mayLeavePartialCapture(d.body, seen, triviaActive);
|
|
6479
|
+
// node() captures into its own private buffers and rolls them back on
|
|
6480
|
+
// failure (emitNode restores _ctx.* and never pushes on the !ok path).
|
|
6481
|
+
case "node":
|
|
6482
|
+
return false;
|
|
6483
|
+
// choice/firstMatch already roll back each failed arm; on overall failure
|
|
6484
|
+
// nothing committed remains.
|
|
6485
|
+
case "choice":
|
|
6486
|
+
return false;
|
|
6487
|
+
case "dispatch":
|
|
6488
|
+
return capturesLeaf(d.selector, new Set(seen)) || d.cases.some((x) => mayLeavePartialCapture(x.parser, new Set(seen), triviaActive) || capturesLeaf(x.parser, new Set(seen))) || (d.matchers ? d.matchers.some((x) => mayLeavePartialCapture(x.parser, new Set(seen), triviaActive) || capturesLeaf(x.parser, new Set(seen))) : false) || (d.otherwise ? mayLeavePartialCapture(d.otherwise, new Set(seen), triviaActive) || capturesLeaf(d.otherwise, new Set(seen)) : false);
|
|
6489
|
+
case "attempt":
|
|
6490
|
+
return false;
|
|
6491
|
+
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
6492
|
+
case "optional":
|
|
6493
|
+
case "many":
|
|
6494
|
+
case "oneOrMore":
|
|
6495
|
+
return false;
|
|
6496
|
+
// sepBy emits its own per-iteration rollback in emitSepBy.
|
|
6497
|
+
case "sepBy":
|
|
6498
|
+
return false;
|
|
6499
|
+
// A sequence is the real case: an earlier capturing term followed by a term
|
|
6500
|
+
// that can fail leaves the earlier captures buffered.
|
|
6501
|
+
//
|
|
6502
|
+
// BOTH halves of that sentence are load-bearing, and the second half is what
|
|
6503
|
+
// this walk exists to exploit. "Some earlier term captures" alone was the old
|
|
6504
|
+
// test, and it is true of very nearly every sequence in a real grammar — a
|
|
6505
|
+
// sequence almost always opens with a literal or a node. The rollback is only
|
|
6506
|
+
// reachable if something AFTER the capture can still fail; once the remainder
|
|
6507
|
+
// is total (`optional`/`many`/trivia/a min-0 `sepBy`), the sequence has no
|
|
6508
|
+
// failure path left to roll back from, and the save+restore is dead code.
|
|
6509
|
+
//
|
|
6510
|
+
// `captured` tracks whether anything is in the buffers yet as the scan moves
|
|
6511
|
+
// left to right. Two things put it there:
|
|
6512
|
+
// - a term that captures (`capturesLeaf`/`hasNodeDef`), and
|
|
6513
|
+
// - the inter-term TRIVIA scan, which under `capturing` pushes into
|
|
6514
|
+
// `_cstRawChildren`/`_cstTriviaLog` BEFORE the term at each boundary
|
|
6515
|
+
// i >= 1. `emitSeqValues` only rewinds that push on the term's SUCCESS
|
|
6516
|
+
// path (the "matched empty, so leave the whitespace out of the span"
|
|
6517
|
+
// branch); a term that FAILS breaks straight out with the trivia still
|
|
6518
|
+
// buffered, so from boundary 1 onward the buffers are dirty regardless of
|
|
6519
|
+
// what the earlier terms did.
|
|
6520
|
+
// Each term is also asked about itself: a nested sequence can leave its own
|
|
6521
|
+
// partial capture behind, and its terms are emitted inline into this same
|
|
6522
|
+
// block with no boundary of their own to roll them back.
|
|
6523
|
+
case "sequence": {
|
|
6524
|
+
const parts = d.parsers;
|
|
6525
|
+
let captured = false;
|
|
6526
|
+
for (let i = 0; i < parts.length; i++) {
|
|
6527
|
+
const part = parts[i];
|
|
6528
|
+
if (i > 0 && triviaActive) captured = true;
|
|
6529
|
+
if (captured && mayFail(part)) return true;
|
|
6530
|
+
if (mayLeavePartialCapture(part, seen, triviaActive)) return true;
|
|
6531
|
+
if (capturesLeaf(part) || hasNodeDef(part)) captured = true;
|
|
6532
|
+
}
|
|
6533
|
+
return false;
|
|
6534
|
+
}
|
|
6535
|
+
// Delegating wrappers: defer to the wrapped parser.
|
|
6536
|
+
case "transform":
|
|
6537
|
+
case "label":
|
|
6538
|
+
case "field":
|
|
6539
|
+
case "expect":
|
|
6540
|
+
case "withCtx":
|
|
6541
|
+
case "grammar":
|
|
6542
|
+
return mayLeavePartialCapture(d.parser, seen, triviaActive);
|
|
6543
|
+
case "recover":
|
|
6544
|
+
return mayLeavePartialCapture(d.parser, seen, triviaActive);
|
|
6545
|
+
case "lazy": {
|
|
6546
|
+
try {
|
|
6547
|
+
return mayLeavePartialCapture(d.thunk(), seen, triviaActive);
|
|
6548
|
+
} catch {
|
|
6549
|
+
return true;
|
|
6550
|
+
}
|
|
6551
|
+
}
|
|
6552
|
+
// Unknown shapes: be safe and keep the rollback.
|
|
6553
|
+
default:
|
|
6554
|
+
return true;
|
|
6555
|
+
}
|
|
6556
|
+
}
|
|
6557
|
+
function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
6558
|
+
if (seen.has(p)) return false;
|
|
6559
|
+
seen.add(p);
|
|
6560
|
+
const d = p._def;
|
|
6561
|
+
switch (d.tag) {
|
|
6562
|
+
case "literal":
|
|
6563
|
+
case "regex":
|
|
6564
|
+
case "keywords":
|
|
6565
|
+
case "node":
|
|
6566
|
+
case "token":
|
|
6567
|
+
case "routed":
|
|
6568
|
+
case "leaf":
|
|
6569
|
+
return true;
|
|
6570
|
+
case "not":
|
|
6571
|
+
case "peek":
|
|
6572
|
+
case "guard":
|
|
6573
|
+
case "adjacency":
|
|
6574
|
+
case "trivia":
|
|
6575
|
+
case "unknown":
|
|
6576
|
+
return false;
|
|
6577
|
+
case "sequence":
|
|
6578
|
+
case "choice":
|
|
6579
|
+
return d.parsers.some((x) => capturesLeaf(x, seen));
|
|
6580
|
+
case "dispatch":
|
|
6581
|
+
return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => capturesLeaf(entry.parser, seen)) : false) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
|
|
6582
|
+
case "sepBy":
|
|
6583
|
+
return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
|
|
6584
|
+
case "many":
|
|
6585
|
+
case "oneOrMore":
|
|
6586
|
+
case "optional":
|
|
6587
|
+
case "attempt":
|
|
6588
|
+
case "transform":
|
|
6589
|
+
case "label":
|
|
6590
|
+
case "field":
|
|
6591
|
+
case "expect":
|
|
6592
|
+
case "withCtx":
|
|
6593
|
+
case "grammar":
|
|
6594
|
+
case "recover":
|
|
6595
|
+
return capturesLeaf(d.parser, seen);
|
|
6596
|
+
case "scanTo":
|
|
6597
|
+
return true;
|
|
6598
|
+
case "lines":
|
|
6599
|
+
return capturesLeaf(d.body, seen);
|
|
6600
|
+
case "columns":
|
|
6601
|
+
return true;
|
|
6602
|
+
case "lazy": {
|
|
6603
|
+
try {
|
|
6604
|
+
return capturesLeaf(d.thunk(), seen);
|
|
6605
|
+
} catch {
|
|
6606
|
+
return true;
|
|
6607
|
+
}
|
|
6753
6608
|
}
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
throw new TypeError(`table char-class range is descending: ${lo} > ${hi}`);
|
|
6609
|
+
default:
|
|
6610
|
+
return true;
|
|
6757
6611
|
}
|
|
6758
6612
|
}
|
|
6759
|
-
function
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6613
|
+
function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
6614
|
+
if (seen.has(p)) return false;
|
|
6615
|
+
seen.add(p);
|
|
6616
|
+
const d = p._def;
|
|
6617
|
+
switch (d.tag) {
|
|
6618
|
+
case "node":
|
|
6619
|
+
return true;
|
|
6620
|
+
case "lazy": {
|
|
6621
|
+
try {
|
|
6622
|
+
return hasNodeDef(d.thunk(), seen);
|
|
6623
|
+
} catch {
|
|
6624
|
+
return false;
|
|
6625
|
+
}
|
|
6626
|
+
}
|
|
6627
|
+
case "grammar":
|
|
6628
|
+
case "trivia":
|
|
6629
|
+
case "token":
|
|
6630
|
+
case "leaf":
|
|
6631
|
+
case "label":
|
|
6632
|
+
case "field":
|
|
6633
|
+
case "optional":
|
|
6634
|
+
case "many":
|
|
6635
|
+
case "oneOrMore":
|
|
6636
|
+
case "not":
|
|
6637
|
+
case "peek":
|
|
6638
|
+
case "transform":
|
|
6639
|
+
return hasNodeDef(d.parser, seen);
|
|
6640
|
+
case "sequence":
|
|
6641
|
+
case "choice":
|
|
6642
|
+
return d.parsers.some((x) => hasNodeDef(x, seen));
|
|
6643
|
+
case "dispatch":
|
|
6644
|
+
return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => hasNodeDef(entry.parser, seen)) : false) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
|
|
6645
|
+
case "sepBy":
|
|
6646
|
+
return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
|
|
6647
|
+
case "scanTo":
|
|
6648
|
+
return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen)) || d.recoverAt !== void 0 && hasNodeDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasNodeDef(d.stopAt, seen);
|
|
6649
|
+
case "lines":
|
|
6650
|
+
return hasNodeDef(d.body, seen);
|
|
6651
|
+
case "recover":
|
|
6652
|
+
return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
|
|
6653
|
+
case "expect":
|
|
6654
|
+
return hasNodeDef(d.parser, seen);
|
|
6655
|
+
case "withCtx":
|
|
6656
|
+
return hasNodeDef(d.parser, seen);
|
|
6657
|
+
default:
|
|
6658
|
+
return false;
|
|
6778
6659
|
}
|
|
6779
|
-
return spec;
|
|
6780
6660
|
}
|
|
6781
|
-
function
|
|
6782
|
-
if (
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6661
|
+
function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
6662
|
+
if (seen.has(p)) return false;
|
|
6663
|
+
seen.add(p);
|
|
6664
|
+
const d = p._def;
|
|
6665
|
+
switch (d.tag) {
|
|
6666
|
+
case "node":
|
|
6667
|
+
return d.build !== void 0 || hasDirectBuildDef(d.parser, seen);
|
|
6668
|
+
case "lazy": {
|
|
6669
|
+
try {
|
|
6670
|
+
return hasDirectBuildDef(d.thunk(), seen);
|
|
6671
|
+
} catch {
|
|
6672
|
+
return false;
|
|
6673
|
+
}
|
|
6791
6674
|
}
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6675
|
+
case "grammar":
|
|
6676
|
+
case "trivia":
|
|
6677
|
+
case "token":
|
|
6678
|
+
case "leaf":
|
|
6679
|
+
case "label":
|
|
6680
|
+
case "field":
|
|
6681
|
+
case "optional":
|
|
6682
|
+
case "many":
|
|
6683
|
+
case "oneOrMore":
|
|
6684
|
+
case "attempt":
|
|
6685
|
+
case "not":
|
|
6686
|
+
case "peek":
|
|
6687
|
+
case "withCtx":
|
|
6688
|
+
case "expect":
|
|
6689
|
+
case "transform":
|
|
6690
|
+
return hasDirectBuildDef(d.parser, seen);
|
|
6691
|
+
case "sequence":
|
|
6692
|
+
case "choice":
|
|
6693
|
+
return d.parsers.some((x) => hasDirectBuildDef(x, seen));
|
|
6694
|
+
case "dispatch":
|
|
6695
|
+
return hasDirectBuildDef(d.selector, seen) || d.cases.some((x) => hasDirectBuildDef(x.parser, seen)) || (d.matchers ? d.matchers.some((x) => hasDirectBuildDef(x.parser, seen)) : false) || (d.otherwise ? hasDirectBuildDef(d.otherwise, seen) : false);
|
|
6696
|
+
case "sepBy":
|
|
6697
|
+
return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.separator, seen);
|
|
6698
|
+
case "scanTo":
|
|
6699
|
+
return hasDirectBuildDef(d.sentinel, seen) || d.skip.some((x) => hasDirectBuildDef(x, seen)) || d.recoverAt !== void 0 && hasDirectBuildDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasDirectBuildDef(d.stopAt, seen);
|
|
6700
|
+
case "lines":
|
|
6701
|
+
return hasDirectBuildDef(d.body, seen);
|
|
6702
|
+
case "recover":
|
|
6703
|
+
return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.sentinel, seen);
|
|
6704
|
+
case "routed":
|
|
6705
|
+
return d.fallback ? hasDirectBuildDef(d.fallback, seen) : false;
|
|
6706
|
+
default:
|
|
6707
|
+
return false;
|
|
6801
6708
|
}
|
|
6802
|
-
return ranges;
|
|
6803
6709
|
}
|
|
6804
|
-
function
|
|
6805
|
-
|
|
6806
|
-
const hi = [];
|
|
6807
|
-
for (const { lo, hi: up } of decodeClassSpec(spec)) {
|
|
6808
|
-
if (lo < 128) for (let c = lo; c <= Math.min(up, 127); c++) ascii[c] = 1;
|
|
6809
|
-
if (up >= 128) hi.push(Math.max(lo, 128), up);
|
|
6810
|
-
}
|
|
6811
|
-
return { ascii, hi };
|
|
6710
|
+
function mayCommitFailure(p, resolve, memo = /* @__PURE__ */ new Map()) {
|
|
6711
|
+
return reachesMarked(p, (node2) => commitFailureSuccessors(node2, resolve), memo);
|
|
6812
6712
|
}
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6713
|
+
var NO_EDGES = [];
|
|
6714
|
+
function commitFailureSuccessors(p, resolve) {
|
|
6715
|
+
const d = p._def;
|
|
6716
|
+
switch (d.tag) {
|
|
6717
|
+
case "dispatch":
|
|
6718
|
+
case "unknown":
|
|
6719
|
+
return true;
|
|
6720
|
+
case "choice":
|
|
6721
|
+
case "sequence":
|
|
6722
|
+
return d.parsers;
|
|
6723
|
+
// A contained transaction reports every failure uncommitted.
|
|
6724
|
+
case "attempt":
|
|
6725
|
+
return d.contain === true ? NO_EDGES : [d.parser];
|
|
6726
|
+
case "many":
|
|
6727
|
+
case "oneOrMore":
|
|
6728
|
+
case "optional":
|
|
6729
|
+
case "transform":
|
|
6730
|
+
case "label":
|
|
6731
|
+
case "field":
|
|
6732
|
+
case "grammar":
|
|
6733
|
+
case "node":
|
|
6734
|
+
case "token":
|
|
6735
|
+
case "leaf":
|
|
6736
|
+
case "withCtx":
|
|
6737
|
+
return [d.parser];
|
|
6738
|
+
case "sepBy":
|
|
6739
|
+
return [d.parser, d.separator];
|
|
6740
|
+
case "scanTo":
|
|
6741
|
+
return [
|
|
6742
|
+
d.sentinel,
|
|
6743
|
+
...d.skip,
|
|
6744
|
+
...d.recoverAt === void 0 ? NO_EDGES : [d.recoverAt],
|
|
6745
|
+
...d.stopAt === void 0 ? NO_EDGES : [d.stopAt]
|
|
6746
|
+
];
|
|
6747
|
+
// A run's prefixes are probed detached; a committed failure reaches it only from its body.
|
|
6748
|
+
case "lines":
|
|
6749
|
+
return [d.body];
|
|
6750
|
+
case "lazy": {
|
|
6751
|
+
try {
|
|
6752
|
+
return [d.thunk()];
|
|
6753
|
+
} catch {
|
|
6754
|
+
const name = p._ruleName;
|
|
6755
|
+
const target = name === void 0 ? void 0 : resolve?.(name);
|
|
6756
|
+
return target === void 0 ? true : [target];
|
|
6757
|
+
}
|
|
6758
|
+
}
|
|
6759
|
+
default:
|
|
6760
|
+
return NO_EDGES;
|
|
6761
|
+
}
|
|
6819
6762
|
}
|
|
6820
|
-
function
|
|
6821
|
-
const
|
|
6822
|
-
|
|
6823
|
-
const
|
|
6824
|
-
const
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
const
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6763
|
+
function reachesMarked(root, successors, memo) {
|
|
6764
|
+
const known = memo.get(root);
|
|
6765
|
+
if (known !== void 0) return known;
|
|
6766
|
+
const order = /* @__PURE__ */ new Map();
|
|
6767
|
+
const low = [];
|
|
6768
|
+
const found = [];
|
|
6769
|
+
const stack = [];
|
|
6770
|
+
const visit = (node2) => {
|
|
6771
|
+
const at = low.length;
|
|
6772
|
+
order.set(node2, at);
|
|
6773
|
+
low.push(at);
|
|
6774
|
+
found.push(false);
|
|
6775
|
+
stack.push(node2);
|
|
6776
|
+
const next = successors(node2);
|
|
6777
|
+
if (next === true) found[at] = true;
|
|
6778
|
+
else {
|
|
6779
|
+
for (const child of next) {
|
|
6780
|
+
if (found[at]) break;
|
|
6781
|
+
const hit = memo.get(child);
|
|
6782
|
+
if (hit !== void 0) {
|
|
6783
|
+
if (hit) found[at] = true;
|
|
6784
|
+
continue;
|
|
6785
|
+
}
|
|
6786
|
+
const seen = order.get(child);
|
|
6787
|
+
if (seen !== void 0) {
|
|
6788
|
+
if (seen < low[at]) low[at] = seen;
|
|
6789
|
+
continue;
|
|
6790
|
+
}
|
|
6791
|
+
const k = visit(child);
|
|
6792
|
+
if (low[k] < low[at]) low[at] = low[k];
|
|
6793
|
+
if (found[k]) found[at] = true;
|
|
6794
|
+
}
|
|
6834
6795
|
}
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6796
|
+
if (low[at] === at) {
|
|
6797
|
+
const answer = found[at];
|
|
6798
|
+
let member;
|
|
6799
|
+
do {
|
|
6800
|
+
member = stack.pop();
|
|
6801
|
+
memo.set(member, answer);
|
|
6802
|
+
} while (member !== node2);
|
|
6841
6803
|
}
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
return { ascii, hi, open, armCls, exclusive };
|
|
6804
|
+
return at;
|
|
6805
|
+
};
|
|
6806
|
+
return found[visit(root)];
|
|
6846
6807
|
}
|
|
6847
|
-
function
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6808
|
+
function hasSemanticReduction(roots, externalRefs) {
|
|
6809
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6810
|
+
const visit = (parser2) => {
|
|
6811
|
+
if (seen.has(parser2)) return false;
|
|
6812
|
+
seen.add(parser2);
|
|
6813
|
+
const def = parser2._def;
|
|
6814
|
+
if (def.tag === "transform" && !def.recognitionOnly) return true;
|
|
6815
|
+
if (def.tag === "choice" && def.gates.some(Boolean)) return true;
|
|
6816
|
+
if (def.tag === "guard" || def.tag === "withCtx") return true;
|
|
6817
|
+
if (def.tag === "node" && def.build !== void 0) return true;
|
|
6818
|
+
if (def.tag === "lazy") {
|
|
6819
|
+
if (externalRefs?.has(parser2)) return false;
|
|
6820
|
+
try {
|
|
6821
|
+
return visit(def.thunk());
|
|
6822
|
+
} catch {
|
|
6823
|
+
return true;
|
|
6824
|
+
}
|
|
6825
|
+
}
|
|
6826
|
+
return childrenOf2(def).some(visit);
|
|
6827
|
+
};
|
|
6828
|
+
return roots.some(visit);
|
|
6859
6829
|
}
|
|
6860
|
-
function
|
|
6861
|
-
const
|
|
6862
|
-
const
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
rules: prog.rules
|
|
6830
|
+
function classifyRuleMap(ruleMap) {
|
|
6831
|
+
const externalRefs = /* @__PURE__ */ new Set();
|
|
6832
|
+
const scanned = /* @__PURE__ */ new Set();
|
|
6833
|
+
const scanExternal = (p) => {
|
|
6834
|
+
if (scanned.has(p)) return;
|
|
6835
|
+
scanned.add(p);
|
|
6836
|
+
const def = p._def;
|
|
6837
|
+
if (def.tag === "lazy") {
|
|
6838
|
+
let resolved;
|
|
6839
|
+
try {
|
|
6840
|
+
resolved = def.thunk();
|
|
6841
|
+
} catch {
|
|
6842
|
+
resolved = void 0;
|
|
6843
|
+
}
|
|
6844
|
+
if (resolved === void 0) {
|
|
6845
|
+
if (p._ruleName) externalRefs.add(p);
|
|
6846
|
+
return;
|
|
6847
|
+
}
|
|
6848
|
+
scanExternal(resolved);
|
|
6849
|
+
return;
|
|
6850
|
+
}
|
|
6851
|
+
for (const child of childrenOf2(def)) scanExternal(child);
|
|
6852
|
+
};
|
|
6853
|
+
for (const [, rule] of ruleMap) scanExternal(rule);
|
|
6854
|
+
return {
|
|
6855
|
+
hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
6856
|
+
isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs)
|
|
6888
6857
|
};
|
|
6889
|
-
if (owner !== void 0) owner.resolved = built;
|
|
6890
|
-
return built;
|
|
6891
6858
|
}
|
|
6892
6859
|
|
|
6860
|
+
// src/table/ops.ts
|
|
6861
|
+
var OP_LIT = 1;
|
|
6862
|
+
var OP_RX = 2;
|
|
6863
|
+
var OP_SEQ = 3;
|
|
6864
|
+
var OP_SEQV = 4;
|
|
6865
|
+
var OP_CHOICE = 5;
|
|
6866
|
+
var OP_REP = 6;
|
|
6867
|
+
var OP_REPV = 7;
|
|
6868
|
+
var OP_OPT = 8;
|
|
6869
|
+
var OP_XFORM = 9;
|
|
6870
|
+
var OP_NODE = 10;
|
|
6871
|
+
var OP_RULE = 11;
|
|
6872
|
+
var OP_GATE = 12;
|
|
6873
|
+
var OP_NOT = 13;
|
|
6874
|
+
var OP_PEEK = 14;
|
|
6875
|
+
var OP_LEAF = 15;
|
|
6876
|
+
var OP_EMPTY = 16;
|
|
6877
|
+
var OP_LIT_TRACK = 17;
|
|
6878
|
+
var OP_RX_TRACK = 18;
|
|
6879
|
+
var OP_NODE_TRACK = 19;
|
|
6880
|
+
var OP_SCOPE = 20;
|
|
6881
|
+
var OP_EXPECT = 21;
|
|
6882
|
+
var OP_SEQX = 22;
|
|
6883
|
+
var OP_SCAN = 23;
|
|
6884
|
+
var OP_FIELD = 24;
|
|
6885
|
+
var OP_LIT_CI = 27;
|
|
6886
|
+
var OP_LIT_CI_TRACK = 28;
|
|
6887
|
+
var OP_TOKEN = 29;
|
|
6888
|
+
var OP_SCOPE_CAP = 30;
|
|
6889
|
+
var OP_WITHCTX = 31;
|
|
6890
|
+
var OP_GUARD = 32;
|
|
6891
|
+
var OP_ADJ = 33;
|
|
6892
|
+
var OP_GREEDY = 34;
|
|
6893
|
+
var OP_REJECT = 35;
|
|
6894
|
+
var OP_ARMGATE = 36;
|
|
6895
|
+
var OP_LIVE = 37;
|
|
6896
|
+
var OP_ATTEMPT = 38;
|
|
6897
|
+
var OP_LABEL = 39;
|
|
6898
|
+
var OP_COV = 40;
|
|
6899
|
+
var OP_SCOPE_PLAIN = 41;
|
|
6900
|
+
var OP_LEX_BODY = 42;
|
|
6901
|
+
var OP_LEX_PROGRAM = 43;
|
|
6902
|
+
var OP_SOURCE_LEAF = 44;
|
|
6903
|
+
var OP_DISPATCH = 25;
|
|
6904
|
+
var OP_ROUTED = 26;
|
|
6905
|
+
var OP_NAMES = {
|
|
6906
|
+
[OP_LIT]: "LIT",
|
|
6907
|
+
[OP_RX]: "RX",
|
|
6908
|
+
[OP_SEQ]: "SEQ",
|
|
6909
|
+
[OP_SEQV]: "SEQV",
|
|
6910
|
+
[OP_CHOICE]: "CHOICE",
|
|
6911
|
+
[OP_REP]: "REP",
|
|
6912
|
+
[OP_REPV]: "REPV",
|
|
6913
|
+
[OP_OPT]: "OPT",
|
|
6914
|
+
[OP_XFORM]: "XFORM",
|
|
6915
|
+
[OP_NODE]: "NODE",
|
|
6916
|
+
[OP_RULE]: "RULE",
|
|
6917
|
+
[OP_GATE]: "GATE",
|
|
6918
|
+
[OP_NOT]: "NOT",
|
|
6919
|
+
[OP_PEEK]: "PEEK",
|
|
6920
|
+
[OP_LEAF]: "LEAF",
|
|
6921
|
+
[OP_SOURCE_LEAF]: "SOURCE_LEAF",
|
|
6922
|
+
[OP_EMPTY]: "EMPTY",
|
|
6923
|
+
[OP_LIT_TRACK]: "LIT_TRACK",
|
|
6924
|
+
[OP_RX_TRACK]: "RX_TRACK",
|
|
6925
|
+
[OP_NODE_TRACK]: "NODE_TRACK",
|
|
6926
|
+
[OP_SCOPE]: "SCOPE",
|
|
6927
|
+
[OP_EXPECT]: "EXPECT",
|
|
6928
|
+
[OP_SEQX]: "SEQX",
|
|
6929
|
+
[OP_SCAN]: "SCAN",
|
|
6930
|
+
[OP_FIELD]: "FIELD",
|
|
6931
|
+
[OP_LIT_CI]: "LIT_CI",
|
|
6932
|
+
[OP_LIT_CI_TRACK]: "LIT_CI_TRACK",
|
|
6933
|
+
[OP_DISPATCH]: "DISPATCH",
|
|
6934
|
+
[OP_ROUTED]: "ROUTED",
|
|
6935
|
+
[OP_TOKEN]: "TOKEN",
|
|
6936
|
+
[OP_SCOPE_CAP]: "SCOPE_CAP",
|
|
6937
|
+
[OP_WITHCTX]: "WITHCTX",
|
|
6938
|
+
[OP_GUARD]: "GUARD",
|
|
6939
|
+
[OP_ADJ]: "ADJ",
|
|
6940
|
+
[OP_GREEDY]: "GREEDY",
|
|
6941
|
+
[OP_REJECT]: "REJECT",
|
|
6942
|
+
[OP_ARMGATE]: "ARMGATE",
|
|
6943
|
+
[OP_LIVE]: "LIVE",
|
|
6944
|
+
[OP_ATTEMPT]: "ATTEMPT",
|
|
6945
|
+
[OP_LABEL]: "LABEL",
|
|
6946
|
+
[OP_COV]: "COV",
|
|
6947
|
+
[OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
|
|
6948
|
+
[OP_LEX_BODY]: "LEX_BODY",
|
|
6949
|
+
[OP_LEX_PROGRAM]: "LEX_PROGRAM"
|
|
6950
|
+
};
|
|
6951
|
+
|
|
6893
6952
|
// src/compiler/direct-projection.ts
|
|
6894
6953
|
function directArrayProjection(fn, fnSrc, arity) {
|
|
6895
6954
|
let source = fnSrc;
|
|
@@ -7770,6 +7829,7 @@ var Encoder = class {
|
|
|
7770
7829
|
case "expect":
|
|
7771
7830
|
case "recover":
|
|
7772
7831
|
case "scanTo":
|
|
7832
|
+
case "lines":
|
|
7773
7833
|
case "unknown":
|
|
7774
7834
|
return true;
|
|
7775
7835
|
case "lazy": {
|
|
@@ -7990,6 +8050,18 @@ var Encoder = class {
|
|
|
7990
8050
|
open: bal.open,
|
|
7991
8051
|
close: bal.close
|
|
7992
8052
|
}));
|
|
8053
|
+
if (d.tag === "lines") {
|
|
8054
|
+
return this.emit(OP_SCAN, this.scanSlot({
|
|
8055
|
+
kind: 2,
|
|
8056
|
+
flags: 0,
|
|
8057
|
+
skip: [],
|
|
8058
|
+
prefix: this.subtree(d.prefix),
|
|
8059
|
+
body: this.subtree(d.body),
|
|
8060
|
+
...d.first === void 0 ? {} : { first: this.subtree(d.first) },
|
|
8061
|
+
...d.lazy === void 0 ? {} : { lazy: this.subtree(d.lazy) }
|
|
8062
|
+
}));
|
|
8063
|
+
}
|
|
8064
|
+
if (d.tag === "columns") return this.emit(OP_SCAN, this.scanSlot({ kind: 3, flags: 0, skip: [], n: d.n }));
|
|
7993
8065
|
if (d.tag === "scanTo") {
|
|
7994
8066
|
const sentDef = d.sentinel._def;
|
|
7995
8067
|
return this.emit(OP_SCAN, this.scanSlot({
|
|
@@ -8436,12 +8508,7 @@ var Encoder = class {
|
|
|
8436
8508
|
};
|
|
8437
8509
|
const seen = /* @__PURE__ */ new Set();
|
|
8438
8510
|
const refs = [];
|
|
8439
|
-
for (const s of this.scans)
|
|
8440
|
-
if (s.sentinel !== void 0) refs.push(s.sentinel);
|
|
8441
|
-
if (s.recoverAt !== void 0) refs.push(s.recoverAt);
|
|
8442
|
-
if (s.stopAt !== void 0) refs.push(s.stopAt);
|
|
8443
|
-
refs.push(...s.skip);
|
|
8444
|
-
}
|
|
8511
|
+
for (const s of this.scans) refs.push(...scanRefs(s));
|
|
8445
8512
|
for (const set of this.scanSkipSets) refs.push(...set);
|
|
8446
8513
|
const stack = [...Object.values(this.rules), ...refs.map((r) => r[0])];
|
|
8447
8514
|
while (stack.length > 0) {
|
|
@@ -8461,7 +8528,11 @@ var Encoder = class {
|
|
|
8461
8528
|
skip: s.skip.map(res),
|
|
8462
8529
|
...s.sentinel === void 0 ? {} : { sentinel: res(s.sentinel) },
|
|
8463
8530
|
...s.recoverAt === void 0 ? {} : { recoverAt: res(s.recoverAt) },
|
|
8464
|
-
...s.stopAt === void 0 ? {} : { stopAt: res(s.stopAt) }
|
|
8531
|
+
...s.stopAt === void 0 ? {} : { stopAt: res(s.stopAt) },
|
|
8532
|
+
...s.prefix === void 0 ? {} : { prefix: res(s.prefix) },
|
|
8533
|
+
...s.body === void 0 ? {} : { body: res(s.body) },
|
|
8534
|
+
...s.first === void 0 ? {} : { first: res(s.first) },
|
|
8535
|
+
...s.lazy === void 0 ? {} : { lazy: res(s.lazy) }
|
|
8465
8536
|
}));
|
|
8466
8537
|
this.scanSkipSets = this.scanSkipSets.map((set) => set.map(res));
|
|
8467
8538
|
}
|
|
@@ -9306,7 +9377,7 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
|
|
|
9306
9377
|
if (shape.kind === "alt") {
|
|
9307
9378
|
const okV = mint("_ok");
|
|
9308
9379
|
const endV2 = mint("_end");
|
|
9309
|
-
const
|
|
9380
|
+
const lines2 = [`${ind}let ${okV} = false`, `${ind}let ${endV2} = ${start}`];
|
|
9310
9381
|
if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
|
|
9311
9382
|
const groups = /* @__PURE__ */ new Map();
|
|
9312
9383
|
for (const a of shape.arms) {
|
|
@@ -9315,60 +9386,60 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
|
|
|
9315
9386
|
const lower = f >= 65 && f <= 90 ? f + 32 : f;
|
|
9316
9387
|
(groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
|
|
9317
9388
|
}
|
|
9318
|
-
|
|
9389
|
+
lines2.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
9319
9390
|
for (const [lower, arms] of groups) {
|
|
9320
9391
|
const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
|
|
9321
|
-
|
|
9392
|
+
lines2.push(`${ind} ${labels} {`);
|
|
9322
9393
|
for (const open of arms) {
|
|
9323
9394
|
const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
|
|
9324
9395
|
const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
|
|
9325
|
-
|
|
9396
|
+
lines2.push(`${ind} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
|
|
9326
9397
|
}
|
|
9327
|
-
|
|
9328
|
-
|
|
9398
|
+
lines2.push(`${ind} break`);
|
|
9399
|
+
lines2.push(`${ind} }`);
|
|
9329
9400
|
}
|
|
9330
|
-
|
|
9331
|
-
return { setup:
|
|
9401
|
+
lines2.push(`${ind}}`);
|
|
9402
|
+
return { setup: lines2, ok: okV, end: endV2 };
|
|
9332
9403
|
}
|
|
9333
9404
|
if (shape.disjoint) {
|
|
9334
9405
|
const plan = planAltSwitch(shape.firsts);
|
|
9335
9406
|
if (plan.kind === "switch") {
|
|
9336
|
-
|
|
9407
|
+
lines2.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
|
|
9337
9408
|
shape.arms.forEach((arm, k) => {
|
|
9338
9409
|
const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
|
|
9339
9410
|
const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9411
|
+
lines2.push(`${ind} ${labels} {`);
|
|
9412
|
+
lines2.push(...m.setup);
|
|
9413
|
+
lines2.push(`${ind} ${okV} = ${m.ok}`);
|
|
9414
|
+
lines2.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
9415
|
+
lines2.push(`${ind} break`);
|
|
9416
|
+
lines2.push(`${ind} }`);
|
|
9346
9417
|
});
|
|
9347
|
-
|
|
9418
|
+
lines2.push(`${ind}}`);
|
|
9348
9419
|
} else {
|
|
9349
9420
|
const c0 = codeAt(start, firstChar);
|
|
9350
9421
|
shape.arms.forEach((arm, k) => {
|
|
9351
9422
|
const fs = shape.firsts[k];
|
|
9352
9423
|
const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
|
|
9353
9424
|
const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9425
|
+
lines2.push(`${ind}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
9426
|
+
lines2.push(...m.setup);
|
|
9427
|
+
lines2.push(`${ind} ${okV} = ${m.ok}`);
|
|
9428
|
+
lines2.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
9429
|
+
lines2.push(`${ind}}`);
|
|
9359
9430
|
});
|
|
9360
9431
|
}
|
|
9361
9432
|
} else {
|
|
9362
9433
|
const lbl = mint("_alt");
|
|
9363
|
-
|
|
9434
|
+
lines2.push(`${ind}${lbl}: {`);
|
|
9364
9435
|
for (const arm of shape.arms) {
|
|
9365
9436
|
const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
|
|
9366
|
-
|
|
9367
|
-
|
|
9437
|
+
lines2.push(...m.setup);
|
|
9438
|
+
lines2.push(`${ind} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
|
|
9368
9439
|
}
|
|
9369
|
-
|
|
9440
|
+
lines2.push(`${ind}}`);
|
|
9370
9441
|
}
|
|
9371
|
-
return { setup:
|
|
9442
|
+
return { setup: lines2, ok: okV, end: endV2 };
|
|
9372
9443
|
}
|
|
9373
9444
|
if (shape.kind === "chars") {
|
|
9374
9445
|
const cur = mint("_e");
|
|
@@ -9403,64 +9474,64 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
|
|
|
9403
9474
|
const c = classCond(`input.charCodeAt(${at})`, p.ranges);
|
|
9404
9475
|
return p.negated ? `!(${c})` : `(${c})`;
|
|
9405
9476
|
};
|
|
9406
|
-
const
|
|
9477
|
+
const lines2 = [`${ind}let ${endV2} = ${start}`, `${ind}let ${okV} = false`, `${ind}do {`];
|
|
9407
9478
|
let first = true;
|
|
9408
9479
|
for (const p of shape.parts) {
|
|
9409
9480
|
if (p.part === "lit") {
|
|
9410
9481
|
const L = p.cps.length;
|
|
9411
9482
|
const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
|
|
9412
9483
|
if (p.optional) {
|
|
9413
|
-
|
|
9484
|
+
lines2.push(`${ind} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
|
|
9414
9485
|
} else {
|
|
9415
|
-
|
|
9416
|
-
|
|
9486
|
+
lines2.push(`${ind} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
|
|
9487
|
+
lines2.push(`${ind} ${endV2} += ${L}`);
|
|
9417
9488
|
}
|
|
9418
9489
|
} else if (p.part === "run" && p.max === Infinity) {
|
|
9419
9490
|
const s = mint("_s");
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
if (p.min === 1)
|
|
9423
|
-
else if (p.min > 1)
|
|
9491
|
+
lines2.push(`${ind} const ${s} = ${endV2}`);
|
|
9492
|
+
lines2.push(`${ind} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
9493
|
+
if (p.min === 1) lines2.push(`${ind} if (${endV2} === ${s}) break`);
|
|
9494
|
+
else if (p.min > 1) lines2.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
|
|
9424
9495
|
} else if (p.part === "run" && p.max === 1) {
|
|
9425
9496
|
if (p.min === 1) {
|
|
9426
|
-
|
|
9427
|
-
|
|
9497
|
+
lines2.push(`${ind} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
|
|
9498
|
+
lines2.push(`${ind} ${endV2}++`);
|
|
9428
9499
|
} else {
|
|
9429
|
-
|
|
9500
|
+
lines2.push(`${ind} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
9430
9501
|
}
|
|
9431
9502
|
} else if (p.part === "run") {
|
|
9432
9503
|
const s = mint("_s");
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
if (p.min > 0)
|
|
9504
|
+
lines2.push(`${ind} const ${s} = ${endV2}`);
|
|
9505
|
+
lines2.push(`${ind} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
9506
|
+
if (p.min > 0) lines2.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
|
|
9436
9507
|
} else {
|
|
9437
9508
|
const inner = emitShapeMatch(p.inner, endV2, mint, `${ind} `, first ? firstChar : void 0);
|
|
9438
9509
|
if (!p.unbounded) {
|
|
9439
|
-
|
|
9510
|
+
lines2.push(...inner.setup);
|
|
9440
9511
|
if (p.min === 1) {
|
|
9441
|
-
|
|
9442
|
-
|
|
9512
|
+
lines2.push(`${ind} if (!(${inner.ok})) break`);
|
|
9513
|
+
lines2.push(`${ind} ${endV2} = ${inner.end}`);
|
|
9443
9514
|
} else {
|
|
9444
|
-
|
|
9515
|
+
lines2.push(`${ind} if (${inner.ok}) ${endV2} = ${inner.end}`);
|
|
9445
9516
|
}
|
|
9446
9517
|
} else {
|
|
9447
9518
|
const progV = mint("_prog");
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
if (p.min === 1)
|
|
9519
|
+
lines2.push(`${ind} let ${progV} = false`);
|
|
9520
|
+
lines2.push(`${ind} while (true) {`);
|
|
9521
|
+
lines2.push(...inner.setup);
|
|
9522
|
+
lines2.push(`${ind} if (!(${inner.ok})) break`);
|
|
9523
|
+
lines2.push(`${ind} ${endV2} = ${inner.end}`);
|
|
9524
|
+
lines2.push(`${ind} ${progV} = true`);
|
|
9525
|
+
lines2.push(`${ind} }`);
|
|
9526
|
+
if (p.min === 1) lines2.push(`${ind} if (!${progV}) break`);
|
|
9456
9527
|
}
|
|
9457
9528
|
}
|
|
9458
9529
|
first = false;
|
|
9459
9530
|
}
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
|
|
9463
|
-
return { setup:
|
|
9531
|
+
lines2.push(`${ind} ${okV} = true`);
|
|
9532
|
+
lines2.push(`${ind}} while (false)`);
|
|
9533
|
+
lines2.push(`${ind}if (!${okV}) ${endV2} = ${start}`);
|
|
9534
|
+
return { setup: lines2, ok: okV, end: endV2 };
|
|
9464
9535
|
}
|
|
9465
9536
|
if (shape.kind === "litFold") {
|
|
9466
9537
|
const openLen2 = shape.open.length;
|
|
@@ -11737,6 +11808,188 @@ finish:_finish
|
|
|
11737
11808
|
};
|
|
11738
11809
|
}
|
|
11739
11810
|
|
|
11811
|
+
// src/combinators/lines.ts
|
|
11812
|
+
function lineIndexAt(view, offset) {
|
|
11813
|
+
const lines2 = view.lines;
|
|
11814
|
+
let lo = 0, hi = lines2.length - 1;
|
|
11815
|
+
while (lo < hi) {
|
|
11816
|
+
const mid = lo + hi + 1 >> 1;
|
|
11817
|
+
if (lines2[mid].offset <= offset) lo = mid;
|
|
11818
|
+
else hi = mid - 1;
|
|
11819
|
+
}
|
|
11820
|
+
return lo;
|
|
11821
|
+
}
|
|
11822
|
+
var lineAt = (view, offset) => view.lines[lineIndexAt(view, offset)];
|
|
11823
|
+
function sourceOffset(view, offset) {
|
|
11824
|
+
const line = lineAt(view, offset);
|
|
11825
|
+
return line === void 0 ? offset : line.source + (offset - line.offset);
|
|
11826
|
+
}
|
|
11827
|
+
function columnAt(input, pos, ctx) {
|
|
11828
|
+
const view = ctx.lineView;
|
|
11829
|
+
let start, column, split;
|
|
11830
|
+
const line = view === void 0 ? void 0 : lineAt(view, pos);
|
|
11831
|
+
if (line !== void 0 && pos >= line.offset) {
|
|
11832
|
+
start = line.offset;
|
|
11833
|
+
column = line.column;
|
|
11834
|
+
split = line.split;
|
|
11835
|
+
} else {
|
|
11836
|
+
start = input.lastIndexOf("\n", pos - 1) + 1;
|
|
11837
|
+
column = 0;
|
|
11838
|
+
split = 0;
|
|
11839
|
+
}
|
|
11840
|
+
for (let i = start; i < pos; i++) {
|
|
11841
|
+
if (input.charCodeAt(i) === 9) {
|
|
11842
|
+
const width = 4 - (column - (i === start ? split : 0)) % 4 - (i === start ? split : 0);
|
|
11843
|
+
column += width;
|
|
11844
|
+
} else column++;
|
|
11845
|
+
split = 0;
|
|
11846
|
+
}
|
|
11847
|
+
return { column, split: pos === start ? split : 0 };
|
|
11848
|
+
}
|
|
11849
|
+
function columns(n) {
|
|
11850
|
+
if (!Number.isInteger(n) || n < 0) throw new TypeError(`columns(n) needs a non-negative integer, got ${n}`);
|
|
11851
|
+
const expected = [`columns(${n})`];
|
|
11852
|
+
const meta = {
|
|
11853
|
+
firstSet: n === 0 ? any() : { kind: "ranges", ranges: [{ lo: 9, hi: 9 }, { lo: 32, hi: 32 }] },
|
|
11854
|
+
canMatchNewline: false,
|
|
11855
|
+
isTrivia: false
|
|
11856
|
+
};
|
|
11857
|
+
return {
|
|
11858
|
+
_tag: "columns",
|
|
11859
|
+
_meta: meta,
|
|
11860
|
+
_def: { tag: "columns", n },
|
|
11861
|
+
parse(input, pos, ctx) {
|
|
11862
|
+
const c = ctx;
|
|
11863
|
+
let { column, split } = columnAt(input, pos, c);
|
|
11864
|
+
const goal = column + n;
|
|
11865
|
+
let at = pos;
|
|
11866
|
+
while (column < goal) {
|
|
11867
|
+
const code = input.charCodeAt(at);
|
|
11868
|
+
if (code === 32) {
|
|
11869
|
+
column++;
|
|
11870
|
+
split = 0;
|
|
11871
|
+
at++;
|
|
11872
|
+
continue;
|
|
11873
|
+
}
|
|
11874
|
+
if (code !== 9) return { ok: false, expected, span: { start: at, end: at } };
|
|
11875
|
+
const width = 4 - (column - split) % 4 - split;
|
|
11876
|
+
if (column + width > goal) {
|
|
11877
|
+
c._columnsSplit = { at, split: split + (goal - column) };
|
|
11878
|
+
return { ok: true, value: input.slice(pos, at), span: { start: pos, end: at } };
|
|
11879
|
+
}
|
|
11880
|
+
column += width;
|
|
11881
|
+
split = 0;
|
|
11882
|
+
at++;
|
|
11883
|
+
}
|
|
11884
|
+
return { ok: true, value: input.slice(pos, at), span: { start: pos, end: at } };
|
|
11885
|
+
}
|
|
11886
|
+
};
|
|
11887
|
+
}
|
|
11888
|
+
function lines(prefix, body, options = {}) {
|
|
11889
|
+
const { first, lazy } = options;
|
|
11890
|
+
const opening = first ?? prefix;
|
|
11891
|
+
const meta = {
|
|
11892
|
+
// An opening that matches empty starts a run at any character.
|
|
11893
|
+
firstSet: matchesEmpty(opening) ? any() : opening._meta.firstSet,
|
|
11894
|
+
canMatchNewline: true,
|
|
11895
|
+
isTrivia: false
|
|
11896
|
+
};
|
|
11897
|
+
return {
|
|
11898
|
+
_tag: "lines",
|
|
11899
|
+
_meta: meta,
|
|
11900
|
+
_def: { tag: "lines", prefix, body, ...first === void 0 ? {} : { first }, ...lazy === void 0 ? {} : { lazy } },
|
|
11901
|
+
parse(input, pos, ctx) {
|
|
11902
|
+
const c = ctx;
|
|
11903
|
+
const probe = createDetachedParseContext(false, ctx.state);
|
|
11904
|
+
probe.lineView = c.lineView;
|
|
11905
|
+
const run3 = [];
|
|
11906
|
+
const parts = [];
|
|
11907
|
+
let offset = 0;
|
|
11908
|
+
for (let start = pos; start < input.length || start === pos; ) {
|
|
11909
|
+
let end2 = input.indexOf("\n", start);
|
|
11910
|
+
if (end2 < 0) end2 = input.length;
|
|
11911
|
+
probe._columnsSplit = void 0;
|
|
11912
|
+
const head = (run3.length === 0 ? opening : prefix).parse(input, start, probe);
|
|
11913
|
+
let content;
|
|
11914
|
+
let joined;
|
|
11915
|
+
if (head.ok && head.span.end <= end2) {
|
|
11916
|
+
content = head.span.end;
|
|
11917
|
+
joined = "prefix";
|
|
11918
|
+
} else if (run3.length === 0) {
|
|
11919
|
+
return { ok: false, expected: head.ok ? ["line prefix"] : head.expected, span: { start, end: start } };
|
|
11920
|
+
} else if (lazy !== void 0 && lazy.parse(input, start, probe).ok) {
|
|
11921
|
+
content = start;
|
|
11922
|
+
joined = "lazy";
|
|
11923
|
+
} else break;
|
|
11924
|
+
const mark = probe._columnsSplit;
|
|
11925
|
+
const here = columnAt(input, content, c);
|
|
11926
|
+
const split = joined === "prefix" && mark !== void 0 && mark.at === content ? mark.split : here.split;
|
|
11927
|
+
run3.push({ offset, source: content, end: end2, column: here.column + (split - here.split), split, lazy: joined === "lazy" });
|
|
11928
|
+
const text = input.slice(content, end2);
|
|
11929
|
+
const newline = end2 < input.length;
|
|
11930
|
+
parts.push(newline ? `${text}
|
|
11931
|
+
` : text);
|
|
11932
|
+
offset += text.length + (newline ? 1 : 0);
|
|
11933
|
+
start = end2 + 1;
|
|
11934
|
+
if (!newline) break;
|
|
11935
|
+
}
|
|
11936
|
+
const view = { lines: run3, parent: c.lineView };
|
|
11937
|
+
const saved = c.lineView, starts = c._lineStarts, lineIndex = c._lineIndex, scanned = c._lineScannedTo, probing = c._probe;
|
|
11938
|
+
const errors = c._errors?.length ?? 0;
|
|
11939
|
+
let cursor = -1;
|
|
11940
|
+
if (probing !== void 0) for (const line2 of run3) {
|
|
11941
|
+
if (probing.offset >= line2.source && probing.offset <= line2.end) {
|
|
11942
|
+
cursor = line2.offset + probing.offset - line2.source;
|
|
11943
|
+
break;
|
|
11944
|
+
}
|
|
11945
|
+
}
|
|
11946
|
+
const inner = cursor < 0 ? void 0 : { offset: cursor, best: null };
|
|
11947
|
+
c.lineView = view;
|
|
11948
|
+
c._lineStarts = void 0;
|
|
11949
|
+
c._lineIndex = void 0;
|
|
11950
|
+
c._lineScannedTo = void 0;
|
|
11951
|
+
c._probe = inner;
|
|
11952
|
+
let result;
|
|
11953
|
+
try {
|
|
11954
|
+
result = body.parse(parts.join(""), 0, ctx);
|
|
11955
|
+
} finally {
|
|
11956
|
+
c.lineView = saved;
|
|
11957
|
+
c._lineStarts = starts;
|
|
11958
|
+
c._lineIndex = lineIndex;
|
|
11959
|
+
c._lineScannedTo = scanned;
|
|
11960
|
+
c._probe = probing;
|
|
11961
|
+
}
|
|
11962
|
+
if (inner?.best) failAt(c, inner.best.expected, sourceOffset(view, inner.best.span.start));
|
|
11963
|
+
if (c._errors !== void 0) for (let i = errors; i < c._errors.length; i++) {
|
|
11964
|
+
const error = c._errors[i];
|
|
11965
|
+
error.span = { start: sourceOffset(view, error.span.start), end: sourceOffset(view, error.span.end) };
|
|
11966
|
+
}
|
|
11967
|
+
if (!result.ok) {
|
|
11968
|
+
const at = sourceOffset(view, result.span.start);
|
|
11969
|
+
return { ...result, span: { start: at, end: at } };
|
|
11970
|
+
}
|
|
11971
|
+
const stop = result.span.end;
|
|
11972
|
+
const index = lineIndexAt(view, stop);
|
|
11973
|
+
const line = run3[index];
|
|
11974
|
+
const end = stop === line.offset && index > 0 ? run3[index - 1].end + 1 : stop - line.offset > line.end - line.source ? line.end + 1 : line.source + (stop - line.offset);
|
|
11975
|
+
recordLineRangeFromContext(c, input, pos, end);
|
|
11976
|
+
return { ok: true, value: result.value, span: { start: pos, end } };
|
|
11977
|
+
}
|
|
11978
|
+
};
|
|
11979
|
+
}
|
|
11980
|
+
|
|
11981
|
+
// src/table/line-scan.ts
|
|
11982
|
+
function rebuildLineScan(s, subtree) {
|
|
11983
|
+
if (s.kind === 2) {
|
|
11984
|
+
return lines(subtree(s.prefix), subtree(s.body), {
|
|
11985
|
+
...s.first === void 0 ? {} : { first: subtree(s.first) },
|
|
11986
|
+
...s.lazy === void 0 ? {} : { lazy: subtree(s.lazy) }
|
|
11987
|
+
});
|
|
11988
|
+
}
|
|
11989
|
+
if (s.kind === 3) return columns(s.n);
|
|
11990
|
+
return void 0;
|
|
11991
|
+
}
|
|
11992
|
+
|
|
11740
11993
|
// src/combinators/scanTo.ts
|
|
11741
11994
|
function resolveScanSkip(explicitSkip, raw, ctx) {
|
|
11742
11995
|
if (raw) return [];
|
|
@@ -12066,7 +12319,7 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
|
|
|
12066
12319
|
...prog.labels === void 0 ? {} : { triviaKindLabels: prog.labels },
|
|
12067
12320
|
...prog.classified === 1 ? { rootTriviaClassified: true } : {}
|
|
12068
12321
|
};
|
|
12069
|
-
const
|
|
12322
|
+
const lines2 = prog.lines === 1;
|
|
12070
12323
|
const mode = prog.hostMode ?? "ast";
|
|
12071
12324
|
const elided = mode === "ast" && hasDirectBuilder(prog);
|
|
12072
12325
|
const coverageDefinitions = prog.cov === void 0 ? void 0 : Object.freeze(covDefinitions(prog).map(Object.freeze));
|
|
@@ -12094,7 +12347,7 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
|
|
|
12094
12347
|
ctx._fe = -1;
|
|
12095
12348
|
ctx._fx = EMPTY_FX;
|
|
12096
12349
|
ctx._fc = false;
|
|
12097
|
-
if (
|
|
12350
|
+
if (lines2 && ctx._lineStarts === void 0) {
|
|
12098
12351
|
ctx._lineStarts = [0];
|
|
12099
12352
|
ctx._lineScannedTo = 0;
|
|
12100
12353
|
}
|
|
@@ -12102,10 +12355,10 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
|
|
|
12102
12355
|
if (end < 0) {
|
|
12103
12356
|
const fe = ctx._fe;
|
|
12104
12357
|
const at = fe === void 0 || fe < 0 ? pos : fe;
|
|
12105
|
-
const fspan =
|
|
12358
|
+
const fspan = lines2 ? spanLines(ctx, at, at) : { start: at, end: at };
|
|
12106
12359
|
return committed2(ctx) ? { ok: false, expected: ctx._fx ?? EMPTY_FX, span: fspan, committed: true } : { ok: false, expected: ctx._fx ?? EMPTY_FX, span: fspan };
|
|
12107
12360
|
}
|
|
12108
|
-
return { ok: true, value: d.lastValue(), span:
|
|
12361
|
+
return { ok: true, value: d.lastValue(), span: lines2 ? spanLines(ctx, pos, end) : { start: pos, end } };
|
|
12109
12362
|
};
|
|
12110
12363
|
const ownTrivia = triviaOfRule(prog.rules[name]);
|
|
12111
12364
|
const meta = ownTrivia === void 0 ? baseMeta : { ...baseMeta, grammarTrivia: ownTrivia };
|
|
@@ -12200,12 +12453,7 @@ function assemble(t, prog, cfg) {
|
|
|
12200
12453
|
return recognize;
|
|
12201
12454
|
}
|
|
12202
12455
|
const labelExtraIps = [];
|
|
12203
|
-
for (const s of prog.scans ?? [])
|
|
12204
|
-
for (const r of s.skip) labelExtraIps.push(r[0]);
|
|
12205
|
-
if (s.sentinel !== void 0) labelExtraIps.push(s.sentinel[0]);
|
|
12206
|
-
if (s.recoverAt !== void 0) labelExtraIps.push(s.recoverAt[0]);
|
|
12207
|
-
if (s.stopAt !== void 0) labelExtraIps.push(s.stopAt[0]);
|
|
12208
|
-
}
|
|
12456
|
+
for (const s of prog.scans ?? []) for (const r of scanRefs(s)) labelExtraIps.push(r[0]);
|
|
12209
12457
|
for (const set of prog.scanSkip ?? []) for (const r of set) labelExtraIps.push(r[0]);
|
|
12210
12458
|
const closureLabels = computeSiteLabels(
|
|
12211
12459
|
code,
|
|
@@ -15016,12 +15264,7 @@ function assemble(t, prog, cfg) {
|
|
|
15016
15264
|
let emitReached;
|
|
15017
15265
|
{
|
|
15018
15266
|
const extraIps = [];
|
|
15019
|
-
for (const s of prog.scans ?? [])
|
|
15020
|
-
for (const r of s.skip) extraIps.push(r[0]);
|
|
15021
|
-
if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
|
|
15022
|
-
if (s.recoverAt !== void 0) extraIps.push(s.recoverAt[0]);
|
|
15023
|
-
if (s.stopAt !== void 0) extraIps.push(s.stopAt[0]);
|
|
15024
|
-
}
|
|
15267
|
+
for (const s of prog.scans ?? []) for (const r of scanRefs(s)) extraIps.push(r[0]);
|
|
15025
15268
|
for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
|
|
15026
15269
|
const roots = [...Object.values(prog.rules), ...extraIps];
|
|
15027
15270
|
for (const ip of reachableSites(code, roots)) {
|
|
@@ -15218,6 +15461,11 @@ function assemble(t, prog, cfg) {
|
|
|
15218
15461
|
return link(ip);
|
|
15219
15462
|
}
|
|
15220
15463
|
for (const s of prog.scans ?? []) {
|
|
15464
|
+
const rebuilt = rebuildLineScan(s, subtreeComb);
|
|
15465
|
+
if (rebuilt !== void 0) {
|
|
15466
|
+
scansArr.push(rebuilt);
|
|
15467
|
+
continue;
|
|
15468
|
+
}
|
|
15221
15469
|
const skip = s.skip.map((r) => subtreeComb(r));
|
|
15222
15470
|
const raw = (s.flags & 1) !== 0;
|
|
15223
15471
|
if (s.kind === 1) {
|
|
@@ -15459,6 +15707,11 @@ function emitScanSpec(s) {
|
|
|
15459
15707
|
if (s.sent !== void 0) parts.push(`sent:${s.sent === null ? "null" : jsString(s.sent)}`);
|
|
15460
15708
|
if (s.open !== void 0) parts.push(`open:${jsString(s.open)}`);
|
|
15461
15709
|
if (s.close !== void 0) parts.push(`close:${jsString(s.close)}`);
|
|
15710
|
+
for (const key of ["prefix", "body", "first", "lazy"]) {
|
|
15711
|
+
const r = s[key];
|
|
15712
|
+
if (r !== void 0) parts.push(`${key}:${emitRef(r)}`);
|
|
15713
|
+
}
|
|
15714
|
+
if (s.n !== void 0) parts.push(`n:${s.n}`);
|
|
15462
15715
|
return `{${parts.join(",")}}`;
|
|
15463
15716
|
}
|
|
15464
15717
|
function emitDispatchSpec(d) {
|
|
@@ -15479,12 +15732,7 @@ function emitAssemblies(prog, cfgs) {
|
|
|
15479
15732
|
if (cfgs.length === 0) return ["a:[],"];
|
|
15480
15733
|
const t = resolveTable(prog);
|
|
15481
15734
|
const extraIps = [];
|
|
15482
|
-
for (const s of prog.scans ?? [])
|
|
15483
|
-
for (const r of s.skip) extraIps.push(r[0]);
|
|
15484
|
-
if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
|
|
15485
|
-
if (s.recoverAt !== void 0) extraIps.push(s.recoverAt[0]);
|
|
15486
|
-
if (s.stopAt !== void 0) extraIps.push(s.stopAt[0]);
|
|
15487
|
-
}
|
|
15735
|
+
for (const s of prog.scans ?? []) for (const r of scanRefs(s)) extraIps.push(r[0]);
|
|
15488
15736
|
for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
|
|
15489
15737
|
const out = [];
|
|
15490
15738
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -15636,6 +15884,8 @@ function children(def, winners) {
|
|
|
15636
15884
|
...def.recoverAt === void 0 ? [] : [def.recoverAt],
|
|
15637
15885
|
...def.stopAt === void 0 ? [] : [def.stopAt]
|
|
15638
15886
|
];
|
|
15887
|
+
case "lines":
|
|
15888
|
+
return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
|
|
15639
15889
|
case "lazy": {
|
|
15640
15890
|
let resolved;
|
|
15641
15891
|
try {
|
|
@@ -15779,6 +16029,10 @@ function payloadKey(p, d) {
|
|
|
15779
16029
|
return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
|
|
15780
16030
|
case "scanTo":
|
|
15781
16031
|
return `scanTo\0${d.raw}\0${d.orEOF}\0${d.recoverAt !== void 0}\0${d.stopAt !== void 0}`;
|
|
16032
|
+
case "lines":
|
|
16033
|
+
return `lines\0${d.first !== void 0}\0${d.lazy !== void 0}`;
|
|
16034
|
+
case "columns":
|
|
16035
|
+
return `columns\0${d.n}`;
|
|
15782
16036
|
case "guard":
|
|
15783
16037
|
return `guard\0${fnKey(d.predSrc, d.predicate)}`;
|
|
15784
16038
|
case "adjacency":
|
|
@@ -15899,6 +16153,8 @@ function segment(d, index) {
|
|
|
15899
16153
|
if (d.recoverAt !== void 0 && index === d.skip.length + 1) return "scanTo.recoverAt";
|
|
15900
16154
|
return "scanTo.stopAt";
|
|
15901
16155
|
}
|
|
16156
|
+
case "lines":
|
|
16157
|
+
return ["lines.prefix", "lines.body", d.first === void 0 ? "lines.lazy" : "lines.first", "lines.lazy"][index] ?? "lines";
|
|
15902
16158
|
default:
|
|
15903
16159
|
return d.tag;
|
|
15904
16160
|
}
|
|
@@ -16941,60 +17197,60 @@ function findKeywordRegexes(visited) {
|
|
|
16941
17197
|
var sitesLine = (sites) => sites.slice(0, 6).map(siteToString).join("\n ") + (sites.length > 6 ? `
|
|
16942
17198
|
\u2026 and ${sites.length - 6} more` : "");
|
|
16943
17199
|
function formatDuplicationFindings(report) {
|
|
16944
|
-
const
|
|
17200
|
+
const lines2 = [];
|
|
16945
17201
|
for (const f of report.structureLoss) {
|
|
16946
|
-
|
|
16947
|
-
|
|
16948
|
-
|
|
16949
|
-
|
|
16950
|
-
|
|
17202
|
+
lines2.push(`parseman BUG [structure-loss] @ ${siteToString(f.site)}: arm[${f.earlier}] flattens \`${f.nodeType}\` where arm[${f.later}] structures it, on ${firstSetToString(f.on)}`);
|
|
17203
|
+
lines2.push(` flat: ${f.earlierShape}`);
|
|
17204
|
+
lines2.push(` rich: ${f.laterShape}`);
|
|
17205
|
+
lines2.push(` lost: ${f.lostNodeTypes.map((t) => `\`${t}\``).join(", ")}`);
|
|
17206
|
+
lines2.push(` fix: ${f.suggestion}`);
|
|
16951
17207
|
}
|
|
16952
17208
|
for (const f of report.rewrites) {
|
|
16953
|
-
|
|
16954
|
-
|
|
16955
|
-
|
|
16956
|
-
|
|
16957
|
-
if (f.perf !== "")
|
|
17209
|
+
lines2.push(`parseman ${f.bug ? "BUG" : "rewrite"} [${f.rewrite}${f.sepByVerdict === void 0 ? "" : `/${f.sepByVerdict}`}] @ ${siteToString(f.site)}${f.astNeutral ? "" : " (candidate \u2014 verify AST identity)"}`);
|
|
17210
|
+
lines2.push(` from: ${f.from}`);
|
|
17211
|
+
lines2.push(` to: ${f.to}`);
|
|
17212
|
+
lines2.push(` fix: ${f.suggestion}`);
|
|
17213
|
+
if (f.perf !== "") lines2.push(` perf: ${f.perf}`);
|
|
16958
17214
|
}
|
|
16959
17215
|
for (const f of report.divergentNodes) {
|
|
16960
|
-
|
|
16961
|
-
for (const p of f.productions)
|
|
17216
|
+
lines2.push(`parseman divergent-node: \`${f.nodeType}\` is built by ${f.count} different productions sharing ${f.sharedTerms.length} term(s): ${f.sharedTerms.map((t) => `\`${t}\``).join(", ")}`);
|
|
17217
|
+
for (const p of f.productions) lines2.push(` ${siteToString(p.site)}
|
|
16962
17218
|
${p.shape}
|
|
16963
17219
|
unique to it: ${p.distinctTerms.length === 0 ? "(none)" : p.distinctTerms.map((t) => `\`${t}\``).join(", ")}`);
|
|
16964
|
-
|
|
17220
|
+
lines2.push(` fix: ${f.suggestion}`);
|
|
16965
17221
|
}
|
|
16966
17222
|
for (const f of report.keywordRegexes) {
|
|
16967
17223
|
const src = f.source.length > 90 ? `${f.source.slice(0, 88)}\u2026` : f.source;
|
|
16968
|
-
|
|
16969
|
-
|
|
17224
|
+
lines2.push(`parseman ${f.bug ? "BUG " : ""}keyword-regex @ ${siteToString(f.site)}: /${src}/${f.flags}${f.vocabulary ? ` [${f.words.length} literal words${f.longestFirst ? "" : ", NOT longest-first"}]` : ""}${f.hazards.length > 0 ? ` [${f.hazards.length} prefix hazard(s)${f.bug ? ", UNRESCUED" : ""}]` : ""}${f.caseFoldRisk ? " [CASE-FOLD RISK]" : ""}`);
|
|
17225
|
+
lines2.push(` fix: ${f.suggestion}`);
|
|
16970
17226
|
}
|
|
16971
17227
|
for (const f of report.duplicates) {
|
|
16972
|
-
|
|
16973
|
-
|
|
16974
|
-
|
|
17228
|
+
lines2.push(`parseman duplication: ${f.count}\xD7 identical ${f.size}-node shape (saves ${f.savings} nodes) \u2014 ${f.shape}`);
|
|
17229
|
+
lines2.push(` at: ${sitesLine(f.sites)}`);
|
|
17230
|
+
lines2.push(` fix: ${f.suggestion}`);
|
|
16975
17231
|
}
|
|
16976
17232
|
for (const f of report.nearDuplicates) {
|
|
16977
|
-
|
|
16978
|
-
|
|
16979
|
-
|
|
16980
|
-
|
|
17233
|
+
lines2.push(`parseman near-duplication: ${f.count} clones of a ${f.sharedSize}-node shape differing at ONE slot (saves ${f.savings} nodes) \u2014 ${f.shape}`);
|
|
17234
|
+
lines2.push(` slot: ${f.slotPath} \u2208 { ${f.variants.join(" | ")} }`);
|
|
17235
|
+
lines2.push(` at: ${sitesLine(f.sites)}`);
|
|
17236
|
+
lines2.push(` fix: ${f.suggestion}`);
|
|
16981
17237
|
}
|
|
16982
17238
|
for (const f of report.regexFragments) {
|
|
16983
|
-
|
|
16984
|
-
|
|
16985
|
-
|
|
17239
|
+
lines2.push(`parseman regex-fragment: \`${f.fragment}\` (${f.branches} branches) re-spelled in ${f.count} regex() terminals`);
|
|
17240
|
+
lines2.push(` at: ${sitesLine(f.sites)}`);
|
|
17241
|
+
lines2.push(` fix: ${f.suggestion}`);
|
|
16986
17242
|
}
|
|
16987
17243
|
for (const f of report.regexClasses) {
|
|
16988
|
-
|
|
16989
|
-
for (const v of f.variants)
|
|
17244
|
+
lines2.push(`parseman regex-class${f.drifted ? " DRIFT" : ""}: [${f.canonical}] across ${f.count} regex() terminals${f.bmpCeiling ? " [BMP ceiling]" : ""}`);
|
|
17245
|
+
for (const v of f.variants) lines2.push(` [${v.source}] \xD7${v.count} ${v.delta}
|
|
16990
17246
|
${sitesLine(v.sites)}`);
|
|
16991
|
-
|
|
17247
|
+
lines2.push(` fix: ${f.suggestion}`);
|
|
16992
17248
|
}
|
|
16993
17249
|
for (const f of report.overlaps) {
|
|
16994
|
-
|
|
16995
|
-
|
|
17250
|
+
lines2.push(`parseman overlap @ ${siteToString(f.site)}: arm[${f.a}] \u2229 arm[${f.b}] on ${firstSetToString(f.on)}${f.sharedPrefix === null ? "" : ` \u2014 shared prefix: ${f.sharedPrefix}`}${f.handledByStrategy ? " [sharedPrefix strategy already handles this]" : ""}`);
|
|
17251
|
+
lines2.push(` fix: ${f.suggestion}`);
|
|
16996
17252
|
}
|
|
16997
|
-
return
|
|
17253
|
+
return lines2;
|
|
16998
17254
|
}
|
|
16999
17255
|
function duplicationFindingCount(report) {
|
|
17000
17256
|
return report.duplicates.length + report.nearDuplicates.length + report.regexFragments.length + report.regexClasses.length + report.overlaps.length + report.rewrites.length + report.divergentNodes.length + report.structureLoss.length + report.keywordRegexes.length;
|
|
@@ -17028,11 +17284,11 @@ function reportDuplication(opt, analyze) {
|
|
|
17028
17284
|
);
|
|
17029
17285
|
return void 0;
|
|
17030
17286
|
}
|
|
17031
|
-
const
|
|
17032
|
-
if (
|
|
17287
|
+
const lines2 = formatDuplicationFindings(report);
|
|
17288
|
+
if (lines2.length > 0) {
|
|
17033
17289
|
if (level === "error") throw new Error(`parseman: ${duplicationFindingCount(report)} duplication/overlap finding(s)
|
|
17034
|
-
${
|
|
17035
|
-
for (const l of
|
|
17290
|
+
${lines2.join("\n")}`);
|
|
17291
|
+
for (const l of lines2) console.warn(l);
|
|
17036
17292
|
}
|
|
17037
17293
|
return report;
|
|
17038
17294
|
}
|
|
@@ -17379,6 +17635,9 @@ function childrenOf3(def) {
|
|
|
17379
17635
|
];
|
|
17380
17636
|
case "routed":
|
|
17381
17637
|
return def.fallback ? [def.fallback] : [];
|
|
17638
|
+
case "lines":
|
|
17639
|
+
return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
|
|
17640
|
+
case "columns":
|
|
17382
17641
|
case "lazy":
|
|
17383
17642
|
case "literal":
|
|
17384
17643
|
case "regex":
|
|
@@ -17705,6 +17964,15 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
17705
17964
|
return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
|
|
17706
17965
|
case "scanTo":
|
|
17707
17966
|
return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}]${def.raw ? ", raw: true" : ""}, orEOF: ${def.orEOF}${def.recoverAt === void 0 ? "" : `, recoverAt: ${kid(def.recoverAt)}`}${def.stopAt === void 0 ? "" : `, stopAt: ${kid(def.stopAt)}`} })`;
|
|
17967
|
+
case "lines": {
|
|
17968
|
+
const options = [
|
|
17969
|
+
...def.first === void 0 ? [] : [`first: ${kid(def.first)}`],
|
|
17970
|
+
...def.lazy === void 0 ? [] : [`lazy: ${kid(def.lazy)}`]
|
|
17971
|
+
];
|
|
17972
|
+
return `lines(${kid(def.prefix)}, ${kid(def.body)}${options.length === 0 ? "" : `, { ${options.join(", ")} }`})`;
|
|
17973
|
+
}
|
|
17974
|
+
case "columns":
|
|
17975
|
+
return `columns(${def.n})`;
|
|
17708
17976
|
case "transform": {
|
|
17709
17977
|
if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
|
|
17710
17978
|
return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)}${def.recognitionOnly ? ", true" : ""})`;
|
|
@@ -17769,7 +18037,7 @@ function fieldOptionsSrc(def) {
|
|
|
17769
18037
|
}
|
|
17770
18038
|
|
|
17771
18039
|
// src/version.ts
|
|
17772
|
-
var PARSEMAN_VERSION = "0.
|
|
18040
|
+
var PARSEMAN_VERSION = "0.56.0";
|
|
17773
18041
|
|
|
17774
18042
|
// src/compiler/compile-linkable-table.ts
|
|
17775
18043
|
function isLocal(rule) {
|
|
@@ -18060,26 +18328,26 @@ function examinedNothing(d) {
|
|
|
18060
18328
|
}
|
|
18061
18329
|
function formatGrammarDiagnosis(d) {
|
|
18062
18330
|
const s = d.summary;
|
|
18063
|
-
const
|
|
18064
|
-
|
|
18331
|
+
const lines2 = [];
|
|
18332
|
+
lines2.push(
|
|
18065
18333
|
examinedNothing(d) ? `parseman: COULD NOT ANALYSE \u2014 0 choice(s) examined; ${s.unanalysable} rule(s) unreadable. No verdict about this grammar is available, good or bad.` : d.ok ? `parseman: grammar OK \u2014 ${s.gated}/${s.totalChoices} choice(s) gate on first char${s.recoverable > 0 ? `, ${s.recoverable} recoverable` : ""}${s.accepted > 0 ? `, ${s.accepted} accepted` : ""}${s.deferred > 0 ? `, ${s.deferred} deferred to the fusing artifact` : ""}.` : `parseman: grammar NOT OK \u2014 ${blockingOf(d).length - s.unanalysable} blocking finding(s) over ${s.totalChoices} examined choice(s).`
|
|
18066
18334
|
);
|
|
18067
18335
|
if (s.unanalysable > 0 && !examinedNothing(d)) {
|
|
18068
|
-
|
|
18336
|
+
lines2.push(
|
|
18069
18337
|
` ${s.unanalysable} rule(s) UNANALYSABLE \u2014 THIS REPORT IS PARTIAL. An empty finding list below does NOT mean the grammar is clean.`
|
|
18070
18338
|
);
|
|
18071
18339
|
}
|
|
18072
18340
|
for (const f of d.findings) {
|
|
18073
|
-
|
|
18074
|
-
for (const detail of f.details)
|
|
18341
|
+
lines2.push(`${f.severity === "blocking" ? "\u2717" : "\xB7"} [${f.code}] ${f.id}: ${f.message}`);
|
|
18342
|
+
for (const detail of f.details) lines2.push(` ${detail.replace(/\n/g, "\n ")}`);
|
|
18075
18343
|
if (f.acceptKey !== void 0) {
|
|
18076
|
-
|
|
18344
|
+
lines2.push(` intentional? add to the gating snapshot: { accept: ['${f.acceptKey}'] }`);
|
|
18077
18345
|
}
|
|
18078
18346
|
}
|
|
18079
18347
|
if (!d.ok && d.acceptSnapshot.length > 0) {
|
|
18080
|
-
|
|
18348
|
+
lines2.push(` accept-all snapshot: { accept: [${d.acceptSnapshot.map((i) => `'${i}'`).join(", ")}] }`);
|
|
18081
18349
|
}
|
|
18082
|
-
return
|
|
18350
|
+
return lines2;
|
|
18083
18351
|
}
|
|
18084
18352
|
function runAnalysis(grammar, opts) {
|
|
18085
18353
|
if (isCombinator2(grammar)) return analyzeGating(grammar, opts);
|
|
@@ -18581,6 +18849,8 @@ function slotLabel(d, index) {
|
|
|
18581
18849
|
if (d.recoverAt !== void 0 && index === d.skip.length + 1) return "scanTo.recoverAt";
|
|
18582
18850
|
return "scanTo.stopAt";
|
|
18583
18851
|
}
|
|
18852
|
+
case "lines":
|
|
18853
|
+
return ["lines.prefix", "lines.body", d.first === void 0 ? "lines.lazy" : "lines.first", "lines.lazy"][index] ?? "lines";
|
|
18584
18854
|
case "grammar":
|
|
18585
18855
|
return index === 0 ? "grammar" : "grammar.trivia";
|
|
18586
18856
|
case "node":
|
|
@@ -20285,6 +20555,13 @@ function coverageEntry(entry, collector, maps, trace) {
|
|
|
20285
20555
|
...def.recoverAt === void 0 ? {} : { recoverAt: build(def.recoverAt) },
|
|
20286
20556
|
...def.stopAt === void 0 ? {} : { stopAt: build(def.stopAt) }
|
|
20287
20557
|
});
|
|
20558
|
+
case "lines":
|
|
20559
|
+
return lines(build(def.prefix), build(def.body), {
|
|
20560
|
+
...def.first === void 0 ? {} : { first: build(def.first) },
|
|
20561
|
+
...def.lazy === void 0 ? {} : { lazy: build(def.lazy) }
|
|
20562
|
+
});
|
|
20563
|
+
case "columns":
|
|
20564
|
+
return parser2;
|
|
20288
20565
|
// Zero-width assertion with no coverage-bearing child; reuse it verbatim so
|
|
20289
20566
|
// the rebuilt sequence still recognises the boundary marker.
|
|
20290
20567
|
case "adjacency":
|
|
@@ -20452,6 +20729,7 @@ export {
|
|
|
20452
20729
|
choice,
|
|
20453
20730
|
choiceSiteKey,
|
|
20454
20731
|
classifiedTrivia,
|
|
20732
|
+
columns,
|
|
20455
20733
|
compile,
|
|
20456
20734
|
compileLinkableTable,
|
|
20457
20735
|
compiledGrammarCoverageDefinitions,
|
|
@@ -20487,6 +20765,7 @@ export {
|
|
|
20487
20765
|
label,
|
|
20488
20766
|
leaf,
|
|
20489
20767
|
leftFactorPreview,
|
|
20768
|
+
lines,
|
|
20490
20769
|
literal,
|
|
20491
20770
|
makeWhen,
|
|
20492
20771
|
makeWord,
|
|
@@ -20524,6 +20803,7 @@ export {
|
|
|
20524
20803
|
shiftAbsolute,
|
|
20525
20804
|
siteToString,
|
|
20526
20805
|
sourceLeaf,
|
|
20806
|
+
sourceOffset,
|
|
20527
20807
|
startsWith,
|
|
20528
20808
|
token,
|
|
20529
20809
|
transform,
|