parseman 0.58.2 → 0.59.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 (90) hide show
  1. package/dist/analysis/diagnostics.js +344 -130
  2. package/dist/analysis/diagnostics.js.map +3 -3
  3. package/dist/cli/index.js +346 -131
  4. package/dist/cli/index.js.map +3 -3
  5. package/dist/combinators/dispatch.d.ts.map +1 -1
  6. package/dist/combinators/field-trivia.d.ts.map +1 -1
  7. package/dist/combinators/first-set.d.ts.map +1 -1
  8. package/dist/combinators/keywords.d.ts.map +1 -1
  9. package/dist/combinators/literal.d.ts.map +1 -1
  10. package/dist/combinators/node.d.ts.map +1 -1
  11. package/dist/combinators/regex.d.ts.map +1 -1
  12. package/dist/combinators/scanTo.d.ts.map +1 -1
  13. package/dist/combinators/token.d.ts.map +1 -1
  14. package/dist/compiler/linker.d.ts.map +1 -1
  15. package/dist/compiler/token-capability.js +28 -4
  16. package/dist/compiler/token-capability.js.map +2 -2
  17. package/dist/compiler/token-scanner.d.ts.map +1 -1
  18. package/dist/cst/capture-buffer.d.ts.map +1 -1
  19. package/dist/cst/plain.js +69 -0
  20. package/dist/cst/plain.js.map +6 -0
  21. package/dist/index.js +350 -138
  22. package/dist/index.js.map +3 -3
  23. package/dist/language-service/index.js +29 -5
  24. package/dist/language-service/index.js.map +3 -3
  25. package/dist/lead-ranges.d.ts +26 -0
  26. package/dist/lead-ranges.d.ts.map +1 -0
  27. package/dist/line-index.d.ts +13 -0
  28. package/dist/line-index.d.ts.map +1 -1
  29. package/dist/plugin/entry-supercompiler.d.ts.map +1 -1
  30. package/dist/plugin/index.js +352 -140
  31. package/dist/plugin/index.js.map +3 -3
  32. package/dist/recovery/scan.d.ts.map +1 -1
  33. package/dist/regex/classes.d.ts +5 -3
  34. package/dist/regex/classes.d.ts.map +1 -1
  35. package/dist/regex/first-set.d.ts +7 -1
  36. package/dist/regex/first-set.d.ts.map +1 -1
  37. package/dist/run/index.js +14 -4
  38. package/dist/run/index.js.map +3 -3
  39. package/dist/spec/index.js +18 -1
  40. package/dist/spec/index.js.map +2 -2
  41. package/dist/table/assemble.d.ts.map +1 -1
  42. package/dist/table/compile-rule-map.d.ts.map +1 -1
  43. package/dist/table/emit-assembly.d.ts +9 -2
  44. package/dist/table/emit-assembly.d.ts.map +1 -1
  45. package/dist/table/emit.d.ts +6 -0
  46. package/dist/table/emit.d.ts.map +1 -1
  47. package/dist/table/encode-baseline.d.ts.map +1 -1
  48. package/dist/table/index.js +330 -129
  49. package/dist/table/index.js.map +3 -3
  50. package/dist/table/program.d.ts.map +1 -1
  51. package/dist/table/run-support.d.ts.map +1 -1
  52. package/dist/table/scan-shapes.d.ts +1 -1
  53. package/dist/table/scan-shapes.d.ts.map +1 -1
  54. package/dist/table/site-labels.d.ts.map +1 -1
  55. package/dist/table/trivia-gap.d.ts +11 -1
  56. package/dist/table/trivia-gap.d.ts.map +1 -1
  57. package/dist/version.d.ts +1 -1
  58. package/package.json +2 -1
  59. package/src/combinators/choice.ts +4 -0
  60. package/src/combinators/dispatch.ts +2 -1
  61. package/src/combinators/field-trivia.ts +6 -5
  62. package/src/combinators/first-set.ts +13 -4
  63. package/src/combinators/keywords.ts +3 -2
  64. package/src/combinators/literal.ts +5 -4
  65. package/src/combinators/node.ts +3 -1
  66. package/src/combinators/regex.ts +33 -13
  67. package/src/combinators/scanTo.ts +2 -1
  68. package/src/combinators/token.ts +3 -6
  69. package/src/compiler/linker.ts +4 -2
  70. package/src/compiler/token-scanner.ts +4 -1
  71. package/src/cst/capture-buffer.ts +13 -5
  72. package/src/cst/plain.d.ts +35 -0
  73. package/src/cst/plain.js +74 -0
  74. package/src/lead-ranges.ts +53 -0
  75. package/src/line-index.ts +37 -0
  76. package/src/plugin/entry-supercompiler.ts +3 -2
  77. package/src/recovery/scan.ts +4 -4
  78. package/src/regex/classes.ts +22 -7
  79. package/src/regex/first-set.ts +67 -12
  80. package/src/table/assemble.ts +75 -38
  81. package/src/table/compile-rule-map.ts +5 -3
  82. package/src/table/emit-assembly.ts +69 -32
  83. package/src/table/emit.ts +9 -1
  84. package/src/table/encode-baseline.ts +2 -1
  85. package/src/table/program.ts +2 -1
  86. package/src/table/run-support.ts +4 -7
  87. package/src/table/scan-shapes.ts +5 -2
  88. package/src/table/site-labels.ts +2 -1
  89. package/src/table/trivia-gap.ts +19 -0
  90. package/src/version.ts +1 -1
