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.js
CHANGED
|
@@ -74,6 +74,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
|
74
74
|
return d.parsers.every(me);
|
|
75
75
|
case "choice":
|
|
76
76
|
return d.parsers.some(me);
|
|
77
|
+
case "dispatch":
|
|
78
|
+
return me(d.selector);
|
|
77
79
|
case "transform":
|
|
78
80
|
case "label":
|
|
79
81
|
case "trivia":
|
|
@@ -147,6 +149,8 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve) {
|
|
|
147
149
|
case "keywords":
|
|
148
150
|
return p._meta.firstSet;
|
|
149
151
|
// terminals: no refs, cached set is exact
|
|
152
|
+
case "dispatch":
|
|
153
|
+
return fs(d.selector);
|
|
150
154
|
case "lazy":
|
|
151
155
|
try {
|
|
152
156
|
return fs(d.thunk());
|
|
@@ -279,6 +283,11 @@ function markUnusedValues(root) {
|
|
|
279
283
|
case "choice":
|
|
280
284
|
for (const p of def.parsers) visit(p, consumed);
|
|
281
285
|
return;
|
|
286
|
+
case "dispatch":
|
|
287
|
+
visit(def.selector, consumed);
|
|
288
|
+
for (const entry of def.cases) visit(entry.parser, consumed);
|
|
289
|
+
if (def.otherwise) visit(def.otherwise, consumed);
|
|
290
|
+
return;
|
|
282
291
|
case "node":
|
|
283
292
|
visit(def.parser, false);
|
|
284
293
|
return;
|
|
@@ -689,6 +698,8 @@ function derive(c, seen) {
|
|
|
689
698
|
return [def.label];
|
|
690
699
|
case "choice":
|
|
691
700
|
return def.parsers.flatMap(deriveExpected2);
|
|
701
|
+
case "dispatch":
|
|
702
|
+
return deriveExpected2(def.selector);
|
|
692
703
|
// Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
|
|
693
704
|
// `optional(…)`/`many(…)` can match nothing, so the term after it is equally
|
|
694
705
|
// able to fail first — deriving from term 0 alone named a token the parse
|
|
@@ -827,6 +838,7 @@ function choice(...args) {
|
|
|
827
838
|
const result = parsers[idx].parse(input, pos, ctx);
|
|
828
839
|
if (result.ok) return result;
|
|
829
840
|
expected.push(...result.expected);
|
|
841
|
+
if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
|
|
830
842
|
return { ok: false, expected, span: { start: pos, end: pos } };
|
|
831
843
|
}
|
|
832
844
|
return {
|
|
@@ -867,6 +879,7 @@ function choice(...args) {
|
|
|
867
879
|
rollbackCstCapture(ctx, mark);
|
|
868
880
|
if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
|
|
869
881
|
expected.push(...result.expected);
|
|
882
|
+
if (result.committed) return { ok: false, expected, span: result.span, committed: true };
|
|
870
883
|
continue;
|
|
871
884
|
}
|
|
872
885
|
const checks = autoNot[i];
|
|
@@ -1817,6 +1830,7 @@ function repItem(combinator, input, cur, ctx, guardable) {
|
|
|
1817
1830
|
const result = combinator.parse(input, pos, ctx);
|
|
1818
1831
|
if (!result.ok) {
|
|
1819
1832
|
rollbackTrivia(ctx, mark);
|
|
1833
|
+
if (result.committed) return { fail: result, failPos: pos };
|
|
1820
1834
|
return { fail: result, failPos: pos };
|
|
1821
1835
|
}
|
|
1822
1836
|
if (result.span.end === pos) {
|
|
@@ -1860,6 +1874,7 @@ function many(combinator, opts = {}) {
|
|
|
1860
1874
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1861
1875
|
if (item === "stop") break;
|
|
1862
1876
|
if ("fail" in item) {
|
|
1877
|
+
if (!item.fail.ok && item.fail.committed) return item.fail;
|
|
1863
1878
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
1864
1879
|
if (sync === void 0) break;
|
|
1865
1880
|
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
@@ -1904,6 +1919,7 @@ function atLeast(combinator, min, max) {
|
|
|
1904
1919
|
while (count < min) {
|
|
1905
1920
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1906
1921
|
if (item === "stop" || "fail" in item) {
|
|
1922
|
+
if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
|
|
1907
1923
|
expected ??= deriveExpected(combinator);
|
|
1908
1924
|
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
|
|
1909
1925
|
}
|
|
@@ -1916,6 +1932,7 @@ function atLeast(combinator, min, max) {
|
|
|
1916
1932
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1917
1933
|
if (item === "stop") break;
|
|
1918
1934
|
if ("fail" in item) {
|
|
1935
|
+
if (!item.fail.ok && item.fail.committed) return item.fail;
|
|
1919
1936
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
1920
1937
|
if (sync === void 0) break;
|
|
1921
1938
|
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
@@ -1954,6 +1971,7 @@ function optional(combinator) {
|
|
|
1954
1971
|
const result = combinator.parse(input, pos, ctx);
|
|
1955
1972
|
if (result.ok) return result;
|
|
1956
1973
|
rollbackTrivia(ctx, mark);
|
|
1974
|
+
if (result.committed) return result;
|
|
1957
1975
|
return { ok: true, value: null, span: { start: pos, end: pos } };
|
|
1958
1976
|
}
|
|
1959
1977
|
};
|
|
@@ -1990,6 +2008,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
1990
2008
|
values.push(first.value);
|
|
1991
2009
|
cur = first.span.end;
|
|
1992
2010
|
} else {
|
|
2011
|
+
if (first.committed) return first;
|
|
1993
2012
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
1994
2013
|
if (term === void 0 || matchesAt(term, input, pos, ctx)) {
|
|
1995
2014
|
if (min >= 1) return failAt2(pos);
|
|
@@ -2017,6 +2036,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2017
2036
|
const sep = separator.parse(input, sepPos, ctx);
|
|
2018
2037
|
if (!sep.ok) {
|
|
2019
2038
|
rollbackTrivia(ctx, loopMark);
|
|
2039
|
+
if (sep.committed) return sep;
|
|
2020
2040
|
break;
|
|
2021
2041
|
}
|
|
2022
2042
|
const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
|
|
@@ -2032,6 +2052,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2032
2052
|
}
|
|
2033
2053
|
const next = combinator.parse(input, nextPos, ctx);
|
|
2034
2054
|
if (!next.ok) {
|
|
2055
|
+
if (next.committed) return next;
|
|
2035
2056
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2036
2057
|
if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
|
|
2037
2058
|
expected ??= deriveExpected(combinator);
|
|
@@ -2265,6 +2286,8 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2265
2286
|
case "sequence":
|
|
2266
2287
|
case "choice":
|
|
2267
2288
|
return d.parsers.some((x) => parserHasOwnFields(x, seen));
|
|
2289
|
+
case "dispatch":
|
|
2290
|
+
return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
|
|
2268
2291
|
case "sepBy":
|
|
2269
2292
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
|
|
2270
2293
|
case "skip":
|
|
@@ -2379,6 +2402,75 @@ function keywords(words, opts = {}) {
|
|
|
2379
2402
|
};
|
|
2380
2403
|
}
|
|
2381
2404
|
|
|
2405
|
+
// src/combinators/dispatch.ts
|
|
2406
|
+
function when(key, parser2) {
|
|
2407
|
+
const keys = Array.isArray(key) ? [...key] : [key];
|
|
2408
|
+
if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
|
|
2409
|
+
for (const item of keys) {
|
|
2410
|
+
if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
|
|
2411
|
+
}
|
|
2412
|
+
return { kind: "when", keys, parser: parser2 };
|
|
2413
|
+
}
|
|
2414
|
+
function otherwise(parser2) {
|
|
2415
|
+
return { kind: "otherwise", parser: parser2 };
|
|
2416
|
+
}
|
|
2417
|
+
function dispatch(selector, ...arms) {
|
|
2418
|
+
let fallback;
|
|
2419
|
+
const cases = [];
|
|
2420
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2421
|
+
for (let i = 0; i < arms.length; i++) {
|
|
2422
|
+
const arm = arms[i];
|
|
2423
|
+
if (arm.kind === "otherwise") {
|
|
2424
|
+
if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
|
|
2425
|
+
if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
|
|
2426
|
+
fallback = arm.parser;
|
|
2427
|
+
continue;
|
|
2428
|
+
}
|
|
2429
|
+
for (const key of arm.keys) {
|
|
2430
|
+
if (seen.has(key)) throw new Error(`parseman: duplicate dispatch key ${JSON.stringify(key)}`);
|
|
2431
|
+
seen.add(key);
|
|
2432
|
+
}
|
|
2433
|
+
cases.push({ keys: arm.keys, parser: arm.parser });
|
|
2434
|
+
}
|
|
2435
|
+
const meta = {
|
|
2436
|
+
firstSet: selector._meta.firstSet,
|
|
2437
|
+
canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
|
|
2438
|
+
isTrivia: false
|
|
2439
|
+
};
|
|
2440
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2441
|
+
for (const entry of cases) for (const key of entry.keys) byKey.set(key, entry.parser);
|
|
2442
|
+
return {
|
|
2443
|
+
_tag: "dispatch",
|
|
2444
|
+
_meta: meta,
|
|
2445
|
+
_def: {
|
|
2446
|
+
tag: "dispatch",
|
|
2447
|
+
selector,
|
|
2448
|
+
cases,
|
|
2449
|
+
...fallback === void 0 ? {} : { otherwise: fallback }
|
|
2450
|
+
},
|
|
2451
|
+
parse(input, pos, ctx) {
|
|
2452
|
+
const selected = selector.parse(input, pos, ctx);
|
|
2453
|
+
if (!selected.ok) return selected;
|
|
2454
|
+
const tail = byKey.get(selected.value) ?? fallback;
|
|
2455
|
+
if (tail === void 0) {
|
|
2456
|
+
const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
|
|
2457
|
+
return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
|
|
2458
|
+
}
|
|
2459
|
+
const mark = saveTriviaMark(ctx);
|
|
2460
|
+
const result = tail.parse(input, selected.span.end, ctx);
|
|
2461
|
+
if (!result.ok) {
|
|
2462
|
+
rollbackTrivia(ctx, mark);
|
|
2463
|
+
return { ...result, committed: true };
|
|
2464
|
+
}
|
|
2465
|
+
return {
|
|
2466
|
+
ok: true,
|
|
2467
|
+
value: [selected.value, result.value],
|
|
2468
|
+
span: { start: pos, end: result.span.end }
|
|
2469
|
+
};
|
|
2470
|
+
}
|
|
2471
|
+
};
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2382
2474
|
// src/combinators/attempt.ts
|
|
2383
2475
|
function attempt(parser2) {
|
|
2384
2476
|
const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
|
|
@@ -2401,6 +2493,7 @@ function attempt(parser2) {
|
|
|
2401
2493
|
if (result.ok) return result;
|
|
2402
2494
|
rollbackCstCapture(ctx, mark);
|
|
2403
2495
|
if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
|
|
2496
|
+
if (result.committed) return result;
|
|
2404
2497
|
return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
|
|
2405
2498
|
}
|
|
2406
2499
|
};
|
|
@@ -2810,6 +2903,9 @@ function evalRuleMapIR(ir) {
|
|
|
2810
2903
|
"keywords",
|
|
2811
2904
|
"sequence",
|
|
2812
2905
|
"choice",
|
|
2906
|
+
"dispatch",
|
|
2907
|
+
"when",
|
|
2908
|
+
"otherwise",
|
|
2813
2909
|
"attempt",
|
|
2814
2910
|
"many",
|
|
2815
2911
|
"oneOrMore",
|
|
@@ -2843,6 +2939,9 @@ function evalRuleMapIR(ir) {
|
|
|
2843
2939
|
keywords,
|
|
2844
2940
|
sequence,
|
|
2845
2941
|
choice,
|
|
2942
|
+
dispatch,
|
|
2943
|
+
when,
|
|
2944
|
+
otherwise,
|
|
2846
2945
|
attempt,
|
|
2847
2946
|
many,
|
|
2848
2947
|
oneOrMore,
|
|
@@ -3037,6 +3136,14 @@ var Builder = class {
|
|
|
3037
3136
|
const items = def.parsers.map((p) => this.walk(p)).filter(nonEmpty);
|
|
3038
3137
|
return flattenChoice(items);
|
|
3039
3138
|
}
|
|
3139
|
+
case "dispatch": {
|
|
3140
|
+
const selector = this.walk(def.selector);
|
|
3141
|
+
const tails = [
|
|
3142
|
+
...def.cases.map((entry) => this.walk(entry.parser)),
|
|
3143
|
+
...def.otherwise ? [this.walk(def.otherwise)] : []
|
|
3144
|
+
].filter(nonEmpty);
|
|
3145
|
+
return flattenSeq([selector, flattenChoice(tails)]);
|
|
3146
|
+
}
|
|
3040
3147
|
// `max`/`min` are carried through so a BOUNDED repeat does not render as an
|
|
3041
3148
|
// unbounded one — the spec is generated to be read as the truth about what
|
|
3042
3149
|
// parses, and `many(x, { min: 3, max: 8 })` is not `x+`.
|