parseman 0.46.0 → 0.47.0

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