parseman 0.38.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/duplication.d.ts.map +1 -1
- package/dist/analysis/gating.d.ts.map +1 -1
- package/dist/combinators/attempt.d.ts.map +1 -1
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/dispatch.d.ts +20 -0
- package/dist/combinators/dispatch.d.ts.map +1 -0
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/fields.d.ts.map +1 -1
- package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
- package/dist/compiler/ir-serialize.d.ts.map +1 -1
- package/dist/compiler/value-usage.d.ts.map +1 -1
- package/dist/coverage.d.ts.map +1 -1
- package/dist/index.cjs +383 -31
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +383 -31
- package/dist/index.js.map +4 -4
- package/dist/language-service/index.cjs.map +2 -2
- package/dist/language-service/index.js.map +2 -2
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +436 -31
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.js +436 -31
- package/dist/plugin/index.js.map +4 -4
- package/dist/spec/index.cjs +107 -0
- package/dist/spec/index.cjs.map +4 -4
- package/dist/spec/index.js +107 -0
- package/dist/spec/index.js.map +4 -4
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/spec/index.cjs
CHANGED
|
@@ -108,6 +108,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
|
108
108
|
return d.parsers.every(me);
|
|
109
109
|
case "choice":
|
|
110
110
|
return d.parsers.some(me);
|
|
111
|
+
case "dispatch":
|
|
112
|
+
return me(d.selector);
|
|
111
113
|
case "transform":
|
|
112
114
|
case "label":
|
|
113
115
|
case "trivia":
|
|
@@ -181,6 +183,8 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
|
181
183
|
case "keywords":
|
|
182
184
|
return p._meta.firstSet;
|
|
183
185
|
// terminals: no refs, cached set is exact
|
|
186
|
+
case "dispatch":
|
|
187
|
+
return fs(d.selector);
|
|
184
188
|
case "lazy":
|
|
185
189
|
try {
|
|
186
190
|
return fs(d.thunk());
|
|
@@ -313,6 +317,11 @@ function markUnusedValues(root) {
|
|
|
313
317
|
case "choice":
|
|
314
318
|
for (const p of def.parsers) visit(p, consumed);
|
|
315
319
|
return;
|
|
320
|
+
case "dispatch":
|
|
321
|
+
visit(def.selector, consumed);
|
|
322
|
+
for (const entry of def.cases) visit(entry.parser, consumed);
|
|
323
|
+
if (def.otherwise) visit(def.otherwise, consumed);
|
|
324
|
+
return;
|
|
316
325
|
case "node":
|
|
317
326
|
visit(def.parser, false);
|
|
318
327
|
return;
|
|
@@ -723,6 +732,8 @@ function derive(c, seen) {
|
|
|
723
732
|
return [def.label];
|
|
724
733
|
case "choice":
|
|
725
734
|
return def.parsers.flatMap(deriveExpected2);
|
|
735
|
+
case "dispatch":
|
|
736
|
+
return deriveExpected2(def.selector);
|
|
726
737
|
// Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
|
|
727
738
|
// `optional(…)`/`many(…)` can match nothing, so the term after it is equally
|
|
728
739
|
// able to fail first — deriving from term 0 alone named a token the parse
|
|
@@ -861,6 +872,7 @@ function choice(...args) {
|
|
|
861
872
|
const result = parsers[idx].parse(input, pos, ctx);
|
|
862
873
|
if (result.ok) return result;
|
|
863
874
|
expected.push(...result.expected);
|
|
875
|
+
if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
|
|
864
876
|
return { ok: false, expected, span: { start: pos, end: pos } };
|
|
865
877
|
}
|
|
866
878
|
return {
|
|
@@ -901,6 +913,7 @@ function choice(...args) {
|
|
|
901
913
|
rollbackCstCapture(ctx, mark);
|
|
902
914
|
if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
|
|
903
915
|
expected.push(...result.expected);
|
|
916
|
+
if (result.committed) return { ok: false, expected, span: result.span, committed: true };
|
|
904
917
|
continue;
|
|
905
918
|
}
|
|
906
919
|
const checks = autoNot[i];
|
|
@@ -1851,6 +1864,7 @@ function repItem(combinator, input, cur, ctx, guardable) {
|
|
|
1851
1864
|
const result = combinator.parse(input, pos, ctx);
|
|
1852
1865
|
if (!result.ok) {
|
|
1853
1866
|
rollbackTrivia(ctx, mark);
|
|
1867
|
+
if (result.committed) return { fail: result, failPos: pos };
|
|
1854
1868
|
return { fail: result, failPos: pos };
|
|
1855
1869
|
}
|
|
1856
1870
|
if (result.span.end === pos) {
|
|
@@ -1894,6 +1908,7 @@ function many(combinator, opts = {}) {
|
|
|
1894
1908
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1895
1909
|
if (item === "stop") break;
|
|
1896
1910
|
if ("fail" in item) {
|
|
1911
|
+
if (!item.fail.ok && item.fail.committed) return item.fail;
|
|
1897
1912
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
1898
1913
|
if (sync === void 0) break;
|
|
1899
1914
|
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
@@ -1938,6 +1953,7 @@ function atLeast(combinator, min, max) {
|
|
|
1938
1953
|
while (count < min) {
|
|
1939
1954
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1940
1955
|
if (item === "stop" || "fail" in item) {
|
|
1956
|
+
if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
|
|
1941
1957
|
expected ??= deriveExpected(combinator);
|
|
1942
1958
|
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
|
|
1943
1959
|
}
|
|
@@ -1950,6 +1966,7 @@ function atLeast(combinator, min, max) {
|
|
|
1950
1966
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1951
1967
|
if (item === "stop") break;
|
|
1952
1968
|
if ("fail" in item) {
|
|
1969
|
+
if (!item.fail.ok && item.fail.committed) return item.fail;
|
|
1953
1970
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
1954
1971
|
if (sync === void 0) break;
|
|
1955
1972
|
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
@@ -1988,6 +2005,7 @@ function optional(combinator) {
|
|
|
1988
2005
|
const result = combinator.parse(input, pos, ctx);
|
|
1989
2006
|
if (result.ok) return result;
|
|
1990
2007
|
rollbackTrivia(ctx, mark);
|
|
2008
|
+
if (result.committed) return result;
|
|
1991
2009
|
return { ok: true, value: null, span: { start: pos, end: pos } };
|
|
1992
2010
|
}
|
|
1993
2011
|
};
|
|
@@ -2024,6 +2042,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2024
2042
|
values.push(first.value);
|
|
2025
2043
|
cur = first.span.end;
|
|
2026
2044
|
} else {
|
|
2045
|
+
if (first.committed) return first;
|
|
2027
2046
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2028
2047
|
if (term === void 0 || matchesAt(term, input, pos, ctx)) {
|
|
2029
2048
|
if (min >= 1) return failAt2(pos);
|
|
@@ -2051,6 +2070,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2051
2070
|
const sep = separator.parse(input, sepPos, ctx);
|
|
2052
2071
|
if (!sep.ok) {
|
|
2053
2072
|
rollbackTrivia(ctx, loopMark);
|
|
2073
|
+
if (sep.committed) return sep;
|
|
2054
2074
|
break;
|
|
2055
2075
|
}
|
|
2056
2076
|
const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
|
|
@@ -2066,6 +2086,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2066
2086
|
}
|
|
2067
2087
|
const next = combinator.parse(input, nextPos, ctx);
|
|
2068
2088
|
if (!next.ok) {
|
|
2089
|
+
if (next.committed) return next;
|
|
2069
2090
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2070
2091
|
if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
|
|
2071
2092
|
expected ??= deriveExpected(combinator);
|
|
@@ -2299,6 +2320,8 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2299
2320
|
case "sequence":
|
|
2300
2321
|
case "choice":
|
|
2301
2322
|
return d.parsers.some((x) => parserHasOwnFields(x, seen));
|
|
2323
|
+
case "dispatch":
|
|
2324
|
+
return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
|
|
2302
2325
|
case "sepBy":
|
|
2303
2326
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
|
|
2304
2327
|
case "skip":
|
|
@@ -2413,6 +2436,75 @@ function keywords(words, opts = {}) {
|
|
|
2413
2436
|
};
|
|
2414
2437
|
}
|
|
2415
2438
|
|
|
2439
|
+
// src/combinators/dispatch.ts
|
|
2440
|
+
function when(key, parser2) {
|
|
2441
|
+
const keys = Array.isArray(key) ? [...key] : [key];
|
|
2442
|
+
if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
|
|
2443
|
+
for (const item of keys) {
|
|
2444
|
+
if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
|
|
2445
|
+
}
|
|
2446
|
+
return { kind: "when", keys, parser: parser2 };
|
|
2447
|
+
}
|
|
2448
|
+
function otherwise(parser2) {
|
|
2449
|
+
return { kind: "otherwise", parser: parser2 };
|
|
2450
|
+
}
|
|
2451
|
+
function dispatch(selector, ...arms) {
|
|
2452
|
+
let fallback;
|
|
2453
|
+
const cases = [];
|
|
2454
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2455
|
+
for (let i = 0; i < arms.length; i++) {
|
|
2456
|
+
const arm = arms[i];
|
|
2457
|
+
if (arm.kind === "otherwise") {
|
|
2458
|
+
if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
|
|
2459
|
+
if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
|
|
2460
|
+
fallback = arm.parser;
|
|
2461
|
+
continue;
|
|
2462
|
+
}
|
|
2463
|
+
for (const key of arm.keys) {
|
|
2464
|
+
if (seen.has(key)) throw new Error(`parseman: duplicate dispatch key ${JSON.stringify(key)}`);
|
|
2465
|
+
seen.add(key);
|
|
2466
|
+
}
|
|
2467
|
+
cases.push({ keys: arm.keys, parser: arm.parser });
|
|
2468
|
+
}
|
|
2469
|
+
const meta = {
|
|
2470
|
+
firstSet: selector._meta.firstSet,
|
|
2471
|
+
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2472
|
+
isTrivia: false
|
|
2473
|
+
};
|
|
2474
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2475
|
+
for (const entry of cases) for (const key of entry.keys) byKey.set(key, entry.parser);
|
|
2476
|
+
return {
|
|
2477
|
+
_tag: "dispatch",
|
|
2478
|
+
_meta: meta,
|
|
2479
|
+
_def: {
|
|
2480
|
+
tag: "dispatch",
|
|
2481
|
+
selector,
|
|
2482
|
+
cases,
|
|
2483
|
+
...fallback === void 0 ? {} : { otherwise: fallback }
|
|
2484
|
+
},
|
|
2485
|
+
parse(input, pos, ctx) {
|
|
2486
|
+
const selected = selector.parse(input, pos, ctx);
|
|
2487
|
+
if (!selected.ok) return selected;
|
|
2488
|
+
const tail = byKey.get(selected.value) ?? fallback;
|
|
2489
|
+
if (tail === void 0) {
|
|
2490
|
+
const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
|
|
2491
|
+
return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
|
|
2492
|
+
}
|
|
2493
|
+
const mark = saveTriviaMark(ctx);
|
|
2494
|
+
const result = tail.parse(input, selected.span.end, ctx);
|
|
2495
|
+
if (!result.ok) {
|
|
2496
|
+
rollbackTrivia(ctx, mark);
|
|
2497
|
+
return { ...result, committed: true };
|
|
2498
|
+
}
|
|
2499
|
+
return {
|
|
2500
|
+
ok: true,
|
|
2501
|
+
value: [selected.value, result.value],
|
|
2502
|
+
span: { start: pos, end: result.span.end }
|
|
2503
|
+
};
|
|
2504
|
+
}
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2416
2508
|
// src/combinators/attempt.ts
|
|
2417
2509
|
function attempt(parser2) {
|
|
2418
2510
|
const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
|
|
@@ -2435,6 +2527,7 @@ function attempt(parser2) {
|
|
|
2435
2527
|
if (result.ok) return result;
|
|
2436
2528
|
rollbackCstCapture(ctx, mark);
|
|
2437
2529
|
if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
|
|
2530
|
+
if (result.committed) return result;
|
|
2438
2531
|
return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
|
|
2439
2532
|
}
|
|
2440
2533
|
};
|
|
@@ -2844,6 +2937,9 @@ function evalRuleMapIR(ir) {
|
|
|
2844
2937
|
"keywords",
|
|
2845
2938
|
"sequence",
|
|
2846
2939
|
"choice",
|
|
2940
|
+
"dispatch",
|
|
2941
|
+
"when",
|
|
2942
|
+
"otherwise",
|
|
2847
2943
|
"attempt",
|
|
2848
2944
|
"many",
|
|
2849
2945
|
"oneOrMore",
|
|
@@ -2877,6 +2973,9 @@ function evalRuleMapIR(ir) {
|
|
|
2877
2973
|
keywords,
|
|
2878
2974
|
sequence,
|
|
2879
2975
|
choice,
|
|
2976
|
+
dispatch,
|
|
2977
|
+
when,
|
|
2978
|
+
otherwise,
|
|
2880
2979
|
attempt,
|
|
2881
2980
|
many,
|
|
2882
2981
|
oneOrMore,
|
|
@@ -3071,6 +3170,14 @@ var Builder = class {
|
|
|
3071
3170
|
const items = def.parsers.map((p) => this.walk(p)).filter(nonEmpty);
|
|
3072
3171
|
return flattenChoice(items);
|
|
3073
3172
|
}
|
|
3173
|
+
case "dispatch": {
|
|
3174
|
+
const selector = this.walk(def.selector);
|
|
3175
|
+
const tails = [
|
|
3176
|
+
...def.cases.map((entry) => this.walk(entry.parser)),
|
|
3177
|
+
...def.otherwise ? [this.walk(def.otherwise)] : []
|
|
3178
|
+
].filter(nonEmpty);
|
|
3179
|
+
return flattenSeq([selector, flattenChoice(tails)]);
|
|
3180
|
+
}
|
|
3074
3181
|
// `max`/`min` are carried through so a BOUNDED repeat does not render as an
|
|
3075
3182
|
// unbounded one — the spec is generated to be read as the truth about what
|
|
3076
3183
|
// parses, and `many(x, { min: 3, max: 8 })` is not `x+`.
|