parseman 0.35.0 → 0.37.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 (61) hide show
  1. package/README.md +34 -19
  2. package/dist/analysis/duplication.d.ts +421 -0
  3. package/dist/analysis/duplication.d.ts.map +1 -0
  4. package/dist/analysis/gating.d.ts +25 -0
  5. package/dist/analysis/gating.d.ts.map +1 -1
  6. package/dist/analysis/grammar.d.ts +12 -0
  7. package/dist/analysis/grammar.d.ts.map +1 -0
  8. package/dist/combinators/expect.d.ts +12 -0
  9. package/dist/combinators/expect.d.ts.map +1 -1
  10. package/dist/combinators/parser.d.ts +26 -0
  11. package/dist/combinators/parser.d.ts.map +1 -1
  12. package/dist/compiler/build-arity.d.ts +12 -0
  13. package/dist/compiler/build-arity.d.ts.map +1 -1
  14. package/dist/compiler/codegen.d.ts +43 -1
  15. package/dist/compiler/codegen.d.ts.map +1 -1
  16. package/dist/compiler/linker.d.ts +64 -7
  17. package/dist/compiler/linker.d.ts.map +1 -1
  18. package/dist/cst/host-mode.d.ts +43 -0
  19. package/dist/cst/host-mode.d.ts.map +1 -0
  20. package/dist/functional/doc.d.ts.map +1 -1
  21. package/dist/functional/run.d.ts.map +1 -1
  22. package/dist/index.cjs +1621 -55
  23. package/dist/index.cjs.map +4 -4
  24. package/dist/index.d.ts +6 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +1621 -55
  27. package/dist/index.js.map +4 -4
  28. package/dist/language-service/index.cjs +37 -1
  29. package/dist/language-service/index.cjs.map +3 -3
  30. package/dist/language-service/index.js +37 -1
  31. package/dist/language-service/index.js.map +3 -3
  32. package/dist/oracle/corpus.d.ts +32 -0
  33. package/dist/oracle/corpus.d.ts.map +1 -0
  34. package/dist/oracle/digest.d.ts +21 -0
  35. package/dist/oracle/digest.d.ts.map +1 -0
  36. package/dist/oracle/identity.d.ts +117 -0
  37. package/dist/oracle/identity.d.ts.map +1 -0
  38. package/dist/oracle/index.cjs +423 -0
  39. package/dist/oracle/index.cjs.map +7 -0
  40. package/dist/oracle/index.d.ts +37 -0
  41. package/dist/oracle/index.d.ts.map +1 -0
  42. package/dist/oracle/index.js +400 -0
  43. package/dist/oracle/index.js.map +7 -0
  44. package/dist/plugin/index.cjs +1628 -62
  45. package/dist/plugin/index.cjs.map +4 -4
  46. package/dist/plugin/index.d.ts.map +1 -1
  47. package/dist/plugin/index.js +1628 -62
  48. package/dist/plugin/index.js.map +4 -4
  49. package/dist/run/index.cjs +23 -0
  50. package/dist/run/index.cjs.map +3 -3
  51. package/dist/run/index.js +23 -0
  52. package/dist/run/index.js.map +3 -3
  53. package/dist/spec/index.cjs +2977 -56
  54. package/dist/spec/index.cjs.map +4 -4
  55. package/dist/spec/index.js +2977 -56
  56. package/dist/spec/index.js.map +4 -4
  57. package/dist/spec/model.d.ts.map +1 -1
  58. package/dist/types.d.ts +7 -0
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/version.d.ts +1 -1
  61. package/package.json +10 -3
@@ -136,6 +136,23 @@ function captureError(ctx, error) {
136
136
  }
137
137
  var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError };
138
138
 
139
+ // src/cst/host-mode.ts
140
+ var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
141
+ var FUSED_HOST_ELIDED = /* @__PURE__ */ Symbol.for("parseman.fusedHostElided");
142
+ function assertHostModeCompatible(mode, build, hostBranchElided = true) {
143
+ const isCstHost = build?._parsemanCstOutput === true;
144
+ if (mode === "ast" && isCstHost && hostBranchElided) {
145
+ throw new Error(
146
+ "parseman: this parser was compiled for host mode \"ast\" (the default), so its nodes build through their own `build` callbacks and no positioned-CST branch was emitted. It cannot be driven with a positioned-CST host (cstBuildHost / a language-service host). Compile a second artifact from the SAME grammar with `compile(grammar, { hostMode: 'cst' })` \u2014 or, under the macro, a second `rules({ hostMode: 'cst' }, factory)` call site \u2014 and drive that one instead."
147
+ );
148
+ }
149
+ if (mode === "cst" && !isCstHost) {
150
+ throw new Error(
151
+ 'parseman: this parser was compiled for host mode "cst", so every node builds through a positioned-CST `ctx.build` host. Pass one (e.g. `{ build: cstBuildHost }`), or use an artifact compiled with the default `hostMode: "ast"` to get the grammar\'s own AST.'
152
+ );
153
+ }
154
+ }
155
+
139
156
  // src/functional/run.ts
140
157
  var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
