parseman 0.10.0 → 0.12.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matthew Dean
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # Parséman (PAR-zə-mahn)
6
6
 
7
- Write parsers in TypeScript — fast enough to run as-is, and blazing fast when the bundler macro kicks in. Same code either way; no grammar files, no generated output to check in. Drop the plugin in tests or anywhere a bundler isn't around and everything still works.
7
+ Write parsers in TypeScript — fast enough to run as-is, and blazing fast when the bundler macro kicks in. Same grammar either way; no grammar files, no generated output to check in. Drop the plugin in tests or anywhere a bundler isn't around and everything still works.
8
8
 
9
9
  > **📖 Full documentation: [matthew-dean.github.io/parseman](https://matthew-dean.github.io/parseman/)**
10
10
 
@@ -56,7 +56,7 @@ See **[The three modes](https://matthew-dean.github.io/parseman/guide/modes)** f
56
56
  - **[Whitespace & trivia](https://matthew-dean.github.io/parseman/guide/trivia)** — grammar-defined filler skipping, with per-chunk kind capture.
57
57
  - **[Recursive rules](https://matthew-dean.github.io/parseman/guide/recursive-rules)** — `rules()` for mutually recursive grammars; fully macro-compilable.
58
58
  - **[CST / AST nodes](https://matthew-dean.github.io/parseman/guide/ast)** — `node()` captures terminals and trivia for you, with a `collapse` option for transparent wrapper rules.
59
- - **[Incremental re-parsing](https://matthew-dean.github.io/parseman/guide/incremental)** — `makeFunctionalDoc` re-parses just the edited subtree on each keystroke.
59
+ - **[Incremental re-parsing](https://matthew-dean.github.io/parseman/guide/incremental)** — `parseDoc` re-parses just the edited subtree on each keystroke.
60
60
  - **[Error recovery](https://matthew-dean.github.io/parseman/guide/error-recovery)** — `recover`, `expect`, and a `{ recover: true }` channel keep parsing broken input and report every error.
61
61
  - **[Context-sensitive parsing](https://matthew-dean.github.io/parseman/guide/context)** — `withCtx` / `guard` without mutating shared state.
62
62
 
@@ -66,13 +66,13 @@ Full API in the **[reference](https://matthew-dean.github.io/parseman/reference/
66
66
 
67
67
  ## Benchmarks
68
68
 
69
- **When parsing to JS values** — objects, row arrays, AST nodes — **Parséman's macro build is the fastest general-purpose JS parser we benchmark**, beating Peggy, Parsimmon, Chevrotain, Nearley, and Jison at every grammar and size. The only thing that edges it out is a purpose-built native like `JSON.parse`; for anything that *doesn't* have a built-in, Parséman is the one to beat.
69
+ **When parsing to JS values** — objects, row arrays, AST nodes — **Parséman's macro build is the fastest general-purpose JS parser we benchmark**, beating [Peggy](https://peggyjs.org/), [Parsimmon](https://github.com/jneen/parsimmon), [Chevrotain](https://chevrotain.io/), [Nearley](https://nearley.js.org/), and [Jison](https://github.com/zaach/jison) at every grammar and size. The only thing that edges it out is a purpose-built native like `JSON.parse`; for anything that *doesn't* have a built-in, Parséman is the one to beat.
70
70
 
71
- For **syntax tree building**, the compiled CST path (macro build) beats [Lezer](https://lezer.codemirror.net/) on the JSON CST fixture too — while producing a richer object tree with spans and trivia. For **incremental re-parse**, the two trade places by edit type: Parséman's `makeFunctionalDoc` is ~300× faster than a full reparse on in-place value edits (and ahead of Lezer), while Lezer's buffer reuse wins on structural edits. Full breakdown in the [benchmarks guide](https://matthew-dean.github.io/parseman/guide/benchmarks).
71
+ For **syntax tree building**, the compiled CST path (macro build) beats [Lezer](https://lezer.codemirror.net/) on the JSON CST fixture too — while producing a richer object tree with spans and trivia. For **incremental re-parse**, the two trade places by edit type: Parséman's `parseDoc` is ~300× faster than a full reparse on in-place value edits (and ahead of Lezer), while Lezer's buffer reuse wins on structural edits. Full breakdown in the [benchmarks guide](https://matthew-dean.github.io/parseman/guide/benchmarks).
72
72
 
73
- Measured on Apple M2 Pro. Bars show µs per parse — shorter is faster. Refresh: `pnpm bench`, then `pnpm bench:svg` (updates `assets/bench-*.svg`).
73
+ Measured on Apple M4 Pro. Bars show µs per parse — shorter is faster. Refresh: `pnpm bench:svg` (benchmarks chart parsers and updates `assets/bench-*.svg`).
74
74
 
75
- Compared parsers: **Parséman**, Peggy, Parsimmon, Chevrotain, Nearley, and Jison (plus `JSON.parse` on JSON). Each implements the same parsing work on the bench fixtures — building JS values / row arrays / GraphQL AST nodes, not syntax-only validation. Peggy grammars in `bench/*.pegjs` are the reference; Nearley JSON uses [kach/nearley `examples/json.ne`](https://github.com/kach/nearley/blob/master/examples/json.ne); other Nearley and Jison grammars are ports of those Peggy files (`bench/vendor/`).
75
+ Compared parsers: **Parséman**, [Peggy](https://peggyjs.org/), [Parsimmon](https://github.com/jneen/parsimmon), [Chevrotain](https://chevrotain.io/), [Nearley](https://nearley.js.org/), and [Jison](https://github.com/zaach/jison) (plus `JSON.parse` on JSON). Each implements the same parsing work on the bench fixtures — building JS values / row arrays / GraphQL AST nodes, not syntax-only validation. Peggy grammars in `bench/*.pegjs` are the reference; Nearley JSON uses [kach/nearley `examples/json.ne`](https://github.com/kach/nearley/blob/master/examples/json.ne); other Nearley and Jison grammars are ports of those Peggy files (`bench/vendor/`).
76
76
 
77
77
  ![JSON parsing benchmarks](https://raw.githubusercontent.com/matthew-dean/parseman/main/assets/bench-json.svg)
78
78
 
@@ -84,7 +84,7 @@ Compared parsers: **Parséman**, Peggy, Parsimmon, Chevrotain, Nearley, and Jiso
84
84
 
85
85
  Parséman has three modes — **interpreter** (zero setup, works anywhere), **macro build** (compiled by the bundler plugin at build time, zero runtime cost), and **`.compile()`** (optional runtime JIT). Most production use lands on one of the first two. The initialization section only shows parsers with a nonzero setup cost: `.compile()` costs 75–650 µs depending on grammar size; Chevrotain always costs 840–1,400 µs. Parsers not listed there start for free. (Init numbers are pinned on the charts — they're noisy run-to-run; warm-parse bars are the meaningful comparison.)
86
86
 
87
- On JSON, CSV, and GraphQL, Parséman macro beats every other library at every fixture size in the charts above — e.g. GraphQL large **154 µs** vs Peggy **423 µs**, JSON large **125 µs** vs Peggy **472 µs** / Chevrotain **1,946 µs**. Native `JSON.parse` is the one thing faster on JSON (**45 µs** large). Even the zero-setup interpreter beats all five libraries at large sizes. On the CST chart, macro build beats Lezer at every size (**344 µs** vs **587 µs** large, parse-only). Full write-up and how to refresh the charts: **[benchmarks guide](https://matthew-dean.github.io/parseman/guide/benchmarks)**. Grammar-level speed levers: [performance guide](https://matthew-dean.github.io/parseman/guide/performance); library-level codegen: [PERF_IDEAS.md](./PERF_IDEAS.md).
87
+ On JSON, CSV, and GraphQL, Parséman macro beats every other library at every fixture size in the charts above — e.g. GraphQL large **154 µs** vs Peggy **423 µs**, JSON large **125 µs** vs Peggy **472 µs** / Chevrotain **1,946 µs**. Native `JSON.parse` is the one thing faster on JSON (**45 µs** large). Even the zero-setup interpreter is highly competitive: on JSON and CSV it's the fastest option after the macro build — ahead of Peggy and every other generator — with Peggy edging it out only on GraphQL. On the CST chart, macro build beats Lezer at every size (**344 µs** vs **587 µs** large, parse-only). Full write-up and how to refresh the charts: **[benchmarks guide](https://matthew-dean.github.io/parseman/guide/benchmarks)**. Grammar-level speed levers: [performance guide](https://matthew-dean.github.io/parseman/guide/performance); library-level codegen: [PERF_IDEAS.md](./PERF_IDEAS.md).
88
88
 
89
89
  ---
90
90
 
@@ -95,8 +95,8 @@ pnpm install
95
95
  pnpm test # Vitest — interpreter + compiler parity + ordered-choice semantics
96
96
  pnpm typecheck # TypeScript 7
97
97
  pnpm build # ESM + CJS + .d.ts → dist/
98
- pnpm bench # Parséman vs Peggy, Parsimmon, Chevrotain, Nearley, Jison
99
- pnpm bench:svg # refresh assets/bench-*.svg after bench
98
+ pnpm bench # full cross-parser suite + Parseman regression report
99
+ pnpm bench:svg # chart-only benchmarks + refresh assets/bench-*.svg
100
100
  pnpm bench:compile-grammars # regenerate Peggy / Nearley / Jison parser output in bench/
101
101
  pnpm docs:dev # run this documentation site locally
102
102
  ```
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Automatic list recovery — tolerant `sepBy` / `many` for editor-grade parsing.
3
+ *
4
+ * Plain `sepBy` / `many` stop at the first item they can't parse. Inside a
5
+ * delimited list (`[ … ]`, `{ … }`, an argument list) that means one malformed
6
+ * element truncates the whole list, which is exactly the wrong behavior for an
7
+ * IDE re-parsing broken input. These variants instead skip a bad element up to
8
+ * the next separator (or the list terminator), record a `ParseError` in its
9
+ * place, and keep going — so the rest of the list still parses.
10
+ *
11
+ * They are built entirely from existing combinators (`recover`, `sepBy`, `many`,
12
+ * `not`, `sequence`, `transform`), so the compiler and CST capture handle them
13
+ * with no special cases — the macro build behaves identically to the interpreter.
14
+ *
15
+ * The `until` terminator (the list's closing delimiter, matched but not consumed)
16
+ * is required: it's how an *empty* list is told apart from a *malformed* first
17
+ * element. `until` must not overlap with what a valid item can start with.
18
+ */
19
+ import type { Combinator } from '../types.ts';
20
+ import { type ParseError } from './recover.ts';
21
+ /**
22
+ * Like `sepBy`, but tolerant: a malformed element is skipped up to the next
23
+ * `separator` or `until` and recorded as a `ParseError` in the result array,
24
+ * instead of ending the list. An empty list (next token is `until`) yields `[]`
25
+ * with no error.
26
+ *
27
+ * const elements = sepByRecover(value, literal(','), literal(']'))
28
+ * const array = sequence(literal('['), elements, literal(']'))
29
+ * // "[1,,3]" → [1, ParseError, 3] ; error collected via { recover: true }
30
+ */
31
+ export declare function sepByRecover<T>(item: Combinator<T>, separator: Combinator<unknown>, until: Combinator<unknown>): Combinator<(T | ParseError)[]>;
32
+ /**
33
+ * Like `many`, but tolerant: junk that isn't a valid item (and isn't the `until`
34
+ * terminator) is skipped up to `until` and recorded as a `ParseError`, instead
35
+ * of stopping the repetition. With no separator to resync on, a bad run is
36
+ * captured as a single error up to the terminator.
37
+ *
38
+ * const items = manyRecover(statement, literal('}'))
39
+ */
40
+ export declare function manyRecover<T>(item: Combinator<T>, until: Combinator<unknown>): Combinator<(T | ParseError)[]>;
41
+ //# sourceMappingURL=recover-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recover-list.d.ts","sourceRoot":"","sources":["../../src/combinators/recover-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAK7C,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAsBvD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAC5B,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,UAAU,CAAC,OAAO,CAAC,EAC9B,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,GACzB,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,EAAE,CAAC,CAGhC;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EACnB,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,GACzB,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,EAAE,CAAC,CAEhC"}
@@ -1 +1 @@
1
- {"version":3,"file":"recover.d.ts","sourceRoot":"","sources":["../../src/combinators/recover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAyC,UAAU,EAAE,MAAM,aAAa,CAAA;AAEhG,YAAY,EAAE,UAAU,EAAE,CAAA;AAE1B;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,OAAO,CAAC,CAAC,EACvB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,GAC5B,UAAU,CAAC,CAAC,GAAG,UAAU,CAAC,CA6B5B;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE"}
1
+ {"version":3,"file":"recover.d.ts","sourceRoot":"","sources":["../../src/combinators/recover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAyC,UAAU,EAAE,MAAM,aAAa,CAAA;AAGhG,YAAY,EAAE,UAAU,EAAE,CAAA;AAE1B;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,OAAO,CAAC,CAAC,EACvB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,GAC5B,UAAU,CAAC,CAAC,GAAG,UAAU,CAAC,CAiC5B;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE"}
@@ -7,6 +7,7 @@
7
7
  * object allocation per node.
8
8
  */
9
9
  import type { Combinator, ParseResult, ParseContext, ParseError } from '../types.ts';
10
+ export declare function __setForceDisjointIf(on: boolean): void;
10
11
  export type CompiledParser<T> = {
11
12
  parse(input: string, pos?: number): ParseResult<T>;
12
13
  /** Like parse(), but with a caller-supplied ParseContext (e.g. `_triviaLog` for CST grammars). */
@@ -1 +1 @@
1
- {"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../../src/compiler/codegen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,UAAU,EAAuB,WAAW,EAAE,YAAY,EAAE,UAAU,EAAkB,MAAM,aAAa,CAAA;AA4jDzH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAClD,kGAAkG;IAClG,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAChF;;;;OAIG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG;QAAE,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE,CAAA;IACvF,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC,CAAA;AAoJD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,CAyGhG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GAC7D;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA6EhD"}
1
+ {"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../../src/compiler/codegen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,UAAU,EAAuB,WAAW,EAAE,YAAY,EAAE,UAAU,EAAkB,MAAM,aAAa,CAAA;AA0azH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAA0B;AA83CjF,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAClD,kGAAkG;IAClG,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAChF;;;;OAIG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG;QAAE,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE,CAAA;IACvF,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC,CAAA;AAoJD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,CA6GhG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GAC7D;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAgFhD"}
@@ -17,6 +17,11 @@
17
17
  export type ScanShape = {
18
18
  kind: 'chars';
19
19
  ranges: Array<[number, number]>;
20
+ minOne: boolean;
21
+ } | {
22
+ kind: 'ident';
23
+ head: Array<[number, number]>;
24
+ tail: Array<[number, number]>;
20
25
  } | {
21
26
  kind: 'until';
22
27
  open: number[];
@@ -25,22 +30,69 @@ export type ScanShape = {
25
30
  kind: 'delimited';
26
31
  open: number[];
27
32
  close: number[];
33
+ } | {
34
+ kind: 'string';
35
+ quote: number;
36
+ excluded: Array<[number, number]>;
37
+ escLineTerm: boolean;
28
38
  };
39
+ /** Backslash (`\`) code point — the escape lead char in string shapes. */
40
+ export declare const BACKSLASH = 92;
29
41
  /**
30
42
  * Recognize one scannable arm from its regex source, or null if it isn't one of
31
- * the three structural shapes. Order matters: char-class run, then
43
+ * the structural shapes. Order matters: char-class run, ident, string, then
32
44
  * open-until-terminator, then delimited.
33
45
  */
34
46
  export declare function parseScanShape(source: string): ScanShape | null;
35
47
  /**
36
- * One loop branch for a shape, dispatched on the current char `c` (= charCodeAt(_e)).
37
- * Each branch advances `_e` past the matched token and `continue`s the scan loop.
48
+ * Flag-aware wrapper around `parseScanShape`. Lowering to a raw code-point scan
49
+ * assumes default regex semantics, so any flag that changes matching (`i` case
50
+ * folding, `u` surrogate handling, `m`/`s` anchor/dot behavior) disables it.
51
+ * `g`/`y` are stickiness-only and safe.
52
+ */
53
+ export declare function scanShapeFromRegex(source: string, flags: string): ScanShape | null;
54
+ export declare const classCond: (cVar: string, ranges: Array<[number, number]>) => string;
55
+ /** Literal-match condition at `base + k` for each code point; uses `firstVar` at offset 0. */
56
+ export declare const litCond: (base: string, cps: number[], firstVar?: string) => string;
57
+ /** Line-terminator code points `.` does not match (`\n \r \u2028 \u2029`). */
58
+ export declare const LINE_TERMINATORS: readonly [10, 13, 8232, 8233];
59
+ /** The body-stop chars that abort a string match (excluded set minus quote/backslash). */
60
+ export declare function stringHardStop(shape: Extract<ScanShape, {
61
+ kind: 'string';
62
+ }>): Array<[number, number]>;
63
+ /** Mints a fresh, unique local variable name (`prefix` + counter). */
64
+ export type Mint = (prefix?: string) => string;
65
+ /**
66
+ * The SINGLE source of truth for how a scannable shape matches at `start`. Both
67
+ * the terminal emitter and the trivia scan loop consume this, so no context can
68
+ * silently reinterpret an incomplete match (e.g. an unterminated string):
69
+ *
70
+ * - `setup` statements (indented by `ind`) that compute the match.
71
+ * - `ok` a boolean expr: did a token match at `start`? (zero-width for
72
+ * `chars*` counts as a match — terminals allow it.)
73
+ * - `end` the position AFTER the token. **Invariant:** `end === start`
74
+ * whenever there is no progress (match failed or matched empty),
75
+ * so the trivia loop can gate purely on `end > start`.
76
+ *
77
+ * `firstChar`, when supplied, is an expression already equal to
78
+ * `charCodeAt(start)` (the trivia loop reads it once and shares it).
79
+ */
80
+ export type ShapeMatch = {
81
+ setup: string[];
82
+ ok: string;
83
+ end: string;
84
+ };
85
+ export declare function emitShapeMatch(shape: ScanShape, start: string, mint: Mint, ind: string, firstChar?: string): ShapeMatch;
86
+ /**
87
+ * One trivia-loop branch for a shape, dispatched on the current char `c`
88
+ * (= charCodeAt(_e)). Advances `_e` and `continue`s ONLY on real progress
89
+ * (`end > _e`) — an unterminated delimited/string token leaves `end === _e`, so
90
+ * the loop stops there exactly as the interpreter's `oneOrMore(choice(…))` would.
38
91
  */
39
- export declare function scanBranch(shape: ScanShape): string;
92
+ export declare function scanBranch(shape: ScanShape, mint: Mint): string;
40
93
  /**
41
- * A labeled branch: consume the shape's full token and log one [start, end,
42
- * kindIndex] trivia chunk. Unlike scanBranch this always consumes the whole run
43
- * (a chunk must span the entire token) and captures inline per-chunk.
94
+ * A labeled branch: match one token and, on progress, log its [start, end,
95
+ * kindIndex] trivia chunk. Same completion semantics as scanBranch.
44
96
  */
45
- export declare function scanBranchLabeled(shape: ScanShape, kindIndex: number): string;
97
+ export declare function scanBranchLabeled(shape: ScanShape, kindIndex: number, mint: Mint): string;
46
98
  //# sourceMappingURL=scannable-run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scannable-run.d.ts","sourceRoot":"","sources":["../../src/compiler/scannable-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,GAChE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAA;AAwD1D;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CA4B/D;AAuDD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAQnD;AAYD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAM7E"}
1
+ {"version":3,"file":"scannable-run.d.ts","sourceRoot":"","sources":["../../src/compiler/scannable-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,GAC/E;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,GAChE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAKtD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAA;AAE9F,0EAA0E;AAC1E,eAAO,MAAM,SAAS,KAAK,CAAA;AA2H3B;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CA4C/D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAGlF;AAED,eAAO,MAAM,SAAS,SAAU,MAAM,UAAU,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KAAG,MAGzD,CAAA;AAEjB,8FAA8F;AAC9F,eAAO,MAAM,OAAO,SAAU,MAAM,OAAO,MAAM,EAAE,aAAa,MAAM,KAAG,MAOxD,CAAA;AAEjB,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,YAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAU,CAAA;AAE7D,0FAA0F;AAC1F,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC,GAC5C,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAIzB;AAED,sEAAsE;AACtE,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;AAE9C;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAKrE,wBAAgB,cAAc,CAC5B,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,GACjB,UAAU,CAgHZ;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,CAG/D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,CAazF"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Lower scannable regex shapes to tight `charCodeAt` scan loops for terminal
3
+ * `regex()` combinators (PERF_IDEAS §8). Trivia uses the SAME match core via
4
+ * `scannable-run.ts` (`emitShapeMatch`); this module is just the terminal
5
+ * wrapper: run the shared match, fail when it doesn't match, then slice the
6
+ * value. Keeping one match core means terminal and trivia can never disagree on
7
+ * what an incomplete token (unclosed string/comment) means.
8
+ */
9
+ import { type ScanShape } from './scannable-run.ts';
10
+ export type ScanEmitCtx = {
11
+ ind: string;
12
+ pos: string;
13
+ valueVar: string;
14
+ /** Emit `if (cond) { failLine }` lines (no trailing blank). */
15
+ failIf: (cond: string) => string[];
16
+ fresh: (prefix?: string) => string;
17
+ };
18
+ /** Emit stmts for one terminal regex match, or null when the shape is unsupported. */
19
+ export declare function emitScannableTerminal(shape: ScanShape, c: ScanEmitCtx): {
20
+ stmts: string[];
21
+ endVar: string;
22
+ } | null;
23
+ //# sourceMappingURL=scannable-terminal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scannable-terminal.d.ts","sourceRoot":"","sources":["../../src/compiler/scannable-terminal.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,SAAS,EAAkB,MAAM,oBAAoB,CAAA;AAEnE,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,+DAA+D;IAC/D,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,CAAA;IAClC,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CACnC,CAAA;AAED,sFAAsF;AACtF,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,EAChB,CAAC,EAAE,WAAW,GACb;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAO5C"}
@@ -1 +1 @@
1
- {"version":3,"file":"trivia-fast-path.d.ts","sourceRoot":"","sources":["../../src/compiler/trivia-fast-path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE/D,OAAO,EAAE,KAAK,SAAS,EAAiD,MAAM,oBAAoB,CAAA;AA0BlG;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,SAAS,EAAE,GAAG,IAAI,CAyBrF;AAoBD,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CASjF;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAC1B,KAAK,CAAC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,CAWvD;AAED,0FAA0F;AAC1F,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,aAAa,CAAC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GAC3D,MAAM,CAYR;AAED,kFAAkF;AAClF,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,MAAM,EAAE,GAChB,MAAM,CA+BR;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,iBAAiB,GAAG,IAAI,CAO5F;AAED,4EAA4E;AAC5E,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,iBAAiB,EACvB,YAAY,EAAE,MAAM,GACnB,MAAM,CA8BR"}
1
+ {"version":3,"file":"trivia-fast-path.d.ts","sourceRoot":"","sources":["../../src/compiler/trivia-fast-path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE/D,OAAO,EAAE,KAAK,SAAS,EAAgE,MAAM,oBAAoB,CAAA;AAgCjH;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,SAAS,EAAE,GAAG,IAAI,CAyBrF;AAqBD,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CASjF;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAC1B,KAAK,CAAC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,CAWvD;AAED,0FAA0F;AAC1F,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,aAAa,CAAC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GAC3D,MAAM,CAaR;AAED,kFAAkF;AAClF,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,MAAM,EAAE,GAChB,MAAM,CA+BR;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,iBAAiB,GAAG,IAAI,CAO5F;AAED,4EAA4E;AAC5E,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,iBAAiB,EACvB,YAAY,EAAE,MAAM,GACnB,MAAM,CA8BR"}
@@ -1 +1 @@
1
- {"version":3,"file":"trivia-kinds.d.ts","sourceRoot":"","sources":["../../src/cst/trivia-kinds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAI3D,MAAM,MAAM,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAA;AAE3E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC,CAAA;IAC/F,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B,CAAA;AAQD,kEAAkE;AAClE,wBAAgB,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;CAAE,GAAG,IAAI,CAGvG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,iBAAiB,GAAG,IAAI,CA2B1F;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,SAAS,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAKvG;AAYD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,iBAAiB,GACtB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,EAAE,CAAA;CAAE,CAsBxC;AAED,qFAAqF;AACrF,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,EAAE,CAAA;CAAE,CA6BxC;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAC1B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,EAAE,CAAA;CAAE,GAAG,IAAI,CAiB/C;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,CAQ1F"}
1
+ {"version":3,"file":"trivia-kinds.d.ts","sourceRoot":"","sources":["../../src/cst/trivia-kinds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAI3D,MAAM,MAAM,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAA;AAE3E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC,CAAA;IAC/F,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B,CAAA;AAQD,kEAAkE;AAClE,wBAAgB,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;CAAE,GAAG,IAAI,CAGvG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,iBAAiB,GAAG,IAAI,CA2B1F;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,SAAS,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAKvG;AAYD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,iBAAiB,GACtB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,EAAE,CAAA;CAAE,CAsBxC;AAED,qFAAqF;AACrF,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,EAAE,CAAA;CAAE,CAmCxC;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAC1B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,EAAE,CAAA;CAAE,GAAG,IAAI,CAiB/C;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,CAQ1F"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Tree traversal for the CST/AST a grammar produces. Parséman hands back a plain
3
+ * object tree, so you *can* recurse it yourself — these helpers save you from
4
+ * re-writing the same recursion for diagnostics, folding, or lowering a tree.
5
+ *
6
+ * Two entry points:
7
+ * - `walk` — imperative depth-first traversal with enter/leave hooks.
8
+ * - `createVisitor` — Chevrotain-style dispatch keyed on each node's `type`.
9
+ *
10
+ * Both default to the built-in CST shape (`CSTChild` — the `node()` / leaf / error
11
+ * union), so with no annotation you get precise typing out of the box. Parsing to
12
+ * a custom AST? Pass your node type as a generic: `walk<MyNode>(root, …)`.
13
+ */
14
+ import type { CSTChild } from './types.ts';
15
+ /**
16
+ * The minimal shape these helpers traverse: a `_tag`, an optional rule `type`,
17
+ * and optional structural `children`. The built-in `CSTChild` satisfies it, and
18
+ * so does any custom AST node (that's what the generic override targets).
19
+ */
20
+ export type Walkable = {
21
+ readonly _tag: string;
22
+ readonly type?: string;
23
+ readonly children?: ReadonlyArray<Walkable>;
24
+ };
25
+ export interface WalkVisitor<N extends Walkable = CSTChild, C = undefined> {
26
+ /**
27
+ * Called on entering a node, before its children. Return `false` to skip
28
+ * descending into this node's children (`leave` still runs).
29
+ */
30
+ enter?(node: N, parent: N | null, ctx: C): boolean | void;
31
+ /** Called on leaving a node, after its children. */
32
+ leave?(node: N, parent: N | null, ctx: C): void;
33
+ }
34
+ /**
35
+ * Depth-first traversal. Visits `root`, then each structural child in order.
36
+ * `ctx` is threaded to both hooks unchanged (use it as an accumulator).
37
+ *
38
+ * Defaults to the CST shape; override with `walk<MyNode>(root, …)`:
39
+ *
40
+ * const idents: string[] = []
41
+ * walk(tree, {
42
+ * enter(node) {
43
+ * if (node._tag === 'leaf') idents.push(node.value)
44
+ * },
45
+ * })
46
+ */
47
+ export declare function walk<N extends Walkable = CSTChild, C = undefined>(root: N, visitor: WalkVisitor<N, C>, ctx?: C): void;
48
+ export interface VisitApi<R, N extends Walkable = CSTChild> {
49
+ /** Dispatch a node to its handler (by `type`); returns the handler's result. */
50
+ visit(node: N): R | undefined;
51
+ /** Visit every structural child, collecting the defined results in order. */
52
+ visitChildren(node: N): R[];
53
+ }
54
+ export type VisitorHandlers<R, N extends Walkable = CSTChild> = Record<string, (node: N, api: VisitApi<R, N>) => R>;
55
+ /**
56
+ * Build a visitor that dispatches on each node's `type` — the direct analog of a
57
+ * generated CST-visitor base class. Handlers are keyed by rule name and receive
58
+ * an `api` with `visit` / `visitChildren` to recurse:
59
+ *
60
+ * const evalExpr = createVisitor<number>({
61
+ * Num: (n) => Number((n as NumNode).value),
62
+ * Add: (n, api) => api.visitChildren(n).reduce((a, b) => a + b, 0),
63
+ * })
64
+ * const total = evalExpr(tree)
65
+ *
66
+ * Defaults to the CST shape; override the return type and node type together with
67
+ * `createVisitor<number, MyNode>({ … })`. A node whose `type` has no handler
68
+ * falls through to visiting its children (results discarded), so partial visitors
69
+ * work without listing every rule.
70
+ */
71
+ export declare function createVisitor<R = void, N extends Walkable = CSTChild>(handlers: VisitorHandlers<R, N>): (node: N) => R | undefined;
72
+ //# sourceMappingURL=walk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../../src/cst/walk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAA;CAC5C,CAAA;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,EAAE,CAAC,GAAG,SAAS;IACvE;;;OAGG;IACH,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,GAAG,IAAI,CAAA;IACzD,oDAAoD;IACpD,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI,CAAA;CAChD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,IAAI,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,EAAE,CAAC,GAAG,SAAS,EAC/D,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,GAAG,GAAE,CAAkB,GACtB,IAAI,CAUN;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC,EAAE,CAAC,SAAS,QAAQ,GAAG,QAAQ;IACxD,gFAAgF;IAChF,KAAK,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAAA;IAC7B,6EAA6E;IAC7E,aAAa,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAA;CAC5B;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,EAAE,CAAC,SAAS,QAAQ,GAAG,QAAQ,IAAI,MAAM,CACpE,MAAM,EACN,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CACpC,CAAA;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,SAAS,QAAQ,GAAG,QAAQ,EACnE,QAAQ,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAC9B,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,SAAS,CAqB5B"}