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