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/ops.ts
CHANGED
|
@@ -37,8 +37,11 @@ export const OP_SEQV = 4
|
|
|
37
37
|
*/
|
|
38
38
|
export const OP_CHOICE = 5
|
|
39
39
|
/**
|
|
40
|
-
* `REP c min max sep flags` — `sep` is a child offset or −1; `max`
|
|
41
|
-
* `
|
|
40
|
+
* `REP c min max sep flags fx sepClass` — `sep` is a child offset or −1; `max`
|
|
41
|
+
* −1 = ∞. `fx` is recovery metadata. The last class is the separator sentinel
|
|
42
|
+
* for a separated list, otherwise the finite/non-nullable optional-item class
|
|
43
|
+
* (or −1). `flags` bit 0 = trailing separator allowed, bit 1 =
|
|
44
|
+
* `keepSeparators`, bit 2 = item expected reporting.
|
|
42
45
|
*/
|
|
43
46
|
export const OP_REP = 6
|
|
44
47
|
/** `REPV …` — `REP` with `valueUnused`. */
|
|
@@ -57,7 +60,8 @@ export const OP_XFORM = 9
|
|
|
57
60
|
* none), `type` and `tags` index the const pool (`tags` −1 = none).
|
|
58
61
|
*
|
|
59
62
|
* `flags` is a BIT FIELD, not a boolean: bit 0 (`&1`) = grammar-owned explicit
|
|
60
|
-
* `captureTrivia`, bit
|
|
63
|
+
* `captureTrivia`, bit 1 (`&2`) = a direct builder is proven not to read
|
|
64
|
+
* `rawChildren`, bit 2 (`&4`) = the builder reads
|
|
61
65
|
* `triviaLog`, bit 3 (`&8`) = it reads `ctx.state`, bit 4 (`&16`) = the node has
|
|
62
66
|
* read fields, bit 5 (`&32`) = `collapse`, bit 6 (`&64`) = `unwrap`, bit 7
|
|
63
67
|
* (`&128`) = `trailingTrivia`. Bit 0 distinguishes capture a host may not
|
|
@@ -110,8 +114,12 @@ export const OP_NODE_TRACK = 19
|
|
|
110
114
|
* on `ctx.trivia` for the duration and restores the outer one after, which is
|
|
111
115
|
* how the runtime's own `advanceTrivia` fast scanner gets reached — the same
|
|
112
116
|
* shared machinery the interpreter uses, not a second copy of it.
|
|
113
|
-
*
|
|
114
|
-
* policy
|
|
117
|
+
*
|
|
118
|
+
* `policy` bit 0 suppresses selected root capture for an opaque scope; bit 1
|
|
119
|
+
* refuses an unclassified local scope while selected root capture is active.
|
|
120
|
+
* Synthetic rule-entry and cross-rule restoration scopes have no policy and use
|
|
121
|
+
* the three-word `OP_SCOPE_PLAIN` row below. Keeping the opcodes distinct means
|
|
122
|
+
* a zero policy costs no extra program word without changing this opcode's ABI.
|
|
115
123
|
*/
|
|
116
124
|
export const OP_SCOPE = 20
|
|
117
125
|
/**
|
|
@@ -122,6 +130,11 @@ export const OP_EXPECT = 21
|
|
|
122
130
|
/**
|
|
123
131
|
* `SEQX f n c1 … cn` — a `transform()` whose child is a `sequence()`.
|
|
124
132
|
*
|
|
133
|
+
* `f >= 0` indexes the reducer in `prog.fns`. `f < 0` is the descriptor
|
|
134
|
+
* `~childIndex`: the transform is the exact direct projection
|
|
135
|
+
* `([…, value, …]) => value`, so the row returns that already-parsed child and
|
|
136
|
+
* carries no reducer. Both forms have the same row width.
|
|
137
|
+
*
|
|
125
138
|
* That pair is the dominant shape in every grammar here (json is nine of them),
|
|
126
139
|
* and running it as two rows costs two switch dispatches and two JS call frames
|
|
127
140
|
* per rule invocation where the emitted code pays neither. Fusing them into one
|
|
@@ -428,10 +441,33 @@ export const OP_LABEL = 39
|
|
|
428
441
|
*/
|
|
429
442
|
export const OP_COV = 40
|
|
430
443
|
/**
|
|
431
|
-
* `SCOPE_PLAIN k c` — a synthetic ambient-trivia scope
|
|
432
|
-
*
|
|
444
|
+
* `SCOPE_PLAIN k c` — a synthetic zero-policy ambient-trivia scope.
|
|
445
|
+
*
|
|
446
|
+
* This is the same scanner/context swap as `OP_SCOPE`, with policy fixed to
|
|
447
|
+
* zero at assembly. `encodeRule()` uses it for a rule entry and `scopedRef()`
|
|
448
|
+
* for the lexical-trivia restoration around a reference reached from
|
|
449
|
+
* `noTrivia()`. Neither row represents an authored `parser()` scope, so neither
|
|
450
|
+
* can be opaque or require the unclassified-scope refusal.
|
|
451
|
+
*
|
|
452
|
+
* A separate opcode is load-bearing: treating a three-word synthetic row as the
|
|
453
|
+
* four-word `OP_SCOPE` makes the following row's opcode become the policy bits.
|
|
433
454
|
*/
|
|
434
455
|
export const OP_SCOPE_PLAIN = 41
|
|
456
|
+
/**
|
|
457
|
+
* `LEX_BODY b fx suffixFx` — one compiler-selected childless lexical replacement
|
|
458
|
+
* body. `b` indexes `prog.lex`; `fx` is the exact base-recognizer failure set;
|
|
459
|
+
* `suffixFx` is optional()'s swallowed literal failure publication.
|
|
460
|
+
*
|
|
461
|
+
* There is deliberately no child operand and no mode bit. The complete
|
|
462
|
+
* CHARACTER and TOKEN candidates were compared before serialization; this row
|
|
463
|
+
* is only the winning TOKEN body. A program without a selected TOKEN body has
|
|
464
|
+
* no row or lexical pool.
|
|
465
|
+
*/
|
|
466
|
+
/** `LEX_BODY body fx suffixFx lineFlags` — selected childless lexical body.
|
|
467
|
+
* lineFlags bit 0 publishes the regex range; bit 1 publishes a matched suffix. */
|
|
468
|
+
export const OP_LEX_BODY = 42
|
|
469
|
+
/** `LEX_PROGRAM p` — selected fixed composite lexical body, childless. */
|
|
470
|
+
export const OP_LEX_PROGRAM = 43
|
|
435
471
|
/**
|
|
436
472
|
* `DISPATCH sel d other otherRouted n a1 … an` — `dispatch()`.
|
|
437
473
|
*
|
|
@@ -464,4 +500,6 @@ export const OP_NAMES: Record<number, string> = {
|
|
|
464
500
|
[OP_GREEDY]: 'GREEDY', [OP_REJECT]: 'REJECT', [OP_ARMGATE]: 'ARMGATE',
|
|
465
501
|
[OP_LIVE]: 'LIVE', [OP_ATTEMPT]: 'ATTEMPT', [OP_LABEL]: 'LABEL', [OP_COV]: 'COV',
|
|
466
502
|
[OP_SCOPE_PLAIN]: 'SCOPE_PLAIN',
|
|
503
|
+
[OP_LEX_BODY]: 'LEX_BODY',
|
|
504
|
+
[OP_LEX_PROGRAM]: 'LEX_PROGRAM',
|
|
467
505
|
}
|
package/src/table/program.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { regex } from '../combinators/regex.ts'
|
|
|
5
5
|
import { choice } from '../combinators/choice.ts'
|
|
6
6
|
import { many } from '../combinators/repeat.ts'
|
|
7
7
|
import type { EmittedFactory, PoolPlan } from './emit-assembly.ts'
|
|
8
|
+
import { buildLexBodyRecognizer, type LexBodyRecognizer } from './lex-body.ts'
|
|
9
|
+
import { buildLexProgramRunner, type LexProgramRunner } from './lex-program.ts'
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* One assembly the BUILD already compiled, so the run does not have to.
|
|
@@ -57,6 +59,11 @@ export type TableProgram = {
|
|
|
57
59
|
* at `resolveTable`, exactly like the char-class ASCII lookups.
|
|
58
60
|
*/
|
|
59
61
|
readonly dsp: readonly DispatchSpec[]
|
|
62
|
+
/** Selected childless lexical bodies. Losing compiler candidates never enter
|
|
63
|
+
* this pool. The first row is `[regexConst, suffixCodeUnit]`. */
|
|
64
|
+
readonly lex?: readonly LexBodySpec[]
|
|
65
|
+
/** Selected composite lexical programs. Losing candidates never enter it. */
|
|
66
|
+
readonly lexPrograms?: readonly LexProgramSpec[]
|
|
60
67
|
/** Rule name → entry offset in `code`. */
|
|
61
68
|
readonly rules: Readonly<Record<string, number>>
|
|
62
69
|
/**
|
|
@@ -291,6 +298,8 @@ export type CompactProgram = {
|
|
|
291
298
|
readonly f: readonly unknown[]
|
|
292
299
|
readonly l?: 0 | 1
|
|
293
300
|
readonly p?: readonly DispatchSpec[]
|
|
301
|
+
readonly lx?: readonly LexBodySpec[]
|
|
302
|
+
readonly lp?: readonly LexProgramSpec[]
|
|
294
303
|
readonly lb?: readonly string[]
|
|
295
304
|
readonly rc?: 0 | 1
|
|
296
305
|
readonly h?: 'ast' | 'cst'
|
|
@@ -309,6 +318,8 @@ export function expandCompact(p: TableProgram | CompactProgram): TableProgram {
|
|
|
309
318
|
return ownTableProgram({
|
|
310
319
|
code: p.c, k: p.k, cc: p.x, fx: p.e, disp: p.d, rules: p.r, fns: p.f,
|
|
311
320
|
lines: p.l ?? 0, dsp: p.p ?? [],
|
|
321
|
+
...(p.lx === undefined ? {} : { lex: p.lx }),
|
|
322
|
+
...(p.lp === undefined ? {} : { lexPrograms: p.lp }),
|
|
312
323
|
...(p.lb === undefined ? {} : { labels: p.lb }),
|
|
313
324
|
...(p.rc === undefined ? {} : { classified: p.rc }),
|
|
314
325
|
...(p.h === undefined ? {} : { hostMode: p.h }),
|
|
@@ -322,6 +333,28 @@ export function expandCompact(p: TableProgram | CompactProgram): TableProgram {
|
|
|
322
333
|
})
|
|
323
334
|
}
|
|
324
335
|
|
|
336
|
+
/** Selected lexical body. `suffixCodeUnit === -1` is a direct regex terminal;
|
|
337
|
+
* 0..0xFFFF is the legacy regex + optional(one code unit) spelling. */
|
|
338
|
+
export type LexBodySpec = readonly [regexConst: number, suffixCodeUnit: number]
|
|
339
|
+
|
|
340
|
+
/** `kind=0`: fixed ordered four-terminal selected body. */
|
|
341
|
+
export type LexProgramSpec = readonly [
|
|
342
|
+
kind: 0, digest: number, commonClass: number, choiceFx: number,
|
|
343
|
+
arm0Matcher: number, arm0Fx: number,
|
|
344
|
+
arm1Matcher: number, arm1Fx: number,
|
|
345
|
+
arm2Matcher: number, arm2Fx: number,
|
|
346
|
+
arm3Matcher: number, arm3Fx: number,
|
|
347
|
+
trackLines: 0 | 1,
|
|
348
|
+
] | readonly [
|
|
349
|
+
kind: 1, digest: number,
|
|
350
|
+
guard0Arm0: number, guard0Arm1: number, guard0Arm2: number, guard0Arm3: number,
|
|
351
|
+
guard0Fx: number,
|
|
352
|
+
guard1Matcher: number, guard1Fx: number,
|
|
353
|
+
terminalMatcher: number, terminalFx: number,
|
|
354
|
+
trackLines: 0 | 1,
|
|
355
|
+
] | readonly [kind: 2, digest: number, ...fixedTreeWords: number[]]
|
|
356
|
+
| readonly [kind: 3, digest: number, scan: number]
|
|
357
|
+
|
|
325
358
|
/** One `dispatch()`'s routing tables, in serialisable form. */
|
|
326
359
|
export type DispatchSpec = {
|
|
327
360
|
/** Exact keys, parallel to `keyArm`. */
|
|
@@ -353,7 +386,12 @@ export type ResolvedDispatchSpec = {
|
|
|
353
386
|
readonly expected: readonly string[]
|
|
354
387
|
}
|
|
355
388
|
|
|
356
|
-
/**
|
|
389
|
+
/**
|
|
390
|
+
* Refuse malformed fixed arm references before closure or emitted linking.
|
|
391
|
+
*
|
|
392
|
+
* This is deliberately an assembly-time trust boundary. `execRules()` stays an
|
|
393
|
+
* independent semantic oracle and never pays artifact validation per parse.
|
|
394
|
+
*/
|
|
357
395
|
export function validateDispatchSpec(
|
|
358
396
|
spec: ResolvedDispatchSpec | undefined,
|
|
359
397
|
arity: number,
|
|
@@ -428,6 +466,8 @@ export type ResolvedTable = {
|
|
|
428
466
|
readonly fx: readonly (readonly string[])[]
|
|
429
467
|
readonly disp: readonly ResolvedDispatch[]
|
|
430
468
|
readonly dsp: readonly ResolvedDispatchSpec[]
|
|
469
|
+
readonly lex: readonly LexBodyRecognizer[]
|
|
470
|
+
readonly lexPrograms: readonly LexProgramRunner[]
|
|
431
471
|
/** Trivia combinators rebuilt from `triviaSpecs`, once per table. */
|
|
432
472
|
readonly trivia: readonly Combinator<unknown>[]
|
|
433
473
|
/**
|
|
@@ -605,6 +645,7 @@ export function resolveTable(prog: TableProgram): ResolvedTable {
|
|
|
605
645
|
if (hit !== undefined) return hit
|
|
606
646
|
const cc = prog.cc.map(resolveClass)
|
|
607
647
|
const triviaBuilt = (prog.triviaSpecs ?? []).map(buildTrivia)
|
|
648
|
+
const lex = (prog.lex ?? []).map(spec => buildLexBodyRecognizer(spec, prog.k))
|
|
608
649
|
const built: ResolvedTable = {
|
|
609
650
|
prog,
|
|
610
651
|
code: Int32Array.from(prog.code),
|
|
@@ -623,6 +664,9 @@ export function resolveTable(prog: TableProgram): ResolvedTable {
|
|
|
623
664
|
routed: d.routed,
|
|
624
665
|
expected: d.expected,
|
|
625
666
|
})),
|
|
667
|
+
lex,
|
|
668
|
+
lexPrograms: (prog.lexPrograms ?? []).map(spec =>
|
|
669
|
+
buildLexProgramRunner(spec, lex, cc, prog.fx)),
|
|
626
670
|
rules: prog.rules,
|
|
627
671
|
}
|
|
628
672
|
if (owner !== undefined) owner.resolved = built
|
|
@@ -687,7 +731,7 @@ export type CompactFolded = {
|
|
|
687
731
|
* Named rather than derived so the refusal can say which one broke.
|
|
688
732
|
*/
|
|
689
733
|
const SHARED_FIELDS = [
|
|
690
|
-
'k', 'cc', 'fx', 'disp', 'dsp', 'rules', 'labels', 'classified',
|
|
734
|
+
'k', 'cc', 'fx', 'disp', 'dsp', 'lex', 'lexPrograms', 'rules', 'labels', 'classified',
|
|
691
735
|
'scanSkip', 'scanSkipOf', 'scans', 'triviaSpecs', 'runtimeOnly',
|
|
692
736
|
// `rec` is a property of the GRAMMAR BUILD, not of the trackLines/hostMode
|
|
693
737
|
// axis a fold varies — every variant of one export is encoded with the same
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { OP_LIT, OP_NOT, OP_RX } from './ops.ts'
|
|
2
|
+
|
|
3
|
+
/** Pure recognition only. Consumption owns value, CST, line, failure and cursor effects. */
|
|
4
|
+
export type ScalarRecognizer = (input: string, pos: number, seed?: number) => number
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* One recognizer per existing terminal constant. This is the common raw/pending
|
|
8
|
+
* seam: a later token cursor may provide a cached end for the same (pos, spec)
|
|
9
|
+
* without changing either consumer's materialization protocol.
|
|
10
|
+
*/
|
|
11
|
+
export function makeScalarRecognizer(op: number, spec: unknown): ScalarRecognizer | undefined {
|
|
12
|
+
if (op === OP_RX && spec instanceof RegExp) {
|
|
13
|
+
return (input, pos) => {
|
|
14
|
+
spec.lastIndex = pos
|
|
15
|
+
return spec.test(input) ? spec.lastIndex : -1
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (op !== OP_LIT || typeof spec !== 'string') return undefined
|
|
19
|
+
const len = spec.length
|
|
20
|
+
if (len === 1) {
|
|
21
|
+
const c0 = spec.charCodeAt(0)
|
|
22
|
+
return (input, pos) => input.charCodeAt(pos) === c0 ? pos + 1 : -1
|
|
23
|
+
}
|
|
24
|
+
if (len === 2) {
|
|
25
|
+
const c0 = spec.charCodeAt(0), c1 = spec.charCodeAt(1)
|
|
26
|
+
return (input, pos) => input.charCodeAt(pos) === c0 && input.charCodeAt(pos + 1) === c1 ? pos + 2 : -1
|
|
27
|
+
}
|
|
28
|
+
if (len === 3) {
|
|
29
|
+
const c0 = spec.charCodeAt(0), c1 = spec.charCodeAt(1), c2 = spec.charCodeAt(2)
|
|
30
|
+
return (input, pos) => input.charCodeAt(pos) === c0
|
|
31
|
+
&& input.charCodeAt(pos + 1) === c1
|
|
32
|
+
&& input.charCodeAt(pos + 2) === c2 ? pos + 3 : -1
|
|
33
|
+
}
|
|
34
|
+
return (input, pos) => input.startsWith(spec, pos) ? pos + len : -1
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Exact bounded shape whose terminal value can be materialized without a node capture frame. */
|
|
38
|
+
export function scalarTerminalNodeChild(code: ArrayLike<number>, ip: number): number {
|
|
39
|
+
if (code[ip + 1]! < 0 || code[ip + 3] !== 0 || code[ip + 4] !== -1) return -1
|
|
40
|
+
const child = code[ip + 2]!
|
|
41
|
+
const op = code[child]
|
|
42
|
+
return op === OP_RX || op === OP_LIT ? child : -1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Direct untracked terminal whose recognition `not()` can inspect without materializing it. */
|
|
46
|
+
export function scalarTerminalNotChild(code: ArrayLike<number>, ip: number): number {
|
|
47
|
+
if (code[ip] !== OP_NOT) return -1
|
|
48
|
+
const child = code[ip + 1]!
|
|
49
|
+
const op = code[child]
|
|
50
|
+
return op === OP_RX || op === OP_LIT ? child : -1
|
|
51
|
+
}
|
package/src/table/site-labels.ts
CHANGED
|
@@ -148,10 +148,20 @@ function transfer(code: Int32Array, ip: number, at: SiteLabel, hostCst: boolean)
|
|
|
148
148
|
return { tri, buf: at.buf, cap }
|
|
149
149
|
}
|
|
150
150
|
if (op === OP_NODE || op === OP_NODE_TRACK) {
|
|
151
|
+
const flags = code[ip + 3]!
|
|
152
|
+
const directChildren = !hostCst && op === OP_NODE && code[ip + 1]! >= 0 && code[ip + 4]! < 0
|
|
153
|
+
&& (flags === 2 || flags === 18 || flags === 34)
|
|
154
|
+
if (directChildren) {
|
|
155
|
+
// The confirmed low-arity builder projection installs split child/leaf
|
|
156
|
+
// arrays instead of a CstCaptureBuf. Descendants still capture, but they
|
|
157
|
+
// must take the generic collector branch rather than dereference `_cstBuf`.
|
|
158
|
+
// False means UNKNOWN in this lattice, which is exactly the safe answer.
|
|
159
|
+
return { tri: at.tri, buf: false, cap: CAP_OFF }
|
|
160
|
+
}
|
|
151
161
|
// `ctx._cstBuf = buf` and `ctx.captureTrivia = <literal>`, both unconditional
|
|
152
162
|
// — `emit-assembly.ts`'s `OP_NODE` body opens the buffer before it descends
|
|
153
163
|
// and closes it after, whatever the host mode is.
|
|
154
|
-
const cap = ((
|
|
164
|
+
const cap = ((flags & 4) !== 0 || hostCst) ? CAP_ON : CAP_OFF
|
|
155
165
|
if (at.buf && cap === at.cap) return at
|
|
156
166
|
return { tri: at.tri, buf: true, cap }
|
|
157
167
|
}
|
package/src/table/stamp.ts
CHANGED
|
@@ -98,7 +98,7 @@ export function stampRuleMap(
|
|
|
98
98
|
// up as `ok: true` with bytes missing, never as an error.
|
|
99
99
|
//
|
|
100
100
|
// Read from the ENTRY ROW, not from one program-wide slot: `encodeRule` wraps each
|
|
101
|
-
// rule's entry in `
|
|
101
|
+
// rule's entry in `OP_SCOPE_PLAIN <triviaSlot> <body>` iff THAT rule has ambient trivia,
|
|
102
102
|
// and a `composeLeaf` grammar's pieces do not agree (the same disagreement
|
|
103
103
|
// `scanSkipOf` exists for). The owning driver passes its resolved table, so
|
|
104
104
|
// this is the exact trivia array it already built.
|
package/src/types.ts
CHANGED
|
@@ -82,7 +82,7 @@ export type ParserDef =
|
|
|
82
82
|
| { tag: 'leaf'; parser: Combinator<unknown>; fn: (v: unknown, span: { start: number; end: number }) => unknown; fnSrc?: string }
|
|
83
83
|
| { tag: 'label'; label: string; parser: Combinator<unknown> }
|
|
84
84
|
| { tag: 'field'; name: string; parser: Combinator<unknown> }
|
|
85
|
-
| { tag: 'grammar'; parser: Combinator<unknown>; triviaParser: Combinator<unknown> | undefined; clearTrivia?: boolean; captureTrivia?: boolean; rootCapture?: 'opaque'; trackLines: boolean }
|
|
85
|
+
| { tag: 'grammar'; parser: Combinator<unknown>; triviaParser: Combinator<unknown> | undefined; clearTrivia?: boolean; captureTrivia?: boolean; rootCapture?: 'opaque'; trackLines: boolean; constructionTrackLines?: 'on' | 'off' | 'inherit'; constructionCaptureTriviaKinds?: readonly string[] }
|
|
86
86
|
| { tag: 'lazy'; thunk: () => Combinator<unknown> }
|
|
87
87
|
| { tag: 'not'; parser: Combinator<unknown> }
|
|
88
88
|
// Positive lookahead. Zero-width like `not`, but — unlike `not` — it KNOWS what
|
package/src/version.ts
CHANGED