parseman 0.1.7 → 0.3.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 (54) hide show
  1. package/README.md +192 -155
  2. package/dist/combinators/choice.d.ts.map +1 -1
  3. package/dist/combinators/completions.d.ts +17 -0
  4. package/dist/combinators/completions.d.ts.map +1 -0
  5. package/dist/combinators/grammar.d.ts +20 -4
  6. package/dist/combinators/grammar.d.ts.map +1 -1
  7. package/dist/combinators/guard.d.ts +2 -2
  8. package/dist/combinators/guard.d.ts.map +1 -1
  9. package/dist/combinators/keywords.d.ts +22 -0
  10. package/dist/combinators/keywords.d.ts.map +1 -0
  11. package/dist/combinators/literal.d.ts.map +1 -1
  12. package/dist/combinators/node.d.ts +24 -0
  13. package/dist/combinators/node.d.ts.map +1 -0
  14. package/dist/combinators/parser.d.ts +7 -7
  15. package/dist/combinators/parser.d.ts.map +1 -1
  16. package/dist/combinators/probe.d.ts +13 -0
  17. package/dist/combinators/probe.d.ts.map +1 -0
  18. package/dist/combinators/recover.d.ts.map +1 -1
  19. package/dist/combinators/regex.d.ts.map +1 -1
  20. package/dist/combinators/repeat.d.ts.map +1 -1
  21. package/dist/combinators/scanTo.d.ts.map +1 -1
  22. package/dist/combinators/sequence.d.ts.map +1 -1
  23. package/dist/combinators/trivia-skip.d.ts +33 -0
  24. package/dist/combinators/trivia-skip.d.ts.map +1 -0
  25. package/dist/combinators/withCtx.d.ts +1 -1
  26. package/dist/compiler/codegen.d.ts +13 -4
  27. package/dist/compiler/codegen.d.ts.map +1 -1
  28. package/dist/cst/grammar.d.ts +33 -2
  29. package/dist/cst/grammar.d.ts.map +1 -1
  30. package/dist/cst/incremental.d.ts +4 -2
  31. package/dist/cst/incremental.d.ts.map +1 -1
  32. package/dist/cst/trivia-index.d.ts +50 -0
  33. package/dist/cst/trivia-index.d.ts.map +1 -0
  34. package/dist/cst/types.d.ts +7 -7
  35. package/dist/cst/types.d.ts.map +1 -1
  36. package/dist/functional/doc.d.ts +53 -0
  37. package/dist/functional/doc.d.ts.map +1 -0
  38. package/dist/index.cjs +953 -221
  39. package/dist/index.cjs.map +4 -4
  40. package/dist/index.d.ts +12 -2
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +6519 -226
  43. package/dist/index.js.map +4 -4
  44. package/dist/plugin/evaluator.d.ts +26 -6
  45. package/dist/plugin/evaluator.d.ts.map +1 -1
  46. package/dist/plugin/index.cjs +1164 -215
  47. package/dist/plugin/index.cjs.map +4 -4
  48. package/dist/plugin/index.d.ts +5 -2
  49. package/dist/plugin/index.d.ts.map +1 -1
  50. package/dist/plugin/index.js +1164 -215
  51. package/dist/plugin/index.js.map +4 -4
  52. package/dist/types.d.ts +51 -6
  53. package/dist/types.d.ts.map +1 -1
  54. package/package.json +5 -3