@@ -69,7 +69,7 @@ import type { Combinator, FieldMap, FirstSet, ParseContext, ParseResult, ParserD
69
69
  import { balanced, scanTo } from '../combinators/scanTo.ts'
70
70
  import { buildFieldMap } from '../compiler/fields.ts'
71
71
  import { FIELD_LEADING_TRIVIA, FIELD_TRAILING_TRIVIA, LEAD, triviaFieldCapture } from '../combinators/field-trivia.ts'
72
- import { skipDeferredTrivia } from './trivia-gap.ts'
72
+ import { skipDeferredTrivia, triviaLeadTable } from './trivia-gap.ts'
73
73
  import { asciiFoldKey } from '../combinators/dispatch.ts'
74
74
  import { projectChild, unwrapChild } from '../combinators/node.ts'
75
75
  import { cstOutputHost } from '../compiler/build-arity.ts'
@@ -83,6 +83,7 @@ import {
83
83
  demoteCapturedToRaw, pushCstChild, pushCstLeaf,
84
84
  type CstCaptureBuf,
85
85
  } from '../cst/capture-buffer.ts'
86
+ import { PlainBuf, PlainField, PlainLeaf, PlainNode, PlainSpan } from '../cst/plain.js'
86
87
  import {
87
88
  OP_CHOICE, OP_EMPTY, OP_GATE, OP_LEAF, OP_SOURCE_LEAF, OP_LIT, OP_NODE, OP_NOT, OP_OPT,
88
89
  OP_PEEK, OP_REP, OP_REPV, OP_RULE, OP_RX, OP_SEQ, OP_SEQV, OP_XFORM,
@@ -197,7 +198,6 @@ function linkMatcher(m: readonly [number, string, string, number]): (key: string
197
198
  }
198
199
  }
199
200
 
200
- type Leaf = { _tag: 'leaf'; value: string; span: { start: number; end: number } }
201
201
 
202
202
  /**
203
203
  * The `COV` slot's value when a parse installs no collector. ONE shared function
@@ -542,7 +542,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
542
542
  const CLEAN = REC ? (): boolean => false : failCleanAnalysis(code)
543
543
 
544
544
  function pushLeaf(ctx: ParseContext, value: string, s: number, e: number): void {
545
- const lf: Leaf = { _tag: 'leaf', value, span: { start: s, end: e } }
545
+ const lf = new PlainLeaf(value, new PlainSpan(s, e))
546
546
  pushCstLeaf(ctx, lf)
547
547
  }
548
548
 
@@ -592,7 +592,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
592
592
  ctx.captureTrivia = sCap
593
593
  if (value === FAIL) return FAIL
594
594
  const end = EC.e
595
- const span = { start: pos, end }
595
+ const span = new PlainSpan(pos, end)
596
596
  const node = build(kids, undefined, span, rawKids, EMPTY_TL, undefined)
597
597
  if (sBuf !== undefined || sCh !== undefined) {
598
598
  pushCstChild(ctx, node, rawEntry(node, input, pos, end))
@@ -646,7 +646,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
646
646
  ctx.captureTrivia = sCap
647
647
  if (value === FAIL) return FAIL
648
648
  const end = EC.e
649
- const node = build(captured, undefined, { start: pos, end }, EMPTY_CH, EMPTY_TL, undefined)
649
+ const node = build(captured, undefined, new PlainSpan(pos, end), EMPTY_CH, EMPTY_TL, undefined)
650
650
  if (sBuf !== undefined || sCh !== undefined) {
651
651
  pushCstChild(ctx, node, sBuf !== undefined || sRaw !== undefined ? rawEntry(node, input, pos, end) : undefined)
652
652
  }
@@ -684,7 +684,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
684
684
  ctx.captureTrivia = sCap
685
685
  if (value === FAIL) return FAIL
686
686
  const end = EC.e
687
- const node = build(captured, fieldMap, { start: pos, end }, EMPTY_CH, EMPTY_TL, undefined)
687
+ const node = build(captured, fieldMap, new PlainSpan(pos, end), EMPTY_CH, EMPTY_TL, undefined)
688
688
  if (sBuf !== undefined || sCh !== undefined) {
689
689
  pushCstChild(ctx, node, sBuf !== undefined || sRaw !== undefined ? rawEntry(node, input, pos, end) : undefined)
690
690
  }
@@ -722,7 +722,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
722
722
  ctx.captureTrivia = sCap
723
723
  if (value === FAIL) return FAIL
724
724
  const end = EC.e
725
- const span = { start: pos, end }
725
+ const span = new PlainSpan(pos, end)
726
726
  const node = kids.length === 1
727
727
  ? kids[0]
728
728
  : build(kids, undefined, span, rawKids, EMPTY_TL, undefined)
@@ -764,7 +764,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
764
764
  const end = EC.e
765
765
  const node = captured.length === 1
766
766
  ? captured[0]
767
- : build(captured, undefined, { start: pos, end }, EMPTY_CH, EMPTY_TL, undefined)
767
+ : build(captured, undefined, new PlainSpan(pos, end), EMPTY_CH, EMPTY_TL, undefined)
768
768
  if (sBuf !== undefined || sCh !== undefined) {
769
769
  pushCstChild(ctx, node, sBuf !== undefined || sRaw !== undefined ? rawEntry(node, input, pos, end) : undefined)
770
770
  }
@@ -959,7 +959,31 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
959
959
  return out
960
960
  }
961
961
 
962
+ /**
963
+ * NO TRIVIA CAN START AT `cur` — `triviaLeadTable` for the installed trivia. A
964
+ * gap there is empty: nothing to scan, nothing recorded, so nothing for a
965
+ * sequence term to mark. One table per trivia slot, built with the assembly;
966
+ * the installed trivia is matched to its slot only when it changes (a trivia
967
+ * the caller passed that is no slot gets its own table then).
968
+ */
969
+ const LEAD_TABLES = trivia.map(tp => tp === undefined ? null : triviaLeadTable(tp))
970
+ let LEAD_OF: Combinator<unknown> | undefined
971
+ let LEAD_TABLE: Uint8Array | null = null
972
+ function cannotStartTrivia(input: string, cur: number, triviaP: Combinator<unknown>): boolean {
973
+ if (triviaP !== LEAD_OF) {
974
+ LEAD_OF = triviaP
975
+ const slot = trivia.indexOf(triviaP)
976
+ LEAD_TABLE = slot >= 0 ? LEAD_TABLES[slot]! : triviaLeadTable(triviaP)
977
+ }
978
+ const table = LEAD_TABLE
979
+ if (table === null) return false
980
+ const c = input.charCodeAt(cur)
981
+ return c < 128 && table[c] === 0
982
+ }
983
+
962
984
  function skipTrivia(input: string, cur: number, ctx: ParseContext): number {
985
+ const t = ctx.trivia
986
+ if (t !== undefined && cannotStartTrivia(input, cur, t)) return cur
963
987
  const s = SCAN
964
988
  if (s !== null
965
989
  && ctx._triviaLog === undefined
@@ -1041,7 +1065,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
1041
1065
  * into them.
1042
1066
  */
1043
1067
  function nextTerm(child: Piece, input: string, cur: number, ctx: ParseContext): number {
1044
- if (ctx.trivia === undefined) {
1068
+ const t = ctx.trivia
1069
+ if (t === undefined || cannotStartTrivia(input, cur, t)) {
1045
1070
  const v = child(input, cur, ctx)
1046
1071
  if (v === FAIL) return -1
1047
1072
  TERMV = v
@@ -1089,7 +1114,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
1089
1114
 
1090
1115
  /** A non-first term at a site proven to run under an open node capture buffer. */
1091
1116
  function nextTermBuffered(child: Piece, input: string, cur: number, ctx: ParseContext): number {
1092
- if (ctx.trivia === undefined) {
1117
+ const t = ctx.trivia
1118
+ if (t === undefined || cannotStartTrivia(input, cur, t)) {
1093
1119
  const v = child(input, cur, ctx)
1094
1120
  if (v === FAIL) return -1
1095
1121
  TERMV = v
@@ -1354,7 +1380,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
1354
1380
  return (input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot) => {
1355
1381
  const savedRouted = ctx._routed
1356
1382
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
1357
- ctx._routed = { value: key, span: { start: pos, end: selEnd } }
1383
+ ctx._routed = { value: key, span: new PlainSpan(pos, selEnd) }
1358
1384
  let v: unknown
1359
1385
  try {
1360
1386
  v = child(input, pos, ctx)
@@ -1886,7 +1912,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
1886
1912
  // table engines. Do not use the incoming ctx bit: a grammar scope can
1887
1913
  // declare tracking even when the outer run context begins untracked.
1888
1914
  const expectSpan = (ctx: ParseContext, pos: number) =>
1889
- prog.lines === 1 ? spanLines(ctx, pos, pos) : { start: pos, end: pos }
1915
+ prog.lines === 1 ? spanLines(ctx, pos, pos) : new PlainSpan(pos, pos)
1890
1916
  // TOLERANT `expect()` EMBEDS ITS ERROR IN THE TREE, not only in the flat
1891
1917
  // `_errors` side-channel (`combinators/expect.ts:150`, and codegen's
1892
1918
  // `_ctx._rec.capture` at codegen.ts:4470) — so a tree walk finds every
@@ -1933,7 +1959,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
1933
1959
  return (input, pos, ctx) => {
1934
1960
  const v = child(input, pos, ctx)
1935
1961
  if (v === FAIL) return FAIL
1936
- ctx._fields?.push({ name, value: v, span: { start: pos, end: EC.e } })
1962
+ ctx._fields?.push(new PlainField(name, v, new PlainSpan(pos, EC.e)))
1937
1963
  return v
1938
1964
  }
1939
1965
  }
@@ -1958,7 +1984,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
1958
1984
  return (input, pos, ctx) => {
1959
1985
  const v = child(input, pos, ctx)
1960
1986
  if (v === FAIL) return FAIL
1961
- return fn(v, { start: pos, end: EC.e })
1987
+ return fn(v, new PlainSpan(pos, EC.e))
1962
1988
  }
1963
1989
  }
1964
1990
 
@@ -2016,6 +2042,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2016
2042
  // known here, so the scope piece holds the scanner it installs as a `const`.
2017
2043
  const scanFor: FastTriviaScanner | null =
2018
2044
  swapLegal && ki >= 0 && !triviaLabelled[ki]! ? triviaScan[ki]! : null
2045
+ // The scope installs its slot's lead table with its trivia, so the gate
2046
+ // (`cannotStartTrivia`) never has to look the slot up.
2047
+ const scopeLead = ki < 0 ? null : LEAD_TABLES[ki]!
2019
2048
  const child = link(code[ip + 2]!)
2020
2049
  // TWO PIECES, SELECTED HERE — not one piece that tests the opcode. Capture
2021
2050
  // is fixed for the whole parse, so `code[ip] === OP_SCOPE_CAP` is an
@@ -2032,6 +2061,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2032
2061
  const savedScan = SCAN
2033
2062
  const savedCap = ctx.captureTrivia
2034
2063
  SCAN = scanFor
2064
+ const savedLeadOf = LEAD_OF, savedLead = LEAD_TABLE
2065
+ LEAD_OF = scopeTrivia
2066
+ LEAD_TABLE = scopeLead
2035
2067
  ctx.trivia = scopeTrivia
2036
2068
  ctx.triviaKindLabels = scopeLabels
2037
2069
  ctx.captureTrivia = true
@@ -2040,6 +2072,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2040
2072
  ctx.trivia = saved
2041
2073
  ctx.triviaKindLabels = savedLabels
2042
2074
  SCAN = savedScan
2075
+ LEAD_OF = savedLeadOf
2076
+ LEAD_TABLE = savedLead
2043
2077
  return v
2044
2078
  }
2045
2079
  : (input, pos, ctx) => {
@@ -2047,12 +2081,17 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2047
2081
  const savedLabels = ctx.triviaKindLabels
2048
2082
  const savedScan = SCAN
2049
2083
  SCAN = scanFor
2084
+ const savedLeadOf = LEAD_OF, savedLead = LEAD_TABLE
2085
+ LEAD_OF = scopeTrivia
2086
+ LEAD_TABLE = scopeLead
2050
2087
  ctx.trivia = scopeTrivia
2051
2088
  ctx.triviaKindLabels = scopeLabels
2052
2089
  const v = child(input, pos, ctx)
2053
2090
  ctx.trivia = saved
2054
2091
  ctx.triviaKindLabels = savedLabels
2055
2092
  SCAN = savedScan
2093
+ LEAD_OF = savedLeadOf
2094
+ LEAD_TABLE = savedLead
2056
2095
  return v
2057
2096
  }
2058
2097
  return scopeRootPolicy(scopePiece, code[ip] === OP_SCOPE_PLAIN ? 0 : code[ip + 3]!)
@@ -2068,7 +2107,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2068
2107
  ctx._fe = pos; ctx._fx = ROUTED_FX
2069
2108
  return FAIL
2070
2109
  }
2071
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: 'leaf', value: item.value, span: item.span })
2110
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, new PlainLeaf(item.value, item.span))
2072
2111
  EC.e = item.span.end
2073
2112
  return item.value
2074
2113
  }
@@ -2256,7 +2295,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2256
2295
  if (v === FAIL) return FAIL
2257
2296
  const end = EC.e
2258
2297
  const value = input.slice(pos, end)
2259
- if (wasCapturing) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
2298
+ if (wasCapturing) pushCstLeaf(ctx, new PlainLeaf(value, new PlainSpan(pos, end)))
2260
2299
  EC.e = end
2261
2300
  return value
2262
2301
  }
@@ -2291,7 +2330,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2291
2330
  if (ctx._probe !== undefined) failAt(ctx, suffixExpected, end)
2292
2331
  }
2293
2332
  const value = input.slice(pos, end)
2294
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
2333
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, new PlainLeaf(value, new PlainSpan(pos, end)))
2295
2334
  EC.e = end
