parseman 0.45.0 → 0.46.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 (64) hide show
  1. package/dist/analysis/choice-cost-gate.d.ts +158 -0
  2. package/dist/analysis/choice-cost-gate.d.ts.map +1 -0
  3. package/dist/analysis/choice-cost-render.d.ts +88 -0
  4. package/dist/analysis/choice-cost-render.d.ts.map +1 -0
  5. package/dist/analysis/choice-cost.d.ts +383 -0
  6. package/dist/analysis/choice-cost.d.ts.map +1 -0
  7. package/dist/analysis/corpus.d.ts +73 -0
  8. package/dist/analysis/corpus.d.ts.map +1 -0
  9. package/dist/analysis/diagnose-render.d.ts +65 -0
  10. package/dist/analysis/diagnose-render.d.ts.map +1 -0
  11. package/dist/analysis/diagnose.d.ts +15 -0
  12. package/dist/analysis/diagnose.d.ts.map +1 -1
  13. package/dist/analysis/diagnostics.cjs +11272 -0
  14. package/dist/analysis/diagnostics.cjs.map +7 -0
  15. package/dist/analysis/diagnostics.d.ts +27 -0
  16. package/dist/analysis/diagnostics.d.ts.map +1 -0
  17. package/dist/analysis/diagnostics.js +11249 -0
  18. package/dist/analysis/diagnostics.js.map +7 -0
  19. package/dist/analysis/fix-render.d.ts +43 -0
  20. package/dist/analysis/fix-render.d.ts.map +1 -0
  21. package/dist/analysis/fix.d.ts +172 -0
  22. package/dist/analysis/fix.d.ts.map +1 -0
  23. package/dist/analysis/gating.d.ts +8 -0
  24. package/dist/analysis/gating.d.ts.map +1 -1
  25. package/dist/analysis/rebuild.d.ts +61 -0
  26. package/dist/analysis/rebuild.d.ts.map +1 -0
  27. package/dist/analysis/terminal.d.ts +142 -0
  28. package/dist/analysis/terminal.d.ts.map +1 -0
  29. package/dist/cli/index.cjs +12507 -0
  30. package/dist/cli/index.cjs.map +7 -0
  31. package/dist/cli/index.d.ts +3 -0
  32. package/dist/cli/index.d.ts.map +1 -0
  33. package/dist/cli/index.js +12484 -0
  34. package/dist/cli/index.js.map +7 -0
  35. package/dist/compiler/build-arity.d.ts.map +1 -1
  36. package/dist/compiler/linker.d.ts +44 -6
  37. package/dist/compiler/linker.d.ts.map +1 -1
  38. package/dist/compiler/module-hoist.d.ts +106 -0
  39. package/dist/compiler/module-hoist.d.ts.map +1 -0
  40. package/dist/index.cjs +1297 -297
  41. package/dist/index.cjs.map +4 -4
  42. package/dist/index.d.ts +8 -2
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +1297 -297
  45. package/dist/index.js.map +4 -4
  46. package/dist/language-service/index.cjs.map +2 -2
  47. package/dist/language-service/index.js.map +2 -2
  48. package/dist/plugin/evaluator.d.ts +1 -1
  49. package/dist/plugin/evaluator.d.ts.map +1 -1
  50. package/dist/plugin/free-identifiers.d.ts +23 -0
  51. package/dist/plugin/free-identifiers.d.ts.map +1 -0
  52. package/dist/plugin/index.cjs +429 -43
  53. package/dist/plugin/index.cjs.map +4 -4
  54. package/dist/plugin/index.d.ts.map +1 -1
  55. package/dist/plugin/index.js +429 -43
  56. package/dist/plugin/index.js.map +4 -4
  57. package/dist/plugin/reducer-resolver.d.ts +17 -1
  58. package/dist/plugin/reducer-resolver.d.ts.map +1 -1
  59. package/dist/spec/index.cjs +2 -1
  60. package/dist/spec/index.cjs.map +2 -2
  61. package/dist/spec/index.js +2 -1
  62. package/dist/spec/index.js.map +2 -2
  63. package/dist/version.d.ts +1 -1
  64. package/package.json +18 -3
@@ -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,88 @@
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
+ export type RenderOptions = {
50
+ /** Rows to show. Default 20. The report always holds all of them. */
51
+ limit?: number;
52
+ /**
53
+ * ANSI colour. Default false — NOT auto-detected from `isTTY`, because a renderer
54
+ * whose bytes depend on where it is piped cannot be diffed or snapshotted.
55
+ */
56
+ color?: boolean;
57
+ };
58
+ /** Deterministic thousands grouping. `toLocaleString()` is locale-dependent and would
59
+ * make the output differ between machines — the one thing a gateable rendering
60
+ * cannot do. */
61
+ export declare function groupDigits(n: number): string;
62
+ /** Byte counts at a glance. Binary units, fixed to one decimal, so 1,376,256 renders
63
+ * as `1.3 MB` on every machine. */
64
+ export declare function bytes(n: number): string;
65
+ /**
66
+ * The combinator source a left-factoring would produce, ready to paste.
67
+ *
68
+ * Offered ONLY for the shape where it is mechanical and total: every arm of the choice
69
+ * in one group. A partial group needs the author to decide which arms move and in what
70
+ * order — an ordering decision that changes what the grammar accepts, which no
71
+ * generated rewrite may make silently.
72
+ *
73
+ * This is a PREVIEW. It is not applied, and — until the digest-verification loop
74
+ * described in `docs/` is wired — it is not yet proven behaviour-preserving, so it is
75
+ * labelled a candidate rather than offered as a fix. An unverified rewrite presented
76
+ * as verified is worse than no suggestion at all.
77
+ */
78
+ export declare function leftFactorPreview(e: ChoiceInventoryEntry): string | null;
79
+ export declare function renderChoiceInventory(r: ChoiceInventoryReport, opts?: RenderOptions): string;
80
+ /**
81
+ * The ranked list, with each top site expanded into its arm ordering.
82
+ *
83
+ * The expansion is the point. A ranked list of site keys tells you WHERE; the arm
84
+ * breakdown beside it tells you WHY, and "arm 0 failed 4,182 of 4,271 times" is an
85
+ * argument for moving arm 0 that needs no prose attached.
86
+ */
87
+ export declare function renderWastedWork(r: WastedWorkReport, opts?: RenderOptions): string;
88
+ //# 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;AAEzB,MAAM,MAAM,aAAa,GAAG;IAC1B,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAID;;iBAEiB;AACjB,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ7C;AAED;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"}
@@ -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. COMPILED output does not: `emitFirstMatch`
9
+ * (src/compiler/codegen.ts:2246-2277) 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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"}