parseman 0.47.1 → 0.48.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -21
- package/dist/analysis/diagnostics.js +2964 -347
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +2960 -343
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/expect.d.ts +1 -0
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/expected.d.ts +8 -0
- package/dist/combinators/expected.d.ts.map +1 -0
- package/dist/combinators/first-set.d.ts +8 -0
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/keywords.d.ts +4 -0
- package/dist/combinators/keywords.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/not.d.ts.map +1 -1
- package/dist/combinators/peek.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/sequence.d.ts.map +1 -1
- package/dist/combinators/trivia-skip.d.ts +16 -3
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/compiler/direct-projection.d.ts +12 -0
- package/dist/compiler/direct-projection.d.ts.map +1 -0
- package/dist/compiler/token-alphabet.d.ts +845 -5
- package/dist/compiler/token-alphabet.d.ts.map +1 -1
- package/dist/compiler/token-capability.d.ts +3 -0
- package/dist/compiler/token-capability.d.ts.map +1 -0
- package/dist/compiler/token-capability.js +3192 -0
- package/dist/compiler/token-capability.js.map +6 -0
- package/dist/cst/trivia-charscan.d.ts +3 -0
- package/dist/cst/trivia-charscan.d.ts.map +1 -1
- package/dist/cst/trivia-css-scanner.d.ts +5 -0
- package/dist/cst/trivia-css-scanner.d.ts.map +1 -0
- package/dist/cst/trivia-kinds.d.ts.map +1 -1
- package/dist/index.js +2973 -343
- package/dist/index.js.map +3 -3
- package/dist/language-service/index.js +5 -1
- package/dist/language-service/index.js.map +1 -1
- package/dist/plugin/index.js +2982 -352
- package/dist/plugin/index.js.map +3 -3
- package/dist/spec/index.js +325 -47
- package/dist/spec/index.js.map +3 -3
- package/dist/table/assemble.d.ts.map +1 -1
- package/dist/table/child-slots.d.ts.map +1 -1
- package/dist/table/compile-rule-map.d.ts +3 -1
- package/dist/table/compile-rule-map.d.ts.map +1 -1
- package/dist/table/emit-assembly.d.ts +12 -13
- package/dist/table/emit-assembly.d.ts.map +1 -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/index.js +2937 -311
- package/dist/table/index.js.map +3 -3
- package/dist/table/lex-body.d.ts +9 -0
- package/dist/table/lex-body.d.ts.map +1 -0
- package/dist/table/lex-program.d.ts +25 -0
- package/dist/table/lex-program.d.ts.map +1 -0
- package/dist/table/ops.d.ts +47 -11
- package/dist/table/ops.d.ts.map +1 -1
- package/dist/table/program.d.ts +50 -2
- package/dist/table/program.d.ts.map +1 -1
- package/dist/table/scalar-terminal.d.ts +13 -0
- package/dist/table/scalar-terminal.d.ts.map +1 -0
- package/dist/table/site-labels.d.ts.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +11 -11
- package/src/combinators/choice.ts +10 -1
- package/src/combinators/expect.ts +1 -0
- package/src/combinators/expected.ts +17 -0
- package/src/combinators/first-set.ts +58 -5
- package/src/combinators/grammar.ts +8 -3
- package/src/combinators/keywords.ts +22 -6
- package/src/combinators/literal.ts +2 -1
- package/src/combinators/not.ts +5 -1
- package/src/combinators/peek.ts +5 -1
- package/src/combinators/regex.ts +5 -3
- package/src/combinators/repeat.ts +4 -10
- package/src/combinators/sequence.ts +17 -14
- package/src/combinators/trivia-skip.ts +46 -13
- package/src/compiler/direct-projection.ts +43 -0
- package/src/compiler/token-alphabet.ts +4316 -12
- package/src/compiler/token-capability.ts +6 -0
- package/src/cst/trivia-charscan.ts +34 -0
- package/src/cst/trivia-css-scanner.ts +142 -0
- package/src/cst/trivia-kinds.ts +3 -1
- package/src/plugin/index.ts +4 -4
- package/src/table/assemble.ts +1253 -240
- package/src/table/child-slots.ts +2 -1
- package/src/table/compile-rule-map.ts +21 -3
- package/src/table/emit-assembly.ts +335 -87
- package/src/table/emit.ts +16 -2
- package/src/table/encode.ts +248 -60
- package/src/table/exec-baseline.ts +3 -7
- package/src/table/exec.ts +134 -25
- package/src/table/lex-body.ts +30 -0
- package/src/table/lex-program.ts +431 -0
- package/src/table/ops.ts +49 -11
- package/src/table/program.ts +46 -2
- package/src/table/scalar-terminal.ts +51 -0
- package/src/table/site-labels.ts +11 -1
- package/src/table/stamp.ts +1 -1
- package/src/types.ts +1 -1
- package/src/version.ts +1 -1
- package/dist/analysis/diagnostics.cjs +0 -13824
- package/dist/analysis/diagnostics.cjs.map +0 -6
- package/dist/cli/index.cjs +0 -14925
- package/dist/cli/index.cjs.map +0 -6
- package/dist/index.cjs +0 -16391
- package/dist/index.cjs.map +0 -6
- package/dist/language-service/index.cjs +0 -1304
- package/dist/language-service/index.cjs.map +0 -6
- package/dist/oracle/index.cjs +0 -254
- package/dist/oracle/index.cjs.map +0 -6
- package/dist/plugin/index.cjs +0 -16097
- package/dist/plugin/index.cjs.map +0 -6
- package/dist/run/index.cjs +0 -577
- package/dist/run/index.cjs.map +0 -6
- package/dist/spec/index.cjs +0 -6236
- package/dist/spec/index.cjs.map +0 -6
- package/dist/table/index.cjs +0 -11463
- package/dist/table/index.cjs.map +0 -6
|
@@ -9,16 +9,11 @@
|
|
|
9
9
|
*
|
|
10
10
|
* ## Why text, and not a rearrangement of `assemble.ts`
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* megamorphic — a hash lookup in the megamorphic stub cache, no inlining, no
|
|
18
|
-
* type feedback for the callee body. No rearrangement of a file that MINTS
|
|
19
|
-
* closures can produce a distinct literal per site. Emitting one
|
|
20
|
-
* `function _pf<N>` per site can: inside `_pf12` the call `_pf7(…)` names ONE
|
|
21
|
-
* binding and stays monomorphic.
|
|
12
|
+
* Emission makes each fixed grammar edge a direct generated identifier and is
|
|
13
|
+
* also the form a build can serialise as an ordinary function literal for CSP.
|
|
14
|
+
* The closure assembler can bind the same topology directly through scalar
|
|
15
|
+
* captures; emission's distinct job is to print that binding, not to assert a
|
|
16
|
+
* V8 property that the semantic design depends on.
|
|
22
17
|
*
|
|
23
18
|
* ## What may be shared, and what must be emitted
|
|
24
19
|
*
|
|
@@ -55,12 +50,14 @@ import {
|
|
|
55
50
|
OP_LABEL, OP_LEAF, OP_LIT, OP_LIT_CI, OP_LIT_CI_TRACK, OP_LIT_TRACK, OP_NAMES,
|
|
56
51
|
OP_NODE, OP_NODE_TRACK, OP_NOT, OP_OPT, OP_PEEK, OP_REP, OP_REPV, OP_ROUTED, OP_RULE, OP_RX,
|
|
57
52
|
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_LEX_BODY, OP_LEX_PROGRAM,
|
|
58
54
|
} from './ops.ts'
|
|
59
55
|
import { validateDispatchSpec, type ResolvedClass, type ResolvedTable, type TableProgram } from './program.ts'
|
|
60
56
|
import { emitShapeMatch, scanShapeFromRegex } from './scan-shapes.ts'
|
|
61
57
|
import {
|
|
62
|
-
CAP_OFF, CAP_ON, TRI_NONE, TRI_UNKNOWN,
|
|
58
|
+
CAP_OFF, CAP_ON, TRI_NONE, TRI_UNKNOWN, computeSiteLabels, reachableSites, type SiteLabel,
|
|
63
59
|
} from './site-labels.ts'
|
|
60
|
+
import { scalarTerminalNodeChild, scalarTerminalNotChild } from './scalar-terminal.ts'
|
|
64
61
|
|
|
65
62
|
/** What the compiled factory hands back — the emitted twin of `Assembly`. */
|
|
66
63
|
export type EmittedPiece = (input: string, pos: number, ctx: ParseContext) => unknown
|
|
@@ -106,6 +103,16 @@ export const EMITTED_PARAMS = [
|
|
|
106
103
|
// span, expected set and CST embedding are produced by the SAME code in every
|
|
107
104
|
// engine and cannot drift.
|
|
108
105
|
'SENTS', 'matchesAt', 'recoverScan', 'orSentinel', 'captureError',
|
|
106
|
+
// Pure scalar terminal recognizers, indexed by the terminal's existing const
|
|
107
|
+
// operand. Appended for compatibility with older precompiled factories.
|
|
108
|
+
'RECOG',
|
|
109
|
+
// Appended so precompiled factories produced by earlier runtimes keep every
|
|
110
|
+
// positional helper binding. Older factories ignore this trailing argument.
|
|
111
|
+
'commitTriviaScan', 'scanTriviaCompact', 'LEX',
|
|
112
|
+
// Appended for old precompiled-factory ABI: old factories ignore it.
|
|
113
|
+
'adjacencyHolds',
|
|
114
|
+
// Appended for selected composite lexical programs; old factories ignore it.
|
|
115
|
+
'LEXPROG',
|
|
109
116
|
] as const
|
|
110
117
|
|
|
111
118
|
/**
|
|
@@ -131,7 +138,7 @@ function _skipTrivia(input,cur,ctx){
|
|
|
131
138
|
const s=_pfScan
|
|
132
139
|
if(s!==null&&ctx._triviaLog===undefined&&!(ctx.captureTrivia===true&&(ctx._cstBuf!==undefined||ctx._cstTriviaLog!==undefined)))return s(input,cur)
|
|
133
140
|
if(s!==null)return skipTriviaScanned(s,input,cur,ctx)
|
|
134
|
-
if(needsDeferredTriviaCommit(ctx))
|
|
141
|
+
if(needsDeferredTriviaCommit(ctx))return commitTriviaScan(scanTriviaCompact(input,cur,ctx))
|
|
135
142
|
return advanceTrivia(input,cur,ctx)
|
|
136
143
|
}
|
|
137
144
|
function _pushLeaf(ctx,value,s,e){pushCstLeaf(ctx,{_tag:'leaf',value,span:{start:s,end:e}})}
|
|
@@ -145,6 +152,13 @@ if(b.raw!==undefined)b.raw.push(l)
|
|
|
145
152
|
else if(b.rawSingle!==undefined){b.raw=[b.rawSingle,l];b.rawSingle=undefined}
|
|
146
153
|
else b.rawSingle=l
|
|
147
154
|
}
|
|
155
|
+
function _capturedFlatChildren(children){
|
|
156
|
+
if(children.length===0)return EMPTY_CH
|
|
157
|
+
if(children[0]!==undefined)return children
|
|
158
|
+
let first=1
|
|
159
|
+
while(first<children.length&&children[first]===undefined)first++
|
|
160
|
+
return first===children.length?EMPTY_CH:children.slice(first)
|
|
161
|
+
}
|
|
148
162
|
function _accSet(ax,acc){
|
|
149
163
|
if(ax===undefined||ax.length===0)return acc
|
|
150
164
|
if(acc===undefined)return ax.slice()
|
|
@@ -370,7 +384,11 @@ export type PoolPlan = {
|
|
|
370
384
|
readonly classes: readonly (readonly number[])[]
|
|
371
385
|
/** Per `AFX` row: the `fx` index of each arm's expected set. */
|
|
372
386
|
readonly armExpected: readonly (readonly number[])[]
|
|
373
|
-
/**
|
|
387
|
+
/**
|
|
388
|
+
* Per `MASK` row: a non-negative legacy `CLS` row index, or `~dispIndex` for
|
|
389
|
+
* a directly bound choice. The negative form names the EXISTING resolved
|
|
390
|
+
* dispatch row rather than serialising a second copy of its arm classes.
|
|
391
|
+
*/
|
|
374
392
|
readonly masks: readonly number[]
|
|
375
393
|
}
|
|
376
394
|
|
|
@@ -408,13 +426,18 @@ function maskForClassRow(gates: readonly (ResolvedClass | null)[]): Uint32Array
|
|
|
408
426
|
export function rebuildPools(
|
|
409
427
|
cc: readonly ResolvedClass[],
|
|
410
428
|
fx: readonly (readonly string[])[],
|
|
429
|
+
disp: ResolvedTable['disp'],
|
|
411
430
|
plan: PoolPlan,
|
|
412
431
|
): { masks: Uint32Array[]; classes: (ResolvedClass | null)[][]; armExpected: (readonly string[])[][] } {
|
|
413
432
|
const classes = plan.classes.map(row => row.map(i => (i < 0 ? null : cc[i] ?? null)))
|
|
414
433
|
return {
|
|
415
434
|
classes,
|
|
416
435
|
armExpected: plan.armExpected.map(row => row.map(i => fx[i] ?? [])),
|
|
417
|
-
masks: plan.masks.map(
|
|
436
|
+
masks: plan.masks.map(source => {
|
|
437
|
+
const row = source >= 0 ? classes[source] : disp[~source]?.armCls
|
|
438
|
+
if (row === undefined) throw new TypeError(`table emitter: invalid MASK plan source ${source}`)
|
|
439
|
+
return maskForClassRow(row)
|
|
440
|
+
}),
|
|
418
441
|
}
|
|
419
442
|
}
|
|
420
443
|
|
|
@@ -476,6 +499,17 @@ export function emitAssemblySource(
|
|
|
476
499
|
// emitted scope supplies a context this pass cannot see.
|
|
477
500
|
const roots = [...Object.values(prog.rules), ...extraIps]
|
|
478
501
|
const labels = computeSiteLabels(code, roots, hostCst)
|
|
502
|
+
// Eligibility is pooled by the terminal's existing constant operand, matching
|
|
503
|
+
// the closure recognizer pool: distinct terminal rows sharing one spec share
|
|
504
|
+
// both the recognizer and the ordinary-terminal lowering.
|
|
505
|
+
const scalarSpecs = new Set<number>()
|
|
506
|
+
if (!hostCst && !cfg.tolerant && !cfg.probe && !cfg.coverage && !cfg.trackLines) {
|
|
507
|
+
for (const ip of reachableSites(code, roots)) {
|
|
508
|
+
if (code[ip] !== OP_NODE) continue
|
|
509
|
+
const child = scalarTerminalNodeChild(code, ip)
|
|
510
|
+
if (child >= 0) scalarSpecs.add(code[child + 1]!)
|
|
511
|
+
}
|
|
512
|
+
}
|
|
479
513
|
|
|
480
514
|
// THE SIDE-SINK CENSUS, over the same reachable set the labels are computed
|
|
481
515
|
// from. `OP_FIELD` is the only ROW that writes `_fields` and `OP_EXPECT` the
|
|
@@ -520,6 +554,7 @@ export function emitAssemblySource(
|
|
|
520
554
|
const reached = new Set<number>()
|
|
521
555
|
const prelude: string[] = []
|
|
522
556
|
const skipDefs: string[] = []
|
|
557
|
+
const choiceDefs: string[] = []
|
|
523
558
|
const skipPool = new Map<string, string>()
|
|
524
559
|
const pool = new Map<string, string>()
|
|
525
560
|
const masks: Uint32Array[] = []
|
|
@@ -548,6 +583,7 @@ export function emitAssemblySource(
|
|
|
548
583
|
const kRef = (i: number): string => hoist('k', `K[${i}]`)
|
|
549
584
|
const fxRef = (i: number): string => hoist('fx', `FX[${i}]`)
|
|
550
585
|
const fnRef = (i: number): string => hoist('fn', `FNS[${i}]`)
|
|
586
|
+
const recognizerRef = (i: number): string => hoist('rec', `RECOG[${i}]`)
|
|
551
587
|
/**
|
|
552
588
|
* The sync sentinel for a char-class index, hoisted once per class.
|
|
553
589
|
*
|
|
@@ -598,9 +634,9 @@ export function emitAssemblySource(
|
|
|
598
634
|
// through `scanTrivia`. `needsDeferredTriviaCommit` is implied by an open
|
|
599
635
|
// buffer, so an in-node site skips the call that asks.
|
|
600
636
|
skipDefs.push(l.buf
|
|
601
|
-
? `function ${nm}(input,cur,ctx){
|
|
637
|
+
? `function ${nm}(input,cur,ctx){return commitTriviaScan(scanTriviaCompact(input,cur,ctx))}`
|
|
602
638
|
: `function ${nm}(input,cur,ctx){
|
|
603
|
-
if(needsDeferredTriviaCommit(ctx))
|
|
639
|
+
if(needsDeferredTriviaCommit(ctx))return commitTriviaScan(scanTriviaCompact(input,cur,ctx))
|
|
604
640
|
return advanceTrivia(input,cur,ctx)}`)
|
|
605
641
|
return nm
|
|
606
642
|
}
|
|
@@ -734,17 +770,17 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)\n` : ''}return FAIL
|
|
|
734
770
|
if (op === OP_GATE && (cfg.tolerant || cfg.probe)) return code[ip + 2]!
|
|
735
771
|
if (op === OP_RULE) return code[ip + 1]!
|
|
736
772
|
// A SCOPE THAT INSTALLS WHAT IS ALREADY INSTALLED. `encode.ts:520` wraps
|
|
737
|
-
// EVERY rule of a `rules({ trivia }, …)` map in its own `
|
|
773
|
+
// EVERY rule of a `rules({ trivia }, …)` map in its own `OP_SCOPE_PLAIN`, so a
|
|
738
774
|
// grammar with one ambient trivia re-installs the same slot at every rule
|
|
739
775
|
// entry — six context stores, a scanner swap and their six restores, per
|
|
740
776
|
// call, to arrive at the values already there.
|
|
741
777
|
//
|
|
742
778
|
// The label is what makes this decidable: `tri >= 0` can ONLY have come from
|
|
743
|
-
// an enclosing
|
|
779
|
+
// an enclosing scope carrying that slot, and that scope set
|
|
744
780
|
// `ctx.triviaKindLabels` and `_pfScan` from the same slot, so all three are
|
|
745
|
-
// already the values this row would write.
|
|
746
|
-
//
|
|
747
|
-
//
|
|
781
|
+
// already the values this row would write. `OP_SCOPE_PLAIN` has no root
|
|
782
|
+
// policy by construction; a policy-bearing `OP_SCOPE` aliases only when its
|
|
783
|
+
// literal policy is zero. `OP_SCOPE_CAP` also raises `captureTrivia`.
|
|
748
784
|
//
|
|
749
785
|
// `ki >= 0` is required rather than implied: `TRI_NONE` and `TRI_UNKNOWN` are
|
|
750
786
|
// themselves negative, so comparing a negative operand against a lattice
|
|
@@ -788,6 +824,19 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)\n` : ''}return FAIL
|
|
|
788
824
|
const s = k[code[ip + 1]!] as string
|
|
789
825
|
const xf = fxRef(code[ip + 2]!)
|
|
790
826
|
const track = op === OP_LIT_TRACK
|
|
827
|
+
if (!track && scalarSpecs.has(code[ip + 1]!)) {
|
|
828
|
+
const recognize = recognizerRef(code[ip + 1]!)
|
|
829
|
+
return `${head}
|
|
830
|
+
const e=${recognize}(input,pos)
|
|
831
|
+
if(e>=0){
|
|
832
|
+
${captureLeaf(q(s))}
|
|
833
|
+
EC.e=e
|
|
834
|
+
return ${q(s)}
|
|
835
|
+
}
|
|
836
|
+
ctx._fe=pos;ctx._fx=${xf}
|
|
837
|
+
return FAIL
|
|
838
|
+
}`
|
|
839
|
+
}
|
|
791
840
|
// LENGTH IS TABLE DATA, so the compare is chosen here — the emitted twin
|
|
792
841
|
// of the closure engine's length-keyed literal bodies.
|
|
793
842
|
const test = s.length === 1
|
|
@@ -862,6 +911,20 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)\n` : ''}return FAIL
|
|
|
862
911
|
case OP_RX_TRACK: {
|
|
863
912
|
const xf = fxRef(code[ip + 2]!)
|
|
864
913
|
const track = op === OP_RX_TRACK
|
|
914
|
+
if (!track && scalarSpecs.has(code[ip + 1]!)) {
|
|
915
|
+
const recognize = recognizerRef(code[ip + 1]!)
|
|
916
|
+
return `${head}
|
|
917
|
+
const e=${recognize}(input,pos)
|
|
918
|
+
if(e>=0){
|
|
919
|
+
const v=input.slice(pos,e)
|
|
920
|
+
${captureLeaf('v')}
|
|
921
|
+
EC.e=e
|
|
922
|
+
return v
|
|
923
|
+
}
|
|
924
|
+
ctx._fe=pos;ctx._fx=${xf}
|
|
925
|
+
return FAIL
|
|
926
|
+
}`
|
|
927
|
+
}
|
|
865
928
|
// THE MATCH ARRAY IS THE COST, not the matching. `re.exec` allocates one
|
|
866
929
|
// per row — 6,005 rows per `json/document` parse, 12.9% of everything
|
|
867
930
|
// executed — and every field of it but `[0]` is discarded here. A shape
|
|
@@ -1109,6 +1172,75 @@ return out
|
|
|
1109
1172
|
}`
|
|
1110
1173
|
}
|
|
1111
1174
|
|
|
1175
|
+
case OP_LEX_BODY: {
|
|
1176
|
+
const recognize = hoist('lex', `LEX[${code[ip + 1]!}]`)
|
|
1177
|
+
const expected = fxRef(code[ip + 2]!)
|
|
1178
|
+
const suffixExpected = fxRef(code[ip + 3]!)
|
|
1179
|
+
const lineFlags = code[ip + 4]!
|
|
1180
|
+
const hasSuffix = (lineFlags & 4) !== 0
|
|
1181
|
+
return `${head}
|
|
1182
|
+
const r=${recognize}(input,pos)
|
|
1183
|
+
if(r<0){ctx._fe=pos;ctx._fx=${expected};if(ctx._probe!==undefined)failAt(ctx,${expected},pos);return FAIL}
|
|
1184
|
+
const sm=${hasSuffix ? 'r%2===1' : 'false'},e=(r-(sm?1:0))/2
|
|
1185
|
+
${(lineFlags & 1) !== 0 ? '_trackLines(ctx,input,sm?e-1:e)' : ''}
|
|
1186
|
+
${hasSuffix ? 'ctx._fc=false' : ''}
|
|
1187
|
+
${hasSuffix && (lineFlags & 2) !== 0 ? 'if(sm)_trackLines(ctx,input,e)' : ''}
|
|
1188
|
+
${hasSuffix ? `if(!sm){ctx._fe=e;ctx._fx=${suffixExpected};if(ctx._probe!==undefined)failAt(ctx,${suffixExpected},e)}` : ''}
|
|
1189
|
+
const v=input.slice(pos,e)
|
|
1190
|
+
if(ctx._cstBuf!==undefined||ctx._cstLeaves!==undefined)pushCstLeaf(ctx,{_tag:'leaf',value:v,span:{start:pos,end:e}})
|
|
1191
|
+
EC.e=e
|
|
1192
|
+
return v
|
|
1193
|
+
}`
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
case OP_LEX_PROGRAM: {
|
|
1197
|
+
const programId = code[ip + 1]!
|
|
1198
|
+
const run = hoist('lexProgram', `LEXPROG[${programId}]`)
|
|
1199
|
+
const scanId = t.lexPrograms[programId]!.scan
|
|
1200
|
+
if (scanId !== undefined) return `${head}
|
|
1201
|
+
const sTri=ctx.trivia,sKinds=ctx.triviaKindLabels,sScan=_pfScan
|
|
1202
|
+
const sBuf=ctx._cstBuf,sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
|
|
1203
|
+
const sOtl=ctx._triviaLog,sRtl=ctx._rootTriviaLog
|
|
1204
|
+
const wasCap=ctx._cstBuf!==undefined||ctx._cstLeaves!==undefined
|
|
1205
|
+
_pfScan=null
|
|
1206
|
+
ctx.trivia=undefined
|
|
1207
|
+
ctx.triviaKindLabels=undefined
|
|
1208
|
+
ctx._cstBuf=undefined
|
|
1209
|
+
ctx._cstChildren=undefined
|
|
1210
|
+
ctx._cstLeaves=undefined
|
|
1211
|
+
ctx._cstRawChildren=undefined
|
|
1212
|
+
ctx._cstTriviaLog=undefined
|
|
1213
|
+
ctx._triviaLog=undefined
|
|
1214
|
+
ctx._rootTriviaLog=undefined
|
|
1215
|
+
let e
|
|
1216
|
+
try{e=${run}(input,pos,ctx,SCANS[${scanId}])}finally{
|
|
1217
|
+
_pfScan=sScan
|
|
1218
|
+
ctx.trivia=sTri
|
|
1219
|
+
ctx.triviaKindLabels=sKinds
|
|
1220
|
+
ctx._cstBuf=sBuf
|
|
1221
|
+
ctx._cstChildren=sCh
|
|
1222
|
+
ctx._cstLeaves=sLv
|
|
1223
|
+
ctx._cstRawChildren=sRaw
|
|
1224
|
+
ctx._cstTriviaLog=sTl
|
|
1225
|
+
ctx._triviaLog=sOtl
|
|
1226
|
+
ctx._rootTriviaLog=sRtl
|
|
1227
|
+
}
|
|
1228
|
+
if(e<0)return FAIL
|
|
1229
|
+
const v=input.slice(pos,e)
|
|
1230
|
+
if(wasCap)pushCstLeaf(ctx,{_tag:'leaf',value:v,span:{start:pos,end:e}})
|
|
1231
|
+
EC.e=e
|
|
1232
|
+
return v
|
|
1233
|
+
}`
|
|
1234
|
+
return `${head}
|
|
1235
|
+
const e=${run}(input,pos,ctx)
|
|
1236
|
+
if(e<0)return FAIL
|
|
1237
|
+
const v=input.slice(pos,e)
|
|
1238
|
+
if(ctx._cstBuf!==undefined||ctx._cstLeaves!==undefined)pushCstLeaf(ctx,{_tag:'leaf',value:v,span:{start:pos,end:e}})
|
|
1239
|
+
EC.e=e
|
|
1240
|
+
return v
|
|
1241
|
+
}`
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1112
1244
|
/* ── transaction ─────────────────────────────────────────────────────── */
|
|
1113
1245
|
|
|
1114
1246
|
case OP_ATTEMPT: {
|
|
@@ -1126,6 +1258,18 @@ return FAIL
|
|
|
1126
1258
|
}
|
|
1127
1259
|
|
|
1128
1260
|
case OP_NOT: {
|
|
1261
|
+
const scalarChild = scalarTerminalNotChild(code, ip)
|
|
1262
|
+
if (scalarChild >= 0) {
|
|
1263
|
+
const recognize = recognizerRef(code[scalarChild + 1]!)
|
|
1264
|
+
const xf = fxRef(code[ip + 2]!)
|
|
1265
|
+
return `${head}
|
|
1266
|
+
if(${recognize}(input,pos)<0){EC.e=pos;return null}
|
|
1267
|
+
ctx._fe=pos
|
|
1268
|
+
ctx._fx=${xf}
|
|
1269
|
+
EC.e=pos
|
|
1270
|
+
return FAIL
|
|
1271
|
+
}`
|
|
1272
|
+
}
|
|
1129
1273
|
const child = link(code[ip + 1]!)
|
|
1130
1274
|
const xf = fxRef(code[ip + 2]!)
|
|
1131
1275
|
const p = tmp()
|
|
@@ -1178,19 +1322,14 @@ return v
|
|
|
1178
1322
|
const base = fused ? ip + 3 : ip + 2
|
|
1179
1323
|
const n = code[fused ? ip + 2 : ip + 1]!
|
|
1180
1324
|
const wantValues = op !== OP_SEQV
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
// position finds the gap already consumed and answers "adjacent" every
|
|
1185
|
-
// time, SILENTLY (`ops.ts:226-251`).
|
|
1186
|
-
for (let i = 1; i < n; i++) {
|
|
1187
|
-
if (code[code[base + i]!] === OP_ADJ) {
|
|
1188
|
-
throw new Unemittable('a sequence carrying an adjacency assertion (OP_ADJ)')
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
const fn = fused ? fnRef(code[ip + 1]!) : undefined
|
|
1325
|
+
const reducer = fused ? code[ip + 1]! : -1
|
|
1326
|
+
const projection = reducer < 0 ? ~reducer : -1
|
|
1327
|
+
const fn = fused && projection < 0 ? fnRef(reducer) : undefined
|
|
1192
1328
|
const kids: string[] = []
|
|
1193
|
-
for (let i = 0; i < n; i++)
|
|
1329
|
+
for (let i = 0; i < n; i++) {
|
|
1330
|
+
const childIp = code[base + i]!
|
|
1331
|
+
kids.push(code[childIp] === OP_ADJ ? '' : link(childIp))
|
|
1332
|
+
}
|
|
1194
1333
|
/**
|
|
1195
1334
|
* A RECOVERY SEQUENCE PUBLISHES `ctx._sync`, and that is the WHOLE of
|
|
1196
1335
|
* "recovery config" — no grammar carries any (`assemble.ts:1446`).
|
|
@@ -1225,7 +1364,9 @@ return v
|
|
|
1225
1364
|
parts.push(`const v0=${kids[0]}(input,pos,ctx)`, 'if(v0===FAIL)return FAIL')
|
|
1226
1365
|
const close = (): string => REC ? `${parts.join('\n')}\n}finally{ctx._sync=${sy}}\n}` : `${parts.join('\n')}\n}`
|
|
1227
1366
|
if (n === 1) {
|
|
1228
|
-
parts.push(fused
|
|
1367
|
+
parts.push(fused
|
|
1368
|
+
? projection === 0 ? 'return v0' : `return ${fn}([v0],{start:pos,end:EC.e})`
|
|
1369
|
+
: wantValues ? 'return [v0]' : 'return undefined')
|
|
1229
1370
|
return close()
|
|
1230
1371
|
}
|
|
1231
1372
|
parts.push('let cur=EC.e')
|
|
@@ -1234,12 +1375,22 @@ return v
|
|
|
1234
1375
|
const vn = `v${i}`
|
|
1235
1376
|
parts.push(`let ${vn}`)
|
|
1236
1377
|
if (REC) parts.push(pub(i))
|
|
1237
|
-
|
|
1378
|
+
const childIp = code[base + i]!
|
|
1379
|
+
if (code[childIp] === OP_ADJ) {
|
|
1380
|
+
const negated = code[childIp + 1] === 1
|
|
1381
|
+
const ki = code[childIp + 2]!
|
|
1382
|
+
const kinds = ki < 0 ? 'undefined' : `K[${ki}]`
|
|
1383
|
+
const expected = fxRef(code[childIp + 3]!)
|
|
1384
|
+
parts.push(
|
|
1385
|
+
`if(!adjacencyHolds(input,cur,ctx,${negated ? 'true' : 'false'},${kinds})){ctx._fe=cur;ctx._fx=${expected};return FAIL}`,
|
|
1386
|
+
`${vn}=null`,
|
|
1387
|
+
)
|
|
1388
|
+
} else parts.push(emitTerm(kids[i]!, vn, tmp(), L, skipFor(L)))
|
|
1238
1389
|
names.push(vn)
|
|
1239
1390
|
}
|
|
1240
1391
|
parts.push('EC.e=cur')
|
|
1241
1392
|
parts.push(fused
|
|
1242
|
-
? `return ${fn}([${names.join(',')}],{start:pos,end:cur})`
|
|
1393
|
+
? projection >= 0 ? `return ${names[projection]}` : `return ${fn}([${names.join(',')}],{start:pos,end:cur})`
|
|
1243
1394
|
: wantValues ? `return [${names.join(',')}]` : 'return undefined')
|
|
1244
1395
|
return close()
|
|
1245
1396
|
}
|
|
@@ -1251,11 +1402,6 @@ return v
|
|
|
1251
1402
|
const base = ip + 4
|
|
1252
1403
|
const arms: string[] = []
|
|
1253
1404
|
for (let i = 0; i < n; i++) arms.push(link(code[base + i]!))
|
|
1254
|
-
const axi = armExpected.push(
|
|
1255
|
-
Array.from({ length: n }, (_, i) => t.fx[code[base + n + i]!] as readonly string[]),
|
|
1256
|
-
) - 1
|
|
1257
|
-
armExpectedPlan.push(Array.from({ length: n }, (_, i) => code[base + n + i]!))
|
|
1258
|
-
const afx = hoist('afx', `AFX[${axi}]`)
|
|
1259
1405
|
|
|
1260
1406
|
if (table.exclusive) {
|
|
1261
1407
|
// NO OPEN ARMS EXIST under `exclusive` — `resolveDispatch` clears the
|
|
@@ -1289,67 +1435,74 @@ return FAIL
|
|
|
1289
1435
|
}`
|
|
1290
1436
|
}
|
|
1291
1437
|
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
//
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
if((bits&${1 << i})!==0){
|
|
1438
|
+
{
|
|
1439
|
+
// Direct topology for every fixed arity. The emitted body names every
|
|
1440
|
+
// child, gate and expected set; no AFX/CLS row survives merely to feed
|
|
1441
|
+
// an indexed winner back into one call site.
|
|
1442
|
+
const di = code[ip + 1]!
|
|
1443
|
+
const expected = Array.from({ length: n }, (_, i) => fxRef(code[base + n + i]!))
|
|
1444
|
+
const gates = Array.from({ length: n }, (_, i) => table.armCls[i] ?? null)
|
|
1445
|
+
const gateRefs = Array.from({ length: n }, (_, i) => hoist('g', `DISP[${di}].armCls[${i}]`))
|
|
1446
|
+
const maskable = n <= 32
|
|
1447
|
+
const maskName = maskable
|
|
1448
|
+
? hoist('mk', `MASK[${masks.push(maskForClassRow(gates)) - 1}]`)
|
|
1449
|
+
: ''
|
|
1450
|
+
// `~di` points back to the authoritative dispatch row. A precompiled
|
|
1451
|
+
// module reconstructs only the input-indexed mask from that row; it
|
|
1452
|
+
// cannot carry a second mutable answer for the arms' classes.
|
|
1453
|
+
if (maskable) maskPlan.push(~di)
|
|
1454
|
+
const p = tmp()
|
|
1455
|
+
const catchName = maskable ? `_cx${uid++}_` : ''
|
|
1456
|
+
if (maskable) {
|
|
1457
|
+
const catchCases = expected.map((e, i) =>
|
|
1458
|
+
`case ${i}:if(target<=${i})return acc;acc=_accSet(${e},acc)`).join('\n')
|
|
1459
|
+
choiceDefs.push(`function ${catchName}(target,prev,acc){switch(prev){\n${catchCases}\n}return acc}`)
|
|
1460
|
+
}
|
|
1461
|
+
const maskArms = maskable ? arms.map((arm, i) => {
|
|
1462
|
+
return `if((bits&${1 << i})!==0){
|
|
1317
1463
|
ctx._fc=false
|
|
1318
|
-
{const v=${
|
|
1464
|
+
{const v=${arm}(input,pos,ctx)
|
|
1319
1465
|
if(v!==FAIL)return v}
|
|
1320
|
-
|
|
1466
|
+
if(best===pos)acc=${catchName}(${i},prev,acc)
|
|
1321
1467
|
prev=${i + 1}
|
|
1322
|
-
|
|
1468
|
+
{const at=ctx._fe??pos
|
|
1469
|
+
if(at>best){best=at;acc=undefined}
|
|
1470
|
+
if(at===best)acc=_accSet(ctx._fx,acc)}
|
|
1323
1471
|
if(ctx._fc===true){if(acc!==undefined)ctx._fx=acc;return FAIL}
|
|
1324
1472
|
${emitRollback(p, L.buf, sinks)}
|
|
1325
|
-
}`
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
if(${gRef}[${i}]===null||classHas(${gRef}[${i}],c)){
|
|
1473
|
+
}`
|
|
1474
|
+
}).join('\n') : ''
|
|
1475
|
+
const generalArms = arms.map((arm, i) => `if(${gateRefs[i]}===null||classHas(${gateRefs[i]},c)){
|
|
1329
1476
|
ctx._fc=false
|
|
1330
|
-
{const v=${
|
|
1477
|
+
{const v=${arm}(input,pos,ctx)
|
|
1331
1478
|
if(v!==FAIL)return v}
|
|
1332
|
-
|
|
1479
|
+
{const at=ctx._fe??pos
|
|
1480
|
+
if(at>best){best=at;acc=undefined}
|
|
1481
|
+
if(at===best)acc=_accSet(ctx._fx,acc)}
|
|
1333
1482
|
if(ctx._fc===true){if(acc!==undefined)ctx._fx=acc;return FAIL}
|
|
1334
1483
|
${emitRollback(p, L.buf, sinks)}
|
|
1335
|
-
}else acc=_accSet(${
|
|
1484
|
+
}else if(best===pos)acc=_accSet(${expected[i]},acc)`).join('\n')
|
|
1336
1485
|
|
|
1337
|
-
|
|
1486
|
+
return `${head}
|
|
1338
1487
|
const c=lead(input,pos)
|
|
1339
1488
|
${emitMark(p, L.buf, sinks)}
|
|
1340
1489
|
let acc
|
|
1490
|
+
let best=pos
|
|
1341
1491
|
${maskable ? `if(c<128){
|
|
1342
1492
|
const bits=${maskName}[c<0?128:c]
|
|
1343
1493
|
let prev=0
|
|
1344
1494
|
${maskArms}
|
|
1345
|
-
|
|
1495
|
+
if(best===pos)acc=${catchName}(${n},prev,acc)
|
|
1346
1496
|
ctx._fe=pos;ctx._fx=acc??${choiceFx}
|
|
1347
1497
|
return FAIL
|
|
1348
1498
|
}
|
|
1349
|
-
` : ''}
|
|
1499
|
+
` : ''}
|
|
1500
|
+
${generalArms}
|
|
1350
1501
|
ctx._fe=pos;ctx._fx=acc??${choiceFx}
|
|
1351
1502
|
return FAIL
|
|
1352
1503
|
}`
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1353
1506
|
}
|
|
1354
1507
|
|
|
1355
1508
|
case OP_DISPATCH: {
|
|
@@ -1371,16 +1524,26 @@ return FAIL
|
|
|
1371
1524
|
// are TABLE DATA, so here they are the test itself and no closure, no
|
|
1372
1525
|
// pool and no indexed call exist. Kinds are `matcherClaims`'s exactly
|
|
1373
1526
|
// — 0/1 raw prefix/suffix, 3/4 the pre-folded pair, anything else a
|
|
1374
|
-
// regex.
|
|
1375
|
-
//
|
|
1376
|
-
//
|
|
1527
|
+
// regex. Public `matches()` refuses global/sticky patterns, so its
|
|
1528
|
+
// RegExp can be compiled once into the assembly prelude. Hand-built
|
|
1529
|
+
// low-level rows with g/y or invalid flags retain the old per-test
|
|
1530
|
+
// construction and therefore the old `lastIndex`/throw behavior.
|
|
1377
1531
|
const claims = (m: readonly [number, string, string, number]): string => {
|
|
1378
1532
|
switch (m[0]) {
|
|
1379
1533
|
case 0: return `key.startsWith(${q(m[1])})`
|
|
1380
1534
|
case 1: return `key.endsWith(${q(m[1])})`
|
|
1381
1535
|
case 3: return `asciiFoldKey(key).startsWith(${q(m[1])})`
|
|
1382
1536
|
case 4: return `asciiFoldKey(key).endsWith(${q(m[1])})`
|
|
1383
|
-
default:
|
|
1537
|
+
default: {
|
|
1538
|
+
if (!m[2].includes('g') && !m[2].includes('y')) {
|
|
1539
|
+
try {
|
|
1540
|
+
// Validate without changing malformed-row error timing.
|
|
1541
|
+
new RegExp(m[1], m[2])
|
|
1542
|
+
return `${hoist('dm', `new RegExp(${q(m[1])},${q(m[2])})`)}.test(key)`
|
|
1543
|
+
} catch {}
|
|
1544
|
+
}
|
|
1545
|
+
return `new RegExp(${q(m[1])},${q(m[2])}).test(key)`
|
|
1546
|
+
}
|
|
1384
1547
|
}
|
|
1385
1548
|
}
|
|
1386
1549
|
const chain = spec.match.length === 0
|
|
@@ -1447,6 +1610,15 @@ return [key,v]
|
|
|
1447
1610
|
const reportItem = (flags & 4) !== 0
|
|
1448
1611
|
const itemFx = reportItem ? fxRef(code[ip + 6]!) : 'EMPTY_FX'
|
|
1449
1612
|
const collect = op === OP_REP
|
|
1613
|
+
let itemCls: string | undefined
|
|
1614
|
+
if (!REC) {
|
|
1615
|
+
const itemClassIndex = sepIp < 0 ? code[ip + 7]! : -1
|
|
1616
|
+
if (itemClassIndex >= 0) {
|
|
1617
|
+
const ci = classes.push([t.cc[itemClassIndex]!]) - 1
|
|
1618
|
+
classPlan.push([itemClassIndex])
|
|
1619
|
+
itemCls = hoist('ri', `CLS[${ci}][0]`)
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1450
1622
|
const skipBeforeFirst = sepIp < 0 && min === 0
|
|
1451
1623
|
const p = tmp()
|
|
1452
1624
|
// `viaRepItem` is `sep === undefined && count >= min && (count > 0 ||
|
|
@@ -1466,7 +1638,6 @@ return [key,v]
|
|
|
1466
1638
|
const skip = skipFor(L)
|
|
1467
1639
|
const knownTrivia = L.tri === TRI_NONE ? false : L.tri !== TRI_UNKNOWN ? true : undefined
|
|
1468
1640
|
const hasTrivia = knownTrivia === undefined ? 'hasTrivia' : String(knownTrivia)
|
|
1469
|
-
const needMark = L.buf ? 'true' : 'needMark'
|
|
1470
1641
|
// THE `_fields` AND `_errors` MARKS ARE TAKEN ONLY WHERE THE TABLE HAS A
|
|
1471
1642
|
// WRITER, which is the same emit-time census `emitMark` applies to the
|
|
1472
1643
|
// non-loop marks. Before `OP_FIELD` and `OP_EXPECT` were emittable the
|
|
@@ -1552,10 +1723,10 @@ continue
|
|
|
1552
1723
|
`
|
|
1553
1724
|
return `${head}
|
|
1554
1725
|
const out=${collect ? '[]' : 'undefined'}
|
|
1555
|
-
${knownTrivia === undefined ? 'const hasTrivia=ctx.trivia!==undefined\n' : ''}${L.buf ? '' : 'const needMark=_rollbackNeeded(ctx)\n'}${REC ? `const ${my}=ctx._sync\n` : ''}let cur=pos
|
|
1726
|
+
${knownTrivia === undefined ? 'const hasTrivia=ctx.trivia!==undefined\n' : ''}${L.buf ? '' : 'const needMark=_rollbackNeeded(ctx)\n'}${REC ? `const ${my}=ctx._sync\n` : ''}${itemCls === undefined ? '' : `const ${p}gate=ctx._probe===undefined\n`}let cur=pos
|
|
1556
1727
|
let count=0
|
|
1557
1728
|
for(;;){
|
|
1558
|
-
${max >= 0 ? `if(count>=${max})break\n` : ''}${sep !== undefined ? `if(count>0&&count>=${min}&&cur>=input.length)break\n` : ''}let ${p}raw=0,${p}tl=0,${p}lv=0,${p}lg=0,${p}rt=0${sinks.fd ? `,${p}fd=0` : ''}${sinks.er ? `,${p}er=0` : ''}
|
|
1729
|
+
${max >= 0 ? `if(count>=${max})break\n` : ''}${sep !== undefined ? `if(count>0&&count>=${min}&&cur>=input.length)break\n` : ''}${itemCls !== undefined && sep === undefined ? `if(count>=${min}&&${p}gate&&${hasTrivia === 'false' ? 'true' : hasTrivia === 'true' ? 'false' : '!hasTrivia'}&&!classHas(${itemCls},lead(input,cur)))break\n` : ''}let ${p}raw=0,${p}tl=0,${p}lv=0,${p}lg=0,${p}rt=0${sinks.fd ? `,${p}fd=0` : ''}${sinks.er ? `,${p}er=0` : ''}
|
|
1559
1730
|
${markBody}
|
|
1560
1731
|
let itemStart=cur
|
|
1561
1732
|
let sepEnd=-1
|
|
@@ -1577,7 +1748,11 @@ itemStart=${hasTrivia === 'false' ? 'EC.e' : hasTrivia === 'true' ? `${skip}(inp
|
|
|
1577
1748
|
${rb}
|
|
1578
1749
|
${trailingAllowed ? 'if(sepEnd>=0)cur=sepEnd\n' : ''}break
|
|
1579
1750
|
}
|
|
1580
|
-
|
|
1751
|
+
${itemCls === undefined ? '' : `if(count>=${min}&&${p}gate&&!classHas(${itemCls},lead(input,itemStart))){
|
|
1752
|
+
${rb}
|
|
1753
|
+
${trailingAllowed ? 'if(sepEnd>=0)cur=sepEnd\n' : ''}break
|
|
1754
|
+
}
|
|
1755
|
+
`}ctx._fc=false
|
|
1581
1756
|
const v=${child}(input,itemStart,ctx)
|
|
1582
1757
|
if(v===FAIL){
|
|
1583
1758
|
${REC
|
|
@@ -1614,6 +1789,26 @@ return out
|
|
|
1614
1789
|
case OP_NODE:
|
|
1615
1790
|
case OP_NODE_TRACK: {
|
|
1616
1791
|
const flags = code[ip + 3]!
|
|
1792
|
+
const scalarChild = scalarTerminalNodeChild(code, ip)
|
|
1793
|
+
if (scalarChild >= 0 && scalarSpecs.has(code[scalarChild + 1]!)) {
|
|
1794
|
+
const recognize = recognizerRef(code[scalarChild + 1]!)
|
|
1795
|
+
const spec = code[scalarChild] === OP_RX ? null : k[code[scalarChild + 1]!] as string
|
|
1796
|
+
const value = spec === null ? 'input.slice(pos,end)' : q(spec)
|
|
1797
|
+
const xf = fxRef(code[scalarChild + 2]!)
|
|
1798
|
+
const build = fnRef(code[ip + 1]!)
|
|
1799
|
+
return `${head}
|
|
1800
|
+
const end=${recognize}(input,pos)
|
|
1801
|
+
if(end<0){ctx._fe=pos;ctx._fx=${xf};return FAIL}
|
|
1802
|
+
const value=${value}
|
|
1803
|
+
const leaf={_tag:'leaf',value,span:{start:pos,end}}
|
|
1804
|
+
const kids=[leaf],rawKids=[leaf],span={start:pos,end}
|
|
1805
|
+
EC.e=end
|
|
1806
|
+
const nd=${build}(kids,undefined,span,rawKids,EMPTY_TL,undefined)
|
|
1807
|
+
${L.buf ? 'pushCstChild(ctx,nd,rawEntry(nd,input,pos,end))' : 'if(ctx._cstBuf!==undefined||ctx._cstChildren!==undefined)pushCstChild(ctx,nd,rawEntry(nd,input,pos,end))'}
|
|
1808
|
+
EC.e=end
|
|
1809
|
+
return nd
|
|
1810
|
+
}`
|
|
1811
|
+
}
|
|
1617
1812
|
const child = link(code[ip + 2]!)
|
|
1618
1813
|
const proj = code[ip + 4]!
|
|
1619
1814
|
const buildIdx = code[ip + 1]!
|
|
@@ -1631,6 +1826,58 @@ return out
|
|
|
1631
1826
|
// interpreter's node case — the most-executed non-terminal in any of
|
|
1632
1827
|
// these grammars. It selects the emitted shape instead.
|
|
1633
1828
|
const build = buildIdx >= 0 ? fnRef(buildIdx) : undefined
|
|
1829
|
+
// The closure assembler's direct-builder capture tiers are table facts,
|
|
1830
|
+
// not closure-only semantics. Print the same body for a precompiled
|
|
1831
|
+
// assembly so a large composeLeaf artifact does not fall back to opening
|
|
1832
|
+
// the generic raw/trivia buffer that its reducer arity proved unread.
|
|
1833
|
+
if (!hostCst && !tracked && build !== undefined && proj < 0
|
|
1834
|
+
&& (flags === 2 || flags === 18 || flags === 34)) {
|
|
1835
|
+
const fields = flags === 18
|
|
1836
|
+
const collapseChildren = flags === 34
|
|
1837
|
+
const publish = `if(sBuf!==undefined){
|
|
1838
|
+
if(sBuf.rawOnly!==true){
|
|
1839
|
+
if(sBuf.ch!==undefined)sBuf.ch.push(nd)
|
|
1840
|
+
else if(sBuf.single!==undefined){sBuf.ch=[sBuf.single,nd];sBuf.single=undefined}
|
|
1841
|
+
else sBuf.single=nd
|
|
1842
|
+
}
|
|
1843
|
+
const rawNd=rawEntry(nd,input,pos,end)
|
|
1844
|
+
if(sBuf.raw!==undefined)sBuf.raw.push(rawNd)
|
|
1845
|
+
else if(sBuf.rawSingle!==undefined){sBuf.raw=[sBuf.rawSingle,rawNd];sBuf.rawSingle=undefined}
|
|
1846
|
+
else sBuf.rawSingle=rawNd
|
|
1847
|
+
}else if(sCh!==undefined){
|
|
1848
|
+
sCh.push(nd)
|
|
1849
|
+
if(sRaw!==undefined)sRaw.push(rawEntry(nd,input,pos,end))
|
|
1850
|
+
}`
|
|
1851
|
+
return `${head}
|
|
1852
|
+
const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
|
|
1853
|
+
const sCap=ctx.captureTrivia,sBuf=ctx._cstBuf,sFields=ctx._fields
|
|
1854
|
+
const flat=[]
|
|
1855
|
+
ctx._cstBuf=undefined
|
|
1856
|
+
ctx._cstChildren=flat
|
|
1857
|
+
ctx._cstLeaves=flat
|
|
1858
|
+
ctx._cstRawChildren=undefined
|
|
1859
|
+
ctx._cstTriviaLog=undefined
|
|
1860
|
+
ctx.captureTrivia=false
|
|
1861
|
+
ctx._fields=${fields ? '[]' : 'undefined'}
|
|
1862
|
+
const v=${child}(input,pos,ctx)
|
|
1863
|
+
const captured=_capturedFlatChildren(flat)
|
|
1864
|
+
${fields ? 'const fieldMap=buildFieldMap(ctx._fields)\n' : ''}ctx._fields=sFields
|
|
1865
|
+
ctx._cstBuf=sBuf
|
|
1866
|
+
ctx._cstChildren=sCh
|
|
1867
|
+
ctx._cstLeaves=sLv
|
|
1868
|
+
ctx._cstRawChildren=sRaw
|
|
1869
|
+
ctx._cstTriviaLog=sTl
|
|
1870
|
+
ctx.captureTrivia=sCap
|
|
1871
|
+
if(v===FAIL)return FAIL
|
|
1872
|
+
const end=EC.e
|
|
1873
|
+
${collapseChildren
|
|
1874
|
+
? `const nd=captured.length===1?captured[0]:${build}(captured,undefined,{start:pos,end},EMPTY_CH,EMPTY_TL,undefined)`
|
|
1875
|
+
: `const nd=${build}(captured,${fields ? 'fieldMap' : 'undefined'},{start:pos,end},EMPTY_CH,EMPTY_TL,undefined)`}
|
|
1876
|
+
${publish}
|
|
1877
|
+
EC.e=end
|
|
1878
|
+
return nd
|
|
1879
|
+
}`
|
|
1880
|
+
}
|
|
1634
1881
|
const structural = build === undefined && proj < 0
|
|
1635
1882
|
const grammarCapture = (flags & 1) !== 0 || trailingTrivia
|
|
1636
1883
|
const hostCapturesThisType = structural && cfg.hostCaptureTrivia !== undefined
|
|
@@ -1745,6 +1992,7 @@ return nd
|
|
|
1745
1992
|
const source = `${RUNTIME_PRELUDE}
|
|
1746
1993
|
${prelude.join('\n')}
|
|
1747
1994
|
${skipDefs.join('\n')}
|
|
1995
|
+
${choiceDefs.join('\n')}
|
|
1748
1996
|
${bodies.join('\n')}
|
|
1749
1997
|
function _begin(ctx){
|
|
1750
1998
|
const host=ctx.build
|