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
package/README.md CHANGED
@@ -11,7 +11,8 @@ usually mean grammar files, generated code, and extra tooling. Parséman is a co
11
11
  library with an optional compiler, and it gives you both.
12
12
 
13
13
  **Parsing to JS values, the macro build is the fastest general-purpose JS parser in the
14
- suite** — ahead of every other library measured, at every grammar and every input size.
14
+ suite** — ahead of every other library measured, at every grammar and every input size in
15
+ that suite.
15
16
  Every parser in the suite builds real output: objects, row arrays, AST nodes. On a 7.7 kB
16
17
  GraphQL document Parséman takes **131 µs**; Peggy takes 328 µs. Only a purpose-built native
17
18
  edges it out, `JSON.parse` on JSON.
@@ -138,8 +139,9 @@ See **[The three modes](https://matthew-dean.github.io/parseman/guide/modes)**.
138
139
  coverage ("which rules and choice arms ran?") and trace ("what did it try, select, and
139
140
  backtrack through?").
140
141
  - 🚦 **[First-char gating diagnostics](https://matthew-dean.github.io/parseman/guide/first-char-gating)** —
141
- the build tells you which `choice` lost its O(1) dispatch, names the overlapping arms,
142
- and says how to fix it.
142
+ `diagnoseGrammar()` tells you which `choice` lost its O(1) dispatch, names the
143
+ overlapping arms, and says how to fix it. Run it in a test or a CI job — compiling
144
+ reports nothing.
143
145
  - 📈 **[Performance guide](https://matthew-dean.github.io/parseman/guide/performance)** —
144
146
  the levers that actually move a grammar, and [how regexes lower](https://matthew-dean.github.io/parseman/guide/regex-lowering).
145
147
 
@@ -161,6 +163,9 @@ GraphQL **131 µs** vs Peggy's 328 µs, JSON **133 µs** vs Chevrotain's 241 µs
161
163
  **75.3 µs** vs Peggy's 420 µs. Native `JSON.parse` does JSON large in 51.6 µs. On the CST
162
164
  chart, macro build runs 174 µs against Lezer's 594 µs parse-only.
163
165
 
166
+ Those are the committed charts, regenerated at 0.29.0 (2026-07-22) on an M4 Pro, measuring
167
+ the JS-codegen lowering that `compile()` and the macro build emit.
168
+
164
169
  ![JSON parsing benchmarks](https://raw.githubusercontent.com/matthew-dean/parseman/main/assets/bench-json.svg)
165
170
 
166
171
  ![CSV parsing benchmarks](https://raw.githubusercontent.com/matthew-dean/parseman/main/assets/bench-csv.svg)
@@ -0,0 +1,158 @@
1
+ /**
2
+ * CHOICE COST — the policy layer.
3
+ * ===============================
4
+ *
5
+ * Analysis says what the grammar costs. This says which of those numbers fail a build.
6
+ * They are separate because they have different consumers and different lifetimes: a
7
+ * developer wants the ranked list on every run, CI wants one bit, and the threshold
8
+ * that produces that bit is a decision the owner makes once and writes down.
9
+ *
10
+ * This module is PURE — it takes a report and a baseline and returns a verdict. It
11
+ * reads no files, spawns nothing, and prints nothing, so it is testable without a
12
+ * fixture checkout and cannot be the reason a gate is flaky.
13
+ *
14
+ * THE BASELINE IS ABSOLUTE, NOT DIFFERENTIAL
15
+ * ------------------------------------------
16
+ * Gating each commit against its parent lets +2% per commit land forever: every
17
+ * individual step is under tolerance and the total is unbounded. So the committed
18
+ * baseline records the ACTUAL byte counts, and drift is measured against those
19
+ * numbers, not against yesterday's.
20
+ *
21
+ * A baseline is committed DATA, reviewed like any other diff. Nothing here writes one.
22
+ * Rebaselining is a deliberate act with a reviewable artifact and owner sign-off; there
23
+ * is no automatic refresh, because a gate that rebaselines itself is a gate that
24
+ * records regressions rather than catching them.
25
+ *
26
+ * AND IT IS TWO-SIDED — A WIN MUST BE BANKED
27
+ * ------------------------------------------
28
+ * Growth past the committed number fails, and so does an unbanked IMPROVEMENT. This is
29
+ * the half usually left to a comment asking a human politely, and it is the half that
30
+ * rots: `bench/grammar-density/config.json` and `bench/workloads/config.json` each
31
+ * carried exactly such a comment and sat unbumped for TEN releases. If a reorder takes
32
+ * a site from 7,846 wasted bytes to 2,000 and the baseline does not move with it, 5,846
33
+ * bytes of fresh headroom silently become budget for the next regression — and the gate
34
+ * that was supposed to catch that regression reads green through the whole of it.
35
+ *
36
+ * So the committed number is a BAND, not a floor: leaving it in either direction fails,
37
+ * and only the remedy differs. Raising a number needs owner sign-off; lowering one is
38
+ * mandatory. Both are the same one-line rebaseline and the same reviewable diff.
39
+ *
40
+ * `bench/size-guard.ts` is the shape this follows — it earned the tightness in 0.45 by
41
+ * catching +0.14% moves a 1% tolerance would have waved through.
42
+ *
43
+ * A BASELINE CANNOT LAUNDER A CEILING
44
+ * -----------------------------------
45
+ * If a ceiling is configured, a baseline that records a number ABOVE it is rejected as
46
+ * invalid rather than honoured. Otherwise the ceiling would be waivable by rebaselining,
47
+ * which makes it a suggestion. (`bench/size-guard.ts` validates its own baseline for
48
+ * exactly this reason; this follows it.)
49
+ *
50
+ * FAILS CLOSED
51
+ * ------------
52
+ * Every way of not having measured is a failure, never a pass: a missing baseline, a
53
+ * baseline of the wrong shape, an empty baseline, a report over zero corpus files, a
54
+ * report where no site was instrumentable, a report whose grammar walk was incomplete
55
+ * (`unresolvedRoots` non-empty), a baselined key that no longer exists, and a measured
56
+ * key with no baseline entry. This repo already contains the alternative — `ratio:
57
+ * ordered.length === 0 ? 1` in src/coverage.ts reports 100% covered when nothing was
58
+ * analysable — and the whole point of a gate is to not be that.
59
+ */
60
+ import type { WastedWorkReport } from './choice-cost.ts';
61
+ export type WastedWorkBaseline = {
62
+ readonly schema: 'parseman.wasted-work-baseline/1';
63
+ /** Informational: which revision produced the numbers. Never compared. */
64
+ gitRev: string;
65
+ /** Informational, `YYYY-MM-DD`. Never compared — comparing it would make the
66
+ * verdict depend on the clock. */
67
+ updatedAt: string;
68
+ /**
69
+ * Absolute per-corpus totals, keyed by corpus id.
70
+ *
71
+ * `totalWastedBytes` is the INTERPRETED column, and that is deliberate even though
72
+ * `WastedWorkReport` names `totalGatedWastedBytes` as the headline. For BYTES the two
73
+ * are the same number, structurally: the modelled first-char guard is derived from the
74
+ * arm's first SET, which over-approximates what the arm can start with, so wherever the
75
+ * guard rejects, the arm's own leading terminal would have rejected at the same position
76
+ * having consumed nothing. Measured over four dialect grammars, zero arms differ (see the
77
+ * header of `choice-cost.ts`). What the guard removes is ATTEMPTS, not rescanned bytes —
78
+ * so `gatedAttempts`, `gatedFailures` and the inversion ranking DO read the gated columns,
79
+ * and only the byte totals are recorded from the interpreted one, where it is additionally
80
+ * the conservative UPPER bound of the two.
81
+ *
82
+ * That identity is not enforced anywhere it could be relied on silently: `checkWastedWork`
83
+ * judges the compiled column against this same band wherever the two columns part, so a
84
+ * divergence goes red instead of unnoticed.
85
+ */
86
+ totals: Record<string, {
87
+ corpusBytes: number;
88
+ totalWastedBytes: number;
89
+ instrumentedSites: number;
90
+ }>;
91
+ /** Absolute per-site wasted bytes, keyed by `<corpus>::<siteKey>`. Interpreted column,
92
+ * for the reason given on `totals` above. */
93
+ sites: Record<string, number>;
94
+ };
95
+ export type WastedWorkPolicy = {
96
+ /**
97
+ * Maximum wasted bytes per corpus byte. A hard ceiling no rebaseline can waive.
98
+ * Omit for drift-only gating; a project should set it once it knows its own number.
99
+ */
100
+ ceilingRatio?: number;
101
+ /**
102
+ * Half-width of the band around each committed number, as a percentage. Default 1.
103
+ *
104
+ * SYMMETRIC: a measurement more than this ABOVE its baseline is a regression, and a
105
+ * measurement more than this BELOW it is an unbanked win. Both fail.
106
+ *
107
+ * This is not a noise allowance — the metric is a deterministic count of input bytes,
108
+ * so its noise floor is exactly zero and any non-zero value here is pure headroom.
109
+ * It exists only so incidental one-byte churn does not thrash the baseline in both
110
+ * directions. Set it as near zero as the corpus permits; `bench/choice-cost-guard.ts`
111
+ * runs at 0.1, matching `bench/size-guard.ts`'s ratchet slack.
112
+ */
113
+ driftTolerancePct?: number;
114
+ /**
115
+ * Fail on an ordering inversion — an arm that failed every one of its attempts while
116
+ * a later arm at the same site matched. Off by default: on an existing grammar this
117
+ * fires immediately, and a gate that is red on arrival for a pre-existing condition
118
+ * gets disabled rather than fixed. Turn it on once the backlog is empty, to keep it
119
+ * empty.
120
+ */
121
+ failOnInversions?: boolean;
122
+ };
123
+ export type GateBreach = {
124
+ /** `drift` is growth past the band; `shrank` is an improvement that was not banked.
125
+ * Both are failures, and the `detail` says which remedy applies. */
126
+ kind: 'ceiling' | 'drift' | 'shrank' | 'unbaselined' | 'stale' | 'inversion' | 'unmeasurable' | 'invalid-baseline';
127
+ key: string;
128
+ /** One line. Says what happened and what number to look at — never advice. */
129
+ detail: string;
130
+ };
131
+ export type GateVerdict = {
132
+ readonly schema: 'parseman.wasted-work-verdict/1';
133
+ ok: boolean;
134
+ /** Ascending by (kind, key). Deterministic, so a verdict is diffable. */
135
+ breaches: readonly GateBreach[];
136
+ checkedCorpora: number;
137
+ checkedSites: number;
138
+ };
139
+ /**
140
+ * Judge one or more measured corpora against a committed baseline.
141
+ *
142
+ * `reports` is keyed by corpus id — a grammar is usually gated over more than one body
143
+ * of input, and a single blended number would let a regression on one hide behind an
144
+ * improvement on another.
145
+ */
146
+ export declare function checkWastedWork(reports: Readonly<Record<string, WastedWorkReport>>, baseline: unknown, policy?: WastedWorkPolicy): GateVerdict;
147
+ /**
148
+ * Build the baseline a passing measurement would record.
149
+ *
150
+ * Deliberately NOT called by the gate. It exists so a rebaseline is one explicit
151
+ * command whose output is a file in the diff — the reviewable record — rather than
152
+ * something the gate can do to itself on a red run.
153
+ */
154
+ export declare function buildWastedWorkBaseline(reports: Readonly<Record<string, WastedWorkReport>>, meta: {
155
+ gitRev: string;
156
+ updatedAt: string;
157
+ }): WastedWorkBaseline;
158
+ //# sourceMappingURL=choice-cost-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"choice-cost-gate.d.ts","sourceRoot":"","sources":["../../src/analysis/choice-cost-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAExD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,MAAM,EAAE,iCAAiC,CAAA;IAClD,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAA;IACd;uCACmC;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACpG;kDAC8C;IAC9C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB;yEACqE;IACrE,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,aAAa,GAAG,OAAO,GAAG,WAAW,GAAG,cAAc,GAAG,kBAAkB,CAAA;IAClH,GAAG,EAAE,MAAM,CAAA;IACX,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,MAAM,EAAE,gCAAgC,CAAA;IACjD,EAAE,EAAE,OAAO,CAAA;IACX,yEAAyE;IACzE,QAAQ,EAAE,SAAS,UAAU,EAAE,CAAA;IAC/B,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAQD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,EACnD,QAAQ,EAAE,OAAO,EACjB,MAAM,GAAE,gBAAqB,GAC5B,WAAW,CAkLb;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,EACnD,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC1C,kBAAkB,CAWpB"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * CHOICE COST — the human layer.
3
+ * ==============================
4
+ *
5
+ * Three layers, not two:
6
+ *
7
+ * ANALYSIS src/analysis/choice-cost.ts — what the sites are and what they cost.
8
+ * POLICY bench/choice-cost-guard.ts — which of those numbers fail a build.
9
+ * RENDERING this file — how a person reads either of them.
10
+ *
11
+ * The structured report is the source of truth. Nothing here computes a finding, and
12
+ * nothing here decides pass or fail; a gate consumes the data and never this text. If
13
+ * a number appears below that is not in the report, that is a bug in this file.
14
+ *
15
+ * WHAT A PARSER GENERATOR CAN SHOW THAT A COMPILER CANNOT
16
+ * -------------------------------------------------------
17
+ * A type error has one source to point at. A wasted-work finding has TWO, and relating
18
+ * them is the whole diagnostic:
19
+ *
20
+ * - the GRAMMAR site — the ordered choice whose arm ordering costs the time
21
+ * - the COST — measured, over a real corpus, per alternative
22
+ *
23
+ * So the ordering is printed WITH its measured cost beside each arm, which makes the
24
+ * fix self-evident without a paragraph explaining it:
25
+ *
26
+ * StylesheetAtRule › dispatch[0] 1.31 MB rescanned
27
+ * 0 RoutedAtRuleStatement failed 4,182 / 4,271 1.31 MB
28
+ * 1 RoutedLayerBlock matched 4,182
29
+ *
30
+ * Reading that, nobody needs to be told what to do.
31
+ *
32
+ * QUIET BY DEFAULT
33
+ * ----------------
34
+ * Nothing in this module prints. It returns strings. There is no console call and no
35
+ * process-wide default-on channel, deliberately: importing `examples/css/parser.ts`
36
+ * already emits roughly sixty lines of gating advice before any user code runs, and a
37
+ * beautiful diagnostic that nobody reads because it is buried in build noise is not
38
+ * beautiful. This one is available on request — from the CLI gate, or by calling it —
39
+ * and silent otherwise.
40
+ *
41
+ * DETERMINISM BINDS THE RENDERING TOO
42
+ * -----------------------------------
43
+ * The rendering is diffable: it preserves the report's ordering, contains no timings,
44
+ * no dates, no absolute paths, and colour is opt-in rather than auto-detected, so the
45
+ * same report always renders to the same bytes. A renderer that sniffed `isTTY` would
46
+ * produce two different outputs from one report and break exactly that.
47
+ */
48
+ import type { ChoiceInventoryReport, ChoiceInventoryEntry, WastedWorkReport } from './choice-cost.ts';
49
+ import { groupDigits } from './format-number.ts';
50
+ export type RenderOptions = {
51
+ /** Rows to show. Default 20. The report always holds all of them. */
52
+ limit?: number;
53
+ /**
54
+ * ANSI colour. Default false — NOT auto-detected from `isTTY`, because a renderer
55
+ * whose bytes depend on where it is piped cannot be diffed or snapshotted.
56
+ */
57
+ color?: boolean;
58
+ };
59
+ /**
60
+ * Deterministic thousands grouping. `toLocaleString()` is locale-dependent and
61
+ * would make the output differ between machines — the one thing a gateable
62
+ * rendering cannot do.
63
+ *
64
+ * DEFINED IN `./format-number.ts` and re-exported here because this module's own
65
+ * callers import it from here. It was a byte-identical second copy: `./terminal.ts`
66
+ * declared one, this file declared another, and the two were one edit away from
67
+ * disagreeing about how a report reads on two different screens.
68
+ */
69
+ export { groupDigits };
70
+ /** Byte counts at a glance. Binary units, fixed to one decimal, so 1,376,256 renders
71
+ * as `1.3 MB` on every machine. */
72
+ export declare function bytes(n: number): string;
73
+ /**
74
+ * The combinator source a left-factoring would produce, ready to paste.
75
+ *
76
+ * Offered ONLY for the shape where it is mechanical and total: every arm of the choice
77
+ * in one group. A partial group needs the author to decide which arms move and in what
78
+ * order — an ordering decision that changes what the grammar accepts, which no
79
+ * generated rewrite may make silently.
80
+ *
81
+ * This is a PREVIEW. It is not applied, and — until the digest-verification loop
82
+ * described in `docs/` is wired — it is not yet proven behaviour-preserving, so it is
83
+ * labelled a candidate rather than offered as a fix. An unverified rewrite presented
84
+ * as verified is worse than no suggestion at all.
85
+ */
86
+ export declare function leftFactorPreview(e: ChoiceInventoryEntry): string | null;
87
+ export declare function renderChoiceInventory(r: ChoiceInventoryReport, opts?: RenderOptions): string;
88
+ /**
89
+ * The ranked list, with each top site expanded into its arm ordering.
90
+ *
91
+ * The expansion is the point. A ranked list of site keys tells you WHERE; the arm
92
+ * breakdown beside it tells you WHY, and "arm 0 failed 4,182 of 4,271 times" is an
93
+ * argument for moving arm 0 that needs no prose attached.
94
+ */
95
+ export declare function renderWastedWork(r: WastedWorkReport, opts?: RenderOptions): string;
96
+ //# sourceMappingURL=choice-cost-render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"choice-cost-render.d.ts","sourceRoot":"","sources":["../../src/analysis/choice-cost-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,KAAK,EACV,qBAAqB,EAAE,oBAAoB,EAAE,gBAAgB,EAC9D,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEhD,MAAM,MAAM,aAAa,GAAG;IAC1B,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAID;;;;;;;;;GASG;AACH,OAAO,EAAE,WAAW,EAAE,CAAA;AAEtB;oCACoC;AACpC,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAIvC;AA8CD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,oBAAoB,GAAG,MAAM,GAAG,IAAI,CAOxE;AAID,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,qBAAqB,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CA0ChG;AAID;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CAmEtF"}