parseman 0.47.1 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -21
- package/dist/analysis/diagnostics.js +2863 -353
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +2854 -344
- package/dist/cli/index.js.map +3 -3
- 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 +2862 -339
- 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 +2870 -347
- package/dist/plugin/index.js.map +3 -3
- package/dist/spec/index.js +315 -46
- package/dist/spec/index.js.map +2 -2
- 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 +2819 -300
- 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 +43 -7
- 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/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 +1211 -221
- package/src/table/child-slots.ts +2 -1
- package/src/table/compile-rule-map.ts +21 -3
- package/src/table/emit-assembly.ts +328 -85
- package/src/table/emit.ts +16 -2
- package/src/table/encode.ts +242 -50
- package/src/table/exec-baseline.ts +3 -7
- package/src/table/exec.ts +121 -7
- package/src/table/lex-body.ts +30 -0
- package/src/table/lex-program.ts +422 -0
- package/src/table/ops.ts +45 -7
- 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
package/src/table/assemble.ts
CHANGED
|
@@ -71,8 +71,8 @@ import { projectChild, unwrapChild } from '../combinators/node.ts'
|
|
|
71
71
|
import { cstOutputHost } from '../compiler/build-arity.ts'
|
|
72
72
|
import { consumeTrivia } from '../combinators/trivia-skip.ts'
|
|
73
73
|
import {
|
|
74
|
-
advanceTrivia, needsDeferredTriviaCommit, rollbackScannedTriviaAt, rollbackTrivia, rollbackTriviaAt,
|
|
75
|
-
saveTriviaMark, scanTrivia, skipTriviaScanned, type FastTriviaScanner,
|
|
74
|
+
advanceTrivia, commitTriviaScan, needsDeferredTriviaCommit, rollbackScannedTriviaAt, rollbackTrivia, rollbackTriviaAt,
|
|
75
|
+
saveTriviaMark, scanTrivia, scanTriviaCompact, skipTriviaScanned, type FastTriviaScanner,
|
|
76
76
|
} from '../combinators/trivia-skip.ts'
|
|
77
77
|
import {
|
|
78
78
|
cstCaptureActive, cstLeavesLen, cstTlLen,
|
|
@@ -87,6 +87,7 @@ import {
|
|
|
87
87
|
OP_FIELD, OP_DISPATCH, OP_ROUTED, OP_LIT_CI, OP_LIT_CI_TRACK, OP_TOKEN, OP_WITHCTX, OP_GUARD, OP_ATTEMPT, OP_LABEL,
|
|
88
88
|
OP_COV,
|
|
89
89
|
OP_ADJ, OP_GREEDY, OP_REJECT, OP_ARMGATE,
|
|
90
|
+
OP_LEX_BODY, OP_LEX_PROGRAM,
|
|
90
91
|
} from './ops.ts'
|
|
91
92
|
import { adjacencyHolds, adjacencyMisuse } from '../combinators/adjacency.ts'
|
|
92
93
|
import { failAt } from '../combinators/probe.ts'
|
|
@@ -118,14 +119,18 @@ import { lead, rawEntry, spanLines } from './run-support.ts'
|
|
|
118
119
|
* property read per terminal miss — the same price codegen pays.
|
|
119
120
|
*/
|
|
120
121
|
import {
|
|
121
|
-
classHas, decodeClassSpec, expandCompact, resolveTable,
|
|
122
|
+
classHas, decodeClassSpec, expandCompact, resolveTable,
|
|
123
|
+
validateDispatchSpec,
|
|
122
124
|
type CompactProgram, type ResolvedClass, type ResolvedTable,
|
|
123
125
|
type SubtreeRef, type TableProgram, type TableRule,
|
|
124
126
|
} from './program.ts'
|
|
125
|
-
import { reachableSites } from './site-labels.ts'
|
|
126
127
|
import { stampRuleMap } from './stamp.ts'
|
|
128
|
+
import { reachableSites } from './site-labels.ts'
|
|
127
129
|
import { refuseUnclassifiedRootScope } from '../cst/root-trivia-scope.ts'
|
|
128
130
|
import { captureError, firstSetSentinel, matchesAt, orSentinel, recoverScan } from '../recovery/scan.ts'
|
|
131
|
+
import {
|
|
132
|
+
makeScalarRecognizer, scalarTerminalNodeChild, scalarTerminalNotChild, type ScalarRecognizer,
|
|
133
|
+
} from './scalar-terminal.ts'
|
|
129
134
|
|
|
130
135
|
/**
|
|
131
136
|
* Is the EMITTED engine (`emit-assembly.ts`) enabled for this process?
|
|
@@ -139,7 +144,7 @@ const EMIT_ENABLED = (globalThis as { process?: { env?: Record<string, string |
|
|
|
139
144
|
.process?.env?.PM_TABLE_EMIT !== '0'
|
|
140
145
|
|
|
141
146
|
/** Failure sentinel — SHARED with the other two engines, see `cell.ts`. */
|
|
142
|
-
import { FAIL, newEndCell
|
|
147
|
+
import { FAIL, newEndCell } from './cell.ts'
|
|
143
148
|
|
|
144
149
|
const EMPTY_TL: readonly number[] = Object.freeze([])
|
|
145
150
|
/** `finishCstBuf`'s two empty sentinels, held here so the node piece can inline it. */
|
|
@@ -167,10 +172,20 @@ function linkMatcher(m: readonly [number, string, string, number]): (key: string
|
|
|
167
172
|
case 1: return key => key.endsWith(value)
|
|
168
173
|
case 3: return key => asciiFoldKey(key).startsWith(value)
|
|
169
174
|
case 4: return key => asciiFoldKey(key).endsWith(value)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
default: {
|
|
176
|
+
const flags = m[2]
|
|
177
|
+
// `matches()` refuses global/sticky patterns, so every compiler-owned
|
|
178
|
+
// matcher has stable `lastIndex` and can be compiled once per assembly.
|
|
179
|
+
// Preserve the old fresh-per-test behavior for hand-built low-level rows
|
|
180
|
+
// that contain g/y or invalid flags rather than broadening the wire ABI.
|
|
181
|
+
if (!flags.includes('g') && !flags.includes('y')) {
|
|
182
|
+
try {
|
|
183
|
+
const pattern = new RegExp(value, flags)
|
|
184
|
+
return key => pattern.test(key)
|
|
185
|
+
} catch {}
|
|
186
|
+
}
|
|
187
|
+
return key => new RegExp(value, flags).test(key)
|
|
188
|
+
}
|
|
174
189
|
}
|
|
175
190
|
}
|
|
176
191
|
|
|
@@ -198,6 +213,36 @@ const NO_COVERAGE = (_id: string): void => {}
|
|
|
198
213
|
* `{ value, end }` pair would be an allocation per row.
|
|
199
214
|
*/
|
|
200
215
|
type Piece = (input: string, pos: number, ctx: ParseContext) => unknown
|
|
216
|
+
type NodeBuilder = (
|
|
217
|
+
children: readonly unknown[], fields: FieldMap | undefined, span: { start: number; end: number },
|
|
218
|
+
rawChildren: readonly unknown[], triviaLog: readonly number[], state: unknown,
|
|
219
|
+
) => unknown
|
|
220
|
+
type ChoiceExpectedPrefix = (
|
|
221
|
+
target: number, prev: number, acc: string[] | undefined,
|
|
222
|
+
) => string[] | undefined
|
|
223
|
+
type MaskedChoiceBlock = (
|
|
224
|
+
input: string, pos: number, ctx: ParseContext, bits: number,
|
|
225
|
+
need: boolean, mRaw: number, mTl: number, mLv: number, mFl: number,
|
|
226
|
+
mEr: number, mLog: number, mRoot: number,
|
|
227
|
+
acc: string[] | undefined, prev: number,
|
|
228
|
+
) => unknown
|
|
229
|
+
type GeneralChoiceBlock = (
|
|
230
|
+
input: string, pos: number, ctx: ParseContext, c: number,
|
|
231
|
+
need: boolean, mRaw: number, mTl: number, mLv: number, mFl: number,
|
|
232
|
+
mEr: number, mLog: number, mRoot: number,
|
|
233
|
+
acc: string[] | undefined, prev: number,
|
|
234
|
+
) => unknown
|
|
235
|
+
type ExclusiveChoiceBlock = (arm: number, input: string, pos: number, ctx: ParseContext) => unknown
|
|
236
|
+
type DispatchMatcher = (key: string) => boolean
|
|
237
|
+
type DispatchMatcherBlock = (key: string) => number | undefined
|
|
238
|
+
type DispatchBranch = (
|
|
239
|
+
input: string, pos: number, selEnd: number, key: string,
|
|
240
|
+
selectorMark: ReturnType<typeof saveTriviaMark>, ctx: ParseContext,
|
|
241
|
+
) => unknown
|
|
242
|
+
type DispatchArmBlock = (
|
|
243
|
+
arm: number, input: string, pos: number, selEnd: number, key: string,
|
|
244
|
+
selectorMark: ReturnType<typeof saveTriviaMark>, ctx: ParseContext,
|
|
245
|
+
) => unknown
|
|
201
246
|
|
|
202
247
|
/**
|
|
203
248
|
* ONE NON-FIRST SEQUENCE TERM, in a sequence that carries an adjacency
|
|
@@ -205,7 +250,14 @@ type Piece = (input: string, pos: number, ctx: ParseContext) => unknown
|
|
|
205
250
|
* `nextTerm`'s protocol, so a bound term and a bound assertion are
|
|
206
251
|
* interchangeable in the loop and the loop tests neither.
|
|
207
252
|
*/
|
|
208
|
-
type TermRunner = (
|
|
253
|
+
type TermRunner = (
|
|
254
|
+
input: string, cur: number, ctx: ParseContext,
|
|
255
|
+
inheritedSync?: Combinator<unknown>,
|
|
256
|
+
) => number
|
|
257
|
+
type SequenceTermBlock = (
|
|
258
|
+
input: string, cur: number, ctx: ParseContext, values: unknown[] | undefined,
|
|
259
|
+
inheritedSync?: Combinator<unknown>,
|
|
260
|
+
) => number
|
|
209
261
|
|
|
210
262
|
/**
|
|
211
263
|
* The option set an assembly is specialised for.
|
|
@@ -415,6 +467,21 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
415
467
|
*/
|
|
416
468
|
const EC = newEndCell()
|
|
417
469
|
|
|
470
|
+
// Indexed by the existing constant-pool operand: the same scalar recognizer
|
|
471
|
+
// serves an ordinary terminal piece and a direct terminal-node materializer.
|
|
472
|
+
// Value/CST/failure/cursor effects stay in those consumers.
|
|
473
|
+
const scalarRecognizers: Array<ScalarRecognizer | undefined> = []
|
|
474
|
+
const rawScalarSpecs = new Set<number>()
|
|
475
|
+
function scalarFor(child: number): ScalarRecognizer | undefined {
|
|
476
|
+
const spec = code[child + 1]!
|
|
477
|
+
let recognize = scalarRecognizers[spec]
|
|
478
|
+
if (recognize === undefined) {
|
|
479
|
+
recognize = makeScalarRecognizer(code[child]!, k[spec])
|
|
480
|
+
scalarRecognizers[spec] = recognize
|
|
481
|
+
}
|
|
482
|
+
return recognize
|
|
483
|
+
}
|
|
484
|
+
|
|
418
485
|
/**
|
|
419
486
|
* The INSTALLED trivia scanner for the scope currently running.
|
|
420
487
|
*
|
|
@@ -433,6 +500,263 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
433
500
|
pushCstLeaf(ctx, lf)
|
|
434
501
|
}
|
|
435
502
|
|
|
503
|
+
/**
|
|
504
|
+
* The three dominant AST node shapes, selected once while linking.
|
|
505
|
+
*
|
|
506
|
+
* The generic node body is necessarily broad: CST hosts, tracked spans,
|
|
507
|
+
* fields, trivia/state reads, structural hosts, projections, collapse and
|
|
508
|
+
* unwrap all share it. That made its one FunctionLiteral 1,100+ V8 bytecodes
|
|
509
|
+
* even for the ordinary AST builder that needs none of those branches. Jess's
|
|
510
|
+
* exact AST workloads execute the three shapes below 42k/66k/205k times per
|
|
511
|
+
* CSS/Less/generated parse. Keep their bodies scalar and keep every other
|
|
512
|
+
* shape on the generic oracle below.
|
|
513
|
+
*/
|
|
514
|
+
function plainBuildNode(child: Piece, build: NodeBuilder): Piece {
|
|
515
|
+
return (input, pos, ctx) => {
|
|
516
|
+
const sCh = ctx._cstChildren
|
|
517
|
+
const sLv = ctx._cstLeaves
|
|
518
|
+
const sRaw = ctx._cstRawChildren
|
|
519
|
+
const sTl = ctx._cstTriviaLog
|
|
520
|
+
const sCap = ctx.captureTrivia
|
|
521
|
+
const sBuf = ctx._cstBuf
|
|
522
|
+
const savedFields = ctx._fields
|
|
523
|
+
const buf: CstCaptureBuf = {}
|
|
524
|
+
ctx._cstBuf = buf
|
|
525
|
+
ctx._cstChildren = undefined
|
|
526
|
+
ctx._cstLeaves = undefined
|
|
527
|
+
ctx._cstRawChildren = undefined
|
|
528
|
+
ctx._cstTriviaLog = undefined
|
|
529
|
+
ctx.captureTrivia = false
|
|
530
|
+
ctx._fields = undefined
|
|
531
|
+
const value = child(input, pos, ctx)
|
|
532
|
+
const kids = buf.ch ?? (buf.single !== undefined ? [buf.single] : EMPTY_CH)
|
|
533
|
+
const rawKids = buf.raw ?? (buf.rawSingle !== undefined ? [buf.rawSingle] : EMPTY_CH)
|
|
534
|
+
ctx._fields = savedFields
|
|
535
|
+
ctx._cstBuf = sBuf
|
|
536
|
+
ctx._cstChildren = sCh
|
|
537
|
+
ctx._cstLeaves = sLv
|
|
538
|
+
ctx._cstRawChildren = sRaw
|
|
539
|
+
ctx._cstTriviaLog = sTl
|
|
540
|
+
ctx.captureTrivia = sCap
|
|
541
|
+
if (value === FAIL) return FAIL
|
|
542
|
+
const end = EC.e
|
|
543
|
+
const span = { start: pos, end }
|
|
544
|
+
const node = build(kids, undefined, span, rawKids, EMPTY_TL, undefined)
|
|
545
|
+
if (sBuf !== undefined || sCh !== undefined) {
|
|
546
|
+
pushCstChild(ctx, node, rawEntry(node, input, pos, end))
|
|
547
|
+
}
|
|
548
|
+
EC.e = end
|
|
549
|
+
return node
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/** The lazy capture buffer uses `single === undefined` as its empty sentinel:
|
|
554
|
+
* leading undefined semantic children are therefore absent, while undefined
|
|
555
|
+
* values after the first present child are retained in `ch`. A flat collector
|
|
556
|
+
* normalizes only that cold prefix so the builder sees the identical list. */
|
|
557
|
+
function capturedFlatChildren(children: unknown[]): readonly unknown[] {
|
|
558
|
+
if (children.length === 0) return EMPTY_CH
|
|
559
|
+
if (children[0] !== undefined) return children
|
|
560
|
+
let first = 1
|
|
561
|
+
while (first < children.length && children[first] === undefined) first++
|
|
562
|
+
return first === children.length ? EMPTY_CH : children.slice(first)
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/** Direct reducer whose declared arity proves `rawChildren` is unobservable.
|
|
566
|
+
* Use the existing split children/leaves collector instead of opening a
|
|
567
|
+
* duplicate raw capture buffer. The body shape is selected while linking. */
|
|
568
|
+
function childrenOnlyBuildNode(child: Piece, build: NodeBuilder): Piece {
|
|
569
|
+
return (input, pos, ctx) => {
|
|
570
|
+
const sCh = ctx._cstChildren
|
|
571
|
+
const sLv = ctx._cstLeaves
|
|
572
|
+
const sRaw = ctx._cstRawChildren
|
|
573
|
+
const sTl = ctx._cstTriviaLog
|
|
574
|
+
const sCap = ctx.captureTrivia
|
|
575
|
+
const sBuf = ctx._cstBuf
|
|
576
|
+
const savedFields = ctx._fields
|
|
577
|
+
const kids: unknown[] = []
|
|
578
|
+
ctx._cstBuf = undefined
|
|
579
|
+
ctx._cstChildren = kids
|
|
580
|
+
ctx._cstLeaves = kids
|
|
581
|
+
ctx._cstRawChildren = undefined
|
|
582
|
+
ctx._cstTriviaLog = undefined
|
|
583
|
+
ctx.captureTrivia = false
|
|
584
|
+
ctx._fields = undefined
|
|
585
|
+
const value = child(input, pos, ctx)
|
|
586
|
+
const captured = capturedFlatChildren(kids)
|
|
587
|
+
ctx._fields = savedFields
|
|
588
|
+
ctx._cstBuf = sBuf
|
|
589
|
+
ctx._cstChildren = sCh
|
|
590
|
+
ctx._cstLeaves = sLv
|
|
591
|
+
ctx._cstRawChildren = sRaw
|
|
592
|
+
ctx._cstTriviaLog = sTl
|
|
593
|
+
ctx.captureTrivia = sCap
|
|
594
|
+
if (value === FAIL) return FAIL
|
|
595
|
+
const end = EC.e
|
|
596
|
+
const node = build(captured, undefined, { start: pos, end }, EMPTY_CH, EMPTY_TL, undefined)
|
|
597
|
+
if (sBuf !== undefined || sCh !== undefined) {
|
|
598
|
+
pushCstChild(ctx, node, sBuf !== undefined || sRaw !== undefined ? rawEntry(node, input, pos, end) : undefined)
|
|
599
|
+
}
|
|
600
|
+
EC.e = end
|
|
601
|
+
return node
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function childrenOnlyFieldsBuildNode(child: Piece, build: NodeBuilder): Piece {
|
|
606
|
+
return (input, pos, ctx) => {
|
|
607
|
+
const sCh = ctx._cstChildren
|
|
608
|
+
const sLv = ctx._cstLeaves
|
|
609
|
+
const sRaw = ctx._cstRawChildren
|
|
610
|
+
const sTl = ctx._cstTriviaLog
|
|
611
|
+
const sCap = ctx.captureTrivia
|
|
612
|
+
const sBuf = ctx._cstBuf
|
|
613
|
+
const savedFields = ctx._fields
|
|
614
|
+
const kids: unknown[] = []
|
|
615
|
+
ctx._cstBuf = undefined
|
|
616
|
+
ctx._cstChildren = kids
|
|
617
|
+
ctx._cstLeaves = kids
|
|
618
|
+
ctx._cstRawChildren = undefined
|
|
619
|
+
ctx._cstTriviaLog = undefined
|
|
620
|
+
ctx.captureTrivia = false
|
|
621
|
+
ctx._fields = []
|
|
622
|
+
const value = child(input, pos, ctx)
|
|
623
|
+
const captured = capturedFlatChildren(kids)
|
|
624
|
+
const fieldMap = buildFieldMap(ctx._fields)
|
|
625
|
+
ctx._fields = savedFields
|
|
626
|
+
ctx._cstBuf = sBuf
|
|
627
|
+
ctx._cstChildren = sCh
|
|
628
|
+
ctx._cstLeaves = sLv
|
|
629
|
+
ctx._cstRawChildren = sRaw
|
|
630
|
+
ctx._cstTriviaLog = sTl
|
|
631
|
+
ctx.captureTrivia = sCap
|
|
632
|
+
if (value === FAIL) return FAIL
|
|
633
|
+
const end = EC.e
|
|
634
|
+
const node = build(captured, fieldMap, { start: pos, end }, EMPTY_CH, EMPTY_TL, undefined)
|
|
635
|
+
if (sBuf !== undefined || sCh !== undefined) {
|
|
636
|
+
pushCstChild(ctx, node, sBuf !== undefined || sRaw !== undefined ? rawEntry(node, input, pos, end) : undefined)
|
|
637
|
+
}
|
|
638
|
+
EC.e = end
|
|
639
|
+
return node
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function collapseBuildNode(child: Piece, build: NodeBuilder): Piece {
|
|
644
|
+
return (input, pos, ctx) => {
|
|
645
|
+
const sCh = ctx._cstChildren
|
|
646
|
+
const sLv = ctx._cstLeaves
|
|
647
|
+
const sRaw = ctx._cstRawChildren
|
|
648
|
+
const sTl = ctx._cstTriviaLog
|
|
649
|
+
const sCap = ctx.captureTrivia
|
|
650
|
+
const sBuf = ctx._cstBuf
|
|
651
|
+
const savedFields = ctx._fields
|
|
652
|
+
const buf: CstCaptureBuf = {}
|
|
653
|
+
ctx._cstBuf = buf
|
|
654
|
+
ctx._cstChildren = undefined
|
|
655
|
+
ctx._cstLeaves = undefined
|
|
656
|
+
ctx._cstRawChildren = undefined
|
|
657
|
+
ctx._cstTriviaLog = undefined
|
|
658
|
+
ctx.captureTrivia = false
|
|
659
|
+
ctx._fields = undefined
|
|
660
|
+
const value = child(input, pos, ctx)
|
|
661
|
+
const kids = buf.ch ?? (buf.single !== undefined ? [buf.single] : EMPTY_CH)
|
|
662
|
+
const rawKids = buf.raw ?? (buf.rawSingle !== undefined ? [buf.rawSingle] : EMPTY_CH)
|
|
663
|
+
ctx._fields = savedFields
|
|
664
|
+
ctx._cstBuf = sBuf
|
|
665
|
+
ctx._cstChildren = sCh
|
|
666
|
+
ctx._cstLeaves = sLv
|
|
667
|
+
ctx._cstRawChildren = sRaw
|
|
668
|
+
ctx._cstTriviaLog = sTl
|
|
669
|
+
ctx.captureTrivia = sCap
|
|
670
|
+
if (value === FAIL) return FAIL
|
|
671
|
+
const end = EC.e
|
|
672
|
+
const span = { start: pos, end }
|
|
673
|
+
const node = kids.length === 1
|
|
674
|
+
? kids[0]
|
|
675
|
+
: build(kids, undefined, span, rawKids, EMPTY_TL, undefined)
|
|
676
|
+
if (sBuf !== undefined || sCh !== undefined) {
|
|
677
|
+
pushCstChild(ctx, node, rawEntry(node, input, pos, end))
|
|
678
|
+
}
|
|
679
|
+
EC.e = end
|
|
680
|
+
return node
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function childrenOnlyCollapseBuildNode(child: Piece, build: NodeBuilder): Piece {
|
|
685
|
+
return (input, pos, ctx) => {
|
|
686
|
+
const sCh = ctx._cstChildren
|
|
687
|
+
const sLv = ctx._cstLeaves
|
|
688
|
+
const sRaw = ctx._cstRawChildren
|
|
689
|
+
const sTl = ctx._cstTriviaLog
|
|
690
|
+
const sCap = ctx.captureTrivia
|
|
691
|
+
const sBuf = ctx._cstBuf
|
|
692
|
+
const savedFields = ctx._fields
|
|
693
|
+
const kids: unknown[] = []
|
|
694
|
+
ctx._cstBuf = undefined
|
|
695
|
+
ctx._cstChildren = kids
|
|
696
|
+
ctx._cstLeaves = kids
|
|
697
|
+
ctx._cstRawChildren = undefined
|
|
698
|
+
ctx._cstTriviaLog = undefined
|
|
699
|
+
ctx.captureTrivia = false
|
|
700
|
+
ctx._fields = undefined
|
|
701
|
+
const value = child(input, pos, ctx)
|
|
702
|
+
const captured = capturedFlatChildren(kids)
|
|
703
|
+
ctx._fields = savedFields
|
|
704
|
+
ctx._cstBuf = sBuf
|
|
705
|
+
ctx._cstChildren = sCh
|
|
706
|
+
ctx._cstLeaves = sLv
|
|
707
|
+
ctx._cstRawChildren = sRaw
|
|
708
|
+
ctx._cstTriviaLog = sTl
|
|
709
|
+
ctx.captureTrivia = sCap
|
|
710
|
+
if (value === FAIL) return FAIL
|
|
711
|
+
const end = EC.e
|
|
712
|
+
const node = captured.length === 1
|
|
713
|
+
? captured[0]
|
|
714
|
+
: build(captured, undefined, { start: pos, end }, EMPTY_CH, EMPTY_TL, undefined)
|
|
715
|
+
if (sBuf !== undefined || sCh !== undefined) {
|
|
716
|
+
pushCstChild(ctx, node, sBuf !== undefined || sRaw !== undefined ? rawEntry(node, input, pos, end) : undefined)
|
|
717
|
+
}
|
|
718
|
+
EC.e = end
|
|
719
|
+
return node
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function plainProjectNode(child: Piece, projection: number, type: string): Piece {
|
|
724
|
+
return (input, pos, ctx) => {
|
|
725
|
+
const sCh = ctx._cstChildren
|
|
726
|
+
const sLv = ctx._cstLeaves
|
|
727
|
+
const sRaw = ctx._cstRawChildren
|
|
728
|
+
const sTl = ctx._cstTriviaLog
|
|
729
|
+
const sCap = ctx.captureTrivia
|
|
730
|
+
const sBuf = ctx._cstBuf
|
|
731
|
+
const savedFields = ctx._fields
|
|
732
|
+
const buf: CstCaptureBuf = {}
|
|
733
|
+
ctx._cstBuf = buf
|
|
734
|
+
ctx._cstChildren = undefined
|
|
735
|
+
ctx._cstLeaves = undefined
|
|
736
|
+
ctx._cstRawChildren = undefined
|
|
737
|
+
ctx._cstTriviaLog = undefined
|
|
738
|
+
ctx.captureTrivia = false
|
|
739
|
+
ctx._fields = undefined
|
|
740
|
+
const value = child(input, pos, ctx)
|
|
741
|
+
const kids = buf.ch ?? (buf.single !== undefined ? [buf.single] : EMPTY_CH)
|
|
742
|
+
ctx._fields = savedFields
|
|
743
|
+
ctx._cstBuf = sBuf
|
|
744
|
+
ctx._cstChildren = sCh
|
|
745
|
+
ctx._cstLeaves = sLv
|
|
746
|
+
ctx._cstRawChildren = sRaw
|
|
747
|
+
ctx._cstTriviaLog = sTl
|
|
748
|
+
ctx.captureTrivia = sCap
|
|
749
|
+
if (value === FAIL) return FAIL
|
|
750
|
+
const end = EC.e
|
|
751
|
+
const node = projectChild(kids, projection, type)
|
|
752
|
+
if (sBuf !== undefined || sCh !== undefined) {
|
|
753
|
+
pushCstChild(ctx, node, rawEntry(node, input, pos, end))
|
|
754
|
+
}
|
|
755
|
+
EC.e = end
|
|
756
|
+
return node
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
436
760
|
/** Mirrors `exec.ts`'s `rollbackNeeded` — a runtime question about live sinks. */
|
|
437
761
|
function rollbackNeeded(ctx: ParseContext): boolean {
|
|
438
762
|
return ctx._cstBuf !== undefined
|
|
@@ -562,9 +886,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
562
886
|
// keeps the exact branch it had.
|
|
563
887
|
if (s !== null) return skipTriviaScanned(s, input, cur, ctx)
|
|
564
888
|
if (needsDeferredTriviaCommit(ctx)) {
|
|
565
|
-
|
|
566
|
-
scan.commit()
|
|
567
|
-
return scan.end
|
|
889
|
+
return commitTriviaScan(scanTriviaCompact(input, cur, ctx))
|
|
568
890
|
}
|
|
569
891
|
return advanceTrivia(input, cur, ctx)
|
|
570
892
|
}
|
|
@@ -662,7 +984,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
662
984
|
TERMV = v
|
|
663
985
|
return EC.e > scanEnd ? EC.e : cur
|
|
664
986
|
}
|
|
665
|
-
// SCALAR MARKS —
|
|
987
|
+
// SCALAR MARKS — only the three trivia sinks can receive ambient-scan rows.
|
|
988
|
+
// Child-owned nodes, fields and errors are deliberately outside this range.
|
|
666
989
|
const need = rollbackNeeded(ctx)
|
|
667
990
|
const mTl = need ? cstTlLen(ctx) : 0
|
|
668
991
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
@@ -705,6 +1028,218 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
705
1028
|
return acc
|
|
706
1029
|
}
|
|
707
1030
|
|
|
1031
|
+
/** No fixed child array is permitted to survive into a choice piece. */
|
|
1032
|
+
const NEVER_PIECE: Piece = () => FAIL
|
|
1033
|
+
const NEVER_CLASS: ResolvedClass = { ascii: new Uint8Array(128), hi: [] }
|
|
1034
|
+
const EMPTY_EXPECTED_PREFIX: ChoiceExpectedPrefix = (_target, _prev, acc) => acc
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* Four directly captured static expected sets. The chain keeps skipped-arm
|
|
1038
|
+
* diagnostics lazy across block boundaries: a later successful arm never
|
|
1039
|
+
* materialises expectations for earlier char-excluded arms.
|
|
1040
|
+
*/
|
|
1041
|
+
function expectedBlock(
|
|
1042
|
+
start: number,
|
|
1043
|
+
e0: readonly string[], e1: readonly string[], e2: readonly string[], e3: readonly string[],
|
|
1044
|
+
prior: ChoiceExpectedPrefix,
|
|
1045
|
+
): ChoiceExpectedPrefix {
|
|
1046
|
+
return (target, prev, acc) => {
|
|
1047
|
+
if (prev < start) { acc = prior(start, prev, acc); prev = start }
|
|
1048
|
+
if (target > start && prev < start + 1) acc = accSet(e0, acc)
|
|
1049
|
+
if (target > start + 1 && prev < start + 2) acc = accSet(e1, acc)
|
|
1050
|
+
if (target > start + 2 && prev < start + 3) acc = accSet(e2, acc)
|
|
1051
|
+
if (target > start + 3 && prev < start + 4) acc = accSet(e3, acc)
|
|
1052
|
+
return acc
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/** One four-arm ASCII-mask block, selected once when the arity is maskable. */
|
|
1057
|
+
function maskedChoiceBlock(
|
|
1058
|
+
start: number,
|
|
1059
|
+
a0: Piece, a1: Piece, a2: Piece, a3: Piece,
|
|
1060
|
+
through: ChoiceExpectedPrefix,
|
|
1061
|
+
next: MaskedChoiceBlock | undefined,
|
|
1062
|
+
total: number,
|
|
1063
|
+
choiceFx: string[],
|
|
1064
|
+
): MaskedChoiceBlock {
|
|
1065
|
+
return (input, pos, ctx, bits, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev) => {
|
|
1066
|
+
if ((bits & (1 << start)) !== 0) {
|
|
1067
|
+
ctx._fc = false
|
|
1068
|
+
const v = a0(input, pos, ctx)
|
|
1069
|
+
if (v !== FAIL) return v
|
|
1070
|
+
acc = through(start, prev, acc); prev = start + 1
|
|
1071
|
+
acc = accSet(ctx._fx, acc)
|
|
1072
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1073
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1074
|
+
}
|
|
1075
|
+
if ((bits & (1 << (start + 1))) !== 0) {
|
|
1076
|
+
ctx._fc = false
|
|
1077
|
+
const v = a1(input, pos, ctx)
|
|
1078
|
+
if (v !== FAIL) return v
|
|
1079
|
+
acc = through(start + 1, prev, acc); prev = start + 2
|
|
1080
|
+
acc = accSet(ctx._fx, acc)
|
|
1081
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1082
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1083
|
+
}
|
|
1084
|
+
if ((bits & (1 << (start + 2))) !== 0) {
|
|
1085
|
+
ctx._fc = false
|
|
1086
|
+
const v = a2(input, pos, ctx)
|
|
1087
|
+
if (v !== FAIL) return v
|
|
1088
|
+
acc = through(start + 2, prev, acc); prev = start + 3
|
|
1089
|
+
acc = accSet(ctx._fx, acc)
|
|
1090
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1091
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1092
|
+
}
|
|
1093
|
+
if ((bits & (1 << (start + 3))) !== 0) {
|
|
1094
|
+
ctx._fc = false
|
|
1095
|
+
const v = a3(input, pos, ctx)
|
|
1096
|
+
if (v !== FAIL) return v
|
|
1097
|
+
acc = through(start + 3, prev, acc); prev = start + 4
|
|
1098
|
+
acc = accSet(ctx._fx, acc)
|
|
1099
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1100
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1101
|
+
}
|
|
1102
|
+
if (next !== undefined) {
|
|
1103
|
+
return next(input, pos, ctx, bits, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev)
|
|
1104
|
+
}
|
|
1105
|
+
acc = through(total, prev, acc)
|
|
1106
|
+
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1107
|
+
return FAIL
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/** One four-arm non-ASCII/general block. No mask state exists in this shape. */
|
|
1112
|
+
function generalChoiceBlock(
|
|
1113
|
+
start: number,
|
|
1114
|
+
a0: Piece, a1: Piece, a2: Piece, a3: Piece,
|
|
1115
|
+
g0: ResolvedClass | null, g1: ResolvedClass | null,
|
|
1116
|
+
g2: ResolvedClass | null, g3: ResolvedClass | null,
|
|
1117
|
+
through: ChoiceExpectedPrefix,
|
|
1118
|
+
next: GeneralChoiceBlock | undefined,
|
|
1119
|
+
total: number,
|
|
1120
|
+
choiceFx: string[],
|
|
1121
|
+
): GeneralChoiceBlock {
|
|
1122
|
+
return (input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev) => {
|
|
1123
|
+
if (g0 === null || classHas(g0, c)) {
|
|
1124
|
+
ctx._fc = false
|
|
1125
|
+
const v = a0(input, pos, ctx)
|
|
1126
|
+
if (v !== FAIL) return v
|
|
1127
|
+
acc = through(start, prev, acc); prev = start + 1
|
|
1128
|
+
acc = accSet(ctx._fx, acc)
|
|
1129
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1130
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1131
|
+
}
|
|
1132
|
+
if (g1 === null || classHas(g1, c)) {
|
|
1133
|
+
ctx._fc = false
|
|
1134
|
+
const v = a1(input, pos, ctx)
|
|
1135
|
+
if (v !== FAIL) return v
|
|
1136
|
+
acc = through(start + 1, prev, acc); prev = start + 2
|
|
1137
|
+
acc = accSet(ctx._fx, acc)
|
|
1138
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1139
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1140
|
+
}
|
|
1141
|
+
if (g2 === null || classHas(g2, c)) {
|
|
1142
|
+
ctx._fc = false
|
|
1143
|
+
const v = a2(input, pos, ctx)
|
|
1144
|
+
if (v !== FAIL) return v
|
|
1145
|
+
acc = through(start + 2, prev, acc); prev = start + 3
|
|
1146
|
+
acc = accSet(ctx._fx, acc)
|
|
1147
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1148
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1149
|
+
}
|
|
1150
|
+
if (g3 === null || classHas(g3, c)) {
|
|
1151
|
+
ctx._fc = false
|
|
1152
|
+
const v = a3(input, pos, ctx)
|
|
1153
|
+
if (v !== FAIL) return v
|
|
1154
|
+
acc = through(start + 3, prev, acc); prev = start + 4
|
|
1155
|
+
acc = accSet(ctx._fx, acc)
|
|
1156
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1157
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1158
|
+
}
|
|
1159
|
+
if (next !== undefined) {
|
|
1160
|
+
return next(input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev)
|
|
1161
|
+
}
|
|
1162
|
+
acc = through(total, prev, acc)
|
|
1163
|
+
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1164
|
+
return FAIL
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/** Four direct exclusive arms, chained for every arity above three. */
|
|
1169
|
+
function exclusiveChoiceBlock(
|
|
1170
|
+
start: number, a0: Piece, a1: Piece, a2: Piece, a3: Piece,
|
|
1171
|
+
next: ExclusiveChoiceBlock | undefined,
|
|
1172
|
+
): ExclusiveChoiceBlock {
|
|
1173
|
+
return (arm, input, pos, ctx) => {
|
|
1174
|
+
if (arm === start) return a0(input, pos, ctx)
|
|
1175
|
+
if (arm === start + 1) return a1(input, pos, ctx)
|
|
1176
|
+
if (arm === start + 2) return a2(input, pos, ctx)
|
|
1177
|
+
if (arm === start + 3) return a3(input, pos, ctx)
|
|
1178
|
+
return next === undefined ? FAIL : next(arm, input, pos, ctx)
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function dispatchMatcherBlock(
|
|
1183
|
+
m0: DispatchMatcher, a0: number, m1: DispatchMatcher, a1: number,
|
|
1184
|
+
m2: DispatchMatcher, a2: number, m3: DispatchMatcher, a3: number,
|
|
1185
|
+
next: DispatchMatcherBlock | undefined,
|
|
1186
|
+
): DispatchMatcherBlock {
|
|
1187
|
+
return key => {
|
|
1188
|
+
if (m0(key)) return a0
|
|
1189
|
+
if (m1(key)) return a1
|
|
1190
|
+
if (m2(key)) return a2
|
|
1191
|
+
if (m3(key)) return a3
|
|
1192
|
+
return next?.(key)
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
function dispatchBranch(child: Piece, usesRouted: boolean): DispatchBranch {
|
|
1197
|
+
if (!usesRouted) {
|
|
1198
|
+
return (input, _pos, selEnd, key, _selectorMark, ctx) => {
|
|
1199
|
+
const mark = saveTriviaMark(ctx)
|
|
1200
|
+
const v = child(input, selEnd, ctx)
|
|
1201
|
+
if (v === FAIL) {
|
|
1202
|
+
rollbackTrivia(ctx, mark)
|
|
1203
|
+
ctx._fc = true
|
|
1204
|
+
return FAIL
|
|
1205
|
+
}
|
|
1206
|
+
return [key, v]
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
return (input, pos, selEnd, key, selectorMark, ctx) => {
|
|
1210
|
+
const savedRouted = ctx._routed
|
|
1211
|
+
rollbackTrivia(ctx, selectorMark)
|
|
1212
|
+
const mark = saveTriviaMark(ctx)
|
|
1213
|
+
ctx._routed = { value: key, span: { start: pos, end: selEnd } }
|
|
1214
|
+
let v: unknown
|
|
1215
|
+
try {
|
|
1216
|
+
v = child(input, pos, ctx)
|
|
1217
|
+
} finally {
|
|
1218
|
+
ctx._routed = savedRouted
|
|
1219
|
+
}
|
|
1220
|
+
if (v === FAIL) {
|
|
1221
|
+
rollbackTrivia(ctx, mark)
|
|
1222
|
+
ctx._fc = true
|
|
1223
|
+
return FAIL
|
|
1224
|
+
}
|
|
1225
|
+
return [key, v]
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
function dispatchArmBlock(
|
|
1230
|
+
start: number,
|
|
1231
|
+
a0: DispatchBranch, a1: DispatchBranch, a2: DispatchBranch, a3: DispatchBranch,
|
|
1232
|
+
next: DispatchArmBlock | undefined,
|
|
1233
|
+
): DispatchArmBlock {
|
|
1234
|
+
return (arm, input, pos, selEnd, key, selectorMark, ctx) => {
|
|
1235
|
+
if (arm === start) return a0(input, pos, selEnd, key, selectorMark, ctx)
|
|
1236
|
+
if (arm === start + 1) return a1(input, pos, selEnd, key, selectorMark, ctx)
|
|
1237
|
+
if (arm === start + 2) return a2(input, pos, selEnd, key, selectorMark, ctx)
|
|
1238
|
+
if (arm === start + 3) return a3(input, pos, selEnd, key, selectorMark, ctx)
|
|
1239
|
+
return next === undefined ? FAIL : next(arm, input, pos, selEnd, key, selectorMark, ctx)
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
708
1243
|
|
|
709
1244
|
|
|
710
1245
|
function trackLinesInto(ctx: ParseContext, input: string, end: number): void {
|
|
@@ -759,6 +1294,20 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
759
1294
|
const s = k[code[ip + 1]!] as string
|
|
760
1295
|
const len = s.length
|
|
761
1296
|
const xf = fx[code[ip + 2]!] as string[]
|
|
1297
|
+
const shared = rawScalarSpecs.has(code[ip + 1]!) ? scalarFor(ip) : undefined
|
|
1298
|
+
if (shared !== undefined) {
|
|
1299
|
+
return (input, pos, ctx) => {
|
|
1300
|
+
const e = shared(input, pos)
|
|
1301
|
+
if (e >= 0) {
|
|
1302
|
+
if (cstCaptureActive(ctx)) pushLeaf(ctx, s, pos, e)
|
|
1303
|
+
EC.e = e
|
|
1304
|
+
return s
|
|
1305
|
+
}
|
|
1306
|
+
ctx._fe = pos; ctx._fx = xf
|
|
1307
|
+
if (ctx._probe !== undefined) failAt(ctx, xf, pos)
|
|
1308
|
+
return FAIL
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
762
1311
|
// LENGTH IS TABLE DATA, so the COMPARE is chosen here rather than
|
|
763
1312
|
// delegated to a builtin that has to rediscover it. See `litBodyNote`.
|
|
764
1313
|
if (len === 1) {
|
|
@@ -884,6 +1433,21 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
884
1433
|
case OP_RX: {
|
|
885
1434
|
const re = k[code[ip + 1]!] as RegExp
|
|
886
1435
|
const xf = fx[code[ip + 2]!] as string[]
|
|
1436
|
+
const shared = rawScalarSpecs.has(code[ip + 1]!) ? scalarFor(ip) : undefined
|
|
1437
|
+
if (shared !== undefined) {
|
|
1438
|
+
return (input, pos, ctx) => {
|
|
1439
|
+
const e = shared(input, pos)
|
|
1440
|
+
if (e >= 0) {
|
|
1441
|
+
const v = input.slice(pos, e)
|
|
1442
|
+
if (cstCaptureActive(ctx)) pushLeaf(ctx, v, pos, e)
|
|
1443
|
+
EC.e = e
|
|
1444
|
+
return v
|
|
1445
|
+
}
|
|
1446
|
+
ctx._fe = pos; ctx._fx = xf
|
|
1447
|
+
if (ctx._probe !== undefined) failAt(ctx, xf, pos)
|
|
1448
|
+
return FAIL
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
887
1451
|
return (input, pos, ctx) => {
|
|
888
1452
|
re.lastIndex = pos
|
|
889
1453
|
// `regex()` rows are sticky and expose only their whole match. `exec()`
|
|
@@ -1307,6 +1871,21 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1307
1871
|
/* ── zero-width ──────────────────────────────────────────────────────── */
|
|
1308
1872
|
|
|
1309
1873
|
case OP_NOT: {
|
|
1874
|
+
const scalarChild = scalarTerminalNotChild(code, ip)
|
|
1875
|
+
if (scalarChild >= 0) {
|
|
1876
|
+
const recognize = scalarFor(scalarChild)!
|
|
1877
|
+
const xf = fx[code[ip + 2]!] as string[]
|
|
1878
|
+
return (input, pos, ctx) => {
|
|
1879
|
+
if (recognize(input, pos) < 0) {
|
|
1880
|
+
EC.e = pos
|
|
1881
|
+
return null
|
|
1882
|
+
}
|
|
1883
|
+
ctx._fe = pos
|
|
1884
|
+
ctx._fx = xf
|
|
1885
|
+
EC.e = pos
|
|
1886
|
+
return FAIL
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1310
1889
|
const child = link(code[ip + 1]!)
|
|
1311
1890
|
const xf = fx[code[ip + 2]!] as string[]
|
|
1312
1891
|
return (input, pos, ctx) => {
|
|
@@ -1420,6 +1999,93 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1420
1999
|
}
|
|
1421
2000
|
}
|
|
1422
2001
|
|
|
2002
|
+
case OP_LEX_BODY: {
|
|
2003
|
+
const recognize = t.lex[code[ip + 1]!]!
|
|
2004
|
+
const expected = fx[code[ip + 2]!] as string[]
|
|
2005
|
+
return (input, pos, ctx) => {
|
|
2006
|
+
const recognized = recognize(input, pos)
|
|
2007
|
+
if (recognized < 0) {
|
|
2008
|
+
ctx._fe = pos
|
|
2009
|
+
ctx._fx = expected as string[]
|
|
2010
|
+
if (ctx._probe !== undefined) failAt(ctx, expected, pos)
|
|
2011
|
+
return FAIL
|
|
2012
|
+
}
|
|
2013
|
+
const lineFlags = code[ip + 4]!
|
|
2014
|
+
const hasSuffix = (lineFlags & 4) !== 0
|
|
2015
|
+
const suffixMatched = hasSuffix && recognized % 2 === 1
|
|
2016
|
+
const end = (recognized - (suffixMatched ? 1 : 0)) / 2
|
|
2017
|
+
if ((lineFlags & 1) !== 0) {
|
|
2018
|
+
trackLinesInto(ctx, input, suffixMatched ? end - 1 : end)
|
|
2019
|
+
}
|
|
2020
|
+
if (hasSuffix) ctx._fc = false
|
|
2021
|
+
if (hasSuffix && suffixMatched && (lineFlags & 2) !== 0) {
|
|
2022
|
+
trackLinesInto(ctx, input, end)
|
|
2023
|
+
}
|
|
2024
|
+
if (hasSuffix && !suffixMatched) {
|
|
2025
|
+
const suffixExpected = fx[code[ip + 3]!] as string[]
|
|
2026
|
+
ctx._fe = end
|
|
2027
|
+
ctx._fx = suffixExpected
|
|
2028
|
+
if (ctx._probe !== undefined) failAt(ctx, suffixExpected, end)
|
|
2029
|
+
}
|
|
2030
|
+
const value = input.slice(pos, end)
|
|
2031
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
|
|
2032
|
+
EC.e = end
|
|
2033
|
+
return value
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
case OP_LEX_PROGRAM: {
|
|
2038
|
+
const run = t.lexPrograms[code[ip + 1]!]!
|
|
2039
|
+
const scanId = run.scan
|
|
2040
|
+
if (scanId !== undefined) {
|
|
2041
|
+
return (input, pos, ctx) => {
|
|
2042
|
+
const sTrivia = ctx.trivia, sKinds = ctx.triviaKindLabels
|
|
2043
|
+
const sBuf = ctx._cstBuf, sChildren = ctx._cstChildren, sLeaves = ctx._cstLeaves
|
|
2044
|
+
const sRaw = ctx._cstRawChildren, sTl = ctx._cstTriviaLog
|
|
2045
|
+
const sOuterTl = ctx._triviaLog, sRootTl = ctx._rootTriviaLog
|
|
2046
|
+
const wasCapturing = cstCaptureActive(ctx)
|
|
2047
|
+
const sScan = SCAN
|
|
2048
|
+
SCAN = null
|
|
2049
|
+
ctx.trivia = undefined
|
|
2050
|
+
ctx.triviaKindLabels = undefined
|
|
2051
|
+
ctx._cstBuf = undefined
|
|
2052
|
+
ctx._cstChildren = undefined
|
|
2053
|
+
ctx._cstLeaves = undefined
|
|
2054
|
+
ctx._cstRawChildren = undefined
|
|
2055
|
+
ctx._cstTriviaLog = undefined
|
|
2056
|
+
ctx._triviaLog = undefined
|
|
2057
|
+
ctx._rootTriviaLog = undefined
|
|
2058
|
+
let end: number
|
|
2059
|
+
try { end = run(input, pos, ctx, scans[scanId]) }
|
|
2060
|
+
finally {
|
|
2061
|
+
SCAN = sScan
|
|
2062
|
+
ctx.trivia = sTrivia
|
|
2063
|
+
ctx.triviaKindLabels = sKinds
|
|
2064
|
+
ctx._cstBuf = sBuf
|
|
2065
|
+
ctx._cstChildren = sChildren
|
|
2066
|
+
ctx._cstLeaves = sLeaves
|
|
2067
|
+
ctx._cstRawChildren = sRaw
|
|
2068
|
+
ctx._cstTriviaLog = sTl
|
|
2069
|
+
ctx._triviaLog = sOuterTl
|
|
2070
|
+
ctx._rootTriviaLog = sRootTl
|
|
2071
|
+
}
|
|
2072
|
+
if (end < 0) return FAIL
|
|
2073
|
+
const value = input.slice(pos, end)
|
|
2074
|
+
if (wasCapturing) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
|
|
2075
|
+
EC.e = end
|
|
2076
|
+
return value
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
return (input, pos, ctx) => {
|
|
2080
|
+
const end = run(input, pos, ctx)
|
|
2081
|
+
if (end < 0) return FAIL
|
|
2082
|
+
const value = input.slice(pos, end)
|
|
2083
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
|
|
2084
|
+
EC.e = end
|
|
2085
|
+
return value
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
|
|
1423
2089
|
case OP_LEAF: {
|
|
1424
2090
|
const fn = fns[code[ip + 1]!] as (value: unknown, span: { start: number; end: number }) => unknown
|
|
1425
2091
|
const child = link(code[ip + 2]!)
|
|
@@ -1464,36 +2130,60 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1464
2130
|
const fused = op === OP_SEQX
|
|
1465
2131
|
const base = fused ? ip + 3 : ip + 2
|
|
1466
2132
|
const n = code[fused ? ip + 2 : ip + 1]!
|
|
1467
|
-
// Children bound as a plain array of DIRECT references, laid out once.
|
|
1468
|
-
const kids: Piece[] = new Array<Piece>(n)
|
|
1469
|
-
for (let i = 0; i < n; i++) kids[i] = link(code[base + i]!)
|
|
1470
2133
|
const wantValues = op !== OP_SEQV
|
|
1471
|
-
const
|
|
1472
|
-
|
|
2134
|
+
const reducer = fused ? code[ip + 1]! : -1
|
|
2135
|
+
const projection = reducer < 0 ? ~reducer : -1
|
|
2136
|
+
const fn = fused && projection < 0
|
|
2137
|
+
? fns[reducer] as (value: unknown, span: { start: number; end: number }) => unknown
|
|
1473
2138
|
: undefined
|
|
1474
2139
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
* INLINING a small monomorphic child. Re-emitting the terminal bodies
|
|
1483
|
-
* would instead grow this function past the inlining budget, which is the
|
|
1484
|
-
* defect being fixed, so they are not re-emitted.
|
|
1485
|
-
*/
|
|
1486
|
-
const runTerms = (input: string, pos: number, ctx: ParseContext, values: unknown[] | undefined): number => {
|
|
1487
|
-
const v0 = kids[0]!(input, pos, ctx)
|
|
1488
|
-
if (v0 === FAIL) return -1
|
|
1489
|
-
if (values !== undefined) values.push(v0)
|
|
1490
|
-
let cur = EC.e
|
|
1491
|
-
for (let i = 1; i < n; i++) {
|
|
1492
|
-
cur = nextTerm(kids[i]!, input, cur, ctx)
|
|
2140
|
+
const runnerBlock = (
|
|
2141
|
+
count: number,
|
|
2142
|
+
r0: TermRunner, r1: TermRunner, r2: TermRunner, r3: TermRunner,
|
|
2143
|
+
next: SequenceTermBlock | undefined,
|
|
2144
|
+
): SequenceTermBlock => {
|
|
2145
|
+
if (count === 1) return (input, cur, ctx, values, inherited) => {
|
|
2146
|
+
cur = r0(input, cur, ctx, inherited)
|
|
1493
2147
|
if (cur < 0) return -1
|
|
1494
2148
|
if (values !== undefined) values.push(TERMV)
|
|
2149
|
+
return next === undefined ? cur : next(input, cur, ctx, values, inherited)
|
|
2150
|
+
}
|
|
2151
|
+
if (count === 2) return (input, cur, ctx, values, inherited) => {
|
|
2152
|
+
cur = r0(input, cur, ctx, inherited)
|
|
2153
|
+
if (cur < 0) return -1
|
|
2154
|
+
if (values !== undefined) values.push(TERMV)
|
|
2155
|
+
cur = r1(input, cur, ctx, inherited)
|
|
2156
|
+
if (cur < 0) return -1
|
|
2157
|
+
if (values !== undefined) values.push(TERMV)
|
|
2158
|
+
return next === undefined ? cur : next(input, cur, ctx, values, inherited)
|
|
2159
|
+
}
|
|
2160
|
+
if (count === 3) return (input, cur, ctx, values, inherited) => {
|
|
2161
|
+
cur = r0(input, cur, ctx, inherited)
|
|
2162
|
+
if (cur < 0) return -1
|
|
2163
|
+
if (values !== undefined) values.push(TERMV)
|
|
2164
|
+
cur = r1(input, cur, ctx, inherited)
|
|
2165
|
+
if (cur < 0) return -1
|
|
2166
|
+
if (values !== undefined) values.push(TERMV)
|
|
2167
|
+
cur = r2(input, cur, ctx, inherited)
|
|
2168
|
+
if (cur < 0) return -1
|
|
2169
|
+
if (values !== undefined) values.push(TERMV)
|
|
2170
|
+
return next === undefined ? cur : next(input, cur, ctx, values, inherited)
|
|
2171
|
+
}
|
|
2172
|
+
return (input, cur, ctx, values, inherited) => {
|
|
2173
|
+
cur = r0(input, cur, ctx, inherited)
|
|
2174
|
+
if (cur < 0) return -1
|
|
2175
|
+
if (values !== undefined) values.push(TERMV)
|
|
2176
|
+
cur = r1(input, cur, ctx, inherited)
|
|
2177
|
+
if (cur < 0) return -1
|
|
2178
|
+
if (values !== undefined) values.push(TERMV)
|
|
2179
|
+
cur = r2(input, cur, ctx, inherited)
|
|
2180
|
+
if (cur < 0) return -1
|
|
2181
|
+
if (values !== undefined) values.push(TERMV)
|
|
2182
|
+
cur = r3(input, cur, ctx, inherited)
|
|
2183
|
+
if (cur < 0) return -1
|
|
2184
|
+
if (values !== undefined) values.push(TERMV)
|
|
2185
|
+
return next === undefined ? cur : next(input, cur, ctx, values, inherited)
|
|
1495
2186
|
}
|
|
1496
|
-
return cur
|
|
1497
2187
|
}
|
|
1498
2188
|
|
|
1499
2189
|
/**
|
|
@@ -1544,21 +2234,24 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1544
2234
|
* sentinel published is the kind of state a later reader will trust.
|
|
1545
2235
|
*/
|
|
1546
2236
|
if (REC) {
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
const
|
|
1550
|
-
|
|
2237
|
+
const first = link(code[base]!)
|
|
2238
|
+
const firstSync = sentinelFor(code[base + n]!)
|
|
2239
|
+
const runnerAt = (i: number): TermRunner => {
|
|
2240
|
+
const sync = sentinelFor(code[base + n + i]!)
|
|
1551
2241
|
const kidIp = code[base + i]!
|
|
1552
2242
|
if (code[kidIp] !== OP_ADJ) {
|
|
1553
|
-
const kid =
|
|
1554
|
-
|
|
1555
|
-
|
|
2243
|
+
const kid = link(kidIp)
|
|
2244
|
+
return (input, cur, ctx, inherited) => {
|
|
2245
|
+
ctx._sync = sync ?? inherited
|
|
2246
|
+
return nextTerm(kid, input, cur, ctx)
|
|
2247
|
+
}
|
|
1556
2248
|
}
|
|
1557
2249
|
const negated = code[kidIp + 1] === 1
|
|
1558
2250
|
const ki = code[kidIp + 2]!
|
|
1559
2251
|
const kinds = ki < 0 ? undefined : k[ki] as readonly string[]
|
|
1560
2252
|
const xf = fx[code[kidIp + 3]!] as string[]
|
|
1561
|
-
|
|
2253
|
+
return (input, cur, ctx, inherited) => {
|
|
2254
|
+
ctx._sync = sync ?? inherited
|
|
1562
2255
|
if (!adjacencyHolds(input, cur, ctx, negated, kinds)) {
|
|
1563
2256
|
ctx._fe = cur; ctx._fx = xf
|
|
1564
2257
|
return -1
|
|
@@ -1567,19 +2260,25 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1567
2260
|
return cur
|
|
1568
2261
|
}
|
|
1569
2262
|
}
|
|
2263
|
+
const bindRunners = (start: number): SequenceTermBlock => {
|
|
2264
|
+
const count = Math.min(4, n - start)
|
|
2265
|
+
const at = (offset: number): TermRunner => runnerAt(start + offset)
|
|
2266
|
+
const never: TermRunner = () => -1
|
|
2267
|
+
return runnerBlock(
|
|
2268
|
+
count, at(0), count > 1 ? at(1) : never,
|
|
2269
|
+
count > 2 ? at(2) : never, count > 3 ? at(3) : never,
|
|
2270
|
+
start + count < n ? bindRunners(start + count) : undefined,
|
|
2271
|
+
)
|
|
2272
|
+
}
|
|
2273
|
+
const tail = n > 1 ? bindRunners(1) : undefined
|
|
1570
2274
|
const runSyncTerms = (input: string, pos: number, ctx: ParseContext, values: unknown[] | undefined): number => {
|
|
1571
2275
|
const inherited = ctx._sync
|
|
1572
|
-
ctx._sync =
|
|
1573
|
-
const v0 =
|
|
2276
|
+
ctx._sync = firstSync ?? inherited
|
|
2277
|
+
const v0 = first(input, pos, ctx)
|
|
1574
2278
|
if (v0 === FAIL) { ctx._sync = inherited; return -1 }
|
|
1575
2279
|
if (values !== undefined) values.push(v0)
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
ctx._sync = syncs[i] ?? inherited
|
|
1579
|
-
cur = runners[i]!(input, cur, ctx)
|
|
1580
|
-
if (cur < 0) { ctx._sync = inherited; return -1 }
|
|
1581
|
-
if (values !== undefined) values.push(TERMV)
|
|
1582
|
-
}
|
|
2280
|
+
const cur = tail === undefined ? EC.e : tail(input, EC.e, ctx, values, inherited)
|
|
2281
|
+
if (cur < 0) { ctx._sync = inherited; return -1 }
|
|
1583
2282
|
ctx._sync = inherited
|
|
1584
2283
|
return cur
|
|
1585
2284
|
}
|
|
@@ -1589,7 +2288,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1589
2288
|
const cur = runSyncTerms(input, pos, ctx, values)
|
|
1590
2289
|
if (cur < 0) return FAIL
|
|
1591
2290
|
EC.e = cur
|
|
1592
|
-
return fn!(values, { start: pos, end: cur })
|
|
2291
|
+
return projection >= 0 ? values[projection] : fn!(values, { start: pos, end: cur })
|
|
1593
2292
|
}
|
|
1594
2293
|
}
|
|
1595
2294
|
if (wantValues) {
|
|
@@ -1612,13 +2311,12 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1612
2311
|
let hasAdj = false
|
|
1613
2312
|
for (let i = 1; i < n; i++) if (code[code[base + i]!] === OP_ADJ) { hasAdj = true; break }
|
|
1614
2313
|
if (hasAdj) {
|
|
1615
|
-
const
|
|
1616
|
-
|
|
2314
|
+
const first = link(code[base]!)
|
|
2315
|
+
const runnerAt = (i: number): TermRunner => {
|
|
1617
2316
|
const kidIp = code[base + i]!
|
|
1618
2317
|
if (code[kidIp] !== OP_ADJ) {
|
|
1619
|
-
const kid =
|
|
1620
|
-
|
|
1621
|
-
continue
|
|
2318
|
+
const kid = link(kidIp)
|
|
2319
|
+
return (input, cur, ctx) => nextTerm(kid, input, cur, ctx)
|
|
1622
2320
|
}
|
|
1623
2321
|
const negated = code[kidIp + 1] === 1
|
|
1624
2322
|
const ki = code[kidIp + 2]!
|
|
@@ -1628,7 +2326,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1628
2326
|
// the interpreter does.
|
|
1629
2327
|
const kinds = ki < 0 ? undefined : k[ki] as readonly string[]
|
|
1630
2328
|
const xf = fx[code[kidIp + 3]!] as string[]
|
|
1631
|
-
|
|
2329
|
+
return (input, cur, ctx) => {
|
|
1632
2330
|
if (!adjacencyHolds(input, cur, ctx, negated, kinds)) {
|
|
1633
2331
|
ctx._fe = cur; ctx._fx = xf
|
|
1634
2332
|
return -1
|
|
@@ -1637,17 +2335,22 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1637
2335
|
return cur
|
|
1638
2336
|
}
|
|
1639
2337
|
}
|
|
2338
|
+
const bindRunners = (start: number): SequenceTermBlock => {
|
|
2339
|
+
const count = Math.min(4, n - start)
|
|
2340
|
+
const at = (offset: number): TermRunner => runnerAt(start + offset)
|
|
2341
|
+
const never: TermRunner = () => -1
|
|
2342
|
+
return runnerBlock(
|
|
2343
|
+
count, at(0), count > 1 ? at(1) : never,
|
|
2344
|
+
count > 2 ? at(2) : never, count > 3 ? at(3) : never,
|
|
2345
|
+
start + count < n ? bindRunners(start + count) : undefined,
|
|
2346
|
+
)
|
|
2347
|
+
}
|
|
2348
|
+
const tail = bindRunners(1)
|
|
1640
2349
|
const runAdjTerms = (input: string, pos: number, ctx: ParseContext, values: unknown[] | undefined): number => {
|
|
1641
|
-
const v0 =
|
|
2350
|
+
const v0 = first(input, pos, ctx)
|
|
1642
2351
|
if (v0 === FAIL) return -1
|
|
1643
2352
|
if (values !== undefined) values.push(v0)
|
|
1644
|
-
|
|
1645
|
-
for (let i = 1; i < n; i++) {
|
|
1646
|
-
cur = runners[i]!(input, cur, ctx)
|
|
1647
|
-
if (cur < 0) return -1
|
|
1648
|
-
if (values !== undefined) values.push(TERMV)
|
|
1649
|
-
}
|
|
1650
|
-
return cur
|
|
2353
|
+
return tail(input, EC.e, ctx, values)
|
|
1651
2354
|
}
|
|
1652
2355
|
if (fused) {
|
|
1653
2356
|
return (input, pos, ctx) => {
|
|
@@ -1655,7 +2358,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1655
2358
|
const cur = runAdjTerms(input, pos, ctx, values)
|
|
1656
2359
|
if (cur < 0) return FAIL
|
|
1657
2360
|
EC.e = cur
|
|
1658
|
-
return fn!(values, { start: pos, end: cur })
|
|
2361
|
+
return projection >= 0 ? values[projection] : fn!(values, { start: pos, end: cur })
|
|
1659
2362
|
}
|
|
1660
2363
|
}
|
|
1661
2364
|
if (wantValues) {
|
|
@@ -1693,12 +2396,12 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1693
2396
|
* runs, so there is no arity this fails to lower.
|
|
1694
2397
|
*/
|
|
1695
2398
|
if (n === 1) {
|
|
1696
|
-
const k0 =
|
|
2399
|
+
const k0 = link(code[base]!)
|
|
1697
2400
|
if (fused) {
|
|
1698
2401
|
return (input, pos, ctx) => {
|
|
1699
2402
|
const v = k0(input, pos, ctx)
|
|
1700
2403
|
if (v === FAIL) return FAIL
|
|
1701
|
-
return fn!([v], { start: pos, end: EC.e })
|
|
2404
|
+
return projection === 0 ? v : fn!([v], { start: pos, end: EC.e })
|
|
1702
2405
|
}
|
|
1703
2406
|
}
|
|
1704
2407
|
if (wantValues) {
|
|
@@ -1716,7 +2419,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1716
2419
|
}
|
|
1717
2420
|
|
|
1718
2421
|
if (n === 2) {
|
|
1719
|
-
const k0 =
|
|
2422
|
+
const k0 = link(code[base]!), k1 = link(code[base + 1]!)
|
|
1720
2423
|
if (fused) {
|
|
1721
2424
|
return (input, pos, ctx) => {
|
|
1722
2425
|
const v0 = k0(input, pos, ctx)
|
|
@@ -1724,7 +2427,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1724
2427
|
const cur = nextTerm(k1, input, EC.e, ctx)
|
|
1725
2428
|
if (cur < 0) return FAIL
|
|
1726
2429
|
EC.e = cur
|
|
1727
|
-
return
|
|
2430
|
+
return projection === 0 ? v0
|
|
2431
|
+
: projection === 1 ? TERMV
|
|
2432
|
+
: fn!([v0, TERMV], { start: pos, end: cur })
|
|
1728
2433
|
}
|
|
1729
2434
|
}
|
|
1730
2435
|
if (wantValues) {
|
|
@@ -1748,7 +2453,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1748
2453
|
}
|
|
1749
2454
|
|
|
1750
2455
|
if (n === 3) {
|
|
1751
|
-
const k0 =
|
|
2456
|
+
const k0 = link(code[base]!), k1 = link(code[base + 1]!), k2 = link(code[base + 2]!)
|
|
1752
2457
|
if (fused) {
|
|
1753
2458
|
return (input, pos, ctx) => {
|
|
1754
2459
|
const v0 = k0(input, pos, ctx)
|
|
@@ -1759,7 +2464,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1759
2464
|
cur = nextTerm(k2, input, cur, ctx)
|
|
1760
2465
|
if (cur < 0) return FAIL
|
|
1761
2466
|
EC.e = cur
|
|
1762
|
-
return
|
|
2467
|
+
return projection === 0 ? v0
|
|
2468
|
+
: projection === 1 ? v1
|
|
2469
|
+
: projection === 2 ? TERMV
|
|
2470
|
+
: fn!([v0, v1, TERMV], { start: pos, end: cur })
|
|
1763
2471
|
}
|
|
1764
2472
|
}
|
|
1765
2473
|
if (wantValues) {
|
|
@@ -1787,26 +2495,97 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1787
2495
|
}
|
|
1788
2496
|
}
|
|
1789
2497
|
|
|
2498
|
+
// Arbitrary strict arities use a bounded scalar block chain. Fixed
|
|
2499
|
+
// children are captured into prewritten four-term blocks at assembly;
|
|
2500
|
+
// the parse path has no `kids[i]`, fixed-child loop, or opcode lookup.
|
|
2501
|
+
const block = (
|
|
2502
|
+
count: number,
|
|
2503
|
+
k0: Piece, k1: Piece, k2: Piece, k3: Piece,
|
|
2504
|
+
next: SequenceTermBlock | undefined,
|
|
2505
|
+
): SequenceTermBlock => {
|
|
2506
|
+
if (count === 1) return (input, cur, ctx, values) => {
|
|
2507
|
+
cur = nextTerm(k0, input, cur, ctx)
|
|
2508
|
+
if (cur < 0) return -1
|
|
2509
|
+
if (values !== undefined) values.push(TERMV)
|
|
2510
|
+
return next === undefined ? cur : next(input, cur, ctx, values)
|
|
2511
|
+
}
|
|
2512
|
+
if (count === 2) return (input, cur, ctx, values) => {
|
|
2513
|
+
cur = nextTerm(k0, input, cur, ctx)
|
|
2514
|
+
if (cur < 0) return -1
|
|
2515
|
+
if (values !== undefined) values.push(TERMV)
|
|
2516
|
+
cur = nextTerm(k1, input, cur, ctx)
|
|
2517
|
+
if (cur < 0) return -1
|
|
2518
|
+
if (values !== undefined) values.push(TERMV)
|
|
2519
|
+
return next === undefined ? cur : next(input, cur, ctx, values)
|
|
2520
|
+
}
|
|
2521
|
+
if (count === 3) return (input, cur, ctx, values) => {
|
|
2522
|
+
cur = nextTerm(k0, input, cur, ctx)
|
|
2523
|
+
if (cur < 0) return -1
|
|
2524
|
+
if (values !== undefined) values.push(TERMV)
|
|
2525
|
+
cur = nextTerm(k1, input, cur, ctx)
|
|
2526
|
+
if (cur < 0) return -1
|
|
2527
|
+
if (values !== undefined) values.push(TERMV)
|
|
2528
|
+
cur = nextTerm(k2, input, cur, ctx)
|
|
2529
|
+
if (cur < 0) return -1
|
|
2530
|
+
if (values !== undefined) values.push(TERMV)
|
|
2531
|
+
return next === undefined ? cur : next(input, cur, ctx, values)
|
|
2532
|
+
}
|
|
2533
|
+
return (input, cur, ctx, values) => {
|
|
2534
|
+
cur = nextTerm(k0, input, cur, ctx)
|
|
2535
|
+
if (cur < 0) return -1
|
|
2536
|
+
if (values !== undefined) values.push(TERMV)
|
|
2537
|
+
cur = nextTerm(k1, input, cur, ctx)
|
|
2538
|
+
if (cur < 0) return -1
|
|
2539
|
+
if (values !== undefined) values.push(TERMV)
|
|
2540
|
+
cur = nextTerm(k2, input, cur, ctx)
|
|
2541
|
+
if (cur < 0) return -1
|
|
2542
|
+
if (values !== undefined) values.push(TERMV)
|
|
2543
|
+
cur = nextTerm(k3, input, cur, ctx)
|
|
2544
|
+
if (cur < 0) return -1
|
|
2545
|
+
if (values !== undefined) values.push(TERMV)
|
|
2546
|
+
return next === undefined ? cur : next(input, cur, ctx, values)
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
const bind = (start: number): SequenceTermBlock => {
|
|
2550
|
+
const count = Math.min(4, n - start)
|
|
2551
|
+
const child = (offset: number): Piece => link(code[base + start + offset]!)
|
|
2552
|
+
return block(
|
|
2553
|
+
count, child(0), count > 1 ? child(1) : NEVER_PIECE,
|
|
2554
|
+
count > 2 ? child(2) : NEVER_PIECE, count > 3 ? child(3) : NEVER_PIECE,
|
|
2555
|
+
start + count < n ? bind(start + count) : undefined,
|
|
2556
|
+
)
|
|
2557
|
+
}
|
|
2558
|
+
const first = link(code[base]!)
|
|
2559
|
+
const tail = bind(1)
|
|
2560
|
+
const runDirectTerms = (
|
|
2561
|
+
input: string, pos: number, ctx: ParseContext, values: unknown[] | undefined,
|
|
2562
|
+
): number => {
|
|
2563
|
+
const v0 = first(input, pos, ctx)
|
|
2564
|
+
if (v0 === FAIL) return -1
|
|
2565
|
+
if (values !== undefined) values.push(v0)
|
|
2566
|
+
return tail(input, EC.e, ctx, values)
|
|
2567
|
+
}
|
|
2568
|
+
|
|
1790
2569
|
if (fused) {
|
|
1791
2570
|
return (input, pos, ctx) => {
|
|
1792
2571
|
const values: unknown[] = []
|
|
1793
|
-
const cur =
|
|
2572
|
+
const cur = runDirectTerms(input, pos, ctx, values)
|
|
1794
2573
|
if (cur < 0) return FAIL
|
|
1795
2574
|
EC.e = cur
|
|
1796
|
-
return fn!(values, { start: pos, end: cur })
|
|
2575
|
+
return projection >= 0 ? values[projection] : fn!(values, { start: pos, end: cur })
|
|
1797
2576
|
}
|
|
1798
2577
|
}
|
|
1799
2578
|
if (wantValues) {
|
|
1800
2579
|
return (input, pos, ctx) => {
|
|
1801
2580
|
const values: unknown[] = []
|
|
1802
|
-
const cur =
|
|
2581
|
+
const cur = runDirectTerms(input, pos, ctx, values)
|
|
1803
2582
|
if (cur < 0) return FAIL
|
|
1804
2583
|
EC.e = cur
|
|
1805
2584
|
return values
|
|
1806
2585
|
}
|
|
1807
2586
|
}
|
|
1808
2587
|
return (input, pos, ctx) => {
|
|
1809
|
-
const cur =
|
|
2588
|
+
const cur = runDirectTerms(input, pos, ctx, undefined)
|
|
1810
2589
|
if (cur < 0) return FAIL
|
|
1811
2590
|
EC.e = cur
|
|
1812
2591
|
return undefined
|
|
@@ -1820,20 +2599,74 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1820
2599
|
const n = code[ip + 2]!
|
|
1821
2600
|
const choiceFx = fx[code[ip + 3]!] as string[]
|
|
1822
2601
|
const base = ip + 4
|
|
1823
|
-
const arms: Piece[] = new Array<Piece>(n)
|
|
1824
|
-
for (let i = 0; i < n; i++) arms[i] = link(code[base + i]!)
|
|
1825
|
-
// PER-ARM STATIC SETS, resolved once at assembly. An arm the char gate
|
|
1826
|
-
// declines to enter still owes its expectation: the interpreter has no
|
|
1827
|
-
// such gate, enters the arm, and its opener's failure is what lands in
|
|
1828
|
-
// the accumulation.
|
|
1829
|
-
const armFx: (string[])[] = new Array<string[]>(n)
|
|
1830
|
-
for (let i = 0; i < n; i++) armFx[i] = fx[code[base + n + i]!] as string[]
|
|
1831
2602
|
|
|
1832
2603
|
// THE TWO CHOICE SHAPES ARE TWO PIECES. `exec.ts` tested `table.exclusive`
|
|
1833
2604
|
// on every choice execution; it is table data, so it selects here.
|
|
1834
2605
|
if (table.exclusive) {
|
|
1835
2606
|
const ascii = table.ascii
|
|
1836
2607
|
const hi = table.hi
|
|
2608
|
+
// THE COMMON ARITIES OWN THEIR TOPOLOGY AS SCALARS. Do not build an
|
|
2609
|
+
// `arms[]` merely to rediscover the same two or three children at
|
|
2610
|
+
// every dispatch. The classifier remains table data; only its result
|
|
2611
|
+
// is turned into a direct captured call.
|
|
2612
|
+
if (n === 2) {
|
|
2613
|
+
const a0 = link(code[base]!), a1 = link(code[base + 1]!)
|
|
2614
|
+
return (input, pos, ctx) => {
|
|
2615
|
+
const c = lead(input, pos)
|
|
2616
|
+
let arm = -1
|
|
2617
|
+
if (c >= 0 && c < 128) {
|
|
2618
|
+
const a = ascii[c]!
|
|
2619
|
+
if (a !== 0) arm = a - 1
|
|
2620
|
+
} else if (c >= 128) {
|
|
2621
|
+
for (let i = 0; i < hi.length; i += 3) {
|
|
2622
|
+
if (c >= hi[i]! && c <= hi[i + 1]!) { arm = hi[i + 2]!; break }
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
if (arm >= 0) {
|
|
2626
|
+
ctx._fc = false
|
|
2627
|
+
const v = arm === 0 ? a0(input, pos, ctx) : a1(input, pos, ctx)
|
|
2628
|
+
if (v !== FAIL) return v
|
|
2629
|
+
if (committed(ctx)) return FAIL
|
|
2630
|
+
const failed = ctx._fx
|
|
2631
|
+
if (failed !== undefined && failed.length > 0) { ctx._fe = pos; return FAIL }
|
|
2632
|
+
}
|
|
2633
|
+
ctx._fe = pos; ctx._fx = choiceFx
|
|
2634
|
+
return FAIL
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
if (n === 3) {
|
|
2638
|
+
const a0 = link(code[base]!), a1 = link(code[base + 1]!), a2 = link(code[base + 2]!)
|
|
2639
|
+
return (input, pos, ctx) => {
|
|
2640
|
+
const c = lead(input, pos)
|
|
2641
|
+
let arm = -1
|
|
2642
|
+
if (c >= 0 && c < 128) {
|
|
2643
|
+
const a = ascii[c]!
|
|
2644
|
+
if (a !== 0) arm = a - 1
|
|
2645
|
+
} else if (c >= 128) {
|
|
2646
|
+
for (let i = 0; i < hi.length; i += 3) {
|
|
2647
|
+
if (c >= hi[i]! && c <= hi[i + 1]!) { arm = hi[i + 2]!; break }
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
if (arm >= 0) {
|
|
2651
|
+
ctx._fc = false
|
|
2652
|
+
const v = arm === 0 ? a0(input, pos, ctx) : arm === 1 ? a1(input, pos, ctx) : a2(input, pos, ctx)
|
|
2653
|
+
if (v !== FAIL) return v
|
|
2654
|
+
if (committed(ctx)) return FAIL
|
|
2655
|
+
const failed = ctx._fx
|
|
2656
|
+
if (failed !== undefined && failed.length > 0) { ctx._fe = pos; return FAIL }
|
|
2657
|
+
}
|
|
2658
|
+
ctx._fe = pos; ctx._fx = choiceFx
|
|
2659
|
+
return FAIL
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
const bindExclusive = (start: number): ExclusiveChoiceBlock => {
|
|
2663
|
+
const at = (i: number): Piece => i < n ? link(code[base + i]!) : NEVER_PIECE
|
|
2664
|
+
const next = start + 4 < n ? bindExclusive(start + 4) : undefined
|
|
2665
|
+
return exclusiveChoiceBlock(
|
|
2666
|
+
start, at(start), at(start + 1), at(start + 2), at(start + 3), next,
|
|
2667
|
+
)
|
|
2668
|
+
}
|
|
2669
|
+
const selected = bindExclusive(0)
|
|
1837
2670
|
// THERE ARE NO OPEN ARMS HERE, and the piece is written to say so. An
|
|
1838
2671
|
// ungated arm is one whose class is −1, and `resolveDispatch` clears
|
|
1839
2672
|
// `exclusive` for exactly those arms (program.ts) — so `table.open` is
|
|
@@ -1852,7 +2685,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1852
2685
|
}
|
|
1853
2686
|
if (arm >= 0) {
|
|
1854
2687
|
ctx._fc = false
|
|
1855
|
-
const v =
|
|
2688
|
+
const v = selected(arm, input, pos, ctx)
|
|
1856
2689
|
if (v !== FAIL) return v
|
|
1857
2690
|
// THE CUT — a committed failure fails the whole choice.
|
|
1858
2691
|
if (committed(ctx)) return FAIL
|
|
@@ -1876,48 +2709,25 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1876
2709
|
// excludes the char at `pos`. `null` means nullable or unmappable, and
|
|
1877
2710
|
// those arms are always entered.
|
|
1878
2711
|
const armCls = table.armCls
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
* CHARACTER and the TABLE, both of which are known here, so the whole
|
|
1890
|
-
* question is answered once at assembly: `cand[c]` is the bitmask of arms
|
|
1891
|
-
* that could match `c`, and the loop visits only set bits, IN ORDER, so
|
|
1892
|
-
* the source ordering the gate exists to preserve is preserved exactly.
|
|
1893
|
-
*
|
|
1894
|
-
* Slot 128 is EOF (`lead` returns −1): `classHas` rejects a negative code,
|
|
1895
|
-
* so only the always-enter arms are candidates there. Code points ≥ 128
|
|
1896
|
-
* keep the per-arm loop — the ranges make a table the wrong shape and
|
|
1897
|
-
* astral input is not the hot path.
|
|
1898
|
-
*
|
|
1899
|
-
* Bounded at 32 arms because the mask is one word. Above that the general
|
|
1900
|
-
* loop runs, which is the same code the ≥ 128 path takes, so there is no
|
|
1901
|
-
* second implementation to drift.
|
|
1902
|
-
*/
|
|
1903
|
-
const maskable = n <= 32
|
|
1904
|
-
let cand: Uint32Array | undefined
|
|
1905
|
-
if (maskable) {
|
|
1906
|
-
const m = new Uint32Array(129)
|
|
1907
|
-
for (let i = 0; i < n; i++) {
|
|
1908
|
-
const cls = gates[i]!
|
|
1909
|
-
const bit = 1 << i
|
|
2712
|
+
if (n === 2 || n === 3) {
|
|
2713
|
+
const a0 = link(code[base]!), a1 = link(code[base + 1]!)
|
|
2714
|
+
const a2 = n === 3 ? link(code[base + 2]!) : undefined
|
|
2715
|
+
const e0 = fx[code[base + n]!] as string[]
|
|
2716
|
+
const e1 = fx[code[base + n + 1]!] as string[]
|
|
2717
|
+
const e2 = n === 3 ? fx[code[base + n + 2]!] as string[] : undefined
|
|
2718
|
+
const g0 = armCls[0] ?? null, g1 = armCls[1] ?? null
|
|
2719
|
+
const g2 = n === 3 ? armCls[2] ?? null : null
|
|
2720
|
+
const mask = new Uint32Array(129)
|
|
2721
|
+
const addGate = (cls: ResolvedClass | null, bit: number): void => {
|
|
1910
2722
|
if (cls === null) {
|
|
1911
|
-
for (let c = 0; c < 129; c++)
|
|
1912
|
-
|
|
2723
|
+
for (let c = 0; c < 129; c++) mask[c]! |= bit
|
|
2724
|
+
return
|
|
1913
2725
|
}
|
|
1914
|
-
for (let c = 0; c < 128; c++) if (cls.ascii[c] === 1)
|
|
2726
|
+
for (let c = 0; c < 128; c++) if (cls.ascii[c] === 1) mask[c]! |= bit
|
|
1915
2727
|
}
|
|
1916
|
-
|
|
1917
|
-
|
|
2728
|
+
addGate(g0, 1); addGate(g1, 2)
|
|
2729
|
+
if (n === 3) addGate(g2, 4)
|
|
1918
2730
|
|
|
1919
|
-
if (cand !== undefined) {
|
|
1920
|
-
const mask = cand
|
|
1921
2731
|
return (input, pos, ctx) => {
|
|
1922
2732
|
const c = lead(input, pos)
|
|
1923
2733
|
const need = markCst(ctx)
|
|
@@ -1930,46 +2740,144 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1930
2740
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
1931
2741
|
let acc: string[] | undefined
|
|
1932
2742
|
if (c < 128) {
|
|
1933
|
-
|
|
1934
|
-
const bits0 = mask[c < 0 ? 128 : c]!
|
|
1935
|
-
let bits = bits0
|
|
1936
|
-
// Arms below `prev` have already contributed. Only the FAILURE path
|
|
1937
|
-
// advances it, so a matching arm returns without paying for any of
|
|
1938
|
-
// this — the skipped arms' sets are read on no other exit.
|
|
2743
|
+
const bits = mask[c < 0 ? 128 : c]!
|
|
1939
2744
|
let prev = 0
|
|
1940
|
-
|
|
1941
|
-
const i = 31 - Math.clz32(bits & -bits)
|
|
1942
|
-
bits &= bits - 1
|
|
2745
|
+
if ((bits & 1) !== 0) {
|
|
1943
2746
|
ctx._fc = false
|
|
1944
|
-
const v =
|
|
2747
|
+
const v = a0(input, pos, ctx)
|
|
1945
2748
|
if (v !== FAIL) return v
|
|
1946
|
-
|
|
1947
|
-
prev = i + 1
|
|
2749
|
+
prev = 1
|
|
1948
2750
|
acc = accSet(ctx._fx, acc)
|
|
1949
|
-
// A committed arm cuts, and the interpreter reports the
|
|
1950
|
-
// accumulation at the ARM's span (choice.ts:170), not at `pos`.
|
|
1951
2751
|
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1952
2752
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1953
2753
|
}
|
|
1954
|
-
|
|
2754
|
+
if ((bits & 2) !== 0) {
|
|
2755
|
+
ctx._fc = false
|
|
2756
|
+
const v = a1(input, pos, ctx)
|
|
2757
|
+
if (v !== FAIL) return v
|
|
2758
|
+
if (prev < 1) acc = accSet(e0, acc)
|
|
2759
|
+
prev = 2
|
|
2760
|
+
acc = accSet(ctx._fx, acc)
|
|
2761
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
2762
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2763
|
+
}
|
|
2764
|
+
if (n === 3 && (bits & 4) !== 0) {
|
|
2765
|
+
ctx._fc = false
|
|
2766
|
+
const v = a2!(input, pos, ctx)
|
|
2767
|
+
if (v !== FAIL) return v
|
|
2768
|
+
if (prev < 1) acc = accSet(e0, acc)
|
|
2769
|
+
if (prev < 2) acc = accSet(e1, acc)
|
|
2770
|
+
prev = 3
|
|
2771
|
+
acc = accSet(ctx._fx, acc)
|
|
2772
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
2773
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2774
|
+
}
|
|
2775
|
+
if (prev < 1) acc = accSet(e0, acc)
|
|
2776
|
+
if (prev < 2) acc = accSet(e1, acc)
|
|
2777
|
+
if (n === 3 && prev < 3) acc = accSet(e2, acc)
|
|
1955
2778
|
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1956
2779
|
return FAIL
|
|
1957
2780
|
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
2781
|
+
|
|
2782
|
+
if (g0 === null || classHas(g0, c)) {
|
|
2783
|
+
ctx._fc = false
|
|
2784
|
+
const v = a0(input, pos, ctx)
|
|
2785
|
+
if (v !== FAIL) return v
|
|
2786
|
+
acc = accSet(ctx._fx, acc)
|
|
2787
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
2788
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2789
|
+
} else acc = accSet(e0, acc)
|
|
2790
|
+
if (g1 === null || classHas(g1, c)) {
|
|
1961
2791
|
ctx._fc = false
|
|
1962
|
-
const v =
|
|
2792
|
+
const v = a1(input, pos, ctx)
|
|
1963
2793
|
if (v !== FAIL) return v
|
|
1964
2794
|
acc = accSet(ctx._fx, acc)
|
|
1965
2795
|
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1966
2796
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2797
|
+
} else acc = accSet(e1, acc)
|
|
2798
|
+
if (n === 3) {
|
|
2799
|
+
if (g2 === null || classHas(g2, c)) {
|
|
2800
|
+
ctx._fc = false
|
|
2801
|
+
const v = a2!(input, pos, ctx)
|
|
2802
|
+
if (v !== FAIL) return v
|
|
2803
|
+
acc = accSet(ctx._fx, acc)
|
|
2804
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
2805
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2806
|
+
} else acc = accSet(e2, acc)
|
|
1967
2807
|
}
|
|
1968
2808
|
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1969
2809
|
return FAIL
|
|
1970
2810
|
}
|
|
1971
2811
|
}
|
|
1972
2812
|
|
|
2813
|
+
const bindGeneral = (start: number, prior: ChoiceExpectedPrefix): GeneralChoiceBlock => {
|
|
2814
|
+
const arm = (i: number): Piece => i < n ? link(code[base + i]!) : NEVER_PIECE
|
|
2815
|
+
const expected = (i: number): readonly string[] => i < n
|
|
2816
|
+
? fx[code[base + n + i]!] as string[]
|
|
2817
|
+
: EMPTY_FX
|
|
2818
|
+
const gate = (i: number): ResolvedClass | null => i < n ? armCls[i] ?? null : NEVER_CLASS
|
|
2819
|
+
const through = expectedBlock(
|
|
2820
|
+
start, expected(start), expected(start + 1), expected(start + 2), expected(start + 3), prior,
|
|
2821
|
+
)
|
|
2822
|
+
const next = start + 4 < n ? bindGeneral(start + 4, through) : undefined
|
|
2823
|
+
return generalChoiceBlock(
|
|
2824
|
+
start,
|
|
2825
|
+
arm(start), arm(start + 1), arm(start + 2), arm(start + 3),
|
|
2826
|
+
gate(start), gate(start + 1), gate(start + 2), gate(start + 3),
|
|
2827
|
+
through, next, n, choiceFx,
|
|
2828
|
+
)
|
|
2829
|
+
}
|
|
2830
|
+
const general = bindGeneral(0, EMPTY_EXPECTED_PREFIX)
|
|
2831
|
+
|
|
2832
|
+
if (n <= 32) {
|
|
2833
|
+
const mask = new Uint32Array(129)
|
|
2834
|
+
for (let i = 0; i < n; i++) {
|
|
2835
|
+
const cls = armCls[i] ?? null
|
|
2836
|
+
const bit = 1 << i
|
|
2837
|
+
if (cls === null) {
|
|
2838
|
+
for (let c = 0; c < 129; c++) mask[c]! |= bit
|
|
2839
|
+
continue
|
|
2840
|
+
}
|
|
2841
|
+
for (let c = 0; c < 128; c++) if (cls.ascii[c] === 1) mask[c]! |= bit
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
const bindMasked = (start: number, prior: ChoiceExpectedPrefix): MaskedChoiceBlock => {
|
|
2845
|
+
const arm = (i: number): Piece => i < n ? link(code[base + i]!) : NEVER_PIECE
|
|
2846
|
+
const expected = (i: number): readonly string[] => i < n
|
|
2847
|
+
? fx[code[base + n + i]!] as string[]
|
|
2848
|
+
: EMPTY_FX
|
|
2849
|
+
const through = expectedBlock(
|
|
2850
|
+
start, expected(start), expected(start + 1), expected(start + 2), expected(start + 3), prior,
|
|
2851
|
+
)
|
|
2852
|
+
const next = start + 4 < n ? bindMasked(start + 4, through) : undefined
|
|
2853
|
+
return maskedChoiceBlock(
|
|
2854
|
+
start, arm(start), arm(start + 1), arm(start + 2), arm(start + 3),
|
|
2855
|
+
through, next, n, choiceFx,
|
|
2856
|
+
)
|
|
2857
|
+
}
|
|
2858
|
+
const masked = bindMasked(0, EMPTY_EXPECTED_PREFIX)
|
|
2859
|
+
return (input, pos, ctx) => {
|
|
2860
|
+
const c = lead(input, pos)
|
|
2861
|
+
const need = markCst(ctx)
|
|
2862
|
+
const mRaw = MRAW
|
|
2863
|
+
const mTl = MTL
|
|
2864
|
+
const mLv = MLV
|
|
2865
|
+
const mFl = need ? ctx._fields?.length ?? 0 : 0
|
|
2866
|
+
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
2867
|
+
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
2868
|
+
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
2869
|
+
if (c < 128) {
|
|
2870
|
+
return masked(
|
|
2871
|
+
input, pos, ctx, mask[c < 0 ? 128 : c]!,
|
|
2872
|
+
need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, undefined, 0,
|
|
2873
|
+
)
|
|
2874
|
+
}
|
|
2875
|
+
return general(
|
|
2876
|
+
input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, undefined, 0,
|
|
2877
|
+
)
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
|
|
1973
2881
|
return (input, pos, ctx) => {
|
|
1974
2882
|
const c = lead(input, pos)
|
|
1975
2883
|
const need = markCst(ctx)
|
|
@@ -1980,19 +2888,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1980
2888
|
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
1981
2889
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
1982
2890
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
if (cls !== null && !classHas(cls, c)) { acc = accSet(armFx[i]!, acc); continue }
|
|
1987
|
-
ctx._fc = false
|
|
1988
|
-
const v = arms[i]!(input, pos, ctx)
|
|
1989
|
-
if (v !== FAIL) return v
|
|
1990
|
-
acc = accSet(ctx._fx, acc)
|
|
1991
|
-
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1992
|
-
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1993
|
-
}
|
|
1994
|
-
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1995
|
-
return FAIL
|
|
2891
|
+
return general(
|
|
2892
|
+
input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, undefined, 0,
|
|
2893
|
+
)
|
|
1996
2894
|
}
|
|
1997
2895
|
}
|
|
1998
2896
|
|
|
@@ -2106,6 +3004,18 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2106
3004
|
const reportItem = (flags & 4) !== 0
|
|
2107
3005
|
const itemFx = reportItem ? fx[code[ip + 6]!] as string[] : EMPTY_FX
|
|
2108
3006
|
const collect = op === OP_REP
|
|
3007
|
+
// The item's finite, non-nullable first set is table data. A strict
|
|
3008
|
+
// optional iteration whose lead is outside it cannot run or commit, so
|
|
3009
|
+
// stop before entering the child graph. Tolerant assemblies keep the
|
|
3010
|
+
// ordinary failure path because it may recover the rejected input.
|
|
3011
|
+
// A separator-less repeat has no separator sentinel, so ip+7 carries
|
|
3012
|
+
// the optional item's class at no extra table width. Separated lists
|
|
3013
|
+
// keep that slot for their recovery sentinel and use the ordinary path.
|
|
3014
|
+
const itemClassIndex = sepIp < 0 ? code[ip + 7]! : -1
|
|
3015
|
+
const itemClass = !REC && itemClassIndex >= 0 ? cc[itemClassIndex]! : undefined
|
|
3016
|
+
const itemMayStart = itemClass === undefined
|
|
3017
|
+
? undefined
|
|
3018
|
+
: (input: string, pos: number): boolean => classHas(itemClass, lead(input, pos))
|
|
2109
3019
|
// `many()` — the min-0, separator-less repeat — is the only shape that runs
|
|
2110
3020
|
// its FIRST item through `repItem` and so skips leading trivia. The shape
|
|
2111
3021
|
// identifies itself, and it is table data, so it is decided here.
|
|
@@ -2236,6 +3146,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2236
3146
|
const out: unknown[] | undefined = collect ? [] : undefined
|
|
2237
3147
|
const hasTrivia = ctx.trivia !== undefined
|
|
2238
3148
|
const needMark = rollbackNeeded(ctx)
|
|
3149
|
+
// A completions probe deliberately observes swallowed item failures.
|
|
3150
|
+
// Keep the ordinary child path while one is active so this structural
|
|
3151
|
+
// fast stop cannot erase an item completion at the cursor.
|
|
3152
|
+
const gateItems = itemMayStart !== undefined && ctx._probe === undefined
|
|
2239
3153
|
let cur = pos
|
|
2240
3154
|
let count = 0
|
|
2241
3155
|
for (;;) {
|
|
@@ -2244,6 +3158,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2244
3158
|
// the LOOP HEAD. Held to `count >= min` so an under-`min` list still
|
|
2245
3159
|
// attempts the separator and reports its expected set.
|
|
2246
3160
|
if (sep !== undefined && count > 0 && count >= min && cur >= input.length) break
|
|
3161
|
+
// With no trivia or separator to position, the item starts at `cur`.
|
|
3162
|
+
// This is the full 0.46 loop-head cut: no mark and no child setup.
|
|
3163
|
+
if (count >= min && sep === undefined && !hasTrivia
|
|
3164
|
+
&& gateItems && !itemMayStart!(input, cur)) break
|
|
2247
3165
|
// Per ITERATION, never hoisted: a preceding item's `node()` opened and
|
|
2248
3166
|
// closed a capture buffer, so `ctx._cstBuf` is not the object it was
|
|
2249
3167
|
// at the loop head. `needMark` is the pre-existing loop-invariant.
|
|
@@ -2280,6 +3198,11 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2280
3198
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
2281
3199
|
break
|
|
2282
3200
|
}
|
|
3201
|
+
if (count >= min && gateItems && !itemMayStart!(input, itemStart)) {
|
|
3202
|
+
if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3203
|
+
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
3204
|
+
break
|
|
3205
|
+
}
|
|
2283
3206
|
ctx._fc = false
|
|
2284
3207
|
const v = child(input, itemStart, ctx)
|
|
2285
3208
|
if (v === FAIL) {
|
|
@@ -2317,75 +3240,69 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2317
3240
|
const otherRouted = code[ip + 4]! === 1
|
|
2318
3241
|
const n = code[ip + 5]!
|
|
2319
3242
|
const armBase = ip + 6
|
|
2320
|
-
const arms: Piece[] = new Array<Piece>(n)
|
|
2321
|
-
for (let i = 0; i < n; i++) arms[i] = link(code[armBase + i]!)
|
|
2322
3243
|
const byKey = spec.byKey
|
|
2323
3244
|
const byFold = spec.byFold
|
|
2324
3245
|
const hasFold = spec.byFold.size > 0
|
|
2325
3246
|
const match = spec.match
|
|
2326
3247
|
const matchN = match.length
|
|
2327
|
-
const matchFn: Array<(key: string) => boolean> = new Array<(key: string) => boolean>(matchN)
|
|
2328
|
-
const matchArm: number[] = new Array<number>(matchN)
|
|
2329
|
-
for (let i = 0; i < matchN; i++) { matchFn[i] = linkMatcher(match[i]!); matchArm[i] = match[i]![3] }
|
|
2330
3248
|
const routed = spec.routed
|
|
2331
3249
|
const expected = spec.expected as string[]
|
|
2332
3250
|
|
|
3251
|
+
const validArm = (arm: number): boolean => Number.isInteger(arm) && arm >= 0 && arm < n
|
|
3252
|
+
if (routed.length !== n) throw new TypeError('table assembler: malformed dispatch routed arity')
|
|
3253
|
+
for (const arm of byKey.values()) if (!validArm(arm)) throw new TypeError('table assembler: malformed dispatch exact arm')
|
|
3254
|
+
for (const arm of byFold.values()) if (!validArm(arm)) throw new TypeError('table assembler: malformed dispatch folded arm')
|
|
3255
|
+
for (const m of match) if (!validArm(m[3])) throw new TypeError('table assembler: malformed dispatch matcher arm')
|
|
3256
|
+
|
|
3257
|
+
const neverMatch: DispatchMatcher = () => false
|
|
3258
|
+
const bindMatchers = (start: number): DispatchMatcherBlock => {
|
|
3259
|
+
const pred = (i: number): DispatchMatcher => i < matchN ? linkMatcher(match[i]!) : neverMatch
|
|
3260
|
+
const arm = (i: number): number => i < matchN ? match[i]![3] : -1
|
|
3261
|
+
const next = start + 4 < matchN ? bindMatchers(start + 4) : undefined
|
|
3262
|
+
return dispatchMatcherBlock(
|
|
3263
|
+
pred(start), arm(start), pred(start + 1), arm(start + 1),
|
|
3264
|
+
pred(start + 2), arm(start + 2), pred(start + 3), arm(start + 3), next,
|
|
3265
|
+
)
|
|
3266
|
+
}
|
|
3267
|
+
const matchBlock = matchN === 0 ? undefined : bindMatchers(0)
|
|
3268
|
+
const classify: (key: string) => number | undefined = hasFold
|
|
3269
|
+
? matchBlock === undefined
|
|
3270
|
+
? key => byKey.get(key) ?? byFold.get(asciiFoldKey(key))
|
|
3271
|
+
: key => byKey.get(key) ?? byFold.get(asciiFoldKey(key)) ?? matchBlock(key)
|
|
3272
|
+
: matchBlock === undefined
|
|
3273
|
+
? key => byKey.get(key)
|
|
3274
|
+
: key => byKey.get(key) ?? matchBlock(key)
|
|
3275
|
+
|
|
3276
|
+
const neverBranch: DispatchBranch = () => FAIL
|
|
3277
|
+
const bindArms = (start: number): DispatchArmBlock => {
|
|
3278
|
+
const branch = (i: number): DispatchBranch => i < n
|
|
3279
|
+
? dispatchBranch(link(code[armBase + i]!), routed[i] === 1)
|
|
3280
|
+
: neverBranch
|
|
3281
|
+
const next = start + 4 < n ? bindArms(start + 4) : undefined
|
|
3282
|
+
return dispatchArmBlock(
|
|
3283
|
+
start, branch(start), branch(start + 1), branch(start + 2), branch(start + 3), next,
|
|
3284
|
+
)
|
|
3285
|
+
}
|
|
3286
|
+
const runArm = n === 0 ? undefined : bindArms(0)
|
|
3287
|
+
const runOther = other === undefined ? undefined : dispatchBranch(other, otherRouted)
|
|
3288
|
+
|
|
2333
3289
|
return (input, pos, ctx) => {
|
|
2334
3290
|
const selectorMark = saveTriviaMark(ctx)
|
|
2335
3291
|
const selVal = selector(input, pos, ctx)
|
|
2336
3292
|
if (selVal === FAIL) return FAIL
|
|
2337
3293
|
const selEnd = EC.e
|
|
2338
3294
|
const key = selVal as string
|
|
2339
|
-
|
|
2340
|
-
let arm = byKey.get(key)
|
|
2341
|
-
if (arm === undefined && hasFold) arm = byFold.get(asciiFoldKey(key))
|
|
3295
|
+
const arm = classify(key)
|
|
2342
3296
|
if (arm === undefined) {
|
|
2343
|
-
|
|
2344
|
-
if (matchFn[i]!(key)) {
|
|
2345
|
-
arm = matchArm[i]!
|
|
2346
|
-
break
|
|
2347
|
-
}
|
|
2348
|
-
}
|
|
2349
|
-
}
|
|
2350
|
-
|
|
2351
|
-
let target: Piece
|
|
2352
|
-
let usesRouted: boolean
|
|
2353
|
-
if (arm === undefined) {
|
|
2354
|
-
if (other === undefined) {
|
|
3297
|
+
if (runOther === undefined) {
|
|
2355
3298
|
// No branch and no fallback: fail AT THE SELECTOR'S END.
|
|
2356
3299
|
ctx._fe = selEnd
|
|
2357
3300
|
ctx._fx = expected
|
|
2358
3301
|
return FAIL
|
|
2359
3302
|
}
|
|
2360
|
-
|
|
2361
|
-
usesRouted = otherRouted
|
|
2362
|
-
} else {
|
|
2363
|
-
target = arms[arm]!
|
|
2364
|
-
usesRouted = routed[arm] === 1
|
|
2365
|
-
}
|
|
2366
|
-
|
|
2367
|
-
let mark = saveTriviaMark(ctx)
|
|
2368
|
-
let v: unknown
|
|
2369
|
-
if (usesRouted) {
|
|
2370
|
-
const savedRouted = ctx._routed
|
|
2371
|
-
rollbackTrivia(ctx, selectorMark)
|
|
2372
|
-
mark = saveTriviaMark(ctx)
|
|
2373
|
-
ctx._routed = { value: key, span: { start: pos, end: selEnd } }
|
|
2374
|
-
try {
|
|
2375
|
-
v = target(input, pos, ctx)
|
|
2376
|
-
} finally {
|
|
2377
|
-
ctx._routed = savedRouted
|
|
2378
|
-
}
|
|
2379
|
-
} else {
|
|
2380
|
-
v = target(input, selEnd, ctx)
|
|
2381
|
-
}
|
|
2382
|
-
if (v === FAIL) {
|
|
2383
|
-
rollbackTrivia(ctx, mark)
|
|
2384
|
-
// A failed dispatch branch is COMMITTED: the selector already matched.
|
|
2385
|
-
ctx._fc = true
|
|
2386
|
-
return FAIL
|
|
3303
|
+
return runOther(input, pos, selEnd, key, selectorMark, ctx)
|
|
2387
3304
|
}
|
|
2388
|
-
return
|
|
3305
|
+
return runArm!(arm, input, pos, selEnd, key, selectorMark, ctx)
|
|
2389
3306
|
}
|
|
2390
3307
|
}
|
|
2391
3308
|
|
|
@@ -2394,6 +3311,41 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2394
3311
|
case OP_NODE:
|
|
2395
3312
|
case OP_NODE_TRACK: {
|
|
2396
3313
|
const flags = code[ip + 3]!
|
|
3314
|
+
const scalarChild = !hostCst && !cfg.tolerant && !cfg.probe && !cfg.coverage && !cfg.trackLines
|
|
3315
|
+
? scalarTerminalNodeChild(code, ip)
|
|
3316
|
+
: -1
|
|
3317
|
+
if (scalarChild >= 0) {
|
|
3318
|
+
const recognize = scalarFor(scalarChild)!
|
|
3319
|
+
const spec = k[code[scalarChild + 1]!]
|
|
3320
|
+
const regexValue = code[scalarChild] === OP_RX
|
|
3321
|
+
const xf = fx[code[scalarChild + 2]!] as string[]
|
|
3322
|
+
const build = fns[code[ip + 1]!] as (
|
|
3323
|
+
children: readonly unknown[], fields: undefined, span: { start: number; end: number },
|
|
3324
|
+
rawChildren: readonly unknown[], triviaLog: readonly number[], state: undefined,
|
|
3325
|
+
) => unknown
|
|
3326
|
+
return (input, pos, ctx) => {
|
|
3327
|
+
const end = recognize(input, pos)
|
|
3328
|
+
if (end < 0) {
|
|
3329
|
+
ctx._fe = pos; ctx._fx = xf
|
|
3330
|
+
if (ctx._probe !== undefined) failAt(ctx, xf, pos)
|
|
3331
|
+
return FAIL
|
|
3332
|
+
}
|
|
3333
|
+
const value = regexValue ? input.slice(pos, end) : spec as string
|
|
3334
|
+
const leaf = { _tag: 'leaf', value, span: { start: pos, end } }
|
|
3335
|
+
const kids = [leaf]
|
|
3336
|
+
const rawKids = [leaf]
|
|
3337
|
+
const span = { start: pos, end }
|
|
3338
|
+
EC.e = end
|
|
3339
|
+
const nd = build(kids, undefined, span, rawKids, EMPTY_TL, undefined)
|
|
3340
|
+
if (ctx._cstBuf !== undefined || ctx._cstChildren !== undefined) {
|
|
3341
|
+
pushCstChild(ctx, nd, ctx._cstRawChildren !== undefined || ctx._cstBuf !== undefined
|
|
3342
|
+
? rawEntry(nd, input, pos, end)
|
|
3343
|
+
: undefined)
|
|
3344
|
+
}
|
|
3345
|
+
EC.e = end
|
|
3346
|
+
return nd
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
2397
3349
|
const child = link(code[ip + 2]!)
|
|
2398
3350
|
const proj = code[ip + 4]!
|
|
2399
3351
|
const buildIdx = code[ip + 1]!
|
|
@@ -2412,12 +3364,34 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2412
3364
|
// 145,512 executions per parse of `benchmark.less`. `cstOutputHost(ctx.build)`
|
|
2413
3365
|
// is fixed by `run()` before the entry is called, so it selects the piece.
|
|
2414
3366
|
const build = buildIdx >= 0
|
|
2415
|
-
? fns[buildIdx] as
|
|
2416
|
-
children: readonly unknown[], fields: FieldMap | undefined, span: { start: number; end: number },
|
|
2417
|
-
rawChildren: readonly unknown[], triviaLog: readonly number[], state: unknown,
|
|
2418
|
-
) => unknown
|
|
3367
|
+
? fns[buildIdx] as NodeBuilder
|
|
2419
3368
|
: undefined
|
|
2420
3369
|
|
|
3370
|
+
// The common AST-only bodies are separate FunctionLiterals, not a mode
|
|
3371
|
+
// branch in the generic node body. Their eligibility is entirely table
|
|
3372
|
+
// data plus this assembly's fixed option set; direct/custom contexts and
|
|
3373
|
+
// every richer node shape retain the generic implementation below.
|
|
3374
|
+
if (!hostCst && !tracked) {
|
|
3375
|
+
if (build !== undefined && proj < 0 && flags === 0) {
|
|
3376
|
+
return plainBuildNode(child, build)
|
|
3377
|
+
}
|
|
3378
|
+
if (build !== undefined && proj < 0 && flags === 2) {
|
|
3379
|
+
return childrenOnlyBuildNode(child, build)
|
|
3380
|
+
}
|
|
3381
|
+
if (build !== undefined && proj < 0 && flags === 18) {
|
|
3382
|
+
return childrenOnlyFieldsBuildNode(child, build)
|
|
3383
|
+
}
|
|
3384
|
+
if (build !== undefined && proj < 0 && flags === 32) {
|
|
3385
|
+
return collapseBuildNode(child, build)
|
|
3386
|
+
}
|
|
3387
|
+
if (build !== undefined && proj < 0 && flags === 34) {
|
|
3388
|
+
return childrenOnlyCollapseBuildNode(child, build)
|
|
3389
|
+
}
|
|
3390
|
+
if (build === undefined && proj >= 0 && flags === 0) {
|
|
3391
|
+
return plainProjectNode(child, proj, type)
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
|
|
2421
3395
|
// A STRUCTURAL node — no builder, no projection — is the only shape that
|
|
2422
3396
|
// takes a per-node-type trivia-kind mask off the host (`node.ts:260`).
|
|
2423
3397
|
// Assembly-time, so a node with a reducer pays nothing for the question.
|
|
@@ -2584,10 +3558,11 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2584
3558
|
* Emitted source is attempted FIRST, and the closure walk above runs only for
|
|
2585
3559
|
* what it refuses.
|
|
2586
3560
|
*
|
|
2587
|
-
*
|
|
2588
|
-
*
|
|
2589
|
-
*
|
|
2590
|
-
* it
|
|
3561
|
+
* Source emission names each fixed grammar edge directly and can be carried
|
|
3562
|
+
* as a build-time factory for CSP. The closure path may bind the same fixed
|
|
3563
|
+
* topology through scalar captures; this ordering selects the generated form
|
|
3564
|
+
* where it exists without making shared-literal IC behavior an architecture
|
|
3565
|
+
* premise.
|
|
2591
3566
|
*
|
|
2592
3567
|
* The refusal is RECORDED, not swallowed. A grammar that silently drops to
|
|
2593
3568
|
* the closure path is a permanently slow path nobody would ever find, which
|
|
@@ -2609,10 +3584,23 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2609
3584
|
if (s.sentinel !== undefined) extraIps.push(s.sentinel[0])
|
|
2610
3585
|
}
|
|
2611
3586
|
for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0])
|
|
2612
|
-
const
|
|
2613
|
-
for (const ip of reachableSites(code,
|
|
2614
|
-
if (code[ip]
|
|
2615
|
-
|
|
3587
|
+
const roots = [...Object.values(prog.rules), ...extraIps]
|
|
3588
|
+
for (const ip of reachableSites(code, roots)) {
|
|
3589
|
+
if (code[ip] === OP_DISPATCH) {
|
|
3590
|
+
const spec = dsp[code[ip + 2]!]
|
|
3591
|
+
validateDispatchSpec(spec, code[ip + 5]!, code[ip + 4]!)
|
|
3592
|
+
}
|
|
3593
|
+
if (!hostCst && !cfg.tolerant && !cfg.probe && !cfg.coverage && !cfg.trackLines) {
|
|
3594
|
+
if (code[ip] === OP_NODE) {
|
|
3595
|
+
const child = scalarTerminalNodeChild(code, ip)
|
|
3596
|
+
if (child >= 0) {
|
|
3597
|
+
rawScalarSpecs.add(code[child + 1]!)
|
|
3598
|
+
scalarFor(child)
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
const notChild = scalarTerminalNotChild(code, ip)
|
|
3603
|
+
if (notChild >= 0) scalarFor(notChild)
|
|
2616
3604
|
}
|
|
2617
3605
|
/**
|
|
2618
3606
|
* THE BUILD'S OWN ANSWER, TRIED BEFORE THE CONSTRUCTOR.
|
|
@@ -2637,7 +3625,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2637
3625
|
? prog.asm?.find(a => a.key === cfgKey(cfg))
|
|
2638
3626
|
: undefined
|
|
2639
3627
|
if (pre !== undefined) {
|
|
2640
|
-
const pools = rebuildPools(t.cc, t.fx, pre.plan)
|
|
3628
|
+
const pools = rebuildPools(t.cc, t.fx, t.disp, pre.plan)
|
|
2641
3629
|
emitted = pre.factory(
|
|
2642
3630
|
EC, FAIL, k, fx, fns, pools.masks, pools.classes, pools.armExpected, trivia,
|
|
2643
3631
|
trivia.map(tv => tv?._meta.triviaKindLabels), triviaScan,
|
|
@@ -2649,6 +3637,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2649
3637
|
asciiFoldKey, ROUTED_FX,
|
|
2650
3638
|
REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
|
|
2651
3639
|
matchesAt, recoverScan, orSentinel, captureError,
|
|
3640
|
+
scalarRecognizers, commitTriviaScan, scanTriviaCompact, t.lex, adjacencyHolds, t.lexPrograms,
|
|
2652
3641
|
)
|
|
2653
3642
|
emitReached = new Set(pre.reached)
|
|
2654
3643
|
}
|
|
@@ -2729,6 +3718,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2729
3718
|
// object rather than to two separately constructed ones.
|
|
2730
3719
|
REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
|
|
2731
3720
|
matchesAt, recoverScan, orSentinel, captureError,
|
|
3721
|
+
scalarRecognizers, commitTriviaScan, scanTriviaCompact, t.lex, adjacencyHolds, t.lexPrograms,
|
|
2732
3722
|
)
|
|
2733
3723
|
emitReached = em.reached
|
|
2734
3724
|
} catch (e) {
|