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/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
|
}
|
|
@@ -865,9 +952,8 @@ function makeDriver(
|
|
|
865
952
|
const d = code[ip + 1]!
|
|
866
953
|
const base = ip + 4
|
|
867
954
|
const choiceFx = fx[code[ip + 3]!] as string[]
|
|
868
|
-
//
|
|
869
|
-
//
|
|
870
|
-
// user an empty expected set.
|
|
955
|
+
// A pure dispatch miss has no attempted arm to rank. Report the static
|
|
956
|
+
// opener union at the choice's own position so the result is meaningful.
|
|
871
957
|
const failChoice = (): typeof FAIL => {
|
|
872
958
|
ctx._fe = pos
|
|
873
959
|
ctx._fx = choiceFx
|
|
@@ -916,12 +1002,9 @@ function makeDriver(
|
|
|
916
1002
|
// union IS that flatMap's answer, without running seven parsers to
|
|
917
1003
|
// rediscover it.
|
|
918
1004
|
//
|
|
919
|
-
//
|
|
920
|
-
//
|
|
921
|
-
//
|
|
922
|
-
// library, they are gated on `_ctx._probe` and they surface as
|
|
923
|
-
// `RunResult.furthestFail` under `{recover:true}` — a different field.
|
|
924
|
-
// Every `_fe`/`_fx` site in all three drivers is last-write-wins.
|
|
1005
|
+
// There is only one selected arm on this path, so ordered-arm depth
|
|
1006
|
+
// ranking does not apply. `furthestFail` under `{recover:true}` remains
|
|
1007
|
+
// a separate probe result.
|
|
925
1008
|
//
|
|
926
1009
|
// A residual JSON divergence survives this and is NOT a failure-reporting
|
|
927
1010
|
// bug: on `{"a":]` this driver reports `["}"]` (the Obj arm's own
|
|
@@ -947,18 +1030,15 @@ function makeDriver(
|
|
|
947
1030
|
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
948
1031
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
949
1032
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
950
|
-
//
|
|
951
|
-
//
|
|
952
|
-
//
|
|
953
|
-
// is a set of OPENERS, so a left-factored or shared-prefix choice whose
|
|
954
|
-
// arms all failed PAST their common opener reported that opener instead of
|
|
955
|
-
// the tokens the arms were actually waiting for: `['/::?/']` where the
|
|
956
|
-
// interpreter says `['"before"', '"hover"']`.
|
|
1033
|
+
// The ordered path keeps only arms at the deepest failure offset. Exact
|
|
1034
|
+
// ties merge in source order. The final failure is still anchored at the
|
|
1035
|
+
// choice's own position; the deeper offset ranks diagnostics only.
|
|
957
1036
|
//
|
|
958
1037
|
// `choiceFx` is still the answer when nothing ran or every arm reported
|
|
959
1038
|
// nothing — the char gate can skip arms the interpreter would have entered,
|
|
960
1039
|
// and the union is what those arms' start-failures would have contributed.
|
|
961
1040
|
let acc: string[] | undefined
|
|
1041
|
+
let best = pos
|
|
962
1042
|
const push = (ax: readonly string[] | undefined): void => {
|
|
963
1043
|
if (ax === undefined || ax.length === 0) return
|
|
964
1044
|
if (acc === undefined) acc = ax.slice()
|
|
@@ -973,7 +1053,7 @@ function makeDriver(
|
|
|
973
1053
|
// enters the arm, the arm's opener fails, and its static set is what
|
|
974
1054
|
// lands in the accumulation. Declining to enter must not also decline
|
|
975
1055
|
// to say what the arm wanted.
|
|
976
|
-
push(fx[code[armFx + i]!] as string[])
|
|
1056
|
+
if (best === pos) push(fx[code[armFx + i]!] as string[])
|
|
977
1057
|
continue
|
|
978
1058
|
}
|
|
979
1059
|
ctx._fc = false
|
|
@@ -982,7 +1062,9 @@ function makeDriver(
|
|
|
982
1062
|
const v = exec(code[base + i]!, input, pos, ctx)
|
|
983
1063
|
if (v !== FAIL) return v
|
|
984
1064
|
if (COUNT) { tableCounters.ungatedFails++; tableCounters.ungatedFailRows += tableCounters.rows - rows0 }
|
|
985
|
-
|
|
1065
|
+
const at = ctx._fe ?? pos
|
|
1066
|
+
if (at > best) { best = at; acc = undefined }
|
|
1067
|
+
if (at === best) push(ctx._fx)
|
|
986
1068
|
// A committed arm cuts, and the interpreter reports the accumulation at
|
|
987
1069
|
// the arm's OWN span (`choice.ts:170`), not at the choice's position.
|
|
988
1070
|
if (committed(ctx)) { if (acc !== undefined) ctx._fx = acc; return FAIL }
|
|
@@ -1116,6 +1198,11 @@ function makeDriver(
|
|
|
1116
1198
|
const mySync = REC ? ctx._sync : undefined
|
|
1117
1199
|
const recFx = REC ? fx[code[ip + 6]!] as string[] : undefined
|
|
1118
1200
|
const sepSent = REC ? sentinelFor(code[ip + 7]!) : undefined
|
|
1201
|
+
const itemClassIndex = sep < 0 ? code[ip + 7]! : -1
|
|
1202
|
+
const itemClass = REC && itemClassIndex >= 0 ? cc[itemClassIndex]! : undefined
|
|
1203
|
+
// `completionsAt` observes failures swallowed by an optional iteration;
|
|
1204
|
+
// tolerant recovery does too. Both keep the ordinary child path.
|
|
1205
|
+
const gateItems = itemClass !== undefined && ctx._tolerant !== true && ctx._probe === undefined
|
|
1119
1206
|
let cur = pos
|
|
1120
1207
|
let count = 0
|
|
1121
1208
|
for (;;) {
|
|
@@ -1128,6 +1215,8 @@ function makeDriver(
|
|
|
1128
1215
|
// must attempt the separator so its failure sets the expected set, which
|
|
1129
1216
|
// is the only thing an under-`min` list has to report.
|
|
1130
1217
|
if (sep >= 0 && count > 0 && count >= min && cur >= input.length) break
|
|
1218
|
+
if (count >= min && sep < 0 && !hasTrivia
|
|
1219
|
+
&& gateItems && !classHas(itemClass!, lead(input, cur))) break
|
|
1131
1220
|
// One mark pair for the whole loop when a rollback is even possible,
|
|
1132
1221
|
// refreshed per iteration rather than reallocated.
|
|
1133
1222
|
// SCALAR MARKS. This loop took TWO allocations per item (a CST mark and
|
|
@@ -1199,6 +1288,11 @@ function makeDriver(
|
|
|
1199
1288
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
1200
1289
|
break
|
|
1201
1290
|
}
|
|
1291
|
+
if (count >= min && gateItems && !classHas(itemClass!, lead(input, itemStart))) {
|
|
1292
|
+
if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1293
|
+
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
1294
|
+
break
|
|
1295
|
+
}
|
|
1202
1296
|
ctx._fc = false
|
|
1203
1297
|
const v = exec(child, input, itemStart, ctx)
|
|
1204
1298
|
if (v === FAIL) {
|
|
@@ -1454,6 +1548,15 @@ function makeDriver(
|
|
|
1454
1548
|
}
|
|
1455
1549
|
|
|
1456
1550
|
case OP_NOT: {
|
|
1551
|
+
// The shipping generic closure/emitted NOT bodies predate pure-global
|
|
1552
|
+
// lookahead rollback. Keep the reference driver aligned with them for
|
|
1553
|
+
// every generic child; only the direct-terminal shape this lane replaces
|
|
1554
|
+
// needs the reference oracle to hide the terminal's diagnostic writes.
|
|
1555
|
+
const scalarChild = scalarTerminalNotChild(code, ip) >= 0
|
|
1556
|
+
const savedFc = scalarChild ? ctx._fc : false
|
|
1557
|
+
const savedFe = scalarChild ? ctx._fe : -1
|
|
1558
|
+
const savedFx = scalarChild ? ctx._fx : undefined
|
|
1559
|
+
const savedProbe = scalarChild ? ctx._probe?.best : undefined
|
|
1457
1560
|
const need = rollbackNeeded(ctx)
|
|
1458
1561
|
const mRaw = need ? cstRawLen(ctx) : 0
|
|
1459
1562
|
const mTl = need ? cstTlLen(ctx) : 0
|
|
@@ -1464,6 +1567,12 @@ function makeDriver(
|
|
|
1464
1567
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
1465
1568
|
const v = exec(code[ip + 1]!, input, pos, ctx)
|
|
1466
1569
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1570
|
+
if (scalarChild) {
|
|
1571
|
+
ctx._fc = savedFc
|
|
1572
|
+
ctx._fe = savedFe
|
|
1573
|
+
ctx._fx = savedFx
|
|
1574
|
+
if (ctx._probe !== undefined) ctx._probe.best = savedProbe ?? null
|
|
1575
|
+
}
|
|
1467
1576
|
if (v === FAIL) { EC.e = pos; return null }
|
|
1468
1577
|
// `not.ts:50` — the ASSERTION's own set, at the assertion's position.
|
|
1469
1578
|
ctx._fe = pos
|
|
@@ -1651,7 +1760,7 @@ export function execRules(
|
|
|
1651
1760
|
const prog = expandCompact(source)
|
|
1652
1761
|
const t = resolveTable(prog)
|
|
1653
1762
|
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())
|
|
1763
|
+
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
1764
|
const names = Object.keys(prog.rules)
|
|
1656
1765
|
const entries = names.map(n => prog.rules[n]!)
|
|
1657
1766
|
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
|
+
}
|