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,69 @@
1
+ /**
2
+ * The compiled-artifact ↔ host contract, in ONE place.
3
+ *
4
+ * This lives on its own — rather than in `compiler/codegen.ts` where it started —
5
+ * because the DRIVER has to enforce it too. `functional/run.ts` is the `parseman/run`
6
+ * entry, whose whole purpose is to stay small (see `test/unit/run-entry-closure.test.ts`),
7
+ * so it cannot import the compiler to reach an assertion. Neither can it carry its own
8
+ * copy: two spellings of one contract is how the two engines drift, and this contract
9
+ * exists precisely because the failure it prevents is silent.
10
+ *
11
+ * No imports, by design — that is what keeps it free for both sides to depend on.
12
+ */
13
+
14
+ /**
15
+ * Which consumer a compiled artifact was lowered FOR.
16
+ *
17
+ * - `'ast'` — the grammar's own `build` callbacks produce the result (the eval consumer).
18
+ * - `'cst'` — a positioned-CST `ctx.build` host produces it (linter / IDE / language
19
+ * service), and every node's capture is sized for that host.
20
+ *
21
+ * ONE grammar source serves both; they are two COMPILATIONS of it, not two grammars.
22
+ * Deciding at compile time is what keeps the eval-AST artifact free of per-node host
23
+ * probing — see the `hostMode` note on the codegen Ctx.
24
+ */
25
+ export type HostMode = 'ast' | 'cst'
26
+
27
+ /** Host mode a fused rule map (and each of its rule functions) was lowered for;
28
+ * absent on hand-built maps → 'ast'. */
29
+ export const FUSED_HOST_MODE = Symbol.for('parseman.fusedHostMode')
30
+
31
+ /** Whether any fused piece omitted a direct builder's positioned-CST branch. */
32
+ export const FUSED_HOST_ELIDED = Symbol.for('parseman.fusedHostElided')
33
+
34
+ /**
35
+ * A compiled artifact and the host it is driven with must agree — and when they do not,
36
+ * that has to be an ERROR, never a quietly-degraded tree.
37
+ *
38
+ * An `'ast'` artifact does not emit the positioned-CST branch at all, so attaching a
39
+ * `_parsemanCstOutput` host to one would silently get the grammar's own AST objects
40
+ * where the caller asked for a CST. A `'cst'` artifact builds every node through the
41
+ * host, so running one without a host would call `undefined`. Both are compile/run
42
+ * mismatches with an obvious fix, so both say so.
43
+ *
44
+ * Called once per parse from the TypeScript entry points, never from generated code.
45
+ */
46
+ export function assertHostModeCompatible(mode: HostMode, build: unknown, hostBranchElided = true): void {
47
+ const isCstHost = (build as { _parsemanCstOutput?: true } | undefined)?._parsemanCstOutput === true
48
+ // A purely STRUCTURAL artifact never had a direct-builder host branch to drop, so it
49
+ // serves a CST host in either mode — that is the long-standing `node(parser)` contract
50
+ // and this change does not touch it. The INTERPRETER passes `false` for the same
51
+ // reason from the other direction: it re-decides per parse and never elides anything.
52
+ if (mode === 'ast' && isCstHost && hostBranchElided) {
53
+ throw new Error(
54
+ 'parseman: this parser was compiled for host mode "ast" (the default), so its nodes '
55
+ + 'build through their own `build` callbacks and no positioned-CST branch was emitted. '
56
+ + 'It cannot be driven with a positioned-CST host (cstBuildHost / a language-service '
57
+ + 'host). Compile a second artifact from the SAME grammar with '
58
+ + "`compile(grammar, { hostMode: 'cst' })` — or, under the macro, a second "
59
+ + "`rules({ hostMode: 'cst' }, factory)` call site — and drive that one instead.",
60
+ )
61
+ }
62
+ if (mode === 'cst' && !isCstHost) {
63
+ throw new Error(
64
+ 'parseman: this parser was compiled for host mode "cst", so every node builds through '
65
+ + 'a positioned-CST `ctx.build` host. Pass one (e.g. `{ build: cstBuildHost }`), or use '
66
+ + 'an artifact compiled with the default `hostMode: "ast"` to get the grammar\'s own AST.',
67
+ )
68
+ }
69
+ }
@@ -0,0 +1,11 @@
1
+ import { GRAMMAR_REFLECTION, type GrammarReflection } from './reflection.ts'
2
+
3
+ /**
4
+ * Legacy interpreted-rule-map attachment. It is deliberately outside the shared
5
+ * table runtime graph: macro/table artifacts construct reflection in their
6
+ * metadata prototype instead of mutating an already-built map.
7
+ */
8
+ export function attachGrammarReflection<T extends object>(grammar: T, reflection: GrammarReflection): T {
9
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true })
10
+ return grammar
11
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Type-level CST identity lives on the hot table-runtime import graph. Keep the
3
+ * symbols separate from reflection collection/attachment: the latter is a
4
+ * grammar-construction concern and must not make every emitted parser load its
5
+ * descriptor-based legacy helper.
6
+ */
7
+ export const GRAMMAR_REFLECTION: unique symbol = Symbol.for('parseman.grammarReflection') as never
8
+ export const NODE_TYPE: unique symbol = Symbol.for('parseman.type.nodeType') as never
9
+ export const NODE_TAG: unique symbol = Symbol.for('parseman.type.nodeTag') as never
@@ -0,0 +1,149 @@
1
+ import type { Combinator, ParserDef } from '../types.ts'
2
+ import { GRAMMAR_REFLECTION, NODE_TAG, NODE_TYPE } from './reflection-symbols.ts'
3
+
4
+ export { GRAMMAR_REFLECTION, NODE_TYPE, NODE_TAG } from './reflection-symbols.ts'
5
+
6
+ export type GrammarNodeReflection = {
7
+ type: string
8
+ tags: readonly string[]
9
+ }
10
+
11
+ export type GrammarReflection = {
12
+ nodes: readonly GrammarNodeReflection[]
13
+ }
14
+
15
+ export type GrammarWithReflection<
16
+ NodeType extends string = string,
17
+ NodeTag extends string = string,
18
+ > = {
19
+ readonly [GRAMMAR_REFLECTION]?: GrammarReflection
20
+ readonly [NODE_TYPE]?: NodeType
21
+ readonly [NODE_TAG]?: NodeTag
22
+ }
23
+
24
+ type NodeDef = Extract<ParserDef, { tag: 'node' }>
25
+
26
+ function childrenOf(def: ParserDef): readonly Combinator<unknown>[] {
27
+ switch (def.tag) {
28
+ case 'sequence':
29
+ case 'choice':
30
+ return def.parsers
31
+ case 'dispatch':
32
+ return [
33
+ def.selector,
34
+ ...def.cases.map(c => c.parser),
35
+ ...(def.matchers ? def.matchers.map(c => c.parser) : []),
36
+ ...(def.otherwise ? [def.otherwise] : []),
37
+ ]
38
+ case 'many':
39
+ case 'oneOrMore':
40
+ case 'optional':
41
+ case 'attempt':
42
+ case 'transform':
43
+ case 'trivia':
44
+ case 'token':
45
+ case 'leaf':
46
+ case 'label':
47
+ case 'field':
48
+ case 'not':
49
+ case 'peek':
50
+ case 'node':
51
+ case 'withCtx':
52
+ case 'expect':
53
+ return [def.parser]
54
+ case 'recover':
55
+ return [def.parser, def.sentinel]
56
+ case 'grammar':
57
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser]
58
+ case 'sepBy':
59
+ return [def.parser, def.separator]
60
+ case 'scanTo':
61
+ return [def.sentinel, ...def.skip]
62
+ case 'routed':
63
+ return def.fallback ? [def.fallback] : []
64
+ case 'literal':
65
+ case 'regex':
66
+ case 'keywords':
67
+ case 'guard':
68
+ case 'adjacency':
69
+ case 'lazy':
70
+ case 'unknown':
71
+ return []
72
+ }
73
+ }
74
+
75
+ function addNode(out: Map<string, string[]>, def: NodeDef): void {
76
+ if (def.type === undefined) return
77
+ const existing = out.get(def.type)
78
+ if (existing === undefined) {
79
+ out.set(def.type, def.tags === undefined ? [] : [...def.tags])
80
+ return
81
+ }
82
+ if (def.tags === undefined) return
83
+ for (const tag of def.tags) {
84
+ if (!existing.includes(tag)) existing.push(tag)
85
+ }
86
+ }
87
+
88
+ function collectFromCombinator(
89
+ parser: Combinator<unknown>,
90
+ out: Map<string, string[]>,
91
+ seen: Set<Combinator<unknown>>,
92
+ followLazy = true,
93
+ ): void {
94
+ if (seen.has(parser)) return
95
+ seen.add(parser)
96
+ const def = parser._def
97
+ if (def.tag === 'lazy') {
98
+ if (followLazy) {
99
+ try { collectFromCombinator(def.thunk(), out, seen, followLazy) } catch {}
100
+ }
101
+ return
102
+ }
103
+ if (def.tag === 'node') addNode(out, def)
104
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy)
105
+ }
106
+
107
+ export function collectGrammarReflection(
108
+ ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>,
109
+ opts?: { followLazy?: boolean },
110
+ ): GrammarReflection {
111
+ const byType = new Map<string, string[]>()
112
+ const followLazy = opts?.followLazy !== false
113
+ for (const [, rule] of ruleMap) {
114
+ if (!followLazy && rule._def.tag === 'lazy') {
115
+ try {
116
+ const resolved = rule._def.thunk()
117
+ if (resolved._def.tag !== 'lazy') collectFromCombinator(resolved, byType, new Set(), false)
118
+ } catch {}
119
+ continue
120
+ }
121
+ collectFromCombinator(rule, byType, new Set(), followLazy)
122
+ }
123
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) }
124
+ }
125
+
126
+ export function mergeGrammarReflections(reflections: readonly GrammarReflection[]): GrammarReflection {
127
+ const byType = new Map<string, string[]>()
128
+ for (const reflection of reflections) {
129
+ for (const node of reflection.nodes) {
130
+ const tags = byType.get(node.type)
131
+ if (tags === undefined) {
132
+ byType.set(node.type, [...node.tags])
133
+ continue
134
+ }
135
+ for (const tag of node.tags) {
136
+ if (!tags.includes(tag)) tags.push(tag)
137
+ }
138
+ }
139
+ }
140
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) }
141
+ }
142
+
143
+ export function grammarReflectionOf(grammar: object): GrammarReflection | undefined {
144
+ return (grammar as GrammarWithReflection)[GRAMMAR_REFLECTION]
145
+ }
146
+
147
+ export function grammarReflectionSource(reflection: GrammarReflection): string {
148
+ return `{ nodes: ${JSON.stringify(reflection.nodes)} }`
149
+ }
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Relative span model for incremental edits.
3
+ *
4
+ * Absolute source offsets are simple but fragile: an insertion at offset `at`
5
+ * shifts EVERY downstream offset, so a whole-tree renumber is O(nodes-after-edit).
6
+ * Storing each node's span RELATIVE to its parent's start makes edits local: an
7
+ * insertion inside child k of a node only changes
8
+ * (a) child k's relative end,
9
+ * (b) the relative start of siblings after k,
10
+ * and leaves the *children* of those shifted siblings untouched — they move with
11
+ * their parent for free. So the mutation count is O(depth + trailing siblings),
12
+ * not O(all following nodes), and every unaffected subtree is shared by identity.
13
+ *
14
+ * An absolute view is recovered on query by accumulating the parent base as you
15
+ * descend (the `absolutize` / `absoluteSpanAt` cursor) — one source of truth,
16
+ * two manifestations.
17
+ */
18
+
19
+ /** A node with an ABSOLUTE source span and ordered children. */
20
+ export type AbsNode = {
21
+ readonly start: number
22
+ readonly end: number
23
+ readonly children: readonly AbsNode[]
24
+ }
25
+
26
+ /** A node whose `start`/`end` are RELATIVE to its parent's start (root base = 0). */
27
+ export type RelNode = {
28
+ readonly start: number
29
+ readonly end: number
30
+ readonly children: readonly RelNode[]
31
+ }
32
+
33
+ /** Convert an absolute tree to parent-relative spans. */
34
+ export function relativize(node: AbsNode, parentBase = 0): RelNode {
35
+ return {
36
+ start: node.start - parentBase,
37
+ end: node.end - parentBase,
38
+ children: node.children.map((c) => relativize(c, node.start)),
39
+ }
40
+ }
41
+
42
+ /** Reconstruct absolute spans from a relative tree. Inverse of `relativize`. */
43
+ export function absolutize(node: RelNode, parentBase = 0): AbsNode {
44
+ const start = parentBase + node.start
45
+ return {
46
+ start,
47
+ end: parentBase + node.end,
48
+ children: node.children.map((c) => absolutize(c, start)),
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Absolute span of the node at `path` (child indices from the root), accumulating
54
+ * the base as it descends — O(path length), no full absolutize. The relative-form
55
+ * cursor: query any node's absolute position without materializing the whole tree.
56
+ */
57
+ export function absoluteSpanAt(root: RelNode, path: readonly number[]): { start: number; end: number } {
58
+ let base = 0
59
+ let node = root
60
+ for (const idx of path) {
61
+ base += node.start
62
+ const child = node.children[idx]
63
+ if (!child) throw new RangeError(`absoluteSpanAt: no child ${idx} on path ${path.join('.')}`)
64
+ node = child
65
+ }
66
+ return { start: base + node.start, end: base + node.end }
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // CST-shaped projection (parseDoc incremental trees)
71
+ //
72
+ // The bare Abs/RelNode helpers above are the reference model. The functions
73
+ // below apply the same relative-span algebra to real CST children — nodes,
74
+ // leaves, and error nodes — where the span is a nested `{ start, end }` and
75
+ // children are heterogeneous (leaves carry a span but no `children`). Trivia
76
+ // never appears in structural `children`, so every child has a span.
77
+ // ---------------------------------------------------------------------------
78
+
79
+ /**
80
+ * A CST child as far as span projection cares: a nested span + optional children.
81
+ * `children` is typed loosely (`unknown[]`) so the real heterogeneous CST unions
82
+ * (nodes, leaves, error nodes — a leaf has no `children`) satisfy it directly.
83
+ */
84
+ type Spanned = {
85
+ readonly span: { readonly start: number; readonly end: number }
86
+ readonly children?: readonly unknown[]
87
+ }
88
+
89
+ /**
90
+ * Rewrite a CST subtree's spans to be RELATIVE to `parentBase` (the absolute
91
+ * start of the node's parent; root base = 0). Each child's own children are
92
+ * relativized against *this* node's absolute start. Preserves every other field
93
+ * (`_tag`, `type`, `state`, leaf `value`, …) by shallow spread.
94
+ */
95
+ export function relativizeCST<T extends Spanned>(node: T, parentBase = 0): T {
96
+ const span = { start: node.span.start - parentBase, end: node.span.end - parentBase }
97
+ if (node.children) {
98
+ const base = node.span.start
99
+ return { ...node, span, children: node.children.map((c) => relativizeCST(c as Spanned, base)) } as T
100
+ }
101
+ return { ...node, span } as T
102
+ }
103
+
104
+ /** Reconstruct absolute CST spans from a relative subtree. Inverse of `relativizeCST`. */
105
+ export function absolutizeCST<T extends Spanned>(node: T, parentBase = 0): T {
106
+ const start = parentBase + node.span.start
107
+ const span = { start, end: parentBase + node.span.end }
108
+ if (node.children) {
109
+ return { ...node, span, children: node.children.map((c) => absolutizeCST(c as Spanned, start)) } as T
110
+ }
111
+ return { ...node, span } as T
112
+ }
113
+
114
+ /**
115
+ * Absolute span of the CST node at `path` (child indices from the root),
116
+ * accumulating the base as it descends — O(path length), no full absolutize.
117
+ * The relative-form cursor for a positioned CST: query one node's absolute
118
+ * position without materializing the whole tree.
119
+ */
120
+ export function absoluteSpanCST(root: Spanned, path: readonly number[]): { start: number; end: number } {
121
+ let base = 0
122
+ let node: Spanned = root
123
+ for (const idx of path) {
124
+ base += node.span.start
125
+ const child = node.children?.[idx] as Spanned | undefined
126
+ if (!child) throw new RangeError(`absoluteSpanCST: no child ${idx} on path ${path.join('.')}`)
127
+ node = child
128
+ }
129
+ return { start: base + node.span.start, end: base + node.span.end }
130
+ }
131
+
132
+ /** Naive absolute reshift (baseline): shift every offset >= `at` by `delta`. */
133
+ export function shiftAbsolute(node: AbsNode, at: number, delta: number): AbsNode {
134
+ const s = node.start >= at ? node.start + delta : node.start
135
+ const e = node.end >= at ? node.end + delta : node.end
136
+ return {
137
+ start: s,
138
+ end: e,
139
+ children: node.children.map((c) => shiftAbsolute(c, at, delta)),
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Apply an edit of `delta` characters at absolute offset `at` to a RELATIVE tree.
145
+ * Returns a new relative tree; every subtree that ends at/before `at` (in absolute
146
+ * terms) is returned by IDENTITY (unchanged), so the result structurally shares
147
+ * all unaffected subtrees with the input.
148
+ *
149
+ * `stats.allocated` counts nodes that had to be re-created — the incremental cost.
150
+ */
151
+ export function applyEdit(
152
+ root: RelNode,
153
+ at: number,
154
+ delta: number,
155
+ stats?: { allocated: number },
156
+ ): RelNode {
157
+ // The root has no parent; its base never shifts (base 0).
158
+ return editNode(root, at, delta, 0, 0, stats)
159
+ }
160
+
161
+ function editNode(
162
+ node: RelNode,
163
+ at: number,
164
+ delta: number,
165
+ parentBase: number,
166
+ parentShift: number, // how much this node's parent base moved (0 or delta)
167
+ stats?: { allocated: number },
168
+ ): RelNode {
169
+ const absStart = parentBase + node.start
170
+ const absEnd = parentBase + node.end
171
+
172
+ // Strictly before the edit → unchanged (its parent base is also unshifted).
173
+ // Uses `< at` (not `<=`) to match the shift convention: offsets `>= at` move,
174
+ // so a node ending exactly at `at` grows and must be rewritten.
175
+ if (absEnd < at) return node
176
+
177
+ // Fully after the edit AND the parent moved by `delta` too → this node moves as
178
+ // one unit with its parent, so its parent-relative offsets are identical. Share
179
+ // the ENTIRE subtree by identity — this is the O(depth) locality win.
180
+ const startShifts = absStart >= at
181
+ if (startShifts && parentShift === delta) return node
182
+
183
+ if (stats) stats.allocated++
184
+
185
+ const newAbsStart = startShifts ? absStart + delta : absStart
186
+ const newAbsEnd = absEnd + delta // absEnd > at here (fully-before returned above)
187
+ const newParentBase = parentBase + parentShift
188
+ const newStart = newAbsStart - newParentBase
189
+ const newEnd = newAbsEnd - newParentBase
190
+
191
+ // Children are relative to this node's absolute start. Our own shift (0 or delta)
192
+ // becomes their parentShift.
193
+ const ownShift = newAbsStart - absStart
194
+ let changed = false
195
+ const newChildren: RelNode[] = []
196
+ for (const c of node.children) {
197
+ const nc = editNode(c, at, delta, absStart, ownShift, stats)
198
+ if (nc !== c) changed = true
199
+ newChildren.push(nc)
200
+ }
201
+
202
+ return {
203
+ start: newStart,
204
+ end: newEnd,
205
+ children: changed ? newChildren : node.children,
206
+ }
207
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The ROOT-CAPTURE POLICY of one local trivia scope, in one place.
3
+ *
4
+ * Three engines install a `parser({ trivia })` scope — the interpreter
5
+ * (`combinators/grammar.ts`), source lowering (`compiler/codegen.ts`) and the
6
+ * table (`table/assemble.ts`, `table/exec.ts`) — and each of them has to answer
7
+ * the same two questions about selected root trivia. The refusal below was a
8
+ * string literal in two of them and ABSENT from the third, which is how a table
9
+ * parse came to accept a grammar the other two reject.
10
+ *
11
+ * Import-free, like the rest of `cst/`, so the drivers can reach it without
12
+ * pulling the combinator set in.
13
+ */
14
+
15
+ /** The message `parser()` raises for an unclassified local scope. Exported so the
16
+ * three engines cannot drift a word apart. */
17
+ export const ROOT_TRIVIA_SCOPE_REFUSAL
18
+ = 'parser(): selected root trivia requires classifiedTrivia() for every local trivia scope, or rootCapture: \'opaque\'.'
19
+
20
+ /**
21
+ * Refuse a local trivia scope that cannot be reported faithfully.
22
+ *
23
+ * With selected root capture active, a scope whose trivia is not classified and
24
+ * is not declared opaque would silently drop the markers inside it. Throwing is
25
+ * the contract (`combinators/grammar.ts:98`); returning a parse that quietly
26
+ * omits them is the failure this exists to prevent.
27
+ */
28
+ export function refuseUnclassifiedRootScope(strict: boolean | undefined): void {
29
+ if (strict === true) throw new TypeError(ROOT_TRIVIA_SCOPE_REFUSAL)
30
+ }