2296
2335
  return value
2297
2336
  }
@@ -2334,7 +2373,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2334
2373
  }
2335
2374
  if (end < 0) return FAIL
2336
2375
  const value = input.slice(pos, end)
2337
- if (wasCapturing) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
2376
+ if (wasCapturing) pushCstLeaf(ctx, new PlainLeaf(value, new PlainSpan(pos, end)))
2338
2377
  EC.e = end
2339
2378
  return value
2340
2379
  }
@@ -2343,7 +2382,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2343
2382
  const end = run(input, pos, ctx)
2344
2383
  if (end < 0) return FAIL
2345
2384
  const value = input.slice(pos, end)
2346
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: 'leaf', value, span: { start: pos, end } })
2385
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, new PlainLeaf(value, new PlainSpan(pos, end)))
2347
2386
  EC.e = end
2348
2387
  return value
2349
2388
  }
@@ -2379,12 +2418,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2379
2418
  }
2380
2419
  if (v === FAIL) return FAIL
2381
2420
  const end = EC.e
2382
- const out = fn(v, { start: pos, end })
2383
- if (wasCapturing) pushCstLeaf(ctx, {
2384
- _tag: 'leaf',
2385
- value: op === OP_SOURCE_LEAF && hostCst ? input.slice(pos, end) : out,
2386
- span: { start: pos, end },
2387
- })
2421
+ const out = fn(v, new PlainSpan(pos, end))
2422
+ if (wasCapturing) pushCstLeaf(ctx, new PlainLeaf(op === OP_SOURCE_LEAF && hostCst ? input.slice(pos, end) : out, new PlainSpan(pos, end)))
2388
2423
  EC.e = end
