parseman 0.27.1 → 0.28.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/README.md +1 -1
- package/dist/combinators/attempt.d.ts +8 -0
- package/dist/combinators/attempt.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/scanTo.d.ts.map +1 -1
- package/dist/combinators/token.d.ts +12 -0
- package/dist/combinators/token.d.ts.map +1 -1
- package/dist/compiler/build-arity.d.ts +8 -4
- package/dist/compiler/build-arity.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +12 -0
- 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 +19 -0
- package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -0
- package/dist/compiler/ir-serialize.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts +12 -1
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/value-usage.d.ts.map +1 -1
- package/dist/coverage.d.ts +68 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/functional/doc.d.ts.map +1 -1
- package/dist/functional/run.d.ts +13 -0
- package/dist/functional/run.d.ts.map +1 -1
- package/dist/index.cjs +816 -77
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +816 -77
- package/dist/index.js.map +4 -4
- package/dist/language-service/index.cjs +4 -1
- package/dist/language-service/index.cjs.map +2 -2
- package/dist/language-service/index.js +4 -1
- package/dist/language-service/index.js.map +2 -2
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +786 -113
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts +4 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +786 -113
- package/dist/plugin/index.js.map +4 -4
- package/dist/spec/index.cjs +3 -0
- package/dist/spec/index.cjs.map +2 -2
- package/dist/spec/index.js +3 -0
- package/dist/spec/index.js.map +2 -2
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -59,6 +59,7 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
59
59
|
case "label":
|
|
60
60
|
case "trivia":
|
|
61
61
|
case "token":
|
|
62
|
+
case "leaf":
|
|
62
63
|
case "expect":
|
|
63
64
|
case "withCtx":
|
|
64
65
|
case "node":
|
|
@@ -122,6 +123,7 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
122
123
|
case "label":
|
|
123
124
|
case "trivia":
|
|
124
125
|
case "token":
|
|
126
|
+
case "leaf":
|
|
125
127
|
case "node":
|
|
126
128
|
case "grammar":
|
|
127
129
|
case "expect":
|
|
@@ -190,10 +192,10 @@ function beginCstNodeCapture(ctx) {
|
|
|
190
192
|
}
|
|
191
193
|
function finishCstBuf(buf) {
|
|
192
194
|
if (!buf) return { children: EMPTY, rawChildren: EMPTY, triviaLog: EMPTY_TL };
|
|
193
|
-
const
|
|
195
|
+
const children2 = buf.ch ?? (buf.single !== void 0 ? [buf.single] : EMPTY);
|
|
194
196
|
const rawChildren = buf.raw ?? (buf.rawSingle !== void 0 ? [buf.rawSingle] : EMPTY);
|
|
195
197
|
const triviaLog = buf.tl ?? EMPTY_TL;
|
|
196
|
-
return { children, rawChildren, triviaLog };
|
|
198
|
+
return { children: children2, rawChildren, triviaLog };
|
|
197
199
|
}
|
|
198
200
|
function endCstNodeCapture(ctx, saved) {
|
|
199
201
|
const materialized = finishCstBuf(ctx._cstBuf);
|
|
@@ -205,8 +207,8 @@ function endCstNodeCapture(ctx, saved) {
|
|
|
205
207
|
ctx.captureTrivia = saved.cap;
|
|
206
208
|
return materialized;
|
|
207
209
|
}
|
|
208
|
-
function pushCstLeaf(ctx,
|
|
209
|
-
pushCstChild(ctx,
|
|
210
|
+
function pushCstLeaf(ctx, leaf2) {
|
|
211
|
+
pushCstChild(ctx, leaf2, leaf2);
|
|
210
212
|
}
|
|
211
213
|
function pushCstChild(ctx, built, rawEntry) {
|
|
212
214
|
const b = ctx._cstBuf;
|
|
@@ -940,10 +942,13 @@ function deriveExpected(c) {
|
|
|
940
942
|
return def.parsers.flatMap(deriveExpected);
|
|
941
943
|
case "sequence":
|
|
942
944
|
return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
|
|
945
|
+
case "attempt":
|
|
946
|
+
return deriveExpected(def.parser);
|
|
943
947
|
case "node":
|
|
944
948
|
case "grammar":
|
|
945
949
|
case "trivia":
|
|
946
950
|
case "token":
|
|
951
|
+
case "leaf":
|
|
947
952
|
case "optional":
|
|
948
953
|
case "many":
|
|
949
954
|
case "oneOrMore":
|
|
@@ -1721,6 +1726,24 @@ function sequence(...parsers) {
|
|
|
1721
1726
|
};
|
|
1722
1727
|
}
|
|
1723
1728
|
|
|
1729
|
+
// src/combinators/attempt.ts
|
|
1730
|
+
function attempt(parser2) {
|
|
1731
|
+
return {
|
|
1732
|
+
_tag: "attempt",
|
|
1733
|
+
_meta: parser2._meta,
|
|
1734
|
+
_def: { tag: "attempt", parser: parser2 },
|
|
1735
|
+
parse(input, pos, ctx) {
|
|
1736
|
+
const mark = saveCstMark(ctx);
|
|
1737
|
+
const log = ctx._triviaLog?.length;
|
|
1738
|
+
const result = parser2.parse(input, pos, ctx);
|
|
1739
|
+
if (result.ok) return result;
|
|
1740
|
+
rollbackCstCapture(ctx, mark);
|
|
1741
|
+
if (log !== void 0 && ctx._triviaLog) ctx._triviaLog.length = log;
|
|
1742
|
+
return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1724
1747
|
// src/combinators/repeat.ts
|
|
1725
1748
|
function repItem(combinator, input, cur, ctx) {
|
|
1726
1749
|
const mark = saveTriviaMark(ctx);
|
|
@@ -2030,10 +2053,14 @@ function markUnusedValues(root) {
|
|
|
2030
2053
|
case "label":
|
|
2031
2054
|
case "trivia":
|
|
2032
2055
|
case "token":
|
|
2056
|
+
case "attempt":
|
|
2033
2057
|
case "withCtx":
|
|
2034
2058
|
case "expect":
|
|
2035
2059
|
visit(def.parser, consumed);
|
|
2036
2060
|
return;
|
|
2061
|
+
case "leaf":
|
|
2062
|
+
visit(def.parser, true);
|
|
2063
|
+
return;
|
|
2037
2064
|
case "lazy":
|
|
2038
2065
|
return;
|
|
2039
2066
|
default:
|
|
@@ -2156,6 +2183,20 @@ function confirmedBuildArity(src) {
|
|
|
2156
2183
|
if (/\barguments\b/.test(s)) return null;
|
|
2157
2184
|
return parts.length;
|
|
2158
2185
|
}
|
|
2186
|
+
function buildReadsChildren(def) {
|
|
2187
|
+
if (!def.build) return true;
|
|
2188
|
+
const src = def.buildSrc ?? def.build.toString();
|
|
2189
|
+
const arity = confirmedBuildArity(src);
|
|
2190
|
+
if (arity === null) return true;
|
|
2191
|
+
return arity >= 1;
|
|
2192
|
+
}
|
|
2193
|
+
function buildReadsRaw(def) {
|
|
2194
|
+
if (!def.build) return true;
|
|
2195
|
+
const src = def.buildSrc ?? def.build.toString();
|
|
2196
|
+
const arity = confirmedBuildArity(src);
|
|
2197
|
+
if (arity === null) return true;
|
|
2198
|
+
return arity >= 4;
|
|
2199
|
+
}
|
|
2159
2200
|
function buildReadsTrivia(def) {
|
|
2160
2201
|
if (!def.build) return true;
|
|
2161
2202
|
const src = def.buildSrc ?? def.build.toString();
|
|
@@ -2211,9 +2252,11 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2211
2252
|
case "many":
|
|
2212
2253
|
case "oneOrMore":
|
|
2213
2254
|
case "optional":
|
|
2255
|
+
case "attempt":
|
|
2214
2256
|
case "transform":
|
|
2215
2257
|
case "trivia":
|
|
2216
2258
|
case "token":
|
|
2259
|
+
case "leaf":
|
|
2217
2260
|
case "label":
|
|
2218
2261
|
case "expect":
|
|
2219
2262
|
case "withCtx":
|
|
@@ -2246,9 +2289,11 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2246
2289
|
case "keywords":
|
|
2247
2290
|
case "guard":
|
|
2248
2291
|
case "token":
|
|
2292
|
+
case "leaf":
|
|
2249
2293
|
return false;
|
|
2250
2294
|
// Transparent single-child wrappers: recurse.
|
|
2251
2295
|
case "optional":
|
|
2296
|
+
case "attempt":
|
|
2252
2297
|
case "transform":
|
|
2253
2298
|
case "trivia":
|
|
2254
2299
|
case "label":
|
|
@@ -2295,6 +2340,7 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2295
2340
|
case "many":
|
|
2296
2341
|
case "oneOrMore":
|
|
2297
2342
|
case "optional":
|
|
2343
|
+
case "attempt":
|
|
2298
2344
|
case "transform":
|
|
2299
2345
|
case "trivia":
|
|
2300
2346
|
case "token":
|
|
@@ -2304,6 +2350,10 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2304
2350
|
case "withCtx":
|
|
2305
2351
|
case "not":
|
|
2306
2352
|
return parserEnablesTriviaCapture(d.parser, seen);
|
|
2353
|
+
// leaf() clears the inner CST/trivia collector and publishes only its own
|
|
2354
|
+
// terminal result. An inner parser() capture cannot reach this node.
|
|
2355
|
+
case "leaf":
|
|
2356
|
+
return false;
|
|
2307
2357
|
case "lazy": {
|
|
2308
2358
|
try {
|
|
2309
2359
|
return parserEnablesTriviaCapture(d.thunk(), seen);
|
|
@@ -2382,12 +2432,12 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
|
2382
2432
|
const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
|
|
2383
2433
|
ctx._fields = savedFields;
|
|
2384
2434
|
ctx._triviaCaptureMask = savedMask;
|
|
2385
|
-
const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
|
|
2435
|
+
const { children: children2, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
|
|
2386
2436
|
if (!r.ok) return r;
|
|
2387
2437
|
const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
|
|
2388
2438
|
const nodeType = def.type ?? missingInferredType();
|
|
2389
2439
|
const cstOutput = ctx.build?._parsemanCstOutput === true;
|
|
2390
|
-
const built = unwrap &&
|
|
2440
|
+
const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : !build && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, r.span, rawChildren, triviaLog, st) : build(children2, fields, r.span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, r.span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
|
|
2391
2441
|
const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span: r.span };
|
|
2392
2442
|
if (saved.buf !== void 0 || saved.ch !== void 0) {
|
|
2393
2443
|
pushCstChild(ctx, built, rawEntry);
|
|
@@ -2618,6 +2668,141 @@ function token(root) {
|
|
|
2618
2668
|
}
|
|
2619
2669
|
};
|
|
2620
2670
|
}
|
|
2671
|
+
function leaf(root, fn) {
|
|
2672
|
+
const meta = {
|
|
2673
|
+
firstSet: root._meta.firstSet,
|
|
2674
|
+
canMatchNewline: root._meta.canMatchNewline,
|
|
2675
|
+
isTrivia: false
|
|
2676
|
+
};
|
|
2677
|
+
return {
|
|
2678
|
+
_tag: "leaf",
|
|
2679
|
+
_meta: meta,
|
|
2680
|
+
_def: { tag: "leaf", parser: root, fn },
|
|
2681
|
+
parse(input, pos, ctx) {
|
|
2682
|
+
const savedBuf = ctx._cstBuf;
|
|
2683
|
+
const savedChildren = ctx._cstChildren;
|
|
2684
|
+
const savedLeaves = ctx._cstLeaves;
|
|
2685
|
+
const savedRaw = ctx._cstRawChildren;
|
|
2686
|
+
const savedTriviaLog = ctx._cstTriviaLog;
|
|
2687
|
+
const savedOuterTriviaLog = ctx._triviaLog;
|
|
2688
|
+
const wasCapturing = cstCaptureActive(ctx);
|
|
2689
|
+
ctx._cstBuf = void 0;
|
|
2690
|
+
ctx._cstChildren = void 0;
|
|
2691
|
+
ctx._cstLeaves = void 0;
|
|
2692
|
+
ctx._cstRawChildren = void 0;
|
|
2693
|
+
ctx._cstTriviaLog = void 0;
|
|
2694
|
+
delete ctx._triviaLog;
|
|
2695
|
+
let result;
|
|
2696
|
+
try {
|
|
2697
|
+
result = root.parse(input, pos, ctx);
|
|
2698
|
+
} finally {
|
|
2699
|
+
ctx._cstBuf = savedBuf;
|
|
2700
|
+
ctx._cstChildren = savedChildren;
|
|
2701
|
+
ctx._cstLeaves = savedLeaves;
|
|
2702
|
+
ctx._cstRawChildren = savedRaw;
|
|
2703
|
+
ctx._cstTriviaLog = savedTriviaLog;
|
|
2704
|
+
if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
|
|
2705
|
+
else ctx._triviaLog = savedOuterTriviaLog;
|
|
2706
|
+
}
|
|
2707
|
+
if (!result.ok) return result;
|
|
2708
|
+
const value = fn(result.value, result.span);
|
|
2709
|
+
if (wasCapturing) pushCstLeaf(ctx, { _tag: "leaf", value, span: result.span });
|
|
2710
|
+
return { ok: true, value, span: result.span };
|
|
2711
|
+
}
|
|
2712
|
+
};
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
// src/compiler/grammar-coverage-ids.ts
|
|
2716
|
+
function children(def, winners) {
|
|
2717
|
+
switch (def.tag) {
|
|
2718
|
+
case "sequence":
|
|
2719
|
+
case "choice":
|
|
2720
|
+
return def.parsers;
|
|
2721
|
+
case "many":
|
|
2722
|
+
case "oneOrMore":
|
|
2723
|
+
case "optional":
|
|
2724
|
+
case "attempt":
|
|
2725
|
+
case "transform":
|
|
2726
|
+
case "trivia":
|
|
2727
|
+
case "token":
|
|
2728
|
+
case "leaf":
|
|
2729
|
+
case "label":
|
|
2730
|
+
case "field":
|
|
2731
|
+
case "grammar":
|
|
2732
|
+
case "not":
|
|
2733
|
+
case "node":
|
|
2734
|
+
case "guard":
|
|
2735
|
+
case "withCtx":
|
|
2736
|
+
case "recover":
|
|
2737
|
+
case "expect":
|
|
2738
|
+
return "parser" in def ? [def.parser] : [];
|
|
2739
|
+
case "sepBy":
|
|
2740
|
+
return [def.parser, def.separator];
|
|
2741
|
+
case "skip":
|
|
2742
|
+
return [def.main, def.skipped];
|
|
2743
|
+
case "scanTo":
|
|
2744
|
+
return [def.sentinel, ...def.skip];
|
|
2745
|
+
case "lazy": {
|
|
2746
|
+
let resolved;
|
|
2747
|
+
try {
|
|
2748
|
+
resolved = def.thunk();
|
|
2749
|
+
} catch {
|
|
2750
|
+
return [];
|
|
2751
|
+
}
|
|
2752
|
+
const name = resolved._ruleName;
|
|
2753
|
+
return name && winners?.[name] ? [winners[name]] : [resolved];
|
|
2754
|
+
}
|
|
2755
|
+
default:
|
|
2756
|
+
return [];
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
function buildGrammarPlan(entry, winners) {
|
|
2760
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
2761
|
+
const choices = /* @__PURE__ */ new WeakMap();
|
|
2762
|
+
const attempts = /* @__PURE__ */ new WeakMap();
|
|
2763
|
+
const labels = /* @__PURE__ */ new WeakMap();
|
|
2764
|
+
const rules2 = /* @__PURE__ */ new WeakMap();
|
|
2765
|
+
const winnerNames = /* @__PURE__ */ new Map();
|
|
2766
|
+
if (winners) {
|
|
2767
|
+
for (const [name, winner] of Object.entries(winners)) {
|
|
2768
|
+
winnerNames.set(winner, name);
|
|
2769
|
+
if (winner._def.tag === "lazy") {
|
|
2770
|
+
try {
|
|
2771
|
+
winnerNames.set(winner._def.thunk(), name);
|
|
2772
|
+
} catch {
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2778
|
+
const visit = (parser2, path) => {
|
|
2779
|
+
if (seen.has(parser2)) return;
|
|
2780
|
+
seen.add(parser2);
|
|
2781
|
+
const rule = winnerNames.get(parser2) ?? parser2._ruleName;
|
|
2782
|
+
if (rule && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
|
|
2783
|
+
const id = `rule:${rule}`;
|
|
2784
|
+
definitions.set(id, { id, kind: "rule" });
|
|
2785
|
+
rules2.set(parser2, id);
|
|
2786
|
+
}
|
|
2787
|
+
if (parser2._def.tag === "choice") {
|
|
2788
|
+
const ids = parser2._def.parsers.map((_, index) => `choice:${path}/arm:${index}`);
|
|
2789
|
+
choices.set(parser2, ids);
|
|
2790
|
+
ids.forEach((id) => definitions.set(id, { id, kind: "choice-arm" }));
|
|
2791
|
+
}
|
|
2792
|
+
if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${path}`);
|
|
2793
|
+
if (parser2._def.tag === "label") {
|
|
2794
|
+
const id = `label:${path}`;
|
|
2795
|
+
labels.set(parser2, [id]);
|
|
2796
|
+
definitions.set(id, { id, kind: "label" });
|
|
2797
|
+
}
|
|
2798
|
+
children(parser2._def, winners).forEach((child, index) => visit(child, `${path}/${parser2._def.tag}:${index}`));
|
|
2799
|
+
};
|
|
2800
|
+
const roots = Array.isArray(entry) ? entry : [entry];
|
|
2801
|
+
for (const root of roots) {
|
|
2802
|
+
visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
|
|
2803
|
+
}
|
|
2804
|
+
return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, attempts, labels, rules: rules2 };
|
|
2805
|
+
}
|
|
2621
2806
|
|
|
2622
2807
|
// src/compiler/scannable-run.ts
|
|
2623
2808
|
var BACKSLASH = 92;
|
|
@@ -4002,21 +4187,23 @@ function probeUpdate(ctx, expectedArr, posExpr, constant = true) {
|
|
|
4002
4187
|
}
|
|
4003
4188
|
function failBody(ctx, expected, posExpr) {
|
|
4004
4189
|
const probe = probeUpdate(ctx, `[${expected}]`, posExpr);
|
|
4190
|
+
const trace = ctx.activeCoverageRuleId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(ctx.activeCoverageRuleId)}, phase: 'failure', offset: ${posExpr} }); `;
|
|
4005
4191
|
if (ctx.failLabel) {
|
|
4006
|
-
if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
|
|
4007
|
-
return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
|
|
4192
|
+
if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
|
|
4193
|
+
return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; ${trace}break ${ctx.failLabel} }`;
|
|
4008
4194
|
}
|
|
4009
|
-
return probe + failReturn(expected, posExpr);
|
|
4195
|
+
return probe + trace + failReturn(expected, posExpr);
|
|
4010
4196
|
}
|
|
4011
4197
|
function failArrBody(ctx, expectedArr, posExpr, constant = true) {
|
|
4012
4198
|
const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
|
|
4199
|
+
const trace = ctx.activeCoverageRuleId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(ctx.activeCoverageRuleId)}, phase: 'failure', offset: ${posExpr} }); `;
|
|
4013
4200
|
if (ctx.failLabel) {
|
|
4014
|
-
if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
|
|
4201
|
+
if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
|
|
4015
4202
|
const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
|
|
4016
|
-
return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
|
|
4203
|
+
return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; ${trace}break ${ctx.failLabel} }`;
|
|
4017
4204
|
}
|
|
4018
|
-
if (!constant) return `${probe}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
4019
|
-
return probe + failReturnArr(expectedArr, posExpr);
|
|
4205
|
+
if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
4206
|
+
return probe + trace + failReturnArr(expectedArr, posExpr);
|
|
4020
4207
|
}
|
|
4021
4208
|
function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
|
|
4022
4209
|
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
|
|
@@ -4051,7 +4238,7 @@ var NAMED_FN_END = "_pfEnd";
|
|
|
4051
4238
|
function namedFnPrelude() {
|
|
4052
4239
|
return [`const ${NAMED_FN_FAIL} = {}`, `let ${NAMED_FN_END}`];
|
|
4053
4240
|
}
|
|
4054
|
-
function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
4241
|
+
function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar, failureRuleId) {
|
|
4055
4242
|
ctx.namedFnDecls.push([
|
|
4056
4243
|
`function ${fnName}(input, _pos, _ctx) {`,
|
|
4057
4244
|
` _pfail: {`,
|
|
@@ -4059,6 +4246,7 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
|
4059
4246
|
` ${NAMED_FN_END} = ${endVar}`,
|
|
4060
4247
|
` return ${valueVar}`,
|
|
4061
4248
|
` }`,
|
|
4249
|
+
...failureRuleId === void 0 ? [] : [` _ctx._grammarTrace?.write({ id: ${JSON.stringify(failureRuleId)}, phase: 'failure', offset: _ctx._fe ?? _pos })`],
|
|
4062
4250
|
` return ${NAMED_FN_FAIL}`,
|
|
4063
4251
|
`}`
|
|
4064
4252
|
].join("\n"));
|
|
@@ -4220,6 +4408,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
4220
4408
|
case "not":
|
|
4221
4409
|
case "trivia":
|
|
4222
4410
|
case "token":
|
|
4411
|
+
case "leaf":
|
|
4223
4412
|
case "scanTo":
|
|
4224
4413
|
case "unknown":
|
|
4225
4414
|
return false;
|
|
@@ -4231,6 +4420,8 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
4231
4420
|
// nothing committed remains.
|
|
4232
4421
|
case "choice":
|
|
4233
4422
|
return false;
|
|
4423
|
+
case "attempt":
|
|
4424
|
+
return false;
|
|
4234
4425
|
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
4235
4426
|
case "optional":
|
|
4236
4427
|
case "many":
|
|
@@ -4283,6 +4474,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
4283
4474
|
case "keywords":
|
|
4284
4475
|
case "node":
|
|
4285
4476
|
case "token":
|
|
4477
|
+
case "leaf":
|
|
4286
4478
|
return true;
|
|
4287
4479
|
case "not":
|
|
4288
4480
|
case "guard":
|
|
@@ -4297,6 +4489,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
4297
4489
|
case "many":
|
|
4298
4490
|
case "oneOrMore":
|
|
4299
4491
|
case "optional":
|
|
4492
|
+
case "attempt":
|
|
4300
4493
|
case "transform":
|
|
4301
4494
|
case "label":
|
|
4302
4495
|
case "field":
|
|
@@ -4638,35 +4831,55 @@ function needsFirstSetGuard(p) {
|
|
|
4638
4831
|
const fs = p._meta.firstSet;
|
|
4639
4832
|
return fs.kind !== "any" && !canMatchEmptyAtStart(p);
|
|
4640
4833
|
}
|
|
4641
|
-
function
|
|
4642
|
-
|
|
4834
|
+
function coverageHit(ctx, id, offset = "_pos", end) {
|
|
4835
|
+
return id === void 0 ? [] : [
|
|
4836
|
+
`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(id)})`,
|
|
4837
|
+
`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'selected', offset: ${offset}${end === void 0 ? "" : `, end: ${end}`} })`,
|
|
4838
|
+
`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'success', offset: ${offset}${end === void 0 ? "" : `, end: ${end}`} })`
|
|
4839
|
+
];
|
|
4840
|
+
}
|
|
4841
|
+
function coverageAttempt(ctx, id, offset) {
|
|
4842
|
+
return id === void 0 ? [] : [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'attempt', offset: ${offset} })`];
|
|
4843
|
+
}
|
|
4844
|
+
function coverageFailureBacktrack(ctx, id, failureOffset, startOffset) {
|
|
4845
|
+
return id === void 0 ? [] : [
|
|
4846
|
+
`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'failure', offset: ${failureOffset} })`,
|
|
4847
|
+
`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'backtrack', offset: ${startOffset} })`
|
|
4848
|
+
];
|
|
4849
|
+
}
|
|
4850
|
+
function emitDisjointArm(p, ctx, pos, valV, endV, coverageId) {
|
|
4851
|
+
if (failsAtStart(p) && coverageId === void 0) {
|
|
4643
4852
|
const r = emit(p, ctx, pos);
|
|
4644
|
-
return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}
|
|
4853
|
+
return [...coverageAttempt(ctx, coverageId, pos), ...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`, ...coverageHit(ctx, coverageId, pos, r.endVar)];
|
|
4645
4854
|
}
|
|
4646
4855
|
const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
4647
4856
|
return [
|
|
4857
|
+
...coverageAttempt(ctx, coverageId, pos),
|
|
4648
4858
|
...stmts,
|
|
4649
|
-
...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
|
|
4859
|
+
...emitIfFail(ctx, `!${okVar}`, `${coverageFailureBacktrack(ctx, coverageId, "_ctx._fe", pos).join("; ")}; ${failArrBody(ctx, "_ctx._fx", pos, false)}`),
|
|
4650
4860
|
`${ind(ctx)}${valV} = ${valVar}`,
|
|
4651
|
-
`${ind(ctx)}${endV} = ${endVar}
|
|
4861
|
+
`${ind(ctx)}${endV} = ${endVar}`,
|
|
4862
|
+
...coverageHit(ctx, coverageId, pos, endVar)
|
|
4652
4863
|
];
|
|
4653
4864
|
}
|
|
4654
|
-
function emitDisjointArmGated(def, i, ctx, pos, valV, endV) {
|
|
4865
|
+
function emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageId) {
|
|
4655
4866
|
const p = def.parsers[i];
|
|
4656
4867
|
const gate = def.gates[i];
|
|
4657
|
-
if (!gate) return emitDisjointArm(p, ctx, pos, valV, endV);
|
|
4868
|
+
if (!gate) return emitDisjointArm(p, ctx, pos, valV, endV, coverageId);
|
|
4658
4869
|
const gateIdx = ctx.mapFns.length;
|
|
4659
4870
|
ctx.mapFns.push(gate);
|
|
4660
4871
|
ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
|
|
4661
4872
|
const gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
4662
4873
|
const stmts = [`${ind(ctx)}if (${gateCond}) {`];
|
|
4663
4874
|
ctx.indent++;
|
|
4664
|
-
stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
|
|
4875
|
+
stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV, coverageId));
|
|
4665
4876
|
ctx.indent--;
|
|
4666
4877
|
stmts.push(`${ind(ctx)}} else { ${failArrBody(ctx, deriveExpectedArr([p]), pos)} }`);
|
|
4667
4878
|
return stmts;
|
|
4668
4879
|
}
|
|
4669
|
-
function emitChoice(def, ctx, pos) {
|
|
4880
|
+
function emitChoice(parser2, def, ctx, pos) {
|
|
4881
|
+
const coverageIds = ctx.coverage?.plan.choices.get(parser2);
|
|
4882
|
+
const coverageBase = coverageIds?.[0]?.slice(0, -1);
|
|
4670
4883
|
const allExpected = deriveExpectedArr(def.parsers);
|
|
4671
4884
|
if (def.disjoint) {
|
|
4672
4885
|
const codeV = v(ctx, "_code");
|
|
@@ -4685,7 +4898,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
4685
4898
|
stmts.push(`${ind(ctx)}{`);
|
|
4686
4899
|
ctx.indent++;
|
|
4687
4900
|
stmts.push(
|
|
4688
|
-
...emitDisjointArmGated(def, i, ctx, pos, valV, endV),
|
|
4901
|
+
...emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageIds?.[i]),
|
|
4689
4902
|
`${ind(ctx)}break`
|
|
4690
4903
|
);
|
|
4691
4904
|
ctx.indent--;
|
|
@@ -4704,16 +4917,16 @@ function emitChoice(def, ctx, pos) {
|
|
|
4704
4917
|
first = false;
|
|
4705
4918
|
stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
|
|
4706
4919
|
ctx.indent++;
|
|
4707
|
-
stmts.push(...emitDisjointArmGated(def, i, ctx, pos, valV, endV));
|
|
4920
|
+
stmts.push(...emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageIds?.[i]));
|
|
4708
4921
|
ctx.indent--;
|
|
4709
4922
|
stmts.push(`${ind(ctx)}}`);
|
|
4710
4923
|
}
|
|
4711
4924
|
stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
|
|
4712
4925
|
return { stmts, valueVar: valV, endVar: endV };
|
|
4713
4926
|
}
|
|
4714
|
-
return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
|
|
4927
|
+
return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos, coverageBase);
|
|
4715
4928
|
}
|
|
4716
|
-
function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
4929
|
+
function emitGreedyClassify(def, superIndex, ctx, pos, coverageBase) {
|
|
4717
4930
|
const superParser = def.parsers[superIndex];
|
|
4718
4931
|
const regexDef = getCoreRegexDef(superParser);
|
|
4719
4932
|
const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
|
|
@@ -4729,10 +4942,12 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
|
4729
4942
|
const endV = v(ctx, "_ge");
|
|
4730
4943
|
const valV = v(ctx, "_gcv");
|
|
4731
4944
|
const regexExpected = JSON.stringify(deriveExpected(superParser));
|
|
4945
|
+
const superCoverageId = coverageBase === void 0 ? void 0 : `${coverageBase}${superIndex}`;
|
|
4732
4946
|
const stmts = [
|
|
4947
|
+
...coverageAttempt(ctx, superCoverageId, pos),
|
|
4733
4948
|
`${ind(ctx)}${reVar}.lastIndex = ${pos}`,
|
|
4734
4949
|
`${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
|
|
4735
|
-
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
|
|
4950
|
+
...emitIfFail(ctx, `${matchV} === null`, `${coverageFailureBacktrack(ctx, superCoverageId, pos, pos).join("; ")}; ${failArrBody(ctx, regexExpected, pos)}`),
|
|
4736
4951
|
`${ind(ctx)}const ${wordV} = ${matchV}[0]`,
|
|
4737
4952
|
`${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
|
|
4738
4953
|
`${ind(ctx)}let ${valV}`
|
|
@@ -4748,8 +4963,9 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
|
4748
4963
|
stmts.push(`${ind(ctx)}${kw} (${wordV} === ${JSON.stringify(litVal)}) {`);
|
|
4749
4964
|
ctx.indent++;
|
|
4750
4965
|
const tR = emitTransformChain(p, JSON.stringify(litVal), endV, pos, ctx);
|
|
4966
|
+
stmts.push(...coverageFailureBacktrack(ctx, superCoverageId, pos, pos));
|
|
4751
4967
|
stmts.push(...emitLeafCapture(ctx, JSON.stringify(litVal), pos, endV));
|
|
4752
|
-
stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}
|
|
4968
|
+
stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}`, ...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${i}`, pos, endV));
|
|
4753
4969
|
ctx.indent--;
|
|
4754
4970
|
stmts.push(`${ind(ctx)}}`);
|
|
4755
4971
|
}
|
|
@@ -4757,12 +4973,12 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
|
4757
4973
|
const regexKw = first ? "if" : "else";
|
|
4758
4974
|
stmts.push(`${ind(ctx)}${regexKw} {`);
|
|
4759
4975
|
ctx.indent++;
|
|
4760
|
-
stmts.push(...emitLeafCapture(ctx, wordV, pos, endV), ...rR.stmts, `${ind(ctx)}${valV} = ${rR.valueVar}
|
|
4976
|
+
stmts.push(...emitLeafCapture(ctx, wordV, pos, endV), ...rR.stmts, `${ind(ctx)}${valV} = ${rR.valueVar}`, ...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${superIndex}`, pos, endV));
|
|
4761
4977
|
ctx.indent--;
|
|
4762
4978
|
stmts.push(`${ind(ctx)}}`);
|
|
4763
4979
|
return { stmts, valueVar: valV, endVar: endV };
|
|
4764
4980
|
}
|
|
4765
|
-
function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
4981
|
+
function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos, coverageBase) {
|
|
4766
4982
|
const valV = v(ctx, "_llv");
|
|
4767
4983
|
const endV = v(ctx, "_lle");
|
|
4768
4984
|
const stmts = [`${ind(ctx)}let ${valV}, ${endV} = ${pos}`];
|
|
@@ -4779,10 +4995,12 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
4779
4995
|
ctx.indent++;
|
|
4780
4996
|
const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
|
|
4781
4997
|
stmts.push(
|
|
4998
|
+
...coverageAttempt(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${idx}`, pos),
|
|
4782
4999
|
...emitLeafCapture(ctx, JSON.stringify(litVal), pos, litEnd),
|
|
4783
5000
|
...tR.stmts,
|
|
4784
5001
|
`${ind(ctx)}${valV} = ${tR.valueVar}`,
|
|
4785
|
-
`${ind(ctx)}${endV} = ${litEnd}
|
|
5002
|
+
`${ind(ctx)}${endV} = ${litEnd}`,
|
|
5003
|
+
...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${idx}`, pos, litEnd)
|
|
4786
5004
|
);
|
|
4787
5005
|
ctx.indent--;
|
|
4788
5006
|
stmts.push(`${ind(ctx)}}`);
|
|
@@ -4790,16 +5008,18 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
4790
5008
|
stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
|
|
4791
5009
|
return { stmts, valueVar: valV, endVar: endV };
|
|
4792
5010
|
}
|
|
4793
|
-
function emitFirstMatch(def, ctx, pos) {
|
|
5011
|
+
function emitFirstMatch(def, ctx, pos, coverageBase) {
|
|
4794
5012
|
const resValV = v(ctx, "_crv");
|
|
4795
5013
|
const resEndV = v(ctx, "_cre");
|
|
4796
5014
|
const resOkV = v(ctx, "_crok");
|
|
4797
5015
|
const codeV = v(ctx, "_chcode");
|
|
4798
5016
|
const slots = def.parsers.map(() => v(ctx, "_cfx"));
|
|
5017
|
+
const autoRejectedV = coverageBase === void 0 ? void 0 : v(ctx, "_carej");
|
|
4799
5018
|
const ind0 = ind(ctx);
|
|
4800
5019
|
const stmts = [
|
|
4801
5020
|
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
|
|
4802
5021
|
`${ind0}let ${slots.join(", ")}`,
|
|
5022
|
+
...autoRejectedV === void 0 ? [] : [`${ind0}const ${autoRejectedV} = []`],
|
|
4803
5023
|
`${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
|
|
4804
5024
|
];
|
|
4805
5025
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
@@ -4835,6 +5055,7 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
4835
5055
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
4836
5056
|
if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
|
|
4837
5057
|
ctx.indent += fsGuard ? 2 : 1;
|
|
5058
|
+
if (coverageBase !== void 0) stmts.push(`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'attempt', offset: ${pos} })`);
|
|
4838
5059
|
if (markLeaves) {
|
|
4839
5060
|
stmts.push(
|
|
4840
5061
|
`${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
|
|
@@ -4857,13 +5078,21 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
4857
5078
|
stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
|
|
4858
5079
|
stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
|
|
4859
5080
|
stmts.push(`${ind(ctx)} ${resOkV} = true`);
|
|
5081
|
+
if (autoRejectedV !== void 0) {
|
|
5082
|
+
stmts.push(`${ind(ctx)} for (const _rejectedId of ${autoRejectedV}) { _ctx._grammarTrace?.write({ id: _rejectedId, phase: 'failure', offset: ${pos} }); _ctx._grammarTrace?.write({ id: _rejectedId, phase: 'backtrack', offset: ${pos} }) }`);
|
|
5083
|
+
}
|
|
5084
|
+
stmts.push(...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${i}`, pos, endVar));
|
|
4860
5085
|
stmts.push(`${ind(ctx)} }`);
|
|
5086
|
+
if (autoRejectedV !== void 0) {
|
|
5087
|
+
stmts.push(`${ind(ctx)} else { ${autoRejectedV}.push(${JSON.stringify(`${coverageBase}${i}`)}) }`);
|
|
5088
|
+
}
|
|
4861
5089
|
stmts.push(`${ind(ctx)}}`);
|
|
4862
|
-
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
|
|
5090
|
+
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} });`} }`);
|
|
4863
5091
|
if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
|
|
4864
5092
|
} else {
|
|
4865
|
-
|
|
4866
|
-
stmts.push(`${ind(ctx)}
|
|
5093
|
+
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} }) }`;
|
|
5094
|
+
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} })`} }`);
|
|
5095
|
+
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} })`} }`);
|
|
4867
5096
|
}
|
|
4868
5097
|
ctx.indent -= fsGuard ? 2 : 1;
|
|
4869
5098
|
if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
|
|
@@ -4873,12 +5102,12 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
4873
5102
|
stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
|
|
4874
5103
|
return { stmts, valueVar: resValV, endVar: resEndV };
|
|
4875
5104
|
}
|
|
4876
|
-
function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
5105
|
+
function emitNonDisjoint(def, strategy, allExpected, ctx, pos, coverageBase) {
|
|
4877
5106
|
if (strategy.tag === "greedyClassify")
|
|
4878
|
-
return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
|
|
5107
|
+
return emitGreedyClassify(def, strategy.superIndex, ctx, pos, coverageBase);
|
|
4879
5108
|
if (strategy.tag === "literalsLongestFirst")
|
|
4880
|
-
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
4881
|
-
return emitFirstMatch(def, ctx, pos);
|
|
5109
|
+
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos, coverageBase);
|
|
5110
|
+
return emitFirstMatch(def, ctx, pos, coverageBase);
|
|
4882
5111
|
}
|
|
4883
5112
|
function pushMapFn(ctx, fn, src) {
|
|
4884
5113
|
if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
|
|
@@ -4996,6 +5225,27 @@ function emitOptional(def, ctx, pos) {
|
|
|
4996
5225
|
];
|
|
4997
5226
|
return { stmts, valueVar: valV, endVar: endV };
|
|
4998
5227
|
}
|
|
5228
|
+
function emitAttempt(p, def, ctx, pos) {
|
|
5229
|
+
const inner = emitFallible(def.parser, ctx, pos);
|
|
5230
|
+
const leaves = v(ctx, "_atl");
|
|
5231
|
+
const raw = v(ctx, "_atr");
|
|
5232
|
+
const trivia2 = v(ctx, "_att");
|
|
5233
|
+
const log = v(ctx, "_atg");
|
|
5234
|
+
const fields = v(ctx, "_atf");
|
|
5235
|
+
const errors = v(ctx, "_ate");
|
|
5236
|
+
const rollback = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${leaves}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${raw}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${trivia2}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${log}; if (_ctx._fields) _ctx._fields.length = ${fields}; if (_ctx._errors) _ctx._errors.length = ${errors}`;
|
|
5237
|
+
const traceId = ctx.coverage?.plan.attempts.get(p);
|
|
5238
|
+
const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
|
|
5239
|
+
return {
|
|
5240
|
+
stmts: [
|
|
5241
|
+
`${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`,
|
|
5242
|
+
...inner.stmts,
|
|
5243
|
+
...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
|
|
5244
|
+
],
|
|
5245
|
+
valueVar: inner.valVar,
|
|
5246
|
+
endVar: inner.endVar
|
|
5247
|
+
};
|
|
5248
|
+
}
|
|
4999
5249
|
function emitSepBy(_p, def, ctx, pos) {
|
|
5000
5250
|
const arrV = v(ctx, "_arr");
|
|
5001
5251
|
const curV = v(ctx, "_cur");
|
|
@@ -5286,6 +5536,36 @@ function emitToken(def, ctx, pos) {
|
|
|
5286
5536
|
endVar: inner.endVar
|
|
5287
5537
|
};
|
|
5288
5538
|
}
|
|
5539
|
+
function emitLeaf(def, ctx, pos) {
|
|
5540
|
+
const inner = emitFallible(def.parser, ctx, pos);
|
|
5541
|
+
const i = ind(ctx);
|
|
5542
|
+
const sc = v(ctx, "_leafCh");
|
|
5543
|
+
const sl = v(ctx, "_leafLv");
|
|
5544
|
+
const sr = v(ctx, "_leafRaw");
|
|
5545
|
+
const stl = v(ctx, "_leafTl");
|
|
5546
|
+
const sol = v(ctx, "_leafLog");
|
|
5547
|
+
const sb = v(ctx, "_leafBuf");
|
|
5548
|
+
const rawV = v(ctx, "_leafRawValue");
|
|
5549
|
+
const valV = v(ctx, "_leaf");
|
|
5550
|
+
const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
|
|
5551
|
+
return {
|
|
5552
|
+
stmts: [
|
|
5553
|
+
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${stl} = _ctx._cstTriviaLog, ${sol} = _ctx._triviaLog, ${sb} = _ctx._cstBuf`,
|
|
5554
|
+
`${i}_ctx._cstChildren = undefined; _ctx._cstLeaves = undefined; _ctx._cstRawChildren = undefined; _ctx._cstTriviaLog = undefined; _ctx._triviaLog = undefined; _ctx._cstBuf = undefined`,
|
|
5555
|
+
`${i}try {`,
|
|
5556
|
+
...reindentStmts(inner.stmts, ctx.indent + 1).map((stmt) => stmt.replace(/^(\s*)(?:const|let)\s+/, "$1var ")),
|
|
5557
|
+
`${i}} finally {`,
|
|
5558
|
+
`${i} _ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx._cstTriviaLog = ${stl}; _ctx._triviaLog = ${sol}; _ctx._cstBuf = ${sb}`,
|
|
5559
|
+
`${i}}`,
|
|
5560
|
+
...emitIfFail(ctx, `!${inner.okVar}`, propagateFailBody(ctx)),
|
|
5561
|
+
`${i}const ${rawV} = ${inner.valVar}`,
|
|
5562
|
+
`${i}const ${valV} = ${mfRef(ctx)}[${fnIdx}](${rawV}, { start: ${pos}, end: ${inner.endVar} })`,
|
|
5563
|
+
...emitLeafCapture(ctx, valV, pos, inner.endVar)
|
|
5564
|
+
],
|
|
5565
|
+
valueVar: valV,
|
|
5566
|
+
endVar: inner.endVar
|
|
5567
|
+
};
|
|
5568
|
+
}
|
|
5289
5569
|
function emitNode(def, ctx, pos) {
|
|
5290
5570
|
if (def.type === void 0) {
|
|
5291
5571
|
throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
|
|
@@ -5301,6 +5581,8 @@ function emitNode(def, ctx, pos) {
|
|
|
5301
5581
|
const i = ind(ctx);
|
|
5302
5582
|
const capturesTrivia = mkType !== null || def.captureTrivia === true || def.trailingTrivia === true || !structural && buildReadsTrivia(def);
|
|
5303
5583
|
const clonesState = !structural && buildReadsState(def);
|
|
5584
|
+
const capturesChildren = !structural && (mkType !== null || def.unwrap || def.collapse || buildReadsChildren(def));
|
|
5585
|
+
const capturesRaw = !structural && (mkType !== null || buildReadsRaw(def));
|
|
5304
5586
|
const hasFields = parserHasOwnFields(def.parser);
|
|
5305
5587
|
const capturesFields = hasFields && !structural && buildReadsFields(def);
|
|
5306
5588
|
const innerEnablesTriviaCapture = parserEnablesTriviaCapture(def.parser);
|
|
@@ -5319,9 +5601,13 @@ function emitNode(def, ctx, pos) {
|
|
|
5319
5601
|
const profHoist = `${i}const ${pmV} = _ctx._pmProfile, ${recV} = ${pmV}?.phase === 'recognizer', ${capV} = ${pmV}?.phase === 'capture'`;
|
|
5320
5602
|
const profileRecognizer = recV;
|
|
5321
5603
|
const profileCapture = capV;
|
|
5604
|
+
const directCstV = !structural && (!capturesChildren || !capturesRaw) ? v(ctx, "_dcst") : null;
|
|
5605
|
+
const directCstGate = directCstV === null ? "false" : `${profileCapture} || _ctx.build?._parsemanCstOutput === true`;
|
|
5322
5606
|
const structuralCapturesTrivia = structural && (def.captureTrivia === true || def.trailingTrivia === true);
|
|
5323
5607
|
const allocStmt = structural ? `${i}const ${capTLv} = !(${profileRecognizer}) && (${profileCapture} || ${structuralCapturesTrivia ? "true" : hostTriviaGate}), ${capSTv} = !(${profileRecognizer} || ${profileCapture}) && (_ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 6)))${capFv ? `, ${capFv} = !(${profileRecognizer}) && (${profileCapture} || (_ctx.build !== undefined && _hostReads(_ctx.build, 2)))` : ""}
|
|
5324
|
-
${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${
|
|
5608
|
+
${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
|
|
5609
|
+
` : ""}${i}const ${capTLv} = !(${profileRecognizer}), ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}, ${tlV} = ${profileRecognizer} ? undefined : []` : `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
|
|
5610
|
+
` : ""}${i}const ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}`;
|
|
5325
5611
|
const innerTl = structural || capturesTrivia ? structural && !innerEnablesTriviaCapture ? `${capTLv} ? ${tlV} : undefined` : tlV : "undefined";
|
|
5326
5612
|
const fieldsOn = structural ? capFv ?? "false" : `${profileRecognizer} ? false : ${capturesFields ? "true" : "false"}`;
|
|
5327
5613
|
const sf = hasFields ? v(ctx, "_sf") : null;
|
|
@@ -5438,14 +5724,23 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
5438
5724
|
const savedIndent = ctx.indent;
|
|
5439
5725
|
const savedFailLabel = ctx.failLabel;
|
|
5440
5726
|
const savedRecord = ctx.recordFail;
|
|
5727
|
+
const failureRuleId = p === ctx.coverage?.entry ? void 0 : ctx.coverage?.plan.rules.get(p) ?? ctx.coverage?.plan.rules.get(resolved);
|
|
5728
|
+
const savedActiveRule = ctx.activeCoverageRuleId;
|
|
5729
|
+
const savedSuppressFailure = ctx.suppressCoverageFailure;
|
|
5441
5730
|
ctx.indent = 1;
|
|
5442
5731
|
ctx.failLabel = "_pfail";
|
|
5443
5732
|
ctx.recordFail = true;
|
|
5733
|
+
if (failureRuleId !== void 0) {
|
|
5734
|
+
ctx.activeCoverageRuleId = void 0;
|
|
5735
|
+
ctx.suppressCoverageFailure = true;
|
|
5736
|
+
}
|
|
5444
5737
|
const r = emit(resolved, ctx, "_pos");
|
|
5445
5738
|
ctx.indent = savedIndent;
|
|
5446
5739
|
ctx.failLabel = savedFailLabel;
|
|
5447
5740
|
ctx.recordFail = savedRecord;
|
|
5448
|
-
|
|
5741
|
+
ctx.activeCoverageRuleId = savedActiveRule;
|
|
5742
|
+
ctx.suppressCoverageFailure = savedSuppressFailure;
|
|
5743
|
+
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar, failureRuleId);
|
|
5449
5744
|
}
|
|
5450
5745
|
const fnName = ctx.namedParsers.get(p);
|
|
5451
5746
|
return emitNamedFnCall(ctx, fnName, pos);
|
|
@@ -5496,6 +5791,30 @@ function emitExpect(def, ctx, pos) {
|
|
|
5496
5791
|
return { stmts, valueVar: valVar, endVar };
|
|
5497
5792
|
}
|
|
5498
5793
|
function emit(p, ctx, pos) {
|
|
5794
|
+
const dispatch = (emissionPos = pos) => {
|
|
5795
|
+
const savedRule = ctx.activeCoverageRuleId;
|
|
5796
|
+
const rule = ctx.coverage?.plan.rules.get(p);
|
|
5797
|
+
if (rule && p !== ctx.coverage?.entry && !ctx.suppressCoverageFailure) ctx.activeCoverageRuleId = rule;
|
|
5798
|
+
const result = emitDispatch(p, ctx, emissionPos);
|
|
5799
|
+
ctx.activeCoverageRuleId = savedRule;
|
|
5800
|
+
return result;
|
|
5801
|
+
};
|
|
5802
|
+
const instrument = (r) => {
|
|
5803
|
+
const ruleId = ctx.coverage?.plan.rules.get(p);
|
|
5804
|
+
const labelIds = ctx.coverage?.plan.labels.get(p);
|
|
5805
|
+
if (!ruleId && !labelIds) return r;
|
|
5806
|
+
return {
|
|
5807
|
+
...r,
|
|
5808
|
+
stmts: [
|
|
5809
|
+
...ruleId ? [`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(ruleId)})`] : [],
|
|
5810
|
+
...ruleId ? [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'enter', offset: ${pos} })`] : [],
|
|
5811
|
+
...r.stmts,
|
|
5812
|
+
...ruleId ? [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'success', offset: ${pos}, end: ${r.endVar} })`] : [],
|
|
5813
|
+
...labelIds?.flatMap((id) => [`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(id)})`]) ?? [],
|
|
5814
|
+
...labelIds?.flatMap((id) => [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'success', offset: ${pos}, end: ${r.endVar} })`]) ?? []
|
|
5815
|
+
]
|
|
5816
|
+
};
|
|
5817
|
+
};
|
|
5499
5818
|
const usage = ctx.lazyUsage;
|
|
5500
5819
|
if (usage && !ctx.noHoist && !ctx.capAsTrivia && !ctx.ruleNames?.has(p) && isHoistableTag(p._def.tag) && (usage.counts.get(p) ?? 0) > 1 && // Only hoist subtrees big enough that de-duplicating them beats the call
|
|
5501
5820
|
// overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
|
|
@@ -5512,14 +5831,14 @@ function emit(p, ctx, pos) {
|
|
|
5512
5831
|
ctx.indent = 1;
|
|
5513
5832
|
ctx.failLabel = "_pfail";
|
|
5514
5833
|
ctx.recordFail = true;
|
|
5515
|
-
const r =
|
|
5834
|
+
const r = dispatch("_pos");
|
|
5516
5835
|
ctx.indent = savedIndent;
|
|
5517
5836
|
ctx.failLabel = savedFailLabel;
|
|
5518
5837
|
ctx.recordFail = savedRecord;
|
|
5519
5838
|
pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
|
|
5520
|
-
return emitNamedFnCall(ctx, fnName, pos);
|
|
5839
|
+
return instrument(emitNamedFnCall(ctx, fnName, pos));
|
|
5521
5840
|
}
|
|
5522
|
-
return
|
|
5841
|
+
return instrument(dispatch());
|
|
5523
5842
|
}
|
|
5524
5843
|
function emitDispatch(p, ctx, pos) {
|
|
5525
5844
|
const def = p._def;
|
|
@@ -5533,7 +5852,9 @@ function emitDispatch(p, ctx, pos) {
|
|
|
5533
5852
|
case "sequence":
|
|
5534
5853
|
return emitSeq(def, ctx, pos);
|
|
5535
5854
|
case "choice":
|
|
5536
|
-
return emitChoice(def, ctx, pos);
|
|
5855
|
+
return emitChoice(p, def, ctx, pos);
|
|
5856
|
+
case "attempt":
|
|
5857
|
+
return emitAttempt(p, def, ctx, pos);
|
|
5537
5858
|
case "many":
|
|
5538
5859
|
case "oneOrMore":
|
|
5539
5860
|
return emitMany(def, ctx, pos);
|
|
@@ -5602,6 +5923,8 @@ function emitDispatch(p, ctx, pos) {
|
|
|
5602
5923
|
return emit(def.parser, ctx, pos);
|
|
5603
5924
|
case "token":
|
|
5604
5925
|
return emitToken(def, ctx, pos);
|
|
5926
|
+
case "leaf":
|
|
5927
|
+
return emitLeaf(def, ctx, pos);
|
|
5605
5928
|
case "label": {
|
|
5606
5929
|
const inner = emitFallible(def.parser, ctx, pos);
|
|
5607
5930
|
return {
|
|
@@ -5733,6 +6056,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
5733
6056
|
case "grammar":
|
|
5734
6057
|
case "trivia":
|
|
5735
6058
|
case "token":
|
|
6059
|
+
case "leaf":
|
|
5736
6060
|
case "label":
|
|
5737
6061
|
case "field":
|
|
5738
6062
|
case "optional":
|
|
@@ -5760,16 +6084,32 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
5760
6084
|
return false;
|
|
5761
6085
|
}
|
|
5762
6086
|
}
|
|
6087
|
+
function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
6088
|
+
if (seen.has(p)) return false;
|
|
6089
|
+
seen.add(p);
|
|
6090
|
+
const d = p._def;
|
|
6091
|
+
if (d.tag === "node" && d.build !== void 0) return true;
|
|
6092
|
+
if (d.tag === "lazy") {
|
|
6093
|
+
try {
|
|
6094
|
+
return hasDirectBuildDef(d.thunk(), seen);
|
|
6095
|
+
} catch {
|
|
6096
|
+
return false;
|
|
6097
|
+
}
|
|
6098
|
+
}
|
|
6099
|
+
return childrenOf(d).some((child) => hasDirectBuildDef(child, seen));
|
|
6100
|
+
}
|
|
5763
6101
|
function childrenOf(def) {
|
|
5764
6102
|
switch (def.tag) {
|
|
5765
6103
|
case "sequence":
|
|
5766
6104
|
case "choice":
|
|
5767
6105
|
return def.parsers;
|
|
5768
6106
|
case "many":
|
|
6107
|
+
case "attempt":
|
|
5769
6108
|
case "optional":
|
|
5770
6109
|
case "transform":
|
|
5771
6110
|
case "trivia":
|
|
5772
6111
|
case "token":
|
|
6112
|
+
case "leaf":
|
|
5773
6113
|
case "label":
|
|
5774
6114
|
case "field":
|
|
5775
6115
|
case "grammar":
|
|
@@ -5944,10 +6284,25 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
5944
6284
|
namedFnDecls: [],
|
|
5945
6285
|
capturing: hasNodeDef(combinator),
|
|
5946
6286
|
recovery: opts?.recovery ?? false,
|
|
6287
|
+
...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
|
|
5947
6288
|
lazyUsage: analyzeLazyUsage(combinator),
|
|
5948
6289
|
...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
|
|
5949
6290
|
};
|
|
5950
6291
|
const r = emit(combinator, ctx, "_pos");
|
|
6292
|
+
const coverageRootRuleId = ctx.coverage?.plan.rules.get(combinator);
|
|
6293
|
+
const resultBody = coverageRootRuleId === void 0 ? [
|
|
6294
|
+
` let pos = _pos`,
|
|
6295
|
+
...r.stmts,
|
|
6296
|
+
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
|
|
6297
|
+
] : [
|
|
6298
|
+
` const _coverageResult = (() => {`,
|
|
6299
|
+
` let pos = _pos`,
|
|
6300
|
+
...r.stmts,
|
|
6301
|
+
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
6302
|
+
` })()`,
|
|
6303
|
+
` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
|
|
6304
|
+
` return _coverageResult`
|
|
6305
|
+
];
|
|
5951
6306
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
5952
6307
|
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
5953
6308
|
const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
|
|
@@ -5960,9 +6315,7 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
5960
6315
|
...namedPrelude,
|
|
5961
6316
|
ctx.namedFnDecls.join("\n\n"),
|
|
5962
6317
|
`function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
|
|
5963
|
-
|
|
5964
|
-
...r.stmts,
|
|
5965
|
-
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
6318
|
+
...resultBody,
|
|
5966
6319
|
`}`
|
|
5967
6320
|
].join("\n");
|
|
5968
6321
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
@@ -5972,9 +6325,15 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
5972
6325
|
...ctx.expectedDecls,
|
|
5973
6326
|
...namedPrelude,
|
|
5974
6327
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
5975
|
-
`let pos = _pos`,
|
|
5976
|
-
|
|
5977
|
-
|
|
6328
|
+
...coverageRootRuleId === void 0 ? [`let pos = _pos`, ...r.stmts, `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`] : [
|
|
6329
|
+
`const _coverageResult = (() => {`,
|
|
6330
|
+
` let pos = _pos`,
|
|
6331
|
+
...r.stmts,
|
|
6332
|
+
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
6333
|
+
`})()`,
|
|
6334
|
+
`if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
|
|
6335
|
+
`return _coverageResult`
|
|
6336
|
+
]
|
|
5978
6337
|
].join("\n"));
|
|
5979
6338
|
const defaultCtx = {
|
|
5980
6339
|
trackLines: false,
|
|
@@ -5986,10 +6345,11 @@ function compile(combinator, mapFnSources, opts) {
|
|
|
5986
6345
|
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
5987
6346
|
const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
|
|
5988
6347
|
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
5989
|
-
const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
|
|
6348
|
+
const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources, coverageRootRuleId) : null;
|
|
5990
6349
|
return {
|
|
5991
6350
|
source,
|
|
5992
6351
|
inlineExpression,
|
|
6352
|
+
...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
|
|
5993
6353
|
parse(input, pos = 0) {
|
|
5994
6354
|
return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
|
|
5995
6355
|
},
|
|
@@ -6052,8 +6412,13 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
6052
6412
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
6053
6413
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
6054
6414
|
namedFnDecls: [],
|
|
6055
|
-
|
|
6415
|
+
// A recognition-only fragment normally has no node collector of its own.
|
|
6416
|
+
// Leaf composition can re-lower it beneath a local semantic node, though;
|
|
6417
|
+
// in that case its terminals must feed the caller's collector rather than
|
|
6418
|
+
// merely returning their scalar parse values.
|
|
6419
|
+
capturing: opts?.captureTerminals === true || ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
6056
6420
|
recovery: opts?.recovery ?? false,
|
|
6421
|
+
...opts?.coverage ? { coverage: { plan: opts.coverage } } : {},
|
|
6057
6422
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
6058
6423
|
ns,
|
|
6059
6424
|
deferFirstSetRefs: true,
|
|
@@ -6127,7 +6492,14 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
6127
6492
|
ctx.indent = savedIndent;
|
|
6128
6493
|
ctx.failLabel = savedFail;
|
|
6129
6494
|
ctx.recordFail = savedRec;
|
|
6130
|
-
|
|
6495
|
+
const ruleId = ctx.coverage?.plan.rules.get(rule);
|
|
6496
|
+
const stmts = ruleId === void 0 ? body.stmts : [
|
|
6497
|
+
`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(ruleId)})`,
|
|
6498
|
+
`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'enter', offset: _pos })`,
|
|
6499
|
+
...body.stmts,
|
|
6500
|
+
`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'success', offset: _pos, end: ${body.endVar} })`
|
|
6501
|
+
];
|
|
6502
|
+
pushNamedFnDecl(ctx, fn, stmts, body.valueVar, body.endVar);
|
|
6131
6503
|
}
|
|
6132
6504
|
perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
|
|
6133
6505
|
}
|
|
@@ -6184,15 +6556,46 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
6184
6556
|
deps: ruleDependencies(ruleMap),
|
|
6185
6557
|
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
6186
6558
|
needsHostReads: !!ctx.needsHostReads,
|
|
6559
|
+
hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
6560
|
+
isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule)),
|
|
6187
6561
|
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
6188
6562
|
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
6189
6563
|
};
|
|
6190
6564
|
}
|
|
6191
|
-
function
|
|
6192
|
-
const
|
|
6565
|
+
function hasSemanticReduction(roots) {
|
|
6566
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6567
|
+
const visit = (parser2) => {
|
|
6568
|
+
if (seen.has(parser2)) return false;
|
|
6569
|
+
seen.add(parser2);
|
|
6570
|
+
const def = parser2._def;
|
|
6571
|
+
if (def.tag === "transform" && !def.recognitionOnly) return true;
|
|
6572
|
+
if (def.tag === "choice" && def.gates.some(Boolean)) return true;
|
|
6573
|
+
if (def.tag === "guard" || def.tag === "withCtx") return true;
|
|
6574
|
+
if (def.tag === "node" && def.build !== void 0) return true;
|
|
6575
|
+
if (def.tag === "lazy") {
|
|
6576
|
+
try {
|
|
6577
|
+
return visit(def.thunk());
|
|
6578
|
+
} catch {
|
|
6579
|
+
return true;
|
|
6580
|
+
}
|
|
6581
|
+
}
|
|
6582
|
+
return childrenOf(def).some(visit);
|
|
6583
|
+
};
|
|
6584
|
+
return roots.some(visit);
|
|
6585
|
+
}
|
|
6586
|
+
function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootRuleId) {
|
|
6587
|
+
const bodyLines = coverageRootRuleId === void 0 ? [
|
|
6193
6588
|
` let pos = _pos`,
|
|
6194
6589
|
...r.stmts,
|
|
6195
6590
|
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
|
|
6591
|
+
] : [
|
|
6592
|
+
` const _coverageResult = (() => {`,
|
|
6593
|
+
` let pos = _pos`,
|
|
6594
|
+
...r.stmts,
|
|
6595
|
+
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
6596
|
+
` })()`,
|
|
6597
|
+
` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
|
|
6598
|
+
` return _coverageResult`
|
|
6196
6599
|
];
|
|
6197
6600
|
const innerFn = [
|
|
6198
6601
|
`function(input, _pos, _ctx) {`,
|
|
@@ -6247,8 +6650,8 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
|
6247
6650
|
};
|
|
6248
6651
|
const emit2 = (end) => {
|
|
6249
6652
|
if (end > pos && cstCaptureActive(ctx)) {
|
|
6250
|
-
const
|
|
6251
|
-
pushCstLeaf(ctx,
|
|
6653
|
+
const leaf2 = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
|
|
6654
|
+
pushCstLeaf(ctx, leaf2);
|
|
6252
6655
|
}
|
|
6253
6656
|
};
|
|
6254
6657
|
while (cur < input.length) {
|
|
@@ -6302,6 +6705,7 @@ function balanced(open, close, options = {}) {
|
|
|
6302
6705
|
);
|
|
6303
6706
|
if (combi._def.tag === "transform") {
|
|
6304
6707
|
combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
|
|
6708
|
+
combi._def.recognitionOnly = true;
|
|
6305
6709
|
}
|
|
6306
6710
|
self.define(combi);
|
|
6307
6711
|
return combi;
|
|
@@ -6347,9 +6751,11 @@ function childrenOf2(def) {
|
|
|
6347
6751
|
case "many":
|
|
6348
6752
|
case "oneOrMore":
|
|
6349
6753
|
case "optional":
|
|
6754
|
+
case "attempt":
|
|
6350
6755
|
case "transform":
|
|
6351
6756
|
case "trivia":
|
|
6352
6757
|
case "token":
|
|
6758
|
+
case "leaf":
|
|
6353
6759
|
case "label":
|
|
6354
6760
|
case "field":
|
|
6355
6761
|
case "not":
|
|
@@ -6398,7 +6804,7 @@ function serializeRuleMap(ruleMap) {
|
|
|
6398
6804
|
}
|
|
6399
6805
|
}
|
|
6400
6806
|
function evalRuleMapIR(ir) {
|
|
6401
|
-
const _tf = (child, src) => {
|
|
6807
|
+
const _tf = (child, src, recognitionOnly = false) => {
|
|
6402
6808
|
let fn2;
|
|
6403
6809
|
try {
|
|
6404
6810
|
fn2 = (0, eval)(`(${src})`);
|
|
@@ -6409,8 +6815,22 @@ function evalRuleMapIR(ir) {
|
|
|
6409
6815
|
}
|
|
6410
6816
|
const t = transform(child, fn2);
|
|
6411
6817
|
t._def.fnSrc = src;
|
|
6818
|
+
if (recognitionOnly) t._def.recognitionOnly = true;
|
|
6412
6819
|
return t;
|
|
6413
6820
|
};
|
|
6821
|
+
const _lf = (child, src) => {
|
|
6822
|
+
let fn2;
|
|
6823
|
+
try {
|
|
6824
|
+
fn2 = (0, eval)(`(${src})`);
|
|
6825
|
+
} catch {
|
|
6826
|
+
fn2 = () => {
|
|
6827
|
+
throw new Error("IR leaf fn not materialized");
|
|
6828
|
+
};
|
|
6829
|
+
}
|
|
6830
|
+
const l = leaf(child, fn2);
|
|
6831
|
+
l._def.fnSrc = src;
|
|
6832
|
+
return l;
|
|
6833
|
+
};
|
|
6414
6834
|
const _nd = (type, child, src, opts, staticError) => {
|
|
6415
6835
|
if (staticError !== void 0 && staticError.length > 0) {
|
|
6416
6836
|
throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
|
|
@@ -6469,6 +6889,7 @@ function evalRuleMapIR(ir) {
|
|
|
6469
6889
|
"keywords",
|
|
6470
6890
|
"sequence",
|
|
6471
6891
|
"choice",
|
|
6892
|
+
"attempt",
|
|
6472
6893
|
"many",
|
|
6473
6894
|
"oneOrMore",
|
|
6474
6895
|
"optional",
|
|
@@ -6478,6 +6899,7 @@ function evalRuleMapIR(ir) {
|
|
|
6478
6899
|
"parser",
|
|
6479
6900
|
"scanTo",
|
|
6480
6901
|
"token",
|
|
6902
|
+
"leaf",
|
|
6481
6903
|
"transform",
|
|
6482
6904
|
"skip",
|
|
6483
6905
|
"trivia",
|
|
@@ -6485,6 +6907,7 @@ function evalRuleMapIR(ir) {
|
|
|
6485
6907
|
"field",
|
|
6486
6908
|
"expect",
|
|
6487
6909
|
"_tf",
|
|
6910
|
+
"_lf",
|
|
6488
6911
|
"_nd",
|
|
6489
6912
|
"_gch",
|
|
6490
6913
|
"_wc",
|
|
@@ -6498,6 +6921,7 @@ function evalRuleMapIR(ir) {
|
|
|
6498
6921
|
keywords,
|
|
6499
6922
|
sequence,
|
|
6500
6923
|
choice,
|
|
6924
|
+
attempt,
|
|
6501
6925
|
many,
|
|
6502
6926
|
oneOrMore,
|
|
6503
6927
|
optional,
|
|
@@ -6507,6 +6931,7 @@ function evalRuleMapIR(ir) {
|
|
|
6507
6931
|
parser,
|
|
6508
6932
|
scanTo,
|
|
6509
6933
|
token,
|
|
6934
|
+
leaf,
|
|
6510
6935
|
transform,
|
|
6511
6936
|
skip,
|
|
6512
6937
|
trivia,
|
|
@@ -6514,6 +6939,7 @@ function evalRuleMapIR(ir) {
|
|
|
6514
6939
|
field,
|
|
6515
6940
|
expect,
|
|
6516
6941
|
_tf,
|
|
6942
|
+
_lf,
|
|
6517
6943
|
_nd,
|
|
6518
6944
|
_gch,
|
|
6519
6945
|
_wc
|
|
@@ -6645,6 +7071,8 @@ ${body}
|
|
|
6645
7071
|
return `oneOrMore(${kid(def.parser)})`;
|
|
6646
7072
|
case "optional":
|
|
6647
7073
|
return `optional(${kid(def.parser)})`;
|
|
7074
|
+
case "attempt":
|
|
7075
|
+
return `attempt(${kid(def.parser)})`;
|
|
6648
7076
|
case "sepBy":
|
|
6649
7077
|
return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
|
|
6650
7078
|
case "not":
|
|
@@ -6653,6 +7081,10 @@ ${body}
|
|
|
6653
7081
|
return `trivia(${kid(def.parser)})`;
|
|
6654
7082
|
case "token":
|
|
6655
7083
|
return `token(${kid(def.parser)})`;
|
|
7084
|
+
case "leaf": {
|
|
7085
|
+
if (def.fnSrc === void 0) throw new Unserializable("leaf without fnSrc");
|
|
7086
|
+
return `_lf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
|
|
7087
|
+
}
|
|
6656
7088
|
case "label":
|
|
6657
7089
|
return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
|
|
6658
7090
|
case "field":
|
|
@@ -6665,7 +7097,7 @@ ${body}
|
|
|
6665
7097
|
return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}], orEOF: ${def.orEOF} })`;
|
|
6666
7098
|
case "transform": {
|
|
6667
7099
|
if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
|
|
6668
|
-
return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
|
|
7100
|
+
return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)}${def.recognitionOnly ? ", true" : ""})`;
|
|
6669
7101
|
}
|
|
6670
7102
|
case "node": {
|
|
6671
7103
|
if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
|
|
@@ -6717,15 +7149,15 @@ function normalizeCstCollapse(collapse) {
|
|
|
6717
7149
|
}
|
|
6718
7150
|
return typeof collapse === "function" ? collapse : void 0;
|
|
6719
7151
|
}
|
|
6720
|
-
function buildCstNode(type,
|
|
6721
|
-
return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...
|
|
7152
|
+
function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
|
|
7153
|
+
return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children2] };
|
|
6722
7154
|
}
|
|
6723
|
-
function cstBuildHost(typeOrOptions,
|
|
7155
|
+
function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
|
|
6724
7156
|
if (typeof typeOrOptions === "string") {
|
|
6725
|
-
return buildCstNode(typeOrOptions,
|
|
7157
|
+
return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
|
|
6726
7158
|
}
|
|
6727
7159
|
const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
|
|
6728
|
-
const host = (type,
|
|
7160
|
+
const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3, fields, span2, rawChildren2, triviaLog2, state2);
|
|
6729
7161
|
host._parsemanCstOutput = true;
|
|
6730
7162
|
if (collapse) host._parsemanCstCollapse = collapse;
|
|
6731
7163
|
return host;
|
|
@@ -6784,13 +7216,39 @@ function fuseRules(pieces) {
|
|
|
6784
7216
|
return new Function("_env", body)(env);
|
|
6785
7217
|
}
|
|
6786
7218
|
var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
|
|
7219
|
+
var LEAF_COMPOSED = /* @__PURE__ */ Symbol.for("parseman.leafComposed");
|
|
6787
7220
|
var COMPOSED_TRIVIA = /* @__PURE__ */ Symbol.for("parseman.composedTrivia");
|
|
7221
|
+
var COMPOSED_COVERAGE_RULES = /* @__PURE__ */ Symbol.for("parseman.composedCoverageRules");
|
|
6788
7222
|
function isIRPiece(p) {
|
|
6789
7223
|
return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
|
|
6790
7224
|
}
|
|
6791
|
-
function
|
|
7225
|
+
function coverageRulesOf(carried) {
|
|
7226
|
+
const winners = {};
|
|
7227
|
+
for (const piece of carried) {
|
|
7228
|
+
if (!isIRPiece(piece)) return void 0;
|
|
7229
|
+
const map = evalRuleMapIR(piece.ir);
|
|
7230
|
+
for (const [name, rule] of map) {
|
|
7231
|
+
if (rule._def.tag === "lazy") {
|
|
7232
|
+
try {
|
|
7233
|
+
rule._def.thunk();
|
|
7234
|
+
} catch {
|
|
7235
|
+
continue;
|
|
7236
|
+
}
|
|
7237
|
+
}
|
|
7238
|
+
winners[name] = rule;
|
|
7239
|
+
}
|
|
7240
|
+
}
|
|
7241
|
+
return winners;
|
|
7242
|
+
}
|
|
7243
|
+
function composedCoverageRules(grammar) {
|
|
7244
|
+
return grammar[COMPOSED_COVERAGE_RULES];
|
|
7245
|
+
}
|
|
7246
|
+
function materializePiece(p, trivia2, captureTerminals = false) {
|
|
6792
7247
|
if (!isIRPiece(p)) return p;
|
|
6793
|
-
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns,
|
|
7248
|
+
const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
|
|
7249
|
+
...trivia2 ? { trivia: trivia2 } : {},
|
|
7250
|
+
...captureTerminals ? { captureTerminals: true } : {}
|
|
7251
|
+
});
|
|
6794
7252
|
if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
|
|
6795
7253
|
return pieces;
|
|
6796
7254
|
}
|
|
@@ -6841,6 +7299,9 @@ function composingTriviaOf(items) {
|
|
|
6841
7299
|
return void 0;
|
|
6842
7300
|
}
|
|
6843
7301
|
function compose(items) {
|
|
7302
|
+
if (items.some((item) => item[LEAF_COMPOSED] === true)) {
|
|
7303
|
+
throw new Error("compose: a composeLeaf() result is terminal and cannot be composed again");
|
|
7304
|
+
}
|
|
6844
7305
|
const used = /* @__PURE__ */ new Set();
|
|
6845
7306
|
const trivia2 = composingTriviaOf(items);
|
|
6846
7307
|
const carried = items.flatMap((item) => itemCarried(item, used, trivia2));
|
|
@@ -6848,8 +7309,13 @@ function compose(items) {
|
|
|
6848
7309
|
const map = fuseRules(pieces);
|
|
6849
7310
|
Object.defineProperty(map, COMPOSED_PIECES, { value: carried, enumerable: false });
|
|
6850
7311
|
if (trivia2) Object.defineProperty(map, COMPOSED_TRIVIA, { value: trivia2, enumerable: false });
|
|
7312
|
+
const coverageRules = coverageRulesOf(carried);
|
|
7313
|
+
if (coverageRules) Object.defineProperty(map, COMPOSED_COVERAGE_RULES, { value: coverageRules, enumerable: false });
|
|
6851
7314
|
return map;
|
|
6852
7315
|
}
|
|
7316
|
+
function composeLeaf(_items) {
|
|
7317
|
+
throw new Error("composeLeaf(): requires Parseman macro lowering; runtime composition is forbidden");
|
|
7318
|
+
}
|
|
6853
7319
|
|
|
6854
7320
|
// src/combinators/guard.ts
|
|
6855
7321
|
function guard(predicate) {
|
|
@@ -7013,7 +7479,9 @@ function producesRepetition(def, depth = 0) {
|
|
|
7013
7479
|
// Transparent wrappers — look through to the inner parser.
|
|
7014
7480
|
case "node":
|
|
7015
7481
|
case "transform":
|
|
7482
|
+
case "attempt":
|
|
7016
7483
|
case "token":
|
|
7484
|
+
case "leaf":
|
|
7017
7485
|
case "label":
|
|
7018
7486
|
case "field":
|
|
7019
7487
|
case "expect":
|
|
@@ -7065,8 +7533,8 @@ function replaceAtPath(rebuild, root, path, newNode) {
|
|
|
7065
7533
|
newChildren[idx] = rest.length === 0 ? newNode : replaceAtPath(rebuild, root.children[idx], rest, newNode);
|
|
7066
7534
|
return rebuild(root, newChildren);
|
|
7067
7535
|
}
|
|
7068
|
-
function defaultRebuild(node2,
|
|
7069
|
-
return { ...node2, children };
|
|
7536
|
+
function defaultRebuild(node2, children2) {
|
|
7537
|
+
return { ...node2, children: children2 };
|
|
7070
7538
|
}
|
|
7071
7539
|
function shiftRelStart(child, delta) {
|
|
7072
7540
|
const c = child;
|
|
@@ -7406,7 +7874,8 @@ function runOnce(entry, input, options, phase, profileState) {
|
|
|
7406
7874
|
state: options.state,
|
|
7407
7875
|
...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
|
|
7408
7876
|
...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {},
|
|
7409
|
-
...options.tolerant ? { _tolerant: true, _rec: REC } : {}
|
|
7877
|
+
...options.tolerant ? { _tolerant: true, _rec: REC } : {},
|
|
7878
|
+
...options.instrumentation === void 0 ? {} : options.instrumentation
|
|
7410
7879
|
};
|
|
7411
7880
|
const r = invoke(entry, input, 0, ctx);
|
|
7412
7881
|
let unconsumedFrom = null;
|
|
@@ -7461,6 +7930,265 @@ function run(entry, input, options = {}) {
|
|
|
7461
7930
|
};
|
|
7462
7931
|
}
|
|
7463
7932
|
|
|
7933
|
+
// src/coverage.ts
|
|
7934
|
+
var GRAMMAR_COVERAGE_DEFINITIONS = /* @__PURE__ */ Symbol.for("parseman.grammarCoverageDefinitions");
|
|
7935
|
+
function compiledGrammarCoverageDefinitions(grammar) {
|
|
7936
|
+
const definitions = grammar[GRAMMAR_COVERAGE_DEFINITIONS];
|
|
7937
|
+
if (!Array.isArray(definitions) || !definitions.every((definition) => typeof definition === "object" && definition !== null && typeof definition.id === "string" && (definition.kind === "rule" || definition.kind === "choice-arm" || definition.kind === "label"))) {
|
|
7938
|
+
throw new TypeError("grammar has no coverage definitions; enable grammarCoverage for this macro build");
|
|
7939
|
+
}
|
|
7940
|
+
return definitions;
|
|
7941
|
+
}
|
|
7942
|
+
function createGrammarInstrumentationContext(options = {}) {
|
|
7943
|
+
return {
|
|
7944
|
+
trackLines: options.trackLines ?? false,
|
|
7945
|
+
...options.state === void 0 ? {} : { state: options.state },
|
|
7946
|
+
...options.collector === void 0 ? {} : { _grammarCoverage: (id) => options.collector.hit(id) },
|
|
7947
|
+
...options.trace === void 0 ? {} : { _grammarTrace: options.trace }
|
|
7948
|
+
};
|
|
7949
|
+
}
|
|
7950
|
+
function createGrammarTraceSink(options) {
|
|
7951
|
+
if (!Number.isInteger(options.capacity) || options.capacity < 0) throw new TypeError("trace capacity must be a finite non-negative integer");
|
|
7952
|
+
const events = [];
|
|
7953
|
+
let detached = false;
|
|
7954
|
+
let truncated = false;
|
|
7955
|
+
let dropped = 0;
|
|
7956
|
+
return {
|
|
7957
|
+
write(event) {
|
|
7958
|
+
if (detached || events.length >= options.capacity) {
|
|
7959
|
+
detached = true;
|
|
7960
|
+
truncated = true;
|
|
7961
|
+
dropped++;
|
|
7962
|
+
return;
|
|
7963
|
+
}
|
|
7964
|
+
events.push(Object.freeze({ ...event }));
|
|
7965
|
+
try {
|
|
7966
|
+
if (options.write?.(event) === false) {
|
|
7967
|
+
detached = true;
|
|
7968
|
+
truncated = true;
|
|
7969
|
+
}
|
|
7970
|
+
} catch {
|
|
7971
|
+
detached = true;
|
|
7972
|
+
truncated = true;
|
|
7973
|
+
}
|
|
7974
|
+
},
|
|
7975
|
+
snapshot() {
|
|
7976
|
+
return Object.freeze({ events: Object.freeze([...events]), truncated, dropped });
|
|
7977
|
+
}
|
|
7978
|
+
};
|
|
7979
|
+
}
|
|
7980
|
+
function createGrammarCoverageCollector(definitions) {
|
|
7981
|
+
const ordered = [...definitions].sort((a, b) => a.id.localeCompare(b.id));
|
|
7982
|
+
const known = new Set(ordered.map((definition) => definition.id));
|
|
7983
|
+
const hits = /* @__PURE__ */ new Set();
|
|
7984
|
+
return {
|
|
7985
|
+
hit(id) {
|
|
7986
|
+
if (known.has(id)) hits.add(id);
|
|
7987
|
+
},
|
|
7988
|
+
snapshot() {
|
|
7989
|
+
const hitList = [...hits].sort();
|
|
7990
|
+
const unhit = ordered.map((definition) => definition.id).filter((id) => !hits.has(id));
|
|
7991
|
+
return { definitions: ordered, hits: hitList, unhit, ratio: ordered.length === 0 ? 1 : hitList.length / ordered.length };
|
|
7992
|
+
},
|
|
7993
|
+
reset() {
|
|
7994
|
+
hits.clear();
|
|
7995
|
+
}
|
|
7996
|
+
};
|
|
7997
|
+
}
|
|
7998
|
+
function grammarCoverageDefinitions(entry, winners) {
|
|
7999
|
+
return buildGrammarPlan(entry, winners).definitions;
|
|
8000
|
+
}
|
|
8001
|
+
function composedGrammarCoverageDefinitions(grammar, startRule) {
|
|
8002
|
+
const winners = composedCoverageRules(grammar);
|
|
8003
|
+
if (!winners) throw new TypeError("semantic coverage needs re-lowerable composed IR; this composition contains an opaque artifact");
|
|
8004
|
+
const entry = winners[startRule];
|
|
8005
|
+
if (!entry) throw new TypeError(`semantic coverage start rule ${JSON.stringify(startRule)} is not a final winner`);
|
|
8006
|
+
return grammarCoverageDefinitions(entry, winners);
|
|
8007
|
+
}
|
|
8008
|
+
function coverageEntry(entry, collector, maps, trace) {
|
|
8009
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
8010
|
+
const build = (parser2) => {
|
|
8011
|
+
const existing = cache.get(parser2);
|
|
8012
|
+
if (existing) return existing;
|
|
8013
|
+
const rule = maps.rules.get(parser2);
|
|
8014
|
+
const wrap = (parse2) => {
|
|
8015
|
+
const wrapped = {
|
|
8016
|
+
_tag: parser2._tag,
|
|
8017
|
+
_meta: parser2._meta,
|
|
8018
|
+
_def: parser2._def,
|
|
8019
|
+
parse(input, pos, ctx) {
|
|
8020
|
+
if (rule) {
|
|
8021
|
+
collector.hit(rule);
|
|
8022
|
+
trace?.write({ id: rule, phase: "enter", offset: pos });
|
|
8023
|
+
}
|
|
8024
|
+
const result = parse2(input, pos, ctx);
|
|
8025
|
+
if (rule) trace?.write(result.ok ? { id: rule, phase: "success", offset: pos, end: result.span.end } : { id: rule, phase: "failure", offset: result.span.end });
|
|
8026
|
+
return result;
|
|
8027
|
+
}
|
|
8028
|
+
};
|
|
8029
|
+
cache.set(parser2, wrapped);
|
|
8030
|
+
return wrapped;
|
|
8031
|
+
};
|
|
8032
|
+
const def = parser2._def;
|
|
8033
|
+
if (def.tag === "lazy") {
|
|
8034
|
+
return wrap((input, pos, ctx) => build(def.thunk()).parse(input, pos, ctx));
|
|
8035
|
+
}
|
|
8036
|
+
if (def.tag === "label") {
|
|
8037
|
+
const base2 = label(def.label, build(def.parser));
|
|
8038
|
+
return wrap((input, pos, ctx) => {
|
|
8039
|
+
const result = base2.parse(input, pos, ctx);
|
|
8040
|
+
if (result.ok) for (const id of maps.labels.get(parser2) ?? []) {
|
|
8041
|
+
collector.hit(id);
|
|
8042
|
+
trace?.write({ id, phase: "success", offset: pos, end: result.span.end });
|
|
8043
|
+
}
|
|
8044
|
+
return result;
|
|
8045
|
+
});
|
|
8046
|
+
}
|
|
8047
|
+
if (def.tag === "choice") {
|
|
8048
|
+
const ids = maps.choices.get(parser2) ?? [];
|
|
8049
|
+
const invocations = [];
|
|
8050
|
+
const arms = def.parsers.map((arm, index) => {
|
|
8051
|
+
const child = build(arm);
|
|
8052
|
+
return {
|
|
8053
|
+
_tag: child._tag,
|
|
8054
|
+
_meta: child._meta,
|
|
8055
|
+
_def: child._def,
|
|
8056
|
+
parse(input, pos, ctx) {
|
|
8057
|
+
const id = ids[index];
|
|
8058
|
+
if (id) trace?.write({ id, phase: "attempt", offset: pos });
|
|
8059
|
+
const result = child.parse(input, pos, ctx);
|
|
8060
|
+
if (result.ok) {
|
|
8061
|
+
if (def.strategy.tag !== "greedyClassify") collector.hit(ids[index]);
|
|
8062
|
+
const invocation = invocations[invocations.length - 1];
|
|
8063
|
+
if (invocation) {
|
|
8064
|
+
invocation.lastSuccessfulArm = index;
|
|
8065
|
+
invocation.successfulArms.push(index);
|
|
8066
|
+
}
|
|
8067
|
+
} else if (id) {
|
|
8068
|
+
trace?.write({ id, phase: "failure", offset: result.span.end });
|
|
8069
|
+
trace?.write({ id, phase: "backtrack", offset: pos });
|
|
8070
|
+
}
|
|
8071
|
+
return result;
|
|
8072
|
+
}
|
|
8073
|
+
};
|
|
8074
|
+
});
|
|
8075
|
+
const args = arms.map((arm, index) => def.gates[index] ? { combinator: arm, gate: def.gates[index] } : arm);
|
|
8076
|
+
const unsafeChoice = choice;
|
|
8077
|
+
const baseChoice = unsafeChoice(...args);
|
|
8078
|
+
return wrap((input, pos, ctx) => {
|
|
8079
|
+
const invocation = { lastSuccessfulArm: void 0, successfulArms: [] };
|
|
8080
|
+
invocations.push(invocation);
|
|
8081
|
+
const result = baseChoice.parse(input, pos, ctx);
|
|
8082
|
+
const strategy = def.strategy;
|
|
8083
|
+
if (result.ok && strategy.tag === "greedyClassify") {
|
|
8084
|
+
const text = input.slice(pos, result.span.end);
|
|
8085
|
+
const classified = def.parsers.findIndex((arm, index) => index !== strategy.superIndex && getCoreLiteralValue(arm) === text);
|
|
8086
|
+
if (classified !== -1) invocation.lastSuccessfulArm = classified;
|
|
8087
|
+
else invocation.lastSuccessfulArm = strategy.superIndex;
|
|
8088
|
+
}
|
|
8089
|
+
if (result.ok && invocation.lastSuccessfulArm !== void 0) {
|
|
8090
|
+
const id = ids[invocation.lastSuccessfulArm];
|
|
8091
|
+
for (const rejected of invocation.successfulArms) {
|
|
8092
|
+
if (rejected !== invocation.lastSuccessfulArm) {
|
|
8093
|
+
const rejectedId = ids[rejected];
|
|
8094
|
+
if (rejectedId) {
|
|
8095
|
+
trace?.write({ id: rejectedId, phase: "failure", offset: pos });
|
|
8096
|
+
trace?.write({ id: rejectedId, phase: "backtrack", offset: pos });
|
|
8097
|
+
}
|
|
8098
|
+
}
|
|
8099
|
+
}
|
|
8100
|
+
if (id) {
|
|
8101
|
+
collector.hit(id);
|
|
8102
|
+
trace?.write({ id, phase: "selected", offset: pos, end: result.span.end });
|
|
8103
|
+
trace?.write({ id, phase: "success", offset: pos, end: result.span.end });
|
|
8104
|
+
}
|
|
8105
|
+
}
|
|
8106
|
+
invocations.pop();
|
|
8107
|
+
return result;
|
|
8108
|
+
});
|
|
8109
|
+
}
|
|
8110
|
+
const base = (() => {
|
|
8111
|
+
switch (def.tag) {
|
|
8112
|
+
case "sequence": {
|
|
8113
|
+
const unsafeSequence = sequence;
|
|
8114
|
+
return unsafeSequence(...def.parsers.map(build));
|
|
8115
|
+
}
|
|
8116
|
+
case "many":
|
|
8117
|
+
return many(build(def.parser));
|
|
8118
|
+
case "oneOrMore":
|
|
8119
|
+
return oneOrMore(build(def.parser));
|
|
8120
|
+
case "optional":
|
|
8121
|
+
return optional(build(def.parser));
|
|
8122
|
+
case "attempt": {
|
|
8123
|
+
const base2 = attempt(build(def.parser));
|
|
8124
|
+
const id = maps.attempts.get(parser2);
|
|
8125
|
+
return id === void 0 ? base2 : {
|
|
8126
|
+
...base2,
|
|
8127
|
+
parse(input, pos, ctx) {
|
|
8128
|
+
const result = base2.parse(input, pos, ctx);
|
|
8129
|
+
if (!result.ok) trace?.write({ id, phase: "rollback", offset: pos });
|
|
8130
|
+
return result;
|
|
8131
|
+
}
|
|
8132
|
+
};
|
|
8133
|
+
}
|
|
8134
|
+
case "sepBy":
|
|
8135
|
+
return sepBy(build(def.parser), build(def.separator));
|
|
8136
|
+
case "transform":
|
|
8137
|
+
return transform(build(def.parser), def.fn);
|
|
8138
|
+
case "skip":
|
|
8139
|
+
return skip(build(def.main), build(def.skipped));
|
|
8140
|
+
case "trivia":
|
|
8141
|
+
return trivia(build(def.parser));
|
|
8142
|
+
case "token":
|
|
8143
|
+
return token(build(def.parser));
|
|
8144
|
+
case "leaf":
|
|
8145
|
+
return leaf(build(def.parser), def.fn);
|
|
8146
|
+
case "field":
|
|
8147
|
+
return field(def.name, build(def.parser));
|
|
8148
|
+
case "grammar":
|
|
8149
|
+
return parser({
|
|
8150
|
+
...def.triviaParser === void 0 ? def.clearTrivia ? { trivia: null } : {} : { trivia: build(def.triviaParser) },
|
|
8151
|
+
...def.captureTrivia ? { captureTrivia: true } : {},
|
|
8152
|
+
...def.trackLines ? { trackLines: true } : {}
|
|
8153
|
+
}, build(def.parser));
|
|
8154
|
+
case "not":
|
|
8155
|
+
return not(build(def.parser));
|
|
8156
|
+
case "node": {
|
|
8157
|
+
const opts = { ...def.unwrap ? { unwrap: true } : {}, ...def.collapse ? { collapse: true } : {}, ...def.captureTrivia ? { captureTrivia: true } : {}, ...def.trailingTrivia ? { trailingTrivia: true } : {} };
|
|
8158
|
+
return def.type === void 0 ? node(build(def.parser), def.build, opts) : node(def.type, build(def.parser), def.build, opts);
|
|
8159
|
+
}
|
|
8160
|
+
case "guard":
|
|
8161
|
+
return guard(def.predicate);
|
|
8162
|
+
case "withCtx":
|
|
8163
|
+
return withCtx(def.extra, build(def.parser));
|
|
8164
|
+
case "expect":
|
|
8165
|
+
return expect(build(def.parser), def.label);
|
|
8166
|
+
case "scanTo":
|
|
8167
|
+
return scanTo(build(def.sentinel), { skip: def.skip.map(build), orEOF: def.orEOF });
|
|
8168
|
+
case "literal":
|
|
8169
|
+
case "regex":
|
|
8170
|
+
case "keywords":
|
|
8171
|
+
return parser2;
|
|
8172
|
+
case "recover":
|
|
8173
|
+
case "unknown":
|
|
8174
|
+
throw new TypeError(`runWithGrammarCoverage does not yet support ${def.tag}`);
|
|
8175
|
+
default:
|
|
8176
|
+
return parser2;
|
|
8177
|
+
}
|
|
8178
|
+
})();
|
|
8179
|
+
return wrap((input, pos, ctx) => base.parse(input, pos, ctx));
|
|
8180
|
+
};
|
|
8181
|
+
return build(entry);
|
|
8182
|
+
}
|
|
8183
|
+
function runWithGrammarCoverage(entry, input, options = {}) {
|
|
8184
|
+
if (typeof entry === "function") throw new TypeError("runWithGrammarCoverage currently requires an interpreter combinator entry");
|
|
8185
|
+
const plan = buildGrammarPlan(entry);
|
|
8186
|
+
const definitions = plan.definitions;
|
|
8187
|
+
const collector = options.collector ?? createGrammarCoverageCollector(definitions);
|
|
8188
|
+
const { collector: _collector, trace, ...runOptions } = options;
|
|
8189
|
+
return { result: run(coverageEntry(entry, collector, plan, trace), input, runOptions), coverage: collector.snapshot() };
|
|
8190
|
+
}
|
|
8191
|
+
|
|
7464
8192
|
// src/cst/trivia-index.ts
|
|
7465
8193
|
function merge(map, key, run2) {
|
|
7466
8194
|
if (run2.length === 0) return;
|
|
@@ -7531,9 +8259,9 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
7531
8259
|
function walk(root, visitor, ctx = void 0) {
|
|
7532
8260
|
const go = (node2, parent) => {
|
|
7533
8261
|
const descend = visitor.enter ? visitor.enter(node2, parent, ctx) : void 0;
|
|
7534
|
-
const
|
|
7535
|
-
if (descend !== false && Array.isArray(
|
|
7536
|
-
for (const child of
|
|
8262
|
+
const children2 = node2.children;
|
|
8263
|
+
if (descend !== false && Array.isArray(children2)) {
|
|
8264
|
+
for (const child of children2) go(child, node2);
|
|
7537
8265
|
}
|
|
7538
8266
|
visitor.leave?.(node2, parent, ctx);
|
|
7539
8267
|
};
|
|
@@ -7549,9 +8277,9 @@ function createVisitor(handlers) {
|
|
|
7549
8277
|
},
|
|
7550
8278
|
visitChildren(node2) {
|
|
7551
8279
|
const out = [];
|
|
7552
|
-
const
|
|
7553
|
-
if (Array.isArray(
|
|
7554
|
-
for (const child of
|
|
8280
|
+
const children2 = node2.children;
|
|
8281
|
+
if (Array.isArray(children2)) {
|
|
8282
|
+
for (const child of children2) {
|
|
7555
8283
|
const r = api.visit(child);
|
|
7556
8284
|
if (r !== void 0) out.push(r);
|
|
7557
8285
|
}
|
|
@@ -7792,6 +8520,7 @@ function buildOffsetIndex(root, input = "") {
|
|
|
7792
8520
|
// src/index.ts
|
|
7793
8521
|
registerRegexAnalyzer(firstSetFromRegex);
|
|
7794
8522
|
export {
|
|
8523
|
+
GRAMMAR_COVERAGE_DEFINITIONS,
|
|
7795
8524
|
OffsetIndex,
|
|
7796
8525
|
absoluteSpanAt,
|
|
7797
8526
|
absoluteSpanCST,
|
|
@@ -7799,6 +8528,7 @@ export {
|
|
|
7799
8528
|
absolutizeCST,
|
|
7800
8529
|
annotateSpan,
|
|
7801
8530
|
applyEdit,
|
|
8531
|
+
attempt,
|
|
7802
8532
|
balanced,
|
|
7803
8533
|
blankLinesIn,
|
|
7804
8534
|
buildLineIndex,
|
|
@@ -7808,20 +8538,28 @@ export {
|
|
|
7808
8538
|
collectLeafSlots,
|
|
7809
8539
|
commentsIn,
|
|
7810
8540
|
compile,
|
|
8541
|
+
compiledGrammarCoverageDefinitions,
|
|
7811
8542
|
completionsAt,
|
|
7812
8543
|
compose,
|
|
8544
|
+
composeLeaf,
|
|
8545
|
+
composedGrammarCoverageDefinitions,
|
|
8546
|
+
createGrammarCoverageCollector,
|
|
8547
|
+
createGrammarInstrumentationContext,
|
|
8548
|
+
createGrammarTraceSink,
|
|
7813
8549
|
createVisitor,
|
|
7814
8550
|
cstBuildHost,
|
|
7815
8551
|
expect,
|
|
7816
8552
|
field,
|
|
7817
8553
|
gapIsSignificant,
|
|
7818
8554
|
gapText,
|
|
8555
|
+
grammarCoverageDefinitions,
|
|
7819
8556
|
guard,
|
|
7820
8557
|
indentMixed,
|
|
7821
8558
|
indentWidth,
|
|
7822
8559
|
isParseError,
|
|
7823
8560
|
keywords,
|
|
7824
8561
|
label,
|
|
8562
|
+
leaf,
|
|
7825
8563
|
lineBreaksIn,
|
|
7826
8564
|
lineStartWithin,
|
|
7827
8565
|
literal,
|
|
@@ -7842,6 +8580,7 @@ export {
|
|
|
7842
8580
|
relativizeCST,
|
|
7843
8581
|
rules,
|
|
7844
8582
|
run,
|
|
8583
|
+
runWithGrammarCoverage,
|
|
7845
8584
|
scanTo,
|
|
7846
8585
|
sepBy,
|
|
7847
8586
|
sequence,
|