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.
Files changed (41) 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/keywords.d.ts +9 -8
  10. package/dist/combinators/keywords.d.ts.map +1 -1
  11. package/dist/combinators/parser.d.ts.map +1 -1
  12. package/dist/combinators/repeat.d.ts.map +1 -1
  13. package/dist/compiler/codegen.d.ts.map +1 -1
  14. package/dist/compiler/fields.d.ts.map +1 -1
  15. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  16. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  17. package/dist/compiler/value-usage.d.ts.map +1 -1
  18. package/dist/coverage.d.ts.map +1 -1
  19. package/dist/index.cjs +432 -53
  20. package/dist/index.cjs.map +4 -4
  21. package/dist/index.d.ts +4 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +432 -53
  24. package/dist/index.js.map +4 -4
  25. package/dist/language-service/index.cjs.map +2 -2
  26. package/dist/language-service/index.js.map +2 -2
  27. package/dist/plugin/evaluator.d.ts +2 -1
  28. package/dist/plugin/evaluator.d.ts.map +1 -1
  29. package/dist/plugin/index.cjs +517 -67
  30. package/dist/plugin/index.cjs.map +4 -4
  31. package/dist/plugin/index.js +517 -67
  32. package/dist/plugin/index.js.map +4 -4
  33. package/dist/spec/index.cjs +133 -6
  34. package/dist/spec/index.cjs.map +4 -4
  35. package/dist/spec/index.js +133 -6
  36. package/dist/spec/index.js.map +4 -4
  37. package/dist/spec/model.d.ts.map +1 -1
  38. package/dist/types.d.ts +17 -0
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/version.d.ts +1 -1
  41. 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;
@@ -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 !== void 0 && typeof placeholder.define === "function") {
408
- tagRule(parser2, key);
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 ruleNameOf(c) {
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" && ruleNameOf(c) === name && !guard.has(c)) {
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 = ruleNameOf(c);
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 = ruleNameOf(grammar) ?? "start";
3269
+ const name = ruleNameOf2(grammar) ?? "start";
3143
3270
  return { [name]: grammar };
3144
3271
  }
3145
3272
  const recovered = recoverComposedRules(grammar);