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,383 @@
1
+ /**
2
+ * CHOICE COST — the shape of an ordered choice, and what that shape costs.
3
+ * ========================================================================
4
+ *
5
+ * READ THIS FIRST: THIS MEASURES THE INTERPRETER, NOT THE SHIPPED PARSER.
6
+ * ----------------------------------------------------------------------
7
+ * The interpreter's `firstMatch` loop (src/combinators/choice.ts:149-165) enters every
8
+ * alternative unconditionally. The EMITTED engine does not: `src/table/emit-assembly.ts`
9
+ * hoists a per-choice candidate mask and emits a per-arm first-CHARACTER guard, so an arm
10
+ * whose first set excludes the character at the current position is never entered.
11
+ * First-set gating is this project's single largest parse lever — 25-48% across all
12
+ * four jess parsers — and a naive interpreted profile is blind to every byte of it.
13
+ *
14
+ * So `profileWastedWork` reports TWO columns and derives neither from the other:
15
+ * `attempts`/`wastedBytes` (interpreted) and `gatedAttempts`/`gatedWastedBytes`
16
+ * (modelling codegen's guard — see `compiledFirstCharGate`). Rankings use the compiled
17
+ * column. The DELTA is itself a result: an arm expensive interpreted and cheap gated is
18
+ * ALREADY SOLVED by codegen, and reordering it is unpaid work.
19
+ *
20
+ * WHAT THE GATE ACTUALLY REMOVES — measured, not assumed.
21
+ * Over jess's four dialect grammars (637 kB of CSS, 212 kB of Less):
22
+ *
23
+ * dialect arms whose GATED BYTES differ arm entries removed by the gate
24
+ * css 0 of 158 331,313 of 435,767 (76%)
25
+ * less 0 of 199 413,737 of 562,852 (74%)
26
+ * scss 0 of 200 617,735 of 777,356 (79%)
27
+ * jess 0 of 158 325,655 of 388,263 (84%)
28
+ *
29
+ * The gate removes ATTEMPTS, not rescanned BYTES — and the byte columns are identical
30
+ * to the byte in all four. That is structural, not a coincidence of these corpora: the
31
+ * guard is derived from the arm's first SET, which over-approximates what the arm can
32
+ * start with, so whenever the guard rejects, the arm's own leading terminal would have
33
+ * rejected at the same position having consumed nothing.
34
+ *
35
+ * The practical consequence, and the reason this warning is at the top: any claim of
36
+ * the form "this arm is entered on every X and fails N% of the time" is a statement
37
+ * about the INTERPRETER and is usually false of the shipped parser. A byte ranking
38
+ * survives the correction; a failure-RATE or attempt-count claim does not. Measured on
39
+ * jess's CSS `Value` choice, arm 0 `Percentage` reads as 25,939 entries at 97% failure
40
+ * interpreted, and 7,455 at 90% gated — the same arm, a fifth as important. Arm 4
41
+ * `IdentBlock` is 100% failure in BOTH columns and carries 27,131 bytes; it is the real
42
+ * finding, and only the gated column makes that legible.
43
+ *
44
+ * KNOWN LIMIT OF THE MODEL. On the compose/linkable path a named-rule arm gets a
45
+ * DEFERRED guard (an `@FS:name@` placeholder) that fuse time rewrites with the winning rule's
46
+ * first set — or with `true`, always-try, if it cannot be resolved. This model always
47
+ * resolves, so for cross-artifact ref arms `gatedAttempts` is a LOWER bound: the
48
+ * shipped artifact may enter such an arm more often than modelled. Where a compiled
49
+ * measurement disagrees with this report on an arm that is a bare rule reference, the
50
+ * compiled measurement is right.
51
+ *
52
+ * A PEG `choice` is ordered: arm `i` is only reached once arms `0..i-1` have been
53
+ * TRIED AND FAILED, and every one of those failures re-scanned input that the
54
+ * winning arm then scans again. So each additional alternative, and each earlier
55
+ * alternative, has a time cost — and that cost is invisible in the grammar source,
56
+ * which is why grammar shape drifts without anything going red.
57
+ *
58
+ * This module makes it visible, in two halves that answer two different questions.
59
+ *
60
+ * STATIC — `analyzeChoiceInventory()`
61
+ * Which choice sites have alternatives that share a leading term, and for each:
62
+ * did the compiler LEFT-FACTOR it, or did it decline, and why? `detectSharedPrefix`
63
+ * (src/combinators/choice.ts) already computes this to decide whether to emit the
64
+ * `sharedPrefix` strategy, but it is ALL-OR-NOTHING (every arm must share) and it
65
+ * returns `null` on the first arm that does not qualify — silently, with no record
66
+ * of how close the site came or which arm blocked it. The inventory reports the
67
+ * declines, with the blocking arm and the reason. That set is the refactor backlog,
68
+ * GENERATED rather than noticed by a human reading grammar source.
69
+ *
70
+ * It is complete, not sampled: every `choice` reachable from the rule map appears,
71
+ * including the ones with nothing to report (`groups: []`).
72
+ *
73
+ * DYNAMIC — `profileWastedWork()`
74
+ * How many BYTES were re-scanned because an alternative was tried and failed,
75
+ * attributed per choice site and per arm, over a real corpus. Ordering pathologies
76
+ * fall out of the ranking for free: an arm that fails 98% of the time while sitting
77
+ * first is exactly the top entry.
78
+ *
79
+ * The static half can only see shape. It cannot know that one declined site is on
80
+ * the hot path of every stylesheet and another is reached twice a year. The dynamic
81
+ * half supplies that weight. Neither is sufficient alone.
82
+ *
83
+ * WHY BYTES, AND WHY THIS IS GATEABLE
84
+ * -----------------------------------
85
+ * The metric is a COUNT of input bytes, not a timing. It is a pure function of
86
+ * (grammar, corpus): the same grammar over the same corpus yields the same number on
87
+ * an idle machine and on a machine at load 10. There is no noise floor to argue about
88
+ * and no rebaseline needed for a hardware change. That is what makes it usable as a
89
+ * deterministic gate rather than a benchmark.
90
+ *
91
+ * INTERPRETED MODE ONLY
92
+ * ---------------------
93
+ * Nothing here is emitted into a compiled parser and nothing here costs codegen a
94
+ * single byte or a single millisecond. The profiler does not add an instrumentation
95
+ * flag to the hot combinators either — `src/combinators/choice.ts` is UNTOUCHED by
96
+ * this file. Instrumentation is installed by temporarily substituting arm slots and
97
+ * terminal `parse` methods on the combinator tree, and removed in a `finally`. When
98
+ * you are not profiling, the cost is exactly zero because the shipping code contains
99
+ * no profiling branch to skip.
100
+ *
101
+ * (For contrast: `run({ profile })` — the node/slot-count profiler — was compiled-path
102
+ * only, was never implemented in the interpreter, and now throws rather than reporting
103
+ * zeros. See src/functional/run.ts. This is a different measurement and a new build,
104
+ * not a port of that one.)
105
+ *
106
+ * WHAT THIS INSTRUMENT CANNOT SEE
107
+ * -------------------------------
108
+ * Stated up front, because a diagnostic whose blind spots are undocumented gets read
109
+ * as complete:
110
+ *
111
+ * 0. It measures the INTERPRETER. See the top of this header — this is the blind spot
112
+ * that reorders results rather than merely shifting them, which is why it is not
113
+ * in this list but above it. The `gated*` columns model it; they do not remove it.
114
+ * 1. Only `firstMatch` and `sharedPrefix` choices are instrumented. Those are the
115
+ * two strategies that try arms in order and can fail one. A `disjoint` choice
116
+ * dispatches on the first character and tries exactly one arm; `greedyClassify`
117
+ * runs one regex; `literalsLongestFirst` captures its sorted arm array at
118
+ * construction, so slot substitution cannot reach it. Their sites still appear
119
+ * in the static inventory, with zero dynamic cost recorded and `instrumented:
120
+ * false` so the zero is not mistaken for a measurement.
121
+ * 2. Backtracking that is not a choice arm — a failed `many` element, a failed
122
+ * `optional`, a rolled-back `attempt` — is not attributed. Those bytes are real
123
+ * but they are not an ORDERING decision, which is what this instrument exists to
124
+ * rank.
125
+ * 3. An arm that SUCCEEDS and is then rejected by the `autoNot` check is recorded as
126
+ * a success, because the rejection happens inside the choice loop after the arm
127
+ * has returned. Its rescan is invisible here.
128
+ * 4. `wastedBytes` is input bytes re-scanned, not CPU. An arm that fails after one
129
+ * byte but allocated on the way is cheap by this metric and not by the clock.
130
+ * This ranks ORDERING, and is deliberately not a profiler.
131
+ *
132
+ * SET EXPECTATIONS ACCORDINGLY, because this has been measured. A jess lane acted
133
+ * on a finding from this report and cut rescanned bytes at one route from 27,678
134
+ * to 8,367 — a 69.8% reduction, exactly as this instrument predicts — for ZERO
135
+ * measurable wall-clock change. A large byte win that produces no time win is the
136
+ * expected outcome, not a failure of the fix: re-scanning is cheap per byte, and
137
+ * what makes a grammar slow is usually allocation and node construction, which
138
+ * this metric cannot see. Use it to find and rank ORDERING defects and to prove a
139
+ * restructure did what it claimed. Do not use it to predict a speedup, and do not
140
+ * let a flat benchmark be read as evidence the finding was wrong.
141
+ * 5. Reach is measured by the furthest position at which a TERMINAL was attempted
142
+ * (`literal`/`regex`/`keywords`/`scanTo`). A combinator that consumes without
143
+ * going through one of those would under-report; none exists today.
144
+ */
145
+ import type { Combinator } from '../types.ts';
146
+ import type { ChoiceStrategyTag } from './gating.ts';
147
+ import type { FirstSet } from '../types.ts';
148
+ /** A combinator's location in the grammar: owning rule plus structural path. */
149
+ export type ChoiceSite = {
150
+ /** Nearest enclosing `_ruleName`, or the rule-map key it was reached under. */
151
+ rule: string;
152
+ /** Structural path inside that rule, e.g. `seq[2] › node(AtRule) › choice[0]`. */
153
+ path: string;
154
+ };
155
+ /** The stable identity of a site. Used as the sort key and the baseline key, so it
156
+ * must be derived only from grammar structure — never from a file path or a run. */
157
+ export declare const choiceSiteKey: (s: ChoiceSite) => string;
158
+ /** Why one ARM contributed no shareable leading term. */
159
+ export type ArmDeclineReason =
160
+ /** The arm is not a (wrapper-peeled) `sequence` — e.g. a bare ref, a nested
161
+ * `choice`, an `optional`. `leadingTermOfArm` peels only node/grammar/transform/
162
+ * label, because those are the wrappers that consume nothing before the sequence. */
163
+ 'not-a-sequence'
164
+ /** A `sequence` of one term: there is no residual to factor out from. */
165
+ | 'sequence-shorter-than-2'
166
+ /** The leading term is a case-INSENSITIVE literal. Excluded deliberately: the
167
+ * matched text differs from the literal's own value, so a replayed leaf would
168
+ * carry the wrong string. */
169
+ | 'lead-case-insensitive-literal'
170
+ /** The leading term is not a bare literal/regex — a ref, a node, a label, a
171
+ * transform, another choice. Factoring through it would change the value or
172
+ * capture shape of the arm. */
173
+ | 'lead-not-concrete-terminal';
174
+ /** Why the SITE as a whole was not left-factored. */
175
+ export type SiteDeclineReason =
176
+ /** First-char dispatch already selects exactly one arm; there is nothing to factor.
177
+ * Not a backlog entry — this is the good state. */
178
+ 'disjoint-dispatch'
179
+ /** At least one arm carries a runtime gate; per-arm predicates are incompatible
180
+ * with the factoring strategies. */
181
+ | 'gated-arms'
182
+ /** `greedyClassify` or `literalsLongestFirst` matched first. Both are stronger than
183
+ * `sharedPrefix` (neither backtracks), so this is also not a backlog entry. */
184
+ | 'strategy-preempted'
185
+ /** Fewer than two arms. */
186
+ | 'fewer-than-two-arms'
187
+ /** At least one arm produced no shareable leading term. See `armDeclines` for which
188
+ * arm and why. THIS is the backlog: a subset of arms may still share a prefix that
189
+ * the all-or-nothing detector never records. */
190
+ | 'arms-not-factorable'
191
+ /** Every arm produced a leading term, but they are not all the same term. Also the
192
+ * backlog: two arms out of nine sharing `@` is real, repeated work. */
193
+ | 'leads-differ';
194
+ /** A maximal set of arms at one site sharing an identical concrete leading term. */
195
+ export type PrefixGroup = {
196
+ /** Structural key of the shared term. Injective by construction (JSON-encoded), so
197
+ * two structurally different regexes can never collide onto one group. */
198
+ key: string;
199
+ /** Human rendering, e.g. `"@"` or `/[-\w]+/`. */
200
+ render: string;
201
+ /** Arm indices, ascending. */
202
+ members: readonly number[];
203
+ };
204
+ export type ChoiceInventoryEntry = {
205
+ site: ChoiceSite;
206
+ siteKey: string;
207
+ arity: number;
208
+ strategy: ChoiceStrategyTag;
209
+ disjoint: boolean;
210
+ gated: boolean;
211
+ /** Every group of >= 2 arms sharing a concrete leading term, ordered by member
212
+ * count descending then key. Empty when no two arms share one. */
213
+ groups: readonly PrefixGroup[];
214
+ /** Per-arm reasons, ascending by arm index. Only arms that produced no key. */
215
+ armDeclines: readonly {
216
+ arm: number;
217
+ reason: ArmDeclineReason;
218
+ detail: string;
219
+ }[];
220
+ factored: boolean;
221
+ /** Present iff `factored` is false. */
222
+ declineReason?: SiteDeclineReason;
223
+ /** Arms that sit in some group but whose site was NOT factored — the count of
224
+ * alternatives currently re-scanning a prefix a sibling already scanned. */
225
+ unfactoredArms: number;
226
+ };
227
+ export type ChoiceInventoryReport = {
228
+ readonly schema: 'parseman.choice-inventory/1';
229
+ rules: number;
230
+ /** Distinct `choice` combinator instances reachable from the rule map. */
231
+ choiceSites: number;
232
+ factoredSites: number;
233
+ /** Sites with >= 1 prefix group that were NOT factored. The backlog. */
234
+ backlogSites: number;
235
+ backlogArms: number;
236
+ /**
237
+ * Rule-map entries that are unresolvable references — a `g.X` hole that only
238
+ * `compose()` binds. Their subtrees were NOT walked, so this report is partial by
239
+ * exactly this much. Ascending. Empty is the only value that means "complete".
240
+ */
241
+ unresolvedRoots: readonly string[];
242
+ /** Ascending by `siteKey`. Complete — every reachable choice, including the ones
243
+ * with nothing to report. */
244
+ entries: readonly ChoiceInventoryEntry[];
245
+ };
246
+ export type WastedWorkArm = {
247
+ siteKey: string;
248
+ site: ChoiceSite;
249
+ arm: number;
250
+ /** Short rendering of the arm's head, so the ranked list reads without opening
251
+ * the grammar. */
252
+ label: string;
253
+ /** INTERPRETED: every entry, because the interpreter's firstMatch loop gates nothing. */
254
+ attempts: number;
255
+ failures: number;
256
+ /** INTERPRETED: input bytes re-scanned by this arm's FAILED attempts, summed over the
257
+ * corpus. For one attempt: (furthest position a terminal was tried) - (entry position). */
258
+ wastedBytes: number;
259
+ /** True when codegen emits a first-char guard for this arm, i.e. the shipped parser
260
+ * enters it less often than the interpreter does. */
261
+ firstCharGated: boolean;
262
+ /** COMPILED MODEL: entries that survive codegen's first-char guard. This is the
263
+ * number the shipped parser actually pays. Equals `attempts` when `firstCharGated`
264
+ * is false. */
265
+ gatedAttempts: number;
266
+ gatedFailures: number;
267
+ /** COMPILED MODEL: the ranking column. Bytes re-scanned by failed attempts that
268
+ * codegen's guard would NOT have skipped. */
269
+ gatedWastedBytes: number;
270
+ };
271
+ export type WastedWorkSite = {
272
+ siteKey: string;
273
+ site: ChoiceSite;
274
+ strategy: ChoiceStrategyTag;
275
+ arity: number;
276
+ /** False when the strategy cannot be instrumented; then `wastedBytes` is 0 because
277
+ * nothing was measured, NOT because nothing was wasted. */
278
+ instrumented: boolean;
279
+ attempts: number;
280
+ failures: number;
281
+ wastedBytes: number;
282
+ gatedAttempts: number;
283
+ gatedFailures: number;
284
+ gatedWastedBytes: number;
285
+ };
286
+ export type WastedWorkReport = {
287
+ readonly schema: 'parseman.wasted-work/1';
288
+ corpusFiles: number;
289
+ corpusBytes: number;
290
+ parsedOk: number;
291
+ parsedFailed: number;
292
+ /** Sites the profiler installed instrumentation on. */
293
+ instrumentedSites: number;
294
+ /** Reachable choice sites whose strategy cannot be instrumented (see the module
295
+ * header, blind spot 1). Reported so a low total is not read as a clean grammar. */
296
+ uninstrumentableSites: number;
297
+ /** Rule-map entries that are unresolvable references; their subtrees carry no
298
+ * instrumentation, so the total below is a LOWER BOUND by exactly this much. */
299
+ unresolvedRoots: readonly string[];
300
+ /** INTERPRETED total. Read `totalGatedWastedBytes` for what the shipped parser pays. */
301
+ totalWastedBytes: number;
302
+ /** COMPILED MODEL total — the headline number, and the one a gate should use. */
303
+ totalGatedWastedBytes: number;
304
+ /** Descending by `gatedWastedBytes`, then `wastedBytes`, then `siteKey`, then `arm`. */
305
+ arms: readonly WastedWorkArm[];
306
+ /**
307
+ * ORDERING INVERSIONS: arms that were attempted at least `inversionMinAttempts`
308
+ * times and failed EVERY time, while a later arm at the same site matched.
309
+ *
310
+ * A second ranking, because bytes alone answer the wrong question for the clearest
311
+ * class of defect. Measured on jess's CSS grammar: `StylesheetAtRule › dispatch[3]`
312
+ * has arm 0 failing 19 of 19 attempts while arm 1 matches — an unambiguous ordering
313
+ * bug — yet it is only the 14th largest site by bytes, because the prelude it
314
+ * re-scans is short. Bytes rank what costs most; this ranks what is most obviously
315
+ * WRONG. Both are in the report and neither is derived from the other.
316
+ *
317
+ * Computed from the GATED columns, so an arm codegen already skips is not reported
318
+ * as an inversion in a parser that never enters it.
319
+ *
320
+ * Descending by `gatedAttempts` — NOT by bytes. An arm can fail 100% of its attempts
321
+ * and re-scan zero bytes, because it fails on its own first terminal; jess's `Value`
322
+ * arm 8 `CustomPropertyValue` does exactly that, 925 times, and is the one ungated arm
323
+ * at that site. Ranked by bytes it is invisible; ranked by attempts it is a finding.
324
+ */
325
+ inversions: readonly WastedWorkArm[];
326
+ /** Descending by `wastedBytes`, then ascending by `siteKey`. */
327
+ sites: readonly WastedWorkSite[];
328
+ };
329
+ /** A short, deterministic rendering of an arm's head, for the ranked list. */
330
+ export declare function armLabel(arm: Combinator<unknown>): string;
331
+ /**
332
+ * Every `choice` reachable from `ruleMap`, with its shared-prefix groups and whether
333
+ * the compiler factored the site.
334
+ *
335
+ * Complete by construction: the walk visits every reachable combinator instance and
336
+ * emits an entry for each `choice`, whether or not it has anything to report. A site
337
+ * missing from this report is a site the walk could not reach, never a site that was
338
+ * judged uninteresting.
339
+ */
340
+ export declare function analyzeChoiceInventory(ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>): ChoiceInventoryReport;
341
+ /** Exposed so the fidelity test can compare the model against emitted guards. */
342
+ export declare function modelledFirstCharGate(p: Combinator<unknown>): FirstSet | null;
343
+ export type WastedWorkCorpusEntry = {
344
+ /** Stable identifier. Use a repo-relative path, never an absolute one — it appears
345
+ * in the report, and an absolute path would make the report machine-dependent. */
346
+ id: string;
347
+ text: string;
348
+ };
349
+ export type ProfileWastedWorkOptions = {
350
+ /** The combinator rule map, for site naming and instrumentation. */
351
+ rules: ReadonlyArray<readonly [string, Combinator<unknown>]>;
352
+ /** The rule to start each parse at. A key of `rules`, or a combinator. */
353
+ entry: string | Combinator<unknown>;
354
+ corpus: ReadonlyArray<WastedWorkCorpusEntry>;
355
+ /** Passed through to `run()`. Must not contain anything machine-dependent. */
356
+ runOptions?: Record<string, unknown>;
357
+ /**
358
+ * How to parse one corpus entry. Defaults to `run()` in interpreted mode.
359
+ *
360
+ * Overridable because a grammar can need a driver of its own — jess's Less dialect,
361
+ * for instance, requires `state: { source: input }` because its reducers read it —
362
+ * and because a caller who has already built a driver should not have to duplicate
363
+ * it here. It must be a plain interpreted parse; anything compiled defeats the
364
+ * instrumentation silently, since a compiled artifact never reads `def.parsers`.
365
+ */
366
+ runner?: (entry: Combinator<unknown>, input: string, options: Record<string, unknown>) => {
367
+ ok: boolean;
368
+ };
369
+ /** Minimum attempts before an always-failing arm counts as an ordering inversion.
370
+ * Default 4 — below that, "failed every time" is a sample size, not a finding. */
371
+ inversionMinAttempts?: number;
372
+ };
373
+ /**
374
+ * Parse `corpus` with `rules` in INTERPRETED mode, counting input bytes re-scanned
375
+ * by failed choice alternatives.
376
+ *
377
+ * Fails closed on every way of measuring nothing: an empty rule map, an unknown entry
378
+ * rule, an empty corpus, an empty corpus file, or a grammar in which no instrumentable
379
+ * choice site exists. Each of those would otherwise produce a report of zero wasted
380
+ * bytes, which reads as a clean grammar.
381
+ */
382
+ export declare function profileWastedWork(opts: ProfileWastedWorkOptions): WastedWorkReport;
383
+ //# sourceMappingURL=choice-cost.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"choice-cost.d.ts","sourceRoot":"","sources":["../../src/analysis/choice-cost.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * COMMITMENT ANALYSIS — where a construct can still fail after it has consumed
3
+ * input or dirtied the capture buffers.
4
+ *
5
+ * This is a whole-grammar property of the rule graph, and it has exactly two
6
+ * consumers: the compiler, which bakes the decisions into the emitted source,
7
+ * and the interpreter, which must reach the SAME decisions at load time. It
8
+ * lives here, in one module, for that reason. Two hand-written copies of a
9
+ * predicate like `mayFail` would drift, and a drift here is not a size
10
+ * regression — it is the compiled and interpreted parsers accepting different
11
+ * languages. One module, two consumers.
12
+ *
13
+ * Every predicate in this file is a sound OVER-approximation of "something can
14
+ * go wrong": each MUST err toward `true`. A `false` is a proof obligation
15
+ * discharged on the caller's behalf, and callers delete code on the strength of
16
+ * it. Elide only where non-failure is PROVEN; when in doubt, say `true` and keep
17
+ * the machinery.
18
+ */
19
+ import type { Combinator } from '../types.ts';
20
+ /**
21
+ * Can `p` FAIL at all?
22
+ *
23
+ * `false` needs a construct whose definition is total: the empty literal,
24
+ * trivia (a scan of width 0 is a match), and the repeats — but the repeats only
25
+ * conditionally, see below.
26
+ *
27
+ * Everything else stays `true`, including two cases that look elidable and are
28
+ * not. A `choice` whose last arm is infallible is NOT infallible, because
29
+ * first-set gating decides at runtime which arms are entered at all and the
30
+ * total arm may be skipped. And a re-entered cycle is `true` rather than
31
+ * `false`: a recursive rule's own fallibility is exactly what the recursion is
32
+ * being asked about, so the safe fixpoint seed is the conservative one.
33
+ *
34
+ * TODO(0.47): `expect` is conservative here. It converts its inner parser's
35
+ * failure into a zero-width success carrying a parse-error node and therefore
36
+ * never fails, but it currently reports its inner parser's fallibility. Making
37
+ * it `false` would elide further; it needs its own pass through the tree-diff
38
+ * gate first, because `expect` sits at exactly the closing-delimiter positions
39
+ * where a wrong answer is most visible.
40
+ */
41
+ export declare function mayFail(p: Combinator<unknown>, seen?: Set<Combinator<unknown>>): boolean;
42
+ /**
43
+ * Does `p`, WHEN IT SUCCEEDS, always consume at least one character?
44
+ *
45
+ * The mirror of `mayFail`, and the opposite polarity from
46
+ * `first-set.ts`'s `matchesEmpty`: this one MUST err toward `false`. A `true`
47
+ * is a proof that the success path advanced the position, and the sequence
48
+ * emitter deletes a trivia rewind on the strength of it.
49
+ *
50
+ * It cannot be written as `!matchesEmpty(p)`. `matchesEmpty` answers a
51
+ * first-set question and is allowed to under-report nullability; for `expect`
52
+ * it does, and that case is exactly the one that matters here. `expect(X)`
53
+ * turns X's FAILURE into a zero-width SUCCESS carrying a parse-error node —
54
+ * so `expect(literal('}'))` succeeds having consumed nothing, while
55
+ * `matchesEmpty` looks through to the non-nullable literal and says "always
56
+ * consumes". Eliding on that answer moves a rule's span end past trailing
57
+ * trivia (measured: 38 tree mismatches across the css corpus, spans absorbing
58
+ * whitespace their legacy build excluded). `recover` manufactures the same
59
+ * zero-width success, and both are `false` here.
60
+ *
61
+ * `dispatch` is `false` rather than deferring to its selector: the selector's
62
+ * consumption is not unconditionally part of the dispatch's own span, and the
63
+ * few bytes are not worth the proof obligation.
64
+ */
65
+ export declare function alwaysConsumes(p: Combinator<unknown>, seen?: Set<Combinator<unknown>>): boolean;
66
+ /**
67
+ * True when parsing `p` may push a capture (leaf/child/trivia) into the active
68
+ * buffers and THEN fail, leaving partial state that an enclosing node() would
69
+ * wrongly absorb. Used to decide whether a fallible block needs CST-rollback.
70
+ *
71
+ * Sound over-approximation: the ONLY constructs that capture-then-fail are
72
+ * - a sequence whose non-final term captures before a later term can fail
73
+ * - a sepBy/oneOrMore item-then-separator partial (handled by their own
74
+ * dedicated rollback, so still covered conservatively here)
75
+ * Atomic terminals (literal/regex/keywords/charClass/guard/not) fail without
76
+ * having captured. node() buffers into a private sub-scope and discards it on
77
+ * failure, so it never leaks. choice/firstMatch roll back each failed arm
78
+ * internally. optional/many never "fail" with partial output. Delegating
79
+ * wrappers (transform/label/grammar/withCtx/expect) pass through to inner.
80
+ */
81
+ export declare function mayLeavePartialCapture(p: Combinator<unknown>, seen?: Set<Combinator<unknown>>, triviaActive?: boolean): boolean;
82
+ /** True when `p` can push a leaf/node into the capture buffers on success. */
83
+ export declare function capturesLeaf(p: Combinator<unknown>, seen?: Set<Combinator<unknown>>): boolean;
84
+ /**
85
+ * Does this combinator tree contain a node() anywhere (following ref/lazy
86
+ * thunks)? Determines whether the compile emits CST capture — so non-node
87
+ * grammars stay byte-identical. `seen` guards against recursion cycles.
88
+ */
89
+ export declare function hasNodeDef(p: Combinator<unknown>, seen?: Set<Combinator<unknown>>): boolean;
90
+ /**
91
+ * Whether a grammar tree owns a DIRECT semantic node reduction — a `node(..., build)`
92
+ * whose callback produces the value itself, as opposed to a purely structural
93
+ * `node(parser)`.
94
+ *
95
+ * It is the predicate behind `hostBranchElided`: an artifact only drops a
96
+ * positioned-CST branch if there was a direct builder to drop, so an all-structural
97
+ * grammar stays usable with either host (`cst/host-mode.ts`).
98
+ *
99
+ * It belongs in THIS module for the reason stated at the top of it: both lowerings
100
+ * must reach the same answer, and a stamp they disagree about is a driver that
101
+ * accepts a host it should refuse. It previously lived in `compiler/codegen.ts`,
102
+ * which forced `table/compile-rule-map.ts` to import the engine the table replaced.
103
+ *
104
+ * The descent mirrors the source lowering's own `childrenOf` exactly — including a
105
+ * `routed()` fallback and a `dispatch` matcher arm, both of which are real emit
106
+ * sites and neither of which `hasNodeDef` above walks.
107
+ */
108
+ export declare function hasDirectBuildDef(p: Combinator<unknown>, seen?: Set<Combinator<unknown>>): boolean;
109
+ /** True when `p` can report a committed failure through emitFallible's failure channel. */
110
+ export declare function mayCommitFailure(p: Combinator<unknown>, seen?: Set<Combinator<unknown>>): boolean;
111
+ /**
112
+ * `hasDirectBuilders` / `isRecognitionOnly` for a rule map WITHOUT lowering it.
113
+ *
114
+ * `composeLeaf()` gates on both: every pre-final grammar must prove recognition-only,
115
+ * and the local leaf's direct builders decide whether the recognition pieces need
116
+ * terminal capture. Both were only ever available as fields on `LinkablePieces`, so
117
+ * the gate forced a full `compileLinkable()` of every piece purely to read two
118
+ * booleans off the result — which is why the table lowering appeared to be blocked on
119
+ * porting the source lowering wholesale.
120
+ *
121
+ * They are not lowering products. Both are predicates over the COMBINATOR GRAPH, and
122
+ * this computes them from the graph directly, with the SAME `externalRefs` rule the
123
+ * lowering applies (`:6008`): a named `lazy` whose thunk throws is a HOLE, bound by
124
+ * name at fuse time, and is therefore not evidence of unknown semantics. An UNNAMED
125
+ * unresolved `ref()` stays semantic — nobody can bind it, so it fails closed.
126
+ */
127
+ export declare function classifyRuleMap(ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>): {
128
+ hasDirectBuilders: boolean;
129
+ isRecognitionOnly: boolean;
130
+ };
131
+ //# sourceMappingURL=commitment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commitment.d.ts","sourceRoot":"","sources":["../../src/analysis/commitment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,aAAa,CAAA;AAIxD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,GAAE,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAa,GAAG,OAAO,CAqCnG;AAkCD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,GAAE,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAa,GAAG,OAAO,CAiC1G;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,GAAE,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAa,EAAE,YAAY,UAAO,GAAG,OAAO,CAwGvI;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,GAAE,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAa,GAAG,OAAO,CAkDxG;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,GAAE,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAa,GAAG,OAAO,CA6BtG;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,GAAE,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAa,GAAG,OAAO,CAkC7G;AAED,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,GAAE,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAa,GAAG,OAAO,CAqC5G;AAwCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GAC7D;IAAE,iBAAiB,EAAE,OAAO,CAAC;IAAC,iBAAiB,EAAE,OAAO,CAAA;CAAE,CAwB5D"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The SECOND world.
3
+ *
4
+ * A type error has one source to point at. A grammar finding has two — the ordered
5
+ * choice whose arms cost the time, and the input that pays for it — and relating them is
6
+ * the whole diagnostic. `gating.ts` owns the first world and is entirely static. This
7
+ * module owns the second, and it is deliberately the smallest honest thing that can be
8
+ * said about a corpus without instrumenting a parse.
9
+ *
10
+ * WHAT IS MEASURED, EXACTLY
11
+ * -------------------------
12
+ * For a choice, every corpus POSITION whose character is in some arm's first set. That
13
+ * is a count of characters, not of times the choice was reached, and the difference
14
+ * matters: a choice nested three rules deep is reached far less often than its first
15
+ * characters occur. So the number is reported as what it is — an upper bound on entries,
16
+ * derived from the same first sets the compiler dispatches on — and never as "this
17
+ * choice ran N times". Naming it precisely is what keeps it useful; a number that
18
+ * overstates itself is worse than no number, because the reader stops trusting all of
19
+ * them.
20
+ *
21
+ * The one thing this DOES settle, exactly and without any modelling: an arm whose first
22
+ * set is ANY is entered at every one of those positions, because no first character can
23
+ * exclude it. That is the cost the fix removes, and it is arithmetic, not a guess.
24
+ *
25
+ * Determinism: counts and the located example are a pure function of (report, corpus).
26
+ * No timings, no paths beyond the caller's own sample names.
27
+ */
28
+ import type { Combinator, FirstSet } from '../types.ts';
29
+ import type { ChoiceGating } from './gating.ts';
30
+ export type CorpusSample = {
31
+ name: string;
32
+ text: string;
33
+ };
34
+ /** A located input position: the caller's sample name plus 1-based line/column. */
35
+ export type CorpusSite = {
36
+ sample: string;
37
+ line: number;
38
+ column: number;
39
+ /** The source line, untrimmed, for a caret rendering. */
40
+ lineText: string;
41
+ };
42
+ export type ArmCorpusCost = {
43
+ index: number;
44
+ /** ANY first set — no character excludes this arm. */
45
+ any: boolean;
46
+ /**
47
+ * Corpus positions whose character this arm's first set accepts. For an `any` arm this
48
+ * equals the choice total, which is the point.
49
+ */
50
+ positions: number;
51
+ /** The first such position, for the two-world rendering. Absent when there are none. */
52
+ firstSite?: CorpusSite;
53
+ };
54
+ export type ChoiceCorpusCost = {
55
+ choiceId: string;
56
+ /** Positions whose character SOME arm accepts — the choice's own reach, bounded. */
57
+ positions: number;
58
+ /** Every position in the corpus, so a share can be read. */
59
+ corpusPositions: number;
60
+ arms: ArmCorpusCost[];
61
+ };
62
+ /**
63
+ * Measure one choice against a corpus. `choice.anyArms` already knows which arms are
64
+ * broad; this adds how much input that costs.
65
+ */
66
+ export declare function measureChoiceCost(c: ChoiceGating, samples: readonly CorpusSample[], arms: readonly {
67
+ firstSet: FirstSet;
68
+ }[]): ChoiceCorpusCost;
69
+ /** The per-arm first sets a choice dispatches on, in arm order. */
70
+ export declare function armFirstSets(choiceArms: readonly Combinator<unknown>[]): {
71
+ firstSet: FirstSet;
72
+ }[];
73
+ //# sourceMappingURL=corpus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"corpus.d.ts","sourceRoot":"","sources":["../../src/analysis/corpus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzD,mFAAmF;AACnF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,GAAG,EAAE,OAAO,CAAA;IACZ;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB,wFAAwF;IACxF,SAAS,CAAC,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,oFAAoF;IACpF,SAAS,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,eAAe,EAAE,MAAM,CAAA;IACvB,IAAI,EAAE,aAAa,EAAE,CAAA;CACtB,CAAA;AAyBD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,EAAE,GAAG,gBAAgB,CAiC9I;AAED,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,UAAU,EAAE,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,EAAE,CAEjG"}