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.
Files changed (37) hide show
  1. package/dist/analysis/duplication.d.ts.map +1 -1
  2. package/dist/analysis/gating.d.ts.map +1 -1
  3. package/dist/combinators/attempt.d.ts.map +1 -1
  4. package/dist/combinators/choice.d.ts.map +1 -1
  5. package/dist/combinators/dispatch.d.ts +20 -0
  6. package/dist/combinators/dispatch.d.ts.map +1 -0
  7. package/dist/combinators/expect.d.ts.map +1 -1
  8. package/dist/combinators/first-set.d.ts.map +1 -1
  9. package/dist/combinators/repeat.d.ts.map +1 -1
  10. package/dist/compiler/codegen.d.ts.map +1 -1
  11. package/dist/compiler/fields.d.ts.map +1 -1
  12. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  13. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  14. package/dist/compiler/value-usage.d.ts.map +1 -1
  15. package/dist/coverage.d.ts.map +1 -1
  16. package/dist/index.cjs +383 -31
  17. package/dist/index.cjs.map +4 -4
  18. package/dist/index.d.ts +3 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +383 -31
  21. package/dist/index.js.map +4 -4
  22. package/dist/language-service/index.cjs.map +2 -2
  23. package/dist/language-service/index.js.map +2 -2
  24. package/dist/plugin/evaluator.d.ts.map +1 -1
  25. package/dist/plugin/index.cjs +436 -31
  26. package/dist/plugin/index.cjs.map +4 -4
  27. package/dist/plugin/index.js +436 -31
  28. package/dist/plugin/index.js.map +4 -4
  29. package/dist/spec/index.cjs +107 -0
  30. package/dist/spec/index.cjs.map +4 -4
  31. package/dist/spec/index.js +107 -0
  32. package/dist/spec/index.js.map +4 -4
  33. package/dist/spec/model.d.ts.map +1 -1
  34. package/dist/types.d.ts +17 -0
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/version.d.ts +1 -1
  37. package/package.json +1 -1
@@ -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+`.