parseman 0.46.0 → 0.47.1
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-render.d.ts +12 -4
- package/dist/analysis/choice-cost-render.d.ts.map +1 -1
- package/dist/analysis/choice-cost.d.ts +2 -2
- package/dist/analysis/choice-cost.d.ts.map +1 -1
- package/dist/analysis/commitment.d.ts +131 -0
- package/dist/analysis/commitment.d.ts.map +1 -0
- package/dist/analysis/diagnostics.cjs +10339 -7787
- package/dist/analysis/diagnostics.cjs.map +3 -4
- package/dist/analysis/diagnostics.d.ts +2 -2
- package/dist/analysis/diagnostics.js +10339 -7787
- package/dist/analysis/diagnostics.js.map +3 -4
- package/dist/analysis/duplication.d.ts.map +1 -1
- package/dist/analysis/fix.d.ts +3 -3
- package/dist/analysis/fix.d.ts.map +1 -1
- 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 +39 -2
- package/dist/analysis/gating.d.ts.map +1 -1
- package/dist/analysis/rebuild.d.ts.map +1 -1
- package/dist/analysis/terminal.d.ts +5 -2
- package/dist/analysis/terminal.d.ts.map +1 -1
- package/dist/cli/index.cjs +10924 -8506
- package/dist/cli/index.cjs.map +3 -4
- package/dist/cli/index.js +10924 -8506
- package/dist/cli/index.js.map +3 -4
- 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 +45 -1
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/combinators/withCtx.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 +47 -53
- 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 +9290 -7181
- package/dist/index.cjs.map +3 -4
- package/dist/index.d.ts +18 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9300 -7191
- package/dist/index.js.map +3 -4
- package/dist/language-service/index.cjs +313 -121
- package/dist/language-service/index.cjs.map +3 -4
- package/dist/language-service/index.js +313 -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 +40 -1
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +11175 -9534
- package/dist/plugin/index.cjs.map +3 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +11175 -9534
- package/dist/plugin/index.js.map +3 -4
- 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 +2934 -1611
- package/dist/spec/index.cjs.map +3 -4
- package/dist/spec/index.js +2934 -1611
- 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 +11463 -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 +11440 -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 +453 -0
- package/dist/table/ops.d.ts.map +1 -0
- package/dist/table/program.d.ts +496 -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 +26 -2
- 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 +348 -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 +237 -0
- package/src/combinators/token.ts +120 -0
- package/src/combinators/trivia-boundary.ts +48 -0
- package/src/combinators/trivia-skip.ts +852 -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 +3006 -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 +1777 -0
- package/src/table/emit.ts +429 -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 +1680 -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 +467 -0
- package/src/table/program.ts +876 -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/module-hoist.d.ts +0 -106
- package/dist/compiler/module-hoist.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,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE PROTOCOL THE THREE ENGINES SHARE — the failure sentinel and the
|
|
3
|
+
* end-position out-parameter.
|
|
4
|
+
*
|
|
5
|
+
* `exec.ts`, `assemble.ts` and `emit-assembly.ts` each ran a private copy of
|
|
6
|
+
* both: three `Symbol('pm.fail')` calls and three `let END = 0` closure slots.
|
|
7
|
+
* That was sound while an assembly picked ONE engine and ran it for the whole
|
|
8
|
+
* parse, which is what every shipped configuration does.
|
|
9
|
+
*
|
|
10
|
+
* It stops being sound the moment two engines are live in one parse. A piece
|
|
11
|
+
* from engine A returning A's `FAIL` to a caller in engine B is a VALUE to B —
|
|
12
|
+
* `!== FAIL` is true — so a failed match reads as a successful one carrying a
|
|
13
|
+
* symbol, and the parse continues from a position nobody wrote. Neither a type
|
|
14
|
+
* nor a test would catch it: the sentinels have the same description, and the
|
|
15
|
+
* three-way identity sweep only ever exercised one engine per process.
|
|
16
|
+
*
|
|
17
|
+
* So both halves move here:
|
|
18
|
+
*
|
|
19
|
+
* - `FAIL` is ONE symbol, module-level, imported by every engine. There is no
|
|
20
|
+
* per-assembly variation possible in a sentinel, so there is no reason for it
|
|
21
|
+
* to be per-assembly state.
|
|
22
|
+
* - `EndCell` is per-ASSEMBLY, not per-module. Two assemblies for two grammars
|
|
23
|
+
* may be live in one process and must not share a slot; the engines *within*
|
|
24
|
+
* one assembly must. `assemble()` mints one and hands it to whichever engines
|
|
25
|
+
* that assembly builds.
|
|
26
|
+
*
|
|
27
|
+
* A one-property object rather than an `Int32Array`: the store is monomorphic
|
|
28
|
+
* either way, and `e` reads in emitted text as `EC.e`, which is the same shape
|
|
29
|
+
* the closure engine writes.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/** The failure sentinel. ONE per process, shared by every engine. */
|
|
33
|
+
export const FAIL: unique symbol = Symbol('pm.fail')
|
|
34
|
+
export type Fail = typeof FAIL
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The end-position out-parameter — `_pfEnd` in emitted text, `END` in the
|
|
38
|
+
* closure walk and the driver. One per assembly.
|
|
39
|
+
*/
|
|
40
|
+
export type EndCell = { e: number }
|
|
41
|
+
|
|
42
|
+
export const newEndCell = (): EndCell => ({ e: 0 })
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OP_ADJ, OP_ARMGATE, OP_ATTEMPT, OP_CHOICE, OP_COV, OP_DISPATCH, OP_EMPTY, OP_EXPECT,
|
|
3
|
+
OP_FIELD, OP_GATE, OP_GREEDY, OP_GUARD, OP_LABEL, OP_LEAF, OP_LIT, OP_LIT_CI,
|
|
4
|
+
OP_LIT_CI_TRACK, OP_LIT_TRACK, OP_LIVE, OP_NODE, OP_NODE_TRACK, OP_NOT, OP_OPT,
|
|
5
|
+
OP_PEEK, OP_REJECT, OP_REP, OP_REPV, OP_ROUTED, OP_RULE, OP_RX, OP_RX_TRACK, OP_SCAN,
|
|
6
|
+
OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, OP_SEQ, OP_SEQV, OP_SEQX, OP_TOKEN, OP_WITHCTX, OP_XFORM,
|
|
7
|
+
} from './ops.ts'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* THE EDGE TABLE. Which operand slots of an instruction are CHILD instructions.
|
|
11
|
+
*
|
|
12
|
+
* This is one fact about the operand layout, and it was written twice: once in
|
|
13
|
+
* `site-labels.ts` over the 35 opcodes `emit-assembly.ts` lowers, once fused into
|
|
14
|
+
* `inspect.ts`'s `reachableIps` traversal over all 40. The two agreed slot-for-slot
|
|
15
|
+
* on every opcode both covered — which is the problem, not the reassurance. Adding
|
|
16
|
+
* an opcode meant editing two switches in two files, and NOTHING failed if you
|
|
17
|
+
* edited one. `site-labels.ts`'s own header names that failure mode, then guards
|
|
18
|
+
* only against it happening WITHIN that file.
|
|
19
|
+
*
|
|
20
|
+
* The subset was never load-bearing. The five opcodes it omitted — COV, GREEDY,
|
|
21
|
+
* REJECT, ARMGATE, WITHCTX — are exactly the five `emit-assembly.ts` has no case
|
|
22
|
+
* for, so any program containing one throws `Unemittable` from the default arm and
|
|
23
|
+
* the whole assembly falls back to closures. Walking into their subtrees can
|
|
24
|
+
* therefore only affect a result that is about to be discarded. (Verified: none of
|
|
25
|
+
* the five is so much as imported by `emit-assembly.ts`. Its one consumer of these
|
|
26
|
+
* edges, the `fd`/`er` sink census, is computed before the lowering that throws.)
|
|
27
|
+
*
|
|
28
|
+
* `code` is `ArrayLike<number>` because the two callers hold different
|
|
29
|
+
* representations of the same stream — `TableProgram.code` is a `readonly number[]`,
|
|
30
|
+
* `ResolvedTable.code` is an `Int32Array`.
|
|
31
|
+
*
|
|
32
|
+
* Returns false for an opcode this table does not know, so each caller keeps its own
|
|
33
|
+
* answer to that: `inspect.ts` throws (a program it cannot decode is a bug it must
|
|
34
|
+
* report), `site-labels.ts` ignores it (an unwalked site resolves to `TOP` via
|
|
35
|
+
* `labelAt`, and the enclosing assembly is unemittable anyway).
|
|
36
|
+
*/
|
|
37
|
+
export function childSlots(code: ArrayLike<number>, ip: number, out: number[]): boolean {
|
|
38
|
+
switch (code[ip]) {
|
|
39
|
+
// ZERO-WIDTH / TERMINAL ROWS — no successor. `OP_GUARD` and `OP_ADJ` are both
|
|
40
|
+
// childless tests. `OP_LIVE` holds a hand-written combinator in `prog.fns`; its
|
|
41
|
+
// structure is a closure, so there is no successor row to reach.
|
|
42
|
+
case OP_LIT: case OP_RX: case OP_LIT_TRACK: case OP_RX_TRACK: case OP_EMPTY:
|
|
43
|
+
case OP_SCAN: case OP_LIT_CI: case OP_LIT_CI_TRACK:
|
|
44
|
+
case OP_GUARD: case OP_ADJ: case OP_LIVE:
|
|
45
|
+
return true
|
|
46
|
+
|
|
47
|
+
// Single child at `ip + 1`. `OP_COV` is a wrapper on the same footing as the
|
|
48
|
+
// rest — a coverage-encoded program is a program, and omitting it made
|
|
49
|
+
// `opHistogram` throw on every table the coverage build produces.
|
|
50
|
+
case OP_RULE: case OP_OPT: case OP_NOT: case OP_PEEK: case OP_EXPECT:
|
|
51
|
+
case OP_TOKEN: case OP_ATTEMPT: case OP_LABEL: case OP_COV: case OP_REJECT:
|
|
52
|
+
out.push(code[ip + 1]!)
|
|
53
|
+
return true
|
|
54
|
+
|
|
55
|
+
// Single child at `ip + 2`.
|
|
56
|
+
case OP_GATE: case OP_SCOPE: case OP_SCOPE_CAP: case OP_SCOPE_PLAIN: case OP_WITHCTX: case OP_XFORM:
|
|
57
|
+
case OP_NODE: case OP_NODE_TRACK: case OP_FIELD: case OP_LEAF: case OP_ARMGATE:
|
|
58
|
+
out.push(code[ip + 2]!)
|
|
59
|
+
return true
|
|
60
|
+
|
|
61
|
+
// `ROUTED fallback` — the operand is an offset or −1. The routed token itself is
|
|
62
|
+
// data the enclosing `dispatch()` already consumed, so the only edge is the
|
|
63
|
+
// fallback.
|
|
64
|
+
case OP_ROUTED:
|
|
65
|
+
if (code[ip + 1]! >= 0) out.push(code[ip + 1]!)
|
|
66
|
+
return true
|
|
67
|
+
|
|
68
|
+
// `DISPATCH sel d other otherRouted n a1 … an` — the SELECTOR is a child on
|
|
69
|
+
// exactly the same footing as the arms: it runs inside whatever node and scope
|
|
70
|
+
// this site sits in.
|
|
71
|
+
case OP_DISPATCH: {
|
|
72
|
+
out.push(code[ip + 1]!)
|
|
73
|
+
if (code[ip + 3]! >= 0) out.push(code[ip + 3]!)
|
|
74
|
+
const n = code[ip + 5]!
|
|
75
|
+
for (let i = 0; i < n; i++) out.push(code[ip + 6 + i]!)
|
|
76
|
+
return true
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
case OP_SEQ: case OP_SEQV: {
|
|
80
|
+
const n = code[ip + 1]!
|
|
81
|
+
for (let i = 0; i < n; i++) out.push(code[ip + 2 + i]!)
|
|
82
|
+
return true
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
case OP_SEQX: {
|
|
86
|
+
const n = code[ip + 2]!
|
|
87
|
+
for (let i = 0; i < n; i++) out.push(code[ip + 3 + i]!)
|
|
88
|
+
return true
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
case OP_CHOICE: {
|
|
92
|
+
const n = code[ip + 2]!
|
|
93
|
+
for (let i = 0; i < n; i++) out.push(code[ip + 4 + i]!)
|
|
94
|
+
return true
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Arms are stride-2 from `ip + 4` — the gate word sits beside each arm offset.
|
|
98
|
+
case OP_GREEDY: {
|
|
99
|
+
out.push(code[ip + 1]!)
|
|
100
|
+
const n = code[ip + 2]!
|
|
101
|
+
for (let i = 0; i < n; i++) out.push(code[ip + 4 + 2 * i]!)
|
|
102
|
+
return true
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
case OP_REP: case OP_REPV:
|
|
106
|
+
out.push(code[ip + 1]!)
|
|
107
|
+
if (code[ip + 4]! >= 0) out.push(code[ip + 4]!)
|
|
108
|
+
return true
|
|
109
|
+
|
|
110
|
+
default:
|
|
111
|
+
return false
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import type { Combinator } from '../types.ts'
|
|
2
|
+
import { hasDirectBuildDef } from '../analysis/commitment.ts'
|
|
3
|
+
import type { HostMode } from '../cst/host-mode.ts'
|
|
4
|
+
import { collectGrammarReflection, type GrammarReflection } from '../cst/reflection.ts'
|
|
5
|
+
import { encodeTableProgram, type TableSettings } from './encode.ts'
|
|
6
|
+
import { emitTableExpression } from './emit.ts'
|
|
7
|
+
import { tableRules } from './assemble.ts'
|
|
8
|
+
import { closureArtifact } from './program.ts'
|
|
9
|
+
import type { TableProgram, TableRule } from './program.ts'
|
|
10
|
+
import { buildGrammarPlan, type GrammarCoverageDefinition } from '../compiler/grammar-coverage-ids.ts'
|
|
11
|
+
import { runDuplicationDiagnosticRules, type DuplicationOption } from './duplication-hook.ts'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* `compileRuleMap()` FOR THE TABLE LOWERING — the counterpart that did not exist.
|
|
15
|
+
*
|
|
16
|
+
* `compile` covered `compile()`, the SINGLE-ROOT entry point, which the
|
|
17
|
+
* plugin uses only for standalone combinators (`plugin/index.ts:1693`, `:1870`).
|
|
18
|
+
* The main path — every `rules()` grammar — goes through `compileRuleMap`, and
|
|
19
|
+
* with no table counterpart there was nothing for the macro build to point at.
|
|
20
|
+
* This is that function, and it is the EASY direction: `encodeTable` takes a
|
|
21
|
+
* named rule map natively, so nothing here adapts a shape. `compile` is the
|
|
22
|
+
* one that adapts (it wraps a root as `{ Entry: root }`).
|
|
23
|
+
*
|
|
24
|
+
* ── THE CONTRACT IT MATCHES ──────────────────────────────────────────────────
|
|
25
|
+
* `compileRuleMap` returns ONE `replacement` string: a self-contained expression
|
|
26
|
+
* that the plugin splices over the whole `rules(factory)` call-expression
|
|
27
|
+
* (`plugin/index.ts:1753`, and as an initialiser at `:1941`), evaluating ONCE to
|
|
28
|
+
* the `{ name: fn, … }` map. `keys` is the entry list, for the caller to check
|
|
29
|
+
* against the source's own keys. `hostMode` / `hostBranchElided` are what the
|
|
30
|
+
* macro stamps the emitted map with. `reflection` is per-grammar CST node
|
|
31
|
+
* reflection. It returns `null`, all-or-nothing, when the map cannot be inlined.
|
|
32
|
+
*
|
|
33
|
+
* Every one of those is reproduced here, with ONE stated divergence, the same
|
|
34
|
+
* one `compile` documents: the expression references `tableRules` and is
|
|
35
|
+
* therefore NOT self-contained. That reference is the entire reason a table
|
|
36
|
+
* artifact is small; inlining the driver per grammar rebuilds exactly the size
|
|
37
|
+
* this lowering exists to remove. The consumer owns the import.
|
|
38
|
+
*
|
|
39
|
+
* ── WHY `emitTableModule` WAS NOT ENOUGH ─────────────────────────────────────
|
|
40
|
+
* It emits a whole rule map as one module — `import` line, `export const` — and
|
|
41
|
+
* the plugin has no module to write; it has an expression slot inside an
|
|
42
|
+
* existing file. `emitTableExpression` was the right emitter and needed one
|
|
43
|
+
* thing: `entry: null`, meaning "the map itself" rather than one rule out of it.
|
|
44
|
+
* That is a two-line change to an existing emitter, not a new one.
|
|
45
|
+
*/
|
|
46
|
+
export type TableRuleMapOptions = {
|
|
47
|
+
/** Grammar-level ambient trivia, as `rules({ trivia }, …)` declares it. */
|
|
48
|
+
readonly trivia?: Combinator<unknown>
|
|
49
|
+
/** Grammar-level ambient scan-skip, as `rules({ scanSkip }, …)` declares it. */
|
|
50
|
+
readonly scanSkip?: Combinator<unknown>[]
|
|
51
|
+
readonly recovery?: boolean
|
|
52
|
+
readonly hostMode?: HostMode
|
|
53
|
+
readonly trackLines?: boolean
|
|
54
|
+
/**
|
|
55
|
+
* GRAMMAR-COVERAGE COUNTERS. This is the MACRO's path — `plugin/index.ts` passes
|
|
56
|
+
* `coverage: grammarCoverage` here for every `rules()` grammar — so this is what
|
|
57
|
+
* makes `{ grammarCoverage: true }` a working build under the table lowering.
|
|
58
|
+
*
|
|
59
|
+
* COUNTERS ONLY, per the owner ruling recorded in `notes/RELEASE-0.48-TARGET.md`
|
|
60
|
+
* §1; `_grammarTrace` parity is 0.48 work. The plugin's coverage path needs the
|
|
61
|
+
* DEFINITIONS STAMP and the HITS, and both are here.
|
|
62
|
+
*/
|
|
63
|
+
readonly coverage?: boolean
|
|
64
|
+
/** Identifier the emitted expression expects `tableRules` to be bound to. */
|
|
65
|
+
readonly runtimeRef?: string
|
|
66
|
+
/**
|
|
67
|
+
* Reducer sources in `prog.fns` order, for a caller that holds them OUT OF
|
|
68
|
+
* BAND — the same escape `compile(combinator, mapFnSources?)` provides.
|
|
69
|
+
*
|
|
70
|
+
* The encoder captures a source per callback from the def (`fnSrc` /
|
|
71
|
+
* `buildSrc` / `predSrc` / `gateSrcs`), which the macro evaluator sets and a
|
|
72
|
+
* runtime-built combinator does not have. Supplying them here is what lets a
|
|
73
|
+
* grammar constructed at runtime still PRINT; it does not override captured
|
|
74
|
+
* ones, it fills in for a pool that has none.
|
|
75
|
+
*/
|
|
76
|
+
readonly fnSources?: readonly string[]
|
|
77
|
+
/**
|
|
78
|
+
* WHY THIS REFUSED, filled in on the `null` return.
|
|
79
|
+
*
|
|
80
|
+
* `null` means "leave this grammar interpreted", which is ~79 ms against ~14 ms
|
|
81
|
+
* on `benchmark.less` — a ~5x regression that produces no test failure and no
|
|
82
|
+
* warning content beyond "couldn't be inlined". The reasons exist inside the
|
|
83
|
+
* encoder (`prog.runtimeOnly`) and simply had no way out; this is that way out,
|
|
84
|
+
* so the plugin's warning can name the construct instead of the outcome.
|
|
85
|
+
*
|
|
86
|
+
* An out-parameter rather than a changed return type: every existing caller
|
|
87
|
+
* checks for `null` and that contract is unchanged.
|
|
88
|
+
*/
|
|
89
|
+
readonly refusals?: string[]
|
|
90
|
+
/**
|
|
91
|
+
* The structural duplication diagnostic. Ran inside the source lowering; it runs
|
|
92
|
+
* here for the same reason, so a build that asked for it keeps getting it.
|
|
93
|
+
*/
|
|
94
|
+
readonly duplication?: DuplicationOption
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export type CompiledRuleMapTable = {
|
|
98
|
+
/** Every entry this saw, in order — same use as `compileRuleMap`'s `keys`. */
|
|
99
|
+
keys: string[]
|
|
100
|
+
/** The expression that replaces the whole `rules(factory)` call. */
|
|
101
|
+
replacement: string
|
|
102
|
+
/** Re-emit the same table call with construction-time artifact metadata. */
|
|
103
|
+
replacementWithMetadata(metadataSource: string): string
|
|
104
|
+
hostMode: HostMode
|
|
105
|
+
hostBranchElided: boolean
|
|
106
|
+
reflection: GrammarReflection
|
|
107
|
+
/**
|
|
108
|
+
* The RUNNABLE map, which the source lowering has no counterpart for — it
|
|
109
|
+
* hands back text and nothing else, because its artifact only exists once the
|
|
110
|
+
* emitted source is evaluated. A table exists as data before it is printed, so
|
|
111
|
+
* the same call that produces the replacement can also hand back the parser.
|
|
112
|
+
* That is what makes a differential against the interpreter possible without
|
|
113
|
+
* `eval`, and it is why the table-vs-interpreter test can compare `expected`
|
|
114
|
+
* sets rather than only accept/reject.
|
|
115
|
+
*/
|
|
116
|
+
rules: Record<string, TableRule>
|
|
117
|
+
/** The encoded program, for a caller that wants to fold or inspect it. */
|
|
118
|
+
prog: TableProgram
|
|
119
|
+
/**
|
|
120
|
+
* The coverage DENOMINATOR — every id this map can hit — present only under
|
|
121
|
+
* `{ coverage: true }`. Same field and same meaning as `compileRuleMap`'s, so
|
|
122
|
+
* the plugin's stamp site needs no branch on which lowering produced it.
|
|
123
|
+
*/
|
|
124
|
+
coverageDefinitions?: readonly GrammarCoverageDefinition[]
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The host mode this map was lowered for, resolved the way `compileRuleMap`
|
|
129
|
+
* resolves it: an explicit option, else a `grammarHostMode` stamp left on the
|
|
130
|
+
* rules by `rules({ hostMode }, …)`. The stamp is the only channel a macro build
|
|
131
|
+
* has, since it passes no compile options.
|
|
132
|
+
*/
|
|
133
|
+
function resolveHostMode(
|
|
134
|
+
ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>,
|
|
135
|
+
explicit: HostMode | undefined,
|
|
136
|
+
): HostMode {
|
|
137
|
+
return explicit
|
|
138
|
+
?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean)
|
|
139
|
+
?? 'ast'
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function resolveTrackLines(
|
|
143
|
+
ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>,
|
|
144
|
+
explicit: boolean | undefined,
|
|
145
|
+
): boolean {
|
|
146
|
+
return explicit === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Apply grammar-level trivia / scan-skip passed as OPTIONS to ENCODING-LOCAL
|
|
151
|
+
* rule wrappers.
|
|
152
|
+
*
|
|
153
|
+
* `rules({ trivia }, …)` stamps `_meta.grammarTrivia` on every non-trivia rule
|
|
154
|
+
* (combinators/parser.ts:203) and `encodeTable` reads it from there, per rule.
|
|
155
|
+
* A caller that instead threads the declaration as an option — which the macro
|
|
156
|
+
* does, because it re-evaluates the factory itself — must land it in the same
|
|
157
|
+
* place or the whole grammar encodes with NO ambient trivia and every
|
|
158
|
+
* whitespace-bearing input fails identically in every engine, which makes an
|
|
159
|
+
* identity check agree while proving nothing.
|
|
160
|
+
*
|
|
161
|
+
* Only fills a GAP: a rule that already carries its own stamp keeps it, so a
|
|
162
|
+
* `composeLeaf` map whose pieces legitimately disagree is not flattened.
|
|
163
|
+
*
|
|
164
|
+
* The wrappers are load-bearing. This used to write directly to `rule._meta`,
|
|
165
|
+
* which made a compile option part of the caller's grammar forever: compile the
|
|
166
|
+
* same map once with whitespace trivia and a later compile with comma trivia
|
|
167
|
+
* would still use whitespace. Encoding is synchronous, but a copy is safer than
|
|
168
|
+
* a save/restore mutation (including on the exception path), and keeps compiler
|
|
169
|
+
* options out of long-lived grammar metadata altogether.
|
|
170
|
+
*/
|
|
171
|
+
function withAmbient(
|
|
172
|
+
ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>,
|
|
173
|
+
opts: TableRuleMapOptions,
|
|
174
|
+
): ReadonlyArray<readonly [string, Combinator<unknown>]> {
|
|
175
|
+
if (opts.trivia === undefined && opts.scanSkip === undefined) return ruleMap
|
|
176
|
+
|
|
177
|
+
const wrappers = new Map<Combinator<unknown>, Combinator<unknown>>()
|
|
178
|
+
return ruleMap.map(([name, rule]) => {
|
|
179
|
+
if (rule._meta.isTrivia) return [name, rule] as const
|
|
180
|
+
const addTrivia = opts.trivia !== undefined && rule._meta.grammarTrivia === undefined
|
|
181
|
+
const addScanSkip = opts.scanSkip !== undefined && rule._meta.grammarScanSkip === undefined
|
|
182
|
+
if (!addTrivia && !addScanSkip) return [name, rule] as const
|
|
183
|
+
|
|
184
|
+
let wrapped = wrappers.get(rule)
|
|
185
|
+
if (wrapped === undefined) {
|
|
186
|
+
wrapped = {
|
|
187
|
+
...rule,
|
|
188
|
+
_meta: {
|
|
189
|
+
...rule._meta,
|
|
190
|
+
...(addTrivia ? { grammarTrivia: opts.trivia } : {}),
|
|
191
|
+
...(addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}),
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
wrappers.set(rule, wrapped)
|
|
195
|
+
}
|
|
196
|
+
return [name, wrapped] as const
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* ENCODE FOR RUNNING, with no printability requirement.
|
|
202
|
+
*
|
|
203
|
+
* `compileRuleMap` refuses a map whose author callbacks have no captured SOURCE,
|
|
204
|
+
* because PRINTING one would emit `() => {}` and the parse would return the wrong tree.
|
|
205
|
+
* That gate is right for the macro, which prints, and wrong for every caller that only
|
|
206
|
+
* ever RUNS the result: a grammar built at runtime has live callbacks and no sources by
|
|
207
|
+
* construction, and `encodeTableProgram` parks the live function in the pool where the
|
|
208
|
+
* driver calls it perfectly well.
|
|
209
|
+
*
|
|
210
|
+
* Conflating the two is the single defect this project has now hit three times — a
|
|
211
|
+
* runtime `compose()`, `linkable()`, and the fuse — each presenting as "this grammar
|
|
212
|
+
* cannot be compiled" for a grammar that runs. The split lives here, once, so the next
|
|
213
|
+
* caller inherits the right answer instead of rediscovering it.
|
|
214
|
+
*/
|
|
215
|
+
export function compileRuleMapRunnable(
|
|
216
|
+
ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>,
|
|
217
|
+
opts: TableRuleMapOptions = {},
|
|
218
|
+
): Omit<CompiledRuleMapTable, 'replacement' | 'replacementWithMetadata'> | null {
|
|
219
|
+
const encoded = encodeForRun(ruleMap, opts)
|
|
220
|
+
if (encoded === null) return null
|
|
221
|
+
const { prog, hostMode, plan } = encoded
|
|
222
|
+
const artifact = closureArtifact(prog)
|
|
223
|
+
return {
|
|
224
|
+
keys: ruleMap.map(([key]) => key),
|
|
225
|
+
hostMode,
|
|
226
|
+
hostBranchElided: hostMode === 'ast' && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
227
|
+
reflection: collectGrammarReflection(ruleMap),
|
|
228
|
+
...(plan === undefined ? {} : { coverageDefinitions: plan.definitions }),
|
|
229
|
+
rules: tableRules(artifact),
|
|
230
|
+
prog: artifact,
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function encodeForRun(
|
|
235
|
+
ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>,
|
|
236
|
+
opts: TableRuleMapOptions,
|
|
237
|
+
): { prog: TableProgram; fnSrcs: (string | null)[]; hostMode: HostMode; plan: ReturnType<typeof buildGrammarPlan> | undefined } | null {
|
|
238
|
+
runDuplicationDiagnosticRules(ruleMap, opts.duplication)
|
|
239
|
+
const encodeMap = withAmbient(ruleMap, opts)
|
|
240
|
+
// WINNERS ARE THE NON-`lazy` ENTRIES, exactly as `codegen.ts:5639` picks them.
|
|
241
|
+
// A `rules()` map stores named lazy proxies beside resolved bodies, and the plan
|
|
242
|
+
// uses the winner map to give a shared subtree ONE owner; filtering differently
|
|
243
|
+
// here would mint `rule:` ids the source lowering does not, so a consumer
|
|
244
|
+
// comparing the two engines' coverage would be comparing two denominators.
|
|
245
|
+
const plan = opts.coverage === true
|
|
246
|
+
? buildGrammarPlan(
|
|
247
|
+
encodeMap.map(([, rule]) => rule),
|
|
248
|
+
Object.fromEntries(encodeMap.filter(([, rule]) => rule._def.tag !== 'lazy')),
|
|
249
|
+
)
|
|
250
|
+
: undefined
|
|
251
|
+
const hostMode = resolveHostMode(ruleMap, opts.hostMode)
|
|
252
|
+
const settings: TableSettings = {
|
|
253
|
+
hostMode,
|
|
254
|
+
...(resolveTrackLines(ruleMap, opts.trackLines) ? { trackLines: true } : {}),
|
|
255
|
+
...(opts.recovery === true ? { recovery: true } : {}),
|
|
256
|
+
...(plan === undefined ? {} : { coverage: plan }),
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
const { prog, fnSrcs } = encodeTableProgram(Object.fromEntries(encodeMap), settings)
|
|
260
|
+
return { prog, fnSrcs, hostMode, plan }
|
|
261
|
+
} catch (e) {
|
|
262
|
+
opts.refusals?.push(e instanceof Error ? e.message : String(e))
|
|
263
|
+
return null
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function compileRuleMap(
|
|
268
|
+
ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>,
|
|
269
|
+
opts: TableRuleMapOptions = {},
|
|
270
|
+
): CompiledRuleMapTable | null {
|
|
271
|
+
// ALL-OR-NOTHING, exactly as `compileRuleMap` is. A construct with no opcode
|
|
272
|
+
// (`UnsupportedConstruct`) and a `g.X` reference that resolves to nothing
|
|
273
|
+
// (the lazy thunk throws) both mean this map cannot become a table, and the
|
|
274
|
+
// caller's existing "leave this rules() call interpreted" fallback covers it.
|
|
275
|
+
const encoded = encodeForRun(ruleMap, opts)
|
|
276
|
+
if (encoded === null) return null
|
|
277
|
+
const { prog, fnSrcs, hostMode, plan } = encoded
|
|
278
|
+
const artifact = closureArtifact(prog)
|
|
279
|
+
|
|
280
|
+
// RUNTIME-ONLY: the program parses but cannot be PRINTED (a live trivia
|
|
281
|
+
// combinator parked in the pool, say). `compileRuleMap` returns null for its
|
|
282
|
+
// own unprintable cases rather than emitting something that loads and
|
|
283
|
+
// misbehaves; this is the same answer to the same question.
|
|
284
|
+
if (artifact.runtimeOnly !== undefined && artifact.runtimeOnly.length > 0) {
|
|
285
|
+
opts.refusals?.push(...artifact.runtimeOnly)
|
|
286
|
+
return null
|
|
287
|
+
}
|
|
288
|
+
// The `mfCovered && buildCovered` gate, for the table's reducer pool: without
|
|
289
|
+
// a source per author callback the emitters print `() => {}`, which produces a
|
|
290
|
+
// module that loads and returns the wrong tree. An out-of-band list fills in
|
|
291
|
+
// only where the encoder captured nothing, and only if it is COMPLETE — a
|
|
292
|
+
// partial list would leave the placeholders it was passed to remove.
|
|
293
|
+
const supplied = opts.fnSources
|
|
294
|
+
const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? null)
|
|
295
|
+
if (sources.some(s => s === null)) {
|
|
296
|
+
const missing = sources.flatMap((s, i) => (s === null ? [i] : []))
|
|
297
|
+
opts.refusals?.push(
|
|
298
|
+
`author reducer source missing for prog.fns[${missing.join(', ')}] — printing would emit \`() => {}\` and the parse would return no value`,
|
|
299
|
+
)
|
|
300
|
+
return null
|
|
301
|
+
}
|
|
302
|
+
if (supplied !== undefined && supplied.length > fnSrcs.length) {
|
|
303
|
+
throw new Error(
|
|
304
|
+
`compileRuleMap: got ${supplied.length} fnSources for a pool of ${fnSrcs.length}. `
|
|
305
|
+
+ 'The list is positional, in prog.fns order, so a longer one means it belongs to a '
|
|
306
|
+
+ 'different encode — and the entries would be silently misassigned.',
|
|
307
|
+
)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const replacementWithMetadata = (metadataSource?: string): string => emitTableExpression(artifact, {
|
|
311
|
+
entry: null,
|
|
312
|
+
runtimeRef: opts.runtimeRef ?? 'tableRules',
|
|
313
|
+
fnSources: sources as string[],
|
|
314
|
+
...(metadataSource === undefined ? {} : { metadataSource }),
|
|
315
|
+
})
|
|
316
|
+
const replacement = replacementWithMetadata()
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
keys: ruleMap.map(([key]) => key),
|
|
320
|
+
replacement,
|
|
321
|
+
replacementWithMetadata,
|
|
322
|
+
hostMode,
|
|
323
|
+
// WHAT THE FLAG MEANS is "a DIRECT BUILDER's positioned-CST branch was
|
|
324
|
+
// dropped", which is what the driver's `'ast' artifact + CST host` check keys
|
|
325
|
+
// off. `mode === 'ast'` alone over-reports it: an all-STRUCTURAL grammar has no
|
|
326
|
+
// direct builder, so there was no branch to drop and it stays usable with either
|
|
327
|
+
// host — the long-standing `node(parser)` contract. Same predicate codegen uses
|
|
328
|
+
// for its own `hasDirectBuilders` (both read `analysis/commitment.ts`), so the two
|
|
329
|
+
// lowerings stamp the same artifact rather than disagreeing about it.
|
|
330
|
+
hostBranchElided: hostMode === 'ast' && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
331
|
+
reflection: collectGrammarReflection(ruleMap),
|
|
332
|
+
...(plan === undefined ? {} : { coverageDefinitions: plan.definitions }),
|
|
333
|
+
rules: tableRules(artifact),
|
|
334
|
+
prog: artifact,
|
|
335
|
+
}
|
|
336
|
+
}
|