parseman 0.46.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +9929 -7465
  9. package/dist/analysis/diagnostics.cjs.map +3 -4
  10. package/dist/analysis/diagnostics.d.ts +2 -2
  11. package/dist/analysis/diagnostics.js +9929 -7465
  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 +10865 -8535
  24. package/dist/cli/index.cjs.map +3 -4
  25. package/dist/cli/index.js +10865 -8535
  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 +40 -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 +9224 -7203
  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 +9219 -7198
  106. package/dist/index.js.map +3 -4
  107. package/dist/language-service/index.cjs +311 -121
  108. package/dist/language-service/index.cjs.map +3 -4
  109. package/dist/language-service/index.js +311 -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 +11146 -9593
  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 +11146 -9593
  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 +2898 -1623
  131. package/dist/spec/index.cjs.map +3 -4
  132. package/dist/spec/index.js +2898 -1623
  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 +11375 -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 +11352 -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 +446 -0
  171. package/dist/table/ops.d.ts.map +1 -0
  172. package/dist/table/program.d.ts +493 -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 +25 -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 +322 -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 +221 -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 +821 -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 +2994 -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 +1766 -0
  286. package/src/table/emit.ts +418 -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 +1672 -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 +459 -0
  295. package/src/table/program.ts +855 -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
@@ -0,0 +1,38 @@
1
+ import { tableRules } from './assemble.ts'
2
+ import { expandCompactFolded, unfoldVariant, type CompactFolded, type FoldedProgram, type TableRule } from './program.ts'
3
+
4
+ // Emitted folded modules expand their wire literal ONCE, so every named export
5
+ // shares the family's structural variant/resolution owner.
6
+ export { expandCompactFolded } from './program.ts'
7
+
8
+ /**
9
+ * Select ONE variant of a folded table and build its rule map.
10
+ *
11
+ * This is the load-time half of G4. A folded artifact ships one table plus, per
12
+ * variant, the words that differ from it; this materialises the named variant
13
+ * and hands the driver an ordinary `TableProgram`. The driver is not told which
14
+ * variant it got and has no way to ask — the fold is finished before the driver
15
+ * sees anything, exactly as `trackLines` is resolved before it today.
16
+ *
17
+ * Its own module rather than a second export from the driver so that a bundle
18
+ * shipping a single-variant table does not pull the fold in, and so the driver
19
+ * keeps having no import of its own.
20
+ *
21
+ * IT DRIVES `tableRules`, the shipped engine. This module predates
22
+ * `63666b6`, which made the assembler `tableRules` by editing `table/index.ts`
23
+ * ALONE; `fold.ts` imports `exec.ts` directly and so kept the interpreter. Every
24
+ * folded artifact ships `import { tableVariants }` (`emit.ts`), so that was a
25
+ * product path on the reference engine. Bound by its own name now.
26
+ */
27
+ export function tableVariants(
28
+ source: FoldedProgram | CompactFolded,
29
+ name: string,
30
+ ): Record<string, TableRule> {
31
+ const folded = 'base' in source ? source : expandCompactFolded(source)
32
+ return tableRules(unfoldVariant(folded, name))
33
+ }
34
+
35
+ /** The variant names a folded table carries, for a caller that wants to check. */
36
+ export function variantNames(source: FoldedProgram | CompactFolded): string[] {
37
+ return Object.keys('base' in source ? source.variants : source.v)
38
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * `parseman/table` — the shared driver for the TABLE lowering (ledger row G5).
3
+ *
4
+ * A grammar lowered to a table contributes only DATA to a bundle; every line of
5
+ * recognition logic lives here and ships once, shared by every grammar and every
6
+ * `(grammar, settings)` variant. An emitted module's whole runtime dependency is
7
+ * `tableRules` from this entry.
8
+ *
9
+ * Its own entry point for the same reason `parseman/run` is: a package that
10
+ * SHIPS a table parser should not drag the combinator set, `compile()` and the
11
+ * first-set analysis along with it.
12
+ */
13
+ /**
14
+ * THE DRIVER (G5) — `tableRules` IS the assembler. The program is LINKED into
15
+ * closures at run start, so the parse path holds no opcode read, no operand
16
+ * decode and no option test.
17
+ *
18
+ * It replaced the bytecode interpreter here rather than shipping beside it. Two
19
+ * live drivers is two places for behaviour to drift, and that already happened
20
+ * once: the ambient `scanSkip` write had to move into the shared `stamp.ts`
21
+ * envelope because each driver was installing it separately. The gate for the
22
+ * swap was `exec === assembled` on the identity sweep, which it clears.
23
+ *
24
+ * `exec.ts` stays REFERENCE — the sweep still gates the assembler against it,
25
+ * and it is what a divergence gets bisected against. Nothing under `src/` binds
26
+ * it; only `bench/` and `test/` do, as the reference side of a differential.
27
+ *
28
+ * THAT SENTENCE WAS FALSE FOR TWO RELEASES, and the shape of the failure is
29
+ * worth keeping. `63666b6` made the assembler `tableRules` by editing THIS FILE
30
+ * and nothing else. `exec.ts` exported its own function ALSO called `tableRules`
31
+ * with the identical signature, so any module reaching PAST this entry bound the
32
+ * interpreter, type-checked clean and ran correctly — just slower. THREE modules
33
+ * did: `table/fold.ts` (predating the swap), `compiler/linker.ts` (added after
34
+ * it, in `37c57b5`), and `bench/jess/fixture.ts` — the CANONICAL fixture
35
+ * harness, whose column printed as `table` was the reference interpreter for the
36
+ * whole cycle its figures were quoted in. Between them they put the entire
37
+ * `compose()`/`fuse()` composition path, every folded artifact's `tableVariants`
38
+ * load, and the repo's headline parse-time figure on the reference engine.
39
+ *
40
+ * BOTH HALVES OF THE MECHANISM ARE GONE, AND ONLY REMOVING BOTH WAS ENOUGH.
41
+ *
42
+ * Half one: `exec.ts` now exports `execRules`. The reference driver no longer
43
+ * answers to the shipped engine's name.
44
+ *
45
+ * Half two, and the one that made the class EXPRESSIBLE: this line used to read
46
+ * `export { assembledRules as tableRules, assembledRules, … }` — ONE function
47
+ * published under TWO names. An `as` re-export across a boundary we own both
48
+ * sides of is not a compatibility shim; it is a second name for a thing that
49
+ * needs one, and it is exactly the seam a wrong import slips through. The
50
+ * function in `assemble.ts` is now NAMED `tableRules` at its declaration and
51
+ * re-exported unrenamed. `assembledRules` no longer exists anywhere.
52
+ *
53
+ * WHY `tableRules` WON AND `assembledRules` DIED:
54
+ *
55
+ * 1. It is the name EMITTED ARTIFACTS import. `emit.ts:emitTableModule` writes
56
+ * `import { <ref> } from 'parseman/table'` with `ref` defaulting to
57
+ * `tableRules`; `compile.ts` and `compile-rule-map.ts` default `runtimeRef`
58
+ * to the same; `plugin/index.ts` prepends that exact import into every
59
+ * consumer bundle it rewrites. Unifying on this name changes NO emitted
60
+ * byte — the alternative direction would have rewritten four emit sites and
61
+ * every artifact they have ever produced, for no gain.
62
+ * 2. It names WHAT THE THING IS at a public boundary — the rule map of a table
63
+ * lowering — where `assembledRules` named HOW it is currently built.
64
+ * Closure assembly is an implementation that has already replaced one
65
+ * engine and may be replaced again; the boundary name has now outlived one
66
+ * such swap and should outlive the next.
67
+ *
68
+ * `scripts/check-invariants.mjs` INV-11 fails any `as` rename across the two
69
+ * engines' vocabularies, and any renaming re-export from a `src/**` entry point,
70
+ * across `src/`, `test/` and `bench/`. The rule exists because the alias above
71
+ * was not a mistake anyone made twice — it was a shape that made the mistake
72
+ * available.
73
+ */
74
+ export { tableRules, assemble, AssemblyCache, type Assembly, type RunCfg } from './assemble.ts'
75
+ export { encodeTable, UnsupportedConstruct, type TableSettings } from './encode.ts'
76
+ /**
77
+ * `compile()` for the table lowering — same `CompiledParser` contract, a table
78
+ * artifact instead of generated source. A root combinator is a one-rule map, so
79
+ * this is a drop-in for the source-lowering `compile()` rather than a second API.
80
+ */
81
+ export { compile, type TableCompileOptions } from './compile.ts'
82
+ export { emitTableModule, emitTableExpression, emitFoldedModule } from './emit.ts'
83
+ /**
84
+ * `compileRuleMap()` for the table lowering — the MAIN macro path. `compile`
85
+ * only ever covered `compile()`, the single-root entry, so a `rules()` grammar had
86
+ * no table counterpart to point the build at at all.
87
+ */
88
+ export { compileRuleMap, type TableRuleMapOptions, type CompiledRuleMapTable } from './compile-rule-map.ts'
89
+ /**
90
+ * THE VARIANT FOLD (G4). One base table plus per-variant row edits, selected at
91
+ * load. Additive: `tableRules` and every existing entry are untouched, and the
92
+ * driver still reads no option on the parse path.
93
+ */
94
+ export { tableVariants, variantNames } from './fold.ts'
95
+ export { foldPrograms, unfoldVariant, expandCompactFolded } from './program.ts'
96
+ export type { FoldedProgram, CompactFolded, TableDelta } from './program.ts'
97
+ /*
98
+ * `emitTableOnly` is NOT re-exported here. It is a SIZE PROBE: it emits the
99
+ * table with an EMPTY reducer pool so the machinery's byte count is comparable
100
+ * to codegen's per-rule cost. The module it produces is not loadable — the code
101
+ * stream still references `fns[i]`, so the first parse throws
102
+ * `build is not a function`. That is fine for a measurement and wrong for a
103
+ * public entry, where it reads like "emit just the table" and fails open.
104
+ * `test/unit/table-emit-roundtrip.test.ts` imports it from `./emit.ts` directly.
105
+ */
106
+ export { opHistogram, reachableOps } from './inspect.ts'
107
+ export { resolveTable } from './program.ts'
108
+ export type { CompactProgram, TableProgram, TableRule } from './program.ts'
@@ -0,0 +1,52 @@
1
+ import { OP_NAMES } from './ops.ts'
2
+ import { childSlots } from './child-slots.ts'
3
+ import type { TableProgram } from './program.ts'
4
+
5
+ /**
6
+ * Walk the REACHABLE instructions of a program.
7
+ *
8
+ * A histogram over the raw word stream is not this: operands are ordinary
9
+ * numbers and collide with opcode values, so counting words would report
10
+ * confident nonsense. Reachability from the rule entries, decoding each
11
+ * instruction's declared width, is the only correct read.
12
+ */
13
+ export function reachableOps(prog: TableProgram): Map<number, number> {
14
+ const counts = new Map<number, number>()
15
+ const code = prog.code
16
+ for (const ip of reachableIps(prog)) {
17
+ const op = code[ip]!
18
+ counts.set(op, (counts.get(op) ?? 0) + 1)
19
+ }
20
+ return counts
21
+ }
22
+
23
+ /**
24
+ * The reachable INSTRUCTION OFFSETS, which is what any per-row question needs —
25
+ * an opcode histogram cannot answer "which functions sit behind this row".
26
+ */
27
+ export function reachableIps(prog: TableProgram): Set<number> {
28
+ const seen = new Set<number>()
29
+ const stack = Object.values(prog.rules)
30
+ const code = prog.code
31
+ const kids: number[] = []
32
+ while (stack.length > 0) {
33
+ const ip = stack.pop()!
34
+ if (seen.has(ip)) continue
35
+ seen.add(ip)
36
+ kids.length = 0
37
+ // The edge table is `child-slots.ts`, shared with `site-labels.ts`. THROWING on
38
+ // an opcode it does not know is this walk's own contract, not the table's: a
39
+ // program `opHistogram` cannot decode is a bug it must report loudly, and
40
+ // `test/unit/table-driver-ops.test.ts` pins that. The emitter's walk over the
41
+ // same edges deliberately stays silent instead.
42
+ if (!childSlots(code, ip, kids)) throw new Error(`inspect: unknown opcode ${String(code[ip])} at ${ip}`)
43
+ for (let i = 0; i < kids.length; i++) stack.push(kids[i]!)
44
+ }
45
+ return seen
46
+ }
47
+
48
+ export function opHistogram(prog: TableProgram): Record<string, number> {
49
+ const out: Record<string, number> = {}
50
+ for (const [op, n] of reachableOps(prog)) out[OP_NAMES[op] ?? `op${op}`] = n
51
+ return out
52
+ }
@@ -0,0 +1,459 @@
1
+ /**
2
+ * Opcodes for the TABLE lowering (design ledger row G5).
3
+ *
4
+ * The premise, stated so it can be falsified: today every rule's recognizer is
5
+ * INLINED BESPOKE into its own emitted function, so an artifact pays the full
6
+ * cost of the recognition machinery once PER RULE (measured: ~4.9 kB for a
7
+ * `node()` rule; ~186 B for each additional call site). Emitting a rule as a
8
+ * TABLE ROW interpreted by one shared driver moves that machinery out of the
9
+ * artifact and into the runtime, where it is emitted ONCE for all grammars and
10
+ * all variants.
11
+ *
12
+ * The instruction stream is a flat `number[]`: `code[ip]` is the opcode and the
13
+ * operands follow it inline. Child instructions are referenced by ABSOLUTE code
14
+ * offset, never by nesting, so the driver is a plain recursive read — no node
15
+ * objects, no closures, no per-combinator allocation.
16
+ *
17
+ * Operand layouts are documented per opcode below and are the contract between
18
+ * `encode.ts` and `exec.ts`. Nothing else may read the stream.
19
+ */
20
+
21
+ /** `LIT k` — `k` indexes a string in the const pool. */
22
+ export const OP_LIT = 1
23
+ /** `RX k` — `k` indexes a STICKY `RegExp` in the const pool. */
24
+ export const OP_RX = 2
25
+ /** `SEQ n c1 … cn` — n child offsets; value is the array of their values. */
26
+ export const OP_SEQ = 3
27
+ /** `SEQV n c1 … cn` — `SEQ` with `valueUnused`: terms run, no tuple is built. */
28
+ export const OP_SEQV = 4
29
+ /**
30
+ * `CHOICE d n fx c1 … cn` — `d` indexes a dispatch table (or −1 for ordered
31
+ * try); `fx` indexes the choice's OWN expected set.
32
+ *
33
+ * Both shipped engines report the UNION of the arms' expectations when a choice
34
+ * fails. The driver reported whatever the last attempted arm left behind — or,
35
+ * on a dispatch miss with no arm claiming the lead character, NOTHING, so a user
36
+ * got an error naming nothing at all.
37
+ */
38
+ export const OP_CHOICE = 5
39
+ /**
40
+ * `REP c min max sep flags` — `sep` is a child offset or −1; `max` −1 = ∞.
41
+ * `flags` bit 0 = trailing separator allowed, bit 1 = `keepSeparators`.
42
+ */
43
+ export const OP_REP = 6
44
+ /** `REPV …` — `REP` with `valueUnused`. */
45
+ export const OP_REPV = 7
46
+ /** `OPT c` — child, or `null` at the same position. NOT `undefined`: `optional()`
47
+ * yields `null` on no-match (src/combinators/repeat.ts:269,277) and grammars TEST for
48
+ * it — examples/lang's `call` reducer is `if (args === null) return callee`, so
49
+ * `undefined` there turned a bare identifier into a call node with `args: undefined`.
50
+ * The parse succeeded and only the tree moved. */
51
+ export const OP_OPT = 8
52
+ /** `XFORM f c` — `f` indexes the reducer; called `(value, span)`. */
53
+ export const OP_XFORM = 9
54
+ /**
55
+ * `NODE b c flags proj type tags` — SEVEN words. `b` indexes the build reducer
56
+ * (−1 = none), `c` is the child offset, `proj` the projected child index (−1 =
57
+ * none), `type` and `tags` index the const pool (`tags` −1 = none).
58
+ *
59
+ * `flags` is a BIT FIELD, not a boolean: bit 0 (`&1`) = grammar-owned explicit
60
+ * `captureTrivia`, bit 2 (`&4`) = the builder reads
61
+ * `triviaLog`, bit 3 (`&8`) = it reads `ctx.state`, bit 4 (`&16`) = the node has
62
+ * read fields, bit 5 (`&32`) = `collapse`, bit 6 (`&64`) = `unwrap`, bit 7
63
+ * (`&128`) = `trailingTrivia`. Bit 0 distinguishes capture a host may not
64
+ * suppress from a structural node's default capture. Bits 2 and 3 are resolved at ENCODE time
65
+ * from the reducer's declared arity by the same analysis codegen runs, and
66
+ * forced on under `hostMode: 'cst'`. The driver reads the bits and re-derives
67
+ * nothing.
68
+ *
69
+ * TWO ENCODERS SHARE THIS OPCODE and they do NOT agree on its length. The layout
70
+ * above is `encode.ts` / `exec.ts`. `encode-baseline.ts` / `exec-baseline.ts` emit
71
+ * the FOUR-word form `NODE b c flags` with bits 2 and 3 only, and refuse
72
+ * `collapse` / `unwrap` / `project` outright. Neither driver walks the stream by
73
+ * instruction length, so the divergence is contained — but read the pair you are
74
+ * in, not this comment alone. */
75
+ export const OP_NODE = 10
76
+ /** `RULE r` — `r` indexes `prog.rules`. */
77
+ export const OP_RULE = 11
78
+ /** `GATE cc c` — first-char gate: `cc` indexes a char class; then run `c`. */
79
+ export const OP_GATE = 12
80
+ /** `NOT c` — zero-width negative lookahead. */
81
+ export const OP_NOT = 13
82
+ /**
83
+ * `PEEK c fx` — zero-width positive lookahead. `fx` is the ASSERTION's own set,
84
+ * `['peek(<child tag>)']`, because `peek.ts:60` DISCARDS the body's expectation:
85
+ * a lookahead's failure is "the guard did not hold", not a request for whatever
86
+ * token the body happened to stop on.
87
+ *
88
+ * `encode-baseline.ts` / `exec-baseline.ts` emit and read the two-word form and
89
+ * are internally consistent; read the pair you are in.
90
+ */
91
+ export const OP_PEEK = 14
92
+ /** `LEAF f c` — `leaf()`: reduce, then record as a spanned leaf. */
93
+ export const OP_LEAF = 15
94
+ /** `EMPTY` — matches the empty string. */
95
+ export const OP_EMPTY = 16
96
+ /**
97
+ * Line-tracking twins of `LIT` / `RX` / `NODE`.
98
+ *
99
+ * This is the variant axis, made concrete. `trackLines` picks these rows when
100
+ * the TABLE IS BUILT; the driver holds a case for each and never asks whether
101
+ * line tracking is on. Same driver, different table contents — G5 exactly.
102
+ */
103
+ export const OP_LIT_TRACK = 17
104
+ export const OP_RX_TRACK = 18
105
+ export const OP_NODE_TRACK = 19
106
+ /**
107
+ * `SCOPE k c` — a `parser({ trivia })` / `rules({ trivia })` scope.
108
+ *
109
+ * `k` is the scope's trivia COMBINATOR in the const pool. The driver installs it
110
+ * on `ctx.trivia` for the duration and restores the outer one after, which is
111
+ * how the runtime's own `advanceTrivia` fast scanner gets reached — the same
112
+ * shared machinery the interpreter uses, not a second copy of it.
113
+ */
114
+ export const OP_SCOPE = 20
115
+ /**
116
+ * `EXPECT c e` — `expect()`. Never fails: on a failed child it yields a
117
+ * zero-width `ParseError` value carrying the expected set at `e`.
118
+ */
119
+ export const OP_EXPECT = 21
120
+ /**
121
+ * `SEQX f n c1 … cn` — a `transform()` whose child is a `sequence()`.
122
+ *
123
+ * That pair is the dominant shape in every grammar here (json is nine of them),
124
+ * and running it as two rows costs two switch dispatches and two JS call frames
125
+ * per rule invocation where the emitted code pays neither. Fusing them into one
126
+ * row halves that for the shape that occurs most.
127
+ */
128
+ export const OP_SEQX = 22
129
+ /**
130
+ * `SCAN s` — the scanning constructs. `s` indexes a `ScanSpec` in `prog.scans`.
131
+ *
132
+ * WAS `OP_CALL k`, which parked the LIVE combinator in the const pool. That ran
133
+ * correctly and made the program unprintable — `emitConst` refuses a live object
134
+ * — so no shipping grammar could be emitted at all: all four jess dialects use
135
+ * `scanTo()`, three of them also `balanced()`. The number is reused because
136
+ * `OP_CALL` has no other user and nothing in the const pool may be a live object
137
+ * any more; that is now an invariant rather than a convention.
138
+ *
139
+ * Neither construct needs a live object. Both are DESCRIBED by data:
140
+ *
141
+ * `scanTo()` is a sentinel, an ordered skipper list, and two flags. Sentinel
142
+ * and skippers are ordinary grammar-graph combinators, so they are
143
+ * encoded as ordinary table SUBTREES and referenced by offset.
144
+ * `balanced()` is an open string, a close string, an own-skip list and two
145
+ * flags. Its `_def` is its EAGER interior (per-call skip only) and
146
+ * its ambient re-resolution lives on `.parse`, so encoding it from
147
+ * `_def` builds the wrong parser — the spec carries the
148
+ * CONSTRUCTOR ARGUMENTS instead, and `balanced()` itself rebuilds.
149
+ *
150
+ * The driver does not re-implement either scan. `resolveTable`'s pool rebuilds
151
+ * each spec with the SHARED constructor (`scanTo`/`balanced`), handing it
152
+ * subtree-backed combinators, exactly as `triviaSpecs` rebuilds trivia with the
153
+ * shared `classifiedTrivia`. So there is one implementation of each, and the
154
+ * table carries only its arguments.
155
+ */
156
+ export const OP_SCAN = 23
157
+ /**
158
+ * `FIELD k c` — `field(name, parser)`. `k` indexes the NAME in the const pool.
159
+ *
160
+ * Runs the child and, on success, records `{ name, value, span }` into
161
+ * `ctx._fields` for the nearest enclosing `node()` to assemble. The recording is
162
+ * conditional on `ctx._fields` being live, exactly as `src/combinators/map.ts`
163
+ * has it — a field outside any field-reading node costs nothing.
164
+ */
165
+ export const OP_FIELD = 24
166
+ /**
167
+ * `LIT_CI k fx` — a case-insensitive `literal()`.
168
+ *
169
+ * Its own row rather than a flag on `LIT`, so the hot exact-match path keeps a
170
+ * bare `startsWith`. NOTE it yields the INPUT's casing, not the literal's —
171
+ * `literal.ts:86` returns `input.slice(pos, end)` — so a node built from it
172
+ * carries the source text. Returning the literal would silently normalise case.
173
+ */
174
+ export const OP_LIT_CI = 27
175
+ /** `LIT_CI_TRACK k fx` — the line-tracking twin of `LIT_CI`. */
176
+ export const OP_LIT_CI_TRACK = 28
177
+ /**
178
+ * `TOKEN c` — `token()`. Clears trivia AND every capture sink for the child,
179
+ * then contributes ONE leaf spanning the whole match.
180
+ *
181
+ * Was `OP_CALL` (a live combinator in the const pool), which ran correctly but
182
+ * made the table unprintable — `emitConst` refuses non-serialisable entries, so
183
+ * no grammar using `token()` could be emitted as a module. Nothing about it
184
+ * needs a live object: it is save / clear / run / restore / one leaf.
185
+ */
186
+ export const OP_TOKEN = 29
187
+ /**
188
+ * `SCOPE_CAP k c` — a `parser({ trivia, captureTrivia: true })` scope. Identical
189
+ * operands to `SCOPE`; it additionally sets `ctx.captureTrivia` for the child.
190
+ *
191
+ * A SEPARATE OPCODE rather than a third operand on `SCOPE`, for two reasons.
192
+ * Widening an instruction means every walker that knows its shape has to learn
193
+ * the new one, and there is no central arity table here to change in one place.
194
+ * And the driver should SELECT this piece, not test a flag inside the scope
195
+ * piece — capture is fixed for the whole parse, so it is an assembly decision.
196
+ *
197
+ * The interpreter's equivalent is `grammar.ts:129`:
198
+ * `if (opts.captureTrivia || _ctx?.captureTrivia) ctx.captureTrivia = true`.
199
+ * Note the INHERITANCE — an inner scope does not switch capture back off, which
200
+ * is why this restores the saved value rather than writing `false`.
201
+ */
202
+ export const OP_SCOPE_CAP = 30
203
+ /**
204
+ * `WITHCTX k c` — `withCtx(extra, c)`. `k` is `extra` in the const pool.
205
+ *
206
+ * SAVE / RESTORE, matching `withCtx.ts`. Both used to CLONE the context, which
207
+ * scoped far more than the state: every scalar the child wrote on `ctx` landed
208
+ * on the clone and died with it, `_fe` / `_fx` included, so a failing `withCtx`
209
+ * subtree contributed nothing to the parent's expected set. That isolation was
210
+ * an implementation detail nobody asked for, not the combinator's contract, so
211
+ * it was fixed at the source rather than mirrored here.
212
+ */
213
+ export const OP_WITHCTX = 31
214
+ /**
215
+ * `GUARD f e` — `gate(predicate)`. Zero-width: runs `fns[f]` against
216
+ * `ctx.state` and yields `null` at `pos`, or fails with the expected set at `e`.
217
+ *
218
+ * NOT `OP_GATE`, which is the first-CHAR gate — different question, different
219
+ * operand (a char class, not a predicate). The names are close because the
220
+ * combinator was renamed to `gate()` at the API surface while its def tag stayed
221
+ * `guard`; the opcode follows the TAG, since that is what the encoder switches on.
222
+ *
223
+ * Its first set is `any` (a state predicate cannot narrow the input), so a
224
+ * `gate()` leading a choice arm poisons that arm's first-char dispatch. That is
225
+ * a grammar-authoring caveat, not a lowering one.
226
+ */
227
+ export const OP_GUARD = 32
228
+ /**
229
+ * `ADJ neg kinds fx` — `adjacent()` / `notAdjacent()`. `neg` is 1 for
230
+ * `notAdjacent`, `kinds` indexes the category filter (a `readonly string[]`) in
231
+ * the const pool or is −1, and `fx` is the expected set.
232
+ *
233
+ * A BOUNDARY TEST, NOT A TERM, and that is the whole reason it is its own
234
+ * opcode rather than a zero-width leaf like `OP_GUARD`. It asks whether trivia
235
+ * sat between the PREVIOUS term and here, so it must be evaluated at the
236
+ * sequence cursor — BEFORE the ambient trivia scan that precedes an ordinary
237
+ * non-first term. A piece handed the post-scan position would find the gap
238
+ * already consumed and answer "adjacent" every time, silently: `adjacent()`
239
+ * would become a no-op and `notAdjacent()` a guaranteed failure. So the SEQ
240
+ * pieces read this row at assembly and run the test themselves, exactly as
241
+ * `sequence()` forks `parseAdjacent` (combinators/sequence.ts:118) and as
242
+ * codegen lowers it at the boundary (codegen.ts:1765).
243
+ *
244
+ * Reached as a row in its own right only where there IS no boundary — a bare
245
+ * choice arm, a `node()` body, a repeat item. The interpreter throws there
246
+ * (adjacency.ts) rather than answering a question that was never asked, and so
247
+ * does the driver: same sentence, from `adjacencyMisuse`.
248
+ *
249
+ * The kind filter is resolved against the ACTIVE trivia table at parse time,
250
+ * matching the interpreter — a scope can swap the table, so it is not an
251
+ * assembly-time fact. (Codegen resolves it at COMPILE time and therefore
252
+ * reports an unlabelled table or an unknown category earlier; both engines
253
+ * raise the same `TypeError`, only the moment differs.)
254
+ */
255
+ export const OP_ADJ = 33
256
+ /**
257
+ * `GREEDY sup n w1 a1 … wn an` — `choice(strategy = greedyClassify)`.
258
+ *
259
+ * NOT a choice. `choice()` auto-selects this strategy (choice.ts:186-202) for the
260
+ * canonical identifier-vs-keyword shape — ONE regex arm that subsumes every other
261
+ * arm, all of which are literals — and it runs a DIFFERENT execution, not a
262
+ * different arm order: the regex arm runs, and then the match is RE-ATTRIBUTED by
263
+ * string equality to a literal arm, whose transform chain is what produces the
264
+ * value (choice.ts:124-136). Encoding it as an ordered `OP_CHOICE` would let the
265
+ * regex arm win every time; the parse would still succeed and only the VALUE and
266
+ * the tree would move. So it gets its own row.
267
+ *
268
+ * `sup` is the super arm's offset, `n` the number of classified literals, and
269
+ * each pair is `(const-pool index of the literal string, that arm's offset)`.
270
+ *
271
+ * The literal arm is RE-RUN at `pos` rather than having its transform chain
272
+ * applied to a known value, and the two are the same thing here: the classified
273
+ * word IS the arm's literal, so `literal()` re-matches at `pos` over exactly
274
+ * `[pos, END)` and every transform then sees the same `(value, span)` pair the
275
+ * interpreter's `applyTransforms` passes. `getCoreLiteralValue` admits only a
276
+ * case-SENSITIVE literal under `transform` wrappers, so the re-run cannot fail
277
+ * and cannot land at a different end. What it does do is push a SECOND CST leaf,
278
+ * which is why the capture sinks are rolled back to the pre-`sup` mark first —
279
+ * leaving exactly one leaf, with the same text and span the interpreter's kept
280
+ * regex leaf has.
281
+ *
282
+ * On a failed `sup` the failure propagates VERBATIM (the regex's own expected
283
+ * set), not the union of the arms — choice.ts:126 returns `superResult` itself,
284
+ * and codegen's `emitGreedyClassify` reports `deriveExpected(superParser)`.
285
+ */
286
+ export const OP_GREEDY = 34
287
+ /**
288
+ * `REJECT c n t1 o1 … tn on` — one choice arm plus its `autoNot` checks.
289
+ *
290
+ * `autoNot` is the OTHER thing `choice()` computes on its own (choice.ts:55,
291
+ * 325-346): for a literal arm, the inline lookahead that a LATER arm — a longer
292
+ * literal with this one as a prefix, or a regex that subsumes it — would have
293
+ * consumed more. It runs AFTER the arm has already succeeded and can still
294
+ * reject it, so a matched arm loses and a later arm wins (choice.ts:160-164).
295
+ * Ignoring it lowers `if` out of `iffy` and the parse still succeeds.
296
+ *
297
+ * `c` is the arm's offset; each check is a pair `(kind, operand)` — kind 0 is
298
+ * `startsWith`, operand a const-pool string; kind 1 is `firstSet`, operand a
299
+ * char-class index. Both are tested at the arm's END, mirroring `autoNotFires`.
300
+ *
301
+ * Rejection returns `FAIL` and CLEARS `_fc`, because the interpreter's rejection
302
+ * is a `continue` — "pretend this arm was never entered" — whereas an ordinary
303
+ * failing arm's committed flag cuts the whole choice. The enclosing choice does
304
+ * the capture-sink rollback, exactly as it does for a failing arm.
305
+ *
306
+ * A site with any `autoNot` can never take the O(1) first-char dispatch: a check
307
+ * exists only when a later arm shares the arm's leading character, so those two
308
+ * arms' classes intersect and `resolveDispatch` reports the site non-exclusive.
309
+ */
310
+ export const OP_REJECT = 35
311
+ /**
312
+ * `ARMGATE f c e` — one choice arm plus its PER-ARM state gate,
313
+ * `choice({ gate, combinator })`. `f` indexes the predicate in `prog.fns`, `c` is
314
+ * the arm's offset, and `e` is the arm's own expected set.
315
+ *
316
+ * NOT `OP_GUARD`, and the difference is the entire reason the option exists.
317
+ * `gate()` is a zero-width TERM whose first set is `any`, so leading an arm with
318
+ * one replaces that arm's first set and collapses the whole choice from O(1)
319
+ * first-char dispatch to the ordered `firstMatch` loop (docs/guide/
320
+ * first-char-gating.md lists that as a known gating defect). This row wraps the
321
+ * arm INSTEAD of sitting inside it, so the encoder still reads the arm's own
322
+ * first set for `disp` and the site keeps its dispatch slot.
323
+ *
324
+ * A blocked arm is SKIPPED, not failed — `choice.ts:150` is `continue`. The row
325
+ * therefore clears `_fc` on the gate-false path exactly as `OP_REJECT` does:
326
+ * "pretend this arm was never entered", so no cut it might have raised survives
327
+ * to cut the choice. It still reports `e`, because the OTHER path a blocked arm
328
+ * can be reached by is first-char dispatch, where skip-and-retry and
329
+ * fail-the-choice are the same thing (choice.ts:23-34, :100-105) and the
330
+ * interpreter answers with `deriveExpected(arm)` — this set.
331
+ *
332
+ * The predicate is a live function, like `OP_GUARD`'s, so a grammar using one is
333
+ * runtime-only for `emitTableModule` unless `fnSources` are supplied.
334
+ */
335
+ export const OP_ARMGATE = 36
336
+ /**
337
+ * `LIVE f` — RUN A HAND-WRITTEN COMBINATOR through its own `.parse`. `f` indexes
338
+ * the combinator in `prog.fns`.
339
+ *
340
+ * NOT the old `OP_CALL` (numbers 23 and 29 both record having been it): that row
341
+ * parked live combinators for `scanTo`/`balanced`/`token` in the CONST pool, and
342
+ * the fix was to describe those constructs AS DATA, which they are. This is the
343
+ * opposite case and the only one left: `Combinator` is a PUBLIC interface, so a
344
+ * caller can hand `compile()` a parser whose `_def.tag` no encoder will ever
345
+ * know. There is nothing to describe — the behaviour lives in a closure the
346
+ * library never sees.
347
+ *
348
+ * It exists because codegen accepts exactly this and delegates at run time
349
+ * (`compiler/codegen.ts`, `emitRuntimeFallback`'s `_rp[i].parse(...)` row). The
350
+ * encoder used to throw `UnsupportedConstruct`, so the table lowering REJECTED
351
+ * grammars the source lowering compiles, and the two lowerings must accept the
352
+ * same language.
353
+ *
354
+ * The cost is stated, not hidden: a live combinator is not data, so a program
355
+ * holding one is `runtimeOnly` — it runs, and `emitTableModule` refuses to print
356
+ * it BY NAME. Codegen degrades identically (a non-empty `runtimeParsers` makes
357
+ * `inlineExpression` null). It never appears for a construct the library itself
358
+ * builds; reaching it means the encoder met a foreign `_def`.
359
+ *
360
+ * The child result is the real interpreter's, so `expected`/`span` propagate
361
+ * verbatim and its `committed` flag is copied into `_fc`, as codegen's does.
362
+ */
363
+ export const OP_LIVE = 37
364
+ /**
365
+ * `ATTEMPT c` — `attempt()`, the transactional ordered-choice arm.
366
+ *
367
+ * WAS A TRANSPARENT WRAPPER (`case 'attempt': return this.node(d.parser).ip`),
368
+ * which is correct for exactly one placement — an arm of an `OP_CHOICE`, whose
369
+ * per-arm loop already saves and restores the eight capture sinks. Anywhere else
370
+ * — a `sequence()` term, a repeat item, a `node()` body — a failed transaction
371
+ * left its CST leaves, raw children, fields, recovery diagnostics and trivia-log
372
+ * entries behind, and reported the failure at the INNER position rather than
373
+ * re-anchored at the transaction's entry. Both are `attempt()`'s whole contract
374
+ * (`combinators/attempt.ts`), not a choice-arm detail.
375
+ *
376
+ * One child operand and no expected-set operand: on a non-committed inner
377
+ * failure the row keeps the inner's `_fx` VERBATIM and only re-anchors `_fe` to
378
+ * `pos`, exactly as `attempt.ts` returns `{ expected: result.expected, span:
379
+ * { start: pos, end: pos } }`. A committed failure propagates untouched — the
380
+ * rollback still happens, the re-anchor does not.
381
+ *
382
+ * The interpreter's first-set fail-fast guard is NOT lowered. It is an
383
+ * optimisation that reports `deriveExpected(parser)` in place of the start-fail
384
+ * the inner would have produced; running the inner produces that set for real,
385
+ * so the row is behaviourally the guard's post-condition without the second
386
+ * definition of what the inner expects.
387
+ */
388
+ export const OP_ATTEMPT = 38
389
+ /**
390
+ * `LABEL c fx` — `label(name, parser)`. `fx` is the one-element set `[name]`.
391
+ *
392
+ * WAS A TRANSPARENT WRAPPER, and a label is not transparent: `map.ts:84` returns
393
+ * `{ expected: [name], span: result.span }` on a failed child, i.e. it REPLACES
394
+ * the child's expected set and keeps the child's span. Dropping the row dropped
395
+ * the whole point of the combinator — a table lowering reported
396
+ * `['/[a-z]+/']` where the grammar had said `label('identifier', …)`, so every
397
+ * labelled diagnostic in every grammar regressed to raw regex source.
398
+ *
399
+ * `_fe` IS NOT TOUCHED, deliberately: `label()` keeps `result.span`, so the
400
+ * failure stays where the child put it.
401
+ */
402
+ export const OP_LABEL = 39
403
+ /**
404
+ * `COV c id on` — A GRAMMAR-COVERAGE COUNTER SITE. Emitted ONLY into a table
405
+ * encoded with `TableSettings.coverage`; an ordinary table contains no such row,
406
+ * which is why turning coverage off costs an ordinary build exactly zero bytes.
407
+ *
408
+ * `c` is the wrapped child's offset — deliberately at `ip + 1`, the slot every
409
+ * other single-child wrapper uses, so `collapseIndirection`, `inspect.ts` and
410
+ * `exec.ts` each gain this opcode by adding it to an existing `case` list rather
411
+ * than by growing a new shape. `id` indexes `prog.cov`, the definition pool.
412
+ *
413
+ * `on` is 0 for ENTRY and 1 for SUCCESS, and it is read at ASSEMBLY TIME, never
414
+ * per parse: `assemble.ts` picks one of two closures from it, exactly as it picks
415
+ * pieces from `RunCfg`. The two phases are not decoration — the source lowering
416
+ * credits a RULE on entry (`codegen.ts:4529`) and a choice arm, dispatch arm or
417
+ * label only once it has SUCCEEDED, and a table that credited all four alike
418
+ * would report a different denominator's worth of hits than the engine it is a
419
+ * drop-in for.
420
+ *
421
+ * THE ROW IS WHY THIS IS NOT A SIDE TABLE keyed by code offset. `encode.ts`
422
+ * memoises by combinator IDENTITY, so one `g.X` reference object that is an arm
423
+ * of three different choices is ONE row — and an offset→id map would have to
424
+ * credit all three arms whenever any one of them ran. A wrapper row is per SITE,
425
+ * which is the granularity the IDs are minted at.
426
+ */
427
+ export const OP_COV = 40
428
+ /**
429
+ * `DISPATCH sel d other otherRouted n a1 … an` — `dispatch()`.
430
+ *
431
+ * `sel` is the selector's offset, `d` indexes a dispatch table in `prog.dsp`,
432
+ * `other` is the `otherwise()` offset (or −1), `otherRouted` is 1 when the
433
+ * fallback consumes the routed token, and `a1…an` are the arm offsets. Arm
434
+ * `usesRouted` bits live in the `dsp` entry beside the key maps.
435
+ *
436
+ * The selector runs ONCE; the key it returns picks the arm. That is the whole
437
+ * point of `dispatch()` over a `choice()` of arms that each re-parse the opener.
438
+ */
439
+ export const OP_DISPATCH = 25
440
+ /**
441
+ * `ROUTED fallback` — `routed()`. Yields the token the enclosing `dispatch()`
442
+ * already consumed, so the selected branch can own it. `fallback` is an offset
443
+ * or −1; it runs when there is no routed token at this position.
444
+ */
445
+ export const OP_ROUTED = 26
446
+
447
+ export const OP_NAMES: Record<number, string> = {
448
+ [OP_LIT]: 'LIT', [OP_RX]: 'RX', [OP_SEQ]: 'SEQ', [OP_SEQV]: 'SEQV',
449
+ [OP_CHOICE]: 'CHOICE', [OP_REP]: 'REP', [OP_REPV]: 'REPV', [OP_OPT]: 'OPT',
450
+ [OP_XFORM]: 'XFORM', [OP_NODE]: 'NODE', [OP_RULE]: 'RULE', [OP_GATE]: 'GATE',
451
+ [OP_NOT]: 'NOT', [OP_PEEK]: 'PEEK', [OP_LEAF]: 'LEAF', [OP_EMPTY]: 'EMPTY',
452
+ [OP_LIT_TRACK]: 'LIT_TRACK', [OP_RX_TRACK]: 'RX_TRACK', [OP_NODE_TRACK]: 'NODE_TRACK',
453
+ [OP_SCOPE]: 'SCOPE', [OP_EXPECT]: 'EXPECT', [OP_SEQX]: 'SEQX', [OP_SCAN]: 'SCAN',
454
+ [OP_FIELD]: 'FIELD', [OP_LIT_CI]: 'LIT_CI', [OP_LIT_CI_TRACK]: 'LIT_CI_TRACK', [OP_DISPATCH]: 'DISPATCH', [OP_ROUTED]: 'ROUTED',
455
+ [OP_TOKEN]: 'TOKEN', [OP_SCOPE_CAP]: 'SCOPE_CAP', [OP_WITHCTX]: 'WITHCTX', [OP_GUARD]: 'GUARD',
456
+ [OP_ADJ]: 'ADJ',
457
+ [OP_GREEDY]: 'GREEDY', [OP_REJECT]: 'REJECT', [OP_ARMGATE]: 'ARMGATE',
458
+ [OP_LIVE]: 'LIVE', [OP_ATTEMPT]: 'ATTEMPT', [OP_LABEL]: 'LABEL', [OP_COV]: 'COV',
459
+ }