parseman 0.52.0 → 0.53.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 (62) hide show
  1. package/dist/analysis/diagnostics.js +358 -8
  2. package/dist/analysis/diagnostics.js.map +3 -3
  3. package/dist/cli/index.js +5929 -6172
  4. package/dist/cli/index.js.map +3 -3
  5. package/dist/combinators/parser.d.ts +34 -0
  6. package/dist/combinators/parser.d.ts.map +1 -1
  7. package/dist/combinators/ref.d.ts +0 -13
  8. package/dist/combinators/ref.d.ts.map +1 -1
  9. package/dist/compiler/borrowed-rules.d.ts +30 -0
  10. package/dist/compiler/borrowed-rules.d.ts.map +1 -0
  11. package/dist/compiler/compile-linkable-table.d.ts +1 -1
  12. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  13. package/dist/compiler/ir-serialize.d.ts +10 -4
  14. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  15. package/dist/compiler/linker.d.ts +48 -91
  16. package/dist/compiler/linker.d.ts.map +1 -1
  17. package/dist/coverage.d.ts +1 -1
  18. package/dist/coverage.d.ts.map +1 -1
  19. package/dist/cst/reflection.d.ts.map +1 -1
  20. package/dist/index.js +416 -704
  21. package/dist/index.js.map +3 -3
  22. package/dist/language-service/index.js.map +1 -1
  23. package/dist/plugin/evaluator.d.ts +38 -2
  24. package/dist/plugin/evaluator.d.ts.map +1 -1
  25. package/dist/plugin/index.d.ts.map +1 -1
  26. package/dist/plugin/index.js +1073 -523
  27. package/dist/plugin/index.js.map +3 -3
  28. package/dist/plugin/ir-eval.d.ts +21 -0
  29. package/dist/plugin/ir-eval.d.ts.map +1 -0
  30. package/dist/plugin/reducer-resolver.d.ts +6 -0
  31. package/dist/plugin/reducer-resolver.d.ts.map +1 -1
  32. package/dist/spec/index.js +555 -6109
  33. package/dist/spec/index.js.map +3 -3
  34. package/dist/spec/railroad-lib.d.ts +7 -2
  35. package/dist/spec/railroad-lib.d.ts.map +1 -1
  36. package/dist/spec/railroad.d.ts.map +1 -1
  37. package/dist/table/compile-rule-map.d.ts +11 -4
  38. package/dist/table/compile-rule-map.d.ts.map +1 -1
  39. package/dist/table/compile.d.ts +16 -0
  40. package/dist/table/compile.d.ts.map +1 -1
  41. package/dist/table/index.js +235 -87
  42. package/dist/table/index.js.map +2 -2
  43. package/dist/version.d.ts +1 -1
  44. package/package.json +1 -1
  45. package/src/combinators/parser.ts +133 -67
  46. package/src/combinators/ref.ts +10 -1
  47. package/src/compiler/borrowed-rules.ts +88 -0
  48. package/src/compiler/compile-linkable-table.ts +1 -1
  49. package/src/compiler/grammar-coverage-ids.ts +35 -6
  50. package/src/compiler/ir-serialize.ts +12 -118
  51. package/src/compiler/linker.ts +190 -666
  52. package/src/coverage.ts +6 -4
  53. package/src/cst/reflection.ts +6 -1
  54. package/src/plugin/evaluator.ts +113 -13
  55. package/src/plugin/index.ts +588 -110
  56. package/src/plugin/ir-eval.ts +165 -0
  57. package/src/plugin/reducer-resolver.ts +20 -3
  58. package/src/spec/railroad-lib.ts +18 -8
  59. package/src/spec/railroad.ts +3 -4
  60. package/src/table/compile-rule-map.ts +18 -8
  61. package/src/table/compile.ts +61 -0
  62. package/src/version.ts +1 -1
@@ -3252,6 +3252,53 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
3252
3252
  return false;