2389
2424
  return out
2390
2425
  }
@@ -2566,7 +2601,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2566
2601
  const cur = runSyncTerms(input, pos, ctx, values)
2567
2602
  if (cur < 0) return FAIL
2568
2603
  EC.e = cur
2569
- return projection >= 0 ? values[projection] : fn!(values, { start: pos, end: cur })
2604
+ return projection >= 0 ? values[projection] : fn!(values, new PlainSpan(pos, cur))
2570
2605
  }
2571
2606
  }
2572
2607
  if (wantValues) {
@@ -2661,7 +2696,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2661
2696
  const cur = runAdjTerms(input, pos, ctx, values)
2662
2697
  if (cur < 0) return FAIL
2663
2698
  EC.e = cur
2664
- return projection >= 0 ? values[projection] : fn!(values, { start: pos, end: cur })
2699
+ return projection >= 0 ? values[projection] : fn!(values, new PlainSpan(pos, cur))
2665
2700
  }
2666
2701
  }
2667
2702
  if (wantValues) {
@@ -2704,7 +2739,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2704
2739
  return (input, pos, ctx) => {
2705
2740
  const v = k0(input, pos, ctx)
2706
2741
  if (v === FAIL) return FAIL
2707
- return projection === 0 ? v : fn!([v], { start: pos, end: EC.e })
2742
+ return projection === 0 ? v : fn!([v], new PlainSpan(pos, EC.e))
2708
2743
  }
2709
2744
  }
