parseman 0.39.0 → 0.40.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 +8 -0
- package/dist/analysis/gating.d.ts.map +1 -1
- package/dist/combinators/dispatch.d.ts +36 -3
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/node.d.ts +15 -0
- package/dist/combinators/node.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/grammar-coverage-ids.d.ts +2 -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 +483 -61
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +483 -61
- package/dist/index.js.map +3 -3
- package/dist/plugin/evaluator.d.ts +6 -0
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +520 -67
- package/dist/plugin/index.cjs.map +3 -3
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +520 -67
- package/dist/plugin/index.js.map +3 -3
- package/dist/spec/index.cjs +234 -26
- package/dist/spec/index.cjs.map +3 -3
- package/dist/spec/index.js +234 -26
- package/dist/spec/index.js.map +3 -3
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/types.d.ts +21 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +2 -1
package/dist/spec/index.cjs
CHANGED
|
@@ -82,6 +82,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
|
82
82
|
return d.value.length === 0;
|
|
83
83
|
case "keywords":
|
|
84
84
|
return false;
|
|
85
|
+
case "routed":
|
|
86
|
+
return false;
|
|
85
87
|
case "regex":
|
|
86
88
|
try {
|
|
87
89
|
const m = new RegExp(d.source).exec("");
|
|
@@ -320,6 +322,7 @@ function markUnusedValues(root) {
|
|
|
320
322
|
case "dispatch":
|
|
321
323
|
visit(def.selector, consumed);
|
|
322
324
|
for (const entry of def.cases) visit(entry.parser, consumed);
|
|
325
|
+
if (def.matchers) for (const entry of def.matchers) visit(entry.parser, consumed);
|
|
323
326
|
if (def.otherwise) visit(def.otherwise, consumed);
|
|
324
327
|
return;
|
|
325
328
|
case "node":
|
|
@@ -773,6 +776,8 @@ function derive(c, seen) {
|
|
|
773
776
|
case "not":
|
|
774
777
|
case "peek":
|
|
775
778
|
return deriveExpected2(def.parser);
|
|
779
|
+
case "routed":
|
|
780
|
+
return ["routed()"];
|
|
776
781
|
case "lazy": {
|
|
777
782
|
const name = c._ruleName;
|
|
778
783
|
if (seen.has(c)) return name ? [name] : [];
|
|
@@ -2321,7 +2326,7 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2321
2326
|
case "choice":
|
|
2322
2327
|
return d.parsers.some((x) => parserHasOwnFields(x, seen));
|
|
2323
2328
|
case "dispatch":
|
|
2324
|
-
return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
|
|
2329
|
+
return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => parserHasOwnFields(entry.parser, seen)) : false) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
|
|
2325
2330
|
case "sepBy":
|
|
2326
2331
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
|
|
2327
2332
|
case "skip":
|
|
@@ -2437,42 +2442,195 @@ function keywords(words, opts = {}) {
|
|
|
2437
2442
|
}
|
|
2438
2443
|
|
|
2439
2444
|
// src/combinators/dispatch.ts
|
|
2440
|
-
function
|
|
2445
|
+
function parserUsesRouted(parser2, seen = /* @__PURE__ */ new Set()) {
|
|
2446
|
+
if (seen.has(parser2)) return false;
|
|
2447
|
+
seen.add(parser2);
|
|
2448
|
+
const def = parser2._def;
|
|
2449
|
+
switch (def.tag) {
|
|
2450
|
+
case "routed":
|
|
2451
|
+
return true;
|
|
2452
|
+
case "sequence":
|
|
2453
|
+
case "choice":
|
|
2454
|
+
return def.parsers.some((entry) => parserUsesRouted(entry, seen));
|
|
2455
|
+
case "dispatch":
|
|
2456
|
+
return false;
|
|
2457
|
+
case "sepBy":
|
|
2458
|
+
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.separator, seen);
|
|
2459
|
+
case "skip":
|
|
2460
|
+
return parserUsesRouted(def.main, seen) || parserUsesRouted(def.skipped, seen);
|
|
2461
|
+
case "grammar":
|
|
2462
|
+
return parserUsesRouted(def.parser, seen) || (def.triviaParser ? parserUsesRouted(def.triviaParser, seen) : false);
|
|
2463
|
+
case "scanTo":
|
|
2464
|
+
return parserUsesRouted(def.sentinel, seen) || def.skip.some((entry) => parserUsesRouted(entry, seen));
|
|
2465
|
+
case "recover":
|
|
2466
|
+
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.sentinel, seen);
|
|
2467
|
+
case "lazy":
|
|
2468
|
+
try {
|
|
2469
|
+
return parserUsesRouted(def.thunk(), seen);
|
|
2470
|
+
} catch {
|
|
2471
|
+
return false;
|
|
2472
|
+
}
|
|
2473
|
+
case "many":
|
|
2474
|
+
case "oneOrMore":
|
|
2475
|
+
case "optional":
|
|
2476
|
+
case "attempt":
|
|
2477
|
+
case "transform":
|
|
2478
|
+
case "trivia":
|
|
2479
|
+
case "token":
|
|
2480
|
+
case "leaf":
|
|
2481
|
+
case "label":
|
|
2482
|
+
case "field":
|
|
2483
|
+
case "not":
|
|
2484
|
+
case "peek":
|
|
2485
|
+
case "node":
|
|
2486
|
+
case "withCtx":
|
|
2487
|
+
case "expect":
|
|
2488
|
+
return parserUsesRouted(def.parser, seen);
|
|
2489
|
+
default:
|
|
2490
|
+
return false;
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
function branchUsesRouted(branch) {
|
|
2494
|
+
return branch.usesRouted === true || parserUsesRouted(branch.parser);
|
|
2495
|
+
}
|
|
2496
|
+
function asciiFoldKey(key) {
|
|
2497
|
+
let out = "";
|
|
2498
|
+
for (let i = 0; i < key.length; i++) {
|
|
2499
|
+
const c = key.charCodeAt(i);
|
|
2500
|
+
out += String.fromCharCode(c >= 65 && c <= 90 ? c + 32 : c);
|
|
2501
|
+
}
|
|
2502
|
+
return out;
|
|
2503
|
+
}
|
|
2504
|
+
function matchesDispatchMatcher(value, matcher) {
|
|
2505
|
+
const candidate = matcher.caseInsensitive ? asciiFoldKey(value) : value;
|
|
2506
|
+
const expected = matcher.caseInsensitive ? asciiFoldKey(matcher.value) : matcher.value;
|
|
2507
|
+
switch (matcher.kind) {
|
|
2508
|
+
case "startsWith":
|
|
2509
|
+
return candidate.startsWith(expected);
|
|
2510
|
+
case "endsWith":
|
|
2511
|
+
return candidate.endsWith(expected);
|
|
2512
|
+
case "matches": {
|
|
2513
|
+
const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
|
|
2514
|
+
return new RegExp(matcher.value, flags).test(value);
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
function isStringMatcher(value) {
|
|
2519
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2520
|
+
const kind = value.kind;
|
|
2521
|
+
return kind === "startsWith" || kind === "endsWith" || kind === "matches";
|
|
2522
|
+
}
|
|
2523
|
+
function fixedMatcher(kind, value) {
|
|
2524
|
+
if (typeof value !== "string") throw new TypeError(`parseman: ${kind}() value must be a string`);
|
|
2525
|
+
if (value.length === 0) throw new RangeError(`parseman: ${kind}() requires a non-empty value`);
|
|
2526
|
+
return { kind, value };
|
|
2527
|
+
}
|
|
2528
|
+
function startsWith(prefix) {
|
|
2529
|
+
return fixedMatcher("startsWith", prefix);
|
|
2530
|
+
}
|
|
2531
|
+
function endsWith(suffix) {
|
|
2532
|
+
return fixedMatcher("endsWith", suffix);
|
|
2533
|
+
}
|
|
2534
|
+
function matches(pattern) {
|
|
2535
|
+
if (!(pattern instanceof RegExp)) throw new TypeError("parseman: matches() requires a RegExp");
|
|
2536
|
+
if (pattern.flags.includes("g") || pattern.flags.includes("y")) {
|
|
2537
|
+
throw new TypeError("parseman: matches() does not accept global or sticky regex flags");
|
|
2538
|
+
}
|
|
2539
|
+
return { kind: "matches", value: pattern.source, flags: pattern.flags };
|
|
2540
|
+
}
|
|
2541
|
+
function when(key, parser2, opts = {}) {
|
|
2542
|
+
const usesRouted = parserUsesRouted(parser2);
|
|
2543
|
+
if (isStringMatcher(key)) {
|
|
2544
|
+
return { kind: "whenMatcher", matcher: key, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2545
|
+
}
|
|
2441
2546
|
const keys = Array.isArray(key) ? [...key] : [key];
|
|
2442
2547
|
if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
|
|
2443
2548
|
for (const item of keys) {
|
|
2444
2549
|
if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
|
|
2445
2550
|
}
|
|
2446
|
-
return { kind: "when", keys, parser: parser2 };
|
|
2551
|
+
return { kind: "when", keys, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2447
2552
|
}
|
|
2448
2553
|
function otherwise(parser2) {
|
|
2449
|
-
return { kind: "otherwise", parser: parser2 };
|
|
2554
|
+
return { kind: "otherwise", parser: parser2, usesRouted: parserUsesRouted(parser2) };
|
|
2555
|
+
}
|
|
2556
|
+
function routed() {
|
|
2557
|
+
return {
|
|
2558
|
+
_tag: "routed",
|
|
2559
|
+
_meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
|
|
2560
|
+
_def: { tag: "routed" },
|
|
2561
|
+
parse(_input, pos, ctx) {
|
|
2562
|
+
const item = ctx._routed;
|
|
2563
|
+
if (item === void 0) {
|
|
2564
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2565
|
+
}
|
|
2566
|
+
if (pos !== item.span.start) {
|
|
2567
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2568
|
+
}
|
|
2569
|
+
if (cstCaptureActive(ctx)) {
|
|
2570
|
+
pushCstLeaf(ctx, { _tag: "leaf", value: item.value, span: item.span });
|
|
2571
|
+
}
|
|
2572
|
+
return { ok: true, value: item.value, span: item.span };
|
|
2573
|
+
}
|
|
2574
|
+
};
|
|
2450
2575
|
}
|
|
2451
2576
|
function dispatch(selector, ...arms) {
|
|
2577
|
+
if (parserUsesRouted(selector)) {
|
|
2578
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2579
|
+
}
|
|
2452
2580
|
let fallback;
|
|
2581
|
+
let fallbackUsesRouted = false;
|
|
2582
|
+
const matchers = [];
|
|
2453
2583
|
const cases = [];
|
|
2454
|
-
const seen =
|
|
2584
|
+
const seen = [];
|
|
2455
2585
|
for (let i = 0; i < arms.length; i++) {
|
|
2456
2586
|
const arm = arms[i];
|
|
2457
2587
|
if (arm.kind === "otherwise") {
|
|
2458
2588
|
if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
|
|
2459
2589
|
if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
|
|
2460
2590
|
fallback = arm.parser;
|
|
2591
|
+
fallbackUsesRouted = arm.usesRouted === true;
|
|
2592
|
+
continue;
|
|
2593
|
+
}
|
|
2594
|
+
if (arm.kind === "whenMatcher") {
|
|
2595
|
+
const matcher = arm.matcher;
|
|
2596
|
+
matchers.push({
|
|
2597
|
+
kind: matcher.kind,
|
|
2598
|
+
value: matcher.value,
|
|
2599
|
+
...matcher.kind === "matches" ? { flags: matcher.flags } : {},
|
|
2600
|
+
parser: arm.parser,
|
|
2601
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2602
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2603
|
+
});
|
|
2461
2604
|
continue;
|
|
2462
2605
|
}
|
|
2463
2606
|
for (const key of arm.keys) {
|
|
2464
|
-
|
|
2465
|
-
seen
|
|
2607
|
+
const folded = asciiFoldKey(key);
|
|
2608
|
+
for (const prior of seen) {
|
|
2609
|
+
const overlaps = arm.caseInsensitive || prior.caseInsensitive ? folded === prior.folded : key === prior.raw;
|
|
2610
|
+
if (overlaps) throw new Error(`parseman: duplicate dispatch key ${JSON.stringify(key)}`);
|
|
2611
|
+
}
|
|
2612
|
+
seen.push({ raw: key, folded, caseInsensitive: arm.caseInsensitive });
|
|
2466
2613
|
}
|
|
2467
|
-
cases.push({
|
|
2614
|
+
cases.push({
|
|
2615
|
+
keys: arm.keys,
|
|
2616
|
+
parser: arm.parser,
|
|
2617
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2618
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2619
|
+
});
|
|
2468
2620
|
}
|
|
2469
2621
|
const meta = {
|
|
2470
2622
|
firstSet: selector._meta.firstSet,
|
|
2471
|
-
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2623
|
+
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || matchers.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2472
2624
|
isTrivia: false
|
|
2473
2625
|
};
|
|
2474
2626
|
const byKey = /* @__PURE__ */ new Map();
|
|
2475
|
-
|
|
2627
|
+
const byFoldedKey = /* @__PURE__ */ new Map();
|
|
2628
|
+
for (const entry of cases) {
|
|
2629
|
+
for (const key of entry.keys) {
|
|
2630
|
+
if (entry.caseInsensitive) byFoldedKey.set(asciiFoldKey(key), entry);
|
|
2631
|
+
else byKey.set(key, entry);
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2476
2634
|
return {
|
|
2477
2635
|
_tag: "dispatch",
|
|
2478
2636
|
_meta: meta,
|
|
@@ -2480,18 +2638,35 @@ function dispatch(selector, ...arms) {
|
|
|
2480
2638
|
tag: "dispatch",
|
|
2481
2639
|
selector,
|
|
2482
2640
|
cases,
|
|
2483
|
-
...
|
|
2641
|
+
...matchers.length === 0 ? {} : { matchers },
|
|
2642
|
+
...fallback === void 0 ? {} : { otherwise: fallback, ...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {} }
|
|
2484
2643
|
},
|
|
2485
2644
|
parse(input, pos, ctx) {
|
|
2645
|
+
if (parserUsesRouted(selector)) {
|
|
2646
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2647
|
+
}
|
|
2648
|
+
const selectorMark = saveTriviaMark(ctx);
|
|
2486
2649
|
const selected = selector.parse(input, pos, ctx);
|
|
2487
2650
|
if (!selected.ok) return selected;
|
|
2488
|
-
const
|
|
2489
|
-
if (
|
|
2651
|
+
const branch = byKey.get(selected.value) ?? (byFoldedKey.size === 0 ? void 0 : byFoldedKey.get(asciiFoldKey(selected.value))) ?? matchers.find((matcher) => matchesDispatchMatcher(selected.value, matcher)) ?? (fallback === void 0 ? void 0 : { keys: [], parser: fallback, caseInsensitive: false, usesRouted: fallbackUsesRouted });
|
|
2652
|
+
if (branch === void 0) {
|
|
2490
2653
|
const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
|
|
2491
2654
|
return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
|
|
2492
2655
|
}
|
|
2493
|
-
const
|
|
2494
|
-
const
|
|
2656
|
+
const savedRouted = ctx._routed;
|
|
2657
|
+
const usesRouted = branchUsesRouted(branch);
|
|
2658
|
+
let mark = saveTriviaMark(ctx);
|
|
2659
|
+
if (usesRouted) {
|
|
2660
|
+
rollbackTrivia(ctx, selectorMark);
|
|
2661
|
+
mark = saveTriviaMark(ctx);
|
|
2662
|
+
ctx._routed = { value: selected.value, span: selected.span };
|
|
2663
|
+
}
|
|
2664
|
+
let result;
|
|
2665
|
+
try {
|
|
2666
|
+
result = branch.parser.parse(input, usesRouted ? pos : selected.span.end, ctx);
|
|
2667
|
+
} finally {
|
|
2668
|
+
if (usesRouted) ctx._routed = savedRouted;
|
|
2669
|
+
}
|
|
2495
2670
|
if (!result.ok) {
|
|
2496
2671
|
rollbackTrivia(ctx, mark);
|
|
2497
2672
|
return { ...result, committed: true };
|
|
@@ -2589,12 +2764,30 @@ function isCstChild(value) {
|
|
|
2589
2764
|
function missingInferredType() {
|
|
2590
2765
|
throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
|
|
2591
2766
|
}
|
|
2767
|
+
function normalizeProject(project) {
|
|
2768
|
+
if (project === void 0) return void 0;
|
|
2769
|
+
if (!Number.isInteger(project) || project < 0) {
|
|
2770
|
+
throw new Error("node() project child must be a non-negative integer");
|
|
2771
|
+
}
|
|
2772
|
+
return project;
|
|
2773
|
+
}
|
|
2774
|
+
function projectChild(children, project, type) {
|
|
2775
|
+
if (!(project in children)) {
|
|
2776
|
+
throw new Error(`node(${JSON.stringify(type)}) project child ${project} was not captured`);
|
|
2777
|
+
}
|
|
2778
|
+
return unwrapChild(children[project]);
|
|
2779
|
+
}
|
|
2592
2780
|
function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
2593
2781
|
const hasExplicitType = typeof typeOrCombinator === "string";
|
|
2594
2782
|
const type = hasExplicitType ? typeOrCombinator : void 0;
|
|
2595
2783
|
const combinator = hasExplicitType ? combinatorOrBuild : typeOrCombinator;
|
|
2596
|
-
const
|
|
2597
|
-
const
|
|
2784
|
+
const buildArg = hasExplicitType ? buildOrOpts : combinatorOrBuild;
|
|
2785
|
+
const build = typeof buildArg === "function" ? buildArg : void 0;
|
|
2786
|
+
const opts = typeof buildArg === "function" ? maybeOpts : buildArg ?? maybeOpts;
|
|
2787
|
+
const project = normalizeProject(opts?.project);
|
|
2788
|
+
if (project !== void 0 && build !== void 0) {
|
|
2789
|
+
throw new Error("node() options cannot combine project with a build callback");
|
|
2790
|
+
}
|
|
2598
2791
|
const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
|
|
2599
2792
|
const meta = {
|
|
2600
2793
|
firstSet: combinator._meta.firstSet,
|
|
@@ -2608,11 +2801,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
|
2608
2801
|
if (unwrap && collapse) {
|
|
2609
2802
|
throw new Error("node() options cannot set both unwrap and collapse");
|
|
2610
2803
|
}
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2804
|
+
if (project !== void 0 && (unwrap || collapse)) {
|
|
2805
|
+
throw new Error("node() options cannot combine project with unwrap or collapse");
|
|
2806
|
+
}
|
|
2807
|
+
const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {} } : baseDef;
|
|
2808
|
+
const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
|
|
2809
|
+
const clonesState = build ? buildReadsState(def) : project === void 0;
|
|
2614
2810
|
const hasOwnFields = parserHasOwnFields(combinator);
|
|
2615
|
-
const capturesFields = hasOwnFields && (build ? buildReadsFields(def) :
|
|
2811
|
+
const capturesFields = hasOwnFields && (build ? buildReadsFields(def) : project === void 0);
|
|
2616
2812
|
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
2617
2813
|
let failExpected;
|
|
2618
2814
|
return {
|
|
@@ -2627,14 +2823,15 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
|
2627
2823
|
}
|
|
2628
2824
|
return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
|
|
2629
2825
|
}
|
|
2630
|
-
const
|
|
2826
|
+
const hasDirectValue = build !== void 0 || project !== void 0;
|
|
2827
|
+
const hostCst = hasDirectValue && cstOutputHost(ctx.build);
|
|
2631
2828
|
const effTrivia = capturesTrivia || hostCst;
|
|
2632
2829
|
const effFields = capturesFields || hostCst && hasOwnFields;
|
|
2633
2830
|
const saved = beginCstNodeCapture(ctx);
|
|
2634
2831
|
const savedFields = ctx._fields;
|
|
2635
2832
|
ctx._fields = effFields ? [] : void 0;
|
|
2636
2833
|
const savedMask = ctx._triviaCaptureMask;
|
|
2637
|
-
if (build === void 0 && ctx.build?._parsemanTriviaKinds !== void 0 && def.type !== void 0) {
|
|
2834
|
+
if (build === void 0 && project === void 0 && ctx.build?._parsemanTriviaKinds !== void 0 && def.type !== void 0) {
|
|
2638
2835
|
ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
|
|
2639
2836
|
}
|
|
2640
2837
|
if (!effTrivia) ctx.captureTrivia = false;
|
|
@@ -2652,7 +2849,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
|
2652
2849
|
const nodeType = def.type ?? missingInferredType();
|
|
2653
2850
|
const cstOutput = cstOutputHost(ctx.build);
|
|
2654
2851
|
const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
|
|
2655
|
-
const built = unwrap && children.length === 1 ? unwrapChild(children[0]) : collapse && children.length === 1 ? children[0] : !build && ctx.build?._parsemanCstCollapse && children.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children[0], children, rawChildren) ? children[0] : build ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, hostState) : build(children, fields, r.span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children };
|
|
2852
|
+
const built = unwrap && children.length === 1 ? unwrapChild(children[0]) : collapse && children.length === 1 ? children[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children[0], children, rawChildren) ? children[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, hostState) : projectChild(children, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, hostState) : build(children, fields, r.span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children };
|
|
2656
2853
|
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 };
|
|
2657
2854
|
if (saved.buf !== void 0 || saved.ch !== void 0) {
|
|
2658
2855
|
pushCstChild(ctx, built, rawEntry);
|
|
@@ -2939,7 +3136,11 @@ function evalRuleMapIR(ir) {
|
|
|
2939
3136
|
"choice",
|
|
2940
3137
|
"dispatch",
|
|
2941
3138
|
"when",
|
|
3139
|
+
"startsWith",
|
|
3140
|
+
"endsWith",
|
|
3141
|
+
"matches",
|
|
2942
3142
|
"otherwise",
|
|
3143
|
+
"routed",
|
|
2943
3144
|
"attempt",
|
|
2944
3145
|
"many",
|
|
2945
3146
|
"oneOrMore",
|
|
@@ -2975,7 +3176,11 @@ function evalRuleMapIR(ir) {
|
|
|
2975
3176
|
choice,
|
|
2976
3177
|
dispatch,
|
|
2977
3178
|
when,
|
|
3179
|
+
startsWith,
|
|
3180
|
+
endsWith,
|
|
3181
|
+
matches,
|
|
2978
3182
|
otherwise,
|
|
3183
|
+
routed,
|
|
2979
3184
|
attempt,
|
|
2980
3185
|
many,
|
|
2981
3186
|
oneOrMore,
|
|
@@ -3174,8 +3379,9 @@ var Builder = class {
|
|
|
3174
3379
|
const selector = this.walk(def.selector);
|
|
3175
3380
|
const tails = [
|
|
3176
3381
|
...def.cases.map((entry) => this.walk(entry.parser)),
|
|
3382
|
+
...def.matchers ? def.matchers.map((entry) => this.walk(entry.parser)) : [],
|
|
3177
3383
|
...def.otherwise ? [this.walk(def.otherwise)] : []
|
|
3178
|
-
]
|
|
3384
|
+
];
|
|
3179
3385
|
return flattenSeq([selector, flattenChoice(tails)]);
|
|
3180
3386
|
}
|
|
3181
3387
|
// `max`/`min` are carried through so a BOUNDED repeat does not render as an
|
|
@@ -3207,6 +3413,8 @@ var Builder = class {
|
|
|
3207
3413
|
return { kind: "not", item: this.walk(def.parser) };
|
|
3208
3414
|
case "peek":
|
|
3209
3415
|
return { kind: "peek", item: this.walk(def.parser) };
|
|
3416
|
+
case "routed":
|
|
3417
|
+
return { kind: "empty" };
|
|
3210
3418
|
// Transparent semantic wrappers — render the inner syntax.
|
|
3211
3419
|
case "transform":
|
|
3212
3420
|
case "token":
|
|
@@ -3364,7 +3572,7 @@ function render(node2) {
|
|
|
3364
3572
|
case "choice": {
|
|
3365
3573
|
const text = node2.items.map((it) => {
|
|
3366
3574
|
const r = render(it);
|
|
3367
|
-
return wrap(r.text, r.level, ALT);
|
|
3575
|
+
return r.text.length === 0 ? "/* empty */" : wrap(r.text, r.level, ALT);
|
|
3368
3576
|
}).join(" | ");
|
|
3369
3577
|
return { text, level: ALT };
|
|
3370
3578
|
}
|