parseman 0.2.0 → 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 +167 -126
  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 +1 -1
@@ -48,6 +48,51 @@ function mergeRanges(ranges) {
48
48
  return out;
49
49
  }
50
50
 
51
+ // src/combinators/ref.ts
52
+ function ref() {
53
+ let resolved = null;
54
+ const meta = {
55
+ firstSet: any(),
56
+ canMatchNewline: true,
57
+ isTrivia: false
58
+ };
59
+ const slot = {
60
+ _tag: "lazy",
61
+ _meta: meta,
62
+ _def: {
63
+ tag: "lazy",
64
+ thunk: () => {
65
+ if (!resolved) throw new Error("ref<T>() used before .define() was called");
66
+ return resolved;
67
+ }
68
+ },
69
+ parse(input, pos, ctx) {
70
+ if (!resolved) throw new Error("ref<T>() used before .define() was called");
71
+ return resolved.parse(input, pos, ctx);
72
+ },
73
+ define(p) {
74
+ if (resolved) throw new Error("ref<T>() already defined");
75
+ resolved = p;
76
+ }
77
+ };
78
+ return slot;
79
+ }
80
+
81
+ // src/combinators/probe.ts
82
+ function failAt(ctx, expected, pos) {
83
+ const r = { ok: false, expected, span: { start: pos, end: pos } };
84
+ const probe = ctx._probe;
85
+ if (probe !== void 0 && pos <= probe.offset) {
86
+ const best = probe.best;
87
+ if (best === null || pos > best.span.start) {
88
+ probe.best = r;
89
+ } else if (pos === best.span.start) {
90
+ probe.best = { ...best, expected: [...best.expected, ...expected] };
91
+ }
92
+ }
93
+ return r;
94
+ }
95
+
51
96
  // src/combinators/literal.ts
52
97
  var _collatorCache = null;
53
98
  function collator() {
@@ -77,21 +122,21 @@ function literal(value, opts = {}) {
77
122
  _tag: "literal",
78
123
  _meta: meta,
79
124
  _def: { tag: "literal", value, caseInsensitive },
80
- parse(input, pos, _ctx) {
125
+ parse(input, pos, ctx) {
81
126
  const end = pos + value.length;
82
127
  if (end > input.length) {
83
- return { ok: false, expected: [JSON.stringify(value)], span: { start: pos, end: pos } };
128
+ return failAt(ctx, [JSON.stringify(value)], pos);
84
129
  }
85
130
  const slice = input.slice(pos, end);
86
131
  const matched = caseInsensitive ? collator().compare(slice, value) === 0 : slice === value;
87
132
  if (matched) {
88
133
  const span = { start: pos, end };
89
134
  const leaf = { _tag: "leaf", value: slice, span };
90
- if (_ctx._cstLeaves) _ctx._cstLeaves.push(leaf);
91
- if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(leaf);
135
+ if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
136
+ if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
92
137
  return { ok: true, value: slice, span };
93
138
  }
94
- return { ok: false, expected: [JSON.stringify(value)], span: { start: pos, end: pos } };
139
+ return failAt(ctx, [JSON.stringify(value)], pos);
95
140
  }
96
141
  };
97
142
  }
@@ -106,38 +151,38 @@ function firstSetFromRegex(pattern) {
106
151
  return { firstSet: any(), canMatchNewline: true };
107
152
  }
108
153
  }
