parseman 0.46.0 → 0.47.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/README.md +8 -3
  2. package/dist/analysis/choice-cost-render.d.ts +12 -4
  3. package/dist/analysis/choice-cost-render.d.ts.map +1 -1
  4. package/dist/analysis/choice-cost.d.ts +2 -2
  5. package/dist/analysis/choice-cost.d.ts.map +1 -1
  6. package/dist/analysis/commitment.d.ts +131 -0
  7. package/dist/analysis/commitment.d.ts.map +1 -0
  8. package/dist/analysis/diagnostics.cjs +10339 -7787
  9. package/dist/analysis/diagnostics.cjs.map +3 -4
  10. package/dist/analysis/diagnostics.d.ts +2 -2
  11. package/dist/analysis/diagnostics.js +10339 -7787
  12. package/dist/analysis/diagnostics.js.map +3 -4
  13. package/dist/analysis/duplication.d.ts.map +1 -1
  14. package/dist/analysis/fix.d.ts +3 -3
  15. package/dist/analysis/fix.d.ts.map +1 -1
  16. package/dist/analysis/format-number.d.ts +23 -0
  17. package/dist/analysis/format-number.d.ts.map +1 -0
  18. package/dist/analysis/gating.d.ts +39 -2
  19. package/dist/analysis/gating.d.ts.map +1 -1
  20. package/dist/analysis/rebuild.d.ts.map +1 -1
  21. package/dist/analysis/terminal.d.ts +5 -2
  22. package/dist/analysis/terminal.d.ts.map +1 -1
  23. package/dist/cli/index.cjs +10924 -8506
  24. package/dist/cli/index.cjs.map +3 -4
  25. package/dist/cli/index.js +10924 -8506
  26. package/dist/cli/index.js.map +3 -4
  27. package/dist/combinators/adjacency.d.ts +100 -0
  28. package/dist/combinators/adjacency.d.ts.map +1 -0
  29. package/dist/combinators/choice.d.ts.map +1 -1
  30. package/dist/combinators/completions.d.ts.map +1 -1
  31. package/dist/combinators/dispatch.d.ts +8 -1
  32. package/dist/combinators/dispatch.d.ts.map +1 -1
  33. package/dist/combinators/expect.d.ts.map +1 -1
  34. package/dist/combinators/first-set.d.ts.map +1 -1
  35. package/dist/combinators/gate.d.ts.map +1 -1
  36. package/dist/combinators/grammar.d.ts +22 -0
  37. package/dist/combinators/grammar.d.ts.map +1 -1
  38. package/dist/combinators/literal.d.ts +8 -0
  39. package/dist/combinators/literal.d.ts.map +1 -1
  40. package/dist/combinators/map.d.ts +0 -1
  41. package/dist/combinators/map.d.ts.map +1 -1
  42. package/dist/combinators/node.d.ts +13 -3
  43. package/dist/combinators/node.d.ts.map +1 -1
  44. package/dist/combinators/parser.d.ts +1 -1
  45. package/dist/combinators/parser.d.ts.map +1 -1
  46. package/dist/combinators/ref.d.ts.map +1 -1
  47. package/dist/combinators/regex.d.ts +1 -16
  48. package/dist/combinators/regex.d.ts.map +1 -1
  49. package/dist/combinators/repeat.d.ts +30 -2
  50. package/dist/combinators/repeat.d.ts.map +1 -1
  51. package/dist/combinators/scanTo.d.ts +71 -1
  52. package/dist/combinators/scanTo.d.ts.map +1 -1
  53. package/dist/combinators/sequence.d.ts.map +1 -1
  54. package/dist/combinators/token.d.ts.map +1 -1
  55. package/dist/combinators/trivia-boundary.d.ts +26 -0
  56. package/dist/combinators/trivia-boundary.d.ts.map +1 -0
  57. package/dist/combinators/trivia-skip.d.ts +45 -1
  58. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  59. package/dist/combinators/withCtx.d.ts.map +1 -1
  60. package/dist/compiler/compile-linkable-table.d.ts +104 -0
  61. package/dist/compiler/compile-linkable-table.d.ts.map +1 -0
  62. package/dist/compiler/degradation.d.ts +0 -2
  63. package/dist/compiler/degradation.d.ts.map +1 -1
  64. package/dist/compiler/fields.d.ts +13 -0
  65. package/dist/compiler/fields.d.ts.map +1 -1
  66. package/dist/compiler/grammar-coverage-ids.d.ts +5 -5
  67. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  68. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  69. package/dist/compiler/linker.d.ts +47 -53
  70. package/dist/compiler/linker.d.ts.map +1 -1
  71. package/dist/compiler/token-alphabet.d.ts +81 -0
  72. package/dist/compiler/token-alphabet.d.ts.map +1 -0
  73. package/dist/compiler/token-dispatch.d.ts +95 -0
  74. package/dist/compiler/token-dispatch.d.ts.map +1 -0
  75. package/dist/compiler/token-scanner.d.ts +53 -0
  76. package/dist/compiler/token-scanner.d.ts.map +1 -0
  77. package/dist/compiler/value-usage.d.ts.map +1 -1
  78. package/dist/coverage.d.ts +1 -1
  79. package/dist/coverage.d.ts.map +1 -1
  80. package/dist/cst/capture-buffer.d.ts +35 -0
  81. package/dist/cst/capture-buffer.d.ts.map +1 -1
  82. package/dist/cst/reflection-attach.d.ts +8 -0
  83. package/dist/cst/reflection-attach.d.ts.map +1 -0
  84. package/dist/cst/reflection-symbols.d.ts +10 -0
  85. package/dist/cst/reflection-symbols.d.ts.map +1 -0
  86. package/dist/cst/reflection.d.ts +2 -4
  87. package/dist/cst/reflection.d.ts.map +1 -1
  88. package/dist/cst/root-trivia-scope.d.ts +26 -0
  89. package/dist/cst/root-trivia-scope.d.ts.map +1 -0
  90. package/dist/cst/trivia-charscan.d.ts +18 -0
  91. package/dist/cst/trivia-charscan.d.ts.map +1 -0
  92. package/dist/cst/trivia-kinds.d.ts +19 -0
  93. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  94. package/dist/functional/doc.d.ts.map +1 -1
  95. package/dist/functional/run-tabled.d.ts +12 -0
  96. package/dist/functional/run-tabled.d.ts.map +1 -0
  97. package/dist/functional/run.d.ts +25 -42
  98. package/dist/functional/run.d.ts.map +1 -1
  99. package/dist/grammar-metadata.d.ts +3 -0
  100. package/dist/grammar-metadata.d.ts.map +1 -0
  101. package/dist/index.cjs +9290 -7181
  102. package/dist/index.cjs.map +3 -4
  103. package/dist/index.d.ts +18 -9
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +9300 -7191
  106. package/dist/index.js.map +3 -4
  107. package/dist/language-service/index.cjs +313 -121
  108. package/dist/language-service/index.cjs.map +3 -4
  109. package/dist/language-service/index.js +313 -121
  110. package/dist/language-service/index.js.map +3 -4
  111. package/dist/oracle/index.cjs.map +0 -1
  112. package/dist/oracle/index.js.map +0 -1
  113. package/dist/parse-context.d.ts +49 -0
  114. package/dist/parse-context.d.ts.map +1 -0
  115. package/dist/plugin/evaluator.d.ts +40 -1
  116. package/dist/plugin/evaluator.d.ts.map +1 -1
  117. package/dist/plugin/index.cjs +11175 -9534
  118. package/dist/plugin/index.cjs.map +3 -4
  119. package/dist/plugin/index.d.ts.map +1 -1
  120. package/dist/plugin/index.js +11175 -9534
  121. package/dist/plugin/index.js.map +3 -4
  122. package/dist/regex/classes.d.ts +28 -0
  123. package/dist/regex/classes.d.ts.map +1 -1
  124. package/dist/regex/first-set.d.ts +14 -0
  125. package/dist/regex/first-set.d.ts.map +1 -1
  126. package/dist/run/index.cjs +136 -51
  127. package/dist/run/index.cjs.map +2 -3
  128. package/dist/run/index.js +136 -51
  129. package/dist/run/index.js.map +2 -3
  130. package/dist/spec/index.cjs +2934 -1611
  131. package/dist/spec/index.cjs.map +3 -4
  132. package/dist/spec/index.js +2934 -1611
  133. package/dist/spec/index.js.map +3 -4
  134. package/dist/spec/model.d.ts +8 -0
  135. package/dist/spec/model.d.ts.map +1 -1
  136. package/dist/table/assemble.d.ts +307 -0
  137. package/dist/table/assemble.d.ts.map +1 -0
  138. package/dist/table/cell.d.ts +42 -0
  139. package/dist/table/cell.d.ts.map +1 -0
  140. package/dist/table/child-slots.d.ts +30 -0
  141. package/dist/table/child-slots.d.ts.map +1 -0
  142. package/dist/table/compile-rule-map.d.ts +136 -0
  143. package/dist/table/compile-rule-map.d.ts.map +1 -0
  144. package/dist/table/compile.d.ts +56 -0
  145. package/dist/table/compile.d.ts.map +1 -0
  146. package/dist/table/duplication-hook.d.ts +32 -0
  147. package/dist/table/duplication-hook.d.ts.map +1 -0
  148. package/dist/table/emit-assembly.d.ts +139 -0
  149. package/dist/table/emit-assembly.d.ts.map +1 -0
  150. package/dist/table/emit.d.ts +113 -0
  151. package/dist/table/emit.d.ts.map +1 -0
  152. package/dist/table/encode-baseline.d.ts +16 -0
  153. package/dist/table/encode-baseline.d.ts.map +1 -0
  154. package/dist/table/encode.d.ts +71 -0
  155. package/dist/table/encode.d.ts.map +1 -0
  156. package/dist/table/exec-baseline.d.ts +9 -0
  157. package/dist/table/exec-baseline.d.ts.map +1 -0
  158. package/dist/table/exec.d.ts +72 -0
  159. package/dist/table/exec.d.ts.map +1 -0
  160. package/dist/table/fold.d.ts +25 -0
  161. package/dist/table/fold.d.ts.map +1 -0
  162. package/dist/table/index.cjs +11463 -0
  163. package/dist/table/index.cjs.map +6 -0
  164. package/dist/table/index.d.ts +100 -0
  165. package/dist/table/index.d.ts.map +1 -0
  166. package/dist/table/index.js +11440 -0
  167. package/dist/table/index.js.map +6 -0
  168. package/dist/table/inspect.d.ts +17 -0
  169. package/dist/table/inspect.d.ts.map +1 -0
  170. package/dist/table/ops.d.ts +453 -0
  171. package/dist/table/ops.d.ts.map +1 -0
  172. package/dist/table/program.d.ts +496 -0
  173. package/dist/table/program.d.ts.map +1 -0
  174. package/dist/table/run-support.d.ts +38 -0
  175. package/dist/table/run-support.d.ts.map +1 -0
  176. package/dist/{compiler/scannable-run.d.ts → table/scan-shapes.d.ts} +14 -13
  177. package/dist/table/scan-shapes.d.ts.map +1 -0
  178. package/dist/table/site-labels.d.ts +58 -0
  179. package/dist/table/site-labels.d.ts.map +1 -0
  180. package/dist/table/stamp.d.ts +70 -0
  181. package/dist/table/stamp.d.ts.map +1 -0
  182. package/dist/types.d.ts +80 -24
  183. package/dist/types.d.ts.map +1 -1
  184. package/dist/version.d.ts +1 -1
  185. package/package.json +26 -2
  186. package/src/analysis/choice-cost-gate.ts +359 -0
  187. package/src/analysis/choice-cost-render.ts +293 -0
  188. package/src/analysis/choice-cost.ts +1008 -0
  189. package/src/analysis/commitment.ts +557 -0
  190. package/src/analysis/corpus.ts +130 -0
  191. package/src/analysis/diagnose-render.ts +625 -0
  192. package/src/analysis/diagnose.ts +317 -0
  193. package/src/analysis/diagnostics.ts +33 -0
  194. package/src/analysis/duplication.ts +1955 -0
  195. package/src/analysis/fix-render.ts +217 -0
  196. package/src/analysis/fix.ts +669 -0
  197. package/src/analysis/format-number.ts +31 -0
  198. package/src/analysis/gating.ts +792 -0
  199. package/src/analysis/grammar.ts +72 -0
  200. package/src/analysis/rebuild.ts +287 -0
  201. package/src/analysis/terminal.ts +336 -0
  202. package/src/cli/index.ts +413 -0
  203. package/src/combinators/adjacency.ts +171 -0
  204. package/src/combinators/attempt.ts +41 -0
  205. package/src/combinators/case-fold.ts +90 -0
  206. package/src/combinators/choice.ts +466 -0
  207. package/src/combinators/completions.ts +64 -0
  208. package/src/combinators/dispatch.ts +358 -0
  209. package/src/combinators/expect.ts +159 -0
  210. package/src/combinators/first-set.ts +348 -0
  211. package/src/combinators/gate.ts +50 -0
  212. package/src/combinators/grammar.ts +272 -0
  213. package/src/combinators/keywords.ts +145 -0
  214. package/src/combinators/literal.ts +102 -0
  215. package/src/combinators/map.ts +106 -0
  216. package/src/combinators/node.ts +345 -0
  217. package/src/combinators/not.ts +55 -0
  218. package/src/combinators/parser.ts +266 -0
  219. package/src/combinators/peek.ts +63 -0
  220. package/src/combinators/probe.ts +25 -0
  221. package/src/combinators/ref.ts +109 -0
  222. package/src/combinators/regex.ts +170 -0
  223. package/src/combinators/repeat.ts +561 -0
  224. package/src/combinators/scanTo.ts +393 -0
  225. package/src/combinators/sequence.ts +237 -0
  226. package/src/combinators/token.ts +120 -0
  227. package/src/combinators/trivia-boundary.ts +48 -0
  228. package/src/combinators/trivia-skip.ts +852 -0
  229. package/src/combinators/withCtx.ts +45 -0
  230. package/src/compiler/build-arity.ts +330 -0
  231. package/src/compiler/compile-linkable-table.ts +209 -0
  232. package/src/compiler/degradation.ts +266 -0
  233. package/src/compiler/fields.ts +191 -0
  234. package/src/compiler/grammar-coverage-ids.ts +129 -0
  235. package/src/compiler/ir-serialize.ts +615 -0
  236. package/src/compiler/linker.ts +954 -0
  237. package/src/compiler/token-alphabet.ts +209 -0
  238. package/src/compiler/token-dispatch.ts +353 -0
  239. package/src/compiler/token-scanner.ts +318 -0
  240. package/src/compiler/value-usage.ts +147 -0
  241. package/src/coverage.ts +383 -0
  242. package/src/cst/capture-buffer.ts +297 -0
  243. package/src/cst/host-mode.ts +69 -0
  244. package/src/cst/reflection-attach.ts +11 -0
  245. package/src/cst/reflection-symbols.ts +9 -0
  246. package/src/cst/reflection.ts +149 -0
  247. package/src/cst/relative-spans.ts +207 -0
  248. package/src/cst/root-trivia-scope.ts +30 -0
  249. package/src/cst/trivia-charscan.ts +312 -0
  250. package/src/cst/trivia-entries.ts +285 -0
  251. package/src/cst/trivia-index.ts +131 -0
  252. package/src/cst/trivia-kinds.ts +358 -0
  253. package/src/cst/types.ts +76 -0
  254. package/src/cst/walk.ts +101 -0
  255. package/src/functional/doc.ts +959 -0
  256. package/src/functional/run-tabled.ts +153 -0
  257. package/src/functional/run.ts +558 -0
  258. package/src/grammar-metadata.ts +2 -0
  259. package/src/index.ts +189 -0
  260. package/src/language-service/index.ts +221 -0
  261. package/src/line-index.ts +131 -0
  262. package/src/oracle/digest.ts +620 -0
  263. package/src/oracle/index.ts +55 -0
  264. package/src/parse-context.ts +91 -0
  265. package/src/plugin/direct-builder-static.ts +102 -0
  266. package/src/plugin/evaluator.ts +1505 -0
  267. package/src/plugin/free-identifiers.ts +338 -0
  268. package/src/plugin/index.ts +2281 -0
  269. package/src/plugin/reducer-resolver.ts +606 -0
  270. package/src/recovery/scan.ts +155 -0
  271. package/src/regex/classes.ts +183 -0
  272. package/src/regex/first-set.ts +316 -0
  273. package/src/run/index.ts +31 -0
  274. package/src/spec/ebnf.ts +128 -0
  275. package/src/spec/index.ts +54 -0
  276. package/src/spec/model.ts +636 -0
  277. package/src/spec/railroad-lib.ts +559 -0
  278. package/src/spec/railroad.ts +256 -0
  279. package/src/table/assemble.ts +3006 -0
  280. package/src/table/cell.ts +42 -0
  281. package/src/table/child-slots.ts +113 -0
  282. package/src/table/compile-rule-map.ts +336 -0
  283. package/src/table/compile.ts +244 -0
  284. package/src/table/duplication-hook.ts +89 -0
  285. package/src/table/emit-assembly.ts +1777 -0
  286. package/src/table/emit.ts +429 -0
  287. package/src/table/encode-baseline.ts +340 -0
  288. package/src/table/encode.ts +1569 -0
  289. package/src/table/exec-baseline.ts +537 -0
  290. package/src/table/exec.ts +1680 -0
  291. package/src/table/fold.ts +38 -0
  292. package/src/table/index.ts +108 -0
  293. package/src/table/inspect.ts +52 -0
  294. package/src/table/ops.ts +467 -0
  295. package/src/table/program.ts +876 -0
  296. package/src/table/run-support.ts +66 -0
  297. package/src/table/scan-shapes.ts +1578 -0
  298. package/src/table/site-labels.ts +228 -0
  299. package/src/table/stamp.ts +229 -0
  300. package/src/types.ts +563 -0
  301. package/src/version.ts +16 -0
  302. package/dist/compiler/codegen.d.ts +0 -334
  303. package/dist/compiler/codegen.d.ts.map +0 -1
  304. package/dist/compiler/inline-build.d.ts +0 -11
  305. package/dist/compiler/inline-build.d.ts.map +0 -1
  306. package/dist/compiler/inline-callback.d.ts +0 -21
  307. package/dist/compiler/inline-callback.d.ts.map +0 -1
  308. package/dist/compiler/line-index.d.ts +0 -2
  309. package/dist/compiler/line-index.d.ts.map +0 -1
  310. package/dist/compiler/module-hoist.d.ts +0 -106
  311. package/dist/compiler/module-hoist.d.ts.map +0 -1
  312. package/dist/compiler/scannable-run.d.ts.map +0 -1
  313. package/dist/compiler/scannable-terminal.d.ts +0 -23
  314. package/dist/compiler/scannable-terminal.d.ts.map +0 -1
  315. package/dist/compiler/trivia-fast-path.d.ts +0 -38
  316. package/dist/compiler/trivia-fast-path.d.ts.map +0 -1
