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.js
CHANGED
|
@@ -48,6 +48,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
|
48
48
|
return d.value.length === 0;
|
|
49
49
|
case "keywords":
|
|
50
50
|
return false;
|
|
51
|
+
case "routed":
|
|
52
|
+
return false;
|
|
51
53
|
case "regex":
|
|
52
54
|
try {
|
|
53
55
|
const m = new RegExp(d.source).exec("");
|
|
@@ -286,6 +288,7 @@ function markUnusedValues(root) {
|
|
|
286
288
|
case "dispatch":
|
|
287
289
|
visit(def.selector, consumed);
|
|
288
290
|
for (const entry of def.cases) visit(entry.parser, consumed);
|
|
291
|
+
if (def.matchers) for (const entry of def.matchers) visit(entry.parser, consumed);
|
|
289
292
|
if (def.otherwise) visit(def.otherwise, consumed);
|
|
290
293
|
return;
|
|
291
294
|
case "node":
|
|
@@ -739,6 +742,8 @@ function derive(c, seen) {
|
|
|
739
742
|
case "not":
|
|
740
743
|
case "peek":
|
|
741
744
|
return deriveExpected2(def.parser);
|
|
745
|
+
case "routed":
|
|
746
|
+
return ["routed()"];
|
|
742
747
|
case "lazy": {
|
|
743
748
|
const name = c._ruleName;
|
|
744
749
|
if (seen.has(c)) return name ? [name] : [];
|
|
@@ -2287,7 +2292,7 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2287
2292
|
case "choice":
|
|
2288
2293
|
return d.parsers.some((x) => parserHasOwnFields(x, seen));
|
|
2289
2294
|
case "dispatch":
|
|
2290
|
-
return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
|
|
2295
|
+
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);
|
|
2291
2296
|
case "sepBy":
|
|
2292
2297
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
|
|
2293
2298
|
case "skip":
|
|
@@ -2403,42 +2408,195 @@ function keywords(words, opts = {}) {
|
|
|
2403
2408
|
}
|
|
2404
2409
|
|
|
2405
2410
|
// src/combinators/dispatch.ts
|
|
2406
|
-
function
|
|
2411
|
+
function parserUsesRouted(parser2, seen = /* @__PURE__ */ new Set()) {
|
|
2412
|
+
if (seen.has(parser2)) return false;
|
|
2413
|
+
seen.add(parser2);
|
|
2414
|
+
const def = parser2._def;
|
|
2415
|
+
switch (def.tag) {
|
|
2416
|
+
case "routed":
|
|
2417
|
+
return true;
|
|
2418
|
+
case "sequence":
|
|
2419
|
+
case "choice":
|
|
2420
|
+
return def.parsers.some((entry) => parserUsesRouted(entry, seen));
|
|
2421
|
+
case "dispatch":
|
|
2422
|
+
return false;
|
|
2423
|
+
case "sepBy":
|
|
2424
|
+
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.separator, seen);
|
|
2425
|
+
case "skip":
|
|
2426
|
+
return parserUsesRouted(def.main, seen) || parserUsesRouted(def.skipped, seen);
|
|
2427
|
+
case "grammar":
|
|
2428
|
+
return parserUsesRouted(def.parser, seen) || (def.triviaParser ? parserUsesRouted(def.triviaParser, seen) : false);
|
|
2429
|
+
case "scanTo":
|
|
2430
|
+
return parserUsesRouted(def.sentinel, seen) || def.skip.some((entry) => parserUsesRouted(entry, seen));
|
|
2431
|
+
case "recover":
|
|
2432
|
+
return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.sentinel, seen);
|
|
2433
|
+
case "lazy":
|
|
2434
|
+
try {
|
|
2435
|
+
return parserUsesRouted(def.thunk(), seen);
|
|
2436
|
+
} catch {
|
|
2437
|
+
return false;
|
|
2438
|
+
}
|
|
2439
|
+
case "many":
|
|
2440
|
+
case "oneOrMore":
|
|
2441
|
+
case "optional":
|
|
2442
|
+
case "attempt":
|
|
2443
|
+
case "transform":
|
|
2444
|
+
case "trivia":
|
|
2445
|
+
case "token":
|
|
2446
|
+
case "leaf":
|
|
2447
|
+
case "label":
|
|
2448
|
+
case "field":
|
|
2449
|
+
case "not":
|
|
2450
|
+
case "peek":
|
|
2451
|
+
case "node":
|
|
2452
|
+
case "withCtx":
|
|
2453
|
+
case "expect":
|
|
2454
|
+
return parserUsesRouted(def.parser, seen);
|
|
2455
|
+
default:
|
|
2456
|
+
return false;
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
function branchUsesRouted(branch) {
|
|
2460
|
+
return branch.usesRouted === true || parserUsesRouted(branch.parser);
|
|
2461
|
+
}
|
|
2462
|
+
function asciiFoldKey(key) {
|
|
2463
|
+
let out = "";
|
|
2464
|
+
for (let i = 0; i < key.length; i++) {
|
|
2465
|
+
const c = key.charCodeAt(i);
|
|
2466
|
+
out += String.fromCharCode(c >= 65 && c <= 90 ? c + 32 : c);
|
|
2467
|
+
}
|
|
2468
|
+
return out;
|
|
2469
|
+
}
|
|
2470
|
+
function matchesDispatchMatcher(value, matcher) {
|
|
2471
|
+
const candidate = matcher.caseInsensitive ? asciiFoldKey(value) : value;
|
|
2472
|
+
const expected = matcher.caseInsensitive ? asciiFoldKey(matcher.value) : matcher.value;
|
|
2473
|
+
switch (matcher.kind) {
|
|
2474
|
+
case "startsWith":
|
|
2475
|
+
return candidate.startsWith(expected);
|
|
2476
|
+
case "endsWith":
|
|
2477
|
+
return candidate.endsWith(expected);
|
|
2478
|
+
case "matches": {
|
|
2479
|
+
const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
|
|
2480
|
+
return new RegExp(matcher.value, flags).test(value);
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
function isStringMatcher(value) {
|
|
2485
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2486
|
+
const kind = value.kind;
|
|
2487
|
+
return kind === "startsWith" || kind === "endsWith" || kind === "matches";
|
|
2488
|
+
}
|
|
2489
|
+
function fixedMatcher(kind, value) {
|
|
2490
|
+
if (typeof value !== "string") throw new TypeError(`parseman: ${kind}() value must be a string`);
|
|
2491
|
+
if (value.length === 0) throw new RangeError(`parseman: ${kind}() requires a non-empty value`);
|
|
2492
|
+
return { kind, value };
|
|
2493
|
+
}
|
|
2494
|
+
function startsWith(prefix) {
|
|
2495
|
+
return fixedMatcher("startsWith", prefix);
|
|
2496
|
+
}
|
|
2497
|
+
function endsWith(suffix) {
|
|
2498
|
+
return fixedMatcher("endsWith", suffix);
|
|
2499
|
+
}
|
|
2500
|
+
function matches(pattern) {
|
|
2501
|
+
if (!(pattern instanceof RegExp)) throw new TypeError("parseman: matches() requires a RegExp");
|
|
2502
|
+
if (pattern.flags.includes("g") || pattern.flags.includes("y")) {
|
|
2503
|
+
throw new TypeError("parseman: matches() does not accept global or sticky regex flags");
|
|
2504
|
+
}
|
|
2505
|
+
return { kind: "matches", value: pattern.source, flags: pattern.flags };
|
|
2506
|
+
}
|
|
2507
|
+
function when(key, parser2, opts = {}) {
|
|
2508
|
+
const usesRouted = parserUsesRouted(parser2);
|
|
2509
|
+
if (isStringMatcher(key)) {
|
|
2510
|
+
return { kind: "whenMatcher", matcher: key, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2511
|
+
}
|
|
2407
2512
|
const keys = Array.isArray(key) ? [...key] : [key];
|
|
2408
2513
|
if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
|
|
2409
2514
|
for (const item of keys) {
|
|
2410
2515
|
if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
|
|
2411
2516
|
}
|
|
2412
|
-
return { kind: "when", keys, parser: parser2 };
|
|
2517
|
+
return { kind: "when", keys, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
|
|
2413
2518
|
}
|
|
2414
2519
|
function otherwise(parser2) {
|
|
2415
|
-
return { kind: "otherwise", parser: parser2 };
|
|
2520
|
+
return { kind: "otherwise", parser: parser2, usesRouted: parserUsesRouted(parser2) };
|
|
2521
|
+
}
|
|
2522
|
+
function routed() {
|
|
2523
|
+
return {
|
|
2524
|
+
_tag: "routed",
|
|
2525
|
+
_meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
|
|
2526
|
+
_def: { tag: "routed" },
|
|
2527
|
+
parse(_input, pos, ctx) {
|
|
2528
|
+
const item = ctx._routed;
|
|
2529
|
+
if (item === void 0) {
|
|
2530
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2531
|
+
}
|
|
2532
|
+
if (pos !== item.span.start) {
|
|
2533
|
+
return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
|
|
2534
|
+
}
|
|
2535
|
+
if (cstCaptureActive(ctx)) {
|
|
2536
|
+
pushCstLeaf(ctx, { _tag: "leaf", value: item.value, span: item.span });
|
|
2537
|
+
}
|
|
2538
|
+
return { ok: true, value: item.value, span: item.span };
|
|
2539
|
+
}
|
|
2540
|
+
};
|
|
2416
2541
|
}
|
|
2417
2542
|
function dispatch(selector, ...arms) {
|
|
2543
|
+
if (parserUsesRouted(selector)) {
|
|
2544
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2545
|
+
}
|
|
2418
2546
|
let fallback;
|
|
2547
|
+
let fallbackUsesRouted = false;
|
|
2548
|
+
const matchers = [];
|
|
2419
2549
|
const cases = [];
|
|
2420
|
-
const seen =
|
|
2550
|
+
const seen = [];
|
|
2421
2551
|
for (let i = 0; i < arms.length; i++) {
|
|
2422
2552
|
const arm = arms[i];
|
|
2423
2553
|
if (arm.kind === "otherwise") {
|
|
2424
2554
|
if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
|
|
2425
2555
|
if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
|
|
2426
2556
|
fallback = arm.parser;
|
|
2557
|
+
fallbackUsesRouted = arm.usesRouted === true;
|
|
2558
|
+
continue;
|
|
2559
|
+
}
|
|
2560
|
+
if (arm.kind === "whenMatcher") {
|
|
2561
|
+
const matcher = arm.matcher;
|
|
2562
|
+
matchers.push({
|
|
2563
|
+
kind: matcher.kind,
|
|
2564
|
+
value: matcher.value,
|
|
2565
|
+
...matcher.kind === "matches" ? { flags: matcher.flags } : {},
|
|
2566
|
+
parser: arm.parser,
|
|
2567
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2568
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2569
|
+
});
|
|
2427
2570
|
continue;
|
|
2428
2571
|
}
|
|
2429
2572
|
for (const key of arm.keys) {
|
|
2430
|
-
|
|
2431
|
-
seen
|
|
2573
|
+
const folded = asciiFoldKey(key);
|
|
2574
|
+
for (const prior of seen) {
|
|
2575
|
+
const overlaps = arm.caseInsensitive || prior.caseInsensitive ? folded === prior.folded : key === prior.raw;
|
|
2576
|
+
if (overlaps) throw new Error(`parseman: duplicate dispatch key ${JSON.stringify(key)}`);
|
|
2577
|
+
}
|
|
2578
|
+
seen.push({ raw: key, folded, caseInsensitive: arm.caseInsensitive });
|
|
2432
2579
|
}
|
|
2433
|
-
cases.push({
|
|
2580
|
+
cases.push({
|
|
2581
|
+
keys: arm.keys,
|
|
2582
|
+
parser: arm.parser,
|
|
2583
|
+
caseInsensitive: arm.caseInsensitive,
|
|
2584
|
+
...arm.usesRouted === true ? { usesRouted: true } : {}
|
|
2585
|
+
});
|
|
2434
2586
|
}
|
|
2435
2587
|
const meta = {
|
|
2436
2588
|
firstSet: selector._meta.firstSet,
|
|
2437
|
-
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2589
|
+
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || matchers.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2438
2590
|
isTrivia: false
|
|
2439
2591
|
};
|
|
2440
2592
|
const byKey = /* @__PURE__ */ new Map();
|
|
2441
|
-
|
|
2593
|
+
const byFoldedKey = /* @__PURE__ */ new Map();
|
|
2594
|
+
for (const entry of cases) {
|
|
2595
|
+
for (const key of entry.keys) {
|
|
2596
|
+
if (entry.caseInsensitive) byFoldedKey.set(asciiFoldKey(key), entry);
|
|
2597
|
+
else byKey.set(key, entry);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2442
2600
|
return {
|
|
2443
2601
|
_tag: "dispatch",
|
|
2444
2602
|
_meta: meta,
|
|
@@ -2446,18 +2604,35 @@ function dispatch(selector, ...arms) {
|
|
|
2446
2604
|
tag: "dispatch",
|
|
2447
2605
|
selector,
|
|
2448
2606
|
cases,
|
|
2449
|
-
...
|
|
2607
|
+
...matchers.length === 0 ? {} : { matchers },
|
|
2608
|
+
...fallback === void 0 ? {} : { otherwise: fallback, ...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {} }
|
|
2450
2609
|
},
|
|
2451
2610
|
parse(input, pos, ctx) {
|
|
2611
|
+
if (parserUsesRouted(selector)) {
|
|
2612
|
+
throw new Error("parseman: routed() can only appear inside a dispatch() branch");
|
|
2613
|
+
}
|
|
2614
|
+
const selectorMark = saveTriviaMark(ctx);
|
|
2452
2615
|
const selected = selector.parse(input, pos, ctx);
|
|
2453
2616
|
if (!selected.ok) return selected;
|
|
2454
|
-
const
|
|
2455
|
-
if (
|
|
2617
|
+
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 });
|
|
2618
|
+
if (branch === void 0) {
|
|
2456
2619
|
const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
|
|
2457
2620
|
return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
|
|
2458
2621
|
}
|
|
2459
|
-
const
|
|
2460
|
-
const
|
|
2622
|
+
const savedRouted = ctx._routed;
|
|
2623
|
+
const usesRouted = branchUsesRouted(branch);
|
|
2624
|
+
let mark = saveTriviaMark(ctx);
|
|
2625
|
+
if (usesRouted) {
|
|
2626
|
+
rollbackTrivia(ctx, selectorMark);
|
|
2627
|
+
mark = saveTriviaMark(ctx);
|
|
2628
|
+
ctx._routed = { value: selected.value, span: selected.span };
|
|
2629
|
+
}
|
|
2630
|
+
let result;
|
|
2631
|
+
try {
|
|
2632
|
+
result = branch.parser.parse(input, usesRouted ? pos : selected.span.end, ctx);
|
|
2633
|
+
} finally {
|
|
2634
|
+
if (usesRouted) ctx._routed = savedRouted;
|
|
2635
|
+
}
|
|
2461
2636
|
if (!result.ok) {
|
|
2462
2637
|
rollbackTrivia(ctx, mark);
|
|
2463
2638
|
return { ...result, committed: true };
|
|
@@ -2555,12 +2730,30 @@ function isCstChild(value) {
|
|
|
2555
2730
|
function missingInferredType() {
|
|
2556
2731
|
throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
|
|
2557
2732
|
}
|
|
2733
|
+
function normalizeProject(project) {
|
|
2734
|
+
if (project === void 0) return void 0;
|
|
2735
|
+
if (!Number.isInteger(project) || project < 0) {
|
|
2736
|
+
throw new Error("node() project child must be a non-negative integer");
|
|
2737
|
+
}
|
|
2738
|
+
return project;
|
|
2739
|
+
}
|
|
2740
|
+
function projectChild(children, project, type) {
|
|
2741
|
+
if (!(project in children)) {
|
|
2742
|
+
throw new Error(`node(${JSON.stringify(type)}) project child ${project} was not captured`);
|
|
2743
|
+
}
|
|
2744
|
+
return unwrapChild(children[project]);
|
|
2745
|
+
}
|
|
2558
2746
|
function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
2559
2747
|
const hasExplicitType = typeof typeOrCombinator === "string";
|
|
2560
2748
|
const type = hasExplicitType ? typeOrCombinator : void 0;
|
|
2561
2749
|
const combinator = hasExplicitType ? combinatorOrBuild : typeOrCombinator;
|
|
2562
|
-
const
|
|
2563
|
-
const
|
|
2750
|
+
const buildArg = hasExplicitType ? buildOrOpts : combinatorOrBuild;
|
|
2751
|
+
const build = typeof buildArg === "function" ? buildArg : void 0;
|
|
2752
|
+
const opts = typeof buildArg === "function" ? maybeOpts : buildArg ?? maybeOpts;
|
|
2753
|
+
const project = normalizeProject(opts?.project);
|
|
2754
|
+
if (project !== void 0 && build !== void 0) {
|
|
2755
|
+
throw new Error("node() options cannot combine project with a build callback");
|
|
2756
|
+
}
|
|
2564
2757
|
const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
|
|
2565
2758
|
const meta = {
|
|
2566
2759
|
firstSet: combinator._meta.firstSet,
|
|
@@ -2574,11 +2767,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
|
2574
2767
|
if (unwrap && collapse) {
|
|
2575
2768
|
throw new Error("node() options cannot set both unwrap and collapse");
|
|
2576
2769
|
}
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2770
|
+
if (project !== void 0 && (unwrap || collapse)) {
|
|
2771
|
+
throw new Error("node() options cannot combine project with unwrap or collapse");
|
|
2772
|
+
}
|
|
2773
|
+
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;
|
|
2774
|
+
const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
|
|
2775
|
+
const clonesState = build ? buildReadsState(def) : project === void 0;
|
|
2580
2776
|
const hasOwnFields = parserHasOwnFields(combinator);
|
|
2581
|
-
const capturesFields = hasOwnFields && (build ? buildReadsFields(def) :
|
|
2777
|
+
const capturesFields = hasOwnFields && (build ? buildReadsFields(def) : project === void 0);
|
|
2582
2778
|
const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
|
|
2583
2779
|
let failExpected;
|
|
2584
2780
|
return {
|
|
@@ -2593,14 +2789,15 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
|
2593
2789
|
}
|
|
2594
2790
|
return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
|
|
2595
2791
|
}
|
|
2596
|
-
const
|
|
2792
|
+
const hasDirectValue = build !== void 0 || project !== void 0;
|
|
2793
|
+
const hostCst = hasDirectValue && cstOutputHost(ctx.build);
|
|
2597
2794
|
const effTrivia = capturesTrivia || hostCst;
|
|
2598
2795
|
const effFields = capturesFields || hostCst && hasOwnFields;
|
|
2599
2796
|
const saved = beginCstNodeCapture(ctx);
|
|
2600
2797
|
const savedFields = ctx._fields;
|
|
2601
2798
|
ctx._fields = effFields ? [] : void 0;
|
|
2602
2799
|
const savedMask = ctx._triviaCaptureMask;
|
|
2603
|
-
if (build === void 0 && ctx.build?._parsemanTriviaKinds !== void 0 && def.type !== void 0) {
|
|
2800
|
+
if (build === void 0 && project === void 0 && ctx.build?._parsemanTriviaKinds !== void 0 && def.type !== void 0) {
|
|
2604
2801
|
ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
|
|
2605
2802
|
}
|
|
2606
2803
|
if (!effTrivia) ctx.captureTrivia = false;
|
|
@@ -2618,7 +2815,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
|
|
|
2618
2815
|
const nodeType = def.type ?? missingInferredType();
|
|
2619
2816
|
const cstOutput = cstOutputHost(ctx.build);
|
|
2620
2817
|
const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
|
|
2621
|
-
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 };
|
|
2818
|
+
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 };
|
|
2622
2819
|
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 };
|
|
2623
2820
|
if (saved.buf !== void 0 || saved.ch !== void 0) {
|
|
2624
2821
|
pushCstChild(ctx, built, rawEntry);
|
|
@@ -2905,7 +3102,11 @@ function evalRuleMapIR(ir) {
|
|
|
2905
3102
|
"choice",
|
|
2906
3103
|
"dispatch",
|
|
2907
3104
|
"when",
|
|
3105
|
+
"startsWith",
|
|
3106
|
+
"endsWith",
|
|
3107
|
+
"matches",
|
|
2908
3108
|
"otherwise",
|
|
3109
|
+
"routed",
|
|
2909
3110
|
"attempt",
|
|
2910
3111
|
"many",
|
|
2911
3112
|
"oneOrMore",
|
|
@@ -2941,7 +3142,11 @@ function evalRuleMapIR(ir) {
|
|
|
2941
3142
|
choice,
|
|
2942
3143
|
dispatch,
|
|
2943
3144
|
when,
|
|
3145
|
+
startsWith,
|
|
3146
|
+
endsWith,
|
|
3147
|
+
matches,
|
|
2944
3148
|
otherwise,
|
|
3149
|
+
routed,
|
|
2945
3150
|
attempt,
|
|
2946
3151
|
many,
|
|
2947
3152
|
oneOrMore,
|
|
@@ -3140,8 +3345,9 @@ var Builder = class {
|
|
|
3140
3345
|
const selector = this.walk(def.selector);
|
|
3141
3346
|
const tails = [
|
|
3142
3347
|
...def.cases.map((entry) => this.walk(entry.parser)),
|
|
3348
|
+
...def.matchers ? def.matchers.map((entry) => this.walk(entry.parser)) : [],
|
|
3143
3349
|
...def.otherwise ? [this.walk(def.otherwise)] : []
|
|
3144
|
-
]
|
|
3350
|
+
];
|
|
3145
3351
|
return flattenSeq([selector, flattenChoice(tails)]);
|
|
3146
3352
|
}
|
|
3147
3353
|
// `max`/`min` are carried through so a BOUNDED repeat does not render as an
|
|
@@ -3173,6 +3379,8 @@ var Builder = class {
|
|
|
3173
3379
|
return { kind: "not", item: this.walk(def.parser) };
|
|
3174
3380
|
case "peek":
|
|
3175
3381
|
return { kind: "peek", item: this.walk(def.parser) };
|
|
3382
|
+
case "routed":
|
|
3383
|
+
return { kind: "empty" };
|
|
3176
3384
|
// Transparent semantic wrappers — render the inner syntax.
|
|
3177
3385
|
case "transform":
|
|
3178
3386
|
case "token":
|
|
@@ -3330,7 +3538,7 @@ function render(node2) {
|
|
|
3330
3538
|
case "choice": {
|
|
3331
3539
|
const text = node2.items.map((it) => {
|
|
3332
3540
|
const r = render(it);
|
|
3333
|
-
return wrap(r.text, r.level, ALT);
|
|
3541
|
+
return r.text.length === 0 ? "/* empty */" : wrap(r.text, r.level, ALT);
|
|
3334
3542
|
}).join(" | ");
|
|
3335
3543
|
return { text, level: ALT };
|
|
3336
3544
|
}
|