141
158
  function runOnce(entry, input, options, phase, profileState) {
@@ -150,6 +167,12 @@ function runOnce(entry, input, options, phase, profileState) {
150
167
  const skipGlobalSinks = phase === "recognizer" || phase === "capture";
151
168
  const grammarTrivia = typeof entry !== "function" ? entry._meta.grammarTrivia : void 0;
152
169
  const grammarScanSkip = typeof entry !== "function" ? entry._meta.grammarScanSkip : void 0;
170
+ const stamped = entry;
171
+ assertHostModeCompatible(
172
+ typeof entry === "function" ? stamped[FUSED_HOST_MODE] ?? "ast" : entry._meta.grammarHostMode ?? "ast",
173
+ options.build,
174
+ typeof entry === "function" && stamped[FUSED_HOST_ELIDED] === true
175
+ );
153
176
  const ctx = {
154
177
  trackLines: false,
155
178
  ...skipGlobalSinks ? { _pmProfile: profile } : { _triviaLog: triviaLog, _errors: errors, ...profile === void 0 ? {} : { _pmProfile: profile } },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/run/index.ts", "../../src/cst/capture-buffer.ts", "../../src/recovery/scan.ts", "../../src/functional/run.ts"],
4
- "sourcesContent": ["/**\n * `parseman/run` \u2014 the minimal entry for EXECUTING an already-compiled grammar.\n *\n * The macro removes the combinators, the compiler and the codegen from your\n * bundle, but not the driver: running a compiled grammar is still a `run()` call,\n * so a package that SHIPS a parser keeps a runtime import of parseman. Importing\n * that driver from the main entry drags the whole library along \u2014 the combinator\n * set, `compile()`, the first-set analysis, the CST builders \u2014 none of which a\n * compiled parser touches.\n *\n * This entry exists so it doesn't have to. `run()`'s complete module closure is\n * three files (this, `recovery/scan.ts`, `cst/capture-buffer.ts`) \u2014 a fraction of\n * the main entry. `test/unit/run-entry-closure.test.ts` asserts that stays true,\n * so a future import into `functional/run.ts` that widens the closure fails the\n * suite rather than quietly re-inflating everyone's bundle.\n *\n * Use the main entry when you BUILD grammars; use this one when you only run\n * output the macro (or `compile()`) already produced.\n *\n * import { run } from 'parseman/run'\n * import { myGrammar } from './grammar.js' // macro-compiled\n *\n * const result = run(myGrammar, source, { trivia })\n */\nexport { run } from '../functional/run.ts'\nexport type { Runnable, RunOptions, RunResult } from '../functional/run.ts'\nexport type { ParseContext, ParseError, ParseResult } from '../types.ts'\n", "import type { ParseContext } from '../types.ts'\n\n/** Lazy per-node capture state \u2014 arrays materialized on first push. */\nexport type CstCaptureBuf = {\n single?: unknown | undefined\n ch?: unknown[] | undefined\n rawSingle?: unknown | undefined\n raw?: unknown[] | undefined\n tl?: number[] | undefined\n}\n\nconst EMPTY: unknown[] = []\nconst EMPTY_TL: number[] = []\n\nexport function cstCaptureActive(ctx: ParseContext): boolean {\n return ctx._cstBuf !== undefined || ctx._cstLeaves !== undefined\n}\n\nexport type CstNodeCaptureSaved = {\n ch: ParseContext['_cstChildren']\n lv: ParseContext['_cstLeaves']\n raw: ParseContext['_cstRawChildren']\n tl: ParseContext['_cstTriviaLog']\n cap: boolean | undefined\n buf: CstCaptureBuf | undefined\n}\n\nexport function beginCstNodeCapture(ctx: ParseContext): CstNodeCaptureSaved {\n const saved: CstNodeCaptureSaved = {\n ch: ctx._cstChildren,\n lv: ctx._cstLeaves,\n raw: ctx._cstRawChildren,\n tl: ctx._cstTriviaLog,\n cap: ctx.captureTrivia,\n buf: ctx._cstBuf,\n }\n ctx._cstBuf = {}\n ctx._cstChildren = undefined\n ctx._cstLeaves = undefined\n ctx._cstRawChildren = undefined\n ctx._cstTriviaLog = undefined\n ctx.captureTrivia = true\n return saved\n}\n\nexport function finishCstBuf(buf: CstCaptureBuf | undefined): {\n children: unknown[]\n rawChildren: unknown[]\n triviaLog: number[]\n} {\n if (!buf) return { children: EMPTY, rawChildren: EMPTY, triviaLog: EMPTY_TL }\n const children = buf.ch ?? (buf.single !== undefined ? [buf.single] : EMPTY)\n const rawChildren = buf.raw ?? (buf.rawSingle !== undefined ? [buf.rawSingle] : EMPTY)\n const triviaLog = buf.tl ?? EMPTY_TL\n return { children, rawChildren, triviaLog }\n}\n\nexport function endCstNodeCapture(\n ctx: ParseContext,\n saved: CstNodeCaptureSaved,\n): { children: unknown[]; rawChildren: unknown[]; triviaLog: number[] } {\n const materialized = finishCstBuf(ctx._cstBuf)\n ctx._cstBuf = saved.buf\n ctx._cstChildren = saved.ch\n ctx._cstLeaves = saved.lv\n ctx._cstRawChildren = saved.raw\n ctx._cstTriviaLog = saved.tl\n ctx.captureTrivia = saved.cap\n return materialized\n}\n\nexport function pushCstLeaf(ctx: ParseContext, leaf: unknown): void {\n pushCstChild(ctx, leaf, leaf)\n}\n\n/** Record a built sub-node into the active collector (children vs rawChildren may differ). */\nexport function pushCstChild(ctx: ParseContext, built: unknown, rawEntry: unknown): void {\n const b = ctx._cstBuf\n if (b) {\n if (b.ch) b.ch.push(built)\n else if (b.single !== undefined) { b.ch = [b.single, built]; b.single = undefined }\n else b.single = built\n\n if (b.raw) b.raw.push(rawEntry)\n else if (b.rawSingle !== undefined) { b.raw = [b.rawSingle, rawEntry]; b.rawSingle = undefined }\n else b.rawSingle = rawEntry\n return\n }\n if (ctx._cstChildren) ctx._cstChildren.push(built)\n else if (ctx._cstLeaves) ctx._cstLeaves.push(built)\n if (ctx._cstRawChildren) ctx._cstRawChildren.push(rawEntry)\n}\n\nexport function cstRawLen(ctx: ParseContext): number {\n const b = ctx._cstBuf\n if (b) {\n if (b.raw) return b.raw.length\n return b.rawSingle !== undefined ? 1 : 0\n }\n return ctx._cstRawChildren?.length ?? 0\n}\n\nexport function cstLeavesLen(ctx: ParseContext): number {\n const b = ctx._cstBuf\n if (b) {\n if (b.ch) return b.ch.length\n return b.single !== undefined ? 1 : 0\n }\n return ctx._cstLeaves?.length ?? 0\n}\n\nexport function cstTlLen(ctx: ParseContext): number {\n const b = ctx._cstBuf\n if (b) return b.tl?.length ?? 0\n return ctx._cstTriviaLog?.length ?? 0\n}\n\nexport type CstRollbackMark = { raw: number; tlog: number; leaves: number; fields: number; errors: number }\n\nexport function saveCstMark(ctx: ParseContext): CstRollbackMark {\n return {\n raw: cstRawLen(ctx),\n tlog: cstTlLen(ctx),\n leaves: cstLeavesLen(ctx),\n fields: ctx._fields?.length ?? 0,\n // Speculative rollback must truncate the flat recovery-error sink in lockstep\n // with the CST leaves: a branch that recovered (pushing a ParseError to\n // ctx._errors AND embedding it as a CST child via captureError) then failed\n // must remove BOTH, or the flat error survives as a GHOST that a tree walk\n // can't see (flat run().errors would disagree with the embedded set).\n errors: ctx._errors?.length ?? 0,\n }\n}\n\nfunction rollbackBufList(\n b: CstCaptureBuf,\n keyMulti: 'ch' | 'raw',\n keySingle: 'single' | 'rawSingle',\n len: number,\n): void {\n const arr = b[keyMulti]\n if (arr) {\n if (len === 0) b[keyMulti] = undefined\n else if (len === 1) { b[keySingle] = arr[0]; b[keyMulti] = undefined }\n // Guarded like every other truncation \u2014 see rollbackCstCapture. This is the\n // buffered node-capture path, so it is reached only by the INTERPRETER (the\n // compiled engine inlines its own capture and never calls here), and it is\n // where the redundant-store rate is highest: over a backtracking fixture\n // this branch ran 32,800 times and 31,198 of those \u2014 95% \u2014 restored a\n // length that had not moved.\n else if (arr.length !== len) arr.length = len\n return\n }\n if (len === 0) b[keySingle] = undefined\n}\n\nexport function rollbackCstCapture(ctx: ParseContext, mark: CstRollbackMark): void {\n // Truncate the flat recovery-error sink alongside the CST, so a rolled-back\n // speculative branch leaves no ghost error (see saveCstMark). Guarded on a\n // present sink + a defined mark (rollbackTrivia forwards the same field).\n //\n // Each truncation is guarded on `length !== mark`, mirroring the compiled\n // engine (codegen.ts `captureRestoreBody`). Assigning `array.length` runs V8's\n // length setter \u2014 a backing-store trim decision \u2014 and costs the same when the\n // value is unchanged, which is the overwhelmingly common case here: a\n // speculative branch that captured nothing still pays for every store. The\n // compare is an in-object load. Both engines must carry the guard or the\n // interpreted path silently keeps the cost the compiled path shed.\n if (ctx._errors && mark.errors !== undefined && ctx._errors.length !== mark.errors) ctx._errors.length = mark.errors\n const b = ctx._cstBuf\n if (b) {\n rollbackBufList(b, 'raw', 'rawSingle', mark.raw)\n rollbackBufList(b, 'ch', 'single', mark.leaves)\n if (b.tl) {\n if (mark.tlog === 0) b.tl = undefined\n else if (b.tl.length !== mark.tlog) b.tl.length = mark.tlog\n }\n if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields\n return\n }\n if (ctx._cstRawChildren && ctx._cstRawChildren.length !== mark.raw) ctx._cstRawChildren.length = mark.raw\n if (ctx._cstTriviaLog && ctx._cstTriviaLog.length !== mark.tlog) ctx._cstTriviaLog.length = mark.tlog\n if (ctx._cstLeaves && ctx._cstLeaves.length !== mark.leaves) ctx._cstLeaves.length = mark.leaves\n if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields\n}\n\nexport function pushCstTriviaEntry(\n ctx: ParseContext,\n start: number,\n end: number,\n kindIndex?: number,\n): void {\n const insertIdx = cstRawLen(ctx)\n const b = ctx._cstBuf\n const withKind = ctx.triviaKindLabels !== undefined && kindIndex !== undefined\n if (b) {\n if (!b.tl) {\n b.tl = withKind ? [start, end, insertIdx, kindIndex] : [start, end, insertIdx]\n } else if (withKind) {\n b.tl.push(start, end, insertIdx, kindIndex)\n } else {\n b.tl.push(start, end, insertIdx)\n }\n return\n }\n if (ctx._cstTriviaLog) {\n if (withKind) ctx._cstTriviaLog.push(start, end, insertIdx, kindIndex)\n else ctx._cstTriviaLog.push(start, end, insertIdx)\n }\n}\n\nexport function pushTriviaLogEntry(\n ctx: ParseContext,\n start: number,\n end: number,\n kindIndex?: number,\n): void {\n if (!ctx._triviaLog) return\n if (ctx.triviaKindLabels !== undefined && kindIndex !== undefined) {\n ctx._triviaLog.push(start, end, kindIndex)\n } else {\n ctx._triviaLog.push(start, end)\n }\n}\n\nexport function cstDeferredTriviaActive(ctx: ParseContext): boolean {\n return ctx._triviaLog !== undefined || ctx._cstBuf !== undefined || ctx._cstTriviaLog !== undefined\n}\n", "import type { Combinator, FirstSet, ParseContext, ParseError, ParseResult, RecoveryHelpers } from '../types.ts'\nimport { cstCaptureActive, pushCstChild } from '../cst/capture-buffer.ts'\n\n/**\n * Recovery MECHANISM \u2014 sync-source-agnostic. These primitives implement \"scan\n * forward to a sync token and emit a ParseError\" and \"is the sync token here?\".\n * They are deliberately independent of WHERE the sync sentinel comes from: the\n * grammar never carries recovery config. Sync points are inferred automatically\n * from grammar structure (see `infer.ts`) and may be overridden externally by the\n * language service via `ctx._listSync`. This is the salvaged core of the old\n * `combinators/recover-scan.ts`.\n */\n\n/**\n * Run `sentinel` at `pos` as PURE lookahead: succeed/fail only, with every\n * side-effecting sink suppressed (CST leaves/children/trivia, the trivia log,\n * the recovery error sink, and the completions probe). A recovery scan probes the\n * sentinel at many positions and must not capture leaves, log trivia, or pollute\n * `completionsAt`'s furthest-failure set as a byproduct of looking ahead.\n */\nfunction probeAt(sentinel: Combinator<unknown>, input: string, pos: number, ctx: ParseContext): boolean {\n const s = {\n _probe: ctx._probe, _errors: ctx._errors,\n _cstLeaves: ctx._cstLeaves, _cstChildren: ctx._cstChildren, _cstRawChildren: ctx._cstRawChildren,\n _cstTriviaLog: ctx._cstTriviaLog, _triviaLog: ctx._triviaLog, _fields: ctx._fields, _cstBuf: ctx._cstBuf,\n _tolerant: ctx._tolerant, _sync: ctx._sync,\n }\n ctx._probe = undefined; ctx._errors = undefined\n ctx._cstLeaves = undefined; ctx._cstChildren = undefined; ctx._cstRawChildren = undefined\n ctx._cstTriviaLog = undefined; ctx._triviaLog = undefined; ctx._fields = undefined; ctx._cstBuf = undefined\n // A sentinel is pure lookahead: it must not itself enter tolerant recovery (nor\n // read a stale outer `_sync`). Otherwise a sentinel that composes many/sepBy would\n // run recovery on every probed position \u2014 wasted work, and it could resync to the\n // enclosing list's sync token. Cleared here and restored in `finally`.\n ctx._tolerant = undefined; ctx._sync = undefined\n try {\n return sentinel.parse(input, pos, ctx).ok\n } finally {\n ctx._probe = s._probe; ctx._errors = s._errors\n ctx._cstLeaves = s._cstLeaves; ctx._cstChildren = s._cstChildren; ctx._cstRawChildren = s._cstRawChildren\n ctx._cstTriviaLog = s._cstTriviaLog; ctx._triviaLog = s._triviaLog; ctx._fields = s._fields; ctx._cstBuf = s._cstBuf\n ctx._tolerant = s._tolerant; ctx._sync = s._sync\n }\n}\n\n/**\n * Combine two recovery sentinels into one that matches when either does \u2014 used by\n * tolerant `sepBy` to resync to its own separator OR the enclosing delimiter. Cold\n * path only; a bare wrapper so we don't pull `choice()` (and its analysis) in.\n */\nexport function orSentinel(\n a: Combinator<unknown>,\n b: Combinator<unknown> | undefined,\n): Combinator<unknown> {\n if (b === undefined) return a\n return {\n _tag: 'orSentinel',\n _meta: { firstSet: { kind: 'any' }, canMatchNewline: false, isTrivia: false },\n _def: { tag: 'unknown' },\n parse(input: string, pos: number, ctx: ParseContext): ParseResult<unknown> {\n const ra = a.parse(input, pos, ctx)\n if (ra.ok) return ra\n return b.parse(input, pos, ctx)\n },\n }\n}\n\n/**\n * A lightweight zero-width sentinel that \"matches\" (succeeds, consuming nothing)\n * when the input code point at `pos` starts the given first set. Sync inference\n * builds one from a list's follow set so a nested list can resync to the enclosing\n * delimiter with no grammar annotation. Returns `null` when the first set is `any`\n * or `empty` (no usable inference).\n */\nexport function firstSetSentinel(fs: FirstSet): Combinator<null> | null {\n if (fs.kind !== 'ranges' || fs.ranges.length === 0) return null\n const ranges = fs.ranges\n return {\n _tag: 'firstSetSentinel',\n _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },\n _def: { tag: 'unknown' },\n parse(input: string, pos: number): ParseResult<null> {\n const code = pos < input.length ? input.codePointAt(pos) : undefined\n if (code !== undefined) {\n for (const r of ranges) {\n if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } }\n }\n }\n return { ok: false, expected: [], span: { start: pos, end: pos } }\n },\n }\n}\n\n/**\n * Cold-path recovery scan shared by tolerant `many()` / `sepBy()`. Scans forward\n * from `from` until `sync` matches (or EOF) \u2014 probing the sentinel as pure\n * lookahead \u2014 emits a {@link ParseError} spanning the skipped range, and pushes it\n * to `ctx._errors`. The sync token is NOT consumed; the caller's loop resumes from\n * `end`. Only ever reached after an element has failed, so entirely off the hot path.\n *\n * The caller owns the loop guard: `sepBy` progress is driven by its separator (a\n * zero-width error for a missing element is fine); `many` checks `matchesAt(sync)`\n * itself and breaks when sitting on the terminator, so a zero-width failure can\n * never spin.\n */\nexport function recoverScan(\n input: string,\n from: number,\n ctx: ParseContext,\n sync: Combinator<unknown>,\n expected: string[],\n): { error: ParseError; end: number } {\n let scanPos = from\n while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++\n const error: ParseError = { _tag: 'parseError', span: { start: from, end: scanPos }, expected }\n ctx._errors?.push(error)\n return { error, end: scanPos }\n}\n\n/**\n * Does `sentinel` match at `pos` (pure lookahead)? Used by the tolerant list loops\n * to detect \"we are sitting on the sync/terminator token\" without consuming it or\n * capturing anything.\n */\nexport function matchesAt(\n sentinel: Combinator<unknown>,\n input: string,\n pos: number,\n ctx: ParseContext,\n): boolean {\n return probeAt(sentinel, input, pos, ctx)\n}\n\n/**\n * Embed a recovered error as a `parseError` CST child at the recovery point, so the\n * error lives IN the tree (rides reused subtrees across incremental edits; a tree\n * walk finds every diagnostic) \u2014 not only in the flat `ctx._errors` side-channel.\n * No-op when CST capture is off (value/AST mode). Both the interpreter list loops\n * and the compiled recovery branches call this (the latter via `_ctx._rec.capture`),\n * so the embedded node is identical on both paths.\n */\nexport function captureError(ctx: ParseContext, error: ParseError): void {\n if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error)\n}\n\n/**\n * The recovery-helper bundle handed to a tolerant parse via `ctx._rec`. A COMPILED\n * grammar can't import these, so the driver (`run`, `parseDoc`) injects them; the\n * interpreter list loops read the same bundle, guaranteeing byte-identical recovery\n * across paths. Shared here so every driver installs the exact same instance.\n */\nexport const REC: RecoveryHelpers = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError }\n", "import type { Combinator, ParseContext, ParseError, ParseResult } from '../types.ts'\nimport { REC } from '../recovery/scan.ts'\n\n/**\n * Run a compiled/interpreted grammar entry against an input and collect the raw\n * outcome a tool needs to shape a parse result \u2014 WITHOUT the consumer having to\n * hand-build a `ctx`, branch on function-vs-combinator, or scan for leftover\n * input itself.\n *\n * A `compile()`/macro grammar hands you a map of bare parse FUNCTIONS; the\n * interpreter hands you COMBINATORS. `run()` invokes either, threads the standard\n * framework ctx (trivia log, recover/expect error sink, the `ctx.build` host,\n * grammar state), and \u2014 given the grammar's trivia parser \u2014 reports where\n * non-trivia input was left unconsumed. The consumer keeps only its own policy:\n * how to shape the tree and how to turn diagnostics into its error type.\n */\n\n/** A compiled rule (macro/`compile()` output) OR an interpreter combinator. */\nexport type Runnable =\n | ((input: string, pos: number, ctx: ParseContext) => ParseResult<unknown>)\n | Combinator<unknown>\n\nexport type RunOptions = {\n /** Optional hooks for a coverage-enabled compiled or macro parser. Ordinary\n * parses omit this completely, so instrumentation has no normal-path cost. */\n instrumentation?: {\n _grammarCoverage?: (id: string) => void\n _grammarTrace?: { write(event: { id: string; phase: 'enter' | 'attempt' | 'selected' | 'success' | 'failure' | 'backtrack' | 'rollback'; offset: number; end?: number }): void }\n }\n /** `ctx.build` host \u2014 makes structural `node()` rules build a CST / AST via the\n * host instead of their own eval builders. Omit for a grammar's own builders. */\n build?: ParseContext['build']\n /** Initial grammar state threaded into `ctx.state`. */\n state?: unknown\n /**\n * The grammar's trivia rule. A root rule consumes trivia BETWEEN terms but not\n * after the last one, so trailing whitespace/comments would otherwise look like\n * unparsed input. Given the trivia rule, `run` skips that tail before computing\n * `unconsumedFrom` \u2014 so only real leftover is reported. Also encodes dialect\n * differences for free: pass the CSS trivia and a trailing `//` counts as\n * leftover; pass the Less trivia (which treats `//` as a line comment) and it\n * doesn't. An UNTERMINATED comment (which the trivia rule won't match) surfaces\n * at its start. Omit to require the parse to reach the exact end itself.\n */\n trivia?: Runnable\n /**\n * Restrict PER-NODE CST trivia capture (the `triviaLog` a node's builder sees)\n * to these trivia kinds \u2014 a bitmask over the grammar's `triviaKindLabels`\n * indices (build it with `triviaKindMask(labels, ['comment', \u2026])`). Unlisted\n * kinds (e.g. whitespace) are skipped over but not recorded per node, so a host\n * that only reads comments doesn't pay to log every whitespace run. The global\n * `triviaLog` returned by `run()` is unaffected. Omit to capture every kind.\n */\n triviaCaptureMask?: number\n /**\n * Activate automatic list recovery. When true, `many`/`sepBy`/`oneOrMore` recover\n * from a failed element \u2014 skip to a sync point (a resume token inferred from the\n * grammar's structure; the grammar carries no recovery config), emit a\n * `ParseError` over the skipped span (collected in `errors`), and keep parsing the\n * rest of the list \u2014 instead of stopping at the first bad element. Omit (the\n * default) for the strict \"one clean error and stop\" behavior, byte-identical to a\n * run with no recovery. Recovery is a cold path: on well-formed input nothing\n * fails, so none of the machinery runs.\n */\n tolerant?: boolean\n /**\n * Run three compiled-parser-only profiling passes: recognizer (no global\n * sinks), structural capture without the host, then the normal host path. This\n * is a measurement boundary, not a parser mode; ordinary `run()` output is\n * unchanged when omitted. The input is parsed THREE times \u2014 each pass gets its\n * own shallow copy of `options.state`, so keep per-parse state shallow (only\n * the top level is isolated between passes).\n */\n profile?: boolean\n}\n\nexport type RunProfilePass = {\n ms: number\n nodes: number\n childSlots: number\n rawSlots: number\n triviaSlots: number\n fieldSlots: number\n hostCalls: number\n}\n\nexport type RunProfile = {\n /** Existing `voidOf(transform(..., () => undefined))` semantics, generalized\n * to compiled structural nodes: no `ch`/`raw`/`tl` capture or raw entries. */\n recognizer: RunProfilePass\n /** Captures children/raw/trivia/fields but suppresses node construction. */\n structuralCapture: RunProfilePass\n /** The ordinary parser path, including its injected build host. */\n hostConstruction: RunProfilePass\n}\n\nexport type RunResult = {\n ok: boolean\n /** The entry's value on success; undefined on failure. */\n value: unknown\n span: { start: number; end: number }\n /** Expected-token set when the TOP-LEVEL parse failed (empty on success). */\n expected: string[]\n /** Recovery diagnostics (tolerant lists / `expect()`) collected during the parse (in order). */\n errors: ParseError[]\n /**\n * Flat trivia log for building a trivia map. Entry width depends on whether the\n * grammar uses labeled trivia: 2 numbers per entry (`start, end`) for plain\n * trivia, 3 numbers per entry (`start, end, kindIndex`) for labeled trivia\n * (grammars with `label()` arms in their trivia choice). Use `buildTriviaIndex`\n * / `triviaEntries` to consume it format-agnostically rather than iterating raw.\n */\n triviaLog: number[]\n /** Offset where unparsed input begins \u2014 the first non-trivia character the parse\n * left unconsumed (trailing trivia skipped when `trivia` is given), or null if\n * the whole input was consumed. This is how you detect \"the grammar stopped short,\n * there's junk here\". Only meaningful on success \u2014 a failed parse reports its own\n * `span`/`expected`. */\n unconsumedFrom: number | null\n /** Present only when `RunOptions.profile` is true. */\n profile?: RunProfile\n}\n\nconst invoke = (r: Runnable, input: string, pos: number, ctx: ParseContext): ParseResult<unknown> =>\n typeof r === 'function' ? r(input, pos, ctx) : r.parse(input, pos, ctx)\n\ntype ProfilePhase = NonNullable<ParseContext['_pmProfile']>['phase']\ntype ProfileState = NonNullable<ParseContext['_pmProfile']>\n\nfunction runOnce(entry: Runnable, input: string, options: RunOptions, phase?: ProfilePhase, profileState?: ProfileState): RunResult {\n if (typeof entry !== 'function' && typeof (entry as Combinator<unknown> | undefined)?.parse !== 'function') {\n throw new TypeError(\n `run(): start production is ${entry === null ? 'null' : typeof entry}, not a rule \u2014 the requested grammar rule does not exist (check the rule name).`,\n )\n }\n const triviaLog: number[] = []\n const errors: ParseError[] = []\n const profile = profileState ?? (phase === undefined\n ? undefined\n : { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 })\n // NOT \"no output\" \u2014 the `capture` phase still allocates per-node children/raw/\n // trivia buffers and records slot counts. This flag only omits the two GLOBAL\n // sinks (`_triviaLog`, `_errors`) from the context; hence `skipGlobalSinks`.\n const skipGlobalSinks = phase === 'recognizer' || phase === 'capture'\n // Grammar-level ambient trivia declared via rules({ trivia }, factory): install\n // it as ctx.trivia so it's ambient for the whole parse (the interpreter path;\n // a compiled entry has it baked in and carries no _meta). parser/noTrivia still\n // override locally.\n const grammarTrivia = typeof entry !== 'function' ? entry._meta.grammarTrivia : undefined\n const grammarScanSkip = typeof entry !== 'function' ? entry._meta.grammarScanSkip : undefined\n const ctx: ParseContext = {\n trackLines: false,\n ...(skipGlobalSinks\n ? { _pmProfile: profile! }\n : { _triviaLog: triviaLog, _errors: errors, ...(profile === undefined ? {} : { _pmProfile: profile }) }),\n build: options.build,\n state: options.state,\n ...(grammarTrivia !== undefined\n ? { trivia: grammarTrivia, ...(grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}) }\n : {}),\n ...(grammarScanSkip !== undefined ? { scanSkip: grammarScanSkip } : {}),\n ...(options.triviaCaptureMask !== undefined ? { _triviaCaptureMask: options.triviaCaptureMask } : {}),\n ...(options.tolerant ? { _tolerant: true, _rec: REC } : {}),\n ...(options.instrumentation === undefined ? {} : options.instrumentation),\n }\n const r = invoke(entry, input, 0, ctx)\n\n let unconsumedFrom: number | null = null\n if (r.ok) {\n let pos = r.span?.end ?? 0\n if (options.trivia && pos < input.length) {\n // Throwaway ctx: trailing trivia must NOT pollute the parse's trivia log.\n const t = invoke(options.trivia, input, pos, { trackLines: false })\n if (t.ok && t.span.end > pos) pos = t.span.end\n }\n unconsumedFrom = pos < input.length ? pos : null\n }\n\n return {\n ok: r.ok,\n value: r.ok ? (r as { value: unknown }).value : undefined,\n span: r.span ?? { start: 0, end: 0 },\n expected: r.ok ? [] : ((r as { expected?: string[] }).expected ?? []),\n errors,\n triviaLog,\n unconsumedFrom,\n }\n}\n\n/** Shallow copy of `options.state` per pass so an in-place mutation by one\n * profiling pass doesn't leak into the next (the parity check only compares\n * `ok`/`unconsumedFrom`, so a diverged structural output would go unnoticed).\n * Only the top level is isolated \u2014 deeply-nested mutable state is shared; a\n * profiled grammar should keep per-parse state shallow (see the `profile` docs). */\nfunction clonePassState(state: unknown): unknown {\n if (state === null || typeof state !== 'object') return state\n return Array.isArray(state) ? [...state] : { ...(state as Record<string, unknown>) }\n}\n\nfunction profilePass(entry: Runnable, input: string, options: RunOptions, phase: ProfilePhase): { result: RunResult; profile: RunProfilePass } {\n const state: ProfileState = { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 }\n const passOptions: RunOptions = options.state === undefined ? options : { ...options, state: clonePassState(options.state) }\n const start = performance.now()\n const result = runOnce(entry, input, passOptions, phase, state)\n const { phase: _phase, ...counts } = state\n return { result, profile: { ms: performance.now() - start, ...counts } }\n}\n\nexport function run(entry: Runnable, input: string, options: RunOptions = {}): RunResult {\n if (!options.profile) return runOnce(entry, input, options)\n if (typeof entry !== 'function') {\n throw new TypeError('run({ profile: true }) requires a compiled parser entry')\n }\n\n const recognizer = profilePass(entry, input, options, 'recognizer')\n const capture = profilePass(entry, input, options, 'capture')\n const host = profilePass(entry, input, options, 'host')\n if (recognizer.result.ok !== host.result.ok || capture.result.ok !== host.result.ok\n || recognizer.result.unconsumedFrom !== host.result.unconsumedFrom\n || capture.result.unconsumedFrom !== host.result.unconsumedFrom) {\n throw new Error('run({ profile: true }) changed recognition; the grammar is not profile-safe')\n }\n return {\n ...host.result,\n profile: {\n recognizer: recognizer.profile,\n structuralCapture: capture.profile,\n hostConstruction: host.profile,\n },\n }\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../../src/run/index.ts", "../../src/cst/capture-buffer.ts", "../../src/recovery/scan.ts", "../../src/cst/host-mode.ts", "../../src/functional/run.ts"],
4
+ "sourcesContent": ["/**\n * `parseman/run` \u2014 the minimal entry for EXECUTING an already-compiled grammar.\n *\n * The macro removes the combinators, the compiler and the codegen from your\n * bundle, but not the driver: running a compiled grammar is still a `run()` call,\n * so a package that SHIPS a parser keeps a runtime import of parseman. Importing\n * that driver from the main entry drags the whole library along \u2014 the combinator\n * set, `compile()`, the first-set analysis, the CST builders \u2014 none of which a\n * compiled parser touches.\n *\n * This entry exists so it doesn't have to. `run()`'s complete module closure is\n * three files (this, `recovery/scan.ts`, `cst/capture-buffer.ts`) \u2014 a fraction of\n * the main entry. `test/unit/run-entry-closure.test.ts` asserts that stays true,\n * so a future import into `functional/run.ts` that widens the closure fails the\n * suite rather than quietly re-inflating everyone's bundle.\n *\n * Use the main entry when you BUILD grammars; use this one when you only run\n * output the macro (or `compile()`) already produced.\n *\n * import { run } from 'parseman/run'\n * import { myGrammar } from './grammar.js' // macro-compiled\n *\n * const result = run(myGrammar, source, { trivia })\n */\nexport { run } from '../functional/run.ts'\nexport type { Runnable, RunOptions, RunResult } from '../functional/run.ts'\nexport type { ParseContext, ParseError, ParseResult } from '../types.ts'\n", "import type { ParseContext } from '../types.ts'\n\n/** Lazy per-node capture state \u2014 arrays materialized on first push. */\nexport type CstCaptureBuf = {\n single?: unknown | undefined\n ch?: unknown[] | undefined\n rawSingle?: unknown | undefined\n raw?: unknown[] | undefined\n tl?: number[] | undefined\n}\n\nconst EMPTY: unknown[] = []\nconst EMPTY_TL: number[] = []\n\nexport function cstCaptureActive(ctx: ParseContext): boolean {\n return ctx._cstBuf !== undefined || ctx._cstLeaves !== undefined\n}\n\nexport type CstNodeCaptureSaved = {\n ch: ParseContext['_cstChildren']\n lv: ParseContext['_cstLeaves']\n raw: ParseContext['_cstRawChildren']\n tl: ParseContext['_cstTriviaLog']\n cap: boolean | undefined\n buf: CstCaptureBuf | undefined\n}\n\nexport function beginCstNodeCapture(ctx: ParseContext): CstNodeCaptureSaved {\n const saved: CstNodeCaptureSaved = {\n ch: ctx._cstChildren,\n lv: ctx._cstLeaves,\n raw: ctx._cstRawChildren,\n tl: ctx._cstTriviaLog,\n cap: ctx.captureTrivia,\n buf: ctx._cstBuf,\n }\n ctx._cstBuf = {}\n ctx._cstChildren = undefined\n ctx._cstLeaves = undefined\n ctx._cstRawChildren = undefined\n ctx._cstTriviaLog = undefined\n ctx.captureTrivia = true\n return saved\n}\n\nexport function finishCstBuf(buf: CstCaptureBuf | undefined): {\n children: unknown[]\n rawChildren: unknown[]\n triviaLog: number[]\n} {\n if (!buf) return { children: EMPTY, rawChildren: EMPTY, triviaLog: EMPTY_TL }\n const children = buf.ch ?? (buf.single !== undefined ? [buf.single] : EMPTY)\n const rawChildren = buf.raw ?? (buf.rawSingle !== undefined ? [buf.rawSingle] : EMPTY)\n const triviaLog = buf.tl ?? EMPTY_TL\n return { children, rawChildren, triviaLog }\n}\n\nexport function endCstNodeCapture(\n ctx: ParseContext,\n saved: CstNodeCaptureSaved,\n): { children: unknown[]; rawChildren: unknown[]; triviaLog: number[] } {\n const materialized = finishCstBuf(ctx._cstBuf)\n ctx._cstBuf = saved.buf\n ctx._cstChildren = saved.ch\n ctx._cstLeaves = saved.lv\n ctx._cstRawChildren = saved.raw\n ctx._cstTriviaLog = saved.tl\n ctx.captureTrivia = saved.cap\n return materialized\n}\n\nexport function pushCstLeaf(ctx: ParseContext, leaf: unknown): void {\n pushCstChild(ctx, leaf, leaf)\n}\n\n/** Record a built sub-node into the active collector (children vs rawChildren may differ). */\nexport function pushCstChild(ctx: ParseContext, built: unknown, rawEntry: unknown): void {\n const b = ctx._cstBuf\n if (b) {\n if (b.ch) b.ch.push(built)\n else if (b.single !== undefined) { b.ch = [b.single, built]; b.single = undefined }\n else b.single = built\n\n if (b.raw) b.raw.push(rawEntry)\n else if (b.rawSingle !== undefined) { b.raw = [b.rawSingle, rawEntry]; b.rawSingle = undefined }\n else b.rawSingle = rawEntry\n return\n }\n if (ctx._cstChildren) ctx._cstChildren.push(built)\n else if (ctx._cstLeaves) ctx._cstLeaves.push(built)\n if (ctx._cstRawChildren) ctx._cstRawChildren.push(rawEntry)\n}\n\nexport function cstRawLen(ctx: ParseContext): number {\n const b = ctx._cstBuf\n if (b) {\n if (b.raw) return b.raw.length\n return b.rawSingle !== undefined ? 1 : 0\n }\n return ctx._cstRawChildren?.length ?? 0\n}\n\nexport function cstLeavesLen(ctx: ParseContext): number {\n const b = ctx._cstBuf\n if (b) {\n if (b.ch) return b.ch.length\n return b.single !== undefined ? 1 : 0\n }\n return ctx._cstLeaves?.length ?? 0\n}\n\nexport function cstTlLen(ctx: ParseContext): number {\n const b = ctx._cstBuf\n if (b) return b.tl?.length ?? 0\n return ctx._cstTriviaLog?.length ?? 0\n}\n\nexport type CstRollbackMark = { raw: number; tlog: number; leaves: number; fields: number; errors: number }\n\nexport function saveCstMark(ctx: ParseContext): CstRollbackMark {\n return {\n raw: cstRawLen(ctx),\n tlog: cstTlLen(ctx),\n leaves: cstLeavesLen(ctx),\n fields: ctx._fields?.length ?? 0,\n // Speculative rollback must truncate the flat recovery-error sink in lockstep\n // with the CST leaves: a branch that recovered (pushing a ParseError to\n // ctx._errors AND embedding it as a CST child via captureError) then failed\n // must remove BOTH, or the flat error survives as a GHOST that a tree walk\n // can't see (flat run().errors would disagree with the embedded set).\n errors: ctx._errors?.length ?? 0,\n }\n}\n\nfunction rollbackBufList(\n b: CstCaptureBuf,\n keyMulti: 'ch' | 'raw',\n keySingle: 'single' | 'rawSingle',\n len: number,\n): void {\n const arr = b[keyMulti]\n if (arr) {\n if (len === 0) b[keyMulti] = undefined\n else if (len === 1) { b[keySingle] = arr[0]; b[keyMulti] = undefined }\n // Guarded like every other truncation \u2014 see rollbackCstCapture. This is the\n // buffered node-capture path, so it is reached only by the INTERPRETER (the\n // compiled engine inlines its own capture and never calls here), and it is\n // where the redundant-store rate is highest: over a backtracking fixture\n // this branch ran 32,800 times and 31,198 of those \u2014 95% \u2014 restored a\n // length that had not moved.\n else if (arr.length !== len) arr.length = len\n return\n }\n if (len === 0) b[keySingle] = undefined\n}\n\nexport function rollbackCstCapture(ctx: ParseContext, mark: CstRollbackMark): void {\n // Truncate the flat recovery-error sink alongside the CST, so a rolled-back\n // speculative branch leaves no ghost error (see saveCstMark). Guarded on a\n // present sink + a defined mark (rollbackTrivia forwards the same field).\n //\n // Each truncation is guarded on `length !== mark`, mirroring the compiled\n // engine (codegen.ts `captureRestoreBody`). Assigning `array.length` runs V8's\n // length setter \u2014 a backing-store trim decision \u2014 and costs the same when the\n // value is unchanged, which is the overwhelmingly common case here: a\n // speculative branch that captured nothing still pays for every store. The\n // compare is an in-object load. Both engines must carry the guard or the\n // interpreted path silently keeps the cost the compiled path shed.\n if (ctx._errors && mark.errors !== undefined && ctx._errors.length !== mark.errors) ctx._errors.length = mark.errors\n const b = ctx._cstBuf\n if (b) {\n rollbackBufList(b, 'raw', 'rawSingle', mark.raw)\n rollbackBufList(b, 'ch', 'single', mark.leaves)\n if (b.tl) {\n if (mark.tlog === 0) b.tl = undefined\n else if (b.tl.length !== mark.tlog) b.tl.length = mark.tlog\n }\n if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields\n return\n }\n if (ctx._cstRawChildren && ctx._cstRawChildren.length !== mark.raw) ctx._cstRawChildren.length = mark.raw\n if (ctx._cstTriviaLog && ctx._cstTriviaLog.length !== mark.tlog) ctx._cstTriviaLog.length = mark.tlog\n if (ctx._cstLeaves && ctx._cstLeaves.length !== mark.leaves) ctx._cstLeaves.length = mark.leaves\n if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields\n}\n\nexport function pushCstTriviaEntry(\n ctx: ParseContext,\n start: number,\n end: number,\n kindIndex?: number,\n): void {\n const insertIdx = cstRawLen(ctx)\n const b = ctx._cstBuf\n const withKind = ctx.triviaKindLabels !== undefined && kindIndex !== undefined\n if (b) {\n if (!b.tl) {\n b.tl = withKind ? [start, end, insertIdx, kindIndex] : [start, end, insertIdx]\n } else if (withKind) {\n b.tl.push(start, end, insertIdx, kindIndex)\n } else {\n b.tl.push(start, end, insertIdx)\n }\n return\n }\n if (ctx._cstTriviaLog) {\n if (withKind) ctx._cstTriviaLog.push(start, end, insertIdx, kindIndex)\n else ctx._cstTriviaLog.push(start, end, insertIdx)\n }\n}\n\nexport function pushTriviaLogEntry(\n ctx: ParseContext,\n start: number,\n end: number,\n kindIndex?: number,\n): void {\n if (!ctx._triviaLog) return\n if (ctx.triviaKindLabels !== undefined && kindIndex !== undefined) {\n ctx._triviaLog.push(start, end, kindIndex)\n } else {\n ctx._triviaLog.push(start, end)\n }\n}\n\nexport function cstDeferredTriviaActive(ctx: ParseContext): boolean {\n return ctx._triviaLog !== undefined || ctx._cstBuf !== undefined || ctx._cstTriviaLog !== undefined\n}\n", "import type { Combinator, FirstSet, ParseContext, ParseError, ParseResult, RecoveryHelpers } from '../types.ts'\nimport { cstCaptureActive, pushCstChild } from '../cst/capture-buffer.ts'\n\n/**\n * Recovery MECHANISM \u2014 sync-source-agnostic. These primitives implement \"scan\n * forward to a sync token and emit a ParseError\" and \"is the sync token here?\".\n * They are deliberately independent of WHERE the sync sentinel comes from: the\n * grammar never carries recovery config. Sync points are inferred automatically\n * from grammar structure (see `infer.ts`) and may be overridden externally by the\n * language service via `ctx._listSync`. This is the salvaged core of the old\n * `combinators/recover-scan.ts`.\n */\n\n/**\n * Run `sentinel` at `pos` as PURE lookahead: succeed/fail only, with every\n * side-effecting sink suppressed (CST leaves/children/trivia, the trivia log,\n * the recovery error sink, and the completions probe). A recovery scan probes the\n * sentinel at many positions and must not capture leaves, log trivia, or pollute\n * `completionsAt`'s furthest-failure set as a byproduct of looking ahead.\n */\nfunction probeAt(sentinel: Combinator<unknown>, input: string, pos: number, ctx: ParseContext): boolean {\n const s = {\n _probe: ctx._probe, _errors: ctx._errors,\n _cstLeaves: ctx._cstLeaves, _cstChildren: ctx._cstChildren, _cstRawChildren: ctx._cstRawChildren,\n _cstTriviaLog: ctx._cstTriviaLog, _triviaLog: ctx._triviaLog, _fields: ctx._fields, _cstBuf: ctx._cstBuf,\n _tolerant: ctx._tolerant, _sync: ctx._sync,\n }\n ctx._probe = undefined; ctx._errors = undefined\n ctx._cstLeaves = undefined; ctx._cstChildren = undefined; ctx._cstRawChildren = undefined\n ctx._cstTriviaLog = undefined; ctx._triviaLog = undefined; ctx._fields = undefined; ctx._cstBuf = undefined\n // A sentinel is pure lookahead: it must not itself enter tolerant recovery (nor\n // read a stale outer `_sync`). Otherwise a sentinel that composes many/sepBy would\n // run recovery on every probed position \u2014 wasted work, and it could resync to the\n // enclosing list's sync token. Cleared here and restored in `finally`.\n ctx._tolerant = undefined; ctx._sync = undefined\n try {\n return sentinel.parse(input, pos, ctx).ok\n } finally {\n ctx._probe = s._probe; ctx._errors = s._errors\n ctx._cstLeaves = s._cstLeaves; ctx._cstChildren = s._cstChildren; ctx._cstRawChildren = s._cstRawChildren\n ctx._cstTriviaLog = s._cstTriviaLog; ctx._triviaLog = s._triviaLog; ctx._fields = s._fields; ctx._cstBuf = s._cstBuf\n ctx._tolerant = s._tolerant; ctx._sync = s._sync\n }\n}\n\n/**\n * Combine two recovery sentinels into one that matches when either does \u2014 used by\n * tolerant `sepBy` to resync to its own separator OR the enclosing delimiter. Cold\n * path only; a bare wrapper so we don't pull `choice()` (and its analysis) in.\n */\nexport function orSentinel(\n a: Combinator<unknown>,\n b: Combinator<unknown> | undefined,\n): Combinator<unknown> {\n if (b === undefined) return a\n return {\n _tag: 'orSentinel',\n _meta: { firstSet: { kind: 'any' }, canMatchNewline: false, isTrivia: false },\n _def: { tag: 'unknown' },\n parse(input: string, pos: number, ctx: ParseContext): ParseResult<unknown> {\n const ra = a.parse(input, pos, ctx)\n if (ra.ok) return ra\n return b.parse(input, pos, ctx)\n },\n }\n}\n\n/**\n * A lightweight zero-width sentinel that \"matches\" (succeeds, consuming nothing)\n * when the input code point at `pos` starts the given first set. Sync inference\n * builds one from a list's follow set so a nested list can resync to the enclosing\n * delimiter with no grammar annotation. Returns `null` when the first set is `any`\n * or `empty` (no usable inference).\n */\nexport function firstSetSentinel(fs: FirstSet): Combinator<null> | null {\n if (fs.kind !== 'ranges' || fs.ranges.length === 0) return null\n const ranges = fs.ranges\n return {\n _tag: 'firstSetSentinel',\n _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },\n _def: { tag: 'unknown' },\n parse(input: string, pos: number): ParseResult<null> {\n const code = pos < input.length ? input.codePointAt(pos) : undefined\n if (code !== undefined) {\n for (const r of ranges) {\n if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } }\n }\n }\n return { ok: false, expected: [], span: { start: pos, end: pos } }\n },\n }\n}\n\n/**\n * Cold-path recovery scan shared by tolerant `many()` / `sepBy()`. Scans forward\n * from `from` until `sync` matches (or EOF) \u2014 probing the sentinel as pure\n * lookahead \u2014 emits a {@link ParseError} spanning the skipped range, and pushes it\n * to `ctx._errors`. The sync token is NOT consumed; the caller's loop resumes from\n * `end`. Only ever reached after an element has failed, so entirely off the hot path.\n *\n * The caller owns the loop guard: `sepBy` progress is driven by its separator (a\n * zero-width error for a missing element is fine); `many` checks `matchesAt(sync)`\n * itself and breaks when sitting on the terminator, so a zero-width failure can\n * never spin.\n */\nexport function recoverScan(\n input: string,\n from: number,\n ctx: ParseContext,\n sync: Combinator<unknown>,\n expected: string[],\n): { error: ParseError; end: number } {\n let scanPos = from\n while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++\n const error: ParseError = { _tag: 'parseError', span: { start: from, end: scanPos }, expected }\n ctx._errors?.push(error)\n return { error, end: scanPos }\n}\n\n/**\n * Does `sentinel` match at `pos` (pure lookahead)? Used by the tolerant list loops\n * to detect \"we are sitting on the sync/terminator token\" without consuming it or\n * capturing anything.\n */\nexport function matchesAt(\n sentinel: Combinator<unknown>,\n input: string,\n pos: number,\n ctx: ParseContext,\n): boolean {\n return probeAt(sentinel, input, pos, ctx)\n}\n\n/**\n * Embed a recovered error as a `parseError` CST child at the recovery point, so the\n * error lives IN the tree (rides reused subtrees across incremental edits; a tree\n * walk finds every diagnostic) \u2014 not only in the flat `ctx._errors` side-channel.\n * No-op when CST capture is off (value/AST mode). Both the interpreter list loops\n * and the compiled recovery branches call this (the latter via `_ctx._rec.capture`),\n * so the embedded node is identical on both paths.\n */\nexport function captureError(ctx: ParseContext, error: ParseError): void {\n if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error)\n}\n\n/**\n * The recovery-helper bundle handed to a tolerant parse via `ctx._rec`. A COMPILED\n * grammar can't import these, so the driver (`run`, `parseDoc`) injects them; the\n * interpreter list loops read the same bundle, guaranteeing byte-identical recovery\n * across paths. Shared here so every driver installs the exact same instance.\n */\nexport const REC: RecoveryHelpers = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError }\n", "/**\n * The compiled-artifact \u2194 host contract, in ONE place.\n *\n * This lives on its own \u2014 rather than in `compiler/codegen.ts` where it started \u2014\n * because the DRIVER has to enforce it too. `functional/run.ts` is the `parseman/run`\n * entry, whose whole purpose is to stay small (see `test/unit/run-entry-closure.test.ts`),\n * so it cannot import the compiler to reach an assertion. Neither can it carry its own\n * copy: two spellings of one contract is how the two engines drift, and this contract\n * exists precisely because the failure it prevents is silent.\n *\n * No imports, by design \u2014 that is what keeps it free for both sides to depend on.\n */\n\n/**\n * Which consumer a compiled artifact was lowered FOR.\n *\n * - `'ast'` \u2014 the grammar's own `build` callbacks produce the result (the eval consumer).\n * - `'cst'` \u2014 a positioned-CST `ctx.build` host produces it (linter / IDE / language\n * service), and every node's capture is sized for that host.\n *\n * ONE grammar source serves both; they are two COMPILATIONS of it, not two grammars.\n * Deciding at compile time is what keeps the eval-AST artifact free of per-node host\n * probing \u2014 see the `hostMode` note on the codegen Ctx.\n */\nexport type HostMode = 'ast' | 'cst'\n\n/** Host mode a fused rule map (and each of its rule functions) was lowered for;\n * absent on hand-built maps \u2192 'ast'. */\nexport const FUSED_HOST_MODE = Symbol.for('parseman.fusedHostMode')\n\n/** Whether any fused piece omitted a direct builder's positioned-CST branch. */\nexport const FUSED_HOST_ELIDED = Symbol.for('parseman.fusedHostElided')\n\n/**\n * A compiled artifact and the host it is driven with must agree \u2014 and when they do not,\n * that has to be an ERROR, never a quietly-degraded tree.\n *\n * An `'ast'` artifact does not emit the positioned-CST branch at all, so attaching a\n * `_parsemanCstOutput` host to one would silently get the grammar's own AST objects\n * where the caller asked for a CST. A `'cst'` artifact builds every node through the\n * host, so running one without a host would call `undefined`. Both are compile/run\n * mismatches with an obvious fix, so both say so.\n *\n * Called once per parse from the TypeScript entry points, never from generated code.\n */\nexport function assertHostModeCompatible(mode: HostMode, build: unknown, hostBranchElided = true): void {\n const isCstHost = (build as { _parsemanCstOutput?: true } | undefined)?._parsemanCstOutput === true\n // A purely STRUCTURAL artifact never had a direct-builder host branch to drop, so it\n // serves a CST host in either mode \u2014 that is the long-standing `node(parser)` contract\n // and this change does not touch it. The INTERPRETER passes `false` for the same\n // reason from the other direction: it re-decides per parse and never elides anything.\n if (mode === 'ast' && isCstHost && hostBranchElided) {\n throw new Error(\n 'parseman: this parser was compiled for host mode \"ast\" (the default), so its nodes '\n + 'build through their own `build` callbacks and no positioned-CST branch was emitted. '\n + 'It cannot be driven with a positioned-CST host (cstBuildHost / a language-service '\n + 'host). Compile a second artifact from the SAME grammar with '\n + \"`compile(grammar, { hostMode: 'cst' })` \u2014 or, under the macro, a second \"\n + \"`rules({ hostMode: 'cst' }, factory)` call site \u2014 and drive that one instead.\",\n )\n }\n if (mode === 'cst' && !isCstHost) {\n throw new Error(\n 'parseman: this parser was compiled for host mode \"cst\", so every node builds through '\n + 'a positioned-CST `ctx.build` host. Pass one (e.g. `{ build: cstBuildHost }`), or use '\n + 'an artifact compiled with the default `hostMode: \"ast\"` to get the grammar\\'s own AST.',\n )\n }\n}\n", "import type { Combinator, ParseContext, ParseError, ParseResult } from '../types.ts'\nimport { REC } from '../recovery/scan.ts'\n/* `cst/host-mode.ts` is import-free by design, so this keeps the `parseman/run`\n * closure minimal \u2014 see test/unit/run-entry-closure.test.ts. */\nimport { assertHostModeCompatible, FUSED_HOST_MODE, FUSED_HOST_ELIDED, type HostMode } from '../cst/host-mode.ts'\n\n/**\n * Run a compiled/interpreted grammar entry against an input and collect the raw\n * outcome a tool needs to shape a parse result \u2014 WITHOUT the consumer having to\n * hand-build a `ctx`, branch on function-vs-combinator, or scan for leftover\n * input itself.\n *\n * A `compile()`/macro grammar hands you a map of bare parse FUNCTIONS; the\n * interpreter hands you COMBINATORS. `run()` invokes either, threads the standard\n * framework ctx (trivia log, recover/expect error sink, the `ctx.build` host,\n * grammar state), and \u2014 given the grammar's trivia parser \u2014 reports where\n * non-trivia input was left unconsumed. The consumer keeps only its own policy:\n * how to shape the tree and how to turn diagnostics into its error type.\n */\n\n/** A compiled rule (macro/`compile()` output) OR an interpreter combinator. */\nexport type Runnable =\n | ((input: string, pos: number, ctx: ParseContext) => ParseResult<unknown>)\n | Combinator<unknown>\n\nexport type RunOptions = {\n /** Optional hooks for a coverage-enabled compiled or macro parser. Ordinary\n * parses omit this completely, so instrumentation has no normal-path cost. */\n instrumentation?: {\n _grammarCoverage?: (id: string) => void\n _grammarTrace?: { write(event: { id: string; phase: 'enter' | 'attempt' | 'selected' | 'success' | 'failure' | 'backtrack' | 'rollback'; offset: number; end?: number }): void }\n }\n /** `ctx.build` host \u2014 makes structural `node()` rules build a CST / AST via the\n * host instead of their own eval builders. Omit for a grammar's own builders. */\n build?: ParseContext['build']\n /** Initial grammar state threaded into `ctx.state`. */\n state?: unknown\n /**\n * The grammar's trivia rule. A root rule consumes trivia BETWEEN terms but not\n * after the last one, so trailing whitespace/comments would otherwise look like\n * unparsed input. Given the trivia rule, `run` skips that tail before computing\n * `unconsumedFrom` \u2014 so only real leftover is reported. Also encodes dialect\n * differences for free: pass the CSS trivia and a trailing `//` counts as\n * leftover; pass the Less trivia (which treats `//` as a line comment) and it\n * doesn't. An UNTERMINATED comment (which the trivia rule won't match) surfaces\n * at its start. Omit to require the parse to reach the exact end itself.\n */\n trivia?: Runnable\n /**\n * Restrict PER-NODE CST trivia capture (the `triviaLog` a node's builder sees)\n * to these trivia kinds \u2014 a bitmask over the grammar's `triviaKindLabels`\n * indices (build it with `triviaKindMask(labels, ['comment', \u2026])`). Unlisted\n * kinds (e.g. whitespace) are skipped over but not recorded per node, so a host\n * that only reads comments doesn't pay to log every whitespace run. The global\n * `triviaLog` returned by `run()` is unaffected. Omit to capture every kind.\n */\n triviaCaptureMask?: number\n /**\n * Activate automatic list recovery. When true, `many`/`sepBy`/`oneOrMore` recover\n * from a failed element \u2014 skip to a sync point (a resume token inferred from the\n * grammar's structure; the grammar carries no recovery config), emit a\n * `ParseError` over the skipped span (collected in `errors`), and keep parsing the\n * rest of the list \u2014 instead of stopping at the first bad element. Omit (the\n * default) for the strict \"one clean error and stop\" behavior, byte-identical to a\n * run with no recovery. Recovery is a cold path: on well-formed input nothing\n * fails, so none of the machinery runs.\n */\n tolerant?: boolean\n /**\n * Run three compiled-parser-only profiling passes: recognizer (no global\n * sinks), structural capture without the host, then the normal host path. This\n * is a measurement boundary, not a parser mode; ordinary `run()` output is\n * unchanged when omitted. The input is parsed THREE times \u2014 each pass gets its\n * own shallow copy of `options.state`, so keep per-parse state shallow (only\n * the top level is isolated between passes).\n */\n profile?: boolean\n}\n\nexport type RunProfilePass = {\n ms: number\n nodes: number\n childSlots: number\n rawSlots: number\n triviaSlots: number\n fieldSlots: number\n hostCalls: number\n}\n\nexport type RunProfile = {\n /** Existing `voidOf(transform(..., () => undefined))` semantics, generalized\n * to compiled structural nodes: no `ch`/`raw`/`tl` capture or raw entries. */\n recognizer: RunProfilePass\n /** Captures children/raw/trivia/fields but suppresses node construction. */\n structuralCapture: RunProfilePass\n /** The ordinary parser path, including its injected build host. */\n hostConstruction: RunProfilePass\n}\n\nexport type RunResult = {\n ok: boolean\n /** The entry's value on success; undefined on failure. */\n value: unknown\n span: { start: number; end: number }\n /** Expected-token set when the TOP-LEVEL parse failed (empty on success). */\n expected: string[]\n /** Recovery diagnostics (tolerant lists / `expect()`) collected during the parse (in order). */\n errors: ParseError[]\n /**\n * Flat trivia log for building a trivia map. Entry width depends on whether the\n * grammar uses labeled trivia: 2 numbers per entry (`start, end`) for plain\n * trivia, 3 numbers per entry (`start, end, kindIndex`) for labeled trivia\n * (grammars with `label()` arms in their trivia choice). Use `buildTriviaIndex`\n * / `triviaEntries` to consume it format-agnostically rather than iterating raw.\n */\n triviaLog: number[]\n /** Offset where unparsed input begins \u2014 the first non-trivia character the parse\n * left unconsumed (trailing trivia skipped when `trivia` is given), or null if\n * the whole input was consumed. This is how you detect \"the grammar stopped short,\n * there's junk here\". Only meaningful on success \u2014 a failed parse reports its own\n * `span`/`expected`. */\n unconsumedFrom: number | null\n /** Present only when `RunOptions.profile` is true. */\n profile?: RunProfile\n}\n\nconst invoke = (r: Runnable, input: string, pos: number, ctx: ParseContext): ParseResult<unknown> =>\n typeof r === 'function' ? r(input, pos, ctx) : r.parse(input, pos, ctx)\n\ntype ProfilePhase = NonNullable<ParseContext['_pmProfile']>['phase']\ntype ProfileState = NonNullable<ParseContext['_pmProfile']>\n\nfunction runOnce(entry: Runnable, input: string, options: RunOptions, phase?: ProfilePhase, profileState?: ProfileState): RunResult {\n if (typeof entry !== 'function' && typeof (entry as Combinator<unknown> | undefined)?.parse !== 'function') {\n throw new TypeError(\n `run(): start production is ${entry === null ? 'null' : typeof entry}, not a rule \u2014 the requested grammar rule does not exist (check the rule name).`,\n )\n }\n const triviaLog: number[] = []\n const errors: ParseError[] = []\n const profile = profileState ?? (phase === undefined\n ? undefined\n : { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 })\n // NOT \"no output\" \u2014 the `capture` phase still allocates per-node children/raw/\n // trivia buffers and records slot counts. This flag only omits the two GLOBAL\n // sinks (`_triviaLog`, `_errors`) from the context; hence `skipGlobalSinks`.\n const skipGlobalSinks = phase === 'recognizer' || phase === 'capture'\n // Grammar-level ambient trivia declared via rules({ trivia }, factory): install\n // it as ctx.trivia so it's ambient for the whole parse (the interpreter path;\n // a compiled entry has it baked in and carries no _meta). parser/noTrivia still\n // override locally.\n const grammarTrivia = typeof entry !== 'function' ? entry._meta.grammarTrivia : undefined\n const grammarScanSkip = typeof entry !== 'function' ? entry._meta.grammarScanSkip : undefined\n /*\n * Refuse an artifact/host mismatch ONCE per parse, exactly as `parseDoc` and a\n * compiled parser's `parseWithContext` already do. `run()` is handed a RULE, not the\n * registry, so the fused rule functions carry the stamp themselves (see `fusedBody`);\n * an interpreter entry carries it on `_meta`. Neither is on a hot path \u2014 this runs\n * once, before `invoke`.\n *\n * Without this, `run()` was the one driver that could silently produce the wrong tree\n * shape: an 'ast' artifact driven with a positioned-CST host returns its own AST\n * objects where the caller asked for a CST, and a CST child filter then drops them\n * and the node simply vanishes.\n *\n * The INTERPRETER passes `elided: false` unconditionally, and that is not a shortcut:\n * it has no compile step, re-decides the host route per parse, and so has never\n * dropped a branch. Only its `'cst'`-without-a-host half can be wrong.\n */\n const stamped = entry as Partial<Record<symbol, unknown>>\n assertHostModeCompatible(\n typeof entry === 'function'\n ? ((stamped[FUSED_HOST_MODE] as HostMode | undefined) ?? 'ast')\n : (entry._meta.grammarHostMode ?? 'ast'),\n options.build,\n typeof entry === 'function' && stamped[FUSED_HOST_ELIDED] === true,\n )\n const ctx: ParseContext = {\n trackLines: false,\n ...(skipGlobalSinks\n ? { _pmProfile: profile! }\n : { _triviaLog: triviaLog, _errors: errors, ...(profile === undefined ? {} : { _pmProfile: profile }) }),\n build: options.build,\n state: options.state,\n ...(grammarTrivia !== undefined\n ? { trivia: grammarTrivia, ...(grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}) }\n : {}),\n ...(grammarScanSkip !== undefined ? { scanSkip: grammarScanSkip } : {}),\n ...(options.triviaCaptureMask !== undefined ? { _triviaCaptureMask: options.triviaCaptureMask } : {}),\n ...(options.tolerant ? { _tolerant: true, _rec: REC } : {}),\n ...(options.instrumentation === undefined ? {} : options.instrumentation),\n }\n const r = invoke(entry, input, 0, ctx)\n\n let unconsumedFrom: number | null = null\n if (r.ok) {\n let pos = r.span?.end ?? 0\n if (options.trivia && pos < input.length) {\n // Throwaway ctx: trailing trivia must NOT pollute the parse's trivia log.\n const t = invoke(options.trivia, input, pos, { trackLines: false })\n if (t.ok && t.span.end > pos) pos = t.span.end\n }\n unconsumedFrom = pos < input.length ? pos : null\n }\n\n return {\n ok: r.ok,\n value: r.ok ? (r as { value: unknown }).value : undefined,\n span: r.span ?? { start: 0, end: 0 },\n expected: r.ok ? [] : ((r as { expected?: string[] }).expected ?? []),\n errors,\n triviaLog,\n unconsumedFrom,\n }\n}\n\n/** Shallow copy of `options.state` per pass so an in-place mutation by one\n * profiling pass doesn't leak into the next (the parity check only compares\n * `ok`/`unconsumedFrom`, so a diverged structural output would go unnoticed).\n * Only the top level is isolated \u2014 deeply-nested mutable state is shared; a\n * profiled grammar should keep per-parse state shallow (see the `profile` docs). */\nfunction clonePassState(state: unknown): unknown {\n if (state === null || typeof state !== 'object') return state\n return Array.isArray(state) ? [...state] : { ...(state as Record<string, unknown>) }\n}\n\nfunction profilePass(entry: Runnable, input: string, options: RunOptions, phase: ProfilePhase): { result: RunResult; profile: RunProfilePass } {\n const state: ProfileState = { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 }\n const passOptions: RunOptions = options.state === undefined ? options : { ...options, state: clonePassState(options.state) }\n const start = performance.now()\n const result = runOnce(entry, input, passOptions, phase, state)\n const { phase: _phase, ...counts } = state\n return { result, profile: { ms: performance.now() - start, ...counts } }\n}\n\nexport function run(entry: Runnable, input: string, options: RunOptions = {}): RunResult {\n if (!options.profile) return runOnce(entry, input, options)\n if (typeof entry !== 'function') {\n throw new TypeError('run({ profile: true }) requires a compiled parser entry')\n }\n\n const recognizer = profilePass(entry, input, options, 'recognizer')\n const capture = profilePass(entry, input, options, 'capture')\n const host = profilePass(entry, input, options, 'host')\n if (recognizer.result.ok !== host.result.ok || capture.result.ok !== host.result.ok\n || recognizer.result.unconsumedFrom !== host.result.unconsumedFrom\n || capture.result.unconsumedFrom !== host.result.unconsumedFrom) {\n throw new Error('run({ profile: true }) changed recognition; the grammar is not profile-safe')\n }\n return {\n ...host.result,\n profile: {\n recognizer: recognizer.profile,\n structuralCapture: capture.profile,\n hostConstruction: host.profile,\n },\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/run/index.js CHANGED
@@ -110,6 +110,23 @@ function captureError(ctx, error) {
110
110
  }
111
111
  var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError };
112
112
 
113
+ // src/cst/host-mode.ts
114
+ var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
115
+ var FUSED_HOST_ELIDED = /* @__PURE__ */ Symbol.for("parseman.fusedHostElided");
116
+ function assertHostModeCompatible(mode, build, hostBranchElided = true) {
117
+ const isCstHost = build?._parsemanCstOutput === true;
118
+ if (mode === "ast" && isCstHost && hostBranchElided) {
119
+ throw new Error(
120
+ "parseman: this parser was compiled for host mode \"ast\" (the default), so its nodes build through their own `build` callbacks and no positioned-CST branch was emitted. It cannot be driven with a positioned-CST host (cstBuildHost / a language-service host). Compile a second artifact from the SAME grammar with `compile(grammar, { hostMode: 'cst' })` \u2014 or, under the macro, a second `rules({ hostMode: 'cst' }, factory)` call site \u2014 and drive that one instead."
121
+ );
122
+ }
123
+ if (mode === "cst" && !isCstHost) {
124
+ throw new Error(
125
+ 'parseman: this parser was compiled for host mode "cst", so every node builds through a positioned-CST `ctx.build` host. Pass one (e.g. `{ build: cstBuildHost }`), or use an artifact compiled with the default `hostMode: "ast"` to get the grammar\'s own AST.'
126
+ );
127
+ }
128
+ }
129
+
113
130
  // src/functional/run.ts
114
131
  var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
115
132
  function runOnce(entry, input, options, phase, profileState) {
@@ -124,6 +141,12 @@ function runOnce(entry, input, options, phase, profileState) {
124
141
  const skipGlobalSinks = phase === "recognizer" || phase === "capture";
125
142
  const grammarTrivia = typeof entry !== "function" ? entry._meta.grammarTrivia : void 0;
126
143
  const grammarScanSkip = typeof entry !== "function" ? entry._meta.grammarScanSkip : void 0;
144
+ const stamped = entry;
145
+ assertHostModeCompatible(
146
+ typeof entry === "function" ? stamped[FUSED_HOST_MODE] ?? "ast" : entry._meta.grammarHostMode ?? "ast",
147
+ options.build,
148
+ typeof entry === "function" && stamped[FUSED_HOST_ELIDED] === true
149
+ );
127
150
  const ctx = {
128
151
  trackLines: false,
129
152
  ...skipGlobalSinks ? { _pmProfile: profile } : { _triviaLog: triviaLog, _errors: errors, ...profile === void 0 ? {} : { _pmProfile: profile } },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/cst/capture-buffer.ts", "../../src/recovery/scan.ts", "../../src/functional/run.ts"],
4
- "sourcesContent": ["import type { ParseContext } from '../types.ts'\n\n/** Lazy per-node capture state \u2014 arrays materialized on first push. */\nexport type CstCaptureBuf = {\n single?: unknown | undefined\n ch?: unknown[] | undefined\n rawSingle?: unknown | undefined\n raw?: unknown[] | undefined\n tl?: number[] | undefined\n}\n\nconst EMPTY: unknown[] = []\nconst EMPTY_TL: number[] = []\n\nexport function cstCaptureActive(ctx: ParseContext): boolean {\n return ctx._cstBuf !== undefined || ctx._cstLeaves !== undefined\n}\n\nexport type CstNodeCaptureSaved = {\n ch: ParseContext['_cstChildren']\n lv: ParseContext['_cstLeaves']\n raw: ParseContext['_cstRawChildren']\n tl: ParseContext['_cstTriviaLog']\n cap: boolean | undefined\n buf: CstCaptureBuf | undefined\n}\n\nexport function beginCstNodeCapture(ctx: ParseContext): CstNodeCaptureSaved {\n const saved: CstNodeCaptureSaved = {\n ch: ctx._cstChildren,\n lv: ctx._cstLeaves,\n raw: ctx._cstRawChildren,\n tl: ctx._cstTriviaLog,\n cap: ctx.captureTrivia,\n buf: ctx._cstBuf,\n }\n ctx._cstBuf = {}\n ctx._cstChildren = undefined\n ctx._cstLeaves = undefined\n ctx._cstRawChildren = undefined\n ctx._cstTriviaLog = undefined\n ctx.captureTrivia = true\n return saved\n}\n\nexport function finishCstBuf(buf: CstCaptureBuf | undefined): {\n children: unknown[]\n rawChildren: unknown[]\n triviaLog: number[]\n} {\n if (!buf) return { children: EMPTY, rawChildren: EMPTY, triviaLog: EMPTY_TL }\n const children = buf.ch ?? (buf.single !== undefined ? [buf.single] : EMPTY)\n const rawChildren = buf.raw ?? (buf.rawSingle !== undefined ? [buf.rawSingle] : EMPTY)\n const triviaLog = buf.tl ?? EMPTY_TL\n return { children, rawChildren, triviaLog }\n}\n\nexport function endCstNodeCapture(\n ctx: ParseContext,\n saved: CstNodeCaptureSaved,\n): { children: unknown[]; rawChildren: unknown[]; triviaLog: number[] } {\n const materialized = finishCstBuf(ctx._cstBuf)\n ctx._cstBuf = saved.buf\n ctx._cstChildren = saved.ch\n ctx._cstLeaves = saved.lv\n ctx._cstRawChildren = saved.raw\n ctx._cstTriviaLog = saved.tl\n ctx.captureTrivia = saved.cap\n return materialized\n}\n\nexport function pushCstLeaf(ctx: ParseContext, leaf: unknown): void {\n pushCstChild(ctx, leaf, leaf)\n}\n\n/** Record a built sub-node into the active collector (children vs rawChildren may differ). */\nexport function pushCstChild(ctx: ParseContext, built: unknown, rawEntry: unknown): void {\n const b = ctx._cstBuf\n if (b) {\n if (b.ch) b.ch.push(built)\n else if (b.single !== undefined) { b.ch = [b.single, built]; b.single = undefined }\n else b.single = built\n\n if (b.raw) b.raw.push(rawEntry)\n else if (b.rawSingle !== undefined) { b.raw = [b.rawSingle, rawEntry]; b.rawSingle = undefined }\n else b.rawSingle = rawEntry\n return\n }\n if (ctx._cstChildren) ctx._cstChildren.push(built)\n else if (ctx._cstLeaves) ctx._cstLeaves.push(built)\n if (ctx._cstRawChildren) ctx._cstRawChildren.push(rawEntry)\n}\n\nexport function cstRawLen(ctx: ParseContext): number {\n const b = ctx._cstBuf\n if (b) {\n if (b.raw) return b.raw.length\n return b.rawSingle !== undefined ? 1 : 0\n }\n return ctx._cstRawChildren?.length ?? 0\n}\n\nexport function cstLeavesLen(ctx: ParseContext): number {\n const b = ctx._cstBuf\n if (b) {\n if (b.ch) return b.ch.length\n return b.single !== undefined ? 1 : 0\n }\n return ctx._cstLeaves?.length ?? 0\n}\n\nexport function cstTlLen(ctx: ParseContext): number {\n const b = ctx._cstBuf\n if (b) return b.tl?.length ?? 0\n return ctx._cstTriviaLog?.length ?? 0\n}\n\nexport type CstRollbackMark = { raw: number; tlog: number; leaves: number; fields: number; errors: number }\n\nexport function saveCstMark(ctx: ParseContext): CstRollbackMark {\n return {\n raw: cstRawLen(ctx),\n tlog: cstTlLen(ctx),\n leaves: cstLeavesLen(ctx),\n fields: ctx._fields?.length ?? 0,\n // Speculative rollback must truncate the flat recovery-error sink in lockstep\n // with the CST leaves: a branch that recovered (pushing a ParseError to\n // ctx._errors AND embedding it as a CST child via captureError) then failed\n // must remove BOTH, or the flat error survives as a GHOST that a tree walk\n // can't see (flat run().errors would disagree with the embedded set).\n errors: ctx._errors?.length ?? 0,\n }\n}\n\nfunction rollbackBufList(\n b: CstCaptureBuf,\n keyMulti: 'ch' | 'raw',\n keySingle: 'single' | 'rawSingle',\n len: number,\n): void {\n const arr = b[keyMulti]\n if (arr) {\n if (len === 0) b[keyMulti] = undefined\n else if (len === 1) { b[keySingle] = arr[0]; b[keyMulti] = undefined }\n // Guarded like every other truncation \u2014 see rollbackCstCapture. This is the\n // buffered node-capture path, so it is reached only by the INTERPRETER (the\n // compiled engine inlines its own capture and never calls here), and it is\n // where the redundant-store rate is highest: over a backtracking fixture\n // this branch ran 32,800 times and 31,198 of those \u2014 95% \u2014 restored a\n // length that had not moved.\n else if (arr.length !== len) arr.length = len\n return\n }\n if (len === 0) b[keySingle] = undefined\n}\n\nexport function rollbackCstCapture(ctx: ParseContext, mark: CstRollbackMark): void {\n // Truncate the flat recovery-error sink alongside the CST, so a rolled-back\n // speculative branch leaves no ghost error (see saveCstMark). Guarded on a\n // present sink + a defined mark (rollbackTrivia forwards the same field).\n //\n // Each truncation is guarded on `length !== mark`, mirroring the compiled\n // engine (codegen.ts `captureRestoreBody`). Assigning `array.length` runs V8's\n // length setter \u2014 a backing-store trim decision \u2014 and costs the same when the\n // value is unchanged, which is the overwhelmingly common case here: a\n // speculative branch that captured nothing still pays for every store. The\n // compare is an in-object load. Both engines must carry the guard or the\n // interpreted path silently keeps the cost the compiled path shed.\n if (ctx._errors && mark.errors !== undefined && ctx._errors.length !== mark.errors) ctx._errors.length = mark.errors\n const b = ctx._cstBuf\n if (b) {\n rollbackBufList(b, 'raw', 'rawSingle', mark.raw)\n rollbackBufList(b, 'ch', 'single', mark.leaves)\n if (b.tl) {\n if (mark.tlog === 0) b.tl = undefined\n else if (b.tl.length !== mark.tlog) b.tl.length = mark.tlog\n }\n if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields\n return\n }\n if (ctx._cstRawChildren && ctx._cstRawChildren.length !== mark.raw) ctx._cstRawChildren.length = mark.raw\n if (ctx._cstTriviaLog && ctx._cstTriviaLog.length !== mark.tlog) ctx._cstTriviaLog.length = mark.tlog\n if (ctx._cstLeaves && ctx._cstLeaves.length !== mark.leaves) ctx._cstLeaves.length = mark.leaves\n if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields\n}\n\nexport function pushCstTriviaEntry(\n ctx: ParseContext,\n start: number,\n end: number,\n kindIndex?: number,\n): void {\n const insertIdx = cstRawLen(ctx)\n const b = ctx._cstBuf\n const withKind = ctx.triviaKindLabels !== undefined && kindIndex !== undefined\n if (b) {\n if (!b.tl) {\n b.tl = withKind ? [start, end, insertIdx, kindIndex] : [start, end, insertIdx]\n } else if (withKind) {\n b.tl.push(start, end, insertIdx, kindIndex)\n } else {\n b.tl.push(start, end, insertIdx)\n }\n return\n }\n if (ctx._cstTriviaLog) {\n if (withKind) ctx._cstTriviaLog.push(start, end, insertIdx, kindIndex)\n else ctx._cstTriviaLog.push(start, end, insertIdx)\n }\n}\n\nexport function pushTriviaLogEntry(\n ctx: ParseContext,\n start: number,\n end: number,\n kindIndex?: number,\n): void {\n if (!ctx._triviaLog) return\n if (ctx.triviaKindLabels !== undefined && kindIndex !== undefined) {\n ctx._triviaLog.push(start, end, kindIndex)\n } else {\n ctx._triviaLog.push(start, end)\n }\n}\n\nexport function cstDeferredTriviaActive(ctx: ParseContext): boolean {\n return ctx._triviaLog !== undefined || ctx._cstBuf !== undefined || ctx._cstTriviaLog !== undefined\n}\n", "import type { Combinator, FirstSet, ParseContext, ParseError, ParseResult, RecoveryHelpers } from '../types.ts'\nimport { cstCaptureActive, pushCstChild } from '../cst/capture-buffer.ts'\n\n/**\n * Recovery MECHANISM \u2014 sync-source-agnostic. These primitives implement \"scan\n * forward to a sync token and emit a ParseError\" and \"is the sync token here?\".\n * They are deliberately independent of WHERE the sync sentinel comes from: the\n * grammar never carries recovery config. Sync points are inferred automatically\n * from grammar structure (see `infer.ts`) and may be overridden externally by the\n * language service via `ctx._listSync`. This is the salvaged core of the old\n * `combinators/recover-scan.ts`.\n */\n\n/**\n * Run `sentinel` at `pos` as PURE lookahead: succeed/fail only, with every\n * side-effecting sink suppressed (CST leaves/children/trivia, the trivia log,\n * the recovery error sink, and the completions probe). A recovery scan probes the\n * sentinel at many positions and must not capture leaves, log trivia, or pollute\n * `completionsAt`'s furthest-failure set as a byproduct of looking ahead.\n */\nfunction probeAt(sentinel: Combinator<unknown>, input: string, pos: number, ctx: ParseContext): boolean {\n const s = {\n _probe: ctx._probe, _errors: ctx._errors,\n _cstLeaves: ctx._cstLeaves, _cstChildren: ctx._cstChildren, _cstRawChildren: ctx._cstRawChildren,\n _cstTriviaLog: ctx._cstTriviaLog, _triviaLog: ctx._triviaLog, _fields: ctx._fields, _cstBuf: ctx._cstBuf,\n _tolerant: ctx._tolerant, _sync: ctx._sync,\n }\n ctx._probe = undefined; ctx._errors = undefined\n ctx._cstLeaves = undefined; ctx._cstChildren = undefined; ctx._cstRawChildren = undefined\n ctx._cstTriviaLog = undefined; ctx._triviaLog = undefined; ctx._fields = undefined; ctx._cstBuf = undefined\n // A sentinel is pure lookahead: it must not itself enter tolerant recovery (nor\n // read a stale outer `_sync`). Otherwise a sentinel that composes many/sepBy would\n // run recovery on every probed position \u2014 wasted work, and it could resync to the\n // enclosing list's sync token. Cleared here and restored in `finally`.\n ctx._tolerant = undefined; ctx._sync = undefined\n try {\n return sentinel.parse(input, pos, ctx).ok\n } finally {\n ctx._probe = s._probe; ctx._errors = s._errors\n ctx._cstLeaves = s._cstLeaves; ctx._cstChildren = s._cstChildren; ctx._cstRawChildren = s._cstRawChildren\n ctx._cstTriviaLog = s._cstTriviaLog; ctx._triviaLog = s._triviaLog; ctx._fields = s._fields; ctx._cstBuf = s._cstBuf\n ctx._tolerant = s._tolerant; ctx._sync = s._sync\n }\n}\n\n/**\n * Combine two recovery sentinels into one that matches when either does \u2014 used by\n * tolerant `sepBy` to resync to its own separator OR the enclosing delimiter. Cold\n * path only; a bare wrapper so we don't pull `choice()` (and its analysis) in.\n */\nexport function orSentinel(\n a: Combinator<unknown>,\n b: Combinator<unknown> | undefined,\n): Combinator<unknown> {\n if (b === undefined) return a\n return {\n _tag: 'orSentinel',\n _meta: { firstSet: { kind: 'any' }, canMatchNewline: false, isTrivia: false },\n _def: { tag: 'unknown' },\n parse(input: string, pos: number, ctx: ParseContext): ParseResult<unknown> {\n const ra = a.parse(input, pos, ctx)\n if (ra.ok) return ra\n return b.parse(input, pos, ctx)\n },\n }\n}\n\n/**\n * A lightweight zero-width sentinel that \"matches\" (succeeds, consuming nothing)\n * when the input code point at `pos` starts the given first set. Sync inference\n * builds one from a list's follow set so a nested list can resync to the enclosing\n * delimiter with no grammar annotation. Returns `null` when the first set is `any`\n * or `empty` (no usable inference).\n */\nexport function firstSetSentinel(fs: FirstSet): Combinator<null> | null {\n if (fs.kind !== 'ranges' || fs.ranges.length === 0) return null\n const ranges = fs.ranges\n return {\n _tag: 'firstSetSentinel',\n _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },\n _def: { tag: 'unknown' },\n parse(input: string, pos: number): ParseResult<null> {\n const code = pos < input.length ? input.codePointAt(pos) : undefined\n if (code !== undefined) {\n for (const r of ranges) {\n if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } }\n }\n }\n return { ok: false, expected: [], span: { start: pos, end: pos } }\n },\n }\n}\n\n/**\n * Cold-path recovery scan shared by tolerant `many()` / `sepBy()`. Scans forward\n * from `from` until `sync` matches (or EOF) \u2014 probing the sentinel as pure\n * lookahead \u2014 emits a {@link ParseError} spanning the skipped range, and pushes it\n * to `ctx._errors`. The sync token is NOT consumed; the caller's loop resumes from\n * `end`. Only ever reached after an element has failed, so entirely off the hot path.\n *\n * The caller owns the loop guard: `sepBy` progress is driven by its separator (a\n * zero-width error for a missing element is fine); `many` checks `matchesAt(sync)`\n * itself and breaks when sitting on the terminator, so a zero-width failure can\n * never spin.\n */\nexport function recoverScan(\n input: string,\n from: number,\n ctx: ParseContext,\n sync: Combinator<unknown>,\n expected: string[],\n): { error: ParseError; end: number } {\n let scanPos = from\n while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++\n const error: ParseError = { _tag: 'parseError', span: { start: from, end: scanPos }, expected }\n ctx._errors?.push(error)\n return { error, end: scanPos }\n}\n\n/**\n * Does `sentinel` match at `pos` (pure lookahead)? Used by the tolerant list loops\n * to detect \"we are sitting on the sync/terminator token\" without consuming it or\n * capturing anything.\n */\nexport function matchesAt(\n sentinel: Combinator<unknown>,\n input: string,\n pos: number,\n ctx: ParseContext,\n): boolean {\n return probeAt(sentinel, input, pos, ctx)\n}\n\n/**\n * Embed a recovered error as a `parseError` CST child at the recovery point, so the\n * error lives IN the tree (rides reused subtrees across incremental edits; a tree\n * walk finds every diagnostic) \u2014 not only in the flat `ctx._errors` side-channel.\n * No-op when CST capture is off (value/AST mode). Both the interpreter list loops\n * and the compiled recovery branches call this (the latter via `_ctx._rec.capture`),\n * so the embedded node is identical on both paths.\n */\nexport function captureError(ctx: ParseContext, error: ParseError): void {\n if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error)\n}\n\n/**\n * The recovery-helper bundle handed to a tolerant parse via `ctx._rec`. A COMPILED\n * grammar can't import these, so the driver (`run`, `parseDoc`) injects them; the\n * interpreter list loops read the same bundle, guaranteeing byte-identical recovery\n * across paths. Shared here so every driver installs the exact same instance.\n */\nexport const REC: RecoveryHelpers = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError }\n", "import type { Combinator, ParseContext, ParseError, ParseResult } from '../types.ts'\nimport { REC } from '../recovery/scan.ts'\n\n/**\n * Run a compiled/interpreted grammar entry against an input and collect the raw\n * outcome a tool needs to shape a parse result \u2014 WITHOUT the consumer having to\n * hand-build a `ctx`, branch on function-vs-combinator, or scan for leftover\n * input itself.\n *\n * A `compile()`/macro grammar hands you a map of bare parse FUNCTIONS; the\n * interpreter hands you COMBINATORS. `run()` invokes either, threads the standard\n * framework ctx (trivia log, recover/expect error sink, the `ctx.build` host,\n * grammar state), and \u2014 given the grammar's trivia parser \u2014 reports where\n * non-trivia input was left unconsumed. The consumer keeps only its own policy:\n * how to shape the tree and how to turn diagnostics into its error type.\n */\n\n/** A compiled rule (macro/`compile()` output) OR an interpreter combinator. */\nexport type Runnable =\n | ((input: string, pos: number, ctx: ParseContext) => ParseResult<unknown>)\n | Combinator<unknown>\n\nexport type RunOptions = {\n /** Optional hooks for a coverage-enabled compiled or macro parser. Ordinary\n * parses omit this completely, so instrumentation has no normal-path cost. */\n instrumentation?: {\n _grammarCoverage?: (id: string) => void\n _grammarTrace?: { write(event: { id: string; phase: 'enter' | 'attempt' | 'selected' | 'success' | 'failure' | 'backtrack' | 'rollback'; offset: number; end?: number }): void }\n }\n /** `ctx.build` host \u2014 makes structural `node()` rules build a CST / AST via the\n * host instead of their own eval builders. Omit for a grammar's own builders. */\n build?: ParseContext['build']\n /** Initial grammar state threaded into `ctx.state`. */\n state?: unknown\n /**\n * The grammar's trivia rule. A root rule consumes trivia BETWEEN terms but not\n * after the last one, so trailing whitespace/comments would otherwise look like\n * unparsed input. Given the trivia rule, `run` skips that tail before computing\n * `unconsumedFrom` \u2014 so only real leftover is reported. Also encodes dialect\n * differences for free: pass the CSS trivia and a trailing `//` counts as\n * leftover; pass the Less trivia (which treats `//` as a line comment) and it\n * doesn't. An UNTERMINATED comment (which the trivia rule won't match) surfaces\n * at its start. Omit to require the parse to reach the exact end itself.\n */\n trivia?: Runnable\n /**\n * Restrict PER-NODE CST trivia capture (the `triviaLog` a node's builder sees)\n * to these trivia kinds \u2014 a bitmask over the grammar's `triviaKindLabels`\n * indices (build it with `triviaKindMask(labels, ['comment', \u2026])`). Unlisted\n * kinds (e.g. whitespace) are skipped over but not recorded per node, so a host\n * that only reads comments doesn't pay to log every whitespace run. The global\n * `triviaLog` returned by `run()` is unaffected. Omit to capture every kind.\n */\n triviaCaptureMask?: number\n /**\n * Activate automatic list recovery. When true, `many`/`sepBy`/`oneOrMore` recover\n * from a failed element \u2014 skip to a sync point (a resume token inferred from the\n * grammar's structure; the grammar carries no recovery config), emit a\n * `ParseError` over the skipped span (collected in `errors`), and keep parsing the\n * rest of the list \u2014 instead of stopping at the first bad element. Omit (the\n * default) for the strict \"one clean error and stop\" behavior, byte-identical to a\n * run with no recovery. Recovery is a cold path: on well-formed input nothing\n * fails, so none of the machinery runs.\n */\n tolerant?: boolean\n /**\n * Run three compiled-parser-only profiling passes: recognizer (no global\n * sinks), structural capture without the host, then the normal host path. This\n * is a measurement boundary, not a parser mode; ordinary `run()` output is\n * unchanged when omitted. The input is parsed THREE times \u2014 each pass gets its\n * own shallow copy of `options.state`, so keep per-parse state shallow (only\n * the top level is isolated between passes).\n */\n profile?: boolean\n}\n\nexport type RunProfilePass = {\n ms: number\n nodes: number\n childSlots: number\n rawSlots: number\n triviaSlots: number\n fieldSlots: number\n hostCalls: number\n}\n\nexport type RunProfile = {\n /** Existing `voidOf(transform(..., () => undefined))` semantics, generalized\n * to compiled structural nodes: no `ch`/`raw`/`tl` capture or raw entries. */\n recognizer: RunProfilePass\n /** Captures children/raw/trivia/fields but suppresses node construction. */\n structuralCapture: RunProfilePass\n /** The ordinary parser path, including its injected build host. */\n hostConstruction: RunProfilePass\n}\n\nexport type RunResult = {\n ok: boolean\n /** The entry's value on success; undefined on failure. */\n value: unknown\n span: { start: number; end: number }\n /** Expected-token set when the TOP-LEVEL parse failed (empty on success). */\n expected: string[]\n /** Recovery diagnostics (tolerant lists / `expect()`) collected during the parse (in order). */\n errors: ParseError[]\n /**\n * Flat trivia log for building a trivia map. Entry width depends on whether the\n * grammar uses labeled trivia: 2 numbers per entry (`start, end`) for plain\n * trivia, 3 numbers per entry (`start, end, kindIndex`) for labeled trivia\n * (grammars with `label()` arms in their trivia choice). Use `buildTriviaIndex`\n * / `triviaEntries` to consume it format-agnostically rather than iterating raw.\n */\n triviaLog: number[]\n /** Offset where unparsed input begins \u2014 the first non-trivia character the parse\n * left unconsumed (trailing trivia skipped when `trivia` is given), or null if\n * the whole input was consumed. This is how you detect \"the grammar stopped short,\n * there's junk here\". Only meaningful on success \u2014 a failed parse reports its own\n * `span`/`expected`. */\n unconsumedFrom: number | null\n /** Present only when `RunOptions.profile` is true. */\n profile?: RunProfile\n}\n\nconst invoke = (r: Runnable, input: string, pos: number, ctx: ParseContext): ParseResult<unknown> =>\n typeof r === 'function' ? r(input, pos, ctx) : r.parse(input, pos, ctx)\n\ntype ProfilePhase = NonNullable<ParseContext['_pmProfile']>['phase']\ntype ProfileState = NonNullable<ParseContext['_pmProfile']>\n\nfunction runOnce(entry: Runnable, input: string, options: RunOptions, phase?: ProfilePhase, profileState?: ProfileState): RunResult {\n if (typeof entry !== 'function' && typeof (entry as Combinator<unknown> | undefined)?.parse !== 'function') {\n throw new TypeError(\n `run(): start production is ${entry === null ? 'null' : typeof entry}, not a rule \u2014 the requested grammar rule does not exist (check the rule name).`,\n )\n }\n const triviaLog: number[] = []\n const errors: ParseError[] = []\n const profile = profileState ?? (phase === undefined\n ? undefined\n : { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 })\n // NOT \"no output\" \u2014 the `capture` phase still allocates per-node children/raw/\n // trivia buffers and records slot counts. This flag only omits the two GLOBAL\n // sinks (`_triviaLog`, `_errors`) from the context; hence `skipGlobalSinks`.\n const skipGlobalSinks = phase === 'recognizer' || phase === 'capture'\n // Grammar-level ambient trivia declared via rules({ trivia }, factory): install\n // it as ctx.trivia so it's ambient for the whole parse (the interpreter path;\n // a compiled entry has it baked in and carries no _meta). parser/noTrivia still\n // override locally.\n const grammarTrivia = typeof entry !== 'function' ? entry._meta.grammarTrivia : undefined\n const grammarScanSkip = typeof entry !== 'function' ? entry._meta.grammarScanSkip : undefined\n const ctx: ParseContext = {\n trackLines: false,\n ...(skipGlobalSinks\n ? { _pmProfile: profile! }\n : { _triviaLog: triviaLog, _errors: errors, ...(profile === undefined ? {} : { _pmProfile: profile }) }),\n build: options.build,\n state: options.state,\n ...(grammarTrivia !== undefined\n ? { trivia: grammarTrivia, ...(grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}) }\n : {}),\n ...(grammarScanSkip !== undefined ? { scanSkip: grammarScanSkip } : {}),\n ...(options.triviaCaptureMask !== undefined ? { _triviaCaptureMask: options.triviaCaptureMask } : {}),\n ...(options.tolerant ? { _tolerant: true, _rec: REC } : {}),\n ...(options.instrumentation === undefined ? {} : options.instrumentation),\n }\n const r = invoke(entry, input, 0, ctx)\n\n let unconsumedFrom: number | null = null\n if (r.ok) {\n let pos = r.span?.end ?? 0\n if (options.trivia && pos < input.length) {\n // Throwaway ctx: trailing trivia must NOT pollute the parse's trivia log.\n const t = invoke(options.trivia, input, pos, { trackLines: false })\n if (t.ok && t.span.end > pos) pos = t.span.end\n }\n unconsumedFrom = pos < input.length ? pos : null\n }\n\n return {\n ok: r.ok,\n value: r.ok ? (r as { value: unknown }).value : undefined,\n span: r.span ?? { start: 0, end: 0 },\n expected: r.ok ? [] : ((r as { expected?: string[] }).expected ?? []),\n errors,\n triviaLog,\n unconsumedFrom,\n }\n}\n\n/** Shallow copy of `options.state` per pass so an in-place mutation by one\n * profiling pass doesn't leak into the next (the parity check only compares\n * `ok`/`unconsumedFrom`, so a diverged structural output would go unnoticed).\n * Only the top level is isolated \u2014 deeply-nested mutable state is shared; a\n * profiled grammar should keep per-parse state shallow (see the `profile` docs). */\nfunction clonePassState(state: unknown): unknown {\n if (state === null || typeof state !== 'object') return state\n return Array.isArray(state) ? [...state] : { ...(state as Record<string, unknown>) }\n}\n\nfunction profilePass(entry: Runnable, input: string, options: RunOptions, phase: ProfilePhase): { result: RunResult; profile: RunProfilePass } {\n const state: ProfileState = { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 }\n const passOptions: RunOptions = options.state === undefined ? options : { ...options, state: clonePassState(options.state) }\n const start = performance.now()\n const result = runOnce(entry, input, passOptions, phase, state)\n const { phase: _phase, ...counts } = state\n return { result, profile: { ms: performance.now() - start, ...counts } }\n}\n\nexport function run(entry: Runnable, input: string, options: RunOptions = {}): RunResult {\n if (!options.profile) return runOnce(entry, input, options)\n if (typeof entry !== 'function') {\n throw new TypeError('run({ profile: true }) requires a compiled parser entry')\n }\n\n const recognizer = profilePass(entry, input, options, 'recognizer')\n const capture = profilePass(entry, input, options, 'capture')\n const host = profilePass(entry, input, options, 'host')\n if (recognizer.result.ok !== host.result.ok || capture.result.ok !== host.result.ok\n || recognizer.result.unconsumedFrom !== host.result.unconsumedFrom\n || capture.result.unconsumedFrom !== host.result.unconsumedFrom) {\n throw new Error('run({ profile: true }) changed recognition; the grammar is not profile-safe')\n }\n return {\n ...host.result,\n profile: {\n recognizer: recognizer.profile,\n structuralCapture: capture.profile,\n hostConstruction: host.profile,\n },\n }\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../../src/cst/capture-buffer.ts", "../../src/recovery/scan.ts", "../../src/cst/host-mode.ts", "../../src/functional/run.ts"],
4
+ "sourcesContent": ["import type { ParseContext } from '../types.ts'\n\n/** Lazy per-node capture state \u2014 arrays materialized on first push. */\nexport type CstCaptureBuf = {\n single?: unknown | undefined\n ch?: unknown[] | undefined\n rawSingle?: unknown | undefined\n raw?: unknown[] | undefined\n tl?: number[] | undefined\n}\n\nconst EMPTY: unknown[] = []\nconst EMPTY_TL: number[] = []\n\nexport function cstCaptureActive(ctx: ParseContext): boolean {\n return ctx._cstBuf !== undefined || ctx._cstLeaves !== undefined\n}\n\nexport type CstNodeCaptureSaved = {\n ch: ParseContext['_cstChildren']\n lv: ParseContext['_cstLeaves']\n raw: ParseContext['_cstRawChildren']\n tl: ParseContext['_cstTriviaLog']\n cap: boolean | undefined\n buf: CstCaptureBuf | undefined\n}\n\nexport function beginCstNodeCapture(ctx: ParseContext): CstNodeCaptureSaved {\n const saved: CstNodeCaptureSaved = {\n ch: ctx._cstChildren,\n lv: ctx._cstLeaves,\n raw: ctx._cstRawChildren,\n tl: ctx._cstTriviaLog,\n cap: ctx.captureTrivia,\n buf: ctx._cstBuf,\n }\n ctx._cstBuf = {}\n ctx._cstChildren = undefined\n ctx._cstLeaves = undefined\n ctx._cstRawChildren = undefined\n ctx._cstTriviaLog = undefined\n ctx.captureTrivia = true\n return saved\n}\n\nexport function finishCstBuf(buf: CstCaptureBuf | undefined): {\n children: unknown[]\n rawChildren: unknown[]\n triviaLog: number[]\n} {\n if (!buf) return { children: EMPTY, rawChildren: EMPTY, triviaLog: EMPTY_TL }\n const children = buf.ch ?? (buf.single !== undefined ? [buf.single] : EMPTY)\n const rawChildren = buf.raw ?? (buf.rawSingle !== undefined ? [buf.rawSingle] : EMPTY)\n const triviaLog = buf.tl ?? EMPTY_TL\n return { children, rawChildren, triviaLog }\n}\n\nexport function endCstNodeCapture(\n ctx: ParseContext,\n saved: CstNodeCaptureSaved,\n): { children: unknown[]; rawChildren: unknown[]; triviaLog: number[] } {\n const materialized = finishCstBuf(ctx._cstBuf)\n ctx._cstBuf = saved.buf\n ctx._cstChildren = saved.ch\n ctx._cstLeaves = saved.lv\n ctx._cstRawChildren = saved.raw\n ctx._cstTriviaLog = saved.tl\n ctx.captureTrivia = saved.cap\n return materialized\n}\n\nexport function pushCstLeaf(ctx: ParseContext, leaf: unknown): void {\n pushCstChild(ctx, leaf, leaf)\n}\n\n/** Record a built sub-node into the active collector (children vs rawChildren may differ). */\nexport function pushCstChild(ctx: ParseContext, built: unknown, rawEntry: unknown): void {\n const b = ctx._cstBuf\n if (b) {\n if (b.ch) b.ch.push(built)\n else if (b.single !== undefined) { b.ch = [b.single, built]; b.single = undefined }\n else b.single = built\n\n if (b.raw) b.raw.push(rawEntry)\n else if (b.rawSingle !== undefined) { b.raw = [b.rawSingle, rawEntry]; b.rawSingle = undefined }\n else b.rawSingle = rawEntry\n return\n }\n if (ctx._cstChildren) ctx._cstChildren.push(built)\n else if (ctx._cstLeaves) ctx._cstLeaves.push(built)\n if (ctx._cstRawChildren) ctx._cstRawChildren.push(rawEntry)\n}\n\nexport function cstRawLen(ctx: ParseContext): number {\n const b = ctx._cstBuf\n if (b) {\n if (b.raw) return b.raw.length\n return b.rawSingle !== undefined ? 1 : 0\n }\n return ctx._cstRawChildren?.length ?? 0\n}\n\nexport function cstLeavesLen(ctx: ParseContext): number {\n const b = ctx._cstBuf\n if (b) {\n if (b.ch) return b.ch.length\n return b.single !== undefined ? 1 : 0\n }\n return ctx._cstLeaves?.length ?? 0\n}\n\nexport function cstTlLen(ctx: ParseContext): number {\n const b = ctx._cstBuf\n if (b) return b.tl?.length ?? 0\n return ctx._cstTriviaLog?.length ?? 0\n}\n\nexport type CstRollbackMark = { raw: number; tlog: number; leaves: number; fields: number; errors: number }\n\nexport function saveCstMark(ctx: ParseContext): CstRollbackMark {\n return {\n raw: cstRawLen(ctx),\n tlog: cstTlLen(ctx),\n leaves: cstLeavesLen(ctx),\n fields: ctx._fields?.length ?? 0,\n // Speculative rollback must truncate the flat recovery-error sink in lockstep\n // with the CST leaves: a branch that recovered (pushing a ParseError to\n // ctx._errors AND embedding it as a CST child via captureError) then failed\n // must remove BOTH, or the flat error survives as a GHOST that a tree walk\n // can't see (flat run().errors would disagree with the embedded set).\n errors: ctx._errors?.length ?? 0,\n }\n}\n\nfunction rollbackBufList(\n b: CstCaptureBuf,\n keyMulti: 'ch' | 'raw',\n keySingle: 'single' | 'rawSingle',\n len: number,\n): void {\n const arr = b[keyMulti]\n if (arr) {\n if (len === 0) b[keyMulti] = undefined\n else if (len === 1) { b[keySingle] = arr[0]; b[keyMulti] = undefined }\n // Guarded like every other truncation \u2014 see rollbackCstCapture. This is the\n // buffered node-capture path, so it is reached only by the INTERPRETER (the\n // compiled engine inlines its own capture and never calls here), and it is\n // where the redundant-store rate is highest: over a backtracking fixture\n // this branch ran 32,800 times and 31,198 of those \u2014 95% \u2014 restored a\n // length that had not moved.\n else if (arr.length !== len) arr.length = len\n return\n }\n if (len === 0) b[keySingle] = undefined\n}\n\nexport function rollbackCstCapture(ctx: ParseContext, mark: CstRollbackMark): void {\n // Truncate the flat recovery-error sink alongside the CST, so a rolled-back\n // speculative branch leaves no ghost error (see saveCstMark). Guarded on a\n // present sink + a defined mark (rollbackTrivia forwards the same field).\n //\n // Each truncation is guarded on `length !== mark`, mirroring the compiled\n // engine (codegen.ts `captureRestoreBody`). Assigning `array.length` runs V8's\n // length setter \u2014 a backing-store trim decision \u2014 and costs the same when the\n // value is unchanged, which is the overwhelmingly common case here: a\n // speculative branch that captured nothing still pays for every store. The\n // compare is an in-object load. Both engines must carry the guard or the\n // interpreted path silently keeps the cost the compiled path shed.\n if (ctx._errors && mark.errors !== undefined && ctx._errors.length !== mark.errors) ctx._errors.length = mark.errors\n const b = ctx._cstBuf\n if (b) {\n rollbackBufList(b, 'raw', 'rawSingle', mark.raw)\n rollbackBufList(b, 'ch', 'single', mark.leaves)\n if (b.tl) {\n if (mark.tlog === 0) b.tl = undefined\n else if (b.tl.length !== mark.tlog) b.tl.length = mark.tlog\n }\n if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields\n return\n }\n if (ctx._cstRawChildren && ctx._cstRawChildren.length !== mark.raw) ctx._cstRawChildren.length = mark.raw\n if (ctx._cstTriviaLog && ctx._cstTriviaLog.length !== mark.tlog) ctx._cstTriviaLog.length = mark.tlog\n if (ctx._cstLeaves && ctx._cstLeaves.length !== mark.leaves) ctx._cstLeaves.length = mark.leaves\n if (ctx._fields && ctx._fields.length !== mark.fields) ctx._fields.length = mark.fields\n}\n\nexport function pushCstTriviaEntry(\n ctx: ParseContext,\n start: number,\n end: number,\n kindIndex?: number,\n): void {\n const insertIdx = cstRawLen(ctx)\n const b = ctx._cstBuf\n const withKind = ctx.triviaKindLabels !== undefined && kindIndex !== undefined\n if (b) {\n if (!b.tl) {\n b.tl = withKind ? [start, end, insertIdx, kindIndex] : [start, end, insertIdx]\n } else if (withKind) {\n b.tl.push(start, end, insertIdx, kindIndex)\n } else {\n b.tl.push(start, end, insertIdx)\n }\n return\n }\n if (ctx._cstTriviaLog) {\n if (withKind) ctx._cstTriviaLog.push(start, end, insertIdx, kindIndex)\n else ctx._cstTriviaLog.push(start, end, insertIdx)\n }\n}\n\nexport function pushTriviaLogEntry(\n ctx: ParseContext,\n start: number,\n end: number,\n kindIndex?: number,\n): void {\n if (!ctx._triviaLog) return\n if (ctx.triviaKindLabels !== undefined && kindIndex !== undefined) {\n ctx._triviaLog.push(start, end, kindIndex)\n } else {\n ctx._triviaLog.push(start, end)\n }\n}\n\nexport function cstDeferredTriviaActive(ctx: ParseContext): boolean {\n return ctx._triviaLog !== undefined || ctx._cstBuf !== undefined || ctx._cstTriviaLog !== undefined\n}\n", "import type { Combinator, FirstSet, ParseContext, ParseError, ParseResult, RecoveryHelpers } from '../types.ts'\nimport { cstCaptureActive, pushCstChild } from '../cst/capture-buffer.ts'\n\n/**\n * Recovery MECHANISM \u2014 sync-source-agnostic. These primitives implement \"scan\n * forward to a sync token and emit a ParseError\" and \"is the sync token here?\".\n * They are deliberately independent of WHERE the sync sentinel comes from: the\n * grammar never carries recovery config. Sync points are inferred automatically\n * from grammar structure (see `infer.ts`) and may be overridden externally by the\n * language service via `ctx._listSync`. This is the salvaged core of the old\n * `combinators/recover-scan.ts`.\n */\n\n/**\n * Run `sentinel` at `pos` as PURE lookahead: succeed/fail only, with every\n * side-effecting sink suppressed (CST leaves/children/trivia, the trivia log,\n * the recovery error sink, and the completions probe). A recovery scan probes the\n * sentinel at many positions and must not capture leaves, log trivia, or pollute\n * `completionsAt`'s furthest-failure set as a byproduct of looking ahead.\n */\nfunction probeAt(sentinel: Combinator<unknown>, input: string, pos: number, ctx: ParseContext): boolean {\n const s = {\n _probe: ctx._probe, _errors: ctx._errors,\n _cstLeaves: ctx._cstLeaves, _cstChildren: ctx._cstChildren, _cstRawChildren: ctx._cstRawChildren,\n _cstTriviaLog: ctx._cstTriviaLog, _triviaLog: ctx._triviaLog, _fields: ctx._fields, _cstBuf: ctx._cstBuf,\n _tolerant: ctx._tolerant, _sync: ctx._sync,\n }\n ctx._probe = undefined; ctx._errors = undefined\n ctx._cstLeaves = undefined; ctx._cstChildren = undefined; ctx._cstRawChildren = undefined\n ctx._cstTriviaLog = undefined; ctx._triviaLog = undefined; ctx._fields = undefined; ctx._cstBuf = undefined\n // A sentinel is pure lookahead: it must not itself enter tolerant recovery (nor\n // read a stale outer `_sync`). Otherwise a sentinel that composes many/sepBy would\n // run recovery on every probed position \u2014 wasted work, and it could resync to the\n // enclosing list's sync token. Cleared here and restored in `finally`.\n ctx._tolerant = undefined; ctx._sync = undefined\n try {\n return sentinel.parse(input, pos, ctx).ok\n } finally {\n ctx._probe = s._probe; ctx._errors = s._errors\n ctx._cstLeaves = s._cstLeaves; ctx._cstChildren = s._cstChildren; ctx._cstRawChildren = s._cstRawChildren\n ctx._cstTriviaLog = s._cstTriviaLog; ctx._triviaLog = s._triviaLog; ctx._fields = s._fields; ctx._cstBuf = s._cstBuf\n ctx._tolerant = s._tolerant; ctx._sync = s._sync\n }\n}\n\n/**\n * Combine two recovery sentinels into one that matches when either does \u2014 used by\n * tolerant `sepBy` to resync to its own separator OR the enclosing delimiter. Cold\n * path only; a bare wrapper so we don't pull `choice()` (and its analysis) in.\n */\nexport function orSentinel(\n a: Combinator<unknown>,\n b: Combinator<unknown> | undefined,\n): Combinator<unknown> {\n if (b === undefined) return a\n return {\n _tag: 'orSentinel',\n _meta: { firstSet: { kind: 'any' }, canMatchNewline: false, isTrivia: false },\n _def: { tag: 'unknown' },\n parse(input: string, pos: number, ctx: ParseContext): ParseResult<unknown> {\n const ra = a.parse(input, pos, ctx)\n if (ra.ok) return ra\n return b.parse(input, pos, ctx)\n },\n }\n}\n\n/**\n * A lightweight zero-width sentinel that \"matches\" (succeeds, consuming nothing)\n * when the input code point at `pos` starts the given first set. Sync inference\n * builds one from a list's follow set so a nested list can resync to the enclosing\n * delimiter with no grammar annotation. Returns `null` when the first set is `any`\n * or `empty` (no usable inference).\n */\nexport function firstSetSentinel(fs: FirstSet): Combinator<null> | null {\n if (fs.kind !== 'ranges' || fs.ranges.length === 0) return null\n const ranges = fs.ranges\n return {\n _tag: 'firstSetSentinel',\n _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },\n _def: { tag: 'unknown' },\n parse(input: string, pos: number): ParseResult<null> {\n const code = pos < input.length ? input.codePointAt(pos) : undefined\n if (code !== undefined) {\n for (const r of ranges) {\n if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } }\n }\n }\n return { ok: false, expected: [], span: { start: pos, end: pos } }\n },\n }\n}\n\n/**\n * Cold-path recovery scan shared by tolerant `many()` / `sepBy()`. Scans forward\n * from `from` until `sync` matches (or EOF) \u2014 probing the sentinel as pure\n * lookahead \u2014 emits a {@link ParseError} spanning the skipped range, and pushes it\n * to `ctx._errors`. The sync token is NOT consumed; the caller's loop resumes from\n * `end`. Only ever reached after an element has failed, so entirely off the hot path.\n *\n * The caller owns the loop guard: `sepBy` progress is driven by its separator (a\n * zero-width error for a missing element is fine); `many` checks `matchesAt(sync)`\n * itself and breaks when sitting on the terminator, so a zero-width failure can\n * never spin.\n */\nexport function recoverScan(\n input: string,\n from: number,\n ctx: ParseContext,\n sync: Combinator<unknown>,\n expected: string[],\n): { error: ParseError; end: number } {\n let scanPos = from\n while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++\n const error: ParseError = { _tag: 'parseError', span: { start: from, end: scanPos }, expected }\n ctx._errors?.push(error)\n return { error, end: scanPos }\n}\n\n/**\n * Does `sentinel` match at `pos` (pure lookahead)? Used by the tolerant list loops\n * to detect \"we are sitting on the sync/terminator token\" without consuming it or\n * capturing anything.\n */\nexport function matchesAt(\n sentinel: Combinator<unknown>,\n input: string,\n pos: number,\n ctx: ParseContext,\n): boolean {\n return probeAt(sentinel, input, pos, ctx)\n}\n\n/**\n * Embed a recovered error as a `parseError` CST child at the recovery point, so the\n * error lives IN the tree (rides reused subtrees across incremental edits; a tree\n * walk finds every diagnostic) \u2014 not only in the flat `ctx._errors` side-channel.\n * No-op when CST capture is off (value/AST mode). Both the interpreter list loops\n * and the compiled recovery branches call this (the latter via `_ctx._rec.capture`),\n * so the embedded node is identical on both paths.\n */\nexport function captureError(ctx: ParseContext, error: ParseError): void {\n if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error)\n}\n\n/**\n * The recovery-helper bundle handed to a tolerant parse via `ctx._rec`. A COMPILED\n * grammar can't import these, so the driver (`run`, `parseDoc`) injects them; the\n * interpreter list loops read the same bundle, guaranteeing byte-identical recovery\n * across paths. Shared here so every driver installs the exact same instance.\n */\nexport const REC: RecoveryHelpers = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError }\n", "/**\n * The compiled-artifact \u2194 host contract, in ONE place.\n *\n * This lives on its own \u2014 rather than in `compiler/codegen.ts` where it started \u2014\n * because the DRIVER has to enforce it too. `functional/run.ts` is the `parseman/run`\n * entry, whose whole purpose is to stay small (see `test/unit/run-entry-closure.test.ts`),\n * so it cannot import the compiler to reach an assertion. Neither can it carry its own\n * copy: two spellings of one contract is how the two engines drift, and this contract\n * exists precisely because the failure it prevents is silent.\n *\n * No imports, by design \u2014 that is what keeps it free for both sides to depend on.\n */\n\n/**\n * Which consumer a compiled artifact was lowered FOR.\n *\n * - `'ast'` \u2014 the grammar's own `build` callbacks produce the result (the eval consumer).\n * - `'cst'` \u2014 a positioned-CST `ctx.build` host produces it (linter / IDE / language\n * service), and every node's capture is sized for that host.\n *\n * ONE grammar source serves both; they are two COMPILATIONS of it, not two grammars.\n * Deciding at compile time is what keeps the eval-AST artifact free of per-node host\n * probing \u2014 see the `hostMode` note on the codegen Ctx.\n */\nexport type HostMode = 'ast' | 'cst'\n\n/** Host mode a fused rule map (and each of its rule functions) was lowered for;\n * absent on hand-built maps \u2192 'ast'. */\nexport const FUSED_HOST_MODE = Symbol.for('parseman.fusedHostMode')\n\n/** Whether any fused piece omitted a direct builder's positioned-CST branch. */\nexport const FUSED_HOST_ELIDED = Symbol.for('parseman.fusedHostElided')\n\n/**\n * A compiled artifact and the host it is driven with must agree \u2014 and when they do not,\n * that has to be an ERROR, never a quietly-degraded tree.\n *\n * An `'ast'` artifact does not emit the positioned-CST branch at all, so attaching a\n * `_parsemanCstOutput` host to one would silently get the grammar's own AST objects\n * where the caller asked for a CST. A `'cst'` artifact builds every node through the\n * host, so running one without a host would call `undefined`. Both are compile/run\n * mismatches with an obvious fix, so both say so.\n *\n * Called once per parse from the TypeScript entry points, never from generated code.\n */\nexport function assertHostModeCompatible(mode: HostMode, build: unknown, hostBranchElided = true): void {\n const isCstHost = (build as { _parsemanCstOutput?: true } | undefined)?._parsemanCstOutput === true\n // A purely STRUCTURAL artifact never had a direct-builder host branch to drop, so it\n // serves a CST host in either mode \u2014 that is the long-standing `node(parser)` contract\n // and this change does not touch it. The INTERPRETER passes `false` for the same\n // reason from the other direction: it re-decides per parse and never elides anything.\n if (mode === 'ast' && isCstHost && hostBranchElided) {\n throw new Error(\n 'parseman: this parser was compiled for host mode \"ast\" (the default), so its nodes '\n + 'build through their own `build` callbacks and no positioned-CST branch was emitted. '\n + 'It cannot be driven with a positioned-CST host (cstBuildHost / a language-service '\n + 'host). Compile a second artifact from the SAME grammar with '\n + \"`compile(grammar, { hostMode: 'cst' })` \u2014 or, under the macro, a second \"\n + \"`rules({ hostMode: 'cst' }, factory)` call site \u2014 and drive that one instead.\",\n )\n }\n if (mode === 'cst' && !isCstHost) {\n throw new Error(\n 'parseman: this parser was compiled for host mode \"cst\", so every node builds through '\n + 'a positioned-CST `ctx.build` host. Pass one (e.g. `{ build: cstBuildHost }`), or use '\n + 'an artifact compiled with the default `hostMode: \"ast\"` to get the grammar\\'s own AST.',\n )\n }\n}\n", "import type { Combinator, ParseContext, ParseError, ParseResult } from '../types.ts'\nimport { REC } from '../recovery/scan.ts'\n/* `cst/host-mode.ts` is import-free by design, so this keeps the `parseman/run`\n * closure minimal \u2014 see test/unit/run-entry-closure.test.ts. */\nimport { assertHostModeCompatible, FUSED_HOST_MODE, FUSED_HOST_ELIDED, type HostMode } from '../cst/host-mode.ts'\n\n/**\n * Run a compiled/interpreted grammar entry against an input and collect the raw\n * outcome a tool needs to shape a parse result \u2014 WITHOUT the consumer having to\n * hand-build a `ctx`, branch on function-vs-combinator, or scan for leftover\n * input itself.\n *\n * A `compile()`/macro grammar hands you a map of bare parse FUNCTIONS; the\n * interpreter hands you COMBINATORS. `run()` invokes either, threads the standard\n * framework ctx (trivia log, recover/expect error sink, the `ctx.build` host,\n * grammar state), and \u2014 given the grammar's trivia parser \u2014 reports where\n * non-trivia input was left unconsumed. The consumer keeps only its own policy:\n * how to shape the tree and how to turn diagnostics into its error type.\n */\n\n/** A compiled rule (macro/`compile()` output) OR an interpreter combinator. */\nexport type Runnable =\n | ((input: string, pos: number, ctx: ParseContext) => ParseResult<unknown>)\n | Combinator<unknown>\n\nexport type RunOptions = {\n /** Optional hooks for a coverage-enabled compiled or macro parser. Ordinary\n * parses omit this completely, so instrumentation has no normal-path cost. */\n instrumentation?: {\n _grammarCoverage?: (id: string) => void\n _grammarTrace?: { write(event: { id: string; phase: 'enter' | 'attempt' | 'selected' | 'success' | 'failure' | 'backtrack' | 'rollback'; offset: number; end?: number }): void }\n }\n /** `ctx.build` host \u2014 makes structural `node()` rules build a CST / AST via the\n * host instead of their own eval builders. Omit for a grammar's own builders. */\n build?: ParseContext['build']\n /** Initial grammar state threaded into `ctx.state`. */\n state?: unknown\n /**\n * The grammar's trivia rule. A root rule consumes trivia BETWEEN terms but not\n * after the last one, so trailing whitespace/comments would otherwise look like\n * unparsed input. Given the trivia rule, `run` skips that tail before computing\n * `unconsumedFrom` \u2014 so only real leftover is reported. Also encodes dialect\n * differences for free: pass the CSS trivia and a trailing `//` counts as\n * leftover; pass the Less trivia (which treats `//` as a line comment) and it\n * doesn't. An UNTERMINATED comment (which the trivia rule won't match) surfaces\n * at its start. Omit to require the parse to reach the exact end itself.\n */\n trivia?: Runnable\n /**\n * Restrict PER-NODE CST trivia capture (the `triviaLog` a node's builder sees)\n * to these trivia kinds \u2014 a bitmask over the grammar's `triviaKindLabels`\n * indices (build it with `triviaKindMask(labels, ['comment', \u2026])`). Unlisted\n * kinds (e.g. whitespace) are skipped over but not recorded per node, so a host\n * that only reads comments doesn't pay to log every whitespace run. The global\n * `triviaLog` returned by `run()` is unaffected. Omit to capture every kind.\n */\n triviaCaptureMask?: number\n /**\n * Activate automatic list recovery. When true, `many`/`sepBy`/`oneOrMore` recover\n * from a failed element \u2014 skip to a sync point (a resume token inferred from the\n * grammar's structure; the grammar carries no recovery config), emit a\n * `ParseError` over the skipped span (collected in `errors`), and keep parsing the\n * rest of the list \u2014 instead of stopping at the first bad element. Omit (the\n * default) for the strict \"one clean error and stop\" behavior, byte-identical to a\n * run with no recovery. Recovery is a cold path: on well-formed input nothing\n * fails, so none of the machinery runs.\n */\n tolerant?: boolean\n /**\n * Run three compiled-parser-only profiling passes: recognizer (no global\n * sinks), structural capture without the host, then the normal host path. This\n * is a measurement boundary, not a parser mode; ordinary `run()` output is\n * unchanged when omitted. The input is parsed THREE times \u2014 each pass gets its\n * own shallow copy of `options.state`, so keep per-parse state shallow (only\n * the top level is isolated between passes).\n */\n profile?: boolean\n}\n\nexport type RunProfilePass = {\n ms: number\n nodes: number\n childSlots: number\n rawSlots: number\n triviaSlots: number\n fieldSlots: number\n hostCalls: number\n}\n\nexport type RunProfile = {\n /** Existing `voidOf(transform(..., () => undefined))` semantics, generalized\n * to compiled structural nodes: no `ch`/`raw`/`tl` capture or raw entries. */\n recognizer: RunProfilePass\n /** Captures children/raw/trivia/fields but suppresses node construction. */\n structuralCapture: RunProfilePass\n /** The ordinary parser path, including its injected build host. */\n hostConstruction: RunProfilePass\n}\n\nexport type RunResult = {\n ok: boolean\n /** The entry's value on success; undefined on failure. */\n value: unknown\n span: { start: number; end: number }\n /** Expected-token set when the TOP-LEVEL parse failed (empty on success). */\n expected: string[]\n /** Recovery diagnostics (tolerant lists / `expect()`) collected during the parse (in order). */\n errors: ParseError[]\n /**\n * Flat trivia log for building a trivia map. Entry width depends on whether the\n * grammar uses labeled trivia: 2 numbers per entry (`start, end`) for plain\n * trivia, 3 numbers per entry (`start, end, kindIndex`) for labeled trivia\n * (grammars with `label()` arms in their trivia choice). Use `buildTriviaIndex`\n * / `triviaEntries` to consume it format-agnostically rather than iterating raw.\n */\n triviaLog: number[]\n /** Offset where unparsed input begins \u2014 the first non-trivia character the parse\n * left unconsumed (trailing trivia skipped when `trivia` is given), or null if\n * the whole input was consumed. This is how you detect \"the grammar stopped short,\n * there's junk here\". Only meaningful on success \u2014 a failed parse reports its own\n * `span`/`expected`. */\n unconsumedFrom: number | null\n /** Present only when `RunOptions.profile` is true. */\n profile?: RunProfile\n}\n\nconst invoke = (r: Runnable, input: string, pos: number, ctx: ParseContext): ParseResult<unknown> =>\n typeof r === 'function' ? r(input, pos, ctx) : r.parse(input, pos, ctx)\n\ntype ProfilePhase = NonNullable<ParseContext['_pmProfile']>['phase']\ntype ProfileState = NonNullable<ParseContext['_pmProfile']>\n\nfunction runOnce(entry: Runnable, input: string, options: RunOptions, phase?: ProfilePhase, profileState?: ProfileState): RunResult {\n if (typeof entry !== 'function' && typeof (entry as Combinator<unknown> | undefined)?.parse !== 'function') {\n throw new TypeError(\n `run(): start production is ${entry === null ? 'null' : typeof entry}, not a rule \u2014 the requested grammar rule does not exist (check the rule name).`,\n )\n }\n const triviaLog: number[] = []\n const errors: ParseError[] = []\n const profile = profileState ?? (phase === undefined\n ? undefined\n : { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 })\n // NOT \"no output\" \u2014 the `capture` phase still allocates per-node children/raw/\n // trivia buffers and records slot counts. This flag only omits the two GLOBAL\n // sinks (`_triviaLog`, `_errors`) from the context; hence `skipGlobalSinks`.\n const skipGlobalSinks = phase === 'recognizer' || phase === 'capture'\n // Grammar-level ambient trivia declared via rules({ trivia }, factory): install\n // it as ctx.trivia so it's ambient for the whole parse (the interpreter path;\n // a compiled entry has it baked in and carries no _meta). parser/noTrivia still\n // override locally.\n const grammarTrivia = typeof entry !== 'function' ? entry._meta.grammarTrivia : undefined\n const grammarScanSkip = typeof entry !== 'function' ? entry._meta.grammarScanSkip : undefined\n /*\n * Refuse an artifact/host mismatch ONCE per parse, exactly as `parseDoc` and a\n * compiled parser's `parseWithContext` already do. `run()` is handed a RULE, not the\n * registry, so the fused rule functions carry the stamp themselves (see `fusedBody`);\n * an interpreter entry carries it on `_meta`. Neither is on a hot path \u2014 this runs\n * once, before `invoke`.\n *\n * Without this, `run()` was the one driver that could silently produce the wrong tree\n * shape: an 'ast' artifact driven with a positioned-CST host returns its own AST\n * objects where the caller asked for a CST, and a CST child filter then drops them\n * and the node simply vanishes.\n *\n * The INTERPRETER passes `elided: false` unconditionally, and that is not a shortcut:\n * it has no compile step, re-decides the host route per parse, and so has never\n * dropped a branch. Only its `'cst'`-without-a-host half can be wrong.\n */\n const stamped = entry as Partial<Record<symbol, unknown>>\n assertHostModeCompatible(\n typeof entry === 'function'\n ? ((stamped[FUSED_HOST_MODE] as HostMode | undefined) ?? 'ast')\n : (entry._meta.grammarHostMode ?? 'ast'),\n options.build,\n typeof entry === 'function' && stamped[FUSED_HOST_ELIDED] === true,\n )\n const ctx: ParseContext = {\n trackLines: false,\n ...(skipGlobalSinks\n ? { _pmProfile: profile! }\n : { _triviaLog: triviaLog, _errors: errors, ...(profile === undefined ? {} : { _pmProfile: profile }) }),\n build: options.build,\n state: options.state,\n ...(grammarTrivia !== undefined\n ? { trivia: grammarTrivia, ...(grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}) }\n : {}),\n ...(grammarScanSkip !== undefined ? { scanSkip: grammarScanSkip } : {}),\n ...(options.triviaCaptureMask !== undefined ? { _triviaCaptureMask: options.triviaCaptureMask } : {}),\n ...(options.tolerant ? { _tolerant: true, _rec: REC } : {}),\n ...(options.instrumentation === undefined ? {} : options.instrumentation),\n }\n const r = invoke(entry, input, 0, ctx)\n\n let unconsumedFrom: number | null = null\n if (r.ok) {\n let pos = r.span?.end ?? 0\n if (options.trivia && pos < input.length) {\n // Throwaway ctx: trailing trivia must NOT pollute the parse's trivia log.\n const t = invoke(options.trivia, input, pos, { trackLines: false })\n if (t.ok && t.span.end > pos) pos = t.span.end\n }\n unconsumedFrom = pos < input.length ? pos : null\n }\n\n return {\n ok: r.ok,\n value: r.ok ? (r as { value: unknown }).value : undefined,\n span: r.span ?? { start: 0, end: 0 },\n expected: r.ok ? [] : ((r as { expected?: string[] }).expected ?? []),\n errors,\n triviaLog,\n unconsumedFrom,\n }\n}\n\n/** Shallow copy of `options.state` per pass so an in-place mutation by one\n * profiling pass doesn't leak into the next (the parity check only compares\n * `ok`/`unconsumedFrom`, so a diverged structural output would go unnoticed).\n * Only the top level is isolated \u2014 deeply-nested mutable state is shared; a\n * profiled grammar should keep per-parse state shallow (see the `profile` docs). */\nfunction clonePassState(state: unknown): unknown {\n if (state === null || typeof state !== 'object') return state\n return Array.isArray(state) ? [...state] : { ...(state as Record<string, unknown>) }\n}\n\nfunction profilePass(entry: Runnable, input: string, options: RunOptions, phase: ProfilePhase): { result: RunResult; profile: RunProfilePass } {\n const state: ProfileState = { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 }\n const passOptions: RunOptions = options.state === undefined ? options : { ...options, state: clonePassState(options.state) }\n const start = performance.now()\n const result = runOnce(entry, input, passOptions, phase, state)\n const { phase: _phase, ...counts } = state\n return { result, profile: { ms: performance.now() - start, ...counts } }\n}\n\nexport function run(entry: Runnable, input: string, options: RunOptions = {}): RunResult {\n if (!options.profile) return runOnce(entry, input, options)\n if (typeof entry !== 'function') {\n throw new TypeError('run({ profile: true }) requires a compiled parser entry')\n }\n\n const recognizer = profilePass(entry, input, options, 'recognizer')\n const capture = profilePass(entry, input, options, 'capture')\n const host = profilePass(entry, input, options, 'host')\n if (recognizer.result.ok !== host.result.ok || capture.result.ok !== host.result.ok\n || recognizer.result.unconsumedFrom !== host.result.unconsumedFrom\n || capture.result.unconsumedFrom !== host.result.unconsumedFrom) {\n throw new Error('run({ profile: true }) changed recognition; the grammar is not profile-safe')\n }\n return {\n ...host.result,\n profile: {\n recognizer: recognizer.profile,\n structuralCapture: capture.profile,\n hostConstruction: host.profile,\n },\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }