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
@@ -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 !== void 0 && typeof placeholder.define === "function") {
374
- tagRule(parser2, key);
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 ruleNameOf(c) {
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" && ruleNameOf(c) === name && !guard.has(c)) {
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 = ruleNameOf(c);
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 = ruleNameOf(grammar) ?? "start";
3235
+ const name = ruleNameOf2(grammar) ?? "start";
3109
3236
  return { [name]: grammar };
3110
3237
  }
3111
3238
  const recovered = recoverComposedRules(grammar);