2710
2745
  if (wantValues) {
@@ -2732,7 +2767,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2732
2767
  EC.e = cur
2733
2768
  return projection === 0 ? v0
2734
2769
  : projection === 1 ? TERMV
2735
- : fn!([v0, TERMV], { start: pos, end: cur })
2770
+ : fn!([v0, TERMV], new PlainSpan(pos, cur))
2736
2771
  }
2737
2772
  }
2738
2773
  if (wantValues) {
@@ -2770,7 +2805,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2770
2805
  return projection === 0 ? v0
2771
2806
  : projection === 1 ? v1
2772
2807
  : projection === 2 ? TERMV
2773
- : fn!([v0, v1, TERMV], { start: pos, end: cur })
2808
+ : fn!([v0, v1, TERMV], new PlainSpan(pos, cur))
2774
2809
  }
2775
2810
  }
2776
2811
  if (wantValues) {
@@ -2875,7 +2910,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
2875
2910
  const cur = runDirectTerms(input, pos, ctx, values)
2876
2911
  if (cur < 0) return FAIL
2877
2912
  EC.e = cur
2878
- return projection >= 0 ? values[projection] : fn!(values, { start: pos, end: cur })
2913
+ return projection >= 0 ? values[projection] : fn!(values, new PlainSpan(pos, cur))
2879
2914
  }
