parseman 0.47.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -21
- package/dist/analysis/diagnostics.js +2955 -357
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +2946 -348
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/expect.d.ts +1 -0
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/expected.d.ts +8 -0
- package/dist/combinators/expected.d.ts.map +1 -0
- package/dist/combinators/first-set.d.ts +8 -0
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/keywords.d.ts +4 -0
- package/dist/combinators/keywords.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/not.d.ts.map +1 -1
- package/dist/combinators/peek.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/sequence.d.ts.map +1 -1
- package/dist/combinators/trivia-skip.d.ts +19 -1
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/compiler/direct-projection.d.ts +12 -0
- package/dist/compiler/direct-projection.d.ts.map +1 -0
- package/dist/compiler/token-alphabet.d.ts +845 -5
- package/dist/compiler/token-alphabet.d.ts.map +1 -1
- package/dist/compiler/token-capability.d.ts +3 -0
- package/dist/compiler/token-capability.d.ts.map +1 -0
- package/dist/compiler/token-capability.js +3192 -0
- package/dist/compiler/token-capability.js.map +6 -0
- package/dist/cst/trivia-charscan.d.ts +3 -0
- package/dist/cst/trivia-charscan.d.ts.map +1 -1
- package/dist/cst/trivia-css-scanner.d.ts +5 -0
- package/dist/cst/trivia-css-scanner.d.ts.map +1 -0
- package/dist/cst/trivia-kinds.d.ts.map +1 -1
- package/dist/index.js +2959 -348
- package/dist/index.js.map +3 -3
- package/dist/language-service/index.js +7 -1
- package/dist/language-service/index.js.map +1 -1
- package/dist/plugin/index.js +2969 -358
- package/dist/plugin/index.js.map +3 -3
- package/dist/spec/index.js +366 -49
- package/dist/spec/index.js.map +2 -2
- package/dist/table/assemble.d.ts.map +1 -1
- package/dist/table/child-slots.d.ts.map +1 -1
- package/dist/table/compile-rule-map.d.ts +3 -1
- package/dist/table/compile-rule-map.d.ts.map +1 -1
- package/dist/table/emit-assembly.d.ts +13 -14
- package/dist/table/emit-assembly.d.ts.map +1 -1
- package/dist/table/emit.d.ts.map +1 -1
- package/dist/table/encode.d.ts.map +1 -1
- package/dist/table/exec-baseline.d.ts.map +1 -1
- package/dist/table/exec.d.ts.map +1 -1
- package/dist/table/index.js +4839 -2232
- package/dist/table/index.js.map +3 -3
- package/dist/table/lex-body.d.ts +9 -0
- package/dist/table/lex-body.d.ts.map +1 -0
- package/dist/table/lex-program.d.ts +25 -0
- package/dist/table/lex-program.d.ts.map +1 -0
- package/dist/table/ops.d.ts +47 -4
- package/dist/table/ops.d.ts.map +1 -1
- package/dist/table/program.d.ts +53 -2
- package/dist/table/program.d.ts.map +1 -1
- package/dist/table/scalar-terminal.d.ts +13 -0
- package/dist/table/scalar-terminal.d.ts.map +1 -0
- package/dist/table/site-labels.d.ts.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +12 -11
- package/src/combinators/expect.ts +1 -0
- package/src/combinators/expected.ts +17 -0
- package/src/combinators/first-set.ts +82 -3
- package/src/combinators/grammar.ts +8 -3
- package/src/combinators/keywords.ts +22 -6
- package/src/combinators/literal.ts +2 -1
- package/src/combinators/not.ts +5 -1
- package/src/combinators/peek.ts +5 -1
- package/src/combinators/regex.ts +5 -3
- package/src/combinators/repeat.ts +4 -10
- package/src/combinators/sequence.ts +35 -16
- package/src/combinators/trivia-skip.ts +75 -11
- package/src/compiler/direct-projection.ts +43 -0
- package/src/compiler/token-alphabet.ts +4316 -12
- package/src/compiler/token-capability.ts +6 -0
- package/src/cst/trivia-charscan.ts +34 -0
- package/src/cst/trivia-css-scanner.ts +142 -0
- package/src/cst/trivia-kinds.ts +3 -1
- package/src/plugin/index.ts +4 -4
- package/src/table/assemble.ts +1224 -222
- package/src/table/child-slots.ts +4 -3
- package/src/table/compile-rule-map.ts +21 -3
- package/src/table/emit-assembly.ts +363 -109
- package/src/table/emit.ts +26 -1
- package/src/table/encode.ts +246 -54
- package/src/table/exec-baseline.ts +3 -7
- package/src/table/exec.ts +140 -18
- package/src/table/lex-body.ts +30 -0
- package/src/table/lex-program.ts +422 -0
- package/src/table/ops.ts +50 -4
- package/src/table/program.ts +67 -2
- package/src/table/scalar-terminal.ts +51 -0
- package/src/table/site-labels.ts +13 -3
- package/src/table/stamp.ts +3 -3
- package/src/types.ts +1 -1
- package/src/version.ts +1 -1
- package/dist/analysis/diagnostics.cjs +0 -13736
- package/dist/analysis/diagnostics.cjs.map +0 -6
- package/dist/cli/index.cjs +0 -14837
- package/dist/cli/index.cjs.map +0 -6
- package/dist/index.cjs +0 -16303
- package/dist/index.cjs.map +0 -6
- package/dist/language-service/index.cjs +0 -1302
- package/dist/language-service/index.cjs.map +0 -6
- package/dist/oracle/index.cjs +0 -254
- package/dist/oracle/index.cjs.map +0 -6
- package/dist/plugin/index.cjs +0 -16009
- package/dist/plugin/index.cjs.map +0 -6
- package/dist/run/index.cjs +0 -577
- package/dist/run/index.cjs.map +0 -6
- package/dist/spec/index.cjs +0 -6188
- package/dist/spec/index.cjs.map +0 -6
- package/dist/table/index.cjs +0 -11375
- package/dist/table/index.cjs.map +0 -6
package/src/table/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,
|
|
@@ -14,11 +17,12 @@ import {
|
|
|
14
17
|
import {
|
|
15
18
|
OP_CHOICE, OP_EMPTY, OP_GATE, OP_LEAF, OP_LIT, OP_NODE, OP_NOT, OP_OPT,
|
|
16
19
|
OP_PEEK, OP_REP, OP_REPV, OP_RULE, OP_RX, OP_SEQ, OP_SEQV, OP_XFORM,
|
|
17
|
-
OP_LIT_TRACK, OP_RX_TRACK, OP_NODE_TRACK, OP_SCOPE, OP_SCOPE_CAP, OP_EXPECT, OP_SEQX, OP_SCAN,
|
|
20
|
+
OP_LIT_TRACK, OP_RX_TRACK, OP_NODE_TRACK, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, OP_EXPECT, OP_SEQX, OP_SCAN,
|
|
18
21
|
OP_LIVE,
|
|
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
|
}
|
|
@@ -609,15 +614,23 @@ function makeDriver(
|
|
|
609
614
|
usesRouted = spec.routed[arm] === 1
|
|
610
615
|
}
|
|
611
616
|
|
|
612
|
-
const savedRouted = ctx._routed
|
|
613
617
|
let mark = saveTriviaMark(ctx)
|
|
618
|
+
let v: unknown
|
|
614
619
|
if (usesRouted) {
|
|
620
|
+
const savedRouted = ctx._routed
|
|
615
621
|
rollbackTrivia(ctx, selectorMark)
|
|
616
622
|
mark = saveTriviaMark(ctx)
|
|
617
623
|
ctx._routed = { value: key, span: { start: pos, end: selEnd } }
|
|
624
|
+
try {
|
|
625
|
+
v = exec(target, input, pos, ctx)
|
|
626
|
+
} finally {
|
|
627
|
+
ctx._routed = savedRouted
|
|
628
|
+
}
|
|
629
|
+
} else {
|
|
630
|
+
// The reference engine mirrors dispatch.parse(): plain arms do not
|
|
631
|
+
// install routed state and stay outside the exception guard.
|
|
632
|
+
v = exec(target, input, selEnd, ctx)
|
|
618
633
|
}
|
|
619
|
-
const v = exec(target, input, usesRouted ? pos : selEnd, ctx)
|
|
620
|
-
if (usesRouted) ctx._routed = savedRouted
|
|
621
634
|
if (v === FAIL) {
|
|
622
635
|
rollbackTrivia(ctx, mark)
|
|
623
636
|
// The interpreter marks a failed dispatch branch COMMITTED: the
|
|
@@ -686,6 +699,84 @@ function makeDriver(
|
|
|
686
699
|
return value
|
|
687
700
|
}
|
|
688
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
|
+
|
|
689
780
|
case OP_SCAN: {
|
|
690
781
|
const c = scans[code[ip + 1]!]!
|
|
691
782
|
const r = c.parse(input, pos, ctx)
|
|
@@ -706,7 +797,8 @@ function makeDriver(
|
|
|
706
797
|
}
|
|
707
798
|
|
|
708
799
|
case OP_SCOPE:
|
|
709
|
-
case OP_SCOPE_CAP:
|
|
800
|
+
case OP_SCOPE_CAP:
|
|
801
|
+
case OP_SCOPE_PLAIN: {
|
|
710
802
|
const ki = code[ip + 1]!
|
|
711
803
|
const saved = ctx.trivia
|
|
712
804
|
const savedLabels = ctx.triviaKindLabels
|
|
@@ -735,7 +827,7 @@ function makeDriver(
|
|
|
735
827
|
// (`grammar.ts:141`), and the flag is restored here because the table
|
|
736
828
|
// shares one ctx where `parser()` copies it. Bit 2 = the unclassified-scope
|
|
737
829
|
// refusal `grammar.ts:98` raises.
|
|
738
|
-
const policy = code[ip + 3]!
|
|
830
|
+
const policy = code[ip] === OP_SCOPE_PLAIN ? 0 : code[ip + 3]!
|
|
739
831
|
if ((policy & 2) !== 0) refuseUnclassifiedRootScope(ctx._rootTriviaStrictScopes)
|
|
740
832
|
const savedRootCap = ctx._rootTriviaCapture
|
|
741
833
|
if ((policy & 1) !== 0) ctx._rootTriviaCapture = false
|
|
@@ -786,18 +878,19 @@ function makeDriver(
|
|
|
786
878
|
// SCALAR MARKS — `saveTriviaMark` allocated TWICE per term (its own
|
|
787
879
|
// seven-field object plus the five-field CST mark it delegates to).
|
|
788
880
|
const need = rollbackNeeded(ctx)
|
|
789
|
-
const mRaw = need ? cstRawLen(ctx) : 0
|
|
790
881
|
const mTl = need ? cstTlLen(ctx) : 0
|
|
791
|
-
const mLv = need ? cstLeavesLen(ctx) : 0
|
|
792
|
-
const mFl = need ? ctx._fields?.length ?? 0 : 0
|
|
793
|
-
const mEr = need ? ctx._errors?.length ?? 0 : 0
|
|
794
882
|
const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
795
883
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
796
884
|
const scanEnd = skipTrivia(input, cur, ctx)
|
|
885
|
+
const scanTl = need ? cstTlLen(ctx) : 0
|
|
886
|
+
const scanLog = need ? ctx._triviaLog?.length ?? 0 : 0
|
|
887
|
+
const scanRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
797
888
|
const v = exec(child, input, scanEnd, ctx)
|
|
798
889
|
if (v === FAIL) { if (REC) ctx._sync = inheritedSync; return FAIL }
|
|
799
890
|
if (EC.e > scanEnd) cur = EC.e
|
|
800
|
-
else if (need)
|
|
891
|
+
else if (need) rollbackScannedTriviaAt(
|
|
892
|
+
ctx, mTl, scanTl, mLog, scanLog, mRoot, scanRoot,
|
|
893
|
+
)
|
|
801
894
|
if (values !== undefined) values.push(v)
|
|
802
895
|
continue
|
|
803
896
|
}
|
|
@@ -846,7 +939,9 @@ function makeDriver(
|
|
|
846
939
|
if (REC) ctx._sync = inheritedSync
|
|
847
940
|
EC.e = cur
|
|
848
941
|
if (fused) {
|
|
849
|
-
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
|
|
850
945
|
if (COUNT) siteFn('SEQX fn()', fn)
|
|
851
946
|
return fn(values, { start: pos, end: cur })
|
|
852
947
|
}
|
|
@@ -1108,6 +1203,11 @@ function makeDriver(
|
|
|
1108
1203
|
const mySync = REC ? ctx._sync : undefined
|
|
1109
1204
|
const recFx = REC ? fx[code[ip + 6]!] as string[] : undefined
|
|
1110
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
|
|
1111
1211
|
let cur = pos
|
|
1112
1212
|
let count = 0
|
|
1113
1213
|
for (;;) {
|
|
@@ -1120,6 +1220,8 @@ function makeDriver(
|
|
|
1120
1220
|
// must attempt the separator so its failure sets the expected set, which
|
|
1121
1221
|
// is the only thing an under-`min` list has to report.
|
|
1122
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
|
|
1123
1225
|
// One mark pair for the whole loop when a rollback is even possible,
|
|
1124
1226
|
// refreshed per iteration rather than reallocated.
|
|
1125
1227
|
// SCALAR MARKS. This loop took TWO allocations per item (a CST mark and
|
|
@@ -1191,6 +1293,11 @@ function makeDriver(
|
|
|
1191
1293
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
1192
1294
|
break
|
|
1193
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
|
+
}
|
|
1194
1301
|
ctx._fc = false
|
|
1195
1302
|
const v = exec(child, input, itemStart, ctx)
|
|
1196
1303
|
if (v === FAIL) {
|
|
@@ -1446,6 +1553,15 @@ function makeDriver(
|
|
|
1446
1553
|
}
|
|
1447
1554
|
|
|
1448
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
|
|
1449
1565
|
const need = rollbackNeeded(ctx)
|
|
1450
1566
|
const mRaw = need ? cstRawLen(ctx) : 0
|
|
1451
1567
|
const mTl = need ? cstTlLen(ctx) : 0
|
|
@@ -1456,6 +1572,12 @@ function makeDriver(
|
|
|
1456
1572
|
const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
|
|
1457
1573
|
const v = exec(code[ip + 1]!, input, pos, ctx)
|
|
1458
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
|
+
}
|
|
1459
1581
|
if (v === FAIL) { EC.e = pos; return null }
|
|
1460
1582
|
// `not.ts:50` — the ASSERTION's own set, at the assertion's position.
|
|
1461
1583
|
ctx._fe = pos
|
|
@@ -1643,7 +1765,7 @@ export function execRules(
|
|
|
1643
1765
|
const prog = expandCompact(source)
|
|
1644
1766
|
const t = resolveTable(prog)
|
|
1645
1767
|
const scan = opts.leafSwap === false ? t.triviaScan.map(() => null) : t.triviaScan
|
|
1646
|
-
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())
|
|
1647
1769
|
const names = Object.keys(prog.rules)
|
|
1648
1770
|
const entries = names.map(n => prog.rules[n]!)
|
|
1649
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
|
+
}
|