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
@@ -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,467 @@
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 policy` — a policy-bearing `parser({ 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
+ * Synthetic rule-entry/restoration scopes use `OP_SCOPE_PLAIN` so a zero
114
+ * policy stays a three-word row without changing this opcode's ABI.
115
+ */
116
+ export const OP_SCOPE = 20
117
+ /**
118
+ * `EXPECT c e` — `expect()`. Never fails: on a failed child it yields a
119
+ * zero-width `ParseError` value carrying the expected set at `e`.
120
+ */
121
+ export const OP_EXPECT = 21
122
+ /**
123
+ * `SEQX f n c1 … cn` — a `transform()` whose child is a `sequence()`.
124
+ *
125
+ * That pair is the dominant shape in every grammar here (json is nine of them),
126
+ * and running it as two rows costs two switch dispatches and two JS call frames
127
+ * per rule invocation where the emitted code pays neither. Fusing them into one
128
+ * row halves that for the shape that occurs most.
129
+ */
130
+ export const OP_SEQX = 22
131
+ /**
132
+ * `SCAN s` — the scanning constructs. `s` indexes a `ScanSpec` in `prog.scans`.
133
+ *
134
+ * WAS `OP_CALL k`, which parked the LIVE combinator in the const pool. That ran
135
+ * correctly and made the program unprintable — `emitConst` refuses a live object
136
+ * — so no shipping grammar could be emitted at all: all four jess dialects use
137
+ * `scanTo()`, three of them also `balanced()`. The number is reused because
138
+ * `OP_CALL` has no other user and nothing in the const pool may be a live object
139
+ * any more; that is now an invariant rather than a convention.
140
+ *
141
+ * Neither construct needs a live object. Both are DESCRIBED by data:
142
+ *
143
+ * `scanTo()` is a sentinel, an ordered skipper list, and two flags. Sentinel
144
+ * and skippers are ordinary grammar-graph combinators, so they are
145
+ * encoded as ordinary table SUBTREES and referenced by offset.
146
+ * `balanced()` is an open string, a close string, an own-skip list and two
147
+ * flags. Its `_def` is its EAGER interior (per-call skip only) and
148
+ * its ambient re-resolution lives on `.parse`, so encoding it from
149
+ * `_def` builds the wrong parser — the spec carries the
150
+ * CONSTRUCTOR ARGUMENTS instead, and `balanced()` itself rebuilds.
151
+ *
152
+ * The driver does not re-implement either scan. `resolveTable`'s pool rebuilds
153
+ * each spec with the SHARED constructor (`scanTo`/`balanced`), handing it
154
+ * subtree-backed combinators, exactly as `triviaSpecs` rebuilds trivia with the
155
+ * shared `classifiedTrivia`. So there is one implementation of each, and the
156
+ * table carries only its arguments.
157
+ */
158
+ export const OP_SCAN = 23
159
+ /**
160
+ * `FIELD k c` — `field(name, parser)`. `k` indexes the NAME in the const pool.
161
+ *
162
+ * Runs the child and, on success, records `{ name, value, span }` into
163
+ * `ctx._fields` for the nearest enclosing `node()` to assemble. The recording is
164
+ * conditional on `ctx._fields` being live, exactly as `src/combinators/map.ts`
165
+ * has it — a field outside any field-reading node costs nothing.
166
+ */
167
+ export const OP_FIELD = 24
168
+ /**
169
+ * `LIT_CI k fx` — a case-insensitive `literal()`.
170
+ *
171
+ * Its own row rather than a flag on `LIT`, so the hot exact-match path keeps a
172
+ * bare `startsWith`. NOTE it yields the INPUT's casing, not the literal's —
173
+ * `literal.ts:86` returns `input.slice(pos, end)` — so a node built from it
174
+ * carries the source text. Returning the literal would silently normalise case.
175
+ */
176
+ export const OP_LIT_CI = 27
177
+ /** `LIT_CI_TRACK k fx` — the line-tracking twin of `LIT_CI`. */
178
+ export const OP_LIT_CI_TRACK = 28
179
+ /**
180
+ * `TOKEN c` — `token()`. Clears trivia AND every capture sink for the child,
181
+ * then contributes ONE leaf spanning the whole match.
182
+ *
183
+ * Was `OP_CALL` (a live combinator in the const pool), which ran correctly but
184
+ * made the table unprintable — `emitConst` refuses non-serialisable entries, so
185
+ * no grammar using `token()` could be emitted as a module. Nothing about it
186
+ * needs a live object: it is save / clear / run / restore / one leaf.
187
+ */
188
+ export const OP_TOKEN = 29
189
+ /**
190
+ * `SCOPE_CAP k c` — a `parser({ trivia, captureTrivia: true })` scope. Identical
191
+ * operands to `SCOPE`; it additionally sets `ctx.captureTrivia` for the child.
192
+ *
193
+ * A SEPARATE OPCODE rather than a third operand on `SCOPE`, for two reasons.
194
+ * Widening an instruction means every walker that knows its shape has to learn
195
+ * the new one, and there is no central arity table here to change in one place.
196
+ * And the driver should SELECT this piece, not test a flag inside the scope
197
+ * piece — capture is fixed for the whole parse, so it is an assembly decision.
198
+ *
199
+ * The interpreter's equivalent is `grammar.ts:129`:
200
+ * `if (opts.captureTrivia || _ctx?.captureTrivia) ctx.captureTrivia = true`.
201
+ * Note the INHERITANCE — an inner scope does not switch capture back off, which
202
+ * is why this restores the saved value rather than writing `false`.
203
+ */
204
+ export const OP_SCOPE_CAP = 30
205
+ /**
206
+ * `WITHCTX k c` — `withCtx(extra, c)`. `k` is `extra` in the const pool.
207
+ *
208
+ * SAVE / RESTORE, matching `withCtx.ts`. Both used to CLONE the context, which
209
+ * scoped far more than the state: every scalar the child wrote on `ctx` landed
210
+ * on the clone and died with it, `_fe` / `_fx` included, so a failing `withCtx`
211
+ * subtree contributed nothing to the parent's expected set. That isolation was
212
+ * an implementation detail nobody asked for, not the combinator's contract, so
213
+ * it was fixed at the source rather than mirrored here.
214
+ */
215
+ export const OP_WITHCTX = 31
216
+ /**
217
+ * `GUARD f e` — `gate(predicate)`. Zero-width: runs `fns[f]` against
218
+ * `ctx.state` and yields `null` at `pos`, or fails with the expected set at `e`.
219
+ *
220
+ * NOT `OP_GATE`, which is the first-CHAR gate — different question, different
221
+ * operand (a char class, not a predicate). The names are close because the
222
+ * combinator was renamed to `gate()` at the API surface while its def tag stayed
223
+ * `guard`; the opcode follows the TAG, since that is what the encoder switches on.
224
+ *
225
+ * Its first set is `any` (a state predicate cannot narrow the input), so a
226
+ * `gate()` leading a choice arm poisons that arm's first-char dispatch. That is
227
+ * a grammar-authoring caveat, not a lowering one.
228
+ */
229
+ export const OP_GUARD = 32
230
+ /**
231
+ * `ADJ neg kinds fx` — `adjacent()` / `notAdjacent()`. `neg` is 1 for
232
+ * `notAdjacent`, `kinds` indexes the category filter (a `readonly string[]`) in
233
+ * the const pool or is −1, and `fx` is the expected set.
234
+ *
235
+ * A BOUNDARY TEST, NOT A TERM, and that is the whole reason it is its own
236
+ * opcode rather than a zero-width leaf like `OP_GUARD`. It asks whether trivia
237
+ * sat between the PREVIOUS term and here, so it must be evaluated at the
238
+ * sequence cursor — BEFORE the ambient trivia scan that precedes an ordinary
239
+ * non-first term. A piece handed the post-scan position would find the gap
240
+ * already consumed and answer "adjacent" every time, silently: `adjacent()`
241
+ * would become a no-op and `notAdjacent()` a guaranteed failure. So the SEQ
242
+ * pieces read this row at assembly and run the test themselves, exactly as
243
+ * `sequence()` forks `parseAdjacent` (combinators/sequence.ts:118) and as
244
+ * codegen lowers it at the boundary (codegen.ts:1765).
245
+ *
246
+ * Reached as a row in its own right only where there IS no boundary — a bare
247
+ * choice arm, a `node()` body, a repeat item. The interpreter throws there
248
+ * (adjacency.ts) rather than answering a question that was never asked, and so
249
+ * does the driver: same sentence, from `adjacencyMisuse`.
250
+ *
251
+ * The kind filter is resolved against the ACTIVE trivia table at parse time,
252
+ * matching the interpreter — a scope can swap the table, so it is not an
253
+ * assembly-time fact. (Codegen resolves it at COMPILE time and therefore
254
+ * reports an unlabelled table or an unknown category earlier; both engines
255
+ * raise the same `TypeError`, only the moment differs.)
256
+ */
257
+ export const OP_ADJ = 33
258
+ /**
259
+ * `GREEDY sup n w1 a1 … wn an` — `choice(strategy = greedyClassify)`.
260
+ *
261
+ * NOT a choice. `choice()` auto-selects this strategy (choice.ts:186-202) for the
262
+ * canonical identifier-vs-keyword shape — ONE regex arm that subsumes every other
263
+ * arm, all of which are literals — and it runs a DIFFERENT execution, not a
264
+ * different arm order: the regex arm runs, and then the match is RE-ATTRIBUTED by
265
+ * string equality to a literal arm, whose transform chain is what produces the
266
+ * value (choice.ts:124-136). Encoding it as an ordered `OP_CHOICE` would let the
267
+ * regex arm win every time; the parse would still succeed and only the VALUE and
268
+ * the tree would move. So it gets its own row.
269
+ *
270
+ * `sup` is the super arm's offset, `n` the number of classified literals, and
271
+ * each pair is `(const-pool index of the literal string, that arm's offset)`.
272
+ *
273
+ * The literal arm is RE-RUN at `pos` rather than having its transform chain
274
+ * applied to a known value, and the two are the same thing here: the classified
275
+ * word IS the arm's literal, so `literal()` re-matches at `pos` over exactly
276
+ * `[pos, END)` and every transform then sees the same `(value, span)` pair the
277
+ * interpreter's `applyTransforms` passes. `getCoreLiteralValue` admits only a
278
+ * case-SENSITIVE literal under `transform` wrappers, so the re-run cannot fail
279
+ * and cannot land at a different end. What it does do is push a SECOND CST leaf,
280
+ * which is why the capture sinks are rolled back to the pre-`sup` mark first —
281
+ * leaving exactly one leaf, with the same text and span the interpreter's kept
282
+ * regex leaf has.
283
+ *
284
+ * On a failed `sup` the failure propagates VERBATIM (the regex's own expected
285
+ * set), not the union of the arms — choice.ts:126 returns `superResult` itself,
286
+ * and codegen's `emitGreedyClassify` reports `deriveExpected(superParser)`.
287
+ */
288
+ export const OP_GREEDY = 34
289
+ /**
290
+ * `REJECT c n t1 o1 … tn on` — one choice arm plus its `autoNot` checks.
291
+ *
292
+ * `autoNot` is the OTHER thing `choice()` computes on its own (choice.ts:55,
293
+ * 325-346): for a literal arm, the inline lookahead that a LATER arm — a longer
294
+ * literal with this one as a prefix, or a regex that subsumes it — would have
295
+ * consumed more. It runs AFTER the arm has already succeeded and can still
296
+ * reject it, so a matched arm loses and a later arm wins (choice.ts:160-164).
297
+ * Ignoring it lowers `if` out of `iffy` and the parse still succeeds.
298
+ *
299
+ * `c` is the arm's offset; each check is a pair `(kind, operand)` — kind 0 is
300
+ * `startsWith`, operand a const-pool string; kind 1 is `firstSet`, operand a
301
+ * char-class index. Both are tested at the arm's END, mirroring `autoNotFires`.
302
+ *
303
+ * Rejection returns `FAIL` and CLEARS `_fc`, because the interpreter's rejection
304
+ * is a `continue` — "pretend this arm was never entered" — whereas an ordinary
305
+ * failing arm's committed flag cuts the whole choice. The enclosing choice does
306
+ * the capture-sink rollback, exactly as it does for a failing arm.
307
+ *
308
+ * A site with any `autoNot` can never take the O(1) first-char dispatch: a check
309
+ * exists only when a later arm shares the arm's leading character, so those two
310
+ * arms' classes intersect and `resolveDispatch` reports the site non-exclusive.
311
+ */
312
+ export const OP_REJECT = 35
313
+ /**
314
+ * `ARMGATE f c e` — one choice arm plus its PER-ARM state gate,
315
+ * `choice({ gate, combinator })`. `f` indexes the predicate in `prog.fns`, `c` is
316
+ * the arm's offset, and `e` is the arm's own expected set.
317
+ *
318
+ * NOT `OP_GUARD`, and the difference is the entire reason the option exists.
319
+ * `gate()` is a zero-width TERM whose first set is `any`, so leading an arm with
320
+ * one replaces that arm's first set and collapses the whole choice from O(1)
321
+ * first-char dispatch to the ordered `firstMatch` loop (docs/guide/
322
+ * first-char-gating.md lists that as a known gating defect). This row wraps the
323
+ * arm INSTEAD of sitting inside it, so the encoder still reads the arm's own
324
+ * first set for `disp` and the site keeps its dispatch slot.
325
+ *
326
+ * A blocked arm is SKIPPED, not failed — `choice.ts:150` is `continue`. The row
327
+ * therefore clears `_fc` on the gate-false path exactly as `OP_REJECT` does:
328
+ * "pretend this arm was never entered", so no cut it might have raised survives
329
+ * to cut the choice. It still reports `e`, because the OTHER path a blocked arm
330
+ * can be reached by is first-char dispatch, where skip-and-retry and
331
+ * fail-the-choice are the same thing (choice.ts:23-34, :100-105) and the
332
+ * interpreter answers with `deriveExpected(arm)` — this set.
333
+ *
334
+ * The predicate is a live function, like `OP_GUARD`'s, so a grammar using one is
335
+ * runtime-only for `emitTableModule` unless `fnSources` are supplied.
336
+ */
337
+ export const OP_ARMGATE = 36
338
+ /**
339
+ * `LIVE f` — RUN A HAND-WRITTEN COMBINATOR through its own `.parse`. `f` indexes
340
+ * the combinator in `prog.fns`.
341
+ *
342
+ * NOT the old `OP_CALL` (numbers 23 and 29 both record having been it): that row
343
+ * parked live combinators for `scanTo`/`balanced`/`token` in the CONST pool, and
344
+ * the fix was to describe those constructs AS DATA, which they are. This is the
345
+ * opposite case and the only one left: `Combinator` is a PUBLIC interface, so a
346
+ * caller can hand `compile()` a parser whose `_def.tag` no encoder will ever
347
+ * know. There is nothing to describe — the behaviour lives in a closure the
348
+ * library never sees.
349
+ *
350
+ * It exists because codegen accepts exactly this and delegates at run time
351
+ * (`compiler/codegen.ts`, `emitRuntimeFallback`'s `_rp[i].parse(...)` row). The
352
+ * encoder used to throw `UnsupportedConstruct`, so the table lowering REJECTED
353
+ * grammars the source lowering compiles, and the two lowerings must accept the
354
+ * same language.
355
+ *
356
+ * The cost is stated, not hidden: a live combinator is not data, so a program
357
+ * holding one is `runtimeOnly` — it runs, and `emitTableModule` refuses to print
358
+ * it BY NAME. Codegen degrades identically (a non-empty `runtimeParsers` makes
359
+ * `inlineExpression` null). It never appears for a construct the library itself
360
+ * builds; reaching it means the encoder met a foreign `_def`.
361
+ *
362
+ * The child result is the real interpreter's, so `expected`/`span` propagate
363
+ * verbatim and its `committed` flag is copied into `_fc`, as codegen's does.
364
+ */
365
+ export const OP_LIVE = 37
366
+ /**
367
+ * `ATTEMPT c` — `attempt()`, the transactional ordered-choice arm.
368
+ *
369
+ * WAS A TRANSPARENT WRAPPER (`case 'attempt': return this.node(d.parser).ip`),
370
+ * which is correct for exactly one placement — an arm of an `OP_CHOICE`, whose
371
+ * per-arm loop already saves and restores the eight capture sinks. Anywhere else
372
+ * — a `sequence()` term, a repeat item, a `node()` body — a failed transaction
373
+ * left its CST leaves, raw children, fields, recovery diagnostics and trivia-log
374
+ * entries behind, and reported the failure at the INNER position rather than
375
+ * re-anchored at the transaction's entry. Both are `attempt()`'s whole contract
376
+ * (`combinators/attempt.ts`), not a choice-arm detail.
377
+ *
378
+ * One child operand and no expected-set operand: on a non-committed inner
379
+ * failure the row keeps the inner's `_fx` VERBATIM and only re-anchors `_fe` to
380
+ * `pos`, exactly as `attempt.ts` returns `{ expected: result.expected, span:
381
+ * { start: pos, end: pos } }`. A committed failure propagates untouched — the
382
+ * rollback still happens, the re-anchor does not.
383
+ *
384
+ * The interpreter's first-set fail-fast guard is NOT lowered. It is an
385
+ * optimisation that reports `deriveExpected(parser)` in place of the start-fail
386
+ * the inner would have produced; running the inner produces that set for real,
387
+ * so the row is behaviourally the guard's post-condition without the second
388
+ * definition of what the inner expects.
389
+ */
390
+ export const OP_ATTEMPT = 38
391
+ /**
392
+ * `LABEL c fx` — `label(name, parser)`. `fx` is the one-element set `[name]`.
393
+ *
394
+ * WAS A TRANSPARENT WRAPPER, and a label is not transparent: `map.ts:84` returns
395
+ * `{ expected: [name], span: result.span }` on a failed child, i.e. it REPLACES
396
+ * the child's expected set and keeps the child's span. Dropping the row dropped
397
+ * the whole point of the combinator — a table lowering reported
398
+ * `['/[a-z]+/']` where the grammar had said `label('identifier', …)`, so every
399
+ * labelled diagnostic in every grammar regressed to raw regex source.
400
+ *
401
+ * `_fe` IS NOT TOUCHED, deliberately: `label()` keeps `result.span`, so the
402
+ * failure stays where the child put it.
403
+ */
404
+ export const OP_LABEL = 39
405
+ /**
406
+ * `COV c id on` — A GRAMMAR-COVERAGE COUNTER SITE. Emitted ONLY into a table
407
+ * encoded with `TableSettings.coverage`; an ordinary table contains no such row,
408
+ * which is why turning coverage off costs an ordinary build exactly zero bytes.
409
+ *
410
+ * `c` is the wrapped child's offset — deliberately at `ip + 1`, the slot every
411
+ * other single-child wrapper uses, so `collapseIndirection`, `inspect.ts` and
412
+ * `exec.ts` each gain this opcode by adding it to an existing `case` list rather
413
+ * than by growing a new shape. `id` indexes `prog.cov`, the definition pool.
414
+ *
415
+ * `on` is 0 for ENTRY and 1 for SUCCESS, and it is read at ASSEMBLY TIME, never
416
+ * per parse: `assemble.ts` picks one of two closures from it, exactly as it picks
417
+ * pieces from `RunCfg`. The two phases are not decoration — the source lowering
418
+ * credits a RULE on entry (`codegen.ts:4529`) and a choice arm, dispatch arm or
419
+ * label only once it has SUCCEEDED, and a table that credited all four alike
420
+ * would report a different denominator's worth of hits than the engine it is a
421
+ * drop-in for.
422
+ *
423
+ * THE ROW IS WHY THIS IS NOT A SIDE TABLE keyed by code offset. `encode.ts`
424
+ * memoises by combinator IDENTITY, so one `g.X` reference object that is an arm
425
+ * of three different choices is ONE row — and an offset→id map would have to
426
+ * credit all three arms whenever any one of them ran. A wrapper row is per SITE,
427
+ * which is the granularity the IDs are minted at.
428
+ */
429
+ export const OP_COV = 40
430
+ /**
431
+ * `SCOPE_PLAIN k c` — a synthetic ambient-trivia scope with policy fixed to zero.
432
+ * A distinct opcode prevents the next row's opcode from being read as policy.
433
+ */
434
+ export const OP_SCOPE_PLAIN = 41
435
+ /**
436
+ * `DISPATCH sel d other otherRouted n a1 … an` — `dispatch()`.
437
+ *
438
+ * `sel` is the selector's offset, `d` indexes a dispatch table in `prog.dsp`,
439
+ * `other` is the `otherwise()` offset (or −1), `otherRouted` is 1 when the
440
+ * fallback consumes the routed token, and `a1…an` are the arm offsets. Arm
441
+ * `usesRouted` bits live in the `dsp` entry beside the key maps.
442
+ *
443
+ * The selector runs ONCE; the key it returns picks the arm. That is the whole
444
+ * point of `dispatch()` over a `choice()` of arms that each re-parse the opener.
445
+ */
446
+ export const OP_DISPATCH = 25
447
+ /**
448
+ * `ROUTED fallback` — `routed()`. Yields the token the enclosing `dispatch()`
449
+ * already consumed, so the selected branch can own it. `fallback` is an offset
450
+ * or −1; it runs when there is no routed token at this position.
451
+ */
452
+ export const OP_ROUTED = 26
453
+
454
+ export const OP_NAMES: Record<number, string> = {
455
+ [OP_LIT]: 'LIT', [OP_RX]: 'RX', [OP_SEQ]: 'SEQ', [OP_SEQV]: 'SEQV',
456
+ [OP_CHOICE]: 'CHOICE', [OP_REP]: 'REP', [OP_REPV]: 'REPV', [OP_OPT]: 'OPT',
457
+ [OP_XFORM]: 'XFORM', [OP_NODE]: 'NODE', [OP_RULE]: 'RULE', [OP_GATE]: 'GATE',
458
+ [OP_NOT]: 'NOT', [OP_PEEK]: 'PEEK', [OP_LEAF]: 'LEAF', [OP_EMPTY]: 'EMPTY',
459
+ [OP_LIT_TRACK]: 'LIT_TRACK', [OP_RX_TRACK]: 'RX_TRACK', [OP_NODE_TRACK]: 'NODE_TRACK',
460
+ [OP_SCOPE]: 'SCOPE', [OP_EXPECT]: 'EXPECT', [OP_SEQX]: 'SEQX', [OP_SCAN]: 'SCAN',
461
+ [OP_FIELD]: 'FIELD', [OP_LIT_CI]: 'LIT_CI', [OP_LIT_CI_TRACK]: 'LIT_CI_TRACK', [OP_DISPATCH]: 'DISPATCH', [OP_ROUTED]: 'ROUTED',
462
+ [OP_TOKEN]: 'TOKEN', [OP_SCOPE_CAP]: 'SCOPE_CAP', [OP_WITHCTX]: 'WITHCTX', [OP_GUARD]: 'GUARD',
463
+ [OP_ADJ]: 'ADJ',
464
+ [OP_GREEDY]: 'GREEDY', [OP_REJECT]: 'REJECT', [OP_ARMGATE]: 'ARMGATE',
465
+ [OP_LIVE]: 'LIVE', [OP_ATTEMPT]: 'ATTEMPT', [OP_LABEL]: 'LABEL', [OP_COV]: 'COV',
466
+ [OP_SCOPE_PLAIN]: 'SCOPE_PLAIN',
467
+ }