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
@@ -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+`.