2880
2915
  }
2881
2916
  if (wantValues) {
@@ -3742,10 +3777,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
3742
3777
  return FAIL
3743
3778
  }
3744
3779
  const value = regexValue ? input.slice(pos, end) : spec as string
3745
- const leaf = { _tag: 'leaf', value, span: { start: pos, end } }
3780
+ const leaf = new PlainLeaf(value, new PlainSpan(pos, end))
3746
3781
  const kids = [leaf]
3747
3782
  const rawKids = [leaf]
3748
- const span = { start: pos, end }
3783
+ const span = new PlainSpan(pos, end)
3749
3784
  EC.e = end
3750
3785
  const nd = build(kids, undefined, span, rawKids, EMPTY_TL, undefined)
3751
3786
  if (ctx._cstBuf !== undefined || ctx._cstChildren !== undefined) {
@@ -3858,8 +3893,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
3858
3893
  const sCap = ctx.captureTrivia
3859
3894
  const sBuf = ctx._cstBuf
3860
3895
  const buf: CstCaptureBuf = rawOnly
3861
- ? { rawOnly: true }
3862
- : omitsRaw ? { noRaw: true, rawLen: 0 } : {}
3896
+ ? new PlainBuf(true, undefined, 0)
3897
+ : omitsRaw ? new PlainBuf(undefined, true, 0) : new PlainBuf(undefined, undefined, 0)
3863
3898
  ctx._cstBuf = buf
3864
3899
  // `begin` sets this true and the caller immediately cleared it when the
3865
3900
  // node does not capture wide. Net: the flag IS `captureWide`, a const.
@@ -3890,7 +3925,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
3890
3925
  ctx.captureTrivia = sCap
3891
3926
  if (v === FAIL) return FAIL
3892
3927
  const end = EC.e
3893
- const span = tracked ? spanLines(ctx, pos, end) : { start: pos, end }
3928
+ const span = tracked ? spanLines(ctx, pos, end) : new PlainSpan(pos, end)
3894
3929
  const st = readsState ? ctx.state : undefined
3895
3930
 
3896
3931
  let nd: unknown
@@ -3923,7 +3958,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
3923
3958
  } else if (host !== undefined) {
3924
3959
  nd = host(type, hostKids, fieldMap, span, rawKids, tlog, st, tags)
3925
3960
  } else {
3926
- nd = { _tag: 'node', type, span, state: st ?? null, children: kids }
3961
+ nd = new PlainNode(type, span, st ?? null, kids === EMPTY_CH ? kids : [...kids])
3927
3962
  }
