parseman 0.47.1 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -21
- package/dist/analysis/diagnostics.js +2863 -353
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +2854 -344
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/expect.d.ts +1 -0
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/expected.d.ts +8 -0
- package/dist/combinators/expected.d.ts.map +1 -0
- package/dist/combinators/first-set.d.ts +8 -0
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/keywords.d.ts +4 -0
- package/dist/combinators/keywords.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/not.d.ts.map +1 -1
- package/dist/combinators/peek.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/sequence.d.ts.map +1 -1
- package/dist/combinators/trivia-skip.d.ts +16 -3
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/compiler/direct-projection.d.ts +12 -0
- package/dist/compiler/direct-projection.d.ts.map +1 -0
- package/dist/compiler/token-alphabet.d.ts +845 -5
- package/dist/compiler/token-alphabet.d.ts.map +1 -1
- package/dist/compiler/token-capability.d.ts +3 -0
- package/dist/compiler/token-capability.d.ts.map +1 -0
- package/dist/compiler/token-capability.js +3192 -0
- package/dist/compiler/token-capability.js.map +6 -0
- package/dist/cst/trivia-charscan.d.ts +3 -0
- package/dist/cst/trivia-charscan.d.ts.map +1 -1
- package/dist/cst/trivia-css-scanner.d.ts +5 -0
- package/dist/cst/trivia-css-scanner.d.ts.map +1 -0
- package/dist/cst/trivia-kinds.d.ts.map +1 -1
- package/dist/index.js +2862 -339
- package/dist/index.js.map +3 -3
- package/dist/language-service/index.js +5 -1
- package/dist/language-service/index.js.map +1 -1
- package/dist/plugin/index.js +2870 -347
- package/dist/plugin/index.js.map +3 -3
- package/dist/spec/index.js +315 -46
- package/dist/spec/index.js.map +2 -2
- package/dist/table/assemble.d.ts.map +1 -1
- package/dist/table/child-slots.d.ts.map +1 -1
- package/dist/table/compile-rule-map.d.ts +3 -1
- package/dist/table/compile-rule-map.d.ts.map +1 -1
- package/dist/table/emit-assembly.d.ts +12 -13
- package/dist/table/emit-assembly.d.ts.map +1 -1
- package/dist/table/emit.d.ts.map +1 -1
- package/dist/table/encode.d.ts.map +1 -1
- package/dist/table/exec-baseline.d.ts.map +1 -1
- package/dist/table/exec.d.ts.map +1 -1
- package/dist/table/index.js +2819 -300
- package/dist/table/index.js.map +3 -3
- package/dist/table/lex-body.d.ts +9 -0
- package/dist/table/lex-body.d.ts.map +1 -0
- package/dist/table/lex-program.d.ts +25 -0
- package/dist/table/lex-program.d.ts.map +1 -0
- package/dist/table/ops.d.ts +43 -7
- package/dist/table/ops.d.ts.map +1 -1
- package/dist/table/program.d.ts +50 -2
- package/dist/table/program.d.ts.map +1 -1
- package/dist/table/scalar-terminal.d.ts +13 -0
- package/dist/table/scalar-terminal.d.ts.map +1 -0
- package/dist/table/site-labels.d.ts.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +11 -11
- package/src/combinators/expect.ts +1 -0
- package/src/combinators/expected.ts +17 -0
- package/src/combinators/first-set.ts +58 -5
- package/src/combinators/grammar.ts +8 -3
- package/src/combinators/keywords.ts +22 -6
- package/src/combinators/literal.ts +2 -1
- package/src/combinators/not.ts +5 -1
- package/src/combinators/peek.ts +5 -1
- package/src/combinators/regex.ts +5 -3
- package/src/combinators/repeat.ts +4 -10
- package/src/combinators/sequence.ts +17 -14
- package/src/combinators/trivia-skip.ts +46 -13
- package/src/compiler/direct-projection.ts +43 -0
- package/src/compiler/token-alphabet.ts +4316 -12
- package/src/compiler/token-capability.ts +6 -0
- package/src/cst/trivia-charscan.ts +34 -0
- package/src/cst/trivia-css-scanner.ts +142 -0
- package/src/cst/trivia-kinds.ts +3 -1
- package/src/plugin/index.ts +4 -4
- package/src/table/assemble.ts +1211 -221
- package/src/table/child-slots.ts +2 -1
- package/src/table/compile-rule-map.ts +21 -3
- package/src/table/emit-assembly.ts +328 -85
- package/src/table/emit.ts +16 -2
- package/src/table/encode.ts +242 -50
- package/src/table/exec-baseline.ts +3 -7
- package/src/table/exec.ts +121 -7
- package/src/table/lex-body.ts +30 -0
- package/src/table/lex-program.ts +422 -0
- package/src/table/ops.ts +45 -7
- package/src/table/program.ts +46 -2
- package/src/table/scalar-terminal.ts +51 -0
- package/src/table/site-labels.ts +11 -1
- package/src/table/stamp.ts +1 -1
- package/src/types.ts +1 -1
- package/src/version.ts +1 -1
- package/dist/analysis/diagnostics.cjs +0 -13824
- package/dist/analysis/diagnostics.cjs.map +0 -6
- package/dist/cli/index.cjs +0 -14925
- package/dist/cli/index.cjs.map +0 -6
- package/dist/index.cjs +0 -16391
- package/dist/index.cjs.map +0 -6
- package/dist/language-service/index.cjs +0 -1304
- package/dist/language-service/index.cjs.map +0 -6
- package/dist/oracle/index.cjs +0 -254
- package/dist/oracle/index.cjs.map +0 -6
- package/dist/plugin/index.cjs +0 -16097
- package/dist/plugin/index.cjs.map +0 -6
- package/dist/run/index.cjs +0 -577
- package/dist/run/index.cjs.map +0 -6
- package/dist/spec/index.cjs +0 -6236
- package/dist/spec/index.cjs.map +0 -6
- package/dist/table/index.cjs +0 -11463
- package/dist/table/index.cjs.map +0 -6
package/src/table/exec.ts
CHANGED
|
@@ -6,7 +6,10 @@ import { projectChild, unwrapChild } from '../combinators/node.ts'
|
|
|
6
6
|
import { asciiFoldEq } from '../combinators/literal.ts'
|
|
7
7
|
import { cstOutputHost } from '../compiler/build-arity.ts'
|
|
8
8
|
import { consumeTrivia } from '../combinators/trivia-skip.ts'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
advanceTrivia, commitTriviaScan, needsDeferredTriviaCommit, rollbackScannedTriviaAt, rollbackTrivia,
|
|
11
|
+
rollbackTriviaAt, saveTriviaMark, scanTriviaCompact, skipTriviaScanned, type FastTriviaScanner,
|
|
12
|
+
} from '../combinators/trivia-skip.ts'
|
|
10
13
|
import {
|
|
11
14
|
beginCstNodeCapture, cstCaptureActive, cstLeavesLen, cstRawLen, cstTlLen,
|
|
12
15
|
demoteCapturedToRaw, endCstNodeCapture, pushCstChild, pushCstLeaf,
|
|
@@ -19,6 +22,7 @@ import {
|
|
|
19
22
|
OP_FIELD, OP_DISPATCH, OP_ROUTED, OP_LIT_CI, OP_LIT_CI_TRACK, OP_TOKEN, OP_WITHCTX, OP_GUARD, OP_ATTEMPT, OP_LABEL,
|
|
20
23
|
OP_COV,
|
|
21
24
|
OP_ADJ, OP_GREEDY, OP_REJECT, OP_ARMGATE,
|
|
25
|
+
OP_LEX_BODY, OP_LEX_PROGRAM,
|
|
22
26
|
} from './ops.ts'
|
|
23
27
|
import { adjacencyHolds, adjacencyMisuse } from '../combinators/adjacency.ts'
|
|
24
28
|
import { failAt } from '../combinators/probe.ts'
|
|
@@ -41,9 +45,10 @@ import { refuseUnclassifiedRootScope } from '../cst/root-trivia-scope.ts'
|
|
|
41
45
|
import { captureError, firstSetSentinel, matchesAt, orSentinel, recoverScan } from '../recovery/scan.ts'
|
|
42
46
|
import {
|
|
43
47
|
decodeClassSpec, expandCompact, resolveTable,
|
|
44
|
-
type CompactProgram, type ResolvedClass, type ResolvedDispatch, type ResolvedDispatchSpec,
|
|
48
|
+
type CompactProgram, type ResolvedClass, type ResolvedDispatch, type ResolvedDispatchSpec, type ResolvedTable,
|
|
45
49
|
type SubtreeRef, type TableProgram, type TableRule,
|
|
46
50
|
} from './program.ts'
|
|
51
|
+
import { scalarTerminalNotChild } from './scalar-terminal.ts'
|
|
47
52
|
|
|
48
53
|
/**
|
|
49
54
|
* THE SHARED DRIVER.
|
|
@@ -199,6 +204,8 @@ function makeDriver(
|
|
|
199
204
|
fx: readonly (readonly string[])[],
|
|
200
205
|
disp: readonly ResolvedDispatch[],
|
|
201
206
|
dsp: readonly ResolvedDispatchSpec[],
|
|
207
|
+
lex: readonly ((input: string, pos: number) => number)[],
|
|
208
|
+
lexPrograms: ResolvedTable['lexPrograms'],
|
|
202
209
|
trivia: readonly unknown[],
|
|
203
210
|
triviaScan: readonly (FastTriviaScanner | null)[],
|
|
204
211
|
triviaLabelled: readonly boolean[],
|
|
@@ -353,9 +360,7 @@ function makeDriver(
|
|
|
353
360
|
// grammar that already had a scanner — keeps the exact branch it had.
|
|
354
361
|
if (s !== null) return skipTriviaScanned(s, input, cur, ctx)
|
|
355
362
|
if (needsDeferredTriviaCommit(ctx)) {
|
|
356
|
-
|
|
357
|
-
scan.commit()
|
|
358
|
-
return scan.end
|
|
363
|
+
return commitTriviaScan(scanTriviaCompact(input, cur, ctx))
|
|
359
364
|
}
|
|
360
365
|
return advanceTrivia(input, cur, ctx)
|
|
361
366
|
}
|
|
@@ -622,6 +627,8 @@ function makeDriver(
|
|
|
622
627
|
ctx._routed = savedRouted
|
|
623
628
|
}
|
|
624
629
|
} else {
|
|
630
|
+
// The reference engine mirrors dispatch.parse(): plain arms do not
|
|
631
|
+
// install routed state and stay outside the exception guard.
|
|
625
632
|
v = exec(target, input, selEnd, ctx)
|
|
626
633
|
}
|
|
627
634
|
if (v === FAIL) {
|
|
@@ -692,6 +699,84 @@ function makeDriver(
|
|
|
692
699
|
return value
|
|
693
700
|
}
|
|
694
701
|
|
|
702
|
+
case OP_LEX_BODY: {
|
|
703
|
+
const recognized = lex[code[ip + 1]!]!(input, pos)
|
|
704
|
+
if (recognized < 0) {
|
|
705
|
+
ctx._fe = pos
|
|
706
|
+
ctx._fx = fx[code[ip + 2]!] as string[]
|
|
707
|
+
if (ctx._probe !== undefined) failAt(ctx, ctx._fx, pos)
|
|
708
|
+
return FAIL
|
|
709
|
+
}
|
|
710
|
+
const lineFlags = code[ip + 4]!
|
|
711
|
+
const hasSuffix = (lineFlags & 4) !== 0
|
|
712
|
+
const suffixMatched = hasSuffix && recognized % 2 === 1
|
|
713
|
+
const end = (recognized - (suffixMatched ? 1 : 0)) / 2
|
|
714
|
+
if ((lineFlags & 1) !== 0) trackLines(ctx, input, suffixMatched ? end - 1 : end)
|
|
715
|
+
if (hasSuffix) ctx._fc = false
|
|
716
|
+
if (hasSuffix && suffixMatched && (lineFlags & 2) !== 0) trackLines(ctx, input, end)
|
|
717
|
+
if (hasSuffix && !suffixMatched) {
|
|
718
|
+
const suffixExpected = fx[code[ip + 3]!] as string[]
|
|
719
|
+
ctx._fe = end
|
|
720
|
+
ctx._fx = suffixExpected
|
|
721
|
+
if (ctx._probe !== undefined) failAt(ctx, suffixExpected, end)
|
|
722
|
+
}
|
|
723
|
+
const value = input.slice(pos, end)
|
|
724
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
|
|
725
|
+
EC.e = end
|
|
726
|
+
return value
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
case OP_LEX_PROGRAM: {
|
|
730
|
+
const run = lexPrograms[code[ip + 1]!]!
|
|
731
|
+
const scanId = run.scan
|
|
732
|
+
if (scanId === undefined) {
|
|
733
|
+
const end = run(input, pos, ctx)
|
|
734
|
+
if (end < 0) return FAIL
|
|
735
|
+
const value = input.slice(pos, end)
|
|
736
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
|
|
737
|
+
EC.e = end
|
|
738
|
+
return value
|
|
739
|
+
}
|
|
740
|
+
// The recognizer is the canonical balanced() pool entry. Reproduce the
|
|
741
|
+
// enclosing token boundary here so its internal token leaf and skippers
|
|
742
|
+
// cannot publish into the outer collector; this row publishes one leaf.
|
|
743
|
+
const sTrivia = ctx.trivia, sKinds = ctx.triviaKindLabels
|
|
744
|
+
const sBuf = ctx._cstBuf, sChildren = ctx._cstChildren, sLeaves = ctx._cstLeaves
|
|
745
|
+
const sRaw = ctx._cstRawChildren, sTl = ctx._cstTriviaLog
|
|
746
|
+
const sOuterTl = ctx._triviaLog, sRootTl = ctx._rootTriviaLog
|
|
747
|
+
const wasCapturing = cstCaptureActive(ctx)
|
|
748
|
+
const sScan = SCAN
|
|
749
|
+
SCAN = null
|
|
750
|
+
ctx.trivia = undefined
|
|
751
|
+
ctx.triviaKindLabels = undefined
|
|
752
|
+
ctx._cstBuf = undefined
|
|
753
|
+
ctx._cstChildren = undefined
|
|
754
|
+
ctx._cstLeaves = undefined
|
|
755
|
+
ctx._cstRawChildren = undefined
|
|
756
|
+
ctx._cstTriviaLog = undefined
|
|
757
|
+
ctx._triviaLog = undefined
|
|
758
|
+
ctx._rootTriviaLog = undefined
|
|
759
|
+
let end: number
|
|
760
|
+
try { end = run(input, pos, ctx, scans[scanId]) }
|
|
761
|
+
finally {
|
|
762
|
+
SCAN = sScan
|
|
763
|
+
ctx.trivia = sTrivia
|
|
764
|
+
ctx.triviaKindLabels = sKinds
|
|
765
|
+
ctx._cstBuf = sBuf
|
|
766
|
+
ctx._cstChildren = sChildren
|
|
767
|
+
ctx._cstLeaves = sLeaves
|
|
768
|
+
ctx._cstRawChildren = sRaw
|
|
769
|
+
ctx._cstTriviaLog = sTl
|
|
770
|
+
ctx._triviaLog = sOuterTl
|
|
771
|
+
ctx._rootTriviaLog = sRootTl
|
|
772
|
+
}
|
|
773
|
+
if (end < 0) return FAIL
|
|
774
|
+
const value = input.slice(pos, end)
|
|
775
|
+
if (wasCapturing) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
|
|
776
|
+
EC.e = end
|
|
777
|
+
return value
|
|
778
|
+
}
|
|
779
|
+
|
|
695
780
|
case OP_SCAN: {
|
|
696
781
|
const c = scans[code[ip + 1]!]!
|
|
697
782
|
const r = c.parse(input, pos, ctx)
|
|
@@ -854,7 +939,9 @@ function makeDriver(
|
|
|
854
939
|
if (REC) ctx._sync = inheritedSync
|
|
855
940
|
EC.e = cur
|
|
856
941
|
if (fused) {
|
|
857
|
-
const
|
|
942
|
+
const reducer = code[ip + 1]!
|
|
943
|
+
if (reducer < 0) return values![~reducer]
|
|
944
|
+
const fn = fns[reducer] as (value: unknown, span: { start: number; end: number }) => unknown
|
|
858
945
|
if (COUNT) siteFn('SEQX fn()', fn)
|
|
859
946
|
return fn(values, { start: pos, end: cur })
|
|
860
947
|
}
|
|
@@ -1116,6 +1203,11 @@ function makeDriver(
|
|
|
1116
1203
|
const mySync = REC ? ctx._sync : undefined
|
|
1117
1204
|
const recFx = REC ? fx[code[ip + 6]!] as string[] : undefined
|
|
1118
1205
|
const sepSent = REC ? sentinelFor(code[ip + 7]!) : undefined
|
|
1206
|
+
const itemClassIndex = sep < 0 ? code[ip + 7]! : -1
|
|
1207
|
+
const itemClass = REC && itemClassIndex >= 0 ? cc[itemClassIndex]! : undefined
|
|
1208
|
+
// `completionsAt` observes failures swallowed by an optional iteration;
|
|
1209
|
+
// tolerant recovery does too. Both keep the ordinary child path.
|
|
1210
|
+
const gateItems = itemClass !== undefined && ctx._tolerant !== true && ctx._probe === undefined
|
|
1119
1211
|
let cur = pos
|
|
1120
1212
|
let count = 0
|
|
1121
1213
|
for (;;) {
|
|
@@ -1128,6 +1220,8 @@ function makeDriver(
|
|
|
1128
1220
|
// must attempt the separator so its failure sets the expected set, which
|
|
1129
1221
|
// is the only thing an under-`min` list has to report.
|
|
1130
1222
|
if (sep >= 0 && count > 0 && count >= min && cur >= input.length) break
|
|
1223
|
+
if (count >= min && sep < 0 && !hasTrivia
|
|
1224
|
+
&& gateItems && !classHas(itemClass!, lead(input, cur))) break
|
|
1131
1225
|
// One mark pair for the whole loop when a rollback is even possible,
|
|
1132
1226
|
// refreshed per iteration rather than reallocated.
|
|
1133
1227
|
// SCALAR MARKS. This loop took TWO allocations per item (a CST mark and
|
|
@@ -1199,6 +1293,11 @@ function makeDriver(
|
|
|
1199
1293
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
1200
1294
|
break
|
|
1201
1295
|
}
|
|
1296
|
+
if (count >= min && gateItems && !classHas(itemClass!, lead(input, itemStart))) {
|
|
1297
|
+
if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1298
|
+
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
1299
|
+
break
|
|
1300
|
+
}
|
|
1202
1301
|
ctx._fc = false
|
|
1203
1302
|
const v = exec(child, input, itemStart, ctx)
|
|
1204
1303
|
if (v === FAIL) {
|
|
@@ -1454,6 +1553,15 @@ function makeDriver(
|
|
|
1454
1553
|
}
|
|
1455
1554
|
|
|
1456
1555
|
case OP_NOT: {
|
|
1556
|
+
// The shipping generic closure/emitted NOT bodies predate pure-global
|
|
1557
|
+
// lookahead rollback. Keep the reference driver aligned with them for
|
|
1558
|
+
// every generic child; only the direct-terminal shape this lane replaces
|
|
1559
|
+
// needs the reference oracle to hide the terminal's diagnostic writes.
|
|
1560
|
+
const scalarChild = scalarTerminalNotChild(code, ip) >= 0
|
|
1561
|
+
const savedFc = scalarChild ? ctx._fc : false
|
|
1562
|
+
const savedFe = scalarChild ? ctx._fe : -1
|
|
1563
|
+
const savedFx = scalarChild ? ctx._fx : undefined
|
|
1564
|
+
const savedProbe = scalarChild ? ctx._probe?.best : undefined
|
|
1457
1565
|
const need = rollbackNeeded(ctx)
|
|
1458
1566
|
const mRaw = need ? cstRawLen(ctx) : 0
|
|
1459
1567
|
const mTl = need ? cstTlLen(ctx) : 0
|
|
@@ -1464,6 +1572,12 @@ function makeDriver(
|
|
|
1464
1572
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
1465
1573
|
const v = exec(code[ip + 1]!, input, pos, ctx)
|
|
1466
1574
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1575
|
+
if (scalarChild) {
|
|
1576
|
+
ctx._fc = savedFc
|
|
1577
|
+
ctx._fe = savedFe
|
|
1578
|
+
ctx._fx = savedFx
|
|
1579
|
+
if (ctx._probe !== undefined) ctx._probe.best = savedProbe ?? null
|
|
1580
|
+
}
|
|
1467
1581
|
if (v === FAIL) { EC.e = pos; return null }
|
|
1468
1582
|
// `not.ts:50` — the ASSERTION's own set, at the assertion's position.
|
|
1469
1583
|
ctx._fe = pos
|
|
@@ -1651,7 +1765,7 @@ export function execRules(
|
|
|
1651
1765
|
const prog = expandCompact(source)
|
|
1652
1766
|
const t = resolveTable(prog)
|
|
1653
1767
|
const scan = opts.leafSwap === false ? t.triviaScan.map(() => null) : t.triviaScan
|
|
1654
|
-
const d = makeDriver(t.code, t.k, t.fns, t.cc, t.fx, t.disp, t.dsp, t.trivia, scan, t.triviaLabelled, prog, newEndCell())
|
|
1768
|
+
const d = makeDriver(t.code, t.k, t.fns, t.cc, t.fx, t.disp, t.dsp, t.lex, t.lexPrograms, t.trivia, scan, t.triviaLabelled, prog, newEndCell())
|
|
1655
1769
|
const names = Object.keys(prog.rules)
|
|
1656
1770
|
const entries = names.map(n => prog.rules[n]!)
|
|
1657
1771
|
let last: unknown
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { LexBodySpec } from './program.ts'
|
|
2
|
+
|
|
3
|
+
/** -1 fails. A success is `end * 2 + suffixMatched`, so the reader can
|
|
4
|
+
* reproduce optional()'s swallowed suffix failure without allocating a tuple. */
|
|
5
|
+
export type LexBodyRecognizer = (input: string, pos: number) => number
|
|
6
|
+
|
|
7
|
+
/** Build one selected lexical body from its numeric table row. Recognition is
|
|
8
|
+
* pure; diagnostics, token boundary state, source value and CST publication
|
|
9
|
+
* remain owned by the reader's OP_LEX_BODY shell. */
|
|
10
|
+
export function buildLexBodyRecognizer(
|
|
11
|
+
spec: LexBodySpec,
|
|
12
|
+
constants: readonly unknown[],
|
|
13
|
+
): LexBodyRecognizer {
|
|
14
|
+
const raw = constants[spec[0]]
|
|
15
|
+
if (!(raw instanceof RegExp)) {
|
|
16
|
+
throw new TypeError('table lexical body does not reference a RegExp constant')
|
|
17
|
+
}
|
|
18
|
+
if (!Number.isInteger(spec[1]) || spec[1] < -1 || spec[1] > 0xFFFF) {
|
|
19
|
+
throw new TypeError('table lexical body suffix is not absent or one UTF-16 code unit')
|
|
20
|
+
}
|
|
21
|
+
const re = raw.sticky ? new RegExp(raw.source, raw.flags) : new RegExp(raw.source, `${raw.flags.replace(/[gy]/g, '')}y`)
|
|
22
|
+
const suffix = spec[1]
|
|
23
|
+
return (input, pos) => {
|
|
24
|
+
re.lastIndex = pos
|
|
25
|
+
if (!re.test(input)) return -1
|
|
26
|
+
const end = re.lastIndex
|
|
27
|
+
if (suffix < 0) return end * 2
|
|
28
|
+
return input.charCodeAt(end) === suffix ? (end + 1) * 2 + 1 : end * 2
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import type { Combinator, ParseContext } from '../types.ts'
|
|
2
|
+
import { failAt } from '../combinators/probe.ts'
|
|
3
|
+
import type { LexBodyRecognizer } from './lex-body.ts'
|
|
4
|
+
import type { LexProgramSpec, ResolvedClass } from './program.ts'
|
|
5
|
+
|
|
6
|
+
/** A selected composite lexical body. Failure state is already published.
|
|
7
|
+
* `scan` is present only for the balanced shell and indexes the canonical
|
|
8
|
+
* driver-owned scan pool; it is not another recognizer implementation. */
|
|
9
|
+
export type LexProgramRunner = {
|
|
10
|
+
(input: string, pos: number, ctx: ParseContext, scan?: Combinator<unknown>): number
|
|
11
|
+
readonly scan?: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const LEX_NODE_TERMINAL = 0
|
|
15
|
+
export const LEX_NODE_SEQUENCE2 = 1
|
|
16
|
+
export const LEX_NODE_SEQUENCE3 = 2
|
|
17
|
+
export const LEX_NODE_SEQUENCE4 = 3
|
|
18
|
+
export const LEX_NODE_SEQUENCE5 = 4
|
|
19
|
+
export const LEX_NODE_CHOICE2 = 5
|
|
20
|
+
export const LEX_NODE_CHOICE4 = 6
|
|
21
|
+
export const LEX_NODE_CHOICE8 = 7
|
|
22
|
+
export const LEX_NODE_NOT = 8
|
|
23
|
+
export const LEX_NODE_OPTIONAL = 9
|
|
24
|
+
|
|
25
|
+
export function lexProgramDigest(words: readonly number[]): number {
|
|
26
|
+
let hash = 0x811C9DC5
|
|
27
|
+
for (const word of words) {
|
|
28
|
+
hash ^= word | 0
|
|
29
|
+
hash = Math.imul(hash, 0x01000193)
|
|
30
|
+
}
|
|
31
|
+
return hash | 0
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function classHas(cls: ResolvedClass, code: number): boolean {
|
|
35
|
+
if (code < 0) return false
|
|
36
|
+
if (code < 128) return cls.ascii[code] === 1
|
|
37
|
+
for (let i = 0; i < cls.hi.length; i += 2) {
|
|
38
|
+
if (code >= cls.hi[i]! && code <= cls.hi[i + 1]!) return true
|
|
39
|
+
}
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type PureLexRunner = (input: string, pos: number) => number
|
|
44
|
+
type BuiltLexNode = { readonly match: PureLexRunner; readonly run: LexProgramRunner }
|
|
45
|
+
const committed = (ctx: ParseContext): boolean => ctx._fc === true
|
|
46
|
+
|
|
47
|
+
function trackLexLines(ctx: ParseContext, input: string, end: number): void {
|
|
48
|
+
let at = ctx._lineScannedTo ?? 0
|
|
49
|
+
const starts = ctx._lineStarts
|
|
50
|
+
if (starts === undefined || end <= at) return
|
|
51
|
+
while (at < end) {
|
|
52
|
+
if (input.charCodeAt(at) === 10) starts.push(at + 1)
|
|
53
|
+
at++
|
|
54
|
+
}
|
|
55
|
+
ctx._lineScannedTo = end
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function terminalNode(
|
|
59
|
+
matcher: LexBodyRecognizer,
|
|
60
|
+
failure: readonly string[],
|
|
61
|
+
tracksLines: boolean,
|
|
62
|
+
): BuiltLexNode {
|
|
63
|
+
const match: PureLexRunner = (input, pos) => {
|
|
64
|
+
const result = matcher(input, pos)
|
|
65
|
+
return result < 0 ? -1 : result / 2
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
match,
|
|
69
|
+
run: (input, pos, ctx) => {
|
|
70
|
+
const end = match(input, pos)
|
|
71
|
+
if (end < 0) {
|
|
72
|
+
ctx._fe = pos
|
|
73
|
+
ctx._fx = failure as string[]
|
|
74
|
+
if (ctx._probe !== undefined) failAt(ctx, ctx._fx, pos)
|
|
75
|
+
return -1
|
|
76
|
+
}
|
|
77
|
+
if (tracksLines) trackLexLines(ctx, input, end)
|
|
78
|
+
return end
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function sequence2(a: BuiltLexNode, b: BuiltLexNode): BuiltLexNode {
|
|
84
|
+
return {
|
|
85
|
+
match: (input, pos) => {
|
|
86
|
+
const e0 = a.match(input, pos)
|
|
87
|
+
return e0 < 0 ? -1 : b.match(input, e0)
|
|
88
|
+
},
|
|
89
|
+
run: (input, pos, ctx) => {
|
|
90
|
+
const e0 = a.run(input, pos, ctx)
|
|
91
|
+
if (e0 < 0) return -1
|
|
92
|
+
ctx._fc = false
|
|
93
|
+
return b.run(input, e0, ctx)
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function sequence3(a: BuiltLexNode, b: BuiltLexNode, c: BuiltLexNode): BuiltLexNode {
|
|
99
|
+
return {
|
|
100
|
+
match: (input, pos) => {
|
|
101
|
+
const e0 = a.match(input, pos)
|
|
102
|
+
if (e0 < 0) return -1
|
|
103
|
+
const e1 = b.match(input, e0)
|
|
104
|
+
return e1 < 0 ? -1 : c.match(input, e1)
|
|
105
|
+
},
|
|
106
|
+
run: (input, pos, ctx) => {
|
|
107
|
+
const e0 = a.run(input, pos, ctx)
|
|
108
|
+
if (e0 < 0) return -1
|
|
109
|
+
ctx._fc = false
|
|
110
|
+
const e1 = b.run(input, e0, ctx)
|
|
111
|
+
if (e1 < 0) return -1
|
|
112
|
+
ctx._fc = false
|
|
113
|
+
return c.run(input, e1, ctx)
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function sequence4(a: BuiltLexNode, b: BuiltLexNode, c: BuiltLexNode, d: BuiltLexNode): BuiltLexNode {
|
|
119
|
+
const abc = sequence3(a, b, c)
|
|
120
|
+
return {
|
|
121
|
+
match: (input, pos) => {
|
|
122
|
+
const end = abc.match(input, pos)
|
|
123
|
+
return end < 0 ? -1 : d.match(input, end)
|
|
124
|
+
},
|
|
125
|
+
run: (input, pos, ctx) => {
|
|
126
|
+
const end = abc.run(input, pos, ctx)
|
|
127
|
+
if (end < 0) return -1
|
|
128
|
+
ctx._fc = false
|
|
129
|
+
return d.run(input, end, ctx)
|
|
130
|
+
},
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function sequence5(
|
|
135
|
+
a: BuiltLexNode, b: BuiltLexNode, c: BuiltLexNode, d: BuiltLexNode, e: BuiltLexNode,
|
|
136
|
+
): BuiltLexNode {
|
|
137
|
+
const abcd = sequence4(a, b, c, d)
|
|
138
|
+
return {
|
|
139
|
+
match: (input, pos) => {
|
|
140
|
+
const end = abcd.match(input, pos)
|
|
141
|
+
return end < 0 ? -1 : e.match(input, end)
|
|
142
|
+
},
|
|
143
|
+
run: (input, pos, ctx) => {
|
|
144
|
+
const end = abcd.run(input, pos, ctx)
|
|
145
|
+
if (end < 0) return -1
|
|
146
|
+
ctx._fc = false
|
|
147
|
+
return e.run(input, end, ctx)
|
|
148
|
+
},
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
type ChoiceTail = (
|
|
153
|
+
input: string, pos: number, ctx: ParseContext, lead: number, acc: string[] | undefined,
|
|
154
|
+
) => number
|
|
155
|
+
|
|
156
|
+
function appendExpected(acc: string[] | undefined, values: readonly string[] | undefined): string[] | undefined {
|
|
157
|
+
if (values === undefined || values.length === 0) return acc
|
|
158
|
+
if (acc === undefined) return values.slice()
|
|
159
|
+
acc.push(...values)
|
|
160
|
+
return acc
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function choiceNode(
|
|
164
|
+
arms: readonly BuiltLexNode[],
|
|
165
|
+
classes: readonly (ResolvedClass | undefined)[],
|
|
166
|
+
armExpected: readonly (readonly string[])[],
|
|
167
|
+
choiceExpected: readonly string[],
|
|
168
|
+
): BuiltLexNode {
|
|
169
|
+
let matchTail: PureLexRunner = () => -1
|
|
170
|
+
for (let i = arms.length - 1; i >= 0; i--) {
|
|
171
|
+
const arm = arms[i]!, next = matchTail
|
|
172
|
+
matchTail = (input, pos) => {
|
|
173
|
+
const end = arm.match(input, pos)
|
|
174
|
+
return end < 0 ? next(input, pos) : end
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
let tail: ChoiceTail = (_input, pos, ctx, _lead, acc) => {
|
|
178
|
+
ctx._fe = pos
|
|
179
|
+
ctx._fx = acc ?? choiceExpected as string[]
|
|
180
|
+
return -1
|
|
181
|
+
}
|
|
182
|
+
for (let i = arms.length - 1; i >= 0; i--) {
|
|
183
|
+
const arm = arms[i]!, cls = classes[i], staticExpected = armExpected[i]!, next = tail
|
|
184
|
+
tail = (input, pos, ctx, lead, acc) => {
|
|
185
|
+
if (cls !== undefined && !classHas(cls, lead)) {
|
|
186
|
+
return next(input, pos, ctx, lead, appendExpected(acc, staticExpected))
|
|
187
|
+
}
|
|
188
|
+
ctx._fc = false
|
|
189
|
+
const end = arm.run(input, pos, ctx)
|
|
190
|
+
if (end >= 0) return end
|
|
191
|
+
const nextAcc = appendExpected(acc, ctx._fx)
|
|
192
|
+
if (committed(ctx)) {
|
|
193
|
+
if (nextAcc !== undefined) ctx._fx = nextAcc
|
|
194
|
+
return -1
|
|
195
|
+
}
|
|
196
|
+
return next(input, pos, ctx, lead, nextAcc)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
match: matchTail,
|
|
201
|
+
run: (input, pos, ctx) => tail(input, pos, ctx, pos < input.length ? input.codePointAt(pos)! : -1, undefined),
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function notNode(body: BuiltLexNode, failure: readonly string[]): BuiltLexNode {
|
|
206
|
+
return {
|
|
207
|
+
match: (input, pos) => body.match(input, pos) < 0 ? pos : -1,
|
|
208
|
+
run: (input, pos, ctx) => {
|
|
209
|
+
if (body.match(input, pos) < 0) return pos
|
|
210
|
+
ctx._fe = pos
|
|
211
|
+
ctx._fx = failure as string[]
|
|
212
|
+
return -1
|
|
213
|
+
},
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function optionalNode(body: BuiltLexNode): BuiltLexNode {
|
|
218
|
+
return {
|
|
219
|
+
match: (input, pos) => {
|
|
220
|
+
const end = body.match(input, pos)
|
|
221
|
+
return end < 0 ? pos : end
|
|
222
|
+
},
|
|
223
|
+
run: (input, pos, ctx) => {
|
|
224
|
+
ctx._fc = false
|
|
225
|
+
const end = body.run(input, pos, ctx)
|
|
226
|
+
return end < 0 && !committed(ctx) ? pos : end
|
|
227
|
+
},
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function fixedTreeRunner(
|
|
232
|
+
words: readonly number[],
|
|
233
|
+
matchers: readonly LexBodyRecognizer[],
|
|
234
|
+
classes: readonly ResolvedClass[],
|
|
235
|
+
expected: readonly (readonly string[])[],
|
|
236
|
+
): LexProgramRunner {
|
|
237
|
+
const stack: BuiltLexNode[] = []
|
|
238
|
+
const pop = (): BuiltLexNode => {
|
|
239
|
+
const value = stack.pop()
|
|
240
|
+
if (value === undefined) throw new TypeError('table lexical tree has an incomplete fixed body')
|
|
241
|
+
return value
|
|
242
|
+
}
|
|
243
|
+
for (let at = 0; at < words.length;) {
|
|
244
|
+
const op = words[at++]!
|
|
245
|
+
if (op === LEX_NODE_TERMINAL) {
|
|
246
|
+
const matcher = matchers[words[at++]!], failure = expected[words[at++]!]
|
|
247
|
+
const tracks = words[at++]
|
|
248
|
+
if (matcher === undefined || failure === undefined || (tracks !== 0 && tracks !== 1)) {
|
|
249
|
+
throw new TypeError('table lexical tree terminal references an invalid pool entry')
|
|
250
|
+
}
|
|
251
|
+
stack.push(terminalNode(matcher, failure, tracks === 1))
|
|
252
|
+
continue
|
|
253
|
+
}
|
|
254
|
+
if (op >= LEX_NODE_SEQUENCE2 && op <= LEX_NODE_SEQUENCE5) {
|
|
255
|
+
const count = op - LEX_NODE_SEQUENCE2 + 2
|
|
256
|
+
const nodes = Array.from({ length: count }, pop).reverse()
|
|
257
|
+
if (count === 2) stack.push(sequence2(nodes[0]!, nodes[1]!))
|
|
258
|
+
else if (count === 3) stack.push(sequence3(nodes[0]!, nodes[1]!, nodes[2]!))
|
|
259
|
+
else if (count === 4) stack.push(sequence4(nodes[0]!, nodes[1]!, nodes[2]!, nodes[3]!))
|
|
260
|
+
else stack.push(sequence5(nodes[0]!, nodes[1]!, nodes[2]!, nodes[3]!, nodes[4]!))
|
|
261
|
+
continue
|
|
262
|
+
}
|
|
263
|
+
if (op >= LEX_NODE_CHOICE2 && op <= LEX_NODE_CHOICE8) {
|
|
264
|
+
const count = op === LEX_NODE_CHOICE2 ? 2 : op === LEX_NODE_CHOICE4 ? 4 : 8
|
|
265
|
+
const choiceFx = expected[words[at++]!]
|
|
266
|
+
if (choiceFx === undefined) throw new TypeError('table lexical tree choice has an invalid expected set')
|
|
267
|
+
const armClasses: Array<ResolvedClass | undefined> = []
|
|
268
|
+
const armFx: Array<readonly string[]> = []
|
|
269
|
+
for (let i = 0; i < count; i++) {
|
|
270
|
+
const cls = words[at++]!, fx = expected[words[at++]!]
|
|
271
|
+
if (cls < -1 || (cls >= 0 && classes[cls] === undefined) || fx === undefined) {
|
|
272
|
+
throw new TypeError('table lexical tree choice references an invalid pool entry')
|
|
273
|
+
}
|
|
274
|
+
armClasses.push(cls < 0 ? undefined : classes[cls])
|
|
275
|
+
armFx.push(fx)
|
|
276
|
+
}
|
|
277
|
+
const arms = Array.from({ length: count }, pop).reverse()
|
|
278
|
+
stack.push(choiceNode(arms, armClasses, armFx, choiceFx))
|
|
279
|
+
continue
|
|
280
|
+
}
|
|
281
|
+
if (op === LEX_NODE_NOT) {
|
|
282
|
+
const failure = expected[words[at++]!]
|
|
283
|
+
if (failure === undefined) throw new TypeError('table lexical tree assertion has an invalid expected set')
|
|
284
|
+
stack.push(notNode(pop(), failure))
|
|
285
|
+
continue
|
|
286
|
+
}
|
|
287
|
+
if (op === LEX_NODE_OPTIONAL) { stack.push(optionalNode(pop())); continue }
|
|
288
|
+
throw new TypeError('table lexical tree has an unknown fixed body')
|
|
289
|
+
}
|
|
290
|
+
if (stack.length !== 1) throw new TypeError('table lexical tree does not resolve to one root body')
|
|
291
|
+
return stack[0]!.run
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Decode one fixed selected-body tuple. There is no lexical opcode loop on
|
|
295
|
+
* the parse path: each supported tuple becomes one scalar closure here. */
|
|
296
|
+
export function buildLexProgramRunner(
|
|
297
|
+
spec: LexProgramSpec,
|
|
298
|
+
matchers: readonly LexBodyRecognizer[],
|
|
299
|
+
classes: readonly ResolvedClass[],
|
|
300
|
+
expected: readonly (readonly string[])[],
|
|
301
|
+
): LexProgramRunner {
|
|
302
|
+
if ((spec[0] !== 0 && spec[0] !== 1 && spec[0] !== 2 && spec[0] !== 3)
|
|
303
|
+
|| (spec[0] === 0 && spec.length !== 13)
|
|
304
|
+
|| (spec[0] === 1 && spec.length !== 12)
|
|
305
|
+
|| (spec[0] === 2 && spec.length < 6)
|
|
306
|
+
|| (spec[0] === 3 && spec.length !== 3)) {
|
|
307
|
+
throw new TypeError('table lexical program has an invalid fixed body width')
|
|
308
|
+
}
|
|
309
|
+
if (spec[1] !== lexProgramDigest(spec.slice(2))) {
|
|
310
|
+
throw new TypeError('table lexical program semantic digest is inconsistent')
|
|
311
|
+
}
|
|
312
|
+
if (spec[0] === 3) {
|
|
313
|
+
if (!Number.isInteger(spec[2]) || spec[2] < 0) {
|
|
314
|
+
throw new TypeError('table balanced lexical program has an invalid scan reference')
|
|
315
|
+
}
|
|
316
|
+
const run: LexProgramRunner = (_input, _pos, ctx, scan) => {
|
|
317
|
+
if (scan === undefined) throw new TypeError('table balanced lexical program lost its scan body')
|
|
318
|
+
const result = scan.parse(_input, _pos, ctx)
|
|
319
|
+
if (!result.ok) {
|
|
320
|
+
ctx._fe = result.span.start
|
|
321
|
+
ctx._fx = (result.expected ?? []) as string[]
|
|
322
|
+
return -1
|
|
323
|
+
}
|
|
324
|
+
return result.span.end
|
|
325
|
+
}
|
|
326
|
+
return Object.assign(run, { scan: spec[2] })
|
|
327
|
+
}
|
|
328
|
+
if (spec[0] === 2) return fixedTreeRunner(spec.slice(2), matchers, classes, expected)
|
|
329
|
+
if (spec[0] === 1) {
|
|
330
|
+
const g00 = matchers[spec[2]], g01 = matchers[spec[3]]
|
|
331
|
+
const g02 = matchers[spec[4]], g03 = matchers[spec[5]]
|
|
332
|
+
const guard0Fx = expected[spec[6]]
|
|
333
|
+
const g1 = matchers[spec[7]], guard1Fx = expected[spec[8]]
|
|
334
|
+
const terminal = matchers[spec[9]], terminalFx = expected[spec[10]]
|
|
335
|
+
const tracks = spec[11]
|
|
336
|
+
if (g00 === undefined || g01 === undefined || g02 === undefined || g03 === undefined
|
|
337
|
+
|| guard0Fx === undefined || g1 === undefined || guard1Fx === undefined
|
|
338
|
+
|| terminal === undefined || terminalFx === undefined || (tracks !== 0 && tracks !== 1)) {
|
|
339
|
+
throw new TypeError('table lexical program references an invalid pool entry')
|
|
340
|
+
}
|
|
341
|
+
return (input, pos, ctx) => {
|
|
342
|
+
ctx._fc = false
|
|
343
|
+
if (g00(input, pos) >= 0 || g01(input, pos) >= 0
|
|
344
|
+
|| g02(input, pos) >= 0 || g03(input, pos) >= 0) {
|
|
345
|
+
ctx._fe = pos
|
|
346
|
+
ctx._fx = guard0Fx as string[]
|
|
347
|
+
return -1
|
|
348
|
+
}
|
|
349
|
+
ctx._fc = false
|
|
350
|
+
if (g1(input, pos) >= 0) {
|
|
351
|
+
ctx._fe = pos
|
|
352
|
+
ctx._fx = guard1Fx as string[]
|
|
353
|
+
return -1
|
|
354
|
+
}
|
|
355
|
+
ctx._fc = false
|
|
356
|
+
const result = terminal(input, pos)
|
|
357
|
+
if (result < 0) {
|
|
358
|
+
ctx._fe = pos
|
|
359
|
+
ctx._fx = terminalFx as string[]
|
|
360
|
+
if (ctx._probe !== undefined) failAt(ctx, ctx._fx, pos)
|
|
361
|
+
return -1
|
|
362
|
+
}
|
|
363
|
+
const end = result / 2
|
|
364
|
+
if (tracks === 1) trackLexLines(ctx, input, end)
|
|
365
|
+
return end
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
const cls = classes[spec[2]]
|
|
369
|
+
const choiceFx = expected[spec[3]]
|
|
370
|
+
const m0 = matchers[spec[4]], x0 = expected[spec[5]]
|
|
371
|
+
const m1 = matchers[spec[6]], x1 = expected[spec[7]]
|
|
372
|
+
const m2 = matchers[spec[8]], x2 = expected[spec[9]]
|
|
373
|
+
const m3 = matchers[spec[10]], x3 = expected[spec[11]]
|
|
374
|
+
const tracks = spec[12]
|
|
375
|
+
if (cls === undefined || choiceFx === undefined
|
|
376
|
+
|| m0 === undefined || x0 === undefined || m1 === undefined || x1 === undefined
|
|
377
|
+
|| m2 === undefined || x2 === undefined || m3 === undefined || x3 === undefined
|
|
378
|
+
|| (tracks !== 0 && tracks !== 1)) {
|
|
379
|
+
throw new TypeError('table lexical program references an invalid pool entry')
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return (input, pos, ctx) => {
|
|
383
|
+
const lead = pos < input.length ? input.codePointAt(pos)! : -1
|
|
384
|
+
if (!classHas(cls, lead)) {
|
|
385
|
+
ctx._fe = pos
|
|
386
|
+
ctx._fx = choiceFx.slice()
|
|
387
|
+
return -1
|
|
388
|
+
}
|
|
389
|
+
let acc: string[] | undefined
|
|
390
|
+
const failed = (fx: readonly string[]): void => {
|
|
391
|
+
ctx._fe = pos
|
|
392
|
+
ctx._fx = fx as string[]
|
|
393
|
+
if (ctx._probe !== undefined) failAt(ctx, ctx._fx, pos)
|
|
394
|
+
if (acc === undefined) acc = fx.slice()
|
|
395
|
+
else acc.push(...fx)
|
|
396
|
+
}
|
|
397
|
+
ctx._fc = false
|
|
398
|
+
const success = (result: number): number => {
|
|
399
|
+
const end = result / 2
|
|
400
|
+
if (tracks === 1) trackLexLines(ctx, input, end)
|
|
401
|
+
return end
|
|
402
|
+
}
|
|
403
|
+
let result = m0(input, pos)
|
|
404
|
+
if (result >= 0) return success(result)
|
|
405
|
+
failed(x0)
|
|
406
|
+
ctx._fc = false
|
|
407
|
+
result = m1(input, pos)
|
|
408
|
+
if (result >= 0) return success(result)
|
|
409
|
+
failed(x1)
|
|
410
|
+
ctx._fc = false
|
|
411
|
+
result = m2(input, pos)
|
|
412
|
+
if (result >= 0) return success(result)
|
|
413
|
+
failed(x2)
|
|
414
|
+
ctx._fc = false
|
|
415
|
+
result = m3(input, pos)
|
|
416
|
+
if (result >= 0) return success(result)
|
|
417
|
+
failed(x3)
|
|
418
|
+
ctx._fe = pos
|
|
419
|
+
ctx._fx = acc ?? choiceFx as string[]
|
|
420
|
+
return -1
|
|
421
|
+
}
|
|
422
|
+
}
|