109
- function canBeEmpty(node) {
110
- if (!node) return true;
111
- switch (node.type) {
154
+ function canBeEmpty(node2) {
155
+ if (!node2) return true;
156
+ switch (node2.type) {
112
157
  case "Repetition": {
113
- const q = node.quantifier;
158
+ const q = node2.quantifier;
114
159
  return q.kind === "*" || q.kind === "?" || q.kind === "Range" && (q.from ?? 1) === 0;
115
160
  }
116
161
  case "Alternative": {
117
- const exprs = node.expressions;
162
+ const exprs = node2.expressions;
118
163
  return !exprs || exprs.length === 0 || exprs.every((e) => canBeEmpty(e));
119
164
  }
120
165
  case "Group":
121
- return canBeEmpty(node.expression);
166
+ return canBeEmpty(node2.expression);
122
167
  case "Disjunction":
123
- return canBeEmpty(node.left) || canBeEmpty(node.right);
168
+ return canBeEmpty(node2.left) || canBeEmpty(node2.right);
124
169
  default:
125
170
  return false;
126
171
  }
127
172
  }
128
- function extractFirstSet(node) {
129
- if (!node) return { firstSet: empty(), canMatchNewline: false };
130
- switch (node.type) {
173
+ function extractFirstSet(node2) {
174
+ if (!node2) return { firstSet: empty(), canMatchNewline: false };
175
+ switch (node2.type) {
131
176
  case "Disjunction": {
132
- const left = extractFirstSet(node.left);
133
- const right = extractFirstSet(node.right);
177
+ const left = extractFirstSet(node2.left);
178
+ const right = extractFirstSet(node2.right);
134
179
  return {
135
180
  firstSet: union(left.firstSet, right.firstSet),
136
181
  canMatchNewline: left.canMatchNewline || right.canMatchNewline
137
182
  };
138
183
  }
139
184
  case "Alternative": {
140
- const exprs = node.expressions;
185
+ const exprs = node2.expressions;
141
186
  if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
142
187
  let fs = empty();
143
188
  let canNL = false;
@@ -150,13 +195,13 @@ function extractFirstSet(node) {
150
195
  return { firstSet: fs, canMatchNewline: canNL };
151
196
  }
152
197
  case "Char": {
153
- const kind = node.kind;
198
+ const kind = node2.kind;
154
199
  if (kind === "simple") {
155
- const code = node.codePoint ?? node.value.codePointAt(0) ?? 0;
200
+ const code = node2.codePoint ?? node2.value.codePointAt(0) ?? 0;
156
201
  return { firstSet: fromRange(code, code), canMatchNewline: code === 10 };
157
202
  }
158
203
  if (kind === "meta") {
159
- const val = node.value;
204
+ const val = node2.value;
160
205
  if (val === ".") return { firstSet: any(), canMatchNewline: false };
161
206
  if (val === "d") return { firstSet: fromRange(48, 57), canMatchNewline: false };
162
207
  if (val === "D") return { firstSet: any(), canMatchNewline: true };
@@ -173,7 +218,7 @@ function extractFirstSet(node) {
173
218
  return { firstSet: any(), canMatchNewline: true };
174
219
  }
175
220
  case "CharacterClass": {
176
- const expressions = node.expressions;
221
+ const expressions = node2.expressions;
177
222
  let fs = empty();
178
223
  let canNL = false;
179
224
  for (const expr of expressions) {
@@ -188,14 +233,14 @@ function extractFirstSet(node) {
188
233
  canNL = canNL || r.canMatchNewline;
189
234
  }
190
235
  }
191
- if (node.negative) return { firstSet: any(), canMatchNewline: true };
236
+ if (node2.negative) return { firstSet: any(), canMatchNewline: true };
192
237
  return { firstSet: fs, canMatchNewline: canNL };
193
238
  }
194
239
  case "Repetition": {
195
- return extractFirstSet(node.expression);
240
+ return extractFirstSet(node2.expression);
196
241
  }
197
242
  case "Group": {
198
- return extractFirstSet(node.expression);
243
+ return extractFirstSet(node2.expression);
199
244
  }
200
245
  default:
201
246
  return { firstSet: any(), canMatchNewline: true };
@@ -226,7 +271,7 @@ function regex(pattern, flags = "") {
226
271
  anchored.lastIndex = pos;
227
272
  const m = anchored.exec(input);
228
273
  if (m === null) {
229
- return { ok: false, expected: [`/${source}/`], span: { start: pos, end: pos } };
274
+ return failAt(ctx, [`/${source}/`], pos);
230
275
  }
231
276
  const span = { start: pos, end: pos + m[0].length };
232
277
  const leaf = { _tag: "leaf", value: m[0], span };
@@ -237,6 +282,55 @@ function regex(pattern, flags = "") {
237
282
  };
238
283
  }
239
284
 
285
+ // src/combinators/trivia-skip.ts
286
+ var NOOP_COMMIT = () => {
287
+ };
288
+ function scanTrivia(input, cur, ctx) {
289
+ const triviaP = ctx.trivia;
290
+ if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
291
+ const log = ctx._triviaLog;
292
+ if (log !== void 0) {
293
+ const tr2 = triviaP.parse(input, cur, { trackLines: false, state: ctx.state });
294
+ if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
295
+ const end = tr2.span.end;
296
+ return {
297
+ end,
298
+ commit: () => {
299
+ log.push(cur, end);
300
+ }
301
+ };
302
+ }
303
+ if (ctx.captureTrivia && ctx._cstRawChildren) {
304
+ const sink = [];
305
+ const tr2 = triviaP.parse(input, cur, {
306
+ trackLines: ctx.trackLines,
307
+ state: ctx.state,
308
+ _cstLeaves: sink
309
+ });
310
+ if (tr2.ok && tr2.span.end > cur) {
311
+ const out = ctx._cstRawChildren;
312
+ return {
313
+ end: tr2.span.end,
314
+ commit: () => {
315
+ for (const tok of sink) {
316
+ if (tok._tag === "leaf" && tok.span.end > tok.span.start) {
317
+ out.push({ _tag: "trivia", value: tok.value, span: tok.span });
318
+ }
319
+ }
320
+ }
321
+ };
322
+ }
323
+ return { end: cur, commit: NOOP_COMMIT };
324
+ }
325
+ const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
326
+ return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
327
+ }
328
+ function consumeTrivia(input, cur, ctx) {
329
+ const scan = scanTrivia(input, cur, ctx);
330
+ scan.commit();
331
+ return scan.end;
332
+ }
333
+
240
334
  // src/combinators/sequence.ts
241
335
  function sequence(...parsers) {
242
336
  const meta = {
@@ -253,12 +347,19 @@ function sequence(...parsers) {
253
347
  let cur = pos;
254
348
  for (let i = 0; i < parsers.length; i++) {
255
349
  if (ctx.trivia && i > 0) {
256
- const tr = ctx.trivia.parse(input, cur, { trivia: ctx.trivia, trackLines: ctx.trackLines, user: ctx.user });
257
- if (tr.ok) {
258
- if (ctx._cstRawChildren && tr.span.end > tr.span.start)
259
- ctx._cstRawChildren.push({ _tag: "trivia", value: input.slice(tr.span.start, tr.span.end), span: tr.span });
260
- cur = tr.span.end;
350
+ const scan = scanTrivia(input, cur, ctx);
351
+ const raw = ctx._cstRawChildren;
352
+ const mark = raw ? raw.length : 0;
353
+ scan.commit();
354
+ const result2 = parsers[i].parse(input, scan.end, ctx);
355
+ if (!result2.ok) return result2;
356
+ if (result2.span.end > scan.end) {
357
+ cur = result2.span.end;
358
+ } else if (raw) {
359
+ raw.length = mark;
261
360
  }
361
+ values.push(result2.value);
362
+ continue;
262
363
  }
263
364
  const result = parsers[i].parse(input, cur, ctx);
264
365
  if (!result.ok) return result;
@@ -359,14 +460,22 @@ function choice(...args) {
359
460
  return { ok: false, expected, span: { start: pos, end: pos } };
360
461
  }
361
462
  for (let i = 0; i < parsers.length; i++) {
362
- if (gates[i] && !gates[i](ctx.user)) continue;
463
+ if (gates[i] && !gates[i](ctx.state)) continue;
464
+ const leavesLen = ctx._cstLeaves?.length;
465
+ const rawLen = ctx._cstRawChildren?.length;
363
466
  const result = parsers[i].parse(input, pos, ctx);
364
467
  if (!result.ok) {
468
+ if (leavesLen !== void 0 && ctx._cstLeaves) ctx._cstLeaves.length = leavesLen;
469
+ if (rawLen !== void 0 && ctx._cstRawChildren) ctx._cstRawChildren.length = rawLen;
365
470
  expected.push(...result.expected);
366
471
  continue;
367
472
  }
368
473
  const checks = autoNot[i];
369
- if (checks && autoNotFires(input, result.span.end, checks)) continue;
474
+ if (checks && autoNotFires(input, result.span.end, checks)) {
475
+ if (leavesLen !== void 0 && ctx._cstLeaves) ctx._cstLeaves.length = leavesLen;
476
+ if (rawLen !== void 0 && ctx._cstRawChildren) ctx._cstRawChildren.length = rawLen;
477
+ continue;
478
+ }
370
479
  return result;
371
480
  }
372
481
  return { ok: false, expected, span: { start: pos, end: pos } };
@@ -497,6 +606,30 @@ function areDisjoint(sets) {
497
606
  }
498
607
 
499
608
  // src/combinators/repeat.ts
609
+ function repItem(parser2, input, cur, ctx) {
610
+ const raw = ctx._cstRawChildren;
611
+ const mark = raw ? raw.length : 0;
612
+ let pos = cur;
613
+ if (ctx.trivia) {
614
+ const scan = scanTrivia(input, cur, ctx);
615
+ scan.commit();
616
+ pos = scan.end;
617
+ }
618
+ if (pos >= input.length) {
619
+ if (raw) raw.length = mark;
620
+ return "stop";
621
+ }
622
+ const result = parser2.parse(input, pos, ctx);
623
+ if (!result.ok) {
624
+ if (raw) raw.length = mark;
625
+ return { fail: result };
626
+ }
627
+ if (result.span.end === pos) {
628
+ if (raw) raw.length = mark;
629
+ return "stop";
630
+ }
631
+ return { value: result.value, end: result.span.end };
632
+ }
500
633
  function many(parser2) {
501
634
  const meta = {
502
635
  firstSet: parser2._meta.firstSet,
@@ -511,11 +644,10 @@ function many(parser2) {
511
644
  const values = [];
512
645
  let cur = pos;
513
646
  while (cur < input.length) {
514
- const result = parser2.parse(input, cur, ctx);
515
- if (!result.ok) break;
516
- if (result.span.end === cur) break;
517
- values.push(result.value);
518
- cur = result.span.end;
647
+ const item = repItem(parser2, input, cur, ctx);
648
+ if (item === "stop" || "fail" in item) break;
649
+ values.push(item.value);
650
+ cur = item.end;
519
651
  }
520
652
  return { ok: true, value: values, span: { start: pos, end: cur } };
521
653
  }
@@ -537,11 +669,10 @@ function oneOrMore(parser2) {
537
669
  const values = [first.value];
538
670
  let cur = first.span.end;
539
671
  while (cur < input.length) {
540
- const result = parser2.parse(input, cur, ctx);
541
- if (!result.ok) break;
542
- if (result.span.end === cur) break;
543
- values.push(result.value);
544
- cur = result.span.end;
672
+ const item = repItem(parser2, input, cur, ctx);
673
+ if (item === "stop" || "fail" in item) break;
674
+ values.push(item.value);
675
+ cur = item.end;
545
676
  }
546
677
  return { ok: true, value: values, span: { start: pos, end: cur } };
547
678
  }
@@ -580,26 +711,10 @@ function sepBy(parser2, separator) {
580
711
  const values = [first.value];
581
712
  let cur = first.span.end;
582
713
  while (cur < input.length) {
583
- let sepPos = cur;
584
- if (ctx.trivia) {
585
- const triviaCtx = { trivia: ctx.trivia, trackLines: ctx.trackLines, user: ctx.user };
586
- const tr = ctx.trivia.parse(input, sepPos, triviaCtx);
587
- if (tr.ok) {
588
- 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 });
589
- sepPos = tr.span.end;
590
- }
591
- }
714
+ const sepPos = ctx.trivia ? consumeTrivia(input, cur, ctx) : cur;
592
715
  const sep = separator.parse(input, sepPos, ctx);
593
716
  if (!sep.ok) break;
594
- let nextPos = sep.span.end;
595
- if (ctx.trivia) {
596
- const triviaCtx = { trivia: ctx.trivia, trackLines: ctx.trackLines, user: ctx.user };
597
- const tr = ctx.trivia.parse(input, nextPos, triviaCtx);
598
- if (tr.ok) {
599
- 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 });
600
- nextPos = tr.span.end;
601
- }
602
- }
717
+ const nextPos = ctx.trivia ? consumeTrivia(input, sep.span.end, ctx) : sep.span.end;
603
718
  const next = parser2.parse(input, nextPos, ctx);
604
719
  if (!next.ok) break;
605
720
  values.push(next.value);
@@ -610,6 +725,148 @@ function sepBy(parser2, separator) {
610
725
  };
611
726
  }
612
727
 
728
+ // src/combinators/not.ts
729
+ function not(parser2) {
730
+ const meta = {
731
+ firstSet: any(),
732
+ // we don't know what NOT matches
733
+ canMatchNewline: false,
734
+ isTrivia: false
735
+ };
736
+ return {
737
+ _tag: "not",
738
+ _meta: meta,
739
+ _def: { tag: "not", parser: parser2 },
740
+ parse(input, pos, ctx) {
741
+ const result = parser2.parse(input, pos, ctx);
742
+ if (result.ok) {
743
+ return { ok: false, expected: [`not(${parser2._tag})`], span: { start: pos, end: pos } };
744
+ }
745
+ return { ok: true, value: null, span: { start: pos, end: pos } };
746
+ }
747
+ };
748
+ }
749
+
750
+ // src/combinators/node.ts
751
+ function node(type, parser2, build) {
752
+ const meta = {
753
+ firstSet: parser2._meta.firstSet,
754
+ canMatchNewline: parser2._meta.canMatchNewline,
755
+ isTrivia: false
756
+ };
757
+ return {
758
+ _tag: "node",
759
+ _meta: meta,
760
+ _def: { tag: "node", type, parser: parser2, build },
761
+ parse(input, pos, ctx) {
762
+ const children = [];
763
+ const rawChildren = [];
764
+ const innerCtx = {
765
+ ...ctx,
766
+ // A node() always records the trivia between its terms — the library owns
767
+ // capture so grammar authors don't reconstruct it. (Matches the compiler.)
768
+ captureTrivia: true,
769
+ _cstChildren: children,
770
+ _cstLeaves: children,
771
+ _cstRawChildren: rawChildren
772
+ };
773
+ const r = parser2.parse(input, pos, innerCtx);
774
+ if (!r.ok) return r;
775
+ const built = build(children, rawChildren, r.span);
776
+ const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
777
+ if (ctx._cstChildren) ctx._cstChildren.push(built);
778
+ if (ctx._cstRawChildren) {
779
+ ctx._cstRawChildren.push(
780
+ isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span }
781
+ );
782
+ }
783
+ return { ok: true, value: built, span: r.span };
784
+ }
785
+ };
786
+ }
787
+
788
+ // src/combinators/map.ts
789
+ function transform(parser2, fn) {
790
+ return {
791
+ _tag: "transform",
792
+ _meta: parser2._meta,
793
+ _def: { tag: "transform", parser: parser2, fn },
794
+ parse(input, pos, ctx) {
795
+ const result = parser2.parse(input, pos, ctx);
796
+ if (!result.ok) return result;
797
+ return { ...result, value: fn(result.value, result.span) };
798
+ }
799
+ };
800
+ }
801
+ function trivia(parser2) {
802
+ return {
803
+ _tag: parser2._tag,
804
+ _meta: { ...parser2._meta, isTrivia: true },
805
+ _def: { tag: "trivia", parser: parser2 },
806
+ parse: parser2.parse.bind(parser2)
807
+ };
808
+ }
809
+
810
+ // src/compiler/line-index.ts
811
+ function buildLineIndex(input) {
812
+ const lineStarts = [0];
813
+ for (let i = 0; i < input.length; i++) {
814
+ if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
815
+ }
816
+ return { lineStarts };
817
+ }
818
+ function offsetToLineCol(index, offset) {
819
+ const { lineStarts } = index;
820
+ let lo = 0;
821
+ let hi = lineStarts.length - 1;
822
+ while (lo < hi) {
823
+ const mid = lo + hi + 1 >> 1;
824
+ if (lineStarts[mid] <= offset) lo = mid;
825
+ else hi = mid - 1;
826
+ }
827
+ return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
828
+ }
829
+ function annotateSpan(span, index) {
830
+ const s = offsetToLineCol(index, span.start);
831
+ const e = offsetToLineCol(index, span.end);
832
+ return {
833
+ ...span,
834
+ startLine: s.line,
835
+ startColumn: s.col,
836
+ endLine: e.line,
837
+ endColumn: e.col
838
+ };
839
+ }
840
+
841
+ // src/combinators/grammar.ts
842
+ function parser(opts, root) {
843
+ return {
844
+ _tag: "grammar",
845
+ _meta: root._meta,
846
+ _def: {
847
+ tag: "grammar",
848
+ parser: root,
849
+ triviaParser: opts.trivia,
850
+ trackLines: opts.trackLines ?? false
851
+ },
852
+ parse(input, pos, _ctx) {
853
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
854
+ const ctx = {
855
+ ..._ctx,
856
+ trackLines,
857
+ ...opts.trivia !== void 0 ? { trivia: opts.trivia } : {},
858
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {}
859
+ };
860
+ const result = root.parse(input, pos ?? 0, ctx);
861
+ if (trackLines) {
862
+ const idx = buildLineIndex(input);
863
+ return { ...result, span: annotateSpan(result.span, idx) };
864
+ }
865
+ return result;
866
+ }
867
+ };
868
+ }
869
+
613
870
  // src/compiler/codegen.ts
614
871
  function v(ctx, prefix = "_v") {
615
872
  return `${prefix}${ctx.vars++}`;
@@ -617,9 +874,58 @@ function v(ctx, prefix = "_v") {
617
874
  function ind(ctx) {
618
875
  return " ".repeat(ctx.indent);
619
876
  }
877
+ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
878
+ if (!ctx.capturing) return [];
879
+ const i = ind(ctx);
880
+ if (ctx.capAsTrivia) {
881
+ return [`${i}if (_ctx._cstRawChildren) _ctx._cstRawChildren.push({ _tag: 'trivia', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } })`];
882
+ }
883
+ const lf = v(ctx, "_lf");
884
+ return [
885
+ `${i}if (_ctx._cstLeaves) {`,
886
+ `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
887
+ `${i} _ctx._cstLeaves.push(${lf})`,
888
+ `${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
889
+ `${i}}`
890
+ ];
891
+ }
892
+ function ensureTriviaCaptureFn(ctx) {
893
+ const trivia2 = ctx.activeTrivia;
894
+ const existing = ctx.triviaCaptureNames.get(trivia2);
895
+ if (existing) return existing;
896
+ const fnName = `_tc${ctx.triviaCaptureNames.size}`;
897
+ ctx.triviaCaptureNames.set(trivia2, fnName);
898
+ const saved = { indent: ctx.indent, failLabel: ctx.failLabel, asTrivia: ctx.capAsTrivia, trivia: ctx.activeTrivia };
899
+ ctx.indent = 2;
900
+ ctx.failLabel = "_cap";
901
+ ctx.capAsTrivia = true;
902
+ delete ctx.activeTrivia;
903
+ const r = emit(trivia2, ctx, "_pos");
904
+ ctx.indent = saved.indent;
905
+ ctx.failLabel = saved.failLabel;
906
+ ctx.capAsTrivia = saved.asTrivia;
907
+ if (saved.trivia) ctx.activeTrivia = saved.trivia;
908
+ ctx.namedFnDecls.push([
909
+ `function ${fnName}(input, _pos, _ctx) {`,
910
+ ` let _e = _pos`,
911
+ ` _cap: {`,
912
+ ...r.stmts,
913
+ ` _e = ${r.endVar}`,
914
+ ` }`,
915
+ ` if (_ctx._triviaLog !== undefined && _e > _pos) _ctx._triviaLog.push(_pos, _e)`,
916
+ ` return _e`,
917
+ `}`
918
+ ].join("\n"));
919
+ return fnName;
920
+ }
620
921
  function failStmt(ctx, expected, posExpr) {
922
+ if (ctx.failLabel) return `${ind(ctx)}break ${ctx.failLabel}`;
621
923
  return `${ind(ctx)}return { ok: false, expected: [${expected}], span: { start: ${posExpr}, end: ${posExpr} } }`;
622
924
  }
925
+ function failArr(ctx, expectedArr, posExpr) {
926
+ if (ctx.failLabel) return `${ind(ctx)}break ${ctx.failLabel}`;
927
+ return `${ind(ctx)}return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
928
+ }
623
929
  function firstSetCond(codeVar, fs) {
624
930
  if (fs.kind === "any") return "true";
625
931
  if (fs.kind === "empty") return "false";
@@ -635,6 +941,30 @@ function asIIFE(stmts, valueVar, endVar, startPos, indent) {
635
941
  `${indent}})()`
636
942
  ].join("\n");
637
943
  }
944
+ function emitFallible(inner, ctx, pos) {
945
+ const lbl = v(ctx, "_lbl");
946
+ const okV = `${lbl}ok`;
947
+ const valV = `${lbl}v`;
948
+ const endV = `${lbl}e`;
949
+ const savedLabel = ctx.failLabel;
950
+ const savedIndent = ctx.indent;
951
+ ctx.failLabel = lbl;
952
+ ctx.indent = 0;
953
+ const r = emit(inner, ctx, pos);
954
+ ctx.failLabel = savedLabel;
955
+ ctx.indent = savedIndent;
956
+ const ind0 = ind(ctx);
957
+ const stmts = [
958
+ `${ind0}let ${okV} = false, ${valV}, ${endV} = ${pos}`,
959
+ `${ind0}${lbl}: {`,
960
+ ...r.stmts,
961
+ `${ind0} ${valV} = ${r.valueVar}`,
962
+ `${ind0} ${endV} = ${r.endVar}`,
963
+ `${ind0} ${okV} = true`,
964
+ `${ind0}}`
965
+ ];
966
+ return { stmts, okVar: okV, valVar: valV, endVar: endV };
967
+ }
638
968
  function emitLit(def, ctx, pos) {
639
969
  const { value, caseInsensitive } = def;
640
970
  const len = value.length;
@@ -672,12 +1002,19 @@ function emitLit(def, ctx, pos) {
672
1002
  `${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
673
1003
  );
674
1004
  }
675
- return { stmts, valueVar: vv, endVar: len === 0 ? pos : `${pos} + ${len}` };
1005
+ const endVar = len === 0 ? pos : `${pos} + ${len}`;
1006
+ stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
1007
+ return { stmts, valueVar: vv, endVar };
676
1008
  }
677
1009
  function emitRegex(def, ctx, pos) {
678
1010
  const flags = "y" + def.flags.replace(/[gy]/g, "");
679
- const rName = `_re${ctx.regexDecls.length}`;
680
- ctx.regexDecls.push(`const ${rName} = /${def.optimizedSource}/${flags}`);
1011
+ const key = `${def.optimizedSource}/${flags}`;
1012
+ let rName = ctx.regexMap.get(key);
1013
+ if (rName === void 0) {
1014
+ rName = `_re${ctx.regexDecls.length}`;
1015
+ ctx.regexDecls.push(`const ${rName} = /${def.optimizedSource}/${flags}`);
1016
+ ctx.regexMap.set(key, rName);
1017
+ }
681
1018
  const mv = v(ctx, "_m");
682
1019
  const vv = v(ctx);
683
1020
  const expectedStr = JSON.stringify(`/${def.source}/`);
@@ -687,7 +1024,9 @@ function emitRegex(def, ctx, pos) {
687
1024
  `${ind(ctx)}if (${mv} === null) ${failStmt({ ...ctx, indent: 0 }, expectedStr, pos).trim()}`,
688
1025
  `${ind(ctx)}const ${vv} = ${mv}[0]`
689
1026
  ];
690
- return { stmts, valueVar: vv, endVar: `${pos} + ${vv}.length` };
1027
+ const endVar = `${pos} + ${vv}.length`;
1028
+ stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
1029
+ return { stmts, valueVar: vv, endVar };
691
1030
  }
692
1031
  function ensureTriviaFn(ctx) {
693
1032
  const trivia2 = ctx.activeTrivia;
@@ -695,9 +1034,15 @@ function ensureTriviaFn(ctx) {
695
1034
  const fnName = `_pf${ctx.namedParsers.size}`;
696
1035
  ctx.namedParsers.set(trivia2, fnName);
697
1036
  const savedIndent = ctx.indent;
1037
+ const savedFailLabel = ctx.failLabel;
1038
+ const savedTrivia = ctx.activeTrivia;
698
1039
  ctx.indent = 1;
1040
+ ctx.failLabel = void 0;
1041
+ delete ctx.activeTrivia;
699
1042
  const r = emit(trivia2, ctx, "_pos");
700
1043
  ctx.indent = savedIndent;
1044
+ ctx.failLabel = savedFailLabel;
1045
+ if (savedTrivia) ctx.activeTrivia = savedTrivia;
701
1046
  ctx.namedFnDecls.push([
702
1047
  `function ${fnName}(input, _pos, _ctx) {`,
703
1048
  ...r.stmts,
@@ -717,12 +1062,17 @@ function emitSeq(def, ctx, pos) {
717
1062
  const valueVars = [];
718
1063
  for (let i = 0; i < def.parsers.length; i++) {
719
1064
  if (i > 0 && ctx.activeTrivia) {
720
- const trivFn = ensureTriviaFn(ctx);
721
- const tmp = v(ctx, "_trv");
722
- stmts.push(
723
- `${ind(ctx)}const ${tmp} = ${trivFn}(input, ${curV}, _ctx)`,
724
- `${ind(ctx)}if (${tmp}.ok) ${curV} = ${tmp}.span.end`
725
- );
1065
+ if (ctx.capturing) {
1066
+ const capFn = ensureTriviaCaptureFn(ctx);
1067
+ stmts.push(`${ind(ctx)}${curV} = ${capFn}(input, ${curV}, _ctx)`);
1068
+ } else {
1069
+ const trivFn = ensureTriviaFn(ctx);
1070
+ const tmp = v(ctx, "_trv");
1071
+ stmts.push(
1072
+ `${ind(ctx)}const ${tmp} = ${trivFn}(input, ${curV}, _ctx)`,
1073
+ `${ind(ctx)}if (${tmp}.ok) ${curV} = ${tmp}.span.end`
1074
+ );
1075
+ }
726
1076
  }
727
1077
  const r = emit(def.parsers[i], ctx, curV);
728
1078
  stmts.push(...r.stmts, `${ind(ctx)}${curV} = ${r.endVar}`);
@@ -762,9 +1112,7 @@ function emitChoice(def, ctx, pos) {
762
1112
  ctx.indent--;
763
1113
  stmts.push(`${ind(ctx)}}`);
764
1114
  }
765
- stmts.push(
766
- `${ind(ctx)}else return { ok: false, expected: ${allExpected}, span: { start: ${pos}, end: ${pos} } }`
767
- );
1115
+ stmts.push(`${ind(ctx)}else ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
768
1116
  return { stmts, valueVar: valV, endVar: endV };
769
1117
  }
770
1118
  return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
@@ -772,17 +1120,21 @@ function emitChoice(def, ctx, pos) {
772
1120
  function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
773
1121
  const superParser = def.parsers[superIndex];
774
1122
  const regexDef = getCoreRegexDef(superParser);
775
- const reIdx = ctx.regexDecls.length;
776
- const reVar = `_re${reIdx}`;
777
1123
  const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
778
- ctx.regexDecls.push(`const ${reVar} = /${regexDef.source}/${cleanFlags}`);
1124
+ const reKey = `${regexDef.source}/${cleanFlags}`;
1125
+ let reVar = ctx.regexMap.get(reKey);
1126
+ if (reVar === void 0) {
1127
+ reVar = `_re${ctx.regexDecls.length}`;
1128
+ ctx.regexDecls.push(`const ${reVar} = /${regexDef.source}/${cleanFlags}`);
1129
+ ctx.regexMap.set(reKey, reVar);
1130
+ }
779
1131
  const matchV = v(ctx, "_gm");
780
1132
  const wordV = v(ctx, "_gw");
781
1133
  const endV = v(ctx, "_ge");
782
1134
  const stmts = [
783
1135
  `${ind(ctx)}${reVar}.lastIndex = ${pos}`,
784
1136
  `${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
785
- `${ind(ctx)}if (${matchV} === null) return { ok: false, expected: ${allExpected}, span: { start: ${pos}, end: ${pos} } }`,
1137
+ `${ind(ctx)}if (${matchV} === null) ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`,
786
1138
  `${ind(ctx)}const ${wordV} = ${matchV}[0]`,
787
1139
  `${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`
788
1140
  ];
@@ -822,7 +1174,7 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
822
1174
  ctx.indent--;
823
1175
  stmts.push(`${ind(ctx)}}`);
824
1176
  }
825
- stmts.push(`${ind(ctx)}else return { ok: false, expected: ${allExpected}, span: { start: ${pos}, end: ${pos} } }`);
1177
+ stmts.push(`${ind(ctx)}else ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
826
1178
  return { stmts, valueVar: valV, endVar: endV };
827
1179
  }
828
1180
  function emitFirstMatch(def, allExpected, ctx, pos) {
@@ -833,40 +1185,60 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
833
1185
  const gate = def.gates[i];
834
1186
  const autoNot = def.autoNot[i];
835
1187
  const savedIndent = ctx.indent;
1188
+ const savedFailLabel = ctx.failLabel;
836
1189
  ctx.indent = 0;
1190
+ ctx.failLabel = void 0;
837
1191
  const r = emit(p, ctx, pos);
838
1192
  ctx.indent = savedIndent;
1193
+ ctx.failLabel = savedFailLabel;
839
1194
  const iife = asIIFE(r.stmts, r.valueVar, r.endVar, pos, ind(ctx));
840
1195
  let gateCond = null;
841
1196
  if (gate) {
842
1197
  const gateIdx = ctx.mapFns.length;
843
1198
  ctx.mapFns.push(gate);
844
- gateCond = `_mf[${gateIdx}](_ctx.user)`;
1199
+ gateCond = `_mf[${gateIdx}](_ctx.state)`;
845
1200
  }
846
1201
  const skipCond = gateCond ? `!${resV}?.ok && ${gateCond}` : `!${resV}?.ok`;
1202
+ const markLeaves = ctx.capturing ? v(ctx, "_cml") : null;
1203
+ const markRaw = ctx.capturing ? v(ctx, "_cmr") : null;
1204
+ const rollback = markLeaves ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLeaves}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRaw}` : "";
1205
+ const markStmts = markLeaves ? [
1206
+ `${ind(ctx)} const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
1207
+ `${ind(ctx)} const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`
1208
+ ] : [];
1209
+ const rollbackStmt = rollback ? `${ind(ctx)} if (!${resV}?.ok) { ${rollback} }` : "";
847
1210
  if (autoNot && autoNot.length > 0) {
848
1211
  const tmp = v(ctx, "_ct");
849
1212
  const anCode = v(ctx, "_anc");
850
1213
  const rejectCond = autoNot.map(
851
1214
  (check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${tmp}.span.end)`
852
1215
  ).join(" || ");
1216
+ stmts.push(`${ind(ctx)}if (${skipCond}) {`);
1217
+ stmts.push(...markStmts);
853
1218
  stmts.push(
854
- `${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} } }`
1219
+ `${ind(ctx)} const ${tmp} = (() => { try { return ${iife} } catch {} })()`,
1220
+ `${ind(ctx)} if (${tmp}?.ok) { const ${anCode} = ${tmp}.span.end < input.length ? input.charCodeAt(${tmp}.span.end) : -1; if (!(${rejectCond})) ${resV} = ${tmp} }`
855
1221
  );
1222
+ if (rollbackStmt) stmts.push(rollbackStmt);
1223
+ stmts.push(`${ind(ctx)}}`);
856
1224
  } else {
857
- stmts.push(`${ind(ctx)}if (${skipCond}) { try { ${resV} = ${iife} } catch {} }`);
1225
+ stmts.push(`${ind(ctx)}if (${skipCond}) {`);
1226
+ stmts.push(...markStmts);
1227
+ stmts.push(`${ind(ctx)} try { ${resV} = ${iife} } catch {}`);
1228
+ if (rollbackStmt) stmts.push(rollbackStmt);
1229
+ stmts.push(`${ind(ctx)}}`);
858
1230
  }
859
1231
  }
860
- stmts.push(
861
- `${ind(ctx)}if (!${resV}?.ok) return { ok: false, expected: ${allExpected}, span: { start: ${pos}, end: ${pos} } }`
862
- );
1232
+ stmts.push(`${ind(ctx)}if (!${resV}?.ok) ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
863
1233
  return { stmts, valueVar: `${resV}.value`, endVar: `${resV}.span.end` };
864
1234
  }
865
1235
  function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
866
- if (strategy.tag === "greedyClassify")
867
- return emitGreedyClassify(def, strategy.superIndex, allExpected, ctx, pos);
868
- if (strategy.tag === "literalsLongestFirst")
869
- return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
1236
+ if (!ctx.capturing) {
1237
+ if (strategy.tag === "greedyClassify")
1238
+ return emitGreedyClassify(def, strategy.superIndex, allExpected, ctx, pos);
1239
+ if (strategy.tag === "literalsLongestFirst")
1240
+ return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
1241
+ }
870
1242
  return emitFirstMatch(def, allExpected, ctx, pos);
871
1243
  }
872
1244
  function emitTransformChain(p, baseValue, endV, startPos, ctx) {
@@ -875,6 +1247,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
875
1247
  const innerR = emitTransformChain(def.parser, baseValue, endV, startPos, ctx);
876
1248
  const fnIdx = ctx.mapFns.length;
877
1249
  ctx.mapFns.push(def.fn);
1250
+ ctx.mapFnSrcs.push(def.fnSrc ?? null);
878
1251
  const vv = v(ctx);
879
1252
  return {
880
1253
  stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = _mf[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
@@ -902,13 +1275,6 @@ function emitMany(def, ctx, pos) {
902
1275
  `${ind(ctx)}const ${arrV} = []`,
903
1276
  `${ind(ctx)}let ${curV} = ${pos}`
904
1277
  ];
905
- const emitInnerIIFE = () => {
906
- const savedIndent = ctx.indent;
907
- ctx.indent = 0;
908
- const r = emit(def.parser, ctx, curV);
909
- ctx.indent = savedIndent;
910
- return asIIFE(r.stmts, r.valueVar, r.endVar, curV, ind(ctx) + " ");
911
- };
912
1278
  if (def.min === 1) {
913
1279
  const firstR = emit(def.parser, ctx, curV);
914
1280
  stmts.push(...firstR.stmts);
@@ -919,11 +1285,35 @@ function emitMany(def, ctx, pos) {
919
1285
  }
920
1286
  stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
921
1287
  ctx.indent++;
1288
+ let itemPos = curV;
1289
+ let rollback = "";
1290
+ if (ctx.activeTrivia) {
1291
+ if (ctx.capturing) {
1292
+ const capFn = ensureTriviaCaptureFn(ctx);
1293
+ const markV = v(ctx, "_mk");
1294
+ const npV = v(ctx, "_np");
1295
+ stmts.push(
1296
+ `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
1297
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx)`
1298
+ );
1299
+ itemPos = npV;
1300
+ rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; `;
1301
+ } else {
1302
+ const trivFn = ensureTriviaFn(ctx);
1303
+ const npV = v(ctx, "_np");
1304
+ stmts.push(
1305
+ `${ind(ctx)}let ${npV} = ${curV}`,
1306
+ `${ind(ctx)}{ const _trv = ${trivFn}(input, ${npV}, _ctx); if (_trv.ok) ${npV} = _trv.span.end }`
1307
+ );
1308
+ itemPos = npV;
1309
+ }
1310
+ }
1311
+ const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
922
1312
  stmts.push(
923
- `${ind(ctx)}const _iter = (() => { try { return ${emitInnerIIFE()} } catch { return null } })()`,
924
- `${ind(ctx)}if (!_iter?.ok || _iter.span.end === ${curV}) break`,
925
- `${ind(ctx)}${arrV}.push(_iter.value)`,
926
- `${ind(ctx)}${curV} = _iter.span.end`
1313
+ ...iterStmts,
1314
+ `${ind(ctx)}if (!${iterOk} || ${iterEnd} === ${curV}) { ${rollback}break }`,
1315
+ `${ind(ctx)}${arrV}.push(${iterVal})`,
1316
+ `${ind(ctx)}${curV} = ${iterEnd}`
927
1317
  );
928
1318
  ctx.indent--;
929
1319
  stmts.push(`${ind(ctx)}}`);
@@ -932,56 +1322,80 @@ function emitMany(def, ctx, pos) {
932
1322
  function emitOptional(def, ctx, pos) {
933
1323
  const valV = v(ctx, "_opt");
934
1324
  const endV = v(ctx, "_opte");
935
- const savedIndent = ctx.indent;
936
- ctx.indent = 0;
937
- const r = emit(def.parser, ctx, pos);
938
- ctx.indent = savedIndent;
939
- const iife = asIIFE(r.stmts, r.valueVar, r.endVar, pos, ind(ctx));
940
- const resV = v(ctx, "_optr");
1325
+ const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
1326
+ const ind0 = ind(ctx);
941
1327
  const stmts = [
942
- `${ind(ctx)}const ${resV} = (() => { try { return ${iife} } catch { return null } })()`,
943
- `${ind(ctx)}const ${valV} = ${resV}?.ok ? ${resV}.value : null`,
944
- `${ind(ctx)}const ${endV} = ${resV}?.ok ? ${resV}.span.end : ${pos}`
1328
+ ...lblStmts,
1329
+ `${ind0}const ${valV} = ${okVar} ? ${valVar} : null`,
1330
+ `${ind0}const ${endV} = ${okVar} ? ${endVar} : ${pos}`
945
1331
  ];
946
1332
  return { stmts, valueVar: valV, endVar: endV };
947
1333
  }
948
1334
  function emitSepBy(_p, def, ctx, pos) {
949
1335
  const arrV = v(ctx, "_arr");
950
1336
  const curV = v(ctx, "_cur");
951
- const iife = (inner, posExpr) => {
952
- const saved = ctx.indent;
953
- ctx.indent = 0;
954
- const r = emit(inner, ctx, posExpr);
955
- ctx.indent = saved;
956
- return asIIFE(r.stmts, r.valueVar, r.endVar, posExpr, ind(ctx));
957
- };
958
- const firstR_saved = ctx.indent;
959
- ctx.indent = 0;
960
- const firstR = emit(def.parser, ctx, pos);
961
- ctx.indent = firstR_saved;
962
- const firstV = v(ctx, "_sb0");
963
- const sepV = v(ctx, "_sbs");
964
- const nextV = v(ctx, "_sbn");
1337
+ const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
965
1338
  const stmts = [
966
1339
  `${ind(ctx)}const ${arrV} = []`,
967
1340
  `${ind(ctx)}let ${curV} = ${pos}`,
968
- `${ind(ctx)}const ${firstV} = (() => { try { return ${asIIFE(firstR.stmts, firstR.valueVar, firstR.endVar, pos, ind(ctx))} } catch { return null } })()`,
969
- `${ind(ctx)}if (${firstV}?.ok) {`
1341
+ ...firstStmts,
1342
+ `${ind(ctx)}if (${firstOk}) {`
970
1343
  ];
971
1344
  ctx.indent++;
972
1345
  stmts.push(
973
- `${ind(ctx)}${arrV}.push(${firstV}.value)`,
974
- `${ind(ctx)}${curV} = ${firstV}.span.end`,
1346
+ `${ind(ctx)}${arrV}.push(${firstVal})`,
1347
+ `${ind(ctx)}${curV} = ${firstEnd}`,
975
1348
  `${ind(ctx)}while (${curV} < input.length) {`
976
1349
  );
977
1350
  ctx.indent++;
1351
+ let rollback = "";
1352
+ if (ctx.capturing) {
1353
+ const markV = v(ctx, "_mk");
1354
+ stmts.push(`${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`);
1355
+ rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; `;
1356
+ }
1357
+ let sepAtPos = curV;
1358
+ if (ctx.activeTrivia) {
1359
+ if (ctx.capturing) {
1360
+ const capFn = ensureTriviaCaptureFn(ctx);
1361
+ const spV = v(ctx, "_sp");
1362
+ stmts.push(`${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx)`);
1363
+ sepAtPos = spV;
1364
+ } else {
1365
+ const trivFn = ensureTriviaFn(ctx);
1366
+ const spV = v(ctx, "_sp");
1367
+ stmts.push(
1368
+ `${ind(ctx)}let ${spV} = ${curV}`,
1369
+ `${ind(ctx)}{ const _trv = ${trivFn}(input, ${spV}, _ctx); if (_trv.ok) ${spV} = _trv.span.end }`
1370
+ );
1371
+ sepAtPos = spV;
1372
+ }
1373
+ }
1374
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
1375
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollback}break }`);
1376
+ let nextAtPos = sepEnd;
1377
+ if (ctx.activeTrivia) {
1378
+ if (ctx.capturing) {
1379
+ const capFn = ensureTriviaCaptureFn(ctx);
1380
+ const npV = v(ctx, "_np");
1381
+ stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx)`);
1382
+ nextAtPos = npV;
1383
+ } else {
1384
+ const trivFn = ensureTriviaFn(ctx);
1385
+ const npV = v(ctx, "_np");
1386
+ stmts.push(
1387
+ `${ind(ctx)}let ${npV} = ${sepEnd}`,
1388
+ `${ind(ctx)}{ const _trv = ${trivFn}(input, ${npV}, _ctx); if (_trv.ok) ${npV} = _trv.span.end }`
1389
+ );
1390
+ nextAtPos = npV;
1391
+ }
1392
+ }
1393
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, nextAtPos);
978
1394
  stmts.push(
979
- `${ind(ctx)}const ${sepV} = (() => { try { return ${iife(def.separator, curV)} } catch { return null } })()`,
980
- `${ind(ctx)}if (!${sepV}?.ok) break`,
981
- `${ind(ctx)}const ${nextV} = (() => { try { return ${iife(def.parser, `${sepV}.span.end`)} } catch { return null } })()`,
982
- `${ind(ctx)}if (!${nextV}?.ok) break`,
983
- `${ind(ctx)}${arrV}.push(${nextV}.value)`,
984
- `${ind(ctx)}${curV} = ${nextV}.span.end`
1395
+ ...nextStmts,
1396
+ `${ind(ctx)}if (!${nextOk}) { ${rollback}break }`,
1397
+ `${ind(ctx)}${arrV}.push(${nextVal})`,
1398
+ `${ind(ctx)}${curV} = ${nextEnd}`
985
1399
  );
986
1400
  ctx.indent--;
987
1401
  stmts.push(`${ind(ctx)}}`);
@@ -990,13 +1404,6 @@ function emitSepBy(_p, def, ctx, pos) {
990
1404
  return { stmts, valueVar: arrV, endVar: curV };
991
1405
  }
992
1406
  function emitScanTo(def, ctx, pos) {
993
- const safeIIFE = (inner, posExpr) => {
994
- const saved = ctx.indent;
995
- ctx.indent = 0;
996
- const r = emit(inner, ctx, posExpr);
997
- ctx.indent = saved;
998
- return `(() => { try { return ${asIIFE(r.stmts, r.valueVar, r.endVar, posExpr, ind(ctx))} } catch { return null } })()`;
999
- };
1000
1407
  const curV = v(ctx, "_stcur");
1001
1408
  const foundV = v(ctx, "_stfnd");
1002
1409
  const stmts = [
@@ -1005,18 +1412,19 @@ function emitScanTo(def, ctx, pos) {
1005
1412
  `${ind(ctx)}while (${curV} < input.length) {`
1006
1413
  ];
1007
1414
  ctx.indent++;
1008
- const sentV = v(ctx, "_sts");
1009
- stmts.push(
1010
- `${ind(ctx)}const ${sentV} = ${safeIIFE(def.sentinel, curV)}`,
1011
- `${ind(ctx)}if (${sentV}?.ok) { ${foundV} = true; break }`
1012
- );
1415
+ const probeCtx = { ...ctx, capturing: false };
1416
+ const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
1417
+ stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
1013
1418
  if (def.skip.length > 0) {
1014
1419
  const advV = v(ctx, "_stadv");
1015
1420
  stmts.push(`${ind(ctx)}let ${advV} = false`);
1016
1421
  for (const skipper of def.skip) {
1017
- const skV = v(ctx, "_sk");
1422
+ const { stmts: skStmts, okVar: skOk, endVar: skEnd } = emitFallible(skipper, probeCtx, curV);
1018
1423
  stmts.push(
1019
- `${ind(ctx)}if (!${advV}) { const ${skV} = ${safeIIFE(skipper, curV)}; if (${skV}?.ok && ${skV}.span.end > ${curV}) { ${curV} = ${skV}.span.end; ${advV} = true } }`
1424
+ `${ind(ctx)}if (!${advV}) {`,
1425
+ ...skStmts,
1426
+ `${ind(ctx)} if (${skOk} && ${skEnd} > ${curV}) { ${curV} = ${skEnd}; ${advV} = true }`,
1427
+ `${ind(ctx)}}`
1020
1428
  );
1021
1429
  }
1022
1430
  stmts.push(`${ind(ctx)}if (!${advV}) ${curV}++`);
@@ -1028,14 +1436,54 @@ function emitScanTo(def, ctx, pos) {
1028
1436
  if (!def.orEOF) {
1029
1437
  const sentDef = def.sentinel._def;
1030
1438
  const expectedStr = sentDef.tag === "literal" ? JSON.stringify([JSON.stringify(sentDef.value)]) : `["sentinel"]`;
1031
- stmts.push(
1032
- `${ind(ctx)}if (!${foundV}) return { ok: false, expected: ${expectedStr}, span: { start: ${pos}, end: ${curV} } }`
1033
- );
1439
+ stmts.push(`${ind(ctx)}if (!${foundV}) ${failArr({ ...ctx, indent: 0 }, expectedStr, pos)}`);
1034
1440
  }
1035
1441
  const valV = v(ctx);
1036
1442
  stmts.push(`${ind(ctx)}const ${valV} = input.slice(${pos}, ${curV})`);
1443
+ if (ctx.capturing) {
1444
+ const cap = emitLeafCapture(ctx, valV, pos, curV).map((s) => " " + s);
1445
+ stmts.push(`${ind(ctx)}if (${curV} > ${pos}) {`, ...cap, `${ind(ctx)}}`);
1446
+ }
1037
1447
  return { stmts, valueVar: valV, endVar: curV };
1038
1448
  }
1449
+ function emitNot(def, ctx, pos) {
1450
+ const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
1451
+ return {
1452
+ stmts: [
1453
+ ...stmts,
1454
+ `${ind(ctx)}if (${okVar}) ${failStmt({ ...ctx, indent: 0 }, '"not"', pos).trim()}`
1455
+ ],
1456
+ valueVar: "null",
1457
+ endVar: pos
1458
+ };
1459
+ }
1460
+ function emitNode(def, ctx, pos) {
1461
+ const fnIdx = ctx.buildFns.length;
1462
+ ctx.buildFns.push(def.build);
1463
+ ctx.buildSrcs.push(def.buildSrc ?? null);
1464
+ const i = ind(ctx);
1465
+ const chV = v(ctx, "_ch");
1466
+ const rawV = v(ctx, "_raw");
1467
+ const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st");
1468
+ const stmts = [
1469
+ `${i}const ${chV} = [], ${rawV} = []`,
1470
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia`,
1471
+ `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true`
1472
+ ];
1473
+ const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
1474
+ stmts.push(...innerStmts);
1475
+ stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}`);
1476
+ stmts.push(`${i}if (!${okVar}) ${failStmt({ ...ctx, indent: 0 }, '"node"', pos).trim()}`);
1477
+ const ndV = v(ctx, "_nd");
1478
+ stmts.push(
1479
+ `${i}const ${ndV} = _build[${fnIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} })`,
1480
+ // Record into the caller's collectors (the node in children; the node, or a
1481
+ // spanned leaf if build collapsed to a non-node value, in rawChildren).
1482
+ `${i}if (${sc}) ${sc}.push(${ndV})`,
1483
+ `${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} } })`
1484
+ );
1485
+ return { stmts, valueVar: ndV, endVar };
1486
+ }
1039
1487
  function emitRuntimeFallback(parser2, ctx, pos) {
1040
1488
  const idx = ctx.runtimeParsers.length;
1041
1489
  ctx.runtimeParsers.push(parser2);
@@ -1044,7 +1492,7 @@ function emitRuntimeFallback(parser2, ctx, pos) {
1044
1492
  const ev = v(ctx, "_rte");
1045
1493
  const stmts = [
1046
1494
  `${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
1047
- `${ind(ctx)}if (!${rv}.ok) return ${rv}`,
1495
+ `${ind(ctx)}if (!${rv}.ok) ${failStmt({ ...ctx, indent: 0 }, '"runtime"', pos).trim()}`,
1048
1496
  `${ind(ctx)}const ${vv} = ${rv}.value`,
1049
1497
  `${ind(ctx)}const ${ev} = ${rv}.span.end`
1050
1498
  ];
@@ -1062,9 +1510,12 @@ function emitLazy(p, def, ctx, pos) {
1062
1510
  return emitRuntimeFallback(p, ctx, pos);
1063
1511
  }
1064
1512
  const savedIndent = ctx.indent;
1513
+ const savedFailLabel = ctx.failLabel;
1065
1514
  ctx.indent = 1;
1515
+ ctx.failLabel = void 0;
1066
1516
  const r = emit(resolved, ctx, "_pos");
1067
1517
  ctx.indent = savedIndent;
1518
+ ctx.failLabel = savedFailLabel;
1068
1519
  ctx.namedFnDecls.push([
1069
1520
  `function ${fnName2}(input, _pos, _ctx) {`,
1070
1521
  ...r.stmts,
@@ -1079,7 +1530,7 @@ function emitLazy(p, def, ctx, pos) {
1079
1530
  return {
1080
1531
  stmts: [
1081
1532
  `${ind(ctx)}const ${rv} = ${fnName}(input, ${pos}, _ctx)`,
1082
- `${ind(ctx)}if (!${rv}.ok) return ${rv}`,
1533
+ `${ind(ctx)}if (!${rv}.ok) ${failStmt({ ...ctx, indent: 0 }, '"parser"', pos).trim()}`,
1083
1534
  `${ind(ctx)}const ${vv} = ${rv}.value`,
1084
1535
  `${ind(ctx)}const ${ev} = ${rv}.span.end`
1085
1536
  ],
@@ -1087,6 +1538,33 @@ function emitLazy(p, def, ctx, pos) {
1087
1538
  endVar: ev
1088
1539
  };
1089
1540
  }
1541
+ function emitRecover(def, ctx, pos) {
1542
+ const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
1543
+ const ind0 = ind(ctx);
1544
+ const scanV = v(ctx, "_sc");
1545
+ const errV = v(ctx, "_err");
1546
+ const savedIndent = ctx.indent;
1547
+ ctx.indent += 2;
1548
+ const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, ctx, scanV);
1549
+ ctx.indent = savedIndent;
1550
+ const stmts = [
1551
+ ...innerStmts,
1552
+ `${ind0}if (!${okVar}) {`,
1553
+ `${ind0} let ${scanV} = ${pos}`,
1554
+ `${ind0} while (${scanV} < input.length) {`,
1555
+ ...sentStmts,
1556
+ `${ind0} if (${sentOk}) break`,
1557
+ `${ind0} ${scanV}++`,
1558
+ `${ind0} }`,
1559
+ `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: [] }`,
1560
+ `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
1561
+ `${ind0} ${valVar} = ${errV}`,
1562
+ `${ind0} ${endVar} = ${scanV}`,
1563
+ `${ind0} ${okVar} = true`,
1564
+ `${ind0}}`
1565
+ ];
1566
+ return { stmts, valueVar: valVar, endVar };
1567
+ }
1090
1568
  function emit(p, ctx, pos) {
1091
1569
  const def = p._def;
1092
1570
  switch (def.tag) {
@@ -1109,6 +1587,7 @@ function emit(p, ctx, pos) {
1109
1587
  const inner = emit(def.parser, ctx, pos);
1110
1588
  const fnIdx = ctx.mapFns.length;
1111
1589
  ctx.mapFns.push(def.fn);
1590
+ ctx.mapFnSrcs.push(def.fnSrc ?? null);
1112
1591
  const mv = v(ctx, "_mapped");
1113
1592
  return {
1114
1593
  stmts: [
@@ -1149,15 +1628,21 @@ function emit(p, ctx, pos) {
1149
1628
  else ctx.activeTrivia = savedTrivia;
1150
1629
  return r;
1151
1630
  }
1631
+ case "not":
1632
+ return emitNot(def, ctx, pos);
1633
+ case "node":
1634
+ return emitNode(def, ctx, pos);
1152
1635
  case "scanTo":
1153
1636
  return emitScanTo(def, ctx, pos);
1637
+ case "recover":
1638
+ return emitRecover(def, ctx, pos);
1154
1639
  case "guard": {
1155
1640
  const fnIdx = ctx.mapFns.length;
1156
1641
  ctx.mapFns.push(def.predicate);
1157
1642
  const vv = v(ctx);
1158
1643
  return {
1159
1644
  stmts: [
1160
- `${ind(ctx)}if (!_mf[${fnIdx}](_ctx.user)) ${failStmt({ ...ctx, indent: 0 }, '"gate"', pos).trim()}`,
1645
+ `${ind(ctx)}if (!_mf[${fnIdx}](_ctx.state)) ${failStmt({ ...ctx, indent: 0 }, '"gate"', pos).trim()}`,
1161
1646
  `${ind(ctx)}const ${vv} = null`
1162
1647
  ],
1163
1648
  valueVar: vv,
@@ -1173,9 +1658,12 @@ function emit(p, ctx, pos) {
1173
1658
  const fnName = `_wcf${ctx.namedParsers.size}`;
1174
1659
  ctx.namedParsers.set(innerParser, fnName);
1175
1660
  const savedIndent = ctx.indent;
1661
+ const savedFailLabel = ctx.failLabel;
1176
1662
  ctx.indent = 1;
1663
+ ctx.failLabel = void 0;
1177
1664
  const innerR = emit(innerParser, ctx, "_pos");
1178
1665
  ctx.indent = savedIndent;
1666
+ ctx.failLabel = savedFailLabel;
1179
1667
  ctx.namedFnDecls.push([
1180
1668
  `function ${fnName}(input, _pos, _ctx) {`,
1181
1669
  ...innerR.stmts,
@@ -1189,8 +1677,8 @@ function emit(p, ctx, pos) {
1189
1677
  const ev = v(ctx, "_wce");
1190
1678
  return {
1191
1679
  stmts: [
1192
- `${ind(ctx)}const ${rv} = ${fn}(input, ${pos}, { ..._ctx, user: _mf[${evIdx}]() })`,
1193
- `${ind(ctx)}if (!${rv}.ok) return ${rv}`,
1680
+ `${ind(ctx)}const ${rv} = ${fn}(input, ${pos}, { ..._ctx, state: _mf[${evIdx}]() })`,
1681
+ `${ind(ctx)}if (!${rv}.ok) ${failStmt({ ...ctx, indent: 0 }, '"withCtx"', pos).trim()}`,
1194
1682
  `${ind(ctx)}const ${vv} = ${rv}.value`,
1195
1683
  `${ind(ctx)}const ${ev} = ${rv}.span.end`
1196
1684
  ],
@@ -1202,16 +1690,61 @@ function emit(p, ctx, pos) {
1202
1690
  return emitRuntimeFallback(p, ctx, pos);
1203
1691
  }
1204
1692
  }
1205
- function compile(parser2) {
1693
+ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
1694
+ if (seen.has(p)) return false;
1695
+ seen.add(p);
1696
+ const d = p._def;
1697
+ switch (d.tag) {
1698
+ case "node":
1699
+ return true;
1700
+ case "lazy": {
1701
+ try {
1702
+ return hasNodeDef(d.thunk(), seen);
1703
+ } catch {
1704
+ return false;
1705
+ }
1706
+ }
1707
+ case "grammar":
1708
+ case "trivia":
1709
+ case "optional":
1710
+ case "many":
1711
+ case "oneOrMore":
1712
+ case "not":
1713
+ case "transform":
1714
+ return hasNodeDef(d.parser, seen);
1715
+ case "skip":
1716
+ return hasNodeDef(d.main, seen) || hasNodeDef(d.skipped, seen);
1717
+ case "sequence":
1718
+ case "choice":
1719
+ return d.parsers.some((x) => hasNodeDef(x, seen));
1720
+ case "sepBy":
1721
+ return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
1722
+ case "scanTo":
1723
+ return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen));
1724
+ case "recover":
1725
+ return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
1726
+ case "withCtx":
1727
+ return hasNodeDef(d.parser, seen);
1728
+ default:
1729
+ return false;
1730
+ }
1731
+ }
1732
+ function compile(parser2, mapFnSources) {
1206
1733
  const ctx = {
1207
1734
  vars: 0,
1208
1735
  indent: 1,
1209
1736
  regexDecls: [],
1737
+ regexMap: /* @__PURE__ */ new Map(),
1210
1738
  mapFns: [],
1739
+ mapFnSrcs: [],
1740
+ buildFns: [],
1741
+ buildSrcs: [],
1211
1742
  runtimeParsers: [],
1212
1743
  needsCollator: false,
1213
1744
  namedParsers: /* @__PURE__ */ new Map(),
1214
- namedFnDecls: []
1745
+ triviaCaptureNames: /* @__PURE__ */ new Map(),
1746
+ namedFnDecls: [],
1747
+ capturing: hasNodeDef(parser2)
1215
1748
  };
1216
1749
  const r = emit(parser2, ctx, "_pos");
1217
1750
  const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
@@ -1220,13 +1753,13 @@ function compile(parser2) {
1220
1753
  ...ctx.regexDecls,
1221
1754
  "",
1222
1755
  ...ctx.namedFnDecls,
1223
- `${collatorDecl}function _parse(input, _pos, _rp, _mf, _ctx) {`,
1756
+ `${collatorDecl}function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
1224
1757
  ` let pos = _pos`,
1225
1758
  ...r.stmts,
1226
1759
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
1227
1760
  `}`
1228
1761
  ].join("\n");
1229
- const fn = new Function("input", "_pos", "_rp", "_mf", "_ctx", [
1762
+ const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
1230
1763
  ...ctx.regexDecls,
1231
1764
  collatorDecl,
1232
1765
  ...ctx.namedFnDecls,
@@ -1235,17 +1768,27 @@ function compile(parser2) {
1235
1768
  `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
1236
1769
  ].join("\n"));
1237
1770
  const defaultCtx = { trackLines: false };
1238
- const canInline = ctx.runtimeParsers.length === 0 && ctx.mapFns.length === 0;
1239
- const inlineExpression = canInline ? buildInlineExpression(ctx, r, collatorDecl) : null;
1771
+ const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
1772
+ const effectiveSources = mapFnSources ?? derivedSrcs;
1773
+ const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
1774
+ const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
1775
+ const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
1776
+ const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
1777
+ const inlineExpression = canInline ? buildInlineExpression(ctx, r, collatorDecl, effectiveSources, buildSources) : null;
1240
1778
  return {
1241
1779
  source,
1242
1780
  inlineExpression,
1243
1781
  parse(input, pos = 0) {
1244
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, defaultCtx);
1782
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
1783
+ },
1784
+ parseWithErrors(input, pos = 0) {
1785
+ const errors = [];
1786
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
1787
+ return { ...result, errors };
1245
1788
  }
1246
1789
  };
1247
1790
  }
1248
- function buildInlineExpression(ctx, r, collatorDecl) {
1791
+ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
1249
1792
  const bodyLines = [
1250
1793
  ` let pos = _pos`,
1251
1794
  ...r.stmts.map((s) => ` ${s}`),
@@ -1256,20 +1799,82 @@ function buildInlineExpression(ctx, r, collatorDecl) {
1256
1799
  ...bodyLines,
1257
1800
  `}`
1258
1801
  ].join("\n");
1259
- const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0;
1260
- if (!needsWrapper) {
1261
- return innerFn;
1262
- }
1802
+ const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
1803
+ const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
1804
+ const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl;
1805
+ if (!needsWrapper) return innerFn;
1263
1806
  return [
1264
1807
  `/* @__PURE__ */ (() => {`,
1265
1808
  ...ctx.regexDecls.map((d) => ` ${d}`),
1266
1809
  collatorDecl ? ` ${collatorDecl.trim()}` : "",
1810
+ mfDecl,
1811
+ buildDecl,
1267
1812
  ...ctx.namedFnDecls.flatMap((f) => f.split("\n").map((l) => ` ${l}`)),
1268
1813
  ` return ${innerFn}`,
1269
1814
  `})()`
1270
1815
  ].filter(Boolean).join("\n");
1271
1816
  }
1272
1817
 
1818
+ // src/combinators/scanTo.ts
1819
+ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
1820
+ const meta = {
1821
+ firstSet: any(),
1822
+ canMatchNewline: true,
1823
+ isTrivia: false
1824
+ };
1825
+ return {
1826
+ _tag: "scanTo",
1827
+ _meta: meta,
1828
+ _def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
1829
+ parse(input, pos, ctx) {
1830
+ let cur = pos;
1831
+ const probeCtx = { trackLines: ctx.trackLines, state: ctx.state };
1832
+ const emit2 = (end) => {
1833
+ if (end > pos && (ctx._cstLeaves || ctx._cstRawChildren)) {
1834
+ const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
1835
+ if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
1836
+ if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
1837
+ }
1838
+ };
1839
+ while (cur < input.length) {
1840
+ const s = sentinel.parse(input, cur, probeCtx);
1841
+ if (s.ok) {
1842
+ emit2(cur);
1843
+ return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
1844
+ }
1845
+ let advanced = false;
1846
+ for (const skipper of skip2) {
1847
+ const r = skipper.parse(input, cur, probeCtx);
1848
+ if (r.ok && r.span.end > cur) {
1849
+ cur = r.span.end;
1850
+ advanced = true;
1851
+ break;
1852
+ }
1853
+ }
1854
+ if (!advanced) cur++;
1855
+ }
1856
+ if (orEOF) {
1857
+ emit2(cur);
1858
+ return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
1859
+ }
1860
+ const sentDef = sentinel._def;
1861
+ const expected = sentDef.tag === "literal" ? [JSON.stringify(sentDef.value)] : ["sentinel"];
1862
+ return { ok: false, expected, span: { start: pos, end: cur } };
1863
+ }
1864
+ };
1865
+ }
1866
+ function balanced(open, close, options = {}) {
1867
+ const inner = scanTo(literal(close), options);
1868
+ const combi = transform(
1869
+ sequence(literal(open), inner, literal(close)),
1870
+ ([o, content, c]) => o + content + c
1871
+ );
1872
+ if (combi._def.tag === "transform") {
1873
+ combi._def.fnSrc = "([o, content, c]) => o + content + c";
1874
+ }
1875
+ return combi;
1876
+ }
1877
+
1273
1878
  // src/plugin/evaluator.ts
1274
1879
  var SUPPORTED = {
1275
1880
  literal: (...a) => literal(a[0], a[1]),
@@ -1279,19 +1884,144 @@ var SUPPORTED = {
1279
1884
  many: (...a) => many(a[0]),
1280
1885
  oneOrMore: (...a) => oneOrMore(a[0]),
1281
1886
  optional: (...a) => optional(a[0]),
1282
- sepBy: (...a) => sepBy(a[0], a[1])
1283
- // transform / grammar intentionally omitted — take user closures that can't be serialized
1887
+ sepBy: (...a) => sepBy(a[0], a[1]),
1888
+ trivia: (...a) => trivia(a[0])
1284
1889
  };
1285
- function evaluateExpr(node, scope) {
1286
- if (node.type === "Identifier") return scope.get(node.name) ?? null;
1287
- if (node.type !== "CallExpression") return null;
1288
- const callee = node.callee;
1890
+ function isScopeEntry(v2) {
1891
+ return !!v2 && typeof v2 === "object" && "combi" in v2 && "mfSrcs" in v2;
1892
+ }
1893
+ function isCombinator(v2) {
1894
+ return !!v2 && typeof v2 === "object" && "_def" in v2;
1895
+ }
1896
+ function scopeGet(scope, name, mfs) {
1897
+ const entry = scope.get(name);
1898
+ if (!entry) return null;
1899
+ if (isScopeEntry(entry)) {
1900
+ if (mfs && entry.mfSrcs.length > 0) mfs.push(...entry.mfSrcs);
1901
+ return entry.combi;
1902
+ }
1903
+ if (isCombinator(entry)) return entry;
1904
+ return null;
1905
+ }
1906
+ function exprToCombi(node2, scope, code, mfs) {
1907
+ if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
1908
+ if (node2.type !== "CallExpression") return null;
1909
+ const callee = node2.callee;
1289
1910
  if (callee.type !== "Identifier") return null;
1911
+ if (callee.name === "transform" && code !== void 0 && mfs !== void 0) {
1912
+ const [parserArg, fnArg] = node2.arguments;
1913
+ if (!parserArg || !fnArg || parserArg.type === "SpreadElement" || fnArg.type === "SpreadElement") return null;
1914
+ const inner = anyValue(parserArg, scope, code, mfs);
1915
+ if (!isCombinator(inner)) return null;
1916
+ const fnSrc = code.slice(fnArg.start, fnArg.end);
1917
+ mfs.push(fnSrc);
1918
+ try {
1919
+ const combi = transform(inner, (v2) => v2);
1920
+ if (combi._def.tag === "transform") combi._def.fnSrc = fnSrc;
1921
+ return combi;
1922
+ } catch {
1923
+ return null;
1924
+ }
1925
+ }
1926
+ if (callee.name === "node" && code !== void 0) {
1927
+ const [typeArg, parserArg, buildArg] = node2.arguments;
1928
+ if (!typeArg || !parserArg || !buildArg || typeArg.type === "SpreadElement" || parserArg.type === "SpreadElement" || buildArg.type === "SpreadElement") return null;
1929
+ const typeVal = anyValue(typeArg, scope, code);
1930
+ if (typeof typeVal !== "string") return null;
1931
+ const inner = anyValue(parserArg, scope, code, mfs);
1932
+ if (!isCombinator(inner)) return null;
1933
+ const buildSrc = code.slice(buildArg.start, buildArg.end);
1934
+ try {
1935
+ const combi = node(typeVal, inner, () => null);
1936
+ if (combi._def.tag === "node") combi._def.buildSrc = buildSrc;
1937
+ return combi;
1938
+ } catch {
1939
+ return null;
1940
+ }
1941
+ }
1942
+ if (callee.name === "rules") return null;
1943
+ if (callee.name === "parser") {
1944
+ const [optsArg, rootArg] = node2.arguments;
1945
+ if (!optsArg || !rootArg || optsArg.type === "SpreadElement" || rootArg.type === "SpreadElement") return null;
1946
+ const opts = anyValue(optsArg, scope, code, []);
1947
+ if (!opts || typeof opts !== "object") return null;
1948
+ const root = anyValue(rootArg, scope, code, mfs);
1949
+ if (!isCombinator(root)) return null;
1950
+ try {
1951
+ return parser(opts, root);
1952
+ } catch {
1953
+ return null;
1954
+ }
1955
+ }
1956
+ if (callee.name === "sepBy") {
1957
+ const [itemArg, sepArg] = node2.arguments;
1958
+ if (!itemArg || !sepArg || itemArg.type === "SpreadElement" || sepArg.type === "SpreadElement") return null;
1959
+ const itemMfs = [];
1960
+ const itemCombi = anyValue(itemArg, scope, code, itemMfs);
1961
+ if (!isCombinator(itemCombi)) return null;
1962
+ const sepMfs = [];
1963
+ const sepCombi = anyValue(sepArg, scope, code, sepMfs);
1964
+ if (!isCombinator(sepCombi)) return null;
1965
+ if (mfs) mfs.push(...itemMfs, ...sepMfs, ...itemMfs);
1966
+ try {
1967
+ return sepBy(itemCombi, sepCombi);
1968
+ } catch {
1969
+ return null;
1970
+ }
1971
+ }
1972
+ if (callee.name === "oneOrMore") {
1973
+ const [itemArg] = node2.arguments;
1974
+ if (!itemArg || itemArg.type === "SpreadElement") return null;
1975
+ const itemMfs = [];
1976
+ const itemCombi = anyValue(itemArg, scope, code, itemMfs);
1977
+ if (!isCombinator(itemCombi)) return null;
1978
+ if (mfs) mfs.push(...itemMfs, ...itemMfs);
1979
+ try {
1980
+ return oneOrMore(itemCombi);
1981
+ } catch {
1982
+ return null;
1983
+ }
1984
+ }
1985
+ if (callee.name === "not") {
1986
+ const [innerArg] = node2.arguments;
1987
+ if (!innerArg || innerArg.type === "SpreadElement") return null;
1988
+ const inner = anyValue(innerArg, scope, code, mfs);
1989
+ if (!isCombinator(inner)) return null;
1990
+ try {
1991
+ return not(inner);
1992
+ } catch {
1993
+ return null;
1994
+ }
1995
+ }
1996
+ if (callee.name === "balanced") {
1997
+ const [openArg, closeArg] = node2.arguments;
1998
+ if (!openArg || !closeArg || openArg.type === "SpreadElement" || closeArg.type === "SpreadElement") return null;
1999
+ const open = anyValue(openArg, scope, code, []);
2000
+ const close = anyValue(closeArg, scope, code, []);
2001
+ if (typeof open !== "string" || typeof close !== "string") return null;
2002
+ try {
2003
+ return balanced(open, close);
2004
+ } catch {
2005
+ return null;
2006
+ }
2007
+ }
2008
+ if (callee.name === "scanTo") {
2009
+ const [sentinelArg, optsArg] = node2.arguments;
2010
+ if (!sentinelArg || sentinelArg.type === "SpreadElement") return null;
2011
+ const sentinel = anyValue(sentinelArg, scope, code, []);
2012
+ if (!isCombinator(sentinel)) return null;
2013
+ const opts = optsArg && optsArg.type !== "SpreadElement" ? anyValue(optsArg, scope, code, []) : void 0;
2014
+ try {
2015
+ return scanTo(sentinel, opts);
2016
+ } catch {
2017
+ return null;
2018
+ }
2019
+ }
1290
2020
  const factory = SUPPORTED[callee.name];
1291
2021
  if (!factory) return null;
1292
- const args = node.arguments.map((arg) => {
2022
+ const args = node2.arguments.map((arg) => {
1293
2023
  if (arg.type === "SpreadElement") return null;
1294
- return evaluateArg(arg, scope);
2024
+ return anyValue(arg, scope, code, mfs);
1295
2025
  });
1296
2026
  if (args.some((a) => a === null)) return null;
1297
2027
  try {
@@ -1300,37 +2030,142 @@ function evaluateExpr(node, scope) {
1300
2030
  return null;
1301
2031
  }
1302
2032
  }
1303
- function evaluateArg(node, scope) {
1304
- if (node.type === "Literal") {
1305
- if ("regex" in node && node.regex !== null && node.regex !== void 0) {
1306
- return new RegExp(node.regex.pattern, node.regex.flags);
2033
+ function anyValue(node2, scope, code, mfs) {
2034
+ if (node2.type === "Literal") {
2035
+ if ("regex" in node2 && node2.regex !== null && node2.regex !== void 0) {
2036
+ return new RegExp(node2.regex.pattern, node2.regex.flags);
1307
2037
  }
1308
- return node.value;
2038
+ return node2.value;
1309
2039
  }
1310
- if (node.type === "ObjectExpression") {
2040
+ if (node2.type === "ArrayExpression") {
2041
+ const arr = node2;
2042
+ const out = [];
2043
+ for (const el of arr.elements) {
2044
+ if (el === null) {
2045
+ out.push(null);
2046
+ continue;
2047
+ }
2048
+ if (el.type === "SpreadElement") return null;
2049
+ out.push(anyValue(el, scope, code, mfs));
2050
+ }
2051
+ return out;
2052
+ }
2053
+ if (node2.type === "ObjectExpression") {
1311
2054
  const obj = {};
1312
- for (const prop of node.properties) {
2055
+ for (const prop of node2.properties) {
1313
2056
  if (prop.type !== "Property") return null;
1314
2057
  if (prop.computed) return null;
1315
2058
  const key = prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" ? String(prop.key.value) : null;
1316
2059
  if (key === null) return null;
1317
- obj[key] = evaluateArg(prop.value, scope);
2060
+ obj[key] = anyValue(prop.value, scope, code, mfs);
1318
2061
  }
1319
2062
  return obj;
1320
2063
  }
1321
- if (node.type === "Identifier") {
1322
- if (node.name === "undefined") return void 0;
1323
- return scope.get(node.name) ?? null;
2064
+ if (node2.type === "Identifier") {
2065
+ if (node2.name === "undefined") return void 0;
2066
+ const entry = scope.get(node2.name) ?? null;
2067
+ if (isScopeEntry(entry)) {
2068
+ if (mfs && entry.mfSrcs.length > 0) mfs.push(...entry.mfSrcs);
2069
+ return entry.combi;
2070
+ }
2071
+ return entry;
1324
2072
  }
1325
- if (node.type === "CallExpression") return evaluateExpr(node, scope);
2073
+ if (node2.type === "MemberExpression") {
2074
+ const mem = node2;
2075
+ const obj = anyValue(mem.object, scope, code, mfs);
2076
+ if (!obj || typeof obj !== "object") return null;
2077
+ if (node2.computed) {
2078
+ const key = anyValue(node2.property, scope, code, mfs);
2079
+ if (typeof key !== "string" && typeof key !== "number") return null;
2080
+ return obj[key] ?? null;
2081
+ }
2082
+ const propName = mem.property.name;
2083
+ if (!propName) return null;
2084
+ return obj[propName] ?? null;
2085
+ }
2086
+ if (node2.type === "CallExpression") return exprToCombi(node2, scope, code, mfs);
1326
2087
  return null;
1327
2088
  }
1328
- function referencesAny(node, names, scope) {
1329
- if (node.type === "Identifier") {
1330
- return names.has(node.name) || scope.has(node.name);
2089
+ function evaluateExpr(node2, scope, code, mapFnSources) {
2090
+ return exprToCombi(node2, scope, code, mapFnSources);
2091
+ }
2092
+ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
2093
+ if (factoryNode.type !== "ArrowFunctionExpression" && factoryNode.type !== "FunctionDeclaration" && factoryNode.type !== "FunctionExpression") return null;
2094
+ const factory = factoryNode;
2095
+ const params = factory.params;
2096
+ if (params.length !== 1) return null;
2097
+ const param = params[0];
2098
+ const proxyName = param.type === "Identifier" ? param.name ?? null : null;
2099
+ if (!proxyName) return null;
2100
+ const body = factory.body;
2101
+ if (!body) return null;
2102
+ const statements = [];
2103
+ let returnExpr = null;
2104
+ if (body.type === "BlockStatement") {
2105
+ const stmts = body.body;
2106
+ for (const stmt of stmts) {
2107
+ const s = stmt;
2108
+ if (s.type === "ReturnStatement") {
2109
+ returnExpr = s.argument ?? null;
2110
+ break;
2111
+ }
2112
+ if (s.type === "VariableDeclaration") {
2113
+ statements.push(s);
2114
+ } else {
2115
+ return null;
2116
+ }
2117
+ }
2118
+ } else {
2119
+ returnExpr = body;
2120
+ }
2121
+ if (!returnExpr) return null;
2122
+ const retObj = returnExpr.type === "ParenthesizedExpression" ? returnExpr.expression : returnExpr;
2123
+ if (retObj.type !== "ObjectExpression") return null;
2124
+ const ruleRefs = /* @__PURE__ */ new Map();
2125
+ for (const prop of retObj.properties) {
2126
+ if (prop.type !== "Property") return null;
2127
+ if (prop.computed) return null;
2128
+ const p = prop;
2129
+ const key = p.key.type === "Identifier" ? p.key.name : p.key.type === "Literal" ? String(p.key.value) : null;
2130
+ if (!key) return null;
2131
+ ruleRefs.set(key, ref());
2132
+ }
2133
+ const localScope = new Map(scope);
2134
+ localScope.set(proxyName, Object.fromEntries(ruleRefs));
2135
+ const bodyMfs = [];
2136
+ for (const stmt of statements) {
2137
+ for (const d of stmt.declarations) {
2138
+ const decl = d;
2139
+ if (!decl.init) return null;
2140
+ const id = decl.id;
2141
+ if (id.type !== "Identifier" && id.type !== "BindingIdentifier") return null;
2142
+ const name = id.name;
2143
+ const before = bodyMfs.length;
2144
+ const val = anyValue(decl.init, localScope, code, bodyMfs);
2145
+ if (val === null) return null;
2146
+ const thisDeclMfSrcs = bodyMfs.slice(before);
2147
+ if (isCombinator(val)) {
2148
+ localScope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
2149
+ } else {
2150
+ localScope.set(name, val);
2151
+ }
2152
+ }
2153
+ }
2154
+ for (const prop of retObj.properties) {
2155
+ const p = prop;
2156
+ const key = p.key.type === "Identifier" ? p.key.name : String(p.key.value);
2157
+ const val = anyValue(p.value, localScope, code, mapFnSources);
2158
+ if (!isCombinator(val)) return null;
2159
+ ruleRefs.get(key).define(val);
2160
+ }
2161
+ return ruleRefs;
2162
+ }
2163
+ function referencesAny(node2, names, scope) {
2164
+ if (node2.type === "Identifier") {
2165
+ return names.has(node2.name) || scope.has(node2.name);
1331
2166
  }
1332
- for (const key of Object.keys(node)) {
1333
- const child = node[key];
2167
+ for (const key of Object.keys(node2)) {
2168
+ const child = node2[key];
1334
2169
  if (!child || typeof child !== "object") continue;
1335
2170
  if (Array.isArray(child)) {
1336
2171
  if (child.some((c) => c && typeof c === "object" && "type" in c && referencesAny(c, names, scope))) return true;
@@ -1345,6 +2180,10 @@ function referencesAny(node, names, scope) {
1345
2180
  var PARSEMAN_MODULE = "parseman";
1346
2181
  var plugin_default = createUnplugin((opts = {}) => ({
1347
2182
  name: "parseman",
2183
+ // Run BEFORE the bundler's TS/JS transform — otherwise esbuild (Vite) strips
2184
+ // the `with { type: 'macro' }` import attribute before we ever see it, and the
2185
+ // macro silently never fires.
2186
+ enforce: "pre",
1348
2187
  transformInclude(id) {
1349
2188
  return /\.[jt]sx?$/.test(id) && !id.includes("node_modules");
1350
2189
  },
@@ -1352,7 +2191,15 @@ var plugin_default = createUnplugin((opts = {}) => ({
1352
2191
  if (!code.includes("parseman")) return null;
1353
2192
  if (!code.includes("macro")) return null;
1354
2193
  const moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE, ...opts.moduleAliases ?? []]);
1355
- return transformMacro(code, id, moduleAliases);
2194
+ const result = transformMacro(code, id, moduleAliases);
2195
+ if (result?.warnings.length) {
2196
+ for (const w of result.warnings) {
2197
+ if (typeof this?.warn === "function") this.warn(`[parseman] ${w}`);
2198
+ else console.warn(`[parseman] ${w}`);
2199
+ }
2200
+ }
2201
+ if (!result) return null;
2202
+ return { code: result.code, map: result.map };
1356
2203
  }
1357
2204
  }));
1358
2205
  function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE])) {
@@ -1385,35 +2232,136 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
1385
2232
  if (macroImports.length === 0) return null;
1386
2233
  const scope = /* @__PURE__ */ new Map();
1387
2234
  const replacements = [];
2235
+ const warnings = [];
1388
2236
  let anyUnresolved = false;
2237
+ const lineOf = (pos) => {
2238
+ let line = 1;
2239
+ for (let i = 0; i < pos && i < code.length; i++) if (code.charCodeAt(i) === 10) line++;
2240
+ return line;
2241
+ };
2242
+ const warn = (pos, msg) => {
2243
+ anyUnresolved = true;
2244
+ warnings.push(`${id}:${lineOf(pos)} \u2014 ${msg} (running via the interpreter; add the plugin or simplify the declaration to compile it)`);
2245
+ };
2246
+ const compileRulesFactory = (init, label) => {
2247
+ const args = init.arguments;
2248
+ const factoryArg = args[0];
2249
+ if (!factoryArg) {
2250
+ warn(init.start, `${label}: rules() needs a factory argument`);
2251
+ return null;
2252
+ }
2253
+ const ruleMap = evaluateParserFactory(factoryArg, scope, code, []);
2254
+ if (!ruleMap) {
2255
+ warn(init.start, `${label}: rules(...) factory isn't statically evaluable`);
2256
+ return null;
2257
+ }
2258
+ const entries = [];
2259
+ for (const [ruleKey, rule] of ruleMap) {
2260
+ const compiled = compile(rule);
2261
+ if (compiled.inlineExpression === null) {
2262
+ warn(init.start, `${label}: rule "${ruleKey}" couldn't be inlined`);
2263
+ return null;
2264
+ }
2265
+ entries.push([ruleKey, compiled.inlineExpression]);
2266
+ }
2267
+ return { entries, ruleMap };
2268
+ };
2269
+ const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
1389
2270
  for (const stmt of body) {
1390
- if (stmt.type !== "VariableDeclaration") continue;
1391
- for (const decl of stmt.declarations) {
2271
+ let vd = null;
2272
+ let stmtStart = stmt.start;
2273
+ let stmtEnd = stmt.end;
2274
+ let exportPrefix = "";
2275
+ if (stmt.type === "VariableDeclaration") {
2276
+ vd = stmt;
2277
+ } else if (stmt.type === "ExportNamedDeclaration") {
2278
+ const expStmt = stmt;
2279
+ if (expStmt.declaration?.type === "VariableDeclaration") {
2280
+ vd = expStmt.declaration;
2281
+ exportPrefix = "export ";
2282
+ }
2283
+ }
2284
+ if (!vd) continue;
2285
+ for (const decl of vd.declarations) {
1392
2286
  const d = decl;
1393
2287
  if (!d.init) continue;
1394
- if (d.id.type !== "Identifier") continue;
1395
- const varName = d.id.name;
1396
2288
  const init = d.init;
1397
- if (!referencesAny(init, allNames, scope)) continue;
1398
- const parser2 = evaluateExpr(init, scope);
1399
- if (parser2 === null) {
1400
- anyUnresolved = true;
1401
- continue;
1402
- }
1403
- const compiled = compile(parser2);
1404
- if (compiled.inlineExpression === null) {
1405
- anyUnresolved = true;
1406
- continue;
2289
+ const kind = vd.kind ?? "const";
2290
+ if (d.id.type === "Identifier") {
2291
+ const varName = d.id.name;
2292
+ if (!referencesAny(init, allNames, scope)) continue;
2293
+ if (isRulesCall(init)) {
2294
+ const compiledRules = compileRulesFactory(init, varName);
2295
+ if (!compiledRules) continue;
2296
+ replacements.push({
2297
+ start: init.start,
2298
+ end: init.end,
2299
+ replacement: `{ ${compiledRules.entries.map(([k, expr]) => `${k}: ${expr}`).join(", ")} }`
2300
+ });
2301
+ continue;
2302
+ }
2303
+ const mapFnSources = [];
2304
+ const parser2 = evaluateExpr(init, scope, code, mapFnSources);
2305
+ if (parser2 === null) {
2306
+ warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
2307
+ continue;
2308
+ }
2309
+ const compiled = compile(parser2);
2310
+ if (compiled.inlineExpression === null) {
2311
+ warn(init.start, `"${varName}" couldn't be inlined (likely closes over a runtime value)`);
2312
+ continue;
2313
+ }
2314
+ replacements.push({
2315
+ start: init.start,
2316
+ end: init.end,
2317
+ replacement: compiled.inlineExpression
2318
+ });
2319
+ scope.set(varName, { combi: parser2, mfSrcs: mapFnSources });
2320
+ } else if (d.id.type === "ObjectPattern") {
2321
+ if (!referencesAny(init, allNames, scope)) continue;
2322
+ if (!isRulesCall(init)) {
2323
+ warn(init.start, `destructured macro binding must come from rules(...)`);
2324
+ continue;
2325
+ }
2326
+ const compiledRules = compileRulesFactory(init, "{ \u2026 }");
2327
+ if (!compiledRules) continue;
2328
+ const byKey = new Map(compiledRules.entries);
2329
+ const pattern = d.id;
2330
+ const lines = [];
2331
+ let allOk = true;
2332
+ for (const prop of pattern.properties) {
2333
+ const p = prop;
2334
+ if (p.type === "RestElement" || p.type === "BindingRestElement") {
2335
+ warn(init.start, `rest element in a rules() destructure isn't supported`);
2336
+ allOk = false;
2337
+ break;
2338
+ }
2339
+ const ruleKey = p.key.type === "Identifier" ? p.key.name : p.key.type === "StringLiteral" ? String(p.key.value) : null;
2340
+ const localName = p.value.type === "Identifier" || p.value.type === "BindingIdentifier" ? p.value.name : ruleKey;
2341
+ if (!ruleKey || !localName) {
2342
+ allOk = false;
2343
+ break;
2344
+ }
2345
+ const inlineExpr = byKey.get(ruleKey);
2346
+ if (inlineExpr === void 0) {
2347
+ warn(init.start, `destructured rule "${ruleKey}" isn't returned by the rules() factory`);
2348
+ allOk = false;
2349
+ break;
2350
+ }
2351
+ lines.push(`${exportPrefix}${kind} ${localName} = ${inlineExpr}`);
2352
+ const rule = compiledRules.ruleMap.get(ruleKey);
2353
+ if (rule) scope.set(localName, { combi: rule, mfSrcs: [] });
2354
+ }
2355
+ if (!allOk) continue;
2356
+ replacements.push({
2357
+ start: stmtStart,
2358
+ end: stmtEnd,
2359
+ replacement: lines.join("\n")
2360
+ });
1407
2361
  }
1408
- replacements.push({
1409
- start: init.start,
1410
- end: init.end,
1411
- replacement: compiled.inlineExpression
1412
- });
1413
- scope.set(varName, parser2);
1414
2362
  }
1415
2363
  }
1416
- if (replacements.length === 0) return null;
2364
+ if (replacements.length === 0 && warnings.length === 0) return null;
1417
2365
  for (const imp of macroImports) {
1418
2366
  imp.fullyResolved = !anyUnresolved;
1419
2367
  }
@@ -1432,7 +2380,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
1432
2380
  }
1433
2381
  return {
1434
2382
  code: ms.toString(),
1435
- map: ms.generateMap({ hires: true })
2383
+ map: ms.generateMap({ hires: true }),
2384
+ warnings
1436
2385
  };
1437
2386
  }
1438
2387
  export {