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,429 @@
1
+ import type { FoldedProgram, TableProgram } from './program.ts'
2
+ import { resolveTable } from './program.ts'
3
+ import { EMITTED_PARAMS, Unemittable, emitAssemblySource } from './emit-assembly.ts'
4
+ import { cfgKey, type RunCfg } from './assemble.ts'
5
+
6
+ /**
7
+ * Print a program as the module a build emits.
8
+ *
9
+ * Everything here is DATA plus the author's own reducers. The recognizer is not
10
+ * in this string — it is `exec.ts`, imported from the runtime, shared by every
11
+ * grammar in the bundle and by every variant of each.
12
+ */
13
+
14
+ function jsString(s: string): string {
15
+ return JSON.stringify(s)
16
+ }
17
+
18
+ /** Emit a char-class pool in the shorter of the ordinary array and an
19
+ * equivalent delimiter-split string. Fall back when no delimiter is absent. */
20
+ function emitClassPool(classes: readonly string[]): string {
21
+ const array = `[${classes.map(jsString).join(',')}]`
22
+ if (classes.length < 2) return array
23
+ const delimiter = '|/~?^`'.split('').find(ch => classes.every(spec => !spec.includes(ch)))
24
+ if (delimiter === undefined) return array
25
+ const split = `${jsString(classes.join(delimiter))}.split(${jsString(delimiter)})`
26
+ return split.length < array.length ? split : array
27
+ }
28
+
29
+ /**
30
+ * Serialise one const-pool entry, or REFUSE it.
31
+ *
32
+ * The fallthrough was `JSON.stringify(v)`, which returns the VALUE `undefined`
33
+ * — not a string — for a function, a symbol, or `undefined` itself. Template
34
+ * interpolation then wrote the text `undefined` into the module, producing an
35
+ * artifact that loads and misbehaves rather than one that fails to build. A
36
+ * combinator object reaching here would emit `{}` for the same reason.
37
+ *
38
+ * Fail closed: the pool must only ever hold what the driver can read back.
39
+ */
40
+ function emitConst(v: unknown): string {
41
+ if (v instanceof RegExp) return `/${v.source}/${v.flags}`
42
+ if (typeof v === 'string') return jsString(v)
43
+ if (v === null || typeof v === 'number' || typeof v === 'boolean') return JSON.stringify(v)
44
+ // An ARRAY of those primitives round-trips exactly, so it belongs on the
45
+ // accept side of this guard rather than the refuse side. `node(tags)` parks a
46
+ // `readonly string[]` in the pool and the host reads it as its 8th argument;
47
+ // refusing it made every tags-bearing grammar unemittable for no reason the
48
+ // guard's own criterion supports. Nested arrays and objects still refuse.
49
+ if (Array.isArray(v) && v.every(x => x === null || typeof x === 'string' || typeof x === 'number' || typeof x === 'boolean')) {
50
+ return `[${v.map(x => (typeof x === 'string' ? jsString(x) : JSON.stringify(x))).join(',')}]`
51
+ }
52
+ // A PLAIN OBJECT whose values are those primitives (or arrays of them)
53
+ // round-trips exactly too, by the same criterion that admitted arrays above.
54
+ // `withCtx(extra, …)` parks one here — `{ inFn: true }` and the like — and
55
+ // refusing it made every withCtx-bearing grammar unemittable for no reason the
56
+ // guard supports. Only a null-prototype-safe plain object qualifies: anything
57
+ // with a class, a function value, or nesting beyond one array level refuses.
58
+ if (
59
+ typeof v === 'object' && v !== null && !Array.isArray(v) && !(v instanceof RegExp)
60
+ && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null)
61
+ && Object.values(v).every(x =>
62
+ x === null || typeof x === 'string' || typeof x === 'number' || typeof x === 'boolean'
63
+ || (Array.isArray(x) && x.every(y => y === null || typeof y === 'string' || typeof y === 'number' || typeof y === 'boolean')))
64
+ ) {
65
+ const entries = Object.entries(v).map(([key, val]) => `${jsString(key)}:${
66
+ typeof val === 'string' ? jsString(val)
67
+ : Array.isArray(val) ? `[${val.map(y => (typeof y === 'string' ? jsString(y) : JSON.stringify(y))).join(',')}]`
68
+ : JSON.stringify(val)}`)
69
+ return `{${entries.join(',')}}`
70
+ }
71
+ const shown = typeof v === 'object' ? Object.prototype.toString.call(v) : typeof v
72
+ throw new TypeError(
73
+ `emitConst: cannot serialise a const-pool entry of type ${shown}. ` +
74
+ 'Only strings, numbers, booleans, null and RegExp round-trip through the emitted module; ' +
75
+ 'anything else must be carried in prog.fns or refused at encode time.',
76
+ )
77
+ }
78
+
79
+ function emitTriviaSpec(t: import('./program.ts').TriviaSpec): string {
80
+ if (t.plain !== undefined) return `{arms:[],plain:[${jsString(t.plain[0])},${jsString(t.plain[1])}]}`
81
+ if (t.alts !== undefined) {
82
+ const alts = t.alts.map(a => `[${jsString(a[0])},${jsString(a[1])}]`).join(',')
83
+ return `{arms:[],alts:[${alts}],min:${t.min ?? 1}}`
84
+ }
85
+ return `{arms:[${t.arms.map(a => `[${jsString(a[0])},${jsString(a[1])},${jsString(a[2])}]`).join(',')}]}`
86
+ }
87
+
88
+ function emitRef(r: import('./program.ts').SubtreeRef): string {
89
+ return `[${r[0]},${r[1]}]`
90
+ }
91
+
92
+ function emitScanSpec(s: import('./program.ts').ScanSpec): string {
93
+ const parts = [`kind:${s.kind}`, `flags:${s.flags}`, `skip:[${s.skip.map(emitRef).join(',')}]`]
94
+ if (s.sentinel !== undefined) parts.push(`sentinel:${emitRef(s.sentinel)}`)
95
+ if (s.sent !== undefined) parts.push(`sent:${s.sent === null ? 'null' : jsString(s.sent)}`)
96
+ if (s.open !== undefined) parts.push(`open:${jsString(s.open)}`)
97
+ if (s.close !== undefined) parts.push(`close:${jsString(s.close)}`)
98
+ return `{${parts.join(',')}}`
99
+ }
100
+
101
+ function emitDispatchSpec(d: import('./program.ts').DispatchSpec): string {
102
+ return '{'
103
+ + `key:[${d.key.map(jsString).join(',')}],`
104
+ + `keyArm:[${d.keyArm.join(',')}],`
105
+ + `fold:[${d.fold.map(jsString).join(',')}],`
106
+ + `foldArm:[${d.foldArm.join(',')}],`
107
+ + `match:[${d.match.map(m => `[${m[0]},${jsString(m[1])},${jsString(m[2])},${m[3]}]`).join(',')}],`
108
+ + `routed:[${d.routed.join(',')}],`
109
+ + `expected:[${d.expected.map(jsString).join(',')}]`
110
+ + '}'
111
+ }
112
+
113
+ /**
114
+ * THE OPTION SETS A BUILD PRE-COMPILES, by default.
115
+ *
116
+ * `hostCst` and `trackLines` are ENCODE settings (`TableSettings`), so a program
117
+ * already fixes them — a table encoded for `hostMode: 'ast'` has no CST rows to
118
+ * select. `tolerant` is a per-CALL option (`run({ tolerant })`), so both answers
119
+ * are live for one artifact and both are emitted.
120
+ *
121
+ * `coverage` is never emitted: `emitAssemblySource` refuses a coverage assembly
122
+ * outright, so there is nothing to pre-compile and the closure engine is the
123
+ * correct answer. `probe` (`completionsAt`) is not emitted by default either —
124
+ * it is a language-service path, cold, and doubling every artifact for it is a
125
+ * cost with no reader. Both fall to the closure engine, which is OBSERVABLE on
126
+ * `Assembly.emitRefusal` rather than silent.
127
+ *
128
+ * Overgeneration is deliberate: an emitted variant nobody selects costs bytes
129
+ * and zero runtime.
130
+ */
131
+ export function defaultAssemblyCfgs(prog: TableProgram): RunCfg[] {
132
+ const hostCst = prog.hostMode === 'cst'
133
+ const trackLines = prog.lines === 1
134
+ return [false, true].map(tolerant => ({
135
+ hostCst, trackLines, tolerant, coverage: false, probe: false,
136
+ }))
137
+ }
138
+
139
+ /**
140
+ * Print the assemblies a build pre-compiled, as the `a:` field of the program
141
+ * literal — see `TableProgram.asm`.
142
+ *
143
+ * THE FACTORY IS A REAL FUNCTION LITERAL. This is a low-level experiment, not
144
+ * a normal compiler route: normal `compile()` and macro output both serialize
145
+ * `a:[]` and use the compact closure assembler. If a future specialization can
146
+ * meet semantic identity and the size budget, it must preserve that one public
147
+ * artifact contract rather than recreate the old runtime/macro split.
148
+ *
149
+ * A refusal is not an error. `Unemittable` names a construct the emitter does
150
+ * not lower; that option set simply gets no entry, and `assemble.ts` runs the
151
+ * closure engine for it and RECORDS why on `Assembly.emitRefusal`.
152
+ */
153
+ function emitAssemblies(prog: TableProgram, cfgs: readonly RunCfg[]): string[] {
154
+ /**
155
+ * `a:[]` IS NOT `a` ABSENT, and the difference is the whole property.
156
+ *
157
+ * The field's PRESENCE is the compiler artifact contract. That is what switches
158
+ * the runtime `Function` constructor off (`assemble.ts`), and it costs four
159
+ * bytes. Its CONTENTS are optional experimental factories; with none, the
160
+ * artifact runs the closure engine — no eval, no
161
+ * size growth, and the refusal readable on `Assembly.emitRefusal`.
162
+ *
163
+ * Pre-compiling is therefore a SPEED option, not a correctness one, and it is
164
+ * priced: two assemblies take json's module from 1,382 B to 58,823 B (42.6x)
165
+ * and css's from 8,987 B to 341,517 B (38.0x). That is the emitted engine's
166
+ * source, which the runtime used to build with `new Function` on every load
167
+ * instead of carrying. Defaulting it ON would hand back the 14x size win the
168
+ * table lowering exists for, so the default is OFF and `defaultAssemblyCfgs`
169
+ * is the one-liner for a consumer who wants emitted speed under a CSP.
170
+ *
171
+ * THE DEFAULT IS A MEASUREMENT, NOT A COMMITMENT. It rests on two numbers —
172
+ * the size cost above, and the absence of a demonstrated speed gap the
173
+ * pre-compiled path closes — and only the first of those is settled. A macro
174
+ * artifact measured ~29% slower than `tableRules` over the same grammar
175
+ * during 0.47 (36.0 vs 27.8 ms, 0/16 wins, -0.1% control, identical trees);
176
+ * that is being diagnosed as an emitted-PROGRAM difference, not an engine
177
+ * one, but if pre-compiling turns out to close it the default is an owner
178
+ * decision to reopen. Do not harden this into a design rule.
179
+ */
180
+ if (cfgs.length === 0) return ['a:[],']
181
+ const t = resolveTable(prog)
182
+ // The scan pool and the scan-skip sets are linked from subtrees, so their
183
+ // sites need emitted names too — same list `assemble.ts` builds.
184
+ const extraIps: number[] = []
185
+ for (const s of prog.scans ?? []) {
186
+ for (const r of s.skip) extraIps.push(r[0])
187
+ if (s.sentinel !== undefined) extraIps.push(s.sentinel[0])
188
+ }
189
+ for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0])
190
+
191
+ const out: string[] = []
192
+ const seen = new Set<number>()
193
+ for (const cfg of cfgs) {
194
+ const key = cfgKey(cfg)
195
+ if (seen.has(key)) continue
196
+ seen.add(key)
197
+ let em
198
+ try {
199
+ em = emitAssemblySource(t, prog, cfg, extraIps)
200
+ } catch (e) {
201
+ if (e instanceof Unemittable) continue
202
+ throw e
203
+ }
204
+ const plan = em.plan
205
+ out.push('{'
206
+ + `key:${key},`
207
+ + `factory:function(${EMITTED_PARAMS.join(',')}){${em.source}\n},`
208
+ + 'plan:{'
209
+ + `classes:[${plan.classes.map(r => `[${r.join(',')}]`).join(',')}],`
210
+ + `armExpected:[${plan.armExpected.map(r => `[${r.join(',')}]`).join(',')}],`
211
+ + `masks:[${plan.masks.join(',')}]`
212
+ + '},'
213
+ + `reached:[${[...em.reached].join(',')}]`
214
+ + '}')
215
+ }
216
+ return [`a:[${out.join(',')}],`]
217
+ }
218
+
219
+ export type EmitOptions = {
220
+ /** Name of the exported binding. */
221
+ readonly name?: string
222
+ /**
223
+ * Optional experimental emitted factories — see `defaultAssemblyCfgs`.
224
+ *
225
+ * The normal compiler and macro both emit `a:[]`: that is the canonical
226
+ * compact closure artifact and it never reaches the `Function` constructor.
227
+ * Supplying factories here is a low-level serialization experiment, not a
228
+ * second normal compilation path; it is not used by the macro plugin.
229
+ */
230
+ readonly assemblies?: readonly RunCfg[]
231
+ /**
232
+ * Sources for the author callbacks, in `prog.fns` order. A build has these
233
+ * from the module it is lowering; pass `undefined` to emit a placeholder and
234
+ * measure only the machinery.
235
+ */
236
+ readonly fnSources?: readonly string[]
237
+ /** Import specifier for the shared driver. */
238
+ readonly runtime?: string
239
+ /**
240
+ * NAME of the driver export to import from `runtime`, defaulting to the
241
+ * shipped one.
242
+ *
243
+ * `runtime` alone was not enough to say which engine a module binds, and that
244
+ * gap was load-bearing: `parseman/table` exports the ASSEMBLER as `tableRules`
245
+ * while `src/table/exec.ts` exports the reference INTERPRETER under a name
246
+ * that used to be identical. Pointing `runtime` at `exec.ts` therefore emitted
247
+ * a module that read as the shipped artifact and ran the reference engine.
248
+ * The reference export is now `execRules`, and a differential that wants it
249
+ * has to SAY so here — which is the whole point of separating the names.
250
+ */
251
+ readonly runtimeRef?: string
252
+ }
253
+
254
+ /** Refuse a program the printer cannot express, naming the CONSTRUCT. */
255
+ function assertPrintable(prog: TableProgram, who: string): void {
256
+ // Fail with the CONSTRUCT, not with a type name from inside the printer.
257
+ // Every one of these is expressible as data and simply not expressed yet, so
258
+ // the message says which one to go and lower.
259
+ if (prog.runtimeOnly !== undefined && prog.runtimeOnly.length > 0) {
260
+ throw new TypeError(
261
+ `${who}: this grammar is RUNTIME-ONLY — it parses correctly but cannot be `
262
+ + `printed as a module. Unlowered constructs: ${prog.runtimeOnly.join(', ')}. `
263
+ + `Each parks a live combinator in the const pool; each is expressible as table rows.`,
264
+ )
265
+ }
266
+ }
267
+
268
+ /**
269
+ * The program's fields as `key:value,` lines — the body of the object literal
270
+ * `tableRules` reads. Shared with the folded emitter, which prints exactly these
271
+ * for its ONE base table.
272
+ */
273
+ function programFields(prog: TableProgram, fns: readonly string[], opts: EmitOptions = {}): string[] {
274
+ return [
275
+ ...emitAssemblies(prog, opts.assemblies ?? []),
276
+ `c:[${prog.code.join(',')}],`,
277
+ `k:[${prog.k.map(emitConst).join(',')}],`,
278
+ `x:${emitClassPool(prog.cc)},`,
279
+ `e:[${prog.fx.map(f => `[${f.map(jsString).join(',')}]`).join(',')}],`,
280
+ `d:[${prog.disp.map(a => `[${a.join(',')}]`).join(',')}],`,
281
+ `r:${JSON.stringify(prog.rules)},`,
282
+ ...(prog.lines === 1 ? ['l:1,'] : []),
283
+ // EVERY FIELD `expandCompact` READS MUST BE WRITTEN HERE. `p`/`lb`/`rc`
284
+ // were read but never emitted: a dispatching grammar's module threw
285
+ // "Cannot read properties of undefined (reading 'byKey')" on every input,
286
+ // and a labelled-trivia grammar's entries carried no `_meta`, so
287
+ // `run({ rootTrivia })` rejected a grammar that plainly has labels — the
288
+ // exact failure the root-trivia work exists to prevent, one hop downstream.
289
+ ...(prog.dsp.length === 0 ? [] : [`p:[${prog.dsp.map(emitDispatchSpec).join(',')}],`]),
290
+ ...(prog.labels === undefined ? [] : [`lb:[${prog.labels.map(jsString).join(',')}],`]),
291
+ ...(prog.classified === 1 ? ['rc:1,'] : []),
292
+ // Without this a recovery table's MODULE loads as a strict one: the extra
293
+ // operands are still in `c`, but nothing selects the pieces that read them,
294
+ // so a tolerant parse of the emitted artifact silently collects no errors.
295
+ ...(prog.rec === 1 ? ['rv:1,'] : []),
296
+ // The coverage DENOMINATOR travels with the counter rows or not at all. An
297
+ // emitted module whose `c` stream holds `OP_COV` rows and whose `cv` pool went
298
+ // missing has no ids to credit and no definitions to divide by — the
299
+ // "no measurement reported as full coverage" shape, one hop downstream, which
300
+ // is why `assemble` throws on that combination rather than counting nothing.
301
+ ...(prog.cov === undefined ? [] : [`cv:[${prog.cov.map(([id, kind]) => `[${jsString(id)},${kind}]`).join(',')}],`]),
302
+ ...(prog.hostMode === undefined ? [] : [`h:${jsString(prog.hostMode)},`]),
303
+ ...(prog.triviaSpecs === undefined ? [] : [`tv:[${prog.triviaSpecs.map(emitTriviaSpec).join(',')}],`]),
304
+ ...(prog.scans === undefined ? [] : [`sc:[${prog.scans.map(emitScanSpec).join(',')}],`]),
305
+ // Both halves or neither: `scanSkipOf` without `scanSkip` installs nothing,
306
+ // and `scanSkip` without `scanSkipOf` installs it nowhere. Either way the
307
+ // module parses and silently skips a different set than the table it came
308
+ // from — the failure shape this lowering exists to remove.
309
+ ...(prog.scanSkip === undefined ? [] : [
310
+ `ss:[${prog.scanSkip.map(set => `[${set.map(emitRef).join(',')}]`).join(',')}],`,
311
+ `so:[${(prog.scanSkipOf ?? []).join(',')}],`,
312
+ ]),
313
+ `f:[${fns.join(',')}]`,
314
+ ]
315
+ }
316
+
317
+ export function emitTableModule(prog: TableProgram, opts: EmitOptions = {}): string {
318
+ assertPrintable(prog, 'emitTableModule')
319
+ const name = opts.name ?? 'grammar'
320
+ const runtime = opts.runtime ?? 'parseman/table'
321
+ const fns = opts.fnSources ?? prog.fns.map(() => '() => {}')
322
+ const ref = opts.runtimeRef ?? 'tableRules'
323
+ return [
324
+ `import { ${ref} } from ${jsString(runtime)}`,
325
+ `export const ${name} = /* @__PURE__ */ ${ref}({`,
326
+ ...programFields(prog, fns, opts),
327
+ `})`,
328
+ ].join('\n')
329
+ }
330
+
331
+ export type ExpressionEmitOptions = EmitOptions & {
332
+ /**
333
+ * Which rule the expression evaluates to, or `null` for THE WHOLE RULE MAP.
334
+ *
335
+ * `null` is what a `rules()` call site needs: its initialiser evaluates to the
336
+ * `{ name: fn, … }` object, exactly what `tableRules(…)` already returns, so
337
+ * the map form is the expression WITHOUT the trailing index — not a second
338
+ * emitter. `compileRuleMap`'s replacement has the same shape and the same
339
+ * splice point, which is what makes the two lowerings interchangeable there.
340
+ */
341
+ readonly entry?: string | null
342
+ /** Static metadata object passed to `tableRules` as its second argument. */
343
+ readonly metadataSource?: string
344
+ }
345
+
346
+ /**
347
+ * The table as an EXPRESSION rather than a module — for an inliner that replaces
348
+ * a grammar's initialiser in place.
349
+ *
350
+ * It references `tableRules` by name instead of carrying the driver, so it is
351
+ * not self-contained. That is deliberate and it is not a cost: the reference
352
+ * resolves to `parseman/table`, a package the consumer already depends on for
353
+ * `run()`. The alternative — inlining the driver per grammar — is precisely the
354
+ * 2.10 MB that this lowering exists to replace with 0.56 MB.
355
+ *
356
+ * The caller owns the import. `emitTableModule` writes its own; an inliner
357
+ * splicing this into existing source must ensure the binding is in scope.
358
+ */
359
+ export function emitTableExpression(prog: TableProgram, opts: ExpressionEmitOptions = {}): string {
360
+ assertPrintable(prog, 'emitTableExpression')
361
+ const entry = opts.entry === undefined ? 'grammar' : opts.entry
362
+ const ref = opts.runtimeRef ?? 'tableRules'
363
+ const fns = opts.fnSources ?? prog.fns.map(() => '() => {}')
364
+ const close = `}${opts.metadataSource === undefined ? '' : `,${opts.metadataSource}`})`
365
+ return [
366
+ `/* @__PURE__ */ ${ref}({`,
367
+ ...programFields(prog, fns, opts),
368
+ entry === null ? close : `${close}[${jsString(entry)}]`,
369
+ ].join('\n')
370
+ }
371
+
372
+ export type FoldedEmitOptions = EmitOptions & {
373
+ /**
374
+ * Exported binding per variant name. A variant with no entry is still carried
375
+ * in the table and simply not given a name of its own.
376
+ */
377
+ readonly names?: Readonly<Record<string, string>>
378
+ }
379
+
380
+ /**
381
+ * Print a FOLDED program: one base table, plus the row edits per variant.
382
+ *
383
+ * This is G4's deliverable. The four `trackLines` x `hostMode` artifacts a
384
+ * dialect ships stop being four near-copies of one table and become one table
385
+ * and three short lists of `(offset, word)` pairs — which is what the measured
386
+ * difference between them actually is. The reducer pool, the const pool, the
387
+ * char classes, the expected sets, the dispatch tables and the rule index are
388
+ * printed ONCE, because they are byte-identical in every variant.
389
+ *
390
+ * The base's own `l`/`h` scalars are NOT printed: every variant, base included,
391
+ * carries its own on its delta, so no variant inherits the base's line-tracking
392
+ * or host mode by accident.
393
+ */
394
+ export function emitFoldedModule(folded: FoldedProgram, opts: FoldedEmitOptions = {}): string {
395
+ assertPrintable(folded.base, 'emitFoldedModule')
396
+ const runtime = opts.runtime ?? 'parseman/table'
397
+ const fns = opts.fnSources ?? folded.base.fns.map(() => '() => {}')
398
+ const base = programFields(folded.base, fns).filter(l => !l.startsWith('l:') && !l.startsWith('h:'))
399
+ const variants = Object.keys(folded.variants).map(n => {
400
+ const d = folded.variants[n]!
401
+ const parts = [
402
+ ...(d.at.length === 0 ? [] : [`a:[${d.at.join(',')}]`, `t:[${d.to.join(',')}]`]),
403
+ ...(d.lines === undefined ? [] : [`l:${d.lines}`]),
404
+ ...(d.hostMode === undefined ? [] : [`h:${jsString(d.hostMode)}`]),
405
+ ]
406
+ return `${jsString(n)}:{${parts.join(',')}}`
407
+ })
408
+ const names = opts.names ?? {}
409
+ return [
410
+ `import { expandCompactFolded, tableVariants } from ${jsString(runtime)}`,
411
+ `const _t = /* @__PURE__ */ expandCompactFolded({`,
412
+ `b:{`,
413
+ ...base,
414
+ `},`,
415
+ `v:{${variants.join(',')}}`,
416
+ `})`,
417
+ ...Object.keys(names).map(n =>
418
+ `export const ${names[n]!} = /* @__PURE__ */ tableVariants(_t, ${jsString(n)})`),
419
+ ].join('\n')
420
+ }
421
+
422
+ /**
423
+ * The part of the emitted module that is MACHINERY — the table, excluding the
424
+ * author's reducers, which every lowering must emit alike. This is the number
425
+ * that is comparable to codegen's per-rule cost.
426
+ */
427
+ export function emitTableOnly(prog: TableProgram): string {
428
+ return emitTableModule(prog, { fnSources: [] })
429
+ }