package/dist/index.cjs CHANGED
@@ -34,12 +34,17 @@ __export(index_exports, {
34
34
  annotateSpan: () => annotateSpan,
35
35
  balanced: () => balanced,
36
36
  buildLineIndex: () => buildLineIndex,
37
+ buildTriviaIndex: () => buildTriviaIndex,
37
38
  choice: () => choice,
38
39
  compile: () => compile,
40
+ completionsAt: () => completionsAt,
39
41
  guard: () => guard,
40
42
  isParseError: () => isParseError,
43
+ keywords: () => keywords,
41
44
  literal: () => literal,
45
+ makeFunctionalDoc: () => makeFunctionalDoc,
42
46
  many: () => many,
47
+ node: () => node,
43
48
  not: () => not,
44
49
  offsetToLineCol: () => offsetToLineCol,
45
50
  oneOrMore: () => oneOrMore,
@@ -49,6 +54,7 @@ __export(index_exports, {
49
54
  recover: () => recover,
50
55
  ref: () => ref,
51
56
  regex: () => regex,
57
+ rules: () => rules,
52
58
  scanTo: () => scanTo,
53
59
  sepBy: () => sepBy,
54
60
  sequence: () => sequence,
@@ -104,6 +110,21 @@ function mergeRanges(ranges) {
104
110
  return out;
105
111
  }
106
112
 
113
+ // src/combinators/probe.ts
114
+ function failAt(ctx, expected, pos) {
115
+ const r = { ok: false, expected, span: { start: pos, end: pos } };
116
+ const probe = ctx._probe;
117
+ if (probe !== void 0 && pos <= probe.offset) {
118
+ const best = probe.best;
119
+ if (best === null || pos > best.span.start) {
120
+ probe.best = r;
121
+ } else if (pos === best.span.start) {
122
+ probe.best = { ...best, expected: [...best.expected, ...expected] };
123
+ }
124
+ }
125
+ return r;
126
+ }
127
+
107
128
  // src/combinators/literal.ts
108
129
  var _collatorCache = null;
109
130
  function collator() {
@@ -133,21 +154,21 @@ function literal(value, opts = {}) {
133
154
  _tag: "literal",
134
155
  _meta: meta,
135
156
  _def: { tag: "literal", value, caseInsensitive },
136
- parse(input, pos, _ctx) {
157
+ parse(input, pos, ctx) {
137
158
  const end = pos + value.length;
138
159
  if (end > input.length) {
139
- return { ok: false, expected: [JSON.stringify(value)], span: { start: pos, end: pos } };
160
+ return failAt(ctx, [JSON.stringify(value)], pos);
140
161
  }
141
162
  const slice = input.slice(pos, end);
142
163
  const matched = caseInsensitive ? collator().compare(slice, value) === 0 : slice === value;
143
164
  if (matched) {
144
165
  const span = { start: pos, end };
145
166
  const leaf = { _tag: "leaf", value: slice, span };
146
- if (_ctx._cstLeaves) _ctx._cstLeaves.push(leaf);
147
- if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(leaf);
167
+ if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
168
+ if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
148
169
  return { ok: true, value: slice, span };
149
170
  }
150
- return { ok: false, expected: [JSON.stringify(value)], span: { start: pos, end: pos } };
171
+ return failAt(ctx, [JSON.stringify(value)], pos);
151
172
  }
152
173
  };
153
174
  }
@@ -162,38 +183,38 @@ function firstSetFromRegex(pattern) {
162
183
  return { firstSet: any(), canMatchNewline: true };
163
184
  }
164
185
  }
165
- function canBeEmpty(node) {
166
- if (!node) return true;
167
- switch (node.type) {
186
+ function canBeEmpty(node2) {
187
+ if (!node2) return true;
188
+ switch (node2.type) {
168
189
  case "Repetition": {
169
- const q = node.quantifier;
190
+ const q = node2.quantifier;
170
191
  return q.kind === "*" || q.kind === "?" || q.kind === "Range" && (q.from ?? 1) === 0;
171
192
  }
172
193
  case "Alternative": {
173
- const exprs = node.expressions;
194
+ const exprs = node2.expressions;
174
195
  return !exprs || exprs.length === 0 || exprs.every((e) => canBeEmpty(e));
175
196
  }
176
197
  case "Group":
177
- return canBeEmpty(node.expression);
198
+ return canBeEmpty(node2.expression);
178
199
  case "Disjunction":
179
- return canBeEmpty(node.left) || canBeEmpty(node.right);
200
+ return canBeEmpty(node2.left) || canBeEmpty(node2.right);
180
201
  default:
181
202
  return false;
182
203
  }
183
204
  }
184
- function extractFirstSet(node) {
185
- if (!node) return { firstSet: empty(), canMatchNewline: false };
186
- switch (node.type) {
205
+ function extractFirstSet(node2) {
206
+ if (!node2) return { firstSet: empty(), canMatchNewline: false };
207
+ switch (node2.type) {
187
208
  case "Disjunction": {
188
- const left = extractFirstSet(node.left);
189
- const right = extractFirstSet(node.right);
209
+ const left = extractFirstSet(node2.left);
210
+ const right = extractFirstSet(node2.right);
190
211
  return {
191
212
  firstSet: union(left.firstSet, right.firstSet),
192
213
  canMatchNewline: left.canMatchNewline || right.canMatchNewline
193
214
  };
194
215
  }
195
216
  case "Alternative": {
196
- const exprs = node.expressions;
217
+ const exprs = node2.expressions;
197
218
  if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
198
219
  let fs = empty();
199
220
  let canNL = false;
@@ -206,13 +227,13 @@ function extractFirstSet(node) {
206
227
  return { firstSet: fs, canMatchNewline: canNL };
207
228
  }
208
229
  case "Char": {
209
- const kind = node.kind;
230
+ const kind = node2.kind;
210
231
  if (kind === "simple") {
211
- const code = node.codePoint ?? node.value.codePointAt(0) ?? 0;
232
+ const code = node2.codePoint ?? node2.value.codePointAt(0) ?? 0;
212
233
  return { firstSet: fromRange(code, code), canMatchNewline: code === 10 };
213
234
  }
214
235
  if (kind === "meta") {
215
- const val = node.value;
236
+ const val = node2.value;
216
237
  if (val === ".") return { firstSet: any(), canMatchNewline: false };
217
238
  if (val === "d") return { firstSet: fromRange(48, 57), canMatchNewline: false };
218
239
  if (val === "D") return { firstSet: any(), canMatchNewline: true };
@@ -229,7 +250,7 @@ function extractFirstSet(node) {
229
250
  return { firstSet: any(), canMatchNewline: true };
230
251
  }
231
252
  case "CharacterClass": {
232
- const expressions = node.expressions;
253
+ const expressions = node2.expressions;
233
254
  let fs = empty();
234
255
  let canNL = false;
235
256
  for (const expr of expressions) {
@@ -244,14 +265,14 @@ function extractFirstSet(node) {
244
265
  canNL = canNL || r.canMatchNewline;
245
266
  }
246
267
  }
247
- if (node.negative) return { firstSet: any(), canMatchNewline: true };
268
+ if (node2.negative) return { firstSet: any(), canMatchNewline: true };
248
269
  return { firstSet: fs, canMatchNewline: canNL };
249
270
  }
250
271
  case "Repetition": {
251
- return extractFirstSet(node.expression);
272
+ return extractFirstSet(node2.expression);
252
273
  }
253
274
  case "Group": {
254
- return extractFirstSet(node.expression);
275
+ return extractFirstSet(node2.expression);
255
276
  }
256
277
  default:
257
278
  return { firstSet: any(), canMatchNewline: true };
@@ -282,7 +303,7 @@ function regex(pattern, flags = "") {
282
303
  anchored.lastIndex = pos;
283
304
  const m = anchored.exec(input);
284
305
  if (m === null) {
285
- return { ok: false, expected: [`/${source}/`], span: { start: pos, end: pos } };
306
+ return failAt(ctx, [`/${source}/`], pos);
286
307
  }
287
308
  const span = { start: pos, end: pos + m[0].length };
288
309
  const leaf = { _tag: "leaf", value: m[0], span };
@@ -293,6 +314,98 @@ function regex(pattern, flags = "") {
293
314
  };
294
315
  }
295
316
 
317
+ // src/combinators/keywords.ts
318
+ function escapeRe(s) {
319
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
320
+ }
321
+ function keywords(words, opts = {}) {
322
+ const sorted = [...new Set(words)].sort((a, b) => b.length - a.length);
323
+ const alt = sorted.map(escapeRe).join("|");
324
+ const boundary = opts.boundary ? `(?![${opts.boundary}])` : "";
325
+ const flags = opts.caseInsensitive ? "iuy" : "uy";
326
+ const re = new RegExp(`(?:${alt})${boundary}`, flags);
327
+ let firstSet = empty();
328
+ for (const w of sorted) {
329
+ if (w.length === 0) continue;
330
+ const cp = w.codePointAt(0);
331
+ firstSet = union(firstSet, fromChar(cp));
332
+ if (opts.caseInsensitive) {
333
+ const u = String.fromCodePoint(cp).toUpperCase().codePointAt(0);
334
+ const l = String.fromCodePoint(cp).toLowerCase().codePointAt(0);
335
+ if (u !== void 0) firstSet = union(firstSet, fromChar(u));
336
+ if (l !== void 0) firstSet = union(firstSet, fromChar(l));
337
+ }
338
+ }
339
+ const meta = { firstSet, canMatchNewline: false, isTrivia: false };
340
+ return {
341
+ _tag: "keywords",
342
+ _meta: meta,
343
+ _def: { tag: "keywords", words: sorted, caseInsensitive: opts.caseInsensitive ?? false, boundary: opts.boundary },
344
+ parse(input, pos, ctx) {
345
+ re.lastIndex = pos;
346
+ const m = re.exec(input);
347
+ if (m === null || m.index !== pos) {
348
+ return failAt(ctx, ["keyword"], pos);
349
+ }
350
+ const value = m[0];
351
+ const span = { start: pos, end: pos + value.length };
352
+ const leaf = { _tag: "leaf", value, span };
353
+ if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
354
+ if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
355
+ return { ok: true, value, span };
356
+ }
357
+ };
358
+ }
359
+
360
+ // src/combinators/trivia-skip.ts
361
+ var NOOP_COMMIT = () => {
362
+ };
363
+ function scanTrivia(input, cur, ctx) {
364
+ const triviaP = ctx.trivia;
365
+ if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
366
+ const log = ctx._triviaLog;
367
+ if (log !== void 0) {
368
+ const tr2 = triviaP.parse(input, cur, { trackLines: false, state: ctx.state });
369
+ if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
370
+ const end = tr2.span.end;
371
+ return {
372
+ end,
373
+ commit: () => {
374
+ log.push(cur, end);
375
+ }
376
+ };
377
+ }
378
+ if (ctx.captureTrivia && ctx._cstRawChildren) {
379
+ const sink = [];
380
+ const tr2 = triviaP.parse(input, cur, {
381
+ trackLines: ctx.trackLines,
382
+ state: ctx.state,
383
+ _cstLeaves: sink
384
+ });
385
+ if (tr2.ok && tr2.span.end > cur) {
386
+ const out = ctx._cstRawChildren;
387
+ return {
388
+ end: tr2.span.end,
389
+ commit: () => {
390
+ for (const tok of sink) {
391
+ if (tok._tag === "leaf" && tok.span.end > tok.span.start) {
392
+ out.push({ _tag: "trivia", value: tok.value, span: tok.span });
393
+ }
394
+ }
395
+ }
396
+ };
397
+ }
398
+ return { end: cur, commit: NOOP_COMMIT };
399
+ }
400
+ const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
401
+ return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
402
+ }
403
+ function consumeTrivia(input, cur, ctx) {
404
+ const scan = scanTrivia(input, cur, ctx);
405
+ scan.commit();
406
+ return scan.end;
407
+ }
408
+
296
409
  // src/combinators/sequence.ts
297
410
  function sequence(...parsers) {
298
411
  const meta = {
@@ -309,12 +422,19 @@ function sequence(...parsers) {
309
422
  let cur = pos;
310
423
  for (let i = 0; i < parsers.length; i++) {
311
424
  if (ctx.trivia && i > 0) {
312
- const tr = ctx.trivia.parse(input, cur, { trivia: ctx.trivia, trackLines: ctx.trackLines, user: ctx.user });
313
- if (tr.ok) {
314
- if (ctx._cstRawChildren && tr.span.end > tr.span.start)
315
- ctx._cstRawChildren.push({ _tag: "trivia", value: input.slice(tr.span.start, tr.span.end), span: tr.span });
316
- cur = tr.span.end;
425
+ const scan = scanTrivia(input, cur, ctx);
426
+ const raw = ctx._cstRawChildren;
427
+ const mark = raw ? raw.length : 0;
428
+ scan.commit();
429
+ const result2 = parsers[i].parse(input, scan.end, ctx);
430
+ if (!result2.ok) return result2;
431
+ if (result2.span.end > scan.end) {
432
+ cur = result2.span.end;
433
+ } else if (raw) {
434
+ raw.length = mark;
317
435
  }
436
+ values.push(result2.value);
437
+ continue;
318
438
  }
319
439
  const result = parsers[i].parse(input, cur, ctx);
320
440
  if (!result.ok) return result;
@@ -415,14 +535,22 @@ function choice(...args) {
415
535
  return { ok: false, expected, span: { start: pos, end: pos } };
416
536
  }
417
537
  for (let i = 0; i < parsers.length; i++) {
418
- if (gates[i] && !gates[i](ctx.user)) continue;
538
+ if (gates[i] && !gates[i](ctx.state)) continue;
539
+ const leavesLen = ctx._cstLeaves?.length;
540
+ const rawLen = ctx._cstRawChildren?.length;
419
541
  const result = parsers[i].parse(input, pos, ctx);
420
542
  if (!result.ok) {
543
+ if (leavesLen !== void 0 && ctx._cstLeaves) ctx._cstLeaves.length = leavesLen;
544
+ if (rawLen !== void 0 && ctx._cstRawChildren) ctx._cstRawChildren.length = rawLen;
421
545
  expected.push(...result.expected);
422
546
  continue;
423
547
  }
424
548
  const checks = autoNot[i];
425
- if (checks && autoNotFires(input, result.span.end, checks)) continue;
549
+ if (checks && autoNotFires(input, result.span.end, checks)) {
550
+ if (leavesLen !== void 0 && ctx._cstLeaves) ctx._cstLeaves.length = leavesLen;
551
+ if (rawLen !== void 0 && ctx._cstRawChildren) ctx._cstRawChildren.length = rawLen;
552
+ continue;
553
+ }
426
554
  return result;
427
555
  }
428
556
  return { ok: false, expected, span: { start: pos, end: pos } };
@@ -553,6 +681,30 @@ function areDisjoint(sets) {
553
681
  }
554
682
 
555
683
  // src/combinators/repeat.ts
684
+ function repItem(parser2, input, cur, ctx) {
685
+ const raw = ctx._cstRawChildren;
686
+ const mark = raw ? raw.length : 0;
687
+ let pos = cur;
688
+ if (ctx.trivia) {
689
+ const scan = scanTrivia(input, cur, ctx);
690
+ scan.commit();
691
+ pos = scan.end;
692
+ }
693
+ if (pos >= input.length) {
694
+ if (raw) raw.length = mark;
695
+ return "stop";
696
+ }
697
+ const result = parser2.parse(input, pos, ctx);
698
+ if (!result.ok) {
699
+ if (raw) raw.length = mark;
700
+ return { fail: result };
701
+ }
702
+ if (result.span.end === pos) {
703
+ if (raw) raw.length = mark;
704
+ return "stop";
705
+ }
706
+ return { value: result.value, end: result.span.end };
707
+ }
556
708
  function many(parser2) {
557
709
  const meta = {
558
710
  firstSet: parser2._meta.firstSet,
@@ -567,11 +719,10 @@ function many(parser2) {
567
719
  const values = [];
568
720
  let cur = pos;
569
721
  while (cur < input.length) {
570
- const result = parser2.parse(input, cur, ctx);
571
- if (!result.ok) break;
572
- if (result.span.end === cur) break;
573
- values.push(result.value);
574
- cur = result.span.end;
722
+ const item = repItem(parser2, input, cur, ctx);
723
+ if (item === "stop" || "fail" in item) break;
724
+ values.push(item.value);
725
+ cur = item.end;
575
726
  }
576
727
  return { ok: true, value: values, span: { start: pos, end: cur } };
577
728
  }
@@ -593,11 +744,10 @@ function oneOrMore(parser2) {
593
744
  const values = [first.value];
594
745
  let cur = first.span.end;
595
746
  while (cur < input.length) {
596
- const result = parser2.parse(input, cur, ctx);
597
- if (!result.ok) break;
598
- if (result.span.end === cur) break;
599
- values.push(result.value);
600
- cur = result.span.end;
747
+ const item = repItem(parser2, input, cur, ctx);
748
+ if (item === "stop" || "fail" in item) break;
749
+ values.push(item.value);
750
+ cur = item.end;
601
751
  }
602
752
  return { ok: true, value: values, span: { start: pos, end: cur } };
603
753
  }
@@ -636,26 +786,10 @@ function sepBy(parser2, separator) {
636
786
  const values = [first.value];
637
787
  let cur = first.span.end;
638
788
  while (cur < input.length) {
639
- let sepPos = cur;
640
- if (ctx.trivia) {
641
- const triviaCtx = { trivia: ctx.trivia, trackLines: ctx.trackLines, user: ctx.user };
642
- const tr = ctx.trivia.parse(input, sepPos, triviaCtx);
643
- if (tr.ok) {
644
- if (ctx._cstRawChildren && tr.span.end > tr.span.start) ctx._cstRawChildren.push({ _tag: "trivia", value: input.slice(tr.span.start, tr.span.end), span: tr.span });
645
- sepPos = tr.span.end;
646
- }
647
- }
789
+ const sepPos = ctx.trivia ? consumeTrivia(input, cur, ctx) : cur;
648
790
  const sep = separator.parse(input, sepPos, ctx);
649
791
  if (!sep.ok) break;
650
- let nextPos = sep.span.end;
651
- if (ctx.trivia) {
652
- const triviaCtx = { trivia: ctx.trivia, trackLines: ctx.trackLines, user: ctx.user };
653
- const tr = ctx.trivia.parse(input, nextPos, triviaCtx);
654
- if (tr.ok) {
655
- if (ctx._cstRawChildren && tr.span.end > tr.span.start) ctx._cstRawChildren.push({ _tag: "trivia", value: input.slice(tr.span.start, tr.span.end), span: tr.span });
656
- nextPos = tr.span.end;
657
- }
658
- }
792
+ const nextPos = ctx.trivia ? consumeTrivia(input, sep.span.end, ctx) : sep.span.end;
659
793
  const next = parser2.parse(input, nextPos, ctx);
660
794
  if (!next.ok) break;
661
795
  values.push(next.value);
@@ -697,7 +831,7 @@ function ref() {
697
831
  }
698
832
 
699
833
  // src/combinators/parser.ts
700
- function parser(factory) {
834
+ function rules(factory) {
701
835
  const cache = {};
702
836
  const proxy = new Proxy(cache, {
703
837
  get(target, key) {
@@ -735,9 +869,9 @@ function not(parser2) {
735
869
  isTrivia: false
736
870
  };
737
871
  return {
738
- _tag: "unknown",
872
+ _tag: "not",
739
873
  _meta: meta,
740
- _def: { tag: "unknown" },
874
+ _def: { tag: "not", parser: parser2 },
741
875
  parse(input, pos, ctx) {
742
876
  const result = parser2.parse(input, pos, ctx);
743
877
  if (result.ok) {
@@ -748,6 +882,44 @@ function not(parser2) {
748
882
  };
749
883
  }
750
884
 
885
+ // src/combinators/node.ts
886
+ function node(type, parser2, build) {
887
+ const meta = {
888
+ firstSet: parser2._meta.firstSet,
889
+ canMatchNewline: parser2._meta.canMatchNewline,
890
+ isTrivia: false
891
+ };
892
+ return {
893
+ _tag: "node",
894
+ _meta: meta,
895
+ _def: { tag: "node", type, parser: parser2, build },
896
+ parse(input, pos, ctx) {
897
+ const children = [];
898
+ const rawChildren = [];
899
+ const innerCtx = {
900
+ ...ctx,
901
+ // A node() always records the trivia between its terms — the library owns
902
+ // capture so grammar authors don't reconstruct it. (Matches the compiler.)
903
+ captureTrivia: true,
904
+ _cstChildren: children,
905
+ _cstLeaves: children,
906
+ _cstRawChildren: rawChildren
907
+ };
908
+ const r = parser2.parse(input, pos, innerCtx);
909
+ if (!r.ok) return r;
910
+ const built = build(children, rawChildren, r.span);
911
+ const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
912
+ if (ctx._cstChildren) ctx._cstChildren.push(built);
913
+ if (ctx._cstRawChildren) {
914
+ ctx._cstRawChildren.push(
915
+ isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span }
916
+ );
917
+ }
918
+ return { ok: true, value: built, span: r.span };
919
+ }
920
+ };
921
+ }
922
+
751
923
  // src/combinators/map.ts
752
924
  function transform(parser2, fn) {
753
925
  return {
@@ -816,15 +988,48 @@ function annotateSpan(span, index) {
816
988
  }
817
989
 
818
990
  // src/combinators/grammar.ts
819
- function parse(parser2, input, opts = {}) {
991
+ function parser(opts, root) {
992
+ return {
993
+ _tag: "grammar",
994
+ _meta: root._meta,
995
+ _def: {
996
+ tag: "grammar",
997
+ parser: root,
998
+ triviaParser: opts.trivia,
999
+ trackLines: opts.trackLines ?? false
1000
+ },
1001
+ parse(input, pos, _ctx) {
1002
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
1003
+ const ctx = {
1004
+ ..._ctx,
1005
+ trackLines,
1006
+ ...opts.trivia !== void 0 ? { trivia: opts.trivia } : {},
1007
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {}
1008
+ };
1009
+ const result = root.parse(input, pos ?? 0, ctx);
1010
+ if (trackLines) {
1011
+ const idx = buildLineIndex(input);
1012
+ return { ...result, span: annotateSpan(result.span, idx) };
1013
+ }
1014
+ return result;
1015
+ }
1016
+ };
1017
+ }
1018
+ function parse(combinator, input, opts = {}) {
820
1019
  const trackLines = opts.trackLines ?? false;
821
- const ctx = opts.trivia !== void 0 ? { trivia: opts.trivia, trackLines } : { trackLines };
822
- const result = parser2.parse(input, 0, ctx);
1020
+ const _errors = opts.recover ? [] : void 0;
1021
+ const ctx = {
1022
+ trackLines,
1023
+ ..._errors !== void 0 ? { _errors } : {}
1024
+ };
1025
+ const result = combinator.parse(input, 0, ctx);
1026
+ if (!result.ok) return result;
1027
+ const withErrors = _errors !== void 0 ? { ...result, errors: _errors } : result;
823
1028
  if (trackLines) {
824
1029
  const idx = buildLineIndex(input);
825
- return { ...result, span: annotateSpan(result.span, idx) };
1030
+ return { ...withErrors, span: annotateSpan(withErrors.span, idx) };
826
1031
  }
827
- return result;
1032
+ return withErrors;
828
1033
  }
829
1034
 
830
1035
  // src/compiler/codegen.ts
@@ -834,9 +1039,58 @@ function v(ctx, prefix = "_v") {
834
1039
  function ind(ctx) {
835
1040
  return " ".repeat(ctx.indent);
836
1041
  }
1042
+ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
1043
+ if (!ctx.capturing) return [];
1044
+ const i = ind(ctx);
1045
+ if (ctx.capAsTrivia) {
1046
+ return [`${i}if (_ctx._cstRawChildren) _ctx._cstRawChildren.push({ _tag: 'trivia', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } })`];
1047
+ }
1048
+ const lf = v(ctx, "_lf");
1049
+ return [
1050
+ `${i}if (_ctx._cstLeaves) {`,
1051
+ `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
1052
+ `${i} _ctx._cstLeaves.push(${lf})`,
1053
+ `${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
1054
+ `${i}}`
1055
+ ];
1056
+ }
1057
+ function ensureTriviaCaptureFn(ctx) {
1058
+ const trivia2 = ctx.activeTrivia;
1059
+ const existing = ctx.triviaCaptureNames.get(trivia2);
1060
+ if (existing) return existing;
1061
+ const fnName = `_tc${ctx.triviaCaptureNames.size}`;
1062
+ ctx.triviaCaptureNames.set(trivia2, fnName);
1063
+ const saved = { indent: ctx.indent, failLabel: ctx.failLabel, asTrivia: ctx.capAsTrivia, trivia: ctx.activeTrivia };
1064
+ ctx.indent = 2;
1065
+ ctx.failLabel = "_cap";
1066
+ ctx.capAsTrivia = true;
1067
+ delete ctx.activeTrivia;
1068
+ const r = emit(trivia2, ctx, "_pos");
1069
+ ctx.indent = saved.indent;
1070
+ ctx.failLabel = saved.failLabel;
1071
+ ctx.capAsTrivia = saved.asTrivia;
1072
+ if (saved.trivia) ctx.activeTrivia = saved.trivia;
1073
+ ctx.namedFnDecls.push([
1074
+ `function ${fnName}(input, _pos, _ctx) {`,
1075
+ ` let _e = _pos`,
1076
+ ` _cap: {`,
1077
+ ...r.stmts,
1078
+ ` _e = ${r.endVar}`,
1079
+ ` }`,
1080
+ ` if (_ctx._triviaLog !== undefined && _e > _pos) _ctx._triviaLog.push(_pos, _e)`,
1081
+ ` return _e`,
1082
+ `}`
1083
+ ].join("\n"));
1084
+ return fnName;
1085
+ }
837
1086
  function failStmt(ctx, expected, posExpr) {
1087
+ if (ctx.failLabel) return `${ind(ctx)}break ${ctx.failLabel}`;
838
1088
  return `${ind(ctx)}return { ok: false, expected: [${expected}], span: { start: ${posExpr}, end: ${posExpr} } }`;
839
1089
  }
1090
+ function failArr(ctx, expectedArr, posExpr) {
1091
+ if (ctx.failLabel) return `${ind(ctx)}break ${ctx.failLabel}`;
1092
+ return `${ind(ctx)}return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
1093
+ }
840
1094
  function firstSetCond(codeVar, fs) {
841
1095
  if (fs.kind === "any") return "true";
842
1096
  if (fs.kind === "empty") return "false";
@@ -852,6 +1106,30 @@ function asIIFE(stmts, valueVar, endVar, startPos, indent) {
852
1106
  `${indent}})()`
853
1107
  ].join("\n");
854
1108
  }
1109
+ function emitFallible(inner, ctx, pos) {
1110
+ const lbl = v(ctx, "_lbl");
1111
+ const okV = `${lbl}ok`;
1112
+ const valV = `${lbl}v`;
1113
+ const endV = `${lbl}e`;
1114
+ const savedLabel = ctx.failLabel;
1115
+ const savedIndent = ctx.indent;
1116
+ ctx.failLabel = lbl;
1117
+ ctx.indent = 0;
1118
+ const r = emit(inner, ctx, pos);
1119
+ ctx.failLabel = savedLabel;
1120
+ ctx.indent = savedIndent;
1121
+ const ind0 = ind(ctx);
1122
+ const stmts = [
1123
+ `${ind0}let ${okV} = false, ${valV}, ${endV} = ${pos}`,
1124
+ `${ind0}${lbl}: {`,
1125
+ ...r.stmts,
1126
+ `${ind0} ${valV} = ${r.valueVar}`,
1127
+ `${ind0} ${endV} = ${r.endVar}`,
1128
+ `${ind0} ${okV} = true`,
1129
+ `${ind0}}`
1130
+ ];
1131
+ return { stmts, okVar: okV, valVar: valV, endVar: endV };
1132
+ }
855
1133
  function emitLit(def, ctx, pos) {
856
1134
  const { value, caseInsensitive } = def;
857
1135
  const len = value.length;
@@ -889,12 +1167,19 @@ function emitLit(def, ctx, pos) {
889
1167
  `${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
890
1168
  );
891
1169
  }
892
- return { stmts, valueVar: vv, endVar: len === 0 ? pos : `${pos} + ${len}` };
1170
+ const endVar = len === 0 ? pos : `${pos} + ${len}`;
1171
+ stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
1172
+ return { stmts, valueVar: vv, endVar };
893
1173
  }
894
1174
  function emitRegex(def, ctx, pos) {
895
1175
  const flags = "y" + def.flags.replace(/[gy]/g, "");
896
- const rName = `_re${ctx.regexDecls.length}`;
897
- ctx.regexDecls.push(`const ${rName} = /${def.optimizedSource}/${flags}`);
1176
+ const key = `${def.optimizedSource}/${flags}`;
1177
+ let rName = ctx.regexMap.get(key);
1178
+ if (rName === void 0) {
1179
+ rName = `_re${ctx.regexDecls.length}`;
1180
+ ctx.regexDecls.push(`const ${rName} = /${def.optimizedSource}/${flags}`);
1181
+ ctx.regexMap.set(key, rName);
1182
+ }
898
1183
  const mv = v(ctx, "_m");
899
1184
  const vv = v(ctx);
900
1185
  const expectedStr = JSON.stringify(`/${def.source}/`);
@@ -904,7 +1189,9 @@ function emitRegex(def, ctx, pos) {
904
1189
  `${ind(ctx)}if (${mv} === null) ${failStmt({ ...ctx, indent: 0 }, expectedStr, pos).trim()}`,
905
1190
  `${ind(ctx)}const ${vv} = ${mv}[0]`
906
1191
  ];
907
- return { stmts, valueVar: vv, endVar: `${pos} + ${vv}.length` };
1192
+ const endVar = `${pos} + ${vv}.length`;
1193
+ stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
1194
+ return { stmts, valueVar: vv, endVar };
908
1195
  }
909
1196
  function ensureTriviaFn(ctx) {
910
1197
  const trivia2 = ctx.activeTrivia;
@@ -912,9 +1199,15 @@ function ensureTriviaFn(ctx) {
912
1199
  const fnName = `_pf${ctx.namedParsers.size}`;
913
1200
  ctx.namedParsers.set(trivia2, fnName);
914
1201
  const savedIndent = ctx.indent;
1202
+ const savedFailLabel = ctx.failLabel;
1203
+ const savedTrivia = ctx.activeTrivia;
915
1204
  ctx.indent = 1;
1205
+ ctx.failLabel = void 0;
1206
+ delete ctx.activeTrivia;
916
1207
  const r = emit(trivia2, ctx, "_pos");
917
1208
  ctx.indent = savedIndent;
1209
+ ctx.failLabel = savedFailLabel;
1210
+ if (savedTrivia) ctx.activeTrivia = savedTrivia;
918
1211
  ctx.namedFnDecls.push([
919
1212
  `function ${fnName}(input, _pos, _ctx) {`,
920
1213
  ...r.stmts,
@@ -934,12 +1227,17 @@ function emitSeq(def, ctx, pos) {
934
1227
  const valueVars = [];
935
1228
  for (let i = 0; i < def.parsers.length; i++) {
936
1229
  if (i > 0 && ctx.activeTrivia) {
937
- const trivFn = ensureTriviaFn(ctx);
938
- const tmp = v(ctx, "_trv");
939
- stmts.push(
940
- `${ind(ctx)}const ${tmp} = ${trivFn}(input, ${curV}, _ctx)`,
941
- `${ind(ctx)}if (${tmp}.ok) ${curV} = ${tmp}.span.end`
942
- );
1230
+ if (ctx.capturing) {
1231
+ const capFn = ensureTriviaCaptureFn(ctx);
1232
+ stmts.push(`${ind(ctx)}${curV} = ${capFn}(input, ${curV}, _ctx)`);
1233
+ } else {
1234
+ const trivFn = ensureTriviaFn(ctx);
1235
+ const tmp = v(ctx, "_trv");
1236
+ stmts.push(
1237
+ `${ind(ctx)}const ${tmp} = ${trivFn}(input, ${curV}, _ctx)`,
1238
+ `${ind(ctx)}if (${tmp}.ok) ${curV} = ${tmp}.span.end`
1239
+ );
1240
+ }
943
1241
  }
944
1242
  const r = emit(def.parsers[i], ctx, curV);
945
1243
  stmts.push(...r.stmts, `${ind(ctx)}${curV} = ${r.endVar}`);
@@ -979,9 +1277,7 @@ function emitChoice(def, ctx, pos) {
979
1277
  ctx.indent--;
980
1278
  stmts.push(`${ind(ctx)}}`);
981
1279
  }
982
- stmts.push(
983
- `${ind(ctx)}else return { ok: false, expected: ${allExpected}, span: { start: ${pos}, end: ${pos} } }`
984
- );
1280
+ stmts.push(`${ind(ctx)}else ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
985
1281
  return { stmts, valueVar: valV, endVar: endV };
986
1282
  }
987
1283
  return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
@@ -989,17 +1285,21 @@ function emitChoice(def, ctx, pos) {
989
1285
  function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
990
1286
  const superParser = def.parsers[superIndex];
991
1287
  const regexDef = getCoreRegexDef(superParser);
992
- const reIdx = ctx.regexDecls.length;
993
- const reVar = `_re${reIdx}`;
994
1288
  const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
995
- ctx.regexDecls.push(`const ${reVar} = /${regexDef.source}/${cleanFlags}`);
1289
+ const reKey = `${regexDef.source}/${cleanFlags}`;
1290
+ let reVar = ctx.regexMap.get(reKey);
1291
+ if (reVar === void 0) {
1292
+ reVar = `_re${ctx.regexDecls.length}`;
1293
+ ctx.regexDecls.push(`const ${reVar} = /${regexDef.source}/${cleanFlags}`);
1294
+ ctx.regexMap.set(reKey, reVar);
1295
+ }
996
1296
  const matchV = v(ctx, "_gm");
997
1297
  const wordV = v(ctx, "_gw");
998
1298
  const endV = v(ctx, "_ge");
999
1299
  const stmts = [
1000
1300
  `${ind(ctx)}${reVar}.lastIndex = ${pos}`,
1001
1301
  `${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
1002
- `${ind(ctx)}if (${matchV} === null) return { ok: false, expected: ${allExpected}, span: { start: ${pos}, end: ${pos} } }`,
1302
+ `${ind(ctx)}if (${matchV} === null) ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`,
1003
1303
  `${ind(ctx)}const ${wordV} = ${matchV}[0]`,
1004
1304
  `${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`
1005
1305
  ];
@@ -1039,7 +1339,7 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
1039
1339
  ctx.indent--;
1040
1340
  stmts.push(`${ind(ctx)}}`);
1041
1341
  }
1042
- stmts.push(`${ind(ctx)}else return { ok: false, expected: ${allExpected}, span: { start: ${pos}, end: ${pos} } }`);
1342
+ stmts.push(`${ind(ctx)}else ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
1043
1343
  return { stmts, valueVar: valV, endVar: endV };
1044
1344
  }
1045
1345
  function emitFirstMatch(def, allExpected, ctx, pos) {
@@ -1050,40 +1350,60 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
1050
1350
  const gate = def.gates[i];
1051
1351
  const autoNot = def.autoNot[i];
1052
1352
  const savedIndent = ctx.indent;
1353
+ const savedFailLabel = ctx.failLabel;
1053
1354
  ctx.indent = 0;
1355
+ ctx.failLabel = void 0;
1054
1356
  const r = emit(p, ctx, pos);
1055
1357
  ctx.indent = savedIndent;
1358
+ ctx.failLabel = savedFailLabel;
1056
1359
  const iife = asIIFE(r.stmts, r.valueVar, r.endVar, pos, ind(ctx));
1057
1360
  let gateCond = null;
1058
1361
  if (gate) {
1059
1362
  const gateIdx = ctx.mapFns.length;
1060
1363
  ctx.mapFns.push(gate);
1061
- gateCond = `_mf[${gateIdx}](_ctx.user)`;
1364
+ gateCond = `_mf[${gateIdx}](_ctx.state)`;
1062
1365
  }
1063
1366
  const skipCond = gateCond ? `!${resV}?.ok && ${gateCond}` : `!${resV}?.ok`;
1367
+ const markLeaves = ctx.capturing ? v(ctx, "_cml") : null;
1368
+ const markRaw = ctx.capturing ? v(ctx, "_cmr") : null;
1369
+ const rollback = markLeaves ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLeaves}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRaw}` : "";
1370
+ const markStmts = markLeaves ? [
1371
+ `${ind(ctx)} const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
1372
+ `${ind(ctx)} const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`
1373
+ ] : [];
1374
+ const rollbackStmt = rollback ? `${ind(ctx)} if (!${resV}?.ok) { ${rollback} }` : "";
1064
1375
  if (autoNot && autoNot.length > 0) {
1065
1376
  const tmp = v(ctx, "_ct");
1066
1377
  const anCode = v(ctx, "_anc");
1067
1378
  const rejectCond = autoNot.map(
1068
1379
  (check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${tmp}.span.end)`
1069
1380
  ).join(" || ");
1381
+ stmts.push(`${ind(ctx)}if (${skipCond}) {`);
1382
+ stmts.push(...markStmts);
1070
1383
  stmts.push(
1071
- `${ind(ctx)}if (${skipCond}) { const ${tmp} = (() => { try { return ${iife} } catch {} })(); if (${tmp}?.ok) { const ${anCode} = ${tmp}.span.end < input.length ? input.charCodeAt(${tmp}.span.end) : -1; if (!(${rejectCond})) ${resV} = ${tmp} } }`
1384
+ `${ind(ctx)} const ${tmp} = (() => { try { return ${iife} } catch {} })()`,
1385
+ `${ind(ctx)} if (${tmp}?.ok) { const ${anCode} = ${tmp}.span.end < input.length ? input.charCodeAt(${tmp}.span.end) : -1; if (!(${rejectCond})) ${resV} = ${tmp} }`
1072
1386
  );
1387
+ if (rollbackStmt) stmts.push(rollbackStmt);
1388
+ stmts.push(`${ind(ctx)}}`);
1073
1389
  } else {
1074
- stmts.push(`${ind(ctx)}if (${skipCond}) { try { ${resV} = ${iife} } catch {} }`);
1390
+ stmts.push(`${ind(ctx)}if (${skipCond}) {`);
1391
+ stmts.push(...markStmts);
1392
+ stmts.push(`${ind(ctx)} try { ${resV} = ${iife} } catch {}`);
1393
+ if (rollbackStmt) stmts.push(rollbackStmt);
1394
+ stmts.push(`${ind(ctx)}}`);
1075
1395
  }
1076
1396
  }
1077
- stmts.push(
1078
- `${ind(ctx)}if (!${resV}?.ok) return { ok: false, expected: ${allExpected}, span: { start: ${pos}, end: ${pos} } }`
1079
- );
1397
+ stmts.push(`${ind(ctx)}if (!${resV}?.ok) ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
1080
1398
  return { stmts, valueVar: `${resV}.value`, endVar: `${resV}.span.end` };
1081
1399
  }
1082
1400
  function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
1083
- if (strategy.tag === "greedyClassify")
1084
- return emitGreedyClassify(def, strategy.superIndex, allExpected, ctx, pos);
1085
- if (strategy.tag === "literalsLongestFirst")
1086
- return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
1401
+ if (!ctx.capturing) {
1402
+ if (strategy.tag === "greedyClassify")
1403
+ return emitGreedyClassify(def, strategy.superIndex, allExpected, ctx, pos);
1404
+ if (strategy.tag === "literalsLongestFirst")
1405
+ return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
1406
+ }
1087
1407
  return emitFirstMatch(def, allExpected, ctx, pos);
1088
1408
  }
1089
1409
  function emitTransformChain(p, baseValue, endV, startPos, ctx) {
@@ -1092,6 +1412,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
1092
1412
  const innerR = emitTransformChain(def.parser, baseValue, endV, startPos, ctx);
1093
1413
  const fnIdx = ctx.mapFns.length;
1094
1414
  ctx.mapFns.push(def.fn);
1415
+ ctx.mapFnSrcs.push(def.fnSrc ?? null);
1095
1416
  const vv = v(ctx);
1096
1417
  return {
1097
1418
  stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = _mf[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
@@ -1119,13 +1440,6 @@ function emitMany(def, ctx, pos) {
1119
1440
  `${ind(ctx)}const ${arrV} = []`,
1120
1441
  `${ind(ctx)}let ${curV} = ${pos}`
1121
1442
  ];
1122
- const emitInnerIIFE = () => {
1123
- const savedIndent = ctx.indent;
1124
- ctx.indent = 0;
1125
- const r = emit(def.parser, ctx, curV);
1126
- ctx.indent = savedIndent;
1127
- return asIIFE(r.stmts, r.valueVar, r.endVar, curV, ind(ctx) + " ");
1128
- };
1129
1443
  if (def.min === 1) {
1130
1444
  const firstR = emit(def.parser, ctx, curV);
1131
1445
  stmts.push(...firstR.stmts);
@@ -1136,11 +1450,35 @@ function emitMany(def, ctx, pos) {
1136
1450
  }
1137
1451
  stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
1138
1452
  ctx.indent++;
1453
+ let itemPos = curV;
1454
+ let rollback = "";
1455
+ if (ctx.activeTrivia) {
1456
+ if (ctx.capturing) {
1457
+ const capFn = ensureTriviaCaptureFn(ctx);
1458
+ const markV = v(ctx, "_mk");
1459
+ const npV = v(ctx, "_np");
1460
+ stmts.push(
1461
+ `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
1462
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx)`
1463
+ );
1464
+ itemPos = npV;
1465
+ rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; `;
1466
+ } else {
1467
+ const trivFn = ensureTriviaFn(ctx);
1468
+ const npV = v(ctx, "_np");
1469
+ stmts.push(
1470
+ `${ind(ctx)}let ${npV} = ${curV}`,
1471
+ `${ind(ctx)}{ const _trv = ${trivFn}(input, ${npV}, _ctx); if (_trv.ok) ${npV} = _trv.span.end }`
1472
+ );
1473
+ itemPos = npV;
1474
+ }
1475
+ }
1476
+ const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
1139
1477
  stmts.push(
1140
- `${ind(ctx)}const _iter = (() => { try { return ${emitInnerIIFE()} } catch { return null } })()`,
1141
- `${ind(ctx)}if (!_iter?.ok || _iter.span.end === ${curV}) break`,
1142
- `${ind(ctx)}${arrV}.push(_iter.value)`,
1143
- `${ind(ctx)}${curV} = _iter.span.end`
1478
+ ...iterStmts,
1479
+ `${ind(ctx)}if (!${iterOk} || ${iterEnd} === ${curV}) { ${rollback}break }`,
1480
+ `${ind(ctx)}${arrV}.push(${iterVal})`,
1481
+ `${ind(ctx)}${curV} = ${iterEnd}`
1144
1482
  );
1145
1483
  ctx.indent--;
1146
1484
  stmts.push(`${ind(ctx)}}`);
@@ -1149,56 +1487,80 @@ function emitMany(def, ctx, pos) {
1149
1487
  function emitOptional(def, ctx, pos) {
1150
1488
  const valV = v(ctx, "_opt");
1151
1489
  const endV = v(ctx, "_opte");
1152
- const savedIndent = ctx.indent;
1153
- ctx.indent = 0;
1154
- const r = emit(def.parser, ctx, pos);
1155
- ctx.indent = savedIndent;
1156
- const iife = asIIFE(r.stmts, r.valueVar, r.endVar, pos, ind(ctx));
1157
- const resV = v(ctx, "_optr");
1490
+ const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
1491
+ const ind0 = ind(ctx);
1158
1492
  const stmts = [
1159
- `${ind(ctx)}const ${resV} = (() => { try { return ${iife} } catch { return null } })()`,
1160
- `${ind(ctx)}const ${valV} = ${resV}?.ok ? ${resV}.value : null`,
1161
- `${ind(ctx)}const ${endV} = ${resV}?.ok ? ${resV}.span.end : ${pos}`
1493
+ ...lblStmts,
1494
+ `${ind0}const ${valV} = ${okVar} ? ${valVar} : null`,
1495
+ `${ind0}const ${endV} = ${okVar} ? ${endVar} : ${pos}`
1162
1496
  ];
1163
1497
  return { stmts, valueVar: valV, endVar: endV };
1164
1498
  }
1165
1499
  function emitSepBy(_p, def, ctx, pos) {
1166
1500
  const arrV = v(ctx, "_arr");
1167
1501
  const curV = v(ctx, "_cur");
1168
- const iife = (inner, posExpr) => {
1169
- const saved = ctx.indent;
1170
- ctx.indent = 0;
1171
- const r = emit(inner, ctx, posExpr);
1172
- ctx.indent = saved;
1173
- return asIIFE(r.stmts, r.valueVar, r.endVar, posExpr, ind(ctx));
1174
- };
1175
- const firstR_saved = ctx.indent;
1176
- ctx.indent = 0;
1177
- const firstR = emit(def.parser, ctx, pos);
1178
- ctx.indent = firstR_saved;
1179
- const firstV = v(ctx, "_sb0");
1180
- const sepV = v(ctx, "_sbs");
1181
- const nextV = v(ctx, "_sbn");
1502
+ const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
1182
1503
  const stmts = [
1183
1504
  `${ind(ctx)}const ${arrV} = []`,
1184
1505
  `${ind(ctx)}let ${curV} = ${pos}`,
1185
- `${ind(ctx)}const ${firstV} = (() => { try { return ${asIIFE(firstR.stmts, firstR.valueVar, firstR.endVar, pos, ind(ctx))} } catch { return null } })()`,
1186
- `${ind(ctx)}if (${firstV}?.ok) {`
1506
+ ...firstStmts,
1507
+ `${ind(ctx)}if (${firstOk}) {`
1187
1508
  ];
1188
1509
  ctx.indent++;
1189
1510
  stmts.push(
1190
- `${ind(ctx)}${arrV}.push(${firstV}.value)`,
1191
- `${ind(ctx)}${curV} = ${firstV}.span.end`,
1511
+ `${ind(ctx)}${arrV}.push(${firstVal})`,
1512
+ `${ind(ctx)}${curV} = ${firstEnd}`,
1192
1513
  `${ind(ctx)}while (${curV} < input.length) {`
1193
1514
  );
1194
1515
  ctx.indent++;
1516
+ let rollback = "";
1517
+ if (ctx.capturing) {
1518
+ const markV = v(ctx, "_mk");
1519
+ stmts.push(`${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`);
1520
+ rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; `;
1521
+ }
1522
+ let sepAtPos = curV;
1523
+ if (ctx.activeTrivia) {
1524
+ if (ctx.capturing) {
1525
+ const capFn = ensureTriviaCaptureFn(ctx);
1526
+ const spV = v(ctx, "_sp");
1527
+ stmts.push(`${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx)`);
1528
+ sepAtPos = spV;
1529
+ } else {
1530
+ const trivFn = ensureTriviaFn(ctx);
1531
+ const spV = v(ctx, "_sp");
1532
+ stmts.push(
1533
+ `${ind(ctx)}let ${spV} = ${curV}`,
1534
+ `${ind(ctx)}{ const _trv = ${trivFn}(input, ${spV}, _ctx); if (_trv.ok) ${spV} = _trv.span.end }`
1535
+ );
1536
+ sepAtPos = spV;
1537
+ }
1538
+ }
1539
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
1540
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollback}break }`);
1541
+ let nextAtPos = sepEnd;
1542
+ if (ctx.activeTrivia) {
1543
+ if (ctx.capturing) {
1544
+ const capFn = ensureTriviaCaptureFn(ctx);
1545
+ const npV = v(ctx, "_np");
1546
+ stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx)`);
1547
+ nextAtPos = npV;
1548
+ } else {
1549
+ const trivFn = ensureTriviaFn(ctx);
1550
+ const npV = v(ctx, "_np");
1551
+ stmts.push(
1552
+ `${ind(ctx)}let ${npV} = ${sepEnd}`,
1553
+ `${ind(ctx)}{ const _trv = ${trivFn}(input, ${npV}, _ctx); if (_trv.ok) ${npV} = _trv.span.end }`
1554
+ );
1555
+ nextAtPos = npV;
1556
+ }
1557
+ }
1558
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, nextAtPos);
1195
1559
  stmts.push(
1196
- `${ind(ctx)}const ${sepV} = (() => { try { return ${iife(def.separator, curV)} } catch { return null } })()`,
1197
- `${ind(ctx)}if (!${sepV}?.ok) break`,
1198
- `${ind(ctx)}const ${nextV} = (() => { try { return ${iife(def.parser, `${sepV}.span.end`)} } catch { return null } })()`,
1199
- `${ind(ctx)}if (!${nextV}?.ok) break`,
1200
- `${ind(ctx)}${arrV}.push(${nextV}.value)`,
1201
- `${ind(ctx)}${curV} = ${nextV}.span.end`
1560
+ ...nextStmts,
1561
+ `${ind(ctx)}if (!${nextOk}) { ${rollback}break }`,
1562
+ `${ind(ctx)}${arrV}.push(${nextVal})`,
1563
+ `${ind(ctx)}${curV} = ${nextEnd}`
1202
1564
  );
1203
1565
  ctx.indent--;
1204
1566
  stmts.push(`${ind(ctx)}}`);
@@ -1207,13 +1569,6 @@ function emitSepBy(_p, def, ctx, pos) {
1207
1569
  return { stmts, valueVar: arrV, endVar: curV };
1208
1570
  }
1209
1571
  function emitScanTo(def, ctx, pos) {
1210
- const safeIIFE = (inner, posExpr) => {
1211
- const saved = ctx.indent;
1212
- ctx.indent = 0;
1213
- const r = emit(inner, ctx, posExpr);
1214
- ctx.indent = saved;
1215
- return `(() => { try { return ${asIIFE(r.stmts, r.valueVar, r.endVar, posExpr, ind(ctx))} } catch { return null } })()`;
1216
- };
1217
1572
  const curV = v(ctx, "_stcur");
1218
1573
  const foundV = v(ctx, "_stfnd");
1219
1574
  const stmts = [
@@ -1222,18 +1577,19 @@ function emitScanTo(def, ctx, pos) {
1222
1577
  `${ind(ctx)}while (${curV} < input.length) {`
1223
1578
  ];
1224
1579
  ctx.indent++;
1225
- const sentV = v(ctx, "_sts");
1226
- stmts.push(
1227
- `${ind(ctx)}const ${sentV} = ${safeIIFE(def.sentinel, curV)}`,
1228
- `${ind(ctx)}if (${sentV}?.ok) { ${foundV} = true; break }`
1229
- );
1580
+ const probeCtx = { ...ctx, capturing: false };
1581
+ const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
1582
+ stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
1230
1583
  if (def.skip.length > 0) {
1231
1584
  const advV = v(ctx, "_stadv");
1232
1585
  stmts.push(`${ind(ctx)}let ${advV} = false`);
1233
1586
  for (const skipper of def.skip) {
1234
- const skV = v(ctx, "_sk");
1587
+ const { stmts: skStmts, okVar: skOk, endVar: skEnd } = emitFallible(skipper, probeCtx, curV);
1235
1588
  stmts.push(
1236
- `${ind(ctx)}if (!${advV}) { const ${skV} = ${safeIIFE(skipper, curV)}; if (${skV}?.ok && ${skV}.span.end > ${curV}) { ${curV} = ${skV}.span.end; ${advV} = true } }`
1589
+ `${ind(ctx)}if (!${advV}) {`,
1590
+ ...skStmts,
1591
+ `${ind(ctx)} if (${skOk} && ${skEnd} > ${curV}) { ${curV} = ${skEnd}; ${advV} = true }`,
1592
+ `${ind(ctx)}}`
1237
1593
  );
1238
1594
  }
1239
1595
  stmts.push(`${ind(ctx)}if (!${advV}) ${curV}++`);
@@ -1245,14 +1601,54 @@ function emitScanTo(def, ctx, pos) {
1245
1601
  if (!def.orEOF) {
1246
1602
  const sentDef = def.sentinel._def;
1247
1603
  const expectedStr = sentDef.tag === "literal" ? JSON.stringify([JSON.stringify(sentDef.value)]) : `["sentinel"]`;
1248
- stmts.push(
1249
- `${ind(ctx)}if (!${foundV}) return { ok: false, expected: ${expectedStr}, span: { start: ${pos}, end: ${curV} } }`
1250
- );
1604
+ stmts.push(`${ind(ctx)}if (!${foundV}) ${failArr({ ...ctx, indent: 0 }, expectedStr, pos)}`);
1251
1605
  }
1252
1606
  const valV = v(ctx);
1253
1607
  stmts.push(`${ind(ctx)}const ${valV} = input.slice(${pos}, ${curV})`);
1608
+ if (ctx.capturing) {
1609
+ const cap = emitLeafCapture(ctx, valV, pos, curV).map((s) => " " + s);
1610
+ stmts.push(`${ind(ctx)}if (${curV} > ${pos}) {`, ...cap, `${ind(ctx)}}`);
1611
+ }
1254
1612
  return { stmts, valueVar: valV, endVar: curV };
1255
1613
  }
1614
+ function emitNot(def, ctx, pos) {
1615
+ const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
1616
+ return {
1617
+ stmts: [
1618
+ ...stmts,
1619
+ `${ind(ctx)}if (${okVar}) ${failStmt({ ...ctx, indent: 0 }, '"not"', pos).trim()}`
1620
+ ],
1621
+ valueVar: "null",
1622
+ endVar: pos
1623
+ };
1624
+ }
1625
+ function emitNode(def, ctx, pos) {
1626
+ const fnIdx = ctx.buildFns.length;
1627
+ ctx.buildFns.push(def.build);
1628
+ ctx.buildSrcs.push(def.buildSrc ?? null);
1629
+ const i = ind(ctx);
1630
+ const chV = v(ctx, "_ch");
1631
+ const rawV = v(ctx, "_raw");
1632
+ const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st");
1633
+ const stmts = [
1634
+ `${i}const ${chV} = [], ${rawV} = []`,
1635
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia`,
1636
+ `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true`
1637
+ ];
1638
+ const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
1639
+ stmts.push(...innerStmts);
1640
+ stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}`);
1641
+ stmts.push(`${i}if (!${okVar}) ${failStmt({ ...ctx, indent: 0 }, '"node"', pos).trim()}`);
1642
+ const ndV = v(ctx, "_nd");
1643
+ stmts.push(
1644
+ `${i}const ${ndV} = _build[${fnIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} })`,
1645
+ // Record into the caller's collectors (the node in children; the node, or a
1646
+ // spanned leaf if build collapsed to a non-node value, in rawChildren).
1647
+ `${i}if (${sc}) ${sc}.push(${ndV})`,
1648
+ `${i}if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && ${ndV}._tag === 'node') ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : '', span: { start: ${pos}, end: ${endVar} } })`
1649
+ );
1650
+ return { stmts, valueVar: ndV, endVar };
1651
+ }
1256
1652
  function emitRuntimeFallback(parser2, ctx, pos) {
1257
1653
  const idx = ctx.runtimeParsers.length;
1258
1654
  ctx.runtimeParsers.push(parser2);
@@ -1261,7 +1657,7 @@ function emitRuntimeFallback(parser2, ctx, pos) {
1261
1657
  const ev = v(ctx, "_rte");
1262
1658
  const stmts = [
1263
1659
  `${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
1264
- `${ind(ctx)}if (!${rv}.ok) return ${rv}`,
1660
+ `${ind(ctx)}if (!${rv}.ok) ${failStmt({ ...ctx, indent: 0 }, '"runtime"', pos).trim()}`,
1265
1661
  `${ind(ctx)}const ${vv} = ${rv}.value`,
1266
1662
  `${ind(ctx)}const ${ev} = ${rv}.span.end`
1267
1663
  ];
@@ -1279,9 +1675,12 @@ function emitLazy(p, def, ctx, pos) {
1279
1675
  return emitRuntimeFallback(p, ctx, pos);
1280
1676
  }
1281
1677
  const savedIndent = ctx.indent;
1678
+ const savedFailLabel = ctx.failLabel;
1282
1679
  ctx.indent = 1;
1680
+ ctx.failLabel = void 0;
1283
1681
  const r = emit(resolved, ctx, "_pos");
1284
1682
  ctx.indent = savedIndent;
1683
+ ctx.failLabel = savedFailLabel;
1285
1684
  ctx.namedFnDecls.push([
1286
1685
  `function ${fnName2}(input, _pos, _ctx) {`,
1287
1686
  ...r.stmts,
@@ -1296,7 +1695,7 @@ function emitLazy(p, def, ctx, pos) {
1296
1695
  return {
1297
1696
  stmts: [
1298
1697
  `${ind(ctx)}const ${rv} = ${fnName}(input, ${pos}, _ctx)`,
1299
- `${ind(ctx)}if (!${rv}.ok) return ${rv}`,
1698
+ `${ind(ctx)}if (!${rv}.ok) ${failStmt({ ...ctx, indent: 0 }, '"parser"', pos).trim()}`,
1300
1699
  `${ind(ctx)}const ${vv} = ${rv}.value`,
1301
1700
  `${ind(ctx)}const ${ev} = ${rv}.span.end`
1302
1701
  ],
@@ -1304,6 +1703,33 @@ function emitLazy(p, def, ctx, pos) {
1304
1703
  endVar: ev
1305
1704
  };
1306
1705
  }
1706
+ function emitRecover(def, ctx, pos) {
1707
+ const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
1708
+ const ind0 = ind(ctx);
1709
+ const scanV = v(ctx, "_sc");
1710
+ const errV = v(ctx, "_err");
1711
+ const savedIndent = ctx.indent;
1712
+ ctx.indent += 2;
1713
+ const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, ctx, scanV);
1714
+ ctx.indent = savedIndent;
1715
+ const stmts = [
1716
+ ...innerStmts,
1717
+ `${ind0}if (!${okVar}) {`,
1718
+ `${ind0} let ${scanV} = ${pos}`,
1719
+ `${ind0} while (${scanV} < input.length) {`,
1720
+ ...sentStmts,
1721
+ `${ind0} if (${sentOk}) break`,
1722
+ `${ind0} ${scanV}++`,
1723
+ `${ind0} }`,
1724
+ `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: [] }`,
1725
+ `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
1726
+ `${ind0} ${valVar} = ${errV}`,
1727
+ `${ind0} ${endVar} = ${scanV}`,
1728
+ `${ind0} ${okVar} = true`,
1729
+ `${ind0}}`
1730
+ ];
1731
+ return { stmts, valueVar: valVar, endVar };
1732
+ }
1307
1733
  function emit(p, ctx, pos) {
1308
1734
  const def = p._def;
1309
1735
  switch (def.tag) {
@@ -1326,6 +1752,7 @@ function emit(p, ctx, pos) {
1326
1752
  const inner = emit(def.parser, ctx, pos);
1327
1753
  const fnIdx = ctx.mapFns.length;
1328
1754
  ctx.mapFns.push(def.fn);
1755
+ ctx.mapFnSrcs.push(def.fnSrc ?? null);
1329
1756
  const mv = v(ctx, "_mapped");
1330
1757
  return {
1331
1758
  stmts: [
@@ -1366,15 +1793,21 @@ function emit(p, ctx, pos) {
1366
1793
  else ctx.activeTrivia = savedTrivia;
1367
1794
  return r;
1368
1795
  }
1796
+ case "not":
1797
+ return emitNot(def, ctx, pos);
1798
+ case "node":
1799
+ return emitNode(def, ctx, pos);
1369
1800
  case "scanTo":
1370
1801
  return emitScanTo(def, ctx, pos);
1802
+ case "recover":
1803
+ return emitRecover(def, ctx, pos);
1371
1804
  case "guard": {
1372
1805
  const fnIdx = ctx.mapFns.length;
1373
1806
  ctx.mapFns.push(def.predicate);
1374
1807
  const vv = v(ctx);
1375
1808
  return {
1376
1809
  stmts: [
1377
- `${ind(ctx)}if (!_mf[${fnIdx}](_ctx.user)) ${failStmt({ ...ctx, indent: 0 }, '"gate"', pos).trim()}`,
1810
+ `${ind(ctx)}if (!_mf[${fnIdx}](_ctx.state)) ${failStmt({ ...ctx, indent: 0 }, '"gate"', pos).trim()}`,
1378
1811
  `${ind(ctx)}const ${vv} = null`
1379
1812
  ],
1380
1813
  valueVar: vv,
@@ -1390,9 +1823,12 @@ function emit(p, ctx, pos) {
1390
1823
  const fnName = `_wcf${ctx.namedParsers.size}`;
1391
1824
  ctx.namedParsers.set(innerParser, fnName);
1392
1825
  const savedIndent = ctx.indent;
1826
+ const savedFailLabel = ctx.failLabel;
1393
1827
  ctx.indent = 1;
1828
+ ctx.failLabel = void 0;
1394
1829
  const innerR = emit(innerParser, ctx, "_pos");
1395
1830
  ctx.indent = savedIndent;
1831
+ ctx.failLabel = savedFailLabel;
1396
1832
  ctx.namedFnDecls.push([
1397
1833
  `function ${fnName}(input, _pos, _ctx) {`,
1398
1834
  ...innerR.stmts,
@@ -1406,8 +1842,8 @@ function emit(p, ctx, pos) {
1406
1842
  const ev = v(ctx, "_wce");
1407
1843
  return {
1408
1844
  stmts: [
1409
- `${ind(ctx)}const ${rv} = ${fn}(input, ${pos}, { ..._ctx, user: _mf[${evIdx}]() })`,
1410
- `${ind(ctx)}if (!${rv}.ok) return ${rv}`,
1845
+ `${ind(ctx)}const ${rv} = ${fn}(input, ${pos}, { ..._ctx, state: _mf[${evIdx}]() })`,
1846
+ `${ind(ctx)}if (!${rv}.ok) ${failStmt({ ...ctx, indent: 0 }, '"withCtx"', pos).trim()}`,
1411
1847
  `${ind(ctx)}const ${vv} = ${rv}.value`,
1412
1848
  `${ind(ctx)}const ${ev} = ${rv}.span.end`
1413
1849
  ],
@@ -1419,16 +1855,61 @@ function emit(p, ctx, pos) {
1419
1855
  return emitRuntimeFallback(p, ctx, pos);
1420
1856
  }
1421
1857
  }
1422
- function compile(parser2) {
1858
+ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
1859
+ if (seen.has(p)) return false;
1860
+ seen.add(p);
1861
+ const d = p._def;
1862
+ switch (d.tag) {
1863
+ case "node":
1864
+ return true;
1865
+ case "lazy": {
1866
+ try {
1867
+ return hasNodeDef(d.thunk(), seen);
1868
+ } catch {
1869
+ return false;
1870
+ }
1871
+ }
1872
+ case "grammar":
1873
+ case "trivia":
1874
+ case "optional":
1875
+ case "many":
1876
+ case "oneOrMore":
1877
+ case "not":
1878
+ case "transform":
1879
+ return hasNodeDef(d.parser, seen);
1880
+ case "skip":
1881
+ return hasNodeDef(d.main, seen) || hasNodeDef(d.skipped, seen);
1882
+ case "sequence":
1883
+ case "choice":
1884
+ return d.parsers.some((x) => hasNodeDef(x, seen));
1885
+ case "sepBy":
1886
+ return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
1887
+ case "scanTo":
1888
+ return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen));
1889
+ case "recover":
1890
+ return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
1891
+ case "withCtx":
1892
+ return hasNodeDef(d.parser, seen);
1893
+ default:
1894
+ return false;
1895
+ }
1896
+ }
1897
+ function compile(parser2, mapFnSources) {
1423
1898
  const ctx = {
1424
1899
  vars: 0,
1425
1900
  indent: 1,
1426
1901
  regexDecls: [],
1902
+ regexMap: /* @__PURE__ */ new Map(),
1427
1903
  mapFns: [],
1904
+ mapFnSrcs: [],
1905
+ buildFns: [],
1906
+ buildSrcs: [],
1428
1907
  runtimeParsers: [],
1429
1908
  needsCollator: false,
1430
1909
  namedParsers: /* @__PURE__ */ new Map(),
1431
- namedFnDecls: []
1910
+ triviaCaptureNames: /* @__PURE__ */ new Map(),
1911
+ namedFnDecls: [],
1912
+ capturing: hasNodeDef(parser2)
1432
1913
  };
1433
1914
  const r = emit(parser2, ctx, "_pos");
1434
1915
  const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
@@ -1437,13 +1918,13 @@ function compile(parser2) {
1437
1918
  ...ctx.regexDecls,
1438
1919
  "",
1439
1920
  ...ctx.namedFnDecls,
1440
- `${collatorDecl}function _parse(input, _pos, _rp, _mf, _ctx) {`,
1921
+ `${collatorDecl}function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
1441
1922
  ` let pos = _pos`,
1442
1923
  ...r.stmts,
1443
1924
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
1444
1925
  `}`
1445
1926
  ].join("\n");
1446
- const fn = new Function("input", "_pos", "_rp", "_mf", "_ctx", [
1927
+ const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
1447
1928
  ...ctx.regexDecls,
1448
1929
  collatorDecl,
1449
1930
  ...ctx.namedFnDecls,
@@ -1452,17 +1933,27 @@ function compile(parser2) {
1452
1933
  `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
1453
1934
  ].join("\n"));
1454
1935
  const defaultCtx = { trackLines: false };
1455
- const canInline = ctx.runtimeParsers.length === 0 && ctx.mapFns.length === 0;
1456
- const inlineExpression = canInline ? buildInlineExpression(ctx, r, collatorDecl) : null;
1936
+ const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
1937
+ const effectiveSources = mapFnSources ?? derivedSrcs;
1938
+ const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
1939
+ const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
1940
+ const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
1941
+ const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
1942
+ const inlineExpression = canInline ? buildInlineExpression(ctx, r, collatorDecl, effectiveSources, buildSources) : null;
1457
1943
  return {
1458
1944
  source,
1459
1945
  inlineExpression,
1460
1946
  parse(input, pos = 0) {
1461
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, defaultCtx);
1947
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
1948
+ },
1949
+ parseWithErrors(input, pos = 0) {
1950
+ const errors = [];
1951
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
1952
+ return { ...result, errors };
1462
1953
  }
1463
1954
  };
1464
1955
  }
1465
- function buildInlineExpression(ctx, r, collatorDecl) {
1956
+ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
1466
1957
  const bodyLines = [
1467
1958
  ` let pos = _pos`,
1468
1959
  ...r.stmts.map((s) => ` ${s}`),
@@ -1473,14 +1964,16 @@ function buildInlineExpression(ctx, r, collatorDecl) {
1473
1964
  ...bodyLines,
1474
1965
  `}`
1475
1966
  ].join("\n");
1476
- const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0;
1477
- if (!needsWrapper) {
1478
- return innerFn;
1479
- }
1967
+ const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
1968
+ const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
1969
+ const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl;
1970
+ if (!needsWrapper) return innerFn;
1480
1971
  return [
1481
1972
  `/* @__PURE__ */ (() => {`,
1482
1973
  ...ctx.regexDecls.map((d) => ` ${d}`),
1483
1974
  collatorDecl ? ` ${collatorDecl.trim()}` : "",
1975
+ mfDecl,
1976
+ buildDecl,
1484
1977
  ...ctx.namedFnDecls.flatMap((f) => f.split("\n").map((l) => ` ${l}`)),
1485
1978
  ` return ${innerFn}`,
1486
1979
  `})()`
@@ -1499,7 +1992,7 @@ function guard(predicate) {
1499
1992
  _meta: meta,
1500
1993
  _def: { tag: "guard", predicate },
1501
1994
  parse(_input, pos, ctx) {
1502
- if (predicate(ctx.user))
1995
+ if (predicate(ctx.state))
1503
1996
  return { ok: true, value: null, span: { start: pos, end: pos } };
1504
1997
  return { ok: false, expected: ["guard"], span: { start: pos, end: pos } };
1505
1998
  }
@@ -1518,7 +2011,7 @@ function withCtx(extra, parser2) {
1518
2011
  _meta: meta,
1519
2012
  _def: { tag: "withCtx", extra, parser: parser2 },
1520
2013
  parse(input, pos, ctx) {
1521
- return parser2.parse(input, pos, { ...ctx, user: extra });
2014
+ return parser2.parse(input, pos, { ...ctx, state: extra });
1522
2015
  }
1523
2016
  };
1524
2017
  }
@@ -1547,6 +2040,7 @@ function recover(parser2, sentinel) {
1547
2040
  span: { start: pos, end: scanPos },
1548
2041
  expected: result.expected
1549
2042
  };
2043
+ ctx._errors?.push(error);
1550
2044
  return { ok: true, value: error, span: { start: pos, end: scanPos } };
1551
2045
  }
1552
2046
  };
@@ -1555,6 +2049,21 @@ function isParseError(value) {
1555
2049
  return typeof value === "object" && value !== null && value._tag === "parseError";
1556
2050
  }
1557
2051
 
2052
+ // src/combinators/completions.ts
2053
+ function completionsAt(parser2, input, offset) {
2054
+ const probe = { offset, best: null };
2055
+ const ctx = { trackLines: false, _probe: probe };
2056
+ const result = parser2.parse(input.slice(0, offset), 0, ctx);
2057
+ if (result.ok) return [];
2058
+ const best = deeperFail(probe.best, result);
2059
+ return best?.expected ?? [];
2060
+ }
2061
+ function deeperFail(a, b) {
2062
+ if (!a) return b;
2063
+ if (!b) return a;
2064
+ return a.span.start >= b.span.start ? a : b;
2065
+ }
2066
+
1558
2067
  // src/combinators/scanTo.ts
1559
2068
  function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
1560
2069
  const meta = {
@@ -1568,14 +2077,23 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
1568
2077
  _def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
1569
2078
  parse(input, pos, ctx) {
1570
2079
  let cur = pos;
2080
+ const probeCtx = { trackLines: ctx.trackLines, state: ctx.state };
2081
+ const emit2 = (end) => {
2082
+ if (end > pos && (ctx._cstLeaves || ctx._cstRawChildren)) {
2083
+ const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
2084
+ if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
2085
+ if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
2086
+ }
2087
+ };
1571
2088
  while (cur < input.length) {
1572
- const s = sentinel.parse(input, cur, ctx);
2089
+ const s = sentinel.parse(input, cur, probeCtx);
1573
2090
  if (s.ok) {
2091
+ emit2(cur);
1574
2092
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
1575
2093
  }
1576
2094
  let advanced = false;
1577
2095
  for (const skipper of skip2) {
1578
- const r = skipper.parse(input, cur, ctx);
2096
+ const r = skipper.parse(input, cur, probeCtx);
1579
2097
  if (r.ok && r.span.end > cur) {
1580
2098
  cur = r.span.end;
1581
2099
  advanced = true;
@@ -1585,6 +2103,7 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
1585
2103
  if (!advanced) cur++;
1586
2104
  }
1587
2105
  if (orEOF) {
2106
+ emit2(cur);
1588
2107
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
1589
2108
  }
1590
2109
  const sentDef = sentinel._def;
@@ -1595,19 +2114,23 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
1595
2114
  }
1596
2115
  function balanced(open, close, options = {}) {
1597
2116
  const inner = scanTo(literal(close), options);
1598
- return transform(
2117
+ const combi = transform(
1599
2118
  sequence(literal(open), inner, literal(close)),
1600
2119
  ([o, content, c]) => o + content + c
1601
2120
  );
2121
+ if (combi._def.tag === "transform") {
2122
+ combi._def.fnSrc = "([o, content, c]) => o + content + c";
2123
+ }
2124
+ return combi;
1602
2125
  }
1603
2126
 
1604
2127
  // src/cst/incremental.ts
1605
2128
  function isNode(x) {
1606
2129
  return typeof x === "object" && x !== null && x._tag === "node";
1607
2130
  }
1608
- function findContaining(node, pos, path = []) {
1609
- for (let i = 0; i < node.children.length; i++) {
1610
- const child = node.children[i];
2131
+ function findContaining(node2, pos, path = []) {
2132
+ for (let i = 0; i < node2.children.length; i++) {
2133
+ const child = node2.children[i];
1611
2134
  if (!isNode(child)) continue;
1612
2135
  if (child.span.start <= pos && pos < child.span.end) {
1613
2136
  return findContaining(child, pos, [...path, i]) ?? { node: child, path: [...path, i] };
@@ -1636,22 +2159,29 @@ function replaceAtPath(grammar, root, path, newNode) {
1636
2159
  var ParseDocImpl = class _ParseDocImpl {
1637
2160
  _parser;
1638
2161
  _ruleName;
2162
+ _trivia;
2163
+ _captureTrivia;
1639
2164
  tree;
1640
2165
  errors;
1641
2166
  input;
1642
- constructor(parser2, ruleName, tree, errors, input) {
2167
+ /** Offset reached by the top-level rule. < input.length means input was left unconsumed. */
2168
+ consumedEnd;
2169
+ constructor(parser2, ruleName, tree, errors, input, trivia2, captureTrivia, consumedEnd = 0) {
1643
2170
  this._parser = parser2;
1644
2171
  this._ruleName = ruleName;
2172
+ this._trivia = trivia2;
2173
+ this._captureTrivia = captureTrivia;
1645
2174
  this.tree = tree;
1646
2175
  this.errors = errors;
1647
2176
  this.input = input;
2177
+ this.consumedEnd = consumedEnd;
1648
2178
  }
1649
2179
  edit(from, to, replacement) {
1650
2180
  const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
1651
- if (!this.tree) return makeParseDoc(this._parser, this._ruleName, newInput);
2181
+ if (!this.tree) return makeParseDoc(this._parser, this._ruleName, newInput, this._trivia, this._captureTrivia);
1652
2182
  const delta = replacement.length - (to - from);
1653
2183
  const found = findContaining(this.tree, from);
1654
- if (!found) return makeParseDoc(this._parser, this._ruleName, newInput);
2184
+ if (!found) return makeParseDoc(this._parser, this._ruleName, newInput, this._trivia, this._captureTrivia);
1655
2185
  const ancestors = ancestorsAt(this.tree, found.path);
1656
2186
  const candidates = [found];
1657
2187
  const pathCopy = [...found.path];
@@ -1660,32 +2190,66 @@ var ParseDocImpl = class _ParseDocImpl {
1660
2190
  candidates.push({ node: ancestors[i + 1], path: [...pathCopy] });
1661
2191
  }
1662
2192
  for (const candidate of candidates) {
1663
- const { node, path } = candidate;
1664
- const expectedEnd = node.span.end + delta;
1665
- const parser2 = this._parser.rule(node.type);
1666
- const ctx = { trackLines: false, user: node.savedContext };
1667
- const r = parser2.parse(newInput, node.span.start, ctx);
2193
+ const { node: node2, path } = candidate;
2194
+ const expectedEnd = node2.span.end + delta;
2195
+ const parser2 = this._parser.rule(node2.type);
2196
+ const ctx = { trackLines: false, state: node2.state, ...this._trivia !== void 0 ? { trivia: this._trivia } : {}, ...this._captureTrivia ? { captureTrivia: true } : {} };
2197
+ const r = parser2.parse(newInput, node2.span.start, ctx);
1668
2198
  if (!r.ok) continue;
1669
2199
  if (r.span.end === expectedEnd) {
1670
2200
  const newTree = replaceAtPath(this._parser, this.tree, path, r.value);
1671
- return new _ParseDocImpl(this._parser, this._ruleName, newTree, [], newInput);
2201
+ return new _ParseDocImpl(this._parser, this._ruleName, newTree, [], newInput, this._trivia, this._captureTrivia);
1672
2202
  }
1673
2203
  }
1674
- return makeParseDoc(this._parser, this._ruleName, newInput);
2204
+ return makeParseDoc(this._parser, this._ruleName, newInput, this._trivia, this._captureTrivia);
1675
2205
  }
1676
2206
  };
1677
- function makeParseDoc(parser2, ruleName, input) {
1678
- const ctx = { trackLines: false };
2207
+ function makeParseDoc(parser2, ruleName, input, trivia2, captureTrivia, triviaLog) {
2208
+ const ctx = {
2209
+ trackLines: false,
2210
+ ...trivia2 !== void 0 ? { trivia: trivia2 } : {},
2211
+ ...captureTrivia ? { captureTrivia: true } : {},
2212
+ ...triviaLog !== void 0 ? { _triviaLog: triviaLog } : {}
2213
+ };
1679
2214
  const r = parser2.rule(ruleName).parse(input, 0, ctx);
1680
2215
  if (r.ok) {
1681
- return new ParseDocImpl(parser2, ruleName, r.value, [], input);
2216
+ return new ParseDocImpl(parser2, ruleName, r.value, [], input, trivia2, captureTrivia, r.span.end);
1682
2217
  }
1683
- return new ParseDocImpl(parser2, ruleName, null, [{ ok: false, expected: r.expected, span: r.span }], input);
2218
+ return new ParseDocImpl(parser2, ruleName, null, [{ ok: false, expected: r.expected, span: r.span }], input, trivia2, captureTrivia, r.span.start);
1684
2219
  }
1685
2220
 
1686
2221
  // src/cst/grammar.ts
1687
2222
  var Parser = class {
1688
2223
  _built = false;
2224
+ /** Set in a subclass to enable automatic trivia skipping between sequence terms. */
2225
+ _trivia;
2226
+ /**
2227
+ * Set in a subclass to make the rule name optional in `parse()`.
2228
+ * When defined, `parse(input)` is equivalent to `parse(defaultRule, input)`.
2229
+ */
2230
+ _defaultRule;
2231
+ /**
2232
+ * Set `true` in a subclass to record trivia (whitespace/comments) consumed
2233
+ * between terms into each node's rawChildren as separate CSTTrivia tokens.
2234
+ * Requires `_trivia` to be set. Default: trivia is skipped without recording.
2235
+ */
2236
+ _captureTrivia;
2237
+ /**
2238
+ * When set, makeParseDoc injects this array as ctx._triviaLog so each
2239
+ * scanTrivia.commit() pushes [runStart, runEnd] pairs into it.
2240
+ * Reset to [] before each parse() call in any subclass that uses it.
2241
+ */
2242
+ _triviaLog;
2243
+ /**
2244
+ * Optional map of public entry-point aliases → actual rule names. Lets callers
2245
+ * `parse('value', src)` when the grammar's rule is `ValueList`, removing
2246
+ * per-adapter name-translation tables.
2247
+ */
2248
+ _aliases;
2249
+ /** Resolve an alias to its target rule name (identity if not aliased). */
2250
+ _resolveRule(name) {
2251
+ return this._aliases?.[name] ?? name;
2252
+ }
1689
2253
  _build() {
1690
2254
  if (this._built) return;
1691
2255
  this._built = true;
@@ -1725,8 +2289,8 @@ var Parser = class {
1725
2289
  * grammar is whitespace-sensitive (e.g. CSS descendant vs adjacent combinators).
1726
2290
  * The default implementation ignores `rawChildren`.
1727
2291
  */
1728
- buildNode(type, span, children, savedContext, _rawChildren) {
1729
- return { _tag: "node", type, span, children, savedContext };
2292
+ buildNode(type, span, children, state, _rawChildren) {
2293
+ return { _tag: "node", type, span, children, state };
1730
2294
  }
1731
2295
  /** Wrap an inner combinator so it produces a CSTNode on each match. */
1732
2296
  _makeNodeParser(type, inner) {
@@ -1741,7 +2305,7 @@ var Parser = class {
1741
2305
  _meta: meta,
1742
2306
  _def: { tag: "unknown" },
1743
2307
  parse(input, pos, ctx) {
1744
- const savedContext = ctx.user !== void 0 ? Object.assign({}, ctx.user) : void 0;
2308
+ const state = ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
1745
2309
  const children = [];
1746
2310
  const rawChildren = [];
1747
2311
  const innerCtx = {
@@ -1752,31 +2316,34 @@ var Parser = class {
1752
2316
  };
1753
2317
  const r = inner.parse(input, pos, innerCtx);
1754
2318
  if (!r.ok) return r;
1755
- const node = self.buildNode(type, r.span, children, savedContext, rawChildren);
1756
- if (ctx._cstChildren) ctx._cstChildren.push(node);
1757
- if (ctx._cstRawChildren) ctx._cstRawChildren.push(node);
1758
- return { ok: true, value: node, span: r.span };
2319
+ const node2 = self.buildNode(type, r.span, children, state, rawChildren);
2320
+ const isNodeLike = typeof node2 === "object" && node2 !== null && node2._tag === "node";
2321
+ if (ctx._cstChildren) ctx._cstChildren.push(node2);
2322
+ if (ctx._cstRawChildren) ctx._cstRawChildren.push(
2323
+ isNodeLike ? node2 : { _tag: "leaf", value: typeof node2 === "string" ? node2 : "", span: r.span }
2324
+ );
2325
+ return { ok: true, value: node2, span: r.span };
1759
2326
  }
1760
2327
  };
1761
2328
  }
1762
2329
  /** Reconstruct a node with a new children array (used by IncrementalParser). */
1763
- rebuild(node, newChildren) {
1764
- return this.buildNode(node.type, node.span, newChildren, node.savedContext, []);
2330
+ rebuild(node2, newChildren) {
2331
+ return this.buildNode(node2.type, node2.span, newChildren, node2.state, []);
1765
2332
  }
1766
- /**
1767
- * Parse input starting from a named rule, returning a ParseDoc.
1768
- * The doc carries the tree, any parse errors, and an edit() method
1769
- * for incremental re-parsing on subsequent changes.
1770
- *
1771
- * const doc = css.parse('Stylesheet', src)
1772
- * doc.tree // the CST root, or null on failure
1773
- * doc.errors // ParseFail[], empty on success
1774
- *
1775
- * // In an editor — just keep calling edit():
1776
- * const doc2 = doc.edit(newSrc, changeStart, changeEnd)
1777
- */
1778
- parse(ruleName, input) {
1779
- return makeParseDoc(this, ruleName, input);
2333
+ parse(ruleNameOrInput, input) {
2334
+ let ruleName;
2335
+ let src;
2336
+ if (input === void 0) {
2337
+ if (this._defaultRule === void 0) {
2338
+ throw new Error("parse(input) requires a _defaultRule to be set on the grammar");
2339
+ }
2340
+ ruleName = this._defaultRule;
2341
+ src = ruleNameOrInput;
2342
+ } else {
2343
+ ruleName = ruleNameOrInput;
2344
+ src = input;
2345
+ }
2346
+ return makeParseDoc(this, this._resolveRule(ruleName), src, this._trivia, this._captureTrivia, this._triviaLog);
1780
2347
  }
1781
2348
  /**
1782
2349
  * Get the compiled Combinator for a named rule.
@@ -1784,7 +2351,8 @@ var Parser = class {
1784
2351
  */
1785
2352
  rule(name) {
1786
2353
  this._build();
1787
- const p = this[name];
2354
+ const resolved = this._resolveRule(name);
2355
+ const p = this[resolved];
1788
2356
  if (!p) throw new Error(`No rule '${String(name)}' on this parser`);
1789
2357
  return p;
1790
2358
  }
@@ -1792,4 +2360,168 @@ var Parser = class {
1792
2360
  function isCombinator(val) {
1793
2361
  return val !== null && typeof val === "object" && "_tag" in val;
1794
2362
  }
2363
+
2364
+ // src/functional/doc.ts
2365
+ function isNode2(x) {
2366
+ return typeof x === "object" && x !== null && x._tag === "node";
2367
+ }
2368
+ function findContaining2(node2, pos, path = []) {
2369
+ for (let i = 0; i < node2.children.length; i++) {
2370
+ const child = node2.children[i];
2371
+ if (!isNode2(child)) continue;
2372
+ if (child.span.start <= pos && pos < child.span.end) {
2373
+ const inner = findContaining2(child, pos, [...path, i]);
2374
+ return inner ?? { node: child, path: [...path, i] };
2375
+ }
2376
+ }
2377
+ return null;
2378
+ }
2379
+ function ancestorsAt2(root, path) {
2380
+ const ancestors = [root];
2381
+ let cur = root;
2382
+ for (const idx of path.slice(0, -1)) {
2383
+ const child = cur.children[idx];
2384
+ if (!child || !isNode2(child)) break;
2385
+ ancestors.push(child);
2386
+ cur = child;
2387
+ }
2388
+ return ancestors;
2389
+ }
2390
+ function replaceAtPath2(rebuild, root, path, newNode) {
2391
+ if (path.length === 0) return newNode;
2392
+ const [idx, ...rest] = path;
2393
+ const newChildren = [...root.children];
2394
+ newChildren[idx] = rest.length === 0 ? newNode : replaceAtPath2(rebuild, root.children[idx], rest, newNode);
2395
+ return rebuild(root, newChildren);
2396
+ }
2397
+ function defaultRebuild(node2, children) {
2398
+ return { ...node2, children };
2399
+ }
2400
+ var FunctionalDocImpl = class _FunctionalDocImpl {
2401
+ _registry;
2402
+ _rootRule;
2403
+ _opts;
2404
+ tree;
2405
+ errors;
2406
+ input;
2407
+ constructor(registry, rootRule, opts, tree, errors, input) {
2408
+ this._registry = registry;
2409
+ this._rootRule = rootRule;
2410
+ this._opts = opts;
2411
+ this.tree = tree;
2412
+ this.errors = errors;
2413
+ this.input = input;
2414
+ }
2415
+ edit(from, to, replacement) {
2416
+ const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
2417
+ const reparse = () => makeFunctionalDoc(this._registry, this._rootRule, newInput, this._opts);
2418
+ if (!this.tree) return reparse();
2419
+ const delta = replacement.length - (to - from);
2420
+ const found = findContaining2(this.tree, from);
2421
+ if (!found) return reparse();
2422
+ const ancestors = ancestorsAt2(this.tree, found.path);
2423
+ const candidates = [found];
2424
+ const pathCopy = [...found.path];
2425
+ for (let i = ancestors.length - 2; i >= 0; i--) {
2426
+ pathCopy.pop();
2427
+ candidates.push({ node: ancestors[i + 1], path: [...pathCopy] });
2428
+ }
2429
+ const rebuild = this._opts.rebuild ?? defaultRebuild;
2430
+ for (const { node: node2, path } of candidates) {
2431
+ const ruleFn = this._registry[node2.type];
2432
+ if (!ruleFn) continue;
2433
+ const ctx = { trackLines: false, state: node2.state };
2434
+ const r = ruleFn(newInput, node2.span.start, ctx);
2435
+ if (!r.ok) continue;
2436
+ if (r.span.end === node2.span.end + delta) {
2437
+ const newTree = replaceAtPath2(rebuild, this.tree, path, r.value);
2438
+ return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
2439
+ }
2440
+ }
2441
+ return reparse();
2442
+ }
2443
+ };
2444
+ function makeFunctionalDoc(registry, rootRule, input, opts = {}) {
2445
+ const ruleFn = registry[rootRule];
2446
+ if (!ruleFn) throw new Error(`No rule '${rootRule}' in registry`);
2447
+ const ctx = { trackLines: false, state: opts.state };
2448
+ const r = ruleFn(input, 0, ctx);
2449
+ if (r.ok) {
2450
+ return new FunctionalDocImpl(registry, rootRule, opts, r.value, [], input);
2451
+ }
2452
+ return new FunctionalDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
2453
+ }
2454
+
2455
+ // src/cst/trivia-index.ts
2456
+ function spanOf(c) {
2457
+ const s = c.span;
2458
+ return s && typeof s.start === "number" ? s : void 0;
2459
+ }
2460
+ function merge(map, key, run) {
2461
+ if (run.length === 0) return;
2462
+ const existing = map.get(key);
2463
+ if (existing) existing.push(...run);
2464
+ else map.set(key, [...run]);
2465
+ }
2466
+ function buildTriviaIndex(root, opts) {
2467
+ const before = /* @__PURE__ */ new Map();
2468
+ const after = /* @__PURE__ */ new Map();
2469
+ const visit = (node2) => {
2470
+ const raw = node2.children;
2471
+ if (!raw) return;
2472
+ let prevEnd;
2473
+ let i = 0;
2474
+ while (i < raw.length) {
2475
+ const c = raw[i];
2476
+ if (c._tag === "trivia") {
2477
+ const run = [];
2478
+ while (i < raw.length && raw[i]._tag === "trivia") {
2479
+ const t = raw[i];
2480
+ const sp2 = spanOf(t);
2481
+ if (sp2 && sp2.end > sp2.start) run.push({ value: t.value ?? "", span: sp2 });
2482
+ i++;
2483
+ }
2484
+ const next = i < raw.length ? spanOf(raw[i]) : void 0;
2485
+ if (next) merge(before, next.start, run);
2486
+ if (prevEnd !== void 0) merge(after, prevEnd, run);
2487
+ continue;
2488
+ }
2489
+ const sp = spanOf(c);
2490
+ if (sp) prevEnd = sp.end;
2491
+ if (c._tag === "node") visit(c);
2492
+ i++;
2493
+ }
2494
+ };
2495
+ if (root && typeof root === "object") visit(root);
2496
+ if (opts) {
2497
+ const rootSpan = spanOf(root);
2498
+ const re = new RegExp(opts.trivia.source, opts.trivia.flags.replace(/[gy]/g, "") + "y");
2499
+ const scanFrom = (from) => {
2500
+ const run = [];
2501
+ let pos = from;
2502
+ re.lastIndex = pos;
2503
+ let m = re.exec(opts.input);
2504
+ while (m && m.index === pos && m[0].length > 0) {
2505
+ run.push({ value: m[0], span: { start: pos, end: pos + m[0].length } });
2506
+ pos += m[0].length;
2507
+ re.lastIndex = pos;
2508
+ m = re.exec(opts.input);
2509
+ }
2510
+ return run;
2511
+ };
2512
+ if (rootSpan && rootSpan.start > 0) {
2513
+ const lead = scanFrom(0);
2514
+ if (lead.length) merge(before, rootSpan.start, lead);
2515
+ }
2516
+ const end = rootSpan ? rootSpan.end : 0;
2517
+ if (end < opts.input.length) {
2518
+ const trail = scanFrom(end);
2519
+ if (trail.length) {
2520
+ merge(after, end, trail);
2521
+ merge(before, end + trail.reduce((n, t) => n + (t.span.end - t.span.start), 0), trail);
2522
+ }
2523
+ }
2524
+ }
2525
+ return { before, after };
2526
+ }
1795
2527
  //# sourceMappingURL=index.cjs.map