parseman 0.46.0 → 0.47.1

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 (316) hide show
  1. package/README.md +8 -3
  2. package/dist/analysis/choice-cost-render.d.ts +12 -4
  3. package/dist/analysis/choice-cost-render.d.ts.map +1 -1
  4. package/dist/analysis/choice-cost.d.ts +2 -2
  5. package/dist/analysis/choice-cost.d.ts.map +1 -1
  6. package/dist/analysis/commitment.d.ts +131 -0
  7. package/dist/analysis/commitment.d.ts.map +1 -0
  8. package/dist/analysis/diagnostics.cjs +10339 -7787
  9. package/dist/analysis/diagnostics.cjs.map +3 -4
  10. package/dist/analysis/diagnostics.d.ts +2 -2
  11. package/dist/analysis/diagnostics.js +10339 -7787
  12. package/dist/analysis/diagnostics.js.map +3 -4
  13. package/dist/analysis/duplication.d.ts.map +1 -1
  14. package/dist/analysis/fix.d.ts +3 -3
  15. package/dist/analysis/fix.d.ts.map +1 -1
  16. package/dist/analysis/format-number.d.ts +23 -0
  17. package/dist/analysis/format-number.d.ts.map +1 -0
  18. package/dist/analysis/gating.d.ts +39 -2
  19. package/dist/analysis/gating.d.ts.map +1 -1
  20. package/dist/analysis/rebuild.d.ts.map +1 -1
  21. package/dist/analysis/terminal.d.ts +5 -2
  22. package/dist/analysis/terminal.d.ts.map +1 -1
  23. package/dist/cli/index.cjs +10924 -8506
  24. package/dist/cli/index.cjs.map +3 -4
  25. package/dist/cli/index.js +10924 -8506
  26. package/dist/cli/index.js.map +3 -4
  27. package/dist/combinators/adjacency.d.ts +100 -0
  28. package/dist/combinators/adjacency.d.ts.map +1 -0
  29. package/dist/combinators/choice.d.ts.map +1 -1
  30. package/dist/combinators/completions.d.ts.map +1 -1
  31. package/dist/combinators/dispatch.d.ts +8 -1
  32. package/dist/combinators/dispatch.d.ts.map +1 -1
  33. package/dist/combinators/expect.d.ts.map +1 -1
  34. package/dist/combinators/first-set.d.ts.map +1 -1
  35. package/dist/combinators/gate.d.ts.map +1 -1
  36. package/dist/combinators/grammar.d.ts +22 -0
  37. package/dist/combinators/grammar.d.ts.map +1 -1
  38. package/dist/combinators/literal.d.ts +8 -0
  39. package/dist/combinators/literal.d.ts.map +1 -1
  40. package/dist/combinators/map.d.ts +0 -1
  41. package/dist/combinators/map.d.ts.map +1 -1
  42. package/dist/combinators/node.d.ts +13 -3
  43. package/dist/combinators/node.d.ts.map +1 -1
  44. package/dist/combinators/parser.d.ts +1 -1
  45. package/dist/combinators/parser.d.ts.map +1 -1
  46. package/dist/combinators/ref.d.ts.map +1 -1
  47. package/dist/combinators/regex.d.ts +1 -16
  48. package/dist/combinators/regex.d.ts.map +1 -1
  49. package/dist/combinators/repeat.d.ts +30 -2
  50. package/dist/combinators/repeat.d.ts.map +1 -1
  51. package/dist/combinators/scanTo.d.ts +71 -1
  52. package/dist/combinators/scanTo.d.ts.map +1 -1
  53. package/dist/combinators/sequence.d.ts.map +1 -1
  54. package/dist/combinators/token.d.ts.map +1 -1
  55. package/dist/combinators/trivia-boundary.d.ts +26 -0
  56. package/dist/combinators/trivia-boundary.d.ts.map +1 -0
  57. package/dist/combinators/trivia-skip.d.ts +45 -1
  58. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  59. package/dist/combinators/withCtx.d.ts.map +1 -1
  60. package/dist/compiler/compile-linkable-table.d.ts +104 -0
  61. package/dist/compiler/compile-linkable-table.d.ts.map +1 -0
  62. package/dist/compiler/degradation.d.ts +0 -2
  63. package/dist/compiler/degradation.d.ts.map +1 -1
  64. package/dist/compiler/fields.d.ts +13 -0
  65. package/dist/compiler/fields.d.ts.map +1 -1
  66. package/dist/compiler/grammar-coverage-ids.d.ts +5 -5
  67. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  68. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  69. package/dist/compiler/linker.d.ts +47 -53
  70. package/dist/compiler/linker.d.ts.map +1 -1
  71. package/dist/compiler/token-alphabet.d.ts +81 -0
  72. package/dist/compiler/token-alphabet.d.ts.map +1 -0
  73. package/dist/compiler/token-dispatch.d.ts +95 -0
  74. package/dist/compiler/token-dispatch.d.ts.map +1 -0
  75. package/dist/compiler/token-scanner.d.ts +53 -0
  76. package/dist/compiler/token-scanner.d.ts.map +1 -0
  77. package/dist/compiler/value-usage.d.ts.map +1 -1
  78. package/dist/coverage.d.ts +1 -1
  79. package/dist/coverage.d.ts.map +1 -1
  80. package/dist/cst/capture-buffer.d.ts +35 -0
  81. package/dist/cst/capture-buffer.d.ts.map +1 -1
  82. package/dist/cst/reflection-attach.d.ts +8 -0
  83. package/dist/cst/reflection-attach.d.ts.map +1 -0
  84. package/dist/cst/reflection-symbols.d.ts +10 -0
  85. package/dist/cst/reflection-symbols.d.ts.map +1 -0
  86. package/dist/cst/reflection.d.ts +2 -4
  87. package/dist/cst/reflection.d.ts.map +1 -1
  88. package/dist/cst/root-trivia-scope.d.ts +26 -0
  89. package/dist/cst/root-trivia-scope.d.ts.map +1 -0
  90. package/dist/cst/trivia-charscan.d.ts +18 -0
  91. package/dist/cst/trivia-charscan.d.ts.map +1 -0
  92. package/dist/cst/trivia-kinds.d.ts +19 -0
  93. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  94. package/dist/functional/doc.d.ts.map +1 -1
  95. package/dist/functional/run-tabled.d.ts +12 -0
  96. package/dist/functional/run-tabled.d.ts.map +1 -0
  97. package/dist/functional/run.d.ts +25 -42
  98. package/dist/functional/run.d.ts.map +1 -1
  99. package/dist/grammar-metadata.d.ts +3 -0
  100. package/dist/grammar-metadata.d.ts.map +1 -0
  101. package/dist/index.cjs +9290 -7181
  102. package/dist/index.cjs.map +3 -4
  103. package/dist/index.d.ts +18 -9
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +9300 -7191
  106. package/dist/index.js.map +3 -4
  107. package/dist/language-service/index.cjs +313 -121
  108. package/dist/language-service/index.cjs.map +3 -4
  109. package/dist/language-service/index.js +313 -121
  110. package/dist/language-service/index.js.map +3 -4
  111. package/dist/oracle/index.cjs.map +0 -1
  112. package/dist/oracle/index.js.map +0 -1
  113. package/dist/parse-context.d.ts +49 -0
  114. package/dist/parse-context.d.ts.map +1 -0
  115. package/dist/plugin/evaluator.d.ts +40 -1
  116. package/dist/plugin/evaluator.d.ts.map +1 -1
  117. package/dist/plugin/index.cjs +11175 -9534
  118. package/dist/plugin/index.cjs.map +3 -4
  119. package/dist/plugin/index.d.ts.map +1 -1
  120. package/dist/plugin/index.js +11175 -9534
  121. package/dist/plugin/index.js.map +3 -4
  122. package/dist/regex/classes.d.ts +28 -0
  123. package/dist/regex/classes.d.ts.map +1 -1
  124. package/dist/regex/first-set.d.ts +14 -0
  125. package/dist/regex/first-set.d.ts.map +1 -1
  126. package/dist/run/index.cjs +136 -51
  127. package/dist/run/index.cjs.map +2 -3
  128. package/dist/run/index.js +136 -51
  129. package/dist/run/index.js.map +2 -3
  130. package/dist/spec/index.cjs +2934 -1611
  131. package/dist/spec/index.cjs.map +3 -4
  132. package/dist/spec/index.js +2934 -1611
  133. package/dist/spec/index.js.map +3 -4
  134. package/dist/spec/model.d.ts +8 -0
  135. package/dist/spec/model.d.ts.map +1 -1
  136. package/dist/table/assemble.d.ts +307 -0
  137. package/dist/table/assemble.d.ts.map +1 -0
  138. package/dist/table/cell.d.ts +42 -0
  139. package/dist/table/cell.d.ts.map +1 -0
  140. package/dist/table/child-slots.d.ts +30 -0
  141. package/dist/table/child-slots.d.ts.map +1 -0
  142. package/dist/table/compile-rule-map.d.ts +136 -0
  143. package/dist/table/compile-rule-map.d.ts.map +1 -0
  144. package/dist/table/compile.d.ts +56 -0
  145. package/dist/table/compile.d.ts.map +1 -0
  146. package/dist/table/duplication-hook.d.ts +32 -0
  147. package/dist/table/duplication-hook.d.ts.map +1 -0
  148. package/dist/table/emit-assembly.d.ts +139 -0
  149. package/dist/table/emit-assembly.d.ts.map +1 -0
  150. package/dist/table/emit.d.ts +113 -0
  151. package/dist/table/emit.d.ts.map +1 -0
  152. package/dist/table/encode-baseline.d.ts +16 -0
  153. package/dist/table/encode-baseline.d.ts.map +1 -0
  154. package/dist/table/encode.d.ts +71 -0
  155. package/dist/table/encode.d.ts.map +1 -0
  156. package/dist/table/exec-baseline.d.ts +9 -0
  157. package/dist/table/exec-baseline.d.ts.map +1 -0
  158. package/dist/table/exec.d.ts +72 -0
  159. package/dist/table/exec.d.ts.map +1 -0
  160. package/dist/table/fold.d.ts +25 -0
  161. package/dist/table/fold.d.ts.map +1 -0
  162. package/dist/table/index.cjs +11463 -0
  163. package/dist/table/index.cjs.map +6 -0
  164. package/dist/table/index.d.ts +100 -0
  165. package/dist/table/index.d.ts.map +1 -0
  166. package/dist/table/index.js +11440 -0
  167. package/dist/table/index.js.map +6 -0
  168. package/dist/table/inspect.d.ts +17 -0
  169. package/dist/table/inspect.d.ts.map +1 -0
  170. package/dist/table/ops.d.ts +453 -0
  171. package/dist/table/ops.d.ts.map +1 -0
  172. package/dist/table/program.d.ts +496 -0
  173. package/dist/table/program.d.ts.map +1 -0
  174. package/dist/table/run-support.d.ts +38 -0
  175. package/dist/table/run-support.d.ts.map +1 -0
  176. package/dist/{compiler/scannable-run.d.ts → table/scan-shapes.d.ts} +14 -13
  177. package/dist/table/scan-shapes.d.ts.map +1 -0
  178. package/dist/table/site-labels.d.ts +58 -0
  179. package/dist/table/site-labels.d.ts.map +1 -0
  180. package/dist/table/stamp.d.ts +70 -0
  181. package/dist/table/stamp.d.ts.map +1 -0
  182. package/dist/types.d.ts +80 -24
  183. package/dist/types.d.ts.map +1 -1
  184. package/dist/version.d.ts +1 -1
  185. package/package.json +26 -2
  186. package/src/analysis/choice-cost-gate.ts +359 -0
  187. package/src/analysis/choice-cost-render.ts +293 -0
  188. package/src/analysis/choice-cost.ts +1008 -0
  189. package/src/analysis/commitment.ts +557 -0
  190. package/src/analysis/corpus.ts +130 -0
  191. package/src/analysis/diagnose-render.ts +625 -0
  192. package/src/analysis/diagnose.ts +317 -0
  193. package/src/analysis/diagnostics.ts +33 -0
  194. package/src/analysis/duplication.ts +1955 -0
  195. package/src/analysis/fix-render.ts +217 -0
  196. package/src/analysis/fix.ts +669 -0
  197. package/src/analysis/format-number.ts +31 -0
  198. package/src/analysis/gating.ts +792 -0
  199. package/src/analysis/grammar.ts +72 -0
  200. package/src/analysis/rebuild.ts +287 -0
  201. package/src/analysis/terminal.ts +336 -0
  202. package/src/cli/index.ts +413 -0
  203. package/src/combinators/adjacency.ts +171 -0
  204. package/src/combinators/attempt.ts +41 -0
  205. package/src/combinators/case-fold.ts +90 -0
  206. package/src/combinators/choice.ts +466 -0
  207. package/src/combinators/completions.ts +64 -0
  208. package/src/combinators/dispatch.ts +358 -0
  209. package/src/combinators/expect.ts +159 -0
  210. package/src/combinators/first-set.ts +348 -0
  211. package/src/combinators/gate.ts +50 -0
  212. package/src/combinators/grammar.ts +272 -0
  213. package/src/combinators/keywords.ts +145 -0
  214. package/src/combinators/literal.ts +102 -0
  215. package/src/combinators/map.ts +106 -0
  216. package/src/combinators/node.ts +345 -0
  217. package/src/combinators/not.ts +55 -0
  218. package/src/combinators/parser.ts +266 -0
  219. package/src/combinators/peek.ts +63 -0
  220. package/src/combinators/probe.ts +25 -0
  221. package/src/combinators/ref.ts +109 -0
  222. package/src/combinators/regex.ts +170 -0
  223. package/src/combinators/repeat.ts +561 -0
  224. package/src/combinators/scanTo.ts +393 -0
  225. package/src/combinators/sequence.ts +237 -0
  226. package/src/combinators/token.ts +120 -0
  227. package/src/combinators/trivia-boundary.ts +48 -0
  228. package/src/combinators/trivia-skip.ts +852 -0
  229. package/src/combinators/withCtx.ts +45 -0
  230. package/src/compiler/build-arity.ts +330 -0
  231. package/src/compiler/compile-linkable-table.ts +209 -0
  232. package/src/compiler/degradation.ts +266 -0
  233. package/src/compiler/fields.ts +191 -0
  234. package/src/compiler/grammar-coverage-ids.ts +129 -0
  235. package/src/compiler/ir-serialize.ts +615 -0
  236. package/src/compiler/linker.ts +954 -0
  237. package/src/compiler/token-alphabet.ts +209 -0
  238. package/src/compiler/token-dispatch.ts +353 -0
  239. package/src/compiler/token-scanner.ts +318 -0
  240. package/src/compiler/value-usage.ts +147 -0
  241. package/src/coverage.ts +383 -0
  242. package/src/cst/capture-buffer.ts +297 -0
  243. package/src/cst/host-mode.ts +69 -0
  244. package/src/cst/reflection-attach.ts +11 -0
  245. package/src/cst/reflection-symbols.ts +9 -0
  246. package/src/cst/reflection.ts +149 -0
  247. package/src/cst/relative-spans.ts +207 -0
  248. package/src/cst/root-trivia-scope.ts +30 -0
  249. package/src/cst/trivia-charscan.ts +312 -0
  250. package/src/cst/trivia-entries.ts +285 -0
  251. package/src/cst/trivia-index.ts +131 -0
  252. package/src/cst/trivia-kinds.ts +358 -0
  253. package/src/cst/types.ts +76 -0
  254. package/src/cst/walk.ts +101 -0
  255. package/src/functional/doc.ts +959 -0
  256. package/src/functional/run-tabled.ts +153 -0
  257. package/src/functional/run.ts +558 -0
  258. package/src/grammar-metadata.ts +2 -0
  259. package/src/index.ts +189 -0
  260. package/src/language-service/index.ts +221 -0
  261. package/src/line-index.ts +131 -0
  262. package/src/oracle/digest.ts +620 -0
  263. package/src/oracle/index.ts +55 -0
  264. package/src/parse-context.ts +91 -0
  265. package/src/plugin/direct-builder-static.ts +102 -0
  266. package/src/plugin/evaluator.ts +1505 -0
  267. package/src/plugin/free-identifiers.ts +338 -0
  268. package/src/plugin/index.ts +2281 -0
  269. package/src/plugin/reducer-resolver.ts +606 -0
  270. package/src/recovery/scan.ts +155 -0
  271. package/src/regex/classes.ts +183 -0
  272. package/src/regex/first-set.ts +316 -0
  273. package/src/run/index.ts +31 -0
  274. package/src/spec/ebnf.ts +128 -0
  275. package/src/spec/index.ts +54 -0
  276. package/src/spec/model.ts +636 -0
  277. package/src/spec/railroad-lib.ts +559 -0
  278. package/src/spec/railroad.ts +256 -0
  279. package/src/table/assemble.ts +3006 -0
  280. package/src/table/cell.ts +42 -0
  281. package/src/table/child-slots.ts +113 -0
  282. package/src/table/compile-rule-map.ts +336 -0
  283. package/src/table/compile.ts +244 -0
  284. package/src/table/duplication-hook.ts +89 -0
  285. package/src/table/emit-assembly.ts +1777 -0
  286. package/src/table/emit.ts +429 -0
  287. package/src/table/encode-baseline.ts +340 -0
  288. package/src/table/encode.ts +1569 -0
  289. package/src/table/exec-baseline.ts +537 -0
  290. package/src/table/exec.ts +1680 -0
  291. package/src/table/fold.ts +38 -0
  292. package/src/table/index.ts +108 -0
  293. package/src/table/inspect.ts +52 -0
  294. package/src/table/ops.ts +467 -0
  295. package/src/table/program.ts +876 -0
  296. package/src/table/run-support.ts +66 -0
  297. package/src/table/scan-shapes.ts +1578 -0
  298. package/src/table/site-labels.ts +228 -0
  299. package/src/table/stamp.ts +229 -0
  300. package/src/types.ts +563 -0
  301. package/src/version.ts +16 -0
  302. package/dist/compiler/codegen.d.ts +0 -334
  303. package/dist/compiler/codegen.d.ts.map +0 -1
  304. package/dist/compiler/inline-build.d.ts +0 -11
  305. package/dist/compiler/inline-build.d.ts.map +0 -1
  306. package/dist/compiler/inline-callback.d.ts +0 -21
  307. package/dist/compiler/inline-callback.d.ts.map +0 -1
  308. package/dist/compiler/line-index.d.ts +0 -2
  309. package/dist/compiler/line-index.d.ts.map +0 -1
  310. package/dist/compiler/module-hoist.d.ts +0 -106
  311. package/dist/compiler/module-hoist.d.ts.map +0 -1
  312. package/dist/compiler/scannable-run.d.ts.map +0 -1
  313. package/dist/compiler/scannable-terminal.d.ts +0 -23
  314. package/dist/compiler/scannable-terminal.d.ts.map +0 -1
  315. package/dist/compiler/trivia-fast-path.d.ts +0 -38
  316. package/dist/compiler/trivia-fast-path.d.ts.map +0 -1
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/line-index.ts", "../../src/cst/capture-buffer.ts", "../../src/recovery/scan.ts", "../../src/cst/trivia-entries.ts", "../../src/cst/host-mode.ts", "../../src/functional/run.ts", "../../src/combinators/completions.ts", "../../src/analysis/duplication.ts", "../../src/compiler/scannable-run.ts", "../../src/compiler/trivia-fast-path.ts", "../../src/compiler/inline-build.ts", "../../src/compiler/linker.ts", "../../src/cst/relative-spans.ts", "../../src/functional/doc.ts", "../../src/language-service/index.ts"],
4
- "sourcesContent": ["import type { Span } from './types.ts'\n\n/**\n * Precomputed index of newline positions for O(log n) offset->line/col lookup.\n * lineStarts[i] is the byte offset of the first character on line i+1.\n */\nexport type LineIndex = { lineStarts: number[] }\ntype LineTrackingContext = {\n trackLines?: boolean | undefined\n _lineIndex?: LineIndex | undefined\n _lineStarts?: number[] | undefined\n _lineScannedTo?: number | undefined\n}\n\nexport function createLineIndex(): LineIndex {\n return { lineStarts: [0] }\n}\n\nexport function buildLineIndex(input: string): LineIndex {\n const lineStarts = [0]\n for (let i = 0; i < input.length; i++) {\n if (input.charCodeAt(i) === 10) lineStarts.push(i + 1)\n }\n return { lineStarts }\n}\n\n/**\n * Record newline offsets for a matched range. Intended for optional parsers that\n * collect line starts while they consume terminals. The collector is deliberately\n * append-only: speculative parses never roll it back. Normalize once at the\n * driver boundary before doing offset->line/column lookup.\n */\nexport function recordLineRange(index: LineIndex, input: string, start: number, end: number): void {\n const lineStarts = index.lineStarts\n for (let i = start; i < end; i++) {\n if (input.charCodeAt(i) === 10) lineStarts.push(i + 1)\n }\n}\n\n/**\n * Records newlines from the context high-water mark through `end`. The start\n * argument is only a caller hint: append-only tracking uses `_lineScannedTo` so\n * overlapping calls can backfill gaps without double-scanning committed ranges.\n */\nexport function recordLineRangeFromContext(\n ctx: LineTrackingContext,\n input: string,\n _startHint: number,\n end: number,\n): void {\n if (!ctx.trackLines) return\n const from = ctx._lineScannedTo ?? 0\n if (end <= from) return\n const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : undefined)\n if (!index) return\n recordLineRange(index, input, from, end)\n ctx._lineScannedTo = end\n}\n\nexport function normalizeLineIndex(index: LineIndex): LineIndex {\n const starts = index.lineStarts\n starts.sort((a, b) => a - b)\n let write = 0\n for (let read = 0; read < starts.length; read++) {\n const value = starts[read]!\n if (write === 0 || starts[write - 1] !== value) starts[write++] = value\n }\n starts.length = write\n if (starts.length === 0 || starts[0] !== 0) starts.unshift(0)\n return index\n}\n\nexport function offsetToLineCol(\n index: LineIndex,\n offset: number\n): { line: number; col: number } {\n const { lineStarts } = index\n let lo = 0\n let hi = lineStarts.length - 1\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1\n if (lineStarts[mid]! <= offset) lo = mid\n else hi = mid - 1\n }\n return { line: lo + 1, col: offset - lineStarts[lo]! + 1 }\n}\n\n/** Fills startLine/startColumn/endLine/endColumn on a span in-place. */\nexport function annotateSpan(span: Span, index: LineIndex): Span {\n const s = offsetToLineCol(index, span.start)\n const e = offsetToLineCol(index, span.end)\n return {\n ...span,\n startLine: s.line,\n startColumn: s.col,\n endLine: e.line,\n endColumn: e.col,\n }\n}\n\nexport function annotateSpanFromLineContext(\n span: Span,\n ctx: LineTrackingContext,\n): Span {\n if (!ctx.trackLines) return span\n const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : undefined)\n return index ? annotateSpan(span, index) : span\n}\n\nfunction isSpan(value: unknown): value is Span {\n return !!value\n && typeof value === 'object'\n && typeof (value as { start?: unknown }).start === 'number'\n && typeof (value as { end?: unknown }).end === 'number'\n}\n\n/** Annotate every span-shaped object reachable from a parse result value. */\nexport function annotateTreeSpans<T>(value: T, index: LineIndex, seen: WeakSet<object> = new WeakSet()): T {\n if (!value || typeof value !== 'object') return value\n if (seen.has(value)) return value\n seen.add(value)\n\n const obj = value as Record<string, unknown>\n if (isSpan(obj.span)) obj.span = annotateSpan(obj.span, index)\n\n for (const key of Object.keys(obj)) {\n const child = obj[key]\n if (child && typeof child === 'object') annotateTreeSpans(child, index, seen)\n }\n return value\n}\n", "import type { ParseContext, Span } from '../types.ts'\nimport { annotateSpanFromLineContext } from '../line-index.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 if (!ctx.trackLines) {\n pushCstChild(ctx, leaf, leaf)\n return\n }\n const annotated = leaf && typeof leaf === 'object' && (leaf as { span?: unknown }).span\n ? { ...(leaf as Record<string, unknown>), span: annotateSpanFromLineContext((leaf as { span: Span }).span, ctx) }\n : leaf\n pushCstChild(ctx, annotated, annotated)\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'\nimport { annotateSpanFromLineContext, recordLineRangeFromContext } from '../line-index.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, _rootTriviaLog: ctx._rootTriviaLog, _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._rootTriviaLog = 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._rootTriviaLog = s._rootTriviaLog; 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 span = { start: from, end: scanPos }\n if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos)\n const error: ParseError = { _tag: 'parseError', span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, 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", "/** View over a flat trivia log (`_triviaLog` or per-node `triviaLog`). */\nexport type TriviaEntriesView = {\n readonly length: number\n readonly labels: readonly string[] | undefined\n readonly stride: number\n start(i: number): number\n end(i: number): number\n /** Raw-child insertion boundary; defined only for a per-node trivia log. */\n insertIndex(i: number): number | undefined\n kindIndex(i: number): number | undefined\n kind(i: number): string | undefined\n text(i: number, input: string): string\n}\n\nexport type RootTriviaGap = {\n /** Start offset of the contiguous trivia gap. */\n readonly start: number\n /** End offset of the contiguous trivia gap. */\n readonly end: number\n /** Entry indices into `entries` that make up this gap. */\n readonly entryIndices: readonly number[]\n /** True when any entry in the gap has the requested labeled trivia kind. */\n hasKind(kind: string): boolean\n /** Slice the full contiguous gap text from the original input. */\n text(input: string): string\n}\n\nexport type RootTriviaIndex = {\n /** Lazy entry-level view over sparse selected-root rows. */\n readonly entries: TriviaEntriesView\n /** Selected-root label table; entry kind indices refer to this array. */\n readonly labels: readonly string[]\n /**\n * Contiguous trivia gaps keyed by the following content offset. Values are\n * entry indices into `entries`, not materialized `{ value, span }` objects.\n */\n readonly before: ReadonlyMap<number, readonly number[]>\n /**\n * Contiguous trivia gaps keyed by the preceding content offset. Values are\n * entry indices into `entries`, not materialized `{ value, span }` objects.\n */\n readonly after: ReadonlyMap<number, readonly number[]>\n entryIndicesBefore(offset: number): readonly number[]\n entryIndicesAfter(offset: number): readonly number[]\n /**\n * Contiguous trivia gap immediately before `offset`, keyed by the following\n * content offset. Returns undefined when no gap sits at that boundary.\n */\n gapBefore(offset: number): RootTriviaGap | undefined\n /**\n * Contiguous trivia gap immediately after `offset`, keyed by the preceding\n * content offset. Returns undefined when no gap sits at that boundary.\n */\n gapAfter(offset: number): RootTriviaGap | undefined\n /** All contiguous trivia gaps in source order. */\n gaps(): readonly RootTriviaGap[]\n /** Source-ordered gaps containing at least one entry with the given label. */\n gapsWithKind(kind: string | readonly string[]): readonly RootTriviaGap[]\n}\n\nconst EMPTY_INDICES: readonly number[] = Object.freeze([])\nconst EMPTY_GAPS: readonly RootTriviaGap[] = Object.freeze([])\n/** Number of packed fields in one selected-root trivia row. */\nexport const SELECTED_ROOT_STRIDE = 5\n\ntype RootTriviaMaps = {\n before: Map<number, number[]>\n after: Map<number, number[]>\n beforeGaps: Map<number, RootTriviaGap>\n afterGaps: Map<number, RootTriviaGap>\n gaps: RootTriviaGap[]\n}\n\nfunction entryOffset(i: number, stride: number): number {\n return i * stride\n}\n\n/**\n * Wrap a flat trivia log. Stride is inferred from `labels`:\n * - root `_triviaLog`: 2 (start, end) or 3 (+ kindIndex)\n * - node `triviaLog`: 3 (start, end, insertIdx) or 4 (+ kindIndex)\n */\nexport function triviaEntries(\n log: readonly number[],\n labels?: readonly string[],\n opts?: { nodeLog?: boolean },\n): TriviaEntriesView {\n const baseStride = opts?.nodeLog ? 3 : 2\n const stride = labels ? baseStride + 1 : baseStride\n const length = Math.floor(log.length / stride)\n\n return {\n length,\n labels,\n stride,\n start(i) {\n return log[entryOffset(i, stride)]!\n },\n end(i) {\n return log[entryOffset(i, stride) + 1]!\n },\n insertIndex(i) {\n return opts?.nodeLog ? log[entryOffset(i, stride) + 2] : undefined\n },\n kindIndex(i) {\n if (!labels) return undefined\n return log[entryOffset(i, stride) + baseStride]\n },\n kind(i) {\n const ki = labels ? log[entryOffset(i, stride) + baseStride] : undefined\n return ki !== undefined ? labels![ki] : undefined\n },\n text(i, input) {\n const s = log[entryOffset(i, stride)]!\n const e = log[entryOffset(i, stride) + 1]!\n return input.slice(s, e)\n },\n }\n}\n\n/**\n * Build a root view over the sparse selected-label rows captured as\n * `[ownedRangeStart, ownedRangeEnd, markerStart, markerEnd, kindIndex]`.\n *\n * This is intentionally a different input shape from `triviaLog`: each row is\n * one selected marker, while the first pair names the complete committed trivia\n * range around it. Thus a mixed trivia run costs one five-number row, not three\n * legacy trivia entries. Rows are source ordered and equal gap pairs are\n * contiguous, which lets singleton boundary lookups binary-search the sparse\n * rows without constructing document-wide maps.\n */\nexport function buildRootTriviaIndex(\n log: readonly number[],\n labels: readonly string[],\n): RootTriviaIndex {\n const length = Math.floor(log.length / SELECTED_ROOT_STRIDE)\n const markerOffset = (i: number) => i * SELECTED_ROOT_STRIDE\n const ownedRangeStart = (i: number) => log[markerOffset(i)]!\n const ownedRangeEnd = (i: number) => log[markerOffset(i) + 1]!\n const markerStart = (i: number) => log[markerOffset(i) + 2]!\n const markerEnd = (i: number) => log[markerOffset(i) + 3]!\n const markerKind = (i: number) => log[markerOffset(i) + 4]!\n\n const entries: TriviaEntriesView = {\n length,\n labels,\n stride: SELECTED_ROOT_STRIDE,\n start: markerStart,\n end: markerEnd,\n insertIndex: () => undefined,\n kindIndex: markerKind,\n kind(i) {\n return labels[markerKind(i)]\n },\n text(i, input) {\n return input.slice(markerStart(i), markerEnd(i))\n },\n }\n\n const lowerBound = (offset: number, by: (i: number) => number): number => {\n let lo = 0\n let hi = length\n while (lo < hi) {\n const mid = (lo + hi) >>> 1\n if (by(mid) < offset) lo = mid + 1\n else hi = mid\n }\n return lo\n }\n\n const gapRowsAt = (offset: number, direction: 'before' | 'after'): [number, number] | undefined => {\n const edge = direction === 'before' ? ownedRangeEnd : ownedRangeStart\n let first = lowerBound(offset, edge)\n if (first === length || edge(first) !== offset) return undefined\n const start = ownedRangeStart(first)\n const end = ownedRangeEnd(first)\n while (first > 0 && ownedRangeStart(first - 1) === start && ownedRangeEnd(first - 1) === end) first--\n let last = first + 1\n while (last < length && ownedRangeStart(last) === start && ownedRangeEnd(last) === end) last++\n return [first, last]\n }\n\n const indices = (first: number, last: number): readonly number[] => {\n const out: number[] = []\n for (let i = first; i < last; i++) out.push(i)\n return out\n }\n\n const makeGap = (first: number, last: number): RootTriviaGap => {\n const start = ownedRangeStart(first)\n const end = ownedRangeEnd(first)\n let entryIndices: readonly number[] | undefined\n return {\n start,\n end,\n get entryIndices() {\n return entryIndices ??= indices(first, last)\n },\n hasKind(kind) {\n for (let i = first; i < last; i++) {\n if (labels[markerKind(i)] === kind) return true\n }\n return false\n },\n text(input) {\n return input.slice(start, end)\n },\n }\n }\n\n let maps: RootTriviaMaps | undefined\n const getMaps = () => {\n if (maps !== undefined) return maps\n const before = new Map<number, number[]>()\n const after = new Map<number, number[]>()\n const beforeGaps = new Map<number, RootTriviaGap>()\n const afterGaps = new Map<number, RootTriviaGap>()\n const gaps: RootTriviaGap[] = []\n for (let first = 0; first < length;) {\n let last = first + 1\n while (last < length && ownedRangeStart(last) === ownedRangeStart(first) && ownedRangeEnd(last) === ownedRangeEnd(first)) last++\n const gap = makeGap(first, last)\n gaps.push(gap)\n afterGaps.set(gap.start, gap)\n beforeGaps.set(gap.end, gap)\n after.set(gap.start, [...gap.entryIndices])\n before.set(gap.end, [...gap.entryIndices])\n first = last\n }\n maps = { before, after, beforeGaps, afterGaps, gaps }\n return maps\n }\n\n return {\n entries,\n labels,\n get before() { return getMaps().before },\n get after() { return getMaps().after },\n entryIndicesBefore(offset) {\n const rows = gapRowsAt(offset, 'before')\n return rows ? indices(rows[0], rows[1]) : EMPTY_INDICES\n },\n entryIndicesAfter(offset) {\n const rows = gapRowsAt(offset, 'after')\n return rows ? indices(rows[0], rows[1]) : EMPTY_INDICES\n },\n gapBefore(offset) {\n const rows = gapRowsAt(offset, 'before')\n return rows ? makeGap(rows[0], rows[1]) : undefined\n },\n gapAfter(offset) {\n const rows = gapRowsAt(offset, 'after')\n return rows ? makeGap(rows[0], rows[1]) : undefined\n },\n gaps() {\n return getMaps().gaps\n },\n gapsWithKind(kind) {\n const kinds = typeof kind === 'string' ? [kind] : kind\n const matches: RootTriviaGap[] = []\n // Do not materialize the document-wide boundary maps for a sparse query.\n // The primary consumer selects comments: it needs only comment-bearing gaps,\n // not an object/Map for every whitespace-owned boundary in the document.\n for (let first = 0; first < length;) {\n let last = first + 1\n while (last < length\n && ownedRangeStart(last) === ownedRangeStart(first)\n && ownedRangeEnd(last) === ownedRangeEnd(first)) last++\n let matched = false\n for (let i = first; i < last && !matched; i++) {\n const label = labels[markerKind(i)]\n for (const kind of kinds) {\n if (label === kind) {\n matched = true\n break\n }\n }\n }\n if (matched) matches.push(makeGap(first, last))\n first = last\n }\n return matches.length === 0 ? EMPTY_GAPS : matches\n },\n }\n}\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'\nimport { buildRootTriviaIndex, type RootTriviaIndex } from '../cst/trivia-entries.ts'\n/* `cst/host-mode.ts` and `cst/trivia-entries.ts` are import-free by design, so\n * this keeps the `parseman/run` closure minimal \u2014 see\n * 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. Omit to\n * capture every kind.\n */\n triviaCaptureMask?: number\n /**\n * Opt into sparse root trivia capture. `run()` retains no root trivia unless\n * this is supplied. Capture records only the named labels, each with the one\n * complete authored gap that owns it; ordinary whitespace has no root entry.\n */\n rootTrivia?: {\n readonly select: readonly string[]\n }\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 RootTriviaCapture = {\n /** Packed `[gapStart, gapEnd, markerStart, markerEnd, selectedKindIndex]` rows. */\n readonly rows: readonly number[]\n /** Labels requested by this caller; row kind indices refer to this array. */\n readonly select: readonly string[]\n /** Lazy lookup over `rows`; no tokens or strings are materialized. */\n readonly index: RootTriviaIndex\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 * Sparse selected root trivia. This is present only when at least one requested\n * category was actually retained; omitted means the parse retained no root\n * trivia at all.\n */\n rootTrivia?: RootTriviaCapture\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\n/**\n * 0.44 dropped three `RunResult` fields \u2014 `triviaMap` and `triviaLog`, both\n * MANDATORY in 0.43, and the optional `triviaKindLabels`. Removing them without\n * a signal makes each read `undefined`, and `undefined` travels: it surfaces as\n * a property access on nothing somewhere inside the CONSUMER's code, in a\n * message naming neither parseman, the field, nor the replacement. That is the\n * same defect as a diagnostic that reports a number when it means \"I could not\n * run\" \u2014 the tool's inability wearing the costume of a result.\n *\n * `triviaKindLabels` is the worst of the three despite being optional: it fed\n * `triviaKindMask`, which treats `undefined` as \"capture everything\", so its\n * removal silently changes behaviour rather than producing so much as a crash.\n *\n * So the names are kept as accessors that throw the migration. They are\n * NON-ENUMERABLE deliberately: absent from `Object.keys`, spreads,\n * `JSON.stringify` and identity digests, so restoring them moves no output and\n * costs nothing on the parse path \u2014 they exist only to answer a read.\n */\nconst SELECT_EXAMPLE = \"run(entry, input, { rootTrivia: { select: ['blockComment', 'lineComment'] } })\"\n\nconst REMOVED_RUN_RESULT_FIELDS: ReadonlyArray<readonly [string, string]> = [\n [\n 'triviaMap',\n 'It was a dense root-trivia index built on every parse; root trivia is now an OPT-IN sparse '\n + 'capture, because most grammars paid for an index they never read. Migration: name the trivia '\n + `labels your grammar defines \u2014 ${SELECT_EXAMPLE} \u2014 and read \\`RunResult.rootTrivia\\`, whose `\n + '`.index` carries the labels and gap lookups `triviaMap` exposed.',\n ],\n [\n 'triviaLog',\n 'It was the flat `start, end[, kindIndex]` log every other trivia view was derived from, and it '\n + 'is no longer allocated at all \u2014 not empty, absent. Migration: request the labels you need '\n + `\u2014 ${SELECT_EXAMPLE} \u2014 and read \\`RunResult.rootTrivia.rows\\`. Do NOT reuse a stride-2/3 reader: `\n + 'rows are a different width and a different unit (one row per SELECTED marker, not one per '\n + 'trivia chunk), so an old loop reads confidently wrong offsets rather than failing.',\n ],\n [\n 'triviaKindLabels',\n 'It was already optional, so nothing ever flagged its disappearance \u2014 and `triviaKindMask(undefined, '\n + '\u2026)` means \"capture everything\", so reading it now silently WIDENS capture instead of erroring. '\n + `Migration: request labels \u2014 ${SELECT_EXAMPLE} \u2014 then the set you asked for is `\n + '`RunResult.rootTrivia.select` and the resolved table is `RunResult.rootTrivia.index.labels`.',\n ],\n]\n\n/**\n * `rootTrivia` is ABSENT rather than empty when no requested category was\n * retained, so branch on it \u2014 do not index into it.\n */\nfunction guardRemovedFields(result: RunResult): RunResult {\n for (const [name, detail] of REMOVED_RUN_RESULT_FIELDS) {\n Object.defineProperty(result, name, {\n configurable: true,\n enumerable: false,\n get(): never {\n throw new TypeError(\n `RunResult.${name} was REMOVED in parseman 0.44.0 and has no default replacement. ${detail}`,\n )\n },\n })\n }\n return result\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']>\ntype RunnableMeta = {\n readonly _meta?: {\n readonly triviaKindLabels?: readonly string[]\n readonly rootTriviaClassified?: true\n }\n}\n\nfunction triviaKindLabelsFromRunnable(r: Runnable | undefined): readonly string[] | undefined {\n if (!r) return undefined\n if (typeof r === 'function') return (r as RunnableMeta)._meta?.triviaKindLabels\n const grammarTrivia = (r._meta as { grammarTrivia?: Combinator<unknown> }).grammarTrivia\n return grammarTrivia?._meta.triviaKindLabels\n ?? r._meta.triviaKindLabels\n ?? (r._def.tag === 'grammar' ? r._def.triviaParser?._meta.triviaKindLabels : undefined)\n}\n\nfunction rootTriviaClassifiedFromRunnable(r: Runnable | undefined): boolean {\n if (!r) return false\n if (typeof r === 'function') return (r as RunnableMeta)._meta?.rootTriviaClassified === true\n const grammarTrivia = (r._meta as { grammarTrivia?: Combinator<unknown> }).grammarTrivia\n return grammarTrivia?._meta.rootTriviaClassified === true || r._meta.rootTriviaClassified === true\n}\n\nfunction makeSelectedRootLabelIndex(labels: readonly string[]): Readonly<Record<string, number>> {\n const index: Record<string, number> = Object.create(null) as Record<string, number>\n for (let i = 0; i < labels.length; i++) {\n if (index[labels[i]!] === undefined) index[labels[i]!] = i\n }\n return index\n}\n\n/** Keep the run-entry closure independent from grammar-construction helpers. */\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 rootTriviaSelection = options.rootTrivia?.select\n const captureRootTrivia = rootTriviaSelection !== undefined && rootTriviaSelection.length > 0\n const selectedRootLog = captureRootTrivia ? [] as number[] : undefined\n const selectedRootLabelIndex = captureRootTrivia\n ? makeSelectedRootLabelIndex(rootTriviaSelection)\n : undefined\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 triviaKindLabels = triviaKindLabelsFromRunnable(entry) ?? triviaKindLabelsFromRunnable(options.trivia)\n if (captureRootTrivia && triviaKindLabels === undefined) {\n throw new TypeError('run(): rootTrivia.select requires labeled grammar trivia.')\n }\n if (captureRootTrivia) {\n for (const label of rootTriviaSelection) {\n if (!triviaKindLabels!.includes(label)) {\n throw new TypeError(`run(): rootTrivia.select contains unknown trivia label ${JSON.stringify(label)}.`)\n }\n }\n }\n if (captureRootTrivia\n && !rootTriviaClassifiedFromRunnable(entry)\n && !rootTriviaClassifiedFromRunnable(options.trivia)) {\n throw new TypeError(\n 'run(): rootTrivia.select requires classifiedTrivia() at the root.',\n )\n }\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 : {\n ...(captureRootTrivia\n ? {\n _rootTriviaLog: selectedRootLog!,\n _rootTriviaKindIndex: selectedRootLabelIndex!,\n _rootTriviaStrictScopes: true,\n }\n : {}),\n _errors: errors,\n ...(profile === undefined ? {} : { _pmProfile: profile }),\n }),\n build: options.build,\n state: options.state,\n ...(grammarTrivia !== undefined\n ? { trivia: grammarTrivia, ...(triviaKindLabels ? { triviaKindLabels } : {}) }\n : triviaKindLabels ? { triviaKindLabels } : {}),\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 ...(selectedRootLog !== undefined && selectedRootLog.length > 0\n ? {\n rootTrivia: {\n rows: selectedRootLog!,\n select: rootTriviaSelection!,\n index: buildRootTriviaIndex(selectedRootLog!, rootTriviaSelection!),\n },\n }\n : {}),\n unconsumedFrom,\n }\n}\n\n/* The three-pass profiling driver (`clonePassState` / `profilePass`) was removed\n * with the emitted counters it read. `runOnce` still accepts `phase`/`profileState`\n * and `RunProfile`/`RunProfilePass` are still exported, so the interpreted\n * reimplementation can restore the driver without reshaping the public result. */\n\nexport function run(entry: Runnable, input: string, options: RunOptions = {}): RunResult {\n if (!options.profile) return guardRemovedFields(runOnce(entry, input, options))\n // Profiling counters are no longer emitted into compiled artifacts \u2014 they cost a\n // `_ctx._pmProfile` read plus ~15 threaded ternaries on EVERY node, which is the\n // \"diagnostic machinery in codegen\" the compiled path must not pay for. The\n // interpreter has never implemented `_pmProfile` either, so no route can answer\n // this today. Fail LOUDLY: the passes below would otherwise all report zero, and\n // an all-zero profile reads as a real measurement rather than as \"I could not\n // run\" \u2014 the exact defect `guardRemovedFields` exists to prevent for the removed\n // 0.44 fields. The pass machinery is retained, unmodified, for the interpreted\n // reimplementation.\n throw new TypeError(\n 'run({ profile: true }) is unavailable: profiling counters are no longer compiled into '\n + 'parser artifacts, and the interpreter does not implement them yet. Profiling is moving '\n + 'to interpreted mode.',\n )\n}\n", "import type { Combinator, ParseFail, ParseContext, ParseResult } from '../types.ts'\nimport { REC } from '../recovery/scan.ts'\n\n/** Anything `completionsAt` can probe: an interpreter combinator OR a `compile()`d\n * grammar. A grammar compiled with `{ recovery: true }` records the probe on its fast\n * path, so completions work on the published compiled artifact \u2014 no interpreter needed. */\ntype CompletionsTarget =\n | Combinator<unknown>\n | { parseWithContext(input: string, ctx: ParseContext, pos?: number): ParseResult<unknown> }\n\n/**\n * Returns the set of expected tokens at the given cursor offset.\n * Useful for implementing IDE completions.\n *\n * Runs the parser on input truncated at `offset` with a probe that tracks the\n * highest-position failure seen, even when sepBy/many backtrack past the cursor.\n * Returns the expected tokens from that deepest failure.\n *\n * Works on the interpreter combinator OR a `compile(g, { recovery: true })` grammar\n * (the probe is recorded on the compiled fast path). Returns an empty array when the\n * input up to `offset` parses completely with no failures at or before the cursor.\n *\n * The returned strings use the same labels as ParseFail.expected:\n * quoted literals like `\"\\\"{\\\"\"` and regex patterns like `\"/[0-9]+/\"`.\n */\nexport function completionsAt(\n target: CompletionsTarget,\n input: string,\n offset: number,\n options: { tolerant?: boolean } = {},\n): string[] {\n const probe: { offset: number; best: ParseFail | null } = { offset, best: null }\n // In tolerant mode, list recovery keeps the enclosing node parsing past a bad\n // element so the failure at the cursor is actually recorded \u2014 the completion set\n // is otherwise empty when a permissive top rule \"succeeds\" with an unconsumed tail.\n const ctx: ParseContext = { trackLines: false, _probe: probe, ...(options.tolerant ? { _tolerant: true, _rec: REC } : {}) }\n const sliced = input.slice(0, offset)\n const result = 'parseWithContext' in target\n ? target.parseWithContext(sliced, ctx, 0)\n : target.parse(sliced, 0, ctx)\n\n // If the parser consumed everything up to offset successfully, there is nothing\n // to complete \u2014 the input is already valid at this position. In tolerant mode the\n // parse \"succeeds\" (list recovery swallowed the incomplete element at the cursor),\n // so the meaningful expectation lives in the deepest failure the probe recorded\n // while recovering: return that instead of an empty set.\n if (result.ok) return options.tolerant ? (probe.best?.expected ?? []) : []\n\n // Use whichever failure (probe or top-level) sits at the deeper position.\n const best = deeperFail(probe.best, result)\n return best?.expected ?? []\n}\n\nfunction deeperFail(a: ParseFail | null, b: ParseFail | null): ParseFail | null {\n if (!a) return b\n if (!b) return a\n return a.span.start >= b.span.start ? a : b\n}\n", "/**\n * Static grammar-duplication / overlap / rewrite diagnostic.\n *\n * A parseman grammar is a COMBINATOR TREE, not source text \u2014 so the question\n * \"did I write this production twice?\" is a structural one a machine answers\n * exactly, and a reviewer answers badly. A few hundred productions is tens of\n * thousands of pairs; nobody reads that. This module walks the same tree\n * `analyzeGating` walks and reports nine families:\n *\n * 1. `duplicates` \u2014 subtrees that are structurally IDENTICAL, in \u22652 places.\n * 2. `nearDuplicates` \u2014 subtrees identical except at ONE slot. This is the\n * high-value case: a general rule cloned with one term\n * swapped, where the fix is one production whose varying\n * slot is a `choice`, not N copies of the scaffolding.\n * 3. `regexFragments` \u2014 alternation runs re-spelled across several `regex()`\n * terminals. Structural hashing cannot see inside a\n * regex, so this is its own pass.\n * 4. `regexClasses` \u2014 character classes re-spelled across terminals, and, more\n * usefully, NEAR-identical ones with the drift shown side\n * by side.\n * 5. `overlaps` \u2014 `choice` arms whose first-sets intersect, reported as\n * \"these two arms, on these chars, sharing these leading\n * terms\" rather than gating's \"dispatch failed\".\n * 6. `rewrites` \u2014 ALGEBRAIC simplifications: mechanically-derived exact\n * rewrites (`choice(sequence(A,B), B)` \u2192 `sequence(\n * optional(A), B)`), plus the dead-arm cases that are\n * outright bugs.\n * 7. `divergentNodes` \u2014 ONE `node()` type built by several structurally different\n * productions that are nonetheless variants of one shape.\n * 8. `structureLoss` \u2014 an earlier `choice` arm that FLATTENS what a later arm\n * would have structured: same `node()` type, overlapping\n * first-sets, and the earlier body contains no `node()`\n * at all. The parse succeeds either way, so nothing else\n * \u2014 not a test suite, not an output diff \u2014 reports it.\n * The ordered, consequential half of `divergentNodes`.\n * 9. `keywordRegexes` \u2014 hand-rolled keyword regexes (`regex(/not(?![-\\w])/i)`)\n * that should be `word()`/`keywords()`. Not a style note:\n * `/i` without `/u` gets non-ASCII case folding wrong,\n * and the fix that lives inside `keywords()` (see\n * `combinators/case-fold.ts`) never reached the copies.\n *\n * ## What this CANNOT see\n *\n * Structural hashing is exactly that: STRUCTURAL. Two productions that accept the\n * same language but are shaped differently \u2014 `many(x)` vs `optional(oneOrMore(x))`,\n * a `regex` spelling of what another rule builds from combinators, a rule inlined\n * once and referenced once \u2014 are invisible to `duplicates`/`nearDuplicates` unless\n * they happen to fall into the `rewrites` algebra below. Equally, two subtrees that\n * differ only in a `transform`/`node` CALLBACK are reported as distinct when the\n * callbacks' source text differs, even if the functions are equivalent. This finds\n * COPY-PASTE and mechanical redundancy. It does not find semantic redundancy, and a\n * clean report is not a proof that a grammar has none.\n *\n * `analyzeDuplication()` / `analyzeDuplicationRules()` are the programmatic\n * surface, mirroring `analyzeGating` / `analyzeGatingRules`. The compile-time\n * wiring is OPT-IN (`compile(g, { duplication: 'warn' })` /\n * `PARSEMAN_DUPLICATION=warn`) and runs on ALL THREE lowering paths \u2014 `compile`,\n * `compileRuleMap` and `compileLinkable` \u2014 because the macro build takes the\n * latter two and a diagnostic that only runs on the first is a diagnostic that\n * reports zero findings forever.\n */\nimport type { Combinator, FirstSet, ParserDef } from '../types.ts'\nimport { firstSetOf, type RefResolver } from '../combinators/first-set.ts'\nimport { firstSetToString } from './gating.ts'\n\n// \u2500\u2500 public shapes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Where a combinator sits: its owning rule, and the accessor path within it. */\nexport type Site = {\n /** Nearest enclosing `_ruleName`, or the seed name it was reached under. */\n rule: string\n /** Structural path inside that rule, e.g. `choice[0] \u203A node(Declaration) \u203A seq[4]`. */\n path: string\n}\n\nexport const siteToString = (s: Site): string => (s.path === '' ? s.rule : `${s.rule} \u203A ${s.path}`)\n\n/** N structurally identical copies of one subtree. */\nexport type DuplicateFinding = {\n kind: 'exact-duplicate'\n id: string\n /** Node count of the repeated subtree. */\n size: number\n /** How many DISTINCT combinator instances share the shape (shared-by-reference\n * reuse is not duplication and is never counted here). */\n count: number\n /** Nodes removed by hoisting the shape to one shared const: `(count - 1) * size`. */\n savings: number\n /** One-line rendering of the repeated shape. */\n shape: string\n sites: Site[]\n suggestion: string\n}\n\n/** N subtrees identical except at ONE slot. */\nexport type NearDuplicateFinding = {\n kind: 'near-duplicate'\n id: string\n /** Node count shared by every member (the scaffolding). */\n sharedSize: number\n count: number\n savings: number\n /** The scaffolding with the varying slot rendered as `\u2039slot\u203A`. */\n shape: string\n /** Path to the varying slot, relative to the members' root. */\n slotPath: string\n /** What each member puts in that slot, aligned with `sites`. */\n variants: string[]\n sites: Site[]\n suggestion: string\n}\n\n/** An alternation run re-spelled across several `regex()` terminals. */\nexport type RegexFragmentFinding = {\n kind: 'regex-fragment'\n id: string\n /** The shared run, as it appears in the sources (`>=|<=|=>|=<|=~|[<>=]`). */\n fragment: string\n /** Number of alternation branches in the run. */\n branches: number\n count: number\n /** Characters removed by hoisting: `(count - 1) * fragment.length`. */\n savings: number\n /** The full source of each regex that carries it, aligned with `sites`. */\n sources: string[]\n sites: Site[]\n suggestion: string\n}\n\n/** One spelling of a character class, and where it appears. */\nexport type RegexClassVariant = {\n /** The class as written, including any `^`: `-_a-zA-Z0-9\u0080-\uFFFF`. */\n source: string\n /** Members this spelling has that the cluster's most common spelling does not,\n * and vice versa \u2014 the DRIFT, rendered `+a-f / -0-9`. */\n delta: string\n count: number\n sites: Site[]\n}\n\n/**\n * A character class (or boundary lookahead class) re-spelled across several\n * `regex()` terminals \u2014 and, when the spellings are not identical, the drift\n * between them.\n */\nexport type RegexClassFinding = {\n kind: 'regex-class'\n id: string\n /** The cluster's most common spelling. */\n canonical: string\n /** Every spelling in the cluster, most common first. Two variants side by side\n * IS the finding: one of them is wrong and reading cannot tell you which. */\n variants: RegexClassVariant[]\n /** Distinct `regex()` terminals across the whole cluster. */\n count: number\n /** True when the cluster holds more than one spelling. */\n drifted: boolean\n /** The class's highest code point is U+FFFF and it was written as an explicit\n * range \u2014 astral-plane characters fall outside it. */\n bmpCeiling: boolean\n suggestion: string\n}\n\n/** Two arms of one `choice` whose first-sets intersect. */\nexport type ArmOverlapFinding = {\n kind: 'arm-overlap'\n id: string\n site: Site\n a: number\n b: number\n /** The SHARED first characters (not the union). */\n on: FirstSet\n /** How many leading terms the two arms spell identically. */\n sharedLeadingTerms: number\n /** Rendering of those shared leading terms, when there are any. */\n sharedPrefix: string | null\n /** True when parseman's `sharedPrefix` choice strategy already recognizes the\n * common prefix once at runtime \u2014 the finding is then about READABILITY, not\n * speed. */\n handledByStrategy: boolean\n /** Both arms lead with a `regex()` whose character classes intersect \u2014 usually\n * a sign the two arms want to be one terminal. */\n regexPair: boolean\n suggestion: string\n}\n\nexport type RewriteKind =\n | 'optional-prefix' // choice(sequence(A, R\u2026), sequence(R\u2026)) \u2192 sequence(optional(A), R\u2026)\n | 'optional-suffix' // choice(sequence(R\u2026, A), sequence(R\u2026)) \u2192 sequence(R\u2026, optional(A))\n | 'left-factor' // choice(sequence(A, B\u2026), sequence(A, C\u2026)) \u2192 sequence(A, choice(B\u2026, C\u2026))\n | 'hand-rolled-sepby' // sequence(X, many(sequence(S, X))) \u2192 sepBy(X, S)\n | 'idempotent-nesting' // optional(optional(X)), many(many(X)), \u2026\n | 'single-element' // choice(X) / sequence(X)\n | 'duplicate-arm' // choice(\u2026, X, \u2026, X, \u2026) \u2014 the later arm is dead\n | 'shadowed-arm' // choice(\u2026, X, \u2026, sequence(X, \u2026), \u2026) \u2014 the later arm is dead\n\n/** A mechanically-derived rewrite. */\nexport type RewriteFinding = {\n kind: 'rewrite'\n id: string\n rewrite: RewriteKind\n site: Site\n /** What is there now. */\n from: string\n /** What it is equal to. */\n to: string\n /**\n * `true` only when the rewrite provably cannot move the parse VALUE \u2014 which,\n * here, means it only deletes an arm that can never be selected. Every other\n * rewrite in this family changes the child arity or nesting of the value the\n * site produces, so a `node()` build / `transform` / downstream consumer that\n * reads positionally WILL see a different tree. Those are reported as\n * CANDIDATES to verify, never as \"fix this\".\n */\n astNeutral: boolean\n /** Non-empty when the rewrite removes speculative work at parse time. */\n perf: string\n /** Only on `hand-rolled-sepby`: whether this SITE can actually take `sepBy`. A\n * count of matches is not a worklist \u2014 in a real grammar most matches are\n * blocked, and reporting them all as convertible generates false work. */\n sepByVerdict?: SepByVerdict\n /** True when this is a latent BUG (an unreachable arm), not a verbosity smell. */\n bug: boolean\n suggestion: string\n}\n\n/**\n * One AST node type built by two or more STRUCTURALLY DIFFERENT productions that\n * are nonetheless variants of a single shape (they spell several of the same\n * terms). This is the clone family near-duplicate detection cannot see: the copies\n * diverge in more than one slot \u2014 extra scaffolding, a hand-rolled whitespace run,\n * a terminal guard \u2014 so no single hole explains them, yet every edit to \"the\n * declaration shape\" still has to land in all of them, and nothing checks that it did.\n */\nexport type DivergentNodeFinding = {\n kind: 'divergent-node'\n id: string\n /** The `node()` type every member produces. */\n nodeType: string\n /** Number of structurally distinct productions building it. */\n count: number\n /** Terms every member spells identically \u2014 the evidence they are one shape. */\n sharedTerms: string[]\n productions: {\n shape: string\n site: Site\n /** Terms this production has that at least one sibling does not. */\n distinctTerms: string[]\n }[]\n suggestion: string\n}\n\n/**\n * An earlier `choice` arm that FLATTENS what a later arm would have STRUCTURED.\n *\n * Both arms build the same `node()` type and can start on the same character, so\n * on the inputs both accept the earlier one wins \u2014 and its body contains no\n * `node()` at all, so the tree it produces is that node over bare leaves. The\n * later arm, on those same inputs, would have produced child nodes. Nothing about\n * this is visible in a parse that succeeds: `ok` is true, the span is right, the\n * text round-trips. Only the SHAPE moved, and only on the subset of inputs the\n * flattening arm happens to accept \u2014 which is why it survives a test suite that\n * checks \"does it parse\" and a corpus diff that checks emitted output. It shows\n * up in whatever consumes the tree: a language service that lints number tokens,\n * a formatter, a refactor.\n *\n * This is the ordered, consequential half of `divergent-node`. That finding says\n * two productions build one type and nothing keeps them in sync, and it expressly\n * allows \"the variants exist for a parse-order reason (a fast path tried first)\".\n * This one is the case where that excuse is the bug: the fast path is not\n * tree-neutral, and here is the list of node types it deletes.\n */\nexport type StructureLossFinding = {\n kind: 'structure-loss'\n id: string\n /** The `choice` that orders the two arms. */\n site: Site\n /** The `node()` type BOTH arms build. */\n nodeType: string\n /** Index of the flattening arm (the earlier one). */\n earlier: number\n /** Index of the structuring arm it shadows. */\n later: number\n /** The characters on which both arms can start \u2014 where the shadowing bites. */\n on: FirstSet\n /** Node types the later arm builds under `nodeType` and the earlier cannot. */\n lostNodeTypes: string[]\n earlierShape: string\n laterShape: string\n suggestion: string\n}\n\n/** A `regex()` that hand-rolls a keyword + word boundary. */\nexport type KeywordRegexFinding = {\n kind: 'keyword-regex'\n id: string\n site: Site\n source: string\n flags: string\n words: string[]\n /** The boundary character class, as a `word()`/`keywords()` `boundary` argument.\n * `null` when the regex enumerates a vocabulary with no guard at all. */\n boundary: string | null\n /** `/i` without `/u`: the fold class is NOT `{c, upper(c), lower(c)}`. */\n caseFoldRisk: boolean\n /** Sibling arm indices in the same `choice` that are also hand-rolled keywords. */\n siblingArms: number[]\n /**\n * >= 3 literal alternatives: a fixed VOCABULARY enumerated by hand rather than a\n * keyword with a guard. The interesting sub-case, because ordering starts to matter\n * and the list is usually long enough that nobody re-reads it.\n */\n vocabulary: boolean\n /**\n * Alternatives are in non-increasing length order. `keywords()` sorts longest-first\n * by construction; a hand-written alternation does not, and nothing checks it.\n */\n longestFirst: boolean\n /**\n * Earlier alternatives that are strict PREFIXES of later ones. Regex alternation is\n * first-match, so the longer branch is reachable only when a trailing boundary guard\n * rejects the following character and forces a backtrack (`rescuedByBoundary`).\n */\n hazards: { shorter: string; longer: string; at: string; rescuedByBoundary: boolean }[]\n /**\n * At least one hazard is NOT rescued by a boundary guard \u2014 a later alternative can\n * never match. That is a live bug, not a cleanup.\n */\n bug: boolean\n suggestion: string\n}\n\nexport type DuplicationReport = {\n duplicates: DuplicateFinding[]\n nearDuplicates: NearDuplicateFinding[]\n regexFragments: RegexFragmentFinding[]\n regexClasses: RegexClassFinding[]\n overlaps: ArmOverlapFinding[]\n rewrites: RewriteFinding[]\n divergentNodes: DivergentNodeFinding[]\n structureLoss: StructureLossFinding[]\n keywordRegexes: KeywordRegexFinding[]\n /** Ids listed in `accept` that matched no finding \u2014 stale entries to prune. */\n acceptedUnused: string[]\n stats: {\n rules: number\n /** Distinct combinator INSTANCES reached. */\n nodes: number\n /** Distinct structural shapes among them. */\n shapes: number\n }\n}\n\nexport type AnalyzeDuplicationOptions = {\n /** Smallest repeated subtree worth reporting, in nodes. Default 3 \u2014 which is\n * what keeps `optional(ws)` (2 nodes) out of the ranking. */\n minSize?: number\n /** Cap per category, applied AFTER ranking. Default 25. */\n maxFindings?: number\n /** Finding `id`s to suppress \u2014 the single per-finding acknowledgement channel,\n * mirroring the gating snapshot allowlist. */\n accept?: Iterable<string>\n /** Bind cross-artifact `g.Foo` holes when computing first-sets for `overlaps`. */\n resolveRef?: RefResolver\n /** Name to attribute an unnamed entry to, instead of `<entry>`. */\n entryName?: string\n}\n\n// \u2500\u2500 canonical structural hashing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Ordered structural children per def tag. Explicit rather than \"every key that\n * holds a Combinator\" because a SLOT's position is what near-duplicate detection\n * varies \u2014 a stable, meaningful order is load-bearing, not cosmetic.\n */\nfunction childrenOf(d: ParserDef): readonly Combinator<unknown>[] {\n switch (d.tag) {\n case 'sequence': case 'choice': return d.parsers\n case 'dispatch': return [\n d.selector,\n ...d.cases.map(c => c.parser),\n ...(d.otherwise === undefined ? [] : [d.otherwise]),\n ]\n case 'skip': return [d.main, d.skipped]\n case 'sepBy': return [d.parser, d.separator]\n case 'recover': return [d.parser, d.sentinel]\n case 'scanTo': return [d.sentinel, ...d.skip]\n case 'grammar': return d.triviaParser ? [d.parser, d.triviaParser] : [d.parser]\n // A `lazy` is a REFERENCE, not a subtree: hashing through it would make every\n // rule's shape include the whole reachable grammar. Treated as a leaf, keyed\n // by the rule name it refers to \u2014 which is also the right semantics, since two\n // productions referencing `g.Ident` really do fill that slot the same way.\n case 'lazy': case 'literal': case 'regex': case 'keywords': case 'guard': case 'unknown':\n return []\n default: {\n const rec = d as unknown as { parser?: Combinator<unknown> }\n return rec.parser ? [rec.parser] : []\n }\n }\n}\n\nconst ruleNameOf = (p: Combinator<unknown>): string | undefined =>\n (p as unknown as { _ruleName?: string })._ruleName\n\n/** Source text of a callback, so two COPY-PASTED lambdas hash alike (identity\n * would make every copy distinct and defeat the whole diagnostic). */\nconst fnKey = (src: string | undefined, fn: unknown): string =>\n src ?? (typeof fn === 'function' ? String(fn) : '')\n\n/** The discriminating payload of a node, excluding its children. */\nfunction payloadKey(p: Combinator<unknown>, d: ParserDef): string {\n switch (d.tag) {\n case 'literal': return `literal\\u0000${d.value}\\u0000${d.caseInsensitive}`\n case 'regex': return `regex\\u0000${d.source}\\u0000${d.flags}`\n case 'keywords': return `keywords\\u0000${[...d.words].join('\\u0001')}\\u0000${d.caseInsensitive}\\u0000${d.boundary ?? ''}`\n case 'lazy': {\n const n = ruleNameOf(p)\n return n === undefined ? `ref\\u0000#${anonId(p)}` : `ref\\u0000${n}`\n }\n case 'label': return `label\\u0000${d.label}`\n case 'field': return `field\\u0000${d.name}`\n case 'node': return `node\\u0000${d.type ?? ''}\\u0000${d.unwrap === true}\\u0000${d.collapse === true}\\u0000${fnKey(d.buildSrc, d.build)}`\n case 'transform': return `transform\\u0000${fnKey(d.fnSrc, d.fn)}\\u0000${d.recognitionOnly === true}`\n case 'leaf': return `leaf\\u0000${fnKey(d.fnSrc, d.fn)}`\n case 'many': case 'oneOrMore': return `${d.tag}\\u0000${d.min}\\u0000${d.max ?? ''}`\n case 'sepBy': return `sepBy\\u0000${d.min}\\u0000${d.max ?? ''}\\u0000${d.trailing ?? ''}`\n case 'dispatch': return `dispatch\\u0000${d.cases.map(c => JSON.stringify(c.keys)).join('\\u0002')}\\u0000${d.otherwise !== undefined}`\n case 'expect': return `expect\\u0000${d.label ?? ''}\\u0000${d.expected.join('\\u0001')}`\n case 'scanTo': return `scanTo\\u0000${d.raw}\\u0000${d.orEOF}`\n case 'guard': return `guard\\u0000${fnKey(d.predSrc, d.predicate)}`\n case 'withCtx': return `withCtx\\u0000${d.extraSrc ?? safeJson(d.extra)}`\n case 'grammar': return `grammar\\u0000${d.clearTrivia === true}\\u0000${d.captureTrivia === true}\\u0000${d.trackLines}`\n default: return d.tag\n }\n}\n\nfunction safeJson(v: unknown): string {\n try { return JSON.stringify(v) ?? String(v) } catch { return '?' }\n}\n\nlet anonCounter = 0\nconst anonIds = new WeakMap<object, number>()\n/** Identity key for an UNNAMED unresolvable ref: nothing can bind it by name, so\n * two of them are the same slot only when they are the same object. */\nfunction anonId(p: object): number {\n let id = anonIds.get(p)\n if (id === undefined) { id = ++anonCounter; anonIds.set(p, id) }\n return id\n}\n\n/**\n * Interning gives EXACT structural equality with short keys: a node's key is its\n * payload plus the interned ids of its children, so equal ids \u21D4 equal subtrees,\n * with no hash collisions to reason about.\n */\nclass Interner {\n private readonly ids = new Map<string, number>()\n readonly hole: number\n constructor() { this.hole = this.of('\\u0000HOLE\\u0000') }\n of(key: string): number {\n let id = this.ids.get(key)\n if (id === undefined) { id = this.ids.size; this.ids.set(key, id) }\n return id\n }\n}\n\n// \u2500\u2500 rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst MAX_RENDER = 160\n\n/** A compact one-line rendering of a subtree, for the finding text. */\nfunction render(p: Combinator<unknown>, depth = 3): string {\n const d = p._def as ParserDef\n const name = ruleNameOf(p)\n switch (d.tag) {\n case 'literal': return `literal('${d.value}')`\n case 'regex': return `regex(/${d.source}/${d.flags})`\n case 'keywords': return `keywords([${[...d.words].slice(0, 4).map(w => `'${w}'`).join(', ')}${d.words.length > 4 ? ', \u2026' : ''}])`\n case 'lazy': return `g.${name ?? '?'}`\n case 'guard': return 'gate(\u2026)'\n default: break\n }\n if (depth <= 0) return `${d.tag}(\u2026)`\n const kids = childrenOf(d).map(k => render(k, depth - 1))\n switch (d.tag) {\n case 'sequence': return `sequence(${kids.join(', ')})`\n case 'choice': return `choice(${kids.join(', ')})`\n case 'dispatch': {\n const arms = d.cases.map((c, i) => `when([${c.keys.map(k => JSON.stringify(k)).join(', ')}], ${kids[i + 1] ?? ''})`)\n if (d.otherwise !== undefined) arms.push(`otherwise(${kids[kids.length - 1] ?? ''})`)\n return `dispatch(${kids[0] ?? ''}, ${arms.join(', ')})`\n }\n case 'node': return `node(${d.type ? `'${d.type}', ` : ''}${kids[0] ?? ''})`\n case 'label': return `label('${d.label}', ${kids[0] ?? ''})`\n case 'field': return `field('${d.name}', ${kids[0] ?? ''})`\n case 'many': return `many(${kids[0] ?? ''}${d.min === 0 && d.max === undefined ? '' : `, { min: ${d.min}${d.max === undefined ? '' : `, max: ${d.max}`} }`})`\n case 'oneOrMore': return `oneOrMore(${kids[0] ?? ''}${d.min === 1 && d.max === undefined ? '' : `, { min: ${d.min}${d.max === undefined ? '' : `, max: ${d.max}`} }`})`\n case 'sepBy': return `sepBy(${kids[0] ?? ''}, ${kids[1] ?? ''})`\n default: return `${d.tag}(${kids.join(', ')})`\n }\n}\n\nconst clamp = (s: string): string => (s.length <= MAX_RENDER ? s : `${s.slice(0, MAX_RENDER - 1)}\u2026`)\n\n/** Path segment for a child index, used to build `Site.path` and slot paths. */\nfunction segment(d: ParserDef, index: number): string {\n switch (d.tag) {\n case 'sequence': return `seq[${index}]`\n case 'choice': return `choice[${index}]`\n case 'dispatch':\n if (index === 0) return 'dispatch.selector'\n if (index <= d.cases.length) return `dispatch.when[${index - 1}]`\n return 'dispatch.otherwise'\n case 'node': return `node(${d.type ?? ''})`\n case 'label': return `label(${d.label})`\n case 'field': return `field(${d.name})`\n case 'sepBy': return index === 0 ? 'sepBy.item' : 'sepBy.sep'\n case 'skip': return index === 0 ? 'skip.main' : 'skip.skipped'\n case 'recover': return index === 0 ? 'recover.body' : 'recover.sentinel'\n case 'scanTo': return index === 0 ? 'scanTo.sentinel' : `scanTo.skip[${index - 1}]`\n default: return d.tag\n }\n}\n\n// \u2500\u2500 regex source: alternation runs + keyword shape \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Every alternation branch list in a regex source, at ANY nesting depth. Depth\n * matters: `/a|b/` and `/x(?:a|b)y/` re-spell the same run, and only one of them\n * has it at top level.\n */\nexport function alternationGroups(src: string): string[][] {\n const out: string[][] = []\n type Frame = { branches: string[]; last: number }\n const stack: Frame[] = [{ branches: [], last: 0 }]\n let i = 0\n while (i < src.length) {\n const c = src[i]!\n if (c === '\\\\') { i += 2; continue }\n if (c === '[') {\n let j = i + 1\n if (src[j] === '^') j++\n if (src[j] === ']') j++\n while (j < src.length && src[j] !== ']') { if (src[j] === '\\\\') j++; j++ }\n i = j + 1\n continue\n }\n if (c === '(') {\n let open = i + 1\n if (src[open] === '?') {\n const rest = src.slice(open)\n const m = /^\\?(?::|=|!|<=|<!|<[^>]*>)/.exec(rest)\n open += m ? m[0].length : 1\n }\n stack.push({ branches: [], last: open })\n i = open\n continue\n }\n if (c === ')') {\n const top = stack.pop()\n if (top !== undefined) {\n top.branches.push(src.slice(top.last, i))\n if (top.branches.length >= 2) out.push(top.branches)\n }\n i++\n continue\n }\n if (c === '|') {\n const top = stack[stack.length - 1]!\n top.branches.push(src.slice(top.last, i))\n top.last = i + 1\n i++\n continue\n }\n i++\n }\n const root = stack[0]!\n root.branches.push(src.slice(root.last))\n if (root.branches.length >= 2) out.push(root.branches)\n return out\n}\n\n/**\n * A LITERAL word: letters, digits, `_`, `-`, optionally vendor-prefixed. No\n * character class, no quantifier, no escape, no group \u2014 anything with regex\n * machinery in it is a pattern, not a vocabulary entry, and is left alone.\n */\nconst WORD_RE_SRC = '-?[A-Za-z][A-Za-z0-9_-]*'\nconst WORDS_RE = new RegExp(`^${WORD_RE_SRC}(?:\\\\|${WORD_RE_SRC})*$`)\n\n/**\n * How many literal alternatives make a regex a VOCABULARY rather than a pattern,\n * when there is no boundary guard to mark it as a keyword. With a guard, one word\n * is already enough (`regex(/not(?![-\\w])/)`); without one, two alternatives are\n * as likely to be an ordinary either/or as a keyword set, so the bar is three.\n */\nconst VOCABULARY_MIN_WORDS = 3\n\n/**\n * Recognize a `regex()` that hand-rolls what `word()`/`keywords()` owns.\n *\n * TWO shapes, because they fail differently:\n *\n * - a word (or word alternation) plus a trailing word-boundary guard \u2014\n * `regex(/not(?![-\\w])/i)`;\n * - a bare alternation of >= 3 literal words with NO guard \u2014 a fixed vocabulary\n * enumerated by hand. A regex enumerating a fixed vocabulary is a keyword set\n * written the hard way: it loses first-set gating, it hand-maintains an ordering\n * the combinator guarantees, and with `/i` and no `/u` it inherits the non-ASCII\n * case-folding bug `keywords()` fixes internally. The no-guard form is also where\n * ordering is load-bearing, since nothing backtracks past a shorter match.\n *\n * Returns the words and the boundary CLASS in the exact form `word(str, boundary)` /\n * `keywords(words, { boundary })` take, so the suggestion names a real call rather\n * than describing one.\n */\nexport function keywordRegexShape(source: string): { words: string[]; boundary: string | null } | null {\n let s = source\n if (s.startsWith('^')) s = s.slice(1)\n // A leading `(?<![-\\w])` is the same boundary on the other side \u2014 word()/keywords()\n // do not emit it, but its presence does not stop the body from being a keyword.\n s = s.replace(/^\\(\\?<!\\[[^\\]]+\\]\\)/, '')\n let boundary: string | null = null\n // Peel a trailing boundary guard.\n const tail = /(?:\\(\\?!\\[([^\\]]+)\\]\\)|\\\\b)$/.exec(s)\n if (tail !== null) {\n const cls = tail[1]\n // `(?![^-\\w])` is NOT a boundary guard \u2014 a negative lookahead of a NEGATED\n // class asserts the opposite, that the next char IS in the class. The capture\n // keeps the leading `^`, so passing it on would hand `word(str, boundary)` a\n // class meaning the reverse, and `boundaryMatches` would read `^` as a literal\n // member and return the inverted verdict. `bug` is derived from unrescued\n // hazards, so that inversion can invent or hide a BUG in error mode. The shape\n // is simply not expressible as a `boundary` argument: reject it.\n if (cls !== undefined && cls.startsWith('^')) return null\n boundary = cls ?? '_0-9A-Za-z'\n s = s.slice(0, tail.index)\n }\n // Unwrap one redundant non-capturing group around the word set.\n const grouped = /^\\(\\?:([^()]*)\\)$/.exec(s)\n if (grouped !== null) s = grouped[1]!\n if (s.endsWith('$')) s = s.slice(0, -1)\n if (!WORDS_RE.test(s)) return null\n const words = s.split('|')\n if (boundary === null && words.length < VOCABULARY_MIN_WORDS) return null\n return { words, boundary }\n}\n\n/** Does a boundary class (`-\\w`, `_0-9A-Za-z`, \u2026) match this character? */\nfunction boundaryMatches(boundary: string, ch: string): boolean {\n const cp = ch.codePointAt(0)!\n for (const m of charClassMembers(boundary)) {\n if (m === '\\\\w') { if (/[A-Za-z0-9_]/.test(ch)) return true; continue }\n if (m === '\\\\d') { if (/[0-9]/.test(ch)) return true; continue }\n if (m === '\\\\s') { if (/\\s/.test(ch)) return true; continue }\n const range = /^(.|\\\\u[0-9a-f]{4})-(.|\\\\u[0-9a-f]{4})$/.exec(m)\n if (range !== null) {\n const dec = (t: string): number => (t.startsWith('\\\\u') ? parseInt(t.slice(2), 16) : t.codePointAt(0)!)\n if (cp >= dec(range[1]!) && cp <= dec(range[2]!)) return true\n continue\n }\n if (m === ch) return true\n }\n return false\n}\n\n/**\n * Ordering hazards in a hand-written alternation: an EARLIER alternative that is a\n * strict prefix of a LATER one. Regex alternation is first-match, not longest-match,\n * so `red|redish` matches only `red` \u2014 unless a trailing boundary guard rejects the\n * character that follows, which makes the engine backtrack into the longer branch.\n *\n * That distinction is the whole point of reporting this: without a rescuing guard the\n * longer word is UNREACHABLE, which is a live bug, not a style finding. `keywords()`\n * sorts longest-first by construction and the hazard cannot exist there at all.\n */\nexport function keywordAlternationHazards(\n words: readonly string[],\n boundary: string | null,\n): { shorter: string; longer: string; at: string; rescuedByBoundary: boolean }[] {\n const out: { shorter: string; longer: string; at: string; rescuedByBoundary: boolean }[] = []\n for (let i = 0; i < words.length; i++) {\n for (let j = i + 1; j < words.length; j++) {\n const a = words[i]!, b = words[j]!\n if (a.length >= b.length || !b.startsWith(a)) continue\n const at = b[a.length]!\n out.push({ shorter: a, longer: b, at, rescuedByBoundary: boundary !== null && boundaryMatches(boundary, at) })\n }\n }\n return out\n}\n\n// \u2500\u2500 the walk \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function analyzeDuplication(\n entry: Combinator<unknown>,\n opts?: AnalyzeDuplicationOptions,\n): DuplicationReport {\n return analyzeDuplicationRules([[ruleNameOf(entry) ?? opts?.entryName ?? '<entry>', entry]], opts)\n}\n\ntype Visited = {\n p: Combinator<unknown>\n d: ParserDef\n site: Site\n shape: number\n size: number\n /** Nearest enclosing `node()` type, if any. A rewrite under one changes the\n * children that node's build fn sees, which is the AST-neutrality question. */\n enclosingNode: string | null\n /** An enclosing `node()`/`transform()`/`leaf()` carries a REDUCER \u2014 a callback\n * that reads the children array. Any rewrite changing that array's length or\n * stride changes what the callback sees. */\n enclosingReducer: boolean\n}\n\n/**\n * The analysis input is the COMBINATOR TREE \u2014 a `rules()` map, or the map handed to\n * `compileRuleMap`/`compileLinkable`. It is NOT the value `compose()` returns: that\n * is a fused, already-compiled artifact whose entries are emitted parse FUNCTIONS\n * with no `_def` to walk.\n *\n * This is checked, loudly, because the alternative failure mode is the one that\n * matters. `analyzeGating()` handed a composed artifact throws deep inside its\n * walker on the first descriptor-less node \u2014 and a diagnostic that can throw or\n * silently report zero on exactly the grammars it exists to serve is worse than no\n * diagnostic, because a clean run reads as \"no findings\" instead of \"saw nothing\".\n * Silence is not a permitted outcome here.\n */\nfunction assertAnalyzable(ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>): void {\n for (const [name, rule] of ruleMap) {\n const def = (rule as { _def?: unknown } | null | undefined)?._def\n if (def !== null && typeof def === 'object' && typeof (def as { tag?: unknown }).tag === 'string') continue\n throw new TypeError(\n `analyzeDuplicationRules: rule '${name}' is not a combinator (no _def). ` +\n `This analysis walks the COMBINATOR TREE; the value returned by compose()/composeLeaf() is a ` +\n `fused, already-compiled artifact whose entries are parse functions. Pass the rules() map itself ` +\n `(or the map given to compileRuleMap/compileLinkable) instead \u2014 for a composed grammar, analyze ` +\n `each piece's own rules() map.`,\n )\n }\n}\n\nexport function analyzeDuplicationRules(\n ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>,\n opts?: AnalyzeDuplicationOptions,\n): DuplicationReport {\n assertAnalyzable(ruleMap)\n const minSize = opts?.minSize ?? 3\n const maxFindings = opts?.maxFindings ?? 25\n const interner = new Interner()\n\n const shapeCache = new Map<Combinator<unknown>, number>()\n const sizeCache = new Map<Combinator<unknown>, number>()\n\n const shapeOf = (p: Combinator<unknown>): number => {\n const hit = shapeCache.get(p)\n if (hit !== undefined) return hit\n const d = p._def as ParserDef\n // Seed against self-reference before recursing (a `lazy` is a leaf, so a true\n // cycle cannot form here \u2014 but a defensive seed keeps a malformed def finite).\n shapeCache.set(p, interner.hole)\n const kids = childrenOf(d)\n const id = interner.of(`${payloadKey(p, d)}|${kids.map(shapeOf).join(',')}`)\n shapeCache.set(p, id)\n return id\n }\n const sizeOf = (p: Combinator<unknown>): number => {\n const hit = sizeCache.get(p)\n if (hit !== undefined) return hit\n sizeCache.set(p, 1)\n let n = 1\n for (const k of childrenOf(p._def as ParserDef)) n += sizeOf(k)\n sizeCache.set(p, n)\n return n\n }\n\n // \u2500\u2500 traverse: one entry per distinct combinator INSTANCE \u2500\u2500\n const visited = new Map<Combinator<unknown>, Visited>()\n const visit = (p: Combinator<unknown>, rule: string, path: string, enclosingNode: string | null, enclosingReducer: boolean): void => {\n if (visited.has(p)) return\n const d = p._def as ParserDef\n const own = ruleNameOf(p)\n const here = own !== undefined && own !== rule ? { rule: own, path: '' } : { rule, path }\n visited.set(p, { p, d, site: here, shape: shapeOf(p), size: sizeOf(p), enclosingNode, enclosingReducer })\n const inner = d.tag === 'node' ? d.type ?? '(anonymous)' : enclosingNode\n const innerReducer = enclosingReducer\n || (d.tag === 'node' && (d.build !== undefined || d.buildSrc !== undefined))\n || d.tag === 'transform' || d.tag === 'leaf'\n const kids = childrenOf(d)\n kids.forEach((k, i) => {\n const seg = segment(d, i)\n visit(k, here.rule, here.path === '' ? seg : `${here.path} \u203A ${seg}`, inner, innerReducer)\n })\n // Follow refs so a rule reachable only through one is still attributed \u2014 under\n // its OWN name, because the ref carries `_ruleName`.\n if (d.tag === 'lazy') {\n try {\n const target = (d as { thunk(): Combinator<unknown> }).thunk()\n visit(target, ruleNameOf(target) ?? ruleNameOf(p) ?? here.rule, '', null, false)\n } catch { /* unresolved cross-artifact hole */ }\n }\n }\n for (const [name, root] of ruleMap) visit(root, ruleNameOf(root) ?? name, '', null, false)\n\n const accept = new Set(opts?.accept ?? [])\n const used = new Set<string>()\n const keep = <T extends { id: string }>(f: T): boolean => {\n if (!accept.has(f.id)) return true\n used.add(f.id)\n return false\n }\n\n const duplicates = findExactDuplicates(visited, minSize)\n const nearDuplicates = findNearDuplicates(visited, interner, shapeOf, minSize)\n const regexFragments = findRegexFragments(visited)\n const regexClasses = findRegexClasses(visited)\n const overlaps = findOverlaps(visited, shapeOf, opts?.resolveRef)\n const rewrites = findRewrites(visited, shapeOf)\n const divergentNodes = findDivergentNodes(visited, shapeOf)\n const structureLoss = findStructureLoss(visited, opts?.resolveRef)\n const keywordRegexes = findKeywordRegexes(visited)\n\n // Subsumption, WITHIN each category: a 40-node clone family otherwise reports\n // once at its root and again at every internal node, since each of those is\n // repeated exactly as often. Deliberately NOT applied across categories \u2014 a\n // near-duplicate family and an exact duplicate inside it are two different facts\n // about the same code, and suppressing the second because the first happens to\n // rank higher hides a real repetition behind a maybe-refactor.\n const subsume = <T extends { id: string; savings: number; _members: Combinator<unknown>[] }>(fs: T[]): Set<string> => {\n const covered = new Set<Combinator<unknown>>()\n const surviving = new Set<string>()\n for (const f of [...fs].sort((a, b) => b.savings - a.savings)) {\n if (f._members.every(m => covered.has(m))) continue\n surviving.add(f.id)\n for (const m of f._members) markSubtree(m, covered)\n }\n return surviving\n }\n const survivingExact = subsume(duplicates)\n const survivingNear = subsume(nearDuplicates)\n\n const report: DuplicationReport = {\n duplicates: duplicates.filter(f => survivingExact.has(f.id)).filter(keep).slice(0, maxFindings).map(stripMembers),\n nearDuplicates: nearDuplicates.filter(f => survivingNear.has(f.id)).filter(keep).slice(0, maxFindings).map(stripMembers),\n regexFragments: regexFragments.filter(keep).slice(0, maxFindings),\n regexClasses: regexClasses.filter(keep).slice(0, maxFindings),\n overlaps: overlaps.filter(keep).slice(0, maxFindings),\n rewrites: rewrites.filter(keep).slice(0, maxFindings),\n divergentNodes: divergentNodes.filter(keep).slice(0, maxFindings),\n structureLoss: structureLoss.filter(keep).slice(0, maxFindings),\n keywordRegexes: keywordRegexes.filter(keep).slice(0, maxFindings),\n acceptedUnused: [...accept].filter(id => !used.has(id)),\n stats: { rules: ruleMap.length, nodes: visited.size, shapes: new Set([...visited.values()].map(v => v.shape)).size },\n }\n return report\n}\n\nfunction markSubtree(p: Combinator<unknown>, into: Set<Combinator<unknown>>): void {\n if (into.has(p)) return\n into.add(p)\n for (const k of childrenOf(p._def as ParserDef)) markSubtree(k, into)\n}\n\ntype WithMembers<T> = T & { _members: Combinator<unknown>[] }\nconst stripMembers = <T extends object>(f: WithMembers<T>): T => {\n const { _members, ...rest } = f\n void _members\n return rest as T\n}\n\n// \u2500\u2500 1. exact duplicates \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction findExactDuplicates(\n visited: Map<Combinator<unknown>, Visited>,\n minSize: number,\n): WithMembers<DuplicateFinding>[] {\n const byShape = new Map<number, Visited[]>()\n for (const v of visited.values()) {\n if (v.size < minSize) continue\n const list = byShape.get(v.shape)\n if (list === undefined) byShape.set(v.shape, [v]); else list.push(v)\n }\n const out: WithMembers<DuplicateFinding>[] = []\n for (const group of byShape.values()) {\n if (group.length < 2) continue\n const size = group[0]!.size\n out.push({\n kind: 'exact-duplicate',\n id: `exact-duplicate:${siteToString(group[0]!.site)}`,\n size,\n count: group.length,\n savings: (group.length - 1) * size,\n shape: clamp(render(group[0]!.p)),\n sites: group.map(g => g.site),\n suggestion: `${group.length} DISTINCT instances build this identical ${size}-node shape. Bind it to one const and reference that \u2014 hoisting removes ${(group.length - 1) * size} nodes and makes a future edit land in one place instead of ${group.length}.`,\n _members: group.map(g => g.p),\n })\n }\n return out.sort((a, b) => b.savings - a.savings)\n}\n\n// \u2500\u2500 2. near duplicates (identical but for one slot) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * \"Same shape, one differing slot\" is decided by HOLE KEYS: the key of a subtree\n * with one descendant position blanked. Two subtrees that differ at exactly one\n * position share exactly the hole key that blanks it, so grouping by hole key and\n * keeping the groups whose members' full shapes DIFFER is precisely the family.\n *\n * The root hole is excluded \u2014 blanking the root makes every subtree in the grammar\n * a \"near duplicate\" of every other.\n */\nfunction findNearDuplicates(\n visited: Map<Combinator<unknown>, Visited>,\n interner: Interner,\n shapeOf: (p: Combinator<unknown>) => number,\n minSize: number,\n): WithMembers<NearDuplicateFinding>[] {\n const holeCache = new Map<Combinator<unknown>, number[]>()\n const holesOf = (p: Combinator<unknown>): number[] => {\n const hit = holeCache.get(p)\n if (hit !== undefined) return hit\n holeCache.set(p, [])\n const d = p._def as ParserDef\n const kids = childrenOf(d)\n const kidShapes = kids.map(shapeOf)\n const payload = payloadKey(p, d)\n const out: number[] = []\n kids.forEach((k, i) => {\n for (const inner of [interner.hole, ...holesOf(k)]) {\n const arr = kidShapes.slice()\n arr[i] = inner\n out.push(interner.of(`${payload}|${arr.join(',')}`))\n }\n })\n holeCache.set(p, out)\n return out\n }\n\n const byHole = new Map<number, Visited[]>()\n for (const v of visited.values()) {\n if (v.size < minSize) continue\n for (const h of holesOf(v.p)) {\n const list = byHole.get(h)\n if (list === undefined) byHole.set(h, [v]); else list.push(v)\n }\n }\n\n const out: WithMembers<NearDuplicateFinding>[] = []\n const seenGroups = new Set<string>()\n for (const group of byHole.values()) {\n if (group.length < 2) continue\n // Exact duplicates share every hole key \u2014 they are the OTHER family.\n const shapes = new Set(group.map(g => g.shape))\n if (shapes.size < 2) continue\n // One representative per distinct shape: N copies of variant A and M of variant\n // B is one clone family with two variants, not N\u00D7M findings.\n const perShape = new Map<number, Visited>()\n for (const g of group) if (!perShape.has(g.shape)) perShape.set(g.shape, g)\n const members = [...perShape.values()]\n const key = members.map(m => m.shape).sort((a, b) => a - b).join(',')\n if (seenGroups.has(key)) continue\n seenGroups.add(key)\n const slot = findDivergentSlot(members.map(m => m.p), shapeOf)\n if (slot === null) continue\n const sharedSize = Math.min(...members.map(m => m.size)) - slot.slotSize\n if (sharedSize < minSize) continue\n out.push({\n kind: 'near-duplicate',\n id: `near-duplicate:${siteToString(members[0]!.site)}`,\n sharedSize,\n count: members.length,\n savings: (members.length - 1) * sharedSize,\n shape: clamp(renderWithHole(members[0]!.p, slot.path)),\n slotPath: slot.path.map((i, k) => segment(slot.defs[k]!, i)).join(' \u203A '),\n variants: slot.variants.map(v => clamp(render(v))),\n sites: members.map(m => m.site),\n suggestion: `${members.length} productions share this ${sharedSize}-node scaffolding and differ ONLY at \\`${slot.path.map((i, k) => segment(slot.defs[k]!, i)).join(' \u203A ')}\\`. That is one production whose varying slot is a \\`choice(${slot.variants.map(v => clamp(render(v, 1))).join(', ')})\\` \u2014 not ${members.length} copies of the scaffolding. Re-check the AST: a single production emits one node type where the clones may emit several.`,\n _members: members.map(m => m.p),\n })\n }\n return out.sort((a, b) => b.savings - a.savings)\n}\n\n/** The single child position at which the given subtrees diverge, if there is one. */\nfunction findDivergentSlot(\n members: readonly Combinator<unknown>[],\n shapeOf: (p: Combinator<unknown>) => number,\n): { path: number[]; defs: ParserDef[]; variants: Combinator<unknown>[]; slotSize: number } | null {\n const path: number[] = []\n const defs: ParserDef[] = []\n let cur = [...members]\n for (let guard = 0; guard < 64; guard++) {\n const d0 = cur[0]!._def as ParserDef\n const kidLists = cur.map(c => childrenOf(c._def as ParserDef))\n const arity = kidLists[0]!.length\n if (kidLists.some(k => k.length !== arity) || arity === 0) break\n const diff: number[] = []\n for (let i = 0; i < arity; i++) {\n const s = shapeOf(kidLists[0]![i]!)\n if (kidLists.some(k => shapeOf(k[i]!) !== s)) diff.push(i)\n }\n if (diff.length !== 1) break\n const i = diff[0]!\n path.push(i)\n defs.push(d0)\n cur = kidLists.map(k => k[i]!)\n // Descend while the divergence is still a single slot; stop when the subtrees\n // are wholly different (that IS the varying slot).\n const nextArity = childrenOf(cur[0]!._def as ParserDef).length\n const samePayload = cur.every(c => payloadKey(c, c._def as ParserDef) === payloadKey(cur[0]!, cur[0]!._def as ParserDef))\n if (!samePayload || nextArity === 0) break\n }\n if (path.length === 0) return null\n let slotSize = 0\n const sizeOf = (p: Combinator<unknown>): number => {\n let n = 1\n for (const k of childrenOf(p._def as ParserDef)) n += sizeOf(k)\n return n\n }\n for (const c of cur) slotSize = Math.max(slotSize, sizeOf(c))\n return { path, defs, variants: cur, slotSize }\n}\n\nfunction renderWithHole(p: Combinator<unknown>, path: readonly number[], depth = 0): string {\n if (depth === path.length) return '\\u2039slot\\u203a'\n const d = p._def as ParserDef\n const kids = childrenOf(d)\n const idx = path[depth]!\n const parts = kids.map((k, i) => (i === idx ? renderWithHole(k, path, depth + 1) : render(k, 2)))\n switch (d.tag) {\n case 'sequence': return `sequence(${parts.join(', ')})`\n case 'choice': return `choice(${parts.join(', ')})`\n case 'node': return `node(${d.type ? `'${d.type}', ` : ''}${parts[0] ?? ''})`\n default: return `${d.tag}(${parts.join(', ')})`\n }\n}\n\n// \u2500\u2500 3. regex alternation fragments \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Longest contiguous branch window compared across regexes. Windows shorter than\n * the true maximal shared run are dropped by the containment pass below, so this\n * only has to be long enough to REACH the maximal run \u2014 a whole shared alternation\n * list is additionally offered as one candidate, however long it is. */\nconst MAX_RUN = 32\n\nfunction findRegexFragments(visited: Map<Combinator<unknown>, Visited>): RegexFragmentFinding[] {\n type Occurrence = { site: Site; source: string }\n const byRun = new Map<string, { occ: Occurrence[]; nodes: Set<Combinator<unknown>>; branches: number }>()\n for (const v of visited.values()) {\n const d = v.d\n if (d.tag !== 'regex') continue\n const runs = new Set<string>()\n for (const branches of alternationGroups(d.source)) {\n if (branches.length >= 2) runs.add(branches.join('|'))\n for (let i = 0; i < branches.length; i++) {\n for (let n = 2; n <= Math.min(MAX_RUN, branches.length - i); n++) {\n const run = branches.slice(i, i + n).join('|')\n if (run.length < 4) continue\n runs.add(run)\n }\n }\n }\n for (const run of runs) {\n let e = byRun.get(run)\n if (e === undefined) { e = { occ: [], nodes: new Set(), branches: run.split('|').length }; byRun.set(run, e) }\n if (e.nodes.has(v.p)) continue\n e.nodes.add(v.p)\n e.occ.push({ site: v.site, source: d.source })\n }\n }\n const candidates = [...byRun.entries()]\n .filter(([, e]) => e.occ.length >= 2)\n .map(([run, e]) => ({ run, e, savings: (e.occ.length - 1) * run.length }))\n // COUNT first: \"re-spelled in 7 terminals\" is the headline \u2014 it is the number\n // of places an edit has to land, and the number of chances for one of them to\n // drift. Length only breaks ties.\n .sort((a, b) => b.e.occ.length - a.e.occ.length || b.savings - a.savings || b.run.length - a.run.length)\n\n // Keep only MAXIMAL runs: a 6-branch run that occurs 7\u00D7 makes its own 5-branch\n // sub-runs (also 7\u00D7) noise.\n const kept: typeof candidates = []\n for (const c of candidates) {\n if (kept.some(k => k.e.occ.length >= c.e.occ.length && (`|${k.run}|`).includes(`|${c.run}|`))) continue\n kept.push(c)\n }\n return kept.map(({ run, e }) => ({\n kind: 'regex-fragment' as const,\n id: `regex-fragment:${run}`,\n fragment: run,\n branches: e.branches,\n count: e.occ.length,\n savings: (e.occ.length - 1) * run.length,\n sources: e.occ.map(o => `/${o.source}/`),\n sites: e.occ.map(o => o.site),\n suggestion: `this ${e.branches}-branch alternation is re-spelled in ${e.occ.length} DISTINCT regex() terminals. Hoist it to one shared source string and interpolate it (\\`new RegExp(\\\\\\`\u2026\\\\\\${OP}\u2026\\\\\\`)\\`) or lift the shared part into its own terminal \u2014 an edit to the operator set currently has to land in ${e.occ.length} places, and the analysis cannot tell you when one of them drifts.`,\n }))\n}\n\n// \u2500\u2500 3b. character classes across regexes (and their DRIFT) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Every `[...]` in a regex source, INCLUDING the ones inside boundary lookaheads\n * (`(?![-\\w])` yields `-\\w`) \u2014 those are the same duplication in a different\n * costume, and the same drift risk.\n */\nexport function extractCharClasses(src: string): string[] {\n const out: string[] = []\n let i = 0\n while (i < src.length) {\n const c = src[i]!\n if (c === '\\\\') { i += 2; continue }\n if (c !== '[') { i++; continue }\n let j = i + 1\n if (src[j] === '^') j++\n if (src[j] === ']') j++\n while (j < src.length && src[j] !== ']') { if (src[j] === '\\\\') j++; j++ }\n out.push(src.slice(i + 1, j))\n i = j + 1\n }\n return out\n}\n\n/**\n * Split a class body into normalized MEMBERS (`a-z`, `\\w`, `-`, `\u0080-\uFFFF`).\n * Non-ASCII literals are rewritten to `\\uXXXX` so a class typed with a raw `\uFFFF`\n * and one typed with `\uFFFF` compare equal \u2014 otherwise the diagnostic reports\n * drift that is only an editor's.\n */\nexport function charClassMembers(body: string): string[] {\n const esc = (ch: string): string => {\n const cp = ch.codePointAt(0)!\n return cp >= 0x80 ? `\\\\u${cp.toString(16).padStart(4, '0')}` : ch\n }\n const atoms: string[] = []\n let i = 0\n while (i < body.length) {\n if (body[i] === '\\\\') {\n // A code-point escape and the RAW character it denotes are the same member.\n // Missing this is not cosmetic: it splits one drifting family into two\n // clusters purely by which file's author typed the escape.\n const m = /^\\\\(?:u\\{([0-9a-fA-F]+)\\}|u([0-9a-fA-F]{4})|x([0-9a-fA-F]{2}))/.exec(body.slice(i))\n if (m !== null) {\n const cp = parseInt(m[1] ?? m[2] ?? m[3]!, 16)\n atoms.push(cp >= 0x80 ? `\\\\u${cp.toString(16).padStart(4, '0')}` : String.fromCodePoint(cp))\n i += m[0].length\n continue\n }\n atoms.push(body.slice(i, i + 2)); i += 2; continue\n }\n atoms.push(esc(body[i]!)); i++\n }\n const members: string[] = []\n for (let k = 0; k < atoms.length; k++) {\n if (k + 2 < atoms.length && atoms[k + 1] === '-' && atoms[k + 2] !== undefined) {\n members.push(`${atoms[k]}-${atoms[k + 2]}`)\n k += 2\n continue\n }\n members.push(atoms[k]!)\n }\n return members\n}\n\nconst CLASS_MIN_MEMBERS = 2\n\nfunction findRegexClasses(visited: Map<Combinator<unknown>, Visited>): RegexClassFinding[] {\n type Spelling = { source: string; members: string[]; key: string; nodes: Set<Combinator<unknown>>; sites: Site[] }\n const spellings = new Map<string, Spelling>()\n for (const v of visited.values()) {\n if (v.d.tag !== 'regex') continue\n for (const body of new Set(extractCharClasses(v.d.source))) {\n const members = charClassMembers(body)\n if (members.length < CLASS_MIN_MEMBERS) continue\n const key = [...new Set(members)].sort().join('\\u0001')\n let s = spellings.get(key)\n if (s === undefined) { s = { source: body, members, key, nodes: new Set(), sites: [] }; spellings.set(key, s) }\n if (s.nodes.has(v.p)) continue\n s.nodes.add(v.p)\n s.sites.push(v.site)\n }\n }\n\n // Cluster spellings that differ by at most `MAX_DELTA` members while sharing a\n // clear majority \u2014 that relation is what makes DRIFT visible: two classes nobody\n // can tell apart by reading, one of which is wrong.\n const MAX_DELTA = 3\n /** Members shared / members in either. Below this the two classes are simply\n * different classes that happen to share a couple of characters \u2014 clustering\n * them would bury the real drift under coincidence. */\n const MIN_JACCARD = 0.6\n const all = [...spellings.values()]\n const parent = all.map((_, i) => i)\n const find = (i: number): number => { while (parent[i] !== i) { parent[i] = parent[parent[i]!]!; i = parent[i]! } return i }\n const union = (a: number, b: number): void => { const ra = find(a), rb = find(b); if (ra !== rb) parent[ra] = rb }\n for (let i = 0; i < all.length; i++) for (let j = i + 1; j < all.length; j++) {\n // A negated class and a positive one are opposites, never drift of each other.\n if (all[i]!.members.includes('^') !== all[j]!.members.includes('^')) continue\n const A = new Set(all[i]!.members), B = new Set(all[j]!.members)\n let shared = 0\n for (const m of A) if (B.has(m)) shared++\n const delta = (A.size - shared) + (B.size - shared)\n const jaccard = shared / (shared + delta)\n if (shared >= 2 && delta > 0 && delta <= MAX_DELTA && jaccard >= MIN_JACCARD) union(i, j)\n }\n const clusters = new Map<number, number[]>()\n all.forEach((_, i) => {\n const r = find(i)\n const list = clusters.get(r)\n if (list === undefined) clusters.set(r, [i]); else list.push(i)\n })\n\n const out: RegexClassFinding[] = []\n for (const idxs of clusters.values()) {\n const members = idxs.map(i => all[i]!).sort((a, b) => b.nodes.size - a.nodes.size)\n const total = members.reduce((n, m) => n + m.nodes.size, 0)\n const drifted = members.length > 1\n if (!drifted && total < 2) continue\n const base = new Set(members[0]!.members)\n const variants: RegexClassVariant[] = members.map(m => {\n const mine = new Set(m.members)\n const plus = [...mine].filter(x => !base.has(x))\n const minus = [...base].filter(x => !mine.has(x))\n return {\n source: m.source,\n delta: m === members[0]\n ? '(reference)'\n : `${plus.map(p => `+\\`${p}\\``).join(' ')}${plus.length > 0 && minus.length > 0 ? ' ' : ''}${minus.map(p => `-\\`${p}\\``).join(' ')}` || '(same members, different spelling)',\n count: m.nodes.size,\n sites: m.sites,\n }\n })\n const bmpCeiling = members.some(m => m.members.some(x => x.endsWith('-\\\\uffff')))\n out.push({\n kind: 'regex-class',\n id: `regex-class:${members[0]!.key}`,\n canonical: members[0]!.source,\n variants,\n count: total,\n drifted,\n bmpCeiling,\n suggestion: [\n drifted\n ? `${members.length} NEARLY-identical spellings of one class, across ${total} regex() terminals. They differ by ${variants.slice(1).map(v => v.delta).join('; ')} \u2014 which is drift, not intent, unless every difference is deliberate. Two classes this close cannot be told apart by reading, so one of them is probably wrong and the tests cannot say which.`\n : `this class is re-spelled in ${total} DISTINCT regex() terminals. Extract it once \u2014 a named sub-leaf, or an interpolated source constant \u2014 so a range fix lands in one place. Re-spelling is how the near-identical variants elsewhere in this report came to exist.`,\n `Prefer the shared recognition surface where one already covers this class rather than a local extraction.`,\n bmpCeiling\n ? `The class tops out at U+FFFF, so astral-plane characters (emoji, many CJK extensions) fall OUTSIDE it. Widening the ceiling is exactly the kind of edit that has to reach every spelling.`\n : '',\n `Single-regex hygiene (duplicate class members, obscure ranges, a missing \\`u\\` flag) is \\`eslint-plugin-regexp\\`'s job and is not duplicated here \u2014 it sees one regex at a time, which is why it cannot see this.`,\n ].filter(s => s !== '').join(' '),\n })\n }\n // Drift first: an exact re-spelling is a tidy-up, a near-match is a latent bug.\n return out.sort((a, b) => Number(b.drifted) - Number(a.drifted) || b.count - a.count)\n}\n\n// \u2500\u2500 4. choice-arm overlap \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction intersects(a: FirstSet, b: FirstSet): boolean {\n if (a.kind === 'any' || b.kind === 'any') return true\n if (a.kind === 'empty' || b.kind === 'empty') return false\n for (const ra of a.ranges) for (const rb of b.ranges) if (ra.lo <= rb.hi && rb.lo <= ra.hi) return true\n return false\n}\n\nfunction intersection(a: FirstSet, b: FirstSet): FirstSet {\n if (a.kind === 'any') return b\n if (b.kind === 'any') return a\n if (a.kind === 'empty' || b.kind === 'empty') return { kind: 'empty' }\n const ranges: { lo: number; hi: number }[] = []\n for (const ra of a.ranges) for (const rb of b.ranges) {\n const lo = Math.max(ra.lo, rb.lo)\n const hi = Math.min(ra.hi, rb.hi)\n if (lo <= hi) ranges.push({ lo, hi })\n }\n return ranges.length === 0 ? { kind: 'empty' } : { kind: 'ranges', ranges }\n}\n\n/** The arm's term list, for shared-prefix comparison (a non-sequence arm is one term). */\nfunction termsOf(arm: Combinator<unknown>): readonly Combinator<unknown>[] {\n let cur = arm\n for (;;) {\n const d = cur._def as ParserDef\n if (d.tag === 'node' || d.tag === 'transform' || d.tag === 'label' || d.tag === 'attempt' || d.tag === 'leaf') {\n cur = (d as { parser: Combinator<unknown> }).parser\n continue\n }\n break\n }\n const d = cur._def as ParserDef\n return d.tag === 'sequence' ? d.parsers : [cur]\n}\n\nfunction leadingRegex(arm: Combinator<unknown>): boolean {\n const t = termsOf(arm)[0]\n return t !== undefined && (t._def as ParserDef).tag === 'regex'\n}\n\nfunction findOverlaps(\n visited: Map<Combinator<unknown>, Visited>,\n shapeOf: (p: Combinator<unknown>) => number,\n resolve?: RefResolver,\n): ArmOverlapFinding[] {\n const out: ArmOverlapFinding[] = []\n for (const v of visited.values()) {\n const d = v.d\n if (d.tag !== 'choice') continue\n const arms = d.parsers\n const fs = arms.map(a => firstSetOf(a, new Set(), resolve))\n const strategy = d.strategy as { tag: string; members?: number[] } | undefined\n for (let i = 0; i < arms.length; i++) {\n for (let j = i + 1; j < arms.length; j++) {\n const A = fs[i]!, B = fs[j]!\n if (A.kind === 'any' || B.kind === 'any') continue // gating's `anyArms` owns that\n if (!intersects(A, B)) continue\n const ta = termsOf(arms[i]!), tb = termsOf(arms[j]!)\n let shared = 0\n while (shared < ta.length && shared < tb.length && shapeOf(ta[shared]!) === shapeOf(tb[shared]!)) shared++\n const handled = strategy?.tag === 'sharedPrefix'\n && Array.isArray(strategy.members) && strategy.members.includes(i) && strategy.members.includes(j)\n const regexPair = leadingRegex(arms[i]!) && leadingRegex(arms[j]!)\n out.push({\n kind: 'arm-overlap',\n id: `arm-overlap:${siteToString(v.site)}:${i}-${j}`,\n site: v.site,\n a: i, b: j,\n on: intersection(A, B),\n sharedLeadingTerms: shared,\n sharedPrefix: shared > 0 ? clamp(ta.slice(0, shared).map(t => render(t, 2)).join(', ')) : null,\n handledByStrategy: handled,\n regexPair,\n suggestion: overlapSuggestion(shared, handled, regexPair),\n })\n }\n }\n }\n return out.sort((a, b) => b.sharedLeadingTerms - a.sharedLeadingTerms)\n}\n\nfunction overlapSuggestion(shared: number, handled: boolean, regexPair: boolean): string {\n if (shared > 0 && handled)\n return `the arms spell ${shared} identical leading term(s). parseman's \\`sharedPrefix\\` strategy already recognizes that prefix ONCE at runtime, so this costs nothing at parse time \u2014 it is a READABILITY finding: left-factoring into \\`sequence(prefix, choice(\u2026))\\` says in the grammar what the compiler had to infer.`\n if (shared > 0)\n return `the arms spell ${shared} identical leading term(s) but do NOT qualify for the \\`sharedPrefix\\` strategy (it needs every arm to be a bare \\`sequence\\` starting with the same concrete terminal). Left-factor into \\`sequence(prefix, choice(\u2026))\\` \u2014 that both removes the re-spelling and lets the inner choice dispatch on its own first char.`\n if (regexPair)\n return `two regex() arms whose character classes intersect. Nothing dispatches between them: the first is tried, and on failure the second re-scans the same position. Two regexes that can start on the same char are usually one terminal \u2014 merge them, or make the distinction explicit with a leading literal.`\n return `the arms can start on the same character, so the choice cannot dispatch between them: on those chars the first arm is entered speculatively and rolled back before the second is tried. Give the arms disjoint leading terminals, or left-factor the common part out.`\n}\n\n// \u2500\u2500 5. algebraic rewrites \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst PERF_ARM = 'removes a speculative arm: on every input position reaching this choice, the deleted arm no longer costs a ctx save, a child-array push and a rollback before the surviving arm is tried. The saving scales with how often the choice is reached and how much the doomed arm consumes before failing.'\nconst PERF_NONE = ''\n\nfunction findRewrites(\n visited: Map<Combinator<unknown>, Visited>,\n shapeOf: (p: Combinator<unknown>) => number,\n): RewriteFinding[] {\n const out: RewriteFinding[] = []\n // `at` discriminates findings that a single site can produce MORE than one of.\n // Without it every `duplicate-arm`/`shadowed-arm` pair at one choice shares an\n // id, so accepting one pair silently suppresses the rest \u2014 and these are\n // `bug: true` findings that error mode is supposed to hold.\n const push = (f: Omit<RewriteFinding, 'kind' | 'id'>, at?: string): void => {\n const site = siteToString(f.site)\n out.push({ kind: 'rewrite', id: `rewrite:${f.rewrite}:${site}${at === undefined ? '' : `:${at}`}`, ...f })\n }\n\n for (const v of visited.values()) {\n const d = v.d\n const site = v.site\n\n // (a) idempotent nesting.\n if (d.tag === 'optional' || d.tag === 'many' || d.tag === 'oneOrMore') {\n const inner = (d as { parser: Combinator<unknown> }).parser\n const id = inner._def as ParserDef\n const collapsed = collapseRepeat(d, id)\n if (collapsed !== null) {\n push({\n rewrite: 'idempotent-nesting', site,\n from: clamp(render(v.p, 2)), to: collapsed,\n astNeutral: false,\n perf: 'the outer wrapper still runs its own bookkeeping (and, for `many(optional(X))`, guards a zero-width iteration on every pass) for a nullability the inner combinator already has.',\n bug: id.tag === 'optional' && d.tag === 'many',\n suggestion: `\\`${clamp(render(v.p, 2))}\\` is exactly \\`${collapsed}\\`. ${id.tag === 'optional' && d.tag === 'many' ? 'The nested form is also a zero-width-iteration hazard: the inner `optional` succeeds consuming nothing, which the repeat has to detect and break on every pass.' : ''} Candidate \u2014 verify AST identity: the collapsed form produces ONE level of array/undefined where the nested form produces two.`,\n })\n }\n }\n\n // (b) single-element choice/sequence.\n if ((d.tag === 'choice' || d.tag === 'sequence') && d.parsers.length === 1) {\n push({\n rewrite: 'single-element', site,\n from: clamp(render(v.p, 2)),\n to: d.tag === 'choice' ? clamp(render(d.parsers[0]!, 2)) : `${clamp(render(d.parsers[0]!, 2))} (note: sequence() of one still wraps its value in a 1-tuple)`,\n astNeutral: d.tag === 'choice',\n perf: d.tag === 'choice' ? 'a one-arm choice still emits its dispatch scaffolding.' : PERF_NONE,\n bug: false,\n suggestion: d.tag === 'choice'\n ? 'a `choice` with one arm IS that arm \u2014 the wrapper only adds dispatch scaffolding. Safe to unwrap (choice forwards its arm value unchanged).'\n : 'a `sequence` with one element only exists to build a 1-tuple. If nothing reads that tuple, drop the wrapper \u2014 CANDIDATE, verify AST identity first.',\n })\n }\n\n if (d.tag !== 'choice') continue\n const arms = d.parsers\n const armShapes = arms.map(shapeOf)\n const strategy = d.strategy as { tag: string; members?: number[] } | undefined\n\n // (c) duplicate arms \u2014 the later one is dead.\n for (let i = 0; i < arms.length; i++) for (let j = i + 1; j < arms.length; j++) {\n if (armShapes[i] !== armShapes[j]) continue\n push({\n rewrite: 'duplicate-arm', site,\n from: `choice(\u2026 arm[${i}] \u2026, arm[${j}] \u2026)`,\n to: `delete arm[${j}]`,\n astNeutral: true,\n perf: PERF_ARM,\n bug: true,\n suggestion: `arm[${i}] and arm[${j}] are the SAME shape (\\`${clamp(render(arms[i]!, 2))}\\`). Under ordered choice arm[${i}] always wins, so arm[${j}] is unreachable \u2014 delete it. This is AST-neutral by construction: an arm that can never be selected contributes nothing to any parse.`,\n }, `${i}-${j}`)\n }\n\n // (d) shadowed arm: an earlier arm is a strict TERM-PREFIX of a later one.\n for (let i = 0; i < arms.length; i++) for (let j = 0; j < arms.length; j++) {\n if (i >= j) continue\n if (armShapes[i] === armShapes[j]) continue\n const ta = termsOf(arms[i]!), tb = termsOf(arms[j]!)\n if (ta.length === 0 || ta.length >= tb.length) continue\n let k = 0\n while (k < ta.length && shapeOf(ta[k]!) === shapeOf(tb[k]!)) k++\n if (k !== ta.length) continue\n push({\n rewrite: 'shadowed-arm', site,\n from: `choice(\u2026 arm[${i}]=${clamp(render(arms[i]!, 2))}, \u2026 arm[${j}]=${clamp(render(arms[j]!, 2))} \u2026)`,\n to: `arm[${j}] is unreachable`,\n astNeutral: true,\n perf: PERF_ARM,\n bug: true,\n suggestion: `arm[${i}] is a strict prefix of arm[${j}] and comes FIRST. Whenever arm[${j}] would match, arm[${i}] also matches, and ordered choice commits to it \u2014 so arm[${j}] can never be selected. Either swap them (longest first) or fold arm[${j}]'s extra terms into arm[${i}] as \\`optional(\u2026)\\`. Verify by hand where a \\`not()\\`/\\`gate()\\` in arm[${i}] makes its success input-dependent in a way this static check cannot see.`,\n }, `${i}-${j}`)\n }\n\n // (e) optional prefix / suffix \u2014 `choice(sequence(A, R\u2026), R\u2026)` is `sequence(optional(A), R\u2026)`.\n for (let i = 0; i + 1 < arms.length; i++) {\n const j = i + 1\n const longer = termsOf(arms[i]!), shorter = termsOf(arms[j]!)\n const ordered = detectOptionalTerm(longer, shorter, shapeOf)\n const reversed = ordered === null ? detectOptionalTerm(shorter, longer, shapeOf) : null\n const hit = ordered ?? reversed\n if (hit === null) continue\n const nodeWrapped = v.enclosingNode ?? wrappingNodeType(arms[i]!) ?? wrappingNodeType(arms[j]!)\n const rest = hit.rest.map(t => render(t, 2)).join(', ')\n const to = hit.where === 'prefix'\n ? `sequence(optional(${render(hit.term, 2)}), ${rest})`\n : `sequence(${rest}, optional(${render(hit.term, 2)}))`\n push({\n rewrite: hit.where === 'prefix' ? 'optional-prefix' : 'optional-suffix',\n site,\n from: clamp(render(v.p, 2)),\n to: clamp(to),\n astNeutral: false,\n perf: PERF_ARM,\n bug: false,\n suggestion: `these two arms differ only by a ${hit.where === 'prefix' ? 'leading' : 'trailing'} \\`${clamp(render(hit.term, 2))}\\`, so the choice is exactly \\`${clamp(to)}\\` \u2014 same language, one arm instead of two.${reversed !== null ? ` NOTE THE ORDER: the shorter arm is FIRST, so under ordered choice the longer arm is only reachable when the shorter one fails \u2014 check that is what you meant.` : ''} CANDIDATE \u2014 verify AST identity before applying:${nodeWrapped === null ? '' : ` this sits under \\`node('${nodeWrapped}')\\`, and`} the two forms produce different child arrays (two children vs. one child plus an absent optional). If a build fn or a downstream consumer reads children positionally, the rewrite MOVES the tree.`,\n }, `${i}-${j}`)\n }\n\n // (f) left-factoring.\n for (let i = 0; i + 1 < arms.length; i++) {\n const j = i + 1\n const ta = termsOf(arms[i]!), tb = termsOf(arms[j]!)\n if (ta.length < 2 || tb.length < 2) continue\n let k = 0\n while (k < ta.length - 1 && k < tb.length - 1 && shapeOf(ta[k]!) === shapeOf(tb[k]!)) k++\n if (k === 0) continue\n const handled = strategy?.tag === 'sharedPrefix'\n && Array.isArray(strategy.members) && strategy.members.includes(i) && strategy.members.includes(j)\n push({\n rewrite: 'left-factor', site,\n from: `choice(sequence(${ta.map(t => render(t, 1)).join(', ')}), sequence(${tb.map(t => render(t, 1)).join(', ')}))`,\n to: clamp(`sequence(${ta.slice(0, k).map(t => render(t, 2)).join(', ')}, choice(sequence(${ta.slice(k).map(t => render(t, 1)).join(', ')}), sequence(${tb.slice(k).map(t => render(t, 1)).join(', ')})))`),\n astNeutral: false,\n perf: handled\n ? 'NONE \u2014 parseman\\'s `sharedPrefix` choice strategy already recognizes this prefix once and replays it, so the runtime cost is identical. This is a readability finding.'\n : PERF_ARM,\n bug: false,\n suggestion: `arm[${i}] and arm[${j}] spell the same ${k} leading term(s). ${handled ? 'The `sharedPrefix` strategy already collapses this AT RUNTIME, so left-factoring buys readability, not speed \u2014 but writing it out means the next reader does not have to trust the optimizer.' : 'Left-factor to `sequence(prefix, choice(tailA, tailB))`: the prefix is then recognized once and the inner choice dispatches on its own first char.'} CANDIDATE \u2014 verify AST identity: factoring nests the tails one level deeper in the value.`,\n }, `${i}-${j}`)\n }\n }\n\n // (g) hand-rolled sepBy \u2014 sequence(X, many(sequence(S, X))).\n for (const v of visited.values()) {\n if (v.d.tag !== 'sequence') continue\n const hit = detectHandRolledSepBy(v.d.parsers, shapeOf)\n if (hit === null) continue\n // Anywhere in the REPETITION, not just on the separator itself: a `field()`\n // wrapping the whole `sequence(sep, item)` is the same blocker.\n const captured = v.d.parsers.some(t => capturesPositionally(t))\n const verdict: SepByVerdict = captured ? 'blocked-by-capture' : v.enclosingReducer ? 'reducer-stride-review' : 'convertible'\n const to = `sepBy(${clamp(render(hit.item, 2))}, ${clamp(render(hit.sep, 2))}${hit.trailing ? `, { trailing: 'allow' }` : ''}, { min: ${hit.min} })`\n out.push({\n kind: 'rewrite',\n id: `rewrite:hand-rolled-sepby:${siteToString(v.site)}`,\n rewrite: 'hand-rolled-sepby',\n site: v.site,\n sepByVerdict: verdict,\n from: clamp(render(v.p, 3)),\n to: verdict === 'convertible' ? to : `${to} \u2014 NOT APPLICABLE HERE (${verdict})`,\n astNeutral: false,\n perf: verdict === 'convertible'\n ? 'the hand-rolled form allocates a tuple per repetition (`sequence(sep, item)`) and a wrapper array around them; `sepBy` builds the item list directly.'\n : PERF_NONE,\n bug: false,\n suggestion: `${SEPBY_SPELLED_OUT(hit.trailing)} ${SEPBY_VERDICT_TEXT[verdict](hit.trailing)}`,\n })\n }\n\n return out\n}\n\nfunction collapseRepeat(outer: ParserDef, inner: ParserDef): string | null {\n const o = outer.tag, i = inner.tag\n if (o === 'optional' && i === 'optional') return 'optional(X)'\n if (o === 'optional' && i === 'many') return 'many(X)'\n if (o === 'many' && i === 'many') return 'many(X)'\n if (o === 'many' && i === 'optional') return 'many(X)'\n if (o === 'optional' && i === 'oneOrMore' && inner.min === 1 && inner.max === undefined) return 'many(X)'\n if (o === 'many' && i === 'oneOrMore' && inner.min === 1 && inner.max === undefined) return 'many(X)'\n return null\n}\n\n/** `node()` type wrapping an arm, for the AST-neutrality caveat. */\nfunction wrappingNodeType(arm: Combinator<unknown>): string | null {\n const d = arm._def as ParserDef\n return d.tag === 'node' ? d.type ?? '(anonymous)' : null\n}\n\n/**\n * `longer` = `shorter` plus ONE extra term at the front or the back. That extra\n * term is exactly an `optional()`.\n */\nfunction detectOptionalTerm(\n longer: readonly Combinator<unknown>[],\n shorter: readonly Combinator<unknown>[],\n shapeOf: (p: Combinator<unknown>) => number,\n): { where: 'prefix' | 'suffix'; term: Combinator<unknown>; rest: readonly Combinator<unknown>[] } | null {\n if (longer.length !== shorter.length + 1 || shorter.length === 0) return null\n const same = (a: readonly Combinator<unknown>[], b: readonly Combinator<unknown>[]): boolean =>\n a.length === b.length && a.every((x, i) => shapeOf(x) === shapeOf(b[i]!))\n if (same(longer.slice(1), shorter)) return { where: 'prefix', term: longer[0]!, rest: shorter }\n if (same(longer.slice(0, -1), shorter)) return { where: 'suffix', term: longer[longer.length - 1]!, rest: shorter }\n return null\n}\n\nconst SEPBY_SPELLED_OUT = (trailing: boolean): string =>\n `this is \\`sepBy(item, sep${trailing ? \", { trailing: 'allow' }\" : ''})\\` spelled out.`\n\n/** The verdict text. Each says what to DO, and the blocked ones say why not. */\nconst SEPBY_VERDICT_TEXT: Record<SepByVerdict, (trailing: boolean) => string> = {\n 'blocked-by-capture': () =>\n 'BLOCKED: the repetition CAPTURES its separator (`field`/`label`), which `sepBy` cannot express \u2014 it yields items and discards separators, so byte-faithful layout replay is lost. Leave this site alone; read it as a parseman gap (a separator-capturing repeat), not as work.',\n 'reducer-stride-review': () =>\n 'NEEDS REVIEW, not conversion: an enclosing `node()`/`transform()` reducer reads this repetition\\'s children, and a left-associating reducer typically STRIDES BY TWO over `[item, sep, item, sep, \u2026]` and re-emits the operator text. `sepBy` yields a FLAT ITEM list with the separators gone, so the reducer has to be rewritten in the same change or the tree moves. Convert only together with the reducer.',\n convertible: (trailing: boolean) =>\n `No separator capture, and no enclosing reducer reads these children \u2014 this site is convertible. Beyond the ${trailing ? 'four' : 'three'} combinators it replaces, \\`sepBy\\` carries the right NULLABILITY: \\`min: 0\\` (the default) matches empty and does NOT gate as a choice arm, \\`min: 1\\` does \u2014 a distinction the hand-rolled shape leaves implicit and gets wrong silently. Still check the consumer: \\`sepBy\\` yields a flat item list where the hand-rolled form yields \\`[first, [[sep, item], \u2026]]\\`.`,\n}\n\n/**\n * Whether a hand-rolled separated list can ACTUALLY become `sepBy` \u2014 a verdict per\n * site, not a count. Reporting every match as convertible generates false work: on\n * the reference Less grammar only a minority are, and the rest are blocked for two\n * concrete, detectable reasons.\n */\nexport type SepByVerdict = 'convertible' | 'blocked-by-capture' | 'reducer-stride-review'\n\n/** Does this subtree capture anything positionally (`field`/`label`)? */\nfunction capturesPositionally(p: Combinator<unknown>, seen = new Set<Combinator<unknown>>()): boolean {\n if (seen.has(p)) return false\n seen.add(p)\n const d = p._def as ParserDef\n if (d.tag === 'field' || d.tag === 'label') return true\n for (const k of childrenOf(d)) if (capturesPositionally(k, seen)) return true\n return false\n}\n\n/** `sequence(X, many(sequence(S, X)) [, optional(S)])` \u2014 a `sepBy` written out. */\nfunction detectHandRolledSepBy(\n terms: readonly Combinator<unknown>[],\n shapeOf: (p: Combinator<unknown>) => number,\n): { item: Combinator<unknown>; sep: Combinator<unknown>; trailing: boolean; min: number } | null {\n if (terms.length < 2 || terms.length > 3) return null\n const item = terms[0]!\n const rep = terms[1]!._def as ParserDef\n if (rep.tag !== 'many' && rep.tag !== 'oneOrMore') return null\n if (rep.max !== undefined) return null\n const innerDef = rep.parser._def as ParserDef\n if (innerDef.tag !== 'sequence' || innerDef.parsers.length !== 2) return null\n const [sep, repeated] = innerDef.parsers as [Combinator<unknown>, Combinator<unknown>]\n if (shapeOf(repeated) !== shapeOf(item)) return null\n let trailing = false\n if (terms.length === 3) {\n const t = terms[2]!._def as ParserDef\n if (t.tag !== 'optional' || shapeOf(t.parser) !== shapeOf(sep)) return null\n trailing = true\n }\n // The leading item plus whatever the repetition itself requires. `rep.min` is\n // already RESOLVED for both tags (`many` defaults it to 0, `oneOrMore` to 1), so\n // one expression covers both \u2014 special-casing the tag dropped an explicit\n // `many(\u2026, { min: n })` back to 1 and under-reported the rewrite's bound.\n return { item, sep, trailing, min: 1 + rep.min }\n}\n\n// \u2500\u2500 5b. one node type, several divergent productions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * The term list of a `node()`'s BODY. Peels more wrappers than `termsOf` \u2014 a\n * clone typically differs from its original by exactly such a wrapper\n * (`noTrivia(\u2026)` around the sequence), and that difference is the thing to see\n * through, not the thing to report.\n */\nfunction bodyTermsOf(p: Combinator<unknown>): readonly Combinator<unknown>[] {\n let cur = p\n for (;;) {\n const d = cur._def as ParserDef\n if (d.tag === 'node' || d.tag === 'grammar' || d.tag === 'transform' || d.tag === 'label'\n || d.tag === 'attempt' || d.tag === 'leaf' || d.tag === 'token' || d.tag === 'expect' || d.tag === 'field') {\n cur = (d as { parser: Combinator<unknown> }).parser\n continue\n }\n break\n }\n const d = cur._def as ParserDef\n return d.tag === 'sequence' ? d.parsers : [cur]\n}\n\n/** Terms must overlap this much before two productions count as variants of one\n * shape rather than two genuinely different constructs that share a node type. */\nconst MIN_SHARED_TERMS = 2\n\nfunction findDivergentNodes(\n visited: Map<Combinator<unknown>, Visited>,\n shapeOf: (p: Combinator<unknown>) => number,\n): DivergentNodeFinding[] {\n const byType = new Map<string, Map<number, Visited>>()\n for (const v of visited.values()) {\n if (v.d.tag !== 'node' || v.d.type === undefined) continue\n let m = byType.get(v.d.type)\n if (m === undefined) { m = new Map(); byType.set(v.d.type, m) }\n if (!m.has(v.shape)) m.set(v.shape, v)\n }\n\n const out: DivergentNodeFinding[] = []\n for (const [type, m] of byType) {\n if (m.size < 2) continue\n const members = [...m.values()]\n const termSets = members.map(v => bodyTermsOf(v.p).map(t => ({ id: shapeOf(t), text: clamp(render(t, 2)) })))\n // Shared = spelled by EVERY production. That is what makes them one shape.\n const shared = termSets[0]!.filter(t => termSets.every(set => set.some(x => x.id === t.id)))\n const uniqueShared = [...new Map(shared.map(t => [t.id, t])).values()]\n if (uniqueShared.length < MIN_SHARED_TERMS) continue\n const sharedIds = new Set(uniqueShared.map(t => t.id))\n out.push({\n kind: 'divergent-node',\n id: `divergent-node:${type}`,\n nodeType: type,\n count: members.length,\n sharedTerms: uniqueShared.map(t => t.text),\n productions: members.map((v, i) => ({\n shape: clamp(render(v.p, 3)),\n site: v.site,\n distinctTerms: termSets[i]!.filter(t => !sharedIds.has(t.id)).map(t => t.text),\n })),\n suggestion: `\\`${type}\\` is built by ${members.length} structurally different productions that nevertheless spell ${uniqueShared.length} identical term(s) (${uniqueShared.map(t => `\\`${t.text}\\``).join(', ')}) \u2014 they are variants of ONE shape, not ${members.length} constructs. Nothing keeps them in sync: an edit to the ${type} shape has to land in all ${members.length}, and no test fails when it lands in one. Fold them into a single production whose varying part is a \\`choice\\`, or, if the variants exist for a parse-order reason (a fast path tried first), say so at the definition \u2014 the cost of the split is that a reader cannot tell which it is.`,\n })\n }\n return out.sort((a, b) => b.sharedTerms.length * b.count - a.sharedTerms.length * a.count)\n}\n\n// \u2500\u2500 5c. an earlier arm that flattens what a later arm structures \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Peel the wrappers that are REFERENTIAL or purely annotative, so an arm written\n * `g.FlatDecl` is compared as the production it names. `transform`/`leaf`/`token`\n * are deliberately NOT peeled: each of them collapses its interior to one value\n * on purpose, and treating a `leaf(node('X', \u2026))` as if it still produced an `X`\n * would invert the very question this analysis asks.\n */\nfunction resolveArm(p: Combinator<unknown>): Combinator<unknown> {\n let cur = p\n const seen = new Set<Combinator<unknown>>()\n for (;;) {\n if (seen.has(cur)) return cur\n seen.add(cur)\n const d = cur._def as ParserDef\n if (d.tag === 'label' || d.tag === 'attempt') { cur = d.parser; continue }\n if (d.tag === 'lazy') {\n try { cur = (d as { thunk(): Combinator<unknown> }).thunk(); continue }\n catch { return cur } // unresolved cross-artifact hole\n }\n return cur\n }\n}\n\n/**\n * The `node()` types this subtree can put INSIDE the tree, excluding its own root\n * wrapper. Refs are followed (the structure a declaration's value builds usually\n * lives behind `g.valueList`, and an analysis that stopped at the ref would report\n * every grammar as flat). `leaf()`/`token()` are opaque: they reduce their\n * interior to a single value, so nothing under one reaches the tree.\n */\nfunction nestedNodeTypes(\n root: Combinator<unknown>,\n out: Set<string>,\n seen: Set<Combinator<unknown>>,\n isRoot: boolean,\n): void {\n if (seen.has(root)) return\n seen.add(root)\n const d = root._def as ParserDef\n if (d.tag === 'leaf' || d.tag === 'token') return\n if (d.tag === 'node' && !isRoot) out.add(d.type ?? '(anonymous)')\n if (d.tag === 'lazy') {\n try { nestedNodeTypes((d as { thunk(): Combinator<unknown> }).thunk(), out, seen, false) }\n catch { /* unresolved cross-artifact hole */ }\n return\n }\n for (const k of childrenOf(d)) nestedNodeTypes(k, out, seen, false)\n}\n\n/** Most node types worth naming in one finding before it stops being a list. */\nconst MAX_LOST_TYPES = 8\n\nfunction findStructureLoss(\n visited: Map<Combinator<unknown>, Visited>,\n resolve?: RefResolver,\n): StructureLossFinding[] {\n const nestedCache = new Map<Combinator<unknown>, Set<string>>()\n const nested = (p: Combinator<unknown>): Set<string> => {\n const hit = nestedCache.get(p)\n if (hit !== undefined) return hit\n const out = new Set<string>()\n nestedNodeTypes(p, out, new Set(), true)\n nestedCache.set(p, out)\n return out\n }\n\n const findings: StructureLossFinding[] = []\n for (const v of visited.values()) {\n const d = v.d\n if (d.tag !== 'choice') continue\n const arms = d.parsers\n const resolved = arms.map(resolveArm)\n const nodeTypes = resolved.map(wrappingNodeType)\n const fs = arms.map(a => firstSetOf(a, new Set(), resolve))\n\n for (let i = 0; i < arms.length; i++) {\n // A GATED arm is selected by a runtime predicate, not by order alone. The\n // author has said which branch applies when; that is a deliberate split,\n // not a shadow, so it is not reported as a bug.\n if (d.gates[i] != null) continue\n const type = nodeTypes[i] ?? null\n if (type === null) continue\n // Only the arm that produces NO nested node is reported. \"Earlier arm is\n // strictly poorer than later arm\" is the same family, but grading it means\n // comparing two ref-reachable type sets, and in a recursive grammar the\n // later set reaches most of the grammar \u2014 so the graded rule fires on\n // almost every pair and stops being a signal. Empty-vs-nonempty needs no\n // threshold and is exactly the reported defect: a node over bare leaves.\n if (nested(resolved[i]!).size !== 0) continue\n const A = fs[i]!\n if (A.kind === 'any') continue // gating's `anyArms` owns the any-first-set case\n\n for (let j = i + 1; j < arms.length; j++) {\n if (nodeTypes[j] !== type) continue\n const B = fs[j]!\n // OVER-APPROXIMATION, and the one place this finding can be wrong. A shared\n // first character is NECESSARY for the earlier arm to shadow the later one,\n // not sufficient: two arms can both start on `-` and still accept disjoint\n // languages (`-webkit-x` vs `-5px`), in which case no input reaches both and\n // nothing is shadowed. Deciding \"do these two arms share an input\" is\n // language intersection, which is undecidable for a general CFG, so a\n // first-set test is the strongest decidable proxy available. The finding is\n // therefore \"these arms overlap on these characters, and IF an input reaches\n // both then structure is lost\" \u2014 which is what the suggestion text says, and\n // why a reader is pointed at the two shapes rather than handed a verdict.\n if (B.kind === 'any' || !intersects(A, B)) continue\n const lost = nested(resolved[j]!)\n if (lost.size === 0) continue\n const lostNodeTypes = [...lost].sort()\n findings.push({\n kind: 'structure-loss',\n id: `structure-loss:${siteToString(v.site)}:${i}-${j}`,\n site: v.site,\n nodeType: type,\n earlier: i,\n later: j,\n on: intersection(A, B),\n lostNodeTypes: lostNodeTypes.slice(0, MAX_LOST_TYPES),\n earlierShape: clamp(render(resolved[i]!, 3)),\n laterShape: clamp(render(resolved[j]!, 3)),\n suggestion: structureLossSuggestion(type, i, j, lostNodeTypes),\n })\n }\n }\n }\n return findings.sort((a, b) => b.lostNodeTypes.length - a.lostNodeTypes.length)\n}\n\nfunction structureLossSuggestion(\n type: string,\n i: number,\n j: number,\n lost: readonly string[],\n): string {\n const names = lost.slice(0, MAX_LOST_TYPES).map(t => `\\`${t}\\``).join(', ')\n const more = lost.length > MAX_LOST_TYPES ? ` (and ${lost.length - MAX_LOST_TYPES} more)` : ''\n return `arm[${i}] and arm[${j}] both build \\`${type}\\` and can start on the same character, so on every input BOTH accept, arm[${i}] wins \u2014 and arm[${i}] contains no \\`node()\\`, so it yields a \\`${type}\\` over bare leaves. arm[${j}] would have built ${names}${more} there. The parse still succeeds and the span is still right; only the tree shape changes, and only on the inputs arm[${i}] happens to accept \u2014 so a suite that asserts \"it parses\" and an output diff both stay green while every consumer of the tree (language service, formatter, refactor) silently loses those nodes on the commonest inputs. Decide which tree is the contract: if the structured one is, DELETE arm[${i}] or move it after arm[${j}]; if the flat one is, it must be flat for ALL inputs of this shape, not just the ones arm[${i}] matches \u2014 a fast path that is not tree-neutral is not a fast path.`\n}\n\n// \u2500\u2500 6. hand-rolled keyword regexes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction findKeywordRegexes(visited: Map<Combinator<unknown>, Visited>): KeywordRegexFinding[] {\n // Sibling map: which choice arms are themselves hand-rolled keywords.\n const armIndexOf = new Map<Combinator<unknown>, { owner: Combinator<unknown>; index: number }>()\n for (const v of visited.values()) {\n if (v.d.tag !== 'choice') continue\n v.d.parsers.forEach((a, i) => {\n let cur = a\n for (;;) {\n const cd = cur._def as ParserDef\n if (cd.tag === 'node' || cd.tag === 'label' || cd.tag === 'transform' || cd.tag === 'leaf' || cd.tag === 'attempt') { cur = cd.parser; continue }\n break\n }\n armIndexOf.set(cur, { owner: v.p, index: i })\n })\n }\n const keywordArms = new Map<Combinator<unknown>, number[]>()\n for (const [arm, { owner }] of armIndexOf) {\n const d = arm._def as ParserDef\n if (d.tag !== 'regex' || keywordRegexShape(d.source) === null) continue\n const list = keywordArms.get(owner) ?? []\n list.push(armIndexOf.get(arm)!.index)\n keywordArms.set(owner, list)\n }\n\n const out: KeywordRegexFinding[] = []\n for (const v of visited.values()) {\n const d = v.d\n if (d.tag !== 'regex') continue\n const shape = keywordRegexShape(d.source)\n if (shape === null) continue\n const ci = d.flags.includes('i')\n const unicode = d.flags.includes('u') || d.flags.includes('v')\n const own = armIndexOf.get(v.p)\n const siblings = own === undefined ? [] : (keywordArms.get(own.owner) ?? []).filter(i => i !== own.index)\n const bq = (s: string): string => `'${s.replace(/\\\\/g, '\\\\\\\\')}'`\n const vocabulary = shape.words.length >= VOCABULARY_MIN_WORDS\n const hazards = keywordAlternationHazards(shape.words, shape.boundary)\n const unrescued = hazards.filter(h => !h.rescuedByBoundary)\n const longestFirst = shape.words.every((w, i) => i === 0 || shape.words[i - 1]!.length >= w.length)\n const shown = shape.words.length > 8 ? [...shape.words.slice(0, 6), '/* \u2026 */', shape.words[shape.words.length - 1]!] : shape.words\n const call = shape.words.length === 1\n ? `word(${bq(shape.words[0]!)}, ${bq(shape.boundary ?? '_0-9A-Za-z')}${ci ? ', { caseInsensitive: true }' : ''})`\n : `keywords([${shown.map(w => (w === '/* \u2026 */' ? w : bq(w))).join(', ')}], { boundary: ${bq(shape.boundary ?? '_0-9A-Za-z')}${ci ? ', caseInsensitive: true' : ''} })`\n out.push({\n kind: 'keyword-regex',\n id: `keyword-regex:${siteToString(v.site)}`,\n site: v.site,\n source: d.source,\n flags: d.flags,\n words: shape.words,\n boundary: shape.boundary,\n caseFoldRisk: ci && !unicode,\n siblingArms: siblings,\n vocabulary,\n longestFirst,\n hazards,\n bug: unrescued.length > 0,\n suggestion: [\n vocabulary\n ? `this regex enumerates a FIXED VOCABULARY of ${shape.words.length} literal words${shape.words.length > 20 ? ' \u2014 the alternation is long enough that nobody re-reads it' : ''}. A regex enumerating a fixed vocabulary is a keyword set written the hard way. Use \\`${call}\\` \u2014 it exposes an EXACT first-set that \\`choice\\` can dispatch on (the alternation exposes none), it owns the boundary so it is not re-spelled per word, it sorts LONGEST-FIRST by construction, and it lowers to a single charCodeAt scan.`\n : `\\`regex(/${d.source}/${d.flags})\\` hand-rolls a keyword and its word boundary. Use \\`${call}\\` \u2014 it owns the boundary (so \\`${shape.boundary ?? ''}\\` is not re-spelled per keyword), exposes an EXACT first-set that \\`choice\\` can dispatch on, and lowers to the same charCodeAt scan.`,\n unrescued.length > 0\n ? `LIVE BUG \u2014 the alternation is ordered so ${unrescued.length} later alternative(s) can NEVER match: ${unrescued.slice(0, 3).map(h => `\\`${h.shorter}\\` precedes \\`${h.longer}\\` and nothing rejects the following \\`${h.at}\\``).join('; ')}${unrescued.length > 3 ? `, and ${unrescued.length - 3} more` : ''}. Regex alternation is FIRST-match, not longest-match, and with no boundary guard to force a backtrack the shorter branch wins outright. \\`keywords()\\` cannot express this bug: it sorts longest-first.`\n : hazards.length > 0\n ? `ORDERING HAZARD (currently harmless): ${hazards.length} earlier alternative(s) are strict prefixes of later ones \u2014 e.g. \\`${hazards[0]!.shorter}\\` before \\`${hazards[0]!.longer}\\`. Regex alternation is first-match, so these work ONLY because the trailing boundary guard rejects the next character and the engine backtracks into the longer branch. That correctness rests on a hand-maintained order plus a guard that happens to cover the right characters; \\`keywords()\\` sorts longest-first by construction and does not depend on either.`\n : !longestFirst && vocabulary\n ? `The alternation is not in longest-first order. It is correct today${shape.boundary === null ? '' : ' (the boundary guard forces a backtrack where it matters)'}, but the ordering is hand-maintained and nothing checks it \u2014 adding a word that extends an existing one is a silent bug. \\`keywords()\\` sorts longest-first by construction.`\n : '',\n ci && !unicode\n ? `CORRECTNESS, not style: \\`/i\\` without \\`/u\\` does NOT fold case the way \\`{c, toUpperCase(c), toLowerCase(c)}\\` suggests \u2014 67 BMP code points fold in ways those three miss (\u03C2/\u03C3, \u00B5/\u03BC, the \u01C4\u01C5\u01C6 digraphs, combining iota subscript). parseman fixed exactly this INSIDE \\`keywords()\\` (see combinators/case-fold.ts); a hand-rolled copy never received the fix, so its first-set is unsound for those inputs.`\n : '',\n siblings.length > 0\n ? `arm[${own?.index}] here sits alongside arm(s) [${siblings.join(', ')}] in the same \\`choice\\`, which are also hand-rolled keywords. A single \\`keywords([\u2026])\\` replaces the whole group and gives longest-match dispatch across it \u2014 which is what that combinator exists for.`\n : '',\n ].filter(s => s !== '').join(' '),\n })\n }\n // Bugs first, then the biggest vocabularies: a 150-word alternation is both the\n // most valuable conversion and the least likely to be spotted by reading.\n return out.sort((a, b) =>\n Number(b.bug) - Number(a.bug)\n || b.hazards.length - a.hazards.length\n || b.words.length - a.words.length\n || b.siblingArms.length - a.siblingArms.length)\n}\n\n// \u2500\u2500 formatting \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type DuplicationWarnLevel = 'off' | 'warn' | 'error'\n\nconst sitesLine = (sites: readonly Site[]): string =>\n sites.slice(0, 6).map(siteToString).join('\\n ') + (sites.length > 6 ? `\\n \u2026 and ${sites.length - 6} more` : '')\n\n/**\n * Ranked, actionable lines \u2014 same tone and shape as `formatGatingWarnings`: what\n * it is, where it is, and one concrete thing to do about it.\n */\nexport function formatDuplicationFindings(report: DuplicationReport): string[] {\n const lines: string[] = []\n // First: the only family here that is silently WRONG rather than redundant \u2014\n // the parse succeeds, so nothing else reports it.\n for (const f of report.structureLoss) {\n lines.push(`parseman BUG [structure-loss] @ ${siteToString(f.site)}: arm[${f.earlier}] flattens \\`${f.nodeType}\\` where arm[${f.later}] structures it, on ${firstSetToString(f.on)}`)\n lines.push(` flat: ${f.earlierShape}`)\n lines.push(` rich: ${f.laterShape}`)\n lines.push(` lost: ${f.lostNodeTypes.map(t => `\\`${t}\\``).join(', ')}`)\n lines.push(` fix: ${f.suggestion}`)\n }\n for (const f of report.rewrites) {\n lines.push(`parseman ${f.bug ? 'BUG' : 'rewrite'} [${f.rewrite}${f.sepByVerdict === undefined ? '' : `/${f.sepByVerdict}`}] @ ${siteToString(f.site)}${f.astNeutral ? '' : ' (candidate \u2014 verify AST identity)'}`)\n lines.push(` from: ${f.from}`)\n lines.push(` to: ${f.to}`)\n lines.push(` fix: ${f.suggestion}`)\n if (f.perf !== '') lines.push(` perf: ${f.perf}`)\n }\n for (const f of report.divergentNodes) {\n lines.push(`parseman divergent-node: \\`${f.nodeType}\\` is built by ${f.count} different productions sharing ${f.sharedTerms.length} term(s): ${f.sharedTerms.map(t => `\\`${t}\\``).join(', ')}`)\n for (const p of f.productions) lines.push(` ${siteToString(p.site)}\\n ${p.shape}\\n unique to it: ${p.distinctTerms.length === 0 ? '(none)' : p.distinctTerms.map(t => `\\`${t}\\``).join(', ')}`)\n lines.push(` fix: ${f.suggestion}`)\n }\n for (const f of report.keywordRegexes) {\n const src = f.source.length > 90 ? `${f.source.slice(0, 88)}\u2026` : f.source\n lines.push(`parseman ${f.bug ? 'BUG ' : ''}keyword-regex @ ${siteToString(f.site)}: /${src}/${f.flags}${f.vocabulary ? ` [${f.words.length} literal words${f.longestFirst ? '' : ', NOT longest-first'}]` : ''}${f.hazards.length > 0 ? ` [${f.hazards.length} prefix hazard(s)${f.bug ? ', UNRESCUED' : ''}]` : ''}${f.caseFoldRisk ? ' [CASE-FOLD RISK]' : ''}`)\n lines.push(` fix: ${f.suggestion}`)\n }\n for (const f of report.duplicates) {\n lines.push(`parseman duplication: ${f.count}\u00D7 identical ${f.size}-node shape (saves ${f.savings} nodes) \u2014 ${f.shape}`)\n lines.push(` at: ${sitesLine(f.sites)}`)\n lines.push(` fix: ${f.suggestion}`)\n }\n for (const f of report.nearDuplicates) {\n lines.push(`parseman near-duplication: ${f.count} clones of a ${f.sharedSize}-node shape differing at ONE slot (saves ${f.savings} nodes) \u2014 ${f.shape}`)\n lines.push(` slot: ${f.slotPath} \u2208 { ${f.variants.join(' | ')} }`)\n lines.push(` at: ${sitesLine(f.sites)}`)\n lines.push(` fix: ${f.suggestion}`)\n }\n for (const f of report.regexFragments) {\n lines.push(`parseman regex-fragment: \\`${f.fragment}\\` (${f.branches} branches) re-spelled in ${f.count} regex() terminals`)\n lines.push(` at: ${sitesLine(f.sites)}`)\n lines.push(` fix: ${f.suggestion}`)\n }\n for (const f of report.regexClasses) {\n lines.push(`parseman regex-class${f.drifted ? ' DRIFT' : ''}: [${f.canonical}] across ${f.count} regex() terminals${f.bmpCeiling ? ' [BMP ceiling]' : ''}`)\n for (const v of f.variants) lines.push(` [${v.source}] \u00D7${v.count} ${v.delta}\\n ${sitesLine(v.sites)}`)\n lines.push(` fix: ${f.suggestion}`)\n }\n for (const f of report.overlaps) {\n lines.push(`parseman overlap @ ${siteToString(f.site)}: arm[${f.a}] \u2229 arm[${f.b}] on ${firstSetToString(f.on)}${f.sharedPrefix === null ? '' : ` \u2014 shared prefix: ${f.sharedPrefix}`}${f.handledByStrategy ? ' [sharedPrefix strategy already handles this]' : ''}`)\n lines.push(` fix: ${f.suggestion}`)\n }\n return lines\n}\n\n/** Total findings across every category \u2014 the number the `'error'` gate keys on. */\nexport function duplicationFindingCount(report: DuplicationReport): number {\n return report.duplicates.length + report.nearDuplicates.length + report.regexFragments.length\n + report.regexClasses.length + report.overlaps.length + report.rewrites.length\n + report.divergentNodes.length + report.structureLoss.length + report.keywordRegexes.length\n}\n", "/**\n * Structural recognition of \"scannable\" parser arms \u2014 regex shapes that lower to\n * a tight character-scan loop instead of `regex.exec` / combinator dispatch. Each\n * shape is derived from the regex STRUCTURE, not from any hardcoded knowledge\n * that a given regex \"is whitespace\" or \"is a comment\":\n *\n * [X]+ / [X]* \u2192 run while the char \u2208 X (chars)\n * <lit>[^X]* \u2192 consume <lit>, run until char \u2208 X (until)\n * <open>(?:\u2026)*<close> \u2192 consume <open>, run to <close> literal (delimited)\n * x?<lit><[X]*>\u2026 \u2192 a general linear chain of lit/run (seq)\n *\n * `seq` is the CATEGORY generalization: any chain of literal segments (required\n * or optional `x?`) and char-class runs (positive/negated, `?`/`*`/`+`). It\n * subsumes CSS/Less `-?ident`, `--custom`, `@-?keyword`, `[^\u2026]+`, `::?`, etc.\n * without hardcoding a single byte \u2014 lowered only when a greedy one-pass scan\n * provably equals the engine's backtracking (see `seqIsUnambiguous`).\n *\n * A `oneOrMore(choice(a, b, \u2026))` where every arm is one of these compiles to a\n * single char-dispatch loop with one branch per arm \u2014 any count/order, because\n * the shapes dispatch on their first 1\u20132 chars and are checked in turn. Trivia\n * (whitespace + comments) is just the value-discarded instance of this; nothing\n * here is trivia-specific.\n */\n\n/**\n * One segment of a `seq` shape. A `seq` is the general category \"a fixed linear\n * chain of literals and char-class runs\" \u2014 no alternation, no groups, no\n * backtracking. It subsumes every CSS/Less token that is \"(optional prefix)?\n * literal* char-run* \u2026\" without hardcoding any particular byte:\n *\n * lit \u2014 a run of fixed code points, optionally present (`x?`) or required.\n * run \u2014 a char-class run: positive/negated ranges, min 0/1, bounded to one\n * char (`[x]`, `[x]?`) or unbounded (`[x]*`, `[x]+`).\n * group \u2014 a non-capturing sub-pattern `(?:\u2026)` (\u00A78f), quantified the same way\n * as `run` (`min`/`unbounded`). `inner` is any already-recognized\n * `ScanShape` for the group's body (so a group can itself contain\n * `seq`/`chars`/`alt`/\u2026 \u2014 including a nested alternation via \u00A78e).\n * Only used when `groupInnerSafe(inner)` holds (see that function).\n *\n * We only lower a `seq` when greedy left-to-right scanning provably equals the\n * regex engine's backtracking match (see `seqIsUnambiguous`).\n */\nexport type SeqPart =\n | { part: 'lit'; cps: number[]; optional: boolean }\n // A char-class run with a general counted quantifier: `min`..`max` occurrences.\n // `max === Infinity` is unbounded (`+`/`*`/`{n,}`). The four legacy shapes map\n // as `+`\u2192{1,\u221E} `*`\u2192{0,\u221E} `?`\u2192{0,1} bare\u2192{1,1}; `{n,m}` (\u00A78c) fills the rest.\n // Greedy scanning has \"wiggle\" a backtracker could exploit iff `max > min`.\n | { part: 'run'; ranges: Array<[number, number]>; negated: boolean; min: number; max: number }\n | { part: 'group'; inner: ScanShape; min: 0 | 1; unbounded: boolean }\n\nexport type ScanShape =\n | { kind: 'chars'; ranges: Array<[number, number]>; minOne: boolean }\n | { kind: 'ident'; head: Array<[number, number]>; tail: Array<[number, number]> }\n | { kind: 'until'; open: number[]; stop: Array<[number, number]> }\n | { kind: 'delimited'; open: number[]; close: number[] }\n // <q>(?:[^<q>\\\\]|\\\\.)*<q> \u2014 a quote-delimited string with backslash escapes.\n // `excluded` = the body's negated class (always contains the quote + backslash,\n // maybe a newline); `escLineTerm` = whether `\\\\` may be followed by a line\n // terminator (`\\\\[\\s\\S]` \u2192 true, `\\\\.` \u2192 false, since `.` excludes them).\n | { kind: 'string'; quote: number; excluded: Array<[number, number]>; escLineTerm: boolean }\n // A linear chain of literal/char-run segments (optional prefixes, literal\n // openers, negated runs, \u2026) \u2014 the general CSS/Less token category.\n | { kind: 'seq'; parts: SeqPart[] }\n // A pure literal matched case-insensitively (`/i`): the general \"ASCII\n // case-fold literal\" category (CSS `url(`, `@media` keywords under `i`, \u2026).\n | { kind: 'litFold'; open: number[] }\n // `<inner>(?!class)` / `<inner>(?=class)` \u2014 a trailing lookahead boundary\n // check on an already-lowered shape. Zero-width: `end` is `inner`'s end, never\n // extended. `classNegated` is the bracket's OWN `[^\u2026]` negation (e.g.\n // `(?![0-9a-fA-F])` vs `(?=[^0-9])`), independent of `negative` (`!` vs `=`).\n | { kind: 'lookahead'; inner: ScanShape; ranges: Array<[number, number]>; classNegated: boolean; negative: boolean }\n // `A|B|C` \u2014 top-level alternation (\u00A78e), split outside any `[]`/`()`, each arm\n // independently lowered. `disjoint` is true iff every pair of arms has\n // provably non-overlapping first-char sets (`firsts[i]` holds that arm's set,\n // or `null` if it could start with any char \u2014 an unbounded-optional `chars`\n // arm, or a non-disjoint mix). When `disjoint`, codegen dispatches straight to\n // the one matching arm; otherwise it tries arms in order and takes the first\n // that succeeds \u2014 the same ordered-choice semantics as regex `|` itself (see\n // the ordered-vs-longest verification in PERF_IDEAS \u00A78e).\n | { kind: 'alt'; arms: ScanShape[]; disjoint: boolean; firsts: Array<CharSet | null> }\n\n/** Backslash (`\\`) code point \u2014 the escape lead char in string shapes. */\nexport const BACKSLASH = 92\n\nconst META = new Set('()[]{}*+?|^$.'.split(''))\n\n// Regex char-class primitives are shared with the interpreter's first-set\n// analyzer and `regex()`'s scanner \u2014 see `../regex/classes.ts`. `SPACE_RANGES`\n// and `parseClassRanges` are re-exported here because codegen's boundary-class\n// lowering imports them from this module.\nimport { CLASS_ESCAPES, SPACE_RANGES, shorthandRanges, readUnicodeEscape, parseClassRanges } from '../regex/classes.ts'\nimport { SELECTED_ROOT_STRIDE } from '../cst/trivia-entries.ts'\n\n/** Placeholder fields filled with the trivia span once the enclosing run ends. */\nconst SELECTED_ROOT_OWNED_RANGE_PLACEHOLDER = Array.from(\n { length: SELECTED_ROOT_STRIDE - 3 },\n () => '0',\n).join(', ')\nexport { SPACE_RANGES, parseClassRanges }\n\n/** A single class token (`[...]` or `\\d`/`\\w`) to ranges, or null. Rejects negation. */\nfunction classToRanges(cls: string): Array<[number, number]> | null {\n if (cls === '\\\\d') return shorthandRanges('d')\n if (cls === '\\\\w') return shorthandRanges('w')\n if (cls === '\\\\s') return shorthandRanges('s')\n const body = cls.slice(1, -1)\n if (body.startsWith('^')) return null\n return parseClassRanges(body)\n}\n\n/** ASCII case-insensitive letter check for `i`-flag literal lowering. */\n// ASCII case-insensitive char compare. For a letter, upper/lower differ only in\n// bit 0x20, and `(c | 32) === <lowercase>` is satisfied by EXACTLY that letter's\n// two cases (nothing else) \u2014 one OR + one compare, ~1.75\u00D7 faster than the\n// two-compare `(c===U || c===L)` form (measured). `(\u2026 | 32)` is parenthesized\n// because `===` binds tighter than `|`. Non-letters keep an exact compare.\n// Exported so the case-insensitive `literal()` emitter (codegen) shares the exact\n// same ASCII fold as `/i`-flag regex lowering \u2014 no `Intl.Collator` on any path.\nexport const foldEq = (cVar: string, cp: number): string => {\n if (cp >= 65 && cp <= 90) return `(${cVar} | 32) === ${cp + 32}`\n if (cp >= 97 && cp <= 122) return `(${cVar} | 32) === ${cp}`\n return `${cVar} === ${cp}`\n}\n\n/** All-literal regex fragment (`\\/\\*`, `\\/\\/`, \u2026) \u2192 its code points, or null on an unescaped metachar. */\nfunction literalCodePoints(frag: string): number[] | null {\n const out: number[] = []\n let i = 0\n while (i < frag.length) {\n const ch = frag[i]!\n if (ch === '\\\\') {\n const e = frag[i + 1]\n if (e === undefined) return null\n out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e]! : e.codePointAt(0)!)\n i += 2\n continue\n }\n if (META.has(ch)) return null\n out.push(ch.codePointAt(0)!)\n i += 1\n }\n return out.length ? out : null\n}\n\n/**\n * Recognize a quote-delimited string with backslash escapes:\n * <q>(?:[^\u2026]|\\\\.)*<q> or <q>(?:\\\\.|[^\u2026])*<q> (`?:` optional)\n * where the negated body class contains the quote and the backslash, and the\n * escape body is `.` (no line terminators) or `[\\s\\S]` (any char). Returns null\n * for anything else so the caller falls back to `regex.exec`.\n */\nfunction parseStringShape(source: string): ScanShape | null {\n const q = source[0]\n if (q === undefined || q === '\\\\' || META.has(q)) return null\n if (source.length < 3 || source[source.length - 1] !== q) return null\n const inner = source.slice(1, -1)\n const CLS = String.raw`\\[\\^((?:\\\\.|[^\\]])+)\\]`\n const ESC = String.raw`\\\\\\\\(\\.|\\[\\\\s\\\\S\\]|\\[\\\\S\\\\s\\])`\n // Two arm orders; `?:` optional. Capture groups differ, so read both out.\n let body: string | undefined\n let esc: string | undefined\n let m = new RegExp(`^\\\\((?:\\\\?:)?${CLS}\\\\|${ESC}\\\\)\\\\*$`).exec(inner)\n if (m) { body = m[1]; esc = m[2] }\n else {\n m = new RegExp(`^\\\\((?:\\\\?:)?${ESC}\\\\|${CLS}\\\\)\\\\*$`).exec(inner)\n if (m) { esc = m[1]; body = m[2] }\n }\n if (body === undefined || esc === undefined) return null\n const excluded = parseClassRanges(body)\n if (!excluded) return null\n const qcp = q.codePointAt(0)!\n const inSet = (cp: number) => excluded.some(([lo, hi]) => cp >= lo && cp <= hi)\n if (!inSet(qcp) || !inSet(BACKSLASH)) return null\n return { kind: 'string', quote: qcp, excluded, escLineTerm: esc !== '.' }\n}\n\n/**\n * Parse a regex source into a linear chain of literal / char-run segments, or\n * null if it uses any construct outside that category (alternation, groups,\n * `.`, `{n,m}`, lookaround, unknown escapes). This is the STRUCTURAL recognizer\n * for the `seq` shape \u2014 it encodes categories (optional prefix, literal opener,\n * negated run, \u2026), never any specific byte value.\n */\nfunction parseSeqParts(source: string): SeqPart[] | null {\n const parts: SeqPart[] = []\n let lit: number[] = []\n const flush = () => {\n if (lit.length) { parts.push({ part: 'lit', cps: lit, optional: false }); lit = [] }\n }\n // Parse a `{n}` / `{n,}` / `{n,m}` counted quantifier at `source[at]` (`'{'`).\n // Returns the bounds and the token length, or null when it isn't a well-formed\n // brace quantifier (then the `{` is left for the caller's metachar decline).\n const braceQuant = (at: number): { min: number; max: number; len: number } | null => {\n const close = source.indexOf('}', at)\n if (close === -1) return null\n const body = source.slice(at + 1, close)\n const m = /^(\\d+)(,(\\d*)?)?$/.exec(body)\n if (!m) return null\n const min = Number(m[1])\n const max = m[2] === undefined ? min : m[3] ? Number(m[3]!) : Infinity\n // Decline degenerate/empty-capable or invalid bounds \u2014 RegExp handles them.\n if (max < 1 || min > max) return null\n return { min, max, len: close - at + 1 }\n }\n const runFrom = (ranges: Array<[number, number]>, negated: boolean, at: number) => {\n const q = source[at]\n if (q === '+') { parts.push({ part: 'run', ranges, negated, min: 1, max: Infinity }); return 1 }\n if (q === '*') { parts.push({ part: 'run', ranges, negated, min: 0, max: Infinity }); return 1 }\n if (q === '?') { parts.push({ part: 'run', ranges, negated, min: 0, max: 1 }); return 1 }\n if (q === '{') {\n const b = braceQuant(at)\n if (b) { parts.push({ part: 'run', ranges, negated, min: b.min, max: b.max }); return b.len }\n }\n parts.push({ part: 'run', ranges, negated, min: 1, max: 1 })\n return 0\n }\n let i = 0\n while (i < source.length) {\n const ch = source[i]!\n if (ch === '[') {\n // Read a char class, honoring `\\]`.\n let k = i + 1\n const neg = source[k] === '^'\n if (neg) k++\n let body = ''\n while (k < source.length && source[k] !== ']') {\n if (source[k] === '\\\\') { body += source[k]! + (source[k + 1] ?? ''); k += 2 }\n else { body += source[k]!; k++ }\n }\n if (source[k] !== ']') return null\n const ranges = parseClassRanges(body)\n if (!ranges) return null\n flush()\n i = k + 1\n i += runFrom(ranges, neg, i)\n continue\n }\n if (ch === '\\\\') {\n const e = source[i + 1]\n if (e === 'd' || e === 'w' || e === 's') {\n flush()\n i += 2\n i += runFrom(shorthandRanges(e), false, i)\n continue\n }\n // Escaped literal code point (incl. `\\uXXXX`, `\\t`, `\\(`, \u2026).\n const uni = readUnicodeEscape(source, i)\n let cp: number\n if (uni) { cp = uni.cp; i = uni.next }\n else if (e === undefined) return null\n else if (e in CLASS_ESCAPES) { cp = CLASS_ESCAPES[e]!; i += 2 }\n else if ((e >= 'a' && e <= 'z') || (e >= 'A' && e <= 'Z')) return null\n else { cp = e.codePointAt(0)!; i += 2 }\n if (source[i] === '?') { flush(); parts.push({ part: 'lit', cps: [cp], optional: true }); i++ }\n else lit.push(cp)\n continue\n }\n if (ch === '(') {\n // Only a non-capturing group `(?:\u2026)` is modeled (\u00A78f) \u2014 a bare capturing\n // group, or a lookaround appearing mid-pattern (a TRAILING one is peeled\n // off separately by `stripTrailingLookahead` before we ever get here), is\n // declined outright rather than risk misreading it.\n if (source[i + 1] !== '?' || source[i + 2] !== ':') return null\n let k = i + 3\n let depth = 1\n while (k < source.length && depth > 0) {\n const c2 = source[k]\n if (c2 === '\\\\') { k += 2; continue }\n if (c2 === '[') {\n let j = k + 1\n if (source[j] === '^') j++\n while (j < source.length && source[j] !== ']') {\n if (source[j] === '\\\\') j += 2\n else j++\n }\n if (source[j] !== ']') return null\n k = j + 1\n continue\n }\n if (c2 === '(') depth++\n else if (c2 === ')') depth--\n k++\n }\n if (depth !== 0) return null\n const body = source.slice(i + 3, k - 1)\n const inner = parseScanShape(body)\n if (!inner || !groupInnerSafe(inner)) return null\n const q = source[k]\n // `{n,m}` on a group is a \u00A78c/\u00A78f combination not modeled \u2014 decline.\n if (q === '{') return null\n flush()\n if (q === '+') {\n if (shapeCanBeEmpty(inner)) return null // would loop forever \u2014 decline\n parts.push({ part: 'group', inner, min: 1, unbounded: true })\n i = k + 1\n } else if (q === '*') {\n if (shapeCanBeEmpty(inner)) return null\n parts.push({ part: 'group', inner, min: 0, unbounded: true })\n i = k + 1\n } else if (q === '?') {\n parts.push({ part: 'group', inner, min: 0, unbounded: false })\n i = k + 1\n } else {\n parts.push({ part: 'group', inner, min: 1, unbounded: false })\n i = k\n }\n continue\n }\n // A bare metachar (`|`, `.`, `{`, stray `*`/`+`/`?`, \u2026) is outside the\n // linear-chain category \u2014 bail to the RegExp fallback.\n if (META.has(ch)) return null\n const cp = ch.codePointAt(0)!\n i += ch.length\n if (source[i] === '?') { flush(); parts.push({ part: 'lit', cps: [cp], optional: true }); i++ }\n else lit.push(cp)\n }\n flush()\n return parts.length ? parts : null\n}\n\n/**\n * Only lower a `seq` when greedy left-to-right scanning matches EXACTLY what the\n * backtracking regex engine would. The dangerous cases are (a) an optional part\n * that could also be consumed by what follows, and (b) an unbounded/repeated\n * part whose accepted class overlaps what follows \u2014 both let the engine\n * backtrack to a different boundary than a one-pass scan. We also require at\n * least one mandatory segment (so the token can't match zero-width).\n *\n * A skippable/repeatable part `p` at index `k` is checked against\n * `seqFirstAccept(parts.slice(k + 1))` \u2014 the accept-set of EVERYTHING that\n * could start at the very next position, unioned through any further chain of\n * optionals up to the next mandatory part (the exact same computation\n * `seqFirstAccept` already does for a seq's OWN leading accept-set). This\n * subsumes the simpler \"check only the immediate neighbor\" rule as the\n * one-part-following case, while also correctly handling a CHAIN of several\n * consecutive optional parts (e.g. two independent optional groups in a row,\n * `(?:\\.\\d+)?(?:[eE][+-]?\\d+)?` \u2014 JSON/GraphQL's number tail, \u00A78f): each is\n * safe precisely when disjoint from everything that could follow it, however\n * many further optionals that spans. An empty tail (`p` is the last part)\n * naturally resolves to a trivially-disjoint empty set \u2014 nothing to check.\n *\n * Uniform across `lit`/`run`/`group`: each part's own \"first char it could\n * start with\" (`partFirstAccept`) is compared via `firstSetsDisjoint` \u2014 the\n * same subset/disjoint math used everywhere else in this file (\u00A78b's\n * lookahead guard, \u00A78e's alt-dispatch guard).\n */\nfunction seqIsUnambiguous(parts: SeqPart[]): boolean {\n let hasMandatory = false\n for (let k = 0; k < parts.length; k++) {\n const p = parts[k]!\n const mandatory = p.part === 'lit' ? !p.optional : p.min >= 1\n if (mandatory) hasMandatory = true\n // A `group` part has a right-edge exposure (`groupPartExposure`): the chars a\n // backtracking engine could give back by shrinking the body's own trailing\n // quantifier, dropping the group (if optional/repeated), or \u2014 for a\n // non-disjoint alt body \u2014 switching arms. Ordered-choice greedy scanning\n // matches the engine only when NONE of those relinquished chars could also\n // start what follows the group (else the scan and the engine disagree on\n // where the group ends). Two outcomes:\n // - a concrete class \u2192 must be disjoint from `seqFirstAccept(rest)`. This\n // is the general fix for a group whose body ends in an unbounded run\n // (`(?:\\d+)\\d` must NOT lower), and it lets a mutually-exclusive fixed\n // alt (\u00A78i, exposure `null`) lower at ANY position, not just trailing.\n // - `'unsupported'` \u2192 the exposure can't be bounded (e.g. a non-disjoint\n // alt like `(?:a|ab)` that genuinely needs arm-switching). Sound only\n // when nothing follows to force reconsideration \u2014 the group must be the\n // trailing, matched-exactly-once part (\u00A78h); otherwise decline.\n if (p.part === 'group') {\n const exposure = groupPartExposure(p)\n if (exposure === 'unsupported') {\n const trailingOnce = k === parts.length - 1 && p.min === 1 && !p.unbounded\n if (!trailingOnce) return false\n } else if (exposure !== null) {\n if (!firstSetsDisjoint(exposure, seqFirstAccept(parts.slice(k + 1)))) return false\n }\n }\n // A part needs the disjoint-from-continuation check when a greedy one-pass\n // scan has freedom a backtracking engine could resolve differently: an\n // optional `lit`, a `run` with wiggle (`max > min` \u2014 covers `?`/`*`/`+` and\n // bounded `{n,m}` with `m > n`; a fixed `{n}` run has none), or an\n // optional/repeated `group`. A fixed-length run (`max === min`) consumes an\n // exact count with no boundary to shift, so it never needs the check.\n const needsDisjoint =\n p.part === 'lit' ? p.optional\n : p.part === 'run' ? p.max > p.min\n : (p.unbounded || p.min === 0)\n if (needsDisjoint) {\n const rest = seqFirstAccept(parts.slice(k + 1))\n if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false\n }\n }\n return hasMandatory\n}\n\nfunction rangesOverlap(a: Array<[number, number]>, b: Array<[number, number]>): boolean {\n return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi))\n}\n\n/** Is every range in `sub` fully contained in some range of `sup`? */\nfunction rangesSubset(sub: Array<[number, number]>, sup: Array<[number, number]>): boolean {\n return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2))\n}\n\n/**\n * A `(?!X)`/`(?=X)` operand: a char class (`[...]`, incl. `[^...]`), a\n * shorthand class (`\\d`/`\\w`/`\\s`), or a single literal/escaped char \u2014 never a\n * sub-pattern (no alternation, no nested groups). Returns the ranges to test\n * membership against, and whether the bracket itself was negated (`[^...]`) \u2014\n * independent of the lookahead's own `!`/`=` polarity.\n */\nfunction parseClassOperand(body: string): { ranges: Array<[number, number]>; negated: boolean } | null {\n if (body === '\\\\d' || body === '\\\\w' || body === '\\\\s') {\n return { ranges: shorthandRanges(body[1] as 'd' | 'w' | 's'), negated: false }\n }\n if (body.length >= 2 && body[0] === '[' && body[body.length - 1] === ']') {\n let inner = body.slice(1, -1)\n const negated = inner.startsWith('^')\n if (negated) inner = inner.slice(1)\n const ranges = parseClassRanges(inner)\n return ranges ? { ranges, negated } : null\n }\n const cps = literalCodePoints(body)\n if (cps && cps.length === 1) return { ranges: [[cps[0]!, cps[0]!]], negated: false }\n return null\n}\n\ntype TrailingLookahead = {\n base: string\n ranges: Array<[number, number]>\n classNegated: boolean\n negative: boolean\n}\n\ntype CharSet = { ranges: Array<[number, number]>; negated: boolean }\n\n/** Is char-set `t` fully contained in char-set `u`? Handles all 4 sign combos. */\nfunction classSubset(t: CharSet, u: CharSet): boolean {\n if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges)\n if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges) // t \u2286 \u00ACu \u27FA t \u2229 u = \u2205\n if (t.negated && !u.negated) return false // \u00ACt \u2286 u is never true for finite ranges u\n return rangesSubset(u.ranges, t.ranges) // \u00ACt \u2286 \u00ACu \u27FA u \u2286 t\n}\n\n/** Are char-sets `t` and `u` disjoint (no overlap)? Handles all 4 sign combos. */\nfunction classDisjoint(t: CharSet, u: CharSet): boolean {\n if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges)\n if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges) // t \u2229 \u00ACu = \u2205 \u27FA t \u2286 u\n if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges) // symmetric\n return false // \u00ACt \u2229 \u00ACu = \u2205 would require t \u222A u = every code point \u2014 can't prove with finite ranges\n}\n\n/** `classDisjoint`, `'any'`-aware: an empty-capable part's first-set degrades\n * to `'any'` (see `shapeFirstAccept`), and `'any'` is never disjoint from\n * anything (it could start with literally any char, including whatever the\n * other side accepts). */\nfunction firstSetsDisjoint(a: CharSet | 'any', b: CharSet | 'any'): boolean {\n if (a === 'any' || b === 'any') return false\n return classDisjoint(a, b)\n}\n\n/**\n * A shape's match as a fixed-length list of per-position char-sets, or `null`\n * if its match length is NOT fixed. Only a `seq` of mandatory `lit`s (each code\n * point is one fixed position) and mandatory single-char runs (`[x]`, min 1,\n * bounded) qualifies \u2014 anything with an optional part, an unbounded/optional\n * run, or a nested group has a variable length and returns `null`. Used to\n * decide when a non-disjoint alternation's arms are mutually exclusive (\u00A78i).\n */\nfunction fixedClassSeq(shape: ScanShape): CharSet[] | null {\n // A case-fold literal is fixed-length: each code point becomes a single-char\n // class holding BOTH ASCII cases (so mutual-exclusivity is judged on what the\n // `/i` match actually accepts \u2014 `and` vs `or` are disjoint at position 0).\n if (shape.kind === 'litFold') {\n return shape.open.map(cp => {\n const other = cp >= 65 && cp <= 90 ? cp + 32 : cp >= 97 && cp <= 122 ? cp - 32 : cp\n return other === cp\n ? { ranges: [[cp, cp]] as Array<[number, number]>, negated: false }\n : { ranges: [[Math.min(cp, other), Math.min(cp, other)], [Math.max(cp, other), Math.max(cp, other)]] as Array<[number, number]>, negated: false }\n })\n }\n if (shape.kind !== 'seq') return null\n const out: CharSet[] = []\n for (const p of shape.parts) {\n if (p.part === 'lit') {\n if (p.optional) return null\n for (const cp of p.cps) out.push({ ranges: [[cp, cp]], negated: false })\n } else if (p.part === 'run' && p.max === p.min && Number.isFinite(p.max)) {\n // A fixed-count run `[x]{n}` (incl. the bare `[x]`, n=1) is n fixed\n // positions of the same class. Variable-length runs return null below.\n for (let c = 0; c < p.max; c++) out.push({ ranges: p.ranges, negated: p.negated })\n } else {\n return null // optional/variable/unbounded run, or a nested group \u2014 length not fixed\n }\n }\n return out.length ? out : null\n}\n\n/**\n * \u00A78i \u2014 is a NON-disjoint `alt` shape one whose arms are pairwise MUTUALLY\n * EXCLUSIVE fixed-length token sets? Every arm must be a `fixedClassSeq`, and no\n * arm's match may ever be a prefix (proper, or an equal-length overlap) of\n * another arm's \u2014 i.e. for every ordered pair (A, B) with `A.length <= B.length`\n * there is a position within A where their classes are DISJOINT. That property\n * guarantees at most one arm matches at any input position, so ordered-choice\n * commit lands on the SAME arm (and the SAME fixed end) the engine would, with\n * no arm to switch to under backtracking \u2014 sound in ANY position, not just\n * trailing (unlike the general non-disjoint alt, which needs the trailing-once\n * gate in `seqIsUnambiguous`). `(?:ab|ac)` and `(?:foo|barn)` qualify;\n * `(?:a|ab)` (arm `a` is a prefix of `ab`) does NOT.\n */\nfunction altFixedMutuallyExclusive(shape: ScanShape): boolean {\n if (shape.kind !== 'alt' || shape.disjoint) return false\n const lists: CharSet[][] = []\n for (const arm of shape.arms) {\n const f = fixedClassSeq(arm)\n if (!f) return false\n lists.push(f)\n }\n for (let i = 0; i < lists.length; i++) {\n for (let j = 0; j < lists.length; j++) {\n if (i === j) continue\n const a = lists[i]!\n const b = lists[j]!\n if (a.length > b.length) continue // only check the shorter (a) as a prefix of the longer (b)\n let disjointAt = false\n for (let p = 0; p < a.length; p++) {\n if (classDisjoint(a[p]!, b[p]!)) { disjointAt = true; break }\n }\n if (!disjointAt) return false // `a` could be a prefix of `b` \u2014 not mutually exclusive\n }\n }\n return true\n}\n\n/**\n * The char-set a backtracking engine could relinquish at a `group` PART's right\n * edge \u2014 its \"trailing exposure\". Combines two independent backtrack moves: the\n * group body's OWN trailing wiggle (`trailingBacktrackClass(inner)`, e.g. the\n * body ends in an unbounded run), and \u2014 if the group is optional (`min 0`) or\n * repeated (`unbounded`) \u2014 the \"drop the last occurrence\" move, which exposes\n * the body's own first-char set. A required-once group (`min 1`, bounded) has\n * only the first move. Returns `'unsupported'` if either component can't be\n * pinned to a concrete set. This is the single computation used everywhere a\n * group's right boundary matters: `trailingBacktrackClass` (a `seq` ending in a\n * group) and `seqIsUnambiguous` (a group at ANY position, followed by more).\n */\nfunction groupPartExposure(part: Extract<SeqPart, { part: 'group' }>): CharSet | null | 'unsupported' {\n const innerWiggle = trailingBacktrackClass(part.inner)\n if (innerWiggle === 'unsupported') return 'unsupported'\n const requiredOnce = !part.unbounded && part.min === 1\n if (requiredOnce) return innerWiggle // no \"drop it\" move possible \u2014 only inner's own wiggle matters\n const dropExposed = shapeFirstAccept(part.inner)\n if (dropExposed === 'any') return 'unsupported'\n if (innerWiggle === null) return dropExposed\n const unioned = unionCharSets([innerWiggle, dropExposed])\n return unioned === 'any' ? 'unsupported' : unioned\n}\n\n/**\n * The char-class a backtracking engine could expose by shrinking `shape`'s\n * OWN trailing quantifier by one position \u2014 i.e. the \"wiggle room\" a trailing\n * lookahead could interact with. Returns `null` when `shape`'s matched length\n * is fully fixed once it succeeds (no quantifier to shrink), which makes\n * wrapping it in a lookahead unconditionally safe. Returns `'unsupported'` for\n * shape kinds whose backtracking semantics aren't modeled here (declines the\n * lookahead lowering rather than risking an unproven guard).\n *\n * Only the LAST quantifier matters: `seqIsUnambiguous` already proves every\n * EARLIER part of a `seq` has exactly one valid parse (no alternate length),\n * so the sole remaining freedom a backtracking engine has \u2014 once we attach an\n * external trailing lookahead \u2014 is in whatever quantifier sits at the very end.\n */\nfunction trailingBacktrackClass(shape: ScanShape): CharSet | null | 'unsupported' {\n if (shape.kind === 'chars') return { ranges: shape.ranges, negated: false }\n if (shape.kind === 'ident') return { ranges: shape.tail, negated: false }\n if (shape.kind === 'litFold') return null // fixed-length literal \u2014 no quantifier at all\n if (shape.kind === 'seq') {\n const last = shape.parts[shape.parts.length - 1]!\n if (last.part === 'lit') return null // fixed literal tail \u2014 no wiggle room\n if (last.part === 'run') {\n // Wiggle iff `max > min`: unbounded (`+`/`*`/`{n,}`), optional (`[x]?`),\n // or bounded `{n,m}` with `m > n` all let a backtracker shrink the trailing\n // run by \u22651 position. A fixed run (`[x]`, `{n}`, `max === min`) has no\n // choice \u2014 no wiggle, hence no risk.\n if (last.max > last.min) return { ranges: last.ranges, negated: last.negated }\n return null\n }\n // A trailing `group` (\u00A78f): its trailing exposure combines its body's own\n // wiggle and (if optional/repeated) the \"drop it\" move \u2014 see\n // `groupPartExposure`, shared with `seqIsUnambiguous`.\n return groupPartExposure(last)\n }\n if (shape.kind === 'alt') {\n // Arm-switching hazard (see `groupInnerSafe`) applies here too: a\n // non-disjoint alt can't be trusted to expose the right wiggle class,\n // since which arm ultimately \"wins\" could itself change under backtrack.\n // A disjoint alt has no such hazard \u2014 exactly one arm is ever reachable\n // from a given starting char, so the union of every arm's OWN wiggle\n // covers whichever one is actually taken. A non-disjoint alt whose arms are\n // fixed-length and mutually exclusive (\u00A78i) also has no wiggle \u2014 exactly one\n // arm matches at any position and its length is fixed, so `null`.\n if (!shape.disjoint) return altFixedMutuallyExclusive(shape) ? null : 'unsupported'\n const classes = shape.arms.map(trailingBacktrackClass)\n if (classes.some(c => c === 'unsupported')) return 'unsupported'\n const concrete = classes.filter((c): c is CharSet => c !== null)\n if (concrete.length === 0) return null\n const unioned = unionCharSets(concrete)\n return unioned === 'any' ? 'unsupported' : unioned\n }\n // `until`/`delimited`/`string`/nested `lookahead` \u2014 not modeled; decline.\n return 'unsupported'\n}\n\n/**\n * Is it safe to lower `inner(?!ranges)` / `inner(?=ranges)` to a one-shot\n * post-match check (vs. requiring real backtracking)? See PERF_IDEAS \u00A78b.\n */\nfunction lookaheadUnambiguous(inner: ScanShape, operand: CharSet, negative: boolean): boolean {\n const tail = trailingBacktrackClass(inner)\n if (tail === 'unsupported') return false\n if (tail === null) return true // inner's length is fixed \u2014 no backtracking possible\n // Negative `(?!op)`: safe iff every char the tail could expose is ALSO\n // excluded by `op` (so shrinking never turns a failure into a pass).\n // Positive `(?=op)`: safe iff the tail is disjoint from `op` (so shrinking\n // could never expose a char that suddenly satisfies `op`).\n return negative ? classSubset(tail, operand) : classDisjoint(tail, operand)\n}\n\n/**\n * Strip a trailing `(?!class)` / `(?=class)` boundary assertion, if present.\n * Only a TRAILING lookahead with a char-class/single-char operand is\n * recognized \u2014 no sub-patterns, no nested groups. Anything else containing an\n * unescaped `(` is left to the other recognizers, which reject it as a\n * metachar (`META`) and fall back to `RegExp.exec`, so a false read here can\n * only fail to lower, never mis-lower.\n */\nfunction stripTrailingLookahead(source: string): TrailingLookahead | null {\n if (!source.endsWith(')')) return null\n for (const negative of [true, false] as const) {\n const lead = negative ? '(?!' : '(?='\n const idx = source.lastIndexOf(lead)\n if (idx === -1) continue\n const body = source.slice(idx + lead.length, -1)\n const operand = body.length ? parseClassOperand(body) : null\n if (!operand) continue\n return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative }\n }\n return null\n}\n\n/**\n * Strip ONE redundant outer `(?:\u2026)` wrapper, if it spans the ENTIRE source\n * (nothing before it, nothing \u2014 not even a quantifier \u2014 after its matching\n * close). A non-capturing group with no trailing quantifier is semantically\n * identical to its bare contents, so this is always safe to unwrap; it's what\n * lets `(?:a|b|c)` (a whole-token alternation written as idiomatic regex\n * style, e.g. CSS `basicSel`) reach the top-level `|` split below. Bails\n * (leaves `source` untouched) on anything that isn't a clean whole-string\n * wrap: unbalanced parens, or a group that closes before the string ends\n * (which would mean a quantifier or trailing content follows it).\n */\nfunction unwrapOuterGroup(source: string): string {\n while (source.startsWith('(?:')) {\n let depth = 1\n let i = 3\n let ok = true\n while (i < source.length && depth > 0) {\n const ch = source[i]\n if (ch === '\\\\') { i += 2; continue }\n if (ch === '[') {\n let j = i + 1\n if (source[j] === '^') j++\n while (j < source.length && source[j] !== ']') {\n if (source[j] === '\\\\') j += 2\n else j++\n }\n if (source[j] !== ']') { ok = false; break }\n i = j + 1\n continue\n }\n if (ch === '(') depth++\n else if (ch === ')') depth--\n i++\n }\n if (!ok || depth !== 0 || i !== source.length) break\n source = source.slice(3, -1)\n }\n return source\n}\n\n/**\n * Split a regex source on top-level `|` \u2014 i.e. `|` outside any `[]` bracket\n * class and outside any `(...)` group (parens still count toward depth even\n * though we don't otherwise understand groups; a `|` nested in one is never a\n * split point). Returns null if there's no top-level `|` at all, if brackets/\n * parens are unbalanced, or if any resulting alternative is empty (a\n * zero-width arm \u2014 an unmodeled edge case, decline rather than mis-lower).\n * One redundant whole-string `(?:\u2026)` wrapper is stripped first (see\n * `unwrapOuterGroup`) so `(?:a|b)` splits the same as bare `a|b`.\n */\nfunction splitTopLevelAlternation(source: string): string[] | null {\n const body = unwrapOuterGroup(source)\n const parts: string[] = []\n let depth = 0\n let last = 0\n let i = 0\n while (i < body.length) {\n const ch = body[i]\n if (ch === '\\\\') { i += 2; continue }\n if (ch === '[') {\n let j = i + 1\n if (body[j] === '^') j++\n while (j < body.length && body[j] !== ']') {\n if (body[j] === '\\\\') j += 2\n else j++\n }\n if (body[j] !== ']') return null\n i = j + 1\n continue\n }\n if (ch === '(') { depth++; i++; continue }\n if (ch === ')') { depth--; if (depth < 0) return null; i++; continue }\n if (ch === '|' && depth === 0) {\n parts.push(body.slice(last, i))\n last = i + 1\n i++\n continue\n }\n i++\n }\n if (depth !== 0) return null\n parts.push(body.slice(last))\n if (parts.length < 2 || parts.some(p => p.length === 0)) return null\n return parts\n}\n\n/** Can `shape` match the empty string? Only an unbounded `chars` run with no\n * `+` (`[x]*`) can \u2014 every other shape requires at least one fixed/mandatory\n * char (`seq` always has \u22651 mandatory part per `seqIsUnambiguous`; `ident`/\n * `until`/`delimited`/`string`/`litFold` all require a fixed opening char). A\n * `lookahead`/`alt` defer to what they wrap. */\nfunction shapeCanBeEmpty(shape: ScanShape): boolean {\n if (shape.kind === 'chars') return !shape.minOne\n if (shape.kind === 'lookahead') return shapeCanBeEmpty(shape.inner)\n if (shape.kind === 'alt') return shape.arms.some(shapeCanBeEmpty)\n return false\n}\n\n/** Exact union of char-sets when it's computable, else `'any'` (conservative:\n * forces ordered dispatch rather than guessing at a mixed-polarity union, or\n * when any input is already `'any'`). A single concrete-set list is returned\n * as-is (including if negated) \u2014 the ambiguity is only in combining \u22652 sets\n * of differing polarity. */\nfunction unionCharSets(sets: Array<CharSet | 'any'>): CharSet | 'any' {\n if (sets.some(s => s === 'any')) return 'any'\n const concrete = sets as CharSet[]\n if (concrete.length === 1) return concrete[0]!\n if (concrete.some(s => s.negated)) return 'any'\n return { ranges: concrete.flatMap(s => s.ranges), negated: false }\n}\n\n/** A single seq part's own first-char set, ignoring whether it's skippable.\n * A `group` part delegates to its inner shape's own first-set (\u00A78f) \u2014\n * possibly `'any'` if the inner shape can match empty or is a non-disjoint\n * `alt` (see `shapeFirstAccept`). */\nfunction partFirstAccept(part: SeqPart): CharSet | 'any' {\n if (part.part === 'lit') return { ranges: [[part.cps[0]!, part.cps[0]!]], negated: false }\n if (part.part === 'run') return { ranges: part.ranges, negated: part.negated }\n return shapeFirstAccept(part.inner)\n}\n\n/** The char-set a `seq` can start with: union of each leading skippable part's\n * own set up to and including the first mandatory part (after which later\n * parts can no longer be what the very first input char matches). */\nfunction seqFirstAccept(parts: SeqPart[]): CharSet | 'any' {\n const sets: Array<CharSet | 'any'> = []\n for (const p of parts) {\n const mandatory = p.part === 'lit' ? !p.optional : p.min >= 1\n sets.push(partFirstAccept(p))\n if (mandatory) break\n }\n return unionCharSets(sets)\n}\n\n/**\n * Is `inner` safe to use as a group's body (\u00A78f)? `chars`/`ident`/`seq`/\n * `litFold` are always safe: each is only ever constructed after proving (via\n * `seqIsUnambiguous` or equivalent) that greedy resolution is the UNIQUE valid\n * match at a given position \u2014 there is no alternate length for a backtracker\n * to reconsider, so treating the group as an atomic, resolve-once unit is\n * sound regardless of what follows it. A `lookahead` defers to what it wraps\n * (zero-width, adds no new choice). An `alt` is safe ONLY when `disjoint`:\n * a NON-disjoint alt (`(?:a|ab)`) genuinely can need real backtracking into a\n * DIFFERENT arm if something after the group fails (verified: `/^(?:a|ab)c/`\n * matches \"abc\" via the SECOND arm, only because the first arm's match left\n * \"c\" unsatisfied) \u2014 our ordered-dispatch codegen resolves once and never\n * reconsiders, so that case must decline. `until`/`delimited`/`string` are\n * declined outright, same conservative call as `trailingBacktrackClass`.\n */\nfunction groupInnerSafe(shape: ScanShape): boolean {\n if (shape.kind === 'chars' || shape.kind === 'ident' || shape.kind === 'seq' || shape.kind === 'litFold') return true\n if (shape.kind === 'lookahead') return groupInnerSafe(shape.inner)\n // A disjoint alt is safe anywhere. A NON-disjoint alt is admitted here too,\n // but its ordered-choice-commit is only sound in the trailing-once position \u2014\n // `seqIsUnambiguous` (\u00A78h) enforces that positional constraint; a non-trailing\n // or optional/repeated non-disjoint-alt group is rejected there.\n if (shape.kind === 'alt') return true\n return false\n}\n\n/**\n * The set of first chars a NON-EMPTY match of `shape` could start with, or\n * `'any'` if that can't be pinned down to a single char-set (used to decide\n * whether `alt` arms have disjoint first-sets). A shape that can match empty\n * degrades to `'any'`: since it could succeed with zero chars regardless of\n * what follows, it would \"accept\" any first char by never actually needing one\n * \u2014 treating that as disjoint from anything else would be unsound.\n */\nfunction shapeFirstAccept(shape: ScanShape): CharSet | 'any' {\n if (shapeCanBeEmpty(shape)) return 'any'\n switch (shape.kind) {\n case 'chars': return { ranges: shape.ranges, negated: false }\n case 'ident': return { ranges: shape.head, negated: false }\n case 'until': return { ranges: [[shape.open[0]!, shape.open[0]!]], negated: false }\n case 'delimited': return { ranges: [[shape.open[0]!, shape.open[0]!]], negated: false }\n case 'string': return { ranges: [[shape.quote, shape.quote]], negated: false }\n case 'litFold': {\n const cp = shape.open[0]!\n if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false }\n if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false }\n return { ranges: [[cp, cp]], negated: false }\n }\n case 'seq': return seqFirstAccept(shape.parts)\n case 'lookahead': return shapeFirstAccept(shape.inner) // zero-width \u2014 doesn't change first-char acceptance\n case 'alt': {\n // The set of first chars is the union of every arm's own first-set,\n // independent of whether the arms are mutually disjoint (that flag only\n // governs dispatch, not what the shape can start with). Any arm whose\n // first-set is itself `'any'` (stored as `null`) makes the union `'any'`.\n const sets = shape.firsts.map((f): CharSet | 'any' => (f === null ? 'any' : f))\n return unionCharSets(sets)\n }\n }\n}\n\n/** True iff every pair of `sets` is provably disjoint; `'any'` is disjoint from nothing. */\nfunction allPairsDisjoint(sets: Array<CharSet | 'any'>): boolean {\n for (let i = 0; i < sets.length; i++) {\n const a = sets[i]!\n if (a === 'any') return false\n for (let j = i + 1; j < sets.length; j++) {\n const b = sets[j]!\n if (b === 'any' || !classDisjoint(a, b)) return false\n }\n }\n return true\n}\n\n/**\n * Recognize top-level alternation `A|B|C` (\u00A78e): split on `|` outside any\n * `[]`/`()`, lower each arm independently (an arm may itself be a `seq`,\n * `chars`, `lookahead`, \u2026, or even nested alternation via one redundant\n * `(?:\u2026)` wrapper \u2014 see `unwrapOuterGroup`). Declines (null) unless EVERY arm\n * independently lowers \u2014 a single unsupported arm (e.g. one with a nested\n * capturing/non-capturing group, \u00A78f) falls the whole alternation back to\n * `RegExp.exec` rather than partially lowering.\n */\nfunction parseAlternation(source: string): ScanShape | null {\n const parts = splitTopLevelAlternation(source)\n if (!parts) return null\n const arms: ScanShape[] = []\n for (const p of parts) {\n const arm = parseScanShape(p)\n if (!arm) return null\n arms.push(arm)\n }\n const firstSets = arms.map(shapeFirstAccept)\n const disjoint = allPairsDisjoint(firstSets)\n return { kind: 'alt', arms, disjoint, firsts: firstSets.map(f => (f === 'any' ? null : f)) }\n}\n\n/**\n * Recognize one scannable arm from its regex source, or null if it isn't one of\n * the structural shapes. Top-level alternation (\u00A78e) is tried FIRST \u2014 `|` has\n * the lowest precedence in regex, so splitting on it happens before anything\n * else, with each arm then recursively re-entering this same function (so an\n * arm's own trailing lookahead, or its own nested `(?:\u2026)`-wrapped alternation,\n * is still recognized). Failing that: char-class run, ident, string, then\n * open-until-terminator, delimited, and finally the general linear `seq` chain.\n * A trailing `(?!class)`/`(?=class)` is peeled off next (\u00A78b) and re-wraps\n * whatever shape the remaining base recognizes as.\n */\nexport function parseScanShape(source: string): ScanShape | null {\n const alt = parseAlternation(source)\n if (alt) return alt\n const la = stripTrailingLookahead(source)\n if (la) {\n const inner = parseScanShapeCore(la.base)\n if (!inner) return null\n const operand: CharSet = { ranges: la.ranges, negated: la.classNegated }\n if (!lookaheadUnambiguous(inner, operand, la.negative)) return null\n return { kind: 'lookahead', inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative }\n }\n return parseScanShapeCore(source)\n}\n\n/**\n * Split a regex fragment on top-level `|` (outside `[]`/`()`), escape-aware.\n * Unlike `splitTopLevelAlternation` this allows a SINGLE arm (no `|` at all) and\n * does not unwrap an outer group \u2014 it's used to enumerate the arms of a\n * `delimited` body, which may legitimately be one bare arm (`[^x]`). Returns\n * null on unbalanced brackets/parens.\n */\nfunction splitDelimArms(body: string): string[] | null {\n const arms: string[] = []\n let depth = 0\n let last = 0\n let i = 0\n while (i < body.length) {\n const ch = body[i]\n if (ch === '\\\\') { i += 2; continue }\n if (ch === '[') {\n let j = i + 1\n if (body[j] === '^') j++\n while (j < body.length && body[j] !== ']') {\n if (body[j] === '\\\\') j += 2\n else j++\n }\n if (body[j] !== ']') return null\n i = j + 1\n continue\n }\n if (ch === '(') { depth++; i++; continue }\n if (ch === ')') { depth--; if (depth < 0) return null; i++; continue }\n if (ch === '|' && depth === 0) { arms.push(body.slice(last, i)); last = i + 1 }\n i++\n }\n if (depth !== 0) return null\n arms.push(body.slice(last))\n return arms\n}\n\n/**\n * Prove that the `delimited` lowering \u2014 \"scan the raw input to the first full\n * `close` literal, ignoring the body entirely\" \u2014 is EQUIVALENT to the real regex\n * `<open>(?:body)*<close>`. That equivalence holds iff `(?:body)*` can never\n * match a string that contains the `close` literal: then greedy repetition stops\n * exactly before the first `close`, which is where the scan lands too.\n *\n * We only prove this for the two-code-point delimiter-safe idiom (the sole shape that expresses\n * \"any char that doesn't form the close\"):\n *\n * n === 1 (`close` is one char l0): body must be exactly `[^l0]`.\n * n === 2 (`close` is `l0 l1`): body must be exactly `[^l0] | l0(?!l1)`.\n *\n * In both the bulk arm `[^l0]` consumes every char except l0, and the guard arm\n * (n === 2) consumes l0 only when it is NOT followed by l1 \u2014 so the body admits\n * l0 but never the full `l0 l1`, and rejects NOTHING else (a body that excluded\n * some non-close char would stop early while the raw scan skipped ahead to the\n * next close, diverging). Anything outside this template \u2014 an alternation whose\n * arms can consume the close (`z(?:a|[0-2]+)*a`), a bulk class narrower/wider\n * than `[^l0]`, a longer close, extra arms \u2014 is declined, falling through to\n * `RegExp.exec`. Sound-by-construction: we only ever return true for a body we\n * can prove never contains `close`.\n */\nfunction delimitedBodySound(body: string, close: number[]): boolean {\n const l0 = close[0]!\n if (close.length > 2) return false\n const arms = splitDelimArms(body)\n if (!arms) return false\n let bulkSeen = false\n let guardSeen = false\n for (const arm of arms) {\n // Bulk arm: a bare negated class `[^l0]` \u2014 excluded set is exactly {l0}.\n const bulk = /^\\[\\^((?:\\\\.|[^\\]])+)\\]$/.exec(arm)\n if (bulk) {\n if (bulkSeen) return false\n const ranges = parseClassRanges(bulk[1]!)\n if (!ranges || ranges.length !== 1 || ranges[0]![0] !== l0 || ranges[0]![1] !== l0) return false\n bulkSeen = true\n continue\n }\n // Guard arm (n === 2 only): `l0(?!l1)` \u2014 consume l0 unless it starts the close.\n if (close.length === 2 && arm.endsWith(')')) {\n const idx = arm.indexOf('(?!')\n if (idx === -1) return false\n const prefix = literalCodePoints(arm.slice(0, idx))\n const op = parseClassOperand(arm.slice(idx + 3, -1))\n const prefixIsL0 = prefix?.length === 1 && prefix[0] === l0\n const forbidsL1 = op && !op.negated && op.ranges.length === 1 &&\n op.ranges[0]![0] === close[1] && op.ranges[0]![1] === close[1]\n if (!prefixIsL0 || !forbidsL1 || guardSeen) return false\n guardSeen = true\n continue\n }\n return false\n }\n // Require the exact arm set: bulk always; guard iff the close is two chars.\n return bulkSeen && guardSeen === (close.length === 2)\n}\n\n/**\n * Structurally recognize `<openLit>(?:<body>)*<closeLit>` \u2014 literal opener, a\n * single non-capturing `*`-repeated group, literal closer \u2014 and lower it to a\n * `delimited` scan ONLY when `delimitedBodySound` proves the body can't contain\n * the close. Returns null (\u2192 fall through to `seq`/`RegExp.exec`) otherwise.\n */\nfunction parseDelimited(source: string): ScanShape | null {\n // An escape-aware body (a literal `\\\\` in the source \u21D2 string-like) is handled\n // by `parseStringShape`; here \"scan to first close\" would wrongly stop at an\n // escaped delimiter, so decline.\n if (source.includes('\\\\\\\\')) return null\n // Opener: literal chars up to the first unescaped `(`, which must open `(?:`.\n let i = 0\n while (i < source.length && source[i] !== '(') {\n i += source[i] === '\\\\' ? 2 : 1\n }\n if (source[i] !== '(' || source[i + 1] !== '?' || source[i + 2] !== ':') return null\n const openFrag = source.slice(0, i)\n // Balanced scan of the group body to its matching `)`.\n let k = i + 3\n let depth = 1\n while (k < source.length && depth > 0) {\n const ch = source[k]\n if (ch === '\\\\') { k += 2; continue }\n if (ch === '[') {\n let j = k + 1\n if (source[j] === '^') j++\n while (j < source.length && source[j] !== ']') {\n if (source[j] === '\\\\') j += 2\n else j++\n }\n if (source[j] !== ']') return null\n k = j + 1\n continue\n }\n if (ch === '(') depth++\n else if (ch === ')') depth--\n k++\n }\n if (depth !== 0) return null\n const body = source.slice(i + 3, k - 1)\n if (source[k] !== '*') return null\n const closeFrag = source.slice(k + 1)\n const open = literalCodePoints(openFrag)\n const close = literalCodePoints(closeFrag)\n if (!open || !close || !delimitedBodySound(body, close)) return null\n return { kind: 'delimited', open, close }\n}\n\nfunction parseScanShapeCore(source: string): ScanShape | null {\n // [X]+ / [X]* \u2014 a positive char-class run. A negated `[^X]+`/`[^X]*` is left to\n // the general `seq` path (which lowers `[^X]+` and declines zero-width `[^X]*`).\n let m = /^\\[((?:\\\\.|[^\\]])+)\\]([+*])$/.exec(source)\n if (m && !m[1]!.startsWith('^')) {\n const ranges = parseClassRanges(m[1]!)\n return ranges ? { kind: 'chars', ranges, minOne: m[2] === '+' } : null\n }\n // \\d+ / \\w+ / \\s+ / \\d* / \\w* / \\s* \u2014 a bare shorthand-class run.\n m = /^\\\\([dws])([+*])$/.exec(source)\n if (m) {\n return { kind: 'chars', ranges: shorthandRanges(m[1] as 'd' | 'w' | 's'), minOne: m[2] === '+' }\n }\n // <head><tail>* \u2014 identifier run: one head char, then a run of tail chars.\n m = /^(\\[(?:\\\\.|[^\\]])+\\]|\\\\[dws])(\\[(?:\\\\.|[^\\]])+\\]|\\\\[dws])\\*$/.exec(source)\n if (m) {\n const head = classToRanges(m[1]!)\n const tail = classToRanges(m[2]!)\n if (head && tail) return { kind: 'ident', head, tail }\n }\n // <q>(?:[^q\\\\]|\\\\.)*<q> \u2014 a quote-delimited string with escapes.\n const str = parseStringShape(source)\n if (str) return str\n // <lit>[^X]* \u2014 consume a literal opener, then run until a terminator char.\n m = /^(.*?)\\[\\^((?:\\\\.|[^\\]])+)\\]\\*$/.exec(source)\n if (m) {\n const open = literalCodePoints(m[1]!)\n const stop = parseClassRanges(m[2]!)\n if (open && stop) return { kind: 'until', open, stop }\n }\n // <open>(?:\u2026)*<close> \u2014 delimited token scanned to its first close literal.\n // Only lowered when the body provably can't contain the close (see\n // `parseDelimited` / `delimitedBodySound`); any other `X(?:\u2026)*Y` declines here\n // and falls through to the `seq` path / `RegExp.exec`.\n const delim = parseDelimited(source)\n if (delim) return delim\n // General category: a linear chain of literals + char runs (optional prefixes,\n // literal openers, negated runs), lowered only when unambiguously greedy.\n const parts = parseSeqParts(source)\n if (parts && seqIsUnambiguous(parts)) return { kind: 'seq', parts }\n return null\n}\n\n/**\n * Flag-aware wrapper around `parseScanShape`. Lowering to a raw code-point scan\n * assumes default regex semantics, so any flag that changes matching (`i` case\n * folding, `u` surrogate handling, `m`/`s` anchor/dot behavior) disables it.\n * `g`/`y` are stickiness-only and safe.\n */\nexport function scanShapeFromRegex(source: string, flags: string): ScanShape | null {\n if (/[msu]/.test(flags)) return null\n if (/i/.test(flags)) return caseFoldShape(source)\n return parseScanShape(source)\n}\n\n/**\n * Case-insensitive (`/i`) scannable shapes. Beyond a pure case-fold literal\n * (`litFold`), this recognizes a top-level alternation of literals (an ordered\n * `alt` of litFolds \u2014 CSS `(?:and|or)`, `(?:media|container|supports)`), each\n * optionally wrapped in a trailing lookahead boundary (`(?![-\\w])`, `(?=\\()`).\n * That last form is the regex spelling of `makeWord` \u2014 a keyword plus a word\n * boundary \u2014 the common CSS at-rule/keyword dispatcher. Anything else declines\n * (\u2192 `RegExp.exec`); a false read here can only fail to lower, never mis-lower.\n */\nfunction caseFoldShape(source: string): ScanShape | null {\n const la = stripTrailingLookahead(source)\n if (la) {\n const inner = caseFoldLiteralOrAlt(la.base)\n if (!inner) return null\n const operand: CharSet = { ranges: la.ranges, negated: la.classNegated }\n if (!lookaheadUnambiguous(inner, operand, la.negative)) return null\n return { kind: 'lookahead', inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative }\n }\n return caseFoldLiteralOrAlt(source)\n}\n\n/** A case-fold literal, or a top-level `a|b|c` alternation of pure literals. */\nfunction caseFoldLiteralOrAlt(source: string): ScanShape | null {\n // `foldEq` only folds ASCII letters (65\u201390 / 97\u2013122). A non-ASCII code point\n // with a Unicode case pair (\u00E9/\u00C9, \u00E4/\u00C4, \u2026) would be emitted as an exact compare\n // and silently miss its alternate case \u2014 a mis-lower, not just a failed lower.\n // Decline those so they fall back to `RegExp.exec` (correct Unicode folding),\n // preserving this file's \"a false read can only fail to lower, never mis-lower\".\n const asciiOnly = (cps: number[]): boolean => cps.every(cp => cp <= 127)\n\n const open = literalCodePoints(source)\n if (open) return asciiOnly(open) ? { kind: 'litFold', open } : null\n // `splitTopLevelAlternation` unwraps the outer group itself, so no pre-strip.\n const arms = splitTopLevelAlternation(source)\n if (!arms || arms.length < 2) return null\n const litArms: ScanShape[] = []\n for (const arm of arms) {\n const cp = literalCodePoints(arm)\n if (!cp || !asciiOnly(cp)) return null\n litArms.push({ kind: 'litFold', open: cp })\n }\n // Ordered choice: each litFold arm case-folds on its own, so skipping the\n // first-char dispatch guard (disjoint:false, firsts:null) is always correct \u2014\n // it just tries arms in order, exactly like regex `|`.\n return { kind: 'alt', arms: litArms, disjoint: false, firsts: litArms.map(() => null) }\n}\n\nexport const classCond = (cVar: string, ranges: Array<[number, number]>): string =>\n ranges\n .map(([lo, hi]) => (lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`))\n .join(' || ')\n\n/** Literal-match condition at `base + k` for each code point; uses `firstVar` at offset 0. */\nexport const litCond = (base: string, cps: number[], firstVar?: string): string =>\n cps\n .map((cp, k) =>\n k === 0 && firstVar\n ? `${firstVar} === ${cp}`\n : `input.charCodeAt(${base}${k ? ` + ${k}` : ''}) === ${cp}`,\n )\n .join(' && ')\n\n/** Line-terminator code points `.` does not match (`\\n \\r \\u2028 \\u2029`). */\nexport const LINE_TERMINATORS = [10, 13, 8232, 8233] as const\n\n/** The body-stop chars that abort a string match (excluded set minus quote/backslash). */\nexport function stringHardStop(\n shape: Extract<ScanShape, { kind: 'string' }>,\n): Array<[number, number]> {\n return shape.excluded.filter(\n ([lo, hi]) => !(lo === hi && (lo === shape.quote || lo === BACKSLASH)),\n )\n}\n\n/** Mints a fresh, unique local variable name (`prefix` + counter). */\nexport type Mint = (prefix?: string) => string\n\n/**\n * The SINGLE source of truth for how a scannable shape matches at `start`. Both\n * the terminal emitter and the trivia scan loop consume this, so no context can\n * silently reinterpret an incomplete match (e.g. an unterminated string):\n *\n * - `setup` statements (indented by `ind`) that compute the match.\n * - `ok` a boolean expr: did a token match at `start`? (zero-width for\n * `chars*` counts as a match \u2014 terminals allow it.)\n * - `end` the position AFTER the token. **Invariant:** `end === start`\n * whenever there is no progress (match failed or matched empty),\n * so the trivia loop can gate purely on `end > start`.\n *\n * `firstChar`, when supplied, is an expression already equal to\n * `charCodeAt(start)` (the trivia loop reads it once and shares it).\n */\nexport type ShapeMatch = { setup: string[]; ok: string; end: string }\n\nconst codeAt = (start: string, firstChar?: string): string =>\n firstChar ?? `input.charCodeAt(${start})`\n\n// Dispatch-planning thresholds for a disjoint alt's first-char `switch` \u2014 same\n// tuning as codegen's `planDisjointDispatch` (kept local to avoid a scannable-run\n// \u2192 codegen import cycle): only worth a jump table when every arm keys off a few\n// DISCRETE points; a wide range (`[a-z]+`) would explode into dozens of cases.\nconst ALT_SWITCH_RANGE_LIMIT = 4\nconst ALT_SWITCH_MAX_CASES = 48\nconst ALT_SWITCH_MIN_CASES = 3\n\n/**\n * A `switch` jump table for a *disjoint* alt when every arm's first-set is a few\n * discrete code points (operator/keyword first chars); else `{ kind: 'if' }` to\n * keep the range-comparison `if/else if` chain. Arms are pairwise-disjoint, so\n * each code point maps to exactly one arm.\n */\nfunction planAltSwitch(firsts: Array<CharSet | null>): { kind: 'switch'; cases: number[][] } | { kind: 'if' } {\n const cases: number[][] = []\n let total = 0\n for (const fs of firsts) {\n if (!fs || fs.negated) return { kind: 'if' } // any / negated \u2192 no discrete keys\n const pts: number[] = []\n for (const [lo, hi] of fs.ranges) {\n if (hi - lo + 1 > ALT_SWITCH_RANGE_LIMIT) return { kind: 'if' }\n for (let cp = lo; cp <= hi; cp++) pts.push(cp)\n }\n total += pts.length\n if (total > ALT_SWITCH_MAX_CASES) return { kind: 'if' }\n cases.push(pts)\n }\n return total >= ALT_SWITCH_MIN_CASES ? { kind: 'switch', cases } : { kind: 'if' }\n}\n\nexport function emitShapeMatch(\n shape: ScanShape,\n start: string,\n mint: Mint,\n ind: string,\n firstChar?: string,\n): ShapeMatch {\n if (shape.kind === 'lookahead') {\n const inner = emitShapeMatch(shape.inner, start, mint, ind, firstChar)\n const okV = mint('_ok')\n const endV = mint('_end')\n // `charInClass` folds the operand's OWN `[^\u2026]` negation in; `guardFails`\n // then applies the lookahead's `!`/`=` polarity on top. Zero-width: `end`\n // is always `inner.end` (on success) or `start` (on failure) \u2014 never past\n // where `inner` itself stopped.\n const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges)\n const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`\n const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`\n const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`\n return {\n setup: [\n ...inner.setup,\n `${ind}let ${okV} = ${inner.ok}`,\n `${ind}let ${endV} = ${inner.end}`,\n `${ind}if (${okV} && (${guardFails})) { ${okV} = false; ${endV} = ${start} }`,\n ],\n ok: okV,\n end: endV,\n }\n }\n\n if (shape.kind === 'alt') {\n const okV = mint('_ok')\n const endV = mint('_end')\n const lines: string[] = [`${ind}let ${okV} = false`, `${ind}let ${endV} = ${start}`]\n // Fast path: an alternation of case-fold literals (CSS keyword lists like\n // `@media|@container|@supports`, `even|odd`) \u2192 a `switch` on the first char\n // that jumps straight to the matching group, tails compared with the bit-OR\n // fold. Measured ~2.4x over the ordered if-chain / ~1.4x over per-arm bit-OR.\n // Ordered-choice is preserved: arms are grouped by their FOLDED first code\n // point and, within a group, emitted in source order (first match wins);\n // arms in different groups can't both match a given first char.\n if (shape.arms.length >= 2 && shape.arms.every(a => a.kind === 'litFold' && a.open.length >= 1)) {\n const groups = new Map<number, number[][]>()\n for (const a of shape.arms) {\n const open = (a as Extract<ScanShape, { kind: 'litFold' }>).open\n const f = open[0]!\n const lower = f >= 65 && f <= 90 ? f + 32 : f\n ;(groups.get(lower) ?? groups.set(lower, []).get(lower)!).push(open)\n }\n lines.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`)\n for (const [lower, arms] of groups) {\n const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`\n lines.push(`${ind} ${labels} {`)\n for (const open of arms) {\n const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp))\n const cond = tail.length ? ` && ${tail.join(' && ')}` : ''\n lines.push(`${ind} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV} = ${start} + ${open.length}; break }`)\n }\n lines.push(`${ind} break`)\n lines.push(`${ind} }`)\n }\n lines.push(`${ind}}`)\n return { setup: lines, ok: okV, end: endV }\n }\n if (shape.disjoint) {\n // Mutually exclusive first-sets: dispatch straight to the one matching\n // arm, no ordering/backtracking concerns (\u00A77b-style disjoint dispatch). A\n // `switch` jump table when arms key off discrete first chars, else the\n // range-comparison `if/else if` chain (same plan as codegen's choice()).\n const plan = planAltSwitch(shape.firsts)\n if (plan.kind === 'switch') {\n lines.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`)\n shape.arms.forEach((arm, k) => {\n const labels = plan.cases[k]!.map(cp => `case ${cp}:`).join(' ')\n const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar)\n lines.push(`${ind} ${labels} {`)\n lines.push(...m.setup)\n lines.push(`${ind} ${okV} = ${m.ok}`)\n lines.push(`${ind} ${endV} = ${m.ok} ? ${m.end} : ${start}`)\n lines.push(`${ind} break`)\n lines.push(`${ind} }`)\n })\n lines.push(`${ind}}`)\n } else {\n const c0 = codeAt(start, firstChar)\n shape.arms.forEach((arm, k) => {\n const fs = shape.firsts[k]! // non-null whenever `disjoint` is true\n const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`\n const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar)\n lines.push(`${ind}${k === 0 ? 'if' : 'else if'} (${start} < input.length && ${cond}) {`)\n lines.push(...m.setup)\n lines.push(`${ind} ${okV} = ${m.ok}`)\n lines.push(`${ind} ${endV} = ${m.ok} ? ${m.end} : ${start}`)\n lines.push(`${ind}}`)\n })\n }\n } else {\n // Overlapping first-sets: ordered choice \u2014 try each arm in turn and take\n // the first that succeeds. This is exactly regex `|`'s own semantics:\n // first alternative to match AT ALL wins on ITS OWN greedy length, never\n // compared against a later alternative's (possibly longer) match.\n const lbl = mint('_alt')\n lines.push(`${ind}${lbl}: {`)\n for (const arm of shape.arms) {\n const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar)\n lines.push(...m.setup)\n lines.push(`${ind} if (${m.ok}) { ${okV} = true; ${endV} = ${m.end}; break ${lbl} }`)\n }\n lines.push(`${ind}}`)\n }\n return { setup: lines, ok: okV, end: endV }\n }\n\n if (shape.kind === 'chars') {\n const cur = mint('_e')\n return {\n setup: [\n `${ind}let ${cur} = ${start}`,\n `${ind}while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.ranges)})) ${cur}++`,\n ],\n // `*` always matches (possibly empty); `+` needs at least one char.\n ok: shape.minOne ? `${cur} > ${start}` : 'true',\n end: cur,\n }\n }\n\n if (shape.kind === 'ident') {\n const cur = mint('_e')\n return {\n setup: [\n `${ind}let ${cur} = ${start}`,\n `${ind}if (${start} < input.length && (${classCond(codeAt(start, firstChar), shape.head)})) {`,\n `${ind} ${cur} = ${start} + 1`,\n `${ind} while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.tail)})) ${cur}++`,\n `${ind}}`,\n ],\n ok: `${cur} > ${start}`,\n end: cur,\n }\n }\n\n if (shape.kind === 'seq') {\n const endV = mint('_e')\n const okV = mint('_ok')\n const runCond = (p: Extract<SeqPart, { part: 'run' }>, at: string) => {\n const c = classCond(`input.charCodeAt(${at})`, p.ranges)\n return p.negated ? `!(${c})` : `(${c})`\n }\n const lines = [`${ind}let ${endV} = ${start}`, `${ind}let ${okV} = false`, `${ind}do {`]\n let first = true\n for (const p of shape.parts) {\n if (p.part === 'lit') {\n const L = p.cps.length\n const cond = litCond(endV, p.cps, first ? firstChar : undefined)\n if (p.optional) {\n lines.push(`${ind} if (${endV} + ${L} <= input.length && (${cond})) ${endV} += ${L}`)\n } else {\n lines.push(`${ind} if (!(${endV} + ${L} <= input.length && (${cond}))) break`)\n lines.push(`${ind} ${endV} += ${L}`)\n }\n } else if (p.part === 'run' && p.max === Infinity) {\n // Unbounded run (`+`/`*`/`{n,}`): greedy scan, then enforce the minimum.\n const s = mint('_s')\n lines.push(`${ind} const ${s} = ${endV}`)\n lines.push(`${ind} while (${endV} < input.length && ${runCond(p, endV)}) ${endV}++`)\n if (p.min === 1) lines.push(`${ind} if (${endV} === ${s}) break`)\n else if (p.min > 1) lines.push(`${ind} if (${endV} - ${s} < ${p.min}) break`)\n } else if (p.part === 'run' && p.max === 1) {\n // Exactly one char: required (min 1) or optional (min 0).\n if (p.min === 1) {\n lines.push(`${ind} if (!(${endV} < input.length && ${runCond(p, endV)})) break`)\n lines.push(`${ind} ${endV}++`)\n } else {\n lines.push(`${ind} if (${endV} < input.length && ${runCond(p, endV)}) ${endV}++`)\n }\n } else if (p.part === 'run') {\n // Bounded counted run `[x]{min,max}` (\u00A78c), max \u2265 2: consume up to `max`\n // occurrences, then require at least `min` (greedy \u2014 sound because\n // `seqIsUnambiguous` proved any wiggle is disjoint from what follows).\n const s = mint('_s')\n lines.push(`${ind} const ${s} = ${endV}`)\n lines.push(`${ind} while (${endV} - ${s} < ${p.max} && ${endV} < input.length && ${runCond(p, endV)}) ${endV}++`)\n if (p.min > 0) lines.push(`${ind} if (${endV} - ${s} < ${p.min}) break`)\n } else {\n // `group` (\u00A78f): the group's own body is just another `ScanShape` \u2014\n // recurse via `emitShapeMatch`, then splice its setup in as the body\n // of a required-once check, an optional take, or a repeat loop.\n const inner = emitShapeMatch(p.inner, endV, mint, `${ind} `, first ? firstChar : undefined)\n if (!p.unbounded) {\n lines.push(...inner.setup)\n if (p.min === 1) {\n lines.push(`${ind} if (!(${inner.ok})) break`)\n lines.push(`${ind} ${endV} = ${inner.end}`)\n } else {\n lines.push(`${ind} if (${inner.ok}) ${endV} = ${inner.end}`)\n }\n } else {\n const progV = mint('_prog')\n lines.push(`${ind} let ${progV} = false`)\n lines.push(`${ind} while (true) {`)\n lines.push(...inner.setup)\n lines.push(`${ind} if (!(${inner.ok})) break`)\n lines.push(`${ind} ${endV} = ${inner.end}`)\n lines.push(`${ind} ${progV} = true`)\n lines.push(`${ind} }`)\n if (p.min === 1) lines.push(`${ind} if (!${progV}) break`)\n }\n }\n first = false\n }\n lines.push(`${ind} ${okV} = true`)\n lines.push(`${ind}} while (false)`)\n lines.push(`${ind}if (!${okV}) ${endV} = ${start}`)\n return { setup: lines, ok: okV, end: endV }\n }\n\n if (shape.kind === 'litFold') {\n const openLen = shape.open.length\n const endV = mint('_end')\n const checks = shape.open.map((cp, k) => {\n const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`\n return foldEq(cVar, cp)\n })\n return {\n setup: [\n `${ind}let ${endV} = ${start}`,\n `${ind}if (${start} + ${openLen} <= input.length && (${checks.join(' && ')})) ${endV} = ${start} + ${openLen}`,\n ],\n ok: `${endV} > ${start}`,\n end: endV,\n }\n }\n\n if (shape.kind === 'until') {\n const j = mint('_j')\n const openLen = shape.open.length\n const openChk = openLen === 1\n ? `${codeAt(start, firstChar)} === ${shape.open[0]}`\n : litCond(start, shape.open, firstChar)\n return {\n setup: [\n `${ind}let ${j} = ${start}`,\n `${ind}if (${start} + ${openLen} <= input.length && (${openChk})) {`,\n `${ind} ${j} = ${start} + ${openLen}`,\n `${ind} while (${j} < input.length && !(${classCond(`input.charCodeAt(${j})`, shape.stop)})) ${j}++`,\n `${ind}}`,\n ],\n // An open-until-terminator always completes (stop char or EOF), so a matched\n // open literal is a full match; `end === start` iff the open didn't match.\n ok: `${j} > ${start}`,\n end: j,\n }\n }\n\n if (shape.kind === 'string') {\n const okV = mint('_ok')\n const endV = mint('_end')\n const i = mint('_i')\n const c2 = mint('_c')\n const hard = stringHardStop(shape)\n const ltLines = shape.escLineTerm\n ? []\n : (() => {\n const c3 = mint('_c')\n return [\n `${ind} const ${c3} = input.charCodeAt(${i} + 1)`,\n `${ind} if (${LINE_TERMINATORS.map(t => `${c3} === ${t}`).join(' || ')}) break`,\n ]\n })()\n return {\n setup: [\n `${ind}let ${okV} = false`,\n `${ind}let ${endV} = ${start}`,\n `${ind}if (${start} < input.length && (${codeAt(start, firstChar)}) === ${shape.quote}) {`,\n `${ind} let ${i} = ${start} + 1`,\n `${ind} while (${i} < input.length) {`,\n `${ind} const ${c2} = input.charCodeAt(${i})`,\n `${ind} if (${c2} === ${shape.quote}) { ${okV} = true; ${endV} = ${i} + 1; break }`,\n `${ind} if (${c2} === ${BACKSLASH}) {`,\n `${ind} if (${i} + 1 >= input.length) break`,\n ...ltLines,\n `${ind} ${i} += 2`,\n `${ind} continue`,\n `${ind} }`,\n ...(hard.length ? [`${ind} if (${classCond(c2, hard)}) break`] : []),\n `${ind} ${i}++`,\n `${ind} }`,\n `${ind}}`,\n ],\n ok: okV,\n end: endV,\n }\n }\n\n // delimited: <open>\u2026<close>, requires the close literal (unclosed \u21D2 no match).\n const j = mint('_j')\n const endV = mint('_end')\n const openLen = shape.open.length\n const closeLen = shape.close.length\n const openChk = openLen === 1\n ? `${codeAt(start, firstChar)} === ${shape.open[0]}`\n : litCond(start, shape.open, firstChar)\n const closeChk = litCond(j, shape.close)\n return {\n setup: [\n `${ind}let ${j} = ${start}`,\n `${ind}let ${endV} = ${start}`,\n `${ind}if (${start} + ${openLen} <= input.length && (${openChk})) {`,\n `${ind} ${j} = ${start} + ${openLen}`,\n `${ind} while (${j} + ${closeLen - 1} < input.length && !(${closeChk})) ${j}++`,\n `${ind} if (${j} + ${closeLen} <= input.length && (${closeChk})) ${endV} = ${j} + ${closeLen}`,\n `${ind}}`,\n ],\n ok: `${endV} > ${start}`,\n end: endV,\n }\n}\n\n/**\n * One trivia-loop branch for a shape, dispatched on the current char `c`\n * (= charCodeAt(_e)). Advances `_e` and `continue`s ONLY on real progress\n * (`end > _e`) \u2014 an unterminated delimited/string token leaves `end === _e`, so\n * the loop stops there exactly as the interpreter's `oneOrMore(choice(\u2026))` would.\n */\nexport function scanBranch(shape: ScanShape, mint: Mint): string {\n const m = emitShapeMatch(shape, '_e', mint, ' ', 'c')\n return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join('\\n')\n}\n\n/**\n * A labeled branch: match one token and, on progress, log its [start, end,\n * kindIndex] trivia chunk. Same completion semantics as scanBranch.\n */\nexport function scanBranchLabeled(shape: ScanShape, kindIndex: number, label: string, mint: Mint): string {\n const m = emitShapeMatch(shape, '_e', mint, ' ', 'c')\n return [\n ...m.setup,\n ` if (${m.end} > _e) {`,\n ` if (_cap) {`,\n ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,\n ` const _rk = _ctx._rootTriviaCapture === false || _ctx._rootTriviaLog === undefined ? -1 : (_ctx._rootTriviaKindIndex?.[${JSON.stringify(label)}] ?? -1)`,\n ` if (_rk >= 0) _ctx._rootTriviaLog.push(${SELECTED_ROOT_OWNED_RANGE_PLACEHOLDER}, _e, ${m.end}, _rk)`,\n ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << kindIndex}))) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,\n ` }`,\n ` _e = ${m.end}`,\n ` continue`,\n ` }`,\n ].join('\\n')\n}\n", "import type { Combinator } from '../types.ts'\nimport { getCoreRegexDef } from '../combinators/choice.ts'\nimport type { LabeledTriviaSpec } from '../cst/trivia-kinds.ts'\nimport { SELECTED_ROOT_STRIDE } from '../cst/trivia-entries.ts'\nimport { analyzeLabeledTrivia } from '../cst/trivia-kinds.ts'\nimport { type ScanShape, type Mint, scanShapeFromRegex, scanBranch, scanBranchLabeled } from './scannable-run.ts'\n\n/** Fresh, collision-free local names for one generated scan function. */\nfunction makeMint(): Mint {\n let n = 0\n return (prefix = '_v') => `${prefix}${n++}`\n}\n\nfunction unwrapTrivia(p: Combinator<unknown>): Combinator<unknown> {\n let cur = p\n while (cur._def.tag === 'trivia') cur = cur._def.parser\n return cur\n}\n\nfunction oneOrMoreInner(p: Combinator<unknown>): Combinator<unknown> | null {\n const d = p._def\n if (d.tag === 'oneOrMore') return d.parser\n if (d.tag === 'many' && d.min >= 1) return d.parser\n return null\n}\n\nfunction choiceArms(p: Combinator<unknown>): Combinator<unknown>[] | null {\n if (p._def.tag === 'choice') return p._def.parsers\n return null\n}\n\n/** The scannable shape of one arm (a regex), or null if it isn't scannable. */\nfunction armShape(arm: Combinator<unknown>): ScanShape | null {\n const d = getCoreRegexDef(arm)\n return d ? scanShapeFromRegex(d.source, d.flags) : null\n}\n\n/**\n * A trivia parser that lowers to a hand-rolled char-scan loop: `oneOrMore(choice(\n * \u2026))` (or `many`, min\u22651) where EVERY arm is a scannable shape (a `[X]+` run, a\n * `<lit>[^X]*` open-until-terminator, or a `<open>(?:\u2026)*<close>` delimited token\n * \u2014 see scannable-run.ts). Returns the per-arm shapes, or a single-element list\n * for a bare scannable regex, or null (\u2192 caller falls back to the regex/generic\n * trivia path). A single alternation regex is NOT scannable (one exec matches one\n * arm per call, defeating the loop).\n */\nexport function analyzeTriviaFastPath(trivia: Combinator<unknown>): ScanShape[] | null {\n const core = unwrapTrivia(trivia)\n\n const directDef = getCoreRegexDef(core)\n if (directDef) {\n const shape = scanShapeFromRegex(directDef.source, directDef.flags)\n return shape && shape.kind === 'chars' ? [shape] : null\n }\n\n const inner = oneOrMoreInner(core)\n if (!inner) return null\n\n const innerShape = armShape(inner)\n if (innerShape) return innerShape.kind === 'chars' ? [innerShape] : null\n\n const arms = choiceArms(inner)\n if (!arms || arms.length < 2) return null\n\n const shapes: ScanShape[] = []\n for (const arm of arms) {\n const shape = armShape(arm)\n if (!shape) return null\n shapes.push(shape)\n }\n return shapes\n}\n\nconst CAP_RECORD = [\n ` if (_cap && _e > _pos) {`,\n ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,\n ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,\n ` }`,\n].join('\\n')\n\nconst SELECTED_ROOT_FINISH = [\n ` if (_cap && _ctx._rootTriviaCapture !== false && _ctx._rootTriviaLog !== undefined && _ctx._rootTriviaLog.length !== _rootMark) {`,\n ` for (let _ri = _rootMark; _ri < _ctx._rootTriviaLog.length; _ri += ${SELECTED_ROOT_STRIDE}) { _ctx._rootTriviaLog[_ri] = _pos; _ctx._rootTriviaLog[_ri + 1] = _e }`,\n ` }`,\n].join('\\n')\n\n/** One char-scan loop carrying a branch per scannable shape, dispatched on `c`. */\nfunction composeFastLoop(shapes: ScanShape[]): string {\n const mint = makeMint()\n return [\n ` while (_e < input.length) {`,\n ` const c = input.charCodeAt(_e)`,\n ...shapes.map(s => scanBranch(s, mint)),\n ` break`,\n ` }`,\n ].join('\\n')\n}\n\n/** Emit a specialized `_tfN` that skips (unlabeled) trivia via a char-scan loop. */\nexport function buildFastTriviaFnDecl(fnName: string, shapes: ScanShape[]): string {\n return [\n `function ${fnName}(input, _pos, _ctx, _cap) {`,\n ` let _e = _pos`,\n composeFastLoop(shapes),\n CAP_RECORD,\n ` return _e`,\n `}`,\n ].join('\\n')\n}\n\n/**\n * A LABELED trivia parser whose every arm is scannable \u2192 per-arm shape + kind\n * index, or null. Same recognition as analyzeTriviaFastPath, but keyed by the\n * label table so the fast loop can log each chunk's category. It accepts any\n * structurally scannable arm set / arm count; labels do not affect recognition.\n */\nexport function analyzeLabeledScannableRun(\n trivia: Combinator<unknown>,\n): Array<{ shape: ScanShape; kindIndex: number; label: string }> | null {\n const spec = analyzeLabeledTrivia(trivia)\n if (!spec) return null\n const out: Array<{ shape: ScanShape; kindIndex: number; label: string }> = []\n for (const arm of spec.arms) {\n const d = getCoreRegexDef(arm.parser)\n const shape = d ? scanShapeFromRegex(d.source, d.flags) : null\n if (!shape) return null\n out.push({ shape, kindIndex: arm.kindIndex, label: arm.label })\n }\n return out\n}\n\n/** Emit a labeled scannable trivia `_tfN`: char-scan loop with per-chunk kind capture. */\nexport function buildLabeledScannableTriviaFnDecl(\n fnName: string,\n arms: ReadonlyArray<{ shape: ScanShape; kindIndex: number; label: string }>,\n): string {\n const mint = makeMint()\n return [\n `function ${fnName}(input, _pos, _ctx, _cap) {`,\n ` let _e = _pos`,\n ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,\n ` while (_e < input.length) {`,\n ` const c = input.charCodeAt(_e)`,\n ...arms.map(a => scanBranchLabeled(a.shape, a.kindIndex, a.label, mint)),\n ` break`,\n ` }`,\n SELECTED_ROOT_FINISH,\n ` return _e`,\n `}`,\n ].join('\\n')\n}\n\n/** Labeled trivia with regex arms \u2014 per-chunk kind capture in compiled output. */\nexport function buildLabeledRegexTriviaFnDecl(\n fnName: string,\n spec: LabeledTriviaSpec,\n reNames: string[],\n): string {\n const tryArms = spec.arms.map((arm, i) => {\n const re = reNames[i]!\n const k = arm.kindIndex\n return [\n ` ${re}.lastIndex = _e`,\n ` const _m${i} = ${re}.exec(input)`,\n ` if (_m${i} && _m${i}.index === _e) {`,\n ` const _ce = _e + _m${i}[0].length`,\n ` if (_cap) {`,\n ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,\n ` const _rk${i} = _ctx._rootTriviaCapture === false || _ctx._rootTriviaLog === undefined ? -1 : (_ctx._rootTriviaKindIndex?.[${JSON.stringify(arm.label)}] ?? -1)`,\n ` if (_rk${i} >= 0) _ctx._rootTriviaLog.push(0, 0, _e, _ce, _rk${i})`,\n ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,\n ` }`,\n ` _e = _ce`,\n ` _matched = true`,\n ` continue`,\n ` }`,\n ].join('\\n')\n }).join('\\n')\n\n return [\n `function ${fnName}(input, _pos, _ctx, _cap) {`,\n ` let _e = _pos`,\n ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,\n ` while (_e < input.length) {`,\n ` let _matched = false`,\n tryArms,\n ` if (!_matched) break`,\n ` }`,\n SELECTED_ROOT_FINISH,\n ` return _e`,\n `}`,\n ].join('\\n')\n}\n\nexport function labeledTriviaRegexArms(trivia: Combinator<unknown>): LabeledTriviaSpec | null {\n const spec = analyzeLabeledTrivia(trivia)\n if (!spec) return null\n for (const arm of spec.arms) {\n if (!getCoreRegexDef(arm.parser)) return null\n }\n return spec\n}\n\n/** Labeled trivia via per-arm runtime parsers (non-regex or mixed arms). */\nexport function buildLabeledRuntimeTriviaFnDecl(\n fnName: string,\n spec: LabeledTriviaSpec,\n rpStartIndex: number,\n): string {\n const tryArms = spec.arms.map((arm, i) => {\n const k = arm.kindIndex\n const ri = rpStartIndex + i\n return [\n ` const _r${i} = _rp[${ri}].parse(input, _e, _ctx)`,\n ` if (_r${i}.ok && _r${i}.span.end > _e) {`,\n ` const _ce = _r${i}.span.end`,\n ` if (_cap) {`,\n ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,\n ` const _rk${i} = _ctx._rootTriviaCapture === false || _ctx._rootTriviaLog === undefined ? -1 : (_ctx._rootTriviaKindIndex?.[${JSON.stringify(arm.label)}] ?? -1)`,\n ` if (_rk${i} >= 0) _ctx._rootTriviaLog.push(0, 0, _e, _ce, _rk${i})`,\n ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,\n ` }`,\n ` _e = _ce`,\n ` _matched = true`,\n ` continue`,\n ` }`,\n ].join('\\n')\n }).join('\\n')\n\n return [\n `function ${fnName}(input, _pos, _ctx, _cap) {`,\n ` let _e = _pos`,\n ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,\n ` while (_e < input.length) {`,\n ` let _matched = false`,\n tryArms,\n ` if (!_matched) break`,\n ` }`,\n SELECTED_ROOT_FINISH,\n ` return _e`,\n `}`,\n ].join('\\n')\n}\n", "import type { ParserDef } from '../types.ts'\nimport { buildAnalysisSrc } from './build-arity.ts'\nimport { recordDegradation } from './degradation.ts'\n\n/**\n * A formal param, optionally carrying a TypeScript annotation.\n *\n * The macro path TS-strips `buildSrc` before it reaches here, so a bare `\\w+` was\n * *usually* enough \u2014 but \"usually\" is exactly the failure class this module is being\n * audited for: a `mk` reducer spelled with a type annotation that survives (a runtime\n * `compile()` over untranspiled source, a hand-written IR string) silently loses the\n * inline-`mk` fast path and pays a `_build[n](...)` call on every match, with nothing\n * said. Admitting the annotation removes the class rather than relying on an upstream\n * pass to have removed it. No `=` (default) \u2014 a default is not a plain positional param.\n */\n/** Optional `?` and/or `: T` suffix on a formal param. */\nconst ANN = String.raw`\\s*\\??\\s*(?::[^,)=]+)?`\n/** A whole ignorable param (the `fields` slot, whose name the shape never re-reads). */\nconst ANY_P = String.raw`\\w+${ANN}`\n\n/** `(c, f, s, r, tl) => mk('Type', c, r, s, tl)` \u2014 CSS perf regression builder shape. */\nconst MK_BUILD_RE = new RegExp(\n String.raw`^\\(\\s*(\\w+)${ANN}\\s*,\\s*${ANY_P}\\s*,\\s*(\\w+)${ANN}\\s*,\\s*(\\w+)${ANN}\\s*,\\s*(\\w+)${ANN}\\s*\\)\\s*=>\\s*mk\\s*\\(\\s*(['\"])([^'\"]+)\\5\\s*,\\s*\\1\\s*,\\s*\\3\\s*,\\s*\\2\\s*,\\s*\\4\\s*\\)\\s*$`,\n)\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n/**\n * Same shape, but the type is named by an IDENTIFIER in BOTH positions rather than by a\n * string literal.\n *\n * `node(type, body, (c, f, s, r, tl) => mk(type, c, r, s, tl))` inside a factory is the\n * ordinary way to write a family of nodes \u2014 jess's `less` grammar does it at 31 sites \u2014\n * and it missed the inline path purely because the matcher demanded a quoted literal.\n * Requiring the SAME identifier in both positions is what makes this sound rather than a\n * loosening: the arrow's own parameters are `(c, f, s, r, tl)`, so nothing between the\n * two occurrences can rebind the name, and the evaluator has already resolved that\n * binding to `def.type`. Two spellings of one value, not two values.\n */\nfunction identMkBuildRe(typeIdent: string): RegExp {\n return new RegExp(\n String.raw`^\\(\\s*(\\w+)${ANN}\\s*,\\s*${ANY_P}\\s*,\\s*(\\w+)${ANN}\\s*,\\s*(\\w+)${ANN}\\s*,\\s*(\\w+)${ANN}\\s*\\)\\s*=>\\s*(?:[\\w$.]*\\.)?mk\\s*\\(\\s*${escapeRegExp(typeIdent)}\\s*,\\s*\\1\\s*,\\s*\\3\\s*,\\s*\\2\\s*,\\s*\\4\\s*\\)\\s*$`,\n )\n}\n\n/** Any callee \u2014 covers `mk`, `import_x.mk`, `__vite_ssr_import_0__.mk`, etc. */\nfunction looseMkBuildRe(type: string): RegExp {\n return new RegExp(\n String.raw`^\\(\\s*(\\w+)${ANN}\\s*,\\s*${ANY_P}\\s*,\\s*(\\w+)${ANN}\\s*,\\s*(\\w+)${ANN}\\s*,\\s*(\\w+)${ANN}\\s*\\)\\s*=>\\s*.+\\(\\s*(['\"])${escapeRegExp(type)}\\5\\s*,\\s*\\1\\s*,\\s*\\3\\s*,\\s*\\2\\s*,\\s*\\4\\s*\\)\\s*$`,\n )\n}\n\n/**\n * Does this source LOOK like it wants the inline-`mk` path? Used only to decide whether\n * a miss is worth reporting: a reducer that never mentions `mk(` was never a candidate,\n * and warning about it would be per-rule noise on every grammar that does not use the\n * pattern at all \u2014 the flood that turns a diagnostic back into silence.\n */\nconst LOOKS_LIKE_MK_RE = /=>\\s*(?:[\\w$.]*\\.)?mk\\s*\\(/\n\n\n/**\n * When a node() build is a thin `mk(type, \u2026)` wrapper, emit the object literal\n * at the call site instead of `_build[n](\u2026)`.\n */\nexport function analyzeMkInlineBuild(def: Extract<ParserDef, { tag: 'node' }>): string | null {\n if (!def.build) return null // structural node \u2014 no own build to inline\n if (def.type === undefined) return null\n const src = buildAnalysisSrc(def).trim()\n const strict = src.match(MK_BUILD_RE)\n if (strict) {\n const mkType = strict[6]!\n if (mkType === def.type) return def.type\n reportMkMiss(def.type, src, `it builds a \"${mkType}\" node, not \"${def.type}\"`)\n return null\n }\n if (looseMkBuildRe(def.type).test(src)) return def.type\n // Factory-authored: `node(t, \u2026, (\u2026) => mk(t, \u2026))`. See `identMkBuildRe`.\n if (def.typeSrc !== undefined && identMkBuildRe(def.typeSrc).test(src)) return def.type\n if (LOOKS_LIKE_MK_RE.test(src)) {\n reportMkMiss(def.type, src, 'its parameter list or argument order does not match '\n + '`(children, fields, span, rawChildren, triviaLog) => mk(type, children, rawChildren, span, triviaLog)`')\n }\n return null\n}\n\n/** A near-miss on the inline-`mk` shape costs a real call per match \u2014 say so. */\nfunction reportMkMiss(type: string, src: string, why: string): void {\n recordDegradation({\n code: 'mk-inline-missed',\n severity: 'warn',\n where: `node(\"${type}\")`,\n subject: `build reducer \\`${src.split('\\n')[0]!.slice(0, 80)}\\``,\n fellBackTo: `looks like an inline-\\`mk\\` builder but ${why}, so every match pays a `\n + '`_build[n](...)` call instead of an inlined object literal',\n otherwise: 'the node object would be constructed at the call site with no call frame',\n })\n}\n\n/** Object literal matching `stub-build.ts` `mk()` without the function call. */\nexport function emitInlineMkNodeExpr(\n type: string,\n chV: string,\n rawV: string,\n spanExpr: string,\n tlV: string,\n): string {\n return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: ${spanExpr}, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`\n}\n", "/**\n * The linker (RULE_ABI_PLAN \u00A74): fuse independently-compiled linkable artifacts\n * (`compileLinkable`) into ONE closure of parse functions.\n *\n * Every rule is a canonical `_r_<Name>` function and every sibling reference is a\n * call to that name; because fusion drops them all into one scope, those calls\n * are **direct local calls** (0% dispatch). Composition is by name:\n * - **override** \u2014 later artifact wins per rule name; because references resolve\n * by name in the shared scope, overriding a rule reroutes EVERY call to it,\n * including calls inside a base artifact's own rules (open recursion).\n * - **\u00E0 la carte** \u2014 `pick(artifact, names)` keeps only those rules + their\n * transitive dependency closure.\n * Private per-artifact state (`_ns_re`, `_ns_pf`, \u2026) is namespaced so it can't\n * collide; the sentinel protocol / `_EMPTY_TL` are shared and\n * emitted once.\n *\n * Uses `new Function` (like `compile()`), so it needs `'unsafe-eval'` under a\n * strict CSP; a build-time variant that emits fused source instead is a later\n * addition. Fusion runs ONCE at parser construction \u2014 parsing is then full speed.\n */\nimport { compileLinkable, firstSetCond, ruleDependencies, HOST_READS_DECL, RAW_ENTRY_DECL } from './codegen.ts'\nimport { FUSED_HOST_MODE, FUSED_HOST_ELIDED } from '../cst/host-mode.ts'\nimport { evalRuleMapIR, serializeRuleMap } from './ir-serialize.ts'\nimport type { LinkablePieces, FirstSetRecipe, HostMode } from './codegen.ts'\nimport { union } from '../combinators/first-set.ts'\nimport { PARSEMAN_VERSION } from '../version.ts'\nimport type { BuildHost, Combinator, CstCollapsePredicate, FirstSet, ParseContext, ParseResult } from '../types.ts'\nimport { grammarReflectionSource, mergeGrammarReflections } from '../cst/reflection.ts'\nimport type { ModuleHoist } from './module-hoist.ts'\n\n/**\n * Compile a `rules()` map to a **linkable artifact** \u2014 the composable, shippable\n * form (RULE_ABI_PLAN \u00A74). A package exports `linkable(rules(g => \u2026))`; consumers\n * import that artifact and `fuse([...])` it \u2014 **no source of the base grammar is\n * ever read**. Under the macro this is precompiled to static pieces; in the\n * interpreter it compiles here at load (like `compile()`).\n *\n * `ns` is a per-artifact namespace; omit it to auto-assign a process-unique one\n * (fine at runtime \u2014 the macro supplies a stable module-derived ns instead).\n */\nlet _nsCounter = 0\nexport function linkable(\n rulesMap: Record<string, Combinator<unknown>>,\n ns?: string,\n trivia?: Combinator<unknown>,\n // Compile-time host mode, same meaning as `compile(g, { hostMode })`: 'ast' (default)\n // emits the grammar's own builders and NO positioned-CST branch; 'cst' builds every\n // node through the host. A linked/fused artifact is version- and mode-locked, so a\n // language service links its own 'cst' artifact rather than switching at parse time.\n hostMode?: HostMode,\n): LinkablePieces {\n const pieces = compileLinkable(\n Object.entries(rulesMap),\n ns ?? `_lk${_nsCounter++}_`,\n { ...(trivia ? { trivia } : {}), ...(hostMode ? { hostMode } : {}) },\n )\n if (!pieces) throw new Error('linkable(): this grammar cannot be compiled to a linkable artifact (contains a runtime-only parser fallback)')\n return pieces\n}\n\nexport type CstBuildHostOptions = {\n /**\n * Collapse transparent one-child CST wrapper nodes at build time.\n * - `true`: collapse any one-child node whose rawChildren also has exactly one\n * entry, so trivia/error boundaries are not silently dropped.\n * - `string[]`: collapse only these grammar node types.\n * - predicate: final policy hook for language-specific public CSTs.\n */\n collapse?: boolean | readonly string[] | CstCollapsePredicate\n /**\n * Materialize `node(..., { tags })` grammar metadata onto produced CST nodes.\n * When omitted, tags stay in grammar reflection for zero per-node tree cost.\n */\n tags?: boolean\n}\n\nfunction normalizeCstCollapse(collapse: CstBuildHostOptions['collapse']): CstCollapsePredicate | undefined {\n if (collapse === true) return () => true\n if (Array.isArray(collapse)) {\n const types = new Set(collapse)\n return type => types.has(type)\n }\n return typeof collapse === 'function' ? collapse : undefined\n}\n\nfunction buildCstNode(\n type: string,\n children: ReadonlyArray<unknown>,\n _fields: unknown,\n span: { start: number; end: number },\n _rawChildren?: ReadonlyArray<unknown>,\n _triviaLog?: readonly number[],\n state?: unknown,\n tags?: readonly string[] | undefined,\n): unknown {\n // Carry the grammar's `ctx.state` snapshot onto the node (null when unset) \u2014 the\n // CST contract includes `state` and incremental re-parse replays it on edit.\n return tags !== undefined && tags.length > 0\n ? { _tag: 'node', type, tags, span: { ...span }, state: state ?? null, children: [...children] }\n : { _tag: 'node', type, span: { ...span }, state: state ?? null, children: [...children] }\n}\n\n/**\n * A generic positioned-CST build host (RULE_ABI_PLAN \u00A77). Pass as `ctx.build`\n * (or `parseDoc(..., { build: cstBuildHost })`) to make ANY linkable/fused\n * grammar produce a uniform CST \u2014 `{ _tag:'node', type, span, state, children }`\n * \u2014 instead of its own eval-AST builders. This is the host the linter and IDE\n * drivers use; the eval driver leaves `ctx.build` unset (grammar's own builders).\n *\n * For public syntax trees, call `cstBuildHost({ collapse })`: Parseman will skip\n * allocating wrapper CST nodes whose single child should stand in for the rule.\n */\nexport function cstBuildHost(options?: CstBuildHostOptions): BuildHost\nexport function cstBuildHost(\n type: string,\n children: ReadonlyArray<unknown>,\n fields: unknown,\n span: { start: number; end: number },\n rawChildren?: ReadonlyArray<unknown>,\n triviaLog?: readonly number[],\n state?: unknown,\n tags?: readonly string[] | undefined,\n): unknown\nexport function cstBuildHost(\n typeOrOptions?: string | CstBuildHostOptions,\n children?: ReadonlyArray<unknown>,\n _fields?: unknown,\n span?: { start: number; end: number },\n rawChildren?: ReadonlyArray<unknown>,\n triviaLog?: readonly number[],\n state?: unknown,\n _tags?: readonly string[] | undefined,\n): unknown {\n if (typeof typeOrOptions === 'string') {\n return buildCstNode(typeOrOptions, children ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state)\n }\n const collapse = normalizeCstCollapse(typeOrOptions?.collapse)\n const materializeTags = typeOrOptions?.tags === true\n const host: BuildHost = (\n type: string,\n children: ReadonlyArray<unknown> | undefined,\n fields: unknown,\n span: { start: number; end: number },\n rawChildren: ReadonlyArray<unknown>,\n triviaLog: readonly number[],\n state: unknown,\n tags?: readonly string[] | undefined,\n // A CST/collapse host always keeps `children` (chV) \u2014 the opt-out never\n // applies \u2014 so `?? []` is unreachable defensive modeling for the widened type.\n ) => buildCstNode(type, children ?? [], fields, span, rawChildren, triviaLog, state, materializeTags ? tags : undefined)\n ;(host as typeof host & { _parsemanCstOutput?: true })._parsemanCstOutput = true\n if (collapse) host._parsemanCstCollapse = collapse\n return host\n}\n\n// `cstBuildHost` itself is also accepted as a BuildHost (without options).\n;(cstBuildHost as unknown as { _parsemanCstOutput?: true })._parsemanCstOutput = true\n\n/**\n * A fused function receives the full ParseContext through `run()`. Direct\n * callers historically supplied a plain context object, so keep that usage\n * valid while making the function assignable to the public `Runnable` type.\n * Generated code treats optional framework fields as absent when they are not\n * provided, matching the interpreter's normal defaults.\n */\nexport type FusedRule = (\n input: string,\n pos: number,\n ctx: ParseContext | Record<string, unknown>,\n) => ParseResult<unknown> & { readonly value?: unknown }\n\n/**\n * Restrict a grammar/artifact to `names` plus their transitive rule-dependency\n * closure (\u00E0 la carte selection) \u2014 e.g. Jess taking parts of Less and parts of\n * Sass: `compose([pick(less, ['MixinCall']), pick(sass, ['EachFor']), css])`. A\n * picked rule pulls in every rule name it references, so the result is always\n * self-consistent within the artifact. Accepts a grammar (`rules()` result) or a\n * compiled artifact; returns an artifact for `compose()`.\n */\nexport function pick(\n grammar: LinkablePieces | Record<string, Combinator<unknown>>,\n names: string[],\n): LinkablePieces {\n // A COMPOSED grammar (`compose([...])` result): it has no single rule map \u2014 its rules\n // live across several carried pieces. Materialize them under the composing trivia it\n // was built with (so the selection keeps composing-wins trivia), then filter across\n // pieces to `names` + their transitive dep closure, keeping each rule in the piece\n // that WINS it (compose override order = last wins). Return a composed-like value so a\n // downstream `compose([pick(composed, \u2026)])` flattens it the same way.\n const carried = (grammar as Record<symbol, unknown>)[COMPOSED_PIECES]\n if (Array.isArray(carried)) {\n const trivia = (grammar as Record<symbol, unknown>)[COMPOSED_TRIVIA] as Combinator<unknown> | undefined\n const pieces = (carried as Array<LinkablePieces | IRPiece>).map(pc => materializePiece(pc, trivia))\n const filtered = pickPieces(pieces, names)\n // A composed-like value (carries COMPOSED_PIECES, no single rule map). It is only\n // ever consumed through compose()'s COMPOSED_PIECES branch \u2014 which is checked before\n // any LinkablePieces field \u2014 so this masquerades as LinkablePieces for the signature.\n const out: Record<string, unknown> = {}\n Object.defineProperty(out, COMPOSED_PIECES, { value: filtered, enumerable: false })\n if (trivia) Object.defineProperty(out, COMPOSED_TRIVIA, { value: trivia, enumerable: false })\n return out as unknown as LinkablePieces\n }\n\n const p = (grammar as LinkablePieces).ruleFns instanceof Map\n ? (grammar as LinkablePieces)\n : linkable(grammar as Record<string, Combinator<unknown>>)\n return pickPieces([p], names)[0] ?? { ...p, keys: [], ruleFns: new Map(), wrappers: new Map(), deps: new Map() }\n}\n\n/** Restrict a set of linkable pieces to `names` + their transitive dep closure,\n * keeping each surviving rule in the piece that WINS it (later piece wins, matching\n * compose override order). Shared by `pick()` (runtime) and the macro's build-time\n * `pick(...)` handling, so \u00E0-la-carte selection is identical on both paths. */\nexport function pickPieces(pieces: LinkablePieces[], names: string[]): LinkablePieces[] {\n const winner = new Map<string, LinkablePieces>()\n for (const pc of pieces) for (const k of pc.keys) winner.set(k, pc)\n // A requested name that isn't in the grammar is a typo \u2014 fail here, not later with a\n // confusing name-closure error at compose() time.\n for (const n of names) {\n if (!winner.has(n)) throw new Error(`pick: rule \"${n}\" is not in this grammar (available: ${[...winner.keys()].join(', ')})`)\n }\n const keep = new Set<string>()\n const visit = (n: string): void => {\n // A missing winner is an EXTERNAL dep (a base-grammar rule) \u2014 it resolves at\n // compose() time, not here. Top-level `names` were already validated above.\n const w = winner.get(n)\n if (keep.has(n) || !w) return\n keep.add(n)\n for (const d of w.deps.get(n) ?? []) visit(d)\n }\n for (const n of names) visit(n)\n const filt = <V>(m: Map<string, V>, pc: LinkablePieces): Map<string, V> =>\n new Map([...m].filter(([k]) => keep.has(k) && winner.get(k) === pc))\n return pieces\n .map(pc => ({ ...pc, keys: pc.keys.filter(k => keep.has(k) && winner.get(k) === pc), ruleFns: filt(pc.ruleFns, pc), wrappers: filt(pc.wrappers, pc), deps: filt(pc.deps, pc), nodeMeta: filt(pc.nodeMeta, pc) }))\n .filter(pc => pc.keys.length > 0)\n}\n\n/**\n * Build the fused-closure body (shared by the runtime `fuseRules` and the\n * build-time `emitFusedSource`). Returns the closure body (which reads `_env`\n * for any non-inlined callbacks) and the `_env` to bind. In macro mode callbacks\n * are inlined from source, so `_env` is empty and the body is fully static.\n */\nexport function fusedBody(pieces: LinkablePieces[], hoist?: ModuleHoist): { body: string; env: Record<string, unknown> } {\n // ARTIFACT VERSION LOCK (see src/version.ts): a compiled artifact is fused by the\n // SAME parseman version that produced it \u2014 the artifact format carries no\n // cross-version back-compat. Reject BOTH a version MISMATCH and an UNSTAMPED piece\n // (a pre-0.32 artifact predates the invariant \u2192 its recipe/pieces shape is\n // unsupported): fail LOUDLY rather than silently mis-reading a stale shape. Every\n // artifact `compileLinkable` produces is stamped, so an absent stamp means a stale\n // serialized artifact, not a current one.\n for (const p of pieces) {\n if (p.v === undefined) {\n throw new Error(\n `parseman: artifact \"${p.ns}\" is UNSTAMPED (compiled before the version-lock invariant). ` +\n `Recompile the grammar with parseman ${PARSEMAN_VERSION}; parseman does not fuse unversioned or cross-version artifacts.`,\n )\n }\n if (p.v !== PARSEMAN_VERSION) {\n throw new Error(\n `parseman: artifact \"${p.ns}\" was compiled with parseman ${p.v}, but is being fused with parseman ${PARSEMAN_VERSION}. ` +\n `Compiled grammar artifacts are version-locked \u2014 recompile the grammar with parseman ${PARSEMAN_VERSION}; parseman does not fuse across versions.`,\n )\n }\n }\n // Winner per rule name \u2014 later pieces override earlier ones.\n const winner = new Map<string, LinkablePieces>()\n for (const p of pieces) for (const k of p.keys) winner.set(k, p)\n\n // Name-closure check: every referenced rule must resolve in the fused set.\n for (const [k, p] of winner) {\n for (const d of p.deps.get(k) ?? []) {\n if (!winner.has(d)) throw new Error(`compose: rule \"${k}\" references missing rule \"${d}\"`)\n }\n }\n\n const contributing = new Set(winner.values())\n const contributingPieces = [...contributing]\n const needsEmptyTl = contributingPieces.some(p => p.needsEmptyTl)\n const needsHostReads = contributingPieces.some(p => p.needsHostReads)\n const needsRawEntry = contributingPieces.some(p => p.needsRawEntry)\n\n const lines: string[] = [\n // Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).\n 'const _pfFail = {}',\n 'let _pfEnd',\n ...(needsEmptyTl ? ['const _EMPTY_TL = Object.freeze([])'] : []),\n ...(needsHostReads ? [HOST_READS_DECL] : []),\n ...(needsRawEntry ? [RAW_ENTRY_DECL] : []),\n // Each contributing artifact's namespaced private prelude (regexes, _pf, \u2026).\n ...new Set(contributingPieces.flatMap(p => p.prelude)),\n // The winning `_r_<Name>` function for each rule (one per name \u2192 no redeclare).\n ...[...winner].map(([k, p]) => p.ruleFns.get(k)!),\n ]\n const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)!}`)\n const reflection = mergeGrammarReflections([...winner].map(([k, p]) => p.nodeMeta.get(k) ?? { nodes: [] }))\n // The host-mode stamp is emitted INTO the body rather than applied afterwards by the\n // caller, so the runtime fuse (`fuseRules`) and the build-time fuse\n // (`emitFusedSource`) cannot disagree about it. They have drifted on exactly this\n // kind of change before, and an unstamped macro artifact is invisible to\n // `assertHostModeCompatible` \u2014 it reads `elided: false` and passes, which is the\n // silent wrong output the whole mechanism exists to prevent.\n const { mode, elided } = hostModeOfPieces(pieces)\n const tailSrc = [\n 'const _map = {',\n wrapperEntries.join(',\\n'),\n '}',\n `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_MODE.description!)}), { value: ${JSON.stringify(mode)}, enumerable: false })`,\n `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_ELIDED.description!)}), { value: ${JSON.stringify(elided)}, enumerable: false })`,\n `Object.defineProperty(_map, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`,\n // \u2026and on each rule FUNCTION, because `run(map.Rule, \u2026)` is handed the rule, not the\n // map, and would otherwise have nothing to check against. Done once at fuse time,\n // before any rule has been called, so it costs nothing per parse.\n 'for (const _k of Object.keys(_map)) {',\n ` Object.defineProperty(_map[_k], Symbol.for(${JSON.stringify(FUSED_HOST_MODE.description!)}), { value: ${JSON.stringify(mode)}, enumerable: false })`,\n ` Object.defineProperty(_map[_k], Symbol.for(${JSON.stringify(FUSED_HOST_ELIDED.description!)}), { value: ${JSON.stringify(elided)}, enumerable: false })`,\n '}',\n 'return _map',\n ].join('\\n')\n const rawBody = [...lines, tailSrc].join('\\n')\n\n // Fuse-time first-set dispatch: a rule-ref choice arm was emitted (in linkable\n // mode) as `/*@FS:rule:codevar@*\u200B/true`. Resolve it now against the WINNING rule's\n // LEADING first-set recipe \u2014 sound under override, since we use the final rule, not\n // the one visible when the referencing rule was compiled. Each rule's recipe is a\n // union of ordered leading-term chains; resolving them over the WINNING rules to a\n // fixpoint makes a `sequence(ref, \u2026)`-led arm/node dispatch on the ref's real first\n // char (parity with a monolithic compile) instead of degrading to always-try\n // because the ref baked `any`. A genuinely unknown ref \u2192 `any` (always try).\n const ANY: FirstSet = { kind: 'any' }\n const EMPTY: FirstSet = { kind: 'empty' }\n const recipes = new Map<string, FirstSetRecipe>()\n const shallow = new Map<string, FirstSet>()\n const nullable = new Map<string, boolean>()\n for (const [k, p] of winner) {\n const r = p.firstSetRecipes?.get(k); if (r) recipes.set(k, r)\n const fs = p.firstSets?.get(k); if (fs) shallow.set(k, fs)\n const nu = p.nullable?.get(k); if (nu !== undefined) nullable.set(k, nu)\n }\n const knownRef = (ref: string): boolean => recipes.has(ref) || shallow.has(ref)\n // A referenced rule's resolved first-set (or `any` for a genuinely unknown ref \u2014\n // always try). A ref whose nullability is unknown defaults to nullable (keep\n // unioning the chain tail \u2014 never drops a valid first char).\n const refFS = (ref: string): FirstSet => (knownRef(ref) ? (finalFS.get(ref) ?? ANY) : ANY)\n const refNullable = (ref: string): boolean => (knownRef(ref) ? (nullable.get(ref) ?? true) : true)\n // Resolve a recipe: union each ordered chain, and within a chain union each\n // segment's first-set left-to-right STOPPING after the first non-nullable segment\n // (its tail can't start the rule). A ref segment is skippable if it was forced so\n // at build (a wrapping optional/many \u2192 `seg.nullable`) OR the resolved rule is\n // itself nullable; only a definitely-consuming ref stops the chain.\n const resolveRecipe = (r: FirstSetRecipe): FirstSet => {\n let out: FirstSet = EMPTY\n for (const chain of r.alts) {\n for (const seg of chain) {\n out = union(out, seg.ref !== undefined ? refFS(seg.ref) : seg.set)\n const skippable = seg.ref !== undefined ? (seg.nullable || refNullable(seg.ref)) : seg.nullable\n if (!skippable) break\n }\n }\n return out\n }\n // Least-fixpoint: recipe-bearing rules start EMPTY and grow monotonically via\n // `resolveRecipe` (union of refs is monotone; the chain stop is static), so a\n // recursive rule converges to its tightest sound first-set. Rules with no recipe\n // keep their static shallow set.\n const finalFS = new Map<string, FirstSet>()\n for (const [k] of winner) finalFS.set(k, recipes.has(k) ? EMPTY : (shallow.get(k) ?? ANY))\n for (let iter = 0; iter <= recipes.size; iter++) {\n let changed = false\n for (const [k, r] of recipes) {\n const fs = resolveRecipe(r)\n if (JSON.stringify(finalFS.get(k)) !== JSON.stringify(fs)) { finalFS.set(k, fs); changed = true }\n }\n if (!changed) break\n }\n const resolveFS = (src: string): string => src.replace(\n // Rule name + code-point var are both JS identifiers (rule names are validated\n // at compile time \u2014 see assertRuleName in codegen), so an identifier class\n // matches every well-formed placeholder.\n /\\/\\*@FS:([A-Za-z0-9_$]+):([A-Za-z0-9_$]+)@\\*\\/true/g,\n (_m, name: string, codevar: string) => {\n const fs = finalFS.get(name)\n if (!fs || fs.kind === 'any' || fs.kind === 'empty') return 'true'\n return `(${firstSetCond(codevar, fs)})`\n },\n )\n // Placeholders never span a line, so resolving the declarations one at a time is\n // identical to resolving the joined body \u2014 and it has to happen BEFORE the hoist\n // claims them, or two variants whose dispatch conditions resolve the same way\n // would still be compared as different text.\n const body = hoist === undefined\n ? resolveFS(rawBody)\n : [...hoist.claim(lines.map(resolveFS)), resolveFS(tailSrc)].join('\\n')\n\n // Non-inlined callbacks (runtime compile() mode), keyed `<ns>mf` / `<ns>build`.\n const env: Record<string, unknown> = {}\n for (const p of contributingPieces) {\n if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns\n if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns\n }\n return { body, env }\n}\n\n/**\n * The host mode a fusion of these pieces lowers to, and whether any of them dropped a\n * direct builder's positioned-CST branch. Shared by BOTH fuses via `fusedBody`, so the\n * runtime and build-time engines cannot label the same artifact differently.\n *\n * A MIXED fusion is the one way the per-parse host check could be defeated. Carried IR\n * re-lowers under this compose's mode, but a piece that arrived ALREADY COMPILED\n * (`linkable()` / `pick()` / a macro artifact) keeps the mode it was built with \u2014 so a\n * 'cst' fusion can contain an 'ast' piece whose direct builders dropped their host\n * branch. The map would be labeled 'cst', `assertHostModeCompatible` would pass, and\n * that piece would hand AST-shaped objects into a positioned CST: precisely the silent\n * wrong output this whole mechanism exists to prevent. Reject it here, where both facts\n * are still in hand.\n */\nfunction hostModeOfPieces(pieces: LinkablePieces[]): { mode: HostMode; elided: boolean } {\n const mode = pieces.find(p => p.hostMode !== undefined && p.hostMode !== 'ast')?.hostMode ?? 'ast'\n const elided = pieces.filter(p => p.hostBranchElided === true)\n if (mode === 'cst' && elided.length > 0) {\n throw new Error(\n `compose: cannot build a positioned-CST artifact from pieces that were `\n + `compiled for host mode \"ast\" \u2014 ${elided.map(p => `\"${p.ns}\"`).join(', ')} `\n + `${elided.length === 1 ? 'dropped its' : 'dropped their'} direct builders' CST branch, `\n + `so ${elided.length === 1 ? 'it' : 'they'} would return AST objects inside the CST. `\n + `An already-compiled piece keeps the mode it was built with; only pieces carried as `\n + `IR are re-lowered by this compose. Re-compile ${elided.length === 1 ? 'it' : 'them'} `\n + `with hostMode: 'cst', or pass the source grammar so it can be re-lowered.`,\n )\n }\n return { mode, elided: elided.length > 0 }\n}\n\n/** Fuse at RUNTIME (via `new Function`) \u2014 used by `compose()` when not compiled\n * by the macro (like `compile()`). The macro path uses `emitFusedSource` instead.\n * Both stamp the host mode from inside `fusedBody`; see `hostModeOfPieces`. */\nexport function fuseRules(pieces: LinkablePieces[]): Record<string, FusedRule> {\n const { body, env } = fusedBody(pieces)\n // eslint-disable-next-line no-new-func\n return new Function('_env', body)(env) as Record<string, FusedRule>\n}\n\nexport { FUSED_HOST_MODE, FUSED_HOST_ELIDED } from '../cst/host-mode.ts'\n\n/** The host mode a fused/composed rule map was built for. Defaults to 'ast'. */\nexport function fusedHostModeOf(registry: object): HostMode {\n const m = (registry as Record<symbol, unknown>)[FUSED_HOST_MODE]\n return m === 'cst' ? 'cst' : 'ast'\n}\n\n/** Whether a fused/composed rule map dropped any direct builder's CST branch. */\nexport function fusedHostElidedOf(registry: object): boolean {\n return (registry as Record<symbol, unknown>)[FUSED_HOST_ELIDED] === true\n}\n\n/**\n * Fuse at BUILD time \u2014 emit the fused closure as a self-contained SOURCE\n * expression (`(() => { \u2026 })()`), with **no `new Function`**. This is what the\n * macro splices in for a `compose([...])` call, so macro output stays eval-free.\n * Requires every callback to be inlined from source (macro mode); throws if any\n * artifact carries runtime-only callback functions.\n */\nexport function emitFusedSource(pieces: LinkablePieces[], hoist?: ModuleHoist): string {\n const { body, env } = fusedBody(pieces, hoist)\n if (Object.keys(env).length > 0) {\n throw new Error('emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source')\n }\n return `/* @__PURE__ */ (() => {\\n${body}\\n})()`\n}\n\n/**\n * Compose grammars/artifacts into a runnable parser map \u2014 the ONLY public\n * composition entry point. `compose([base, ext, \u2026])`: later entries override\n * earlier ones by rule name, and because fusion re-binds every reference in one\n * shared scope, an override reroutes the base's OWN calls too (open recursion).\n *\n * Each entry may be a **grammar** (a `rules()` result \u2014 a map of combinators,\n * linkable-ified here) OR an already-compiled **linkable artifact** (what the\n * macro emits and a package ships). So a package needs no opt-in wrapper to be\n * composable \u2014 `compose([importedGrammar, myRules])` just works.\n *\n * The macro compiles `compose([...])` to STATIC fused source (no `new Function`).\n * Called at runtime (no macro, like `compile()`) it fuses via `new Function`.\n */\n/** A composed parser carries its flattened source pieces (non-enumerable) so it\n * can be composed AGAIN \u2014 `compose([lessGrammar, delta])` where `lessGrammar` is\n * itself a `compose([...])` result. */\nconst COMPOSED_PIECES = Symbol.for('parseman.composedPieces')\n\n/** The carried pieces a `compose()`/`composeLeaf()` result holds, or `undefined` when\n * the value is not a composed grammar. This is what makes a fused grammar analysable:\n * the pieces are re-lowerable IR even though the fused map itself is only functions. */\nexport function composedPiecesOf(\n grammar: Record<string, unknown>,\n): ReadonlyArray<LinkablePieces | IRPiece> | undefined {\n const pieces = (grammar as unknown as Record<symbol, unknown>)[COMPOSED_PIECES]\n return Array.isArray(pieces) ? pieces as ReadonlyArray<LinkablePieces | IRPiece> : undefined\n}\n\n/**\n * A terminal fused grammar may be used to run a parser, but not as an input to\n * another composition. Macro `composeLeaf()` uses this for a local semantic\n * reduction over imported recognition-only IR: the local reductions stay in\n * their lexical module and therefore never become carried IR.\n */\nconst LEAF_COMPOSED = Symbol.for('parseman.leafComposed')\n\n/** The composing (outermost) trivia a runtime `compose()` applied \u2014 stored so a\n * later `pick(composedGrammar, \u2026)` can re-lower the selected rules under the SAME\n * trivia (composing-wins survives \u00E0-la-carte selection). The carried IR pieces hold\n * no trivia of their own, so it must be remembered separately. Not serialized by the\n * macro (which delegates pick to the runtime linker). */\nconst COMPOSED_TRIVIA = Symbol.for('parseman.composedTrivia')\n\n/** Final winner map for semantic-coverage tooling. It exists only when every\n * carried compose piece is re-lowerable IR; opaque precompiled artifacts have no\n * combinator graph to inspect and therefore deliberately expose no fake map. */\nconst COMPOSED_COVERAGE_RULES = Symbol.for('parseman.composedCoverageRules')\n\n/** The compact IR form a grammar carries instead of its lowered rule source: the\n * combinator-construction expression, re-lowered here at fuse time. */\nexport type IRPiece = { ns: string; ir: string; trackLines?: true }\n\nfunction isIRPiece(p: unknown): p is IRPiece {\n return !!p && typeof p === 'object'\n && typeof (p as IRPiece).ir === 'string' && typeof (p as IRPiece).ns === 'string'\n && !('ruleFns' in (p as object))\n}\n\n/** Memoize a zero-arg thunk, keeping it LAZY. Used where two diagnostic thunks want\n * the same carried-IR hydration: the work must not happen when the diagnostic is off,\n * and must not happen twice when it is on. */\nexport function once<T>(fn: () => T): () => T {\n let done = false\n let value: T\n return () => {\n if (!done) { value = fn(); done = true }\n return value\n }\n}\n\n/** The re-lowerable carried pieces' rule maps, in compose order \u2014 the input to the\n * gating analysis (`diagnoseGrammar`). An opaque precompiled artifact contributes no\n * combinator graph, so it is skipped: a hole it would have bound stays unresolved\n * and its choice stays deferred, never falsely warned.\n *\n * Skipping is not the same as having nothing to say. Use `carriedRuleMapsDetailed`\n * where the skip must be REPORTED \u2014 a diagnostic that drops part of the grammar and\n * then returns a clean result is indistinguishable from one that verified it. */\nexport function carriedRuleMaps(carried: ReadonlyArray<LinkablePieces | IRPiece>): Array<Array<[string, Combinator<unknown>]>> {\n return carriedRuleMapsDetailed(carried).maps\n}\n\n/** `carriedRuleMaps` plus the pieces it could NOT re-lower, named by namespace and\n * rule count, so a caller can report exactly how much of the grammar went unseen. */\nexport function carriedRuleMapsDetailed(\n carried: ReadonlyArray<LinkablePieces | IRPiece>,\n): { maps: Array<Array<[string, Combinator<unknown>]>>; opaque: Array<{ ns: string; ruleNames: string[] }> } {\n const maps: Array<Array<[string, Combinator<unknown>]>> = []\n const opaque: Array<{ ns: string; ruleNames: string[] }> = []\n for (const p of carried) {\n if (isIRPiece(p)) { maps.push(evalRuleMapIR(p.ir)); continue }\n // `ruleFns` is a Map, not a plain object \u2014 `Object.keys` on it silently yields []\n // and every opaque piece would degrade to an anonymous `<artifact _lkN_>`, which is\n // exactly the \"reported, but uselessly\" failure this whole change is against.\n const ruleFns = (p as { ruleFns?: Map<string, string> }).ruleFns\n opaque.push({ ns: p.ns, ruleNames: ruleFns instanceof Map ? [...ruleFns.keys()] : [] })\n }\n return { maps, opaque }\n}\n\n/**\n * Recover the override-winner COMBINATOR map behind a `compose()` result, plus the\n * pieces that could not be recovered.\n *\n * A fused map holds rule functions, so any consumer that walks a combinator graph\n * (gating analysis, the spec/EBNF/railroad model) cannot read it directly. The graph\n * is not lost, though \u2014 `compose()` retains re-lowerable IR \u2014 so this is the single\n * shared recovery both consumers use. Sharing it is the point: two copies of this\n * logic is how one walker gets fixed and the other silently keeps failing.\n *\n * Returns `undefined` when `grammar` is not a composed result.\n */\nexport function recoverComposedRules(\n grammar: Record<string, unknown>,\n): { rules: Map<string, Combinator<unknown>>; opaque: Array<{ ns: string; ruleNames: string[] }> } | undefined {\n const carried = composedPiecesOf(grammar)\n if (carried === undefined) return undefined\n const { maps, opaque } = carriedRuleMapsDetailed(carried)\n const rules = new Map<string, Combinator<unknown>>()\n // Later wins, matching the linker's own fuse semantics. An accessed-but-undefined\n // `g.X` leaks in as an unresolved lazy \u2014 a REFERENCE, not a definition \u2014 and must\n // never shadow the artifact that really defines X.\n for (const map of maps) for (const [name, rule] of map) {\n if (rule._def.tag === 'lazy') { try { rule._def.thunk() } catch { continue } }\n rules.set(name, rule)\n }\n return { rules, opaque }\n}\n\nfunction coverageRulesOf(carried: Array<LinkablePieces | IRPiece>): Record<string, Combinator<unknown>> | undefined {\n const winners: Record<string, Combinator<unknown>> = {}\n for (const piece of carried) {\n if (!isIRPiece(piece)) return undefined\n const map = evalRuleMapIR(piece.ir)\n for (const [name, rule] of map) {\n // An accessed-but-undefined `g.Name` is an external reference, never a\n // rule definition. Match compose's IR filtering rule exactly.\n if (rule._def.tag === 'lazy') {\n try { rule._def.thunk() } catch { continue }\n }\n winners[name] = rule\n }\n }\n return winners\n}\n\n/** Return the final override-winner combinator map carried by runtime\n * `compose()`, or `undefined` when a precompiled opaque artifact participated.\n * This is intentionally internal: callers must not treat it as a parser API. */\nexport function composedCoverageRules(grammar: Record<string, unknown>): Record<string, Combinator<unknown>> | undefined {\n return (grammar as Record<symbol, unknown>)[COMPOSED_COVERAGE_RULES] as Record<string, Combinator<unknown>> | undefined\n}\n\n/** Materialize a carried item to full `LinkablePieces`: an IR piece is evaluated\n * back to a rule map and re-lowered; a full piece passes through. */\nexport function materializePiece(\n p: LinkablePieces | IRPiece,\n trivia?: Combinator<unknown>,\n captureTerminals = false,\n hostMode?: HostMode,\n): LinkablePieces {\n // A piece carried as IR is RE-LOWERED here, which is what lets `compose()` choose the\n // host mode: the same carried grammar can be fused into an eval-AST artifact and into\n // a positioned-CST one. An already-lowered piece keeps whatever mode it was built\n // with \u2014 that is why `compose({ hostMode: 'cst' })` only works on carried IR.\n if (!isIRPiece(p)) return p\n const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {\n ...(trivia ? { trivia } : {}),\n ...(captureTerminals ? { captureTerminals: true } : {}),\n ...(hostMode ? { hostMode } : {}),\n ...(p.trackLines ? { trackLines: true } : {}),\n })\n if (!pieces) throw new Error(`compose: carried IR for ns \"${p.ns}\" could not be re-lowered`)\n return pieces\n}\n\n/** Flatten one `compose()` item to its pieces: a prior composed result \u2192 its\n * carried list; an artifact \u2192 itself; a grammar (`rules()` map) \u2192 linkable-ified. */\nfunction nextComposeNs(used: Set<string>): string {\n let ns: string\n do { ns = `_lk${_nsCounter++}_` } while (used.has(ns))\n used.add(ns)\n return ns\n}\n\n/** Flatten one `compose()` item to its RE-LOWERABLE carried items \u2014 the form stored\n * on the composed result so it can be composed AGAIN under a NEW composing trivia.\n * A grammar (`rules()` map) is carried as compact IR (`{ns, ir}`), NOT baked source,\n * so a later `compose([thisResult, delta])` re-lowers it with the delta's trivia\n * (multi-level composing-wins). A prior composed result contributes its OWN carried\n * items (already IR); a pre-compiled artifact has no source, so it stays baked. */\nfunction itemCarried(\n item: LinkablePieces | Record<string, unknown>,\n used: Set<string>,\n trivia?: Combinator<unknown>,\n // Only reaches the non-serializable fallback below, where the grammar is baked\n // immediately instead of carried as re-lowerable IR. The IR path gets the mode later,\n // in `materializePiece`.\n hostMode?: HostMode,\n): Array<LinkablePieces | IRPiece> {\n const carried = (item as Record<symbol, unknown>)[COMPOSED_PIECES]\n // A prior composed result (runtime or macro-compiled): its carried list is already\n // re-lowerable (IR pieces, plus any pre-compiled artifacts). Pass it through so THIS\n // compose re-lowers it under its own composing trivia. Reserve its namespaces so a\n // sibling grammar map can't collide with them.\n if (Array.isArray(carried)) {\n const items = carried as Array<LinkablePieces | IRPiece>\n for (const p of items) used.add(p.ns)\n return items\n }\n // A pre-compiled artifact (`linkable()`/`pick()`): no source to re-lower \u2014 its trivia\n // was baked when it was compiled. Keep it as-is.\n if ((item as LinkablePieces).ruleFns instanceof Map) {\n const p = item as LinkablePieces\n used.add(p.ns)\n return [p]\n }\n // A grammar (`rules()` map): carry it as compact IR so a later compose re-lowers it\n // under ITS trivia. Unserializable \u2192 bake now with this compose's trivia (can't\n // re-lower later; acceptable fallback, mirrors the macro's full-pieces fallback).\n const map = item as Record<string, Combinator<unknown>>\n const ns = nextComposeNs(used)\n // Drop EXTERNAL entries first (same filter as compileLinkable): a `rules()` cache\n // also holds every ACCESSED-but-undefined `g.X` as an unresolved-lazy entry. Left in,\n // serializeRuleMap would emit `X: g[\"X\"]` \u2014 a self-referential rule that shadows the\n // sibling artifact defining X and recurses forever. They resolve by name at fuse time.\n const entries = Object.entries(map).filter(([, val]) => {\n const d = val._def\n if (d.tag !== 'lazy') return true\n try { d.thunk(); return true } catch { return false }\n })\n // Carry this grammar's ambient `scanSkip` (per-piece \u2014 opaque units are\n // dialect-specific, NOT composing-wins) into the IR so a re-lower stamps\n // `grammarScanSkip` back on. `linkable()`'s fallback reads it off `_meta` directly.\n const scanSkip = entries\n .map(([, val]) => (val._meta as { grammarScanSkip?: Combinator<unknown>[] }).grammarScanSkip)\n .find(Boolean)\n const ir = serializeRuleMap(entries, scanSkip)\n return ir ? [{ ns, ir }] : [linkable(map, ns, trivia, hostMode)]\n}\n\n/** The composed grammar's ambient trivia = the LAST composed item that declares a\n * grammar-level trivia (via `rules({ trivia }, \u2026)`, which tags `grammarTrivia` on its\n * rules). Outermost wins: the composing grammar's trivia applies to every fused rule,\n * including those inherited from a base \u2014 so e.g. an SCSS `rw` (which extends Less's)\n * governs the inherited Less/CSS rules too. `parser`/`noTrivia` still override locally. */\nfunction composingTriviaOf(items: Array<LinkablePieces | Record<string, unknown>>): Combinator<unknown> | undefined {\n for (let i = items.length - 1; i >= 0; i--) {\n const item = items[i] as Record<string, unknown> | undefined\n if (!item || (item as LinkablePieces).ruleFns instanceof Map || (item as Record<symbol, unknown>)[COMPOSED_PIECES]) continue\n for (const v of Object.values(item)) {\n const t = (v as Combinator<unknown> | undefined)?._meta?.grammarTrivia\n if (t) return t\n }\n }\n return undefined\n}\n\nexport function compose(\n items: Array<LinkablePieces | Record<string, unknown>>,\n /**\n * Compile-time host mode for the fused artifact, same meaning as\n * `compile(g, { hostMode })`. Omit (or `'ast'`) for the eval driver \u2014 the fused rules\n * build through the grammar's own `build` callbacks and carry no positioned-CST\n * branch. Pass `'cst'` to fuse a SECOND artifact from the same pieces for the linter /\n * IDE / language-service driver. Two compilations of one grammar, decided here, rather\n * than one artifact deciding per node on every parse.\n */\n opts?: { hostMode?: HostMode },\n): Record<string, FusedRule> {\n if (items.some(item => (item as Record<symbol, unknown>)[LEAF_COMPOSED] === true)) {\n throw new Error('compose: a composeLeaf() result is terminal and cannot be composed again')\n }\n const used = new Set<string>()\n // The composed grammar's ambient trivia comes from the composing grammar itself \u2014\n // whatever the last piece declared via rules({ trivia }, \u2026). No separate option:\n // the trivia rides with the grammar that declared it.\n const trivia = composingTriviaOf(items)\n // Carried items are RE-LOWERABLE (IR); materialize them ONCE with this compose's\n // trivia for the now-fuse, but STORE the un-materialized carried list so a later\n // compose can re-lower it under a different trivia (multi-level composing-wins).\n const carried = items.flatMap(item => itemCarried(item, used, trivia, opts?.hostMode))\n const pieces = carried.map(p => materializePiece(p, trivia, false, opts?.hostMode))\n const map = fuseRules(pieces)\n Object.defineProperty(map, COMPOSED_PIECES, { value: carried, enumerable: false })\n if (trivia) Object.defineProperty(map, COMPOSED_TRIVIA, { value: trivia, enumerable: false })\n const coverageRules = coverageRulesOf(carried)\n if (coverageRules) Object.defineProperty(map, COMPOSED_COVERAGE_RULES, { value: coverageRules, enumerable: false })\n return map\n}\n\n/**\n * Compose a terminal grammar. This is for a leaf parser that overlays local\n * semantic reductions on reusable recognition rules.\n *\n * Under the macro this lowers to STATIC fused source (functions), exactly like\n * `compose()`. It is still macro-only as a *compiled* artifact: without macro\n * lowering there is no safe way to keep lexical builders out of carried IR, so it\n * never falls back to runtime CODEGEN composition.\n *\n * Called at runtime (no macro) it returns the INTERPRETED fuse of the same items\n * \u2014 a combinator map, not a map of compiled functions (`fuseInterpreted`). That is\n * the supported way to run/inspect a leaf grammar without a build step: drive it\n * with `run()` / `parseDoc()`, which accept either shape. The declared return type\n * is the MACRO-path type (a leaf grammar is shipped compiled); use\n * `isInterpretedFuse(map)` when a caller must tell the two apart.\n */\nexport function composeLeaf(\n items: Array<LinkablePieces | Record<string, unknown>>,\n): Record<string, FusedRule> {\n const pieces = items.flatMap(interpretedPieces)\n let fused: Record<string, Combinator<unknown>> | undefined\n const map: Record<string, unknown> = {}\n // LAZY on purpose. A grammar module typically builds SEVERAL leaf grammars over\n // one shared recognition piece (`cssGrammar`, `cssLineGrammar`, `cssCstGrammar`,\n // \u2026). An interpreted fuse binds that shared piece IN PLACE, so only one of them\n // can exist at a time \u2014 fusing all of them at import would make merely importing\n // the module throw. Fusing on first ACCESS means the grammar you actually use\n // works, and reaching for a second, conflicting one fails loudly at that point.\n // (`trackLines`/`hostMode` are compile-time distinctions; the interpreter decides\n // both per parse, so those variants are the same interpreted grammar anyway.)\n for (const name of ruleNamesOf(pieces)) {\n Object.defineProperty(map, name, {\n enumerable: true,\n configurable: true,\n get: () => (fused ??= fusePieces(pieces))[name],\n })\n }\n Object.defineProperty(map, LEAF_COMPOSED, { value: true, enumerable: false })\n Object.defineProperty(map, INTERPRETED_PIECES, {\n value: pieces.filter(p => p.plain).map(p => p.entries),\n enumerable: false,\n })\n return map as unknown as Record<string, FusedRule>\n}\n\n/* \u2500\u2500 Interpreted fuse \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * `compose()` fuses by CODEGEN: every piece is compiled to `_r_<Name>` functions\n * dropped into one scope, so a reference resolves by NAME and an override reroutes\n * the base piece's own calls (open recursion). None of that exists interpreted \u2014\n * the interpreter runs the combinator graph, and a cross-piece reference is an\n * ordinary `ref()` placeholder that nobody ever `.define()`d. That is why a\n * composed grammar could not be run interpreted at all, and why every diagnostic\n * that must NOT reach codegen (profiling, gating analysis, coverage) had to be\n * hand-fused in throwaway scripts.\n *\n * The interpreted fuse binds those placeholders directly, with the SAME semantics\n * the compiled fuse gets from name resolution:\n * - later piece wins per rule name (matching `fuseRules`/`pickPieces`);\n * - an override REPOINTS the slot every call site already holds, so a base\n * piece's internal calls reroute too (open recursion);\n * - the composing grammar's trivia governs every fused rule (`composingTriviaOf`);\n * - a referenced-but-undefined rule is a fuse-time error, not a parse-time one.\n *\n * It is MUTATING by construction: a hole is a shared object, and binding it is the\n * only way its call sites can see the answer. `repointRef` therefore records what\n * it changed and refuses a CONFLICTING second bind, so two different fusions over\n * one shared piece fail loudly instead of silently rewriting each other's parser.\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/**\n * A rule slot produced by `ref()` \u2014 what `rules()` stores for every rule that is\n * referenced by name, and for every `g.X` hole a piece leaves for another piece to\n * fill. `parse`/`_def.thunk` are OWN properties of that object, which is what lets\n * the interpreted fuse repoint it in place.\n */\ntype RefSlot = Combinator<unknown> & {\n _def: { tag: 'lazy'; thunk: () => Combinator<unknown> }\n define(p: Combinator<unknown>): void\n parse(input: string, pos: number, ctx: ParseContext): ParseResult<unknown>\n}\n\nfunction isRefSlot(c: Combinator<unknown>): c is RefSlot {\n return c._def.tag === 'lazy' && typeof (c as unknown as { define?: unknown }).define === 'function'\n}\n\n/** What a slot resolved to BEFORE any interpreted fuse touched it (`null` = it was\n * an unbound cross-piece hole). Recorded on first repoint so a LATER fuse computes\n * its winner from the grammar as authored, never from another fusion's binding. */\nconst FUSE_ORIGINAL = Symbol.for('parseman.interpretedFuseOriginal')\n/** What an interpreted fuse repointed this slot at. */\nconst FUSE_TARGET = Symbol.for('parseman.interpretedFuseTarget')\n/** The source rule maps behind a `fuseInterpreted()` result, so it can be fused\n * again \u2014 the interpreted mirror of `COMPOSED_PIECES`. */\nconst INTERPRETED_PIECES = Symbol.for('parseman.interpretedPieces')\n\n/** Whether `map` is an interpreted fuse (a combinator map) rather than a compiled\n * `compose()` result (a map of fused functions). */\nexport function isInterpretedFuse(map: object): boolean {\n return Array.isArray((map as Record<symbol, unknown>)[INTERPRETED_PIECES])\n}\n\ntype NamedEntries = ReadonlyArray<[string, Combinator<unknown>]>\n/** `plain` = the item was an authored `rules()` map. Only plain maps declare the\n * composing trivia, mirroring `composingTriviaOf`, which skips artifacts and prior\n * composed results for exactly the same reason: their trivia was already applied. */\ntype FusePiece = { entries: NamedEntries; plain: boolean }\n\n/** The rule a map entry DEFINES, or `undefined` when it is an external reference\n * (an accessed-but-undefined `g.X`). Same local-vs-external test `compileLinkable`\n * and `itemCarried` apply, so the two fuses agree on what a piece contributes. */\nfunction definitionOf(entry: Combinator<unknown>): Combinator<unknown> | undefined {\n if (isRefSlot(entry)) {\n const original = (entry as unknown as Record<symbol, unknown>)[FUSE_ORIGINAL]\n if (original !== undefined) return (original as Combinator<unknown> | null) ?? undefined\n try { return entry._def.thunk() } catch { return undefined }\n }\n if (entry._def.tag === 'lazy') {\n try { (entry._def as { thunk: () => Combinator<unknown> }).thunk() } catch { return undefined }\n }\n return entry\n}\n\n/** Point `slot` at `target`, updating every call site that holds it. Mirrors\n * `ref().define()`'s metadata propagation; refuses to overwrite a binding a\n * DIFFERENT fusion already made (see the mutation note above). */\nfunction repointRef(slot: RefSlot, target: Combinator<unknown>, name: string): void {\n const tagged = slot as unknown as Record<symbol, unknown>\n const bound = tagged[FUSE_TARGET] as Combinator<unknown> | undefined\n const original = FUSE_ORIGINAL in tagged\n ? tagged[FUSE_ORIGINAL] as Combinator<unknown> | null\n : (() => { try { return slot._def.thunk() } catch { return null } })()\n if ((bound ?? original) === target) return\n if (bound !== undefined) {\n throw new Error(\n `fuseInterpreted: rule \"${name}\" is already bound by a DIFFERENT interpreted fusion of the same grammar piece. `\n + `An interpreted fuse binds the shared placeholder objects in place, so two fusions cannot share a piece \u2014 `\n + `build a fresh instance of the piece (call its rules() factory again, or import the module under a distinct specifier) for the second fusion.`,\n )\n }\n if (!(FUSE_ORIGINAL in tagged)) Object.defineProperty(slot, FUSE_ORIGINAL, { value: original, enumerable: false })\n Object.defineProperty(slot, FUSE_TARGET, { value: target, enumerable: false })\n slot._def.thunk = () => target\n slot.parse = (input, pos, ctx) => target.parse(input, pos, ctx)\n const meta = slot._meta\n meta.firstSet = target._meta.firstSet\n meta.canMatchNewline = target._meta.canMatchNewline\n meta.isTrivia = target._meta.isTrivia\n if (target._meta.triviaKindLabels !== undefined) meta.triviaKindLabels = target._meta.triviaKindLabels\n else delete meta.triviaKindLabels\n if (target._meta.disjoint !== undefined) meta.disjoint = target._meta.disjoint\n else delete meta.disjoint\n}\n\n/** Flatten one `fuseInterpreted()` item to the rule maps it contributes, in order. */\nfunction interpretedPieces(item: LinkablePieces | Record<string, unknown>): FusePiece[] {\n const fused = (item as Record<symbol, unknown>)[INTERPRETED_PIECES]\n if (Array.isArray(fused)) return (fused as NamedEntries[]).map(entries => ({ entries, plain: false }))\n const carried = composedPiecesOf(item as Record<string, unknown>)\n if (carried !== undefined) {\n // A compiled `compose()` result. Its carried IR re-lowers to combinators, but a\n // piece that arrived already COMPILED has no combinator graph at all \u2014 fusing\n // around it would silently drop its rules, which is the one failure mode a\n // diagnostic must never have.\n const { maps, opaque } = carriedRuleMapsDetailed(carried)\n if (opaque.length > 0) {\n throw new Error(\n `fuseInterpreted: cannot interpret a composed grammar containing precompiled artifact(s) `\n + `${opaque.map(o => `\"${o.ns}\" (${o.ruleNames.length} rules)`).join(', ')} \u2014 they carry compiled functions, not a combinator graph. `\n + `Pass the source grammars (the same items you passed to compose()) instead.`,\n )\n }\n return maps.map(entries => ({ entries, plain: false }))\n }\n if ((item as LinkablePieces).ruleFns instanceof Map) {\n throw new Error('fuseInterpreted: a precompiled linkable artifact has no combinator graph to interpret; pass the source grammar (a rules() map) instead')\n }\n return [{ entries: Object.entries(item as Record<string, Combinator<unknown>>), plain: true }]\n}\n\n/**\n * Materialize a composition as a RUNNABLE INTERPRETED rule map \u2014 the interpreted\n * counterpart of `compose()`, with identical fuse semantics (later piece wins,\n * override reroutes the base's own calls, composing trivia governs every rule).\n * No codegen, no `new Function`, no macro build step: the result is a plain map of\n * combinators that `run()` / `parseDoc()` accept exactly like a fused map.\n *\n * This is what diagnostics and profiling run against \u2014 they must stay in\n * interpreted mode, and before this they could not see a composed grammar at all.\n *\n * Items are the SAME items `compose()`/`composeLeaf()` take: `rules()` maps\n * (the intended input), a prior `fuseInterpreted()` result, or a runtime\n * `compose()` result (re-lowered from its carried IR \u2014 note that carried IR cannot\n * materialize direct `node()` builders, so prefer the source maps). A precompiled\n * `linkable()` artifact is rejected: it has no combinator graph.\n *\n * MUTATION: binding a cross-piece hole rewrites the shared placeholder object every\n * call site already holds \u2014 that IS how an override reaches a base piece's own\n * calls. A second, DIFFERENT fusion over the same piece objects therefore throws\n * rather than silently rewriting the first one's parser.\n */\nexport function fuseInterpreted(\n items: Array<LinkablePieces | Record<string, unknown>>,\n opts?: { hostMode?: HostMode },\n): Record<string, Combinator<unknown>> {\n return fusePieces(items.flatMap(interpretedPieces), opts)\n}\n\n/** The rule names a fusion of these pieces defines, in winner order \u2014 computable\n * WITHOUT binding anything, which is what lets `composeLeaf()` expose its key set\n * before it fuses. */\nfunction ruleNamesOf(pieces: FusePiece[]): string[] {\n const names = new Set<string>()\n for (const piece of pieces) {\n for (const [name, value] of piece.entries) if (definitionOf(value) !== undefined) names.add(name)\n }\n return [...names]\n}\n\nfunction fusePieces(\n pieces: FusePiece[],\n opts?: { hostMode?: HostMode },\n): Record<string, Combinator<unknown>> {\n // Composing-wins trivia, read exactly as `composingTriviaOf` reads it for compose():\n // the LAST authored grammar that declared `rules({ trivia }, \u2026)`.\n let trivia: Combinator<unknown> | undefined\n for (let i = pieces.length - 1; i >= 0 && trivia === undefined; i--) {\n if (!pieces[i]!.plain) continue\n for (const [, rule] of pieces[i]!.entries) {\n const t = rule._meta.grammarTrivia\n if (t) { trivia = t; break }\n }\n }\n\n // Winner per rule name \u2014 later piece wins, matching `fuseRules`. The winner is the\n // DEFINITION, never the slot holding it: a slot can be repointed, and a chain\n // through one would make an override of X reroute into itself.\n const winner = new Map<string, Combinator<unknown>>()\n const entry = new Map<string, Combinator<unknown>>()\n for (const piece of pieces) {\n for (const [name, value] of piece.entries) {\n const def = definitionOf(value)\n if (def === undefined) continue\n winner.set(name, def)\n entry.set(name, value)\n }\n }\n\n // Bind every hole (and repoint every overridden slot) before anything runs.\n const missing = new Set<string>()\n for (const piece of pieces) {\n for (const [name, value] of piece.entries) {\n if (!isRefSlot(value)) continue\n const target = winner.get(name)\n if (target === undefined) { missing.add(name); continue }\n repointRef(value, target, name)\n }\n }\n if (missing.size > 0) throw new Error(missingRuleMessage(pieces, missing))\n\n const out: Record<string, Combinator<unknown>> = {}\n for (const [name, value] of entry) {\n out[name] = value\n const meta = value._meta as {\n isTrivia: boolean\n grammarTrivia?: Combinator<unknown>\n grammarHostMode?: HostMode\n }\n // A trivia rule must never carry the ambient trivia (it would recursively skip\n // trivia within itself) \u2014 the same guard `compileLinkable` applies per rule.\n if (trivia !== undefined && !meta.isTrivia) meta.grammarTrivia = trivia\n // Host mode is PER PIECE, exactly as `compileLinkable` resolves it: an explicit\n // option wins, otherwise the owning piece's own `rules({ hostMode })` stamp.\n if (opts?.hostMode !== undefined && !meta.isTrivia) {\n if (opts.hostMode === 'cst') meta.grammarHostMode = 'cst'\n else delete meta.grammarHostMode\n }\n }\n Object.defineProperty(out, INTERPRETED_PIECES, {\n value: pieces.filter(p => p.plain).map(p => p.entries),\n enumerable: false,\n })\n return out\n}\n\n/** Name-closure failure, reported the way the compiled fuse reports it: which rule\n * referenced the missing name. Computed only on the error path. */\nfunction missingRuleMessage(pieces: FusePiece[], missing: Set<string>): string {\n for (const piece of pieces) {\n const deps = ruleDependencies(piece.entries.filter(([, v]) => definitionOf(v) !== undefined))\n for (const [name, ds] of deps) {\n for (const d of ds) if (missing.has(d)) return `fuseInterpreted: rule \"${name}\" references missing rule \"${d}\"`\n }\n }\n return `fuseInterpreted: missing rule(s) ${[...missing].map(n => `\"${n}\"`).join(', ')}`\n}\n", "/**\n * Relative span model for incremental edits.\n *\n * Absolute source offsets are simple but fragile: an insertion at offset `at`\n * shifts EVERY downstream offset, so a whole-tree renumber is O(nodes-after-edit).\n * Storing each node's span RELATIVE to its parent's start makes edits local: an\n * insertion inside child k of a node only changes\n * (a) child k's relative end,\n * (b) the relative start of siblings after k,\n * and leaves the *children* of those shifted siblings untouched \u2014 they move with\n * their parent for free. So the mutation count is O(depth + trailing siblings),\n * not O(all following nodes), and every unaffected subtree is shared by identity.\n *\n * An absolute view is recovered on query by accumulating the parent base as you\n * descend (the `absolutize` / `absoluteSpanAt` cursor) \u2014 one source of truth,\n * two manifestations.\n */\n\n/** A node with an ABSOLUTE source span and ordered children. */\nexport type AbsNode = {\n readonly start: number\n readonly end: number\n readonly children: readonly AbsNode[]\n}\n\n/** A node whose `start`/`end` are RELATIVE to its parent's start (root base = 0). */\nexport type RelNode = {\n readonly start: number\n readonly end: number\n readonly children: readonly RelNode[]\n}\n\n/** Convert an absolute tree to parent-relative spans. */\nexport function relativize(node: AbsNode, parentBase = 0): RelNode {\n return {\n start: node.start - parentBase,\n end: node.end - parentBase,\n children: node.children.map((c) => relativize(c, node.start)),\n }\n}\n\n/** Reconstruct absolute spans from a relative tree. Inverse of `relativize`. */\nexport function absolutize(node: RelNode, parentBase = 0): AbsNode {\n const start = parentBase + node.start\n return {\n start,\n end: parentBase + node.end,\n children: node.children.map((c) => absolutize(c, start)),\n }\n}\n\n/**\n * Absolute span of the node at `path` (child indices from the root), accumulating\n * the base as it descends \u2014 O(path length), no full absolutize. The relative-form\n * cursor: query any node's absolute position without materializing the whole tree.\n */\nexport function absoluteSpanAt(root: RelNode, path: readonly number[]): { start: number; end: number } {\n let base = 0\n let node = root\n for (const idx of path) {\n base += node.start\n const child = node.children[idx]\n if (!child) throw new RangeError(`absoluteSpanAt: no child ${idx} on path ${path.join('.')}`)\n node = child\n }\n return { start: base + node.start, end: base + node.end }\n}\n\n// ---------------------------------------------------------------------------\n// CST-shaped projection (parseDoc incremental trees)\n//\n// The bare Abs/RelNode helpers above are the reference model. The functions\n// below apply the same relative-span algebra to real CST children \u2014 nodes,\n// leaves, and error nodes \u2014 where the span is a nested `{ start, end }` and\n// children are heterogeneous (leaves carry a span but no `children`). Trivia\n// never appears in structural `children`, so every child has a span.\n// ---------------------------------------------------------------------------\n\n/**\n * A CST child as far as span projection cares: a nested span + optional children.\n * `children` is typed loosely (`unknown[]`) so the real heterogeneous CST unions\n * (nodes, leaves, error nodes \u2014 a leaf has no `children`) satisfy it directly.\n */\ntype Spanned = {\n readonly span: { readonly start: number; readonly end: number }\n readonly children?: readonly unknown[]\n}\n\n/**\n * Rewrite a CST subtree's spans to be RELATIVE to `parentBase` (the absolute\n * start of the node's parent; root base = 0). Each child's own children are\n * relativized against *this* node's absolute start. Preserves every other field\n * (`_tag`, `type`, `state`, leaf `value`, \u2026) by shallow spread.\n */\nexport function relativizeCST<T extends Spanned>(node: T, parentBase = 0): T {\n const span = { start: node.span.start - parentBase, end: node.span.end - parentBase }\n if (node.children) {\n const base = node.span.start\n return { ...node, span, children: node.children.map((c) => relativizeCST(c as Spanned, base)) } as T\n }\n return { ...node, span } as T\n}\n\n/** Reconstruct absolute CST spans from a relative subtree. Inverse of `relativizeCST`. */\nexport function absolutizeCST<T extends Spanned>(node: T, parentBase = 0): T {\n const start = parentBase + node.span.start\n const span = { start, end: parentBase + node.span.end }\n if (node.children) {\n return { ...node, span, children: node.children.map((c) => absolutizeCST(c as Spanned, start)) } as T\n }\n return { ...node, span } as T\n}\n\n/**\n * Absolute span of the CST node at `path` (child indices from the root),\n * accumulating the base as it descends \u2014 O(path length), no full absolutize.\n * The relative-form cursor for a positioned CST: query one node's absolute\n * position without materializing the whole tree.\n */\nexport function absoluteSpanCST(root: Spanned, path: readonly number[]): { start: number; end: number } {\n let base = 0\n let node: Spanned = root\n for (const idx of path) {\n base += node.span.start\n const child = node.children?.[idx] as Spanned | undefined\n if (!child) throw new RangeError(`absoluteSpanCST: no child ${idx} on path ${path.join('.')}`)\n node = child\n }\n return { start: base + node.span.start, end: base + node.span.end }\n}\n\n/** Naive absolute reshift (baseline): shift every offset >= `at` by `delta`. */\nexport function shiftAbsolute(node: AbsNode, at: number, delta: number): AbsNode {\n const s = node.start >= at ? node.start + delta : node.start\n const e = node.end >= at ? node.end + delta : node.end\n return {\n start: s,\n end: e,\n children: node.children.map((c) => shiftAbsolute(c, at, delta)),\n }\n}\n\n/**\n * Apply an edit of `delta` characters at absolute offset `at` to a RELATIVE tree.\n * Returns a new relative tree; every subtree that ends at/before `at` (in absolute\n * terms) is returned by IDENTITY (unchanged), so the result structurally shares\n * all unaffected subtrees with the input.\n *\n * `stats.allocated` counts nodes that had to be re-created \u2014 the incremental cost.\n */\nexport function applyEdit(\n root: RelNode,\n at: number,\n delta: number,\n stats?: { allocated: number },\n): RelNode {\n // The root has no parent; its base never shifts (base 0).\n return editNode(root, at, delta, 0, 0, stats)\n}\n\nfunction editNode(\n node: RelNode,\n at: number,\n delta: number,\n parentBase: number,\n parentShift: number, // how much this node's parent base moved (0 or delta)\n stats?: { allocated: number },\n): RelNode {\n const absStart = parentBase + node.start\n const absEnd = parentBase + node.end\n\n // Strictly before the edit \u2192 unchanged (its parent base is also unshifted).\n // Uses `< at` (not `<=`) to match the shift convention: offsets `>= at` move,\n // so a node ending exactly at `at` grows and must be rewritten.\n if (absEnd < at) return node\n\n // Fully after the edit AND the parent moved by `delta` too \u2192 this node moves as\n // one unit with its parent, so its parent-relative offsets are identical. Share\n // the ENTIRE subtree by identity \u2014 this is the O(depth) locality win.\n const startShifts = absStart >= at\n if (startShifts && parentShift === delta) return node\n\n if (stats) stats.allocated++\n\n const newAbsStart = startShifts ? absStart + delta : absStart\n const newAbsEnd = absEnd + delta // absEnd > at here (fully-before returned above)\n const newParentBase = parentBase + parentShift\n const newStart = newAbsStart - newParentBase\n const newEnd = newAbsEnd - newParentBase\n\n // Children are relative to this node's absolute start. Our own shift (0 or delta)\n // becomes their parentShift.\n const ownShift = newAbsStart - absStart\n let changed = false\n const newChildren: RelNode[] = []\n for (const c of node.children) {\n const nc = editNode(c, at, delta, absStart, ownShift, stats)\n if (nc !== c) changed = true\n newChildren.push(nc)\n }\n\n return {\n start: newStart,\n end: newEnd,\n children: changed ? newChildren : node.children,\n }\n}\n", "/**\n * Incremental parse document \u2014 incremental re-parse over a rules() registry.\n */\nimport type { Combinator, ParseContext, ParseError, ParseResult, ParserDef, Span } from '../types.ts'\nimport type { NodeLike, CSTLeaf, CSTError } from '../cst/types.ts'\nimport { relativizeCST, absoluteSpanCST } from '../cst/relative-spans.ts'\nimport { REC } from '../recovery/scan.ts'\nimport { fusedHostModeOf, fusedHostElidedOf } from '../compiler/linker.ts'\nimport { assertHostModeCompatible } from '../compiler/codegen.ts'\n\n/**\n * Build the parse ctx for a (re)parse. In `tolerant` mode the same recovery bundle\n * the interpreter/`run` use is installed (`_tolerant` + `_rec`), so a broken edit\n * keeps producing a tree (with recovered `ParseError`s embedded) instead of\n * collapsing to `null`. Strict mode is byte-identical to before. Every reparse site\n * \u2014 root, localized rule reparse, list-splice middle, and the soundness probes \u2014\n * threads the SAME flag, so a probe's tolerance matches the parse it is checking\n * (a mismatch would spuriously diverge and forgo reuse, never miscompare).\n */\nfunction mkCtx(rawState: unknown, build: ParseContext['build'], tolerant: boolean): ParseContext {\n // A CST node stores `state ?? null`, so \"unset\" comes back as `null`. Feeding that\n // null straight back in makes a REPARSE diverge from a fresh parse: the state-clone\n // guard tests `!== undefined`, and `Object.assign({}, null)` is `{}` \u2014 so a reused\n // subtree carries `state: {}` where a fresh parse carries `state: null`. Normalize the\n // round-trip back to \"unset\" at this single seeding site.\n const state = rawState ?? undefined\n return tolerant\n ? { trackLines: false, state, build, _tolerant: true, _rec: REC, _errors: [] }\n : { trackLines: false, state, build }\n}\n\n/** A single compiled (or interpreted) rule: parse from `pos`, producing node `N`. */\nexport type RuleFn<N> = (input: string, pos: number, ctx: ParseContext) => ParseResult<N>\n\n/**\n * Rule name \u2192 parser. Each entry is either a bare parse function or a\n * `Combinator` (what `rules()` returns). Passing the `rules()` combinators\n * directly lets `.edit()` inspect the grammar \u2014 required for **sound** structural\n * list-reuse (see `structuralReuse`): only a rule the grammar proves is a genuine\n * repetition is ever spliced. A bare-function registry still parses correctly; it\n * just can't be structurally reused (the splice is skipped, never guessed).\n */\nexport type Registry<N> = Record<string, RuleFn<N> | Combinator<N>>\n\n/** Normalize a registry entry to a callable parse function. */\nfunction asRuleFn<N>(entry: RuleFn<N> | Combinator<N>): RuleFn<N> {\n return typeof entry === 'function' ? entry : (input, pos, ctx) => entry.parse(input, pos, ctx)\n}\n\n/** The combinator def behind a registry entry, if it carries one (bare functions don't). */\nfunction defOf(entry: unknown): ParserDef | undefined {\n return typeof entry === 'object' && entry !== null && '_def' in entry\n ? (entry as { _def: ParserDef })._def\n : undefined\n}\n\n/**\n * Does this rule's grammar produce its element children via a genuine, unbounded\n * **repetition** (`sepBy` / `many` / `oneOrMore`) \u2014 as opposed to a fixed-arity\n * sequence of same-typed tokens (e.g. `Triple = Num ',' Num ',' Num`)? Only the\n * former is sound to structurally reuse: a full reparse accepts any element count,\n * so splicing one in or out matches it; a fixed-arity rule would not. We walk the\n * def, transparently unwrapping semantic wrappers (`node`, `transform`, \u2026) and\n * looking through a top-level `sequence` / `optional` (the `[ (sepBy)? ]` shape),\n * and return true iff a repetition combinator is reachable that way. Structurally\n * indistinguishable-from-the-CST cases (fixed sequences) return false and fall\n * back to a full, correct reparse. This is what makes `structuralReuse` sound\n * rather than a promise the caller has to keep.\n */\nfunction producesRepetition(def: ParserDef | undefined, depth = 0): boolean {\n if (!def || depth > 24) return false\n const d = def as ParserDef & Record<string, unknown>\n switch (d.tag) {\n case 'sepBy':\n case 'many':\n case 'oneOrMore':\n return true\n // Rule entries and `ref`s wrap their body in a `lazy` thunk \u2014 resolve it. The\n // `depth` cap bounds any self-referential cycle (a repetition, if present, is\n // found shallowly before recursion goes deep).\n case 'lazy': {\n const thunk = d.thunk as (() => { _def?: ParserDef }) | undefined\n let inner: { _def?: ParserDef } | undefined\n try { inner = typeof thunk === 'function' ? thunk() : undefined } catch { return false }\n return producesRepetition(inner?._def, depth + 1)\n }\n // Transparent wrappers \u2014 look through to the inner parser.\n case 'node':\n case 'transform':\n case 'attempt':\n case 'token':\n case 'leaf':\n case 'label':\n case 'field':\n case 'expect':\n case 'withCtx':\n case 'grammar':\n case 'optional':\n return producesRepetition(defOf(d.parser), depth + 1)\n case 'skip':\n return producesRepetition(defOf(d.main), depth + 1)\n // A bracketed/anchored list is a sequence whose element run is a repetition.\n case 'sequence':\n return (d.parsers as unknown[]).some(p => producesRepetition(defOf(p), depth + 1))\n default:\n return false\n }\n}\n\nexport type ParseDocOptions<N extends NodeLike> = {\n /** Initial grammar state threaded into ctx.state for the root parse. */\n state?: unknown\n /**\n * Reconstruct a parent node with one child replaced (used when grafting a\n * re-parsed subtree into its ancestors). Defaults to a shallow spread, which\n * works for plain-object nodes; class-instance ASTs should supply their own.\n */\n rebuild?: (node: N, children: ReadonlyArray<N | CSTLeaf | CSTError>) => N\n /**\n * Mode host for a linkable/fused grammar (RULE_ABI_PLAN \u00A77): threaded into\n * `ctx.build` on every (re)parse so `node()` rules build a positioned CST /\n * language-service tree instead of their own eval-AST. Unset \u2192 the grammar's\n * own builders (eval mode).\n */\n build?: ParseContext['build']\n /**\n * Enable structural list-reuse: on a length-changing *structural* edit (adding\n * or removing a whole element in a collection) that would otherwise force a full\n * reparse, reparse only the disturbed span and reuse the collection's untouched\n * tail elements by identity \u2014 turning an insert near the top of a large list from\n * O(list) into O(edit + trailing siblings).\n *\n * OFF by default because it is sound only when a rule whose CST children form a\n * homogeneous, separator-delimited element list is a genuine REPETITION\n * (`many` / `sepBy` / `oneOrMore`), not a fixed-arity sequence of same-typed\n * tokens (e.g. `Triple = Num ',' Num ',' Num`) \u2014 the two are structurally\n * indistinguishable without the grammar, and splicing the latter would accept an\n * element count it shouldn't. Turn it on when your list rules are true\n * repetitions (the common case: JSON arrays/objects, CSS value lists, argument\n * lists). Every splice is still guarded (exact tiling + lookahead probe +\n * stateless-tail check) and falls back to a full, correct reparse when unproven;\n * the flag only authorises *attempting* the reuse.\n */\n structuralReuse?: boolean\n /**\n * Parse tolerantly: `many`/`sepBy`/`oneOrMore` recover from a failed element\n * (skip to an inferred sync point, embed a `ParseError` over the skipped span),\n * so a broken edit keeps producing a tree instead of collapsing to `null` \u2014 the\n * editor-backend path. Off by default (strict, byte-identical). Recovery is a cold\n * path: well-formed input never triggers it. Every reparse the doc does \u2014 root,\n * localized rule, and list-splice \u2014 inherits this flag, and the reuse-soundness\n * probes run at the same tolerance so incremental reuse stays valid under recovery\n * (a scan that would cross a splice boundary just falls back to a full reparse).\n */\n tolerant?: boolean\n /**\n * The grammar's trivia rule, used ONLY to compute `unconsumedFrom`: a root rule\n * consumes trivia BETWEEN terms but not after the last, so trailing\n * whitespace/comments would otherwise read as leftover input. Given the trivia\n * rule, the tail is skipped before reporting the first unconsumed offset \u2014\n * matching `run()`'s semantics. Defaults to the root rule's own\n * `_meta.grammarTrivia` (from `rules({ trivia })`); set it only to override.\n */\n trivia?: Combinator<unknown>\n}\n\nexport interface ParseDoc<N extends NodeLike> {\n /**\n * The parse tree with PARENT-RELATIVE spans \u2014 each node's `span` is relative to\n * its parent's start (root base 0). This is the shareable representation: a\n * length-changing `.edit()` keeps every untouched subtree shared by identity,\n * and reading the tree is O(1) (no offset rewrite). For absolute positions use\n * the O(depth) cursor `spanAt(path)`, or `absolutizeCST(doc.tree)` to\n * materialize the whole absolute tree. A fresh non-incremental `node().parse()`\n * result is unchanged \u2014 still absolute.\n */\n readonly tree: N | null\n /**\n * Recovery diagnostics collected during the (re)parse \u2014 the missing-token\n * `expect()` errors and tolerant-list recovery errors that ride the tree as\n * `parseError` nodes, surfaced here as a flat list too (spans ABSOLUTE). Empty\n * in strict mode. On a hard (non-recovered) parse failure this holds the single\n * top-level failure. This is what makes an editor document able to see syntax\n * errors \u2014 a blank `errors: []` (the prior behaviour) hid every recovery.\n */\n readonly errors: ParseError[]\n /**\n * Offset where unparsed input begins \u2014 the first non-trivia character the parse\n * left unconsumed (trailing trivia skipped when a trivia rule is available), or\n * `null` if the whole input was consumed. This is how a document detects \"the\n * grammar stopped short, there's junk here\"; computed exactly as `run()` does.\n */\n readonly unconsumedFrom: number | null\n readonly input: string\n /**\n * Absolute span of the node at `path` (child indices from the root) \u2014 O(depth),\n * without materializing the absolute tree. The projection cursor for the\n * relative representation; use it for spot queries on a large incremental doc.\n */\n spanAt(path: readonly number[]): { start: number; end: number }\n /**\n * Incrementally re-parse after a text change. `from`/`to` are byte offsets in\n * the OLD input; `replacement` fills that range (editor change-event shape).\n * Sound: the result tree is always structurally identical to a fresh\n * `parseDoc` of the edited text (the Stage-2 guard falls back to a full\n * reparse whenever reuse can't be proven safe). Reuse/strategy is intentionally\n * NOT reported here \u2014 an observer derives it by diffing this tree against the\n * previous one (see the incremental tests); the runtime's job is to be fast,\n * not to measure itself.\n */\n edit(from: number, to: number, replacement: string): ParseDoc<N>\n}\n\n// ---------------------------------------------------------------------------\n// Tree navigation (generic over NodeLike \u2014 no class, no CST assumptions)\n// ---------------------------------------------------------------------------\n\n/**\n * A reentry candidate is only worth reparsing if it's meaningfully smaller than\n * the whole document \u2014 reparsing a rule that spans (say) >half the input costs\n * about as much as a full reparse with none of the reuse. Above this fraction of\n * the input length, `.edit()` skips straight to a full reparse. Keeps the worst\n * case (a structural edit near the front) at ~1\u00D7 full reparse, never several.\n */\nconst REENTRY_MAX_SPAN_FRACTION = 0.5\n\ntype FoundNode<N extends NodeLike> = { node: N; path: number[] }\n\nfunction isNode(x: unknown): x is NodeLike {\n return typeof x === 'object' && x !== null && (x as { _tag?: string })._tag === 'node'\n}\n\n/**\n * Locate the deepest node containing absolute offset `pos`. The tree stores\n * PARENT-RELATIVE spans (see relative-spans), so we thread each node's absolute\n * start down the descent: a child's absolute start is `nodeAbsStart +\n * child.span.start`.\n */\nfunction findContaining<N extends NodeLike>(\n node: N,\n nodeAbsStart: number,\n pos: number,\n path: number[] = [],\n): FoundNode<N> | null {\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]!\n if (!isNode(child)) continue\n const childAbsStart = nodeAbsStart + child.span.start\n const childAbsEnd = nodeAbsStart + child.span.end\n if (childAbsStart <= pos && pos < childAbsEnd) {\n const inner = findContaining(child as N, childAbsStart, pos, [...path, i])\n return inner ?? { node: child as N, path: [...path, i] }\n }\n }\n return null\n}\n\nfunction ancestorsAt<N extends NodeLike>(root: N, path: number[]): N[] {\n const ancestors: N[] = [root]\n let cur: N = root\n for (const idx of path.slice(0, -1)) {\n const child = cur.children[idx]\n if (!child || !isNode(child)) break\n ancestors.push(child as N)\n cur = child as N\n }\n return ancestors\n}\n\nfunction replaceAtPath<N extends NodeLike>(\n rebuild: NonNullable<ParseDocOptions<N>['rebuild']>,\n root: N,\n path: number[],\n newNode: N,\n): N {\n if (path.length === 0) return newNode\n const [idx, ...rest] = path as [number, ...number[]]\n const newChildren = [...root.children] as Array<N | CSTLeaf | CSTError>\n newChildren[idx] = rest.length === 0\n ? newNode\n : replaceAtPath(rebuild, root.children[idx] as N, rest, newNode)\n return rebuild(root, newChildren)\n}\n\nfunction defaultRebuild<N extends NodeLike>(node: N, children: ReadonlyArray<N | CSTLeaf | CSTError>): N {\n return { ...node, children } as N\n}\n\ntype SpanChild = { span: { start: number; end: number }; children?: readonly unknown[] }\n\n/**\n * Shift a node's PARENT-RELATIVE span by `delta`. Used for siblings that sit\n * *after* a length-changing edit inside the same parent: the node moved as a\n * unit, so both endpoints slide by `delta`, but its own children are relative to\n * *its* start and are unchanged \u2014 so `children` is shared by identity. That's the\n * relative-span win: O(1) per trailing sibling, not O(subtree). (Contrast the old\n * absolute model, which had to deep-rewrite every descendant.)\n */\nfunction shiftRelStart<T>(child: T, delta: number): T {\n const c = child as unknown as SpanChild\n return { ...(c as object), span: { start: c.span.start + delta, end: c.span.end + delta } } as unknown as T\n}\n\n/**\n * Graft `newNode` at `path` into a parent-relative tree for a length-changing\n * edit (`delta !== 0`): children before the edit are shared by reference, the\n * edited child is replaced, children *after* it have their relative start slid by\n * `delta` (one shallow alloc each \u2014 their subtrees are shared by identity), and\n * each ancestor's relative `span.end` grows by `delta` while its start is\n * untouched. Cost is O(depth + trailing siblings along the spine), independent of\n * how many nodes sit inside those trailing siblings.\n */\nfunction graftRelative<N extends NodeLike>(root: N, path: number[], newNode: N, delta: number): N {\n if (path.length === 0) return newNode\n const [idx, ...rest] = path as [number, ...number[]]\n const oldChildren = root.children as ReadonlyArray<N | CSTLeaf | CSTError>\n const newChildren = oldChildren.map((child, i) => {\n if (i < idx) return child\n if (i === idx) {\n return rest.length === 0 ? newNode : graftRelative(child as N, rest, newNode, delta)\n }\n return shiftRelStart(child, delta)\n })\n return {\n ...root,\n span: { start: root.span.start, end: root.span.end + delta },\n children: newChildren,\n } as N\n}\n\n// ---------------------------------------------------------------------------\n// List-splice reuse (structural edits in a collection)\n//\n// A structural edit \u2014 inserting or deleting a whole element in a list \u2014 makes the\n// innermost-rule reparse fail to converge, and the containing collection is often\n// too big to reparse (that's the \"insert at the top of a large array\" case where\n// a whole-rule reparse costs ~a full reparse). Instead of reparsing the whole\n// collection, reparse ONLY the disturbed span between the last untouched element\n// before the edit and the first untouched element after it, then splice: the head\n// children are shared as-is, the freshly-parsed middle replaces the changed run,\n// and the tail children are reused with their relative start slid by `delta`\n// (O(1) each \u2014 their subtrees are shared by identity, the relative-span win).\n//\n// Soundness rests on: (1) the reparsed middle exactly fills [reStart, reEnd) and\n// lands on element/separator boundaries; (2) a lookahead probe proving the middle\n// read nothing at/after the splice point (else it peeked into the reused tail);\n// (3) a stateless-reentry precondition on the reused tail (its saved `state` is\n// null), so a forward, context-free grammar reproduces those subtrees unchanged.\n// Any failure \u21D2 `null` \u21D2 the caller falls back to a full, correct reparse. The\n// incremental oracle fuzz (edit() \u2261 full reparse, 400 seeds \u00D7 3 list grammars +\n// edge cases) is the end-to-end correctness net.\n// ---------------------------------------------------------------------------\n\ntype AnyChild = { readonly _tag: string; readonly span: { start: number; end: number }; readonly value?: string; readonly type?: string; readonly state?: unknown; readonly children?: readonly unknown[] }\n\n/**\n * Parse the collection's disturbed middle `[reStart, reEnd)` as an\n * `element (separator element)*` run (optionally with a trailing separator that\n * connects to the reused tail), C-relative. Alternation is STRICT: after an\n * element, if we're not yet at `reEnd`, the separator MUST match \u2014 element/element\n * juxtaposition is rejected (that's a `sepBy` violation). When the grammar has no\n * separator (`separator === null`, i.e. a bare repetition), consecutive elements\n * are allowed. Returns the C-relative children, or `null` if the run doesn't tile\n * `[reStart, reEnd)` exactly on token boundaries.\n */\nfunction parseMiddle(\n input: string,\n reStart: number,\n reEnd: number,\n cStart: number,\n ruleFn: RuleFn<NodeLike>,\n separator: string | null,\n build: ParseContext['build'],\n tolerant: boolean,\n): AnyChild[] | null {\n const ctx: ParseContext = mkCtx(null, build, tolerant)\n const out: AnyChild[] = []\n let pos = reStart\n let guard = 0\n while (pos < reEnd) {\n if (++guard > reEnd - reStart + 2) return null // no-progress backstop\n // After an element, a separator is mandatory before the next element (unless\n // the grammar is separator-free). This is what keeps `elem elem` \u2014 invalid in\n // a `sepBy` \u2014 from being accepted as two elements.\n if (separator && out.length > 0 && out[out.length - 1]!._tag === 'node') {\n if (pos + separator.length > reEnd || !input.startsWith(separator, pos)) return null\n out.push({ _tag: 'leaf', value: separator, span: { start: pos - cStart, end: pos + separator.length - cStart } })\n pos += separator.length\n continue\n }\n let r: ParseResult<NodeLike>\n try {\n r = ruleFn(input, pos, ctx)\n } catch {\n return null\n }\n if (!r.ok || r.span.end <= pos || r.span.end > reEnd) return null\n out.push(relativizeCST(r.value as unknown as AnyChild, cStart))\n pos = r.span.end\n }\n return pos === reEnd ? out : null\n}\n\n/**\n * Try to reuse the untouched tail of collection `C` (at `path`, absolute start\n * `cStart`) around a length-changing edit, reparsing only the disturbed middle.\n * Returns the new relative root, or `null` to fall back to a full reparse.\n */\nfunction tryListSplice<N extends NodeLike>(\n root: N,\n path: number[],\n C: N,\n cStart: number,\n newInput: string,\n from: number,\n to: number,\n delta: number,\n registry: Record<string, RuleFn<N>>,\n build: ParseContext['build'],\n tolerant: boolean,\n): N | null {\n const kids = C.children as ReadonlyArray<AnyChild>\n if (kids.length === 0) return null\n\n // head = maximal prefix ending before the edit, backed off so it ends at a\n // leaf (open delimiter or separator) \u2014 i.e. where an ELEMENT is next expected.\n let h = 0\n while (h < kids.length && cStart + kids[h]!.span.end <= from) h++\n while (h > 0 && kids[h - 1]!._tag === 'node') h--\n // tail = maximal suffix starting after the edit, advanced so it BEGINS at an\n // element (node) boundary \u2014 a self-contained element run to reuse.\n let t = kids.length\n while (t > h && cStart + kids[t - 1]!.span.start >= to) t--\n while (t < kids.length && kids[t]!._tag !== 'node') t++\n if (t >= kids.length) return null // nothing reusable after the edit\n\n // Stateless-reentry precondition on the reused tail (see soundness note).\n for (let i = t; i < kids.length; i++) {\n if (kids[i]!._tag === 'node' && kids[i]!.state != null) return null\n }\n\n // C must look like a genuine homogeneous COLLECTION \u2014 a repetition of one\n // element rule joined by one separator \u2014 not a fixed heterogeneous sequence\n // (e.g. `Pair = Key ':' Val`, whose `:` is not a list separator). Require: all\n // element (node) children share a type, and there are \u22652 elements joined by a\n // consistent separator leaf. This rejects fixed sequences (their nodes differ\n // in type, or there's only one) so we never treat them as splice-able lists.\n let elemType: string | undefined\n let elemCount = 0\n for (const k of kids) {\n if (k._tag !== 'node') continue\n elemCount++\n if (elemType === undefined) elemType = k.type\n else if (k.type !== elemType) return null // heterogeneous \u2192 not a collection\n }\n if (elemType === undefined || elemCount < 2) return null\n const ruleFn = registry[elemType]\n if (!ruleFn) return null\n // The separator is the leaf that appears between two elements; require it to be\n // consistent everywhere two elements meet (a real list delimiter).\n let separator: string | null = null\n for (let i = 1; i < kids.length - 1; i++) {\n if (kids[i - 1]!._tag === 'node' && kids[i + 1]!._tag === 'node') {\n if (kids[i]!._tag !== 'leaf') return null\n const sep = kids[i]!.value ?? null\n if (separator === null) separator = sep\n else if (sep !== separator) return null // inconsistent delimiter \u2192 not a plain list\n }\n }\n if (separator === null) return null // \u22652 elements but no delimiter between any pair\n\n // The disturbed middle must be pure elements + separators. If the collection is\n // bracketed, its OPENING / CLOSING delimiter (a leaf whose value isn't the\n // separator, sitting before the first / after the last element) must lie OUTSIDE\n // the edit \u2014 otherwise the edit changed the collection's own framing and a\n // whole-rule reparse (not a splice) is required.\n const first = kids[0]!\n const last = kids[kids.length - 1]!\n if (first._tag === 'leaf' && first.value !== separator && from < cStart + first.span.end) return null\n if (last._tag === 'leaf' && last.value !== separator && to > cStart + last.span.start) return null\n\n const reStart = h > 0 ? cStart + kids[h - 1]!.span.end : cStart\n const reEnd = cStart + kids[t]!.span.start + delta\n if (reEnd < reStart) return null\n\n const middle = parseMiddle(newInput, reStart, reEnd, cStart, ruleFn as RuleFn<NodeLike>, separator, build, tolerant)\n if (!middle) return null\n\n // Junction check: `tail` always begins with an ELEMENT (we advanced `t` to a\n // node), so a non-empty middle must END with a SEPARATOR to connect validly \u2014\n // otherwise the edit deleted the delimiter between the middle's last element and\n // the tail's first, and splicing would fuse two elements with no separator (a\n // `sepBy` violation the whole-collection reparse would never produce). When the\n // middle is empty the tail is preceded by `head`'s last token, itself already a\n // separator or the open delimiter (head was backed off to a leaf), so it's fine.\n if (middle.length > 0) {\n const last = middle[middle.length - 1]!\n if (!(last._tag === 'leaf' && last.value === separator)) return null\n }\n\n // Lookahead guard: the middle must have read nothing at/after `reEnd`, else it\n // peeked into the reused tail. Re-run over an input whose tail is overwritten\n // with a sentinel and require an identical middle. Two sentinels so the real\n // byte at `reEnd` can't accidentally match the probe.\n if (reEnd < newInput.length) {\n for (const sentinel of [' ', '\uFFFF']) {\n if (newInput[reEnd] === sentinel) continue\n const probed = newInput.slice(0, reEnd) + sentinel.repeat(newInput.length - reEnd)\n const probe = parseMiddle(probed, reStart, reEnd, cStart, ruleFn as RuleFn<NodeLike>, separator, build, tolerant)\n if (!probe || probe.length !== middle.length) return null\n for (let i = 0; i < middle.length; i++) if (!structurallyEqual(probe[i], middle[i])) return null\n }\n }\n\n const head = kids.slice(0, h)\n const tail = kids.slice(t).map((k) => shiftRelStart(k, delta))\n const newC = {\n ...C,\n span: { start: C.span.start, end: C.span.end + delta },\n children: [...head, ...middle, ...tail],\n } as N\n\n // Replace C at `path` and slide C's own trailing siblings / ancestor ends.\n return graftRelative(root, path, newC, delta)\n}\n\n// ---------------------------------------------------------------------------\n// Stage-2 soundness guard\n// ---------------------------------------------------------------------------\n\n/**\n * Deep structural equality on parse trees: `_tag`, `span`, node `type`, leaf\n * `value`, and children pairwise. This is the oracle relation `.edit()` must\n * preserve against a full reparse; it's also what the Stage-2 guard compares\n * probe results with.\n */\nexport function structurallyEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false\n const at = (a as { _tag?: string })._tag\n const bt = (b as { _tag?: string })._tag\n if (at !== bt) return false\n const as = (a as { span?: { start: number; end: number } }).span\n const bs = (b as { span?: { start: number; end: number } }).span\n if (as || bs) {\n if (!as || !bs || as.start !== bs.start || as.end !== bs.end) return false\n }\n if (at === 'leaf' || at === 'trivia') {\n return (a as { value: string }).value === (b as { value: string }).value\n }\n if (at === 'parseError') {\n // Embedded recovered error: span already compared above; also compare the\n // expected-token set so two errors at the same span but different expectations\n // are not conflated (the oracle must distinguish them).\n const ae = (a as { expected?: readonly string[] }).expected ?? []\n const be = (b as { expected?: readonly string[] }).expected ?? []\n return ae.length === be.length && ae.every((x, i) => x === be[i])\n }\n if ((a as { type?: string }).type !== (b as { type?: string }).type) return false\n const ac = (a as { children?: readonly unknown[] }).children\n const bc = (b as { children?: readonly unknown[] }).children\n if (ac || bc) {\n if (!ac || !bc || ac.length !== bc.length) return false\n for (let i = 0; i < ac.length; i++) if (!structurallyEqual(ac[i], bc[i])) return false\n }\n return true\n}\n\n/**\n * A reused suffix spliced at `boundary` (new-input coords) is sound only if the\n * re-parse of the containing rule did NOT read any input at or after `boundary`\n * \u2014 otherwise a lookahead or backtrack peeked across the splice and the reused\n * tail could be wrong. We prove independence by re-running the same rule at the\n * same start on an input whose entire tail from `boundary` is overwritten with a\n * sentinel: if the produced node is byte-for-byte structurally identical,\n * nothing past `boundary` was inspected. Two distinct sentinels are tried so the\n * real char at `boundary` can't accidentally equal the probe. Conservative by\n * construction \u2014 any probe difference, failure, or throw \u21D2 not safe \u21D2 the caller\n * widens toward a full reparse (correctness over reuse fraction).\n */\nfunction boundaryIsSafe<N extends NodeLike>(\n ruleFn: RuleFn<N>,\n newInput: string,\n start: number,\n boundary: number,\n state: unknown,\n build: ParseContext['build'],\n produced: ParseResult<N>,\n tolerant: boolean,\n): boolean {\n if (!produced.ok) return false\n if (boundary >= newInput.length) return true // nothing after the node to peek at\n for (const sentinel of [' ', '\uFFFF']) {\n if (newInput[boundary] === sentinel) continue\n const probed = newInput.slice(0, boundary) + sentinel.repeat(newInput.length - boundary)\n const ctx: ParseContext = mkCtx(state, build, tolerant)\n let r: ParseResult<N>\n try {\n r = ruleFn(probed, start, ctx)\n } catch {\n return false\n }\n if (!r.ok || r.span.end !== produced.span.end) return false\n if (!structurallyEqual(r.value, produced.value)) return false\n }\n return true\n}\n\n// ---------------------------------------------------------------------------\n// Document\n// ---------------------------------------------------------------------------\n\nclass ParseDocImpl<N extends NodeLike> implements ParseDoc<N> {\n private readonly _registry: Registry<N>\n /** Registry entries normalized to callable parse functions (memoized once). */\n private readonly _fns: Record<string, RuleFn<N>>\n /** Rule names the grammar proves are genuine repetitions \u2014 the ONLY splice-safe types. */\n private readonly _reps: Set<string>\n private readonly _rootRule: string\n private readonly _opts: ParseDocOptions<N>\n /**\n * The tree, held in whichever coordinate system it arrived in. A fresh parse\n * arrives ABSOLUTE (`_abs` set); an incremental graft arrives PARENT-RELATIVE\n * (`_rel` set). The public `tree` is always the RELATIVE form (so untouched\n * subtrees stay shared by identity across edits and reads are O(1)); it's\n * materialized from `_abs` once, on demand, and memoized. `undefined` = not yet\n * computed; `null` = failed parse. A fresh parse that's never edited nor\n * tree-read pays no conversion \u2014 important for the full-reparse fallback path.\n */\n private _abs: N | null | undefined\n private _rel: N | null | undefined\n readonly errors: ParseError[]\n readonly unconsumedFrom: number | null\n readonly input: string\n\n constructor(\n registry: Registry<N>,\n rootRule: string,\n opts: ParseDocOptions<N>,\n trees: { abs?: N | null; rel?: N | null },\n errors: ParseError[],\n unconsumedFrom: number | null,\n input: string,\n ) {\n this._registry = registry\n this._fns = {}\n this._reps = new Set()\n for (const [name, entry] of Object.entries(registry)) {\n this._fns[name] = asRuleFn(entry)\n if (producesRepetition(defOf(entry))) this._reps.add(name)\n }\n this._rootRule = rootRule\n this._opts = opts\n this._abs = trees.abs\n this._rel = trees.rel\n this.errors = errors\n this.unconsumedFrom = unconsumedFrom\n this.input = input\n }\n\n /**\n * The parse tree with PARENT-RELATIVE spans \u2014 a node's `span` is relative to\n * its parent's start (root base 0). This is the shareable representation:\n * untouched subtrees keep the same identity across `.edit()`s, and reading the\n * tree after a length-changing edit is O(1) (no offset rewrite). For absolute\n * positions use the O(depth) cursor `spanAt(path)`, or `absolutizeCST(tree)` to\n * materialize the whole absolute tree. (A fresh non-incremental `node().parse()`\n * result is unchanged \u2014 still absolute.)\n */\n get tree(): N | null {\n if (this._rel === undefined) {\n this._rel = this._abs ? (relativizeCST(this._abs as unknown as N & { span: Span }, 0) as unknown as N) : null\n }\n return this._rel\n }\n\n spanAt(path: readonly number[]): { start: number; end: number } {\n const rel = this.tree\n if (!rel) throw new Error('spanAt on a failed parse (tree is null)')\n return absoluteSpanCST(rel as unknown as { span: Span; children?: readonly unknown[] }, path)\n }\n\n /**\n * Wrap a reused (grafted/spliced) RELATIVE tree, recomputing `errors` and\n * `unconsumedFrom` from it so both stay consistent with the tree across the\n * edit (the flat errors are recovered errors embedded in the reused subtrees;\n * unconsumedFrom re-derives trailing junk over the new input).\n */\n private wrapReuse(newTree: N, newInput: string): ParseDoc<N> {\n const trivia = triviaOf(this._registry[this._rootRule], this._opts)\n const end = (newTree as unknown as { span: { end: number } }).span.end\n const errors = collectEmbeddedErrors(newTree, 0, [])\n const unconsumedFrom = unconsumedAfter(end, newInput, trivia)\n return new ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree }, errors, unconsumedFrom, newInput)\n }\n\n edit(from: number, to: number, replacement: string): ParseDoc<N> {\n const newInput = this.input.slice(0, from) + replacement + this.input.slice(to)\n const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts)\n\n const root = this.tree\n if (!root) return reparse()\n\n const delta = replacement.length - (to - from)\n // When `from` sits on the root's own boundary (e.g. right after a top-level\n // element, before its separator), no child node contains it and\n // `findContaining` returns null \u2014 but the root still does. Fall back to the\n // root as the container so the structural splice below still gets a shot\n // (this is exactly the \"insert/delete a top-level list element\" position).\n const found = findContaining(root, 0, from) ?? { node: root, path: [] as number[] }\n\n // Try the innermost containing rule first, then widen outward.\n const ancestors = ancestorsAt(root, found.path)\n const candidates: FoundNode<N>[] = [found]\n const pathCopy = [...found.path]\n for (let i = ancestors.length - 2; i >= 0; i--) {\n pathCopy.pop()\n candidates.push({ node: ancestors[i + 1]!, path: [...pathCopy] })\n }\n\n const rebuild = this._opts.rebuild ?? defaultRebuild\n for (const { node, path } of candidates) {\n // Absolute span of this candidate, projected from the relative root (O(depth)).\n const { start: absStart, end: absEnd } = absoluteSpanCST(root as unknown as { span: Span; children?: readonly unknown[] }, path)\n // Reentry only pays off when the re-parsed rule is substantially smaller\n // than the whole document. Candidates widen deepest\u2192root (monotonically\n // growing span), so once one covers most of the input, reparsing it \u2014 and\n // every larger ancestor after it \u2014 can't beat a full reparse and would just\n // be wasted work before the fallback. Bail to the full reparse NOW. This\n // caps `.edit()` at ~one full reparse in the worst case (e.g. a structural\n // insert near the front) instead of stacking several near-full reparses.\n if (absEnd - absStart > this.input.length * REENTRY_MAX_SPAN_FRACTION) break\n const ruleFn = this._fns[node.type]\n if (!ruleFn) continue\n // The reused subtree must FULLY CONTAIN the edited range (old coords).\n // `findContaining` only locates `from`; if the edit's end `to` spills past\n // this node's end, the edit also changed a sibling/separator after it, and\n // reusing the untouched suffix would be unsound \u2014 widen to an ancestor\n // that does span the whole edit (ultimately a full reparse).\n if (!(absStart <= from && to <= absEnd)) continue\n const ctx: ParseContext = mkCtx(node.state, this._opts.build, !!this._opts.tolerant)\n const r = ruleFn(newInput, absStart, ctx)\n if (!r.ok) continue\n if (r.span.end !== absEnd + delta) continue\n\n // Stage-2 soundness guard: only reuse the untouched suffix if the re-parse\n // provably read no input past its own end (else a lookahead/backtrack\n // crossed the splice). Widen to the next candidate \u2014 ultimately a full\n // reparse \u2014 when it can't be proven.\n if (!boundaryIsSafe(ruleFn, newInput, absStart, absEnd + delta, node.state, this._opts.build, r, !!this._opts.tolerant)) {\n continue\n }\n\n // The re-parse produced an ABSOLUTE subtree; rebase it to parent-relative\n // for splicing into the relative tree. The parent's absolute start is\n // `absStart - node.span.start` (node.span is already relative to it).\n const parentBase = absStart - node.span.start\n const newRel = relativizeCST(r.value as unknown as N & { span: Span }, parentBase) as unknown as N\n\n // delta === 0: relative spans are unchanged, so the spine graft (sharing\n // every untouched sibling by reference) is already correct.\n if (delta === 0) {\n const newTree = replaceAtPath(rebuild, root, path, newRel)\n return this.wrapReuse(newTree, newInput)\n }\n // Length-changing edit: trailing siblings' relative starts slide by `delta`.\n // A custom `rebuild` (possibly a class instance) can't have its span slid\n // safely, so fall back to a full, correct reparse.\n if (this._opts.rebuild) return reparse()\n const newTree = graftRelative(root, path, newRel, delta)\n return this.wrapReuse(newTree, newInput)\n }\n\n // No localized rule reparse converged \u2014 the edit is structural. Before paying\n // a full reparse, try reusing the untouched tail of a containing collection\n // (add/remove an element in a list). We only ever splice a rule the GRAMMAR\n // proves is a genuine repetition (`this._reps`, from its combinator def) \u2014 a\n // fixed-arity same-typed sequence is structurally indistinguishable from a list\n // by its CST alone, so splicing it could accept a wrong element count; it's\n // excluded here and falls back to a full, correct reparse. Innermost containing\n // collection first; a candidate whose disturbed middle doesn't tile cleanly\n // returns null and we widen. Correctness net is the incremental oracle fuzz.\n if (this._opts.structuralReuse && delta !== 0 && !this._opts.rebuild) {\n // Candidates run innermost\u2192ancestor but never include the root itself; the\n // splice-able collection can BE the root (e.g. a top-level `sepBy` list), so\n // consider it last.\n const spliceCandidates: FoundNode<N>[] = [...candidates, { node: root, path: [] }]\n for (const { node, path } of spliceCandidates) {\n if (!this._reps.has(node.type)) continue // grammar didn't prove this rule a repetition\n const { start: cStart, end: cEnd } = absoluteSpanCST(root as unknown as { span: Span; children?: readonly unknown[] }, path)\n if (!(cStart <= from && to <= cEnd)) continue\n const spliced = tryListSplice(root, path, node, cStart, newInput, from, to, delta, this._fns, this._opts.build, !!this._opts.tolerant)\n if (spliced) return this.wrapReuse(spliced, newInput)\n }\n }\n\n return reparse()\n }\n}\n\n/**\n * The relative (parent-offset) tree backing a doc. Currently identical to the\n * public `doc.tree` (which is relative); kept as a named internal handle for the\n * reuse-metric tests, which assert on the shareable representation explicitly.\n */\nexport function relTreeOf<N extends NodeLike>(doc: ParseDoc<N>): N | null {\n return doc.tree\n}\n\n/**\n * The grammar trivia rule to skip when computing `unconsumedFrom`: an explicit\n * `trivia` option wins; otherwise the root entry's ambient `grammarTrivia` (the\n * same source `run()` derives it from). A bare-function registry carries no meta,\n * so trailing trivia can't be inferred and the parse must reach the exact end.\n */\nfunction triviaOf<N extends NodeLike>(\n entry: RuleFn<N> | Combinator<N> | undefined,\n opts: ParseDocOptions<N>,\n): Combinator<unknown> | undefined {\n if (opts.trivia) return opts.trivia\n // Only an interpreter Combinator carries `_meta`; a bare parse function or a\n // compiled-grammar object (which bakes its ambient trivia into codegen) does\n // not, so there's nothing to derive and trailing trivia isn't skipped.\n if (entry === undefined || typeof entry === 'function') return undefined\n const meta = (entry as { _meta?: { grammarTrivia?: Combinator<unknown> } })._meta\n return meta ? meta.grammarTrivia : undefined\n}\n\n/**\n * First unconsumed non-trivia offset after a parse that ended at `end` (trailing\n * trivia skipped when a trivia rule is available), or `null` when the whole input\n * was consumed \u2014 byte-for-byte the computation in `run()` (run.ts:117-129).\n */\nfunction unconsumedAfter(end: number, input: string, trivia: Combinator<unknown> | undefined): number | null {\n let pos = end\n if (trivia && pos < input.length) {\n const t = trivia.parse(input, pos, { trackLines: false })\n if (t.ok && t.span.end > pos) pos = t.span.end\n }\n return pos < input.length ? pos : null\n}\n\n/**\n * Collect embedded `parseError` recovery nodes from a PARENT-RELATIVE tree,\n * projecting each to an ABSOLUTE span (root base 0) \u2014 the flat mirror of the\n * errors that ride the tree, recomputed for a reuse-path (grafted/spliced) result\n * so `doc.errors` stays consistent with the tree across incremental edits.\n */\nfunction collectEmbeddedErrors(node: unknown, base: number, out: ParseError[]): ParseError[] {\n const c = node as { _tag?: string; span?: { start: number; end: number }; expected?: string[]; children?: readonly unknown[] }\n if (!c || typeof c !== 'object' || !c.span) return out\n const start = base + c.span.start\n if (c._tag === 'parseError') {\n out.push({ _tag: 'parseError', span: { start, end: base + c.span.end }, expected: c.expected ?? [] })\n return out\n }\n if (Array.isArray(c.children)) for (const k of c.children) collectEmbeddedErrors(k, start, out)\n return out\n}\n\n/**\n * Parse `input` from `rootRule` and wrap the result in a ParseDoc that can\n * be incrementally re-parsed via `.edit()`.\n */\nexport function parseDoc<N extends NodeLike>(\n registry: Registry<N>,\n rootRule: string,\n input: string,\n opts: ParseDocOptions<N> = {},\n): ParseDoc<N> {\n const entry = registry[rootRule]\n if (!entry) throw new Error(`No rule '${rootRule}' in registry`)\n // Once per document, not once per node: the fused artifact records the host mode it\n // was lowered for, so a mismatched driver is an error here rather than a wrong-shaped\n // tree later. Every reparse this doc performs reuses the same registry and host.\n assertHostModeCompatible(fusedHostModeOf(registry), opts.build, fusedHostElidedOf(registry))\n const ctx: ParseContext = mkCtx(opts.state, opts.build, !!opts.tolerant)\n const r: ParseResult<N> = asRuleFn(entry)(input, 0, ctx)\n if (r.ok) {\n // A fresh parse is ABSOLUTE; the relative form is materialized lazily on the\n // first edit/spanAt, so a parse that's never edited pays no conversion. The\n // recovery errors the tolerant parse collected into ctx._errors are surfaced\n // flat too (like run()), and unconsumedFrom reports any trailing junk.\n const errors = ctx._errors ? [...ctx._errors] : []\n const unconsumedFrom = unconsumedAfter(r.span.end, input, triviaOf(entry, opts))\n return new ParseDocImpl(registry, rootRule, opts, { abs: r.value }, errors, unconsumedFrom, input)\n }\n // Hard (non-recovered) failure: the single top-level failure, as a parseError.\n const fail: ParseError = { _tag: 'parseError', span: r.span, expected: r.expected }\n return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [fail], null, input)\n}\n", "/**\n * `parseman/language-service` \u2014 the external, grammar-agnostic IDE layer.\n *\n * A grammar is pure structure (it carries no recovery/completion/lint config).\n * `languageService(grammar, config)` layers editor behaviour ON TOP, keyed by rule\n * name = node type, without ever modifying the grammar:\n *\n * - `parse(src, { tolerant })` \u2192 CST + `ParseError[]` (recovers on the compiled\n * fast path when `grammar` is a `compile(g, { recovery: true })` grammar).\n * - `diagnostics(src)` \u2192 structural parse errors + your per-node-type lint rules.\n * - `completionsAt(src, offset)` \u2192 the grammar's expected-token set at the cursor,\n * mapped through your per-rule semantic completion handlers.\n *\n * The grammar can be the interpreter combinator OR a compiled grammar \u2014 the domain\n * knowledge (what to suggest after `color:`, which nodes to lint) lives here, in the\n * consumer's config, never in the grammar.\n */\nimport type { Combinator, ParseContext, ParseResult, Span, ParseError } from '../types.ts'\nimport { run } from '../functional/run.ts'\nimport { completionsAt as coreCompletionsAt } from '../combinators/completions.ts'\nimport { cstBuildHost } from '../compiler/linker.ts'\nimport type { Walkable } from '../cst/walk.ts'\nimport { parseDoc, type Registry } from '../functional/doc.ts'\nimport { absolutizeCST } from '../cst/relative-spans.ts'\nimport type { NodeLike } from '../cst/types.ts'\n\nexport type Severity = 'error' | 'warning' | 'info'\nexport type Diagnostic = { severity: Severity; message: string; span: Span }\nexport type CompletionItem = { label: string; detail?: string }\n\n/** A CST node/leaf as a diagnostics handler sees it: the walk shape plus span/value. */\nexport type LsNode = Walkable & { readonly span: Span; readonly value?: string }\n\n/** Context handed to a completion handler: which rule the cursor sits in and the\n * grammar's raw expected-token labels there. */\nexport type CompletionContext = { rule: string | null; expected: string[]; offset: number }\n\nexport type LanguageServiceConfig = {\n /** Lint/diagnostic rules keyed by node type (rule name). Return zero or more\n * Diagnostics for each matching node. */\n diagnostics?: Record<string, (node: LsNode) => Diagnostic | Diagnostic[] | null | undefined | void>\n /** Semantic completion handlers keyed by the rule the cursor is in. Turn the\n * grammar's structural expectation into domain suggestions. */\n complete?: Record<string, (ctx: CompletionContext) => CompletionItem[]>\n}\n\n/** Grammar the service drives: an interpreter combinator or a compiled entry fn. */\ntype Grammar = Combinator<unknown> | ((input: string, pos: number, ctx: ParseContext) => ParseResult<unknown>)\n\n/**\n * What you hand `languageService`:\n * - a single entry (combinator or `compile()`d fn) \u2014 stateless methods only, or\n * - `{ rules, root }` (a `rules()` registry + its root rule name) \u2014 also unlocks\n * `openDocument`, the live incremental editor document.\n */\nexport type GrammarInput = Grammar | { rules: Registry<NodeLike>; root: string }\n\n/**\n * A live incremental document \u2014 the fused editor backend. Holds a tolerant\n * `parseDoc` whose tree survives broken input (recovery) and re-parses only the\n * edited span on `edit()`. Diagnostics walk the maintained tree (recovered errors\n * ride inside it as `parseError` nodes), so they stay complete across edits without\n * re-parsing the whole document.\n */\nexport type LsDocument = {\n /** The current CST with ABSOLUTE spans (recovered errors embedded as parseError nodes). */\n readonly tree: LsNode | null\n /** Apply an editor change (byte offsets in the CURRENT text); returns the next document. */\n edit(from: number, to: number, replacement: string): LsDocument\n /** Structural parse errors (from the tree) + your per-node-type lint rules. */\n diagnostics(): Diagnostic[]\n /** Completions at a cursor offset in the current text. */\n completionsAt(offset: number): CompletionItem[]\n}\n\nexport type LanguageService = {\n parse(src: string, opts?: { tolerant?: boolean }): ParseResult<unknown> & { errors: ParseError[] }\n diagnostics(src: string): Diagnostic[]\n completionsAt(src: string, offset: number): CompletionItem[]\n /** Open a live incremental document. Requires `languageService({ rules, root }, \u2026)`. */\n openDocument(src: string): LsDocument\n}\n\nconst errorMessage = (expected: readonly string[]): string =>\n expected.length ? `Unexpected input; expected ${expected.join(' or ')}` : 'Unexpected input'\n\nfunction visitTree(root: LsNode, enter: (node: LsNode) => void): void {\n const go = (node: LsNode): void => {\n enter(node)\n const children = node.children as ReadonlyArray<LsNode> | undefined\n if (Array.isArray(children)) for (const child of children) go(child)\n }\n go(root)\n}\n\n/**\n * Merge every syntax-error source into one deduped diagnostic list:\n * (a) recovered `parseError` nodes embedded in the tree + per-node lint rules\n * (via {@link diagnoseTree}),\n * (b) residual FLAT recovery/failure errors from the run/doc (present when the\n * parse produced no tree, or as the flat mirror of embedded errors), and\n * (c) a sentinel over any trailing `unconsumedFrom..end` junk the grammar left.\n * Deduped by span+message so an embedded error and its flat twin collapse to one,\n * while distinct diagnostics at the same offset survive. With the reporting fixes\n * (expect() embeds, speculative rollback is clean) (a) is already complete; the\n * merge is what guarantees missing-closers and trailing junk are never invisible.\n */\nfunction mergeDiagnostics(\n tree: LsNode | null,\n flat: ReadonlyArray<{ expected: readonly string[]; span: Span }>,\n unconsumedFrom: number | null,\n inputLength: number,\n config: LanguageServiceConfig,\n): Diagnostic[] {\n const out: Diagnostic[] = []\n const seen = new Set<string>()\n const add = (d: Diagnostic): void => {\n const key = `${d.severity}|${d.span.start}|${d.span.end}|${d.message}`\n if (seen.has(key)) return\n seen.add(key)\n out.push(d)\n }\n if (tree) for (const d of diagnoseTree(tree, config)) add(d)\n for (const e of flat) add({ severity: 'error', message: errorMessage(e.expected), span: e.span })\n if (unconsumedFrom !== null) {\n add({ severity: 'error', message: 'Unexpected input', span: { start: unconsumedFrom, end: inputLength } })\n }\n return out\n}\n\n/** One walk over a CST: recovered `parseError` nodes \u2192 error diagnostics, and each\n * node dispatched to its `config.diagnostics[type]` lint handler. Errors live in the\n * tree, so this is the single source for both \u2014 no separate flat channel to merge. */\nfunction diagnoseTree(root: LsNode, config: LanguageServiceConfig): Diagnostic[] {\n const out: Diagnostic[] = []\n const rules = config.diagnostics\n visitTree(root, node => {\n if (node._tag === 'parseError') {\n out.push({ severity: 'error', message: errorMessage((node as { expected?: string[] }).expected ?? []), span: node.span })\n return\n }\n const h = rules && node.type !== undefined ? rules[node.type] : undefined\n if (h) {\n const d = h(node)\n if (d) out.push(...(Array.isArray(d) ? d : [d]))\n }\n })\n return out\n}\n\nexport function languageService(grammar: GrammarInput, config: LanguageServiceConfig = {}): LanguageService {\n const build = cstBuildHost()\n const asReg = typeof grammar === 'object' && grammar !== null && 'rules' in grammar\n const registry = asReg ? grammar.rules : undefined\n const root = asReg ? grammar.root : undefined\n const entry: Grammar = asReg ? (registry![root!] as Grammar) : grammar\n\n // The grammar's ambient trivia (interpreter combinators carry it in _meta) \u2014\n // handed to run() so it skips a trailing whitespace/comment tail before\n // reporting unconsumedFrom, matching parseDoc/run semantics.\n const trivia = typeof entry !== 'function' ? (entry as Combinator<unknown>)._meta.grammarTrivia : undefined\n const parseTolerant = (src: string) =>\n run(entry as never, src, { tolerant: true, build, ...(trivia !== undefined ? { trivia } : {}) })\n\n const complete = (input: string, offset: number, treeForCursor: unknown): CompletionItem[] => {\n const expected = coreCompletionsAt(entry as never, input, offset, { tolerant: true })\n const rule = ruleAtCursor(treeForCursor, offset)\n const handler = rule !== null ? config.complete?.[rule] : undefined\n if (handler) return handler({ rule, expected, offset })\n return expected.map(label => ({ label }))\n }\n\n return {\n parse(src, opts) {\n return run(entry as never, src, { tolerant: opts?.tolerant ?? true, build }) as ParseResult<unknown> & { errors: ParseError[] }\n },\n\n diagnostics(src) {\n const r = parseTolerant(src)\n const tree = r.value && typeof r.value === 'object' ? (r.value as LsNode) : null\n return mergeDiagnostics(tree, r.errors, r.unconsumedFrom, src.length, config)\n },\n\n completionsAt(src, offset) {\n return complete(src, offset, parseTolerant(src.slice(0, offset)).value)\n },\n\n openDocument(src) {\n if (!registry || root === undefined) {\n throw new Error('openDocument requires languageService({ rules, root }, config) \u2014 a rules() registry, not a single entry')\n }\n const host = cstBuildHost()\n const make = (doc: ReturnType<typeof parseDoc<NodeLike>>): LsDocument => {\n // Absolutize once per document version (memoized in this closure).\n const abs = doc.tree ? (absolutizeCST(doc.tree as never) as unknown as LsNode) : null\n return {\n get tree() { return abs },\n edit: (from, to, replacement) => make(doc.edit(from, to, replacement)),\n // Same unified merge as the stateless path: embedded parseError nodes +\n // lint, the flat recovery errors the doc collected, and a trailing-junk\n // sentinel \u2014 so a live editor document surfaces missing-closers and junk.\n diagnostics: () => mergeDiagnostics(abs, doc.errors, doc.unconsumedFrom, doc.input.length, config),\n completionsAt: (offset) => complete(doc.input, offset, abs),\n }\n }\n return make(parseDoc<NodeLike>(registry, root, src, { tolerant: true, structuralReuse: true, build: host }))\n },\n }\n}\n\n/** Deepest node whose span brackets the cursor \u2014 \"which rule is the cursor in\".\n * The grammar carries no cursor knowledge, so we reconstruct it from the CST. */\nfunction ruleAtCursor(root: unknown, offset: number): string | null {\n if (!root || typeof root !== 'object') return null\n let best: string | null = null\n visitTree(root as LsNode, node => {\n const s = node.span\n if (s && s.start <= offset && offset <= s.end && node.type !== undefined) best = node.type\n })\n return best\n}\n"],
5
- "mappings": ";AAgCO,SAAS,gBAAgB,OAAkB,OAAe,OAAe,KAAmB;AACjG,QAAM,aAAa,MAAM;AACzB,WAAS,IAAI,OAAO,IAAI,KAAK,KAAK;AAChC,QAAI,MAAM,WAAW,CAAC,MAAM,GAAI,YAAW,KAAK,IAAI,CAAC;AAAA,EACvD;AACF;AAOO,SAAS,2BACd,KACA,OACA,YACA,KACM;AACN,MAAI,CAAC,IAAI,WAAY;AACrB,QAAM,OAAO,IAAI,kBAAkB;AACnC,MAAI,OAAO,KAAM;AACjB,QAAM,QAAQ,IAAI,eAAe,IAAI,cAAc,EAAE,YAAY,IAAI,YAAY,IAAI;AACrF,MAAI,CAAC,MAAO;AACZ,kBAAgB,OAAO,OAAO,MAAM,GAAG;AACvC,MAAI,iBAAiB;AACvB;AAeO,SAAS,gBACd,OACA,QAC+B;AAC/B,QAAM,EAAE,WAAW,IAAI;AACvB,MAAI,KAAK;AACT,MAAI,KAAK,WAAW,SAAS;AAC7B,SAAO,KAAK,IAAI;AACd,UAAM,MAAO,KAAK,KAAK,KAAM;AAC7B,QAAI,WAAW,GAAG,KAAM,OAAQ,MAAK;AAAA,QAChC,MAAK,MAAM;AAAA,EAClB;AACA,SAAO,EAAE,MAAM,KAAK,GAAG,KAAK,SAAS,WAAW,EAAE,IAAK,EAAE;AAC3D;AAGO,SAAS,aAAa,MAAY,OAAwB;AAC/D,QAAM,IAAI,gBAAgB,OAAO,KAAK,KAAK;AAC3C,QAAM,IAAI,gBAAgB,OAAO,KAAK,GAAG;AACzC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,WAAW,EAAE;AAAA,IACb,aAAa,EAAE;AAAA,IACf,SAAS,EAAE;AAAA,IACX,WAAW,EAAE;AAAA,EACf;AACF;AAEO,SAAS,4BACd,MACA,KACM;AACN,MAAI,CAAC,IAAI,WAAY,QAAO;AAC5B,QAAM,QAAQ,IAAI,eAAe,IAAI,cAAc,EAAE,YAAY,IAAI,YAAY,IAAI;AACrF,SAAO,QAAQ,aAAa,MAAM,KAAK,IAAI;AAC7C;;;AC5FO,SAAS,iBAAiB,KAA4B;AAC3D,SAAO,IAAI,YAAY,UAAa,IAAI,eAAe;AACzD;AAmEO,SAAS,aAAa,KAAmB,OAAgB,UAAyB;AACvF,QAAM,IAAI,IAAI;AACd,MAAI,GAAG;AACL,QAAI,EAAE,GAAI,GAAE,GAAG,KAAK,KAAK;AAAA,aAChB,EAAE,WAAW,QAAW;AAAE,QAAE,KAAK,CAAC,EAAE,QAAQ,KAAK;AAAG,QAAE,SAAS;AAAA,IAAU,MAC7E,GAAE,SAAS;AAEhB,QAAI,EAAE,IAAK,GAAE,IAAI,KAAK,QAAQ;AAAA,aACrB,EAAE,cAAc,QAAW;AAAE,QAAE,MAAM,CAAC,EAAE,WAAW,QAAQ;AAAG,QAAE,YAAY;AAAA,IAAU,MAC1F,GAAE,YAAY;AACnB;AAAA,EACF;AACA,MAAI,IAAI,aAAc,KAAI,aAAa,KAAK,KAAK;AAAA,WACxC,IAAI,WAAY,KAAI,WAAW,KAAK,KAAK;AAClD,MAAI,IAAI,gBAAiB,KAAI,gBAAgB,KAAK,QAAQ;AAC5D;;;AC9EA,SAAS,QAAQ,UAA+B,OAAe,KAAa,KAA4B;AACtG,QAAM,IAAI;AAAA,IACR,QAAQ,IAAI;AAAA,IAAQ,SAAS,IAAI;AAAA,IACjC,YAAY,IAAI;AAAA,IAAY,cAAc,IAAI;AAAA,IAAc,iBAAiB,IAAI;AAAA,IACjF,eAAe,IAAI;AAAA,IAAe,YAAY,IAAI;AAAA,IAAY,gBAAgB,IAAI;AAAA,IAAgB,SAAS,IAAI;AAAA,IAAS,SAAS,IAAI;AAAA,IACrI,WAAW,IAAI;AAAA,IAAW,OAAO,IAAI;AAAA,EACvC;AACA,MAAI,SAAS;AAAW,MAAI,UAAU;AACtC,MAAI,aAAa;AAAW,MAAI,eAAe;AAAW,MAAI,kBAAkB;AAChF,MAAI,gBAAgB;AAAW,MAAI,aAAa;AAAW,MAAI,iBAAiB;AAAW,MAAI,UAAU;AAAW,MAAI,UAAU;AAKlI,MAAI,YAAY;AAAW,MAAI,QAAQ;AACvC,MAAI;AACF,WAAO,SAAS,MAAM,OAAO,KAAK,GAAG,EAAE;AAAA,EACzC,UAAE;AACA,QAAI,SAAS,EAAE;AAAQ,QAAI,UAAU,EAAE;AACvC,QAAI,aAAa,EAAE;AAAY,QAAI,eAAe,EAAE;AAAc,QAAI,kBAAkB,EAAE;AAC1F,QAAI,gBAAgB,EAAE;AAAe,QAAI,aAAa,EAAE;AAAY,QAAI,iBAAiB,EAAE;AAAgB,QAAI,UAAU,EAAE;AAAS,QAAI,UAAU,EAAE;AACpJ,QAAI,YAAY,EAAE;AAAW,QAAI,QAAQ,EAAE;AAAA,EAC7C;AACF;AAOO,SAAS,WACd,GACA,GACqB;AACrB,MAAI,MAAM,OAAW,QAAO;AAC5B,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,GAAG,iBAAiB,OAAO,UAAU,MAAM;AAAA,IAC5E,MAAM,EAAE,KAAK,UAAU;AAAA,IACvB,MAAM,OAAe,KAAa,KAAyC;AACzE,YAAM,KAAK,EAAE,MAAM,OAAO,KAAK,GAAG;AAClC,UAAI,GAAG,GAAI,QAAO;AAClB,aAAO,EAAE,MAAM,OAAO,KAAK,GAAG;AAAA,IAChC;AAAA,EACF;AACF;AASO,SAAS,iBAAiB,IAAuC;AACtE,MAAI,GAAG,SAAS,YAAY,GAAG,OAAO,WAAW,EAAG,QAAO;AAC3D,QAAM,SAAS,GAAG;AAClB,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,EAAE,UAAU,IAAI,iBAAiB,OAAO,UAAU,MAAM;AAAA,IAC/D,MAAM,EAAE,KAAK,UAAU;AAAA,IACvB,MAAM,OAAe,KAAgC;AACnD,YAAM,OAAO,MAAM,MAAM,SAAS,MAAM,YAAY,GAAG,IAAI;AAC3D,UAAI,SAAS,QAAW;AACtB,mBAAW,KAAK,QAAQ;AACtB,cAAI,QAAQ,EAAE,MAAM,QAAQ,EAAE,GAAI,QAAO,EAAE,IAAI,MAAM,OAAO,MAAM,MAAM,EAAE,OAAO,KAAK,KAAK,IAAI,EAAE;AAAA,QACnG;AAAA,MACF;AACA,aAAO,EAAE,IAAI,OAAO,UAAU,CAAC,GAAG,MAAM,EAAE,OAAO,KAAK,KAAK,IAAI,EAAE;AAAA,IACnE;AAAA,EACF;AACF;AAcO,SAAS,YACd,OACA,MACA,KACA,MACA,UACoC;AACpC,MAAI,UAAU;AACd,SAAO,UAAU,MAAM,UAAU,CAAC,QAAQ,MAAM,OAAO,SAAS,GAAG,EAAG;AACtE,QAAM,OAAO,EAAE,OAAO,MAAM,KAAK,QAAQ;AACzC,MAAI,IAAI,WAAY,4BAA2B,KAAK,OAAO,MAAM,OAAO;AACxE,QAAM,QAAoB,EAAE,MAAM,cAAc,MAAM,IAAI,aAAa,4BAA4B,MAAM,GAAG,IAAI,MAAM,SAAS;AAC/H,MAAI,SAAS,KAAK,KAAK;AACvB,SAAO,EAAE,OAAO,KAAK,QAAQ;AAC/B;AAOO,SAAS,UACd,UACA,OACA,KACA,KACS;AACT,SAAO,QAAQ,UAAU,OAAO,KAAK,GAAG;AAC1C;AAUO,SAAS,aAAa,KAAmB,OAAyB;AACvE,MAAI,iBAAiB,GAAG,EAAG,cAAa,KAAK,OAAO,KAAK;AAC3D;AAQO,IAAM,MAAuB,EAAE,MAAM,aAAa,IAAI,WAAW,IAAI,YAAY,UAAU,kBAAkB,SAAS,aAAa;;;AC9F1I,IAAM,gBAAmC,OAAO,OAAO,CAAC,CAAC;AACzD,IAAM,aAAuC,OAAO,OAAO,CAAC,CAAC;AAEtD,IAAM,uBAAuB;AAoE7B,SAAS,qBACd,KACA,QACiB;AACjB,QAAM,SAAS,KAAK,MAAM,IAAI,SAAS,oBAAoB;AAC3D,QAAM,eAAe,CAAC,MAAc,IAAI;AACxC,QAAM,kBAAkB,CAAC,MAAc,IAAI,aAAa,CAAC,CAAC;AAC1D,QAAM,gBAAgB,CAAC,MAAc,IAAI,aAAa,CAAC,IAAI,CAAC;AAC5D,QAAM,cAAc,CAAC,MAAc,IAAI,aAAa,CAAC,IAAI,CAAC;AAC1D,QAAM,YAAY,CAAC,MAAc,IAAI,aAAa,CAAC,IAAI,CAAC;AACxD,QAAM,aAAa,CAAC,MAAc,IAAI,aAAa,CAAC,IAAI,CAAC;AAEzD,QAAM,UAA6B;AAAA,IACjC;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,KAAK;AAAA,IACL,aAAa,MAAM;AAAA,IACnB,WAAW;AAAA,IACX,KAAK,GAAG;AACN,aAAO,OAAO,WAAW,CAAC,CAAC;AAAA,IAC7B;AAAA,IACA,KAAK,GAAG,OAAO;AACb,aAAO,MAAM,MAAM,YAAY,CAAC,GAAG,UAAU,CAAC,CAAC;AAAA,IACjD;AAAA,EACF;AAEA,QAAM,aAAa,CAAC,QAAgB,OAAsC;AACxE,QAAI,KAAK;AACT,QAAI,KAAK;AACT,WAAO,KAAK,IAAI;AACd,YAAM,MAAO,KAAK,OAAQ;AAC1B,UAAI,GAAG,GAAG,IAAI,OAAQ,MAAK,MAAM;AAAA,UAC5B,MAAK;AAAA,IACZ;AACA,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,CAAC,QAAgB,cAAgE;AACjG,UAAM,OAAO,cAAc,WAAW,gBAAgB;AACtD,QAAI,QAAQ,WAAW,QAAQ,IAAI;AACnC,QAAI,UAAU,UAAU,KAAK,KAAK,MAAM,OAAQ,QAAO;AACvD,UAAM,QAAQ,gBAAgB,KAAK;AACnC,UAAM,MAAM,cAAc,KAAK;AAC/B,WAAO,QAAQ,KAAK,gBAAgB,QAAQ,CAAC,MAAM,SAAS,cAAc,QAAQ,CAAC,MAAM,IAAK;AAC9F,QAAI,OAAO,QAAQ;AACnB,WAAO,OAAO,UAAU,gBAAgB,IAAI,MAAM,SAAS,cAAc,IAAI,MAAM,IAAK;AACxF,WAAO,CAAC,OAAO,IAAI;AAAA,EACrB;AAEA,QAAM,UAAU,CAAC,OAAe,SAAoC;AAClE,UAAM,MAAgB,CAAC;AACvB,aAAS,IAAI,OAAO,IAAI,MAAM,IAAK,KAAI,KAAK,CAAC;AAC7C,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,CAAC,OAAe,SAAgC;AAC9D,UAAM,QAAQ,gBAAgB,KAAK;AACnC,UAAM,MAAM,cAAc,KAAK;AAC/B,QAAI;AACJ,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,IAAI,eAAe;AACjB,eAAO,iBAAiB,QAAQ,OAAO,IAAI;AAAA,MAC7C;AAAA,MACA,QAAQ,MAAM;AACZ,iBAAS,IAAI,OAAO,IAAI,MAAM,KAAK;AACjC,cAAI,OAAO,WAAW,CAAC,CAAC,MAAM,KAAM,QAAO;AAAA,QAC7C;AACA,eAAO;AAAA,MACT;AAAA,MACA,KAAK,OAAO;AACV,eAAO,MAAM,MAAM,OAAO,GAAG;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAEA,MAAI;AACJ,QAAM,UAAU,MAAM;AACpB,QAAI,SAAS,OAAW,QAAO;AAC/B,UAAM,SAAS,oBAAI,IAAsB;AACzC,UAAM,QAAQ,oBAAI,IAAsB;AACxC,UAAM,aAAa,oBAAI,IAA2B;AAClD,UAAM,YAAY,oBAAI,IAA2B;AACjD,UAAM,OAAwB,CAAC;AAC/B,aAAS,QAAQ,GAAG,QAAQ,UAAS;AACnC,UAAI,OAAO,QAAQ;AACnB,aAAO,OAAO,UAAU,gBAAgB,IAAI,MAAM,gBAAgB,KAAK,KAAK,cAAc,IAAI,MAAM,cAAc,KAAK,EAAG;AAC1H,YAAM,MAAM,QAAQ,OAAO,IAAI;AAC/B,WAAK,KAAK,GAAG;AACb,gBAAU,IAAI,IAAI,OAAO,GAAG;AAC5B,iBAAW,IAAI,IAAI,KAAK,GAAG;AAC3B,YAAM,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,YAAY,CAAC;AAC1C,aAAO,IAAI,IAAI,KAAK,CAAC,GAAG,IAAI,YAAY,CAAC;AACzC,cAAQ;AAAA,IACV;AACA,WAAO,EAAE,QAAQ,OAAO,YAAY,WAAW,KAAK;AACpD,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,IAAI,SAAS;AAAE,aAAO,QAAQ,EAAE;AAAA,IAAO;AAAA,IACvC,IAAI,QAAQ;AAAE,aAAO,QAAQ,EAAE;AAAA,IAAM;AAAA,IACrC,mBAAmB,QAAQ;AACzB,YAAM,OAAO,UAAU,QAAQ,QAAQ;AACvC,aAAO,OAAO,QAAQ,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI;AAAA,IAC5C;AAAA,IACA,kBAAkB,QAAQ;AACxB,YAAM,OAAO,UAAU,QAAQ,OAAO;AACtC,aAAO,OAAO,QAAQ,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI;AAAA,IAC5C;AAAA,IACA,UAAU,QAAQ;AAChB,YAAM,OAAO,UAAU,QAAQ,QAAQ;AACvC,aAAO,OAAO,QAAQ,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI;AAAA,IAC5C;AAAA,IACA,SAAS,QAAQ;AACf,YAAM,OAAO,UAAU,QAAQ,OAAO;AACtC,aAAO,OAAO,QAAQ,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI;AAAA,IAC5C;AAAA,IACA,OAAO;AACL,aAAO,QAAQ,EAAE;AAAA,IACnB;AAAA,IACA,aAAa,MAAM;AACjB,YAAM,QAAQ,OAAO,SAAS,WAAW,CAAC,IAAI,IAAI;AAClD,YAAMA,WAA2B,CAAC;AAIlC,eAAS,QAAQ,GAAG,QAAQ,UAAS;AACnC,YAAI,OAAO,QAAQ;AACnB,eAAO,OAAO,UACT,gBAAgB,IAAI,MAAM,gBAAgB,KAAK,KAC/C,cAAc,IAAI,MAAM,cAAc,KAAK,EAAG;AACnD,YAAI,UAAU;AACd,iBAAS,IAAI,OAAO,IAAI,QAAQ,CAAC,SAAS,KAAK;AAC7C,gBAAMC,SAAQ,OAAO,WAAW,CAAC,CAAC;AAClC,qBAAWC,SAAQ,OAAO;AACxB,gBAAID,WAAUC,OAAM;AAClB,wBAAU;AACV;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,YAAI,QAAS,CAAAF,SAAQ,KAAK,QAAQ,OAAO,IAAI,CAAC;AAC9C,gBAAQ;AAAA,MACV;AACA,aAAOA,SAAQ,WAAW,IAAI,aAAaA;AAAA,IAC7C;AAAA,EACF;AACF;;;AChQO,IAAM,kBAAkB,uBAAO,IAAI,wBAAwB;AAG3D,IAAM,oBAAoB,uBAAO,IAAI,0BAA0B;AAc/D,SAAS,yBAAyB,MAAgB,OAAgB,mBAAmB,MAAY;AACtG,QAAM,YAAa,OAAqD,uBAAuB;AAK/F,MAAI,SAAS,SAAS,aAAa,kBAAkB;AACnD,UAAM,IAAI;AAAA,MACR;AAAA,IAMF;AAAA,EACF;AACA,MAAI,SAAS,SAAS,CAAC,WAAW;AAChC,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACF;;;AC6FA,IAAM,iBAAiB;AAEvB,IAAM,4BAAsE;AAAA,EAC1E;AAAA,IACE;AAAA,IACA,8NAEmC,cAAc;AAAA,EAEnD;AAAA,EACA;AAAA,IACE;AAAA,IACA,0MAEO,cAAc;AAAA,EAGvB;AAAA,EACA;AAAA,IACE;AAAA,IACA,mPAEiC,cAAc;AAAA,EAEjD;AACF;AAMA,SAAS,mBAAmB,QAA8B;AACxD,aAAW,CAAC,MAAM,MAAM,KAAK,2BAA2B;AACtD,WAAO,eAAe,QAAQ,MAAM;AAAA,MAClC,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,MAAa;AACX,cAAM,IAAI;AAAA,UACR,aAAa,IAAI,mEAAmE,MAAM;AAAA,QAC5F;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,IAAM,SAAS,CAAC,GAAa,OAAe,KAAa,QACvD,OAAO,MAAM,aAAa,EAAE,OAAO,KAAK,GAAG,IAAI,EAAE,MAAM,OAAO,KAAK,GAAG;AAWxE,SAAS,6BAA6B,GAAwD;AAC5F,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,OAAO,MAAM,WAAY,QAAQ,EAAmB,OAAO;AAC/D,QAAM,gBAAiB,EAAE,MAAkD;AAC3E,SAAO,eAAe,MAAM,oBACvB,EAAE,MAAM,qBACP,EAAE,KAAK,QAAQ,YAAY,EAAE,KAAK,cAAc,MAAM,mBAAmB;AACjF;AAEA,SAAS,iCAAiC,GAAkC;AAC1E,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,OAAO,MAAM,WAAY,QAAQ,EAAmB,OAAO,yBAAyB;AACxF,QAAM,gBAAiB,EAAE,MAAkD;AAC3E,SAAO,eAAe,MAAM,yBAAyB,QAAQ,EAAE,MAAM,yBAAyB;AAChG;AAEA,SAAS,2BAA2B,QAA6D;AAC/F,QAAM,QAAgC,uBAAO,OAAO,IAAI;AACxD,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,QAAI,MAAM,OAAO,CAAC,CAAE,MAAM,OAAW,OAAM,OAAO,CAAC,CAAE,IAAI;AAAA,EAC3D;AACA,SAAO;AACT;AAGA,SAAS,QAAQ,OAAiB,OAAe,SAAqB,OAAsB,cAAwC;AAClI,MAAI,OAAO,UAAU,cAAc,OAAQ,OAA2C,UAAU,YAAY;AAC1G,UAAM,IAAI;AAAA,MACR,8BAA8B,UAAU,OAAO,SAAS,OAAO,KAAK;AAAA,IACtE;AAAA,EACF;AACA,QAAM,sBAAsB,QAAQ,YAAY;AAChD,QAAM,oBAAoB,wBAAwB,UAAa,oBAAoB,SAAS;AAC5F,QAAM,kBAAkB,oBAAoB,CAAC,IAAgB;AAC7D,QAAM,yBAAyB,oBAC3B,2BAA2B,mBAAmB,IAC9C;AACJ,QAAM,SAAuB,CAAC;AAC9B,QAAM,UAAU,iBAAiB,UAAU,SACvC,SACA,EAAE,OAAO,OAAO,GAAG,YAAY,GAAG,UAAU,GAAG,aAAa,GAAG,YAAY,GAAG,WAAW,EAAE;AAI/F,QAAM,kBAAkB,UAAU,gBAAgB,UAAU;AAK5D,QAAM,gBAAgB,OAAO,UAAU,aAAa,MAAM,MAAM,gBAAgB;AAChF,QAAM,kBAAkB,OAAO,UAAU,aAAa,MAAM,MAAM,kBAAkB;AACpF,QAAM,mBAAmB,6BAA6B,KAAK,KAAK,6BAA6B,QAAQ,MAAM;AAC3G,MAAI,qBAAqB,qBAAqB,QAAW;AACvD,UAAM,IAAI,UAAU,2DAA2D;AAAA,EACjF;AACA,MAAI,mBAAmB;AACrB,eAAWG,UAAS,qBAAqB;AACvC,UAAI,CAAC,iBAAkB,SAASA,MAAK,GAAG;AACtC,cAAM,IAAI,UAAU,0DAA0D,KAAK,UAAUA,MAAK,CAAC,GAAG;AAAA,MACxG;AAAA,IACF;AAAA,EACF;AACA,MAAI,qBACC,CAAC,iCAAiC,KAAK,KACvC,CAAC,iCAAiC,QAAQ,MAAM,GAAG;AACtD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAiBA,QAAM,UAAU;AAChB;AAAA,IACE,OAAO,UAAU,aACX,QAAQ,eAAe,KAA8B,QACtD,MAAM,MAAM,mBAAmB;AAAA,IACpC,QAAQ;AAAA,IACR,OAAO,UAAU,cAAc,QAAQ,iBAAiB,MAAM;AAAA,EAChE;AACA,QAAM,MAAoB;AAAA,IACxB,YAAY;AAAA,IACZ,GAAI,kBACA,EAAE,YAAY,QAAS,IACvB;AAAA,MACA,GAAI,oBACA;AAAA,QACE,gBAAgB;AAAA,QAChB,sBAAsB;AAAA,QACtB,yBAAyB;AAAA,MAC3B,IACA,CAAC;AAAA,MACL,SAAS;AAAA,MACT,GAAI,YAAY,SAAY,CAAC,IAAI,EAAE,YAAY,QAAQ;AAAA,IACzD;AAAA,IACF,OAAO,QAAQ;AAAA,IACf,OAAO,QAAQ;AAAA,IACf,GAAI,kBAAkB,SAClB,EAAE,QAAQ,eAAe,GAAI,mBAAmB,EAAE,iBAAiB,IAAI,CAAC,EAAG,IAC3E,mBAAmB,EAAE,iBAAiB,IAAI,CAAC;AAAA,IAC/C,GAAI,oBAAoB,SAAY,EAAE,UAAU,gBAAgB,IAAI,CAAC;AAAA,IACrE,GAAI,QAAQ,sBAAsB,SAAY,EAAE,oBAAoB,QAAQ,kBAAkB,IAAI,CAAC;AAAA,IACnG,GAAI,QAAQ,WAAW,EAAE,WAAW,MAAM,MAAM,IAAI,IAAI,CAAC;AAAA,IACzD,GAAI,QAAQ,oBAAoB,SAAY,CAAC,IAAI,QAAQ;AAAA,EAC3D;AACA,QAAM,IAAI,OAAO,OAAO,OAAO,GAAG,GAAG;AAErC,MAAI,iBAAgC;AACpC,MAAI,EAAE,IAAI;AACR,QAAI,MAAM,EAAE,MAAM,OAAO;AACzB,QAAI,QAAQ,UAAU,MAAM,MAAM,QAAQ;AAExC,YAAM,IAAI,OAAO,QAAQ,QAAQ,OAAO,KAAK,EAAE,YAAY,MAAM,CAAC;AAClE,UAAI,EAAE,MAAM,EAAE,KAAK,MAAM,IAAK,OAAM,EAAE,KAAK;AAAA,IAC7C;AACA,qBAAiB,MAAM,MAAM,SAAS,MAAM;AAAA,EAC9C;AAEA,SAAO;AAAA,IACL,IAAI,EAAE;AAAA,IACN,OAAO,EAAE,KAAM,EAAyB,QAAQ;AAAA,IAChD,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,KAAK,EAAE;AAAA,IACnC,UAAU,EAAE,KAAK,CAAC,IAAM,EAA8B,YAAY,CAAC;AAAA,IACnE;AAAA,IACA,GAAI,oBAAoB,UAAa,gBAAgB,SAAS,IAC1D;AAAA,MACE,YAAY;AAAA,QACV,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,OAAO,qBAAqB,iBAAkB,mBAAoB;AAAA,MACpE;AAAA,IACF,IACA,CAAC;AAAA,IACL;AAAA,EACF;AACF;AAOO,SAAS,IAAI,OAAiB,OAAe,UAAsB,CAAC,GAAc;AACvF,MAAI,CAAC,QAAQ,QAAS,QAAO,mBAAmB,QAAQ,OAAO,OAAO,OAAO,CAAC;AAU9E,QAAM,IAAI;AAAA,IACR;AAAA,EAGF;AACF;;;AC7WO,SAAS,cACd,QACA,OACA,QACA,UAAkC,CAAC,GACzB;AACV,QAAM,QAAoD,EAAE,QAAQ,MAAM,KAAK;AAI/E,QAAM,MAAoB,EAAE,YAAY,OAAO,QAAQ,OAAO,GAAI,QAAQ,WAAW,EAAE,WAAW,MAAM,MAAM,IAAI,IAAI,CAAC,EAAG;AAC1H,QAAM,SAAS,MAAM,MAAM,GAAG,MAAM;AACpC,QAAM,SAAS,sBAAsB,SACjC,OAAO,iBAAiB,QAAQ,KAAK,CAAC,IACtC,OAAO,MAAM,QAAQ,GAAG,GAAG;AAO/B,MAAI,OAAO,GAAI,QAAO,QAAQ,WAAY,MAAM,MAAM,YAAY,CAAC,IAAK,CAAC;AAGzE,QAAM,OAAO,WAAW,MAAM,MAAM,MAAM;AAC1C,SAAO,MAAM,YAAY,CAAC;AAC5B;AAEA,SAAS,WAAW,GAAqB,GAAuC;AAC9E,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,IAAI;AAC5C;;;ACkhBA,IAAM,cAAc;AACpB,IAAM,WAAW,IAAI,OAAO,IAAI,WAAW,SAAS,WAAW,KAAK;;;ACvfpE,IAAM,OAAO,IAAI,IAAI,gBAAgB,MAAM,EAAE,CAAC;AAU9C,IAAM,wCAAwC,MAAM;AAAA,EAClD,EAAE,QAAQ,uBAAuB,EAAE;AAAA,EACnC,MAAM;AACR,EAAE,KAAK,IAAI;;;ACzBX,IAAM,aAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAEX,IAAM,uBAAuB;AAAA,EAC3B;AAAA,EACA,0EAA0E,oBAAoB;AAAA,EAC9F;AACF,EAAE,KAAK,IAAI;;;ACpEX,IAAM,MAAM,OAAO;AAEnB,IAAM,QAAQ,OAAO,SAAS,GAAG;AAGjC,IAAM,cAAc,IAAI;AAAA,EACtB,OAAO,iBAAiB,GAAG,UAAU,KAAK,eAAe,GAAG,eAAe,GAAG,eAAe,GAAG;AAClG;;;ACqDA,SAAS,qBAAqB,UAA6E;AACzG,MAAI,aAAa,KAAM,QAAO,MAAM;AACpC,MAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,UAAM,QAAQ,IAAI,IAAI,QAAQ;AAC9B,WAAO,UAAQ,MAAM,IAAI,IAAI;AAAA,EAC/B;AACA,SAAO,OAAO,aAAa,aAAa,WAAW;AACrD;AAEA,SAAS,aACP,MACA,UACA,SACA,MACA,cACA,YACA,OACA,MACS;AAGT,SAAO,SAAS,UAAa,KAAK,SAAS,IACvC,EAAE,MAAM,QAAQ,MAAM,MAAM,MAAM,EAAE,GAAG,KAAK,GAAG,OAAO,SAAS,MAAM,UAAU,CAAC,GAAG,QAAQ,EAAE,IAC7F,EAAE,MAAM,QAAQ,MAAM,MAAM,EAAE,GAAG,KAAK,GAAG,OAAO,SAAS,MAAM,UAAU,CAAC,GAAG,QAAQ,EAAE;AAC7F;AAuBO,SAAS,aACd,eACA,UACA,SACA,MACA,aACA,WACA,OACA,OACS;AACT,MAAI,OAAO,kBAAkB,UAAU;AACrC,WAAO,aAAa,eAAe,YAAY,CAAC,GAAG,SAAS,QAAQ,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,aAAa,WAAW,KAAK;AAAA,EACzH;AACA,QAAM,WAAW,qBAAqB,eAAe,QAAQ;AAC7D,QAAM,kBAAkB,eAAe,SAAS;AAChD,QAAM,OAAkB,CACtB,MACAC,WACA,QACAC,OACAC,cACAC,YACAC,QACA,SAGG,aAAa,MAAMJ,aAAY,CAAC,GAAG,QAAQC,OAAMC,cAAaC,YAAWC,QAAO,kBAAkB,OAAO,MAAS;AACtH,EAAC,KAAqD,qBAAqB;AAC5E,MAAI,SAAU,MAAK,uBAAuB;AAC1C,SAAO;AACT;AAGE,aAA0D,qBAAqB;AAmS1E,SAAS,gBAAgB,UAA4B;AAC1D,QAAM,IAAK,SAAqC,eAAe;AAC/D,SAAO,MAAM,QAAQ,QAAQ;AAC/B;AAGO,SAAS,kBAAkB,UAA2B;AAC3D,SAAQ,SAAqC,iBAAiB,MAAM;AACtE;;;ACzWO,SAAS,cAAiCC,OAAS,aAAa,GAAM;AAC3E,QAAM,OAAO,EAAE,OAAOA,MAAK,KAAK,QAAQ,YAAY,KAAKA,MAAK,KAAK,MAAM,WAAW;AACpF,MAAIA,MAAK,UAAU;AACjB,UAAM,OAAOA,MAAK,KAAK;AACvB,WAAO,EAAE,GAAGA,OAAM,MAAM,UAAUA,MAAK,SAAS,IAAI,CAAC,MAAM,cAAc,GAAc,IAAI,CAAC,EAAE;AAAA,EAChG;AACA,SAAO,EAAE,GAAGA,OAAM,KAAK;AACzB;AAGO,SAAS,cAAiCA,OAAS,aAAa,GAAM;AAC3E,QAAM,QAAQ,aAAaA,MAAK,KAAK;AACrC,QAAM,OAAO,EAAE,OAAO,KAAK,aAAaA,MAAK,KAAK,IAAI;AACtD,MAAIA,MAAK,UAAU;AACjB,WAAO,EAAE,GAAGA,OAAM,MAAM,UAAUA,MAAK,SAAS,IAAI,CAAC,MAAM,cAAc,GAAc,KAAK,CAAC,EAAE;AAAA,EACjG;AACA,SAAO,EAAE,GAAGA,OAAM,KAAK;AACzB;AAQO,SAAS,gBAAgB,MAAe,MAAyD;AACtG,MAAI,OAAO;AACX,MAAIA,QAAgB;AACpB,aAAW,OAAO,MAAM;AACtB,YAAQA,MAAK,KAAK;AAClB,UAAM,QAAQA,MAAK,WAAW,GAAG;AACjC,QAAI,CAAC,MAAO,OAAM,IAAI,WAAW,6BAA6B,GAAG,YAAY,KAAK,KAAK,GAAG,CAAC,EAAE;AAC7F,IAAAA,QAAO;AAAA,EACT;AACA,SAAO,EAAE,OAAO,OAAOA,MAAK,KAAK,OAAO,KAAK,OAAOA,MAAK,KAAK,IAAI;AACpE;;;AC9GA,SAAS,MAAM,UAAmB,OAA8B,UAAiC;AAM/F,QAAM,QAAQ,YAAY;AAC1B,SAAO,WACH,EAAE,YAAY,OAAO,OAAO,OAAO,WAAW,MAAM,MAAM,KAAK,SAAS,CAAC,EAAE,IAC3E,EAAE,YAAY,OAAO,OAAO,MAAM;AACxC;AAgBA,SAAS,SAAY,OAA6C;AAChE,SAAO,OAAO,UAAU,aAAa,QAAQ,CAAC,OAAO,KAAK,QAAQ,MAAM,MAAM,OAAO,KAAK,GAAG;AAC/F;AAGA,SAAS,MAAM,OAAuC;AACpD,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,UAAU,QAC3D,MAA8B,OAC/B;AACN;AAeA,SAAS,mBAAmB,KAA4B,QAAQ,GAAY;AAC1E,MAAI,CAAC,OAAO,QAAQ,GAAI,QAAO;AAC/B,QAAM,IAAI;AACV,UAAQ,EAAE,KAAK;AAAA,IACb,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA,IAIT,KAAK,QAAQ;AACX,YAAM,QAAQ,EAAE;AAChB,UAAI;AACJ,UAAI;AAAE,gBAAQ,OAAO,UAAU,aAAa,MAAM,IAAI;AAAA,MAAU,QAAQ;AAAE,eAAO;AAAA,MAAM;AACvF,aAAO,mBAAmB,OAAO,MAAM,QAAQ,CAAC;AAAA,IAClD;AAAA;AAAA,IAEA,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,mBAAmB,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;AAAA,IACtD,KAAK;AACH,aAAO,mBAAmB,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;AAAA;AAAA,IAEpD,KAAK;AACH,aAAQ,EAAE,QAAsB,KAAK,OAAK,mBAAmB,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC;AAAA,IACnF;AACE,aAAO;AAAA,EACX;AACF;AAqHA,IAAM,4BAA4B;AAIlC,SAAS,OAAO,GAA2B;AACzC,SAAO,OAAO,MAAM,YAAY,MAAM,QAAS,EAAwB,SAAS;AAClF;AAQA,SAAS,eACPC,OACA,cACA,KACA,OAAiB,CAAC,GACG;AACrB,WAAS,IAAI,GAAG,IAAIA,MAAK,SAAS,QAAQ,KAAK;AAC7C,UAAM,QAAQA,MAAK,SAAS,CAAC;AAC7B,QAAI,CAAC,OAAO,KAAK,EAAG;AACpB,UAAM,gBAAgB,eAAe,MAAM,KAAK;AAChD,UAAM,cAAc,eAAe,MAAM,KAAK;AAC9C,QAAI,iBAAiB,OAAO,MAAM,aAAa;AAC7C,YAAM,QAAQ,eAAe,OAAY,eAAe,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC;AACzE,aAAO,SAAS,EAAE,MAAM,OAAY,MAAM,CAAC,GAAG,MAAM,CAAC,EAAE;AAAA,IACzD;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAgC,MAAS,MAAqB;AACrE,QAAM,YAAiB,CAAC,IAAI;AAC5B,MAAI,MAAS;AACb,aAAW,OAAO,KAAK,MAAM,GAAG,EAAE,GAAG;AACnC,UAAM,QAAQ,IAAI,SAAS,GAAG;AAC9B,QAAI,CAAC,SAAS,CAAC,OAAO,KAAK,EAAG;AAC9B,cAAU,KAAK,KAAU;AACzB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEA,SAAS,cACP,SACA,MACA,MACA,SACG;AACH,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAM,CAAC,KAAK,GAAG,IAAI,IAAI;AACvB,QAAM,cAAc,CAAC,GAAG,KAAK,QAAQ;AACrC,cAAY,GAAG,IAAI,KAAK,WAAW,IAC/B,UACA,cAAc,SAAS,KAAK,SAAS,GAAG,GAAQ,MAAM,OAAO;AACjE,SAAO,QAAQ,MAAM,WAAW;AAClC;AAEA,SAAS,eAAmCA,OAAS,UAAoD;AACvG,SAAO,EAAE,GAAGA,OAAM,SAAS;AAC7B;AAYA,SAAS,cAAiB,OAAU,OAAkB;AACpD,QAAM,IAAI;AACV,SAAO,EAAE,GAAI,GAAc,MAAM,EAAE,OAAO,EAAE,KAAK,QAAQ,OAAO,KAAK,EAAE,KAAK,MAAM,MAAM,EAAE;AAC5F;AAWA,SAAS,cAAkC,MAAS,MAAgB,SAAY,OAAkB;AAChG,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAM,CAAC,KAAK,GAAG,IAAI,IAAI;AACvB,QAAM,cAAc,KAAK;AACzB,QAAM,cAAc,YAAY,IAAI,CAAC,OAAO,MAAM;AAChD,QAAI,IAAI,IAAK,QAAO;AACpB,QAAI,MAAM,KAAK;AACb,aAAO,KAAK,WAAW,IAAI,UAAU,cAAc,OAAY,MAAM,SAAS,KAAK;AAAA,IACrF;AACA,WAAO,cAAc,OAAO,KAAK;AAAA,EACnC,CAAC;AACD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,EAAE,OAAO,KAAK,KAAK,OAAO,KAAK,KAAK,KAAK,MAAM,MAAM;AAAA,IAC3D,UAAU;AAAA,EACZ;AACF;AAqCA,SAAS,YACP,OACA,SACA,OACA,QACA,QACA,WACA,OACA,UACmB;AACnB,QAAM,MAAoB,MAAM,MAAM,OAAO,QAAQ;AACrD,QAAM,MAAkB,CAAC;AACzB,MAAI,MAAM;AACV,MAAI,QAAQ;AACZ,SAAO,MAAM,OAAO;AAClB,QAAI,EAAE,QAAQ,QAAQ,UAAU,EAAG,QAAO;AAI1C,QAAI,aAAa,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,EAAG,SAAS,QAAQ;AACvE,UAAI,MAAM,UAAU,SAAS,SAAS,CAAC,MAAM,WAAW,WAAW,GAAG,EAAG,QAAO;AAChF,UAAI,KAAK,EAAE,MAAM,QAAQ,OAAO,WAAW,MAAM,EAAE,OAAO,MAAM,QAAQ,KAAK,MAAM,UAAU,SAAS,OAAO,EAAE,CAAC;AAChH,aAAO,UAAU;AACjB;AAAA,IACF;AACA,QAAI;AACJ,QAAI;AACF,UAAI,OAAO,OAAO,KAAK,GAAG;AAAA,IAC5B,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,CAAC,EAAE,MAAM,EAAE,KAAK,OAAO,OAAO,EAAE,KAAK,MAAM,MAAO,QAAO;AAC7D,QAAI,KAAK,cAAc,EAAE,OAA8B,MAAM,CAAC;AAC9D,UAAM,EAAE,KAAK;AAAA,EACf;AACA,SAAO,QAAQ,QAAQ,MAAM;AAC/B;AAOA,SAAS,cACP,MACA,MACA,GACA,QACA,UACA,MACA,IACA,OACA,UACA,OACA,UACU;AACV,QAAM,OAAO,EAAE;AACf,MAAI,KAAK,WAAW,EAAG,QAAO;AAI9B,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,UAAU,SAAS,KAAK,CAAC,EAAG,KAAK,OAAO,KAAM;AAC9D,SAAO,IAAI,KAAK,KAAK,IAAI,CAAC,EAAG,SAAS,OAAQ;AAG9C,MAAI,IAAI,KAAK;AACb,SAAO,IAAI,KAAK,SAAS,KAAK,IAAI,CAAC,EAAG,KAAK,SAAS,GAAI;AACxD,SAAO,IAAI,KAAK,UAAU,KAAK,CAAC,EAAG,SAAS,OAAQ;AACpD,MAAI,KAAK,KAAK,OAAQ,QAAO;AAG7B,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAI,KAAK,CAAC,EAAG,SAAS,UAAU,KAAK,CAAC,EAAG,SAAS,KAAM,QAAO;AAAA,EACjE;AAQA,MAAI;AACJ,MAAI,YAAY;AAChB,aAAW,KAAK,MAAM;AACpB,QAAI,EAAE,SAAS,OAAQ;AACvB;AACA,QAAI,aAAa,OAAW,YAAW,EAAE;AAAA,aAChC,EAAE,SAAS,SAAU,QAAO;AAAA,EACvC;AACA,MAAI,aAAa,UAAa,YAAY,EAAG,QAAO;AACpD,QAAM,SAAS,SAAS,QAAQ;AAChC,MAAI,CAAC,OAAQ,QAAO;AAGpB,MAAI,YAA2B;AAC/B,WAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;AACxC,QAAI,KAAK,IAAI,CAAC,EAAG,SAAS,UAAU,KAAK,IAAI,CAAC,EAAG,SAAS,QAAQ;AAChE,UAAI,KAAK,CAAC,EAAG,SAAS,OAAQ,QAAO;AACrC,YAAM,MAAM,KAAK,CAAC,EAAG,SAAS;AAC9B,UAAI,cAAc,KAAM,aAAY;AAAA,eAC3B,QAAQ,UAAW,QAAO;AAAA,IACrC;AAAA,EACF;AACA,MAAI,cAAc,KAAM,QAAO;AAO/B,QAAM,QAAQ,KAAK,CAAC;AACpB,QAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AACjC,MAAI,MAAM,SAAS,UAAU,MAAM,UAAU,aAAa,OAAO,SAAS,MAAM,KAAK,IAAK,QAAO;AACjG,MAAI,KAAK,SAAS,UAAU,KAAK,UAAU,aAAa,KAAK,SAAS,KAAK,KAAK,MAAO,QAAO;AAE9F,QAAM,UAAU,IAAI,IAAI,SAAS,KAAK,IAAI,CAAC,EAAG,KAAK,MAAM;AACzD,QAAM,QAAQ,SAAS,KAAK,CAAC,EAAG,KAAK,QAAQ;AAC7C,MAAI,QAAQ,QAAS,QAAO;AAE5B,QAAM,SAAS,YAAY,UAAU,SAAS,OAAO,QAAQ,QAA4B,WAAW,OAAO,QAAQ;AACnH,MAAI,CAAC,OAAQ,QAAO;AASpB,MAAI,OAAO,SAAS,GAAG;AACrB,UAAMC,QAAO,OAAO,OAAO,SAAS,CAAC;AACrC,QAAI,EAAEA,MAAK,SAAS,UAAUA,MAAK,UAAU,WAAY,QAAO;AAAA,EAClE;AAMA,MAAI,QAAQ,SAAS,QAAQ;AAC3B,eAAW,YAAY,CAAC,KAAK,QAAG,GAAG;AACjC,UAAI,SAAS,KAAK,MAAM,SAAU;AAClC,YAAM,SAAS,SAAS,MAAM,GAAG,KAAK,IAAI,SAAS,OAAO,SAAS,SAAS,KAAK;AACjF,YAAM,QAAQ,YAAY,QAAQ,SAAS,OAAO,QAAQ,QAA4B,WAAW,OAAO,QAAQ;AAChH,UAAI,CAAC,SAAS,MAAM,WAAW,OAAO,OAAQ,QAAO;AACrD,eAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,IAAK,KAAI,CAAC,kBAAkB,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,EAAG,QAAO;AAAA,IAC9F;AAAA,EACF;AAEA,QAAM,OAAO,KAAK,MAAM,GAAG,CAAC;AAC5B,QAAM,OAAO,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC;AAC7D,QAAM,OAAO;AAAA,IACX,GAAG;AAAA,IACH,MAAM,EAAE,OAAO,EAAE,KAAK,OAAO,KAAK,EAAE,KAAK,MAAM,MAAM;AAAA,IACrD,UAAU,CAAC,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI;AAAA,EACxC;AAGA,SAAO,cAAc,MAAM,MAAM,MAAM,KAAK;AAC9C;AAYO,SAAS,kBAAkB,GAAY,GAAqB;AACjE,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,MAAM,QAAQ,MAAM,KAAM,QAAO;AACvF,QAAM,KAAM,EAAwB;AACpC,QAAM,KAAM,EAAwB;AACpC,MAAI,OAAO,GAAI,QAAO;AACtB,QAAM,KAAM,EAAgD;AAC5D,QAAM,KAAM,EAAgD;AAC5D,MAAI,MAAM,IAAI;AACZ,QAAI,CAAC,MAAM,CAAC,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,IAAK,QAAO;AAAA,EACvE;AACA,MAAI,OAAO,UAAU,OAAO,UAAU;AACpC,WAAQ,EAAwB,UAAW,EAAwB;AAAA,EACrE;AACA,MAAI,OAAO,cAAc;AAIvB,UAAM,KAAM,EAAuC,YAAY,CAAC;AAChE,UAAM,KAAM,EAAuC,YAAY,CAAC;AAChE,WAAO,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC,GAAG,MAAM,MAAM,GAAG,CAAC,CAAC;AAAA,EAClE;AACA,MAAK,EAAwB,SAAU,EAAwB,KAAM,QAAO;AAC5E,QAAM,KAAM,EAAwC;AACpD,QAAM,KAAM,EAAwC;AACpD,MAAI,MAAM,IAAI;AACZ,QAAI,CAAC,MAAM,CAAC,MAAM,GAAG,WAAW,GAAG,OAAQ,QAAO;AAClD,aAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,IAAK,KAAI,CAAC,kBAAkB,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,EAAG,QAAO;AAAA,EACnF;AACA,SAAO;AACT;AAcA,SAAS,eACP,QACA,UACA,OACA,UACA,OACA,OACA,UACA,UACS;AACT,MAAI,CAAC,SAAS,GAAI,QAAO;AACzB,MAAI,YAAY,SAAS,OAAQ,QAAO;AACxC,aAAW,YAAY,CAAC,KAAK,QAAG,GAAG;AACjC,QAAI,SAAS,QAAQ,MAAM,SAAU;AACrC,UAAM,SAAS,SAAS,MAAM,GAAG,QAAQ,IAAI,SAAS,OAAO,SAAS,SAAS,QAAQ;AACvF,UAAM,MAAoB,MAAM,OAAO,OAAO,QAAQ;AACtD,QAAI;AACJ,QAAI;AACF,UAAI,OAAO,QAAQ,OAAO,GAAG;AAAA,IAC/B,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,CAAC,EAAE,MAAM,EAAE,KAAK,QAAQ,SAAS,KAAK,IAAK,QAAO;AACtD,QAAI,CAAC,kBAAkB,EAAE,OAAO,SAAS,KAAK,EAAG,QAAO;AAAA,EAC1D;AACA,SAAO;AACT;AAMA,IAAM,eAAN,MAAM,cAAwD;AAAA,EAC3C;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUT;AAAA,EACA;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EAET,YACE,UACA,UACA,MACA,OACA,QACA,gBACA,OACA;AACA,SAAK,YAAY;AACjB,SAAK,OAAO,CAAC;AACb,SAAK,QAAQ,oBAAI,IAAI;AACrB,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACpD,WAAK,KAAK,IAAI,IAAI,SAAS,KAAK;AAChC,UAAI,mBAAmB,MAAM,KAAK,CAAC,EAAG,MAAK,MAAM,IAAI,IAAI;AAAA,IAC3D;AACA,SAAK,YAAY;AACjB,SAAK,QAAQ;AACb,SAAK,OAAO,MAAM;AAClB,SAAK,OAAO,MAAM;AAClB,SAAK,SAAS;AACd,SAAK,iBAAiB;AACtB,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,IAAI,OAAiB;AACnB,QAAI,KAAK,SAAS,QAAW;AAC3B,WAAK,OAAO,KAAK,OAAQ,cAAc,KAAK,MAAuC,CAAC,IAAqB;AAAA,IAC3G;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,OAAO,MAAyD;AAC9D,UAAM,MAAM,KAAK;AACjB,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,yCAAyC;AACnE,WAAO,gBAAgB,KAAiE,IAAI;AAAA,EAC9F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,UAAU,SAAY,UAA+B;AAC3D,UAAMC,UAAS,SAAS,KAAK,UAAU,KAAK,SAAS,GAAG,KAAK,KAAK;AAClE,UAAM,MAAO,QAAiD,KAAK;AACnE,UAAM,SAAS,sBAAsB,SAAS,GAAG,CAAC,CAAC;AACnD,UAAM,iBAAiB,gBAAgB,KAAK,UAAUA,OAAM;AAC5D,WAAO,IAAI,cAAa,KAAK,WAAW,KAAK,WAAW,KAAK,OAAO,EAAE,KAAK,QAAQ,GAAG,QAAQ,gBAAgB,QAAQ;AAAA,EACxH;AAAA,EAEA,KAAK,MAAc,IAAY,aAAkC;AAC/D,UAAM,WAAW,KAAK,MAAM,MAAM,GAAG,IAAI,IAAI,cAAc,KAAK,MAAM,MAAM,EAAE;AAC9E,UAAM,UAAU,MAAM,SAAS,KAAK,WAAW,KAAK,WAAW,UAAU,KAAK,KAAK;AAEnF,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,KAAM,QAAO,QAAQ;AAE1B,UAAM,QAAQ,YAAY,UAAU,KAAK;AAMzC,UAAM,QAAQ,eAAe,MAAM,GAAG,IAAI,KAAK,EAAE,MAAM,MAAM,MAAM,CAAC,EAAc;AAGlF,UAAM,YAAY,YAAY,MAAM,MAAM,IAAI;AAC9C,UAAM,aAA6B,CAAC,KAAK;AACzC,UAAM,WAAW,CAAC,GAAG,MAAM,IAAI;AAC/B,aAAS,IAAI,UAAU,SAAS,GAAG,KAAK,GAAG,KAAK;AAC9C,eAAS,IAAI;AACb,iBAAW,KAAK,EAAE,MAAM,UAAU,IAAI,CAAC,GAAI,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;AAAA,IAClE;AAEA,UAAM,UAAU,KAAK,MAAM,WAAW;AACtC,eAAW,EAAE,MAAAF,OAAM,KAAK,KAAK,YAAY;AAEvC,YAAM,EAAE,OAAO,UAAU,KAAK,OAAO,IAAI,gBAAgB,MAAkE,IAAI;AAQ/H,UAAI,SAAS,WAAW,KAAK,MAAM,SAAS,0BAA2B;AACvE,YAAM,SAAS,KAAK,KAAKA,MAAK,IAAI;AAClC,UAAI,CAAC,OAAQ;AAMb,UAAI,EAAE,YAAY,QAAQ,MAAM,QAAS;AACzC,YAAM,MAAoB,MAAMA,MAAK,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC,KAAK,MAAM,QAAQ;AACnF,YAAM,IAAI,OAAO,UAAU,UAAU,GAAG;AACxC,UAAI,CAAC,EAAE,GAAI;AACX,UAAI,EAAE,KAAK,QAAQ,SAAS,MAAO;AAMnC,UAAI,CAAC,eAAe,QAAQ,UAAU,UAAU,SAAS,OAAOA,MAAK,OAAO,KAAK,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,MAAM,QAAQ,GAAG;AACvH;AAAA,MACF;AAKA,YAAM,aAAa,WAAWA,MAAK,KAAK;AACxC,YAAM,SAAS,cAAc,EAAE,OAAwC,UAAU;AAIjF,UAAI,UAAU,GAAG;AACf,cAAMG,WAAU,cAAc,SAAS,MAAM,MAAM,MAAM;AACzD,eAAO,KAAK,UAAUA,UAAS,QAAQ;AAAA,MACzC;AAIA,UAAI,KAAK,MAAM,QAAS,QAAO,QAAQ;AACvC,YAAM,UAAU,cAAc,MAAM,MAAM,QAAQ,KAAK;AACvD,aAAO,KAAK,UAAU,SAAS,QAAQ;AAAA,IACzC;AAWA,QAAI,KAAK,MAAM,mBAAmB,UAAU,KAAK,CAAC,KAAK,MAAM,SAAS;AAIpE,YAAM,mBAAmC,CAAC,GAAG,YAAY,EAAE,MAAM,MAAM,MAAM,CAAC,EAAE,CAAC;AACjF,iBAAW,EAAE,MAAAH,OAAM,KAAK,KAAK,kBAAkB;AAC7C,YAAI,CAAC,KAAK,MAAM,IAAIA,MAAK,IAAI,EAAG;AAChC,cAAM,EAAE,OAAO,QAAQ,KAAK,KAAK,IAAI,gBAAgB,MAAkE,IAAI;AAC3H,YAAI,EAAE,UAAU,QAAQ,MAAM,MAAO;AACrC,cAAM,UAAU,cAAc,MAAM,MAAMA,OAAM,QAAQ,UAAU,MAAM,IAAI,OAAO,KAAK,MAAM,KAAK,MAAM,OAAO,CAAC,CAAC,KAAK,MAAM,QAAQ;AACrI,YAAI,QAAS,QAAO,KAAK,UAAU,SAAS,QAAQ;AAAA,MACtD;AAAA,IACF;AAEA,WAAO,QAAQ;AAAA,EACjB;AACF;AAiBA,SAAS,SACP,OACA,MACiC;AACjC,MAAI,KAAK,OAAQ,QAAO,KAAK;AAI7B,MAAI,UAAU,UAAa,OAAO,UAAU,WAAY,QAAO;AAC/D,QAAM,OAAQ,MAA8D;AAC5E,SAAO,OAAO,KAAK,gBAAgB;AACrC;AAOA,SAAS,gBAAgB,KAAa,OAAeI,SAAwD;AAC3G,MAAI,MAAM;AACV,MAAIA,WAAU,MAAM,MAAM,QAAQ;AAChC,UAAM,IAAIA,QAAO,MAAM,OAAO,KAAK,EAAE,YAAY,MAAM,CAAC;AACxD,QAAI,EAAE,MAAM,EAAE,KAAK,MAAM,IAAK,OAAM,EAAE,KAAK;AAAA,EAC7C;AACA,SAAO,MAAM,MAAM,SAAS,MAAM;AACpC;AAQA,SAAS,sBAAsBC,OAAe,MAAc,KAAiC;AAC3F,QAAM,IAAIA;AACV,MAAI,CAAC,KAAK,OAAO,MAAM,YAAY,CAAC,EAAE,KAAM,QAAO;AACnD,QAAM,QAAQ,OAAO,EAAE,KAAK;AAC5B,MAAI,EAAE,SAAS,cAAc;AAC3B,QAAI,KAAK,EAAE,MAAM,cAAc,MAAM,EAAE,OAAO,KAAK,OAAO,EAAE,KAAK,IAAI,GAAG,UAAU,EAAE,YAAY,CAAC,EAAE,CAAC;AACpG,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,EAAE,QAAQ,EAAG,YAAW,KAAK,EAAE,SAAU,uBAAsB,GAAG,OAAO,GAAG;AAC9F,SAAO;AACT;AAMO,SAAS,SACd,UACA,UACA,OACA,OAA2B,CAAC,GACf;AACb,QAAM,QAAQ,SAAS,QAAQ;AAC/B,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,YAAY,QAAQ,eAAe;AAI/D,2BAAyB,gBAAgB,QAAQ,GAAG,KAAK,OAAO,kBAAkB,QAAQ,CAAC;AAC3F,QAAM,MAAoB,MAAM,KAAK,OAAO,KAAK,OAAO,CAAC,CAAC,KAAK,QAAQ;AACvE,QAAM,IAAoB,SAAS,KAAK,EAAE,OAAO,GAAG,GAAG;AACvD,MAAI,EAAE,IAAI;AAKR,UAAM,SAAS,IAAI,UAAU,CAAC,GAAG,IAAI,OAAO,IAAI,CAAC;AACjD,UAAM,iBAAiB,gBAAgB,EAAE,KAAK,KAAK,OAAO,SAAS,OAAO,IAAI,CAAC;AAC/E,WAAO,IAAI,aAAa,UAAU,UAAU,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,gBAAgB,KAAK;AAAA,EACnG;AAEA,QAAM,OAAmB,EAAE,MAAM,cAAc,MAAM,EAAE,MAAM,UAAU,EAAE,SAAS;AAClF,SAAO,IAAI,aAAa,UAAU,UAAU,MAAM,EAAE,KAAK,KAAK,GAAG,CAAC,IAAI,GAAG,MAAM,KAAK;AACtF;;;ACvyBA,IAAM,eAAe,CAAC,aACpB,SAAS,SAAS,8BAA8B,SAAS,KAAK,MAAM,CAAC,KAAK;AAE5E,SAAS,UAAU,MAAc,OAAqC;AACpE,QAAM,KAAK,CAACC,UAAuB;AACjC,UAAMA,KAAI;AACV,UAAM,WAAWA,MAAK;AACtB,QAAI,MAAM,QAAQ,QAAQ,EAAG,YAAW,SAAS,SAAU,IAAG,KAAK;AAAA,EACrE;AACA,KAAG,IAAI;AACT;AAcA,SAAS,iBACP,MACA,MACA,gBACA,aACA,QACc;AACd,QAAM,MAAoB,CAAC;AAC3B,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,MAAM,CAAC,MAAwB;AACnC,UAAM,MAAM,GAAG,EAAE,QAAQ,IAAI,EAAE,KAAK,KAAK,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO;AACpE,QAAI,KAAK,IAAI,GAAG,EAAG;AACnB,SAAK,IAAI,GAAG;AACZ,QAAI,KAAK,CAAC;AAAA,EACZ;AACA,MAAI,KAAM,YAAW,KAAK,aAAa,MAAM,MAAM,EAAG,KAAI,CAAC;AAC3D,aAAW,KAAK,KAAM,KAAI,EAAE,UAAU,SAAS,SAAS,aAAa,EAAE,QAAQ,GAAG,MAAM,EAAE,KAAK,CAAC;AAChG,MAAI,mBAAmB,MAAM;AAC3B,QAAI,EAAE,UAAU,SAAS,SAAS,oBAAoB,MAAM,EAAE,OAAO,gBAAgB,KAAK,YAAY,EAAE,CAAC;AAAA,EAC3G;AACA,SAAO;AACT;AAKA,SAAS,aAAa,MAAc,QAA6C;AAC/E,QAAM,MAAoB,CAAC;AAC3B,QAAMC,SAAQ,OAAO;AACrB,YAAU,MAAM,CAAAD,UAAQ;AACtB,QAAIA,MAAK,SAAS,cAAc;AAC9B,UAAI,KAAK,EAAE,UAAU,SAAS,SAAS,aAAcA,MAAiC,YAAY,CAAC,CAAC,GAAG,MAAMA,MAAK,KAAK,CAAC;AACxH;AAAA,IACF;AACA,UAAM,IAAIC,UAASD,MAAK,SAAS,SAAYC,OAAMD,MAAK,IAAI,IAAI;AAChE,QAAI,GAAG;AACL,YAAM,IAAI,EAAEA,KAAI;AAChB,UAAI,EAAG,KAAI,KAAK,GAAI,MAAM,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAC,CAAE;AAAA,IACjD;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEO,SAAS,gBAAgB,SAAuB,SAAgC,CAAC,GAAoB;AAC1G,QAAM,QAAQ,aAAa;AAC3B,QAAM,QAAQ,OAAO,YAAY,YAAY,YAAY,QAAQ,WAAW;AAC5E,QAAM,WAAW,QAAQ,QAAQ,QAAQ;AACzC,QAAM,OAAO,QAAQ,QAAQ,OAAO;AACpC,QAAM,QAAiB,QAAS,SAAU,IAAK,IAAgB;AAK/D,QAAME,UAAS,OAAO,UAAU,aAAc,MAA8B,MAAM,gBAAgB;AAClG,QAAM,gBAAgB,CAAC,QACrB,IAAI,OAAgB,KAAK,EAAE,UAAU,MAAM,OAAO,GAAIA,YAAW,SAAY,EAAE,QAAAA,QAAO,IAAI,CAAC,EAAG,CAAC;AAEjG,QAAM,WAAW,CAAC,OAAe,QAAgB,kBAA6C;AAC5F,UAAM,WAAW,cAAkB,OAAgB,OAAO,QAAQ,EAAE,UAAU,KAAK,CAAC;AACpF,UAAM,OAAO,aAAa,eAAe,MAAM;AAC/C,UAAM,UAAU,SAAS,OAAO,OAAO,WAAW,IAAI,IAAI;AAC1D,QAAI,QAAS,QAAO,QAAQ,EAAE,MAAM,UAAU,OAAO,CAAC;AACtD,WAAO,SAAS,IAAI,CAAAC,YAAU,EAAE,OAAAA,OAAM,EAAE;AAAA,EAC1C;AAEA,SAAO;AAAA,IACL,MAAM,KAAK,MAAM;AACf,aAAO,IAAI,OAAgB,KAAK,EAAE,UAAU,MAAM,YAAY,MAAM,MAAM,CAAC;AAAA,IAC7E;AAAA,IAEA,YAAY,KAAK;AACf,YAAM,IAAI,cAAc,GAAG;AAC3B,YAAM,OAAO,EAAE,SAAS,OAAO,EAAE,UAAU,WAAY,EAAE,QAAmB;AAC5E,aAAO,iBAAiB,MAAM,EAAE,QAAQ,EAAE,gBAAgB,IAAI,QAAQ,MAAM;AAAA,IAC9E;AAAA,IAEA,cAAc,KAAK,QAAQ;AACzB,aAAO,SAAS,KAAK,QAAQ,cAAc,IAAI,MAAM,GAAG,MAAM,CAAC,EAAE,KAAK;AAAA,IACxE;AAAA,IAEA,aAAa,KAAK;AAChB,UAAI,CAAC,YAAY,SAAS,QAAW;AACnC,cAAM,IAAI,MAAM,8GAAyG;AAAA,MAC3H;AACA,YAAM,OAAO,aAAa;AAC1B,YAAM,OAAO,CAAC,QAA2D;AAEvE,cAAM,MAAM,IAAI,OAAQ,cAAc,IAAI,IAAa,IAA0B;AACjF,eAAO;AAAA,UACL,IAAI,OAAO;AAAE,mBAAO;AAAA,UAAI;AAAA,UACxB,MAAM,CAAC,MAAM,IAAI,gBAAgB,KAAK,IAAI,KAAK,MAAM,IAAI,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA,UAIrE,aAAa,MAAM,iBAAiB,KAAK,IAAI,QAAQ,IAAI,gBAAgB,IAAI,MAAM,QAAQ,MAAM;AAAA,UACjG,eAAe,CAAC,WAAW,SAAS,IAAI,OAAO,QAAQ,GAAG;AAAA,QAC5D;AAAA,MACF;AACA,aAAO,KAAK,SAAmB,UAAU,MAAM,KAAK,EAAE,UAAU,MAAM,iBAAiB,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IAC7G;AAAA,EACF;AACF;AAIA,SAAS,aAAa,MAAe,QAA+B;AAClE,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,MAAI,OAAsB;AAC1B,YAAU,MAAgB,CAAAH,UAAQ;AAChC,UAAM,IAAIA,MAAK;AACf,QAAI,KAAK,EAAE,SAAS,UAAU,UAAU,EAAE,OAAOA,MAAK,SAAS,OAAW,QAAOA,MAAK;AAAA,EACxF,CAAC;AACD,SAAO;AACT;",
6
- "names": ["matches", "label", "kind", "label", "children", "span", "rawChildren", "triviaLog", "state", "node", "node", "last", "trivia", "newTree", "trivia", "node", "node", "rules", "trivia", "label"]
3
+ "sources": ["../../src/line-index.ts", "../../src/cst/capture-buffer.ts", "../../src/recovery/scan.ts", "../../src/parse-context.ts", "../../src/cst/trivia-entries.ts", "../../src/cst/host-mode.ts", "../../src/functional/run.ts", "../../src/combinators/completions.ts", "../../src/regex/classes.ts", "../../src/table/ops.ts", "../../src/table/scan-shapes.ts", "../../src/table/assemble.ts", "../../src/analysis/duplication.ts", "../../src/compiler/linker.ts", "../../src/cst/relative-spans.ts", "../../src/functional/doc.ts", "../../src/language-service/index.ts"],
4
+ "mappings": "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",
5
+ "names": ["rawEntry", "matches", "label", "kind", "label", "META", "children", "span", "rawChildren", "triviaLog", "state", "node", "parser", "node", "trivia", "newTree", "trivia", "node", "node", "rules", "trivia", "label"]
7
6
  }