3253
3253
  }
3254
3254
  }
3255
+ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
3256
+ if (seen.has(p)) return false;
3257
+ seen.add(p);
3258
+ const d = p._def;
3259
+ switch (d.tag) {
3260
+ case "node":
3261
+ return d.build !== void 0 || hasDirectBuildDef(d.parser, seen);
3262
+ case "lazy": {
3263
+ try {
3264
+ return hasDirectBuildDef(d.thunk(), seen);
3265
+ } catch {
3266
+ return false;
3267
+ }
3268
+ }
3269
+ case "grammar":
3270
+ case "trivia":
3271
+ case "token":
3272
+ case "leaf":
3273
+ case "label":
3274
+ case "field":
3275
+ case "optional":
3276
+ case "many":
3277
+ case "oneOrMore":
3278
+ case "attempt":
3279
+ case "not":
3280
+ case "peek":
3281
+ case "withCtx":
3282
+ case "expect":
3283
+ case "transform":
3284
+ return hasDirectBuildDef(d.parser, seen);
3285
+ case "sequence":
3286
+ case "choice":
3287
+ return d.parsers.some((x) => hasDirectBuildDef(x, seen));
3288
+ case "dispatch":
3289
+ return hasDirectBuildDef(d.selector, seen) || d.cases.some((x) => hasDirectBuildDef(x.parser, seen)) || (d.matchers ? d.matchers.some((x) => hasDirectBuildDef(x.parser, seen)) : false) || (d.otherwise ? hasDirectBuildDef(d.otherwise, seen) : false);
3290
+ case "sepBy":
3291
+ return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.separator, seen);
3292
+ case "scanTo":
3293
+ return hasDirectBuildDef(d.sentinel, seen) || d.skip.some((x) => hasDirectBuildDef(x, seen)) || d.recoverAt !== void 0 && hasDirectBuildDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasDirectBuildDef(d.stopAt, seen);
3294
+ case "recover":
3295
+ return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.sentinel, seen);
3296
+ case "routed":
3297
+ return d.fallback ? hasDirectBuildDef(d.fallback, seen) : false;
3298
+ default:
3299
+ return false;
3300
+ }
3301
+ }
3255
3302
  function mayCommitFailure(p, seen = /* @__PURE__ */ new Set(), resolve) {
3256
3303
  if (seen.has(p)) return false;
3257
3304
  seen.add(p);
@@ -10803,6 +10850,7 @@ function withRuleTrivia(trivia2, ctx, run) {
10803
10850
  ctx.triviaKindLabels = savedLabels;
10804
10851
  }
10805
10852
  }
10853
+ var UNDEFINED_REF = new Error("ref<T>() used before .define() was called");
10806
10854
  function ref() {
10807
10855
  let resolved = null;
10808
10856
  let resolvedScalar = null;
@@ -10818,7 +10866,7 @@ function ref() {
10818
10866
  _def: {
10819
10867
  tag: "lazy",
10820
10868
  thunk: () => {
10821
- if (!resolved) throw new Error("ref<T>() used before .define() was called");
10869
+ if (!resolved) throw UNDEFINED_REF;
10822
10870
  return resolved;
10823
10871
  }
10824
10872
  },
@@ -14745,7 +14793,8 @@ function children(def, winners) {
14745
14793
  return [];
14746
14794
  }
14747
14795
  const name = resolved._ruleName;
14748
- return name && winners?.[name] ? [winners[name]] : [resolved];
14796
+ const winner = name ? winners?.[name] : void 0;
14797
+ return winner !== void 0 && winner._def !== def ? [winner] : [resolved];
14749
14798
  }
14750
14799
  default:
14751
14800
  return [];
@@ -14770,16 +14819,39 @@ function buildGrammarPlan(entry, winners) {
14770
14819
  }
14771
14820
  }
14772
14821
  }
14822
+ const selfWinnerBody = (parser2) => {
14823
+ if (parser2._def.tag !== "lazy" || winners === void 0) return void 0;
14824
+ const name = winnerNames.get(parser2);
14825
+ if (name === void 0 || winners[name] !== parser2) return void 0;
14826
+ try {
14827
+ return parser2._def.thunk();
14828
+ } catch {
14829
+ return void 0;
14830
+ }
14831
+ };
14832
+ const slotsPerBody = /* @__PURE__ */ new Map();
14833
+ if (winners) {
14834
+ for (const winner of Object.values(winners)) {
14835
+ const body = selfWinnerBody(winner);
14836
+ if (body !== void 0) slotsPerBody.set(body, (slotsPerBody.get(body) ?? 0) + 1);
14837
+ }
14838
+ }
14839
+ const sharedBody = (parser2) => (slotsPerBody.get(parser2) ?? 0) > 1;
14840
+ const ownRule = (parser2, rule2) => {
14841
+ const id = `rule:${rule2}`;
14842
+ definitions.set(id, { id, kind: "rule" });
14843
+ rules.set(parser2, id);
14844
+ };
14773
14845
  const seen = /* @__PURE__ */ new Set();
14774
14846
  const visit = (parser2, path) => {
14775
14847
  if (seen.has(parser2)) return;
14776
14848
  seen.add(parser2);
14777
14849
  const rule2 = winnerNames.get(parser2) ?? parser2._ruleName;
14778
- if (rule2 && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
14779
- const id = `rule:${rule2}`;
14780
- definitions.set(id, { id, kind: "rule" });
14781
- rules.set(parser2, id);
14850
+ const body = selfWinnerBody(parser2);
14851
+ if (rule2 && body === void 0 && !sharedBody(parser2) && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
14852
+ ownRule(parser2, rule2);
14782
14853
  }
14854
+ if (rule2 && body !== void 0 && sharedBody(body)) ownRule(parser2, rule2);
14783
14855
  if (parser2._def.tag === "choice") {
14784
14856
  const ids = parser2._def.parsers.map((_, index) => `choice:${path}/arm:${index}`);
14785
14857
  choices.set(parser2, ids);
@@ -14800,7 +14872,13 @@ function buildGrammarPlan(entry, winners) {
14800
14872
  };
14801
14873
  const roots = Array.isArray(entry) ? entry : [entry];
14802
14874
  for (const root of roots) {
14803
- visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
14875
+ const name = winnerNames.get(root) ?? root._ruleName ?? "entry";
14876
+ const body = selfWinnerBody(root);
14877
+ if (body !== void 0 && sharedBody(body) && !seen.has(root)) {
14878
+ seen.add(root);
14879
+ ownRule(root, name);
14880
+ }
14881
+ visit(body ?? root, name);
14804
14882
  }
14805
14883
  return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules };
14806
14884
  }
@@ -16102,6 +16180,238 @@ ${lines.join("\n")}`);
16102
16180
  function runDuplicationDiagnostic(combinator, opt) {
16103
16181
  return reportDuplication(opt, (o) => analyzeDuplication(combinator, o));
16104
16182
  }
16183
+ function runDuplicationDiagnosticRules(ruleMap, opt) {
16184
+ return reportDuplication(opt, (o) => analyzeDuplicationRules(ruleMap, o));
16185
+ }
16186
+
16187
+ // src/cst/reflection-symbols.ts
16188
+ var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
16189
+
16190
+ // src/cst/reflection.ts
16191
+ function childrenOf2(def) {
16192
+ switch (def.tag) {
16193
+ case "sequence":
16194
+ case "choice":
16195
+ return def.parsers;
16196
+ case "dispatch":
16197
+ return [
16198
+ def.selector,
16199
+ ...def.cases.map((c) => c.parser),
16200
+ ...def.matchers ? def.matchers.map((c) => c.parser) : [],
16201
+ ...def.otherwise ? [def.otherwise] : []
16202
+ ];
16203
+ case "many":
16204
+ case "oneOrMore":
16205
+ case "optional":
16206
+ case "attempt":
16207
+ case "transform":
16208
+ case "trivia":
16209
+ case "token":
16210
+ case "leaf":
16211
+ case "label":
16212
+ case "field":
16213
+ case "not":
16214
+ case "peek":
16215
+ case "node":
16216
+ case "withCtx":
16217
+ case "expect":
16218
+ return [def.parser];
16219
+ case "recover":
16220
+ return [def.parser, def.sentinel];
16221
+ case "grammar":
16222
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
16223
+ case "sepBy":
16224
+ return [def.parser, def.separator];
16225
+ case "scanTo":
16226
+ return [
16227
+ def.sentinel,
16228
+ ...def.skip,
16229
+ ...def.recoverAt === void 0 ? [] : [def.recoverAt],
16230
+ ...def.stopAt === void 0 ? [] : [def.stopAt]
16231
+ ];
16232
+ case "routed":
16233
+ return def.fallback ? [def.fallback] : [];
16234
+ case "literal":
16235
+ case "regex":
16236
+ case "keywords":
16237
+ case "guard":
16238
+ case "adjacency":
16239
+ case "lazy":
16240
+ case "unknown":
16241
+ return [];
16242
+ }
16243
+ }
16244
+ function addNode(out, def) {
16245
+ if (def.type === void 0) return;
16246
+ const existing = out.get(def.type);
16247
+ if (existing === void 0) {
16248
+ out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
16249
+ return;
16250
+ }
16251
+ if (def.tags === void 0) return;
16252
+ for (const tag of def.tags) {
16253
+ if (!existing.includes(tag)) existing.push(tag);
16254
+ }
16255
+ }
16256
+ function collectFromCombinator(parser2, out, seen, followLazy = true) {
16257
+ if (seen.has(parser2)) return;
16258
+ seen.add(parser2);
16259
+ const def = parser2._def;
16260
+ if (def.tag === "lazy") {
16261
+ if (followLazy) {
16262
+ try {
16263
+ collectFromCombinator(def.thunk(), out, seen, followLazy);
16264
+ } catch {
16265
+ }
16266
+ }
16267
+ return;
16268
+ }
16269
+ if (def.tag === "node") addNode(out, def);
16270
+ for (const child of childrenOf2(def)) collectFromCombinator(child, out, seen, followLazy);
16271
+ }
16272
+ function collectGrammarReflection(ruleMap, opts) {
16273
+ const byType = /* @__PURE__ */ new Map();
16274
+ const followLazy = opts?.followLazy !== false;
16275
+ const seen = /* @__PURE__ */ new Set();
16276
+ for (const [, rule2] of ruleMap) {
16277
+ if (!followLazy && rule2._def.tag === "lazy") {
16278
+ try {
16279
+ const resolved = rule2._def.thunk();
16280
+ if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
16281
+ } catch {
16282
+ }
16283
+ continue;
16284
+ }
16285
+ collectFromCombinator(rule2, byType, seen, followLazy);
16286
+ }
16287
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
16288
+ }
16289
+
16290
+ // src/table/compile-rule-map.ts
16291
+ function resolveHostMode(ruleMap, explicit) {
16292
+ return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
16293
+ }
16294
+ function resolveTrackLines(ruleMap, explicit) {
16295
+ return explicit === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
16296
+ }
16297
+ function withAmbient(ruleMap, opts) {
16298
+ if (opts.trivia === void 0 && opts.scanSkip === void 0) return ruleMap;
16299
+ const wrappers = /* @__PURE__ */ new Map();
16300
+ return ruleMap.map(([name, rule2]) => {
16301
+ if (rule2._meta.isTrivia) return [name, rule2];
16302
+ const addTrivia = opts.trivia !== void 0 && rule2._meta.grammarTrivia === void 0;
16303
+ const addScanSkip = opts.scanSkip !== void 0 && rule2._meta.grammarScanSkip === void 0;
16304
+ if (!addTrivia && !addScanSkip) return [name, rule2];
16305
+ let wrapped = wrappers.get(rule2);
16306
+ if (wrapped === void 0) {
16307
+ wrapped = {
16308
+ ...rule2,
16309
+ _meta: {
16310
+ ...rule2._meta,
16311
+ ...addTrivia ? { grammarTrivia: opts.trivia } : {},
16312
+ ...addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}
16313
+ }
16314
+ };
16315
+ wrappers.set(rule2, wrapped);
16316
+ }
16317
+ return [name, wrapped];
16318
+ });
16319
+ }
16320
+ function compileRuleMapRunnable(ruleMap, opts = {}) {
16321
+ const encoded = encodeForRun(ruleMap, opts);
16322
+ if (encoded === null) return null;
16323
+ const { prog, hostMode, plan } = encoded;
16324
+ const artifact = closureArtifact(prog);
16325
+ const reflection = collectGrammarReflection(ruleMap);
16326
+ return {
16327
+ keys: ruleMap.map(([key]) => key),
16328
+ hostMode,
16329
+ hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule2]) => hasDirectBuildDef(rule2)),
16330
+ reflection,
16331
+ ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
16332
+ // Reflection rides on the map's metadata prototype from birth. Only `compile()`
16333
+ // asks to specialise; every other caller runs the closure artifact.
16334
+ rules: tableRules(opts.specialise === true ? prog : artifact, { [GRAMMAR_REFLECTION]: reflection }),
16335
+ prog: artifact
16336
+ };
16337
+ }
16338
+ function encodeForRun(ruleMap, opts) {
16339
+ runDuplicationDiagnosticRules(ruleMap, opts.duplication);
16340
+ const encodeMap = withAmbient(ruleMap, opts);
16341
+ const plan = opts.coverage === true ? buildGrammarPlan(
16342
+ encodeMap.map(([, rule2]) => rule2),
16343
+ Object.fromEntries(encodeMap.filter(([, rule2]) => rule2._def.tag !== "lazy"))
16344
+ ) : void 0;
16345
+ const hostMode = resolveHostMode(ruleMap, opts.hostMode);
16346
+ const settings = {
16347
+ hostMode,
16348
+ ...resolveTrackLines(ruleMap, opts.trackLines) ? { trackLines: true } : {},
16349
+ ...opts.recovery === true ? { recovery: true } : {},
16350
+ ...plan === void 0 ? {} : { coverage: plan }
16351
+ };
16352
+ try {
16353
+ const { prog, fnSrcs } = encodeTableProgram(Object.fromEntries(encodeMap), settings);
16354
+ return { prog, fnSrcs, hostMode, plan };
16355
+ } catch (e) {
16356
+ opts.refusals?.push(e instanceof Error ? e.message : String(e));
16357
+ return null;
16358
+ }
16359
+ }
16360
+
16361
+ // src/compiler/borrowed-rules.ts
16362
+ var nameOf = (c) => c._ruleName;
16363
+ function deref(c) {
16364
+ const seen = /* @__PURE__ */ new Set();
16365
+ let current = c;
16366
+ while (current._def.tag === "lazy" && !seen.has(current)) {
16367
+ seen.add(current);
16368
+ try {
16369
+ current = current._def.thunk();
16370
+ } catch {
16371
+ return void 0;
16372
+ }
16373
+ }
16374
+ return current;
16375
+ }
16376
+ function borrowedRuleReference(map) {
16377
+ const seen = /* @__PURE__ */ new Set();
16378
+ for (const [rule2, self] of Object.entries(map)) {
16379
+ let found;
16380
+ const visit = (c) => {
16381
+ if (found !== void 0) return;
16382
+ if (c._def.tag === "lazy") {
16383
+ if (!winnerWrapsReference(self, c)) {
16384
+ const name = nameOf(c);
16385
+ const winner = name !== void 0 && Object.hasOwn(map, name) ? map[name] : void 0;
16386
+ if (winner !== void 0) {
16387
+ if (winnerWrapsReference(winner, c) || deref(c) === deref(winner)) return;
16388
+ found = name;
16389
+ return;
16390
+ }
16391
+ }
16392
+ if (seen.has(c)) return;
16393
+ seen.add(c);
16394
+ let body;
16395
+ try {
16396
+ body = c._def.thunk();
16397
+ } catch {
16398
+ return;
16399
+ }
16400
+ visit(body);
16401
+ return;
16402
+ }
16403
+ if (seen.has(c)) return;
16404
+ seen.add(c);
16405
+ for (const child of childrenOf(c._def)) visit(child);
16406
+ };
16407
+ visit(self);
16408
+ if (found !== void 0) return { rule: rule2, reference: found };
16409
+ }
16410
+ return void 0;
16411
+ }
16412
+ function borrowedRuleMessage(api, hit) {
16413
+ return `${api}: rule "${hit.rule}" is a rule object borrowed from another grammar, and its graph references "${hit.reference}", which this grammar also defines. The interpreter would keep the other grammar's "${hit.reference}" while a compiled table binds this grammar's, so the two would accept different input. Compose the grammar that defines "${hit.rule}" instead of borrowing its rule object, so its references resolve to this grammar's rules.`;
16414
+ }
16105
16415
 
16106
16416
  // src/table/compile.ts
16107
16417
  var ENTRY = "Entry";
@@ -16112,7 +16422,47 @@ function ownSource(fn) {
16112
16422
  const name = fn.name;
16113
16423
  return typeof name === "string" && name.length > 0 && globalThis[name] === fn ? name : null;
16114
16424
  }
16115
- function compile(combinator, mapFnSources, opts = {}) {
16425
+ function compile(target, second, third) {
16426
+ if (!isCombinator(target)) return compileMap(target, second);
16427
+ return compileRoot(target, second, third);
16428
+ }
16429
+ function isCombinator(x) {
16430
+ return typeof x === "object" && x !== null && typeof x.parse === "function" && "_def" in x;
16431
+ }
16432
+ function compileMap(grammar, opts = {}) {
16433
+ const entries = [];
16434
+ for (const [key, value] of Object.entries(grammar)) {
16435
+ if (!isCombinator(value)) throw new TypeError(`compile: grammar entry "${key}" is not a parser`);
16436
+ if (value._def.tag === "lazy") {
16437
+ try {
16438
+ value._def.thunk();
16439
+ } catch {
16440
+ continue;
16441
+ }
16442
+ }
16443
+ entries.push([key, value]);
16444
+ }
16445
+ const borrowed = borrowedRuleReference(Object.fromEntries(entries));
16446
+ if (borrowed !== void 0) throw new Error(borrowedRuleMessage("compile", borrowed));
16447
+ const drain = beginCompileDegradationDrain();
16448
+ let done = false;
16449
+ try {
16450
+ const refusals = [];
16451
+ const compiled = compileRuleMapRunnable(entries, {
16452
+ ...opts.hostMode ? { hostMode: opts.hostMode } : {},
16453
+ refusals,
16454
+ specialise: true
16455
+ });
16456
+ if (compiled === null) {
16457
+ throw new Error(`compile: this grammar could not be encoded to a table${refusals.length ? ` \u2014 ${refusals.join("; ")}` : ""}`);
16458
+ }
16459
+ done = true;
16460
+ return compiled.rules;
16461
+ } finally {
16462
+ drain(done);
16463
+ }
16464
+ }
16465
+ function compileRoot(combinator, mapFnSources, opts = {}) {
16116
16466
  runDuplicationDiagnostic(combinator, opts.duplication);
16117
16467
  const drain = beginCompileDegradationDrain();
16118
16468
  let compiled = false;