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.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;
|
|
@@ -350,6 +359,16 @@ function tagRule(r, key) {
|
|
|
350
359
|
r._ruleName = key;
|
|
351
360
|
if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
|
|
352
361
|
}
|
|
362
|
+
function isDefinableRef(v) {
|
|
363
|
+
return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
|
|
364
|
+
}
|
|
365
|
+
function ruleNameOf(r) {
|
|
366
|
+
return r._ruleName;
|
|
367
|
+
}
|
|
368
|
+
function isNamedRuleRefForAnotherRule(r, key) {
|
|
369
|
+
const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
|
|
370
|
+
return name !== void 0 && name !== key;
|
|
371
|
+
}
|
|
353
372
|
function rules(a, b) {
|
|
354
373
|
const factory = typeof a === "function" ? a : b;
|
|
355
374
|
const options = typeof a === "function" ? b : a;
|
|
@@ -370,11 +389,21 @@ function rules(a, b) {
|
|
|
370
389
|
for (const key of Object.keys(definitions)) {
|
|
371
390
|
const placeholder = cache[key];
|
|
372
391
|
const parser2 = definitions[key];
|
|
373
|
-
if (placeholder
|
|
374
|
-
|
|
392
|
+
if (placeholder === parser2) {
|
|
393
|
+
throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
|
|
394
|
+
}
|
|
395
|
+
if (isDefinableRef(placeholder)) {
|
|
396
|
+
if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
|
|
375
397
|
placeholder.define(parser2);
|
|
376
398
|
placeholder._meta.firstSet = parser2._meta.firstSet;
|
|
377
399
|
placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
|
|
400
|
+
} else if (isNamedRuleRefForAnotherRule(parser2, key)) {
|
|
401
|
+
const alias = ref();
|
|
402
|
+
tagRule(alias, key);
|
|
403
|
+
alias.define(parser2);
|
|
404
|
+
alias._meta.firstSet = parser2._meta.firstSet;
|
|
405
|
+
alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
|
|
406
|
+
cache[key] = alias;
|
|
378
407
|
} else {
|
|
379
408
|
tagRule(parser2, key);
|
|
380
409
|
cache[key] = parser2;
|
|
@@ -669,6 +698,8 @@ function derive(c, seen) {
|
|
|
669
698
|
return [def.label];
|
|
670
699
|
case "choice":
|
|
671
700
|
return def.parsers.flatMap(deriveExpected2);
|
|
701
|
+
case "dispatch":
|
|
702
|
+
return deriveExpected2(def.selector);
|
|
672
703
|
// Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
|
|
673
704
|
// `optional(…)`/`many(…)` can match nothing, so the term after it is equally
|
|
674
705
|
// able to fail first — deriving from term 0 alone named a token the parse
|
|
@@ -807,6 +838,7 @@ function choice(...args) {
|
|
|
807
838
|
const result = parsers[idx].parse(input, pos, ctx);
|
|
808
839
|
if (result.ok) return result;
|
|
809
840
|
expected.push(...result.expected);
|
|
841
|
+
if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
|
|
810
842
|
return { ok: false, expected, span: { start: pos, end: pos } };
|
|
811
843
|
}
|
|
812
844
|
return {
|
|
@@ -847,6 +879,7 @@ function choice(...args) {
|
|
|
847
879
|
rollbackCstCapture(ctx, mark);
|
|
848
880
|
if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
|
|
849
881
|
expected.push(...result.expected);
|
|
882
|
+
if (result.committed) return { ok: false, expected, span: result.span, committed: true };
|
|
850
883
|
continue;
|
|
851
884
|
}
|
|
852
885
|
const checks = autoNot[i];
|
|
@@ -1797,6 +1830,7 @@ function repItem(combinator, input, cur, ctx, guardable) {
|
|
|
1797
1830
|
const result = combinator.parse(input, pos, ctx);
|
|
1798
1831
|
if (!result.ok) {
|
|
1799
1832
|
rollbackTrivia(ctx, mark);
|
|
1833
|
+
if (result.committed) return { fail: result, failPos: pos };
|
|
1800
1834
|
return { fail: result, failPos: pos };
|
|
1801
1835
|
}
|
|
1802
1836
|
if (result.span.end === pos) {
|
|
@@ -1840,6 +1874,7 @@ function many(combinator, opts = {}) {
|
|
|
1840
1874
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1841
1875
|
if (item === "stop") break;
|
|
1842
1876
|
if ("fail" in item) {
|
|
1877
|
+
if (!item.fail.ok && item.fail.committed) return item.fail;
|
|
1843
1878
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
1844
1879
|
if (sync === void 0) break;
|
|
1845
1880
|
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
@@ -1884,6 +1919,7 @@ function atLeast(combinator, min, max) {
|
|
|
1884
1919
|
while (count < min) {
|
|
1885
1920
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1886
1921
|
if (item === "stop" || "fail" in item) {
|
|
1922
|
+
if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
|
|
1887
1923
|
expected ??= deriveExpected(combinator);
|
|
1888
1924
|
return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
|
|
1889
1925
|
}
|
|
@@ -1896,6 +1932,7 @@ function atLeast(combinator, min, max) {
|
|
|
1896
1932
|
const item = repItem(combinator, input, cur, ctx, guardable);
|
|
1897
1933
|
if (item === "stop") break;
|
|
1898
1934
|
if ("fail" in item) {
|
|
1935
|
+
if (!item.fail.ok && item.fail.committed) return item.fail;
|
|
1899
1936
|
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
1900
1937
|
if (sync === void 0) break;
|
|
1901
1938
|
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
@@ -1934,6 +1971,7 @@ function optional(combinator) {
|
|
|
1934
1971
|
const result = combinator.parse(input, pos, ctx);
|
|
1935
1972
|
if (result.ok) return result;
|
|
1936
1973
|
rollbackTrivia(ctx, mark);
|
|
1974
|
+
if (result.committed) return result;
|
|
1937
1975
|
return { ok: true, value: null, span: { start: pos, end: pos } };
|
|
1938
1976
|
}
|
|
1939
1977
|
};
|
|
@@ -1970,6 +2008,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
1970
2008
|
values.push(first.value);
|
|
1971
2009
|
cur = first.span.end;
|
|
1972
2010
|
} else {
|
|
2011
|
+
if (first.committed) return first;
|
|
1973
2012
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
1974
2013
|
if (term === void 0 || matchesAt(term, input, pos, ctx)) {
|
|
1975
2014
|
if (min >= 1) return failAt2(pos);
|
|
@@ -1997,6 +2036,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
1997
2036
|
const sep = separator.parse(input, sepPos, ctx);
|
|
1998
2037
|
if (!sep.ok) {
|
|
1999
2038
|
rollbackTrivia(ctx, loopMark);
|
|
2039
|
+
if (sep.committed) return sep;
|
|
2000
2040
|
break;
|
|
2001
2041
|
}
|
|
2002
2042
|
const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
|
|
@@ -2012,6 +2052,7 @@ function sepBy(combinator, separator, opts = {}) {
|
|
|
2012
2052
|
}
|
|
2013
2053
|
const next = combinator.parse(input, nextPos, ctx);
|
|
2014
2054
|
if (!next.ok) {
|
|
2055
|
+
if (next.committed) return next;
|
|
2015
2056
|
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
2016
2057
|
if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
|
|
2017
2058
|
expected ??= deriveExpected(combinator);
|
|
@@ -2245,6 +2286,8 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2245
2286
|
case "sequence":
|
|
2246
2287
|
case "choice":
|
|
2247
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);
|
|
2248
2291
|
case "sepBy":
|
|
2249
2292
|
return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
|
|
2250
2293
|
case "skip":
|
|
@@ -2359,6 +2402,75 @@ function keywords(words, opts = {}) {
|
|
|
2359
2402
|
};
|
|
2360
2403
|
}
|
|
2361
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
|
+
|
|
2362
2474
|
// src/combinators/attempt.ts
|
|
2363
2475
|
function attempt(parser2) {
|
|
2364
2476
|
const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
|
|
@@ -2381,6 +2493,7 @@ function attempt(parser2) {
|
|
|
2381
2493
|
if (result.ok) return result;
|
|
2382
2494
|
rollbackCstCapture(ctx, mark);
|
|
2383
2495
|
if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
|
|
2496
|
+
if (result.committed) return result;
|
|
2384
2497
|
return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
|
|
2385
2498
|
}
|
|
2386
2499
|
};
|
|
@@ -2790,6 +2903,9 @@ function evalRuleMapIR(ir) {
|
|
|
2790
2903
|
"keywords",
|
|
2791
2904
|
"sequence",
|
|
2792
2905
|
"choice",
|
|
2906
|
+
"dispatch",
|
|
2907
|
+
"when",
|
|
2908
|
+
"otherwise",
|
|
2793
2909
|
"attempt",
|
|
2794
2910
|
"many",
|
|
2795
2911
|
"oneOrMore",
|
|
@@ -2823,6 +2939,9 @@ function evalRuleMapIR(ir) {
|
|
|
2823
2939
|
keywords,
|
|
2824
2940
|
sequence,
|
|
2825
2941
|
choice,
|
|
2942
|
+
dispatch,
|
|
2943
|
+
when,
|
|
2944
|
+
otherwise,
|
|
2826
2945
|
attempt,
|
|
2827
2946
|
many,
|
|
2828
2947
|
oneOrMore,
|
|
@@ -2913,7 +3032,7 @@ function recoverComposedRules(grammar) {
|
|
|
2913
3032
|
}
|
|
2914
3033
|
|
|
2915
3034
|
// src/spec/model.ts
|
|
2916
|
-
function
|
|
3035
|
+
function ruleNameOf2(c) {
|
|
2917
3036
|
return c._ruleName;
|
|
2918
3037
|
}
|
|
2919
3038
|
function resolveLazy(c) {
|
|
@@ -2978,7 +3097,7 @@ var Builder = class {
|
|
|
2978
3097
|
if (pinned !== void 0) return { kind: "terminal", text: pinned, literal: false };
|
|
2979
3098
|
let c = comb;
|
|
2980
3099
|
const guard = /* @__PURE__ */ new Set();
|
|
2981
|
-
while (c._def.tag === "lazy" &&
|
|
3100
|
+
while (c._def.tag === "lazy" && ruleNameOf2(c) === name && !guard.has(c)) {
|
|
2982
3101
|
guard.add(c);
|
|
2983
3102
|
const next = resolveLazy(c);
|
|
2984
3103
|
if (!next) break;
|
|
@@ -2988,7 +3107,7 @@ var Builder = class {
|
|
|
2988
3107
|
}
|
|
2989
3108
|
/** Walk any combinator. `isRuleRoot` suppresses treating it as a self-reference. */
|
|
2990
3109
|
walk(c, isRuleRoot = false) {
|
|
2991
|
-
const rn =
|
|
3110
|
+
const rn = ruleNameOf2(c);
|
|
2992
3111
|
if (rn !== void 0 && !isRuleRoot) {
|
|
2993
3112
|
if (!this.record[rn]) this.record[rn] = c;
|
|
2994
3113
|
this.enqueue(rn, c);
|
|
@@ -3017,6 +3136,14 @@ var Builder = class {
|
|
|
3017
3136
|
const items = def.parsers.map((p) => this.walk(p)).filter(nonEmpty);
|
|
3018
3137
|
return flattenChoice(items);
|
|
3019
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
|
+
}
|
|
3020
3147
|
// `max`/`min` are carried through so a BOUNDED repeat does not render as an
|
|
3021
3148
|
// unbounded one — the spec is generated to be read as the truth about what
|
|
3022
3149
|
// parses, and `many(x, { min: 3, max: 8 })` is not `x+`.
|
|
@@ -3105,7 +3232,7 @@ function flattenChoice(items) {
|
|
|
3105
3232
|
}
|
|
3106
3233
|
function toRecord(grammar) {
|
|
3107
3234
|
if (isCombinator(grammar)) {
|
|
3108
|
-
const name =
|
|
3235
|
+
const name = ruleNameOf2(grammar) ?? "start";
|
|
3109
3236
|
return { [name]: grammar };
|
|
3110
3237
|
}
|
|
3111
3238
|
const recovered = recoverComposedRules(grammar);
|