3928
3963
  // A ROOT NODE IS NOT A CHILD. `node()` pushes only when it was entered
3929
3964
  // from an enclosing collector — `saved.buf !== undefined || saved.ch !==
@@ -3964,7 +3999,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
3964
3999
  const at = fe === undefined || fe < 0 ? pos : fe
3965
4000
  return { ok: false, expected: (ctx._fx ?? EMPTY_FX) as string[], span: { start: at, end: at } }
3966
4001
  }
3967
- return { ok: true, value: v, span: { start: pos, end: EC.e } }
4002
+ return { ok: true, value: v, span: new PlainSpan(pos, EC.e) }
3968
4003
  },
3969
4004
  }
3970
4005
  }
@@ -4080,6 +4115,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
4080
4115
  scalarRecognizers, commitTriviaScan, scanTriviaCompact, t.lex, adjacencyHolds, t.lexPrograms,
4081
4116
  LEAD, triviaFieldCapture, skipDeferredTrivia,
4082
4117
  collapseEmpty,
4118
+ PlainSpan, PlainLeaf, PlainNode, PlainField, PlainBuf,
4083
4119
  )
4084
4120
  emitReached = new Set(pre.reached)
4085
4121
  if (emitted.ext !== undefined) HYBRID = emitted
@@ -4164,6 +4200,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
4164
4200
  scalarRecognizers, commitTriviaScan, scanTriviaCompact, t.lex, adjacencyHolds, t.lexPrograms,
4165
4201
  LEAD, triviaFieldCapture, skipDeferredTrivia,
4166
4202
  collapseEmpty,
4203
+ PlainSpan, PlainLeaf, PlainNode, PlainField, PlainBuf,
4167
4204
  )
4168
4205
  emitReached = em.reached
4169
4206
  } catch (e) {
@@ -355,10 +355,12 @@ export function compileRuleMap(
355
355
  const roots = assemblyExtraIps(artifact)
356
356
  let siteBytes, fixed
357
357
  try {
358
- siteBytes = emitAssemblySource(table, artifact, cfg, roots, true).siteBytes
358
+ // Priced without the `_errors` marks a lexical program over a scan adds
359
+ // (`scanSinkNeutral`), so they move no site in or out of the budget.
360
+ siteBytes = emitAssemblySource(table, artifact, cfg, roots, true, undefined, true).siteBytes
359
361
  // What any selective assembly costs before its first site body: the shared
360
362
  // prelude and the factory's tail. A budget that cannot cover it buys nothing.
361
- fixed = emitAssemblySource(table, artifact, cfg, roots, true, new Set()).source.length
363
+ fixed = emitAssemblySource(table, artifact, cfg, roots, true, new Set(), true).source.length
362
364
  } catch (e) {
363
365
  if (e instanceof Unemittable) return { ...none, refusal: e.construct }
364
366
  throw e
@@ -384,7 +386,7 @@ export function compileRuleMap(
384
386
  refusal: `a site within its ${Math.floor(ceiling)} B selection ceiling (the prelude alone is ${fixed} B)`,
385
387
  }
386
388
  }
387
- const bytes = emitAssemblySource(table, artifact, cfg, roots, true, sites).source.length
389
+ const bytes = emitAssemblySource(table, artifact, cfg, roots, true, sites, true).source.length
388
390
  if (bytes <= ceiling) return { sites, bytes: Math.ceil(bytes * SUPERCOMPILE_GROWTH) }
389
391
  // Scale by the overshoot, and by at least 1%: near the ceiling a site is
390
392
  // the unit, and the ratio alone can stall a few bytes over it.