parseman 0.45.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 (333) hide show
  1. package/README.md +8 -3
  2. package/dist/analysis/choice-cost-gate.d.ts +158 -0
  3. package/dist/analysis/choice-cost-gate.d.ts.map +1 -0
  4. package/dist/analysis/choice-cost-render.d.ts +96 -0
  5. package/dist/analysis/choice-cost-render.d.ts.map +1 -0
  6. package/dist/analysis/choice-cost.d.ts +383 -0
  7. package/dist/analysis/choice-cost.d.ts.map +1 -0
  8. package/dist/analysis/commitment.d.ts +131 -0
  9. package/dist/analysis/commitment.d.ts.map +1 -0
  10. package/dist/analysis/corpus.d.ts +73 -0
  11. package/dist/analysis/corpus.d.ts.map +1 -0
  12. package/dist/analysis/diagnose-render.d.ts +65 -0
  13. package/dist/analysis/diagnose-render.d.ts.map +1 -0
  14. package/dist/analysis/diagnose.d.ts +15 -0
  15. package/dist/analysis/diagnose.d.ts.map +1 -1
  16. package/dist/analysis/diagnostics.cjs +13736 -0
  17. package/dist/analysis/diagnostics.cjs.map +6 -0
  18. package/dist/analysis/diagnostics.d.ts +27 -0
  19. package/dist/analysis/diagnostics.d.ts.map +1 -0
  20. package/dist/analysis/diagnostics.js +13713 -0
  21. package/dist/analysis/diagnostics.js.map +6 -0
  22. package/dist/analysis/duplication.d.ts.map +1 -1
  23. package/dist/analysis/fix-render.d.ts +43 -0
  24. package/dist/analysis/fix-render.d.ts.map +1 -0
  25. package/dist/analysis/fix.d.ts +172 -0
  26. package/dist/analysis/fix.d.ts.map +1 -0
  27. package/dist/analysis/format-number.d.ts +23 -0
  28. package/dist/analysis/format-number.d.ts.map +1 -0
  29. package/dist/analysis/gating.d.ts +47 -2
  30. package/dist/analysis/gating.d.ts.map +1 -1
  31. package/dist/analysis/rebuild.d.ts +61 -0
  32. package/dist/analysis/rebuild.d.ts.map +1 -0
  33. package/dist/analysis/terminal.d.ts +145 -0
  34. package/dist/analysis/terminal.d.ts.map +1 -0
  35. package/dist/cli/index.cjs +14837 -0
  36. package/dist/cli/index.cjs.map +6 -0
  37. package/dist/cli/index.d.ts +3 -0
  38. package/dist/cli/index.d.ts.map +1 -0
  39. package/dist/cli/index.js +14814 -0
  40. package/dist/cli/index.js.map +6 -0
  41. package/dist/combinators/adjacency.d.ts +100 -0
  42. package/dist/combinators/adjacency.d.ts.map +1 -0
  43. package/dist/combinators/choice.d.ts.map +1 -1
  44. package/dist/combinators/completions.d.ts.map +1 -1
  45. package/dist/combinators/dispatch.d.ts +8 -1
  46. package/dist/combinators/dispatch.d.ts.map +1 -1
  47. package/dist/combinators/expect.d.ts.map +1 -1
  48. package/dist/combinators/first-set.d.ts.map +1 -1
  49. package/dist/combinators/gate.d.ts.map +1 -1
  50. package/dist/combinators/grammar.d.ts +22 -0
  51. package/dist/combinators/grammar.d.ts.map +1 -1
  52. package/dist/combinators/literal.d.ts +8 -0
  53. package/dist/combinators/literal.d.ts.map +1 -1
  54. package/dist/combinators/map.d.ts +0 -1
  55. package/dist/combinators/map.d.ts.map +1 -1
  56. package/dist/combinators/node.d.ts +13 -3
  57. package/dist/combinators/node.d.ts.map +1 -1
  58. package/dist/combinators/parser.d.ts +1 -1
  59. package/dist/combinators/parser.d.ts.map +1 -1
  60. package/dist/combinators/ref.d.ts.map +1 -1
  61. package/dist/combinators/regex.d.ts +1 -16
  62. package/dist/combinators/regex.d.ts.map +1 -1
  63. package/dist/combinators/repeat.d.ts +30 -2
  64. package/dist/combinators/repeat.d.ts.map +1 -1
  65. package/dist/combinators/scanTo.d.ts +71 -1
  66. package/dist/combinators/scanTo.d.ts.map +1 -1
  67. package/dist/combinators/sequence.d.ts.map +1 -1
  68. package/dist/combinators/token.d.ts.map +1 -1
  69. package/dist/combinators/trivia-boundary.d.ts +26 -0
  70. package/dist/combinators/trivia-boundary.d.ts.map +1 -0
  71. package/dist/combinators/trivia-skip.d.ts +40 -1
  72. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  73. package/dist/combinators/withCtx.d.ts.map +1 -1
  74. package/dist/compiler/build-arity.d.ts.map +1 -1
  75. package/dist/compiler/compile-linkable-table.d.ts +104 -0
  76. package/dist/compiler/compile-linkable-table.d.ts.map +1 -0
  77. package/dist/compiler/degradation.d.ts +0 -2
  78. package/dist/compiler/degradation.d.ts.map +1 -1
  79. package/dist/compiler/fields.d.ts +13 -0
  80. package/dist/compiler/fields.d.ts.map +1 -1
  81. package/dist/compiler/grammar-coverage-ids.d.ts +5 -5
  82. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  83. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  84. package/dist/compiler/linker.d.ts +79 -47
  85. package/dist/compiler/linker.d.ts.map +1 -1
  86. package/dist/compiler/token-alphabet.d.ts +81 -0
  87. package/dist/compiler/token-alphabet.d.ts.map +1 -0
  88. package/dist/compiler/token-dispatch.d.ts +95 -0
  89. package/dist/compiler/token-dispatch.d.ts.map +1 -0
  90. package/dist/compiler/token-scanner.d.ts +53 -0
  91. package/dist/compiler/token-scanner.d.ts.map +1 -0
  92. package/dist/compiler/value-usage.d.ts.map +1 -1
  93. package/dist/coverage.d.ts +1 -1
  94. package/dist/coverage.d.ts.map +1 -1
  95. package/dist/cst/capture-buffer.d.ts +35 -0
  96. package/dist/cst/capture-buffer.d.ts.map +1 -1
  97. package/dist/cst/reflection-attach.d.ts +8 -0
  98. package/dist/cst/reflection-attach.d.ts.map +1 -0
  99. package/dist/cst/reflection-symbols.d.ts +10 -0
  100. package/dist/cst/reflection-symbols.d.ts.map +1 -0
  101. package/dist/cst/reflection.d.ts +2 -4
  102. package/dist/cst/reflection.d.ts.map +1 -1
  103. package/dist/cst/root-trivia-scope.d.ts +26 -0
  104. package/dist/cst/root-trivia-scope.d.ts.map +1 -0
  105. package/dist/cst/trivia-charscan.d.ts +18 -0
  106. package/dist/cst/trivia-charscan.d.ts.map +1 -0
  107. package/dist/cst/trivia-kinds.d.ts +19 -0
  108. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  109. package/dist/functional/doc.d.ts.map +1 -1
  110. package/dist/functional/run-tabled.d.ts +12 -0
  111. package/dist/functional/run-tabled.d.ts.map +1 -0
  112. package/dist/functional/run.d.ts +25 -42
  113. package/dist/functional/run.d.ts.map +1 -1
  114. package/dist/grammar-metadata.d.ts +3 -0
  115. package/dist/grammar-metadata.d.ts.map +1 -0
  116. package/dist/index.cjs +10499 -7478
  117. package/dist/index.cjs.map +3 -4
  118. package/dist/index.d.ts +24 -9
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +10499 -7478
  121. package/dist/index.js.map +3 -4
  122. package/dist/language-service/index.cjs +311 -121
  123. package/dist/language-service/index.cjs.map +3 -4
  124. package/dist/language-service/index.js +311 -121
  125. package/dist/language-service/index.js.map +3 -4
  126. package/dist/oracle/index.cjs.map +0 -1
  127. package/dist/oracle/index.js.map +0 -1
  128. package/dist/parse-context.d.ts +49 -0
  129. package/dist/parse-context.d.ts.map +1 -0
  130. package/dist/plugin/evaluator.d.ts +41 -2
  131. package/dist/plugin/evaluator.d.ts.map +1 -1
  132. package/dist/plugin/free-identifiers.d.ts +23 -0
  133. package/dist/plugin/free-identifiers.d.ts.map +1 -0
  134. package/dist/plugin/index.cjs +11381 -9442
  135. package/dist/plugin/index.cjs.map +3 -4
  136. package/dist/plugin/index.d.ts.map +1 -1
  137. package/dist/plugin/index.js +11381 -9442
  138. package/dist/plugin/index.js.map +3 -4
  139. package/dist/plugin/reducer-resolver.d.ts +17 -1
  140. package/dist/plugin/reducer-resolver.d.ts.map +1 -1
  141. package/dist/regex/classes.d.ts +28 -0
  142. package/dist/regex/classes.d.ts.map +1 -1
  143. package/dist/regex/first-set.d.ts +14 -0
  144. package/dist/regex/first-set.d.ts.map +1 -1
  145. package/dist/run/index.cjs +136 -51
  146. package/dist/run/index.cjs.map +2 -3
  147. package/dist/run/index.js +136 -51
  148. package/dist/run/index.js.map +2 -3
  149. package/dist/spec/index.cjs +2898 -1622
  150. package/dist/spec/index.cjs.map +3 -4
  151. package/dist/spec/index.js +2898 -1622
  152. package/dist/spec/index.js.map +3 -4
  153. package/dist/spec/model.d.ts +8 -0
  154. package/dist/spec/model.d.ts.map +1 -1
  155. package/dist/table/assemble.d.ts +307 -0
  156. package/dist/table/assemble.d.ts.map +1 -0
  157. package/dist/table/cell.d.ts +42 -0
  158. package/dist/table/cell.d.ts.map +1 -0
  159. package/dist/table/child-slots.d.ts +30 -0
  160. package/dist/table/child-slots.d.ts.map +1 -0
  161. package/dist/table/compile-rule-map.d.ts +136 -0
  162. package/dist/table/compile-rule-map.d.ts.map +1 -0
  163. package/dist/table/compile.d.ts +56 -0
  164. package/dist/table/compile.d.ts.map +1 -0
  165. package/dist/table/duplication-hook.d.ts +32 -0
  166. package/dist/table/duplication-hook.d.ts.map +1 -0
  167. package/dist/table/emit-assembly.d.ts +139 -0
  168. package/dist/table/emit-assembly.d.ts.map +1 -0
  169. package/dist/table/emit.d.ts +113 -0
  170. package/dist/table/emit.d.ts.map +1 -0
  171. package/dist/table/encode-baseline.d.ts +16 -0
  172. package/dist/table/encode-baseline.d.ts.map +1 -0
  173. package/dist/table/encode.d.ts +71 -0
  174. package/dist/table/encode.d.ts.map +1 -0
  175. package/dist/table/exec-baseline.d.ts +9 -0
  176. package/dist/table/exec-baseline.d.ts.map +1 -0
  177. package/dist/table/exec.d.ts +72 -0
  178. package/dist/table/exec.d.ts.map +1 -0
  179. package/dist/table/fold.d.ts +25 -0
  180. package/dist/table/fold.d.ts.map +1 -0
  181. package/dist/table/index.cjs +11375 -0
  182. package/dist/table/index.cjs.map +6 -0
  183. package/dist/table/index.d.ts +100 -0
  184. package/dist/table/index.d.ts.map +1 -0
  185. package/dist/table/index.js +11352 -0
  186. package/dist/table/index.js.map +6 -0
  187. package/dist/table/inspect.d.ts +17 -0
  188. package/dist/table/inspect.d.ts.map +1 -0
  189. package/dist/table/ops.d.ts +446 -0
  190. package/dist/table/ops.d.ts.map +1 -0
  191. package/dist/table/program.d.ts +493 -0
  192. package/dist/table/program.d.ts.map +1 -0
  193. package/dist/table/run-support.d.ts +38 -0
  194. package/dist/table/run-support.d.ts.map +1 -0
  195. package/dist/{compiler/scannable-run.d.ts → table/scan-shapes.d.ts} +14 -13
  196. package/dist/table/scan-shapes.d.ts.map +1 -0
  197. package/dist/table/site-labels.d.ts +58 -0
  198. package/dist/table/site-labels.d.ts.map +1 -0
  199. package/dist/table/stamp.d.ts +70 -0
  200. package/dist/table/stamp.d.ts.map +1 -0
  201. package/dist/types.d.ts +80 -24
  202. package/dist/types.d.ts.map +1 -1
  203. package/dist/version.d.ts +1 -1
  204. package/package.json +41 -3
  205. package/src/analysis/choice-cost-gate.ts +359 -0
  206. package/src/analysis/choice-cost-render.ts +293 -0
  207. package/src/analysis/choice-cost.ts +1008 -0
  208. package/src/analysis/commitment.ts +557 -0
  209. package/src/analysis/corpus.ts +130 -0
  210. package/src/analysis/diagnose-render.ts +625 -0
  211. package/src/analysis/diagnose.ts +317 -0
  212. package/src/analysis/diagnostics.ts +33 -0
  213. package/src/analysis/duplication.ts +1955 -0
  214. package/src/analysis/fix-render.ts +217 -0
  215. package/src/analysis/fix.ts +669 -0
  216. package/src/analysis/format-number.ts +31 -0
  217. package/src/analysis/gating.ts +792 -0
  218. package/src/analysis/grammar.ts +72 -0
  219. package/src/analysis/rebuild.ts +287 -0
  220. package/src/analysis/terminal.ts +336 -0
  221. package/src/cli/index.ts +413 -0
  222. package/src/combinators/adjacency.ts +171 -0
  223. package/src/combinators/attempt.ts +41 -0
  224. package/src/combinators/case-fold.ts +90 -0
  225. package/src/combinators/choice.ts +466 -0
  226. package/src/combinators/completions.ts +64 -0
  227. package/src/combinators/dispatch.ts +358 -0
  228. package/src/combinators/expect.ts +159 -0
  229. package/src/combinators/first-set.ts +322 -0
  230. package/src/combinators/gate.ts +50 -0
  231. package/src/combinators/grammar.ts +272 -0
  232. package/src/combinators/keywords.ts +145 -0
  233. package/src/combinators/literal.ts +102 -0
  234. package/src/combinators/map.ts +106 -0
  235. package/src/combinators/node.ts +345 -0
  236. package/src/combinators/not.ts +55 -0
  237. package/src/combinators/parser.ts +266 -0
  238. package/src/combinators/peek.ts +63 -0
  239. package/src/combinators/probe.ts +25 -0
  240. package/src/combinators/ref.ts +109 -0
  241. package/src/combinators/regex.ts +170 -0
  242. package/src/combinators/repeat.ts +561 -0
  243. package/src/combinators/scanTo.ts +393 -0
  244. package/src/combinators/sequence.ts +221 -0
  245. package/src/combinators/token.ts +120 -0
  246. package/src/combinators/trivia-boundary.ts +48 -0
  247. package/src/combinators/trivia-skip.ts +821 -0
  248. package/src/combinators/withCtx.ts +45 -0
  249. package/src/compiler/build-arity.ts +330 -0
  250. package/src/compiler/compile-linkable-table.ts +209 -0
  251. package/src/compiler/degradation.ts +266 -0
  252. package/src/compiler/fields.ts +191 -0
  253. package/src/compiler/grammar-coverage-ids.ts +129 -0
  254. package/src/compiler/ir-serialize.ts +615 -0
  255. package/src/compiler/linker.ts +954 -0
  256. package/src/compiler/token-alphabet.ts +209 -0
  257. package/src/compiler/token-dispatch.ts +353 -0
  258. package/src/compiler/token-scanner.ts +318 -0
  259. package/src/compiler/value-usage.ts +147 -0
  260. package/src/coverage.ts +383 -0
  261. package/src/cst/capture-buffer.ts +297 -0
  262. package/src/cst/host-mode.ts +69 -0
  263. package/src/cst/reflection-attach.ts +11 -0
  264. package/src/cst/reflection-symbols.ts +9 -0
  265. package/src/cst/reflection.ts +149 -0
  266. package/src/cst/relative-spans.ts +207 -0
  267. package/src/cst/root-trivia-scope.ts +30 -0
  268. package/src/cst/trivia-charscan.ts +312 -0
  269. package/src/cst/trivia-entries.ts +285 -0
  270. package/src/cst/trivia-index.ts +131 -0
  271. package/src/cst/trivia-kinds.ts +358 -0
  272. package/src/cst/types.ts +76 -0
  273. package/src/cst/walk.ts +101 -0
  274. package/src/functional/doc.ts +959 -0
  275. package/src/functional/run-tabled.ts +153 -0
  276. package/src/functional/run.ts +558 -0
  277. package/src/grammar-metadata.ts +2 -0
  278. package/src/index.ts +189 -0
  279. package/src/language-service/index.ts +221 -0
  280. package/src/line-index.ts +131 -0
  281. package/src/oracle/digest.ts +620 -0
  282. package/src/oracle/index.ts +55 -0
  283. package/src/parse-context.ts +91 -0
  284. package/src/plugin/direct-builder-static.ts +102 -0
  285. package/src/plugin/evaluator.ts +1505 -0
  286. package/src/plugin/free-identifiers.ts +338 -0
  287. package/src/plugin/index.ts +2281 -0
  288. package/src/plugin/reducer-resolver.ts +606 -0
  289. package/src/recovery/scan.ts +155 -0
  290. package/src/regex/classes.ts +183 -0
  291. package/src/regex/first-set.ts +316 -0
  292. package/src/run/index.ts +31 -0
  293. package/src/spec/ebnf.ts +128 -0
  294. package/src/spec/index.ts +54 -0
  295. package/src/spec/model.ts +636 -0
  296. package/src/spec/railroad-lib.ts +559 -0
  297. package/src/spec/railroad.ts +256 -0
  298. package/src/table/assemble.ts +2994 -0
  299. package/src/table/cell.ts +42 -0
  300. package/src/table/child-slots.ts +113 -0
  301. package/src/table/compile-rule-map.ts +336 -0
  302. package/src/table/compile.ts +244 -0
  303. package/src/table/duplication-hook.ts +89 -0
  304. package/src/table/emit-assembly.ts +1766 -0
  305. package/src/table/emit.ts +418 -0
  306. package/src/table/encode-baseline.ts +340 -0
  307. package/src/table/encode.ts +1569 -0
  308. package/src/table/exec-baseline.ts +537 -0
  309. package/src/table/exec.ts +1672 -0
  310. package/src/table/fold.ts +38 -0
  311. package/src/table/index.ts +108 -0
  312. package/src/table/inspect.ts +52 -0
  313. package/src/table/ops.ts +459 -0
  314. package/src/table/program.ts +855 -0
  315. package/src/table/run-support.ts +66 -0
  316. package/src/table/scan-shapes.ts +1578 -0
  317. package/src/table/site-labels.ts +228 -0
  318. package/src/table/stamp.ts +229 -0
  319. package/src/types.ts +563 -0
  320. package/src/version.ts +16 -0
  321. package/dist/compiler/codegen.d.ts +0 -334
  322. package/dist/compiler/codegen.d.ts.map +0 -1
  323. package/dist/compiler/inline-build.d.ts +0 -11
  324. package/dist/compiler/inline-build.d.ts.map +0 -1
  325. package/dist/compiler/inline-callback.d.ts +0 -21
  326. package/dist/compiler/inline-callback.d.ts.map +0 -1
  327. package/dist/compiler/line-index.d.ts +0 -2
  328. package/dist/compiler/line-index.d.ts.map +0 -1
  329. package/dist/compiler/scannable-run.d.ts.map +0 -1
  330. package/dist/compiler/scannable-terminal.d.ts +0 -23
  331. package/dist/compiler/scannable-terminal.d.ts.map +0 -1
  332. package/dist/compiler/trivia-fast-path.d.ts +0 -38
  333. package/dist/compiler/trivia-fast-path.d.ts.map +0 -1
