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
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
import type { ParseContext } from '../types.ts'
|
|
48
48
|
import { regexCanMatchEmpty } from '../regex/first-set.ts'
|
|
49
49
|
import {
|
|
50
|
-
OP_ADJ, OP_ATTEMPT, OP_CHOICE, OP_DISPATCH, OP_EMPTY, OP_EXPECT, OP_FIELD, OP_GATE,
|
|
50
|
+
OP_ADJ, OP_ARMGATE, OP_RGATE, OP_ATTEMPT, OP_CHOICE, OP_DISPATCH, OP_EMPTY, OP_EXPECT, OP_FIELD, OP_GATE,
|
|
51
51
|
OP_LABEL, OP_LEAF, OP_SOURCE_LEAF, OP_LIT, OP_LIT_CI, OP_LIT_CI_TRACK, OP_LIT_TRACK, OP_NAMES,
|
|
52
52
|
OP_NODE, OP_NODE_TRACK, OP_NOT, OP_OPT, OP_PEEK, OP_REP, OP_REPV, OP_ROUTED, OP_RULE, OP_RX,
|
|
53
|
-
OP_RX_TRACK, OP_SCAN, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, OP_SEQ, OP_SEQV, OP_SEQX, OP_TOKEN, OP_XFORM,
|
|
53
|
+
OP_RX_TRACK, OP_SCAN, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, OP_SEQ, OP_SEQV, OP_SEQX, OP_TOKEN, OP_WITHCTX, OP_XFORM,
|
|
54
54
|
OP_LEX_BODY, OP_LEX_PROGRAM,
|
|
55
55
|
} from './ops.ts'
|
|
56
56
|
import {
|
|
@@ -60,13 +60,14 @@ import {
|
|
|
60
60
|
import { emitShapeMatch, scanShapeFromRegex } from './scan-shapes.ts'
|
|
61
61
|
import {
|
|
62
62
|
CAP_OFF, CAP_ON, RAW_CAPTURE, RAW_OMIT, TRI_NONE, TRI_UNKNOWN,
|
|
63
|
-
computeSiteLabels, reachableSites, type SiteLabel,
|
|
63
|
+
computeSiteLabels, gatedPassCollapse, passCollapse, reachableSites, scopeIsIdentity, triviaLeadRanges, type SiteLabel,
|
|
64
64
|
} from './site-labels.ts'
|
|
65
65
|
import {
|
|
66
66
|
largeChoiceLiteralPretest, leadingScalarSequence, leadingScalarTerminal,
|
|
67
|
-
scalarTerminalNodeChild, scalarTerminalNotChild, type LeadingScalarSequence,
|
|
67
|
+
scalarTerminalNodeChild, scalarTerminalNotChild, scalarTerminalPeekChild, type LeadingScalarSequence,
|
|
68
68
|
} from './scalar-terminal.ts'
|
|
69
69
|
import { childSlots } from './child-slots.ts'
|
|
70
|
+
import { holdsGap } from './hold-gap.ts'
|
|
70
71
|
import { fieldSpec, leadingTriviaFieldAt } from './field-spec.ts'
|
|
71
72
|
import { FIELD_LEADING_TRIVIA, FIELD_TRAILING_TRIVIA } from '../combinators/field-trivia.ts'
|
|
72
73
|
|
|
@@ -79,6 +80,14 @@ export type EmittedAssembly = {
|
|
|
79
80
|
readonly end: () => number
|
|
80
81
|
readonly begin: (ctx: ParseContext) => void
|
|
81
82
|
readonly finish: () => void
|
|
83
|
+
/**
|
|
84
|
+
* Present only on a SELECTIVE assembly: the unselected sites its bodies call,
|
|
85
|
+
* the hook that binds them to closure pieces (with a setter for the closure
|
|
86
|
+
* engine's installed trivia scanner), and that same setter for this scope's.
|
|
87
|
+
*/
|
|
88
|
+
readonly ext?: readonly number[]
|
|
89
|
+
readonly bind?: (setClosureScan: (s: unknown) => unknown, pieces: readonly EmittedPiece[]) => void
|
|
90
|
+
readonly scan?: (s: unknown) => unknown
|
|
82
91
|
}
|
|
83
92
|
/** The `new Function` result. Its parameters are `EMITTED_PARAMS`, in order. */
|
|
84
93
|
export type EmittedFactory = (...args: readonly unknown[]) => EmittedAssembly
|
|
@@ -127,6 +136,12 @@ export const EMITTED_PARAMS = [
|
|
|
127
136
|
// Appended for trivia-attributing fields (`field(…, { leadingTrivia })`);
|
|
128
137
|
// old factories ignore them.
|
|
129
138
|
'LEAD', 'triviaFieldCapture',
|
|
139
|
+
// Appended for the deferred-commit trivia skip (`trivia-gap.ts`); old
|
|
140
|
+
// factories ignore it.
|
|
141
|
+
'skipDeferredTrivia',
|
|
142
|
+
// Appended for the pass-through collapse (`run-support.ts`); old factories
|
|
143
|
+
// ignore it.
|
|
144
|
+
'collapseEmpty',
|
|
130
145
|
] as const
|
|
131
146
|
|
|
132
147
|
/**
|
|
@@ -162,7 +177,7 @@ function _skipTrivia(input,cur,ctx){
|
|
|
162
177
|
const s=_pfScan
|
|
163
178
|
if(s!==null&&ctx._triviaLog===undefined&&!(ctx.captureTrivia===true&&(ctx._cstBuf!==undefined||ctx._cstTriviaLog!==undefined)))return s(input,cur)
|
|
164
179
|
if(s!==null)return skipTriviaScanned(s,input,cur,ctx)
|
|
165
|
-
if(needsDeferredTriviaCommit(ctx))return
|
|
180
|
+
if(needsDeferredTriviaCommit(ctx))return skipDeferredTrivia(input,cur,ctx)
|
|
166
181
|
return advanceTrivia(input,cur,ctx)
|
|
167
182
|
}
|
|
168
183
|
function _pushLeaf(ctx,value,s,e){pushCstLeaf(ctx,{_tag:'leaf',value,span:{start:s,end:e}})}
|
|
@@ -425,6 +440,30 @@ ${scanned}
|
|
|
425
440
|
}`
|
|
426
441
|
}
|
|
427
442
|
|
|
443
|
+
/**
|
|
444
|
+
* A lookahead term that HOLDS its gap (`hold-gap.ts`): the gap is scanned and
|
|
445
|
+
* kept, the lookahead runs at its end, and the cursor stays there for the next
|
|
446
|
+
* term, which cannot match empty.
|
|
447
|
+
*/
|
|
448
|
+
function emitHoldTerm(callee: string, dst: string, t: string, l: SiteLabel, skip: string): string {
|
|
449
|
+
const fast = `const ${t}v=${callee}(input,cur,ctx)
|
|
450
|
+
if(${t}v===FAIL)return FAIL
|
|
451
|
+
${dst}=${t}v
|
|
452
|
+
cur=EC.e`
|
|
453
|
+
if (l.tri === TRI_NONE) return fast
|
|
454
|
+
const held = `const ${t}s=${skip}(input,cur,ctx)
|
|
455
|
+
const ${t}v=${callee}(input,${t}s,ctx)
|
|
456
|
+
if(${t}v===FAIL)return FAIL
|
|
457
|
+
${dst}=${t}v
|
|
458
|
+
cur=${t}s`
|
|
459
|
+
if (l.tri !== TRI_UNKNOWN) return held
|
|
460
|
+
return `if(ctx.trivia===undefined){
|
|
461
|
+
${fast}
|
|
462
|
+
}else{
|
|
463
|
+
${held}
|
|
464
|
+
}`
|
|
465
|
+
}
|
|
466
|
+
|
|
428
467
|
/**
|
|
429
468
|
* The repetition's LEAD-TRIVIA skip, as a BLOCK.
|
|
430
469
|
*
|
|
@@ -479,6 +518,8 @@ export type EmitResult = {
|
|
|
479
518
|
readonly armExpected: readonly (readonly string[])[][]
|
|
480
519
|
/** The same three pools as indices, for a build-time emitter. */
|
|
481
520
|
readonly plan: PoolPlan
|
|
521
|
+
/** Source length of each emitted site body, by site — what selection prices. */
|
|
522
|
+
readonly siteBytes: ReadonlyMap<number, number>
|
|
482
523
|
}
|
|
483
524
|
|
|
484
525
|
/** The candidate mask for one `CLS` row — the ONE definition, shared with the emitter. */
|
|
@@ -526,6 +567,11 @@ export function rebuildPools(
|
|
|
526
567
|
* ordinary factory literal in a macro artifact. It permits macro-only code
|
|
527
568
|
* shaping without perturbing the runtime `compile()` emitter, whose generated
|
|
528
569
|
* parser must remain byte-identical when the optimization cannot affect it.
|
|
570
|
+
*
|
|
571
|
+
* `select`, when given, makes this a SELECTIVE (hybrid) assembly: only sites
|
|
572
|
+
* whose alias-resolved target is in the set get a body. Every other child a
|
|
573
|
+
* body calls is an EXTERNAL — `_px<ip>`, a wrapper bound after construction to
|
|
574
|
+
* the closure engine's piece for that site (`assemble.ts`).
|
|
529
575
|
*/
|
|
530
576
|
export function emitAssemblySource(
|
|
531
577
|
t: ResolvedTable,
|
|
@@ -541,6 +587,7 @@ export function emitAssemblySource(
|
|
|
541
587
|
},
|
|
542
588
|
extraIps: readonly number[] = [],
|
|
543
589
|
staticBuild = false,
|
|
590
|
+
select?: ReadonlySet<number>,
|
|
544
591
|
): EmitResult {
|
|
545
592
|
const { code, k, fx, disp, dsp, triviaLabelled } = t
|
|
546
593
|
const swapLegal = !cfg.trackLines
|
|
@@ -584,6 +631,8 @@ export function emitAssemblySource(
|
|
|
584
631
|
// the closure recognizer pool: distinct terminal rows sharing one spec share
|
|
585
632
|
// both the recognizer and the ordinary-terminal lowering.
|
|
586
633
|
const scalarSpecs = new Set<number>()
|
|
634
|
+
// Row nullability for `holdsGap`, memoised for this emit.
|
|
635
|
+
const nullable = new Map<number, boolean>()
|
|
587
636
|
// Large choices are different: admitting every wrapped scalar would add a
|
|
588
637
|
// recognizer call to many arms whose one-character gate already did all the
|
|
589
638
|
// useful work. A multi-character literal can still refine an overlapping
|
|
@@ -716,6 +765,9 @@ export function emitAssemblySource(
|
|
|
716
765
|
|
|
717
766
|
const bodies: string[] = []
|
|
718
767
|
const byIp = new Map<number, string>()
|
|
768
|
+
/** Unselected sites a selective assembly calls into — see `select`. */
|
|
769
|
+
const externals: number[] = []
|
|
770
|
+
const siteBytes = new Map<number, number>()
|
|
719
771
|
const reached = new Set<number>()
|
|
720
772
|
const prelude: string[] = []
|
|
721
773
|
const skipDefs: string[] = []
|
|
@@ -900,6 +952,28 @@ return 1
|
|
|
900
952
|
* site property — nothing in `src` writes it except a caller-supplied
|
|
901
953
|
* `ParseContext` (`types.ts:524`) — so it stays the one runtime read.
|
|
902
954
|
*/
|
|
955
|
+
/** The test an `OP_GATE` row at `gip` fails on, with its class hoisted. */
|
|
956
|
+
function gateMissTest(gip: number): string {
|
|
957
|
+
const resolved = t.cc[code[gip + 1]!]!
|
|
958
|
+
const ci = classes.push([resolved]) - 1
|
|
959
|
+
classPlan.push([code[gip + 1]!])
|
|
960
|
+
const cls = hoist('cl', `CLS[${ci}][0]`)
|
|
961
|
+
return isAsciiOnlyClass(resolved)
|
|
962
|
+
? `${cls}.ascii[input.charCodeAt(pos)]!==1`
|
|
963
|
+
: `!classHas(${cls},lead(input,pos))`
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* An `optional`/`not`/`peek` child's first-character gate (`encode.ts`
|
|
968
|
+
* `lookaheadGate`), tested BEFORE the wrapper's mark: a miss is the wrapper's
|
|
969
|
+
* decided outcome, `miss`. Nothing where the child has no gate, or where a
|
|
970
|
+
* probe or tolerant parse forwards through it (`aliasOf`).
|
|
971
|
+
*/
|
|
972
|
+
function lookaheadMiss(child: number, miss: string): string {
|
|
973
|
+
if (code[child] !== OP_GATE || cfg.tolerant || cfg.probe) return ''
|
|
974
|
+
return `if(${gateMissTest(child)}){${miss}}\n`
|
|
975
|
+
}
|
|
976
|
+
|
|
903
977
|
function skipFor(l: SiteLabel): string {
|
|
904
978
|
if (l.tri < 0) return '_skipTrivia'
|
|
905
979
|
const ki = l.tri
|
|
@@ -919,9 +993,9 @@ return 1
|
|
|
919
993
|
// through `scanTrivia`. `needsDeferredTriviaCommit` is implied by an open
|
|
920
994
|
// buffer, so an in-node site skips the call that asks.
|
|
921
995
|
skipDefs.push(l.buf
|
|
922
|
-
? `function ${nm}(input,cur,ctx){return
|
|
996
|
+
? `function ${nm}(input,cur,ctx){return skipDeferredTrivia(input,cur,ctx)}`
|
|
923
997
|
: `function ${nm}(input,cur,ctx){
|
|
924
|
-
if(needsDeferredTriviaCommit(ctx))return
|
|
998
|
+
if(needsDeferredTriviaCommit(ctx))return skipDeferredTrivia(input,cur,ctx)
|
|
925
999
|
return advanceTrivia(input,cur,ctx)}`)
|
|
926
1000
|
return nm
|
|
927
1001
|
}
|
|
@@ -1002,12 +1076,20 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)\n` : ''}return FAIL
|
|
|
1002
1076
|
const target = resolveAlias(ip)
|
|
1003
1077
|
const done = byIp.get(target)
|
|
1004
1078
|
if (done !== undefined) return done
|
|
1079
|
+
if (select !== undefined && !select.has(target)) {
|
|
1080
|
+
const ext = `_px${target}`
|
|
1081
|
+
byIp.set(target, ext)
|
|
1082
|
+
externals.push(target)
|
|
1083
|
+
return ext
|
|
1084
|
+
}
|
|
1005
1085
|
const fname = `_pf${target}`
|
|
1006
1086
|
// Reserved BEFORE lowering, so a back-edge into a site still in flight binds
|
|
1007
1087
|
// to the hoisted declaration rather than to a forwarding stub. This is the
|
|
1008
1088
|
// whole of `assemble.ts`'s `inFlight` map and its one shared closure.
|
|
1009
1089
|
byIp.set(target, fname)
|
|
1010
|
-
|
|
1090
|
+
const body = lower(target, fname)
|
|
1091
|
+
siteBytes.set(target, body.length)
|
|
1092
|
+
bodies.push(body)
|
|
1011
1093
|
return fname
|
|
1012
1094
|
}
|
|
1013
1095
|
|
|
@@ -1063,17 +1145,9 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)\n` : ''}return FAIL
|
|
|
1063
1145
|
// The label is what makes this decidable: `tri >= 0` can ONLY have come from
|
|
1064
1146
|
// an enclosing scope carrying that slot, and that scope set
|
|
1065
1147
|
// `ctx.triviaKindLabels` and `_pfScan` from the same slot, so all three are
|
|
1066
|
-
// already the values this row would write.
|
|
1067
|
-
//
|
|
1068
|
-
|
|
1069
|
-
//
|
|
1070
|
-
// `ki >= 0` is required rather than implied: `TRI_NONE` and `TRI_UNKNOWN` are
|
|
1071
|
-
// themselves negative, so comparing a negative operand against a lattice
|
|
1072
|
-
// element would read "unknown" as a match.
|
|
1073
|
-
if ((op === OP_SCOPE_PLAIN || (op === OP_SCOPE && code[ip + 3]! === 0)) && code[ip + 1]! >= 0
|
|
1074
|
-
&& labels.at(ip).tri === code[ip + 1]!) {
|
|
1075
|
-
return code[ip + 2]!
|
|
1076
|
-
}
|
|
1148
|
+
// already the values this row would write. A scope with no trivia under
|
|
1149
|
+
// `TRI_NONE` is the same case (`scopeIsIdentity`).
|
|
1150
|
+
if (scopeIsIdentity(code, ip, labels.at(ip))) return code[ip + 2]!
|
|
1077
1151
|
return undefined
|
|
1078
1152
|
}
|
|
1079
1153
|
|
|
@@ -1266,19 +1340,57 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)\n` : ''}return FAIL
|
|
|
1266
1340
|
|
|
1267
1341
|
case OP_GATE: {
|
|
1268
1342
|
const child = link(code[ip + 2]!)
|
|
1343
|
+
const xf = fxRef(code[ip + 3]!)
|
|
1344
|
+
return `${head}
|
|
1345
|
+
if(${gateMissTest(ip)}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
|
|
1346
|
+
return ${child}(input,pos,ctx)
|
|
1347
|
+
}`
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
case OP_RGATE: {
|
|
1351
|
+
const child = link(code[ip + 2]!)
|
|
1352
|
+
if (cfg.tolerant || cfg.probe) return `${head}
|
|
1353
|
+
return ${child}(input,pos,ctx)
|
|
1354
|
+
}`
|
|
1269
1355
|
const resolved = t.cc[code[ip + 1]!]!
|
|
1270
1356
|
const ci = classes.push([resolved]) - 1
|
|
1271
1357
|
classPlan.push([code[ip + 1]!])
|
|
1272
1358
|
const cls = hoist('cl', `CLS[${ci}][0]`)
|
|
1273
1359
|
const xf = fxRef(code[ip + 3]!)
|
|
1274
1360
|
return `${head}
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1361
|
+
const it=ctx._routed
|
|
1362
|
+
if(it!==undefined&&it.span.start===pos&&!classHas(${cls},lead(input,it.span.end))){ctx._fe=it.span.end;ctx._fx=${xf};return FAIL}
|
|
1363
|
+
return ${child}(input,pos,ctx)
|
|
1364
|
+
}`
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
case OP_ARMGATE: {
|
|
1368
|
+
// `choice({ gate, combinator })`, as `assemble.ts` lowers it. A blocked
|
|
1369
|
+
// arm is SKIPPED, not failed (choice.ts `continue`): it clears the cut,
|
|
1370
|
+
// sets no failure position for the ordered path to rank, and leaves the
|
|
1371
|
+
// arm's own expected set for the dispatch path.
|
|
1372
|
+
const pred = fnRef(code[ip + 1]!)
|
|
1373
|
+
const child = link(code[ip + 2]!)
|
|
1374
|
+
const xf = fxRef(code[ip + 3]!)
|
|
1375
|
+
return `${head}
|
|
1376
|
+
if(!${pred}(ctx.state)){ctx._fc=false;ctx._fe=-1;ctx._fx=${xf};return FAIL}
|
|
1278
1377
|
return ${child}(input,pos,ctx)
|
|
1279
1378
|
}`
|
|
1280
1379
|
}
|
|
1281
1380
|
|
|
1381
|
+
case OP_WITHCTX: {
|
|
1382
|
+
// `withCtx(extra | derive, p)`, as `assemble.ts` lowers it: a negative
|
|
1383
|
+
// operand is a derive callback over the outer state, else a constant.
|
|
1384
|
+
// `finally` restores the outer state on every exit, a throw included.
|
|
1385
|
+
const ki = code[ip + 1]!
|
|
1386
|
+
const child = link(code[ip + 2]!)
|
|
1387
|
+
return `${head}
|
|
1388
|
+
const saved=ctx.state
|
|
1389
|
+
ctx.state=${ki < 0 ? `${fnRef(~ki)}(saved)` : kRef(ki)}
|
|
1390
|
+
try{return ${child}(input,pos,ctx)}finally{ctx.state=saved}
|
|
1391
|
+
}`
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1282
1394
|
case OP_XFORM: {
|
|
1283
1395
|
const fn = fnRef(code[ip + 1]!)
|
|
1284
1396
|
const child = link(code[ip + 2]!)
|
|
@@ -1589,10 +1701,10 @@ return FAIL
|
|
|
1589
1701
|
const xf = fxRef(code[ip + 2]!)
|
|
1590
1702
|
const p = tmp()
|
|
1591
1703
|
return `${head}
|
|
1592
|
-
${emitMark(p, L.buf, L.raw, sinks)}
|
|
1593
|
-
const v=${child}(input,pos,ctx)
|
|
1704
|
+
${lookaheadMiss(code[ip + 1]!, 'EC.e=pos;return null')}${emitMark(p, L.buf, L.raw, sinks)}
|
|
1705
|
+
${cfg.probe ? 'const pb=ctx._probe?.best\n' : ''}const v=${child}(input,pos,ctx)
|
|
1594
1706
|
${emitRollback(p, L.buf, L.raw, sinks)}
|
|
1595
|
-
if(v===FAIL){EC.e=pos;return null}
|
|
1707
|
+
${cfg.probe ? 'if(ctx._probe!==undefined)ctx._probe.best=pb??null\n' : ''}if(v===FAIL){EC.e=pos;return null}
|
|
1596
1708
|
ctx._fe=pos
|
|
1597
1709
|
ctx._fx=${xf}
|
|
1598
1710
|
return FAIL
|
|
@@ -1600,14 +1712,24 @@ return FAIL
|
|
|
1600
1712
|
}
|
|
1601
1713
|
|
|
1602
1714
|
case OP_PEEK: {
|
|
1715
|
+
const scalarChild = scalarTerminalPeekChild(code, ip)
|
|
1716
|
+
if (scalarChild >= 0) {
|
|
1717
|
+
const recognize = recognizerRef(code[scalarChild + 1]!)
|
|
1718
|
+
const xf = fxRef(code[ip + 2]!)
|
|
1719
|
+
return `${head}
|
|
1720
|
+
if(${recognize}(input,pos)<0){ctx._fe=pos;ctx._fx=${xf};return FAIL}
|
|
1721
|
+
EC.e=pos
|
|
1722
|
+
return null
|
|
1723
|
+
}`
|
|
1724
|
+
}
|
|
1603
1725
|
const child = link(code[ip + 1]!)
|
|
1604
1726
|
const xf = fxRef(code[ip + 2]!)
|
|
1605
1727
|
const p = tmp()
|
|
1606
1728
|
return `${head}
|
|
1607
|
-
${emitMark(p, L.buf, L.raw, sinks)}
|
|
1608
|
-
const v=${child}(input,pos,ctx)
|
|
1729
|
+
${lookaheadMiss(code[ip + 1]!, `ctx._fe=pos;ctx._fx=${xf};return FAIL`)}${emitMark(p, L.buf, L.raw, sinks)}
|
|
1730
|
+
${cfg.probe ? 'const pb=ctx._probe?.best\n' : ''}const v=${child}(input,pos,ctx)
|
|
1609
1731
|
${emitRollback(p, L.buf, L.raw, sinks)}
|
|
1610
|
-
if(v===FAIL){ctx._fe=pos;ctx._fx=${xf};return FAIL}
|
|
1732
|
+
${cfg.probe ? 'if(ctx._probe!==undefined)ctx._probe.best=pb??null\n' : ''}if(v===FAIL){ctx._fe=pos;ctx._fx=${xf};return FAIL}
|
|
1611
1733
|
EC.e=pos
|
|
1612
1734
|
return null
|
|
1613
1735
|
}`
|
|
@@ -1618,7 +1740,7 @@ return null
|
|
|
1618
1740
|
const clean = !REC && failureRollbackClean(prog, ip)
|
|
1619
1741
|
const p = tmp()
|
|
1620
1742
|
return `${head}
|
|
1621
|
-
${clean ? '' : emitMark(p, L.buf, L.raw, sinks)}
|
|
1743
|
+
${lookaheadMiss(code[ip + 1]!, 'EC.e=pos;return null')}${clean ? '' : emitMark(p, L.buf, L.raw, sinks)}
|
|
1622
1744
|
ctx._fc=false
|
|
1623
1745
|
const v=${child}(input,pos,ctx)
|
|
1624
1746
|
if(v===FAIL){
|
|
@@ -1706,7 +1828,9 @@ return v
|
|
|
1706
1828
|
// The callee expression hands it over AFTER the scan statement, right
|
|
1707
1829
|
// before the call, so a field inside the trivia grammar cannot take it.
|
|
1708
1830
|
const callee = leadingTriviaFieldAt(code, k, childIp) ? `(LEAD.at=cur,${kids[i]!})` : kids[i]!
|
|
1709
|
-
parts.push(
|
|
1831
|
+
parts.push(holdsGap(code, k, base, n, i, nullable)
|
|
1832
|
+
? emitHoldTerm(callee, vn, tmp(), L, skipFor(L))
|
|
1833
|
+
: emitTerm(callee, vn, tmp(), L, skipFor(L)))
|
|
1710
1834
|
}
|
|
1711
1835
|
names.push(vn)
|
|
1712
1836
|
}
|
|
@@ -1735,7 +1859,7 @@ return v
|
|
|
1735
1859
|
// array index feeding ONE call site, which is the megamorphic site
|
|
1736
1860
|
// this unit exists to remove.
|
|
1737
1861
|
const armSwitch = arms.map((a, i) => `case ${i}:v=${a}(input,pos,ctx);break`).join('\n')
|
|
1738
|
-
const directAscii =
|
|
1862
|
+
const directAscii = table.hi.length === 0
|
|
1739
1863
|
return `${head}
|
|
1740
1864
|
const c=${directAscii ? 'input.charCodeAt(pos)' : 'lead(input,pos)'}
|
|
1741
1865
|
let arm=-1
|
|
@@ -1766,7 +1890,7 @@ return FAIL
|
|
|
1766
1890
|
const expected = Array.from({ length: n }, (_, i) => fxRef(code[base + n + i]!))
|
|
1767
1891
|
const gates = Array.from({ length: n }, (_, i) => table.armCls[i] ?? null)
|
|
1768
1892
|
const maskable = n <= 32
|
|
1769
|
-
const directAsciiMask =
|
|
1893
|
+
const directAsciiMask = maskable && gates.every(isAsciiOnlyClass)
|
|
1770
1894
|
const gateRefs = directAsciiMask
|
|
1771
1895
|
? []
|
|
1772
1896
|
: Array.from({ length: n }, (_, i) => hoist('g', `DISP[${di}].armCls[${i}]`))
|
|
@@ -2001,6 +2125,9 @@ ${rollbackFor(i)}
|
|
|
2001
2125
|
// A zero compatible-arm mask is stronger than a speculative miss: no
|
|
2002
2126
|
// arm can run, so the choice's encoded flat expected set is already
|
|
2003
2127
|
// the exact result. Do not walk the arm ladder merely to rebuild it.
|
|
2128
|
+
// The same holds when the mask admitted arms but none was entered
|
|
2129
|
+
// (`prev` still 0, so `acc` is unset): the catch-up would rebuild
|
|
2130
|
+
// `choiceFx`, the arms' flatMap, set by set.
|
|
2004
2131
|
return `${head}
|
|
2005
2132
|
const c=${directAsciiMask ? 'input.charCodeAt(pos)' : 'lead(input,pos)'}
|
|
2006
2133
|
${hasRollback ? emitMark(p, L.buf, L.raw, sinks) : ''}
|
|
@@ -2011,7 +2138,7 @@ const bits=${maskName}[${directAsciiMask ? 'c' : 'c<0?128:c'}]
|
|
|
2011
2138
|
if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
|
|
2012
2139
|
${startFailureExact ? '' : 'let prev=0'}
|
|
2013
2140
|
${maskArms}
|
|
2014
|
-
${startFailureExact ? '' : `if(best===pos)acc=${catchName}(${n},prev,acc)`}
|
|
2141
|
+
${startFailureExact ? '' : `if(best===pos&&prev!==0)acc=${catchName}(${n},prev,acc)`}
|
|
2015
2142
|
ctx._fe=pos;ctx._fx=${startFailureExact ? `best===pos?${choiceFx}:acc??${choiceFx}` : `acc??${choiceFx}`}
|
|
2016
2143
|
return FAIL
|
|
2017
2144
|
}
|
|
@@ -2123,7 +2250,7 @@ return [key,v]
|
|
|
2123
2250
|
const ci = classes.push([resolved]) - 1
|
|
2124
2251
|
classPlan.push([itemClassIndex])
|
|
2125
2252
|
itemCls = hoist('ri', `CLS[${ci}][0]`)
|
|
2126
|
-
itemAscii =
|
|
2253
|
+
itemAscii = isAsciiOnlyClass(resolved)
|
|
2127
2254
|
}
|
|
2128
2255
|
}
|
|
2129
2256
|
const itemMiss = (at: string): string => itemAscii
|
|
@@ -2357,6 +2484,35 @@ return nd
|
|
|
2357
2484
|
// The trailing-trivia bit (128) rides on the children-only shape as it
|
|
2358
2485
|
// does in the closure assembler: a consume after the body, no capture.
|
|
2359
2486
|
const leanFlags = flags & ~128
|
|
2487
|
+
// THE PASS-THROUGH COLLAPSE (`run-support.ts` `collapseEmpty`): a
|
|
2488
|
+
// collapse over one node is that node. `passCollapse` is the closure
|
|
2489
|
+
// engine's test too.
|
|
2490
|
+
if (build !== undefined && passCollapse(code, ip, hostCst)) {
|
|
2491
|
+
return `${head}
|
|
2492
|
+
const v=${child}(input,pos,ctx)
|
|
2493
|
+
if(v!==undefined)return v
|
|
2494
|
+
return collapseEmpty(ctx,${build},input,pos,EC.e,${flags === 32})
|
|
2495
|
+
}`
|
|
2496
|
+
}
|
|
2497
|
+
// A GATED PASS COLLAPSE (`site-labels.ts` `gatedPassCollapse`): where the
|
|
2498
|
+
// character at `pos` opens none of the body's optional terms and no
|
|
2499
|
+
// trivia, the body is its node term, run as a pass-through collapse.
|
|
2500
|
+
// Not under recovery or a probe: there a gated-out term still runs.
|
|
2501
|
+
const gated = build !== undefined && !cfg.tolerant && !cfg.probe ? gatedPassCollapse(code, ip, hostCst) : undefined
|
|
2502
|
+
const triviaLead = gated === undefined ? undefined : triviaLeadRanges(labels.at(code[ip + 2]!).tri, t.trivia)
|
|
2503
|
+
const gp = gated !== undefined && triviaLead !== undefined && triviaLead.length <= 16
|
|
2504
|
+
? `const gc=lead(input,pos)
|
|
2505
|
+
if(!(${[
|
|
2506
|
+
...gated.leads.map(g => `!(${gateMissTest(g)})`),
|
|
2507
|
+
...gated.lits.map(i => `gc===${(k[i] as string).codePointAt(0)!}`),
|
|
2508
|
+
...triviaLead.map(r => r.lo === r.hi ? `gc===${r.lo}` : `(gc>=${r.lo}&&gc<=${r.hi})`),
|
|
2509
|
+
].join('||') || 'false'})){
|
|
2510
|
+
const gv=${link(gated.last)}(input,pos,ctx)
|
|
2511
|
+
if(gv!==undefined)return gv
|
|
2512
|
+
return collapseEmpty(ctx,${build},input,pos,EC.e,${flags === 32})
|
|
2513
|
+
}
|
|
2514
|
+
`
|
|
2515
|
+
: ''
|
|
2360
2516
|
if (!hostCst && !tracked && build !== undefined && proj < 0
|
|
2361
2517
|
&& (leanFlags === 2 || (flags === 18 || flags === 34))) {
|
|
2362
2518
|
const fields = flags === 18
|
|
@@ -2394,7 +2550,7 @@ sCh.push(nd)
|
|
|
2394
2550
|
if(sRaw!==undefined)sRaw.push(rawEntry(nd,input,pos,end))
|
|
2395
2551
|
}`
|
|
2396
2552
|
return `${head}
|
|
2397
|
-
const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
|
|
2553
|
+
${gp}const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
|
|
2398
2554
|
const sCap=ctx.captureTrivia,sBuf=ctx._cstBuf,sFields=ctx._fields
|
|
2399
2555
|
const flat=[]
|
|
2400
2556
|
ctx._cstBuf=undefined
|
|
@@ -2437,7 +2593,7 @@ return nd
|
|
|
2437
2593
|
const keepChildren = !structural || cfg.hostReadsChildren !== false || collapse || unwrap
|
|
2438
2594
|
const omitsRaw = !hostCst && build !== undefined && proj < 0 && (flags & 2) !== 0
|
|
2439
2595
|
const ty = q(type)
|
|
2440
|
-
const stArg =
|
|
2596
|
+
const stArg = 'ctx.state'
|
|
2441
2597
|
const hostCall = `_pfHost(${ty},hostKids,fieldMap,span,rawKids,tlog,${stArg},${tags})`
|
|
2442
2598
|
let value: string
|
|
2443
2599
|
if (proj >= 0) {
|
|
@@ -2489,7 +2645,7 @@ const hostKids=kids
|
|
|
2489
2645
|
const rawKids=buf.raw??(buf.rawSingle!==undefined?[buf.rawSingle]:EMPTY_CH)
|
|
2490
2646
|
const tlog=buf.tl??EMPTY_TLOG`
|
|
2491
2647
|
return `${head}
|
|
2492
|
-
const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
|
|
2648
|
+
${!hostCst && !tracked ? gp : ''}const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
|
|
2493
2649
|
const sCap=ctx.captureTrivia,sBuf=ctx._cstBuf
|
|
2494
2650
|
${openCapture}
|
|
2495
2651
|
ctx.captureTrivia=${captureWide}
|
|
@@ -2512,7 +2668,7 @@ ctx.captureTrivia=sCap
|
|
|
2512
2668
|
if(v===FAIL)return FAIL
|
|
2513
2669
|
const end=EC.e
|
|
2514
2670
|
const span=${tracked ? 'spanLines(ctx,pos,end)' : '{start:pos,end}'}
|
|
2515
|
-
const st=${readsState ? '
|
|
2671
|
+
const st=${readsState ? 'ctx.state' : 'undefined'}
|
|
2516
2672
|
let nd
|
|
2517
2673
|
${unwrap ? 'if(kids.length===1)nd=unwrapChild(kids[0])\nelse ' : ''}${collapse ? 'if(kids.length===1)nd=kids[0]\nelse ' : ''}${collapsible ? `if(_pfHost!==undefined&&_pfHost._parsemanCstCollapse!==undefined&&kids.length===1&&rawKids.length===1&&_pfHost._parsemanCstCollapse(${ty},kids[0],kids,rawKids))nd=kids[0]
|
|
2518
2674
|
else ` : ''}{${value}}
|
|
@@ -2535,8 +2691,12 @@ return nd
|
|
|
2535
2691
|
}
|
|
2536
2692
|
}
|
|
2537
2693
|
|
|
2694
|
+
// A selective assembly serves only its selected sites; the closure engine
|
|
2695
|
+
// serves every other rule entry and scan root (`assemble.ts`).
|
|
2696
|
+
const served = (ip: number): boolean => select === undefined || select.has(resolveAlias(ip))
|
|
2538
2697
|
const ruleEntries: string[] = []
|
|
2539
2698
|
for (const [rname, entryIp] of Object.entries(prog.rules)) {
|
|
2699
|
+
if (!served(entryIp)) continue
|
|
2540
2700
|
// `_r_<Name>` — the composition surface, in `codegen.ts`'s own spelling and
|
|
2541
2701
|
// deliberately NOT namespaced, so a sibling calls it by name.
|
|
2542
2702
|
const target = link(entryIp)
|
|
@@ -2547,7 +2707,49 @@ return nd
|
|
|
2547
2707
|
// Sites the SCAN pool and the scan-skip sets reference. They are linked
|
|
2548
2708
|
// through `subtreeComb` outside the emitted scope, so they need names.
|
|
2549
2709
|
const extra: string[] = []
|
|
2550
|
-
|
|
2710
|
+
const exported = new Set<number>()
|
|
2711
|
+
for (const ip of extraIps) {
|
|
2712
|
+
if (!served(ip)) continue
|
|
2713
|
+
exported.add(ip)
|
|
2714
|
+
extra.push(`${ip}:${link(ip)}`)
|
|
2715
|
+
}
|
|
2716
|
+
// Selected sites a CLOSURE piece may call: every selected child of an
|
|
2717
|
+
// unselected reachable site. Linking one may lower it for the first time,
|
|
2718
|
+
// when only the closure side reaches it.
|
|
2719
|
+
if (select !== undefined) {
|
|
2720
|
+
const kids: number[] = []
|
|
2721
|
+
for (const ip of reachable) {
|
|
2722
|
+
if (select.has(resolveAlias(ip))) continue
|
|
2723
|
+
kids.length = 0
|
|
2724
|
+
childSlots(code, ip, kids)
|
|
2725
|
+
for (const kid of kids) {
|
|
2726
|
+
const target = resolveAlias(kid)
|
|
2727
|
+
if (!select.has(target) || exported.has(target)) continue
|
|
2728
|
+
exported.add(target)
|
|
2729
|
+
extra.push(`${target}:${link(target)}`)
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
const hybrid = select === undefined ? '' : `,
|
|
2734
|
+
ext:[${externals.join(',')}],
|
|
2735
|
+
bind:function(xs,ps){_xScan=xs
|
|
2736
|
+
${externals.map((ip, i) => `_xp${ip}=ps[${i}]`).join('\n')}},
|
|
2737
|
+
scan:function(s){const o=_pfScan;_pfScan=s;return o}`
|
|
2738
|
+
// One wrapper per external: the closure engine reads its OWN installed trivia
|
|
2739
|
+
// scanner, so the emitted one is handed across for the call and restored. A
|
|
2740
|
+
// site whose label names its trivia hands that trivia's own scanner, which is
|
|
2741
|
+
// what any scope installing it would have installed: the emitted slot is read
|
|
2742
|
+
// only where the labels cannot say (`site-labels.ts` `scanHandoffNeeded`).
|
|
2743
|
+
const scannerFor = (ip: number): string => {
|
|
2744
|
+
const ki = labels.at(ip).tri
|
|
2745
|
+
if (ki === TRI_UNKNOWN) return '_pfScan'
|
|
2746
|
+
if (ki === TRI_NONE) return 'null'
|
|
2747
|
+
return swapLegal && !triviaLabelled[ki]! && t.triviaScan[ki] != null ? hoist('ts', `TRIVIASCAN[${ki}]`) : 'null'
|
|
2748
|
+
}
|
|
2749
|
+
const externalDefs = select === undefined ? '' : `let _xScan
|
|
2750
|
+
${externals.map(ip => `let _xp${ip}
|
|
2751
|
+
function _px${ip}(input,pos,ctx){const o=_xScan(${scannerFor(ip)});const v=_xp${ip}(input,pos,ctx);_xScan(o);return v}`).join('\n')}
|
|
2752
|
+
`
|
|
2551
2753
|
|
|
2552
2754
|
// The per-label trivia scans sit AFTER the hoisted pool they close over: they
|
|
2553
2755
|
// are `function` declarations, so a body may call one that is textually below
|
|
@@ -2558,7 +2760,7 @@ ${prelude.join('\n')}
|
|
|
2558
2760
|
${skipDefs.join('\n')}
|
|
2559
2761
|
${choiceDefs.join('\n')}
|
|
2560
2762
|
${bodies.join('\n')}
|
|
2561
|
-
function _begin(ctx){
|
|
2763
|
+
${externalDefs}function _begin(ctx){
|
|
2562
2764
|
const host=ctx.build
|
|
2563
2765
|
if(_pfDepth>0)_pfFrames.push([_pfScan,_pfHost,EC.e])
|
|
2564
2766
|
_pfDepth++
|
|
@@ -2585,11 +2787,11 @@ pieces:{${ruleEntries.join(',')}},
|
|
|
2585
2787
|
byIp:{${extra.join(',')}},
|
|
2586
2788
|
end:function(){return EC.e},
|
|
2587
2789
|
begin:_begin,
|
|
2588
|
-
finish:_finish
|
|
2790
|
+
finish:_finish${hybrid}
|
|
2589
2791
|
}`
|
|
2590
2792
|
|
|
2591
2793
|
return {
|
|
2592
|
-
source, reached, masks, classes, armExpected,
|
|
2794
|
+
source, reached, masks, classes, armExpected, siteBytes,
|
|
2593
2795
|
plan: { classes: classPlan, armExpected: armExpectedPlan, masks: maskPlan },
|
|
2594
2796
|
}
|
|
2595
2797
|
}
|
package/src/table/emit.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assemblyExtraIps } from './program.ts'
|
|
2
2
|
import type { FoldedProgram, TableProgram } from './program.ts'
|
|
3
3
|
import { resolveTable } from './program.ts'
|
|
4
4
|
import { EMITTED_PARAMS, Unemittable, emitAssemblySource } from './emit-assembly.ts'
|
|
@@ -35,7 +35,9 @@ function emitClassPool(classes: readonly string[]): string {
|
|
|
35
35
|
if (classes.length < 2) return array
|
|
36
36
|
const delimiter = '|/~?^`'.split('').find(ch => classes.every(spec => !spec.includes(ch)))
|
|
37
37
|
if (delimiter === undefined) return array
|
|
38
|
-
|
|
38
|
+
// A call a bundler cannot prove pure is kept even when nothing reads its result,
|
|
39
|
+
// so a sibling variant shaken out of a bundle left its whole pool behind.
|
|
40
|
+
const split = `/* @__PURE__ */ ${jsString(classes.join(delimiter))}.split(${jsString(delimiter)})`
|
|
39
41
|
return split.length < array.length ? split : array
|
|
40
42
|
}
|
|
41
43
|
|
|
@@ -170,7 +172,7 @@ export function defaultAssemblyCfgs(prog: TableProgram): RunCfg[] {
|
|
|
170
172
|
* not lower; that option set simply gets no entry, and `assemble.ts` runs the
|
|
171
173
|
* closure engine for it and RECORDS why on `Assembly.emitRefusal`.
|
|
172
174
|
*/
|
|
173
|
-
function emitAssemblies(prog: TableProgram, cfgs: readonly
|
|
175
|
+
function emitAssemblies(prog: TableProgram, cfgs: readonly AssemblyCfg[]): string[] {
|
|
174
176
|
/**
|
|
175
177
|
* `a:[]` IS NOT `a` ABSENT, and the difference is the whole property.
|
|
176
178
|
*
|
|
@@ -201,9 +203,7 @@ function emitAssemblies(prog: TableProgram, cfgs: readonly RunCfg[]): string[] {
|
|
|
201
203
|
const t = resolveTable(prog)
|
|
202
204
|
// The scan pool and the scan-skip sets are linked from subtrees, so their
|
|
203
205
|
// sites need emitted names too — same list `assemble.ts` builds.
|
|
204
|
-
const extraIps
|
|
205
|
-
for (const s of prog.scans ?? []) for (const r of scanRefs(s)) extraIps.push(r[0])
|
|
206
|
-
for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0])
|
|
206
|
+
const extraIps = assemblyExtraIps(prog)
|
|
207
207
|
|
|
208
208
|
const out: string[] = []
|
|
209
209
|
const seen = new Set<number>()
|
|
@@ -213,7 +213,7 @@ function emitAssemblies(prog: TableProgram, cfgs: readonly RunCfg[]): string[] {
|
|
|
213
213
|
seen.add(key)
|
|
214
214
|
let em
|
|
215
215
|
try {
|
|
216
|
-
em = emitAssemblySource(t, prog, cfg, extraIps, true)
|
|
216
|
+
em = emitAssemblySource(t, prog, cfg, extraIps, true, cfg.select)
|
|
217
217
|
} catch (e) {
|
|
218
218
|
if (e instanceof Unemittable) continue
|
|
219
219
|
throw e
|
|
@@ -233,6 +233,9 @@ function emitAssemblies(prog: TableProgram, cfgs: readonly RunCfg[]): string[] {
|
|
|
233
233
|
return [`a:[${out.join(',')}],`]
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
/** One pre-compiled option set; `select` makes it SELECTIVE (see `emitAssemblySource`). */
|
|
237
|
+
export type AssemblyCfg = RunCfg & { readonly select?: ReadonlySet<number> }
|
|
238
|
+
|
|
236
239
|
export type EmitOptions = {
|
|
237
240
|
/** Name of the exported binding. */
|
|
238
241
|
readonly name?: string
|
|
@@ -244,7 +247,7 @@ export type EmitOptions = {
|
|
|
244
247
|
* Supplying factories here is a low-level serialization experiment, not a
|
|
245
248
|
* second normal compilation path; it is not used by the macro plugin.
|
|
246
249
|
*/
|
|
247
|
-
readonly assemblies?: readonly
|
|
250
|
+
readonly assemblies?: readonly AssemblyCfg[]
|
|
248
251
|
/**
|
|
249
252
|
* Sources for the author callbacks, in `prog.fns` order. A build has these
|
|
250
253
|
* from the module it is lowering; pass `undefined` to emit a placeholder and
|