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,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SITE LABELS — the encoder's downward pass over an encoded program.
|
|
3
|
+
*
|
|
4
|
+
* ## The criterion
|
|
5
|
+
*
|
|
6
|
+
* *Any consulting of options at parse time, per rule or per combinator, is a
|
|
7
|
+
* FAIL.* After U4 four consultations remained, and three of them are here:
|
|
8
|
+
* `ctx.trivia` per sequence term, `cstCaptureActive` per leaf, and
|
|
9
|
+
* `hasTrivia`/`needMark` per repetition. (`forCtx`, once per parse at the
|
|
10
|
+
* boundary, is not one of these — it selects the assembly rather than running
|
|
11
|
+
* inside one.)
|
|
12
|
+
*
|
|
13
|
+
* ## Why a label and not a `RunCfg` bit
|
|
14
|
+
*
|
|
15
|
+
* `assemble.ts:213-277` states the rule `RunCfg` is held to: a bit belongs there
|
|
16
|
+
* only when it is FIXED FOR THE LIFETIME OF A PARSE. Neither of these facts is.
|
|
17
|
+
*
|
|
18
|
+
* - `ctx.trivia` is per-SCOPE. `OP_SCOPE` swaps it mid-parse, so a `RunCfg`
|
|
19
|
+
* bit would be answering a question that has different answers at different
|
|
20
|
+
* sites in the same parse.
|
|
21
|
+
* - `ctx._cstBuf` is per-NODE — `assemble.ts:247-249` records that a previous
|
|
22
|
+
* lane proposed keying on it and that doing so would have been *incorrect,
|
|
23
|
+
* not merely redundant*.
|
|
24
|
+
*
|
|
25
|
+
* A site label is strictly stronger than either, and it is what makes both facts
|
|
26
|
+
* legal to resolve: it is computed from the PROGRAM STRUCTURE at encode time,
|
|
27
|
+
* before any option set exists, and it says what is true *at that site* rather
|
|
28
|
+
* than what is true for the parse. `OP_NODE` sets `ctx._cstBuf` unconditionally
|
|
29
|
+
* on entry and restores it on exit, so every site dynamically inside one has
|
|
30
|
+
* `_cstBuf !== undefined` — not because of an option, but because of where it
|
|
31
|
+
* sits in the program.
|
|
32
|
+
*
|
|
33
|
+
* ## The two axes, kept apart
|
|
34
|
+
*
|
|
35
|
+
* The capture label does not compress into one bit, and an earlier attempt to
|
|
36
|
+
* make it one was wrong in both directions:
|
|
37
|
+
*
|
|
38
|
+
* - `cap` is THREE-VALUED. `OP_NODE` writes `ctx.captureTrivia` a LITERAL —
|
|
39
|
+
* `readsTrivia || hostCst`, resolved at emit — so a site under a node has it
|
|
40
|
+
* definitely true OR definitely false. `OP_SCOPE_CAP` only ever sets it true.
|
|
41
|
+
* Collapsing "definitely off" into "unknown" loses the case that lets
|
|
42
|
+
* `_skipTrivia` drop its capture arm entirely.
|
|
43
|
+
* - `buf` is a separate FLAG, because `_cstBuf` and `captureTrivia` are set by
|
|
44
|
+
* different ops and neither implies the other: `OP_SCOPE_CAP` turns capture
|
|
45
|
+
* on without opening a buffer, and a builder-less `OP_NODE` opens a buffer
|
|
46
|
+
* with capture off.
|
|
47
|
+
*
|
|
48
|
+
* ## Asymmetry, deliberately
|
|
49
|
+
*
|
|
50
|
+
* `buf` is TRUE only for "guaranteed present". FALSE means UNKNOWN, never
|
|
51
|
+
* "guaranteed absent" — no root can prove absence, because every entry point
|
|
52
|
+
* (`prog.rules`, and the `extraIps` a scan pool reaches from outside the emitted
|
|
53
|
+
* scope) is called with a context this pass cannot see. Nothing is elided on
|
|
54
|
+
* `buf === false`; it only declines to elide. The same holds for `TRI_UNKNOWN`
|
|
55
|
+
* and `CAP_UNKNOWN`, which are the lattice's top element, not a third fact.
|
|
56
|
+
*
|
|
57
|
+
* That asymmetry is what rules out the unsound version of this pass: eliding a
|
|
58
|
+
* leaf's capture when `hostCst === false`. `OP_NODE` opens `_cstBuf` whatever the
|
|
59
|
+
* host mode is, and those leaves feed `kids` → `build(...)`, so the ONLY thing a
|
|
60
|
+
* label licenses at a leaf is dropping the TEST — never the capture.
|
|
61
|
+
*/
|
|
62
|
+
import {
|
|
63
|
+
OP_ATTEMPT, OP_CHOICE, OP_DISPATCH, OP_EXPECT, OP_FIELD, OP_GATE, OP_LABEL, OP_LEAF,
|
|
64
|
+
OP_NODE, OP_NODE_TRACK, OP_NOT, OP_OPT, OP_PEEK, OP_REP, OP_REPV, OP_ROUTED, OP_RULE,
|
|
65
|
+
OP_SCOPE, OP_SCOPE_CAP, OP_SEQ, OP_SEQV, OP_SEQX, OP_TOKEN, OP_XFORM,
|
|
66
|
+
} from './ops.ts'
|
|
67
|
+
import { childSlots } from './child-slots.ts'
|
|
68
|
+
|
|
69
|
+
/** No scope on this path installs a known trivia — the lattice's top element. */
|
|
70
|
+
export const TRI_UNKNOWN = -2
|
|
71
|
+
/** `ctx.trivia === undefined` is guaranteed here. */
|
|
72
|
+
export const TRI_NONE = -1
|
|
73
|
+
/** Any value `>= 0` is a trivia slot index, guaranteed installed here. */
|
|
74
|
+
|
|
75
|
+
/** `ctx.captureTrivia` is not known at this site. */
|
|
76
|
+
export const CAP_UNKNOWN = 0
|
|
77
|
+
/** `ctx.captureTrivia` is guaranteed NOT `true` here. */
|
|
78
|
+
export const CAP_OFF = 1
|
|
79
|
+
/** `ctx.captureTrivia === true` is guaranteed here. */
|
|
80
|
+
export const CAP_ON = 2
|
|
81
|
+
|
|
82
|
+
export type SiteLabel = {
|
|
83
|
+
/** `TRI_UNKNOWN`, `TRI_NONE`, or the trivia slot the enclosing scope installed. */
|
|
84
|
+
readonly tri: number
|
|
85
|
+
/**
|
|
86
|
+
* `ctx._cstBuf !== undefined` is GUARANTEED. False means unknown — see the
|
|
87
|
+
* asymmetry note in this file's header. Never read as "guaranteed absent".
|
|
88
|
+
*/
|
|
89
|
+
readonly buf: boolean
|
|
90
|
+
/** `CAP_UNKNOWN` / `CAP_OFF` / `CAP_ON`. */
|
|
91
|
+
readonly cap: number
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The lattice's top: every entry point starts here. */
|
|
95
|
+
export const TOP: SiteLabel = { tri: TRI_UNKNOWN, buf: false, cap: CAP_UNKNOWN }
|
|
96
|
+
|
|
97
|
+
function meet(a: SiteLabel, b: SiteLabel): SiteLabel {
|
|
98
|
+
const tri = a.tri === b.tri ? a.tri : TRI_UNKNOWN
|
|
99
|
+
const buf = a.buf && b.buf
|
|
100
|
+
const cap = a.cap === b.cap ? a.cap : CAP_UNKNOWN
|
|
101
|
+
if (tri === a.tri && buf === a.buf && cap === a.cap) return a
|
|
102
|
+
return { tri, buf, cap }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The sites reachable from `roots` THROUGH THE SHARED EDGE TABLE.
|
|
107
|
+
*
|
|
108
|
+
* Not `inspect.ts`'s `reachableIps`: that one walks a whole program from its rule
|
|
109
|
+
* entries, and the emitter's question is about the set IT will lower — the rule
|
|
110
|
+
* entries plus the scan pool's `extraIps`. Different ROOTS, same EDGES, and the
|
|
111
|
+
* edges now live in `child-slots.ts` so the two answers cannot drift when an
|
|
112
|
+
* opcode is added to one and not the other. That drift is the failure this file's
|
|
113
|
+
* header used to warn about while guarding only against it happening within this
|
|
114
|
+
* file; the copy it warned about was in `inspect.ts` the whole time.
|
|
115
|
+
*
|
|
116
|
+
* `childSlots`'s false return (an opcode it does not know) is ignored here: the
|
|
117
|
+
* site resolves to `TOP` through `labelAt`, and the enclosing assembly is
|
|
118
|
+
* unemittable regardless.
|
|
119
|
+
*/
|
|
120
|
+
export function reachableSites(code: Int32Array, roots: Iterable<number>): Set<number> {
|
|
121
|
+
const seen = new Set<number>()
|
|
122
|
+
const stack = [...roots]
|
|
123
|
+
const kids: number[] = []
|
|
124
|
+
while (stack.length > 0) {
|
|
125
|
+
const ip = stack.pop()!
|
|
126
|
+
if (seen.has(ip)) continue
|
|
127
|
+
seen.add(ip)
|
|
128
|
+
kids.length = 0
|
|
129
|
+
childSlots(code, ip, kids)
|
|
130
|
+
for (const c of kids) stack.push(c)
|
|
131
|
+
}
|
|
132
|
+
return seen
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* What a site hands DOWN to its children. Every op but the two that write the
|
|
137
|
+
* context passes its own label through unchanged.
|
|
138
|
+
*/
|
|
139
|
+
function transfer(code: Int32Array, ip: number, at: SiteLabel, hostCst: boolean): SiteLabel {
|
|
140
|
+
const op = code[ip]
|
|
141
|
+
if (op === OP_SCOPE || op === OP_SCOPE_CAP) {
|
|
142
|
+
const ki = code[ip + 1]!
|
|
143
|
+
const tri = ki < 0 ? TRI_NONE : ki
|
|
144
|
+
// `OP_SCOPE_CAP` is an OR with the inherited context, never a switch-off
|
|
145
|
+
// (`encode.ts:1153-1158`), so it can only ever raise `cap` to `CAP_ON`.
|
|
146
|
+
const cap = op === OP_SCOPE_CAP ? CAP_ON : at.cap
|
|
147
|
+
if (tri === at.tri && cap === at.cap) return at
|
|
148
|
+
return { tri, buf: at.buf, cap }
|
|
149
|
+
}
|
|
150
|
+
if (op === OP_NODE || op === OP_NODE_TRACK) {
|
|
151
|
+
// `ctx._cstBuf = buf` and `ctx.captureTrivia = <literal>`, both unconditional
|
|
152
|
+
// — `emit-assembly.ts`'s `OP_NODE` body opens the buffer before it descends
|
|
153
|
+
// and closes it after, whatever the host mode is.
|
|
154
|
+
const cap = ((code[ip + 3]! & 4) !== 0 || hostCst) ? CAP_ON : CAP_OFF
|
|
155
|
+
if (at.buf && cap === at.cap) return at
|
|
156
|
+
return { tri: at.tri, buf: true, cap }
|
|
157
|
+
}
|
|
158
|
+
// THE TWO BOUNDARIES clear every capture sink for their child and contribute
|
|
159
|
+
// ONE leaf for the whole match; `token()` clears `ctx.trivia` as well. So a
|
|
160
|
+
// site inside either has NO open `_cstBuf`.
|
|
161
|
+
//
|
|
162
|
+
// The label can only say `buf: false`, which this lattice reads as UNKNOWN
|
|
163
|
+
// rather than as "guaranteed absent" (see the asymmetry note above). That is
|
|
164
|
+
// the SOUND direction and it is the one that matters: `_rbBuf` and
|
|
165
|
+
// `_pushLeafBuf` dereference `ctx._cstBuf` with no null test, so a site that
|
|
166
|
+
// inherited `buf: true` through a `token()` would throw on the first rollback.
|
|
167
|
+
// Losing the elision inside a token is the price; being wrong there is not
|
|
168
|
+
// an option.
|
|
169
|
+
if (op === OP_TOKEN) {
|
|
170
|
+
if (at.tri === TRI_NONE && !at.buf) return at
|
|
171
|
+
return { tri: TRI_NONE, buf: false, cap: at.cap }
|
|
172
|
+
}
|
|
173
|
+
if (op === OP_LEAF) {
|
|
174
|
+
if (!at.buf) return at
|
|
175
|
+
return { tri: at.tri, buf: false, cap: at.cap }
|
|
176
|
+
}
|
|
177
|
+
return at
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** A site's label, or `TOP` for anything the walk did not reach. */
|
|
181
|
+
export type SiteLabels = { at: (ip: number) => SiteLabel }
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Compute a label for every site reachable from `roots`.
|
|
185
|
+
*
|
|
186
|
+
* A worklist to a fixpoint, because the program has cycles (a recursive rule is
|
|
187
|
+
* a back-edge into a site already in flight) and because a site shared by two
|
|
188
|
+
* parents must carry the MEET of what both hand it — a downward pass that simply
|
|
189
|
+
* overwrote would give a shared site whichever parent it happened to visit last,
|
|
190
|
+
* and the emitted body for that site is ONE body serving both.
|
|
191
|
+
*
|
|
192
|
+
* The meet only ever moves a label toward `TOP`, and `TOP` is absorbing, so the
|
|
193
|
+
* lattice has height three and the walk terminates.
|
|
194
|
+
*/
|
|
195
|
+
export function computeSiteLabels(
|
|
196
|
+
code: Int32Array,
|
|
197
|
+
roots: Iterable<number>,
|
|
198
|
+
hostCst: boolean,
|
|
199
|
+
): SiteLabels {
|
|
200
|
+
const labels = new Map<number, SiteLabel>()
|
|
201
|
+
const work: number[] = []
|
|
202
|
+
|
|
203
|
+
const push = (ip: number, l: SiteLabel): void => {
|
|
204
|
+
const cur = labels.get(ip)
|
|
205
|
+
if (cur === undefined) {
|
|
206
|
+
labels.set(ip, l)
|
|
207
|
+
work.push(ip)
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
const next = meet(cur, l)
|
|
211
|
+
if (next === cur) return
|
|
212
|
+
labels.set(ip, next)
|
|
213
|
+
work.push(ip)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
for (const r of roots) push(r, TOP)
|
|
217
|
+
|
|
218
|
+
const kids: number[] = []
|
|
219
|
+
while (work.length > 0) {
|
|
220
|
+
const ip = work.pop()!
|
|
221
|
+
const down = transfer(code, ip, labels.get(ip)!, hostCst)
|
|
222
|
+
kids.length = 0
|
|
223
|
+
childSlots(code, ip, kids)
|
|
224
|
+
for (const c of kids) push(c, down)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return { at: (ip: number): SiteLabel => labels.get(ip) ?? TOP }
|
|
228
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rule-map envelope both table drivers share.
|
|
3
|
+
*
|
|
4
|
+
* Everything here is about what a table ENTRY has to look like to `run()` and to
|
|
5
|
+
* the linker's public wrappers — trivia metadata, host-mode stamps, the
|
|
6
|
+
* `_lineStarts` seeding, the ambient `scanSkip` install and the failure-to-
|
|
7
|
+
* `ParseResult` conversion. None of it is recognition, and having two copies of
|
|
8
|
+
* it (one in `exec.ts`, one in `assemble.ts`) would be two places for a
|
|
9
|
+
* divergence the three-way identity sweep would then have to catch.
|
|
10
|
+
*
|
|
11
|
+
* The drivers supply only what differs: how a rule RUNS.
|
|
12
|
+
*/
|
|
13
|
+
import type { Combinator, ParseContext, ParseResult } from '../types.ts'
|
|
14
|
+
import { FUSED_HOST_ELIDED, FUSED_HOST_MODE } from '../cst/host-mode.ts'
|
|
15
|
+
import { GRAMMAR_COVERAGE_DEFINITIONS } from '../grammar-metadata.ts'
|
|
16
|
+
import { reachableIps } from './inspect.ts'
|
|
17
|
+
import { OP_NODE, OP_NODE_TRACK, OP_SCOPE } from './ops.ts'
|
|
18
|
+
import { covDefinitions, resolveTable, type ResolvedTable, type TableProgram, type TableRule } from './program.ts'
|
|
19
|
+
|
|
20
|
+
const EMPTY_FX: string[] = []
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Did something inside CUT?
|
|
24
|
+
*
|
|
25
|
+
* Read through a call so TypeScript does not narrow `_fc` to `false` from the
|
|
26
|
+
* assignment that precedes each speculative attempt — the driver mutates it and
|
|
27
|
+
* the checker cannot see that. Lives here rather than in a driver because the
|
|
28
|
+
* ENVELOPE reads it too, and a third copy would be a third place to drift.
|
|
29
|
+
*/
|
|
30
|
+
export function committed(c: ParseContext): boolean {
|
|
31
|
+
return c._fc === true
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The ENTRY result's span, line-annotated when the table tracks lines.
|
|
36
|
+
*
|
|
37
|
+
* Codegen annotates the root result the moment `ctx.lineTracking` is on — both
|
|
38
|
+
* the success span and the failure span (`_spanLines(_ctx, …)`), and so does the
|
|
39
|
+
* interpreter's `parser({ trackLines: true })` scope. The table's envelope built
|
|
40
|
+
* a bare `{ start, end }` regardless, so a tracked table parse handed back a
|
|
41
|
+
* result with no `startLine`/`endColumn` at all while paying for the tracking.
|
|
42
|
+
*
|
|
43
|
+
* The binary search over `_lineStarts` now lives in `run-support.ts`. The
|
|
44
|
+
* private copy that sat here was justified by "not worth giving the hot node
|
|
45
|
+
* piece a cross-module call"; the EMITTED engine needs the same answer, the
|
|
46
|
+
* node piece no longer calls this copy at all, and a THIRD copy is how a
|
|
47
|
+
* reported column drifts between two engines that are gated against each other
|
|
48
|
+
* precisely so that it cannot.
|
|
49
|
+
*/
|
|
50
|
+
import { spanLines } from './run-support.ts'
|
|
51
|
+
export { spanLines }
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* What a driver must provide per parse, in the order the entry needs it.
|
|
55
|
+
*
|
|
56
|
+
* `run` returns the driver's own FAIL sentinel for a failed parse. The sentinel
|
|
57
|
+
* is module-private on each side, so it is never compared here — `runRule`
|
|
58
|
+
* answers with `undefined` for failure instead, and the caller's sentinel never
|
|
59
|
+
* leaves its module.
|
|
60
|
+
*/
|
|
61
|
+
export type RuleRunner = {
|
|
62
|
+
/**
|
|
63
|
+
* Run rule `ri` and return its end position, or −1 on failure.
|
|
64
|
+
*
|
|
65
|
+
* Returning the position rather than the value keeps the driver's FAIL
|
|
66
|
+
* sentinel private; the value is fetched separately only on success.
|
|
67
|
+
*/
|
|
68
|
+
readonly runRule: (ri: number, input: string, pos: number, ctx: ParseContext) => number
|
|
69
|
+
/** The value the last successful `runRule` produced. */
|
|
70
|
+
readonly lastValue: () => unknown
|
|
71
|
+
/** The ambient `scanSkip` set for rule `ri`, if the program declares one. */
|
|
72
|
+
readonly scanSkipFor: (ri: number, ctx: ParseContext) => readonly Combinator<unknown>[] | undefined
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Wrap a driver's per-rule run into the map an artifact exports.
|
|
77
|
+
*
|
|
78
|
+
* The entries have the SAME signature as codegen rule functions, so `run()`, the
|
|
79
|
+
* linker's public wrappers and every consumer are unchanged.
|
|
80
|
+
*/
|
|
81
|
+
export function stampRuleMap(
|
|
82
|
+
prog: TableProgram,
|
|
83
|
+
d: RuleRunner,
|
|
84
|
+
artifactMetadata: Readonly<Record<symbol, unknown>> = {},
|
|
85
|
+
resolved: ResolvedTable = resolveTable(prog),
|
|
86
|
+
): Record<string, TableRule> {
|
|
87
|
+
// `run()` reads trivia metadata off the ENTRY and takes its
|
|
88
|
+
// `typeof r === 'function'` branch for compiled entries, which codegen stamps
|
|
89
|
+
// with `_meta`. A table entry is a function too, so it must be stamped or
|
|
90
|
+
// `run({ rootTrivia })` rejects a grammar that plainly has labelled trivia.
|
|
91
|
+
//
|
|
92
|
+
// `grammarTrivia` joins them for the same reason and with a sharper edge: `run()`
|
|
93
|
+
// now consumes the DOCUMENT ROOT's trailing trivia off the entry's own ambient
|
|
94
|
+
// trivia (see `ambientTriviaFromRunnable`), which a combinator entry carries on
|
|
95
|
+
// `_meta.grammarTrivia`. A table entry that did not carry it would leave the tail
|
|
96
|
+
// unconsumed and report a short `span` / a non-null `unconsumedFrom` where the
|
|
97
|
+
// interpreter reports the whole file — a three-way identity divergence that shows
|
|
98
|
+
// up as `ok: true` with bytes missing, never as an error.
|
|
99
|
+
//
|
|
100
|
+
// Read from the ENTRY ROW, not from one program-wide slot: `encodeRule` wraps each
|
|
101
|
+
// rule's entry in `OP_SCOPE <triviaSlot> <body>` iff THAT rule has ambient trivia,
|
|
102
|
+
// and a `composeLeaf` grammar's pieces do not agree (the same disagreement
|
|
103
|
+
// `scanSkipOf` exists for). The owning driver passes its resolved table, so
|
|
104
|
+
// this is the exact trivia array it already built.
|
|
105
|
+
const triviaOfRule = (ip: number): Combinator<unknown> | undefined => {
|
|
106
|
+
if (prog.code[ip] !== OP_SCOPE) return undefined
|
|
107
|
+
const slot = prog.code[ip + 1]!
|
|
108
|
+
return slot < 0 ? undefined : resolved.trivia[slot]
|
|
109
|
+
}
|
|
110
|
+
const baseMeta = prog.labels === undefined && prog.classified !== 1
|
|
111
|
+
? undefined
|
|
112
|
+
: {
|
|
113
|
+
...(prog.labels === undefined ? {} : { triviaKindLabels: prog.labels }),
|
|
114
|
+
...(prog.classified === 1 ? { rootTriviaClassified: true as const } : {}),
|
|
115
|
+
}
|
|
116
|
+
// Chosen ONCE, from table data, at rule-map construction. Not a per-parse
|
|
117
|
+
// branch on an option: a plain table never has this wrapper at all.
|
|
118
|
+
const lines = prog.lines === 1
|
|
119
|
+
const mode = prog.hostMode ?? 'ast'
|
|
120
|
+
const elided = mode === 'ast' && hasDirectBuilder(prog)
|
|
121
|
+
const coverageDefinitions = prog.cov === undefined
|
|
122
|
+
? undefined
|
|
123
|
+
: Object.freeze(covDefinitions(prog).map(Object.freeze))
|
|
124
|
+
// Metadata lives on the map's prototype FROM BIRTH. Symbol reads keep their
|
|
125
|
+
// existing contract, while Object.keys, object spread and Object.assign see
|
|
126
|
+
// only the map's own rule entries. This is the no-descriptor equivalent of the
|
|
127
|
+
// old non-enumerable stamps: no post-construction shape transition, WeakMap or
|
|
128
|
+
// generated wrapper is involved.
|
|
129
|
+
const metadataPrototype = {
|
|
130
|
+
...artifactMetadata,
|
|
131
|
+
[FUSED_HOST_MODE]: mode,
|
|
132
|
+
[FUSED_HOST_ELIDED]: elided,
|
|
133
|
+
...(coverageDefinitions === undefined
|
|
134
|
+
? {}
|
|
135
|
+
: { [GRAMMAR_COVERAGE_DEFINITIONS]: coverageDefinitions }),
|
|
136
|
+
}
|
|
137
|
+
const out = Object.create(metadataPrototype) as Record<string, TableRule>
|
|
138
|
+
const names = Object.keys(prog.rules)
|
|
139
|
+
for (let ri = 0; ri < names.length; ri++) {
|
|
140
|
+
const name = names[ri]!
|
|
141
|
+
const index = ri
|
|
142
|
+
const entryFn = (input: string, pos: number, ctx: ParseContext): ParseResult<unknown> => {
|
|
143
|
+
// The table lowering ships coverage counters in 0.47 but not codegen's
|
|
144
|
+
// fine-grained trace phases. Failing at the artifact boundary is deliberate:
|
|
145
|
+
// accepting the public sink and writing no events produces a plausible empty
|
|
146
|
+
// trace, which is indistinguishable from a genuinely unvisited grammar.
|
|
147
|
+
if (ctx._grammarTrace !== undefined) {
|
|
148
|
+
throw new TypeError(
|
|
149
|
+
'parseman: grammar tracing is not supported by table-backed parsers in 0.47; '
|
|
150
|
+
+ 'remove the _grammarTrace sink or run the combinator through the interpreter',
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
// Ambient scanSkip, which `run()` cannot install for a function entry.
|
|
154
|
+
// Chosen PER RULE, because that is what `run()` does — it reads the ENTRY
|
|
155
|
+
// rule's own `_meta.grammarScanSkip`. One program-wide set instead gave a
|
|
156
|
+
// `composeLeaf` piece's rules a skip list the interpreter never gives them.
|
|
157
|
+
const ownSkip = d.scanSkipFor(index, ctx)
|
|
158
|
+
if (ownSkip !== undefined && ctx.scanSkip === undefined) {
|
|
159
|
+
ctx.scanSkip = ownSkip as ParseContext['scanSkip']
|
|
160
|
+
}
|
|
161
|
+
ctx._fe = -1
|
|
162
|
+
ctx._fx = EMPTY_FX
|
|
163
|
+
// `_fc` is a SPECULATION-LOCAL bit cleared at the boundary so a stale
|
|
164
|
+
// `true` from a PREVIOUS parse on a reused `ctx` cannot read as "the cut
|
|
165
|
+
// fired".
|
|
166
|
+
ctx._fc = false
|
|
167
|
+
if (lines && ctx._lineStarts === undefined) { ctx._lineStarts = [0]; ctx._lineScannedTo = 0 }
|
|
168
|
+
const end = d.runRule(index, input, pos, ctx)
|
|
169
|
+
if (end < 0) {
|
|
170
|
+
const fe = ctx._fe
|
|
171
|
+
const at = fe === undefined || fe < 0 ? pos : fe
|
|
172
|
+
const fspan = lines ? spanLines(ctx, at, at) : { start: at, end: at }
|
|
173
|
+
// `committed` IS PART OF THE FAILURE ENVELOPE, not a diagnostic extra.
|
|
174
|
+
// The interpreter carries it on the result (`{ ok: false, …, committed: true }`)
|
|
175
|
+
// and codegen re-derives it from the same bit this driver uses
|
|
176
|
+
// (`codegen.ts`: `...(_ctx._fc ? { committed: true } : {})`). The table
|
|
177
|
+
// dropped it, so a table entry embedded as a CHILD of another parser — the
|
|
178
|
+
// one place the field is read rather than merely reported — lost the cut.
|
|
179
|
+
return committed(ctx)
|
|
180
|
+
? { ok: false, expected: (ctx._fx ?? EMPTY_FX) as string[], span: fspan, committed: true }
|
|
181
|
+
: { ok: false, expected: (ctx._fx ?? EMPTY_FX) as string[], span: fspan }
|
|
182
|
+
}
|
|
183
|
+
return { ok: true, value: d.lastValue(), span: lines ? spanLines(ctx, pos, end) : { start: pos, end } }
|
|
184
|
+
}
|
|
185
|
+
const ownTrivia = triviaOfRule(prog.rules[name]!)
|
|
186
|
+
const meta = ownTrivia === undefined
|
|
187
|
+
? baseMeta
|
|
188
|
+
: { ...baseMeta, grammarTrivia: ownTrivia }
|
|
189
|
+
// A standalone entry has no registry from which `run()` could recover host
|
|
190
|
+
// mode, so keep the two ordinary symbol fields on the function itself. Every
|
|
191
|
+
// entry receives them in the same construction-time order.
|
|
192
|
+
out[name] = Object.assign(entryFn, {
|
|
193
|
+
...(meta === undefined ? {} : { _meta: meta }),
|
|
194
|
+
[FUSED_HOST_MODE]: mode,
|
|
195
|
+
[FUSED_HOST_ELIDED]: elided,
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
// STAMP THE HOST MODE. `run()` reads it off the entry through `FUSED_HOST_MODE`
|
|
199
|
+
// and `assertHostModeCompatible` throws when a 'cst' artifact runs without a
|
|
200
|
+
// CST host. Encoding with `hostMode: 'cst'` set the capture flags but nothing
|
|
201
|
+
// stamped the mode, so such a table returned the grammar's own AST objects with
|
|
202
|
+
// `ok: true` while paying full CST capture.
|
|
203
|
+
// WHAT `FUSED_HOST_ELIDED` MEANS is "a DIRECT BUILDER's positioned-CST branch was
|
|
204
|
+
// dropped" — it is what makes `'ast' artifact + CST host` an error rather than a
|
|
205
|
+
// preference. `mode === 'ast'` alone over-reports it: an all-STRUCTURAL grammar has
|
|
206
|
+
// no direct builder, so no branch was dropped and it stays usable with either host
|
|
207
|
+
// (the `node(parser)` contract). A node row carries `-1` in its build slot when it
|
|
208
|
+
// has no direct builder, so the program answers this itself — and it MUST agree with
|
|
209
|
+
// the macro's emitted stamp, or re-stamping the same map throws
|
|
210
|
+
// "Cannot redefine property".
|
|
211
|
+
return out
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Does any REACHABLE node row own a direct builder?
|
|
216
|
+
*
|
|
217
|
+
* Reachability, not a scan for the opcode's numeric value: operands are ordinary
|
|
218
|
+
* integers and collide with opcodes, so counting words reports confident nonsense
|
|
219
|
+
* (`inspect.ts` states the same thing about `opHistogram`). One linear pass at load,
|
|
220
|
+
* beside the assembly pass that already visits every row.
|
|
221
|
+
*/
|
|
222
|
+
function hasDirectBuilder(prog: TableProgram): boolean {
|
|
223
|
+
const code = prog.code
|
|
224
|
+
for (const ip of reachableIps(prog)) {
|
|
225
|
+
const op = code[ip]
|
|
226
|
+
if ((op === OP_NODE || op === OP_NODE_TRACK) && code[ip + 1] !== -1) return true
|
|
227
|
+
}
|
|
228
|
+
return false
|
|
229
|
+
}
|