parseman 0.47.0 → 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 +2955 -357
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +2946 -348
- 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 +19 -1
- 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 +2959 -348
- package/dist/index.js.map +3 -3
- package/dist/language-service/index.js +7 -1
- package/dist/language-service/index.js.map +1 -1
- package/dist/plugin/index.js +2969 -358
- package/dist/plugin/index.js.map +3 -3
- package/dist/spec/index.js +366 -49
- 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 +13 -14
- 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 +4839 -2232
- package/dist/table/index.js.map +3 -3
- package/dist/table/lex-body.d.ts +9 -0
- package/dist/table/lex-body.d.ts.map +1 -0
- package/dist/table/lex-program.d.ts +25 -0
- package/dist/table/lex-program.d.ts.map +1 -0
- package/dist/table/ops.d.ts +47 -4
- package/dist/table/ops.d.ts.map +1 -1
- package/dist/table/program.d.ts +53 -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 +12 -11
- package/src/combinators/expect.ts +1 -0
- package/src/combinators/expected.ts +17 -0
- package/src/combinators/first-set.ts +82 -3
- 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 +35 -16
- package/src/combinators/trivia-skip.ts +75 -11
- 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 +1224 -222
- package/src/table/child-slots.ts +4 -3
- package/src/table/compile-rule-map.ts +21 -3
- package/src/table/emit-assembly.ts +363 -109
- package/src/table/emit.ts +26 -1
- package/src/table/encode.ts +246 -54
- package/src/table/exec-baseline.ts +3 -7
- package/src/table/exec.ts +140 -18
- package/src/table/lex-body.ts +30 -0
- package/src/table/lex-program.ts +422 -0
- package/src/table/ops.ts +50 -4
- package/src/table/program.ts +67 -2
- package/src/table/scalar-terminal.ts +51 -0
- package/src/table/site-labels.ts +13 -3
- package/src/table/stamp.ts +3 -3
- package/src/types.ts +1 -1
- package/src/version.ts +1 -1
- package/dist/analysis/diagnostics.cjs +0 -13736
- package/dist/analysis/diagnostics.cjs.map +0 -6
- package/dist/cli/index.cjs +0 -14837
- package/dist/cli/index.cjs.map +0 -6
- package/dist/index.cjs +0 -16303
- package/dist/index.cjs.map +0 -6
- package/dist/language-service/index.cjs +0 -1302
- 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 -16009
- 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 -6188
- package/dist/spec/index.cjs.map +0 -6
- package/dist/table/index.cjs +0 -11375
- package/dist/table/index.cjs.map +0 -6
package/src/table/assemble.ts
CHANGED
|
@@ -71,22 +71,23 @@ 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, 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
|
-
cstCaptureActive, cstLeavesLen,
|
|
78
|
+
cstCaptureActive, cstLeavesLen, cstTlLen,
|
|
79
79
|
demoteCapturedToRaw, pushCstChild, pushCstLeaf,
|
|
80
80
|
type CstCaptureBuf,
|
|
81
81
|
} from '../cst/capture-buffer.ts'
|
|
82
82
|
import {
|
|
83
83
|
OP_CHOICE, OP_EMPTY, OP_GATE, OP_LEAF, OP_LIT, OP_NODE, OP_NOT, OP_OPT,
|
|
84
84
|
OP_PEEK, OP_REP, OP_REPV, OP_RULE, OP_RX, OP_SEQ, OP_SEQV, OP_XFORM,
|
|
85
|
-
OP_LIT_TRACK, OP_RX_TRACK, OP_NODE_TRACK, OP_SCOPE, OP_SCOPE_CAP, OP_EXPECT, OP_SEQX, OP_SCAN,
|
|
85
|
+
OP_LIT_TRACK, OP_RX_TRACK, OP_NODE_TRACK, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, OP_EXPECT, OP_SEQX, OP_SCAN,
|
|
86
86
|
OP_LIVE,
|
|
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'
|
|
@@ -119,12 +120,17 @@ import { lead, rawEntry, spanLines } from './run-support.ts'
|
|
|
119
120
|
*/
|
|
120
121
|
import {
|
|
121
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
127
|
import { stampRuleMap } from './stamp.ts'
|
|
128
|
+
import { reachableSites } from './site-labels.ts'
|
|
126
129
|
import { refuseUnclassifiedRootScope } from '../cst/root-trivia-scope.ts'
|
|
127
130
|
import { captureError, firstSetSentinel, matchesAt, orSentinel, recoverScan } from '../recovery/scan.ts'
|
|
131
|
+
import {
|
|
132
|
+
makeScalarRecognizer, scalarTerminalNodeChild, scalarTerminalNotChild, type ScalarRecognizer,
|
|
133
|
+
} from './scalar-terminal.ts'
|
|
128
134
|
|
|
129
135
|
/**
|
|
130
136
|
* Is the EMITTED engine (`emit-assembly.ts`) enabled for this process?
|
|
@@ -138,7 +144,7 @@ const EMIT_ENABLED = (globalThis as { process?: { env?: Record<string, string |
|
|
|
138
144
|
.process?.env?.PM_TABLE_EMIT !== '0'
|
|
139
145
|
|
|
140
146
|
/** Failure sentinel — SHARED with the other two engines, see `cell.ts`. */
|
|
141
|
-
import { FAIL, newEndCell
|
|
147
|
+
import { FAIL, newEndCell } from './cell.ts'
|
|
142
148
|
|
|
143
149
|
const EMPTY_TL: readonly number[] = Object.freeze([])
|
|
144
150
|
/** `finishCstBuf`'s two empty sentinels, held here so the node piece can inline it. */
|
|
@@ -166,10 +172,20 @@ function linkMatcher(m: readonly [number, string, string, number]): (key: string
|
|
|
166
172
|
case 1: return key => key.endsWith(value)
|
|
167
173
|
case 3: return key => asciiFoldKey(key).startsWith(value)
|
|
168
174
|
case 4: return key => asciiFoldKey(key).endsWith(value)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
+
}
|
|
173
189
|
}
|
|
174
190
|
}
|
|
175
191
|
|
|
@@ -197,6 +213,36 @@ const NO_COVERAGE = (_id: string): void => {}
|
|
|
197
213
|
* `{ value, end }` pair would be an allocation per row.
|
|
198
214
|
*/
|
|
199
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
|
|
200
246
|
|
|
201
247
|
/**
|
|
202
248
|
* ONE NON-FIRST SEQUENCE TERM, in a sequence that carries an adjacency
|
|
@@ -204,7 +250,14 @@ type Piece = (input: string, pos: number, ctx: ParseContext) => unknown
|
|
|
204
250
|
* `nextTerm`'s protocol, so a bound term and a bound assertion are
|
|
205
251
|
* interchangeable in the loop and the loop tests neither.
|
|
206
252
|
*/
|
|
207
|
-
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
|
|
208
261
|
|
|
209
262
|
/**
|
|
210
263
|
* The option set an assembly is specialised for.
|
|
@@ -414,6 +467,21 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
414
467
|
*/
|
|
415
468
|
const EC = newEndCell()
|
|
416
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
|
+
|
|
417
485
|
/**
|
|
418
486
|
* The INSTALLED trivia scanner for the scope currently running.
|
|
419
487
|
*
|
|
@@ -432,6 +500,263 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
432
500
|
pushCstLeaf(ctx, lf)
|
|
433
501
|
}
|
|
434
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
|
+
|
|
435
760
|
/** Mirrors `exec.ts`'s `rollbackNeeded` — a runtime question about live sinks. */
|
|
436
761
|
function rollbackNeeded(ctx: ParseContext): boolean {
|
|
437
762
|
return ctx._cstBuf !== undefined
|
|
@@ -561,9 +886,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
561
886
|
// keeps the exact branch it had.
|
|
562
887
|
if (s !== null) return skipTriviaScanned(s, input, cur, ctx)
|
|
563
888
|
if (needsDeferredTriviaCommit(ctx)) {
|
|
564
|
-
|
|
565
|
-
scan.commit()
|
|
566
|
-
return scan.end
|
|
889
|
+
return commitTriviaScan(scanTriviaCompact(input, cur, ctx))
|
|
567
890
|
}
|
|
568
891
|
return advanceTrivia(input, cur, ctx)
|
|
569
892
|
}
|
|
@@ -661,23 +984,23 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
661
984
|
TERMV = v
|
|
662
985
|
return EC.e > scanEnd ? EC.e : cur
|
|
663
986
|
}
|
|
664
|
-
// SCALAR MARKS —
|
|
665
|
-
|
|
666
|
-
const
|
|
667
|
-
const mTl =
|
|
668
|
-
const mLv = MLV
|
|
669
|
-
const mFl = need ? ctx._fields?.length ?? 0 : 0
|
|
670
|
-
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
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.
|
|
989
|
+
const need = rollbackNeeded(ctx)
|
|
990
|
+
const mTl = need ? cstTlLen(ctx) : 0
|
|
671
991
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
672
992
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
673
993
|
const scanEnd = skipTrivia(input, cur, ctx)
|
|
994
|
+
const scanTl = need ? cstTlLen(ctx) : 0
|
|
995
|
+
const scanLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
996
|
+
const scanRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
674
997
|
const v = child(input, scanEnd, ctx)
|
|
675
998
|
if (v === FAIL) return -1
|
|
676
999
|
TERMV = v
|
|
677
1000
|
if (EC.e > scanEnd) return EC.e
|
|
678
1001
|
// The term matched nothing, so the trivia in front of it was never consumed
|
|
679
1002
|
// by anything — unrecord it and leave the cursor where it was.
|
|
680
|
-
if (need)
|
|
1003
|
+
if (need) rollbackScannedTriviaAt(ctx, mTl, scanTl, mLog, scanLog, mRoot, scanRoot)
|
|
681
1004
|
return cur
|
|
682
1005
|
}
|
|
683
1006
|
|
|
@@ -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()`
|
|
@@ -1212,7 +1776,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1212
1776
|
}
|
|
1213
1777
|
|
|
1214
1778
|
case OP_SCOPE:
|
|
1215
|
-
case OP_SCOPE_CAP:
|
|
1779
|
+
case OP_SCOPE_CAP:
|
|
1780
|
+
case OP_SCOPE_PLAIN: {
|
|
1216
1781
|
const ki = code[ip + 1]!
|
|
1217
1782
|
const scopeTrivia = ki < 0 ? undefined : (trivia[ki] as ParseContext['trivia'])
|
|
1218
1783
|
const scopeLabels = scopeTrivia?._meta.triviaKindLabels
|
|
@@ -1261,7 +1826,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1261
1826
|
SCAN = savedScan
|
|
1262
1827
|
return v
|
|
1263
1828
|
}
|
|
1264
|
-
return scopeRootPolicy(scopePiece, code[ip + 3]!)
|
|
1829
|
+
return scopeRootPolicy(scopePiece, code[ip] === OP_SCOPE_PLAIN ? 0 : code[ip + 3]!)
|
|
1265
1830
|
}
|
|
1266
1831
|
|
|
1267
1832
|
case OP_ROUTED: {
|
|
@@ -1306,6 +1871,21 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1306
1871
|
/* ── zero-width ──────────────────────────────────────────────────────── */
|
|
1307
1872
|
|
|
1308
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
|
+
}
|
|
1309
1889
|
const child = link(code[ip + 1]!)
|
|
1310
1890
|
const xf = fx[code[ip + 2]!] as string[]
|
|
1311
1891
|
return (input, pos, ctx) => {
|
|
@@ -1419,6 +1999,93 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1419
1999
|
}
|
|
1420
2000
|
}
|
|
1421
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
|
+
|
|
1422
2089
|
case OP_LEAF: {
|
|
1423
2090
|
const fn = fns[code[ip + 1]!] as (value: unknown, span: { start: number; end: number }) => unknown
|
|
1424
2091
|
const child = link(code[ip + 2]!)
|
|
@@ -1463,36 +2130,60 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1463
2130
|
const fused = op === OP_SEQX
|
|
1464
2131
|
const base = fused ? ip + 3 : ip + 2
|
|
1465
2132
|
const n = code[fused ? ip + 2 : ip + 1]!
|
|
1466
|
-
// Children bound as a plain array of DIRECT references, laid out once.
|
|
1467
|
-
const kids: Piece[] = new Array<Piece>(n)
|
|
1468
|
-
for (let i = 0; i < n; i++) kids[i] = link(code[base + i]!)
|
|
1469
2133
|
const wantValues = op !== OP_SEQV
|
|
1470
|
-
const
|
|
1471
|
-
|
|
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
|
|
1472
2138
|
: undefined
|
|
1473
2139
|
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
* INLINING a small monomorphic child. Re-emitting the terminal bodies
|
|
1482
|
-
* would instead grow this function past the inlining budget, which is the
|
|
1483
|
-
* defect being fixed, so they are not re-emitted.
|
|
1484
|
-
*/
|
|
1485
|
-
const runTerms = (input: string, pos: number, ctx: ParseContext, values: unknown[] | undefined): number => {
|
|
1486
|
-
const v0 = kids[0]!(input, pos, ctx)
|
|
1487
|
-
if (v0 === FAIL) return -1
|
|
1488
|
-
if (values !== undefined) values.push(v0)
|
|
1489
|
-
let cur = EC.e
|
|
1490
|
-
for (let i = 1; i < n; i++) {
|
|
1491
|
-
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)
|
|
1492
2147
|
if (cur < 0) return -1
|
|
1493
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)
|
|
1494
2186
|
}
|
|
1495
|
-
return cur
|
|
1496
2187
|
}
|
|
1497
2188
|
|
|
1498
2189
|
/**
|
|
@@ -1543,21 +2234,24 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1543
2234
|
* sentinel published is the kind of state a later reader will trust.
|
|
1544
2235
|
*/
|
|
1545
2236
|
if (REC) {
|
|
1546
|
-
const
|
|
1547
|
-
|
|
1548
|
-
const
|
|
1549
|
-
|
|
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]!)
|
|
1550
2241
|
const kidIp = code[base + i]!
|
|
1551
2242
|
if (code[kidIp] !== OP_ADJ) {
|
|
1552
|
-
const kid =
|
|
1553
|
-
|
|
1554
|
-
|
|
2243
|
+
const kid = link(kidIp)
|
|
2244
|
+
return (input, cur, ctx, inherited) => {
|
|
2245
|
+
ctx._sync = sync ?? inherited
|
|
2246
|
+
return nextTerm(kid, input, cur, ctx)
|
|
2247
|
+
}
|
|
1555
2248
|
}
|
|
1556
2249
|
const negated = code[kidIp + 1] === 1
|
|
1557
2250
|
const ki = code[kidIp + 2]!
|
|
1558
2251
|
const kinds = ki < 0 ? undefined : k[ki] as readonly string[]
|
|
1559
2252
|
const xf = fx[code[kidIp + 3]!] as string[]
|
|
1560
|
-
|
|
2253
|
+
return (input, cur, ctx, inherited) => {
|
|
2254
|
+
ctx._sync = sync ?? inherited
|
|
1561
2255
|
if (!adjacencyHolds(input, cur, ctx, negated, kinds)) {
|
|
1562
2256
|
ctx._fe = cur; ctx._fx = xf
|
|
1563
2257
|
return -1
|
|
@@ -1566,19 +2260,25 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1566
2260
|
return cur
|
|
1567
2261
|
}
|
|
1568
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
|
|
1569
2274
|
const runSyncTerms = (input: string, pos: number, ctx: ParseContext, values: unknown[] | undefined): number => {
|
|
1570
2275
|
const inherited = ctx._sync
|
|
1571
|
-
ctx._sync =
|
|
1572
|
-
const v0 =
|
|
2276
|
+
ctx._sync = firstSync ?? inherited
|
|
2277
|
+
const v0 = first(input, pos, ctx)
|
|
1573
2278
|
if (v0 === FAIL) { ctx._sync = inherited; return -1 }
|
|
1574
2279
|
if (values !== undefined) values.push(v0)
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
ctx._sync = syncs[i] ?? inherited
|
|
1578
|
-
cur = runners[i]!(input, cur, ctx)
|
|
1579
|
-
if (cur < 0) { ctx._sync = inherited; return -1 }
|
|
1580
|
-
if (values !== undefined) values.push(TERMV)
|
|
1581
|
-
}
|
|
2280
|
+
const cur = tail === undefined ? EC.e : tail(input, EC.e, ctx, values, inherited)
|
|
2281
|
+
if (cur < 0) { ctx._sync = inherited; return -1 }
|
|
1582
2282
|
ctx._sync = inherited
|
|
1583
2283
|
return cur
|
|
1584
2284
|
}
|
|
@@ -1588,7 +2288,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1588
2288
|
const cur = runSyncTerms(input, pos, ctx, values)
|
|
1589
2289
|
if (cur < 0) return FAIL
|
|
1590
2290
|
EC.e = cur
|
|
1591
|
-
return fn!(values, { start: pos, end: cur })
|
|
2291
|
+
return projection >= 0 ? values[projection] : fn!(values, { start: pos, end: cur })
|
|
1592
2292
|
}
|
|
1593
2293
|
}
|
|
1594
2294
|
if (wantValues) {
|
|
@@ -1611,13 +2311,12 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1611
2311
|
let hasAdj = false
|
|
1612
2312
|
for (let i = 1; i < n; i++) if (code[code[base + i]!] === OP_ADJ) { hasAdj = true; break }
|
|
1613
2313
|
if (hasAdj) {
|
|
1614
|
-
const
|
|
1615
|
-
|
|
2314
|
+
const first = link(code[base]!)
|
|
2315
|
+
const runnerAt = (i: number): TermRunner => {
|
|
1616
2316
|
const kidIp = code[base + i]!
|
|
1617
2317
|
if (code[kidIp] !== OP_ADJ) {
|
|
1618
|
-
const kid =
|
|
1619
|
-
|
|
1620
|
-
continue
|
|
2318
|
+
const kid = link(kidIp)
|
|
2319
|
+
return (input, cur, ctx) => nextTerm(kid, input, cur, ctx)
|
|
1621
2320
|
}
|
|
1622
2321
|
const negated = code[kidIp + 1] === 1
|
|
1623
2322
|
const ki = code[kidIp + 2]!
|
|
@@ -1627,7 +2326,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1627
2326
|
// the interpreter does.
|
|
1628
2327
|
const kinds = ki < 0 ? undefined : k[ki] as readonly string[]
|
|
1629
2328
|
const xf = fx[code[kidIp + 3]!] as string[]
|
|
1630
|
-
|
|
2329
|
+
return (input, cur, ctx) => {
|
|
1631
2330
|
if (!adjacencyHolds(input, cur, ctx, negated, kinds)) {
|
|
1632
2331
|
ctx._fe = cur; ctx._fx = xf
|
|
1633
2332
|
return -1
|
|
@@ -1636,17 +2335,22 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1636
2335
|
return cur
|
|
1637
2336
|
}
|
|
1638
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)
|
|
1639
2349
|
const runAdjTerms = (input: string, pos: number, ctx: ParseContext, values: unknown[] | undefined): number => {
|
|
1640
|
-
const v0 =
|
|
2350
|
+
const v0 = first(input, pos, ctx)
|
|
1641
2351
|
if (v0 === FAIL) return -1
|
|
1642
2352
|
if (values !== undefined) values.push(v0)
|
|
1643
|
-
|
|
1644
|
-
for (let i = 1; i < n; i++) {
|
|
1645
|
-
cur = runners[i]!(input, cur, ctx)
|
|
1646
|
-
if (cur < 0) return -1
|
|
1647
|
-
if (values !== undefined) values.push(TERMV)
|
|
1648
|
-
}
|
|
1649
|
-
return cur
|
|
2353
|
+
return tail(input, EC.e, ctx, values)
|
|
1650
2354
|
}
|
|
1651
2355
|
if (fused) {
|
|
1652
2356
|
return (input, pos, ctx) => {
|
|
@@ -1654,7 +2358,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1654
2358
|
const cur = runAdjTerms(input, pos, ctx, values)
|
|
1655
2359
|
if (cur < 0) return FAIL
|
|
1656
2360
|
EC.e = cur
|
|
1657
|
-
return fn!(values, { start: pos, end: cur })
|
|
2361
|
+
return projection >= 0 ? values[projection] : fn!(values, { start: pos, end: cur })
|
|
1658
2362
|
}
|
|
1659
2363
|
}
|
|
1660
2364
|
if (wantValues) {
|
|
@@ -1692,12 +2396,12 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1692
2396
|
* runs, so there is no arity this fails to lower.
|
|
1693
2397
|
*/
|
|
1694
2398
|
if (n === 1) {
|
|
1695
|
-
const k0 =
|
|
2399
|
+
const k0 = link(code[base]!)
|
|
1696
2400
|
if (fused) {
|
|
1697
2401
|
return (input, pos, ctx) => {
|
|
1698
2402
|
const v = k0(input, pos, ctx)
|
|
1699
2403
|
if (v === FAIL) return FAIL
|
|
1700
|
-
return fn!([v], { start: pos, end: EC.e })
|
|
2404
|
+
return projection === 0 ? v : fn!([v], { start: pos, end: EC.e })
|
|
1701
2405
|
}
|
|
1702
2406
|
}
|
|
1703
2407
|
if (wantValues) {
|
|
@@ -1715,7 +2419,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1715
2419
|
}
|
|
1716
2420
|
|
|
1717
2421
|
if (n === 2) {
|
|
1718
|
-
const k0 =
|
|
2422
|
+
const k0 = link(code[base]!), k1 = link(code[base + 1]!)
|
|
1719
2423
|
if (fused) {
|
|
1720
2424
|
return (input, pos, ctx) => {
|
|
1721
2425
|
const v0 = k0(input, pos, ctx)
|
|
@@ -1723,7 +2427,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1723
2427
|
const cur = nextTerm(k1, input, EC.e, ctx)
|
|
1724
2428
|
if (cur < 0) return FAIL
|
|
1725
2429
|
EC.e = cur
|
|
1726
|
-
return
|
|
2430
|
+
return projection === 0 ? v0
|
|
2431
|
+
: projection === 1 ? TERMV
|
|
2432
|
+
: fn!([v0, TERMV], { start: pos, end: cur })
|
|
1727
2433
|
}
|
|
1728
2434
|
}
|
|
1729
2435
|
if (wantValues) {
|
|
@@ -1747,7 +2453,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1747
2453
|
}
|
|
1748
2454
|
|
|
1749
2455
|
if (n === 3) {
|
|
1750
|
-
const k0 =
|
|
2456
|
+
const k0 = link(code[base]!), k1 = link(code[base + 1]!), k2 = link(code[base + 2]!)
|
|
1751
2457
|
if (fused) {
|
|
1752
2458
|
return (input, pos, ctx) => {
|
|
1753
2459
|
const v0 = k0(input, pos, ctx)
|
|
@@ -1758,7 +2464,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1758
2464
|
cur = nextTerm(k2, input, cur, ctx)
|
|
1759
2465
|
if (cur < 0) return FAIL
|
|
1760
2466
|
EC.e = cur
|
|
1761
|
-
return
|
|
2467
|
+
return projection === 0 ? v0
|
|
2468
|
+
: projection === 1 ? v1
|
|
2469
|
+
: projection === 2 ? TERMV
|
|
2470
|
+
: fn!([v0, v1, TERMV], { start: pos, end: cur })
|
|
1762
2471
|
}
|
|
1763
2472
|
}
|
|
1764
2473
|
if (wantValues) {
|
|
@@ -1786,26 +2495,97 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1786
2495
|
}
|
|
1787
2496
|
}
|
|
1788
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
|
+
|
|
1789
2569
|
if (fused) {
|
|
1790
2570
|
return (input, pos, ctx) => {
|
|
1791
2571
|
const values: unknown[] = []
|
|
1792
|
-
const cur =
|
|
2572
|
+
const cur = runDirectTerms(input, pos, ctx, values)
|
|
1793
2573
|
if (cur < 0) return FAIL
|
|
1794
2574
|
EC.e = cur
|
|
1795
|
-
return fn!(values, { start: pos, end: cur })
|
|
2575
|
+
return projection >= 0 ? values[projection] : fn!(values, { start: pos, end: cur })
|
|
1796
2576
|
}
|
|
1797
2577
|
}
|
|
1798
2578
|
if (wantValues) {
|
|
1799
2579
|
return (input, pos, ctx) => {
|
|
1800
2580
|
const values: unknown[] = []
|
|
1801
|
-
const cur =
|
|
2581
|
+
const cur = runDirectTerms(input, pos, ctx, values)
|
|
1802
2582
|
if (cur < 0) return FAIL
|
|
1803
2583
|
EC.e = cur
|
|
1804
2584
|
return values
|
|
1805
2585
|
}
|
|
1806
2586
|
}
|
|
1807
2587
|
return (input, pos, ctx) => {
|
|
1808
|
-
const cur =
|
|
2588
|
+
const cur = runDirectTerms(input, pos, ctx, undefined)
|
|
1809
2589
|
if (cur < 0) return FAIL
|
|
1810
2590
|
EC.e = cur
|
|
1811
2591
|
return undefined
|
|
@@ -1819,20 +2599,74 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1819
2599
|
const n = code[ip + 2]!
|
|
1820
2600
|
const choiceFx = fx[code[ip + 3]!] as string[]
|
|
1821
2601
|
const base = ip + 4
|
|
1822
|
-
const arms: Piece[] = new Array<Piece>(n)
|
|
1823
|
-
for (let i = 0; i < n; i++) arms[i] = link(code[base + i]!)
|
|
1824
|
-
// PER-ARM STATIC SETS, resolved once at assembly. An arm the char gate
|
|
1825
|
-
// declines to enter still owes its expectation: the interpreter has no
|
|
1826
|
-
// such gate, enters the arm, and its opener's failure is what lands in
|
|
1827
|
-
// the accumulation.
|
|
1828
|
-
const armFx: (string[])[] = new Array<string[]>(n)
|
|
1829
|
-
for (let i = 0; i < n; i++) armFx[i] = fx[code[base + n + i]!] as string[]
|
|
1830
2602
|
|
|
1831
2603
|
// THE TWO CHOICE SHAPES ARE TWO PIECES. `exec.ts` tested `table.exclusive`
|
|
1832
2604
|
// on every choice execution; it is table data, so it selects here.
|
|
1833
2605
|
if (table.exclusive) {
|
|
1834
2606
|
const ascii = table.ascii
|
|
1835
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)
|
|
1836
2670
|
// THERE ARE NO OPEN ARMS HERE, and the piece is written to say so. An
|
|
1837
2671
|
// ungated arm is one whose class is −1, and `resolveDispatch` clears
|
|
1838
2672
|
// `exclusive` for exactly those arms (program.ts) — so `table.open` is
|
|
@@ -1851,7 +2685,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1851
2685
|
}
|
|
1852
2686
|
if (arm >= 0) {
|
|
1853
2687
|
ctx._fc = false
|
|
1854
|
-
const v =
|
|
2688
|
+
const v = selected(arm, input, pos, ctx)
|
|
1855
2689
|
if (v !== FAIL) return v
|
|
1856
2690
|
// THE CUT — a committed failure fails the whole choice.
|
|
1857
2691
|
if (committed(ctx)) return FAIL
|
|
@@ -1875,48 +2709,25 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1875
2709
|
// excludes the char at `pos`. `null` means nullable or unmappable, and
|
|
1876
2710
|
// those arms are always entered.
|
|
1877
2711
|
const armCls = table.armCls
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
* CHARACTER and the TABLE, both of which are known here, so the whole
|
|
1889
|
-
* question is answered once at assembly: `cand[c]` is the bitmask of arms
|
|
1890
|
-
* that could match `c`, and the loop visits only set bits, IN ORDER, so
|
|
1891
|
-
* the source ordering the gate exists to preserve is preserved exactly.
|
|
1892
|
-
*
|
|
1893
|
-
* Slot 128 is EOF (`lead` returns −1): `classHas` rejects a negative code,
|
|
1894
|
-
* so only the always-enter arms are candidates there. Code points ≥ 128
|
|
1895
|
-
* keep the per-arm loop — the ranges make a table the wrong shape and
|
|
1896
|
-
* astral input is not the hot path.
|
|
1897
|
-
*
|
|
1898
|
-
* Bounded at 32 arms because the mask is one word. Above that the general
|
|
1899
|
-
* loop runs, which is the same code the ≥ 128 path takes, so there is no
|
|
1900
|
-
* second implementation to drift.
|
|
1901
|
-
*/
|
|
1902
|
-
const maskable = n <= 32
|
|
1903
|
-
let cand: Uint32Array | undefined
|
|
1904
|
-
if (maskable) {
|
|
1905
|
-
const m = new Uint32Array(129)
|
|
1906
|
-
for (let i = 0; i < n; i++) {
|
|
1907
|
-
const cls = gates[i]!
|
|
1908
|
-
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 => {
|
|
1909
2722
|
if (cls === null) {
|
|
1910
|
-
for (let c = 0; c < 129; c++)
|
|
1911
|
-
|
|
2723
|
+
for (let c = 0; c < 129; c++) mask[c]! |= bit
|
|
2724
|
+
return
|
|
1912
2725
|
}
|
|
1913
|
-
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
|
|
1914
2727
|
}
|
|
1915
|
-
|
|
1916
|
-
|
|
2728
|
+
addGate(g0, 1); addGate(g1, 2)
|
|
2729
|
+
if (n === 3) addGate(g2, 4)
|
|
1917
2730
|
|
|
1918
|
-
if (cand !== undefined) {
|
|
1919
|
-
const mask = cand
|
|
1920
2731
|
return (input, pos, ctx) => {
|
|
1921
2732
|
const c = lead(input, pos)
|
|
1922
2733
|
const need = markCst(ctx)
|
|
@@ -1929,46 +2740,144 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1929
2740
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
1930
2741
|
let acc: string[] | undefined
|
|
1931
2742
|
if (c < 128) {
|
|
1932
|
-
|
|
1933
|
-
const bits0 = mask[c < 0 ? 128 : c]!
|
|
1934
|
-
let bits = bits0
|
|
1935
|
-
// Arms below `prev` have already contributed. Only the FAILURE path
|
|
1936
|
-
// advances it, so a matching arm returns without paying for any of
|
|
1937
|
-
// this — the skipped arms' sets are read on no other exit.
|
|
2743
|
+
const bits = mask[c < 0 ? 128 : c]!
|
|
1938
2744
|
let prev = 0
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
2745
|
+
if ((bits & 1) !== 0) {
|
|
2746
|
+
ctx._fc = false
|
|
2747
|
+
const v = a0(input, pos, ctx)
|
|
2748
|
+
if (v !== FAIL) return v
|
|
2749
|
+
prev = 1
|
|
2750
|
+
acc = accSet(ctx._fx, acc)
|
|
2751
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
2752
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2753
|
+
}
|
|
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) {
|
|
1942
2765
|
ctx._fc = false
|
|
1943
|
-
const v =
|
|
2766
|
+
const v = a2!(input, pos, ctx)
|
|
1944
2767
|
if (v !== FAIL) return v
|
|
1945
|
-
|
|
1946
|
-
prev =
|
|
2768
|
+
if (prev < 1) acc = accSet(e0, acc)
|
|
2769
|
+
if (prev < 2) acc = accSet(e1, acc)
|
|
2770
|
+
prev = 3
|
|
1947
2771
|
acc = accSet(ctx._fx, acc)
|
|
1948
|
-
// A committed arm cuts, and the interpreter reports the
|
|
1949
|
-
// accumulation at the ARM's span (choice.ts:170), not at `pos`.
|
|
1950
2772
|
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1951
2773
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1952
2774
|
}
|
|
1953
|
-
|
|
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)
|
|
1954
2778
|
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1955
2779
|
return FAIL
|
|
1956
2780
|
}
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
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)) {
|
|
1960
2791
|
ctx._fc = false
|
|
1961
|
-
const v =
|
|
2792
|
+
const v = a1(input, pos, ctx)
|
|
1962
2793
|
if (v !== FAIL) return v
|
|
1963
2794
|
acc = accSet(ctx._fx, acc)
|
|
1964
2795
|
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1965
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)
|
|
1966
2807
|
}
|
|
1967
2808
|
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1968
2809
|
return FAIL
|
|
1969
2810
|
}
|
|
1970
2811
|
}
|
|
1971
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
|
+
|
|
1972
2881
|
return (input, pos, ctx) => {
|
|
1973
2882
|
const c = lead(input, pos)
|
|
1974
2883
|
const need = markCst(ctx)
|
|
@@ -1979,19 +2888,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1979
2888
|
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
1980
2889
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
1981
2890
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
if (cls !== null && !classHas(cls, c)) { acc = accSet(armFx[i]!, acc); continue }
|
|
1986
|
-
ctx._fc = false
|
|
1987
|
-
const v = arms[i]!(input, pos, ctx)
|
|
1988
|
-
if (v !== FAIL) return v
|
|
1989
|
-
acc = accSet(ctx._fx, acc)
|
|
1990
|
-
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1991
|
-
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1992
|
-
}
|
|
1993
|
-
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1994
|
-
return FAIL
|
|
2891
|
+
return general(
|
|
2892
|
+
input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, undefined, 0,
|
|
2893
|
+
)
|
|
1995
2894
|
}
|
|
1996
2895
|
}
|
|
1997
2896
|
|
|
@@ -2105,6 +3004,18 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2105
3004
|
const reportItem = (flags & 4) !== 0
|
|
2106
3005
|
const itemFx = reportItem ? fx[code[ip + 6]!] as string[] : EMPTY_FX
|
|
2107
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))
|
|
2108
3019
|
// `many()` — the min-0, separator-less repeat — is the only shape that runs
|
|
2109
3020
|
// its FIRST item through `repItem` and so skips leading trivia. The shape
|
|
2110
3021
|
// identifies itself, and it is table data, so it is decided here.
|
|
@@ -2235,6 +3146,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2235
3146
|
const out: unknown[] | undefined = collect ? [] : undefined
|
|
2236
3147
|
const hasTrivia = ctx.trivia !== undefined
|
|
2237
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
|
|
2238
3153
|
let cur = pos
|
|
2239
3154
|
let count = 0
|
|
2240
3155
|
for (;;) {
|
|
@@ -2243,6 +3158,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2243
3158
|
// the LOOP HEAD. Held to `count >= min` so an under-`min` list still
|
|
2244
3159
|
// attempts the separator and reports its expected set.
|
|
2245
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
|
|
2246
3165
|
// Per ITERATION, never hoisted: a preceding item's `node()` opened and
|
|
2247
3166
|
// closed a capture buffer, so `ctx._cstBuf` is not the object it was
|
|
2248
3167
|
// at the loop head. `needMark` is the pre-existing loop-invariant.
|
|
@@ -2279,6 +3198,11 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2279
3198
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
2280
3199
|
break
|
|
2281
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
|
+
}
|
|
2282
3206
|
ctx._fc = false
|
|
2283
3207
|
const v = child(input, itemStart, ctx)
|
|
2284
3208
|
if (v === FAIL) {
|
|
@@ -2316,69 +3240,69 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2316
3240
|
const otherRouted = code[ip + 4]! === 1
|
|
2317
3241
|
const n = code[ip + 5]!
|
|
2318
3242
|
const armBase = ip + 6
|
|
2319
|
-
const arms: Piece[] = new Array<Piece>(n)
|
|
2320
|
-
for (let i = 0; i < n; i++) arms[i] = link(code[armBase + i]!)
|
|
2321
3243
|
const byKey = spec.byKey
|
|
2322
3244
|
const byFold = spec.byFold
|
|
2323
3245
|
const hasFold = spec.byFold.size > 0
|
|
2324
3246
|
const match = spec.match
|
|
2325
3247
|
const matchN = match.length
|
|
2326
|
-
const matchFn: Array<(key: string) => boolean> = new Array<(key: string) => boolean>(matchN)
|
|
2327
|
-
const matchArm: number[] = new Array<number>(matchN)
|
|
2328
|
-
for (let i = 0; i < matchN; i++) { matchFn[i] = linkMatcher(match[i]!); matchArm[i] = match[i]![3] }
|
|
2329
3248
|
const routed = spec.routed
|
|
2330
3249
|
const expected = spec.expected as string[]
|
|
2331
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
|
+
|
|
2332
3289
|
return (input, pos, ctx) => {
|
|
2333
3290
|
const selectorMark = saveTriviaMark(ctx)
|
|
2334
3291
|
const selVal = selector(input, pos, ctx)
|
|
2335
3292
|
if (selVal === FAIL) return FAIL
|
|
2336
3293
|
const selEnd = EC.e
|
|
2337
3294
|
const key = selVal as string
|
|
2338
|
-
|
|
2339
|
-
let arm = byKey.get(key)
|
|
2340
|
-
if (arm === undefined && hasFold) arm = byFold.get(asciiFoldKey(key))
|
|
2341
|
-
if (arm === undefined) {
|
|
2342
|
-
for (let i = 0; i < matchN; i++) {
|
|
2343
|
-
if (matchFn[i]!(key)) {
|
|
2344
|
-
arm = matchArm[i]!
|
|
2345
|
-
break
|
|
2346
|
-
}
|
|
2347
|
-
}
|
|
2348
|
-
}
|
|
2349
|
-
|
|
2350
|
-
let target: Piece
|
|
2351
|
-
let usesRouted: boolean
|
|
3295
|
+
const arm = classify(key)
|
|
2352
3296
|
if (arm === undefined) {
|
|
2353
|
-
if (
|
|
3297
|
+
if (runOther === undefined) {
|
|
2354
3298
|
// No branch and no fallback: fail AT THE SELECTOR'S END.
|
|
2355
3299
|
ctx._fe = selEnd
|
|
2356
3300
|
ctx._fx = expected
|
|
2357
3301
|
return FAIL
|
|
2358
3302
|
}
|
|
2359
|
-
|
|
2360
|
-
usesRouted = otherRouted
|
|
2361
|
-
} else {
|
|
2362
|
-
target = arms[arm]!
|
|
2363
|
-
usesRouted = routed[arm] === 1
|
|
2364
|
-
}
|
|
2365
|
-
|
|
2366
|
-
const savedRouted = ctx._routed
|
|
2367
|
-
let mark = saveTriviaMark(ctx)
|
|
2368
|
-
if (usesRouted) {
|
|
2369
|
-
rollbackTrivia(ctx, selectorMark)
|
|
2370
|
-
mark = saveTriviaMark(ctx)
|
|
2371
|
-
ctx._routed = { value: key, span: { start: pos, end: selEnd } }
|
|
3303
|
+
return runOther(input, pos, selEnd, key, selectorMark, ctx)
|
|
2372
3304
|
}
|
|
2373
|
-
|
|
2374
|
-
if (usesRouted) ctx._routed = savedRouted
|
|
2375
|
-
if (v === FAIL) {
|
|
2376
|
-
rollbackTrivia(ctx, mark)
|
|
2377
|
-
// A failed dispatch branch is COMMITTED: the selector already matched.
|
|
2378
|
-
ctx._fc = true
|
|
2379
|
-
return FAIL
|
|
2380
|
-
}
|
|
2381
|
-
return [key, v]
|
|
3305
|
+
return runArm!(arm, input, pos, selEnd, key, selectorMark, ctx)
|
|
2382
3306
|
}
|
|
2383
3307
|
}
|
|
2384
3308
|
|
|
@@ -2387,6 +3311,41 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2387
3311
|
case OP_NODE:
|
|
2388
3312
|
case OP_NODE_TRACK: {
|
|
2389
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
|
+
}
|
|
2390
3349
|
const child = link(code[ip + 2]!)
|
|
2391
3350
|
const proj = code[ip + 4]!
|
|
2392
3351
|
const buildIdx = code[ip + 1]!
|
|
@@ -2405,12 +3364,34 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2405
3364
|
// 145,512 executions per parse of `benchmark.less`. `cstOutputHost(ctx.build)`
|
|
2406
3365
|
// is fixed by `run()` before the entry is called, so it selects the piece.
|
|
2407
3366
|
const build = buildIdx >= 0
|
|
2408
|
-
? fns[buildIdx] as
|
|
2409
|
-
children: readonly unknown[], fields: FieldMap | undefined, span: { start: number; end: number },
|
|
2410
|
-
rawChildren: readonly unknown[], triviaLog: readonly number[], state: unknown,
|
|
2411
|
-
) => unknown
|
|
3367
|
+
? fns[buildIdx] as NodeBuilder
|
|
2412
3368
|
: undefined
|
|
2413
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
|
+
|
|
2414
3395
|
// A STRUCTURAL node — no builder, no projection — is the only shape that
|
|
2415
3396
|
// takes a per-node-type trivia-kind mask off the host (`node.ts:260`).
|
|
2416
3397
|
// Assembly-time, so a node with a reducer pays nothing for the question.
|
|
@@ -2577,10 +3558,11 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2577
3558
|
* Emitted source is attempted FIRST, and the closure walk above runs only for
|
|
2578
3559
|
* what it refuses.
|
|
2579
3560
|
*
|
|
2580
|
-
*
|
|
2581
|
-
*
|
|
2582
|
-
*
|
|
2583
|
-
* 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.
|
|
2584
3566
|
*
|
|
2585
3567
|
* The refusal is RECORDED, not swallowed. A grammar that silently drops to
|
|
2586
3568
|
* the closure path is a permanently slow path nobody would ever find, which
|
|
@@ -2602,6 +3584,24 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2602
3584
|
if (s.sentinel !== undefined) extraIps.push(s.sentinel[0])
|
|
2603
3585
|
}
|
|
2604
3586
|
for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0])
|
|
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)
|
|
3604
|
+
}
|
|
2605
3605
|
/**
|
|
2606
3606
|
* THE BUILD'S OWN ANSWER, TRIED BEFORE THE CONSTRUCTOR.
|
|
2607
3607
|
*
|
|
@@ -2625,18 +3625,19 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2625
3625
|
? prog.asm?.find(a => a.key === cfgKey(cfg))
|
|
2626
3626
|
: undefined
|
|
2627
3627
|
if (pre !== undefined) {
|
|
2628
|
-
const pools = rebuildPools(t.cc, t.fx, pre.plan)
|
|
3628
|
+
const pools = rebuildPools(t.cc, t.fx, t.disp, pre.plan)
|
|
2629
3629
|
emitted = pre.factory(
|
|
2630
3630
|
EC, FAIL, k, fx, fns, pools.masks, pools.classes, pools.armExpected, trivia,
|
|
2631
3631
|
trivia.map(tv => tv?._meta.triviaKindLabels), triviaScan,
|
|
2632
3632
|
scansArr, disp, dsp, EMPTY_FX, EMPTY_CH, EMPTY_TLOG, EMPTY_TL,
|
|
2633
|
-
cstCaptureActive, pushCstLeaf, pushCstChild, rollbackTriviaAt, failAt,
|
|
3633
|
+
cstCaptureActive, pushCstLeaf, pushCstChild, rollbackTriviaAt, rollbackScannedTriviaAt, failAt,
|
|
2634
3634
|
classHas, consumeTrivia, buildFieldMap, projectChild, unwrapChild,
|
|
2635
3635
|
demoteCapturedToRaw, cstLeavesLen, skipTriviaScanned, needsDeferredTriviaCommit,
|
|
2636
3636
|
scanTrivia, advanceTrivia, refuseUnclassifiedRootScope, spanLines, rawEntry, lead,
|
|
2637
3637
|
asciiFoldKey, ROUTED_FX,
|
|
2638
3638
|
REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
|
|
2639
3639
|
matchesAt, recoverScan, orSentinel, captureError,
|
|
3640
|
+
scalarRecognizers, commitTriviaScan, scanTriviaCompact, t.lex, adjacencyHolds, t.lexPrograms,
|
|
2640
3641
|
)
|
|
2641
3642
|
emitReached = new Set(pre.reached)
|
|
2642
3643
|
}
|
|
@@ -2705,7 +3706,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2705
3706
|
EC, FAIL, k, fx, fns, em.masks, em.classes, em.armExpected, trivia,
|
|
2706
3707
|
trivia.map(tv => tv?._meta.triviaKindLabels), triviaScan,
|
|
2707
3708
|
scansArr, disp, dsp, EMPTY_FX, EMPTY_CH, EMPTY_TLOG, EMPTY_TL,
|
|
2708
|
-
cstCaptureActive, pushCstLeaf, pushCstChild, rollbackTriviaAt, failAt,
|
|
3709
|
+
cstCaptureActive, pushCstLeaf, pushCstChild, rollbackTriviaAt, rollbackScannedTriviaAt, failAt,
|
|
2709
3710
|
classHas, consumeTrivia, buildFieldMap, projectChild, unwrapChild,
|
|
2710
3711
|
demoteCapturedToRaw, cstLeavesLen, skipTriviaScanned, needsDeferredTriviaCommit,
|
|
2711
3712
|
scanTrivia, advanceTrivia, refuseUnclassifiedRootScope, spanLines, rawEntry, lead,
|
|
@@ -2717,6 +3718,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2717
3718
|
// object rather than to two separately constructed ones.
|
|
2718
3719
|
REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
|
|
2719
3720
|
matchesAt, recoverScan, orSentinel, captureError,
|
|
3721
|
+
scalarRecognizers, commitTriviaScan, scanTriviaCompact, t.lex, adjacencyHolds, t.lexPrograms,
|
|
2720
3722
|
)
|
|
2721
3723
|
emitReached = em.reached
|
|
2722
3724
|
} catch (e) {
|