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.
- package/README.md +8 -3
- package/dist/analysis/choice-cost-gate.d.ts +158 -0
- package/dist/analysis/choice-cost-gate.d.ts.map +1 -0
- package/dist/analysis/choice-cost-render.d.ts +96 -0
- package/dist/analysis/choice-cost-render.d.ts.map +1 -0
- package/dist/analysis/choice-cost.d.ts +383 -0
- package/dist/analysis/choice-cost.d.ts.map +1 -0
- package/dist/analysis/commitment.d.ts +131 -0
- package/dist/analysis/commitment.d.ts.map +1 -0
- package/dist/analysis/corpus.d.ts +73 -0
- package/dist/analysis/corpus.d.ts.map +1 -0
- package/dist/analysis/diagnose-render.d.ts +65 -0
- package/dist/analysis/diagnose-render.d.ts.map +1 -0
- package/dist/analysis/diagnose.d.ts +15 -0
- package/dist/analysis/diagnose.d.ts.map +1 -1
- package/dist/analysis/diagnostics.cjs +13736 -0
- package/dist/analysis/diagnostics.cjs.map +6 -0
- package/dist/analysis/diagnostics.d.ts +27 -0
- package/dist/analysis/diagnostics.d.ts.map +1 -0
- package/dist/analysis/diagnostics.js +13713 -0
- package/dist/analysis/diagnostics.js.map +6 -0
- package/dist/analysis/duplication.d.ts.map +1 -1
- package/dist/analysis/fix-render.d.ts +43 -0
- package/dist/analysis/fix-render.d.ts.map +1 -0
- package/dist/analysis/fix.d.ts +172 -0
- package/dist/analysis/fix.d.ts.map +1 -0
- package/dist/analysis/format-number.d.ts +23 -0
- package/dist/analysis/format-number.d.ts.map +1 -0
- package/dist/analysis/gating.d.ts +47 -2
- package/dist/analysis/gating.d.ts.map +1 -1
- package/dist/analysis/rebuild.d.ts +61 -0
- package/dist/analysis/rebuild.d.ts.map +1 -0
- package/dist/analysis/terminal.d.ts +145 -0
- package/dist/analysis/terminal.d.ts.map +1 -0
- package/dist/cli/index.cjs +14837 -0
- package/dist/cli/index.cjs.map +6 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +14814 -0
- package/dist/cli/index.js.map +6 -0
- package/dist/combinators/adjacency.d.ts +100 -0
- package/dist/combinators/adjacency.d.ts.map +1 -0
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/completions.d.ts.map +1 -1
- package/dist/combinators/dispatch.d.ts +8 -1
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/gate.d.ts.map +1 -1
- package/dist/combinators/grammar.d.ts +22 -0
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts +8 -0
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/map.d.ts +0 -1
- package/dist/combinators/map.d.ts.map +1 -1
- package/dist/combinators/node.d.ts +13 -3
- package/dist/combinators/node.d.ts.map +1 -1
- package/dist/combinators/parser.d.ts +1 -1
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/ref.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts +1 -16
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts +30 -2
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/scanTo.d.ts +71 -1
- package/dist/combinators/scanTo.d.ts.map +1 -1
- package/dist/combinators/sequence.d.ts.map +1 -1
- package/dist/combinators/token.d.ts.map +1 -1
- package/dist/combinators/trivia-boundary.d.ts +26 -0
- package/dist/combinators/trivia-boundary.d.ts.map +1 -0
- package/dist/combinators/trivia-skip.d.ts +40 -1
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/combinators/withCtx.d.ts.map +1 -1
- package/dist/compiler/build-arity.d.ts.map +1 -1
- package/dist/compiler/compile-linkable-table.d.ts +104 -0
- package/dist/compiler/compile-linkable-table.d.ts.map +1 -0
- package/dist/compiler/degradation.d.ts +0 -2
- package/dist/compiler/degradation.d.ts.map +1 -1
- package/dist/compiler/fields.d.ts +13 -0
- package/dist/compiler/fields.d.ts.map +1 -1
- package/dist/compiler/grammar-coverage-ids.d.ts +5 -5
- package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
- package/dist/compiler/ir-serialize.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts +79 -47
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/token-alphabet.d.ts +81 -0
- package/dist/compiler/token-alphabet.d.ts.map +1 -0
- package/dist/compiler/token-dispatch.d.ts +95 -0
- package/dist/compiler/token-dispatch.d.ts.map +1 -0
- package/dist/compiler/token-scanner.d.ts +53 -0
- package/dist/compiler/token-scanner.d.ts.map +1 -0
- package/dist/compiler/value-usage.d.ts.map +1 -1
- package/dist/coverage.d.ts +1 -1
- package/dist/coverage.d.ts.map +1 -1
- package/dist/cst/capture-buffer.d.ts +35 -0
- package/dist/cst/capture-buffer.d.ts.map +1 -1
- package/dist/cst/reflection-attach.d.ts +8 -0
- package/dist/cst/reflection-attach.d.ts.map +1 -0
- package/dist/cst/reflection-symbols.d.ts +10 -0
- package/dist/cst/reflection-symbols.d.ts.map +1 -0
- package/dist/cst/reflection.d.ts +2 -4
- package/dist/cst/reflection.d.ts.map +1 -1
- package/dist/cst/root-trivia-scope.d.ts +26 -0
- package/dist/cst/root-trivia-scope.d.ts.map +1 -0
- package/dist/cst/trivia-charscan.d.ts +18 -0
- package/dist/cst/trivia-charscan.d.ts.map +1 -0
- package/dist/cst/trivia-kinds.d.ts +19 -0
- package/dist/cst/trivia-kinds.d.ts.map +1 -1
- package/dist/functional/doc.d.ts.map +1 -1
- package/dist/functional/run-tabled.d.ts +12 -0
- package/dist/functional/run-tabled.d.ts.map +1 -0
- package/dist/functional/run.d.ts +25 -42
- package/dist/functional/run.d.ts.map +1 -1
- package/dist/grammar-metadata.d.ts +3 -0
- package/dist/grammar-metadata.d.ts.map +1 -0
- package/dist/index.cjs +10499 -7478
- package/dist/index.cjs.map +3 -4
- package/dist/index.d.ts +24 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10499 -7478
- package/dist/index.js.map +3 -4
- package/dist/language-service/index.cjs +311 -121
- package/dist/language-service/index.cjs.map +3 -4
- package/dist/language-service/index.js +311 -121
- package/dist/language-service/index.js.map +3 -4
- package/dist/oracle/index.cjs.map +0 -1
- package/dist/oracle/index.js.map +0 -1
- package/dist/parse-context.d.ts +49 -0
- package/dist/parse-context.d.ts.map +1 -0
- package/dist/plugin/evaluator.d.ts +41 -2
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/free-identifiers.d.ts +23 -0
- package/dist/plugin/free-identifiers.d.ts.map +1 -0
- package/dist/plugin/index.cjs +11381 -9442
- package/dist/plugin/index.cjs.map +3 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +11381 -9442
- package/dist/plugin/index.js.map +3 -4
- package/dist/plugin/reducer-resolver.d.ts +17 -1
- package/dist/plugin/reducer-resolver.d.ts.map +1 -1
- package/dist/regex/classes.d.ts +28 -0
- package/dist/regex/classes.d.ts.map +1 -1
- package/dist/regex/first-set.d.ts +14 -0
- package/dist/regex/first-set.d.ts.map +1 -1
- package/dist/run/index.cjs +136 -51
- package/dist/run/index.cjs.map +2 -3
- package/dist/run/index.js +136 -51
- package/dist/run/index.js.map +2 -3
- package/dist/spec/index.cjs +2898 -1622
- package/dist/spec/index.cjs.map +3 -4
- package/dist/spec/index.js +2898 -1622
- package/dist/spec/index.js.map +3 -4
- package/dist/spec/model.d.ts +8 -0
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/table/assemble.d.ts +307 -0
- package/dist/table/assemble.d.ts.map +1 -0
- package/dist/table/cell.d.ts +42 -0
- package/dist/table/cell.d.ts.map +1 -0
- package/dist/table/child-slots.d.ts +30 -0
- package/dist/table/child-slots.d.ts.map +1 -0
- package/dist/table/compile-rule-map.d.ts +136 -0
- package/dist/table/compile-rule-map.d.ts.map +1 -0
- package/dist/table/compile.d.ts +56 -0
- package/dist/table/compile.d.ts.map +1 -0
- package/dist/table/duplication-hook.d.ts +32 -0
- package/dist/table/duplication-hook.d.ts.map +1 -0
- package/dist/table/emit-assembly.d.ts +139 -0
- package/dist/table/emit-assembly.d.ts.map +1 -0
- package/dist/table/emit.d.ts +113 -0
- package/dist/table/emit.d.ts.map +1 -0
- package/dist/table/encode-baseline.d.ts +16 -0
- package/dist/table/encode-baseline.d.ts.map +1 -0
- package/dist/table/encode.d.ts +71 -0
- package/dist/table/encode.d.ts.map +1 -0
- package/dist/table/exec-baseline.d.ts +9 -0
- package/dist/table/exec-baseline.d.ts.map +1 -0
- package/dist/table/exec.d.ts +72 -0
- package/dist/table/exec.d.ts.map +1 -0
- package/dist/table/fold.d.ts +25 -0
- package/dist/table/fold.d.ts.map +1 -0
- package/dist/table/index.cjs +11375 -0
- package/dist/table/index.cjs.map +6 -0
- package/dist/table/index.d.ts +100 -0
- package/dist/table/index.d.ts.map +1 -0
- package/dist/table/index.js +11352 -0
- package/dist/table/index.js.map +6 -0
- package/dist/table/inspect.d.ts +17 -0
- package/dist/table/inspect.d.ts.map +1 -0
- package/dist/table/ops.d.ts +446 -0
- package/dist/table/ops.d.ts.map +1 -0
- package/dist/table/program.d.ts +493 -0
- package/dist/table/program.d.ts.map +1 -0
- package/dist/table/run-support.d.ts +38 -0
- package/dist/table/run-support.d.ts.map +1 -0
- package/dist/{compiler/scannable-run.d.ts → table/scan-shapes.d.ts} +14 -13
- package/dist/table/scan-shapes.d.ts.map +1 -0
- package/dist/table/site-labels.d.ts +58 -0
- package/dist/table/site-labels.d.ts.map +1 -0
- package/dist/table/stamp.d.ts +70 -0
- package/dist/table/stamp.d.ts.map +1 -0
- package/dist/types.d.ts +80 -24
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +41 -3
- package/src/analysis/choice-cost-gate.ts +359 -0
- package/src/analysis/choice-cost-render.ts +293 -0
- package/src/analysis/choice-cost.ts +1008 -0
- package/src/analysis/commitment.ts +557 -0
- package/src/analysis/corpus.ts +130 -0
- package/src/analysis/diagnose-render.ts +625 -0
- package/src/analysis/diagnose.ts +317 -0
- package/src/analysis/diagnostics.ts +33 -0
- package/src/analysis/duplication.ts +1955 -0
- package/src/analysis/fix-render.ts +217 -0
- package/src/analysis/fix.ts +669 -0
- package/src/analysis/format-number.ts +31 -0
- package/src/analysis/gating.ts +792 -0
- package/src/analysis/grammar.ts +72 -0
- package/src/analysis/rebuild.ts +287 -0
- package/src/analysis/terminal.ts +336 -0
- package/src/cli/index.ts +413 -0
- package/src/combinators/adjacency.ts +171 -0
- package/src/combinators/attempt.ts +41 -0
- package/src/combinators/case-fold.ts +90 -0
- package/src/combinators/choice.ts +466 -0
- package/src/combinators/completions.ts +64 -0
- package/src/combinators/dispatch.ts +358 -0
- package/src/combinators/expect.ts +159 -0
- package/src/combinators/first-set.ts +322 -0
- package/src/combinators/gate.ts +50 -0
- package/src/combinators/grammar.ts +272 -0
- package/src/combinators/keywords.ts +145 -0
- package/src/combinators/literal.ts +102 -0
- package/src/combinators/map.ts +106 -0
- package/src/combinators/node.ts +345 -0
- package/src/combinators/not.ts +55 -0
- package/src/combinators/parser.ts +266 -0
- package/src/combinators/peek.ts +63 -0
- package/src/combinators/probe.ts +25 -0
- package/src/combinators/ref.ts +109 -0
- package/src/combinators/regex.ts +170 -0
- package/src/combinators/repeat.ts +561 -0
- package/src/combinators/scanTo.ts +393 -0
- package/src/combinators/sequence.ts +221 -0
- package/src/combinators/token.ts +120 -0
- package/src/combinators/trivia-boundary.ts +48 -0
- package/src/combinators/trivia-skip.ts +821 -0
- package/src/combinators/withCtx.ts +45 -0
- package/src/compiler/build-arity.ts +330 -0
- package/src/compiler/compile-linkable-table.ts +209 -0
- package/src/compiler/degradation.ts +266 -0
- package/src/compiler/fields.ts +191 -0
- package/src/compiler/grammar-coverage-ids.ts +129 -0
- package/src/compiler/ir-serialize.ts +615 -0
- package/src/compiler/linker.ts +954 -0
- package/src/compiler/token-alphabet.ts +209 -0
- package/src/compiler/token-dispatch.ts +353 -0
- package/src/compiler/token-scanner.ts +318 -0
- package/src/compiler/value-usage.ts +147 -0
- package/src/coverage.ts +383 -0
- package/src/cst/capture-buffer.ts +297 -0
- package/src/cst/host-mode.ts +69 -0
- package/src/cst/reflection-attach.ts +11 -0
- package/src/cst/reflection-symbols.ts +9 -0
- package/src/cst/reflection.ts +149 -0
- package/src/cst/relative-spans.ts +207 -0
- package/src/cst/root-trivia-scope.ts +30 -0
- package/src/cst/trivia-charscan.ts +312 -0
- package/src/cst/trivia-entries.ts +285 -0
- package/src/cst/trivia-index.ts +131 -0
- package/src/cst/trivia-kinds.ts +358 -0
- package/src/cst/types.ts +76 -0
- package/src/cst/walk.ts +101 -0
- package/src/functional/doc.ts +959 -0
- package/src/functional/run-tabled.ts +153 -0
- package/src/functional/run.ts +558 -0
- package/src/grammar-metadata.ts +2 -0
- package/src/index.ts +189 -0
- package/src/language-service/index.ts +221 -0
- package/src/line-index.ts +131 -0
- package/src/oracle/digest.ts +620 -0
- package/src/oracle/index.ts +55 -0
- package/src/parse-context.ts +91 -0
- package/src/plugin/direct-builder-static.ts +102 -0
- package/src/plugin/evaluator.ts +1505 -0
- package/src/plugin/free-identifiers.ts +338 -0
- package/src/plugin/index.ts +2281 -0
- package/src/plugin/reducer-resolver.ts +606 -0
- package/src/recovery/scan.ts +155 -0
- package/src/regex/classes.ts +183 -0
- package/src/regex/first-set.ts +316 -0
- package/src/run/index.ts +31 -0
- package/src/spec/ebnf.ts +128 -0
- package/src/spec/index.ts +54 -0
- package/src/spec/model.ts +636 -0
- package/src/spec/railroad-lib.ts +559 -0
- package/src/spec/railroad.ts +256 -0
- package/src/table/assemble.ts +2994 -0
- package/src/table/cell.ts +42 -0
- package/src/table/child-slots.ts +113 -0
- package/src/table/compile-rule-map.ts +336 -0
- package/src/table/compile.ts +244 -0
- package/src/table/duplication-hook.ts +89 -0
- package/src/table/emit-assembly.ts +1766 -0
- package/src/table/emit.ts +418 -0
- package/src/table/encode-baseline.ts +340 -0
- package/src/table/encode.ts +1569 -0
- package/src/table/exec-baseline.ts +537 -0
- package/src/table/exec.ts +1672 -0
- package/src/table/fold.ts +38 -0
- package/src/table/index.ts +108 -0
- package/src/table/inspect.ts +52 -0
- package/src/table/ops.ts +459 -0
- package/src/table/program.ts +855 -0
- package/src/table/run-support.ts +66 -0
- package/src/table/scan-shapes.ts +1578 -0
- package/src/table/site-labels.ts +228 -0
- package/src/table/stamp.ts +229 -0
- package/src/types.ts +563 -0
- package/src/version.ts +16 -0
- package/dist/compiler/codegen.d.ts +0 -334
- package/dist/compiler/codegen.d.ts.map +0 -1
- package/dist/compiler/inline-build.d.ts +0 -11
- package/dist/compiler/inline-build.d.ts.map +0 -1
- package/dist/compiler/inline-callback.d.ts +0 -21
- package/dist/compiler/inline-callback.d.ts.map +0 -1
- package/dist/compiler/line-index.d.ts +0 -2
- package/dist/compiler/line-index.d.ts.map +0 -1
- package/dist/compiler/scannable-run.d.ts.map +0 -1
- package/dist/compiler/scannable-terminal.d.ts +0 -23
- package/dist/compiler/scannable-terminal.d.ts.map +0 -1
- package/dist/compiler/trivia-fast-path.d.ts +0 -38
- package/dist/compiler/trivia-fast-path.d.ts.map +0 -1
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
import type { Combinator, ParseContext, ParseError, ParseResult, Span } from '../types.ts'
|
|
2
|
+
import { REC } from '../recovery/scan.ts'
|
|
3
|
+
import { createParseContext } from '../parse-context.ts'
|
|
4
|
+
import { buildRootTriviaIndex, type RootTriviaIndex } from '../cst/trivia-entries.ts'
|
|
5
|
+
/* `cst/host-mode.ts` and `cst/trivia-entries.ts` are import-free by design, so
|
|
6
|
+
* this keeps the `parseman/run` closure minimal — see
|
|
7
|
+
* test/unit/run-entry-closure.test.ts. */
|
|
8
|
+
import { assertHostModeCompatible, FUSED_HOST_MODE, FUSED_HOST_ELIDED, type HostMode } from '../cst/host-mode.ts'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Run a compiled/interpreted grammar entry against an input and collect the raw
|
|
12
|
+
* outcome a tool needs to shape a parse result — WITHOUT the consumer having to
|
|
13
|
+
* hand-build a `ctx`, branch on function-vs-combinator, or scan for leftover
|
|
14
|
+
* input itself.
|
|
15
|
+
*
|
|
16
|
+
* A `compile()`/macro grammar hands you a map of bare parse FUNCTIONS; the
|
|
17
|
+
* interpreter hands you COMBINATORS. `run()` invokes either, threads the standard
|
|
18
|
+
* framework ctx (trivia log, recover/expect error sink, the `ctx.build` host,
|
|
19
|
+
* grammar state), and — given the grammar's trivia parser — reports where
|
|
20
|
+
* non-trivia input was left unconsumed. The consumer keeps only its own policy:
|
|
21
|
+
* how to shape the tree and how to turn diagnostics into its error type.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** A compiled rule (macro/`compile()` output) OR an interpreter combinator. */
|
|
25
|
+
export type Runnable =
|
|
26
|
+
| ((input: string, pos: number, ctx: ParseContext) => ParseResult<unknown>)
|
|
27
|
+
| Combinator<unknown>
|
|
28
|
+
|
|
29
|
+
export type RunOptions = {
|
|
30
|
+
/** Optional hooks for a coverage-enabled compiled or macro parser. Ordinary
|
|
31
|
+
* parses omit this completely, so instrumentation has no normal-path cost.
|
|
32
|
+
* Table-backed 0.47 artifacts support `_grammarCoverage` but reject
|
|
33
|
+
* `_grammarTrace` explicitly; interpreted/source-lowered entries may support
|
|
34
|
+
* both. */
|
|
35
|
+
instrumentation?: {
|
|
36
|
+
_grammarCoverage?: (id: string) => void
|
|
37
|
+
_grammarTrace?: { write(event: { id: string; phase: 'enter' | 'attempt' | 'selected' | 'success' | 'failure' | 'backtrack' | 'rollback'; offset: number; end?: number }): void }
|
|
38
|
+
}
|
|
39
|
+
/** `ctx.build` host — makes structural `node()` rules build a CST / AST via the
|
|
40
|
+
* host instead of their own eval builders. Omit for a grammar's own builders. */
|
|
41
|
+
build?: ParseContext['build']
|
|
42
|
+
/** Initial grammar state threaded into `ctx.state`. */
|
|
43
|
+
state?: unknown
|
|
44
|
+
/**
|
|
45
|
+
* The grammar's trivia rule — an OVERRIDE, no longer a requirement.
|
|
46
|
+
*
|
|
47
|
+
* A root rule consumes trivia BETWEEN terms but not after the last one, so
|
|
48
|
+
* trailing whitespace/comments would otherwise look like unparsed input. `run`
|
|
49
|
+
* skips that tail before computing `unconsumedFrom` and before reporting
|
|
50
|
+
* `span`, so only real leftover is reported.
|
|
51
|
+
*
|
|
52
|
+
* The trivia used is the ENTRY's own ambient trivia — whatever
|
|
53
|
+
* `rules({ trivia })` / `parser({ trivia })` declared — resolved automatically
|
|
54
|
+
* (see `ambientTriviaFromRunnable`). Pass this only to use a DIFFERENT trivia
|
|
55
|
+
* for the document tail than the grammar parses with; it wins when given.
|
|
56
|
+
* Passing the grammar's own trivia is now a no-op, and remains supported.
|
|
57
|
+
*
|
|
58
|
+
* Either way it encodes dialect differences for free: CSS trivia leaves a
|
|
59
|
+
* trailing `//` as leftover, Less trivia (which treats `//` as a line comment)
|
|
60
|
+
* does not. An UNTERMINATED comment (which the trivia rule won't match)
|
|
61
|
+
* surfaces at its start.
|
|
62
|
+
*/
|
|
63
|
+
trivia?: Runnable
|
|
64
|
+
/**
|
|
65
|
+
* Restrict PER-NODE CST trivia capture (the `triviaLog` a node's builder sees)
|
|
66
|
+
* to these trivia kinds — a bitmask over the grammar's `triviaKindLabels`
|
|
67
|
+
* indices (build it with `triviaKindMask(labels, ['comment', …])`). Unlisted
|
|
68
|
+
* kinds (e.g. whitespace) are skipped over but not recorded per node, so a host
|
|
69
|
+
* that only reads comments doesn't pay to log every whitespace run. Omit to
|
|
70
|
+
* capture every kind.
|
|
71
|
+
*/
|
|
72
|
+
triviaCaptureMask?: number
|
|
73
|
+
/**
|
|
74
|
+
* Opt into sparse root trivia capture. `run()` retains no root trivia unless
|
|
75
|
+
* this is supplied. Capture records only the named labels, each with the one
|
|
76
|
+
* complete authored gap that owns it; ordinary whitespace has no root entry.
|
|
77
|
+
*/
|
|
78
|
+
rootTrivia?: {
|
|
79
|
+
readonly select: readonly string[]
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Activate automatic list recovery. When true, `many`/`sepBy`/`oneOrMore` recover
|
|
83
|
+
* from a failed element — skip to a sync point (a resume token inferred from the
|
|
84
|
+
* grammar's structure; the grammar carries no recovery config), emit a
|
|
85
|
+
* `ParseError` over the skipped span (collected in `errors`), and keep parsing the
|
|
86
|
+
* rest of the list — instead of stopping at the first bad element. Omit (the
|
|
87
|
+
* default) for the strict "one clean error and stop" behavior, byte-identical to a
|
|
88
|
+
* run with no recovery. Recovery is a cold path: on well-formed input nothing
|
|
89
|
+
* fails, so none of the machinery runs.
|
|
90
|
+
*/
|
|
91
|
+
tolerant?: boolean
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type RootTriviaCapture = {
|
|
95
|
+
/** Packed `[gapStart, gapEnd, markerStart, markerEnd, selectedKindIndex]` rows. */
|
|
96
|
+
readonly rows: readonly number[]
|
|
97
|
+
/** Labels requested by this caller; row kind indices refer to this array. */
|
|
98
|
+
readonly select: readonly string[]
|
|
99
|
+
/** Lazy lookup over `rows`; no tokens or strings are materialized. */
|
|
100
|
+
readonly index: RootTriviaIndex
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type RunResult = {
|
|
104
|
+
ok: boolean
|
|
105
|
+
/** The entry's value on success; undefined on failure. */
|
|
106
|
+
value: unknown
|
|
107
|
+
span: { start: number; end: number }
|
|
108
|
+
/** Expected-token set when the TOP-LEVEL parse failed (empty on success). */
|
|
109
|
+
expected: string[]
|
|
110
|
+
/** Recovery diagnostics (tolerant lists / `expect()`) collected during the parse (in order). */
|
|
111
|
+
errors: ParseError[]
|
|
112
|
+
/**
|
|
113
|
+
* Sparse selected root trivia. This is present only when at least one requested
|
|
114
|
+
* category was actually retained; omitted means the parse retained no root
|
|
115
|
+
* trivia at all.
|
|
116
|
+
*/
|
|
117
|
+
rootTrivia?: RootTriviaCapture
|
|
118
|
+
/** Offset where unparsed input begins — the first non-trivia character the parse
|
|
119
|
+
* left unconsumed (the document's trailing trivia is always skipped; see
|
|
120
|
+
* `RunOptions.trivia`), or null if the whole input was consumed. This is how you
|
|
121
|
+
* detect "the grammar stopped short, there's junk here". Only meaningful on
|
|
122
|
+
* success — a failed parse reports its own `span`/`expected`. */
|
|
123
|
+
unconsumedFrom: number | null
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 0.44 dropped three `RunResult` fields — `triviaMap` and `triviaLog`, both
|
|
128
|
+
* MANDATORY in 0.43, and the optional `triviaKindLabels`. Removing them without
|
|
129
|
+
* a signal makes each read `undefined`, and `undefined` travels: it surfaces as
|
|
130
|
+
* a property access on nothing somewhere inside the CONSUMER's code, in a
|
|
131
|
+
* message naming neither parseman, the field, nor the replacement. That is the
|
|
132
|
+
* same defect as a diagnostic that reports a number when it means "I could not
|
|
133
|
+
* run" — the tool's inability wearing the costume of a result.
|
|
134
|
+
*
|
|
135
|
+
* `triviaKindLabels` is the worst of the three despite being optional: it fed
|
|
136
|
+
* `triviaKindMask`, which treats `undefined` as "capture everything", so its
|
|
137
|
+
* removal silently changes behaviour rather than producing so much as a crash.
|
|
138
|
+
*
|
|
139
|
+
* So the names are kept as accessors that throw the migration. They are
|
|
140
|
+
* NON-ENUMERABLE deliberately: absent from `Object.keys`, spreads,
|
|
141
|
+
* `JSON.stringify` and identity digests, so restoring them moves no output —
|
|
142
|
+
* they exist only to answer a read.
|
|
143
|
+
*
|
|
144
|
+
* ## They live on a SHARED PROTOTYPE, and that is the whole point
|
|
145
|
+
*
|
|
146
|
+
* "Costs nothing on the parse path" was ASSERTED here and was FALSE. Until
|
|
147
|
+
* 0.47 every `run()` called a `guardRemovedFields(result)` that ran three
|
|
148
|
+
* `Object.defineProperty` calls — three fresh getter closures, three
|
|
149
|
+
* AccessorPairs, three runtime calls — on the result of EVERY parse. Measured
|
|
150
|
+
* through `bench/ab-harness.ts` (both sides in one process, paired and
|
|
151
|
+
* order-alternated, sides rebuilt per pass, control pair measuring the null):
|
|
152
|
+
* removing it made `run()` **-42% on a 7-byte input and -18% on the 52-byte
|
|
153
|
+
* `SMALL_JSON`**, at 12/12 interleaved pairs in every pass against a null of
|
|
154
|
+
* 48-55%. It is a FIXED cost per run, so it vanishes into the noise by
|
|
155
|
+
* `MEDIUM_JSON` — which is exactly why no size-scaled benchmark ever saw it,
|
|
156
|
+
* and exactly why the smallest parses paid the most.
|
|
157
|
+
*
|
|
158
|
+
* The realised-map count was NOT the mechanism, and saying so matters because
|
|
159
|
+
* that was the first guess: `%HaveSameMap` over 2000 parses read **2** maps,
|
|
160
|
+
* not 2000 — V8 shares the accessor transition even though each result got its
|
|
161
|
+
* own closures. The cost was pure per-parse WORK.
|
|
162
|
+
*
|
|
163
|
+
* A prototype pays it once, at module load, and keeps the notice
|
|
164
|
+
* byte-for-byte. Every property the tests pin is preserved: a read still
|
|
165
|
+
* throws the migration `TypeError`, and non-enumerable prototype accessors are
|
|
166
|
+
* invisible to `Object.keys`, spread and `JSON.stringify` for the same reason
|
|
167
|
+
* non-enumerable own ones were. The one difference is that
|
|
168
|
+
* `Object.getOwnPropertyDescriptor(result, 'triviaLog')` is now `undefined`
|
|
169
|
+
* and the descriptor lives on the prototype — nothing reads these names except
|
|
170
|
+
* a stale consumer, and a stale consumer reads them, it does not describe them.
|
|
171
|
+
*
|
|
172
|
+
* The prototype is attached at CREATION — see `RunResultRecord` below for the
|
|
173
|
+
* measured cost of each way of doing that, and for why "attach a prototype" is
|
|
174
|
+
* not one decision but two.
|
|
175
|
+
*/
|
|
176
|
+
const SELECT_EXAMPLE = "run(entry, input, { rootTrivia: { select: ['blockComment', 'lineComment'] } })"
|
|
177
|
+
|
|
178
|
+
const REMOVED_RUN_RESULT_FIELDS: ReadonlyArray<readonly [string, string]> = [
|
|
179
|
+
[
|
|
180
|
+
'triviaMap',
|
|
181
|
+
'It was a dense root-trivia index built on every parse; root trivia is now an OPT-IN sparse '
|
|
182
|
+
+ 'capture, because most grammars paid for an index they never read. Migration: name the trivia '
|
|
183
|
+
+ `labels your grammar defines — ${SELECT_EXAMPLE} — and read \`RunResult.rootTrivia\`, whose `
|
|
184
|
+
+ '`.index` carries the labels and gap lookups `triviaMap` exposed.',
|
|
185
|
+
],
|
|
186
|
+
[
|
|
187
|
+
'triviaLog',
|
|
188
|
+
'It was the flat `start, end[, kindIndex]` log every other trivia view was derived from, and it '
|
|
189
|
+
+ 'is no longer allocated at all — not empty, absent. Migration: request the labels you need '
|
|
190
|
+
+ `— ${SELECT_EXAMPLE} — and read \`RunResult.rootTrivia.rows\`. Do NOT reuse a stride-2/3 reader: `
|
|
191
|
+
+ 'rows are a different width and a different unit (one row per SELECTED marker, not one per '
|
|
192
|
+
+ 'trivia chunk), so an old loop reads confidently wrong offsets rather than failing.',
|
|
193
|
+
],
|
|
194
|
+
[
|
|
195
|
+
'triviaKindLabels',
|
|
196
|
+
'It was already optional, so nothing ever flagged its disappearance — and `triviaKindMask(undefined, '
|
|
197
|
+
+ '…)` means "capture everything", so reading it now silently WIDENS capture instead of erroring. '
|
|
198
|
+
+ `Migration: request labels — ${SELECT_EXAMPLE} — then the set you asked for is `
|
|
199
|
+
+ '`RunResult.rootTrivia.select` and the resolved table is `RunResult.rootTrivia.index.labels`.',
|
|
200
|
+
],
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* The two shapes a `RunResult` comes in, as constructors.
|
|
205
|
+
*
|
|
206
|
+
* TWO, because `rootTrivia` is ABSENT rather than empty when no requested
|
|
207
|
+
* category was retained and that is the contract — `RunResultWithRootTrivia`
|
|
208
|
+
* is not a wider `RunResultRecord`, it is the other case. Assignment order
|
|
209
|
+
* fixes `Object.keys` order, and plain assignment keeps every field an
|
|
210
|
+
* enumerable own property, so both are indistinguishable from the object
|
|
211
|
+
* literals they replace.
|
|
212
|
+
*
|
|
213
|
+
* CONSTRUCTORS rather than literals carrying `__proto__`, and the difference is
|
|
214
|
+
* not stylistic. Measured at 200k allocations of this exact shape, against a
|
|
215
|
+
* plain literal at 7.28 ms: three per-object `Object.defineProperty` calls
|
|
216
|
+
* (what 0.44-0.46 did) cost **105.27 ms**, a `{ __proto__: PROTO, … }` literal
|
|
217
|
+
* **25.58 ms**, and `new` on a constructor whose `.prototype` carries the
|
|
218
|
+
* accessors **8.72 ms**. `__proto__` in a literal is a runtime prototype set,
|
|
219
|
+
* about 91 ns per result; a constructor bakes the prototype into the initial
|
|
220
|
+
* map and costs about 7 ns. Both readings were confirmed end-to-end through
|
|
221
|
+
* `bench/ab-harness.ts` on `run()` itself.
|
|
222
|
+
*/
|
|
223
|
+
class RunResultRecord {
|
|
224
|
+
declare ok: RunResult['ok']
|
|
225
|
+
declare value: RunResult['value']
|
|
226
|
+
declare span: RunResult['span']
|
|
227
|
+
declare expected: RunResult['expected']
|
|
228
|
+
declare errors: RunResult['errors']
|
|
229
|
+
declare unconsumedFrom: RunResult['unconsumedFrom']
|
|
230
|
+
|
|
231
|
+
constructor(
|
|
232
|
+
ok: RunResult['ok'],
|
|
233
|
+
value: RunResult['value'],
|
|
234
|
+
span: RunResult['span'],
|
|
235
|
+
expected: RunResult['expected'],
|
|
236
|
+
errors: RunResult['errors'],
|
|
237
|
+
unconsumedFrom: RunResult['unconsumedFrom'],
|
|
238
|
+
) {
|
|
239
|
+
this.ok = ok
|
|
240
|
+
this.value = value
|
|
241
|
+
this.span = span
|
|
242
|
+
this.expected = expected
|
|
243
|
+
this.errors = errors
|
|
244
|
+
this.unconsumedFrom = unconsumedFrom
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
class RunResultWithRootTrivia {
|
|
249
|
+
declare ok: RunResult['ok']
|
|
250
|
+
declare value: RunResult['value']
|
|
251
|
+
declare span: RunResult['span']
|
|
252
|
+
declare expected: RunResult['expected']
|
|
253
|
+
declare errors: RunResult['errors']
|
|
254
|
+
declare rootTrivia: RootTriviaCapture
|
|
255
|
+
declare unconsumedFrom: RunResult['unconsumedFrom']
|
|
256
|
+
|
|
257
|
+
constructor(
|
|
258
|
+
ok: RunResult['ok'],
|
|
259
|
+
value: RunResult['value'],
|
|
260
|
+
span: RunResult['span'],
|
|
261
|
+
expected: RunResult['expected'],
|
|
262
|
+
errors: RunResult['errors'],
|
|
263
|
+
rootTrivia: RootTriviaCapture,
|
|
264
|
+
unconsumedFrom: RunResult['unconsumedFrom'],
|
|
265
|
+
) {
|
|
266
|
+
this.ok = ok
|
|
267
|
+
this.value = value
|
|
268
|
+
this.span = span
|
|
269
|
+
this.expected = expected
|
|
270
|
+
this.errors = errors
|
|
271
|
+
this.rootTrivia = rootTrivia
|
|
272
|
+
this.unconsumedFrom = unconsumedFrom
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Install the removed-field notices ONCE, at module load, on both prototypes.
|
|
278
|
+
*
|
|
279
|
+
* Non-enumerable, as they were as own properties, and for the same reason:
|
|
280
|
+
* invisible to `Object.keys`, spread, `JSON.stringify` and identity digests, so
|
|
281
|
+
* restoring the names moves no output. A read still throws the migration.
|
|
282
|
+
*/
|
|
283
|
+
for (const proto of [RunResultRecord.prototype, RunResultWithRootTrivia.prototype]) {
|
|
284
|
+
for (const [name, detail] of REMOVED_RUN_RESULT_FIELDS) {
|
|
285
|
+
Object.defineProperty(proto, name, {
|
|
286
|
+
configurable: true,
|
|
287
|
+
enumerable: false,
|
|
288
|
+
get(): never {
|
|
289
|
+
throw new TypeError(
|
|
290
|
+
`RunResult.${name} was REMOVED in parseman 0.44.0 and has no default replacement. ${detail}`,
|
|
291
|
+
)
|
|
292
|
+
},
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const invoke = (r: Runnable, input: string, pos: number, ctx: ParseContext): ParseResult<unknown> =>
|
|
298
|
+
typeof r === 'function' ? r(input, pos, ctx) : r.parse(input, pos, ctx)
|
|
299
|
+
|
|
300
|
+
type RunnableMeta = {
|
|
301
|
+
readonly _meta?: {
|
|
302
|
+
readonly triviaKindLabels?: readonly string[]
|
|
303
|
+
readonly rootTriviaClassified?: true
|
|
304
|
+
readonly grammarTrivia?: Runnable
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* THE DOCUMENT ROOT OWNS ITS TRAILING TRIVIA, and it is `run()` that decides
|
|
310
|
+
* which rule the document root is.
|
|
311
|
+
*
|
|
312
|
+
* `node({ trailingTrivia })` says the same thing per NODE, and its failure mode
|
|
313
|
+
* is a silent success: of jess's four shipping grammars two set it on
|
|
314
|
+
* `Stylesheet` and two did not, so a comment-only `.jess` document matched
|
|
315
|
+
* ZERO-WIDTH at the root and reported `ok: true` with `unconsumedFrom: 0` — 0 of
|
|
316
|
+
* 124 bytes consumed, no error, every gate green. 1626 of 2409 sass-spec inputs
|
|
317
|
+
* stopped exactly one byte short of the file for the same reason. An option
|
|
318
|
+
* every grammar author must remember, whose cost of forgetting is a parse that
|
|
319
|
+
* claims to have succeeded, is not an option.
|
|
320
|
+
*
|
|
321
|
+
* WHY THIS CANNOT BE A DEFAULT ON `node()`. A block bounded by a closing
|
|
322
|
+
* delimiter must NOT swallow the gap after its closer — that gap belongs to the
|
|
323
|
+
* parent, and handing it to the child mis-attributes trivia in the CST. That is
|
|
324
|
+
* a different silent defect, not a fix. Only the document root has no parent to
|
|
325
|
+
* take it.
|
|
326
|
+
*
|
|
327
|
+
* WHY IT CANNOT BE STAMPED BY `rules()` / `parser()` EITHER, which is the
|
|
328
|
+
* question this defect really turns on. A `rules()` map has many rules and ANY
|
|
329
|
+
* of them can be an entry; `compose()`/`linkable()` pieces bring their own. A
|
|
330
|
+
* rule is not a root — a rule is a root OF A PARSE. `Stylesheet` is the document
|
|
331
|
+
* root when you hand it to `run()`, and an ordinary interior rule when some
|
|
332
|
+
* other rule references it, and BOTH can be true of the same combinator object
|
|
333
|
+
* in the same process. Nothing at grammar-construction time can distinguish
|
|
334
|
+
* those two uses, which is exactly why this was made an opt-in in the first
|
|
335
|
+
* place. `run()` is the one place that knows: it is handed the entry.
|
|
336
|
+
*
|
|
337
|
+
* So there is no stamp and no per-rule flag. A referenced rule runs inside its
|
|
338
|
+
* referencing rule's body, `run()` never sees it, and the enclosing rule keeps
|
|
339
|
+
* ownership of the gap that follows it — unchanged, in every engine.
|
|
340
|
+
*
|
|
341
|
+
* THE THREE PLACES the ambient trivia can live are the same three
|
|
342
|
+
* `triviaKindLabelsFromRunnable` already reads, for the same reason: `rules({
|
|
343
|
+
* trivia }, …)` leaves it on `_meta.grammarTrivia`, `parser({ trivia }, …)`
|
|
344
|
+
* leaves it on `_def.triviaParser`, and a table/compiled entry is a FUNCTION
|
|
345
|
+
* that carries neither, so its rule map stamps `_meta.grammarTrivia` itself
|
|
346
|
+
* (`src/table/stamp.ts`). Reading fewer than three is how one engine's entry
|
|
347
|
+
* silently keeps the old behaviour while the others move.
|
|
348
|
+
*/
|
|
349
|
+
function ambientTriviaFromRunnable(r: Runnable | undefined): Runnable | undefined {
|
|
350
|
+
if (!r) return undefined
|
|
351
|
+
if (typeof r === 'function') return (r as RunnableMeta)._meta?.grammarTrivia
|
|
352
|
+
// A trivia rule handed in as the entry parses trivia; it has no tail of its own.
|
|
353
|
+
if (r._meta.isTrivia) return undefined
|
|
354
|
+
return (r._meta as { grammarTrivia?: Combinator<unknown> }).grammarTrivia
|
|
355
|
+
?? (r._def.tag === 'grammar' ? r._def.triviaParser : undefined)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function triviaKindLabelsFromRunnable(r: Runnable | undefined): readonly string[] | undefined {
|
|
359
|
+
if (!r) return undefined
|
|
360
|
+
if (typeof r === 'function') return (r as RunnableMeta)._meta?.triviaKindLabels
|
|
361
|
+
const grammarTrivia = (r._meta as { grammarTrivia?: Combinator<unknown> }).grammarTrivia
|
|
362
|
+
return grammarTrivia?._meta.triviaKindLabels
|
|
363
|
+
?? r._meta.triviaKindLabels
|
|
364
|
+
?? (r._def.tag === 'grammar' ? r._def.triviaParser?._meta.triviaKindLabels : undefined)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function rootTriviaClassifiedFromRunnable(r: Runnable | undefined): boolean {
|
|
368
|
+
if (!r) return false
|
|
369
|
+
if (typeof r === 'function') return (r as RunnableMeta)._meta?.rootTriviaClassified === true
|
|
370
|
+
const grammarTrivia = (r._meta as { grammarTrivia?: Combinator<unknown> }).grammarTrivia
|
|
371
|
+
return grammarTrivia?._meta.rootTriviaClassified === true || r._meta.rootTriviaClassified === true
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function makeSelectedRootLabelIndex(labels: readonly string[]): Readonly<Record<string, number>> {
|
|
375
|
+
const index: Record<string, number> = Object.create(null) as Record<string, number>
|
|
376
|
+
for (let i = 0; i < labels.length; i++) {
|
|
377
|
+
if (index[labels[i]!] === undefined) index[labels[i]!] = i
|
|
378
|
+
}
|
|
379
|
+
return index
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Extend a result span's END over the document's trailing trivia.
|
|
384
|
+
*
|
|
385
|
+
* A NEW object, never a mutation: the entry's span may be the very object a node
|
|
386
|
+
* already published to the tree, and growing that in place would move a node's
|
|
387
|
+
* span as a side effect of asking the driver a document-level question.
|
|
388
|
+
*
|
|
389
|
+
* When the run tracked lines, `endLine`/`endColumn` are re-derived rather than
|
|
390
|
+
* carried over. Leaving them describing the OLD end while `end` names the new
|
|
391
|
+
* one reports a position that is off by however much trivia the file ends with —
|
|
392
|
+
* the same class of quietly-wrong answer this whole change exists to remove. The
|
|
393
|
+
* scan is over the trailing trivia only, and only when the fields are present,
|
|
394
|
+
* so a run without line tracking does no work at all.
|
|
395
|
+
*/
|
|
396
|
+
function growSpanEnd(span: Span, input: string, end: number): Span {
|
|
397
|
+
if (span.endLine === undefined || span.endColumn === undefined) {
|
|
398
|
+
return { start: span.start, end }
|
|
399
|
+
}
|
|
400
|
+
let line = span.endLine
|
|
401
|
+
let column = span.endColumn
|
|
402
|
+
for (let i = span.end; i < end; i++) {
|
|
403
|
+
if (input.charCodeAt(i) === 10) { line++; column = 1 } else { column++ }
|
|
404
|
+
}
|
|
405
|
+
return { ...span, end, endLine: line, endColumn: column }
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/** Keep the run-entry closure independent from grammar-construction helpers. */
|
|
409
|
+
function runOnce(entry: Runnable, input: string, options: RunOptions): RunResult {
|
|
410
|
+
if (typeof entry !== 'function' && typeof (entry as Combinator<unknown> | undefined)?.parse !== 'function') {
|
|
411
|
+
throw new TypeError(
|
|
412
|
+
`run(): start production is ${entry === null ? 'null' : typeof entry}, not a rule — the requested grammar rule does not exist (check the rule name).`,
|
|
413
|
+
)
|
|
414
|
+
}
|
|
415
|
+
const rootTriviaSelection = options.rootTrivia?.select
|
|
416
|
+
const captureRootTrivia = rootTriviaSelection !== undefined && rootTriviaSelection.length > 0
|
|
417
|
+
const selectedRootLog = captureRootTrivia ? [] as number[] : undefined
|
|
418
|
+
const selectedRootLabelIndex = captureRootTrivia
|
|
419
|
+
? makeSelectedRootLabelIndex(rootTriviaSelection)
|
|
420
|
+
: undefined
|
|
421
|
+
const errors: ParseError[] = []
|
|
422
|
+
// Grammar-level ambient trivia declared via rules({ trivia }, factory): install
|
|
423
|
+
// it as ctx.trivia so it's ambient for the whole parse (the interpreter path;
|
|
424
|
+
// a compiled entry has it baked in and carries no _meta). parser/noTrivia still
|
|
425
|
+
// override locally.
|
|
426
|
+
const grammarTrivia = typeof entry !== 'function' ? entry._meta.grammarTrivia : undefined
|
|
427
|
+
const grammarScanSkip = typeof entry !== 'function' ? entry._meta.grammarScanSkip : undefined
|
|
428
|
+
const triviaKindLabels = triviaKindLabelsFromRunnable(entry) ?? triviaKindLabelsFromRunnable(options.trivia)
|
|
429
|
+
if (captureRootTrivia && triviaKindLabels === undefined) {
|
|
430
|
+
throw new TypeError('run(): rootTrivia.select requires labeled grammar trivia.')
|
|
431
|
+
}
|
|
432
|
+
if (captureRootTrivia) {
|
|
433
|
+
for (const label of rootTriviaSelection) {
|
|
434
|
+
if (!triviaKindLabels!.includes(label)) {
|
|
435
|
+
throw new TypeError(`run(): rootTrivia.select contains unknown trivia label ${JSON.stringify(label)}.`)
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (captureRootTrivia
|
|
440
|
+
&& !rootTriviaClassifiedFromRunnable(entry)
|
|
441
|
+
&& !rootTriviaClassifiedFromRunnable(options.trivia)) {
|
|
442
|
+
throw new TypeError(
|
|
443
|
+
'run(): rootTrivia.select requires classifiedTrivia() at the root.',
|
|
444
|
+
)
|
|
445
|
+
}
|
|
446
|
+
/*
|
|
447
|
+
* Refuse an artifact/host mismatch ONCE per parse, exactly as `parseDoc` and a
|
|
448
|
+
* compiled parser's `parseWithContext` already do. `run()` is handed a RULE, not the
|
|
449
|
+
* registry, so the fused rule functions carry the stamp themselves (see `fusedBody`);
|
|
450
|
+
* an interpreter entry carries it on `_meta`. Neither is on a hot path — this runs
|
|
451
|
+
* once, before `invoke`.
|
|
452
|
+
*
|
|
453
|
+
* Without this, `run()` was the one driver that could silently produce the wrong tree
|
|
454
|
+
* shape: an 'ast' artifact driven with a positioned-CST host returns its own AST
|
|
455
|
+
* objects where the caller asked for a CST, and a CST child filter then drops them
|
|
456
|
+
* and the node simply vanishes.
|
|
457
|
+
*
|
|
458
|
+
* The INTERPRETER passes `elided: false` unconditionally, and that is not a shortcut:
|
|
459
|
+
* it has no compile step, re-decides the host route per parse, and so has never
|
|
460
|
+
* dropped a branch. Only its `'cst'`-without-a-host half can be wrong.
|
|
461
|
+
*/
|
|
462
|
+
const stamped = entry as Partial<Record<symbol, unknown>>
|
|
463
|
+
assertHostModeCompatible(
|
|
464
|
+
typeof entry === 'function'
|
|
465
|
+
? ((stamped[FUSED_HOST_MODE] as HostMode | undefined) ?? 'ast')
|
|
466
|
+
: (entry._meta.grammarHostMode ?? 'ast'),
|
|
467
|
+
options.build,
|
|
468
|
+
typeof entry === 'function' && stamped[FUSED_HOST_ELIDED] === true,
|
|
469
|
+
)
|
|
470
|
+
// ONE shape, every configuration. These were six conditional spreads; see
|
|
471
|
+
// `createParseContext` for why that is a hidden-class hazard on the object
|
|
472
|
+
// every combinator reads. Assignments below are in-object stores to slots that
|
|
473
|
+
// already exist, so none of them transitions the map.
|
|
474
|
+
const ctx = createParseContext()
|
|
475
|
+
if (captureRootTrivia) {
|
|
476
|
+
ctx._rootTriviaLog = selectedRootLog!
|
|
477
|
+
ctx._rootTriviaKindIndex = selectedRootLabelIndex!
|
|
478
|
+
ctx._rootTriviaStrictScopes = true
|
|
479
|
+
}
|
|
480
|
+
ctx._errors = errors
|
|
481
|
+
ctx.build = options.build
|
|
482
|
+
ctx.state = options.state
|
|
483
|
+
if (grammarTrivia !== undefined) ctx.trivia = grammarTrivia
|
|
484
|
+
if (triviaKindLabels) ctx.triviaKindLabels = triviaKindLabels
|
|
485
|
+
if (grammarScanSkip !== undefined) ctx.scanSkip = grammarScanSkip
|
|
486
|
+
if (options.triviaCaptureMask !== undefined) ctx._triviaCaptureMask = options.triviaCaptureMask
|
|
487
|
+
if (options.tolerant) {
|
|
488
|
+
ctx._tolerant = true
|
|
489
|
+
ctx._rec = REC
|
|
490
|
+
}
|
|
491
|
+
if (options.instrumentation !== undefined) {
|
|
492
|
+
ctx._grammarCoverage = options.instrumentation._grammarCoverage
|
|
493
|
+
ctx._grammarTrace = options.instrumentation._grammarTrace
|
|
494
|
+
}
|
|
495
|
+
const r = invoke(entry, input, 0, ctx)
|
|
496
|
+
|
|
497
|
+
const value = r.ok ? (r as { value: unknown }).value : undefined
|
|
498
|
+
let span = r.span ?? { start: 0, end: 0 }
|
|
499
|
+
const expected = r.ok ? [] : ((r as { expected?: string[] }).expected ?? [])
|
|
500
|
+
|
|
501
|
+
let unconsumedFrom: number | null = null
|
|
502
|
+
if (r.ok) {
|
|
503
|
+
let pos = span.end
|
|
504
|
+
// The root's trailing trivia — see `ambientTriviaFromRunnable`. `options.trivia`
|
|
505
|
+
// wins because it is an explicit request to measure the tail with a DIFFERENT
|
|
506
|
+
// trivia than the grammar parses with.
|
|
507
|
+
const tail = options.trivia ?? ambientTriviaFromRunnable(entry)
|
|
508
|
+
if (tail !== undefined && pos < input.length) {
|
|
509
|
+
// Throwaway ctx: trailing trivia must NOT pollute the parse's trivia log.
|
|
510
|
+
// This is a POSITION question, and the root's per-node trivia log is the
|
|
511
|
+
// separate, node-scoped answer that `node({ trailingTrivia })` gives.
|
|
512
|
+
const t = invoke(tail, input, pos, createParseContext())
|
|
513
|
+
if (t.ok && t.span.end > pos) {
|
|
514
|
+
span = growSpanEnd(span, input, t.span.end)
|
|
515
|
+
pos = t.span.end
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
unconsumedFrom = pos < input.length ? pos : null
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// `rootTrivia` is ABSENT rather than empty when no requested category was
|
|
522
|
+
// retained, so branch on it — do not index into it. The branch is spelled as
|
|
523
|
+
// two constructors rather than one literal with a
|
|
524
|
+
// `...(cond ? { rootTrivia } : {})` spread: the two arms produce exactly the
|
|
525
|
+
// two shapes the contract calls for, which is what the spread was silently
|
|
526
|
+
// doing anyway, and a conditional spread is the shape hazard this repo has
|
|
527
|
+
// an incident over.
|
|
528
|
+
return selectedRootLog !== undefined && selectedRootLog.length > 0
|
|
529
|
+
? new RunResultWithRootTrivia(
|
|
530
|
+
r.ok,
|
|
531
|
+
value,
|
|
532
|
+
span,
|
|
533
|
+
expected,
|
|
534
|
+
errors,
|
|
535
|
+
{
|
|
536
|
+
rows: selectedRootLog,
|
|
537
|
+
select: rootTriviaSelection!,
|
|
538
|
+
index: buildRootTriviaIndex(selectedRootLog, rootTriviaSelection!),
|
|
539
|
+
},
|
|
540
|
+
unconsumedFrom,
|
|
541
|
+
)
|
|
542
|
+
: new RunResultRecord(r.ok, value, span, expected, errors, unconsumedFrom)
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/* `RunOptions.profile` — the three-pass profiling boundary (recognizer /
|
|
546
|
+
* structuralCapture / hostConstruction) — is GONE, along with `RunResult.profile`,
|
|
547
|
+
* `RunProfile`, `RunProfilePass`, the `ProfilePhase`/`ProfileState` plumbing and
|
|
548
|
+
* `ParseContext._pmProfile`. The counters it read stopped being emitted into
|
|
549
|
+
* compiled artifacts in 9751cce (profiling is interpreted-mode only: the gates cost
|
|
550
|
+
* a `_ctx._pmProfile` read plus ~15 threaded ternaries on EVERY node), the
|
|
551
|
+
* interpreter never implemented them, and so the option only ever threw. A
|
|
552
|
+
* well-typed call into an unconditional throw is worse than no option at all.
|
|
553
|
+
* What a restoration would take is recorded in
|
|
554
|
+
* `docs/future/bench-typecheck-followups.md`. */
|
|
555
|
+
|
|
556
|
+
export function run(entry: Runnable, input: string, options: RunOptions = {}): RunResult {
|
|
557
|
+
return runOnce(entry, input, options)
|
|
558
|
+
}
|