parseman 0.37.0 → 0.39.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/duplication.d.ts.map +1 -1
- package/dist/analysis/gating.d.ts.map +1 -1
- package/dist/combinators/attempt.d.ts.map +1 -1
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/dispatch.d.ts +20 -0
- package/dist/combinators/dispatch.d.ts.map +1 -0
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/keywords.d.ts +9 -8
- package/dist/combinators/keywords.d.ts.map +1 -1
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/compiler/codegen.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/value-usage.d.ts.map +1 -1
- package/dist/coverage.d.ts.map +1 -1
- package/dist/index.cjs +432 -53
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +432 -53
- package/dist/index.js.map +4 -4
- package/dist/language-service/index.cjs.map +2 -2
- package/dist/language-service/index.js.map +2 -2
- package/dist/plugin/evaluator.d.ts +2 -1
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +517 -67
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.js +517 -67
- package/dist/plugin/index.js.map +4 -4
- package/dist/spec/index.cjs +133 -6
- package/dist/spec/index.cjs.map +4 -4
- package/dist/spec/index.js +133 -6
- package/dist/spec/index.js.map +4 -4
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -55,6 +55,7 @@ __export(index_exports, {
|
|
|
55
55
|
createGrammarTraceSink: () => createGrammarTraceSink,
|
|
56
56
|
createVisitor: () => createVisitor,
|
|
57
57
|
cstBuildHost: () => cstBuildHost,
|
|
58
|
+
dispatch: () => dispatch,
|
|
58
59
|
duplicationFindingCount: () => duplicationFindingCount,
|
|
59
60
|
expect: () => expect,
|
|
60
61
|
extractCharClasses: () => extractCharClasses,
|
|
@@ -87,6 +88,7 @@ __export(index_exports, {
|
|
|
87
88
|
oneOrMore: () => oneOrMore,
|
|
88
89
|
oneOrMoreSep: () => oneOrMoreSep,
|
|
89
90
|
optional: () => optional,
|
|
91
|
+
otherwise: () => otherwise,
|
|
90
92
|
parse: () => parse,
|
|
91
93
|
parseDoc: () => parseDoc,
|
|
92
94
|
parser: () => parser,
|
|
@@ -110,6 +112,7 @@ __export(index_exports, {
|
|
|
110
112
|
triviaEntries: () => triviaEntries,
|
|
111
113
|
triviaKindMask: () => triviaKindMask,
|
|
112
114
|
walk: () => walk,
|
|
115
|
+
when: () => when,
|
|
113
116
|
withCtx: () => withCtx,
|
|
114
117
|
word: () => word
|
|
115
118
|
});
|
|
@@ -194,6 +197,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
|
194
197
|
return d.parsers.every(me);
|
|
195
198
|
case "choice":
|
|
196
199
|
return d.parsers.some(me);
|
|
200
|
+
case "dispatch":
|
|
201
|
+
return me(d.selector);
|
|
197
202
|
case "transform":
|
|
198
203
|
case "label":
|
|
199
204
|
case "trivia":
|
|
@@ -267,6 +272,8 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
|
267
272
|
case "keywords":
|
|
268
273
|
return p._meta.firstSet;
|
|
269
274
|
// terminals: no refs, cached set is exact
|
|
275
|
+
case "dispatch":
|
|
276
|
+
return fs(d.selector);
|
|
270
277
|
case "lazy":
|
|
271
278
|
try {
|
|
272
279
|
return fs(d.thunk());
|
|
@@ -1036,8 +1043,10 @@ function word(str, boundaryOrOpts, opts) {
|
|
|
1036
1043
|
const rest = typeof boundaryOrOpts === "object" && boundaryOrOpts !== null ? boundaryOrOpts : opts;
|
|
1037
1044
|
return keywords([str], { ...rest, boundary });
|
|
1038
1045
|
}
|
|
1039
|
-
function makeWord(
|
|
1040
|
-
|
|
1046
|
+
function makeWord(boundaryOrOpts = "_0-9A-Za-z", opts) {
|
|
1047
|
+
const boundary = typeof boundaryOrOpts === "string" ? boundaryOrOpts : "_0-9A-Za-z";
|
|
1048
|
+
const rest = typeof boundaryOrOpts === "object" && boundaryOrOpts !== null ? boundaryOrOpts : opts;
|
|
1049
|
+
return (str) => keywords([str], { ...rest, boundary });
|
|
1041
1050
|
}
|
|
1042
1051
|
function escapeRe(s) {
|
|
1043
1052
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -1182,6 +1191,8 @@ function derive(c, seen) {
|
|
|
1182
1191
|
return [def.label];
|
|
1183
1192
|
case "choice":
|
|
1184
1193
|
return def.parsers.flatMap(deriveExpected2);
|
|
1194
|
+
case "dispatch":
|
|
1195
|
+
return deriveExpected2(def.selector);
|
|
1185
1196
|
// Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
|
|
1186
1197
|
// `optional(…)`/`many(…)` can match nothing, so the term after it is equally
|
|
1187
1198
|
// able to fail first — deriving from term 0 alone named a token the parse
|
|
@@ -1323,6 +1334,7 @@ function choice(...args) {
|
|
|
1323
1334
|
const result = parsers[idx].parse(input, pos, ctx);
|
|
1324
1335
|
if (result.ok) return result;
|
|
1325
1336
|
expected.push(...result.expected);
|
|
1337
|
+
if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
|
|
1326
1338
|
return { ok: false, expected, span: { start: pos, end: pos } };
|
|
1327
1339
|
}
|
|
1328
1340
|
return {
|
|
@@ -1363,6 +1375,7 @@ function choice(...args) {
|
|
|
1363
1375
|
rollbackCstCapture(ctx, mark);
|
|
1364
1376
|
if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
|
|
1365
1377
|
expected.push(...result.expected);
|
|
1378
|
+
if (result.committed) return { ok: false, expected, span: result.span, committed: true };
|
|
1366
1379
|
continue;
|
|
1367
1380
|
}
|
|
1368
1381
|
const checks = autoNot[i];
|
|
@@ -2042,6 +2055,75 @@ function sequence(...parsers) {
|
|
|
2042
2055
|
};
|
|
2043
2056
|
}
|
|
2044
2057
|
|
|
2058
|
+
// src/combinators/dispatch.ts
|
|
2059
|
+
function when(key, parser2) {
|
|
2060
|
+
const keys = Array.isArray(key) ? [...key] : [key];
|
|
2061
|
+
if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
|
|
2062
|
+
for (const item of keys) {
|
|
2063
|
+
if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
|
|
2064
|
+
}
|
|
2065
|
+
return { kind: "when", keys, parser: parser2 };
|
|
2066
|
+
}
|
|
2067
|
+
function otherwise(parser2) {
|
|
2068
|
+
return { kind: "otherwise", parser: parser2 };
|
|
2069
|
+
}
|
|
2070
|
+
function dispatch(selector, ...arms) {
|
|
2071
|
+
let fallback;
|
|
2072
|
+
const cases = [];
|
|
2073
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2074
|
+
for (let i = 0; i < arms.length; i++) {
|
|
2075
|
+
const arm = arms[i];
|
|
2076
|
+
if (arm.kind === "otherwise") {
|
|
2077
|
+
if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
|
|
2078
|
+
if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
|
|
2079
|
+
fallback = arm.parser;
|
|
2080
|
+
continue;
|
|
2081
|
+
}
|
|
2082
|
+
for (const key of arm.keys) {
|
|
2083
|
+
if (seen.has(key)) throw new Error(`parseman: duplicate dispatch key ${JSON.stringify(key)}`);
|
|
2084
|
+
seen.add(key);
|
|
2085
|
+
}
|
|
2086
|
+
cases.push({ keys: arm.keys, parser: arm.parser });
|
|
2087
|
+
}
|
|
2088
|
+
const meta = {
|
|
2089
|
+
firstSet: selector._meta.firstSet,
|
|
2090
|
+
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2091
|
+
isTrivia: false
|
|
2092
|
+
};
|
|
2093
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2094
|
+
for (const entry of cases) for (const key of entry.keys) byKey.set(key, entry.parser);
|
|
2095
|
+
return {
|
|
2096
|
+
_tag: "dispatch",
|
|
2097
|
+
_meta: meta,
|
|
2098
|
+
_def: {
|
|
2099
|
+
tag: "dispatch",
|
|
2100
|
+
selector,
|
|
2101
|
+
cases,
|
|
2102
|
+
...fallback === void 0 ? {} : { otherwise: fallback }
|
|
2103
|
+
},
|
|
2104
|
+
parse(input, pos, ctx) {
|
|
2105
|
+
const selected = selector.parse(input, pos, ctx);
|
|
2106
|
+
if (!selected.ok) return selected;
|
|
2107
|
+
const tail = byKey.get(selected.value) ?? fallback;
|
|
2108
|
+
if (tail === void 0) {
|
|
2109
|
+
const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
|
|
2110
|
+
return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
|
|
2111
|
+
}
|
|
2112
|
+
const mark = saveTriviaMark(ctx);
|
|
2113
|
+
const result = tail.parse(input, selected.span.end, ctx);
|
|
2114
|
+
if (!result.ok) {
|
|
2115
|
+
rollbackTrivia(ctx, mark);
|
|
2116
|
+
return { ...result, committed: true };
|
|
2117
|
+
}
|
|
2118
|
+
return {
|
|
2119
|
+
ok: true,
|
|
2120
|
+
value: [selected.value, result.value],
|
|
2121
|
+
span: { start: pos, end: result.span.end }
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2045
2127
|
// src/combinators/attempt.ts
|
|
2046
2128
|
function attempt(parser2) {
|
|
2047
2129
|
const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
|
|
@@ -2064,6 +2146,7 @@ function attempt(parser2) {
|
|
|
2064
2146
|
if (result.ok) return result;
|
|
2065
2147
|
rollbackCstCapture(ctx, mark);
|
|
2066
2148
|
if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
|
|
2149
|
+
if (result.committed) return result;
|
|
2067
2150
|
return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
|
|
2068
2151
|
}
|
|
2069
2152
|
};
|
|
@@ -2093,6 +2176,7 @@ function repItem(combinator, input, cur, ctx, guardable) {
|
|
|
2093
2176
|
const result = combinator.parse(input, pos, ctx);
|
|
2094
2177
|
if (!result.ok) {
|
|
2095
2178
|
rollbackTrivia(ctx, mark);
|
|
2179
|
+
if (result.committed) return { fail: result, failPos: pos };
|
|
2096
2180
|
return { fail: result, failPos: pos };
|
|
2097
2181
|
}
|
|
2098
2182
|
if (result.span.end === pos) {
|
|
@@ -2136,6 +2220,7 @@ function many(combinator, opts = {}) {
|
|
|
2136
2220
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
2137
2221
|
if (item === "stop") break;
|
|
2138
2222
|
if ("fail" in item) {
|
|
2223
|
+
if (!item.fail.ok && item.fail.committed) return item.fail;
|
|
2139
2224
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
2140
2225
|
if (sync === void 0) break;
|
|
2141
2226
|
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
@@ -2180,6 +2265,7 @@ function atLeast(combinator, min, max) {
|
|
|
2180
2265
|
while (count < min) {
|
|
2181
2266
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
2182
2267
|
if (item === "stop" || "fail" in item) {
|
|
2268
|
+
if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
|
|
2183
2269
|
expected ??= deriveExpected(combinator);
|
|
2184
2270
|
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
|
|
2185
2271
|
}
|
|
@@ -2192,6 +2278,7 @@ function atLeast(combinator, min, max) {
|
|
|
2192
2278
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
2193
2279
|
if (item === "stop") break;
|
|
2194
2280
|
if ("fail" in item) {
|
|
2281
|
+
if (!item.fail.ok && item.fail.committed) return item.fail;
|
|
2195
2282
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
2196
2283
|
if (sync === void 0) break;
|
|
2197
2284
|
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
@@ -2230,6 +2317,7 @@ function optional(combinator) {
|
|
|
2230
2317
|
const result = combinator.parse(input, pos, ctx);
|
|
2231
2318
|
if (result.ok) return result;
|
|
2232
2319
|
rollbackTrivia(ctx, mark);
|
|
2320
|
+
if (result.committed) return result;
|
|
2233
2321
|
return { ok: true, value: null, span: { start: pos, end: pos } };
|
|
2234
2322
|
}
|
|
2235
2323
|
};
|
|
@@ -2269,6 +2357,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2269
2357
|
values.push(first.value);
|
|
2270
2358
|
cur = first.span.end;
|
|
2271
2359
|
} else {
|
|
2360
|
+
if (first.committed) return first;
|
|
2272
2361
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2273
2362
|
if (term === void 0 || matchesAt(term, input, pos, ctx)) {
|
|
2274
2363
|
if (min >= 1) return failAt2(pos);
|
|
@@ -2296,6 +2385,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2296
2385
|
const sep = separator.parse(input, sepPos, ctx);
|
|
2297
2386
|
if (!sep.ok) {
|
|
2298
2387
|
rollbackTrivia(ctx, loopMark);
|
|
2388
|
+
if (sep.committed) return sep;
|
|
2299
2389
|
break;
|
|
2300
2390
|
}
|
|
2301
2391
|
const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
|
|
@@ -2311,6 +2401,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2311
2401
|
}
|
|
2312
2402
|
const next = combinator.parse(input, nextPos, ctx);
|
|
2313
2403
|
if (!next.ok) {
|
|
2404
|
+
if (next.committed) return next;
|
|
2314
2405
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2315
2406
|
if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
|
|
2316
2407
|
expected ??= deriveExpected(combinator);
|
|
@@ -2403,6 +2494,11 @@ function markUnusedValues(root) {
|
|
|
2403
2494
|
case "choice":
|
|
2404
2495
|
for (const p of def.parsers) visit(p, consumed);
|
|
2405
2496
|
return;
|
|
2497
|
+
case "dispatch":
|
|
2498
|
+
visit(def.selector, consumed);
|
|
2499
|
+
for (const entry of def.cases) visit(entry.parser, consumed);
|
|
2500
|
+
if (def.otherwise) visit(def.otherwise, consumed);
|
|
2501
|
+
return;
|
|
2406
2502
|
case "node":
|
|
2407
2503
|
visit(def.parser, false);
|
|
2408
2504
|
return;
|
|
@@ -2474,6 +2570,16 @@ function tagRule(r, key) {
|
|
|
2474
2570
|
r._ruleName = key;
|
|
2475
2571
|
if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
|
|
2476
2572
|
}
|
|
2573
|
+
function isDefinableRef(v2) {
|
|
2574
|
+
return !!v2 && typeof v2 === "object" && "_def" in v2 && v2._def.tag === "lazy" && typeof v2.define === "function";
|
|
2575
|
+
}
|
|
2576
|
+
function ruleNameOf(r) {
|
|
2577
|
+
return r._ruleName;
|
|
2578
|
+
}
|
|
2579
|
+
function isNamedRuleRefForAnotherRule(r, key) {
|
|
2580
|
+
const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
|
|
2581
|
+
return name !== void 0 && name !== key;
|
|
2582
|
+
}
|
|
2477
2583
|
function rules(a, b) {
|
|
2478
2584
|
const factory = typeof a === "function" ? a : b;
|
|
2479
2585
|
const options = typeof a === "function" ? b : a;
|
|
@@ -2494,11 +2600,21 @@ function rules(a, b) {
|
|
|
2494
2600
|
for (const key of Object.keys(definitions)) {
|
|
2495
2601
|
const placeholder = cache[key];
|
|
2496
2602
|
const parser2 = definitions[key];
|
|
2497
|
-
if (placeholder
|
|
2498
|
-
|
|
2603
|
+
if (placeholder === parser2) {
|
|
2604
|
+
throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
|
|
2605
|
+
}
|
|
2606
|
+
if (isDefinableRef(placeholder)) {
|
|
2607
|
+
if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
|
|
2499
2608
|
placeholder.define(parser2);
|
|
2500
2609
|
placeholder._meta.firstSet = parser2._meta.firstSet;
|
|
2501
2610
|
placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
|
|
2611
|
+
} else if (isNamedRuleRefForAnotherRule(parser2, key)) {
|
|
2612
|
+
const alias = ref();
|
|
2613
|
+
tagRule(alias, key);
|
|
2614
|
+
alias.define(parser2);
|
|
2615
|
+
alias._meta.firstSet = parser2._meta.firstSet;
|
|
2616
|
+
alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
|
|
2617
|
+
cache[key] = alias;
|
|
2502
2618
|
} else {
|
|
2503
2619
|
tagRule(parser2, key);
|
|
2504
2620
|
cache[key] = parser2;
|
|
@@ -2658,6 +2774,8 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2658
2774
|
case "sequence":
|
|
2659
2775
|
case "choice":
|
|
2660
2776
|
return d.parsers.some((x) => parserHasOwnFields(x, seen));
|
|
2777
|
+
case "dispatch":
|
|
2778
|
+
return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
|
|
2661
2779
|
case "sepBy":
|
|
2662
2780
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
|
|
2663
2781
|
case "skip":
|
|
@@ -2725,6 +2843,8 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2725
2843
|
return parserHasTriviaSite(d.parser, seen);
|
|
2726
2844
|
case "choice":
|
|
2727
2845
|
return d.parsers.some((x) => parserHasTriviaSite(x, seen));
|
|
2846
|
+
case "dispatch":
|
|
2847
|
+
return parserHasTriviaSite(d.selector, seen) || d.cases.some((x) => parserHasTriviaSite(x.parser, seen)) || (d.otherwise ? parserHasTriviaSite(d.otherwise, seen) : false);
|
|
2728
2848
|
case "grammar":
|
|
2729
2849
|
return parserHasTriviaSite(d.parser, seen);
|
|
2730
2850
|
case "lazy": {
|
|
@@ -2750,6 +2870,8 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2750
2870
|
case "sequence":
|
|
2751
2871
|
case "choice":
|
|
2752
2872
|
return d.parsers.some((x) => parserEnablesTriviaCapture(x, seen));
|
|
2873
|
+
case "dispatch":
|
|
2874
|
+
return parserEnablesTriviaCapture(d.selector, seen) || d.cases.some((x) => parserEnablesTriviaCapture(x.parser, seen)) || (d.otherwise ? parserEnablesTriviaCapture(d.otherwise, seen) : false);
|
|
2753
2875
|
case "sepBy":
|
|
2754
2876
|
return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.separator, seen);
|
|
2755
2877
|
case "skip":
|
|
@@ -3151,7 +3273,7 @@ function leaf(root, fn) {
|
|
|
3151
3273
|
}
|
|
3152
3274
|
|
|
3153
3275
|
// src/version.ts
|
|
3154
|
-
var PARSEMAN_VERSION = "0.
|
|
3276
|
+
var PARSEMAN_VERSION = "0.39.0";
|
|
3155
3277
|
|
|
3156
3278
|
// src/cst/host-mode.ts
|
|
3157
3279
|
var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
|
|
@@ -3205,7 +3327,7 @@ function firstSetToString(fs) {
|
|
|
3205
3327
|
if (fs.kind === "empty") return "(empty)";
|
|
3206
3328
|
return fs.ranges.map((r) => r.lo === r.hi ? ch(r.lo) : `${ch(r.lo)}-${ch(r.hi)}`).join(",");
|
|
3207
3329
|
}
|
|
3208
|
-
var
|
|
3330
|
+
var ruleNameOf2 = (p) => p._ruleName;
|
|
3209
3331
|
var SUGGESTIONS = {
|
|
3210
3332
|
"broad-recognizer": "if this arm leads with a keyword regex, use word('kw', boundary) / keywords([...]) \u2014 they expose an EXACT, gating first-set and lower to the same charCodeAt scan. A genuine scanTo fallback is fine; accept it in the gating snapshot if intentional.",
|
|
3211
3333
|
"leading-not": "let the arm lead with its actual consuming terminal (first-sets gate it automatically); keep not(...) only as a TRAILING boundary. Do not hand-roll not(not(...)) to fake gating \u2014 use peek(X), which is zero-width AND carries X first-set.",
|
|
@@ -3307,6 +3429,8 @@ function classifyBroadArm(arm, resolve) {
|
|
|
3307
3429
|
}
|
|
3308
3430
|
return null;
|
|
3309
3431
|
}
|
|
3432
|
+
case "dispatch":
|
|
3433
|
+
return walk2(d.selector);
|
|
3310
3434
|
default:
|
|
3311
3435
|
return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
|
|
3312
3436
|
}
|
|
@@ -3363,7 +3487,7 @@ function detectAntiPatterns(rule, arms) {
|
|
|
3363
3487
|
return out;
|
|
3364
3488
|
}
|
|
3365
3489
|
function analyzeGating(entry, opts) {
|
|
3366
|
-
return analyzeGatingRules([[
|
|
3490
|
+
return analyzeGatingRules([[ruleNameOf2(entry) ?? opts?.entryName ?? "<entry>", entry]], opts);
|
|
3367
3491
|
}
|
|
3368
3492
|
function analyzeGatingRules(ruleMap, opts) {
|
|
3369
3493
|
const raw = [];
|
|
@@ -3396,7 +3520,7 @@ function analyzeGatingRules(ruleMap, opts) {
|
|
|
3396
3520
|
return;
|
|
3397
3521
|
}
|
|
3398
3522
|
const d = def;
|
|
3399
|
-
const rule =
|
|
3523
|
+
const rule = ruleNameOf2(p) ?? enclosingRule;
|
|
3400
3524
|
if (d.tag === "choice") {
|
|
3401
3525
|
raw.push({ g: analyzeChoice(p, d, rule, opts?.resolveRef), rule });
|
|
3402
3526
|
antiPatterns.push(...detectAntiPatterns(rule, d.parsers));
|
|
@@ -3406,6 +3530,11 @@ function analyzeGatingRules(ruleMap, opts) {
|
|
|
3406
3530
|
if (Array.isArray(rec.parsers)) kids.push(...rec.parsers);
|
|
3407
3531
|
for (const k of ["parser", "main", "skipped", "separator", "sentinel"])
|
|
3408
3532
|
if (rec[k]) kids.push(rec[k]);
|
|
3533
|
+
if (d.tag === "dispatch") {
|
|
3534
|
+
kids.push(d.selector);
|
|
3535
|
+
for (const c of d.cases) kids.push(c.parser);
|
|
3536
|
+
if (d.otherwise !== void 0) kids.push(d.otherwise);
|
|
3537
|
+
}
|
|
3409
3538
|
if (Array.isArray(rec.skip)) kids.push(...rec.skip);
|
|
3410
3539
|
if (d.tag === "lazy") {
|
|
3411
3540
|
try {
|
|
@@ -3415,7 +3544,7 @@ function analyzeGatingRules(ruleMap, opts) {
|
|
|
3415
3544
|
}
|
|
3416
3545
|
for (const k of kids) visit(k, rule);
|
|
3417
3546
|
};
|
|
3418
|
-
for (const [name, root] of ruleMap) visit(root,
|
|
3547
|
+
for (const [name, root] of ruleMap) visit(root, ruleNameOf2(root) ?? name);
|
|
3419
3548
|
const perRule = /* @__PURE__ */ new Map();
|
|
3420
3549
|
for (const r of raw) perRule.set(r.rule, (perRule.get(r.rule) ?? 0) + 1);
|
|
3421
3550
|
const seenInRule = /* @__PURE__ */ new Map();
|
|
@@ -3512,6 +3641,12 @@ function childrenOf(d) {
|
|
|
3512
3641
|
case "sequence":
|
|
3513
3642
|
case "choice":
|
|
3514
3643
|
return d.parsers;
|
|
3644
|
+
case "dispatch":
|
|
3645
|
+
return [
|
|
3646
|
+
d.selector,
|
|
3647
|
+
...d.cases.map((c) => c.parser),
|
|
3648
|
+
...d.otherwise === void 0 ? [] : [d.otherwise]
|
|
3649
|
+
];
|
|
3515
3650
|
case "skip":
|
|
3516
3651
|
return [d.main, d.skipped];
|
|
3517
3652
|
case "sepBy":
|
|
@@ -3539,7 +3674,7 @@ function childrenOf(d) {
|
|
|
3539
3674
|
}
|
|
3540
3675
|
}
|
|
3541
3676
|
}
|
|
3542
|
-
var
|
|
3677
|
+
var ruleNameOf3 = (p) => p._ruleName;
|
|
3543
3678
|
var fnKey = (src, fn) => src ?? (typeof fn === "function" ? String(fn) : "");
|
|
3544
3679
|
function payloadKey(p, d) {
|
|
3545
3680
|
switch (d.tag) {
|
|
@@ -3550,7 +3685,7 @@ function payloadKey(p, d) {
|
|
|
3550
3685
|
case "keywords":
|
|
3551
3686
|
return `keywords\0${[...d.words].join("")}\0${d.caseInsensitive}\0${d.boundary ?? ""}`;
|
|
3552
3687
|
case "lazy": {
|
|
3553
|
-
const n =
|
|
3688
|
+
const n = ruleNameOf3(p);
|
|
3554
3689
|
return n === void 0 ? `ref\0#${anonId(p)}` : `ref\0${n}`;
|
|
3555
3690
|
}
|
|
3556
3691
|
case "label":
|
|
@@ -3568,6 +3703,8 @@ function payloadKey(p, d) {
|
|
|
3568
3703
|
return `${d.tag}\0${d.min}\0${d.max ?? ""}`;
|
|
3569
3704
|
case "sepBy":
|
|
3570
3705
|
return `sepBy\0${d.min}\0${d.max ?? ""}\0${d.trailing ?? ""}`;
|
|
3706
|
+
case "dispatch":
|
|
3707
|
+
return `dispatch\0${d.cases.map((c) => JSON.stringify(c.keys)).join("")}\0${d.otherwise !== void 0}`;
|
|
3571
3708
|
case "expect":
|
|
3572
3709
|
return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
|
|
3573
3710
|
case "scanTo":
|
|
@@ -3617,7 +3754,7 @@ var Interner = class {
|
|
|
3617
3754
|
var MAX_RENDER = 160;
|
|
3618
3755
|
function render(p, depth = 3) {
|
|
3619
3756
|
const d = p._def;
|
|
3620
|
-
const name =
|
|
3757
|
+
const name = ruleNameOf3(p);
|
|
3621
3758
|
switch (d.tag) {
|
|
3622
3759
|
case "literal":
|
|
3623
3760
|
return `literal('${d.value}')`;
|
|
@@ -3639,6 +3776,11 @@ function render(p, depth = 3) {
|
|
|
3639
3776
|
return `sequence(${kids.join(", ")})`;
|
|
3640
3777
|
case "choice":
|
|
3641
3778
|
return `choice(${kids.join(", ")})`;
|
|
3779
|
+
case "dispatch": {
|
|
3780
|
+
const arms = d.cases.map((c, i) => `when([${c.keys.map((k) => JSON.stringify(k)).join(", ")}], ${kids[i + 1] ?? ""})`);
|
|
3781
|
+
if (d.otherwise !== void 0) arms.push(`otherwise(${kids[kids.length - 1] ?? ""})`);
|
|
3782
|
+
return `dispatch(${kids[0] ?? ""}, ${arms.join(", ")})`;
|
|
3783
|
+
}
|
|
3642
3784
|
case "node":
|
|
3643
3785
|
return `node(${d.type ? `'${d.type}', ` : ""}${kids[0] ?? ""})`;
|
|
3644
3786
|
case "label":
|
|
@@ -3662,6 +3804,10 @@ function segment(d, index) {
|
|
|
3662
3804
|
return `seq[${index}]`;
|
|
3663
3805
|
case "choice":
|
|
3664
3806
|
return `choice[${index}]`;
|
|
3807
|
+
case "dispatch":
|
|
3808
|
+
if (index === 0) return "dispatch.selector";
|
|
3809
|
+
if (index <= d.cases.length) return `dispatch.when[${index - 1}]`;
|
|
3810
|
+
return "dispatch.otherwise";
|
|
3665
3811
|
case "node":
|
|
3666
3812
|
return `node(${d.type ?? ""})`;
|
|
3667
3813
|
case "label":
|
|
@@ -3796,7 +3942,7 @@ function keywordAlternationHazards(words, boundary) {
|
|
|
3796
3942
|
return out;
|
|
3797
3943
|
}
|
|
3798
3944
|
function analyzeDuplication(entry, opts) {
|
|
3799
|
-
return analyzeDuplicationRules([[
|
|
3945
|
+
return analyzeDuplicationRules([[ruleNameOf3(entry) ?? opts?.entryName ?? "<entry>", entry]], opts);
|
|
3800
3946
|
}
|
|
3801
3947
|
function assertAnalyzable(ruleMap) {
|
|
3802
3948
|
for (const [name, rule] of ruleMap) {
|
|
@@ -3837,7 +3983,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
|
|
|
3837
3983
|
const visit = (p, rule, path, enclosingNode, enclosingReducer) => {
|
|
3838
3984
|
if (visited.has(p)) return;
|
|
3839
3985
|
const d = p._def;
|
|
3840
|
-
const own =
|
|
3986
|
+
const own = ruleNameOf3(p);
|
|
3841
3987
|
const here = own !== void 0 && own !== rule ? { rule: own, path: "" } : { rule, path };
|
|
3842
3988
|
visited.set(p, { p, d, site: here, shape: shapeOf(p), size: sizeOf(p), enclosingNode, enclosingReducer });
|
|
3843
3989
|
const inner = d.tag === "node" ? d.type ?? "(anonymous)" : enclosingNode;
|
|
@@ -3850,12 +3996,12 @@ function analyzeDuplicationRules(ruleMap, opts) {
|
|
|
3850
3996
|
if (d.tag === "lazy") {
|
|
3851
3997
|
try {
|
|
3852
3998
|
const target = d.thunk();
|
|
3853
|
-
visit(target,
|
|
3999
|
+
visit(target, ruleNameOf3(target) ?? ruleNameOf3(p) ?? here.rule, "", null, false);
|
|
3854
4000
|
} catch {
|
|
3855
4001
|
}
|
|
3856
4002
|
}
|
|
3857
4003
|
};
|
|
3858
|
-
for (const [name, root] of ruleMap) visit(root,
|
|
4004
|
+
for (const [name, root] of ruleMap) visit(root, ruleNameOf3(root) ?? name, "", null, false);
|
|
3859
4005
|
const accept = new Set(opts?.accept ?? []);
|
|
3860
4006
|
const used = /* @__PURE__ */ new Set();
|
|
3861
4007
|
const keep = (f) => {
|
|
@@ -4915,6 +5061,8 @@ function children(def, winners) {
|
|
|
4915
5061
|
case "sequence":
|
|
4916
5062
|
case "choice":
|
|
4917
5063
|
return def.parsers;
|
|
5064
|
+
case "dispatch":
|
|
5065
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
4918
5066
|
case "many":
|
|
4919
5067
|
case "oneOrMore":
|
|
4920
5068
|
case "optional":
|
|
@@ -6379,6 +6527,8 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6379
6527
|
for (const arm of d.parsers) alts.push(...rec(arm).alts);
|
|
6380
6528
|
return { alts };
|
|
6381
6529
|
}
|
|
6530
|
+
case "dispatch":
|
|
6531
|
+
return rec(d.selector);
|
|
6382
6532
|
case "sequence": {
|
|
6383
6533
|
const chain = [];
|
|
6384
6534
|
for (const term of d.parsers) {
|
|
@@ -6453,6 +6603,9 @@ function failReturn(expected, posExpr) {
|
|
|
6453
6603
|
function failReturnArr(expectedArr, posExpr) {
|
|
6454
6604
|
return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
6455
6605
|
}
|
|
6606
|
+
function committedReturnArr(expectedArr, posExpr) {
|
|
6607
|
+
return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} }, committed: true }`;
|
|
6608
|
+
}
|
|
6456
6609
|
function hoistExpected(ctx, constArrSource) {
|
|
6457
6610
|
let name = ctx.expectedMap.get(constArrSource);
|
|
6458
6611
|
if (name === void 0) {
|
|
@@ -6489,15 +6642,22 @@ function failArrBody(ctx, expectedArr, posExpr, constant = true) {
|
|
|
6489
6642
|
return probe + trace + failReturnArr(expectedArr, posExpr);
|
|
6490
6643
|
}
|
|
6491
6644
|
function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
|
|
6492
|
-
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
|
|
6645
|
+
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} }, ...(_ctx._fc ? { committed: true } : {}) }`;
|
|
6646
|
+
}
|
|
6647
|
+
function committedFailBody(ctx, expectedArr = "_ctx._fx", posExpr = "_ctx._fe") {
|
|
6648
|
+
if (ctx.failLabel) {
|
|
6649
|
+
if (!ctx.recordFail) return `{ _ctx._fc = true; break ${ctx.failLabel} }`;
|
|
6650
|
+
return `{ _ctx._fc = true; _ctx._fe = ${posExpr}; _ctx._fx = ${expectedArr}; break ${ctx.failLabel} }`;
|
|
6651
|
+
}
|
|
6652
|
+
return committedReturnArr(expectedArr, posExpr);
|
|
6493
6653
|
}
|
|
6494
6654
|
function propagateFailBody(ctx, srcCtx = "_ctx") {
|
|
6495
6655
|
if (srcCtx !== "_ctx") {
|
|
6496
6656
|
if (ctx.failLabel) {
|
|
6497
6657
|
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
6498
|
-
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; break ${ctx.failLabel} }`;
|
|
6658
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; break ${ctx.failLabel} }`;
|
|
6499
6659
|
}
|
|
6500
|
-
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; ${resultFromRecorded()} }`;
|
|
6660
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded()} }`;
|
|
6501
6661
|
}
|
|
6502
6662
|
if (ctx.failLabel) return `break ${ctx.failLabel}`;
|
|
6503
6663
|
return resultFromRecorded();
|
|
@@ -6710,6 +6870,8 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6710
6870
|
// nothing committed remains.
|
|
6711
6871
|
case "choice":
|
|
6712
6872
|
return false;
|
|
6873
|
+
case "dispatch":
|
|
6874
|
+
return capturesLeaf(d.selector, seen) || d.cases.some((x) => mayLeavePartialCapture(x.parser, seen) || capturesLeaf(x.parser, seen)) || (d.otherwise ? mayLeavePartialCapture(d.otherwise, seen) || capturesLeaf(d.otherwise, seen) : false);
|
|
6713
6875
|
case "attempt":
|
|
6714
6876
|
return false;
|
|
6715
6877
|
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
@@ -6754,6 +6916,50 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6754
6916
|
return true;
|
|
6755
6917
|
}
|
|
6756
6918
|
}
|
|
6919
|
+
function mayCommitFailure(p, seen = /* @__PURE__ */ new Set()) {
|
|
6920
|
+
if (seen.has(p)) return false;
|
|
6921
|
+
seen.add(p);
|
|
6922
|
+
const d = p._def;
|
|
6923
|
+
switch (d.tag) {
|
|
6924
|
+
case "dispatch":
|
|
6925
|
+
return true;
|
|
6926
|
+
case "choice":
|
|
6927
|
+
case "sequence":
|
|
6928
|
+
return d.parsers.some((x) => mayCommitFailure(x, seen));
|
|
6929
|
+
case "many":
|
|
6930
|
+
case "oneOrMore":
|
|
6931
|
+
case "optional":
|
|
6932
|
+
case "attempt":
|
|
6933
|
+
case "transform":
|
|
6934
|
+
case "label":
|
|
6935
|
+
case "field":
|
|
6936
|
+
case "grammar":
|
|
6937
|
+
case "node":
|
|
6938
|
+
return mayCommitFailure(d.parser, seen);
|
|
6939
|
+
case "sepBy":
|
|
6940
|
+
return mayCommitFailure(d.parser, seen) || mayCommitFailure(d.separator, seen);
|
|
6941
|
+
case "skip":
|
|
6942
|
+
return mayCommitFailure(d.main, seen) || mayCommitFailure(d.skipped, seen);
|
|
6943
|
+
case "token":
|
|
6944
|
+
case "leaf":
|
|
6945
|
+
return mayCommitFailure(d.parser, seen);
|
|
6946
|
+
case "withCtx":
|
|
6947
|
+
return mayCommitFailure(d.parser, seen);
|
|
6948
|
+
case "scanTo":
|
|
6949
|
+
return mayCommitFailure(d.sentinel, seen) || d.skip.some((x) => mayCommitFailure(x, seen));
|
|
6950
|
+
case "lazy": {
|
|
6951
|
+
try {
|
|
6952
|
+
return mayCommitFailure(d.thunk(), seen);
|
|
6953
|
+
} catch {
|
|
6954
|
+
return true;
|
|
6955
|
+
}
|
|
6956
|
+
}
|
|
6957
|
+
case "unknown":
|
|
6958
|
+
return true;
|
|
6959
|
+
default:
|
|
6960
|
+
return false;
|
|
6961
|
+
}
|
|
6962
|
+
}
|
|
6757
6963
|
function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
6758
6964
|
if (seen.has(p)) return false;
|
|
6759
6965
|
seen.add(p);
|
|
@@ -6775,6 +6981,8 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
6775
6981
|
case "sequence":
|
|
6776
6982
|
case "choice":
|
|
6777
6983
|
return d.parsers.some((x) => capturesLeaf(x, seen));
|
|
6984
|
+
case "dispatch":
|
|
6985
|
+
return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
|
|
6778
6986
|
case "sepBy":
|
|
6779
6987
|
return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
|
|
6780
6988
|
case "many":
|
|
@@ -6821,6 +7029,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
|
|
|
6821
7029
|
ctx.recordFail = savedRecord;
|
|
6822
7030
|
const ind0 = ind(ctx);
|
|
6823
7031
|
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
7032
|
+
const mayCommit = mayCommitFailure(inner);
|
|
6824
7033
|
const needsFieldRollback = needsRollback && parserHasOwnFields(inner);
|
|
6825
7034
|
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
6826
7035
|
const mR = needsRollback ? v(ctx, "_fcr") : null;
|
|
@@ -6834,6 +7043,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
|
|
|
6834
7043
|
`${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
6835
7044
|
...mF ? [`${ind0}const ${mF} = _ctx._fields?.length ?? 0`] : []
|
|
6836
7045
|
] : [],
|
|
7046
|
+
...mayCommit ? [`${ind0}_ctx._fc = false`] : [],
|
|
6837
7047
|
`${ind0}${lbl}: {`,
|
|
6838
7048
|
...r.stmts,
|
|
6839
7049
|
`${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
|
|
@@ -6842,7 +7052,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
|
|
|
6842
7052
|
`${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null, mF)} }`
|
|
6843
7053
|
] : []
|
|
6844
7054
|
];
|
|
6845
|
-
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
7055
|
+
return { stmts, okVar: okV, valVar: valV, endVar: endV, mayCommit };
|
|
6846
7056
|
}
|
|
6847
7057
|
var CHARCODE_CHAIN_MAX = 16;
|
|
6848
7058
|
function emitLit(def, ctx, pos) {
|
|
@@ -7219,6 +7429,118 @@ function emitChoice(parser2, def, ctx, pos) {
|
|
|
7219
7429
|
}
|
|
7220
7430
|
return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos, coverageBase);
|
|
7221
7431
|
}
|
|
7432
|
+
function emitTailFallible(parser2, ctx, pos) {
|
|
7433
|
+
const savedRecord = ctx.recordFail;
|
|
7434
|
+
ctx.recordFail = true;
|
|
7435
|
+
const out = emitFallible(parser2, ctx, pos);
|
|
7436
|
+
ctx.recordFail = savedRecord;
|
|
7437
|
+
return out;
|
|
7438
|
+
}
|
|
7439
|
+
function mayRecordRecoveryError(p, recovery, seen = /* @__PURE__ */ new Set()) {
|
|
7440
|
+
if (seen.has(p)) return false;
|
|
7441
|
+
seen.add(p);
|
|
7442
|
+
const d = p._def;
|
|
7443
|
+
switch (d.tag) {
|
|
7444
|
+
case "expect":
|
|
7445
|
+
case "recover":
|
|
7446
|
+
return true;
|
|
7447
|
+
case "many":
|
|
7448
|
+
case "oneOrMore":
|
|
7449
|
+
case "sepBy":
|
|
7450
|
+
return recovery || mayRecordRecoveryError(d.parser, recovery, seen);
|
|
7451
|
+
case "choice":
|
|
7452
|
+
case "sequence": {
|
|
7453
|
+
for (const item of d.parsers) {
|
|
7454
|
+
if (mayRecordRecoveryError(item, recovery, seen)) return true;
|
|
7455
|
+
}
|
|
7456
|
+
return false;
|
|
7457
|
+
}
|
|
7458
|
+
case "dispatch": {
|
|
7459
|
+
if (mayRecordRecoveryError(d.selector, recovery, seen)) return true;
|
|
7460
|
+
for (const item of d.cases) {
|
|
7461
|
+
if (mayRecordRecoveryError(item.parser, recovery, seen)) return true;
|
|
7462
|
+
}
|
|
7463
|
+
return d.otherwise ? mayRecordRecoveryError(d.otherwise, recovery, seen) : false;
|
|
7464
|
+
}
|
|
7465
|
+
case "attempt":
|
|
7466
|
+
case "transform":
|
|
7467
|
+
case "label":
|
|
7468
|
+
case "field":
|
|
7469
|
+
case "grammar":
|
|
7470
|
+
case "node":
|
|
7471
|
+
case "token":
|
|
7472
|
+
case "leaf":
|
|
7473
|
+
case "withCtx":
|
|
7474
|
+
case "optional":
|
|
7475
|
+
return mayRecordRecoveryError(d.parser, recovery, seen);
|
|
7476
|
+
case "skip":
|
|
7477
|
+
return mayRecordRecoveryError(d.main, recovery, seen) || mayRecordRecoveryError(d.skipped, recovery, seen);
|
|
7478
|
+
case "scanTo":
|
|
7479
|
+
if (mayRecordRecoveryError(d.sentinel, recovery, seen)) return true;
|
|
7480
|
+
for (const item of d.skip) {
|
|
7481
|
+
if (mayRecordRecoveryError(item, recovery, seen)) return true;
|
|
7482
|
+
}
|
|
7483
|
+
return false;
|
|
7484
|
+
case "lazy": {
|
|
7485
|
+
try {
|
|
7486
|
+
return mayRecordRecoveryError(d.thunk(), recovery, seen);
|
|
7487
|
+
} catch {
|
|
7488
|
+
return true;
|
|
7489
|
+
}
|
|
7490
|
+
}
|
|
7491
|
+
case "unknown":
|
|
7492
|
+
return true;
|
|
7493
|
+
default:
|
|
7494
|
+
return false;
|
|
7495
|
+
}
|
|
7496
|
+
}
|
|
7497
|
+
function emitDispatchCombinator(def, ctx, pos) {
|
|
7498
|
+
const selector = emit(def.selector, ctx, pos);
|
|
7499
|
+
const outV = v(ctx, "_dval");
|
|
7500
|
+
const outE = v(ctx, "_dend");
|
|
7501
|
+
const keyV = v(ctx, "_dkey");
|
|
7502
|
+
const stmts = [
|
|
7503
|
+
...selector.stmts,
|
|
7504
|
+
`${ind(ctx)}const ${keyV} = ${selector.valueVar}`,
|
|
7505
|
+
`${ind(ctx)}let ${outV}, ${outE} = ${selector.endVar}`
|
|
7506
|
+
];
|
|
7507
|
+
let wroteBranch = false;
|
|
7508
|
+
const emitSelectedTail = (parser2, keyword, condition) => {
|
|
7509
|
+
const head = keyword === "else" ? `${ind(ctx)}else {` : `${ind(ctx)}${keyword} (${condition}) {`;
|
|
7510
|
+
stmts.push(head);
|
|
7511
|
+
ctx.indent++;
|
|
7512
|
+
const mayError = mayRecordRecoveryError(parser2, !!ctx.recovery);
|
|
7513
|
+
const errMark = mayError ? v(ctx, "_derr") : null;
|
|
7514
|
+
const logMark = ctx.activeTrivia ? v(ctx, "_dlog") : null;
|
|
7515
|
+
if (errMark) stmts.push(`${ind(ctx)}const ${errMark} = _ctx._errors?.length ?? 0`);
|
|
7516
|
+
if (logMark) stmts.push(`${ind(ctx)}const ${logMark} = _ctx._triviaLog?.length ?? 0`);
|
|
7517
|
+
const tail = emitTailFallible(parser2, ctx, selector.endVar);
|
|
7518
|
+
const errorRollback = errMark ? `if (_ctx._errors && _ctx._errors.length !== ${errMark}) _ctx._errors.length = ${errMark}; ` : "";
|
|
7519
|
+
const logRollback = logMark ? `if (_ctx._triviaLog && _ctx._triviaLog.length !== ${logMark}) _ctx._triviaLog.length = ${logMark}; ` : "";
|
|
7520
|
+
stmts.push(
|
|
7521
|
+
...tail.stmts,
|
|
7522
|
+
`${ind(ctx)}if (!${tail.okVar}) { ${errorRollback}${logRollback}${committedFailBody(ctx)} }`,
|
|
7523
|
+
`${ind(ctx)}${outV} = [${selector.valueVar}, ${tail.valVar}]`,
|
|
7524
|
+
`${ind(ctx)}${outE} = ${tail.endVar}`
|
|
7525
|
+
);
|
|
7526
|
+
ctx.indent--;
|
|
7527
|
+
stmts.push(`${ind(ctx)}}`);
|
|
7528
|
+
};
|
|
7529
|
+
for (const entry of def.cases) {
|
|
7530
|
+
const condition = entry.keys.map((key) => `${keyV} === ${JSON.stringify(key)}`).join(" || ");
|
|
7531
|
+
emitSelectedTail(entry.parser, wroteBranch ? "else if" : "if", condition);
|
|
7532
|
+
wroteBranch = true;
|
|
7533
|
+
}
|
|
7534
|
+
if (def.otherwise) {
|
|
7535
|
+
if (wroteBranch) emitSelectedTail(def.otherwise, "else");
|
|
7536
|
+
else emitSelectedTail(def.otherwise, "if", "true");
|
|
7537
|
+
} else {
|
|
7538
|
+
const expected = JSON.stringify(def.cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key))));
|
|
7539
|
+
const fail = failArrBody(ctx, expected, selector.endVar);
|
|
7540
|
+
stmts.push(wroteBranch ? `${ind(ctx)}else { ${fail} }` : `${ind(ctx)}${fail}`);
|
|
7541
|
+
}
|
|
7542
|
+
return { stmts, valueVar: outV, endVar: outE };
|
|
7543
|
+
}
|
|
7222
7544
|
function emitGreedyClassify(def, superIndex, ctx, pos, coverageBase) {
|
|
7223
7545
|
const superParser = def.parsers[superIndex];
|
|
7224
7546
|
const regexDef = getCoreRegexDef(superParser);
|
|
@@ -7340,12 +7662,16 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
|
|
|
7340
7662
|
const armHasAutoNot = !!(autoNot && autoNot.length > 0);
|
|
7341
7663
|
const armNeedsRollback = ctx.capturing && (mayLeavePartialCapture(p) || armHasAutoNot && capturesLeaf(p));
|
|
7342
7664
|
const armNeedsFieldRollback = armNeedsRollback && parserHasOwnFields(p);
|
|
7665
|
+
const armMayRecordError = mayRecordRecoveryError(p, !!ctx.recovery);
|
|
7343
7666
|
const markLeaves = armNeedsRollback ? v(ctx, "_cml") : null;
|
|
7344
7667
|
const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
|
|
7345
7668
|
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
7346
7669
|
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
7347
7670
|
const markFields = armNeedsFieldRollback ? v(ctx, "_cmf") : null;
|
|
7348
|
-
const
|
|
7671
|
+
const markErrors = armMayRecordError ? v(ctx, "_cme") : null;
|
|
7672
|
+
const captureRollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog, markFields) : "";
|
|
7673
|
+
const errorRollback = markErrors ? `if (_ctx._errors && _ctx._errors.length !== ${markErrors}) _ctx._errors.length = ${markErrors}` : "";
|
|
7674
|
+
const rollback = [captureRollback, errorRollback].filter(Boolean).join("; ");
|
|
7349
7675
|
const failSlot = atStart ? staticFx : "_ctx._fx";
|
|
7350
7676
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
7351
7677
|
if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
|
|
@@ -7360,7 +7686,9 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
|
|
|
7360
7686
|
...markFields ? [`${ind(ctx)}const ${markFields} = _ctx._fields?.length ?? 0`] : []
|
|
7361
7687
|
);
|
|
7362
7688
|
}
|
|
7363
|
-
|
|
7689
|
+
if (markErrors) stmts.push(`${ind(ctx)}const ${markErrors} = _ctx._errors?.length ?? 0`);
|
|
7690
|
+
const arm = emitFallible(p, ctx, pos, atStart);
|
|
7691
|
+
const { stmts: armStmts, okVar, valVar, endVar } = arm;
|
|
7364
7692
|
stmts.push(...armStmts);
|
|
7365
7693
|
if (autoNot && autoNot.length > 0) {
|
|
7366
7694
|
const anCode = v(ctx, "_anc");
|
|
@@ -7384,10 +7712,12 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
|
|
|
7384
7712
|
stmts.push(`${ind(ctx)}}`);
|
|
7385
7713
|
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}; ${coverageBase === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'failure', offset: ${atStart ? pos : "_ctx._fe"} }); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'backtrack', offset: ${pos} });`} }`);
|
|
7386
7714
|
if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
|
|
7715
|
+
if (arm.mayCommit) stmts.push(`${ind(ctx)}if (!${resOkV} && _ctx._fc) ${committedFailBody(ctx)}`);
|
|
7387
7716
|
} else {
|
|
7388
7717
|
const rejectedFlush = autoRejectedV === void 0 ? "" : `; for (const _rejectedId of ${autoRejectedV}) { _ctx._grammarTrace?.write({ id: _rejectedId, phase: 'failure', offset: ${pos} }); _ctx._grammarTrace?.write({ id: _rejectedId, phase: 'backtrack', offset: ${pos} }) }`;
|
|
7389
7718
|
stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true${coverageBase === void 0 ? "" : `${rejectedFlush}; _ctx._grammarCoverage?.(${JSON.stringify(`${coverageBase}${i}`)}); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'selected', offset: ${pos}, end: ${endVar} }); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'success', offset: ${pos}, end: ${endVar} })`} }`);
|
|
7390
7719
|
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""}${coverageBase === void 0 ? "" : `; _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'failure', offset: ${atStart ? pos : "_ctx._fe"} }); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'backtrack', offset: ${pos} })`} }`);
|
|
7720
|
+
if (arm.mayCommit) stmts.push(`${ind(ctx)}if (!${resOkV} && _ctx._fc) ${committedFailBody(ctx)}`);
|
|
7391
7721
|
}
|
|
7392
7722
|
ctx.indent -= fsGuard ? 2 : 1;
|
|
7393
7723
|
if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
|
|
@@ -7544,7 +7874,8 @@ function emitMany(def, ctx, pos) {
|
|
|
7544
7874
|
`${ind(ctx)}if (!(${firstSetCond(lcV, def.parser._meta.firstSet)})) { ${rollback}break }`
|
|
7545
7875
|
);
|
|
7546
7876
|
}
|
|
7547
|
-
const
|
|
7877
|
+
const iter = emitFallible(def.parser, ctx, itemPos, true);
|
|
7878
|
+
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = iter;
|
|
7548
7879
|
stmts.push(...iterStmts);
|
|
7549
7880
|
if (ctx.recovery) {
|
|
7550
7881
|
const rrV = v(ctx, "_rr");
|
|
@@ -7563,7 +7894,8 @@ function emitMany(def, ctx, pos) {
|
|
|
7563
7894
|
`${ind(ctx)}if (${iterEnd} <= ${itemPos}) { ${rollback}break }`
|
|
7564
7895
|
);
|
|
7565
7896
|
} else {
|
|
7566
|
-
stmts.push(`${ind(ctx)}if (!${iterOk}
|
|
7897
|
+
stmts.push(`${ind(ctx)}if (!${iterOk}) { ${rollback}${iter.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
|
|
7898
|
+
stmts.push(`${ind(ctx)}if (${iterEnd} <= ${itemPos}) { ${rollback}break }`);
|
|
7567
7899
|
}
|
|
7568
7900
|
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
7569
7901
|
else if (maxV !== null) stmts.push(`${ind(ctx)}${maxV}++`);
|
|
@@ -7575,10 +7907,12 @@ function emitMany(def, ctx, pos) {
|
|
|
7575
7907
|
function emitOptional(def, ctx, pos) {
|
|
7576
7908
|
const valV = v(ctx, "_opt");
|
|
7577
7909
|
const endV = v(ctx, "_opte");
|
|
7578
|
-
const
|
|
7910
|
+
const inner = emitFallible(def.parser, ctx, pos, true);
|
|
7911
|
+
const { stmts: lblStmts, okVar, valVar, endVar } = inner;
|
|
7579
7912
|
const ind0 = ind(ctx);
|
|
7580
7913
|
const stmts = [
|
|
7581
7914
|
...lblStmts,
|
|
7915
|
+
...inner.mayCommit ? [`${ind0}if (!${okVar} && _ctx._fc) ${committedFailBody(ctx)}`] : [],
|
|
7582
7916
|
`${ind0}const ${valV} = ${okVar} ? ${valVar} : null`,
|
|
7583
7917
|
`${ind0}const ${endV} = ${okVar} ? ${endVar} : ${pos}`
|
|
7584
7918
|
];
|
|
@@ -7610,7 +7944,7 @@ function emitAttempt(p, def, ctx, pos) {
|
|
|
7610
7944
|
...preGuard,
|
|
7611
7945
|
`${ind(ctx)}const ${leaves} = _ctx._cstLeaves?.length ?? 0, ${raw} = _ctx._cstRawChildren?.length ?? 0, ${trivia2} = _ctx._cstTriviaLog?.length ?? 0, ${log} = _ctx._triviaLog?.length ?? 0, ${fields} = _ctx._fields?.length ?? 0, ${errors} = _ctx._errors?.length ?? 0`,
|
|
7612
7946
|
...inner.stmts,
|
|
7613
|
-
...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
|
|
7947
|
+
...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback}${inner.mayCommit ? ` if (_ctx._fc) ${committedFailBody(ctx)};` : ""} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
|
|
7614
7948
|
],
|
|
7615
7949
|
valueVar: inner.valVar,
|
|
7616
7950
|
endVar: inner.endVar
|
|
@@ -7624,18 +7958,21 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
7624
7958
|
const exp = rec ? hoistExpected(ctx, deriveExpectedArr([def.parser])) : "";
|
|
7625
7959
|
const sepSent = rec ? syncSentinelExpr(firstSetOf(def.separator)) : null;
|
|
7626
7960
|
const scanSync = sepSent ? `_ctx._rec.or(${sepSent}, ${mySyncV})` : mySyncV;
|
|
7627
|
-
const
|
|
7961
|
+
const first = emitFallible(def.parser, ctx, pos, true);
|
|
7962
|
+
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = first;
|
|
7628
7963
|
const stmts = [
|
|
7629
7964
|
`${ind(ctx)}const ${arrV} = []`,
|
|
7630
7965
|
`${ind(ctx)}let ${curV} = ${pos}`,
|
|
7631
7966
|
...rec ? [`${ind(ctx)}const ${mySyncV} = _ctx._sync`] : [],
|
|
7632
7967
|
...firstStmts
|
|
7633
7968
|
];
|
|
7634
|
-
const failItem = (nextOkVar, itemPosVar, breakStmt) => {
|
|
7635
|
-
if (
|
|
7969
|
+
const failItem = (nextOkVar, nextMayCommit, itemPosVar, breakStmt) => {
|
|
7970
|
+
const commit = nextMayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : "";
|
|
7971
|
+
if (!rec) return [`${ind(ctx)}if (!${nextOkVar}) { ${commit}${breakStmt} }`];
|
|
7636
7972
|
const rr = v(ctx, "_rr");
|
|
7637
7973
|
return [
|
|
7638
7974
|
`${ind(ctx)}if (!${nextOkVar}) {`,
|
|
7975
|
+
...nextMayCommit ? [`${ind(ctx)} if (_ctx._fc) ${committedFailBody(ctx)}`] : [],
|
|
7639
7976
|
`${ind(ctx)} if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPosVar}, _ctx)) {`,
|
|
7640
7977
|
`${ind(ctx)} const ${rr} = _ctx._rec.scan(input, ${itemPosVar}, _ctx, ${scanSync}, ${exp})`,
|
|
7641
7978
|
`${ind(ctx)} ${arrV}.push(${rr}.error); _ctx._rec.capture(_ctx, ${rr}.error); ${curV} = ${rr}.end; continue`,
|
|
@@ -7664,6 +8001,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
7664
8001
|
stmts.push(
|
|
7665
8002
|
`${ind(ctx)}let ${didV} = false`,
|
|
7666
8003
|
`${ind(ctx)}if (${firstOk}) { ${arrV}.push(${firstVal}); ${curV} = ${firstEnd}; ${didV} = true }`,
|
|
8004
|
+
...first.mayCommit ? [`${ind(ctx)}else if (_ctx._fc) ${committedFailBody(ctx)}`] : [],
|
|
7667
8005
|
`${ind(ctx)}else if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${pos}, _ctx)) {`,
|
|
7668
8006
|
`${ind(ctx)} const ${rr0} = _ctx._rec.scan(input, ${pos}, _ctx, ${scanSync}, ${exp})`,
|
|
7669
8007
|
`${ind(ctx)} ${arrV}.push(${rr0}.error); _ctx._rec.capture(_ctx, ${rr0}.error); ${curV} = ${rr0}.end; ${didV} = true`,
|
|
@@ -7674,6 +8012,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
7674
8012
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
7675
8013
|
ctx.indent++;
|
|
7676
8014
|
} else {
|
|
8015
|
+
if (first.mayCommit) stmts.push(`${ind(ctx)}if (!${firstOk} && _ctx._fc) ${committedFailBody(ctx)}`);
|
|
7677
8016
|
stmts.push(`${ind(ctx)}if (${firstOk}) {`);
|
|
7678
8017
|
ctx.indent++;
|
|
7679
8018
|
stmts.push(
|
|
@@ -7702,16 +8041,18 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
7702
8041
|
);
|
|
7703
8042
|
sepAtPos = spV;
|
|
7704
8043
|
const rollbackToSep = `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markV}) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${markTl}) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}; `;
|
|
7705
|
-
const
|
|
7706
|
-
stmts
|
|
8044
|
+
const sep = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
8045
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep;
|
|
8046
|
+
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}${sep.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
|
|
7707
8047
|
const post = postSepMarks();
|
|
7708
8048
|
stmts.push(...post.decl);
|
|
7709
8049
|
const npV = v(ctx, "_np");
|
|
7710
8050
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
7711
|
-
const
|
|
8051
|
+
const next = emitFallible(def.parser, ctx, npV, true);
|
|
8052
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
|
|
7712
8053
|
stmts.push(
|
|
7713
8054
|
...nextStmts,
|
|
7714
|
-
...failItem(nextOk, npV, itemFailBreak(sepEnd, rollbackToSep, post.rb)),
|
|
8055
|
+
...failItem(nextOk, next.mayCommit, npV, itemFailBreak(sepEnd, rollbackToSep, post.rb)),
|
|
7715
8056
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
7716
8057
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
7717
8058
|
);
|
|
@@ -7720,14 +8061,16 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
7720
8061
|
const spV = v(ctx, "_sp");
|
|
7721
8062
|
stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
|
|
7722
8063
|
sepAtPos = spV;
|
|
7723
|
-
const
|
|
7724
|
-
stmts
|
|
8064
|
+
const sep = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
8065
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep;
|
|
8066
|
+
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
|
|
7725
8067
|
const npV = v(ctx, "_np");
|
|
7726
8068
|
stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
|
|
7727
|
-
const
|
|
8069
|
+
const next = emitFallible(def.parser, ctx, npV, true);
|
|
8070
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
|
|
7728
8071
|
stmts.push(
|
|
7729
8072
|
...nextStmts,
|
|
7730
|
-
...failItem(nextOk, npV, itemFailBreak(sepEnd, "", "")),
|
|
8073
|
+
...failItem(nextOk, next.mayCommit, npV, itemFailBreak(sepEnd, "", "")),
|
|
7731
8074
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
7732
8075
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
7733
8076
|
);
|
|
@@ -7741,15 +8084,17 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
7741
8084
|
`${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
|
|
7742
8085
|
);
|
|
7743
8086
|
}
|
|
7744
|
-
const
|
|
7745
|
-
stmts
|
|
8087
|
+
const sep = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
8088
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep;
|
|
8089
|
+
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
|
|
7746
8090
|
const nextRb = markLv ? `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markRw}) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
7747
8091
|
const post = postSepMarks();
|
|
7748
8092
|
stmts.push(...post.decl);
|
|
7749
|
-
const
|
|
8093
|
+
const next = emitFallible(def.parser, ctx, sepEnd, true);
|
|
8094
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
|
|
7750
8095
|
stmts.push(
|
|
7751
8096
|
...nextStmts,
|
|
7752
|
-
...failItem(nextOk, sepEnd, itemFailBreak(sepEnd, nextRb, post.rb)),
|
|
8097
|
+
...failItem(nextOk, next.mayCommit, sepEnd, itemFailBreak(sepEnd, nextRb, post.rb)),
|
|
7753
8098
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
7754
8099
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
7755
8100
|
);
|
|
@@ -8130,7 +8475,7 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
8130
8475
|
const rv = v(ctx, "_rt");
|
|
8131
8476
|
const vv = v(ctx, "_rtv");
|
|
8132
8477
|
const ev = v(ctx, "_rte");
|
|
8133
|
-
const failStmt = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${rv}.span.start; _ctx._fx = ${rv}.expected; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : `return { ok: false, expected: ${rv}.expected, span: ${rv}.span }`;
|
|
8478
|
+
const failStmt = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${rv}.span.start; _ctx._fx = ${rv}.expected; _ctx._fc = ${rv}.committed === true; break ${ctx.failLabel} }` : `{ _ctx._fc = ${rv}.committed === true; break ${ctx.failLabel} }` : `return { ok: false, expected: ${rv}.expected, span: ${rv}.span, ...(${rv}.committed ? { committed: true } : {}) }`;
|
|
8134
8479
|
const stmts = [
|
|
8135
8480
|
`${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
|
|
8136
8481
|
...emitIfFail(ctx, `!${rv}.ok`, failStmt),
|
|
@@ -8211,7 +8556,7 @@ function emitRecover(def, ctx, pos) {
|
|
|
8211
8556
|
return { stmts, valueVar: valVar, endVar };
|
|
8212
8557
|
}
|
|
8213
8558
|
function emitExpect(def, ctx, pos) {
|
|
8214
|
-
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
8559
|
+
const { stmts: innerStmts, okVar, valVar, endVar, mayCommit } = emitFallible(def.parser, ctx, pos);
|
|
8215
8560
|
const ind0 = ind(ctx);
|
|
8216
8561
|
const errV = v(ctx, "_err");
|
|
8217
8562
|
const stmts = [
|
|
@@ -8223,6 +8568,7 @@ function emitExpect(def, ctx, pos) {
|
|
|
8223
8568
|
// interpreter (expect.ts) and the list-recovery emit sites — guarded on
|
|
8224
8569
|
// `_ctx._tolerant`, under which the driver always installs `_ctx._rec`.
|
|
8225
8570
|
`${ind0} if (_ctx._tolerant) _ctx._rec.capture(_ctx, ${errV})`,
|
|
8571
|
+
...mayCommit ? [`${ind0} _ctx._fc = false`] : [],
|
|
8226
8572
|
`${ind0} ${valVar} = ${errV}`,
|
|
8227
8573
|
`${ind0} ${endVar} = ${pos}`,
|
|
8228
8574
|
`${ind0} ${okVar} = true`,
|
|
@@ -8233,7 +8579,7 @@ function emitExpect(def, ctx, pos) {
|
|
|
8233
8579
|
function emit(p, ctx, pos) {
|
|
8234
8580
|
const replay = ctx.replayPrefix?.get(p);
|
|
8235
8581
|
if (replay !== void 0) return emitReplayPrefixLeaf(ctx, pos, replay);
|
|
8236
|
-
const
|
|
8582
|
+
const dispatch2 = (emissionPos = pos) => {
|
|
8237
8583
|
const savedRule = ctx.activeCoverageRuleId;
|
|
8238
8584
|
const rule = ctx.coverage?.plan.rules.get(p);
|
|
8239
8585
|
if (rule && p !== ctx.coverage?.entry && !ctx.suppressCoverageFailure) ctx.activeCoverageRuleId = rule;
|
|
@@ -8273,14 +8619,14 @@ function emit(p, ctx, pos) {
|
|
|
8273
8619
|
ctx.indent = 1;
|
|
8274
8620
|
ctx.failLabel = "_pfail";
|
|
8275
8621
|
ctx.recordFail = true;
|
|
8276
|
-
const r =
|
|
8622
|
+
const r = dispatch2("_pos");
|
|
8277
8623
|
ctx.indent = savedIndent;
|
|
8278
8624
|
ctx.failLabel = savedFailLabel;
|
|
8279
8625
|
ctx.recordFail = savedRecord;
|
|
8280
8626
|
pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
|
|
8281
8627
|
return instrument(emitNamedFnCall(ctx, fnName, pos));
|
|
8282
8628
|
}
|
|
8283
|
-
return instrument(
|
|
8629
|
+
return instrument(dispatch2());
|
|
8284
8630
|
}
|
|
8285
8631
|
function emitDispatch(p, ctx, pos) {
|
|
8286
8632
|
const bal = p._balancedAmbient;
|
|
@@ -8313,6 +8659,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
8313
8659
|
return emitSeq(def, ctx, pos);
|
|
8314
8660
|
case "choice":
|
|
8315
8661
|
return emitChoice(p, def, ctx, pos);
|
|
8662
|
+
case "dispatch":
|
|
8663
|
+
return emitDispatchCombinator(def, ctx, pos);
|
|
8316
8664
|
case "attempt":
|
|
8317
8665
|
return emitAttempt(p, def, ctx, pos);
|
|
8318
8666
|
case "many":
|
|
@@ -8533,6 +8881,8 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
8533
8881
|
case "sequence":
|
|
8534
8882
|
case "choice":
|
|
8535
8883
|
return d.parsers.some((x) => hasNodeDef(x, seen));
|
|
8884
|
+
case "dispatch":
|
|
8885
|
+
return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
|
|
8536
8886
|
case "sepBy":
|
|
8537
8887
|
return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
|
|
8538
8888
|
case "scanTo":
|
|
@@ -8566,6 +8916,8 @@ function childrenOf2(def) {
|
|
|
8566
8916
|
case "sequence":
|
|
8567
8917
|
case "choice":
|
|
8568
8918
|
return def.parsers;
|
|
8919
|
+
case "dispatch":
|
|
8920
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
8569
8921
|
case "many":
|
|
8570
8922
|
case "attempt":
|
|
8571
8923
|
case "optional":
|
|
@@ -8609,6 +8961,7 @@ function childrenOf2(def) {
|
|
|
8609
8961
|
var HOISTABLE_TAGS = /* @__PURE__ */ new Set([
|
|
8610
8962
|
"sequence",
|
|
8611
8963
|
"choice",
|
|
8964
|
+
"dispatch",
|
|
8612
8965
|
"many",
|
|
8613
8966
|
"oneOrMore",
|
|
8614
8967
|
"optional",
|
|
@@ -8702,7 +9055,7 @@ function ruleDependencies(ruleMap) {
|
|
|
8702
9055
|
const seen = /* @__PURE__ */ new Set();
|
|
8703
9056
|
const walk2 = (p, isRoot) => {
|
|
8704
9057
|
const def = p._def;
|
|
8705
|
-
const boundary = def.tag === "lazy" ?
|
|
9058
|
+
const boundary = def.tag === "lazy" ? nameOf.get(p) ?? p._ruleName : nameOf.get(p);
|
|
8706
9059
|
if (!isRoot && boundary !== void 0) {
|
|
8707
9060
|
found.add(boundary);
|
|
8708
9061
|
return;
|
|
@@ -9307,6 +9660,8 @@ function childrenOf3(def) {
|
|
|
9307
9660
|
case "sequence":
|
|
9308
9661
|
case "choice":
|
|
9309
9662
|
return def.parsers;
|
|
9663
|
+
case "dispatch":
|
|
9664
|
+
return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
|
|
9310
9665
|
case "many":
|
|
9311
9666
|
case "oneOrMore":
|
|
9312
9667
|
case "optional":
|
|
@@ -9341,7 +9696,7 @@ function childrenOf3(def) {
|
|
|
9341
9696
|
return [];
|
|
9342
9697
|
}
|
|
9343
9698
|
}
|
|
9344
|
-
var
|
|
9699
|
+
var ruleNameOf4 = (c) => c._ruleName;
|
|
9345
9700
|
function lazyTarget(c) {
|
|
9346
9701
|
if (c._def.tag !== "lazy") return null;
|
|
9347
9702
|
try {
|
|
@@ -9456,6 +9811,9 @@ function evalRuleMapIR(ir) {
|
|
|
9456
9811
|
"keywords",
|
|
9457
9812
|
"sequence",
|
|
9458
9813
|
"choice",
|
|
9814
|
+
"dispatch",
|
|
9815
|
+
"when",
|
|
9816
|
+
"otherwise",
|
|
9459
9817
|
"attempt",
|
|
9460
9818
|
"many",
|
|
9461
9819
|
"oneOrMore",
|
|
@@ -9489,6 +9847,9 @@ function evalRuleMapIR(ir) {
|
|
|
9489
9847
|
keywords,
|
|
9490
9848
|
sequence,
|
|
9491
9849
|
choice,
|
|
9850
|
+
dispatch,
|
|
9851
|
+
when,
|
|
9852
|
+
otherwise,
|
|
9492
9853
|
attempt,
|
|
9493
9854
|
many,
|
|
9494
9855
|
oneOrMore,
|
|
@@ -9523,12 +9884,16 @@ var Serializer = class {
|
|
|
9523
9884
|
emitted = /* @__PURE__ */ new Set();
|
|
9524
9885
|
decls = [];
|
|
9525
9886
|
ruleValues = /* @__PURE__ */ new Set();
|
|
9887
|
+
localRuleNames = /* @__PURE__ */ new Map();
|
|
9526
9888
|
ruleMap;
|
|
9527
9889
|
scanSkip;
|
|
9528
9890
|
constructor(ruleMap, scanSkip = []) {
|
|
9529
9891
|
this.ruleMap = ruleMap;
|
|
9530
9892
|
this.scanSkip = scanSkip;
|
|
9531
|
-
for (const [, c] of ruleMap)
|
|
9893
|
+
for (const [name, c] of ruleMap) {
|
|
9894
|
+
this.localRuleNames.set(c, name);
|
|
9895
|
+
this.ruleValues.add(this.body(c));
|
|
9896
|
+
}
|
|
9532
9897
|
}
|
|
9533
9898
|
/** A grammar-level `scanSkip` unit is an opaque TERMINAL (a string, a balanced
|
|
9534
9899
|
* bracket) — it must not reach a grammar-rule ref (`g[name]`), because the
|
|
@@ -9539,7 +9904,7 @@ var Serializer = class {
|
|
|
9539
9904
|
if (seen.has(c)) return;
|
|
9540
9905
|
seen.add(c);
|
|
9541
9906
|
if (c._def.tag === "lazy") {
|
|
9542
|
-
if (
|
|
9907
|
+
if (ruleNameOf4(c) !== void 0) throw new Unserializable("scanSkip unit references a grammar rule");
|
|
9543
9908
|
const target = lazyTarget(c);
|
|
9544
9909
|
if (target) this.assertNoRuleRef(target, seen);
|
|
9545
9910
|
return;
|
|
@@ -9550,7 +9915,7 @@ var Serializer = class {
|
|
|
9550
9915
|
* its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
|
|
9551
9916
|
* `g[name]` ref — only the top-level entry is unwrapped. */
|
|
9552
9917
|
body(c) {
|
|
9553
|
-
return c._def.tag === "lazy" &&
|
|
9918
|
+
return c._def.tag === "lazy" && ruleNameOf4(c) !== void 0 ? lazyTarget(c) ?? c : c;
|
|
9554
9919
|
}
|
|
9555
9920
|
run() {
|
|
9556
9921
|
for (const [, c] of this.ruleMap) this.analyze(this.body(c), /* @__PURE__ */ new Set());
|
|
@@ -9592,7 +9957,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
9592
9957
|
analyze(c, active) {
|
|
9593
9958
|
const def = c._def;
|
|
9594
9959
|
if (def.tag === "lazy") {
|
|
9595
|
-
if (
|
|
9960
|
+
if (ruleNameOf4(c) !== void 0) return;
|
|
9596
9961
|
const target = lazyTarget(c);
|
|
9597
9962
|
if (!target) throw new Unserializable("unresolved unnamed ref");
|
|
9598
9963
|
if (active.has(target)) {
|
|
@@ -9644,7 +10009,8 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
9644
10009
|
const kid = (x) => this.ref(x, selfOf);
|
|
9645
10010
|
switch (def.tag) {
|
|
9646
10011
|
case "lazy": {
|
|
9647
|
-
|
|
10012
|
+
const name = this.localRuleNames.get(c) ?? ruleNameOf4(c);
|
|
10013
|
+
if (name !== void 0) return `g[${JSON.stringify(name)}]`;
|
|
9648
10014
|
const target = lazyTarget(c);
|
|
9649
10015
|
if (selfOf && target === selfOf.comb) return selfOf.var;
|
|
9650
10016
|
return this.ref(target, selfOf);
|
|
@@ -9669,6 +10035,13 @@ ${outerDecls.map((d) => " " + d).join("\n")}
|
|
|
9669
10035
|
});
|
|
9670
10036
|
return `_gch([${items.join(", ")}])`;
|
|
9671
10037
|
}
|
|
10038
|
+
case "dispatch": {
|
|
10039
|
+
const arms = def.cases.map(
|
|
10040
|
+
(entry) => `when(${entry.keys.length === 1 ? JSON.stringify(entry.keys[0]) : JSON.stringify(entry.keys)}, ${kid(entry.parser)})`
|
|
10041
|
+
);
|
|
10042
|
+
if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)})`);
|
|
10043
|
+
return `dispatch(${kid(def.selector)}${arms.length === 0 ? "" : `, ${arms.join(", ")}`})`;
|
|
10044
|
+
}
|
|
9672
10045
|
case "many":
|
|
9673
10046
|
return `many(${kid(def.parser)}${repeatOpts(def.min, def.max)})`;
|
|
9674
10047
|
// A `oneOrMore` def with min > 1 came from `many(x, { min: n })`; round-trip
|
|
@@ -10888,6 +11261,12 @@ function coverageEntry(entry, collector, maps, trace) {
|
|
|
10888
11261
|
const unsafeSequence = sequence;
|
|
10889
11262
|
return unsafeSequence(...def.parsers.map(build));
|
|
10890
11263
|
}
|
|
11264
|
+
case "dispatch": {
|
|
11265
|
+
const arms = def.cases.map((entry2) => when(entry2.keys, build(entry2.parser)));
|
|
11266
|
+
if (def.otherwise) arms.push(otherwise(build(def.otherwise)));
|
|
11267
|
+
const unsafeDispatch = dispatch;
|
|
11268
|
+
return unsafeDispatch(build(def.selector), ...arms);
|
|
11269
|
+
}
|
|
10891
11270
|
case "many":
|
|
10892
11271
|
return many(build(def.parser));
|
|
10893
11272
|
case "oneOrMore":
|