@@ -0,0 +1,537 @@
1
+ import type { ParseContext, ParseResult } from '../types.ts'
2
+ import { advanceTrivia, needsDeferredTriviaCommit, rollbackTrivia, saveTriviaMark, scanTrivia } from '../combinators/trivia-skip.ts'
3
+ import {
4
+ beginCstNodeCapture, cstCaptureActive, endCstNodeCapture, pushCstChild,
5
+ pushCstLeaf, rollbackCstCapture, saveCstMark, type CstRollbackMark,
6
+ } from '../cst/capture-buffer.ts'
7
+ import {
8
+ OP_CHOICE, OP_EMPTY, OP_GATE, OP_LEAF, OP_LIT, OP_NODE, OP_NOT, OP_OPT,
9
+ OP_PEEK, OP_REP, OP_REPV, OP_RULE, OP_RX, OP_SEQ, OP_SEQV, OP_XFORM,
10
+ OP_LIT_TRACK, OP_RX_TRACK, OP_NODE_TRACK, OP_SCOPE, OP_EXPECT,
11
+ } from './ops.ts'
12
+ import {
13
+ expandCompact, resolveTable,
14
+ type CompactProgram, type ResolvedClass, type ResolvedDispatch, type TableProgram, type TableRule,
15
+ } from './program.ts'
16
+
17
+ /**
18
+ * THE SHARED DRIVER.
19
+ *
20
+ * One recognizer for every grammar, every rule and every variant — the thing
21
+ * codegen emits once PER RULE, and which therefore sets the per-rule byte floor.
22
+ * It uses the same zero-allocation protocol the emitted code uses (a sentinel
23
+ * for failure plus a shared end-position slot), so what it adds over open-coded
24
+ * recognition is the opcode read and the switch, not an allocation.
25
+ *
26
+ * The driver is instantiated ONCE PER RESOLVED TABLE, closing over that table's
27
+ * arrays. Instruction operands are then reads of context slots rather than
28
+ * property loads through an object, and — the point of G5 — an option can never
29
+ * be consulted here: two settings pairs are two tables, not two code paths.
30
+ */
31
+
32
+ /** Failure sentinel — identity-compared, never inspected. */
33
+ const FAIL: unique symbol = Symbol('pm.fail')
34
+
35
+ const EMPTY_TL: readonly number[] = Object.freeze([])
36
+ const EMPTY_FX: string[] = []
37
+
38
+ type Leaf = { _tag: 'leaf'; value: string; span: { start: number; end: number } }
39
+
40
+ function rawEntry(v: unknown, input: string, s: number, e: number): unknown {
41
+ if (typeof v === 'object' && v !== null) {
42
+ const t = (v as { _tag?: string })._tag
43
+ if (t === 'node' || t === 'leaf' || t === 'parseError') return v
44
+ }
45
+ return { _tag: 'leaf', value: typeof v === 'string' ? v : (typeof v === 'object' && v !== null ? input.slice(s, e) : ''), span: { start: s, end: e } }
46
+ }
47
+
48
+ /** Same semantics as `trackLinesInto` in `src/table/assemble.ts`. (Named for the
49
+ * source lowering before it, deleted in `37c57b5`.) */
50
+ function trackLines(ctx: ParseContext, input: string, end: number): void {
51
+ const from = ctx._lineScannedTo ?? 0
52
+ if (end <= from) return
53
+ const starts = ctx._lineStarts
54
+ if (starts === undefined) return
55
+ for (let i = from; i < end; i++) if (input.charCodeAt(i) === 10) starts.push(i + 1)
56
+ ctx._lineScannedTo = end
57
+ }
58
+
59
+ /** Same semantics as `lineCol` in `src/table/run-support.ts`, which is where the
60
+ * live copy moved. (Named for the source lowering before it, deleted in `37c57b5`.) */
61
+ function lineCol(ctx: ParseContext, offset: number): [number, number] {
62
+ const starts = ctx._lineStarts ?? [0]
63
+ let lo = 0, hi = starts.length - 1
64
+ while (lo < hi) {
65
+ const mid = (lo + hi + 1) >> 1
66
+ if (starts[mid]! <= offset) lo = mid
67
+ else hi = mid - 1
68
+ }
69
+ return [lo + 1, offset - starts[lo]! + 1]
70
+ }
71
+
72
+ function spanLines(ctx: ParseContext, start: number, end: number): { start: number; end: number; startLine: number; startColumn: number; endLine: number; endColumn: number } {
73
+ const s = lineCol(ctx, start), e = lineCol(ctx, end)
74
+ return { start, end, startLine: s[0], startColumn: s[1], endLine: e[0], endColumn: e[1] }
75
+ }
76
+
77
+ function classHas(cls: ResolvedClass, code: number): boolean {
78
+ if (code < 0) return false
79
+ if (code < 128) return cls.ascii[code] === 1
80
+ const hi = cls.hi
81
+ for (let i = 0; i < hi.length; i += 2) if (code >= hi[i]! && code <= hi[i + 1]!) return true
82
+ return false
83
+ }
84
+
85
+ type Driver = {
86
+ exec: (ip: number, input: string, pos: number, ctx: ParseContext) => unknown
87
+ end: () => number
88
+ }
89
+
90
+ function makeDriver(
91
+ code: Int32Array,
92
+ k: readonly unknown[],
93
+ fns: readonly unknown[],
94
+ cc: readonly ResolvedClass[],
95
+ fx: readonly (readonly string[])[],
96
+ disp: readonly ResolvedDispatch[],
97
+ ): Driver {
98
+ /** Shared end-position out-parameter (`_pfEnd` in emitted code). */
99
+ let END = 0
100
+
101
+ /**
102
+ * Capture goes through the runtime's own buffer (`src/cst/capture-buffer.ts`),
103
+ * not a hand-rolled copy: it is already the shared mechanism the interpreter
104
+ * uses, and a second implementation of it is precisely the duplication G5 is
105
+ * about removing.
106
+ */
107
+ function pushLeaf(ctx: ParseContext, value: string, s: number, e: number): void {
108
+ const lf: Leaf = { _tag: 'leaf', value, span: { start: s, end: e } }
109
+ pushCstLeaf(ctx, lf)
110
+ }
111
+
112
+ /**
113
+ * Is there anything a failed branch could need to UNRECORD?
114
+ *
115
+ * `saveCstMark` allocates a 5-field object and `saveTriviaMark` allocates that
116
+ * plus a 7-field one; `rollbackTrivia` allocates a third to call through. In
117
+ * the driver those sat in the repetition and choice loops, so a parse
118
+ * allocated three-to-four objects PER ITEM that codegen never allocates (it
119
+ * emits scalar locals). That is a per-item cost, which is what the widening
120
+ * gap with input size was: +82% small, +275% large.
121
+ *
122
+ * When every sink either rollback touches is absent, nothing was recorded,
123
+ * so nothing needs unrecording and the mark need not exist. A grammar with no
124
+ * `node()` and no trivia log — json, csv — never allocates one.
125
+ */
126
+ function rollbackNeeded(ctx: ParseContext): boolean {
127
+ return ctx._cstBuf !== undefined
128
+ || ctx._cstLeaves !== undefined
129
+ || ctx._cstRawChildren !== undefined
130
+ || ctx._cstTriviaLog !== undefined
131
+ || ctx._fields !== undefined
132
+ || ctx._errors !== undefined
133
+ || ctx._triviaLog !== undefined
134
+ || ctx._rootTriviaLog !== undefined
135
+ }
136
+
137
+ /**
138
+ * The lead character at `pos`, as the first-set tables index it.
139
+ *
140
+ * `charCodeAt` is the hot read; only a high surrogate needs the code-point
141
+ * decode, so that branch is paid on astral input and nowhere else.
142
+ */
143
+ function skipTrivia(input: string, cur: number, ctx: ParseContext): number {
144
+ if (needsDeferredTriviaCommit(ctx)) {
145
+ const scan = scanTrivia(input, cur, ctx)
146
+ scan.commit()
147
+ return scan.end
148
+ }
149
+ return advanceTrivia(input, cur, ctx)
150
+ }
151
+
152
+ function lead(input: string, pos: number): number {
153
+ if (pos >= input.length) return -1
154
+ const c = input.charCodeAt(pos)
155
+ if (c < 0xd800 || c > 0xdbff) return c
156
+ return input.codePointAt(pos) ?? c
157
+ }
158
+
159
+ function exec(ip: number, input: string, pos: number, ctx: ParseContext): unknown {
160
+ switch (code[ip]) {
161
+ case OP_LIT: {
162
+ const s = k[code[ip + 1]!] as string
163
+ if (input.startsWith(s, pos)) {
164
+ const e = pos + s.length
165
+ if (cstCaptureActive(ctx)) pushLeaf(ctx, s, pos, e)
166
+ END = e
167
+ return s
168
+ }
169
+ ctx._fe = pos; ctx._fx = fx[code[ip + 2]!] as string[]
170
+ return FAIL
171
+ }
172
+ case OP_RX: {
173
+ const re = k[code[ip + 1]!] as RegExp
174
+ re.lastIndex = pos
175
+ const m = re.exec(input)
176
+ if (m !== null) {
177
+ const v = m[0]
178
+ const e = pos + v.length
179
+ if (cstCaptureActive(ctx)) pushLeaf(ctx, v, pos, e)
180
+ END = e
181
+ return v
182
+ }
183
+ ctx._fe = pos; ctx._fx = fx[code[ip + 2]!] as string[]
184
+ return FAIL
185
+ }
186
+ case OP_LIT_TRACK: {
187
+ const s = k[code[ip + 1]!] as string
188
+ if (input.startsWith(s, pos)) {
189
+ const e = pos + s.length
190
+ if (cstCaptureActive(ctx)) pushLeaf(ctx, s, pos, e)
191
+ trackLines(ctx, input, e)
192
+ END = e
193
+ return s
194
+ }
195
+ ctx._fe = pos; ctx._fx = fx[code[ip + 2]!] as string[]
196
+ return FAIL
197
+ }
198
+ case OP_RX_TRACK: {
199
+ const re = k[code[ip + 1]!] as RegExp
200
+ re.lastIndex = pos
201
+ const m = re.exec(input)
202
+ if (m !== null) {
203
+ const v = m[0]
204
+ const e = pos + v.length
205
+ if (cstCaptureActive(ctx)) pushLeaf(ctx, v, pos, e)
206
+ trackLines(ctx, input, e)
207
+ END = e
208
+ return v
209
+ }
210
+ ctx._fe = pos; ctx._fx = fx[code[ip + 2]!] as string[]
211
+ return FAIL
212
+ }
213
+ case OP_EMPTY:
214
+ END = pos
215
+ return ''
216
+
217
+ case OP_GATE: {
218
+ if (!classHas(cc[code[ip + 1]!]!, lead(input, pos))) {
219
+ ctx._fe = pos; ctx._fx = fx[code[ip + 3]!] as string[]
220
+ return FAIL
221
+ }
222
+ return exec(code[ip + 2]!, input, pos, ctx)
223
+ }
224
+ case OP_RULE:
225
+ return exec(code[ip + 1]!, input, pos, ctx)
226
+
227
+ case OP_EXPECT: {
228
+ const v = exec(code[ip + 1]!, input, pos, ctx)
229
+ if (v !== FAIL) return v
230
+ // Mirrors src/combinators/expect.ts:135-150 — succeed at zero width with
231
+ // a ParseError value, and record it in the flat sink when present.
232
+ const span = { start: pos, end: pos }
233
+ const err = { _tag: 'parseError' as const, span, expected: fx[code[ip + 2]!] as string[] }
234
+ ctx._errors?.push(err)
235
+ END = pos
236
+ return err
237
+ }
238
+
239
+ case OP_SCOPE: {
240
+ const ki = code[ip + 1]!
241
+ const saved = ctx.trivia
242
+ ctx.trivia = ki < 0 ? undefined : (k[ki] as ParseContext['trivia'])
243
+ const v = exec(code[ip + 2]!, input, pos, ctx)
244
+ ctx.trivia = saved
245
+ return v
246
+ }
247
+
248
+ case OP_SEQ:
249
+ case OP_SEQV: {
250
+ const n = code[ip + 1]!
251
+ const values: unknown[] | undefined = code[ip] === OP_SEQ ? [] : undefined
252
+ let cur = pos
253
+ for (let i = 0; i < n; i++) {
254
+ const child = code[ip + 2 + i]!
255
+ if (i > 0 && ctx.trivia !== undefined) {
256
+ const mark = rollbackNeeded(ctx) ? saveTriviaMark(ctx) : null
257
+ let scanEnd: number
258
+ if (needsDeferredTriviaCommit(ctx)) {
259
+ const scan = scanTrivia(input, cur, ctx)
260
+ scan.commit()
261
+ scanEnd = scan.end
262
+ } else {
263
+ scanEnd = advanceTrivia(input, cur, ctx)
264
+ }
265
+ const v = exec(child, input, scanEnd, ctx)
266
+ if (v === FAIL) return FAIL
267
+ if (END > scanEnd) cur = END
268
+ else if (mark !== null) rollbackTrivia(ctx, mark)
269
+ if (values !== undefined) values.push(v)
270
+ continue
271
+ }
272
+ // TERMINAL FAST PATH. Terminals are the majority of executed
273
+ // instructions, and reaching one through `exec` costs a JS call frame
274
+ // plus a switch dispatch that the emitted code does not pay. Running
275
+ // LIT/RX in place removes both. The duplication is in the DRIVER,
276
+ // which ships once for every grammar — the cost this design trades on.
277
+ const cop = code[child]
278
+ if (cop === OP_LIT) {
279
+ const lit = k[code[child + 1]!] as string
280
+ if (!input.startsWith(lit, cur)) {
281
+ ctx._fe = cur; ctx._fx = fx[code[child + 2]!] as string[]
282
+ return FAIL
283
+ }
284
+ const e = cur + lit.length
285
+ if (cstCaptureActive(ctx)) pushLeaf(ctx, lit, cur, e)
286
+ if (values !== undefined) values.push(lit)
287
+ cur = e
288
+ continue
289
+ }
290
+ if (cop === OP_RX) {
291
+ const re = k[code[child + 1]!] as RegExp
292
+ re.lastIndex = cur
293
+ const m = re.exec(input)
294
+ if (m === null) {
295
+ ctx._fe = cur; ctx._fx = fx[code[child + 2]!] as string[]
296
+ return FAIL
297
+ }
298
+ const mv = m[0]
299
+ const e = cur + mv.length
300
+ if (cstCaptureActive(ctx)) pushLeaf(ctx, mv, cur, e)
301
+ if (values !== undefined) values.push(mv)
302
+ cur = e
303
+ continue
304
+ }
305
+ const v = exec(child, input, cur, ctx)
306
+ if (v === FAIL) return FAIL
307
+ if (values !== undefined) values.push(v)
308
+ cur = END
309
+ }
310
+ END = cur
311
+ return values
312
+ }
313
+
314
+ case OP_CHOICE: {
315
+ const d = code[ip + 1]!
316
+ const base = ip + 3
317
+ if (d >= 0) {
318
+ const table = disp[d]!
319
+ const c = lead(input, pos)
320
+ let arm = -1
321
+ if (c >= 0 && c < 128) {
322
+ const a = table.ascii[c]!
323
+ if (a !== 0) arm = a - 1
324
+ } else if (c >= 128) {
325
+ const hi = table.hi
326
+ for (let i = 0; i < hi.length; i += 3) {
327
+ if (c >= hi[i]! && c <= hi[i + 1]!) { arm = hi[i + 2]!; break }
328
+ }
329
+ }
330
+ if (arm >= 0) {
331
+ const v = exec(code[base + arm]!, input, pos, ctx)
332
+ if (v !== FAIL) return v
333
+ }
334
+ const open = table.open
335
+ if (open.length === 0) return FAIL
336
+ const mark: CstRollbackMark | null = rollbackNeeded(ctx) ? saveCstMark(ctx) : null
337
+ if (mark !== null) rollbackCstCapture(ctx, mark)
338
+ for (let i = 0; i < open.length; i++) {
339
+ const v = exec(code[base + open[i]!]!, input, pos, ctx)
340
+ if (v !== FAIL) return v
341
+ if (mark !== null) rollbackCstCapture(ctx, mark)
342
+ }
343
+ return FAIL
344
+ }
345
+ const n = code[ip + 2]!
346
+ const mark: CstRollbackMark | null = rollbackNeeded(ctx) ? saveCstMark(ctx) : null
347
+ for (let i = 0; i < n; i++) {
348
+ const v = exec(code[base + i]!, input, pos, ctx)
349
+ if (v !== FAIL) return v
350
+ if (mark !== null) rollbackCstCapture(ctx, mark)
351
+ }
352
+ return FAIL
353
+ }
354
+
355
+ case OP_OPT: {
356
+ const mark: CstRollbackMark | null = rollbackNeeded(ctx) ? saveCstMark(ctx) : null
357
+ const v = exec(code[ip + 1]!, input, pos, ctx)
358
+ if (v === FAIL) {
359
+ if (mark !== null) rollbackCstCapture(ctx, mark)
360
+ END = pos
361
+ // NULL, not undefined. `optional()` yields `null` on no-match
362
+ // (src/combinators/repeat.ts:269,277) and grammars TEST for it:
363
+ // examples/lang's `call` reducer is `if (args === null) return callee`,
364
+ // so `undefined` there turned a bare identifier into a call node with
365
+ // `args: undefined`. The parse succeeded and only the tree moved.
366
+ return null
367
+ }
368
+ return v
369
+ }
370
+
371
+ case OP_REP:
372
+ case OP_REPV: {
373
+ const child = code[ip + 1]!
374
+ const min = code[ip + 2]!
375
+ const max = code[ip + 3]!
376
+ const sep = code[ip + 4]!
377
+ const out: unknown[] | undefined = code[ip] === OP_REP ? [] : undefined
378
+ const hasTrivia = ctx.trivia !== undefined
379
+ const needMark = rollbackNeeded(ctx)
380
+ let cur = pos
381
+ let count = 0
382
+ for (;;) {
383
+ if (max >= 0 && count >= max) break
384
+ // One mark pair for the whole loop when a rollback is even possible,
385
+ // refreshed per iteration rather than reallocated.
386
+ const cmark = needMark ? saveCstMark(ctx) : null
387
+ const tmark = needMark ? saveTriviaMark(ctx) : null
388
+ let itemStart = cur
389
+ if (sep >= 0 && count > 0) {
390
+ // separator, with trivia on BOTH sides — mirrors repeat.ts's sepBy loop
391
+ let sp = cur
392
+ if (hasTrivia) sp = skipTrivia(input, sp, ctx)
393
+ const sv = exec(sep, input, sp, ctx)
394
+ if (sv === FAIL) {
395
+ if (tmark !== null) rollbackTrivia(ctx, tmark)
396
+ if (cmark !== null) rollbackCstCapture(ctx, cmark)
397
+ break
398
+ }
399
+ itemStart = hasTrivia ? skipTrivia(input, END, ctx) : END
400
+ } else if (hasTrivia) {
401
+ // Trivia precedes EVERY item, the first included: `repItem` in
402
+ // repeat.ts does this, and skipping it only for later items dropped
403
+ // exactly one trivia-log entry per repetition — invisible in the
404
+ // parse, visible in a node's `triviaLog`.
405
+ itemStart = skipTrivia(input, itemStart, ctx)
406
+ }
407
+ // Nothing but trivia left: don't speculatively parse an item at EOF.
408
+ if (itemStart >= input.length) {
409
+ if (tmark !== null) rollbackTrivia(ctx, tmark)
410
+ if (cmark !== null) rollbackCstCapture(ctx, cmark)
411
+ break
412
+ }
413
+ const v = exec(child, input, itemStart, ctx)
414
+ if (v === FAIL) {
415
+ if (tmark !== null) rollbackTrivia(ctx, tmark)
416
+ if (cmark !== null) rollbackCstCapture(ctx, cmark)
417
+ break
418
+ }
419
+ if (END === itemStart) {
420
+ // Zero-width item: it cannot make progress, so stop without taking it.
421
+ if (tmark !== null) rollbackTrivia(ctx, tmark)
422
+ if (cmark !== null) rollbackCstCapture(ctx, cmark)
423
+ break
424
+ }
425
+ if (out !== undefined) out.push(v)
426
+ cur = END
427
+ count++
428
+ }
429
+ if (count < min) return FAIL
430
+ END = cur
431
+ return out
432
+ }
433
+
434
+ case OP_XFORM: {
435
+ const v = exec(code[ip + 2]!, input, pos, ctx)
436
+ if (v === FAIL) return FAIL
437
+ const fn = fns[code[ip + 1]!] as (value: unknown, span: { start: number; end: number }) => unknown
438
+ return fn(v, { start: pos, end: END })
439
+ }
440
+
441
+ case OP_LEAF: {
442
+ const v = exec(code[ip + 2]!, input, pos, ctx)
443
+ if (v === FAIL) return FAIL
444
+ const end = END
445
+ const fn = fns[code[ip + 1]!] as (value: unknown, span: { start: number; end: number }) => unknown
446
+ const out = fn(v, { start: pos, end })
447
+ END = end
448
+ return out
449
+ }
450
+
451
+ case OP_NODE:
452
+ case OP_NODE_TRACK: {
453
+ // `flags` bit 2 = the builder reads `triviaLog`. Decided by
454
+ // `src/table/encode.ts` from the reducer's arity — the SAME analysis
455
+ // codegen runs — and baked into the row, so the driver reads a bit
456
+ // rather than re-deriving anything.
457
+ const flags = code[ip + 3]!
458
+ const saved = beginCstNodeCapture(ctx)
459
+ if ((flags & 4) === 0) ctx.captureTrivia = false
460
+ const v = exec(code[ip + 2]!, input, pos, ctx)
461
+ const cap = endCstNodeCapture(ctx, saved)
462
+ if (v === FAIL) return FAIL
463
+ const end = END
464
+ const build = fns[code[ip + 1]!] as (
465
+ children: readonly unknown[], fields: undefined, span: { start: number; end: number },
466
+ rawChildren: readonly unknown[], triviaLog: readonly number[], state: unknown,
467
+ ) => unknown
468
+ const st = (flags & 8) !== 0 && ctx.state !== undefined
469
+ ? Object.assign({}, ctx.state as Record<string, unknown>)
470
+ : undefined
471
+ const nd = build(
472
+ cap.children, undefined,
473
+ code[ip] === OP_NODE_TRACK ? spanLines(ctx, pos, end) : { start: pos, end },
474
+ cap.rawChildren, (flags & 4) !== 0 ? cap.triviaLog : EMPTY_TL, st,
475
+ )
476
+ pushCstChild(ctx, nd, rawEntry(nd, input, pos, end))
477
+ END = end
478
+ return nd
479
+ }
480
+
481
+ case OP_NOT: {
482
+ const mark: CstRollbackMark | null = rollbackNeeded(ctx) ? saveCstMark(ctx) : null
483
+ const v = exec(code[ip + 1]!, input, pos, ctx)
484
+ if (mark !== null) rollbackCstCapture(ctx, mark)
485
+ if (v === FAIL) { END = pos; return null }
486
+ ctx._fe = pos
487
+ return FAIL
488
+ }
489
+
490
+ case OP_PEEK: {
491
+ const mark: CstRollbackMark | null = rollbackNeeded(ctx) ? saveCstMark(ctx) : null
492
+ const v = exec(code[ip + 1]!, input, pos, ctx)
493
+ if (mark !== null) rollbackCstCapture(ctx, mark)
494
+ if (v === FAIL) return FAIL
495
+ END = pos
496
+ return null
497
+ }
498
+
499
+ default:
500
+ throw new Error(`table driver: unknown opcode ${String(code[ip])} at ${ip}`)
501
+ }
502
+ }
503
+
504
+ return { exec, end: () => END }
505
+ }
506
+
507
+ /**
508
+ * Turn a program into the rule map a compiled artifact exports.
509
+ *
510
+ * The entries have the SAME signature as codegen rule functions, so `run()`,
511
+ * the linker's public wrappers and every consumer are unchanged.
512
+ */
513
+ export function execRulesBaseline(source: TableProgram | CompactProgram): Record<string, TableRule> {
514
+ const prog = expandCompact(source)
515
+ const t = resolveTable(prog)
516
+ const d = makeDriver(t.code, t.k, t.fns, t.cc, t.fx, t.disp)
517
+ const out: Record<string, TableRule> = {}
518
+ // Chosen ONCE, from table data, at rule-map construction. Not a per-parse
519
+ // branch on an option: a plain table never has this wrapper at all.
520
+ const lines = prog.lines === 1
521
+ for (const name of Object.keys(prog.rules)) {
522
+ const entry = prog.rules[name]!
523
+ out[name] = (input: string, pos: number, ctx: ParseContext): ParseResult<unknown> => {
524
+ ctx._fe = -1
525
+ ctx._fx = EMPTY_FX
526
+ if (lines && ctx._lineStarts === undefined) { ctx._lineStarts = [0]; ctx._lineScannedTo = 0 }
527
+ const v = d.exec(entry, input, pos, ctx)
528
+ if (v === FAIL) {
529
+ const fe = ctx._fe
530
+ const at = fe === undefined || fe < 0 ? pos : fe
531
+ return { ok: false, expected: (ctx._fx ?? EMPTY_FX) as string[], span: { start: at, end: at } }
532
+ }
533
+ return { ok: true, value: v, span: { start: pos, end: d.end() } }
534
+ }
535
+ }
536
+ return out
537
+ }