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.
Files changed (124) hide show
  1. package/README.md +27 -21
  2. package/dist/analysis/diagnostics.js +2863 -353
  3. package/dist/analysis/diagnostics.js.map +3 -3
  4. package/dist/cli/index.js +2854 -344
  5. package/dist/cli/index.js.map +3 -3
  6. package/dist/combinators/expect.d.ts +1 -0
  7. package/dist/combinators/expect.d.ts.map +1 -1
  8. package/dist/combinators/expected.d.ts +8 -0
  9. package/dist/combinators/expected.d.ts.map +1 -0
  10. package/dist/combinators/first-set.d.ts +8 -0
  11. package/dist/combinators/first-set.d.ts.map +1 -1
  12. package/dist/combinators/grammar.d.ts.map +1 -1
  13. package/dist/combinators/keywords.d.ts +4 -0
  14. package/dist/combinators/keywords.d.ts.map +1 -1
  15. package/dist/combinators/literal.d.ts.map +1 -1
  16. package/dist/combinators/not.d.ts.map +1 -1
  17. package/dist/combinators/peek.d.ts.map +1 -1
  18. package/dist/combinators/regex.d.ts.map +1 -1
  19. package/dist/combinators/repeat.d.ts.map +1 -1
  20. package/dist/combinators/sequence.d.ts.map +1 -1
  21. package/dist/combinators/trivia-skip.d.ts +16 -3
  22. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  23. package/dist/compiler/direct-projection.d.ts +12 -0
  24. package/dist/compiler/direct-projection.d.ts.map +1 -0
  25. package/dist/compiler/token-alphabet.d.ts +845 -5
  26. package/dist/compiler/token-alphabet.d.ts.map +1 -1
  27. package/dist/compiler/token-capability.d.ts +3 -0
  28. package/dist/compiler/token-capability.d.ts.map +1 -0
  29. package/dist/compiler/token-capability.js +3192 -0
  30. package/dist/compiler/token-capability.js.map +6 -0
  31. package/dist/cst/trivia-charscan.d.ts +3 -0
  32. package/dist/cst/trivia-charscan.d.ts.map +1 -1
  33. package/dist/cst/trivia-css-scanner.d.ts +5 -0
  34. package/dist/cst/trivia-css-scanner.d.ts.map +1 -0
  35. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  36. package/dist/index.js +2862 -339
  37. package/dist/index.js.map +3 -3
  38. package/dist/language-service/index.js +5 -1
  39. package/dist/language-service/index.js.map +1 -1
  40. package/dist/plugin/index.js +2870 -347
  41. package/dist/plugin/index.js.map +3 -3
  42. package/dist/spec/index.js +315 -46
  43. package/dist/spec/index.js.map +2 -2
  44. package/dist/table/assemble.d.ts.map +1 -1
  45. package/dist/table/child-slots.d.ts.map +1 -1
  46. package/dist/table/compile-rule-map.d.ts +3 -1
  47. package/dist/table/compile-rule-map.d.ts.map +1 -1
  48. package/dist/table/emit-assembly.d.ts +12 -13
  49. package/dist/table/emit-assembly.d.ts.map +1 -1
  50. package/dist/table/emit.d.ts.map +1 -1
  51. package/dist/table/encode.d.ts.map +1 -1
  52. package/dist/table/exec-baseline.d.ts.map +1 -1
  53. package/dist/table/exec.d.ts.map +1 -1
  54. package/dist/table/index.js +2819 -300
  55. package/dist/table/index.js.map +3 -3
  56. package/dist/table/lex-body.d.ts +9 -0
  57. package/dist/table/lex-body.d.ts.map +1 -0
  58. package/dist/table/lex-program.d.ts +25 -0
  59. package/dist/table/lex-program.d.ts.map +1 -0
  60. package/dist/table/ops.d.ts +43 -7
  61. package/dist/table/ops.d.ts.map +1 -1
  62. package/dist/table/program.d.ts +50 -2
  63. package/dist/table/program.d.ts.map +1 -1
  64. package/dist/table/scalar-terminal.d.ts +13 -0
  65. package/dist/table/scalar-terminal.d.ts.map +1 -0
  66. package/dist/table/site-labels.d.ts.map +1 -1
  67. package/dist/types.d.ts +2 -0
  68. package/dist/types.d.ts.map +1 -1
  69. package/dist/version.d.ts +1 -1
  70. package/package.json +11 -11
  71. package/src/combinators/expect.ts +1 -0
  72. package/src/combinators/expected.ts +17 -0
  73. package/src/combinators/first-set.ts +58 -5
  74. package/src/combinators/grammar.ts +8 -3
  75. package/src/combinators/keywords.ts +22 -6
  76. package/src/combinators/literal.ts +2 -1
  77. package/src/combinators/not.ts +5 -1
  78. package/src/combinators/peek.ts +5 -1
  79. package/src/combinators/regex.ts +5 -3
  80. package/src/combinators/repeat.ts +4 -10
  81. package/src/combinators/sequence.ts +17 -14
  82. package/src/combinators/trivia-skip.ts +46 -13
  83. package/src/compiler/direct-projection.ts +43 -0
  84. package/src/compiler/token-alphabet.ts +4316 -12
  85. package/src/compiler/token-capability.ts +6 -0
  86. package/src/cst/trivia-charscan.ts +34 -0
  87. package/src/cst/trivia-css-scanner.ts +142 -0
  88. package/src/cst/trivia-kinds.ts +3 -1
  89. package/src/plugin/index.ts +4 -4
  90. package/src/table/assemble.ts +1211 -221
  91. package/src/table/child-slots.ts +2 -1
  92. package/src/table/compile-rule-map.ts +21 -3
  93. package/src/table/emit-assembly.ts +328 -85
  94. package/src/table/emit.ts +16 -2
  95. package/src/table/encode.ts +242 -50
  96. package/src/table/exec-baseline.ts +3 -7
  97. package/src/table/exec.ts +121 -7
  98. package/src/table/lex-body.ts +30 -0
  99. package/src/table/lex-program.ts +422 -0
  100. package/src/table/ops.ts +45 -7
  101. package/src/table/program.ts +46 -2
  102. package/src/table/scalar-terminal.ts +51 -0
  103. package/src/table/site-labels.ts +11 -1
  104. package/src/table/stamp.ts +1 -1
  105. package/src/types.ts +1 -1
  106. package/src/version.ts +1 -1
  107. package/dist/analysis/diagnostics.cjs +0 -13824
  108. package/dist/analysis/diagnostics.cjs.map +0 -6
  109. package/dist/cli/index.cjs +0 -14925
  110. package/dist/cli/index.cjs.map +0 -6
  111. package/dist/index.cjs +0 -16391
  112. package/dist/index.cjs.map +0 -6
  113. package/dist/language-service/index.cjs +0 -1304
  114. package/dist/language-service/index.cjs.map +0 -6
  115. package/dist/oracle/index.cjs +0 -254
  116. package/dist/oracle/index.cjs.map +0 -6
  117. package/dist/plugin/index.cjs +0 -16097
  118. package/dist/plugin/index.cjs.map +0 -6
  119. package/dist/run/index.cjs +0 -577
  120. package/dist/run/index.cjs.map +0 -6
  121. package/dist/spec/index.cjs +0 -6236
  122. package/dist/spec/index.cjs.map +0 -6
  123. package/dist/table/index.cjs +0 -11463
  124. 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` −1 = ∞.
41
- * `flags` bit 0 = trailing separator allowed, bit 1 = `keepSeparators`.
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 2 (`&4`) = the builder reads
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
- * Synthetic rule-entry/restoration scopes use `OP_SCOPE_PLAIN` so a zero
114
- * policy stays a three-word row without changing this opcode's ABI.
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 with policy fixed to zero.
432
- * A distinct opcode prevents the next row's opcode from being read as policy.
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
  }
@@ -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
- /** Refuse malformed dispatch arm references before closure/emitted linking. */
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
+ }
@@ -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 = ((code[ip + 3]! & 4) !== 0 || hostCst) ? CAP_ON : CAP_OFF
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
  }
@@ -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 `OP_SCOPE <triviaSlot> <body>` iff THAT rule has ambient trivia,
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
@@ -13,4 +13,4 @@
13
13
  * Kept in sync with `package.json` by `test/unit/version-sync.test.ts`, which fails
14
14
  * the build if this constant drifts from the published version.
15
15
  */
16
- export const PARSEMAN_VERSION = '0.47.1'
16
+ export const PARSEMAN_VERSION = '0.48.0'