parseman 0.56.0 → 0.58.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/dist/analysis/diagnostics.js +2232 -1088
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +2247 -1103
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/combinators/node.d.ts +1 -1
- package/dist/compiler/build-arity.d.ts +1 -1
- package/dist/compiler/build-arity.d.ts.map +1 -1
- package/dist/compiler/token-alphabet.d.ts +11 -1
- package/dist/compiler/token-alphabet.d.ts.map +1 -1
- package/dist/compiler/token-capability.js +69 -16
- package/dist/compiler/token-capability.js.map +2 -2
- package/dist/cst/capture-buffer.d.ts +6 -0
- package/dist/cst/capture-buffer.d.ts.map +1 -1
- package/dist/cst/trivia-kinds.d.ts +9 -1
- package/dist/cst/trivia-kinds.d.ts.map +1 -1
- package/dist/index.js +1446 -236
- package/dist/index.js.map +3 -3
- package/dist/language-service/index.js +1 -0
- package/dist/language-service/index.js.map +1 -1
- package/dist/parse-context.d.ts.map +1 -1
- package/dist/plugin/entry-supercompiler.d.ts +1 -1
- package/dist/plugin/entry-supercompiler.d.ts.map +1 -1
- package/dist/plugin/index.d.ts +51 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +2126 -380
- package/dist/plugin/index.js.map +3 -3
- package/dist/plugin/ir-eval.d.ts +2 -0
- package/dist/plugin/ir-eval.d.ts.map +1 -1
- package/dist/regex/classes.d.ts.map +1 -1
- package/dist/run/index.js +1 -0
- package/dist/run/index.js.map +1 -1
- package/dist/table/assemble.d.ts +9 -0
- package/dist/table/assemble.d.ts.map +1 -1
- package/dist/table/child-slots.d.ts +4 -6
- package/dist/table/child-slots.d.ts.map +1 -1
- package/dist/table/compile-rule-map.d.ts +14 -2
- package/dist/table/compile-rule-map.d.ts.map +1 -1
- package/dist/table/emit-assembly.d.ts +17 -2
- package/dist/table/emit-assembly.d.ts.map +1 -1
- package/dist/table/emit.d.ts +5 -1
- package/dist/table/emit.d.ts.map +1 -1
- package/dist/table/encode.d.ts.map +1 -1
- package/dist/table/exec-baseline.d.ts.map +1 -1
- package/dist/table/exec.d.ts.map +1 -1
- package/dist/table/factor-choice.d.ts +42 -0
- package/dist/table/factor-choice.d.ts.map +1 -0
- package/dist/table/fail-clean.d.ts +3 -0
- package/dist/table/fail-clean.d.ts.map +1 -0
- package/dist/table/hold-gap.d.ts +8 -0
- package/dist/table/hold-gap.d.ts.map +1 -0
- package/dist/table/index.js +1798 -316
- package/dist/table/index.js.map +3 -3
- package/dist/table/lex-program.d.ts +7 -3
- package/dist/table/lex-program.d.ts.map +1 -1
- package/dist/table/ops.d.ts +17 -6
- package/dist/table/ops.d.ts.map +1 -1
- package/dist/table/program.d.ts +13 -0
- package/dist/table/program.d.ts.map +1 -1
- package/dist/table/run-support.d.ts +19 -0
- package/dist/table/run-support.d.ts.map +1 -1
- package/dist/table/scalar-terminal.d.ts +2 -0
- package/dist/table/scalar-terminal.d.ts.map +1 -1
- package/dist/table/select-hot.d.ts +6 -0
- package/dist/table/select-hot.d.ts.map +1 -0
- package/dist/table/site-labels.d.ts +66 -0
- package/dist/table/site-labels.d.ts.map +1 -1
- package/dist/table/trivia-gap.d.ts +31 -0
- package/dist/table/trivia-gap.d.ts.map +1 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +4 -1
- package/src/combinators/dispatch.ts +12 -3
- package/src/combinators/node.ts +6 -13
- package/src/combinators/trivia-skip.ts +44 -1
- package/src/compiler/build-arity.ts +3 -3
- package/src/compiler/token-alphabet.ts +57 -17
- package/src/cst/capture-buffer.ts +25 -0
- package/src/cst/trivia-kinds.ts +12 -1
- package/src/parse-context.ts +1 -0
- package/src/plugin/entry-supercompiler.ts +8 -9
- package/src/plugin/index.ts +362 -42
- package/src/plugin/ir-eval.ts +7 -1
- package/src/regex/classes.ts +3 -0
- package/src/table/assemble.ts +371 -83
- package/src/table/child-slots.ts +6 -8
- package/src/table/compile-rule-map.ts +64 -15
- package/src/table/emit-assembly.ts +244 -42
- package/src/table/emit.ts +11 -8
- package/src/table/encode.ts +149 -35
- package/src/table/exec-baseline.ts +1 -3
- package/src/table/exec.ts +34 -14
- package/src/table/factor-choice.ts +173 -0
- package/src/table/fail-clean.ts +130 -0
- package/src/table/hold-gap.ts +68 -0
- package/src/table/lex-program.ts +197 -43
- package/src/table/ops.ts +18 -7
- package/src/table/program.ts +24 -0
- package/src/table/run-support.ts +35 -0
- package/src/table/scalar-terminal.ts +8 -0
- package/src/table/select-hot.ts +70 -0
- package/src/table/site-labels.ts +135 -4
- package/src/table/trivia-gap.ts +109 -0
- package/src/types.ts +18 -0
- package/src/version.ts +1 -1
package/src/table/assemble.ts
CHANGED
|
@@ -64,18 +64,19 @@
|
|
|
64
64
|
* the three-way identity sweep gates this against it.
|
|
65
65
|
*/
|
|
66
66
|
import { rebuildLineScan } from './line-scan.ts'
|
|
67
|
-
import {
|
|
67
|
+
import { assemblyExtraIps } from './program.ts'
|
|
68
68
|
import type { Combinator, FieldMap, FirstSet, ParseContext, ParseResult, ParserDef } from '../types.ts'
|
|
69
69
|
import { balanced, scanTo } from '../combinators/scanTo.ts'
|
|
70
70
|
import { buildFieldMap } from '../compiler/fields.ts'
|
|
71
71
|
import { FIELD_LEADING_TRIVIA, FIELD_TRAILING_TRIVIA, LEAD, triviaFieldCapture } from '../combinators/field-trivia.ts'
|
|
72
|
+
import { skipDeferredTrivia } from './trivia-gap.ts'
|
|
72
73
|
import { asciiFoldKey } from '../combinators/dispatch.ts'
|
|
73
74
|
import { projectChild, unwrapChild } from '../combinators/node.ts'
|
|
74
75
|
import { cstOutputHost } from '../compiler/build-arity.ts'
|
|
75
76
|
import { consumeTrivia } from '../combinators/trivia-skip.ts'
|
|
76
77
|
import {
|
|
77
|
-
advanceTrivia, commitTriviaScan, needsDeferredTriviaCommit, rollbackScannedTriviaAt,
|
|
78
|
-
|
|
78
|
+
advanceTrivia, commitTriviaScan, needsDeferredTriviaCommit, rollbackScannedTriviaAt, rollbackTriviaAt,
|
|
79
|
+
scanTrivia, scanTriviaCompact, skipTriviaScanned, type FastTriviaScanner,
|
|
79
80
|
} from '../combinators/trivia-skip.ts'
|
|
80
81
|
import {
|
|
81
82
|
cstCaptureActive, cstLeavesLen, cstTlLen,
|
|
@@ -89,7 +90,7 @@ import {
|
|
|
89
90
|
OP_LIVE,
|
|
90
91
|
OP_FIELD, OP_DISPATCH, OP_ROUTED, OP_LIT_CI, OP_LIT_CI_TRACK, OP_TOKEN, OP_WITHCTX, OP_GUARD, OP_ATTEMPT, OP_LABEL,
|
|
91
92
|
OP_COV,
|
|
92
|
-
OP_ADJ, OP_GREEDY, OP_REJECT, OP_ARMGATE,
|
|
93
|
+
OP_ADJ, OP_GREEDY, OP_REJECT, OP_ARMGATE, OP_RGATE,
|
|
93
94
|
OP_LEX_BODY, OP_LEX_PROGRAM,
|
|
94
95
|
} from './ops.ts'
|
|
95
96
|
import { adjacencyHolds, adjacencyMisuse } from '../combinators/adjacency.ts'
|
|
@@ -106,7 +107,9 @@ import {
|
|
|
106
107
|
EMITTED_PARAMS, Unemittable, emitAssemblySource, rebuildPools,
|
|
107
108
|
type EmittedAssembly, type EmittedFactory,
|
|
108
109
|
} from './emit-assembly.ts'
|
|
109
|
-
import { lead, rawEntry, spanLines } from './run-support.ts'
|
|
110
|
+
import { collapseEmpty, lead, rawEntry, spanLines } from './run-support.ts'
|
|
111
|
+
import { holdsGap, rowNullable } from './hold-gap.ts'
|
|
112
|
+
import { failCleanAnalysis } from './fail-clean.ts'
|
|
110
113
|
/**
|
|
111
114
|
* THE COMPLETIONS PROBE, at the terminal fail sites and nowhere else.
|
|
112
115
|
*
|
|
@@ -122,18 +125,18 @@ import { lead, rawEntry, spanLines } from './run-support.ts'
|
|
|
122
125
|
* property read per terminal miss — the same price codegen pays.
|
|
123
126
|
*/
|
|
124
127
|
import {
|
|
125
|
-
classHas, decodeClassSpec, expandCompact, resolveTable,
|
|
128
|
+
classHas, decodeClassSpec, encodeClassSpec, expandCompact, ownTableProgram, resolveClass, resolveTable,
|
|
126
129
|
validateDispatchSpec,
|
|
127
130
|
type CompactProgram, type ResolvedClass, type ResolvedTable,
|
|
128
131
|
type SubtreeRef, type TableProgram, type TableRule,
|
|
129
132
|
} from './program.ts'
|
|
130
133
|
import { stampRuleMap } from './stamp.ts'
|
|
131
|
-
import { computeSiteLabels, reachableSites } from './site-labels.ts'
|
|
134
|
+
import { computeSiteLabels, gatedPassCollapse, passCollapse, reachableSites, scanHandoffNeeded, scopeIsIdentity, triviaLeadRanges } from './site-labels.ts'
|
|
132
135
|
import { refuseUnclassifiedRootScope } from '../cst/root-trivia-scope.ts'
|
|
133
136
|
import { captureError, firstSetSentinel, matchesAt, orSentinel, recoverScan } from '../recovery/scan.ts'
|
|
134
137
|
import {
|
|
135
138
|
largeChoiceLiteralPretest, leadingScalarSequence, leadingScalarTerminal, makeScalarRecognizer, scalarTerminalNodeChild,
|
|
136
|
-
scalarTerminalNotChild, type ScalarRecognizer,
|
|
139
|
+
scalarTerminalNotChild, scalarTerminalPeekChild, type ScalarRecognizer,
|
|
137
140
|
} from './scalar-terminal.ts'
|
|
138
141
|
|
|
139
142
|
/**
|
|
@@ -240,13 +243,14 @@ type GeneralChoiceBlock = (
|
|
|
240
243
|
type ExclusiveChoiceBlock = (arm: number, input: string, pos: number, ctx: ParseContext) => unknown
|
|
241
244
|
type DispatchMatcher = (key: string) => boolean
|
|
242
245
|
type DispatchMatcherBlock = (key: string) => number | undefined
|
|
246
|
+
/** The selector's rollback mark rides as the eight scalars a choice block takes. */
|
|
243
247
|
type DispatchBranch = (
|
|
244
|
-
input: string, pos: number, selEnd: number, key: string,
|
|
245
|
-
|
|
248
|
+
input: string, pos: number, selEnd: number, key: string, ctx: ParseContext,
|
|
249
|
+
need: boolean, mRaw: number, mTl: number, mLv: number, mFl: number, mEr: number, mLog: number, mRoot: number,
|
|
246
250
|
) => unknown
|
|
247
251
|
type DispatchArmBlock = (
|
|
248
|
-
arm: number, input: string, pos: number, selEnd: number, key: string,
|
|
249
|
-
|
|
252
|
+
arm: number, input: string, pos: number, selEnd: number, key: string, ctx: ParseContext,
|
|
253
|
+
need: boolean, mRaw: number, mTl: number, mLv: number, mFl: number, mEr: number, mLog: number, mRoot: number,
|
|
250
254
|
) => unknown
|
|
251
255
|
|
|
252
256
|
/**
|
|
@@ -440,6 +444,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
440
444
|
* reference.
|
|
441
445
|
*/
|
|
442
446
|
const REC = prog.rec === 1 && cfg.tolerant
|
|
447
|
+
/** A completions-probe assembly: lookaheads restore `_probe.best` around their child. */
|
|
448
|
+
const PROBE = cfg.probe
|
|
443
449
|
/**
|
|
444
450
|
* The sync sentinel for a char-class index, built ONCE at assembly.
|
|
445
451
|
*
|
|
@@ -477,6 +483,15 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
477
483
|
// Value/CST/failure/cursor effects stay in those consumers.
|
|
478
484
|
const scalarRecognizers: Array<ScalarRecognizer | undefined> = []
|
|
479
485
|
const rawScalarSpecs = new Set<number>()
|
|
486
|
+
/**
|
|
487
|
+
* The class of an `optional`/`not`/`peek` child's first-character gate
|
|
488
|
+
* (`encode.ts` `lookaheadGate`), tested before the wrapper takes its mark.
|
|
489
|
+
* `null` where there is none, or where a probe or tolerant parse forwards
|
|
490
|
+
* through the gate (`OP_GATE` below).
|
|
491
|
+
*/
|
|
492
|
+
function lookaheadClass(child: number): ResolvedClass | null {
|
|
493
|
+
return code[child] === OP_GATE && !cfg.tolerant && !cfg.probe ? cc[code[child + 1]!]! : null
|
|
494
|
+
}
|
|
480
495
|
function scalarFor(child: number): ScalarRecognizer | undefined {
|
|
481
496
|
const spec = code[child + 1]!
|
|
482
497
|
let recognize = scalarRecognizers[spec]
|
|
@@ -487,9 +502,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
487
502
|
return recognize
|
|
488
503
|
}
|
|
489
504
|
|
|
490
|
-
const labelExtraIps
|
|
491
|
-
for (const s of prog.scans ?? []) for (const r of scanRefs(s)) labelExtraIps.push(r[0])
|
|
492
|
-
for (const set of prog.scanSkip ?? []) for (const r of set) labelExtraIps.push(r[0])
|
|
505
|
+
const labelExtraIps = assemblyExtraIps(prog)
|
|
493
506
|
const closureLabels = computeSiteLabels(
|
|
494
507
|
code, [...Object.values(prog.rules), ...labelExtraIps], hostCst,
|
|
495
508
|
)
|
|
@@ -504,8 +517,29 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
504
517
|
*/
|
|
505
518
|
let SCAN: FastTriviaScanner | null = null
|
|
506
519
|
|
|
520
|
+
/**
|
|
521
|
+
* A SELECTIVE build-time assembly: emitted bodies for its selected sites, this
|
|
522
|
+
* engine for the rest. `link` hands out its exported sites, wrapped so the
|
|
523
|
+
* callee sees this engine's installed scanner — the one slot both engines keep
|
|
524
|
+
* separately (the end cell `EC` is already shared).
|
|
525
|
+
*/
|
|
526
|
+
let HYBRID: EmittedAssembly | undefined
|
|
527
|
+
function hybridPiece(piece: Piece): Piece {
|
|
528
|
+
const setScan = HYBRID!.scan!
|
|
529
|
+
return (input, pos, ctx) => {
|
|
530
|
+
const saved = setScan(SCAN)
|
|
531
|
+
const value = piece(input, pos, ctx)
|
|
532
|
+
setScan(saved)
|
|
533
|
+
return value
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
507
537
|
const memo = new Map<number, Piece>()
|
|
508
538
|
const reached = new Set<number>()
|
|
539
|
+
/** Row nullability for `holdsGap`, memoised for this assembly. */
|
|
540
|
+
const NULLABLE = new Map<number, boolean>()
|
|
541
|
+
/** A child whose failure writes nothing needs no rollback mark (`fail-clean.ts`). */
|
|
542
|
+
const CLEAN = REC ? (): boolean => false : failCleanAnalysis(code)
|
|
509
543
|
|
|
510
544
|
function pushLeaf(ctx: ParseContext, value: string, s: number, e: number): void {
|
|
511
545
|
const lf: Leaf = { _tag: 'leaf', value, span: { start: s, end: e } }
|
|
@@ -739,6 +773,36 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
739
773
|
}
|
|
740
774
|
}
|
|
741
775
|
|
|
776
|
+
/**
|
|
777
|
+
* A collapse over one node is that node — `run-support.ts` `collapseEmpty` has
|
|
778
|
+
* the contract, and the one case the node's value cannot stand for itself.
|
|
779
|
+
*/
|
|
780
|
+
function passCollapseNode(child: Piece, build: NodeBuilder, readsRaw: boolean): Piece {
|
|
781
|
+
return (input, pos, ctx) => {
|
|
782
|
+
const v = child(input, pos, ctx)
|
|
783
|
+
if (v !== undefined) return v
|
|
784
|
+
return collapseEmpty(ctx, build, input, pos, EC.e, readsRaw)
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* A collapse whose body is `optional`/`not` terms and then one node: where the
|
|
790
|
+
* character at `pos` opens none of those terms and no trivia, the body is that
|
|
791
|
+
* node (`site-labels.ts` `gatedPassCollapse`), so it runs as a pass-through
|
|
792
|
+
* collapse; anywhere else the node keeps its frame.
|
|
793
|
+
*/
|
|
794
|
+
function gatedPassCollapseNode(
|
|
795
|
+
blockers: readonly ResolvedClass[], last: Piece, classic: Piece, build: NodeBuilder, readsRaw: boolean,
|
|
796
|
+
): Piece {
|
|
797
|
+
return (input, pos, ctx) => {
|
|
798
|
+
const c = lead(input, pos)
|
|
799
|
+
for (let i = 0; i < blockers.length; i++) if (classHas(blockers[i]!, c)) return classic(input, pos, ctx)
|
|
800
|
+
const v = last(input, pos, ctx)
|
|
801
|
+
if (v !== undefined) return v
|
|
802
|
+
return collapseEmpty(ctx, build, input, pos, EC.e, readsRaw)
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
742
806
|
function plainProjectNode(child: Piece, projection: number, type: string): Piece {
|
|
743
807
|
return (input, pos, ctx) => {
|
|
744
808
|
const sCh = ctx._cstChildren
|
|
@@ -906,9 +970,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
906
970
|
// Appended rather than folded into the test above so the non-capturing path
|
|
907
971
|
// keeps the exact branch it had.
|
|
908
972
|
if (s !== null) return skipTriviaScanned(s, input, cur, ctx)
|
|
909
|
-
if (needsDeferredTriviaCommit(ctx))
|
|
910
|
-
return commitTriviaScan(scanTriviaCompact(input, cur, ctx))
|
|
911
|
-
}
|
|
973
|
+
if (needsDeferredTriviaCommit(ctx)) return skipDeferredTrivia(input, cur, ctx)
|
|
912
974
|
return advanceTrivia(input, cur, ctx)
|
|
913
975
|
}
|
|
914
976
|
|
|
@@ -1159,7 +1221,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1159
1221
|
if (next !== undefined) {
|
|
1160
1222
|
return next(input, pos, ctx, bits, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev, best)
|
|
1161
1223
|
}
|
|
1162
|
-
|
|
1224
|
+
// No arm entered (`prev` 0, `acc` unset): the catch-up would rebuild
|
|
1225
|
+
// `choiceFx`, the arms' flatMap, set by set. It IS the answer.
|
|
1226
|
+
if (best === pos && prev !== 0) acc = through(total, prev, acc)
|
|
1163
1227
|
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1164
1228
|
return FAIL
|
|
1165
1229
|
}
|
|
@@ -1228,7 +1292,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1228
1292
|
if (next !== undefined) {
|
|
1229
1293
|
return next(input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev, best)
|
|
1230
1294
|
}
|
|
1231
|
-
|
|
1295
|
+
// No arm entered (`prev` 0, `acc` unset): the catch-up would rebuild
|
|
1296
|
+
// `choiceFx`, the arms' flatMap, set by set. It IS the answer.
|
|
1297
|
+
if (best === pos && prev !== 0) acc = through(total, prev, acc)
|
|
1232
1298
|
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1233
1299
|
return FAIL
|
|
1234
1300
|
}
|
|
@@ -1264,21 +1330,30 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1264
1330
|
|
|
1265
1331
|
function dispatchBranch(child: Piece, usesRouted: boolean): DispatchBranch {
|
|
1266
1332
|
if (!usesRouted) {
|
|
1267
|
-
|
|
1268
|
-
|
|
1333
|
+
// The arm runs after the selector's match, so it marks where it starts.
|
|
1334
|
+
return (input, _pos, selEnd, key, ctx) => {
|
|
1335
|
+
const need = markCst(ctx)
|
|
1336
|
+
const mRaw = MRAW
|
|
1337
|
+
const mTl = MTL
|
|
1338
|
+
const mLv = MLV
|
|
1339
|
+
const mFl = need ? ctx._fields?.length ?? 0 : 0
|
|
1340
|
+
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
1341
|
+
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
1342
|
+
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
1269
1343
|
const v = child(input, selEnd, ctx)
|
|
1270
1344
|
if (v === FAIL) {
|
|
1271
|
-
|
|
1345
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1272
1346
|
ctx._fc = true
|
|
1273
1347
|
return FAIL
|
|
1274
1348
|
}
|
|
1275
1349
|
return [key, v]
|
|
1276
1350
|
}
|
|
1277
1351
|
}
|
|
1278
|
-
|
|
1352
|
+
// A routed arm re-reads the selector's match from `pos`: undo the selector,
|
|
1353
|
+
// which leaves every sink at the selector's mark, so that mark is the arm's too.
|
|
1354
|
+
return (input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot) => {
|
|
1279
1355
|
const savedRouted = ctx._routed
|
|
1280
|
-
|
|
1281
|
-
const mark = saveTriviaMark(ctx)
|
|
1356
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1282
1357
|
ctx._routed = { value: key, span: { start: pos, end: selEnd } }
|
|
1283
1358
|
let v: unknown
|
|
1284
1359
|
try {
|
|
@@ -1287,7 +1362,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1287
1362
|
ctx._routed = savedRouted
|
|
1288
1363
|
}
|
|
1289
1364
|
if (v === FAIL) {
|
|
1290
|
-
|
|
1365
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1291
1366
|
ctx._fc = true
|
|
1292
1367
|
return FAIL
|
|
1293
1368
|
}
|
|
@@ -1300,12 +1375,14 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1300
1375
|
a0: DispatchBranch, a1: DispatchBranch, a2: DispatchBranch, a3: DispatchBranch,
|
|
1301
1376
|
next: DispatchArmBlock | undefined,
|
|
1302
1377
|
): DispatchArmBlock {
|
|
1303
|
-
return (arm, input, pos, selEnd, key,
|
|
1304
|
-
if (arm === start) return a0(input, pos, selEnd, key,
|
|
1305
|
-
if (arm === start + 1) return a1(input, pos, selEnd, key,
|
|
1306
|
-
if (arm === start + 2) return a2(input, pos, selEnd, key,
|
|
1307
|
-
if (arm === start + 3) return a3(input, pos, selEnd, key,
|
|
1308
|
-
return next === undefined
|
|
1378
|
+
return (arm, input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot) => {
|
|
1379
|
+
if (arm === start) return a0(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1380
|
+
if (arm === start + 1) return a1(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1381
|
+
if (arm === start + 2) return a2(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1382
|
+
if (arm === start + 3) return a3(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1383
|
+
return next === undefined
|
|
1384
|
+
? FAIL
|
|
1385
|
+
: next(arm, input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1309
1386
|
}
|
|
1310
1387
|
}
|
|
1311
1388
|
|
|
@@ -1339,22 +1416,29 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1339
1416
|
if (done !== undefined) return done
|
|
1340
1417
|
const flight = inFlight.get(ip)
|
|
1341
1418
|
if (flight !== undefined) return flight.fwd
|
|
1419
|
+
const selected = HYBRID?.byIp[ip]
|
|
1420
|
+
if (selected !== undefined) {
|
|
1421
|
+
// The scanner handoff only where the emitted body reads the slot
|
|
1422
|
+
// (`scanHandoffNeeded`); elsewhere the body runs on its labels' scanners.
|
|
1423
|
+
const piece = scanHandoffNeeded(code, ip, closureLabels) ? hybridPiece(selected as Piece) : selected as Piece
|
|
1424
|
+
memo.set(ip, piece)
|
|
1425
|
+
reached.add(ip)
|
|
1426
|
+
return piece
|
|
1427
|
+
}
|
|
1342
1428
|
|
|
1343
1429
|
let target: Piece | undefined
|
|
1344
1430
|
const fwd: Piece = (input, pos, ctx) => target!(input, pos, ctx)
|
|
1345
1431
|
const holder = { fwd, set: (p: Piece) => { target = p } }
|
|
1346
1432
|
inFlight.set(ip, holder)
|
|
1347
1433
|
|
|
1348
|
-
|
|
1349
|
-
//
|
|
1350
|
-
//
|
|
1351
|
-
//
|
|
1434
|
+
// Match the emitted assembly's label-proven scope alias (`scopeIsIdentity`):
|
|
1435
|
+
// rule entries in a single ambient trivia scope, and no-trivia scopes inside
|
|
1436
|
+
// a token, otherwise reinstall and restore the exact same trivia, labels and
|
|
1437
|
+
// scanner on every call. Install the forwarding
|
|
1352
1438
|
// holder first so a recursive child can still link back through this site.
|
|
1353
|
-
const piece = (
|
|
1354
|
-
&& code[ip + 1]! >= 0
|
|
1355
|
-
&& closureLabels.at(ip).tri === code[ip + 1]!
|
|
1439
|
+
const piece = scopeIsIdentity(code, ip, closureLabels.at(ip))
|
|
1356
1440
|
? link(code[ip + 2]!)
|
|
1357
|
-
: lower(ip)
|
|
1441
|
+
: countedPiece(lookaheadLedNode(ip) ?? lower(ip), ip)
|
|
1358
1442
|
|
|
1359
1443
|
inFlight.delete(ip)
|
|
1360
1444
|
holder.set(piece)
|
|
@@ -1363,6 +1447,48 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1363
1447
|
return piece
|
|
1364
1448
|
}
|
|
1365
1449
|
|
|
1450
|
+
/**
|
|
1451
|
+
* A NODE WHOSE BODY OPENS WITH A LOOKAHEAD opens its frame after it. The body
|
|
1452
|
+
* is a sequence whose first term is `not`/`peek` of a direct terminal: it is
|
|
1453
|
+
* decided at `pos` without writing anything, and a node that fails there would
|
|
1454
|
+
* only open its frame to drop it. So the recognizer runs first, and the node
|
|
1455
|
+
* runs on success with that term already decided (`FIRST_DECIDED`). The
|
|
1456
|
+
* recognizer runs once on either path; the failure is the term's own.
|
|
1457
|
+
*/
|
|
1458
|
+
let FIRST_DECIDED: Piece | undefined
|
|
1459
|
+
let NODE_BODY: Piece | undefined
|
|
1460
|
+
const DECIDED: Piece = (_input, pos) => { EC.e = pos; return null }
|
|
1461
|
+
function lookaheadLedNode(ip: number): Piece | undefined {
|
|
1462
|
+
const op = code[ip]
|
|
1463
|
+
if ((op !== OP_NODE && op !== OP_NODE_TRACK) || hostCst || REC || cfg.probe || cfg.coverage || cfg.trackLines) return undefined
|
|
1464
|
+
if (passCollapse(code, ip, hostCst) || gatedPassCollapse(code, ip, hostCst) !== undefined) return undefined
|
|
1465
|
+
const body = code[ip + 2]!
|
|
1466
|
+
const bop = code[body]
|
|
1467
|
+
if (bop !== OP_SEQ && bop !== OP_SEQV && bop !== OP_SEQX) return undefined
|
|
1468
|
+
const base = bop === OP_SEQX ? body + 3 : body + 2
|
|
1469
|
+
if (code[bop === OP_SEQX ? body + 2 : body + 1]! < 2) return undefined
|
|
1470
|
+
const first = code[base]!
|
|
1471
|
+
const negative = code[first] === OP_NOT
|
|
1472
|
+
const terminal = negative ? scalarTerminalNotChild(code, first)
|
|
1473
|
+
: code[first] === OP_PEEK ? scalarTerminalPeekChild(code, first) : -1
|
|
1474
|
+
if (terminal < 0) return undefined
|
|
1475
|
+
const recognize = scalarFor(terminal)!
|
|
1476
|
+
const xf = fx[code[first + 2]!] as string[]
|
|
1477
|
+
FIRST_DECIDED = DECIDED
|
|
1478
|
+
NODE_BODY = lower(body)
|
|
1479
|
+
const node = lower(ip)
|
|
1480
|
+
NODE_BODY = undefined
|
|
1481
|
+
return negative
|
|
1482
|
+
? (input, pos, ctx) => {
|
|
1483
|
+
if (recognize(input, pos) >= 0) { ctx._fe = pos; ctx._fx = xf; EC.e = pos; return FAIL }
|
|
1484
|
+
return node(input, pos, ctx)
|
|
1485
|
+
}
|
|
1486
|
+
: (input, pos, ctx) => {
|
|
1487
|
+
if (recognize(input, pos) < 0) { ctx._fe = pos; ctx._fx = xf; return FAIL }
|
|
1488
|
+
return node(input, pos, ctx)
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1366
1492
|
function lower(ip: number): Piece {
|
|
1367
1493
|
const op = code[ip]
|
|
1368
1494
|
switch (op) {
|
|
@@ -1996,7 +2122,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1996
2122
|
}
|
|
1997
2123
|
const child = link(code[ip + 1]!)
|
|
1998
2124
|
const xf = fx[code[ip + 2]!] as string[]
|
|
2125
|
+
const gate = lookaheadClass(code[ip + 1]!)
|
|
1999
2126
|
return (input, pos, ctx) => {
|
|
2127
|
+
if (gate !== null && !classHas(gate, lead(input, pos))) { EC.e = pos; return null }
|
|
2000
2128
|
const need = markCst(ctx)
|
|
2001
2129
|
const mRaw = MRAW
|
|
2002
2130
|
const mTl = MTL
|
|
@@ -2005,8 +2133,12 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2005
2133
|
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
2006
2134
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
2007
2135
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
2136
|
+
// A completions probe sees nothing raised inside a lookahead
|
|
2137
|
+
// (`not.ts`, `rollbackLookahead`): those tokens are not reachable here.
|
|
2138
|
+
const probeBest = PROBE ? ctx._probe?.best : undefined
|
|
2008
2139
|
const v = child(input, pos, ctx)
|
|
2009
2140
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2141
|
+
if (PROBE && ctx._probe !== undefined) ctx._probe.best = probeBest ?? null
|
|
2010
2142
|
if (v === FAIL) { EC.e = pos; return null }
|
|
2011
2143
|
// `not.ts:50` — the ASSERTION's own set, at the assertion's position.
|
|
2012
2144
|
ctx._fe = pos
|
|
@@ -2016,9 +2148,27 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2016
2148
|
}
|
|
2017
2149
|
|
|
2018
2150
|
case OP_PEEK: {
|
|
2151
|
+
// A direct terminal decides the lookahead by its match alone: no mark,
|
|
2152
|
+
// no leaf to capture and roll back (`scalarTerminalPeekChild`).
|
|
2153
|
+
const scalarChild = scalarTerminalPeekChild(code, ip)
|
|
2154
|
+
if (scalarChild >= 0) {
|
|
2155
|
+
const recognize = scalarFor(scalarChild)!
|
|
2156
|
+
const xf = fx[code[ip + 2]!] as string[]
|
|
2157
|
+
return (input, pos, ctx) => {
|
|
2158
|
+
if (recognize(input, pos) < 0) {
|
|
2159
|
+
ctx._fe = pos
|
|
2160
|
+
ctx._fx = xf
|
|
2161
|
+
return FAIL
|
|
2162
|
+
}
|
|
2163
|
+
EC.e = pos
|
|
2164
|
+
return null
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2019
2167
|
const child = link(code[ip + 1]!)
|
|
2020
2168
|
const xf = fx[code[ip + 2]!] as string[]
|
|
2169
|
+
const gate = lookaheadClass(code[ip + 1]!)
|
|
2021
2170
|
return (input, pos, ctx) => {
|
|
2171
|
+
if (gate !== null && !classHas(gate, lead(input, pos))) { ctx._fe = pos; ctx._fx = xf; return FAIL }
|
|
2022
2172
|
const need = markCst(ctx)
|
|
2023
2173
|
const mRaw = MRAW
|
|
2024
2174
|
const mTl = MTL
|
|
@@ -2027,8 +2177,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2027
2177
|
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
2028
2178
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
2029
2179
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
2180
|
+
const probeBest = PROBE ? ctx._probe?.best : undefined
|
|
2030
2181
|
const v = child(input, pos, ctx)
|
|
2031
2182
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2183
|
+
if (PROBE && ctx._probe !== undefined) ctx._probe.best = probeBest ?? null
|
|
2032
2184
|
// `peek.ts:60` — the ASSERTION's own set, at the assertion's position.
|
|
2033
2185
|
if (v === FAIL) { ctx._fe = pos; ctx._fx = xf; return FAIL }
|
|
2034
2186
|
EC.e = pos
|
|
@@ -2038,8 +2190,11 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2038
2190
|
|
|
2039
2191
|
case OP_OPT: {
|
|
2040
2192
|
const child = link(code[ip + 1]!)
|
|
2193
|
+
const gate = lookaheadClass(code[ip + 1]!)
|
|
2194
|
+
const childClean = CLEAN(code[ip + 1]!)
|
|
2041
2195
|
return (input, pos, ctx) => {
|
|
2042
|
-
|
|
2196
|
+
if (gate !== null && !classHas(gate, lead(input, pos))) { EC.e = pos; return null }
|
|
2197
|
+
const need = !childClean && markCst(ctx)
|
|
2043
2198
|
const mRaw = MRAW
|
|
2044
2199
|
const mTl = MTL
|
|
2045
2200
|
const mLv = MLV
|
|
@@ -2250,6 +2405,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2250
2405
|
? fns[reducer] as (value: unknown, span: { start: number; end: number }) => unknown
|
|
2251
2406
|
: undefined
|
|
2252
2407
|
const step = closureLabels.at(ip).buf ? nextTermBuffered : nextTerm
|
|
2408
|
+
const decided = FIRST_DECIDED
|
|
2409
|
+
FIRST_DECIDED = undefined
|
|
2410
|
+
const linkFirst = (): Piece => decided ?? link(code[base]!)
|
|
2253
2411
|
|
|
2254
2412
|
const runnerBlock = (
|
|
2255
2413
|
count: number,
|
|
@@ -2348,7 +2506,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2348
2506
|
* sentinel published is the kind of state a later reader will trust.
|
|
2349
2507
|
*/
|
|
2350
2508
|
if (REC) {
|
|
2351
|
-
const first =
|
|
2509
|
+
const first = linkFirst()
|
|
2352
2510
|
const firstSync = sentinelFor(code[base + n]!)
|
|
2353
2511
|
const runnerAt = (i: number): TermRunner => {
|
|
2354
2512
|
const sync = sentinelFor(code[base + n + i]!)
|
|
@@ -2430,15 +2588,32 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2430
2588
|
|
|
2431
2589
|
// A `field(…, { leadingTrivia: true })` term is a boundary fact too: the
|
|
2432
2590
|
// runner hands it the cursor from BEFORE the scan (`LEAD`, cell.ts).
|
|
2591
|
+
// So is a lookahead that holds its gap for the next term (`hold-gap.ts`).
|
|
2433
2592
|
let hasAdj = false
|
|
2434
2593
|
for (let i = 1; i < n; i++) {
|
|
2435
2594
|
const kidIp = code[base + i]!
|
|
2436
|
-
if (code[kidIp] === OP_ADJ || leadingTriviaFieldAt(code, k, kidIp)) { hasAdj = true; break }
|
|
2595
|
+
if (code[kidIp] === OP_ADJ || leadingTriviaFieldAt(code, k, kidIp) || holdsGap(code, k, base, n, i, NULLABLE)) { hasAdj = true; break }
|
|
2437
2596
|
}
|
|
2438
2597
|
if (hasAdj) {
|
|
2439
|
-
const first =
|
|
2598
|
+
const first = linkFirst()
|
|
2440
2599
|
const runnerAt = (i: number): TermRunner => {
|
|
2441
2600
|
const kidIp = code[base + i]!
|
|
2601
|
+
if (holdsGap(code, k, base, n, i, NULLABLE)) {
|
|
2602
|
+
const kid = link(kidIp)
|
|
2603
|
+
return (input, cur, ctx) => {
|
|
2604
|
+
if (ctx.trivia === undefined) {
|
|
2605
|
+
const v = kid(input, cur, ctx)
|
|
2606
|
+
if (v === FAIL) return -1
|
|
2607
|
+
TERMV = v
|
|
2608
|
+
return EC.e
|
|
2609
|
+
}
|
|
2610
|
+
const scanEnd = skipTrivia(input, cur, ctx)
|
|
2611
|
+
const v = kid(input, scanEnd, ctx)
|
|
2612
|
+
if (v === FAIL) return -1
|
|
2613
|
+
TERMV = v
|
|
2614
|
+
return scanEnd
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2442
2617
|
if (code[kidIp] !== OP_ADJ) {
|
|
2443
2618
|
const kid = link(kidIp)
|
|
2444
2619
|
if (leadingTriviaFieldAt(code, k, kidIp)) {
|
|
@@ -2524,7 +2699,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2524
2699
|
* runs, so there is no arity this fails to lower.
|
|
2525
2700
|
*/
|
|
2526
2701
|
if (n === 1) {
|
|
2527
|
-
const k0 =
|
|
2702
|
+
const k0 = linkFirst()
|
|
2528
2703
|
if (fused) {
|
|
2529
2704
|
return (input, pos, ctx) => {
|
|
2530
2705
|
const v = k0(input, pos, ctx)
|
|
@@ -2547,7 +2722,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2547
2722
|
}
|
|
2548
2723
|
|
|
2549
2724
|
if (n === 2) {
|
|
2550
|
-
const k0 =
|
|
2725
|
+
const k0 = linkFirst(), k1 = link(code[base + 1]!)
|
|
2551
2726
|
if (fused) {
|
|
2552
2727
|
return (input, pos, ctx) => {
|
|
2553
2728
|
const v0 = k0(input, pos, ctx)
|
|
@@ -2581,7 +2756,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2581
2756
|
}
|
|
2582
2757
|
|
|
2583
2758
|
if (n === 3) {
|
|
2584
|
-
const k0 =
|
|
2759
|
+
const k0 = linkFirst(), k1 = link(code[base + 1]!), k2 = link(code[base + 2]!)
|
|
2585
2760
|
if (fused) {
|
|
2586
2761
|
return (input, pos, ctx) => {
|
|
2587
2762
|
const v0 = k0(input, pos, ctx)
|
|
@@ -2683,7 +2858,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2683
2858
|
start + count < n ? bind(start + count) : undefined,
|
|
2684
2859
|
)
|
|
2685
2860
|
}
|
|
2686
|
-
const first =
|
|
2861
|
+
const first = linkFirst()
|
|
2687
2862
|
const tail = bind(1)
|
|
2688
2863
|
const runDirectTerms = (
|
|
2689
2864
|
input: string, pos: number, ctx: ParseContext, values: unknown[] | undefined,
|
|
@@ -2727,6 +2902,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2727
2902
|
const n = code[ip + 2]!
|
|
2728
2903
|
const choiceFx = fx[code[ip + 3]!] as string[]
|
|
2729
2904
|
const base = ip + 4
|
|
2905
|
+
// The arms first, so the answer reads theirs rather than walking them again.
|
|
2906
|
+
for (let i = 0; i < n; i++) link(code[base + i]!)
|
|
2907
|
+
const armsClean = CLEAN(ip)
|
|
2730
2908
|
|
|
2731
2909
|
// THE TWO CHOICE SHAPES ARE TWO PIECES. `exec.ts` tested `table.exclusive`
|
|
2732
2910
|
// on every choice execution; it is table data, so it selects here.
|
|
@@ -2863,7 +3041,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2863
3041
|
|
|
2864
3042
|
return (input, pos, ctx) => {
|
|
2865
3043
|
const c = lead(input, pos)
|
|
2866
|
-
const need = markCst(ctx)
|
|
3044
|
+
const need = !armsClean && markCst(ctx)
|
|
2867
3045
|
const mRaw = MRAW
|
|
2868
3046
|
const mTl = MTL
|
|
2869
3047
|
const mLv = MLV
|
|
@@ -2915,6 +3093,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2915
3093
|
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
2916
3094
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2917
3095
|
}
|
|
3096
|
+
if (prev === 0) { ctx._fe = pos; ctx._fx = choiceFx; return FAIL }
|
|
2918
3097
|
if (best === pos && prev < 1) acc = accSet(e0, acc)
|
|
2919
3098
|
if (best === pos && prev < 2) acc = accSet(e1, acc)
|
|
2920
3099
|
if (best === pos && n === 3 && prev < 3) acc = accSet(e2, acc)
|
|
@@ -3027,7 +3206,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3027
3206
|
if (pn !== 0) {
|
|
3028
3207
|
return (input, pos, ctx) => {
|
|
3029
3208
|
const c = lead(input, pos)
|
|
3030
|
-
const need = markCst(ctx)
|
|
3209
|
+
const need = !armsClean && markCst(ctx)
|
|
3031
3210
|
const mRaw = MRAW
|
|
3032
3211
|
const mTl = MTL
|
|
3033
3212
|
const mLv = MLV
|
|
@@ -3057,7 +3236,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3057
3236
|
}
|
|
3058
3237
|
return (input, pos, ctx) => {
|
|
3059
3238
|
const c = lead(input, pos)
|
|
3060
|
-
const need = markCst(ctx)
|
|
3239
|
+
const need = !armsClean && markCst(ctx)
|
|
3061
3240
|
const mRaw = MRAW
|
|
3062
3241
|
const mTl = MTL
|
|
3063
3242
|
const mLv = MLV
|
|
@@ -3079,7 +3258,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3079
3258
|
|
|
3080
3259
|
return (input, pos, ctx) => {
|
|
3081
3260
|
const c = lead(input, pos)
|
|
3082
|
-
const need = markCst(ctx)
|
|
3261
|
+
const need = !armsClean && markCst(ctx)
|
|
3083
3262
|
const mRaw = MRAW
|
|
3084
3263
|
const mTl = MTL
|
|
3085
3264
|
const mLv = MLV
|
|
@@ -3125,6 +3304,22 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3125
3304
|
}
|
|
3126
3305
|
}
|
|
3127
3306
|
|
|
3307
|
+
case OP_RGATE: {
|
|
3308
|
+
const cls = cc[code[ip + 1]!]!
|
|
3309
|
+
const xf = fx[code[ip + 3]!] as string[]
|
|
3310
|
+
const child = link(code[ip + 2]!)
|
|
3311
|
+
// Forwards under a probe or tolerant recovery, as OP_GATE does.
|
|
3312
|
+
if (cfg.tolerant || cfg.probe) return child
|
|
3313
|
+
return (input, pos, ctx) => {
|
|
3314
|
+
const item = ctx._routed
|
|
3315
|
+
if (item !== undefined && item.span.start === pos && !classHas(cls, lead(input, item.span.end))) {
|
|
3316
|
+
ctx._fe = item.span.end; ctx._fx = xf
|
|
3317
|
+
return FAIL
|
|
3318
|
+
}
|
|
3319
|
+
return child(input, pos, ctx)
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3128
3323
|
case OP_ARMGATE: {
|
|
3129
3324
|
// Predicate, arm and expected set all bound HERE — the body reads no
|
|
3130
3325
|
// opcode, decodes no operand and tests no setting.
|
|
@@ -3134,9 +3329,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3134
3329
|
return (input, pos, ctx) => {
|
|
3135
3330
|
if (!pred(ctx.state)) {
|
|
3136
3331
|
// A `continue` in the interpreter (choice.ts:150), not a failure —
|
|
3137
|
-
// so no cut survives to the enclosing choice
|
|
3332
|
+
// so no cut survives to the enclosing choice, and no failure position
|
|
3333
|
+
// ranks it on the ordered path. A dispatched choice reads `_fx` alone.
|
|
3138
3334
|
ctx._fc = false
|
|
3139
|
-
ctx._fe =
|
|
3335
|
+
ctx._fe = -1
|
|
3140
3336
|
ctx._fx = xf
|
|
3141
3337
|
return FAIL
|
|
3142
3338
|
}
|
|
@@ -3221,6 +3417,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3221
3417
|
// its FIRST item through `repItem` and so skips leading trivia. The shape
|
|
3222
3418
|
// identifies itself, and it is table data, so it is decided here.
|
|
3223
3419
|
const skipBeforeFirst = sepIp < 0 && min === 0
|
|
3420
|
+
const itemClean = sepIp < 0 && CLEAN(code[ip + 1]!) && !rowNullable(code, k, code[ip + 1]!, NULLABLE)
|
|
3224
3421
|
|
|
3225
3422
|
/**
|
|
3226
3423
|
* TOLERANT RECOVERY — the third implementation of `recoverScan`'s
|
|
@@ -3348,6 +3545,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3348
3545
|
const out: unknown[] | undefined = collect ? [] : undefined
|
|
3349
3546
|
const hasTrivia = ctx.trivia !== undefined
|
|
3350
3547
|
const needMark = rollbackNeeded(ctx)
|
|
3548
|
+
// A clean item with no trivia scanned in front of it has nothing to undo.
|
|
3549
|
+
const markLater = needMark && (!itemClean || hasTrivia)
|
|
3550
|
+
const markFirst = needMark && (!itemClean || (hasTrivia && skipBeforeFirst))
|
|
3351
3551
|
// A completions probe deliberately observes swallowed item failures.
|
|
3352
3552
|
// Keep the ordinary child path while one is active so this structural
|
|
3353
3553
|
// fast stop cannot erase an item completion at the cursor.
|
|
@@ -3367,14 +3567,15 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3367
3567
|
// Per ITERATION, never hoisted: a preceding item's `node()` opened and
|
|
3368
3568
|
// closed a capture buffer, so `ctx._cstBuf` is not the object it was
|
|
3369
3569
|
// at the loop head. `needMark` is the pre-existing loop-invariant.
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
const
|
|
3373
|
-
const
|
|
3374
|
-
const
|
|
3375
|
-
const
|
|
3376
|
-
const
|
|
3377
|
-
const
|
|
3570
|
+
const markIt = count > 0 ? markLater : markFirst
|
|
3571
|
+
if (markIt) markCst(ctx)
|
|
3572
|
+
const mRaw = markIt ? MRAW : 0
|
|
3573
|
+
const mTl = markIt ? MTL : 0
|
|
3574
|
+
const mLv = markIt ? MLV : 0
|
|
3575
|
+
const mFl = markIt ? ctx._fields?.length ?? 0 : 0
|
|
3576
|
+
const mEr = markIt ? ctx._errors?.length ?? 0 : 0
|
|
3577
|
+
const mLog = markIt ? ctx._triviaLog?.length ?? 0 : 0
|
|
3578
|
+
const mRoot = markIt ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
3378
3579
|
let itemStart = cur
|
|
3379
3580
|
let sepEnd = -1
|
|
3380
3581
|
const viaRepItem = sep === undefined && count >= min && (count > 0 || skipBeforeFirst)
|
|
@@ -3386,7 +3587,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3386
3587
|
if (sepLeads) LEAD.at = cur
|
|
3387
3588
|
const sv = sep(input, sp, ctx)
|
|
3388
3589
|
if (sv === FAIL) {
|
|
3389
|
-
if (
|
|
3590
|
+
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3390
3591
|
if (committed(ctx)) return FAIL
|
|
3391
3592
|
break
|
|
3392
3593
|
}
|
|
@@ -3397,19 +3598,19 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3397
3598
|
itemStart = skipTrivia(input, itemStart, ctx)
|
|
3398
3599
|
}
|
|
3399
3600
|
if (itemStart >= input.length && viaRepItem) {
|
|
3400
|
-
if (
|
|
3601
|
+
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3401
3602
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
3402
3603
|
break
|
|
3403
3604
|
}
|
|
3404
3605
|
if (count >= min && gateItems && !itemMayStart!(input, itemStart)) {
|
|
3405
|
-
if (
|
|
3606
|
+
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3406
3607
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
3407
3608
|
break
|
|
3408
3609
|
}
|
|
3409
3610
|
ctx._fc = false
|
|
3410
3611
|
const v = child(input, itemStart, ctx)
|
|
3411
3612
|
if (v === FAIL) {
|
|
3412
|
-
if (
|
|
3613
|
+
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3413
3614
|
if (committed(ctx)) return FAIL
|
|
3414
3615
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
3415
3616
|
break
|
|
@@ -3417,7 +3618,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3417
3618
|
if (EC.e === itemStart && viaRepItem) {
|
|
3418
3619
|
// Zero-width item: `repItem`'s TERMINATION device, not a semantic
|
|
3419
3620
|
// filter — and it applies only where `repItem` runs.
|
|
3420
|
-
if (
|
|
3621
|
+
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3421
3622
|
break
|
|
3422
3623
|
}
|
|
3423
3624
|
if (out !== undefined) out.push(v)
|
|
@@ -3490,7 +3691,14 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3490
3691
|
const runOther = other === undefined ? undefined : dispatchBranch(other, otherRouted)
|
|
3491
3692
|
|
|
3492
3693
|
return (input, pos, ctx) => {
|
|
3493
|
-
const
|
|
3694
|
+
const need = markCst(ctx)
|
|
3695
|
+
const mRaw = MRAW
|
|
3696
|
+
const mTl = MTL
|
|
3697
|
+
const mLv = MLV
|
|
3698
|
+
const mFl = need ? ctx._fields?.length ?? 0 : 0
|
|
3699
|
+
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
3700
|
+
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
3701
|
+
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
3494
3702
|
const selVal = selector(input, pos, ctx)
|
|
3495
3703
|
if (selVal === FAIL) return FAIL
|
|
3496
3704
|
const selEnd = EC.e
|
|
@@ -3503,9 +3711,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3503
3711
|
ctx._fx = expected
|
|
3504
3712
|
return FAIL
|
|
3505
3713
|
}
|
|
3506
|
-
return runOther(input, pos, selEnd, key,
|
|
3714
|
+
return runOther(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3507
3715
|
}
|
|
3508
|
-
return runArm!(arm, input, pos, selEnd, key,
|
|
3716
|
+
return runArm!(arm, input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3509
3717
|
}
|
|
3510
3718
|
}
|
|
3511
3719
|
|
|
@@ -3549,7 +3757,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3549
3757
|
return nd
|
|
3550
3758
|
}
|
|
3551
3759
|
}
|
|
3552
|
-
const child = link(code[ip + 2]!)
|
|
3760
|
+
const child = NODE_BODY ?? link(code[ip + 2]!)
|
|
3761
|
+
NODE_BODY = undefined
|
|
3553
3762
|
const proj = code[ip + 4]!
|
|
3554
3763
|
const buildIdx = code[ip + 1]!
|
|
3555
3764
|
const type = k[code[ip + 5]!] as string
|
|
@@ -3586,6 +3795,25 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3586
3795
|
if (build !== undefined && proj < 0 && flags === 18) {
|
|
3587
3796
|
return childrenOnlyFieldsBuildNode(child, build)
|
|
3588
3797
|
}
|
|
3798
|
+
if (build !== undefined && passCollapse(code, ip, hostCst)) {
|
|
3799
|
+
return passCollapseNode(child, build, flags === 32)
|
|
3800
|
+
}
|
|
3801
|
+
// Not under recovery or a probe: there a gated-out term still runs.
|
|
3802
|
+
const gated = build !== undefined && !cfg.tolerant && !cfg.probe ? gatedPassCollapse(code, ip, hostCst) : undefined
|
|
3803
|
+
const triviaLead = gated === undefined ? undefined : triviaLeadRanges(closureLabels.at(code[ip + 2]!).tri, trivia)
|
|
3804
|
+
if (build !== undefined && gated !== undefined && triviaLead !== undefined) {
|
|
3805
|
+
const classic = flags === 32 ? collapseBuildNode(child, build) : childrenOnlyCollapseBuildNode(child, build)
|
|
3806
|
+
return gatedPassCollapseNode(
|
|
3807
|
+
[
|
|
3808
|
+
...gated.leads.map(g => cc[code[g + 1]!]!),
|
|
3809
|
+
resolveClass(encodeClassSpec([
|
|
3810
|
+
...triviaLead,
|
|
3811
|
+
...gated.lits.map(i => { const c = (k[i] as string).codePointAt(0)!; return { lo: c, hi: c } }),
|
|
3812
|
+
])),
|
|
3813
|
+
],
|
|
3814
|
+
link(gated.last), classic, build, flags === 32,
|
|
3815
|
+
)
|
|
3816
|
+
}
|
|
3589
3817
|
if (build !== undefined && proj < 0 && flags === 32) {
|
|
3590
3818
|
return collapseBuildNode(child, build)
|
|
3591
3819
|
}
|
|
@@ -3663,9 +3891,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3663
3891
|
if (v === FAIL) return FAIL
|
|
3664
3892
|
const end = EC.e
|
|
3665
3893
|
const span = tracked ? spanLines(ctx, pos, end) : { start: pos, end }
|
|
3666
|
-
const st = readsState
|
|
3667
|
-
? Object.assign({}, ctx.state as Record<string, unknown>)
|
|
3668
|
-
: undefined
|
|
3894
|
+
const st = readsState ? ctx.state : undefined
|
|
3669
3895
|
|
|
3670
3896
|
let nd: unknown
|
|
3671
3897
|
if (unwrap && kids.length === 1) {
|
|
@@ -3685,12 +3911,12 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3685
3911
|
nd = kids[0]
|
|
3686
3912
|
} else if (proj >= 0) {
|
|
3687
3913
|
nd = hostCst && host !== undefined
|
|
3688
|
-
? host(type, kids, fieldMap, span, rawKids, tlog,
|
|
3914
|
+
? host(type, kids, fieldMap, span, rawKids, tlog, ctx.state, tags)
|
|
3689
3915
|
: projectChild(kids, proj, type)
|
|
3690
3916
|
} else if (build !== undefined) {
|
|
3691
3917
|
if (hostCst && host !== undefined) {
|
|
3692
3918
|
// A direct builder is bypassed under a CST host.
|
|
3693
|
-
nd = host(type, kids, fieldMap, span, rawKids, tlog,
|
|
3919
|
+
nd = host(type, kids, fieldMap, span, rawKids, tlog, ctx.state, tags)
|
|
3694
3920
|
} else {
|
|
3695
3921
|
nd = build(kids, fieldMap, span, rawKids, captureWide ? tlog : EMPTY_TL, st)
|
|
3696
3922
|
}
|
|
@@ -3777,9 +4003,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3777
4003
|
// The scan pool and the scan-skip sets are built FROM subtrees, so their
|
|
3778
4004
|
// sites need emitted names too or `subtreeComb` would have to fall back to
|
|
3779
4005
|
// a closure for them and the two engines would both be live in one parse.
|
|
3780
|
-
const extraIps
|
|
3781
|
-
for (const s of prog.scans ?? []) for (const r of scanRefs(s)) extraIps.push(r[0])
|
|
3782
|
-
for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0])
|
|
4006
|
+
const extraIps = assemblyExtraIps(prog)
|
|
3783
4007
|
const roots = [...Object.values(prog.rules), ...extraIps]
|
|
3784
4008
|
for (const ip of reachableSites(code, roots)) {
|
|
3785
4009
|
if (code[ip] === OP_DISPATCH) {
|
|
@@ -3815,6 +4039,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3815
4039
|
}
|
|
3816
4040
|
const notChild = scalarTerminalNotChild(code, ip)
|
|
3817
4041
|
if (notChild >= 0) scalarFor(notChild)
|
|
4042
|
+
const peekChild = scalarTerminalPeekChild(code, ip)
|
|
4043
|
+
if (peekChild >= 0) scalarFor(peekChild)
|
|
3818
4044
|
}
|
|
3819
4045
|
/**
|
|
3820
4046
|
* THE BUILD'S OWN ANSWER, TRIED BEFORE THE CONSTRUCTOR.
|
|
@@ -3852,9 +4078,11 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3852
4078
|
REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
|
|
3853
4079
|
matchesAt, recoverScan, orSentinel, captureError,
|
|
3854
4080
|
scalarRecognizers, commitTriviaScan, scanTriviaCompact, t.lex, adjacencyHolds, t.lexPrograms,
|
|
3855
|
-
LEAD, triviaFieldCapture,
|
|
4081
|
+
LEAD, triviaFieldCapture, skipDeferredTrivia,
|
|
4082
|
+
collapseEmpty,
|
|
3856
4083
|
)
|
|
3857
4084
|
emitReached = new Set(pre.reached)
|
|
4085
|
+
if (emitted.ext !== undefined) HYBRID = emitted
|
|
3858
4086
|
}
|
|
3859
4087
|
/**
|
|
3860
4088
|
* ONCE A PROGRAM CARRIES ASSEMBLIES, THE CONSTRUCTOR IS OFF FOR IT — for
|
|
@@ -3934,7 +4162,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3934
4162
|
REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
|
|
3935
4163
|
matchesAt, recoverScan, orSentinel, captureError,
|
|
3936
4164
|
scalarRecognizers, commitTriviaScan, scanTriviaCompact, t.lex, adjacencyHolds, t.lexPrograms,
|
|
3937
|
-
LEAD, triviaFieldCapture,
|
|
4165
|
+
LEAD, triviaFieldCapture, skipDeferredTrivia,
|
|
4166
|
+
collapseEmpty,
|
|
3938
4167
|
)
|
|
3939
4168
|
emitReached = em.reached
|
|
3940
4169
|
} catch (e) {
|
|
@@ -3947,7 +4176,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3947
4176
|
/** One piece for a site, from whichever engine this assembly is running. */
|
|
3948
4177
|
function pieceAt(ip: number): Piece {
|
|
3949
4178
|
const em = emitted
|
|
3950
|
-
if (em !== undefined) {
|
|
4179
|
+
if (em !== undefined && HYBRID === undefined) {
|
|
3951
4180
|
const p = em.byIp[ip]
|
|
3952
4181
|
if (p === undefined) throw new Error(`table emitter: no emitted body for site ${ip}`)
|
|
3953
4182
|
return p as Piece
|
|
@@ -3982,6 +4211,28 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3982
4211
|
|
|
3983
4212
|
/* ── link the rules ──────────────────────────────────────────────────────── */
|
|
3984
4213
|
|
|
4214
|
+
if (HYBRID !== undefined) {
|
|
4215
|
+
const em = HYBRID
|
|
4216
|
+
em.bind!(
|
|
4217
|
+
s => { const saved = SCAN; SCAN = s as FastTriviaScanner | null; return saved },
|
|
4218
|
+
em.ext!.map(ip => link(ip)),
|
|
4219
|
+
)
|
|
4220
|
+
const pieces: Record<string, Piece> = {}
|
|
4221
|
+
for (const [name, entryIp] of Object.entries(prog.rules)) {
|
|
4222
|
+
pieces[name] = (em.pieces[name] as Piece | undefined) ?? link(entryIp)
|
|
4223
|
+
}
|
|
4224
|
+
for (const ip of emitReached!) reached.add(ip)
|
|
4225
|
+
return {
|
|
4226
|
+
pieces,
|
|
4227
|
+
end: () => EC.e,
|
|
4228
|
+
begin: ctx => { begin(ctx); em.begin(ctx) },
|
|
4229
|
+
finish: () => { em.finish(); finish() },
|
|
4230
|
+
scanSkip,
|
|
4231
|
+
reached,
|
|
4232
|
+
emitRefusal: undefined,
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
|
|
3985
4236
|
if (emitted !== undefined) {
|
|
3986
4237
|
const em = emitted
|
|
3987
4238
|
return {
|
|
@@ -4034,6 +4285,43 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
4034
4285
|
return { pieces, end: () => EC.e, begin, finish, scanSkip, reached, emitRefusal }
|
|
4035
4286
|
}
|
|
4036
4287
|
|
|
4288
|
+
/**
|
|
4289
|
+
* BUILD-TIME SITE PROFILING (`countSiteVisits`). Set only while that call runs;
|
|
4290
|
+
* `link` reads it when it LOWERS a site, never per parse, so an ordinary assembly
|
|
4291
|
+
* carries no counter and no branch.
|
|
4292
|
+
*/
|
|
4293
|
+
let SITE_COUNTS: Int32Array | undefined
|
|
4294
|
+
|
|
4295
|
+
function countedPiece(piece: Piece, ip: number): Piece {
|
|
4296
|
+
const counts = SITE_COUNTS
|
|
4297
|
+
if (counts === undefined) return piece
|
|
4298
|
+
return (input, pos, ctx) => {
|
|
4299
|
+
counts[ip]!++
|
|
4300
|
+
return piece(input, pos, ctx)
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
|
|
4304
|
+
/**
|
|
4305
|
+
* How many times each table site runs while `parse` drives the program — the
|
|
4306
|
+
* profile an optional corpus gives selective assembly (`select-hot.ts`). Runs the
|
|
4307
|
+
* CLOSURE engine, so it constructs no code; `counts[ip]` is the number of calls
|
|
4308
|
+
* of the piece lowered for site `ip` (an alias site counts at its target).
|
|
4309
|
+
*/
|
|
4310
|
+
export function countSiteVisits(
|
|
4311
|
+
prog: TableProgram,
|
|
4312
|
+
parse: (rules: Record<string, TableRule>) => void,
|
|
4313
|
+
/** A replacement callback pool (same indices), e.g. stand-ins for inert builders. */
|
|
4314
|
+
fns: readonly unknown[] = prog.fns,
|
|
4315
|
+
): Int32Array {
|
|
4316
|
+
const counts = new Int32Array(prog.code.length)
|
|
4317
|
+
// A fresh program, not a spread: a spread keeps the source's cached resolution,
|
|
4318
|
+
// which holds the ORIGINAL callback pool.
|
|
4319
|
+
const profiled = ownTableProgram({ ...prog, fns, asm: [] })
|
|
4320
|
+
SITE_COUNTS = counts
|
|
4321
|
+
try { parse(tableRules(profiled)) } finally { SITE_COUNTS = undefined }
|
|
4322
|
+
return counts
|
|
4323
|
+
}
|
|
4324
|
+
|
|
4037
4325
|
/**
|
|
4038
4326
|
* Assemblies for one resolved table, one per option set, built on demand.
|
|
4039
4327
|
*
|