parseman 0.37.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/keywords.d.ts +9 -8
- package/dist/combinators/keywords.d.ts.map +1 -1
- package/dist/combinators/parser.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 +432 -53
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +432 -53
- 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 +2 -1
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +517 -67
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.js +517 -67
- package/dist/plugin/index.js.map +4 -4
- package/dist/spec/index.cjs +133 -6
- package/dist/spec/index.cjs.map +4 -4
- package/dist/spec/index.js +133 -6
- 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;
|
|
@@ -384,6 +393,16 @@ function tagRule(r, key) {
|
|
|
384
393
|
r._ruleName = key;
|
|
385
394
|
if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
|
|
386
395
|
}
|
|
396
|
+
function isDefinableRef(v) {
|
|
397
|
+
return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
|
|
398
|
+
}
|
|
399
|
+
function ruleNameOf(r) {
|
|
400
|
+
return r._ruleName;
|
|
401
|
+
}
|
|
402
|
+
function isNamedRuleRefForAnotherRule(r, key) {
|
|
403
|
+
const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
|
|
404
|
+
return name !== void 0 && name !== key;
|
|
405
|
+
}
|
|
387
406
|
function rules(a, b) {
|
|
388
407
|
const factory = typeof a === "function" ? a : b;
|
|
389
408
|
const options = typeof a === "function" ? b : a;
|
|
@@ -404,11 +423,21 @@ function rules(a, b) {
|
|
|
404
423
|
for (const key of Object.keys(definitions)) {
|
|
405
424
|
const placeholder = cache[key];
|
|
406
425
|
const parser2 = definitions[key];
|
|
407
|
-
if (placeholder
|
|
408
|
-
|
|
426
|
+
if (placeholder === parser2) {
|
|
427
|
+
throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
|
|
428
|
+
}
|
|
429
|
+
if (isDefinableRef(placeholder)) {
|
|
430
|
+
if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
|
|
409
431
|
placeholder.define(parser2);
|
|
410
432
|
placeholder._meta.firstSet = parser2._meta.firstSet;
|
|
411
433
|
placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
|
|
434
|
+
} else if (isNamedRuleRefForAnotherRule(parser2, key)) {
|
|
435
|
+
const alias = ref();
|
|
436
|
+
tagRule(alias, key);
|
|
437
|
+
alias.define(parser2);
|
|
438
|
+
alias._meta.firstSet = parser2._meta.firstSet;
|
|
439
|
+
alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
|
|
440
|
+
cache[key] = alias;
|
|
412
441
|
} else {
|
|
413
442
|
tagRule(parser2, key);
|
|
414
443
|
cache[key] = parser2;
|
|
@@ -703,6 +732,8 @@ function derive(c, seen) {
|
|
|
703
732
|
return [def.label];
|
|
704
733
|
case "choice":
|
|
705
734
|
return def.parsers.flatMap(deriveExpected2);
|
|
735
|
+
case "dispatch":
|
|
736
|
+
return deriveExpected2(def.selector);
|
|
706
737
|
// Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
|
|
707
738
|
// `optional(…)`/`many(…)` can match nothing, so the term after it is equally
|
|
708
739
|
// able to fail first — deriving from term 0 alone named a token the parse
|
|
@@ -841,6 +872,7 @@ function choice(...args) {
|
|
|
841
872
|
const result = parsers[idx].parse(input, pos, ctx);
|
|
842
873
|
if (result.ok) return result;
|
|
843
874
|
expected.push(...result.expected);
|
|
875
|
+
if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
|
|
844
876
|
return { ok: false, expected, span: { start: pos, end: pos } };
|
|
845
877
|
}
|
|
846
878
|
return {
|
|
@@ -881,6 +913,7 @@ function choice(...args) {
|
|
|
881
913
|
rollbackCstCapture(ctx, mark);
|
|
882
914
|
if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
|
|
883
915
|
expected.push(...result.expected);
|
|
916
|
+
if (result.committed) return { ok: false, expected, span: result.span, committed: true };
|
|
884
917
|
continue;
|
|
885
918
|
}
|
|
886
919
|
const checks = autoNot[i];
|
|
@@ -1831,6 +1864,7 @@ function repItem(combinator, input, cur, ctx, guardable) {
|
|
|
1831
1864
|
const result = combinator.parse(input, pos, ctx);
|
|
1832
1865
|
if (!result.ok) {
|
|
1833
1866
|
rollbackTrivia(ctx, mark);
|
|
1867
|
+
if (result.committed) return { fail: result, failPos: pos };
|
|
1834
1868
|
return { fail: result, failPos: pos };
|
|
1835
1869
|
}
|
|
1836
1870
|
if (result.span.end === pos) {
|
|
@@ -1874,6 +1908,7 @@ function many(combinator, opts = {}) {
|
|
|
1874
1908
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1875
1909
|
if (item === "stop") break;
|
|
1876
1910
|
if ("fail" in item) {
|
|
1911
|
+
if (!item.fail.ok && item.fail.committed) return item.fail;
|
|
1877
1912
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
1878
1913
|
if (sync === void 0) break;
|
|
1879
1914
|
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
@@ -1918,6 +1953,7 @@ function atLeast(combinator, min, max) {
|
|
|
1918
1953
|
while (count < min) {
|
|
1919
1954
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1920
1955
|
if (item === "stop" || "fail" in item) {
|
|
1956
|
+
if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
|
|
1921
1957
|
expected ??= deriveExpected(combinator);
|
|
1922
1958
|
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
|
|
1923
1959
|
}
|
|
@@ -1930,6 +1966,7 @@ function atLeast(combinator, min, max) {
|
|
|
1930
1966
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1931
1967
|
if (item === "stop") break;
|
|
1932
1968
|
if ("fail" in item) {
|
|
1969
|
+
if (!item.fail.ok && item.fail.committed) return item.fail;
|
|
1933
1970
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
1934
1971
|
if (sync === void 0) break;
|
|
1935
1972
|
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
@@ -1968,6 +2005,7 @@ function optional(combinator) {
|
|
|
1968
2005
|
const result = combinator.parse(input, pos, ctx);
|
|
1969
2006
|
if (result.ok) return result;
|
|
1970
2007
|
rollbackTrivia(ctx, mark);
|
|
2008
|
+
if (result.committed) return result;
|
|
1971
2009
|
return { ok: true, value: null, span: { start: pos, end: pos } };
|
|
1972
2010
|
}
|
|
1973
2011
|
};
|
|
@@ -2004,6 +2042,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2004
2042
|
values.push(first.value);
|
|
2005
2043
|
cur = first.span.end;
|
|
2006
2044
|
} else {
|
|
2045
|
+
if (first.committed) return first;
|
|
2007
2046
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2008
2047
|
if (term === void 0 || matchesAt(term, input, pos, ctx)) {
|
|
2009
2048
|
if (min >= 1) return failAt2(pos);
|
|
@@ -2031,6 +2070,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2031
2070
|
const sep = separator.parse(input, sepPos, ctx);
|
|
2032
2071
|
if (!sep.ok) {
|
|
2033
2072
|
rollbackTrivia(ctx, loopMark);
|
|
2073
|
+
if (sep.committed) return sep;
|
|
2034
2074
|
break;
|
|
2035
2075
|
}
|
|
2036
2076
|
const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
|
|
@@ -2046,6 +2086,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2046
2086
|
}
|
|
2047
2087
|
const next = combinator.parse(input, nextPos, ctx);
|
|
2048
2088
|
if (!next.ok) {
|
|
2089
|
+
if (next.committed) return next;
|
|
2049
2090
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2050
2091
|
if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
|
|
2051
2092
|
expected ??= deriveExpected(combinator);
|
|
@@ -2279,6 +2320,8 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2279
2320
|
case "sequence":
|
|
2280
2321
|
case "choice":
|
|
2281
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);
|
|
2282
2325
|
case "sepBy":
|
|
2283
2326
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
|
|
2284
2327
|
case "skip":
|
|
@@ -2393,6 +2436,75 @@ function keywords(words, opts = {}) {
|
|
|
2393
2436
|
};
|
|
2394
2437
|
}
|
|
2395
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
|
+
|
|
2396
2508
|
// src/combinators/attempt.ts
|
|
2397
2509
|
function attempt(parser2) {
|
|
2398
2510
|
const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
|
|
@@ -2415,6 +2527,7 @@ function attempt(parser2) {
|
|
|
2415
2527
|
if (result.ok) return result;
|
|
2416
2528
|
rollbackCstCapture(ctx, mark);
|
|
2417
2529
|
if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
|
|
2530
|
+
if (result.committed) return result;
|
|
2418
2531
|
return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
|
|
2419
2532
|
}
|
|
2420
2533
|
};
|
|
@@ -2824,6 +2937,9 @@ function evalRuleMapIR(ir) {
|
|
|
2824
2937
|
"keywords",
|
|
2825
2938
|
"sequence",
|
|
2826
2939
|
"choice",
|
|
2940
|
+
"dispatch",
|
|
2941
|
+
"when",
|
|
2942
|
+
"otherwise",
|
|
2827
2943
|
"attempt",
|
|
2828
2944
|
"many",
|
|
2829
2945
|
"oneOrMore",
|
|
@@ -2857,6 +2973,9 @@ function evalRuleMapIR(ir) {
|
|
|
2857
2973
|
keywords,
|
|
2858
2974
|
sequence,
|
|
2859
2975
|
choice,
|
|
2976
|
+
dispatch,
|
|
2977
|
+
when,
|
|
2978
|
+
otherwise,
|
|
2860
2979
|
attempt,
|
|
2861
2980
|
many,
|
|
2862
2981
|
oneOrMore,
|
|
@@ -2947,7 +3066,7 @@ function recoverComposedRules(grammar) {
|
|
|
2947
3066
|
}
|
|
2948
3067
|
|
|
2949
3068
|
// src/spec/model.ts
|
|
2950
|
-
function
|
|
3069
|
+
function ruleNameOf2(c) {
|
|
2951
3070
|
return c._ruleName;
|
|
2952
3071
|
}
|
|
2953
3072
|
function resolveLazy(c) {
|
|
@@ -3012,7 +3131,7 @@ var Builder = class {
|
|
|
3012
3131
|
if (pinned !== void 0) return { kind: "terminal", text: pinned, literal: false };
|
|
3013
3132
|
let c = comb;
|
|
3014
3133
|
const guard = /* @__PURE__ */ new Set();
|
|
3015
|
-
while (c._def.tag === "lazy" &&
|
|
3134
|
+
while (c._def.tag === "lazy" && ruleNameOf2(c) === name && !guard.has(c)) {
|
|
3016
3135
|
guard.add(c);
|
|
3017
3136
|
const next = resolveLazy(c);
|
|
3018
3137
|
if (!next) break;
|
|
@@ -3022,7 +3141,7 @@ var Builder = class {
|
|
|
3022
3141
|
}
|
|
3023
3142
|
/** Walk any combinator. `isRuleRoot` suppresses treating it as a self-reference. */
|
|
3024
3143
|
walk(c, isRuleRoot = false) {
|
|
3025
|
-
const rn =
|
|
3144
|
+
const rn = ruleNameOf2(c);
|
|
3026
3145
|
if (rn !== void 0 && !isRuleRoot) {
|
|
3027
3146
|
if (!this.record[rn]) this.record[rn] = c;
|
|
3028
3147
|
this.enqueue(rn, c);
|
|
@@ -3051,6 +3170,14 @@ var Builder = class {
|
|
|
3051
3170
|
const items = def.parsers.map((p) => this.walk(p)).filter(nonEmpty);
|
|
3052
3171
|
return flattenChoice(items);
|
|
3053
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
|
+
}
|
|
3054
3181
|
// `max`/`min` are carried through so a BOUNDED repeat does not render as an
|
|
3055
3182
|
// unbounded one — the spec is generated to be read as the truth about what
|
|
3056
3183
|
// parses, and `many(x, { min: 3, max: 8 })` is not `x+`.
|
|
@@ -3139,7 +3266,7 @@ function flattenChoice(items) {
|
|
|
3139
3266
|
}
|
|
3140
3267
|
function toRecord(grammar) {
|
|
3141
3268
|
if (isCombinator(grammar)) {
|
|
3142
|
-
const name =
|
|
3269
|
+
const name = ruleNameOf2(grammar) ?? "start";
|
|
3143
3270
|
return { [name]: grammar };
|
|
3144
3271
|
}
|
|
3145
3272
|
const recovered = recoverComposedRules(grammar);
|