parseman 0.47.1 → 0.48.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -21
- package/dist/analysis/diagnostics.js +2964 -347
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +2960 -343
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/expect.d.ts +1 -0
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/expected.d.ts +8 -0
- package/dist/combinators/expected.d.ts.map +1 -0
- package/dist/combinators/first-set.d.ts +8 -0
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/keywords.d.ts +4 -0
- package/dist/combinators/keywords.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/not.d.ts.map +1 -1
- package/dist/combinators/peek.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/sequence.d.ts.map +1 -1
- package/dist/combinators/trivia-skip.d.ts +16 -3
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/compiler/direct-projection.d.ts +12 -0
- package/dist/compiler/direct-projection.d.ts.map +1 -0
- package/dist/compiler/token-alphabet.d.ts +845 -5
- package/dist/compiler/token-alphabet.d.ts.map +1 -1
- package/dist/compiler/token-capability.d.ts +3 -0
- package/dist/compiler/token-capability.d.ts.map +1 -0
- package/dist/compiler/token-capability.js +3192 -0
- package/dist/compiler/token-capability.js.map +6 -0
- package/dist/cst/trivia-charscan.d.ts +3 -0
- package/dist/cst/trivia-charscan.d.ts.map +1 -1
- package/dist/cst/trivia-css-scanner.d.ts +5 -0
- package/dist/cst/trivia-css-scanner.d.ts.map +1 -0
- package/dist/cst/trivia-kinds.d.ts.map +1 -1
- package/dist/index.js +2973 -343
- package/dist/index.js.map +3 -3
- package/dist/language-service/index.js +5 -1
- package/dist/language-service/index.js.map +1 -1
- package/dist/plugin/index.js +2982 -352
- package/dist/plugin/index.js.map +3 -3
- package/dist/spec/index.js +325 -47
- package/dist/spec/index.js.map +3 -3
- package/dist/table/assemble.d.ts.map +1 -1
- package/dist/table/child-slots.d.ts.map +1 -1
- package/dist/table/compile-rule-map.d.ts +3 -1
- package/dist/table/compile-rule-map.d.ts.map +1 -1
- package/dist/table/emit-assembly.d.ts +12 -13
- package/dist/table/emit-assembly.d.ts.map +1 -1
- package/dist/table/emit.d.ts.map +1 -1
- package/dist/table/encode.d.ts.map +1 -1
- package/dist/table/exec-baseline.d.ts.map +1 -1
- package/dist/table/exec.d.ts.map +1 -1
- package/dist/table/index.js +2937 -311
- package/dist/table/index.js.map +3 -3
- package/dist/table/lex-body.d.ts +9 -0
- package/dist/table/lex-body.d.ts.map +1 -0
- package/dist/table/lex-program.d.ts +25 -0
- package/dist/table/lex-program.d.ts.map +1 -0
- package/dist/table/ops.d.ts +47 -11
- package/dist/table/ops.d.ts.map +1 -1
- package/dist/table/program.d.ts +50 -2
- package/dist/table/program.d.ts.map +1 -1
- package/dist/table/scalar-terminal.d.ts +13 -0
- package/dist/table/scalar-terminal.d.ts.map +1 -0
- package/dist/table/site-labels.d.ts.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +11 -11
- package/src/combinators/choice.ts +10 -1
- package/src/combinators/expect.ts +1 -0
- package/src/combinators/expected.ts +17 -0
- package/src/combinators/first-set.ts +58 -5
- package/src/combinators/grammar.ts +8 -3
- package/src/combinators/keywords.ts +22 -6
- package/src/combinators/literal.ts +2 -1
- package/src/combinators/not.ts +5 -1
- package/src/combinators/peek.ts +5 -1
- package/src/combinators/regex.ts +5 -3
- package/src/combinators/repeat.ts +4 -10
- package/src/combinators/sequence.ts +17 -14
- package/src/combinators/trivia-skip.ts +46 -13
- package/src/compiler/direct-projection.ts +43 -0
- package/src/compiler/token-alphabet.ts +4316 -12
- package/src/compiler/token-capability.ts +6 -0
- package/src/cst/trivia-charscan.ts +34 -0
- package/src/cst/trivia-css-scanner.ts +142 -0
- package/src/cst/trivia-kinds.ts +3 -1
- package/src/plugin/index.ts +4 -4
- package/src/table/assemble.ts +1253 -240
- package/src/table/child-slots.ts +2 -1
- package/src/table/compile-rule-map.ts +21 -3
- package/src/table/emit-assembly.ts +335 -87
- package/src/table/emit.ts +16 -2
- package/src/table/encode.ts +248 -60
- package/src/table/exec-baseline.ts +3 -7
- package/src/table/exec.ts +134 -25
- package/src/table/lex-body.ts +30 -0
- package/src/table/lex-program.ts +431 -0
- package/src/table/ops.ts +49 -11
- package/src/table/program.ts +46 -2
- package/src/table/scalar-terminal.ts +51 -0
- package/src/table/site-labels.ts +11 -1
- package/src/table/stamp.ts +1 -1
- package/src/types.ts +1 -1
- package/src/version.ts +1 -1
- package/dist/analysis/diagnostics.cjs +0 -13824
- package/dist/analysis/diagnostics.cjs.map +0 -6
- package/dist/cli/index.cjs +0 -14925
- package/dist/cli/index.cjs.map +0 -6
- package/dist/index.cjs +0 -16391
- package/dist/index.cjs.map +0 -6
- package/dist/language-service/index.cjs +0 -1304
- package/dist/language-service/index.cjs.map +0 -6
- package/dist/oracle/index.cjs +0 -254
- package/dist/oracle/index.cjs.map +0 -6
- package/dist/plugin/index.cjs +0 -16097
- package/dist/plugin/index.cjs.map +0 -6
- package/dist/run/index.cjs +0 -577
- package/dist/run/index.cjs.map +0 -6
- package/dist/spec/index.cjs +0 -6236
- package/dist/spec/index.cjs.map +0 -6
- package/dist/table/index.cjs +0 -11463
- package/dist/table/index.cjs.map +0 -6
package/src/table/assemble.ts
CHANGED
|
@@ -71,8 +71,8 @@ import { projectChild, unwrapChild } from '../combinators/node.ts'
|
|
|
71
71
|
import { cstOutputHost } from '../compiler/build-arity.ts'
|
|
72
72
|
import { consumeTrivia } from '../combinators/trivia-skip.ts'
|
|
73
73
|
import {
|
|
74
|
-
advanceTrivia, needsDeferredTriviaCommit, rollbackScannedTriviaAt, rollbackTrivia, rollbackTriviaAt,
|
|
75
|
-
saveTriviaMark, scanTrivia, skipTriviaScanned, type FastTriviaScanner,
|
|
74
|
+
advanceTrivia, commitTriviaScan, needsDeferredTriviaCommit, rollbackScannedTriviaAt, rollbackTrivia, rollbackTriviaAt,
|
|
75
|
+
saveTriviaMark, scanTrivia, scanTriviaCompact, skipTriviaScanned, type FastTriviaScanner,
|
|
76
76
|
} from '../combinators/trivia-skip.ts'
|
|
77
77
|
import {
|
|
78
78
|
cstCaptureActive, cstLeavesLen, cstTlLen,
|
|
@@ -87,6 +87,7 @@ import {
|
|
|
87
87
|
OP_FIELD, OP_DISPATCH, OP_ROUTED, OP_LIT_CI, OP_LIT_CI_TRACK, OP_TOKEN, OP_WITHCTX, OP_GUARD, OP_ATTEMPT, OP_LABEL,
|
|
88
88
|
OP_COV,
|
|
89
89
|
OP_ADJ, OP_GREEDY, OP_REJECT, OP_ARMGATE,
|
|
90
|
+
OP_LEX_BODY, OP_LEX_PROGRAM,
|
|
90
91
|
} from './ops.ts'
|
|
91
92
|
import { adjacencyHolds, adjacencyMisuse } from '../combinators/adjacency.ts'
|
|
92
93
|
import { failAt } from '../combinators/probe.ts'
|
|
@@ -118,14 +119,18 @@ import { lead, rawEntry, spanLines } from './run-support.ts'
|
|
|
118
119
|
* property read per terminal miss — the same price codegen pays.
|
|
119
120
|
*/
|
|
120
121
|
import {
|
|
121
|
-
classHas, decodeClassSpec, expandCompact, resolveTable,
|
|
122
|
+
classHas, decodeClassSpec, expandCompact, resolveTable,
|
|
123
|
+
validateDispatchSpec,
|
|
122
124
|
type CompactProgram, type ResolvedClass, type ResolvedTable,
|
|
123
125
|
type SubtreeRef, type TableProgram, type TableRule,
|
|
124
126
|
} from './program.ts'
|
|
125
|
-
import { reachableSites } from './site-labels.ts'
|
|
126
127
|
import { stampRuleMap } from './stamp.ts'
|
|
128
|
+
import { reachableSites } from './site-labels.ts'
|
|
127
129
|
import { refuseUnclassifiedRootScope } from '../cst/root-trivia-scope.ts'
|
|
128
130
|
import { captureError, firstSetSentinel, matchesAt, orSentinel, recoverScan } from '../recovery/scan.ts'
|
|
131
|
+
import {
|
|
132
|
+
makeScalarRecognizer, scalarTerminalNodeChild, scalarTerminalNotChild, type ScalarRecognizer,
|
|
133
|
+
} from './scalar-terminal.ts'
|
|
129
134
|
|
|
130
135
|
/**
|
|
131
136
|
* Is the EMITTED engine (`emit-assembly.ts`) enabled for this process?
|
|
@@ -139,7 +144,7 @@ const EMIT_ENABLED = (globalThis as { process?: { env?: Record<string, string |
|
|
|
139
144
|
.process?.env?.PM_TABLE_EMIT !== '0'
|
|
140
145
|
|
|
141
146
|
/** Failure sentinel — SHARED with the other two engines, see `cell.ts`. */
|
|
142
|
-
import { FAIL, newEndCell
|
|
147
|
+
import { FAIL, newEndCell } from './cell.ts'
|
|
143
148
|
|
|
144
149
|
const EMPTY_TL: readonly number[] = Object.freeze([])
|
|
145
150
|
/** `finishCstBuf`'s two empty sentinels, held here so the node piece can inline it. */
|
|
@@ -167,10 +172,20 @@ function linkMatcher(m: readonly [number, string, string, number]): (key: string
|
|
|
167
172
|
case 1: return key => key.endsWith(value)
|
|
168
173
|
case 3: return key => asciiFoldKey(key).startsWith(value)
|
|
169
174
|
case 4: return key => asciiFoldKey(key).endsWith(value)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
default: {
|
|
176
|
+
const flags = m[2]
|
|
177
|
+
// `matches()` refuses global/sticky patterns, so every compiler-owned
|
|
178
|
+
// matcher has stable `lastIndex` and can be compiled once per assembly.
|
|
179
|
+
// Preserve the old fresh-per-test behavior for hand-built low-level rows
|
|
180
|
+
// that contain g/y or invalid flags rather than broadening the wire ABI.
|
|
181
|
+
if (!flags.includes('g') && !flags.includes('y')) {
|
|
182
|
+
try {
|
|
183
|
+
const pattern = new RegExp(value, flags)
|
|
184
|
+
return key => pattern.test(key)
|
|
185
|
+
} catch {}
|
|
186
|
+
}
|
|
187
|
+
return key => new RegExp(value, flags).test(key)
|
|
188
|
+
}
|
|
174
189
|
}
|
|
175
190
|
}
|
|
176
191
|
|
|
@@ -198,6 +213,36 @@ const NO_COVERAGE = (_id: string): void => {}
|
|
|
198
213
|
* `{ value, end }` pair would be an allocation per row.
|
|
199
214
|
*/
|
|
200
215
|
type Piece = (input: string, pos: number, ctx: ParseContext) => unknown
|
|
216
|
+
type NodeBuilder = (
|
|
217
|
+
children: readonly unknown[], fields: FieldMap | undefined, span: { start: number; end: number },
|
|
218
|
+
rawChildren: readonly unknown[], triviaLog: readonly number[], state: unknown,
|
|
219
|
+
) => unknown
|
|
220
|
+
type ChoiceExpectedPrefix = (
|
|
221
|
+
target: number, prev: number, acc: string[] | undefined,
|
|
222
|
+
) => string[] | undefined
|
|
223
|
+
type MaskedChoiceBlock = (
|
|
224
|
+
input: string, pos: number, ctx: ParseContext, bits: number,
|
|
225
|
+
need: boolean, mRaw: number, mTl: number, mLv: number, mFl: number,
|
|
226
|
+
mEr: number, mLog: number, mRoot: number,
|
|
227
|
+
acc: string[] | undefined, prev: number, best: 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, best: number,
|
|
234
|
+
) => unknown
|
|
235
|
+
type ExclusiveChoiceBlock = (arm: number, input: string, pos: number, ctx: ParseContext) => unknown
|
|
236
|
+
type DispatchMatcher = (key: string) => boolean
|
|
237
|
+
type DispatchMatcherBlock = (key: string) => number | undefined
|
|
238
|
+
type DispatchBranch = (
|
|
239
|
+
input: string, pos: number, selEnd: number, key: string,
|
|
240
|
+
selectorMark: ReturnType<typeof saveTriviaMark>, ctx: ParseContext,
|
|
241
|
+
) => unknown
|
|
242
|
+
type DispatchArmBlock = (
|
|
243
|
+
arm: number, input: string, pos: number, selEnd: number, key: string,
|
|
244
|
+
selectorMark: ReturnType<typeof saveTriviaMark>, ctx: ParseContext,
|
|
245
|
+
) => unknown
|
|
201
246
|
|
|
202
247
|
/**
|
|
203
248
|
* ONE NON-FIRST SEQUENCE TERM, in a sequence that carries an adjacency
|
|
@@ -205,7 +250,14 @@ type Piece = (input: string, pos: number, ctx: ParseContext) => unknown
|
|
|
205
250
|
* `nextTerm`'s protocol, so a bound term and a bound assertion are
|
|
206
251
|
* interchangeable in the loop and the loop tests neither.
|
|
207
252
|
*/
|
|
208
|
-
type TermRunner = (
|
|
253
|
+
type TermRunner = (
|
|
254
|
+
input: string, cur: number, ctx: ParseContext,
|
|
255
|
+
inheritedSync?: Combinator<unknown>,
|
|
256
|
+
) => number
|
|
257
|
+
type SequenceTermBlock = (
|
|
258
|
+
input: string, cur: number, ctx: ParseContext, values: unknown[] | undefined,
|
|
259
|
+
inheritedSync?: Combinator<unknown>,
|
|
260
|
+
) => number
|
|
209
261
|
|
|
210
262
|
/**
|
|
211
263
|
* The option set an assembly is specialised for.
|
|
@@ -415,6 +467,21 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
415
467
|
*/
|
|
416
468
|
const EC = newEndCell()
|
|
417
469
|
|
|
470
|
+
// Indexed by the existing constant-pool operand: the same scalar recognizer
|
|
471
|
+
// serves an ordinary terminal piece and a direct terminal-node materializer.
|
|
472
|
+
// Value/CST/failure/cursor effects stay in those consumers.
|
|
473
|
+
const scalarRecognizers: Array<ScalarRecognizer | undefined> = []
|
|
474
|
+
const rawScalarSpecs = new Set<number>()
|
|
475
|
+
function scalarFor(child: number): ScalarRecognizer | undefined {
|
|
476
|
+
const spec = code[child + 1]!
|
|
477
|
+
let recognize = scalarRecognizers[spec]
|
|
478
|
+
if (recognize === undefined) {
|
|
479
|
+
recognize = makeScalarRecognizer(code[child]!, k[spec])
|
|
480
|
+
scalarRecognizers[spec] = recognize
|
|
481
|
+
}
|
|
482
|
+
return recognize
|
|
483
|
+
}
|
|
484
|
+
|
|
418
485
|
/**
|
|
419
486
|
* The INSTALLED trivia scanner for the scope currently running.
|
|
420
487
|
*
|
|
@@ -433,6 +500,263 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
433
500
|
pushCstLeaf(ctx, lf)
|
|
434
501
|
}
|
|
435
502
|
|
|
503
|
+
/**
|
|
504
|
+
* The three dominant AST node shapes, selected once while linking.
|
|
505
|
+
*
|
|
506
|
+
* The generic node body is necessarily broad: CST hosts, tracked spans,
|
|
507
|
+
* fields, trivia/state reads, structural hosts, projections, collapse and
|
|
508
|
+
* unwrap all share it. That made its one FunctionLiteral 1,100+ V8 bytecodes
|
|
509
|
+
* even for the ordinary AST builder that needs none of those branches. Jess's
|
|
510
|
+
* exact AST workloads execute the three shapes below 42k/66k/205k times per
|
|
511
|
+
* CSS/Less/generated parse. Keep their bodies scalar and keep every other
|
|
512
|
+
* shape on the generic oracle below.
|
|
513
|
+
*/
|
|
514
|
+
function plainBuildNode(child: Piece, build: NodeBuilder): Piece {
|
|
515
|
+
return (input, pos, ctx) => {
|
|
516
|
+
const sCh = ctx._cstChildren
|
|
517
|
+
const sLv = ctx._cstLeaves
|
|
518
|
+
const sRaw = ctx._cstRawChildren
|
|
519
|
+
const sTl = ctx._cstTriviaLog
|
|
520
|
+
const sCap = ctx.captureTrivia
|
|
521
|
+
const sBuf = ctx._cstBuf
|
|
522
|
+
const savedFields = ctx._fields
|
|
523
|
+
const buf: CstCaptureBuf = {}
|
|
524
|
+
ctx._cstBuf = buf
|
|
525
|
+
ctx._cstChildren = undefined
|
|
526
|
+
ctx._cstLeaves = undefined
|
|
527
|
+
ctx._cstRawChildren = undefined
|
|
528
|
+
ctx._cstTriviaLog = undefined
|
|
529
|
+
ctx.captureTrivia = false
|
|
530
|
+
ctx._fields = undefined
|
|
531
|
+
const value = child(input, pos, ctx)
|
|
532
|
+
const kids = buf.ch ?? (buf.single !== undefined ? [buf.single] : EMPTY_CH)
|
|
533
|
+
const rawKids = buf.raw ?? (buf.rawSingle !== undefined ? [buf.rawSingle] : EMPTY_CH)
|
|
534
|
+
ctx._fields = savedFields
|
|
535
|
+
ctx._cstBuf = sBuf
|
|
536
|
+
ctx._cstChildren = sCh
|
|
537
|
+
ctx._cstLeaves = sLv
|
|
538
|
+
ctx._cstRawChildren = sRaw
|
|
539
|
+
ctx._cstTriviaLog = sTl
|
|
540
|
+
ctx.captureTrivia = sCap
|
|
541
|
+
if (value === FAIL) return FAIL
|
|
542
|
+
const end = EC.e
|
|
543
|
+
const span = { start: pos, end }
|
|
544
|
+
const node = build(kids, undefined, span, rawKids, EMPTY_TL, undefined)
|
|
545
|
+
if (sBuf !== undefined || sCh !== undefined) {
|
|
546
|
+
pushCstChild(ctx, node, rawEntry(node, input, pos, end))
|
|
547
|
+
}
|
|
548
|
+
EC.e = end
|
|
549
|
+
return node
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/** The lazy capture buffer uses `single === undefined` as its empty sentinel:
|
|
554
|
+
* leading undefined semantic children are therefore absent, while undefined
|
|
555
|
+
* values after the first present child are retained in `ch`. A flat collector
|
|
556
|
+
* normalizes only that cold prefix so the builder sees the identical list. */
|
|
557
|
+
function capturedFlatChildren(children: unknown[]): readonly unknown[] {
|
|
558
|
+
if (children.length === 0) return EMPTY_CH
|
|
559
|
+
if (children[0] !== undefined) return children
|
|
560
|
+
let first = 1
|
|
561
|
+
while (first < children.length && children[first] === undefined) first++
|
|
562
|
+
return first === children.length ? EMPTY_CH : children.slice(first)
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/** Direct reducer whose declared arity proves `rawChildren` is unobservable.
|
|
566
|
+
* Use the existing split children/leaves collector instead of opening a
|
|
567
|
+
* duplicate raw capture buffer. The body shape is selected while linking. */
|
|
568
|
+
function childrenOnlyBuildNode(child: Piece, build: NodeBuilder): Piece {
|
|
569
|
+
return (input, pos, ctx) => {
|
|
570
|
+
const sCh = ctx._cstChildren
|
|
571
|
+
const sLv = ctx._cstLeaves
|
|
572
|
+
const sRaw = ctx._cstRawChildren
|
|
573
|
+
const sTl = ctx._cstTriviaLog
|
|
574
|
+
const sCap = ctx.captureTrivia
|
|
575
|
+
const sBuf = ctx._cstBuf
|
|
576
|
+
const savedFields = ctx._fields
|
|
577
|
+
const kids: unknown[] = []
|
|
578
|
+
ctx._cstBuf = undefined
|
|
579
|
+
ctx._cstChildren = kids
|
|
580
|
+
ctx._cstLeaves = kids
|
|
581
|
+
ctx._cstRawChildren = undefined
|
|
582
|
+
ctx._cstTriviaLog = undefined
|
|
583
|
+
ctx.captureTrivia = false
|
|
584
|
+
ctx._fields = undefined
|
|
585
|
+
const value = child(input, pos, ctx)
|
|
586
|
+
const captured = capturedFlatChildren(kids)
|
|
587
|
+
ctx._fields = savedFields
|
|
588
|
+
ctx._cstBuf = sBuf
|
|
589
|
+
ctx._cstChildren = sCh
|
|
590
|
+
ctx._cstLeaves = sLv
|
|
591
|
+
ctx._cstRawChildren = sRaw
|
|
592
|
+
ctx._cstTriviaLog = sTl
|
|
593
|
+
ctx.captureTrivia = sCap
|
|
594
|
+
if (value === FAIL) return FAIL
|
|
595
|
+
const end = EC.e
|
|
596
|
+
const node = build(captured, undefined, { start: pos, end }, EMPTY_CH, EMPTY_TL, undefined)
|
|
597
|
+
if (sBuf !== undefined || sCh !== undefined) {
|
|
598
|
+
pushCstChild(ctx, node, sBuf !== undefined || sRaw !== undefined ? rawEntry(node, input, pos, end) : undefined)
|
|
599
|
+
}
|
|
600
|
+
EC.e = end
|
|
601
|
+
return node
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function childrenOnlyFieldsBuildNode(child: Piece, build: NodeBuilder): Piece {
|
|
606
|
+
return (input, pos, ctx) => {
|
|
607
|
+
const sCh = ctx._cstChildren
|
|
608
|
+
const sLv = ctx._cstLeaves
|
|
609
|
+
const sRaw = ctx._cstRawChildren
|
|
610
|
+
const sTl = ctx._cstTriviaLog
|
|
611
|
+
const sCap = ctx.captureTrivia
|
|
612
|
+
const sBuf = ctx._cstBuf
|
|
613
|
+
const savedFields = ctx._fields
|
|
614
|
+
const kids: unknown[] = []
|
|
615
|
+
ctx._cstBuf = undefined
|
|
616
|
+
ctx._cstChildren = kids
|
|
617
|
+
ctx._cstLeaves = kids
|
|
618
|
+
ctx._cstRawChildren = undefined
|
|
619
|
+
ctx._cstTriviaLog = undefined
|
|
620
|
+
ctx.captureTrivia = false
|
|
621
|
+
ctx._fields = []
|
|
622
|
+
const value = child(input, pos, ctx)
|
|
623
|
+
const captured = capturedFlatChildren(kids)
|
|
624
|
+
const fieldMap = buildFieldMap(ctx._fields)
|
|
625
|
+
ctx._fields = savedFields
|
|
626
|
+
ctx._cstBuf = sBuf
|
|
627
|
+
ctx._cstChildren = sCh
|
|
628
|
+
ctx._cstLeaves = sLv
|
|
629
|
+
ctx._cstRawChildren = sRaw
|
|
630
|
+
ctx._cstTriviaLog = sTl
|
|
631
|
+
ctx.captureTrivia = sCap
|
|
632
|
+
if (value === FAIL) return FAIL
|
|
633
|
+
const end = EC.e
|
|
634
|
+
const node = build(captured, fieldMap, { start: pos, end }, EMPTY_CH, EMPTY_TL, undefined)
|
|
635
|
+
if (sBuf !== undefined || sCh !== undefined) {
|
|
636
|
+
pushCstChild(ctx, node, sBuf !== undefined || sRaw !== undefined ? rawEntry(node, input, pos, end) : undefined)
|
|
637
|
+
}
|
|
638
|
+
EC.e = end
|
|
639
|
+
return node
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function collapseBuildNode(child: Piece, build: NodeBuilder): Piece {
|
|
644
|
+
return (input, pos, ctx) => {
|
|
645
|
+
const sCh = ctx._cstChildren
|
|
646
|
+
const sLv = ctx._cstLeaves
|
|
647
|
+
const sRaw = ctx._cstRawChildren
|
|
648
|
+
const sTl = ctx._cstTriviaLog
|
|
649
|
+
const sCap = ctx.captureTrivia
|
|
650
|
+
const sBuf = ctx._cstBuf
|
|
651
|
+
const savedFields = ctx._fields
|
|
652
|
+
const buf: CstCaptureBuf = {}
|
|
653
|
+
ctx._cstBuf = buf
|
|
654
|
+
ctx._cstChildren = undefined
|
|
655
|
+
ctx._cstLeaves = undefined
|
|
656
|
+
ctx._cstRawChildren = undefined
|
|
657
|
+
ctx._cstTriviaLog = undefined
|
|
658
|
+
ctx.captureTrivia = false
|
|
659
|
+
ctx._fields = undefined
|
|
660
|
+
const value = child(input, pos, ctx)
|
|
661
|
+
const kids = buf.ch ?? (buf.single !== undefined ? [buf.single] : EMPTY_CH)
|
|
662
|
+
const rawKids = buf.raw ?? (buf.rawSingle !== undefined ? [buf.rawSingle] : EMPTY_CH)
|
|
663
|
+
ctx._fields = savedFields
|
|
664
|
+
ctx._cstBuf = sBuf
|
|
665
|
+
ctx._cstChildren = sCh
|
|
666
|
+
ctx._cstLeaves = sLv
|
|
667
|
+
ctx._cstRawChildren = sRaw
|
|
668
|
+
ctx._cstTriviaLog = sTl
|
|
669
|
+
ctx.captureTrivia = sCap
|
|
670
|
+
if (value === FAIL) return FAIL
|
|
671
|
+
const end = EC.e
|
|
672
|
+
const span = { start: pos, end }
|
|
673
|
+
const node = kids.length === 1
|
|
674
|
+
? kids[0]
|
|
675
|
+
: build(kids, undefined, span, rawKids, EMPTY_TL, undefined)
|
|
676
|
+
if (sBuf !== undefined || sCh !== undefined) {
|
|
677
|
+
pushCstChild(ctx, node, rawEntry(node, input, pos, end))
|
|
678
|
+
}
|
|
679
|
+
EC.e = end
|
|
680
|
+
return node
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function childrenOnlyCollapseBuildNode(child: Piece, build: NodeBuilder): Piece {
|
|
685
|
+
return (input, pos, ctx) => {
|
|
686
|
+
const sCh = ctx._cstChildren
|
|
687
|
+
const sLv = ctx._cstLeaves
|
|
688
|
+
const sRaw = ctx._cstRawChildren
|
|
689
|
+
const sTl = ctx._cstTriviaLog
|
|
690
|
+
const sCap = ctx.captureTrivia
|
|
691
|
+
const sBuf = ctx._cstBuf
|
|
692
|
+
const savedFields = ctx._fields
|
|
693
|
+
const kids: unknown[] = []
|
|
694
|
+
ctx._cstBuf = undefined
|
|
695
|
+
ctx._cstChildren = kids
|
|
696
|
+
ctx._cstLeaves = kids
|
|
697
|
+
ctx._cstRawChildren = undefined
|
|
698
|
+
ctx._cstTriviaLog = undefined
|
|
699
|
+
ctx.captureTrivia = false
|
|
700
|
+
ctx._fields = undefined
|
|
701
|
+
const value = child(input, pos, ctx)
|
|
702
|
+
const captured = capturedFlatChildren(kids)
|
|
703
|
+
ctx._fields = savedFields
|
|
704
|
+
ctx._cstBuf = sBuf
|
|
705
|
+
ctx._cstChildren = sCh
|
|
706
|
+
ctx._cstLeaves = sLv
|
|
707
|
+
ctx._cstRawChildren = sRaw
|
|
708
|
+
ctx._cstTriviaLog = sTl
|
|
709
|
+
ctx.captureTrivia = sCap
|
|
710
|
+
if (value === FAIL) return FAIL
|
|
711
|
+
const end = EC.e
|
|
712
|
+
const node = captured.length === 1
|
|
713
|
+
? captured[0]
|
|
714
|
+
: build(captured, undefined, { start: pos, end }, EMPTY_CH, EMPTY_TL, undefined)
|
|
715
|
+
if (sBuf !== undefined || sCh !== undefined) {
|
|
716
|
+
pushCstChild(ctx, node, sBuf !== undefined || sRaw !== undefined ? rawEntry(node, input, pos, end) : undefined)
|
|
717
|
+
}
|
|
718
|
+
EC.e = end
|
|
719
|
+
return node
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function plainProjectNode(child: Piece, projection: number, type: string): Piece {
|
|
724
|
+
return (input, pos, ctx) => {
|
|
725
|
+
const sCh = ctx._cstChildren
|
|
726
|
+
const sLv = ctx._cstLeaves
|
|
727
|
+
const sRaw = ctx._cstRawChildren
|
|
728
|
+
const sTl = ctx._cstTriviaLog
|
|
729
|
+
const sCap = ctx.captureTrivia
|
|
730
|
+
const sBuf = ctx._cstBuf
|
|
731
|
+
const savedFields = ctx._fields
|
|
732
|
+
const buf: CstCaptureBuf = {}
|
|
733
|
+
ctx._cstBuf = buf
|
|
734
|
+
ctx._cstChildren = undefined
|
|
735
|
+
ctx._cstLeaves = undefined
|
|
736
|
+
ctx._cstRawChildren = undefined
|
|
737
|
+
ctx._cstTriviaLog = undefined
|
|
738
|
+
ctx.captureTrivia = false
|
|
739
|
+
ctx._fields = undefined
|
|
740
|
+
const value = child(input, pos, ctx)
|
|
741
|
+
const kids = buf.ch ?? (buf.single !== undefined ? [buf.single] : EMPTY_CH)
|
|
742
|
+
ctx._fields = savedFields
|
|
743
|
+
ctx._cstBuf = sBuf
|
|
744
|
+
ctx._cstChildren = sCh
|
|
745
|
+
ctx._cstLeaves = sLv
|
|
746
|
+
ctx._cstRawChildren = sRaw
|
|
747
|
+
ctx._cstTriviaLog = sTl
|
|
748
|
+
ctx.captureTrivia = sCap
|
|
749
|
+
if (value === FAIL) return FAIL
|
|
750
|
+
const end = EC.e
|
|
751
|
+
const node = projectChild(kids, projection, type)
|
|
752
|
+
if (sBuf !== undefined || sCh !== undefined) {
|
|
753
|
+
pushCstChild(ctx, node, rawEntry(node, input, pos, end))
|
|
754
|
+
}
|
|
755
|
+
EC.e = end
|
|
756
|
+
return node
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
436
760
|
/** Mirrors `exec.ts`'s `rollbackNeeded` — a runtime question about live sinks. */
|
|
437
761
|
function rollbackNeeded(ctx: ParseContext): boolean {
|
|
438
762
|
return ctx._cstBuf !== undefined
|
|
@@ -562,9 +886,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
562
886
|
// keeps the exact branch it had.
|
|
563
887
|
if (s !== null) return skipTriviaScanned(s, input, cur, ctx)
|
|
564
888
|
if (needsDeferredTriviaCommit(ctx)) {
|
|
565
|
-
|
|
566
|
-
scan.commit()
|
|
567
|
-
return scan.end
|
|
889
|
+
return commitTriviaScan(scanTriviaCompact(input, cur, ctx))
|
|
568
890
|
}
|
|
569
891
|
return advanceTrivia(input, cur, ctx)
|
|
570
892
|
}
|
|
@@ -662,7 +984,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
662
984
|
TERMV = v
|
|
663
985
|
return EC.e > scanEnd ? EC.e : cur
|
|
664
986
|
}
|
|
665
|
-
// SCALAR MARKS —
|
|
987
|
+
// SCALAR MARKS — only the three trivia sinks can receive ambient-scan rows.
|
|
988
|
+
// Child-owned nodes, fields and errors are deliberately outside this range.
|
|
666
989
|
const need = rollbackNeeded(ctx)
|
|
667
990
|
const mTl = need ? cstTlLen(ctx) : 0
|
|
668
991
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
@@ -685,19 +1008,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
685
1008
|
return c._fc === true
|
|
686
1009
|
}
|
|
687
1010
|
|
|
688
|
-
/**
|
|
689
|
-
* Accumulate one failed choice arm's expected set, in arm order.
|
|
690
|
-
*
|
|
691
|
-
* `choice.ts:167` is `expected.push(...result.expected)` per failed arm, and the
|
|
692
|
-
* accumulation — not the static union of the arms' OPENERS — is what a failing
|
|
693
|
-
* ordered choice reports. A left-factored or shared-prefix choice whose arms all
|
|
694
|
-
* failed PAST their common opener reported that opener alone: `['/::?/']` where
|
|
695
|
-
* the interpreter says `['"before"', '"hover"']`.
|
|
696
|
-
*
|
|
697
|
-
* Returns the accumulator so the common one-failing-arm case borrows the arm's
|
|
698
|
-
* own array (`slice` once) instead of building one per choice execution the way
|
|
699
|
-
* the interpreter's eager `const expected: string[] = []` does.
|
|
700
|
-
*/
|
|
1011
|
+
/** Append an exact-depth failed arm set in source order. Callers own depth ranking. */
|
|
701
1012
|
function accSet(ax: readonly string[] | undefined, acc: string[] | undefined): string[] | undefined {
|
|
702
1013
|
if (ax === undefined || ax.length === 0) return acc
|
|
703
1014
|
if (acc === undefined) return ax.slice()
|
|
@@ -705,6 +1016,242 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
705
1016
|
return acc
|
|
706
1017
|
}
|
|
707
1018
|
|
|
1019
|
+
/** No fixed child array is permitted to survive into a choice piece. */
|
|
1020
|
+
const NEVER_PIECE: Piece = () => FAIL
|
|
1021
|
+
const NEVER_CLASS: ResolvedClass = { ascii: new Uint8Array(128), hi: [] }
|
|
1022
|
+
const EMPTY_EXPECTED_PREFIX: ChoiceExpectedPrefix = (_target, _prev, acc) => acc
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* Four directly captured static expected sets. The chain keeps skipped-arm
|
|
1026
|
+
* diagnostics lazy across block boundaries: a later successful arm never
|
|
1027
|
+
* materialises expectations for earlier char-excluded arms.
|
|
1028
|
+
*/
|
|
1029
|
+
function expectedBlock(
|
|
1030
|
+
start: number,
|
|
1031
|
+
e0: readonly string[], e1: readonly string[], e2: readonly string[], e3: readonly string[],
|
|
1032
|
+
prior: ChoiceExpectedPrefix,
|
|
1033
|
+
): ChoiceExpectedPrefix {
|
|
1034
|
+
return (target, prev, acc) => {
|
|
1035
|
+
if (prev < start) { acc = prior(start, prev, acc); prev = start }
|
|
1036
|
+
if (target > start && prev < start + 1) acc = accSet(e0, acc)
|
|
1037
|
+
if (target > start + 1 && prev < start + 2) acc = accSet(e1, acc)
|
|
1038
|
+
if (target > start + 2 && prev < start + 3) acc = accSet(e2, acc)
|
|
1039
|
+
if (target > start + 3 && prev < start + 4) acc = accSet(e3, acc)
|
|
1040
|
+
return acc
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/** One four-arm ASCII-mask block, selected once when the arity is maskable. */
|
|
1045
|
+
function maskedChoiceBlock(
|
|
1046
|
+
start: number,
|
|
1047
|
+
a0: Piece, a1: Piece, a2: Piece, a3: Piece,
|
|
1048
|
+
through: ChoiceExpectedPrefix,
|
|
1049
|
+
next: MaskedChoiceBlock | undefined,
|
|
1050
|
+
total: number,
|
|
1051
|
+
choiceFx: string[],
|
|
1052
|
+
): MaskedChoiceBlock {
|
|
1053
|
+
return (input, pos, ctx, bits, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev, best) => {
|
|
1054
|
+
if ((bits & (1 << start)) !== 0) {
|
|
1055
|
+
ctx._fc = false
|
|
1056
|
+
const v = a0(input, pos, ctx)
|
|
1057
|
+
if (v !== FAIL) return v
|
|
1058
|
+
if (best === pos) acc = through(start, prev, acc)
|
|
1059
|
+
prev = start + 1
|
|
1060
|
+
const at = ctx._fe ?? pos
|
|
1061
|
+
if (at > best) { best = at; acc = undefined }
|
|
1062
|
+
if (at === best) acc = accSet(ctx._fx, acc)
|
|
1063
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1064
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1065
|
+
}
|
|
1066
|
+
if ((bits & (1 << (start + 1))) !== 0) {
|
|
1067
|
+
ctx._fc = false
|
|
1068
|
+
const v = a1(input, pos, ctx)
|
|
1069
|
+
if (v !== FAIL) return v
|
|
1070
|
+
if (best === pos) acc = through(start + 1, prev, acc)
|
|
1071
|
+
prev = start + 2
|
|
1072
|
+
const at = ctx._fe ?? pos
|
|
1073
|
+
if (at > best) { best = at; acc = undefined }
|
|
1074
|
+
if (at === best) acc = accSet(ctx._fx, acc)
|
|
1075
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1076
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1077
|
+
}
|
|
1078
|
+
if ((bits & (1 << (start + 2))) !== 0) {
|
|
1079
|
+
ctx._fc = false
|
|
1080
|
+
const v = a2(input, pos, ctx)
|
|
1081
|
+
if (v !== FAIL) return v
|
|
1082
|
+
if (best === pos) acc = through(start + 2, prev, acc)
|
|
1083
|
+
prev = start + 3
|
|
1084
|
+
const at = ctx._fe ?? pos
|
|
1085
|
+
if (at > best) { best = at; acc = undefined }
|
|
1086
|
+
if (at === best) acc = accSet(ctx._fx, acc)
|
|
1087
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1088
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1089
|
+
}
|
|
1090
|
+
if ((bits & (1 << (start + 3))) !== 0) {
|
|
1091
|
+
ctx._fc = false
|
|
1092
|
+
const v = a3(input, pos, ctx)
|
|
1093
|
+
if (v !== FAIL) return v
|
|
1094
|
+
if (best === pos) acc = through(start + 3, prev, acc)
|
|
1095
|
+
prev = start + 4
|
|
1096
|
+
const at = ctx._fe ?? pos
|
|
1097
|
+
if (at > best) { best = at; acc = undefined }
|
|
1098
|
+
if (at === best) 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, best)
|
|
1104
|
+
}
|
|
1105
|
+
if (best === pos) 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, best) => {
|
|
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
|
+
if (best === pos) acc = through(start, prev, acc)
|
|
1128
|
+
prev = start + 1
|
|
1129
|
+
const at = ctx._fe ?? pos
|
|
1130
|
+
if (at > best) { best = at; acc = undefined }
|
|
1131
|
+
if (at === best) acc = accSet(ctx._fx, acc)
|
|
1132
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1133
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1134
|
+
}
|
|
1135
|
+
if (g1 === null || classHas(g1, c)) {
|
|
1136
|
+
ctx._fc = false
|
|
1137
|
+
const v = a1(input, pos, ctx)
|
|
1138
|
+
if (v !== FAIL) return v
|
|
1139
|
+
if (best === pos) acc = through(start + 1, prev, acc)
|
|
1140
|
+
prev = start + 2
|
|
1141
|
+
const at = ctx._fe ?? pos
|
|
1142
|
+
if (at > best) { best = at; acc = undefined }
|
|
1143
|
+
if (at === best) acc = accSet(ctx._fx, acc)
|
|
1144
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1145
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1146
|
+
}
|
|
1147
|
+
if (g2 === null || classHas(g2, c)) {
|
|
1148
|
+
ctx._fc = false
|
|
1149
|
+
const v = a2(input, pos, ctx)
|
|
1150
|
+
if (v !== FAIL) return v
|
|
1151
|
+
if (best === pos) acc = through(start + 2, prev, acc)
|
|
1152
|
+
prev = start + 3
|
|
1153
|
+
const at = ctx._fe ?? pos
|
|
1154
|
+
if (at > best) { best = at; acc = undefined }
|
|
1155
|
+
if (at === best) 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 (g3 === null || classHas(g3, c)) {
|
|
1160
|
+
ctx._fc = false
|
|
1161
|
+
const v = a3(input, pos, ctx)
|
|
1162
|
+
if (v !== FAIL) return v
|
|
1163
|
+
if (best === pos) acc = through(start + 3, prev, acc)
|
|
1164
|
+
prev = start + 4
|
|
1165
|
+
const at = ctx._fe ?? pos
|
|
1166
|
+
if (at > best) { best = at; acc = undefined }
|
|
1167
|
+
if (at === best) acc = accSet(ctx._fx, acc)
|
|
1168
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1169
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1170
|
+
}
|
|
1171
|
+
if (next !== undefined) {
|
|
1172
|
+
return next(input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev, best)
|
|
1173
|
+
}
|
|
1174
|
+
if (best === pos) acc = through(total, prev, acc)
|
|
1175
|
+
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1176
|
+
return FAIL
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/** Four direct exclusive arms, chained for every arity above three. */
|
|
1181
|
+
function exclusiveChoiceBlock(
|
|
1182
|
+
start: number, a0: Piece, a1: Piece, a2: Piece, a3: Piece,
|
|
1183
|
+
next: ExclusiveChoiceBlock | undefined,
|
|
1184
|
+
): ExclusiveChoiceBlock {
|
|
1185
|
+
return (arm, input, pos, ctx) => {
|
|
1186
|
+
if (arm === start) return a0(input, pos, ctx)
|
|
1187
|
+
if (arm === start + 1) return a1(input, pos, ctx)
|
|
1188
|
+
if (arm === start + 2) return a2(input, pos, ctx)
|
|
1189
|
+
if (arm === start + 3) return a3(input, pos, ctx)
|
|
1190
|
+
return next === undefined ? FAIL : next(arm, input, pos, ctx)
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
function dispatchMatcherBlock(
|
|
1195
|
+
m0: DispatchMatcher, a0: number, m1: DispatchMatcher, a1: number,
|
|
1196
|
+
m2: DispatchMatcher, a2: number, m3: DispatchMatcher, a3: number,
|
|
1197
|
+
next: DispatchMatcherBlock | undefined,
|
|
1198
|
+
): DispatchMatcherBlock {
|
|
1199
|
+
return key => {
|
|
1200
|
+
if (m0(key)) return a0
|
|
1201
|
+
if (m1(key)) return a1
|
|
1202
|
+
if (m2(key)) return a2
|
|
1203
|
+
if (m3(key)) return a3
|
|
1204
|
+
return next?.(key)
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
function dispatchBranch(child: Piece, usesRouted: boolean): DispatchBranch {
|
|
1209
|
+
if (!usesRouted) {
|
|
1210
|
+
return (input, _pos, selEnd, key, _selectorMark, ctx) => {
|
|
1211
|
+
const mark = saveTriviaMark(ctx)
|
|
1212
|
+
const v = child(input, selEnd, ctx)
|
|
1213
|
+
if (v === FAIL) {
|
|
1214
|
+
rollbackTrivia(ctx, mark)
|
|
1215
|
+
ctx._fc = true
|
|
1216
|
+
return FAIL
|
|
1217
|
+
}
|
|
1218
|
+
return [key, v]
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
return (input, pos, selEnd, key, selectorMark, ctx) => {
|
|
1222
|
+
const savedRouted = ctx._routed
|
|
1223
|
+
rollbackTrivia(ctx, selectorMark)
|
|
1224
|
+
const mark = saveTriviaMark(ctx)
|
|
1225
|
+
ctx._routed = { value: key, span: { start: pos, end: selEnd } }
|
|
1226
|
+
let v: unknown
|
|
1227
|
+
try {
|
|
1228
|
+
v = child(input, pos, ctx)
|
|
1229
|
+
} finally {
|
|
1230
|
+
ctx._routed = savedRouted
|
|
1231
|
+
}
|
|
1232
|
+
if (v === FAIL) {
|
|
1233
|
+
rollbackTrivia(ctx, mark)
|
|
1234
|
+
ctx._fc = true
|
|
1235
|
+
return FAIL
|
|
1236
|
+
}
|
|
1237
|
+
return [key, v]
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
function dispatchArmBlock(
|
|
1242
|
+
start: number,
|
|
1243
|
+
a0: DispatchBranch, a1: DispatchBranch, a2: DispatchBranch, a3: DispatchBranch,
|
|
1244
|
+
next: DispatchArmBlock | undefined,
|
|
1245
|
+
): DispatchArmBlock {
|
|
1246
|
+
return (arm, input, pos, selEnd, key, selectorMark, ctx) => {
|
|
1247
|
+
if (arm === start) return a0(input, pos, selEnd, key, selectorMark, ctx)
|
|
1248
|
+
if (arm === start + 1) return a1(input, pos, selEnd, key, selectorMark, ctx)
|
|
1249
|
+
if (arm === start + 2) return a2(input, pos, selEnd, key, selectorMark, ctx)
|
|
1250
|
+
if (arm === start + 3) return a3(input, pos, selEnd, key, selectorMark, ctx)
|
|
1251
|
+
return next === undefined ? FAIL : next(arm, input, pos, selEnd, key, selectorMark, ctx)
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
|
|
708
1255
|
|
|
709
1256
|
|
|
710
1257
|
function trackLinesInto(ctx: ParseContext, input: string, end: number): void {
|
|
@@ -759,6 +1306,20 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
759
1306
|
const s = k[code[ip + 1]!] as string
|
|
760
1307
|
const len = s.length
|
|
761
1308
|
const xf = fx[code[ip + 2]!] as string[]
|
|
1309
|
+
const shared = rawScalarSpecs.has(code[ip + 1]!) ? scalarFor(ip) : undefined
|
|
1310
|
+
if (shared !== undefined) {
|
|
1311
|
+
return (input, pos, ctx) => {
|
|
1312
|
+
const e = shared(input, pos)
|
|
1313
|
+
if (e >= 0) {
|
|
1314
|
+
if (cstCaptureActive(ctx)) pushLeaf(ctx, s, pos, e)
|
|
1315
|
+
EC.e = e
|
|
1316
|
+
return s
|
|
1317
|
+
}
|
|
1318
|
+
ctx._fe = pos; ctx._fx = xf
|
|
1319
|
+
if (ctx._probe !== undefined) failAt(ctx, xf, pos)
|
|
1320
|
+
return FAIL
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
762
1323
|
// LENGTH IS TABLE DATA, so the COMPARE is chosen here rather than
|
|
763
1324
|
// delegated to a builtin that has to rediscover it. See `litBodyNote`.
|
|
764
1325
|
if (len === 1) {
|
|
@@ -884,6 +1445,21 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
884
1445
|
case OP_RX: {
|
|
885
1446
|
const re = k[code[ip + 1]!] as RegExp
|
|
886
1447
|
const xf = fx[code[ip + 2]!] as string[]
|
|
1448
|
+
const shared = rawScalarSpecs.has(code[ip + 1]!) ? scalarFor(ip) : undefined
|
|
1449
|
+
if (shared !== undefined) {
|
|
1450
|
+
return (input, pos, ctx) => {
|
|
1451
|
+
const e = shared(input, pos)
|
|
1452
|
+
if (e >= 0) {
|
|
1453
|
+
const v = input.slice(pos, e)
|
|
1454
|
+
if (cstCaptureActive(ctx)) pushLeaf(ctx, v, pos, e)
|
|
1455
|
+
EC.e = e
|
|
1456
|
+
return v
|
|
1457
|
+
}
|
|
1458
|
+
ctx._fe = pos; ctx._fx = xf
|
|
1459
|
+
if (ctx._probe !== undefined) failAt(ctx, xf, pos)
|
|
1460
|
+
return FAIL
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
887
1463
|
return (input, pos, ctx) => {
|
|
888
1464
|
re.lastIndex = pos
|
|
889
1465
|
// `regex()` rows are sticky and expose only their whole match. `exec()`
|
|
@@ -1307,6 +1883,21 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1307
1883
|
/* ── zero-width ──────────────────────────────────────────────────────── */
|
|
1308
1884
|
|
|
1309
1885
|
case OP_NOT: {
|
|
1886
|
+
const scalarChild = scalarTerminalNotChild(code, ip)
|
|
1887
|
+
if (scalarChild >= 0) {
|
|
1888
|
+
const recognize = scalarFor(scalarChild)!
|
|
1889
|
+
const xf = fx[code[ip + 2]!] as string[]
|
|
1890
|
+
return (input, pos, ctx) => {
|
|
1891
|
+
if (recognize(input, pos) < 0) {
|
|
1892
|
+
EC.e = pos
|
|
1893
|
+
return null
|
|
1894
|
+
}
|
|
1895
|
+
ctx._fe = pos
|
|
1896
|
+
ctx._fx = xf
|
|
1897
|
+
EC.e = pos
|
|
1898
|
+
return FAIL
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1310
1901
|
const child = link(code[ip + 1]!)
|
|
1311
1902
|
const xf = fx[code[ip + 2]!] as string[]
|
|
1312
1903
|
return (input, pos, ctx) => {
|
|
@@ -1420,6 +2011,93 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1420
2011
|
}
|
|
1421
2012
|
}
|
|
1422
2013
|
|
|
2014
|
+
case OP_LEX_BODY: {
|
|
2015
|
+
const recognize = t.lex[code[ip + 1]!]!
|
|
2016
|
+
const expected = fx[code[ip + 2]!] as string[]
|
|
2017
|
+
return (input, pos, ctx) => {
|
|
2018
|
+
const recognized = recognize(input, pos)
|
|
2019
|
+
if (recognized < 0) {
|
|
2020
|
+
ctx._fe = pos
|
|
2021
|
+
ctx._fx = expected as string[]
|
|
2022
|
+
if (ctx._probe !== undefined) failAt(ctx, expected, pos)
|
|
2023
|
+
return FAIL
|
|
2024
|
+
}
|
|
2025
|
+
const lineFlags = code[ip + 4]!
|
|
2026
|
+
const hasSuffix = (lineFlags & 4) !== 0
|
|
2027
|
+
const suffixMatched = hasSuffix && recognized % 2 === 1
|
|
2028
|
+
const end = (recognized - (suffixMatched ? 1 : 0)) / 2
|
|
2029
|
+
if ((lineFlags & 1) !== 0) {
|
|
2030
|
+
trackLinesInto(ctx, input, suffixMatched ? end - 1 : end)
|
|
2031
|
+
}
|
|
2032
|
+
if (hasSuffix) ctx._fc = false
|
|
2033
|
+
if (hasSuffix && suffixMatched && (lineFlags & 2) !== 0) {
|
|
2034
|
+
trackLinesInto(ctx, input, end)
|
|
2035
|
+
}
|
|
2036
|
+
if (hasSuffix && !suffixMatched) {
|
|
2037
|
+
const suffixExpected = fx[code[ip + 3]!] as string[]
|
|
2038
|
+
ctx._fe = end
|
|
2039
|
+
ctx._fx = suffixExpected
|
|
2040
|
+
if (ctx._probe !== undefined) failAt(ctx, suffixExpected, end)
|
|
2041
|
+
}
|
|
2042
|
+
const value = input.slice(pos, end)
|
|
2043
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
|
|
2044
|
+
EC.e = end
|
|
2045
|
+
return value
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
case OP_LEX_PROGRAM: {
|
|
2050
|
+
const run = t.lexPrograms[code[ip + 1]!]!
|
|
2051
|
+
const scanId = run.scan
|
|
2052
|
+
if (scanId !== undefined) {
|
|
2053
|
+
return (input, pos, ctx) => {
|
|
2054
|
+
const sTrivia = ctx.trivia, sKinds = ctx.triviaKindLabels
|
|
2055
|
+
const sBuf = ctx._cstBuf, sChildren = ctx._cstChildren, sLeaves = ctx._cstLeaves
|
|
2056
|
+
const sRaw = ctx._cstRawChildren, sTl = ctx._cstTriviaLog
|
|
2057
|
+
const sOuterTl = ctx._triviaLog, sRootTl = ctx._rootTriviaLog
|
|
2058
|
+
const wasCapturing = cstCaptureActive(ctx)
|
|
2059
|
+
const sScan = SCAN
|
|
2060
|
+
SCAN = null
|
|
2061
|
+
ctx.trivia = undefined
|
|
2062
|
+
ctx.triviaKindLabels = undefined
|
|
2063
|
+
ctx._cstBuf = undefined
|
|
2064
|
+
ctx._cstChildren = undefined
|
|
2065
|
+
ctx._cstLeaves = undefined
|
|
2066
|
+
ctx._cstRawChildren = undefined
|
|
2067
|
+
ctx._cstTriviaLog = undefined
|
|
2068
|
+
ctx._triviaLog = undefined
|
|
2069
|
+
ctx._rootTriviaLog = undefined
|
|
2070
|
+
let end: number
|
|
2071
|
+
try { end = run(input, pos, ctx, scans[scanId]) }
|
|
2072
|
+
finally {
|
|
2073
|
+
SCAN = sScan
|
|
2074
|
+
ctx.trivia = sTrivia
|
|
2075
|
+
ctx.triviaKindLabels = sKinds
|
|
2076
|
+
ctx._cstBuf = sBuf
|
|
2077
|
+
ctx._cstChildren = sChildren
|
|
2078
|
+
ctx._cstLeaves = sLeaves
|
|
2079
|
+
ctx._cstRawChildren = sRaw
|
|
2080
|
+
ctx._cstTriviaLog = sTl
|
|
2081
|
+
ctx._triviaLog = sOuterTl
|
|
2082
|
+
ctx._rootTriviaLog = sRootTl
|
|
2083
|
+
}
|
|
2084
|
+
if (end < 0) return FAIL
|
|
2085
|
+
const value = input.slice(pos, end)
|
|
2086
|
+
if (wasCapturing) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
|
|
2087
|
+
EC.e = end
|
|
2088
|
+
return value
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
return (input, pos, ctx) => {
|
|
2092
|
+
const end = run(input, pos, ctx)
|
|
2093
|
+
if (end < 0) return FAIL
|
|
2094
|
+
const value = input.slice(pos, end)
|
|
2095
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
|
|
2096
|
+
EC.e = end
|
|
2097
|
+
return value
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
|
|
1423
2101
|
case OP_LEAF: {
|
|
1424
2102
|
const fn = fns[code[ip + 1]!] as (value: unknown, span: { start: number; end: number }) => unknown
|
|
1425
2103
|
const child = link(code[ip + 2]!)
|
|
@@ -1464,36 +2142,60 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1464
2142
|
const fused = op === OP_SEQX
|
|
1465
2143
|
const base = fused ? ip + 3 : ip + 2
|
|
1466
2144
|
const n = code[fused ? ip + 2 : ip + 1]!
|
|
1467
|
-
// Children bound as a plain array of DIRECT references, laid out once.
|
|
1468
|
-
const kids: Piece[] = new Array<Piece>(n)
|
|
1469
|
-
for (let i = 0; i < n; i++) kids[i] = link(code[base + i]!)
|
|
1470
2145
|
const wantValues = op !== OP_SEQV
|
|
1471
|
-
const
|
|
1472
|
-
|
|
2146
|
+
const reducer = fused ? code[ip + 1]! : -1
|
|
2147
|
+
const projection = reducer < 0 ? ~reducer : -1
|
|
2148
|
+
const fn = fused && projection < 0
|
|
2149
|
+
? fns[reducer] as (value: unknown, span: { start: number; end: number }) => unknown
|
|
1473
2150
|
: undefined
|
|
1474
2151
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
if (v0 === FAIL) return -1
|
|
1489
|
-
if (values !== undefined) values.push(v0)
|
|
1490
|
-
let cur = EC.e
|
|
1491
|
-
for (let i = 1; i < n; i++) {
|
|
1492
|
-
cur = nextTerm(kids[i]!, input, cur, ctx)
|
|
2152
|
+
const runnerBlock = (
|
|
2153
|
+
count: number,
|
|
2154
|
+
r0: TermRunner, r1: TermRunner, r2: TermRunner, r3: TermRunner,
|
|
2155
|
+
next: SequenceTermBlock | undefined,
|
|
2156
|
+
): SequenceTermBlock => {
|
|
2157
|
+
if (count === 1) return (input, cur, ctx, values, inherited) => {
|
|
2158
|
+
cur = r0(input, cur, ctx, inherited)
|
|
2159
|
+
if (cur < 0) return -1
|
|
2160
|
+
if (values !== undefined) values.push(TERMV)
|
|
2161
|
+
return next === undefined ? cur : next(input, cur, ctx, values, inherited)
|
|
2162
|
+
}
|
|
2163
|
+
if (count === 2) return (input, cur, ctx, values, inherited) => {
|
|
2164
|
+
cur = r0(input, cur, ctx, inherited)
|
|
1493
2165
|
if (cur < 0) return -1
|
|
1494
2166
|
if (values !== undefined) values.push(TERMV)
|
|
2167
|
+
cur = r1(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
|
+
if (count === 3) 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
|
+
return next === undefined ? cur : next(input, cur, ctx, values, inherited)
|
|
2183
|
+
}
|
|
2184
|
+
return (input, cur, ctx, values, inherited) => {
|
|
2185
|
+
cur = r0(input, cur, ctx, inherited)
|
|
2186
|
+
if (cur < 0) return -1
|
|
2187
|
+
if (values !== undefined) values.push(TERMV)
|
|
2188
|
+
cur = r1(input, cur, ctx, inherited)
|
|
2189
|
+
if (cur < 0) return -1
|
|
2190
|
+
if (values !== undefined) values.push(TERMV)
|
|
2191
|
+
cur = r2(input, cur, ctx, inherited)
|
|
2192
|
+
if (cur < 0) return -1
|
|
2193
|
+
if (values !== undefined) values.push(TERMV)
|
|
2194
|
+
cur = r3(input, cur, ctx, inherited)
|
|
2195
|
+
if (cur < 0) return -1
|
|
2196
|
+
if (values !== undefined) values.push(TERMV)
|
|
2197
|
+
return next === undefined ? cur : next(input, cur, ctx, values, inherited)
|
|
1495
2198
|
}
|
|
1496
|
-
return cur
|
|
1497
2199
|
}
|
|
1498
2200
|
|
|
1499
2201
|
/**
|
|
@@ -1544,21 +2246,24 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1544
2246
|
* sentinel published is the kind of state a later reader will trust.
|
|
1545
2247
|
*/
|
|
1546
2248
|
if (REC) {
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
const
|
|
1550
|
-
|
|
2249
|
+
const first = link(code[base]!)
|
|
2250
|
+
const firstSync = sentinelFor(code[base + n]!)
|
|
2251
|
+
const runnerAt = (i: number): TermRunner => {
|
|
2252
|
+
const sync = sentinelFor(code[base + n + i]!)
|
|
1551
2253
|
const kidIp = code[base + i]!
|
|
1552
2254
|
if (code[kidIp] !== OP_ADJ) {
|
|
1553
|
-
const kid =
|
|
1554
|
-
|
|
1555
|
-
|
|
2255
|
+
const kid = link(kidIp)
|
|
2256
|
+
return (input, cur, ctx, inherited) => {
|
|
2257
|
+
ctx._sync = sync ?? inherited
|
|
2258
|
+
return nextTerm(kid, input, cur, ctx)
|
|
2259
|
+
}
|
|
1556
2260
|
}
|
|
1557
2261
|
const negated = code[kidIp + 1] === 1
|
|
1558
2262
|
const ki = code[kidIp + 2]!
|
|
1559
2263
|
const kinds = ki < 0 ? undefined : k[ki] as readonly string[]
|
|
1560
2264
|
const xf = fx[code[kidIp + 3]!] as string[]
|
|
1561
|
-
|
|
2265
|
+
return (input, cur, ctx, inherited) => {
|
|
2266
|
+
ctx._sync = sync ?? inherited
|
|
1562
2267
|
if (!adjacencyHolds(input, cur, ctx, negated, kinds)) {
|
|
1563
2268
|
ctx._fe = cur; ctx._fx = xf
|
|
1564
2269
|
return -1
|
|
@@ -1567,19 +2272,25 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1567
2272
|
return cur
|
|
1568
2273
|
}
|
|
1569
2274
|
}
|
|
2275
|
+
const bindRunners = (start: number): SequenceTermBlock => {
|
|
2276
|
+
const count = Math.min(4, n - start)
|
|
2277
|
+
const at = (offset: number): TermRunner => runnerAt(start + offset)
|
|
2278
|
+
const never: TermRunner = () => -1
|
|
2279
|
+
return runnerBlock(
|
|
2280
|
+
count, at(0), count > 1 ? at(1) : never,
|
|
2281
|
+
count > 2 ? at(2) : never, count > 3 ? at(3) : never,
|
|
2282
|
+
start + count < n ? bindRunners(start + count) : undefined,
|
|
2283
|
+
)
|
|
2284
|
+
}
|
|
2285
|
+
const tail = n > 1 ? bindRunners(1) : undefined
|
|
1570
2286
|
const runSyncTerms = (input: string, pos: number, ctx: ParseContext, values: unknown[] | undefined): number => {
|
|
1571
2287
|
const inherited = ctx._sync
|
|
1572
|
-
ctx._sync =
|
|
1573
|
-
const v0 =
|
|
2288
|
+
ctx._sync = firstSync ?? inherited
|
|
2289
|
+
const v0 = first(input, pos, ctx)
|
|
1574
2290
|
if (v0 === FAIL) { ctx._sync = inherited; return -1 }
|
|
1575
2291
|
if (values !== undefined) values.push(v0)
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
ctx._sync = syncs[i] ?? inherited
|
|
1579
|
-
cur = runners[i]!(input, cur, ctx)
|
|
1580
|
-
if (cur < 0) { ctx._sync = inherited; return -1 }
|
|
1581
|
-
if (values !== undefined) values.push(TERMV)
|
|
1582
|
-
}
|
|
2292
|
+
const cur = tail === undefined ? EC.e : tail(input, EC.e, ctx, values, inherited)
|
|
2293
|
+
if (cur < 0) { ctx._sync = inherited; return -1 }
|
|
1583
2294
|
ctx._sync = inherited
|
|
1584
2295
|
return cur
|
|
1585
2296
|
}
|
|
@@ -1589,7 +2300,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1589
2300
|
const cur = runSyncTerms(input, pos, ctx, values)
|
|
1590
2301
|
if (cur < 0) return FAIL
|
|
1591
2302
|
EC.e = cur
|
|
1592
|
-
return fn!(values, { start: pos, end: cur })
|
|
2303
|
+
return projection >= 0 ? values[projection] : fn!(values, { start: pos, end: cur })
|
|
1593
2304
|
}
|
|
1594
2305
|
}
|
|
1595
2306
|
if (wantValues) {
|
|
@@ -1612,13 +2323,12 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1612
2323
|
let hasAdj = false
|
|
1613
2324
|
for (let i = 1; i < n; i++) if (code[code[base + i]!] === OP_ADJ) { hasAdj = true; break }
|
|
1614
2325
|
if (hasAdj) {
|
|
1615
|
-
const
|
|
1616
|
-
|
|
2326
|
+
const first = link(code[base]!)
|
|
2327
|
+
const runnerAt = (i: number): TermRunner => {
|
|
1617
2328
|
const kidIp = code[base + i]!
|
|
1618
2329
|
if (code[kidIp] !== OP_ADJ) {
|
|
1619
|
-
const kid =
|
|
1620
|
-
|
|
1621
|
-
continue
|
|
2330
|
+
const kid = link(kidIp)
|
|
2331
|
+
return (input, cur, ctx) => nextTerm(kid, input, cur, ctx)
|
|
1622
2332
|
}
|
|
1623
2333
|
const negated = code[kidIp + 1] === 1
|
|
1624
2334
|
const ki = code[kidIp + 2]!
|
|
@@ -1628,7 +2338,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1628
2338
|
// the interpreter does.
|
|
1629
2339
|
const kinds = ki < 0 ? undefined : k[ki] as readonly string[]
|
|
1630
2340
|
const xf = fx[code[kidIp + 3]!] as string[]
|
|
1631
|
-
|
|
2341
|
+
return (input, cur, ctx) => {
|
|
1632
2342
|
if (!adjacencyHolds(input, cur, ctx, negated, kinds)) {
|
|
1633
2343
|
ctx._fe = cur; ctx._fx = xf
|
|
1634
2344
|
return -1
|
|
@@ -1637,17 +2347,22 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1637
2347
|
return cur
|
|
1638
2348
|
}
|
|
1639
2349
|
}
|
|
2350
|
+
const bindRunners = (start: number): SequenceTermBlock => {
|
|
2351
|
+
const count = Math.min(4, n - start)
|
|
2352
|
+
const at = (offset: number): TermRunner => runnerAt(start + offset)
|
|
2353
|
+
const never: TermRunner = () => -1
|
|
2354
|
+
return runnerBlock(
|
|
2355
|
+
count, at(0), count > 1 ? at(1) : never,
|
|
2356
|
+
count > 2 ? at(2) : never, count > 3 ? at(3) : never,
|
|
2357
|
+
start + count < n ? bindRunners(start + count) : undefined,
|
|
2358
|
+
)
|
|
2359
|
+
}
|
|
2360
|
+
const tail = bindRunners(1)
|
|
1640
2361
|
const runAdjTerms = (input: string, pos: number, ctx: ParseContext, values: unknown[] | undefined): number => {
|
|
1641
|
-
const v0 =
|
|
2362
|
+
const v0 = first(input, pos, ctx)
|
|
1642
2363
|
if (v0 === FAIL) return -1
|
|
1643
2364
|
if (values !== undefined) values.push(v0)
|
|
1644
|
-
|
|
1645
|
-
for (let i = 1; i < n; i++) {
|
|
1646
|
-
cur = runners[i]!(input, cur, ctx)
|
|
1647
|
-
if (cur < 0) return -1
|
|
1648
|
-
if (values !== undefined) values.push(TERMV)
|
|
1649
|
-
}
|
|
1650
|
-
return cur
|
|
2365
|
+
return tail(input, EC.e, ctx, values)
|
|
1651
2366
|
}
|
|
1652
2367
|
if (fused) {
|
|
1653
2368
|
return (input, pos, ctx) => {
|
|
@@ -1655,7 +2370,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1655
2370
|
const cur = runAdjTerms(input, pos, ctx, values)
|
|
1656
2371
|
if (cur < 0) return FAIL
|
|
1657
2372
|
EC.e = cur
|
|
1658
|
-
return fn!(values, { start: pos, end: cur })
|
|
2373
|
+
return projection >= 0 ? values[projection] : fn!(values, { start: pos, end: cur })
|
|
1659
2374
|
}
|
|
1660
2375
|
}
|
|
1661
2376
|
if (wantValues) {
|
|
@@ -1693,12 +2408,12 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1693
2408
|
* runs, so there is no arity this fails to lower.
|
|
1694
2409
|
*/
|
|
1695
2410
|
if (n === 1) {
|
|
1696
|
-
const k0 =
|
|
2411
|
+
const k0 = link(code[base]!)
|
|
1697
2412
|
if (fused) {
|
|
1698
2413
|
return (input, pos, ctx) => {
|
|
1699
2414
|
const v = k0(input, pos, ctx)
|
|
1700
2415
|
if (v === FAIL) return FAIL
|
|
1701
|
-
return fn!([v], { start: pos, end: EC.e })
|
|
2416
|
+
return projection === 0 ? v : fn!([v], { start: pos, end: EC.e })
|
|
1702
2417
|
}
|
|
1703
2418
|
}
|
|
1704
2419
|
if (wantValues) {
|
|
@@ -1716,7 +2431,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1716
2431
|
}
|
|
1717
2432
|
|
|
1718
2433
|
if (n === 2) {
|
|
1719
|
-
const k0 =
|
|
2434
|
+
const k0 = link(code[base]!), k1 = link(code[base + 1]!)
|
|
1720
2435
|
if (fused) {
|
|
1721
2436
|
return (input, pos, ctx) => {
|
|
1722
2437
|
const v0 = k0(input, pos, ctx)
|
|
@@ -1724,7 +2439,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1724
2439
|
const cur = nextTerm(k1, input, EC.e, ctx)
|
|
1725
2440
|
if (cur < 0) return FAIL
|
|
1726
2441
|
EC.e = cur
|
|
1727
|
-
return
|
|
2442
|
+
return projection === 0 ? v0
|
|
2443
|
+
: projection === 1 ? TERMV
|
|
2444
|
+
: fn!([v0, TERMV], { start: pos, end: cur })
|
|
1728
2445
|
}
|
|
1729
2446
|
}
|
|
1730
2447
|
if (wantValues) {
|
|
@@ -1748,7 +2465,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1748
2465
|
}
|
|
1749
2466
|
|
|
1750
2467
|
if (n === 3) {
|
|
1751
|
-
const k0 =
|
|
2468
|
+
const k0 = link(code[base]!), k1 = link(code[base + 1]!), k2 = link(code[base + 2]!)
|
|
1752
2469
|
if (fused) {
|
|
1753
2470
|
return (input, pos, ctx) => {
|
|
1754
2471
|
const v0 = k0(input, pos, ctx)
|
|
@@ -1759,7 +2476,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1759
2476
|
cur = nextTerm(k2, input, cur, ctx)
|
|
1760
2477
|
if (cur < 0) return FAIL
|
|
1761
2478
|
EC.e = cur
|
|
1762
|
-
return
|
|
2479
|
+
return projection === 0 ? v0
|
|
2480
|
+
: projection === 1 ? v1
|
|
2481
|
+
: projection === 2 ? TERMV
|
|
2482
|
+
: fn!([v0, v1, TERMV], { start: pos, end: cur })
|
|
1763
2483
|
}
|
|
1764
2484
|
}
|
|
1765
2485
|
if (wantValues) {
|
|
@@ -1787,26 +2507,97 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1787
2507
|
}
|
|
1788
2508
|
}
|
|
1789
2509
|
|
|
2510
|
+
// Arbitrary strict arities use a bounded scalar block chain. Fixed
|
|
2511
|
+
// children are captured into prewritten four-term blocks at assembly;
|
|
2512
|
+
// the parse path has no `kids[i]`, fixed-child loop, or opcode lookup.
|
|
2513
|
+
const block = (
|
|
2514
|
+
count: number,
|
|
2515
|
+
k0: Piece, k1: Piece, k2: Piece, k3: Piece,
|
|
2516
|
+
next: SequenceTermBlock | undefined,
|
|
2517
|
+
): SequenceTermBlock => {
|
|
2518
|
+
if (count === 1) return (input, cur, ctx, values) => {
|
|
2519
|
+
cur = nextTerm(k0, input, cur, ctx)
|
|
2520
|
+
if (cur < 0) return -1
|
|
2521
|
+
if (values !== undefined) values.push(TERMV)
|
|
2522
|
+
return next === undefined ? cur : next(input, cur, ctx, values)
|
|
2523
|
+
}
|
|
2524
|
+
if (count === 2) return (input, cur, ctx, values) => {
|
|
2525
|
+
cur = nextTerm(k0, input, cur, ctx)
|
|
2526
|
+
if (cur < 0) return -1
|
|
2527
|
+
if (values !== undefined) values.push(TERMV)
|
|
2528
|
+
cur = nextTerm(k1, 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
|
+
if (count === 3) 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
|
+
return next === undefined ? cur : next(input, cur, ctx, values)
|
|
2544
|
+
}
|
|
2545
|
+
return (input, cur, ctx, values) => {
|
|
2546
|
+
cur = nextTerm(k0, input, cur, ctx)
|
|
2547
|
+
if (cur < 0) return -1
|
|
2548
|
+
if (values !== undefined) values.push(TERMV)
|
|
2549
|
+
cur = nextTerm(k1, input, cur, ctx)
|
|
2550
|
+
if (cur < 0) return -1
|
|
2551
|
+
if (values !== undefined) values.push(TERMV)
|
|
2552
|
+
cur = nextTerm(k2, input, cur, ctx)
|
|
2553
|
+
if (cur < 0) return -1
|
|
2554
|
+
if (values !== undefined) values.push(TERMV)
|
|
2555
|
+
cur = nextTerm(k3, input, cur, ctx)
|
|
2556
|
+
if (cur < 0) return -1
|
|
2557
|
+
if (values !== undefined) values.push(TERMV)
|
|
2558
|
+
return next === undefined ? cur : next(input, cur, ctx, values)
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
const bind = (start: number): SequenceTermBlock => {
|
|
2562
|
+
const count = Math.min(4, n - start)
|
|
2563
|
+
const child = (offset: number): Piece => link(code[base + start + offset]!)
|
|
2564
|
+
return block(
|
|
2565
|
+
count, child(0), count > 1 ? child(1) : NEVER_PIECE,
|
|
2566
|
+
count > 2 ? child(2) : NEVER_PIECE, count > 3 ? child(3) : NEVER_PIECE,
|
|
2567
|
+
start + count < n ? bind(start + count) : undefined,
|
|
2568
|
+
)
|
|
2569
|
+
}
|
|
2570
|
+
const first = link(code[base]!)
|
|
2571
|
+
const tail = bind(1)
|
|
2572
|
+
const runDirectTerms = (
|
|
2573
|
+
input: string, pos: number, ctx: ParseContext, values: unknown[] | undefined,
|
|
2574
|
+
): number => {
|
|
2575
|
+
const v0 = first(input, pos, ctx)
|
|
2576
|
+
if (v0 === FAIL) return -1
|
|
2577
|
+
if (values !== undefined) values.push(v0)
|
|
2578
|
+
return tail(input, EC.e, ctx, values)
|
|
2579
|
+
}
|
|
2580
|
+
|
|
1790
2581
|
if (fused) {
|
|
1791
2582
|
return (input, pos, ctx) => {
|
|
1792
2583
|
const values: unknown[] = []
|
|
1793
|
-
const cur =
|
|
2584
|
+
const cur = runDirectTerms(input, pos, ctx, values)
|
|
1794
2585
|
if (cur < 0) return FAIL
|
|
1795
2586
|
EC.e = cur
|
|
1796
|
-
return fn!(values, { start: pos, end: cur })
|
|
2587
|
+
return projection >= 0 ? values[projection] : fn!(values, { start: pos, end: cur })
|
|
1797
2588
|
}
|
|
1798
2589
|
}
|
|
1799
2590
|
if (wantValues) {
|
|
1800
2591
|
return (input, pos, ctx) => {
|
|
1801
2592
|
const values: unknown[] = []
|
|
1802
|
-
const cur =
|
|
2593
|
+
const cur = runDirectTerms(input, pos, ctx, values)
|
|
1803
2594
|
if (cur < 0) return FAIL
|
|
1804
2595
|
EC.e = cur
|
|
1805
2596
|
return values
|
|
1806
2597
|
}
|
|
1807
2598
|
}
|
|
1808
2599
|
return (input, pos, ctx) => {
|
|
1809
|
-
const cur =
|
|
2600
|
+
const cur = runDirectTerms(input, pos, ctx, undefined)
|
|
1810
2601
|
if (cur < 0) return FAIL
|
|
1811
2602
|
EC.e = cur
|
|
1812
2603
|
return undefined
|
|
@@ -1820,20 +2611,74 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1820
2611
|
const n = code[ip + 2]!
|
|
1821
2612
|
const choiceFx = fx[code[ip + 3]!] as string[]
|
|
1822
2613
|
const base = ip + 4
|
|
1823
|
-
const arms: Piece[] = new Array<Piece>(n)
|
|
1824
|
-
for (let i = 0; i < n; i++) arms[i] = link(code[base + i]!)
|
|
1825
|
-
// PER-ARM STATIC SETS, resolved once at assembly. An arm the char gate
|
|
1826
|
-
// declines to enter still owes its expectation: the interpreter has no
|
|
1827
|
-
// such gate, enters the arm, and its opener's failure is what lands in
|
|
1828
|
-
// the accumulation.
|
|
1829
|
-
const armFx: (string[])[] = new Array<string[]>(n)
|
|
1830
|
-
for (let i = 0; i < n; i++) armFx[i] = fx[code[base + n + i]!] as string[]
|
|
1831
2614
|
|
|
1832
2615
|
// THE TWO CHOICE SHAPES ARE TWO PIECES. `exec.ts` tested `table.exclusive`
|
|
1833
2616
|
// on every choice execution; it is table data, so it selects here.
|
|
1834
2617
|
if (table.exclusive) {
|
|
1835
2618
|
const ascii = table.ascii
|
|
1836
2619
|
const hi = table.hi
|
|
2620
|
+
// THE COMMON ARITIES OWN THEIR TOPOLOGY AS SCALARS. Do not build an
|
|
2621
|
+
// `arms[]` merely to rediscover the same two or three children at
|
|
2622
|
+
// every dispatch. The classifier remains table data; only its result
|
|
2623
|
+
// is turned into a direct captured call.
|
|
2624
|
+
if (n === 2) {
|
|
2625
|
+
const a0 = link(code[base]!), a1 = link(code[base + 1]!)
|
|
2626
|
+
return (input, pos, ctx) => {
|
|
2627
|
+
const c = lead(input, pos)
|
|
2628
|
+
let arm = -1
|
|
2629
|
+
if (c >= 0 && c < 128) {
|
|
2630
|
+
const a = ascii[c]!
|
|
2631
|
+
if (a !== 0) arm = a - 1
|
|
2632
|
+
} else if (c >= 128) {
|
|
2633
|
+
for (let i = 0; i < hi.length; i += 3) {
|
|
2634
|
+
if (c >= hi[i]! && c <= hi[i + 1]!) { arm = hi[i + 2]!; break }
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
if (arm >= 0) {
|
|
2638
|
+
ctx._fc = false
|
|
2639
|
+
const v = arm === 0 ? a0(input, pos, ctx) : a1(input, pos, ctx)
|
|
2640
|
+
if (v !== FAIL) return v
|
|
2641
|
+
if (committed(ctx)) return FAIL
|
|
2642
|
+
const failed = ctx._fx
|
|
2643
|
+
if (failed !== undefined && failed.length > 0) { ctx._fe = pos; return FAIL }
|
|
2644
|
+
}
|
|
2645
|
+
ctx._fe = pos; ctx._fx = choiceFx
|
|
2646
|
+
return FAIL
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
if (n === 3) {
|
|
2650
|
+
const a0 = link(code[base]!), a1 = link(code[base + 1]!), a2 = link(code[base + 2]!)
|
|
2651
|
+
return (input, pos, ctx) => {
|
|
2652
|
+
const c = lead(input, pos)
|
|
2653
|
+
let arm = -1
|
|
2654
|
+
if (c >= 0 && c < 128) {
|
|
2655
|
+
const a = ascii[c]!
|
|
2656
|
+
if (a !== 0) arm = a - 1
|
|
2657
|
+
} else if (c >= 128) {
|
|
2658
|
+
for (let i = 0; i < hi.length; i += 3) {
|
|
2659
|
+
if (c >= hi[i]! && c <= hi[i + 1]!) { arm = hi[i + 2]!; break }
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
if (arm >= 0) {
|
|
2663
|
+
ctx._fc = false
|
|
2664
|
+
const v = arm === 0 ? a0(input, pos, ctx) : arm === 1 ? a1(input, pos, ctx) : a2(input, pos, ctx)
|
|
2665
|
+
if (v !== FAIL) return v
|
|
2666
|
+
if (committed(ctx)) return FAIL
|
|
2667
|
+
const failed = ctx._fx
|
|
2668
|
+
if (failed !== undefined && failed.length > 0) { ctx._fe = pos; return FAIL }
|
|
2669
|
+
}
|
|
2670
|
+
ctx._fe = pos; ctx._fx = choiceFx
|
|
2671
|
+
return FAIL
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
const bindExclusive = (start: number): ExclusiveChoiceBlock => {
|
|
2675
|
+
const at = (i: number): Piece => i < n ? link(code[base + i]!) : NEVER_PIECE
|
|
2676
|
+
const next = start + 4 < n ? bindExclusive(start + 4) : undefined
|
|
2677
|
+
return exclusiveChoiceBlock(
|
|
2678
|
+
start, at(start), at(start + 1), at(start + 2), at(start + 3), next,
|
|
2679
|
+
)
|
|
2680
|
+
}
|
|
2681
|
+
const selected = bindExclusive(0)
|
|
1837
2682
|
// THERE ARE NO OPEN ARMS HERE, and the piece is written to say so. An
|
|
1838
2683
|
// ungated arm is one whose class is −1, and `resolveDispatch` clears
|
|
1839
2684
|
// `exclusive` for exactly those arms (program.ts) — so `table.open` is
|
|
@@ -1852,15 +2697,13 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1852
2697
|
}
|
|
1853
2698
|
if (arm >= 0) {
|
|
1854
2699
|
ctx._fc = false
|
|
1855
|
-
const v =
|
|
2700
|
+
const v = selected(arm, input, pos, ctx)
|
|
1856
2701
|
if (v !== FAIL) return v
|
|
1857
2702
|
// THE CUT — a committed failure fails the whole choice.
|
|
1858
2703
|
if (committed(ctx)) return FAIL
|
|
1859
2704
|
// THE DISPATCHED ARM'S OWN SET IS THE ANSWER, at the choice's own
|
|
1860
|
-
// position
|
|
1861
|
-
//
|
|
1862
|
-
// declining to overwrite it. See `exec.ts` for why this is NOT
|
|
1863
|
-
// furthest-failure merging and what the residual JSON gap is.
|
|
2705
|
+
// position. `_fx` already holds it; reporting the sole selected arm
|
|
2706
|
+
// is declining to overwrite it.
|
|
1864
2707
|
const armFx = ctx._fx
|
|
1865
2708
|
if (armFx !== undefined && armFx.length > 0) { ctx._fe = pos; return FAIL }
|
|
1866
2709
|
}
|
|
@@ -1876,48 +2719,25 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1876
2719
|
// excludes the char at `pos`. `null` means nullable or unmappable, and
|
|
1877
2720
|
// those arms are always entered.
|
|
1878
2721
|
const armCls = table.armCls
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
* CHARACTER and the TABLE, both of which are known here, so the whole
|
|
1890
|
-
* question is answered once at assembly: `cand[c]` is the bitmask of arms
|
|
1891
|
-
* that could match `c`, and the loop visits only set bits, IN ORDER, so
|
|
1892
|
-
* the source ordering the gate exists to preserve is preserved exactly.
|
|
1893
|
-
*
|
|
1894
|
-
* Slot 128 is EOF (`lead` returns −1): `classHas` rejects a negative code,
|
|
1895
|
-
* so only the always-enter arms are candidates there. Code points ≥ 128
|
|
1896
|
-
* keep the per-arm loop — the ranges make a table the wrong shape and
|
|
1897
|
-
* astral input is not the hot path.
|
|
1898
|
-
*
|
|
1899
|
-
* Bounded at 32 arms because the mask is one word. Above that the general
|
|
1900
|
-
* loop runs, which is the same code the ≥ 128 path takes, so there is no
|
|
1901
|
-
* second implementation to drift.
|
|
1902
|
-
*/
|
|
1903
|
-
const maskable = n <= 32
|
|
1904
|
-
let cand: Uint32Array | undefined
|
|
1905
|
-
if (maskable) {
|
|
1906
|
-
const m = new Uint32Array(129)
|
|
1907
|
-
for (let i = 0; i < n; i++) {
|
|
1908
|
-
const cls = gates[i]!
|
|
1909
|
-
const bit = 1 << i
|
|
2722
|
+
if (n === 2 || n === 3) {
|
|
2723
|
+
const a0 = link(code[base]!), a1 = link(code[base + 1]!)
|
|
2724
|
+
const a2 = n === 3 ? link(code[base + 2]!) : undefined
|
|
2725
|
+
const e0 = fx[code[base + n]!] as string[]
|
|
2726
|
+
const e1 = fx[code[base + n + 1]!] as string[]
|
|
2727
|
+
const e2 = n === 3 ? fx[code[base + n + 2]!] as string[] : undefined
|
|
2728
|
+
const g0 = armCls[0] ?? null, g1 = armCls[1] ?? null
|
|
2729
|
+
const g2 = n === 3 ? armCls[2] ?? null : null
|
|
2730
|
+
const mask = new Uint32Array(129)
|
|
2731
|
+
const addGate = (cls: ResolvedClass | null, bit: number): void => {
|
|
1910
2732
|
if (cls === null) {
|
|
1911
|
-
for (let c = 0; c < 129; c++)
|
|
1912
|
-
|
|
2733
|
+
for (let c = 0; c < 129; c++) mask[c]! |= bit
|
|
2734
|
+
return
|
|
1913
2735
|
}
|
|
1914
|
-
for (let c = 0; c < 128; c++) if (cls.ascii[c] === 1)
|
|
2736
|
+
for (let c = 0; c < 128; c++) if (cls.ascii[c] === 1) mask[c]! |= bit
|
|
1915
2737
|
}
|
|
1916
|
-
|
|
1917
|
-
|
|
2738
|
+
addGate(g0, 1); addGate(g1, 2)
|
|
2739
|
+
if (n === 3) addGate(g2, 4)
|
|
1918
2740
|
|
|
1919
|
-
if (cand !== undefined) {
|
|
1920
|
-
const mask = cand
|
|
1921
2741
|
return (input, pos, ctx) => {
|
|
1922
2742
|
const c = lead(input, pos)
|
|
1923
2743
|
const need = markCst(ctx)
|
|
@@ -1929,47 +2749,158 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1929
2749
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
1930
2750
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
1931
2751
|
let acc: string[] | undefined
|
|
2752
|
+
let best = pos
|
|
1932
2753
|
if (c < 128) {
|
|
1933
|
-
|
|
1934
|
-
const bits0 = mask[c < 0 ? 128 : c]!
|
|
1935
|
-
let bits = bits0
|
|
1936
|
-
// Arms below `prev` have already contributed. Only the FAILURE path
|
|
1937
|
-
// advances it, so a matching arm returns without paying for any of
|
|
1938
|
-
// this — the skipped arms' sets are read on no other exit.
|
|
2754
|
+
const bits = mask[c < 0 ? 128 : c]!
|
|
1939
2755
|
let prev = 0
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
2756
|
+
if ((bits & 1) !== 0) {
|
|
2757
|
+
ctx._fc = false
|
|
2758
|
+
const v = a0(input, pos, ctx)
|
|
2759
|
+
if (v !== FAIL) return v
|
|
2760
|
+
prev = 1
|
|
2761
|
+
const at = ctx._fe ?? pos
|
|
2762
|
+
if (at > best) { best = at; acc = undefined }
|
|
2763
|
+
if (at === best) acc = accSet(ctx._fx, acc)
|
|
2764
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
2765
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2766
|
+
}
|
|
2767
|
+
if ((bits & 2) !== 0) {
|
|
2768
|
+
ctx._fc = false
|
|
2769
|
+
const v = a1(input, pos, ctx)
|
|
2770
|
+
if (v !== FAIL) return v
|
|
2771
|
+
if (best === pos && prev < 1) acc = accSet(e0, acc)
|
|
2772
|
+
prev = 2
|
|
2773
|
+
const at = ctx._fe ?? pos
|
|
2774
|
+
if (at > best) { best = at; acc = undefined }
|
|
2775
|
+
if (at === best) acc = accSet(ctx._fx, acc)
|
|
2776
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
2777
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2778
|
+
}
|
|
2779
|
+
if (n === 3 && (bits & 4) !== 0) {
|
|
1943
2780
|
ctx._fc = false
|
|
1944
|
-
const v =
|
|
2781
|
+
const v = a2!(input, pos, ctx)
|
|
1945
2782
|
if (v !== FAIL) return v
|
|
1946
|
-
|
|
1947
|
-
prev =
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
2783
|
+
if (best === pos && prev < 1) acc = accSet(e0, acc)
|
|
2784
|
+
if (best === pos && prev < 2) acc = accSet(e1, acc)
|
|
2785
|
+
prev = 3
|
|
2786
|
+
const at = ctx._fe ?? pos
|
|
2787
|
+
if (at > best) { best = at; acc = undefined }
|
|
2788
|
+
if (at === best) acc = accSet(ctx._fx, acc)
|
|
1951
2789
|
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1952
2790
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1953
2791
|
}
|
|
1954
|
-
|
|
2792
|
+
if (best === pos && prev < 1) acc = accSet(e0, acc)
|
|
2793
|
+
if (best === pos && prev < 2) acc = accSet(e1, acc)
|
|
2794
|
+
if (best === pos && n === 3 && prev < 3) acc = accSet(e2, acc)
|
|
1955
2795
|
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1956
2796
|
return FAIL
|
|
1957
2797
|
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
if (cls !== null && !classHas(cls, c)) { acc = accSet(armFx[i]!, acc); continue }
|
|
2798
|
+
|
|
2799
|
+
if (g0 === null || classHas(g0, c)) {
|
|
1961
2800
|
ctx._fc = false
|
|
1962
|
-
const v =
|
|
2801
|
+
const v = a0(input, pos, ctx)
|
|
1963
2802
|
if (v !== FAIL) return v
|
|
1964
|
-
|
|
2803
|
+
const at = ctx._fe ?? pos
|
|
2804
|
+
if (at > best) { best = at; acc = undefined }
|
|
2805
|
+
if (at === best) acc = accSet(ctx._fx, acc)
|
|
1965
2806
|
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1966
2807
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2808
|
+
} else if (best === pos) acc = accSet(e0, acc)
|
|
2809
|
+
if (g1 === null || classHas(g1, c)) {
|
|
2810
|
+
ctx._fc = false
|
|
2811
|
+
const v = a1(input, pos, ctx)
|
|
2812
|
+
if (v !== FAIL) return v
|
|
2813
|
+
const at = ctx._fe ?? pos
|
|
2814
|
+
if (at > best) { best = at; acc = undefined }
|
|
2815
|
+
if (at === best) acc = accSet(ctx._fx, acc)
|
|
2816
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
2817
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2818
|
+
} else if (best === pos) acc = accSet(e1, acc)
|
|
2819
|
+
if (n === 3) {
|
|
2820
|
+
if (g2 === null || classHas(g2, c)) {
|
|
2821
|
+
ctx._fc = false
|
|
2822
|
+
const v = a2!(input, pos, ctx)
|
|
2823
|
+
if (v !== FAIL) return v
|
|
2824
|
+
const at = ctx._fe ?? pos
|
|
2825
|
+
if (at > best) { best = at; acc = undefined }
|
|
2826
|
+
if (at === best) acc = accSet(ctx._fx, acc)
|
|
2827
|
+
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
2828
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2829
|
+
} else if (best === pos) acc = accSet(e2, acc)
|
|
1967
2830
|
}
|
|
1968
2831
|
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1969
2832
|
return FAIL
|
|
1970
2833
|
}
|
|
1971
2834
|
}
|
|
1972
2835
|
|
|
2836
|
+
const bindGeneral = (start: number, prior: ChoiceExpectedPrefix): GeneralChoiceBlock => {
|
|
2837
|
+
const arm = (i: number): Piece => i < n ? link(code[base + i]!) : NEVER_PIECE
|
|
2838
|
+
const expected = (i: number): readonly string[] => i < n
|
|
2839
|
+
? fx[code[base + n + i]!] as string[]
|
|
2840
|
+
: EMPTY_FX
|
|
2841
|
+
const gate = (i: number): ResolvedClass | null => i < n ? armCls[i] ?? null : NEVER_CLASS
|
|
2842
|
+
const through = expectedBlock(
|
|
2843
|
+
start, expected(start), expected(start + 1), expected(start + 2), expected(start + 3), prior,
|
|
2844
|
+
)
|
|
2845
|
+
const next = start + 4 < n ? bindGeneral(start + 4, through) : undefined
|
|
2846
|
+
return generalChoiceBlock(
|
|
2847
|
+
start,
|
|
2848
|
+
arm(start), arm(start + 1), arm(start + 2), arm(start + 3),
|
|
2849
|
+
gate(start), gate(start + 1), gate(start + 2), gate(start + 3),
|
|
2850
|
+
through, next, n, choiceFx,
|
|
2851
|
+
)
|
|
2852
|
+
}
|
|
2853
|
+
const general = bindGeneral(0, EMPTY_EXPECTED_PREFIX)
|
|
2854
|
+
|
|
2855
|
+
if (n <= 32) {
|
|
2856
|
+
const mask = new Uint32Array(129)
|
|
2857
|
+
for (let i = 0; i < n; i++) {
|
|
2858
|
+
const cls = armCls[i] ?? null
|
|
2859
|
+
const bit = 1 << i
|
|
2860
|
+
if (cls === null) {
|
|
2861
|
+
for (let c = 0; c < 129; c++) mask[c]! |= bit
|
|
2862
|
+
continue
|
|
2863
|
+
}
|
|
2864
|
+
for (let c = 0; c < 128; c++) if (cls.ascii[c] === 1) mask[c]! |= bit
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
const bindMasked = (start: number, prior: ChoiceExpectedPrefix): MaskedChoiceBlock => {
|
|
2868
|
+
const arm = (i: number): Piece => i < n ? link(code[base + i]!) : NEVER_PIECE
|
|
2869
|
+
const expected = (i: number): readonly string[] => i < n
|
|
2870
|
+
? fx[code[base + n + i]!] as string[]
|
|
2871
|
+
: EMPTY_FX
|
|
2872
|
+
const through = expectedBlock(
|
|
2873
|
+
start, expected(start), expected(start + 1), expected(start + 2), expected(start + 3), prior,
|
|
2874
|
+
)
|
|
2875
|
+
const next = start + 4 < n ? bindMasked(start + 4, through) : undefined
|
|
2876
|
+
return maskedChoiceBlock(
|
|
2877
|
+
start, arm(start), arm(start + 1), arm(start + 2), arm(start + 3),
|
|
2878
|
+
through, next, n, choiceFx,
|
|
2879
|
+
)
|
|
2880
|
+
}
|
|
2881
|
+
const masked = bindMasked(0, EMPTY_EXPECTED_PREFIX)
|
|
2882
|
+
return (input, pos, ctx) => {
|
|
2883
|
+
const c = lead(input, pos)
|
|
2884
|
+
const need = markCst(ctx)
|
|
2885
|
+
const mRaw = MRAW
|
|
2886
|
+
const mTl = MTL
|
|
2887
|
+
const mLv = MLV
|
|
2888
|
+
const mFl = need ? ctx._fields?.length ?? 0 : 0
|
|
2889
|
+
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
2890
|
+
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
2891
|
+
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
2892
|
+
if (c < 128) {
|
|
2893
|
+
return masked(
|
|
2894
|
+
input, pos, ctx, mask[c < 0 ? 128 : c]!,
|
|
2895
|
+
need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, undefined, 0, pos,
|
|
2896
|
+
)
|
|
2897
|
+
}
|
|
2898
|
+
return general(
|
|
2899
|
+
input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, undefined, 0, pos,
|
|
2900
|
+
)
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
|
|
1973
2904
|
return (input, pos, ctx) => {
|
|
1974
2905
|
const c = lead(input, pos)
|
|
1975
2906
|
const need = markCst(ctx)
|
|
@@ -1980,19 +2911,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1980
2911
|
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
1981
2912
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
1982
2913
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
if (cls !== null && !classHas(cls, c)) { acc = accSet(armFx[i]!, acc); continue }
|
|
1987
|
-
ctx._fc = false
|
|
1988
|
-
const v = arms[i]!(input, pos, ctx)
|
|
1989
|
-
if (v !== FAIL) return v
|
|
1990
|
-
acc = accSet(ctx._fx, acc)
|
|
1991
|
-
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
1992
|
-
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1993
|
-
}
|
|
1994
|
-
ctx._fe = pos; ctx._fx = acc ?? choiceFx
|
|
1995
|
-
return FAIL
|
|
2914
|
+
return general(
|
|
2915
|
+
input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, undefined, 0, pos,
|
|
2916
|
+
)
|
|
1996
2917
|
}
|
|
1997
2918
|
}
|
|
1998
2919
|
|
|
@@ -2106,6 +3027,18 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2106
3027
|
const reportItem = (flags & 4) !== 0
|
|
2107
3028
|
const itemFx = reportItem ? fx[code[ip + 6]!] as string[] : EMPTY_FX
|
|
2108
3029
|
const collect = op === OP_REP
|
|
3030
|
+
// The item's finite, non-nullable first set is table data. A strict
|
|
3031
|
+
// optional iteration whose lead is outside it cannot run or commit, so
|
|
3032
|
+
// stop before entering the child graph. Tolerant assemblies keep the
|
|
3033
|
+
// ordinary failure path because it may recover the rejected input.
|
|
3034
|
+
// A separator-less repeat has no separator sentinel, so ip+7 carries
|
|
3035
|
+
// the optional item's class at no extra table width. Separated lists
|
|
3036
|
+
// keep that slot for their recovery sentinel and use the ordinary path.
|
|
3037
|
+
const itemClassIndex = sepIp < 0 ? code[ip + 7]! : -1
|
|
3038
|
+
const itemClass = !REC && itemClassIndex >= 0 ? cc[itemClassIndex]! : undefined
|
|
3039
|
+
const itemMayStart = itemClass === undefined
|
|
3040
|
+
? undefined
|
|
3041
|
+
: (input: string, pos: number): boolean => classHas(itemClass, lead(input, pos))
|
|
2109
3042
|
// `many()` — the min-0, separator-less repeat — is the only shape that runs
|
|
2110
3043
|
// its FIRST item through `repItem` and so skips leading trivia. The shape
|
|
2111
3044
|
// identifies itself, and it is table data, so it is decided here.
|
|
@@ -2236,6 +3169,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2236
3169
|
const out: unknown[] | undefined = collect ? [] : undefined
|
|
2237
3170
|
const hasTrivia = ctx.trivia !== undefined
|
|
2238
3171
|
const needMark = rollbackNeeded(ctx)
|
|
3172
|
+
// A completions probe deliberately observes swallowed item failures.
|
|
3173
|
+
// Keep the ordinary child path while one is active so this structural
|
|
3174
|
+
// fast stop cannot erase an item completion at the cursor.
|
|
3175
|
+
const gateItems = itemMayStart !== undefined && ctx._probe === undefined
|
|
2239
3176
|
let cur = pos
|
|
2240
3177
|
let count = 0
|
|
2241
3178
|
for (;;) {
|
|
@@ -2244,6 +3181,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2244
3181
|
// the LOOP HEAD. Held to `count >= min` so an under-`min` list still
|
|
2245
3182
|
// attempts the separator and reports its expected set.
|
|
2246
3183
|
if (sep !== undefined && count > 0 && count >= min && cur >= input.length) break
|
|
3184
|
+
// With no trivia or separator to position, the item starts at `cur`.
|
|
3185
|
+
// This is the full 0.46 loop-head cut: no mark and no child setup.
|
|
3186
|
+
if (count >= min && sep === undefined && !hasTrivia
|
|
3187
|
+
&& gateItems && !itemMayStart!(input, cur)) break
|
|
2247
3188
|
// Per ITERATION, never hoisted: a preceding item's `node()` opened and
|
|
2248
3189
|
// closed a capture buffer, so `ctx._cstBuf` is not the object it was
|
|
2249
3190
|
// at the loop head. `needMark` is the pre-existing loop-invariant.
|
|
@@ -2280,6 +3221,11 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2280
3221
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
2281
3222
|
break
|
|
2282
3223
|
}
|
|
3224
|
+
if (count >= min && gateItems && !itemMayStart!(input, itemStart)) {
|
|
3225
|
+
if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3226
|
+
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
3227
|
+
break
|
|
3228
|
+
}
|
|
2283
3229
|
ctx._fc = false
|
|
2284
3230
|
const v = child(input, itemStart, ctx)
|
|
2285
3231
|
if (v === FAIL) {
|
|
@@ -2317,75 +3263,69 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2317
3263
|
const otherRouted = code[ip + 4]! === 1
|
|
2318
3264
|
const n = code[ip + 5]!
|
|
2319
3265
|
const armBase = ip + 6
|
|
2320
|
-
const arms: Piece[] = new Array<Piece>(n)
|
|
2321
|
-
for (let i = 0; i < n; i++) arms[i] = link(code[armBase + i]!)
|
|
2322
3266
|
const byKey = spec.byKey
|
|
2323
3267
|
const byFold = spec.byFold
|
|
2324
3268
|
const hasFold = spec.byFold.size > 0
|
|
2325
3269
|
const match = spec.match
|
|
2326
3270
|
const matchN = match.length
|
|
2327
|
-
const matchFn: Array<(key: string) => boolean> = new Array<(key: string) => boolean>(matchN)
|
|
2328
|
-
const matchArm: number[] = new Array<number>(matchN)
|
|
2329
|
-
for (let i = 0; i < matchN; i++) { matchFn[i] = linkMatcher(match[i]!); matchArm[i] = match[i]![3] }
|
|
2330
3271
|
const routed = spec.routed
|
|
2331
3272
|
const expected = spec.expected as string[]
|
|
2332
3273
|
|
|
3274
|
+
const validArm = (arm: number): boolean => Number.isInteger(arm) && arm >= 0 && arm < n
|
|
3275
|
+
if (routed.length !== n) throw new TypeError('table assembler: malformed dispatch routed arity')
|
|
3276
|
+
for (const arm of byKey.values()) if (!validArm(arm)) throw new TypeError('table assembler: malformed dispatch exact arm')
|
|
3277
|
+
for (const arm of byFold.values()) if (!validArm(arm)) throw new TypeError('table assembler: malformed dispatch folded arm')
|
|
3278
|
+
for (const m of match) if (!validArm(m[3])) throw new TypeError('table assembler: malformed dispatch matcher arm')
|
|
3279
|
+
|
|
3280
|
+
const neverMatch: DispatchMatcher = () => false
|
|
3281
|
+
const bindMatchers = (start: number): DispatchMatcherBlock => {
|
|
3282
|
+
const pred = (i: number): DispatchMatcher => i < matchN ? linkMatcher(match[i]!) : neverMatch
|
|
3283
|
+
const arm = (i: number): number => i < matchN ? match[i]![3] : -1
|
|
3284
|
+
const next = start + 4 < matchN ? bindMatchers(start + 4) : undefined
|
|
3285
|
+
return dispatchMatcherBlock(
|
|
3286
|
+
pred(start), arm(start), pred(start + 1), arm(start + 1),
|
|
3287
|
+
pred(start + 2), arm(start + 2), pred(start + 3), arm(start + 3), next,
|
|
3288
|
+
)
|
|
3289
|
+
}
|
|
3290
|
+
const matchBlock = matchN === 0 ? undefined : bindMatchers(0)
|
|
3291
|
+
const classify: (key: string) => number | undefined = hasFold
|
|
3292
|
+
? matchBlock === undefined
|
|
3293
|
+
? key => byKey.get(key) ?? byFold.get(asciiFoldKey(key))
|
|
3294
|
+
: key => byKey.get(key) ?? byFold.get(asciiFoldKey(key)) ?? matchBlock(key)
|
|
3295
|
+
: matchBlock === undefined
|
|
3296
|
+
? key => byKey.get(key)
|
|
3297
|
+
: key => byKey.get(key) ?? matchBlock(key)
|
|
3298
|
+
|
|
3299
|
+
const neverBranch: DispatchBranch = () => FAIL
|
|
3300
|
+
const bindArms = (start: number): DispatchArmBlock => {
|
|
3301
|
+
const branch = (i: number): DispatchBranch => i < n
|
|
3302
|
+
? dispatchBranch(link(code[armBase + i]!), routed[i] === 1)
|
|
3303
|
+
: neverBranch
|
|
3304
|
+
const next = start + 4 < n ? bindArms(start + 4) : undefined
|
|
3305
|
+
return dispatchArmBlock(
|
|
3306
|
+
start, branch(start), branch(start + 1), branch(start + 2), branch(start + 3), next,
|
|
3307
|
+
)
|
|
3308
|
+
}
|
|
3309
|
+
const runArm = n === 0 ? undefined : bindArms(0)
|
|
3310
|
+
const runOther = other === undefined ? undefined : dispatchBranch(other, otherRouted)
|
|
3311
|
+
|
|
2333
3312
|
return (input, pos, ctx) => {
|
|
2334
3313
|
const selectorMark = saveTriviaMark(ctx)
|
|
2335
3314
|
const selVal = selector(input, pos, ctx)
|
|
2336
3315
|
if (selVal === FAIL) return FAIL
|
|
2337
3316
|
const selEnd = EC.e
|
|
2338
3317
|
const key = selVal as string
|
|
2339
|
-
|
|
2340
|
-
let arm = byKey.get(key)
|
|
2341
|
-
if (arm === undefined && hasFold) arm = byFold.get(asciiFoldKey(key))
|
|
3318
|
+
const arm = classify(key)
|
|
2342
3319
|
if (arm === undefined) {
|
|
2343
|
-
|
|
2344
|
-
if (matchFn[i]!(key)) {
|
|
2345
|
-
arm = matchArm[i]!
|
|
2346
|
-
break
|
|
2347
|
-
}
|
|
2348
|
-
}
|
|
2349
|
-
}
|
|
2350
|
-
|
|
2351
|
-
let target: Piece
|
|
2352
|
-
let usesRouted: boolean
|
|
2353
|
-
if (arm === undefined) {
|
|
2354
|
-
if (other === undefined) {
|
|
3320
|
+
if (runOther === undefined) {
|
|
2355
3321
|
// No branch and no fallback: fail AT THE SELECTOR'S END.
|
|
2356
3322
|
ctx._fe = selEnd
|
|
2357
3323
|
ctx._fx = expected
|
|
2358
3324
|
return FAIL
|
|
2359
3325
|
}
|
|
2360
|
-
|
|
2361
|
-
usesRouted = otherRouted
|
|
2362
|
-
} else {
|
|
2363
|
-
target = arms[arm]!
|
|
2364
|
-
usesRouted = routed[arm] === 1
|
|
2365
|
-
}
|
|
2366
|
-
|
|
2367
|
-
let mark = saveTriviaMark(ctx)
|
|
2368
|
-
let v: unknown
|
|
2369
|
-
if (usesRouted) {
|
|
2370
|
-
const savedRouted = ctx._routed
|
|
2371
|
-
rollbackTrivia(ctx, selectorMark)
|
|
2372
|
-
mark = saveTriviaMark(ctx)
|
|
2373
|
-
ctx._routed = { value: key, span: { start: pos, end: selEnd } }
|
|
2374
|
-
try {
|
|
2375
|
-
v = target(input, pos, ctx)
|
|
2376
|
-
} finally {
|
|
2377
|
-
ctx._routed = savedRouted
|
|
2378
|
-
}
|
|
2379
|
-
} else {
|
|
2380
|
-
v = target(input, selEnd, ctx)
|
|
2381
|
-
}
|
|
2382
|
-
if (v === FAIL) {
|
|
2383
|
-
rollbackTrivia(ctx, mark)
|
|
2384
|
-
// A failed dispatch branch is COMMITTED: the selector already matched.
|
|
2385
|
-
ctx._fc = true
|
|
2386
|
-
return FAIL
|
|
3326
|
+
return runOther(input, pos, selEnd, key, selectorMark, ctx)
|
|
2387
3327
|
}
|
|
2388
|
-
return
|
|
3328
|
+
return runArm!(arm, input, pos, selEnd, key, selectorMark, ctx)
|
|
2389
3329
|
}
|
|
2390
3330
|
}
|
|
2391
3331
|
|
|
@@ -2394,6 +3334,41 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2394
3334
|
case OP_NODE:
|
|
2395
3335
|
case OP_NODE_TRACK: {
|
|
2396
3336
|
const flags = code[ip + 3]!
|
|
3337
|
+
const scalarChild = !hostCst && !cfg.tolerant && !cfg.probe && !cfg.coverage && !cfg.trackLines
|
|
3338
|
+
? scalarTerminalNodeChild(code, ip)
|
|
3339
|
+
: -1
|
|
3340
|
+
if (scalarChild >= 0) {
|
|
3341
|
+
const recognize = scalarFor(scalarChild)!
|
|
3342
|
+
const spec = k[code[scalarChild + 1]!]
|
|
3343
|
+
const regexValue = code[scalarChild] === OP_RX
|
|
3344
|
+
const xf = fx[code[scalarChild + 2]!] as string[]
|
|
3345
|
+
const build = fns[code[ip + 1]!] as (
|
|
3346
|
+
children: readonly unknown[], fields: undefined, span: { start: number; end: number },
|
|
3347
|
+
rawChildren: readonly unknown[], triviaLog: readonly number[], state: undefined,
|
|
3348
|
+
) => unknown
|
|
3349
|
+
return (input, pos, ctx) => {
|
|
3350
|
+
const end = recognize(input, pos)
|
|
3351
|
+
if (end < 0) {
|
|
3352
|
+
ctx._fe = pos; ctx._fx = xf
|
|
3353
|
+
if (ctx._probe !== undefined) failAt(ctx, xf, pos)
|
|
3354
|
+
return FAIL
|
|
3355
|
+
}
|
|
3356
|
+
const value = regexValue ? input.slice(pos, end) : spec as string
|
|
3357
|
+
const leaf = { _tag: 'leaf', value, span: { start: pos, end } }
|
|
3358
|
+
const kids = [leaf]
|
|
3359
|
+
const rawKids = [leaf]
|
|
3360
|
+
const span = { start: pos, end }
|
|
3361
|
+
EC.e = end
|
|
3362
|
+
const nd = build(kids, undefined, span, rawKids, EMPTY_TL, undefined)
|
|
3363
|
+
if (ctx._cstBuf !== undefined || ctx._cstChildren !== undefined) {
|
|
3364
|
+
pushCstChild(ctx, nd, ctx._cstRawChildren !== undefined || ctx._cstBuf !== undefined
|
|
3365
|
+
? rawEntry(nd, input, pos, end)
|
|
3366
|
+
: undefined)
|
|
3367
|
+
}
|
|
3368
|
+
EC.e = end
|
|
3369
|
+
return nd
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
2397
3372
|
const child = link(code[ip + 2]!)
|
|
2398
3373
|
const proj = code[ip + 4]!
|
|
2399
3374
|
const buildIdx = code[ip + 1]!
|
|
@@ -2412,12 +3387,34 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2412
3387
|
// 145,512 executions per parse of `benchmark.less`. `cstOutputHost(ctx.build)`
|
|
2413
3388
|
// is fixed by `run()` before the entry is called, so it selects the piece.
|
|
2414
3389
|
const build = buildIdx >= 0
|
|
2415
|
-
? fns[buildIdx] as
|
|
2416
|
-
children: readonly unknown[], fields: FieldMap | undefined, span: { start: number; end: number },
|
|
2417
|
-
rawChildren: readonly unknown[], triviaLog: readonly number[], state: unknown,
|
|
2418
|
-
) => unknown
|
|
3390
|
+
? fns[buildIdx] as NodeBuilder
|
|
2419
3391
|
: undefined
|
|
2420
3392
|
|
|
3393
|
+
// The common AST-only bodies are separate FunctionLiterals, not a mode
|
|
3394
|
+
// branch in the generic node body. Their eligibility is entirely table
|
|
3395
|
+
// data plus this assembly's fixed option set; direct/custom contexts and
|
|
3396
|
+
// every richer node shape retain the generic implementation below.
|
|
3397
|
+
if (!hostCst && !tracked) {
|
|
3398
|
+
if (build !== undefined && proj < 0 && flags === 0) {
|
|
3399
|
+
return plainBuildNode(child, build)
|
|
3400
|
+
}
|
|
3401
|
+
if (build !== undefined && proj < 0 && flags === 2) {
|
|
3402
|
+
return childrenOnlyBuildNode(child, build)
|
|
3403
|
+
}
|
|
3404
|
+
if (build !== undefined && proj < 0 && flags === 18) {
|
|
3405
|
+
return childrenOnlyFieldsBuildNode(child, build)
|
|
3406
|
+
}
|
|
3407
|
+
if (build !== undefined && proj < 0 && flags === 32) {
|
|
3408
|
+
return collapseBuildNode(child, build)
|
|
3409
|
+
}
|
|
3410
|
+
if (build !== undefined && proj < 0 && flags === 34) {
|
|
3411
|
+
return childrenOnlyCollapseBuildNode(child, build)
|
|
3412
|
+
}
|
|
3413
|
+
if (build === undefined && proj >= 0 && flags === 0) {
|
|
3414
|
+
return plainProjectNode(child, proj, type)
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
|
|
2421
3418
|
// A STRUCTURAL node — no builder, no projection — is the only shape that
|
|
2422
3419
|
// takes a per-node-type trivia-kind mask off the host (`node.ts:260`).
|
|
2423
3420
|
// Assembly-time, so a node with a reducer pays nothing for the question.
|
|
@@ -2584,10 +3581,11 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2584
3581
|
* Emitted source is attempted FIRST, and the closure walk above runs only for
|
|
2585
3582
|
* what it refuses.
|
|
2586
3583
|
*
|
|
2587
|
-
*
|
|
2588
|
-
*
|
|
2589
|
-
*
|
|
2590
|
-
* it
|
|
3584
|
+
* Source emission names each fixed grammar edge directly and can be carried
|
|
3585
|
+
* as a build-time factory for CSP. The closure path may bind the same fixed
|
|
3586
|
+
* topology through scalar captures; this ordering selects the generated form
|
|
3587
|
+
* where it exists without making shared-literal IC behavior an architecture
|
|
3588
|
+
* premise.
|
|
2591
3589
|
*
|
|
2592
3590
|
* The refusal is RECORDED, not swallowed. A grammar that silently drops to
|
|
2593
3591
|
* the closure path is a permanently slow path nobody would ever find, which
|
|
@@ -2609,10 +3607,23 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2609
3607
|
if (s.sentinel !== undefined) extraIps.push(s.sentinel[0])
|
|
2610
3608
|
}
|
|
2611
3609
|
for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0])
|
|
2612
|
-
const
|
|
2613
|
-
for (const ip of reachableSites(code,
|
|
2614
|
-
if (code[ip]
|
|
2615
|
-
|
|
3610
|
+
const roots = [...Object.values(prog.rules), ...extraIps]
|
|
3611
|
+
for (const ip of reachableSites(code, roots)) {
|
|
3612
|
+
if (code[ip] === OP_DISPATCH) {
|
|
3613
|
+
const spec = dsp[code[ip + 2]!]
|
|
3614
|
+
validateDispatchSpec(spec, code[ip + 5]!, code[ip + 4]!)
|
|
3615
|
+
}
|
|
3616
|
+
if (!hostCst && !cfg.tolerant && !cfg.probe && !cfg.coverage && !cfg.trackLines) {
|
|
3617
|
+
if (code[ip] === OP_NODE) {
|
|
3618
|
+
const child = scalarTerminalNodeChild(code, ip)
|
|
3619
|
+
if (child >= 0) {
|
|
3620
|
+
rawScalarSpecs.add(code[child + 1]!)
|
|
3621
|
+
scalarFor(child)
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
const notChild = scalarTerminalNotChild(code, ip)
|
|
3626
|
+
if (notChild >= 0) scalarFor(notChild)
|
|
2616
3627
|
}
|
|
2617
3628
|
/**
|
|
2618
3629
|
* THE BUILD'S OWN ANSWER, TRIED BEFORE THE CONSTRUCTOR.
|
|
@@ -2637,7 +3648,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2637
3648
|
? prog.asm?.find(a => a.key === cfgKey(cfg))
|
|
2638
3649
|
: undefined
|
|
2639
3650
|
if (pre !== undefined) {
|
|
2640
|
-
const pools = rebuildPools(t.cc, t.fx, pre.plan)
|
|
3651
|
+
const pools = rebuildPools(t.cc, t.fx, t.disp, pre.plan)
|
|
2641
3652
|
emitted = pre.factory(
|
|
2642
3653
|
EC, FAIL, k, fx, fns, pools.masks, pools.classes, pools.armExpected, trivia,
|
|
2643
3654
|
trivia.map(tv => tv?._meta.triviaKindLabels), triviaScan,
|
|
@@ -2649,6 +3660,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2649
3660
|
asciiFoldKey, ROUTED_FX,
|
|
2650
3661
|
REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
|
|
2651
3662
|
matchesAt, recoverScan, orSentinel, captureError,
|
|
3663
|
+
scalarRecognizers, commitTriviaScan, scanTriviaCompact, t.lex, adjacencyHolds, t.lexPrograms,
|
|
2652
3664
|
)
|
|
2653
3665
|
emitReached = new Set(pre.reached)
|
|
2654
3666
|
}
|
|
@@ -2729,6 +3741,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2729
3741
|
// object rather than to two separately constructed ones.
|
|
2730
3742
|
REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
|
|
2731
3743
|
matchesAt, recoverScan, orSentinel, captureError,
|
|
3744
|
+
scalarRecognizers, commitTriviaScan, scanTriviaCompact, t.lex, adjacencyHolds, t.lexPrograms,
|
|
2732
3745
|
)
|
|
2733
3746
|
emitReached = em.reached
|
|
2734
3747
|
} catch (e) {
|