@@ -0,0 +1,228 @@
1
+ /**
2
+ * SITE LABELS — the encoder's downward pass over an encoded program.
3
+ *
4
+ * ## The criterion
5
+ *
6
+ * *Any consulting of options at parse time, per rule or per combinator, is a
7
+ * FAIL.* After U4 four consultations remained, and three of them are here:
8
+ * `ctx.trivia` per sequence term, `cstCaptureActive` per leaf, and
9
+ * `hasTrivia`/`needMark` per repetition. (`forCtx`, once per parse at the
10
+ * boundary, is not one of these — it selects the assembly rather than running
11
+ * inside one.)
12
+ *
13
+ * ## Why a label and not a `RunCfg` bit
14
+ *
15
+ * `assemble.ts:213-277` states the rule `RunCfg` is held to: a bit belongs there
16
+ * only when it is FIXED FOR THE LIFETIME OF A PARSE. Neither of these facts is.
17
+ *
18
+ * - `ctx.trivia` is per-SCOPE. `OP_SCOPE` swaps it mid-parse, so a `RunCfg`
19
+ * bit would be answering a question that has different answers at different
20
+ * sites in the same parse.
21
+ * - `ctx._cstBuf` is per-NODE — `assemble.ts:247-249` records that a previous
22
+ * lane proposed keying on it and that doing so would have been *incorrect,
23
+ * not merely redundant*.
24
+ *
25
+ * A site label is strictly stronger than either, and it is what makes both facts
26
+ * legal to resolve: it is computed from the PROGRAM STRUCTURE at encode time,
27
+ * before any option set exists, and it says what is true *at that site* rather
28
+ * than what is true for the parse. `OP_NODE` sets `ctx._cstBuf` unconditionally
29
+ * on entry and restores it on exit, so every site dynamically inside one has
30
+ * `_cstBuf !== undefined` — not because of an option, but because of where it
31
+ * sits in the program.
32
+ *
33
+ * ## The two axes, kept apart
34
+ *
35
+ * The capture label does not compress into one bit, and an earlier attempt to
36
+ * make it one was wrong in both directions:
37
+ *
38
+ * - `cap` is THREE-VALUED. `OP_NODE` writes `ctx.captureTrivia` a LITERAL —
39
+ * `readsTrivia || hostCst`, resolved at emit — so a site under a node has it
40
+ * definitely true OR definitely false. `OP_SCOPE_CAP` only ever sets it true.
41
+ * Collapsing "definitely off" into "unknown" loses the case that lets
42
+ * `_skipTrivia` drop its capture arm entirely.
43
+ * - `buf` is a separate FLAG, because `_cstBuf` and `captureTrivia` are set by
44
+ * different ops and neither implies the other: `OP_SCOPE_CAP` turns capture
45
+ * on without opening a buffer, and a builder-less `OP_NODE` opens a buffer
46
+ * with capture off.
47
+ *
48
+ * ## Asymmetry, deliberately
49
+ *
50
+ * `buf` is TRUE only for "guaranteed present". FALSE means UNKNOWN, never
51
+ * "guaranteed absent" — no root can prove absence, because every entry point
52
+ * (`prog.rules`, and the `extraIps` a scan pool reaches from outside the emitted
53
+ * scope) is called with a context this pass cannot see. Nothing is elided on
54
+ * `buf === false`; it only declines to elide. The same holds for `TRI_UNKNOWN`
55
+ * and `CAP_UNKNOWN`, which are the lattice's top element, not a third fact.
56
+ *
57
+ * That asymmetry is what rules out the unsound version of this pass: eliding a
58
+ * leaf's capture when `hostCst === false`. `OP_NODE` opens `_cstBuf` whatever the
59
+ * host mode is, and those leaves feed `kids` → `build(...)`, so the ONLY thing a
60
+ * label licenses at a leaf is dropping the TEST — never the capture.
61
+ */
62
+ import {
63
+ OP_ATTEMPT, OP_CHOICE, OP_DISPATCH, OP_EXPECT, OP_FIELD, OP_GATE, OP_LABEL, OP_LEAF,
64
+ OP_NODE, OP_NODE_TRACK, OP_NOT, OP_OPT, OP_PEEK, OP_REP, OP_REPV, OP_ROUTED, OP_RULE,
65
+ OP_SCOPE, OP_SCOPE_CAP, OP_SEQ, OP_SEQV, OP_SEQX, OP_TOKEN, OP_XFORM,
66
+ } from './ops.ts'
67
+ import { childSlots } from './child-slots.ts'
68
+
69
+ /** No scope on this path installs a known trivia — the lattice's top element. */
70
+ export const TRI_UNKNOWN = -2
71
+ /** `ctx.trivia === undefined` is guaranteed here. */
72
+ export const TRI_NONE = -1
73
+ /** Any value `>= 0` is a trivia slot index, guaranteed installed here. */
74
+
75
+ /** `ctx.captureTrivia` is not known at this site. */
76
+ export const CAP_UNKNOWN = 0
77
+ /** `ctx.captureTrivia` is guaranteed NOT `true` here. */
78
+ export const CAP_OFF = 1
79
+ /** `ctx.captureTrivia === true` is guaranteed here. */
80
+ export const CAP_ON = 2
81
+
82
+ export type SiteLabel = {
83
+ /** `TRI_UNKNOWN`, `TRI_NONE`, or the trivia slot the enclosing scope installed. */
84
+ readonly tri: number
85
+ /**
86
+ * `ctx._cstBuf !== undefined` is GUARANTEED. False means unknown — see the
87
+ * asymmetry note in this file's header. Never read as "guaranteed absent".
88
+ */
89
+ readonly buf: boolean
90
+ /** `CAP_UNKNOWN` / `CAP_OFF` / `CAP_ON`. */
91
+ readonly cap: number
92
+ }
93
+
94
+ /** The lattice's top: every entry point starts here. */
95
+ export const TOP: SiteLabel = { tri: TRI_UNKNOWN, buf: false, cap: CAP_UNKNOWN }
96
+
97
+ function meet(a: SiteLabel, b: SiteLabel): SiteLabel {
98
+ const tri = a.tri === b.tri ? a.tri : TRI_UNKNOWN
99
+ const buf = a.buf && b.buf
100
+ const cap = a.cap === b.cap ? a.cap : CAP_UNKNOWN
101
+ if (tri === a.tri && buf === a.buf && cap === a.cap) return a
102
+ return { tri, buf, cap }
103
+ }
104
+
105
+ /**
106
+ * The sites reachable from `roots` THROUGH THE SHARED EDGE TABLE.
107
+ *
108
+ * Not `inspect.ts`'s `reachableIps`: that one walks a whole program from its rule
109
+ * entries, and the emitter's question is about the set IT will lower — the rule
110
+ * entries plus the scan pool's `extraIps`. Different ROOTS, same EDGES, and the
111
+ * edges now live in `child-slots.ts` so the two answers cannot drift when an
112
+ * opcode is added to one and not the other. That drift is the failure this file's
113
+ * header used to warn about while guarding only against it happening within this
114
+ * file; the copy it warned about was in `inspect.ts` the whole time.
115
+ *
116
+ * `childSlots`'s false return (an opcode it does not know) is ignored here: the
117
+ * site resolves to `TOP` through `labelAt`, and the enclosing assembly is
118
+ * unemittable regardless.
119
+ */
120
+ export function reachableSites(code: Int32Array, roots: Iterable<number>): Set<number> {
121
+ const seen = new Set<number>()
122
+ const stack = [...roots]
123
+ const kids: number[] = []
124
+ while (stack.length > 0) {
125
+ const ip = stack.pop()!
126
+ if (seen.has(ip)) continue
127
+ seen.add(ip)
128
+ kids.length = 0
129
+ childSlots(code, ip, kids)
130
+ for (const c of kids) stack.push(c)
131
+ }
132
+ return seen
133
+ }
134
+
135
+ /**
136
+ * What a site hands DOWN to its children. Every op but the two that write the
137
+ * context passes its own label through unchanged.
138
+ */
139
+ function transfer(code: Int32Array, ip: number, at: SiteLabel, hostCst: boolean): SiteLabel {
140
+ const op = code[ip]
141
+ if (op === OP_SCOPE || op === OP_SCOPE_CAP) {
142
+ const ki = code[ip + 1]!
143
+ const tri = ki < 0 ? TRI_NONE : ki
144
+ // `OP_SCOPE_CAP` is an OR with the inherited context, never a switch-off
145
+ // (`encode.ts:1153-1158`), so it can only ever raise `cap` to `CAP_ON`.
146
+ const cap = op === OP_SCOPE_CAP ? CAP_ON : at.cap
147
+ if (tri === at.tri && cap === at.cap) return at
148
+ return { tri, buf: at.buf, cap }
149
+ }
150
+ if (op === OP_NODE || op === OP_NODE_TRACK) {
151
+ // `ctx._cstBuf = buf` and `ctx.captureTrivia = <literal>`, both unconditional
152
+ // — `emit-assembly.ts`'s `OP_NODE` body opens the buffer before it descends
153
+ // and closes it after, whatever the host mode is.
154
+ const cap = ((code[ip + 3]! & 4) !== 0 || hostCst) ? CAP_ON : CAP_OFF
155
+ if (at.buf && cap === at.cap) return at
156
+ return { tri: at.tri, buf: true, cap }
157
+ }
158
+ // THE TWO BOUNDARIES clear every capture sink for their child and contribute
159
+ // ONE leaf for the whole match; `token()` clears `ctx.trivia` as well. So a
160
+ // site inside either has NO open `_cstBuf`.
161
+ //
162
+ // The label can only say `buf: false`, which this lattice reads as UNKNOWN
163
+ // rather than as "guaranteed absent" (see the asymmetry note above). That is
164
+ // the SOUND direction and it is the one that matters: `_rbBuf` and
165
+ // `_pushLeafBuf` dereference `ctx._cstBuf` with no null test, so a site that
166
+ // inherited `buf: true` through a `token()` would throw on the first rollback.
167
+ // Losing the elision inside a token is the price; being wrong there is not
168
+ // an option.
169
+ if (op === OP_TOKEN) {
170
+ if (at.tri === TRI_NONE && !at.buf) return at
171
+ return { tri: TRI_NONE, buf: false, cap: at.cap }
172
+ }
173
+ if (op === OP_LEAF) {
174
+ if (!at.buf) return at
175
+ return { tri: at.tri, buf: false, cap: at.cap }
176
+ }
177
+ return at
178
+ }
179
+
180
+ /** A site's label, or `TOP` for anything the walk did not reach. */
181
+ export type SiteLabels = { at: (ip: number) => SiteLabel }
182
+
183
+ /**
184
+ * Compute a label for every site reachable from `roots`.
185
+ *
186
+ * A worklist to a fixpoint, because the program has cycles (a recursive rule is
187
+ * a back-edge into a site already in flight) and because a site shared by two
188
+ * parents must carry the MEET of what both hand it — a downward pass that simply
189
+ * overwrote would give a shared site whichever parent it happened to visit last,
190
+ * and the emitted body for that site is ONE body serving both.
191
+ *
192
+ * The meet only ever moves a label toward `TOP`, and `TOP` is absorbing, so the
193
+ * lattice has height three and the walk terminates.
194
+ */
195
+ export function computeSiteLabels(
196
+ code: Int32Array,
197
+ roots: Iterable<number>,
198
+ hostCst: boolean,
199
+ ): SiteLabels {
200
+ const labels = new Map<number, SiteLabel>()
201
+ const work: number[] = []
202
+
203
+ const push = (ip: number, l: SiteLabel): void => {
204
+ const cur = labels.get(ip)
205
+ if (cur === undefined) {
206
+ labels.set(ip, l)
207
+ work.push(ip)
208
+ return
209
+ }
210
+ const next = meet(cur, l)
211
+ if (next === cur) return
212
+ labels.set(ip, next)
213
+ work.push(ip)
214
+ }
215
+
216
+ for (const r of roots) push(r, TOP)
217
+
218
+ const kids: number[] = []
219
+ while (work.length > 0) {
220
+ const ip = work.pop()!
221
+ const down = transfer(code, ip, labels.get(ip)!, hostCst)
222
+ kids.length = 0
223
+ childSlots(code, ip, kids)
224
+ for (const c of kids) push(c, down)
225
+ }
226
+
227
+ return { at: (ip: number): SiteLabel => labels.get(ip) ?? TOP }
228
+ }
@@ -0,0 +1,229 @@
1
+ /**
2
+ * The rule-map envelope both table drivers share.
3
+ *
4
+ * Everything here is about what a table ENTRY has to look like to `run()` and to
5
+ * the linker's public wrappers — trivia metadata, host-mode stamps, the
6
+ * `_lineStarts` seeding, the ambient `scanSkip` install and the failure-to-
7
+ * `ParseResult` conversion. None of it is recognition, and having two copies of
8
+ * it (one in `exec.ts`, one in `assemble.ts`) would be two places for a
9
+ * divergence the three-way identity sweep would then have to catch.
10
+ *
11
+ * The drivers supply only what differs: how a rule RUNS.
12
+ */
13
+ import type { Combinator, ParseContext, ParseResult } from '../types.ts'
14
+ import { FUSED_HOST_ELIDED, FUSED_HOST_MODE } from '../cst/host-mode.ts'
15
+ import { GRAMMAR_COVERAGE_DEFINITIONS } from '../grammar-metadata.ts'
16
+ import { reachableIps } from './inspect.ts'
17
+ import { OP_NODE, OP_NODE_TRACK, OP_SCOPE } from './ops.ts'
18
+ import { covDefinitions, resolveTable, type ResolvedTable, type TableProgram, type TableRule } from './program.ts'
19
+
20
+ const EMPTY_FX: string[] = []
21
+
22
+ /**
23
+ * Did something inside CUT?
24
+ *
25
+ * Read through a call so TypeScript does not narrow `_fc` to `false` from the
26
+ * assignment that precedes each speculative attempt — the driver mutates it and
27
+ * the checker cannot see that. Lives here rather than in a driver because the
28
+ * ENVELOPE reads it too, and a third copy would be a third place to drift.
29
+ */
30
+ export function committed(c: ParseContext): boolean {
31
+ return c._fc === true
32
+ }
33
+
34
+ /**
35
+ * The ENTRY result's span, line-annotated when the table tracks lines.
36
+ *
37
+ * Codegen annotates the root result the moment `ctx.lineTracking` is on — both
38
+ * the success span and the failure span (`_spanLines(_ctx, …)`), and so does the
39
+ * interpreter's `parser({ trackLines: true })` scope. The table's envelope built
40
+ * a bare `{ start, end }` regardless, so a tracked table parse handed back a
41
+ * result with no `startLine`/`endColumn` at all while paying for the tracking.
42
+ *
43
+ * The binary search over `_lineStarts` now lives in `run-support.ts`. The
44
+ * private copy that sat here was justified by "not worth giving the hot node
45
+ * piece a cross-module call"; the EMITTED engine needs the same answer, the
46
+ * node piece no longer calls this copy at all, and a THIRD copy is how a
47
+ * reported column drifts between two engines that are gated against each other
48
+ * precisely so that it cannot.
49
+ */
50
+ import { spanLines } from './run-support.ts'
51
+ export { spanLines }
52
+
53
+ /**
54
+ * What a driver must provide per parse, in the order the entry needs it.
55
+ *
56
+ * `run` returns the driver's own FAIL sentinel for a failed parse. The sentinel
57
+ * is module-private on each side, so it is never compared here — `runRule`
58
+ * answers with `undefined` for failure instead, and the caller's sentinel never
59
+ * leaves its module.
60
+ */
61
+ export type RuleRunner = {
62
+ /**
63
+ * Run rule `ri` and return its end position, or −1 on failure.
64
+ *
65
+ * Returning the position rather than the value keeps the driver's FAIL
66
+ * sentinel private; the value is fetched separately only on success.
67
+ */
68
+ readonly runRule: (ri: number, input: string, pos: number, ctx: ParseContext) => number
69
+ /** The value the last successful `runRule` produced. */
70
+ readonly lastValue: () => unknown
71
+ /** The ambient `scanSkip` set for rule `ri`, if the program declares one. */
72
+ readonly scanSkipFor: (ri: number, ctx: ParseContext) => readonly Combinator<unknown>[] | undefined
73
+ }
74
+
75
+ /**
76
+ * Wrap a driver's per-rule run into the map an artifact exports.
77
+ *
78
+ * The entries have the SAME signature as codegen rule functions, so `run()`, the
79
+ * linker's public wrappers and every consumer are unchanged.
80
+ */
81
+ export function stampRuleMap(
82
+ prog: TableProgram,
83
+ d: RuleRunner,
84
+ artifactMetadata: Readonly<Record<symbol, unknown>> = {},
85
+ resolved: ResolvedTable = resolveTable(prog),
86
+ ): Record<string, TableRule> {
87
+ // `run()` reads trivia metadata off the ENTRY and takes its
88
+ // `typeof r === 'function'` branch for compiled entries, which codegen stamps
89
+ // with `_meta`. A table entry is a function too, so it must be stamped or
90
+ // `run({ rootTrivia })` rejects a grammar that plainly has labelled trivia.
91
+ //
92
+ // `grammarTrivia` joins them for the same reason and with a sharper edge: `run()`
93
+ // now consumes the DOCUMENT ROOT's trailing trivia off the entry's own ambient
94
+ // trivia (see `ambientTriviaFromRunnable`), which a combinator entry carries on
95
+ // `_meta.grammarTrivia`. A table entry that did not carry it would leave the tail
96
+ // unconsumed and report a short `span` / a non-null `unconsumedFrom` where the
97
+ // interpreter reports the whole file — a three-way identity divergence that shows
98
+ // up as `ok: true` with bytes missing, never as an error.
99
+ //
100
+ // Read from the ENTRY ROW, not from one program-wide slot: `encodeRule` wraps each
101
+ // rule's entry in `OP_SCOPE <triviaSlot> <body>` iff THAT rule has ambient trivia,
102
+ // and a `composeLeaf` grammar's pieces do not agree (the same disagreement
103
+ // `scanSkipOf` exists for). The owning driver passes its resolved table, so
104
+ // this is the exact trivia array it already built.
105
+ const triviaOfRule = (ip: number): Combinator<unknown> | undefined => {
106
+ if (prog.code[ip] !== OP_SCOPE) return undefined
107
+ const slot = prog.code[ip + 1]!
108
+ return slot < 0 ? undefined : resolved.trivia[slot]
109
+ }
110
+ const baseMeta = prog.labels === undefined && prog.classified !== 1
111
+ ? undefined
112
+ : {
113
+ ...(prog.labels === undefined ? {} : { triviaKindLabels: prog.labels }),
114
+ ...(prog.classified === 1 ? { rootTriviaClassified: true as const } : {}),
115
+ }
116
+ // Chosen ONCE, from table data, at rule-map construction. Not a per-parse
117
+ // branch on an option: a plain table never has this wrapper at all.
118
+ const lines = prog.lines === 1
119
+ const mode = prog.hostMode ?? 'ast'
120
+ const elided = mode === 'ast' && hasDirectBuilder(prog)
121
+ const coverageDefinitions = prog.cov === undefined
122
+ ? undefined
123
+ : Object.freeze(covDefinitions(prog).map(Object.freeze))
124
+ // Metadata lives on the map's prototype FROM BIRTH. Symbol reads keep their
125
+ // existing contract, while Object.keys, object spread and Object.assign see
126
+ // only the map's own rule entries. This is the no-descriptor equivalent of the
127
+ // old non-enumerable stamps: no post-construction shape transition, WeakMap or
128
+ // generated wrapper is involved.
129
+ const metadataPrototype = {
130
+ ...artifactMetadata,
131
+ [FUSED_HOST_MODE]: mode,
132
+ [FUSED_HOST_ELIDED]: elided,
133
+ ...(coverageDefinitions === undefined
134
+ ? {}
135
+ : { [GRAMMAR_COVERAGE_DEFINITIONS]: coverageDefinitions }),
136
+ }
137
+ const out = Object.create(metadataPrototype) as Record<string, TableRule>
138
+ const names = Object.keys(prog.rules)
139
+ for (let ri = 0; ri < names.length; ri++) {
140
+ const name = names[ri]!
141
+ const index = ri
142
+ const entryFn = (input: string, pos: number, ctx: ParseContext): ParseResult<unknown> => {
143
+ // The table lowering ships coverage counters in 0.47 but not codegen's
144
+ // fine-grained trace phases. Failing at the artifact boundary is deliberate:
145
+ // accepting the public sink and writing no events produces a plausible empty
146
+ // trace, which is indistinguishable from a genuinely unvisited grammar.
147
+ if (ctx._grammarTrace !== undefined) {
148
+ throw new TypeError(
149
+ 'parseman: grammar tracing is not supported by table-backed parsers in 0.47; '
150
+ + 'remove the _grammarTrace sink or run the combinator through the interpreter',
151
+ )
152
+ }
153
+ // Ambient scanSkip, which `run()` cannot install for a function entry.
154
+ // Chosen PER RULE, because that is what `run()` does — it reads the ENTRY
155
+ // rule's own `_meta.grammarScanSkip`. One program-wide set instead gave a
156
+ // `composeLeaf` piece's rules a skip list the interpreter never gives them.
157
+ const ownSkip = d.scanSkipFor(index, ctx)
158
+ if (ownSkip !== undefined && ctx.scanSkip === undefined) {
159
+ ctx.scanSkip = ownSkip as ParseContext['scanSkip']
160
+ }
161
+ ctx._fe = -1
162
+ ctx._fx = EMPTY_FX
163
+ // `_fc` is a SPECULATION-LOCAL bit cleared at the boundary so a stale
164
+ // `true` from a PREVIOUS parse on a reused `ctx` cannot read as "the cut
165
+ // fired".
166
+ ctx._fc = false
167
+ if (lines && ctx._lineStarts === undefined) { ctx._lineStarts = [0]; ctx._lineScannedTo = 0 }
168
+ const end = d.runRule(index, input, pos, ctx)
169
+ if (end < 0) {
170
+ const fe = ctx._fe
171
+ const at = fe === undefined || fe < 0 ? pos : fe
172
+ const fspan = lines ? spanLines(ctx, at, at) : { start: at, end: at }
173
+ // `committed` IS PART OF THE FAILURE ENVELOPE, not a diagnostic extra.
174
+ // The interpreter carries it on the result (`{ ok: false, …, committed: true }`)
175
+ // and codegen re-derives it from the same bit this driver uses
176
+ // (`codegen.ts`: `...(_ctx._fc ? { committed: true } : {})`). The table
177
+ // dropped it, so a table entry embedded as a CHILD of another parser — the
178
+ // one place the field is read rather than merely reported — lost the cut.
179
+ return committed(ctx)
180
+ ? { ok: false, expected: (ctx._fx ?? EMPTY_FX) as string[], span: fspan, committed: true }
181
+ : { ok: false, expected: (ctx._fx ?? EMPTY_FX) as string[], span: fspan }
182
+ }
183
+ return { ok: true, value: d.lastValue(), span: lines ? spanLines(ctx, pos, end) : { start: pos, end } }
184
+ }
185
+ const ownTrivia = triviaOfRule(prog.rules[name]!)
186
+ const meta = ownTrivia === undefined
187
+ ? baseMeta
188
+ : { ...baseMeta, grammarTrivia: ownTrivia }
189
+ // A standalone entry has no registry from which `run()` could recover host
190
+ // mode, so keep the two ordinary symbol fields on the function itself. Every
191
+ // entry receives them in the same construction-time order.
192
+ out[name] = Object.assign(entryFn, {
193
+ ...(meta === undefined ? {} : { _meta: meta }),
194
+ [FUSED_HOST_MODE]: mode,
195
+ [FUSED_HOST_ELIDED]: elided,
196
+ })
197
+ }
198
+ // STAMP THE HOST MODE. `run()` reads it off the entry through `FUSED_HOST_MODE`
199
+ // and `assertHostModeCompatible` throws when a 'cst' artifact runs without a
200
+ // CST host. Encoding with `hostMode: 'cst'` set the capture flags but nothing
201
+ // stamped the mode, so such a table returned the grammar's own AST objects with
202
+ // `ok: true` while paying full CST capture.
203
+ // WHAT `FUSED_HOST_ELIDED` MEANS is "a DIRECT BUILDER's positioned-CST branch was
204
+ // dropped" — it is what makes `'ast' artifact + CST host` an error rather than a
205
+ // preference. `mode === 'ast'` alone over-reports it: an all-STRUCTURAL grammar has
206
+ // no direct builder, so no branch was dropped and it stays usable with either host
207
+ // (the `node(parser)` contract). A node row carries `-1` in its build slot when it
208
+ // has no direct builder, so the program answers this itself — and it MUST agree with
209
+ // the macro's emitted stamp, or re-stamping the same map throws
210
+ // "Cannot redefine property".
211
+ return out
212
+ }
213
+
214
+ /**
215
+ * Does any REACHABLE node row own a direct builder?
216
+ *
217
+ * Reachability, not a scan for the opcode's numeric value: operands are ordinary
218
+ * integers and collide with opcodes, so counting words reports confident nonsense
219
+ * (`inspect.ts` states the same thing about `opHistogram`). One linear pass at load,
220
+ * beside the assembly pass that already visits every row.
221
+ */
222
+ function hasDirectBuilder(prog: TableProgram): boolean {
223
+ const code = prog.code
224
+ for (const ip of reachableIps(prog)) {
225
+ const op = code[ip]
226
+ if ((op === OP_NODE || op === OP_NODE_TRACK) && code[ip + 1] !== -1) return true
227
+ }
228
+ return false
229
+ }