parseman 0.57.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/diagnostics.js +1799 -962
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +1928 -1091
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/compiler/token-alphabet.d.ts +11 -1
- package/dist/compiler/token-alphabet.d.ts.map +1 -1
- package/dist/compiler/token-capability.js +69 -16
- package/dist/compiler/token-capability.js.map +2 -2
- package/dist/cst/capture-buffer.d.ts +6 -0
- package/dist/cst/capture-buffer.d.ts.map +1 -1
- package/dist/index.js +1016 -177
- package/dist/index.js.map +3 -3
- package/dist/plugin/index.d.ts +25 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1209 -236
- package/dist/plugin/index.js.map +3 -3
- package/dist/table/assemble.d.ts.map +1 -1
- package/dist/table/compile-rule-map.d.ts.map +1 -1
- package/dist/table/emit-assembly.d.ts +1 -1
- 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.d.ts.map +1 -1
- package/dist/table/factor-choice.d.ts +42 -0
- package/dist/table/factor-choice.d.ts.map +1 -0
- package/dist/table/fail-clean.d.ts +3 -0
- package/dist/table/fail-clean.d.ts.map +1 -0
- package/dist/table/hold-gap.d.ts +8 -0
- package/dist/table/hold-gap.d.ts.map +1 -0
- package/dist/table/index.js +1369 -259
- package/dist/table/index.js.map +3 -3
- package/dist/table/lex-program.d.ts +7 -3
- package/dist/table/lex-program.d.ts.map +1 -1
- package/dist/table/ops.d.ts +8 -0
- package/dist/table/ops.d.ts.map +1 -1
- package/dist/table/run-support.d.ts +19 -0
- package/dist/table/run-support.d.ts.map +1 -1
- package/dist/table/scalar-terminal.d.ts +2 -0
- package/dist/table/scalar-terminal.d.ts.map +1 -1
- package/dist/table/site-labels.d.ts +66 -0
- package/dist/table/site-labels.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/combinators/dispatch.ts +12 -3
- package/src/compiler/token-alphabet.ts +57 -17
- package/src/cst/capture-buffer.ts +25 -0
- package/src/plugin/index.ts +230 -35
- package/src/table/assemble.ts +252 -62
- package/src/table/child-slots.ts +2 -2
- package/src/table/compile-rule-map.ts +14 -5
- package/src/table/emit-assembly.ts +112 -23
- package/src/table/emit.ts +3 -1
- package/src/table/encode.ts +80 -14
- package/src/table/exec.ts +27 -8
- package/src/table/factor-choice.ts +173 -0
- package/src/table/fail-clean.ts +130 -0
- package/src/table/hold-gap.ts +68 -0
- package/src/table/lex-program.ts +197 -43
- package/src/table/ops.ts +9 -1
- package/src/table/run-support.ts +35 -0
- package/src/table/scalar-terminal.ts +8 -0
- package/src/table/site-labels.ts +135 -4
- package/src/version.ts +1 -1
package/src/table/assemble.ts
CHANGED
|
@@ -75,8 +75,8 @@ import { projectChild, unwrapChild } from '../combinators/node.ts'
|
|
|
75
75
|
import { cstOutputHost } from '../compiler/build-arity.ts'
|
|
76
76
|
import { consumeTrivia } from '../combinators/trivia-skip.ts'
|
|
77
77
|
import {
|
|
78
|
-
advanceTrivia, commitTriviaScan, needsDeferredTriviaCommit, rollbackScannedTriviaAt,
|
|
79
|
-
|
|
78
|
+
advanceTrivia, commitTriviaScan, needsDeferredTriviaCommit, rollbackScannedTriviaAt, rollbackTriviaAt,
|
|
79
|
+
scanTrivia, scanTriviaCompact, skipTriviaScanned, type FastTriviaScanner,
|
|
80
80
|
} from '../combinators/trivia-skip.ts'
|
|
81
81
|
import {
|
|
82
82
|
cstCaptureActive, cstLeavesLen, cstTlLen,
|
|
@@ -90,7 +90,7 @@ import {
|
|
|
90
90
|
OP_LIVE,
|
|
91
91
|
OP_FIELD, OP_DISPATCH, OP_ROUTED, OP_LIT_CI, OP_LIT_CI_TRACK, OP_TOKEN, OP_WITHCTX, OP_GUARD, OP_ATTEMPT, OP_LABEL,
|
|
92
92
|
OP_COV,
|
|
93
|
-
OP_ADJ, OP_GREEDY, OP_REJECT, OP_ARMGATE,
|
|
93
|
+
OP_ADJ, OP_GREEDY, OP_REJECT, OP_ARMGATE, OP_RGATE,
|
|
94
94
|
OP_LEX_BODY, OP_LEX_PROGRAM,
|
|
95
95
|
} from './ops.ts'
|
|
96
96
|
import { adjacencyHolds, adjacencyMisuse } from '../combinators/adjacency.ts'
|
|
@@ -107,7 +107,9 @@ import {
|
|
|
107
107
|
EMITTED_PARAMS, Unemittable, emitAssemblySource, rebuildPools,
|
|
108
108
|
type EmittedAssembly, type EmittedFactory,
|
|
109
109
|
} from './emit-assembly.ts'
|
|
110
|
-
import { lead, rawEntry, spanLines } from './run-support.ts'
|
|
110
|
+
import { collapseEmpty, lead, rawEntry, spanLines } from './run-support.ts'
|
|
111
|
+
import { holdsGap, rowNullable } from './hold-gap.ts'
|
|
112
|
+
import { failCleanAnalysis } from './fail-clean.ts'
|
|
111
113
|
/**
|
|
112
114
|
* THE COMPLETIONS PROBE, at the terminal fail sites and nowhere else.
|
|
113
115
|
*
|
|
@@ -123,18 +125,18 @@ import { lead, rawEntry, spanLines } from './run-support.ts'
|
|
|
123
125
|
* property read per terminal miss — the same price codegen pays.
|
|
124
126
|
*/
|
|
125
127
|
import {
|
|
126
|
-
classHas, decodeClassSpec, expandCompact, ownTableProgram, resolveTable,
|
|
128
|
+
classHas, decodeClassSpec, encodeClassSpec, expandCompact, ownTableProgram, resolveClass, resolveTable,
|
|
127
129
|
validateDispatchSpec,
|
|
128
130
|
type CompactProgram, type ResolvedClass, type ResolvedTable,
|
|
129
131
|
type SubtreeRef, type TableProgram, type TableRule,
|
|
130
132
|
} from './program.ts'
|
|
131
133
|
import { stampRuleMap } from './stamp.ts'
|
|
132
|
-
import { computeSiteLabels, reachableSites } from './site-labels.ts'
|
|
134
|
+
import { computeSiteLabels, gatedPassCollapse, passCollapse, reachableSites, scanHandoffNeeded, scopeIsIdentity, triviaLeadRanges } from './site-labels.ts'
|
|
133
135
|
import { refuseUnclassifiedRootScope } from '../cst/root-trivia-scope.ts'
|
|
134
136
|
import { captureError, firstSetSentinel, matchesAt, orSentinel, recoverScan } from '../recovery/scan.ts'
|
|
135
137
|
import {
|
|
136
138
|
largeChoiceLiteralPretest, leadingScalarSequence, leadingScalarTerminal, makeScalarRecognizer, scalarTerminalNodeChild,
|
|
137
|
-
scalarTerminalNotChild, type ScalarRecognizer,
|
|
139
|
+
scalarTerminalNotChild, scalarTerminalPeekChild, type ScalarRecognizer,
|
|
138
140
|
} from './scalar-terminal.ts'
|
|
139
141
|
|
|
140
142
|
/**
|
|
@@ -241,13 +243,14 @@ type GeneralChoiceBlock = (
|
|
|
241
243
|
type ExclusiveChoiceBlock = (arm: number, input: string, pos: number, ctx: ParseContext) => unknown
|
|
242
244
|
type DispatchMatcher = (key: string) => boolean
|
|
243
245
|
type DispatchMatcherBlock = (key: string) => number | undefined
|
|
246
|
+
/** The selector's rollback mark rides as the eight scalars a choice block takes. */
|
|
244
247
|
type DispatchBranch = (
|
|
245
|
-
input: string, pos: number, selEnd: number, key: string,
|
|
246
|
-
|
|
248
|
+
input: string, pos: number, selEnd: number, key: string, ctx: ParseContext,
|
|
249
|
+
need: boolean, mRaw: number, mTl: number, mLv: number, mFl: number, mEr: number, mLog: number, mRoot: number,
|
|
247
250
|
) => unknown
|
|
248
251
|
type DispatchArmBlock = (
|
|
249
|
-
arm: number, input: string, pos: number, selEnd: number, key: string,
|
|
250
|
-
|
|
252
|
+
arm: number, input: string, pos: number, selEnd: number, key: string, ctx: ParseContext,
|
|
253
|
+
need: boolean, mRaw: number, mTl: number, mLv: number, mFl: number, mEr: number, mLog: number, mRoot: number,
|
|
251
254
|
) => unknown
|
|
252
255
|
|
|
253
256
|
/**
|
|
@@ -441,6 +444,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
441
444
|
* reference.
|
|
442
445
|
*/
|
|
443
446
|
const REC = prog.rec === 1 && cfg.tolerant
|
|
447
|
+
/** A completions-probe assembly: lookaheads restore `_probe.best` around their child. */
|
|
448
|
+
const PROBE = cfg.probe
|
|
444
449
|
/**
|
|
445
450
|
* The sync sentinel for a char-class index, built ONCE at assembly.
|
|
446
451
|
*
|
|
@@ -531,6 +536,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
531
536
|
|
|
532
537
|
const memo = new Map<number, Piece>()
|
|
533
538
|
const reached = new Set<number>()
|
|
539
|
+
/** Row nullability for `holdsGap`, memoised for this assembly. */
|
|
540
|
+
const NULLABLE = new Map<number, boolean>()
|
|
541
|
+
/** A child whose failure writes nothing needs no rollback mark (`fail-clean.ts`). */
|
|
542
|
+
const CLEAN = REC ? (): boolean => false : failCleanAnalysis(code)
|
|
534
543
|
|
|
535
544
|
function pushLeaf(ctx: ParseContext, value: string, s: number, e: number): void {
|
|
536
545
|
const lf: Leaf = { _tag: 'leaf', value, span: { start: s, end: e } }
|
|
@@ -764,6 +773,36 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
764
773
|
}
|
|
765
774
|
}
|
|
766
775
|
|
|
776
|
+
/**
|
|
777
|
+
* A collapse over one node is that node — `run-support.ts` `collapseEmpty` has
|
|
778
|
+
* the contract, and the one case the node's value cannot stand for itself.
|
|
779
|
+
*/
|
|
780
|
+
function passCollapseNode(child: Piece, build: NodeBuilder, readsRaw: boolean): Piece {
|
|
781
|
+
return (input, pos, ctx) => {
|
|
782
|
+
const v = child(input, pos, ctx)
|
|
783
|
+
if (v !== undefined) return v
|
|
784
|
+
return collapseEmpty(ctx, build, input, pos, EC.e, readsRaw)
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* A collapse whose body is `optional`/`not` terms and then one node: where the
|
|
790
|
+
* character at `pos` opens none of those terms and no trivia, the body is that
|
|
791
|
+
* node (`site-labels.ts` `gatedPassCollapse`), so it runs as a pass-through
|
|
792
|
+
* collapse; anywhere else the node keeps its frame.
|
|
793
|
+
*/
|
|
794
|
+
function gatedPassCollapseNode(
|
|
795
|
+
blockers: readonly ResolvedClass[], last: Piece, classic: Piece, build: NodeBuilder, readsRaw: boolean,
|
|
796
|
+
): Piece {
|
|
797
|
+
return (input, pos, ctx) => {
|
|
798
|
+
const c = lead(input, pos)
|
|
799
|
+
for (let i = 0; i < blockers.length; i++) if (classHas(blockers[i]!, c)) return classic(input, pos, ctx)
|
|
800
|
+
const v = last(input, pos, ctx)
|
|
801
|
+
if (v !== undefined) return v
|
|
802
|
+
return collapseEmpty(ctx, build, input, pos, EC.e, readsRaw)
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
767
806
|
function plainProjectNode(child: Piece, projection: number, type: string): Piece {
|
|
768
807
|
return (input, pos, ctx) => {
|
|
769
808
|
const sCh = ctx._cstChildren
|
|
@@ -1291,21 +1330,30 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1291
1330
|
|
|
1292
1331
|
function dispatchBranch(child: Piece, usesRouted: boolean): DispatchBranch {
|
|
1293
1332
|
if (!usesRouted) {
|
|
1294
|
-
|
|
1295
|
-
|
|
1333
|
+
// The arm runs after the selector's match, so it marks where it starts.
|
|
1334
|
+
return (input, _pos, selEnd, key, ctx) => {
|
|
1335
|
+
const need = markCst(ctx)
|
|
1336
|
+
const mRaw = MRAW
|
|
1337
|
+
const mTl = MTL
|
|
1338
|
+
const mLv = MLV
|
|
1339
|
+
const mFl = need ? ctx._fields?.length ?? 0 : 0
|
|
1340
|
+
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
1341
|
+
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
1342
|
+
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
1296
1343
|
const v = child(input, selEnd, ctx)
|
|
1297
1344
|
if (v === FAIL) {
|
|
1298
|
-
|
|
1345
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1299
1346
|
ctx._fc = true
|
|
1300
1347
|
return FAIL
|
|
1301
1348
|
}
|
|
1302
1349
|
return [key, v]
|
|
1303
1350
|
}
|
|
1304
1351
|
}
|
|
1305
|
-
|
|
1352
|
+
// A routed arm re-reads the selector's match from `pos`: undo the selector,
|
|
1353
|
+
// which leaves every sink at the selector's mark, so that mark is the arm's too.
|
|
1354
|
+
return (input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot) => {
|
|
1306
1355
|
const savedRouted = ctx._routed
|
|
1307
|
-
|
|
1308
|
-
const mark = saveTriviaMark(ctx)
|
|
1356
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1309
1357
|
ctx._routed = { value: key, span: { start: pos, end: selEnd } }
|
|
1310
1358
|
let v: unknown
|
|
1311
1359
|
try {
|
|
@@ -1314,7 +1362,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1314
1362
|
ctx._routed = savedRouted
|
|
1315
1363
|
}
|
|
1316
1364
|
if (v === FAIL) {
|
|
1317
|
-
|
|
1365
|
+
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1318
1366
|
ctx._fc = true
|
|
1319
1367
|
return FAIL
|
|
1320
1368
|
}
|
|
@@ -1327,12 +1375,14 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1327
1375
|
a0: DispatchBranch, a1: DispatchBranch, a2: DispatchBranch, a3: DispatchBranch,
|
|
1328
1376
|
next: DispatchArmBlock | undefined,
|
|
1329
1377
|
): DispatchArmBlock {
|
|
1330
|
-
return (arm, input, pos, selEnd, key,
|
|
1331
|
-
if (arm === start) return a0(input, pos, selEnd, key,
|
|
1332
|
-
if (arm === start + 1) return a1(input, pos, selEnd, key,
|
|
1333
|
-
if (arm === start + 2) return a2(input, pos, selEnd, key,
|
|
1334
|
-
if (arm === start + 3) return a3(input, pos, selEnd, key,
|
|
1335
|
-
return next === undefined
|
|
1378
|
+
return (arm, input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot) => {
|
|
1379
|
+
if (arm === start) return a0(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1380
|
+
if (arm === start + 1) return a1(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1381
|
+
if (arm === start + 2) return a2(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1382
|
+
if (arm === start + 3) return a3(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1383
|
+
return next === undefined
|
|
1384
|
+
? FAIL
|
|
1385
|
+
: next(arm, input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1336
1386
|
}
|
|
1337
1387
|
}
|
|
1338
1388
|
|
|
@@ -1368,7 +1418,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1368
1418
|
if (flight !== undefined) return flight.fwd
|
|
1369
1419
|
const selected = HYBRID?.byIp[ip]
|
|
1370
1420
|
if (selected !== undefined) {
|
|
1371
|
-
|
|
1421
|
+
// The scanner handoff only where the emitted body reads the slot
|
|
1422
|
+
// (`scanHandoffNeeded`); elsewhere the body runs on its labels' scanners.
|
|
1423
|
+
const piece = scanHandoffNeeded(code, ip, closureLabels) ? hybridPiece(selected as Piece) : selected as Piece
|
|
1372
1424
|
memo.set(ip, piece)
|
|
1373
1425
|
reached.add(ip)
|
|
1374
1426
|
return piece
|
|
@@ -1379,16 +1431,14 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1379
1431
|
const holder = { fwd, set: (p: Piece) => { target = p } }
|
|
1380
1432
|
inFlight.set(ip, holder)
|
|
1381
1433
|
|
|
1382
|
-
|
|
1383
|
-
//
|
|
1384
|
-
//
|
|
1385
|
-
//
|
|
1434
|
+
// Match the emitted assembly's label-proven scope alias (`scopeIsIdentity`):
|
|
1435
|
+
// rule entries in a single ambient trivia scope, and no-trivia scopes inside
|
|
1436
|
+
// a token, otherwise reinstall and restore the exact same trivia, labels and
|
|
1437
|
+
// scanner on every call. Install the forwarding
|
|
1386
1438
|
// holder first so a recursive child can still link back through this site.
|
|
1387
|
-
const piece = (
|
|
1388
|
-
&& code[ip + 1]! >= 0
|
|
1389
|
-
&& closureLabels.at(ip).tri === code[ip + 1]!
|
|
1439
|
+
const piece = scopeIsIdentity(code, ip, closureLabels.at(ip))
|
|
1390
1440
|
? link(code[ip + 2]!)
|
|
1391
|
-
: countedPiece(lower(ip), ip)
|
|
1441
|
+
: countedPiece(lookaheadLedNode(ip) ?? lower(ip), ip)
|
|
1392
1442
|
|
|
1393
1443
|
inFlight.delete(ip)
|
|
1394
1444
|
holder.set(piece)
|
|
@@ -1397,6 +1447,48 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1397
1447
|
return piece
|
|
1398
1448
|
}
|
|
1399
1449
|
|
|
1450
|
+
/**
|
|
1451
|
+
* A NODE WHOSE BODY OPENS WITH A LOOKAHEAD opens its frame after it. The body
|
|
1452
|
+
* is a sequence whose first term is `not`/`peek` of a direct terminal: it is
|
|
1453
|
+
* decided at `pos` without writing anything, and a node that fails there would
|
|
1454
|
+
* only open its frame to drop it. So the recognizer runs first, and the node
|
|
1455
|
+
* runs on success with that term already decided (`FIRST_DECIDED`). The
|
|
1456
|
+
* recognizer runs once on either path; the failure is the term's own.
|
|
1457
|
+
*/
|
|
1458
|
+
let FIRST_DECIDED: Piece | undefined
|
|
1459
|
+
let NODE_BODY: Piece | undefined
|
|
1460
|
+
const DECIDED: Piece = (_input, pos) => { EC.e = pos; return null }
|
|
1461
|
+
function lookaheadLedNode(ip: number): Piece | undefined {
|
|
1462
|
+
const op = code[ip]
|
|
1463
|
+
if ((op !== OP_NODE && op !== OP_NODE_TRACK) || hostCst || REC || cfg.probe || cfg.coverage || cfg.trackLines) return undefined
|
|
1464
|
+
if (passCollapse(code, ip, hostCst) || gatedPassCollapse(code, ip, hostCst) !== undefined) return undefined
|
|
1465
|
+
const body = code[ip + 2]!
|
|
1466
|
+
const bop = code[body]
|
|
1467
|
+
if (bop !== OP_SEQ && bop !== OP_SEQV && bop !== OP_SEQX) return undefined
|
|
1468
|
+
const base = bop === OP_SEQX ? body + 3 : body + 2
|
|
1469
|
+
if (code[bop === OP_SEQX ? body + 2 : body + 1]! < 2) return undefined
|
|
1470
|
+
const first = code[base]!
|
|
1471
|
+
const negative = code[first] === OP_NOT
|
|
1472
|
+
const terminal = negative ? scalarTerminalNotChild(code, first)
|
|
1473
|
+
: code[first] === OP_PEEK ? scalarTerminalPeekChild(code, first) : -1
|
|
1474
|
+
if (terminal < 0) return undefined
|
|
1475
|
+
const recognize = scalarFor(terminal)!
|
|
1476
|
+
const xf = fx[code[first + 2]!] as string[]
|
|
1477
|
+
FIRST_DECIDED = DECIDED
|
|
1478
|
+
NODE_BODY = lower(body)
|
|
1479
|
+
const node = lower(ip)
|
|
1480
|
+
NODE_BODY = undefined
|
|
1481
|
+
return negative
|
|
1482
|
+
? (input, pos, ctx) => {
|
|
1483
|
+
if (recognize(input, pos) >= 0) { ctx._fe = pos; ctx._fx = xf; EC.e = pos; return FAIL }
|
|
1484
|
+
return node(input, pos, ctx)
|
|
1485
|
+
}
|
|
1486
|
+
: (input, pos, ctx) => {
|
|
1487
|
+
if (recognize(input, pos) < 0) { ctx._fe = pos; ctx._fx = xf; return FAIL }
|
|
1488
|
+
return node(input, pos, ctx)
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1400
1492
|
function lower(ip: number): Piece {
|
|
1401
1493
|
const op = code[ip]
|
|
1402
1494
|
switch (op) {
|
|
@@ -2041,8 +2133,12 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2041
2133
|
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
2042
2134
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
2043
2135
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
2136
|
+
// A completions probe sees nothing raised inside a lookahead
|
|
2137
|
+
// (`not.ts`, `rollbackLookahead`): those tokens are not reachable here.
|
|
2138
|
+
const probeBest = PROBE ? ctx._probe?.best : undefined
|
|
2044
2139
|
const v = child(input, pos, ctx)
|
|
2045
2140
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2141
|
+
if (PROBE && ctx._probe !== undefined) ctx._probe.best = probeBest ?? null
|
|
2046
2142
|
if (v === FAIL) { EC.e = pos; return null }
|
|
2047
2143
|
// `not.ts:50` — the ASSERTION's own set, at the assertion's position.
|
|
2048
2144
|
ctx._fe = pos
|
|
@@ -2052,6 +2148,22 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2052
2148
|
}
|
|
2053
2149
|
|
|
2054
2150
|
case OP_PEEK: {
|
|
2151
|
+
// A direct terminal decides the lookahead by its match alone: no mark,
|
|
2152
|
+
// no leaf to capture and roll back (`scalarTerminalPeekChild`).
|
|
2153
|
+
const scalarChild = scalarTerminalPeekChild(code, ip)
|
|
2154
|
+
if (scalarChild >= 0) {
|
|
2155
|
+
const recognize = scalarFor(scalarChild)!
|
|
2156
|
+
const xf = fx[code[ip + 2]!] as string[]
|
|
2157
|
+
return (input, pos, ctx) => {
|
|
2158
|
+
if (recognize(input, pos) < 0) {
|
|
2159
|
+
ctx._fe = pos
|
|
2160
|
+
ctx._fx = xf
|
|
2161
|
+
return FAIL
|
|
2162
|
+
}
|
|
2163
|
+
EC.e = pos
|
|
2164
|
+
return null
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2055
2167
|
const child = link(code[ip + 1]!)
|
|
2056
2168
|
const xf = fx[code[ip + 2]!] as string[]
|
|
2057
2169
|
const gate = lookaheadClass(code[ip + 1]!)
|
|
@@ -2065,8 +2177,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2065
2177
|
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
2066
2178
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
2067
2179
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
2180
|
+
const probeBest = PROBE ? ctx._probe?.best : undefined
|
|
2068
2181
|
const v = child(input, pos, ctx)
|
|
2069
2182
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
2183
|
+
if (PROBE && ctx._probe !== undefined) ctx._probe.best = probeBest ?? null
|
|
2070
2184
|
// `peek.ts:60` — the ASSERTION's own set, at the assertion's position.
|
|
2071
2185
|
if (v === FAIL) { ctx._fe = pos; ctx._fx = xf; return FAIL }
|
|
2072
2186
|
EC.e = pos
|
|
@@ -2077,9 +2191,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2077
2191
|
case OP_OPT: {
|
|
2078
2192
|
const child = link(code[ip + 1]!)
|
|
2079
2193
|
const gate = lookaheadClass(code[ip + 1]!)
|
|
2194
|
+
const childClean = CLEAN(code[ip + 1]!)
|
|
2080
2195
|
return (input, pos, ctx) => {
|
|
2081
2196
|
if (gate !== null && !classHas(gate, lead(input, pos))) { EC.e = pos; return null }
|
|
2082
|
-
const need = markCst(ctx)
|
|
2197
|
+
const need = !childClean && markCst(ctx)
|
|
2083
2198
|
const mRaw = MRAW
|
|
2084
2199
|
const mTl = MTL
|
|
2085
2200
|
const mLv = MLV
|
|
@@ -2290,6 +2405,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2290
2405
|
? fns[reducer] as (value: unknown, span: { start: number; end: number }) => unknown
|
|
2291
2406
|
: undefined
|
|
2292
2407
|
const step = closureLabels.at(ip).buf ? nextTermBuffered : nextTerm
|
|
2408
|
+
const decided = FIRST_DECIDED
|
|
2409
|
+
FIRST_DECIDED = undefined
|
|
2410
|
+
const linkFirst = (): Piece => decided ?? link(code[base]!)
|
|
2293
2411
|
|
|
2294
2412
|
const runnerBlock = (
|
|
2295
2413
|
count: number,
|
|
@@ -2388,7 +2506,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2388
2506
|
* sentinel published is the kind of state a later reader will trust.
|
|
2389
2507
|
*/
|
|
2390
2508
|
if (REC) {
|
|
2391
|
-
const first =
|
|
2509
|
+
const first = linkFirst()
|
|
2392
2510
|
const firstSync = sentinelFor(code[base + n]!)
|
|
2393
2511
|
const runnerAt = (i: number): TermRunner => {
|
|
2394
2512
|
const sync = sentinelFor(code[base + n + i]!)
|
|
@@ -2470,15 +2588,32 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2470
2588
|
|
|
2471
2589
|
// A `field(…, { leadingTrivia: true })` term is a boundary fact too: the
|
|
2472
2590
|
// runner hands it the cursor from BEFORE the scan (`LEAD`, cell.ts).
|
|
2591
|
+
// So is a lookahead that holds its gap for the next term (`hold-gap.ts`).
|
|
2473
2592
|
let hasAdj = false
|
|
2474
2593
|
for (let i = 1; i < n; i++) {
|
|
2475
2594
|
const kidIp = code[base + i]!
|
|
2476
|
-
if (code[kidIp] === OP_ADJ || leadingTriviaFieldAt(code, k, kidIp)) { hasAdj = true; break }
|
|
2595
|
+
if (code[kidIp] === OP_ADJ || leadingTriviaFieldAt(code, k, kidIp) || holdsGap(code, k, base, n, i, NULLABLE)) { hasAdj = true; break }
|
|
2477
2596
|
}
|
|
2478
2597
|
if (hasAdj) {
|
|
2479
|
-
const first =
|
|
2598
|
+
const first = linkFirst()
|
|
2480
2599
|
const runnerAt = (i: number): TermRunner => {
|
|
2481
2600
|
const kidIp = code[base + i]!
|
|
2601
|
+
if (holdsGap(code, k, base, n, i, NULLABLE)) {
|
|
2602
|
+
const kid = link(kidIp)
|
|
2603
|
+
return (input, cur, ctx) => {
|
|
2604
|
+
if (ctx.trivia === undefined) {
|
|
2605
|
+
const v = kid(input, cur, ctx)
|
|
2606
|
+
if (v === FAIL) return -1
|
|
2607
|
+
TERMV = v
|
|
2608
|
+
return EC.e
|
|
2609
|
+
}
|
|
2610
|
+
const scanEnd = skipTrivia(input, cur, ctx)
|
|
2611
|
+
const v = kid(input, scanEnd, ctx)
|
|
2612
|
+
if (v === FAIL) return -1
|
|
2613
|
+
TERMV = v
|
|
2614
|
+
return scanEnd
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2482
2617
|
if (code[kidIp] !== OP_ADJ) {
|
|
2483
2618
|
const kid = link(kidIp)
|
|
2484
2619
|
if (leadingTriviaFieldAt(code, k, kidIp)) {
|
|
@@ -2564,7 +2699,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2564
2699
|
* runs, so there is no arity this fails to lower.
|
|
2565
2700
|
*/
|
|
2566
2701
|
if (n === 1) {
|
|
2567
|
-
const k0 =
|
|
2702
|
+
const k0 = linkFirst()
|
|
2568
2703
|
if (fused) {
|
|
2569
2704
|
return (input, pos, ctx) => {
|
|
2570
2705
|
const v = k0(input, pos, ctx)
|
|
@@ -2587,7 +2722,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2587
2722
|
}
|
|
2588
2723
|
|
|
2589
2724
|
if (n === 2) {
|
|
2590
|
-
const k0 =
|
|
2725
|
+
const k0 = linkFirst(), k1 = link(code[base + 1]!)
|
|
2591
2726
|
if (fused) {
|
|
2592
2727
|
return (input, pos, ctx) => {
|
|
2593
2728
|
const v0 = k0(input, pos, ctx)
|
|
@@ -2621,7 +2756,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2621
2756
|
}
|
|
2622
2757
|
|
|
2623
2758
|
if (n === 3) {
|
|
2624
|
-
const k0 =
|
|
2759
|
+
const k0 = linkFirst(), k1 = link(code[base + 1]!), k2 = link(code[base + 2]!)
|
|
2625
2760
|
if (fused) {
|
|
2626
2761
|
return (input, pos, ctx) => {
|
|
2627
2762
|
const v0 = k0(input, pos, ctx)
|
|
@@ -2723,7 +2858,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2723
2858
|
start + count < n ? bind(start + count) : undefined,
|
|
2724
2859
|
)
|
|
2725
2860
|
}
|
|
2726
|
-
const first =
|
|
2861
|
+
const first = linkFirst()
|
|
2727
2862
|
const tail = bind(1)
|
|
2728
2863
|
const runDirectTerms = (
|
|
2729
2864
|
input: string, pos: number, ctx: ParseContext, values: unknown[] | undefined,
|
|
@@ -2767,6 +2902,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2767
2902
|
const n = code[ip + 2]!
|
|
2768
2903
|
const choiceFx = fx[code[ip + 3]!] as string[]
|
|
2769
2904
|
const base = ip + 4
|
|
2905
|
+
// The arms first, so the answer reads theirs rather than walking them again.
|
|
2906
|
+
for (let i = 0; i < n; i++) link(code[base + i]!)
|
|
2907
|
+
const armsClean = CLEAN(ip)
|
|
2770
2908
|
|
|
2771
2909
|
// THE TWO CHOICE SHAPES ARE TWO PIECES. `exec.ts` tested `table.exclusive`
|
|
2772
2910
|
// on every choice execution; it is table data, so it selects here.
|
|
@@ -2903,7 +3041,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2903
3041
|
|
|
2904
3042
|
return (input, pos, ctx) => {
|
|
2905
3043
|
const c = lead(input, pos)
|
|
2906
|
-
const need = markCst(ctx)
|
|
3044
|
+
const need = !armsClean && markCst(ctx)
|
|
2907
3045
|
const mRaw = MRAW
|
|
2908
3046
|
const mTl = MTL
|
|
2909
3047
|
const mLv = MLV
|
|
@@ -3068,7 +3206,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3068
3206
|
if (pn !== 0) {
|
|
3069
3207
|
return (input, pos, ctx) => {
|
|
3070
3208
|
const c = lead(input, pos)
|
|
3071
|
-
const need = markCst(ctx)
|
|
3209
|
+
const need = !armsClean && markCst(ctx)
|
|
3072
3210
|
const mRaw = MRAW
|
|
3073
3211
|
const mTl = MTL
|
|
3074
3212
|
const mLv = MLV
|
|
@@ -3098,7 +3236,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3098
3236
|
}
|
|
3099
3237
|
return (input, pos, ctx) => {
|
|
3100
3238
|
const c = lead(input, pos)
|
|
3101
|
-
const need = markCst(ctx)
|
|
3239
|
+
const need = !armsClean && markCst(ctx)
|
|
3102
3240
|
const mRaw = MRAW
|
|
3103
3241
|
const mTl = MTL
|
|
3104
3242
|
const mLv = MLV
|
|
@@ -3120,7 +3258,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3120
3258
|
|
|
3121
3259
|
return (input, pos, ctx) => {
|
|
3122
3260
|
const c = lead(input, pos)
|
|
3123
|
-
const need = markCst(ctx)
|
|
3261
|
+
const need = !armsClean && markCst(ctx)
|
|
3124
3262
|
const mRaw = MRAW
|
|
3125
3263
|
const mTl = MTL
|
|
3126
3264
|
const mLv = MLV
|
|
@@ -3166,6 +3304,22 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3166
3304
|
}
|
|
3167
3305
|
}
|
|
3168
3306
|
|
|
3307
|
+
case OP_RGATE: {
|
|
3308
|
+
const cls = cc[code[ip + 1]!]!
|
|
3309
|
+
const xf = fx[code[ip + 3]!] as string[]
|
|
3310
|
+
const child = link(code[ip + 2]!)
|
|
3311
|
+
// Forwards under a probe or tolerant recovery, as OP_GATE does.
|
|
3312
|
+
if (cfg.tolerant || cfg.probe) return child
|
|
3313
|
+
return (input, pos, ctx) => {
|
|
3314
|
+
const item = ctx._routed
|
|
3315
|
+
if (item !== undefined && item.span.start === pos && !classHas(cls, lead(input, item.span.end))) {
|
|
3316
|
+
ctx._fe = item.span.end; ctx._fx = xf
|
|
3317
|
+
return FAIL
|
|
3318
|
+
}
|
|
3319
|
+
return child(input, pos, ctx)
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3169
3323
|
case OP_ARMGATE: {
|
|
3170
3324
|
// Predicate, arm and expected set all bound HERE — the body reads no
|
|
3171
3325
|
// opcode, decodes no operand and tests no setting.
|
|
@@ -3263,6 +3417,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3263
3417
|
// its FIRST item through `repItem` and so skips leading trivia. The shape
|
|
3264
3418
|
// identifies itself, and it is table data, so it is decided here.
|
|
3265
3419
|
const skipBeforeFirst = sepIp < 0 && min === 0
|
|
3420
|
+
const itemClean = sepIp < 0 && CLEAN(code[ip + 1]!) && !rowNullable(code, k, code[ip + 1]!, NULLABLE)
|
|
3266
3421
|
|
|
3267
3422
|
/**
|
|
3268
3423
|
* TOLERANT RECOVERY — the third implementation of `recoverScan`'s
|
|
@@ -3390,6 +3545,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3390
3545
|
const out: unknown[] | undefined = collect ? [] : undefined
|
|
3391
3546
|
const hasTrivia = ctx.trivia !== undefined
|
|
3392
3547
|
const needMark = rollbackNeeded(ctx)
|
|
3548
|
+
// A clean item with no trivia scanned in front of it has nothing to undo.
|
|
3549
|
+
const markLater = needMark && (!itemClean || hasTrivia)
|
|
3550
|
+
const markFirst = needMark && (!itemClean || (hasTrivia && skipBeforeFirst))
|
|
3393
3551
|
// A completions probe deliberately observes swallowed item failures.
|
|
3394
3552
|
// Keep the ordinary child path while one is active so this structural
|
|
3395
3553
|
// fast stop cannot erase an item completion at the cursor.
|
|
@@ -3409,14 +3567,15 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3409
3567
|
// Per ITERATION, never hoisted: a preceding item's `node()` opened and
|
|
3410
3568
|
// closed a capture buffer, so `ctx._cstBuf` is not the object it was
|
|
3411
3569
|
// at the loop head. `needMark` is the pre-existing loop-invariant.
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
const
|
|
3415
|
-
const
|
|
3416
|
-
const
|
|
3417
|
-
const
|
|
3418
|
-
const
|
|
3419
|
-
const
|
|
3570
|
+
const markIt = count > 0 ? markLater : markFirst
|
|
3571
|
+
if (markIt) markCst(ctx)
|
|
3572
|
+
const mRaw = markIt ? MRAW : 0
|
|
3573
|
+
const mTl = markIt ? MTL : 0
|
|
3574
|
+
const mLv = markIt ? MLV : 0
|
|
3575
|
+
const mFl = markIt ? ctx._fields?.length ?? 0 : 0
|
|
3576
|
+
const mEr = markIt ? ctx._errors?.length ?? 0 : 0
|
|
3577
|
+
const mLog = markIt ? ctx._triviaLog?.length ?? 0 : 0
|
|
3578
|
+
const mRoot = markIt ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
3420
3579
|
let itemStart = cur
|
|
3421
3580
|
let sepEnd = -1
|
|
3422
3581
|
const viaRepItem = sep === undefined && count >= min && (count > 0 || skipBeforeFirst)
|
|
@@ -3428,7 +3587,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3428
3587
|
if (sepLeads) LEAD.at = cur
|
|
3429
3588
|
const sv = sep(input, sp, ctx)
|
|
3430
3589
|
if (sv === FAIL) {
|
|
3431
|
-
if (
|
|
3590
|
+
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3432
3591
|
if (committed(ctx)) return FAIL
|
|
3433
3592
|
break
|
|
3434
3593
|
}
|
|
@@ -3439,19 +3598,19 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3439
3598
|
itemStart = skipTrivia(input, itemStart, ctx)
|
|
3440
3599
|
}
|
|
3441
3600
|
if (itemStart >= input.length && viaRepItem) {
|
|
3442
|
-
if (
|
|
3601
|
+
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3443
3602
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
3444
3603
|
break
|
|
3445
3604
|
}
|
|
3446
3605
|
if (count >= min && gateItems && !itemMayStart!(input, itemStart)) {
|
|
3447
|
-
if (
|
|
3606
|
+
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3448
3607
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
3449
3608
|
break
|
|
3450
3609
|
}
|
|
3451
3610
|
ctx._fc = false
|
|
3452
3611
|
const v = child(input, itemStart, ctx)
|
|
3453
3612
|
if (v === FAIL) {
|
|
3454
|
-
if (
|
|
3613
|
+
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3455
3614
|
if (committed(ctx)) return FAIL
|
|
3456
3615
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
3457
3616
|
break
|
|
@@ -3459,7 +3618,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3459
3618
|
if (EC.e === itemStart && viaRepItem) {
|
|
3460
3619
|
// Zero-width item: `repItem`'s TERMINATION device, not a semantic
|
|
3461
3620
|
// filter — and it applies only where `repItem` runs.
|
|
3462
|
-
if (
|
|
3621
|
+
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3463
3622
|
break
|
|
3464
3623
|
}
|
|
3465
3624
|
if (out !== undefined) out.push(v)
|
|
@@ -3532,7 +3691,14 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3532
3691
|
const runOther = other === undefined ? undefined : dispatchBranch(other, otherRouted)
|
|
3533
3692
|
|
|
3534
3693
|
return (input, pos, ctx) => {
|
|
3535
|
-
const
|
|
3694
|
+
const need = markCst(ctx)
|
|
3695
|
+
const mRaw = MRAW
|
|
3696
|
+
const mTl = MTL
|
|
3697
|
+
const mLv = MLV
|
|
3698
|
+
const mFl = need ? ctx._fields?.length ?? 0 : 0
|
|
3699
|
+
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
3700
|
+
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
3701
|
+
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
3536
3702
|
const selVal = selector(input, pos, ctx)
|
|
3537
3703
|
if (selVal === FAIL) return FAIL
|
|
3538
3704
|
const selEnd = EC.e
|
|
@@ -3545,9 +3711,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3545
3711
|
ctx._fx = expected
|
|
3546
3712
|
return FAIL
|
|
3547
3713
|
}
|
|
3548
|
-
return runOther(input, pos, selEnd, key,
|
|
3714
|
+
return runOther(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3549
3715
|
}
|
|
3550
|
-
return runArm!(arm, input, pos, selEnd, key,
|
|
3716
|
+
return runArm!(arm, input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3551
3717
|
}
|
|
3552
3718
|
}
|
|
3553
3719
|
|
|
@@ -3591,7 +3757,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3591
3757
|
return nd
|
|
3592
3758
|
}
|
|
3593
3759
|
}
|
|
3594
|
-
const child = link(code[ip + 2]!)
|
|
3760
|
+
const child = NODE_BODY ?? link(code[ip + 2]!)
|
|
3761
|
+
NODE_BODY = undefined
|
|
3595
3762
|
const proj = code[ip + 4]!
|
|
3596
3763
|
const buildIdx = code[ip + 1]!
|
|
3597
3764
|
const type = k[code[ip + 5]!] as string
|
|
@@ -3628,6 +3795,25 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3628
3795
|
if (build !== undefined && proj < 0 && flags === 18) {
|
|
3629
3796
|
return childrenOnlyFieldsBuildNode(child, build)
|
|
3630
3797
|
}
|
|
3798
|
+
if (build !== undefined && passCollapse(code, ip, hostCst)) {
|
|
3799
|
+
return passCollapseNode(child, build, flags === 32)
|
|
3800
|
+
}
|
|
3801
|
+
// Not under recovery or a probe: there a gated-out term still runs.
|
|
3802
|
+
const gated = build !== undefined && !cfg.tolerant && !cfg.probe ? gatedPassCollapse(code, ip, hostCst) : undefined
|
|
3803
|
+
const triviaLead = gated === undefined ? undefined : triviaLeadRanges(closureLabels.at(code[ip + 2]!).tri, trivia)
|
|
3804
|
+
if (build !== undefined && gated !== undefined && triviaLead !== undefined) {
|
|
3805
|
+
const classic = flags === 32 ? collapseBuildNode(child, build) : childrenOnlyCollapseBuildNode(child, build)
|
|
3806
|
+
return gatedPassCollapseNode(
|
|
3807
|
+
[
|
|
3808
|
+
...gated.leads.map(g => cc[code[g + 1]!]!),
|
|
3809
|
+
resolveClass(encodeClassSpec([
|
|
3810
|
+
...triviaLead,
|
|
3811
|
+
...gated.lits.map(i => { const c = (k[i] as string).codePointAt(0)!; return { lo: c, hi: c } }),
|
|
3812
|
+
])),
|
|
3813
|
+
],
|
|
3814
|
+
link(gated.last), classic, build, flags === 32,
|
|
3815
|
+
)
|
|
3816
|
+
}
|
|
3631
3817
|
if (build !== undefined && proj < 0 && flags === 32) {
|
|
3632
3818
|
return collapseBuildNode(child, build)
|
|
3633
3819
|
}
|
|
@@ -3853,6 +4039,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3853
4039
|
}
|
|
3854
4040
|
const notChild = scalarTerminalNotChild(code, ip)
|
|
3855
4041
|
if (notChild >= 0) scalarFor(notChild)
|
|
4042
|
+
const peekChild = scalarTerminalPeekChild(code, ip)
|
|
4043
|
+
if (peekChild >= 0) scalarFor(peekChild)
|
|
3856
4044
|
}
|
|
3857
4045
|
/**
|
|
3858
4046
|
* THE BUILD'S OWN ANSWER, TRIED BEFORE THE CONSTRUCTOR.
|
|
@@ -3891,6 +4079,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3891
4079
|
matchesAt, recoverScan, orSentinel, captureError,
|
|
3892
4080
|
scalarRecognizers, commitTriviaScan, scanTriviaCompact, t.lex, adjacencyHolds, t.lexPrograms,
|
|
3893
4081
|
LEAD, triviaFieldCapture, skipDeferredTrivia,
|
|
4082
|
+
collapseEmpty,
|
|
3894
4083
|
)
|
|
3895
4084
|
emitReached = new Set(pre.reached)
|
|
3896
4085
|
if (emitted.ext !== undefined) HYBRID = emitted
|
|
@@ -3974,6 +4163,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3974
4163
|
matchesAt, recoverScan, orSentinel, captureError,
|
|
3975
4164
|
scalarRecognizers, commitTriviaScan, scanTriviaCompact, t.lex, adjacencyHolds, t.lexPrograms,
|
|
3976
4165
|
LEAD, triviaFieldCapture, skipDeferredTrivia,
|
|
4166
|
+
collapseEmpty,
|
|
3977
4167
|
)
|
|
3978
4168
|
emitReached = em.reached
|
|
3979
4169
|
} catch (e) {
|