parseman 0.4.0 → 0.6.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 (65) hide show
  1. package/README.md +48 -17
  2. package/dist/combinators/choice.d.ts.map +1 -1
  3. package/dist/combinators/completions.d.ts +1 -1
  4. package/dist/combinators/completions.d.ts.map +1 -1
  5. package/dist/combinators/grammar.d.ts.map +1 -1
  6. package/dist/combinators/keywords.d.ts +22 -0
  7. package/dist/combinators/keywords.d.ts.map +1 -1
  8. package/dist/combinators/literal.d.ts.map +1 -1
  9. package/dist/combinators/map.d.ts +8 -2
  10. package/dist/combinators/map.d.ts.map +1 -1
  11. package/dist/combinators/node.d.ts +10 -11
  12. package/dist/combinators/node.d.ts.map +1 -1
  13. package/dist/combinators/not.d.ts +3 -3
  14. package/dist/combinators/not.d.ts.map +1 -1
  15. package/dist/combinators/recover.d.ts +2 -2
  16. package/dist/combinators/recover.d.ts.map +1 -1
  17. package/dist/combinators/regex.d.ts.map +1 -1
  18. package/dist/combinators/repeat.d.ts +4 -4
  19. package/dist/combinators/repeat.d.ts.map +1 -1
  20. package/dist/combinators/scanTo.d.ts.map +1 -1
  21. package/dist/combinators/sequence.d.ts.map +1 -1
  22. package/dist/combinators/trivia-skip.d.ts +16 -10
  23. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  24. package/dist/combinators/withCtx.d.ts +2 -2
  25. package/dist/combinators/withCtx.d.ts.map +1 -1
  26. package/dist/compiler/codegen.d.ts +4 -2
  27. package/dist/compiler/codegen.d.ts.map +1 -1
  28. package/dist/compiler/inline-build.d.ts +11 -0
  29. package/dist/compiler/inline-build.d.ts.map +1 -0
  30. package/dist/compiler/inline-callback.d.ts +12 -0
  31. package/dist/compiler/inline-callback.d.ts.map +1 -0
  32. package/dist/compiler/trivia-fast-path.d.ts +24 -0
  33. package/dist/compiler/trivia-fast-path.d.ts.map +1 -0
  34. package/dist/cst/capture-buffer.d.ts +46 -0
  35. package/dist/cst/capture-buffer.d.ts.map +1 -0
  36. package/dist/cst/trivia-entries.d.ts +20 -0
  37. package/dist/cst/trivia-entries.d.ts.map +1 -0
  38. package/dist/cst/trivia-index.d.ts +11 -11
  39. package/dist/cst/trivia-index.d.ts.map +1 -1
  40. package/dist/cst/trivia-kinds.d.ts +47 -0
  41. package/dist/cst/trivia-kinds.d.ts.map +1 -0
  42. package/dist/cst/types.d.ts +9 -13
  43. package/dist/cst/types.d.ts.map +1 -1
  44. package/dist/functional/doc.d.ts +2 -20
  45. package/dist/functional/doc.d.ts.map +1 -1
  46. package/dist/index.cjs +1238 -662
  47. package/dist/index.cjs.map +4 -4
  48. package/dist/index.d.ts +4 -5
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +1256 -6241
  51. package/dist/index.js.map +4 -4
  52. package/dist/plugin/evaluator.d.ts +7 -0
  53. package/dist/plugin/evaluator.d.ts.map +1 -1
  54. package/dist/plugin/index.cjs +1249 -369
  55. package/dist/plugin/index.cjs.map +4 -4
  56. package/dist/plugin/index.d.ts.map +1 -1
  57. package/dist/plugin/index.js +1249 -369
  58. package/dist/plugin/index.js.map +4 -4
  59. package/dist/types.d.ts +32 -9
  60. package/dist/types.d.ts.map +1 -1
  61. package/package.json +8 -3
  62. package/dist/cst/grammar.d.ts +0 -115
  63. package/dist/cst/grammar.d.ts.map +0 -1
  64. package/dist/cst/incremental.d.ts +0 -40
  65. package/dist/cst/incremental.d.ts.map +0 -1
@@ -93,6 +93,151 @@ function failAt(ctx, expected, pos) {
93
93
  return r;
94
94
  }
95
95
 
96
+ // src/cst/capture-buffer.ts
97
+ var EMPTY = [];
98
+ var EMPTY_TL = [];
99
+ function cstCaptureActive(ctx) {
100
+ return ctx._cstBuf !== void 0 || ctx._cstLeaves !== void 0;
101
+ }
102
+ function beginCstNodeCapture(ctx) {
103
+ const saved = {
104
+ ch: ctx._cstChildren,
105
+ lv: ctx._cstLeaves,
106
+ raw: ctx._cstRawChildren,
107
+ tl: ctx._cstTriviaLog,
108
+ cap: ctx.captureTrivia,
109
+ buf: ctx._cstBuf
110
+ };
111
+ ctx._cstBuf = {};
112
+ ctx._cstChildren = void 0;
113
+ ctx._cstLeaves = void 0;
114
+ ctx._cstRawChildren = void 0;
115
+ ctx._cstTriviaLog = void 0;
116
+ ctx.captureTrivia = true;
117
+ return saved;
118
+ }
119
+ function finishCstBuf(buf) {
120
+ if (!buf) return { children: EMPTY, rawChildren: EMPTY, triviaLog: EMPTY_TL };
121
+ const children = buf.ch ?? (buf.single !== void 0 ? [buf.single] : EMPTY);
122
+ const rawChildren = buf.raw ?? (buf.rawSingle !== void 0 ? [buf.rawSingle] : EMPTY);
123
+ const triviaLog = buf.tl ?? EMPTY_TL;
124
+ return { children, rawChildren, triviaLog };
125
+ }
126
+ function endCstNodeCapture(ctx, saved) {
127
+ const materialized = finishCstBuf(ctx._cstBuf);
128
+ ctx._cstBuf = saved.buf;
129
+ ctx._cstChildren = saved.ch;
130
+ ctx._cstLeaves = saved.lv;
131
+ ctx._cstRawChildren = saved.raw;
132
+ ctx._cstTriviaLog = saved.tl;
133
+ ctx.captureTrivia = saved.cap;
134
+ return materialized;
135
+ }
136
+ function pushCstLeaf(ctx, leaf) {
137
+ pushCstChild(ctx, leaf, leaf);
138
+ }
139
+ function pushCstChild(ctx, built, rawEntry) {
140
+ const b = ctx._cstBuf;
141
+ if (b) {
142
+ if (b.ch) b.ch.push(built);
143
+ else if (b.single !== void 0) {
144
+ b.ch = [b.single, built];
145
+ b.single = void 0;
146
+ } else b.single = built;
147
+ if (b.raw) b.raw.push(rawEntry);
148
+ else if (b.rawSingle !== void 0) {
149
+ b.raw = [b.rawSingle, rawEntry];
150
+ b.rawSingle = void 0;
151
+ } else b.rawSingle = rawEntry;
152
+ return;
153
+ }
154
+ if (ctx._cstChildren) ctx._cstChildren.push(built);
155
+ else if (ctx._cstLeaves) ctx._cstLeaves.push(built);
156
+ if (ctx._cstRawChildren) ctx._cstRawChildren.push(rawEntry);
157
+ }
158
+ function cstRawLen(ctx) {
159
+ const b = ctx._cstBuf;
160
+ if (b) {
161
+ if (b.raw) return b.raw.length;
162
+ return b.rawSingle !== void 0 ? 1 : 0;
163
+ }
164
+ return ctx._cstRawChildren?.length ?? 0;
165
+ }
166
+ function cstLeavesLen(ctx) {
167
+ const b = ctx._cstBuf;
168
+ if (b) {
169
+ if (b.ch) return b.ch.length;
170
+ return b.single !== void 0 ? 1 : 0;
171
+ }
172
+ return ctx._cstLeaves?.length ?? 0;
173
+ }
174
+ function cstTlLen(ctx) {
175
+ const b = ctx._cstBuf;
176
+ if (b) return b.tl?.length ?? 0;
177
+ return ctx._cstTriviaLog?.length ?? 0;
178
+ }
179
+ function saveCstMark(ctx) {
180
+ return {
181
+ raw: cstRawLen(ctx),
182
+ tlog: cstTlLen(ctx),
183
+ leaves: cstLeavesLen(ctx)
184
+ };
185
+ }
186
+ function rollbackBufList(b, keyMulti, keySingle, len) {
187
+ const arr = b[keyMulti];
188
+ if (arr) {
189
+ if (len === 0) b[keyMulti] = void 0;
190
+ else if (len === 1) {
191
+ b[keySingle] = arr[0];
192
+ b[keyMulti] = void 0;
193
+ } else arr.length = len;
194
+ return;
195
+ }
196
+ if (len === 0) b[keySingle] = void 0;
197
+ }
198
+ function rollbackCstCapture(ctx, mark) {
199
+ const b = ctx._cstBuf;
200
+ if (b) {
201
+ rollbackBufList(b, "raw", "rawSingle", mark.raw);
202
+ rollbackBufList(b, "ch", "single", mark.leaves);
203
+ if (b.tl) {
204
+ if (mark.tlog === 0) b.tl = void 0;
205
+ else b.tl.length = mark.tlog;
206
+ }
207
+ return;
208
+ }
209
+ if (ctx._cstRawChildren) ctx._cstRawChildren.length = mark.raw;
210
+ if (ctx._cstTriviaLog) ctx._cstTriviaLog.length = mark.tlog;
211
+ if (ctx._cstLeaves) ctx._cstLeaves.length = mark.leaves;
212
+ }
213
+ function pushCstTriviaEntry(ctx, start, end, kindIndex) {
214
+ const insertIdx = cstRawLen(ctx);
215
+ const b = ctx._cstBuf;
216
+ const withKind = ctx.triviaKindLabels !== void 0 && kindIndex !== void 0;
217
+ if (b) {
218
+ if (!b.tl) {
219
+ b.tl = withKind ? [start, end, insertIdx, kindIndex] : [start, end, insertIdx];
220
+ } else if (withKind) {
221
+ b.tl.push(start, end, insertIdx, kindIndex);
222
+ } else {
223
+ b.tl.push(start, end, insertIdx);
224
+ }
225
+ return;
226
+ }
227
+ if (ctx._cstTriviaLog) {
228
+ if (withKind) ctx._cstTriviaLog.push(start, end, insertIdx, kindIndex);
229
+ else ctx._cstTriviaLog.push(start, end, insertIdx);
230
+ }
231
+ }
232
+ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
233
+ if (!ctx._triviaLog) return;
234
+ if (ctx.triviaKindLabels !== void 0 && kindIndex !== void 0) {
235
+ ctx._triviaLog.push(start, end, kindIndex);
236
+ } else {
237
+ ctx._triviaLog.push(start, end);
238
+ }
239
+ }
240
+
96
241
  // src/combinators/literal.ts
97
242
  var _collatorCache = null;
98
243
  function collator() {
@@ -132,8 +277,7 @@ function literal(value, opts = {}) {
132
277
  if (matched) {
133
278
  const span = { start: pos, end };
134
279
  const leaf = { _tag: "leaf", value: slice, span };
135
- if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
136
- if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
280
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
137
281
  return { ok: true, value: slice, span };
138
282
  }
139
283
  return failAt(ctx, [JSON.stringify(value)], pos);
@@ -275,109 +419,60 @@ function regex(pattern, flags = "") {
275
419
  }
276
420
  const span = { start: pos, end: pos + m[0].length };
277
421
  const leaf = { _tag: "leaf", value: m[0], span };
278
- if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
279
- if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
422
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
280
423
  return { ok: true, value: m[0], span };
281
424
  }
282
425
  };
283
426
  }
284
427
 
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
-
334
- // src/combinators/sequence.ts
335
- function sequence(...parsers) {
336
- const meta = {
337
- firstSet: parsers[0]?._meta.firstSet ?? empty(),
338
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
339
- isTrivia: false
340
- };
428
+ // src/combinators/keywords.ts
429
+ function word(str, boundary = "_0-9A-Za-z") {
430
+ return keywords([str], { boundary });
431
+ }
432
+ function escapeRe(s) {
433
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
434
+ }
435
+ function keywords(words, opts = {}) {
436
+ const sorted = [...new Set(words)].sort((a, b) => b.length - a.length);
437
+ const alt = sorted.map(escapeRe).join("|");
438
+ const boundary = opts.boundary ? `(?![${opts.boundary}])` : "";
439
+ const flags = opts.caseInsensitive ? "iuy" : "uy";
440
+ const re = new RegExp(`(?:${alt})${boundary}`, flags);
441
+ let firstSet = empty();
442
+ for (const w of sorted) {
443
+ if (w.length === 0) continue;
444
+ const cp = w.codePointAt(0);
445
+ firstSet = union(firstSet, fromChar(cp));
446
+ if (opts.caseInsensitive) {
447
+ const u = String.fromCodePoint(cp).toUpperCase().codePointAt(0);
448
+ const l = String.fromCodePoint(cp).toLowerCase().codePointAt(0);
449
+ if (u !== void 0) firstSet = union(firstSet, fromChar(u));
450
+ if (l !== void 0) firstSet = union(firstSet, fromChar(l));
451
+ }
452
+ }
453
+ const meta = { firstSet, canMatchNewline: false, isTrivia: false };
341
454
  return {
342
- _tag: "sequence",
455
+ _tag: "keywords",
343
456
  _meta: meta,
344
- _def: { tag: "sequence", parsers },
457
+ _def: { tag: "keywords", words: sorted, caseInsensitive: opts.caseInsensitive ?? false, boundary: opts.boundary },
345
458
  parse(input, pos, ctx) {
346
- const values = [];
347
- let cur = pos;
348
- for (let i = 0; i < parsers.length; i++) {
349
- if (ctx.trivia && i > 0) {
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;
360
- }
361
- values.push(result2.value);
362
- continue;
363
- }
364
- const result = parsers[i].parse(input, cur, ctx);
365
- if (!result.ok) return result;
366
- values.push(result.value);
367
- cur = result.span.end;
459
+ re.lastIndex = pos;
460
+ const m = re.exec(input);
461
+ if (m === null || m.index !== pos) {
462
+ return failAt(ctx, ["keyword"], pos);
368
463
  }
369
- return {
370
- ok: true,
371
- value: values,
372
- span: { start: pos, end: cur }
373
- };
464
+ const value = m[0];
465
+ const span = { start: pos, end: pos + value.length };
466
+ const leaf = { _tag: "leaf", value, span };
467
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
468
+ return { ok: true, value, span };
374
469
  }
375
470
  };
376
471
  }
377
472
 
378
473
  // src/combinators/choice.ts
379
474
  function choice(...args) {
380
- const parsers = args.map((a) => "gate" in a ? a.parser : a);
475
+ const parsers = args.map((a) => "gate" in a ? a.combinator : a);
381
476
  const gates = args.map((a) => "gate" in a ? a.gate : null);
382
477
  const hasGates = gates.some((g) => g !== null);
383
478
  const disjoint = !hasGates && areDisjoint(parsers.map((p) => p._meta.firstSet));
@@ -461,19 +556,19 @@ function choice(...args) {
461
556
  }
462
557
  for (let i = 0; i < parsers.length; i++) {
463
558
  if (gates[i] && !gates[i](ctx.state)) continue;
464
- const leavesLen = ctx._cstLeaves?.length;
465
- const rawLen = ctx._cstRawChildren?.length;
559
+ const mark = saveCstMark(ctx);
560
+ const logLen = ctx._triviaLog?.length;
466
561
  const result = parsers[i].parse(input, pos, ctx);
467
562
  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;
563
+ rollbackCstCapture(ctx, mark);
564
+ if (logLen !== void 0 && ctx._triviaLog) ctx._triviaLog.length = logLen;
470
565
  expected.push(...result.expected);
471
566
  continue;
472
567
  }
473
568
  const checks = autoNot[i];
474
569
  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;
570
+ rollbackCstCapture(ctx, mark);
571
+ if (logLen !== void 0 && ctx._triviaLog) ctx._triviaLog.length = logLen;
477
572
  continue;
478
573
  }
479
574
  return result;
@@ -552,6 +647,7 @@ function getCoreRegexDef(p) {
552
647
  const def = p._def;
553
648
  if (def.tag === "regex") return { source: def.source, flags: def.flags };
554
649
  if (def.tag === "transform") return getCoreRegexDef(def.parser);
650
+ if (def.tag === "label") return getCoreRegexDef(def.parser);
555
651
  return null;
556
652
  }
557
653
  function applyTransforms(p, value, span) {
@@ -605,46 +701,275 @@ function areDisjoint(sets) {
605
701
  return true;
606
702
  }
607
703
 
704
+ // src/cst/trivia-kinds.ts
705
+ function unwrapTrivia(p) {
706
+ let cur = p;
707
+ while (cur._def.tag === "trivia") cur = cur._def.parser;
708
+ return cur;
709
+ }
710
+ function peelLabel(p) {
711
+ if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
712
+ return null;
713
+ }
714
+ function analyzeLabeledTrivia(trivia2) {
715
+ let core = unwrapTrivia(trivia2);
716
+ let minRepeats = 1;
717
+ if (core._def.tag === "oneOrMore") {
718
+ core = core._def.parser;
719
+ minRepeats = 1;
720
+ } else if (core._def.tag === "many") {
721
+ core = core._def.parser;
722
+ minRepeats = 0;
723
+ }
724
+ const arms = [];
725
+ if (core._def.tag === "choice") {
726
+ for (let i = 0; i < core._def.parsers.length; i++) {
727
+ const peeled = peelLabel(core._def.parsers[i]);
728
+ if (!peeled) return null;
729
+ arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
730
+ }
731
+ } else {
732
+ const peeled = peelLabel(core);
733
+ if (!peeled) return null;
734
+ arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
735
+ }
736
+ return { labels: arms.map((a) => a.label), arms, minRepeats };
737
+ }
738
+ function matchArmAt(input, pos, arm) {
739
+ const r = arm.parse(input, pos, { trackLines: false });
740
+ if (!r.ok || r.span.end <= pos) return null;
741
+ return { end: r.span.end };
742
+ }
743
+ function scanLabeledTriviaChunks(input, cur, spec) {
744
+ const chunks = [];
745
+ let pos = cur;
746
+ while (pos < input.length) {
747
+ let matched = null;
748
+ for (const arm of spec.arms) {
749
+ const m = matchArmAt(input, pos, arm.parser);
750
+ if (m) {
751
+ matched = { end: m.end, kindIndex: arm.kindIndex };
752
+ break;
753
+ }
754
+ }
755
+ if (!matched) break;
756
+ chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
757
+ pos = matched.end;
758
+ }
759
+ if (chunks.length < spec.minRepeats) {
760
+ return { end: cur, chunks: [] };
761
+ }
762
+ return { end: pos, chunks };
763
+ }
764
+ function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
765
+ const chunks = [];
766
+ let pos = cur;
767
+ while (pos < input.length) {
768
+ const c = input.charCodeAt(pos);
769
+ if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {
770
+ const start = pos;
771
+ pos++;
772
+ while (pos < input.length) {
773
+ const c2 = input.charCodeAt(pos);
774
+ if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) pos++;
775
+ else break;
776
+ }
777
+ chunks.push({ start, end: pos, kindIndex: wsKind });
778
+ continue;
779
+ }
780
+ if (c === 47 && input.charCodeAt(pos + 1) === 42) {
781
+ const start = pos;
782
+ let j = pos + 2;
783
+ while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++;
784
+ pos = j + 2 <= input.length ? j + 2 : input.length;
785
+ chunks.push({ start, end: pos, kindIndex: commentKind });
786
+ continue;
787
+ }
788
+ break;
789
+ }
790
+ return { end: pos, chunks };
791
+ }
792
+ function tryFastLabeledScan(input, cur, trivia2) {
793
+ const spec = analyzeLabeledTrivia(trivia2);
794
+ if (!spec || spec.arms.length !== 2) return null;
795
+ const wsArm = spec.arms.find((a) => {
796
+ const src = getCoreRegexDef(a.parser)?.source;
797
+ return src != null && !src.includes("\\*");
798
+ });
799
+ const commentArm = spec.arms.find((a) => {
800
+ const src = getCoreRegexDef(a.parser)?.source;
801
+ return src != null && src.includes("\\*");
802
+ });
803
+ if (!wsArm || !commentArm) return null;
804
+ const { chunks } = scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
805
+ if (chunks.length < spec.minRepeats) return { end: cur, chunks: [] };
806
+ return scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
807
+ }
808
+ function recordTriviaChunks(ctx, chunks) {
809
+ const kinds = ctx.triviaKindLabels;
810
+ for (const ch of chunks) {
811
+ pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
812
+ if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
813
+ pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
814
+ }
815
+ }
816
+ }
817
+
818
+ // src/combinators/trivia-skip.ts
819
+ var NOOP_COMMIT = () => {
820
+ };
821
+ function needsDeferredTriviaCommit(ctx) {
822
+ return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
823
+ }
824
+ function saveTriviaMark(ctx) {
825
+ const m = saveCstMark(ctx);
826
+ return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
827
+ }
828
+ function rollbackTrivia(ctx, mark) {
829
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves });
830
+ if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
831
+ }
832
+ function scanWithLabels(input, cur, ctx) {
833
+ const triviaP = ctx.trivia;
834
+ const spec = analyzeLabeledTrivia(triviaP);
835
+ if (!spec) return { end: cur, commit: NOOP_COMMIT };
836
+ const fast = tryFastLabeledScan(input, cur, triviaP);
837
+ const { end, chunks } = fast ?? scanLabeledTriviaChunks(input, cur, spec);
838
+ if (end === cur) return { end: cur, commit: NOOP_COMMIT };
839
+ return {
840
+ end,
841
+ commit: () => recordTriviaChunks(ctx, chunks)
842
+ };
843
+ }
844
+ function advanceTrivia(input, cur, ctx) {
845
+ const triviaP = ctx.trivia;
846
+ if (!triviaP) return cur;
847
+ if (ctx.triviaKindLabels) {
848
+ const scan = scanWithLabels(input, cur, ctx);
849
+ return scan.end;
850
+ }
851
+ const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
852
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
853
+ }
854
+ function scanTrivia(input, cur, ctx) {
855
+ const triviaP = ctx.trivia;
856
+ if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
857
+ const log = ctx._triviaLog;
858
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
859
+ if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
860
+ return scanWithLabels(input, cur, ctx);
861
+ }
862
+ if (log !== void 0 || captureTl) {
863
+ const tr2 = triviaP.parse(input, cur, {
864
+ trackLines: log !== void 0 ? false : ctx.trackLines,
865
+ state: ctx.state
866
+ });
867
+ if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
868
+ const end = tr2.span.end;
869
+ return {
870
+ end,
871
+ commit: () => {
872
+ pushTriviaLogEntry(ctx, cur, end);
873
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
874
+ }
875
+ };
876
+ }
877
+ const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
878
+ return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
879
+ }
880
+
881
+ // src/combinators/sequence.ts
882
+ function sequence(...parsers) {
883
+ const meta = {
884
+ firstSet: parsers[0]?._meta.firstSet ?? empty(),
885
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
886
+ isTrivia: false
887
+ };
888
+ return {
889
+ _tag: "sequence",
890
+ _meta: meta,
891
+ _def: { tag: "sequence", parsers },
892
+ parse(input, pos, ctx) {
893
+ const values = [];
894
+ let cur = pos;
895
+ for (let i = 0; i < parsers.length; i++) {
896
+ if (ctx.trivia && i > 0) {
897
+ let scanEnd;
898
+ let mark = saveTriviaMark(ctx);
899
+ if (needsDeferredTriviaCommit(ctx)) {
900
+ const scan = scanTrivia(input, cur, ctx);
901
+ scan.commit();
902
+ scanEnd = scan.end;
903
+ } else {
904
+ scanEnd = advanceTrivia(input, cur, ctx);
905
+ }
906
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
907
+ if (!result2.ok) return result2;
908
+ if (result2.span.end > scanEnd) {
909
+ cur = result2.span.end;
910
+ } else {
911
+ rollbackTrivia(ctx, mark);
912
+ }
913
+ values.push(result2.value);
914
+ continue;
915
+ }
916
+ const result = parsers[i].parse(input, cur, ctx);
917
+ if (!result.ok) return result;
918
+ values.push(result.value);
919
+ cur = result.span.end;
920
+ }
921
+ return {
922
+ ok: true,
923
+ value: values,
924
+ span: { start: pos, end: cur }
925
+ };
926
+ }
927
+ };
928
+ }
929
+
608
930
  // src/combinators/repeat.ts
609
- function repItem(parser2, input, cur, ctx) {
610
- const raw = ctx._cstRawChildren;
611
- const mark = raw ? raw.length : 0;
931
+ function repItem(combinator, input, cur, ctx) {
932
+ const mark = saveTriviaMark(ctx);
612
933
  let pos = cur;
613
934
  if (ctx.trivia) {
614
- const scan = scanTrivia(input, cur, ctx);
615
- scan.commit();
616
- pos = scan.end;
935
+ if (needsDeferredTriviaCommit(ctx)) {
936
+ const scan = scanTrivia(input, cur, ctx);
937
+ scan.commit();
938
+ pos = scan.end;
939
+ } else {
940
+ pos = advanceTrivia(input, cur, ctx);
941
+ }
617
942
  }
618
943
  if (pos >= input.length) {
619
- if (raw) raw.length = mark;
944
+ rollbackTrivia(ctx, mark);
620
945
  return "stop";
621
946
  }
622
- const result = parser2.parse(input, pos, ctx);
947
+ const result = combinator.parse(input, pos, ctx);
623
948
  if (!result.ok) {
624
- if (raw) raw.length = mark;
949
+ rollbackTrivia(ctx, mark);
625
950
  return { fail: result };
626
951
  }
627
952
  if (result.span.end === pos) {
628
- if (raw) raw.length = mark;
953
+ rollbackTrivia(ctx, mark);
629
954
  return "stop";
630
955
  }
631
956
  return { value: result.value, end: result.span.end };
632
957
  }
633
- function many(parser2) {
958
+ function many(combinator) {
634
959
  const meta = {
635
- firstSet: parser2._meta.firstSet,
636
- canMatchNewline: parser2._meta.canMatchNewline,
960
+ firstSet: combinator._meta.firstSet,
961
+ canMatchNewline: combinator._meta.canMatchNewline,
637
962
  isTrivia: false
638
963
  };
639
964
  return {
640
965
  _tag: "many",
641
966
  _meta: meta,
642
- _def: { tag: "many", parser: parser2, min: 0 },
967
+ _def: { tag: "many", parser: combinator, min: 0 },
643
968
  parse(input, pos, ctx) {
644
969
  const values = [];
645
970
  let cur = pos;
646
971
  while (cur < input.length) {
647
- const item = repItem(parser2, input, cur, ctx);
972
+ const item = repItem(combinator, input, cur, ctx);
648
973
  if (item === "stop" || "fail" in item) break;
649
974
  values.push(item.value);
650
975
  cur = item.end;
@@ -653,23 +978,23 @@ function many(parser2) {
653
978
  }
654
979
  };
655
980
  }
656
- function oneOrMore(parser2) {
981
+ function oneOrMore(combinator) {
657
982
  const meta = {
658
- firstSet: parser2._meta.firstSet,
659
- canMatchNewline: parser2._meta.canMatchNewline,
983
+ firstSet: combinator._meta.firstSet,
984
+ canMatchNewline: combinator._meta.canMatchNewline,
660
985
  isTrivia: false
661
986
  };
662
987
  return {
663
988
  _tag: "oneOrMore",
664
989
  _meta: meta,
665
- _def: { tag: "oneOrMore", parser: parser2, min: 1 },
990
+ _def: { tag: "oneOrMore", parser: combinator, min: 1 },
666
991
  parse(input, pos, ctx) {
667
- const first = parser2.parse(input, pos, ctx);
992
+ const first = combinator.parse(input, pos, ctx);
668
993
  if (!first.ok) return first;
669
994
  const values = [first.value];
670
995
  let cur = first.span.end;
671
996
  while (cur < input.length) {
672
- const item = repItem(parser2, input, cur, ctx);
997
+ const item = repItem(combinator, input, cur, ctx);
673
998
  if (item === "stop" || "fail" in item) break;
674
999
  values.push(item.value);
675
1000
  cur = item.end;
@@ -678,45 +1003,71 @@ function oneOrMore(parser2) {
678
1003
  }
679
1004
  };
680
1005
  }
681
- function optional(parser2) {
1006
+ function optional(combinator) {
682
1007
  const meta = {
683
- firstSet: parser2._meta.firstSet,
684
- canMatchNewline: parser2._meta.canMatchNewline,
1008
+ firstSet: combinator._meta.firstSet,
1009
+ canMatchNewline: combinator._meta.canMatchNewline,
685
1010
  isTrivia: false
686
1011
  };
687
1012
  return {
688
1013
  _tag: "optional",
689
1014
  _meta: meta,
690
- _def: { tag: "optional", parser: parser2 },
1015
+ _def: { tag: "optional", parser: combinator },
691
1016
  parse(input, pos, ctx) {
692
- const result = parser2.parse(input, pos, ctx);
1017
+ const result = combinator.parse(input, pos, ctx);
693
1018
  if (result.ok) return result;
694
1019
  return { ok: true, value: null, span: { start: pos, end: pos } };
695
1020
  }
696
1021
  };
697
1022
  }
698
- function sepBy(parser2, separator) {
1023
+ function sepBy(combinator, separator) {
699
1024
  const meta = {
700
- firstSet: parser2._meta.firstSet,
701
- canMatchNewline: parser2._meta.canMatchNewline || separator._meta.canMatchNewline,
1025
+ firstSet: combinator._meta.firstSet,
1026
+ canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
702
1027
  isTrivia: false
703
1028
  };
704
1029
  return {
705
1030
  _tag: "sepBy",
706
1031
  _meta: meta,
707
- _def: { tag: "sepBy", parser: parser2, separator },
1032
+ _def: { tag: "sepBy", parser: combinator, separator },
708
1033
  parse(input, pos, ctx) {
709
- const first = parser2.parse(input, pos, ctx);
1034
+ const first = combinator.parse(input, pos, ctx);
710
1035
  if (!first.ok) return { ok: true, value: [], span: { start: pos, end: pos } };
711
1036
  const values = [first.value];
712
1037
  let cur = first.span.end;
713
1038
  while (cur < input.length) {
714
- const sepPos = ctx.trivia ? consumeTrivia(input, cur, ctx) : cur;
1039
+ let mark = saveTriviaMark(ctx);
1040
+ let sepPos = cur;
1041
+ if (ctx.trivia) {
1042
+ if (needsDeferredTriviaCommit(ctx)) {
1043
+ const scan = scanTrivia(input, cur, ctx);
1044
+ scan.commit();
1045
+ sepPos = scan.end;
1046
+ } else {
1047
+ sepPos = advanceTrivia(input, cur, ctx);
1048
+ }
1049
+ }
715
1050
  const sep = separator.parse(input, sepPos, ctx);
716
- if (!sep.ok) break;
717
- const nextPos = ctx.trivia ? consumeTrivia(input, sep.span.end, ctx) : sep.span.end;
718
- const next = parser2.parse(input, nextPos, ctx);
719
- if (!next.ok) break;
1051
+ if (!sep.ok) {
1052
+ rollbackTrivia(ctx, mark);
1053
+ break;
1054
+ }
1055
+ mark = saveTriviaMark(ctx);
1056
+ let nextPos = sep.span.end;
1057
+ if (ctx.trivia) {
1058
+ if (needsDeferredTriviaCommit(ctx)) {
1059
+ const scan = scanTrivia(input, sep.span.end, ctx);
1060
+ scan.commit();
1061
+ nextPos = scan.end;
1062
+ } else {
1063
+ nextPos = advanceTrivia(input, sep.span.end, ctx);
1064
+ }
1065
+ }
1066
+ const next = combinator.parse(input, nextPos, ctx);
1067
+ if (!next.ok) {
1068
+ rollbackTrivia(ctx, mark);
1069
+ break;
1070
+ }
720
1071
  values.push(next.value);
721
1072
  cur = next.span.end;
722
1073
  }
@@ -726,7 +1077,7 @@ function sepBy(parser2, separator) {
726
1077
  }
727
1078
 
728
1079
  // src/combinators/not.ts
729
- function not(parser2) {
1080
+ function not(combinator) {
730
1081
  const meta = {
731
1082
  firstSet: any(),
732
1083
  // we don't know what NOT matches
@@ -736,11 +1087,11 @@ function not(parser2) {
736
1087
  return {
737
1088
  _tag: "not",
738
1089
  _meta: meta,
739
- _def: { tag: "not", parser: parser2 },
1090
+ _def: { tag: "not", parser: combinator },
740
1091
  parse(input, pos, ctx) {
741
- const result = parser2.parse(input, pos, ctx);
1092
+ const result = combinator.parse(input, pos, ctx);
742
1093
  if (result.ok) {
743
- return { ok: false, expected: [`not(${parser2._tag})`], span: { start: pos, end: pos } };
1094
+ return { ok: false, expected: [`not(${combinator._tag})`], span: { start: pos, end: pos } };
744
1095
  }
745
1096
  return { ok: true, value: null, span: { start: pos, end: pos } };
746
1097
  }
@@ -748,37 +1099,27 @@ function not(parser2) {
748
1099
  }
749
1100
 
750
1101
  // src/combinators/node.ts
751
- function node(type, parser2, build) {
1102
+ function node(type, combinator, build) {
752
1103
  const meta = {
753
- firstSet: parser2._meta.firstSet,
754
- canMatchNewline: parser2._meta.canMatchNewline,
1104
+ firstSet: combinator._meta.firstSet,
1105
+ canMatchNewline: combinator._meta.canMatchNewline,
755
1106
  isTrivia: false
756
1107
  };
757
1108
  return {
758
1109
  _tag: "node",
759
1110
  _meta: meta,
760
- _def: { tag: "node", type, parser: parser2, build },
1111
+ _def: { tag: "node", type, parser: combinator, build },
761
1112
  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);
1113
+ const saved = beginCstNodeCapture(ctx);
1114
+ const r = combinator.parse(input, pos, ctx);
1115
+ const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
774
1116
  if (!r.ok) return r;
775
- const built = build(children, rawChildren, r.span);
1117
+ const state = ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
1118
+ const built = build(children, rawChildren, r.span, triviaLog, state);
776
1119
  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
- );
1120
+ const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1121
+ if (saved.buf !== void 0 || saved.ch !== void 0) {
1122
+ pushCstChild(ctx, built, rawEntry);
782
1123
  }
783
1124
  return { ok: true, value: built, span: r.span };
784
1125
  }
@@ -786,24 +1127,37 @@ function node(type, parser2, build) {
786
1127
  }
787
1128
 
788
1129
  // src/combinators/map.ts
789
- function transform(parser2, fn) {
1130
+ function transform(combinator, fn) {
790
1131
  return {
791
1132
  _tag: "transform",
792
- _meta: parser2._meta,
793
- _def: { tag: "transform", parser: parser2, fn },
1133
+ _meta: combinator._meta,
1134
+ _def: { tag: "transform", parser: combinator, fn },
794
1135
  parse(input, pos, ctx) {
795
- const result = parser2.parse(input, pos, ctx);
1136
+ const result = combinator.parse(input, pos, ctx);
796
1137
  if (!result.ok) return result;
797
1138
  return { ...result, value: fn(result.value, result.span) };
798
1139
  }
799
1140
  };
800
1141
  }
801
- function trivia(parser2) {
1142
+ function trivia(combinator) {
1143
+ const kindLabels = analyzeLabeledTrivia(combinator)?.labels;
1144
+ return {
1145
+ _tag: combinator._tag,
1146
+ _meta: {
1147
+ ...combinator._meta,
1148
+ isTrivia: true,
1149
+ ...kindLabels ? { triviaKindLabels: kindLabels } : {}
1150
+ },
1151
+ _def: { tag: "trivia", parser: combinator },
1152
+ parse: combinator.parse.bind(combinator)
1153
+ };
1154
+ }
1155
+ function label(name, combinator) {
802
1156
  return {
803
- _tag: parser2._tag,
804
- _meta: { ...parser2._meta, isTrivia: true },
805
- _def: { tag: "trivia", parser: parser2 },
806
- parse: parser2.parse.bind(parser2)
1157
+ _tag: combinator._tag,
1158
+ _meta: combinator._meta,
1159
+ _def: { tag: "label", label: name, parser: combinator },
1160
+ parse: combinator.parse.bind(combinator)
807
1161
  };
808
1162
  }
809
1163
 
@@ -854,7 +1208,10 @@ function parser(opts, root) {
854
1208
  const ctx = {
855
1209
  ..._ctx,
856
1210
  trackLines,
857
- ...opts.trivia !== void 0 ? { trivia: opts.trivia } : {},
1211
+ ...opts.trivia !== void 0 ? {
1212
+ trivia: opts.trivia,
1213
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
1214
+ } : {},
858
1215
  ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {}
859
1216
  };
860
1217
  const result = root.parse(input, pos ?? 0, ctx);
@@ -867,6 +1224,320 @@ function parser(opts, root) {
867
1224
  };
868
1225
  }
869
1226
 
1227
+ // src/compiler/trivia-fast-path.ts
1228
+ var BLOCK_COMMENT = String.raw`\/\*(?:[^*]|\*(?!\/))*\*\/`;
1229
+ var WS_CLASS_SOURCES = /* @__PURE__ */ new Set([
1230
+ String.raw`[ \t\n\r\f]+`,
1231
+ String.raw`[ \t\n]+`,
1232
+ String.raw`[ \t]+`,
1233
+ String.raw`[\t\n\f\r ]+`
1234
+ ]);
1235
+ function unwrapTrivia2(p) {
1236
+ let cur = p;
1237
+ while (cur._def.tag === "trivia") cur = cur._def.parser;
1238
+ return cur;
1239
+ }
1240
+ function oneOrMoreInner(p) {
1241
+ const d = p._def;
1242
+ if (d.tag === "oneOrMore") return d.parser;
1243
+ if (d.tag === "many" && d.min >= 1) return d.parser;
1244
+ return null;
1245
+ }
1246
+ function choiceArms(p) {
1247
+ if (p._def.tag === "choice") return p._def.parsers;
1248
+ return null;
1249
+ }
1250
+ function isWsClassSource(source) {
1251
+ return WS_CLASS_SOURCES.has(source);
1252
+ }
1253
+ function isBlockCommentSource(source) {
1254
+ return source === BLOCK_COMMENT;
1255
+ }
1256
+ function analyzeTriviaFastPath(trivia2) {
1257
+ const core = unwrapTrivia2(trivia2);
1258
+ const direct = getCoreRegexDef(core)?.source;
1259
+ if (direct) {
1260
+ if (isWsClassSource(direct)) return "wsOnly";
1261
+ return null;
1262
+ }
1263
+ const inner = oneOrMoreInner(core);
1264
+ if (!inner) return null;
1265
+ const innerSrc = getCoreRegexDef(inner)?.source;
1266
+ if (innerSrc && isWsClassSource(innerSrc)) return "wsOnly";
1267
+ const arms = choiceArms(inner);
1268
+ if (!arms || arms.length !== 2) return null;
1269
+ let hasWs = false;
1270
+ let hasComment = false;
1271
+ for (const arm of arms) {
1272
+ const src = getCoreRegexDef(arm)?.source;
1273
+ if (!src) return null;
1274
+ if (isWsClassSource(src)) hasWs = true;
1275
+ else if (isBlockCommentSource(src)) hasComment = true;
1276
+ else return null;
1277
+ }
1278
+ if (hasWs && hasComment) return "wsComments";
1279
+ return null;
1280
+ }
1281
+ var CAP_RECORD = [
1282
+ ` if (_cap && _e > _pos) {`,
1283
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
1284
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
1285
+ ` }`
1286
+ ].join("\n");
1287
+ var WS_ONLY_LOOP = [
1288
+ ` while (_e < input.length) {`,
1289
+ ` const c = input.charCodeAt(_e)`,
1290
+ ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) { _e++; continue }`,
1291
+ ` break`,
1292
+ ` }`
1293
+ ].join("\n");
1294
+ var WS_COMMENTS_LOOP = [
1295
+ ` while (_e < input.length) {`,
1296
+ ` const c = input.charCodeAt(_e)`,
1297
+ ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) { _e++; continue }`,
1298
+ ` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
1299
+ ` let j = _e + 2`,
1300
+ ` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
1301
+ ` _e = j + 2 <= input.length ? j + 2 : input.length`,
1302
+ ` continue`,
1303
+ ` }`,
1304
+ ` break`,
1305
+ ` }`
1306
+ ].join("\n");
1307
+ var CAP_CHUNK = (wsKind, commentKind) => [
1308
+ ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {`,
1309
+ ` const _cs = _e`,
1310
+ ` _e++`,
1311
+ ` while (_e < input.length) {`,
1312
+ ` const c2 = input.charCodeAt(_e)`,
1313
+ ` if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) _e++`,
1314
+ ` else break`,
1315
+ ` }`,
1316
+ ` if (_cap) {`,
1317
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${wsKind})`,
1318
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${wsKind})`,
1319
+ ` }`,
1320
+ ` continue`,
1321
+ ` }`,
1322
+ ` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
1323
+ ` const _cs = _e`,
1324
+ ` let j = _e + 2`,
1325
+ ` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
1326
+ ` _e = j + 2 <= input.length ? j + 2 : input.length`,
1327
+ ` if (_cap) {`,
1328
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${commentKind})`,
1329
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${commentKind})`,
1330
+ ` }`,
1331
+ ` continue`,
1332
+ ` }`
1333
+ ].join("\n");
1334
+ var WS_COMMENTS_LOOP_LABELED = (wsKind, commentKind) => [
1335
+ ` while (_e < input.length) {`,
1336
+ ` const c = input.charCodeAt(_e)`,
1337
+ CAP_CHUNK(wsKind, commentKind),
1338
+ ` break`,
1339
+ ` }`
1340
+ ].join("\n");
1341
+ function buildFastTriviaFnDecl(fnName, kind, kindIndices) {
1342
+ const loop = kind === "wsComments" && kindIndices ? WS_COMMENTS_LOOP_LABELED(kindIndices.ws, kindIndices.comment) : kind === "wsComments" ? WS_COMMENTS_LOOP : WS_ONLY_LOOP;
1343
+ const cap = kindIndices ? "" : CAP_RECORD;
1344
+ const lines = [
1345
+ `function ${fnName}(input, _pos, _ctx, _cap) {`,
1346
+ ` let _e = _pos`,
1347
+ loop
1348
+ ];
1349
+ if (cap) lines.push(cap);
1350
+ lines.push(` return _e`, `}`);
1351
+ return lines.join("\n");
1352
+ }
1353
+ function regexArmIndex(spec, isComment) {
1354
+ for (const arm of spec.arms) {
1355
+ const src = getCoreRegexDef(arm.parser)?.source;
1356
+ if (!src) return null;
1357
+ const comment = src.includes("\\*") || src.startsWith("\\/\\/");
1358
+ if (comment === isComment) return arm.kindIndex;
1359
+ }
1360
+ return null;
1361
+ }
1362
+ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
1363
+ const tryArms = spec.arms.map((arm, i) => {
1364
+ const re = reNames[i];
1365
+ const k = arm.kindIndex;
1366
+ return [
1367
+ ` ${re}.lastIndex = _e`,
1368
+ ` const _m${i} = ${re}.exec(input)`,
1369
+ ` if (_m${i} && _m${i}.index === _e) {`,
1370
+ ` const _ce = _e + _m${i}[0].length`,
1371
+ ` if (_cap) {`,
1372
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
1373
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
1374
+ ` }`,
1375
+ ` _e = _ce`,
1376
+ ` _matched = true`,
1377
+ ` continue`,
1378
+ ` }`
1379
+ ].join("\n");
1380
+ }).join("\n");
1381
+ return [
1382
+ `function ${fnName}(input, _pos, _ctx, _cap) {`,
1383
+ ` let _e = _pos`,
1384
+ ` while (_e < input.length) {`,
1385
+ ` let _matched = false`,
1386
+ tryArms,
1387
+ ` if (!_matched) break`,
1388
+ ` }`,
1389
+ ` return _e`,
1390
+ `}`
1391
+ ].join("\n");
1392
+ }
1393
+ function labeledTriviaKindIndices(trivia2) {
1394
+ const spec = analyzeLabeledTrivia(trivia2);
1395
+ if (!spec || spec.arms.length !== 2) return null;
1396
+ const ws = regexArmIndex(spec, false);
1397
+ const comment = regexArmIndex(spec, true);
1398
+ if (ws === null || comment === null) return null;
1399
+ return { ws, comment };
1400
+ }
1401
+ function labeledTriviaRegexArms(trivia2) {
1402
+ const spec = analyzeLabeledTrivia(trivia2);
1403
+ if (!spec) return null;
1404
+ for (const arm of spec.arms) {
1405
+ if (!getCoreRegexDef(arm.parser)) return null;
1406
+ }
1407
+ return spec;
1408
+ }
1409
+ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
1410
+ const tryArms = spec.arms.map((arm, i) => {
1411
+ const k = arm.kindIndex;
1412
+ const ri = rpStartIndex + i;
1413
+ return [
1414
+ ` const _r${i} = _rp[${ri}].parse(input, _e, _ctx)`,
1415
+ ` if (_r${i}.ok && _r${i}.span.end > _e) {`,
1416
+ ` const _ce = _r${i}.span.end`,
1417
+ ` if (_cap) {`,
1418
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
1419
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
1420
+ ` }`,
1421
+ ` _e = _ce`,
1422
+ ` _matched = true`,
1423
+ ` continue`,
1424
+ ` }`
1425
+ ].join("\n");
1426
+ }).join("\n");
1427
+ return [
1428
+ `function ${fnName}(input, _pos, _ctx, _cap) {`,
1429
+ ` let _e = _pos`,
1430
+ ` while (_e < input.length) {`,
1431
+ ` let _matched = false`,
1432
+ tryArms,
1433
+ ` if (!_matched) break`,
1434
+ ` }`,
1435
+ ` return _e`,
1436
+ `}`
1437
+ ].join("\n");
1438
+ }
1439
+
1440
+ // src/compiler/inline-build.ts
1441
+ var MK_BUILD_RE = /^\(\s*(?:\w+\s*,\s*){3}\w+\s*\)\s*=>\s*mk\s*\(\s*(['"])([^'"]+)\1\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*\)\s*$/;
1442
+ function escapeRegExp(s) {
1443
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1444
+ }
1445
+ function looseMkBuildRe(type) {
1446
+ return new RegExp(
1447
+ String.raw`^\(\s*(?:\w+\s*,\s*){3}\w+\s*\)\s*=>\s*.+\(\s*(['"])${escapeRegExp(type)}\1\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*\)\s*$`
1448
+ );
1449
+ }
1450
+ function analyzeMkInlineBuild(def) {
1451
+ const src = (def.buildSrc ?? def.build.toString()).trim();
1452
+ const strict = src.match(MK_BUILD_RE);
1453
+ if (strict) {
1454
+ const mkType = strict[2];
1455
+ return mkType === def.type ? def.type : null;
1456
+ }
1457
+ if (looseMkBuildRe(def.type).test(src)) return def.type;
1458
+ return null;
1459
+ }
1460
+ function emitInlineMkNodeExpr(type, chV, rawV, pos, endVar, tlV) {
1461
+ return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: { start: ${pos}, end: ${endVar} }, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
1462
+ }
1463
+
1464
+ // src/compiler/inline-callback.ts
1465
+ var INLINE_BUILTINS = /* @__PURE__ */ new Set([
1466
+ "parseInt",
1467
+ "parseFloat",
1468
+ "String",
1469
+ "Number",
1470
+ "Boolean",
1471
+ "Object",
1472
+ "Array",
1473
+ "Math",
1474
+ "JSON",
1475
+ "undefined",
1476
+ "null",
1477
+ "true",
1478
+ "false",
1479
+ "NaN",
1480
+ "Infinity"
1481
+ ]);
1482
+ function escapeRegExp2(s) {
1483
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1484
+ }
1485
+ function transformFnSource(fn, fnSrc) {
1486
+ if (fnSrc) return fnSrc.trim();
1487
+ const s = fn.toString().trim();
1488
+ if (s.includes("[native code]")) return null;
1489
+ return s;
1490
+ }
1491
+ function replaceParam(body, param, valueVar) {
1492
+ return body.replace(new RegExp(`\\b${escapeRegExp2(param)}\\b`, "g"), valueVar);
1493
+ }
1494
+ function stripForIdCheck(body) {
1495
+ return body.replace(/'[^'\\]*(?:\\.[^'\\]*)*'|"[^"\\]*(?:\\.[^"\\]*)*"/g, '""').replace(/\bas\s+const\b/g, "").replace(/\b[A-Za-z_$][\w$]*\s*:/g, ":");
1496
+ }
1497
+ function hasFreeIdentifiers(body, allowed) {
1498
+ const stripped = stripForIdCheck(body);
1499
+ const ids = stripped.match(/\b[A-Za-z_$][\w$]*\b/g) ?? [];
1500
+ for (const id of ids) {
1501
+ if (INLINE_BUILTINS.has(id)) continue;
1502
+ if (allowed.has(id)) continue;
1503
+ return true;
1504
+ }
1505
+ return false;
1506
+ }
1507
+ function tryInlineUnaryTransform(src, valueVar) {
1508
+ const allowed = /* @__PURE__ */ new Set([valueVar]);
1509
+ const nullary = src.match(/^\(\s*\)\s*=>\s*(.+)$/);
1510
+ if (nullary) {
1511
+ const body2 = nullary[1].trim();
1512
+ return hasFreeIdentifiers(body2, allowed) ? null : body2;
1513
+ }
1514
+ const m = src.match(/^(\w+)\s*=>\s*(.+)$/);
1515
+ if (!m) return null;
1516
+ const body = replaceParam(m[2].trim(), m[1], valueVar);
1517
+ return hasFreeIdentifiers(body, allowed) ? null : body;
1518
+ }
1519
+ function parseArrayDestructure(pattern, arity) {
1520
+ const slots = [];
1521
+ for (const part of pattern.split(",")) {
1522
+ const name = part.trim();
1523
+ slots.push(name === "" || name === "_" ? null : name);
1524
+ }
1525
+ while (slots.length < arity) slots.push(null);
1526
+ return slots.slice(0, arity);
1527
+ }
1528
+ function tryInlineDestructureTransform(src, valueVars) {
1529
+ const m = src.match(/^\(\s*\[([^\]]*)\]\s*\)\s*=>\s*(.+)$/s);
1530
+ if (!m) return null;
1531
+ const slots = parseArrayDestructure(m[1], valueVars.length);
1532
+ let body = m[2].trim();
1533
+ const allowed = new Set(valueVars);
1534
+ for (let i = 0; i < slots.length; i++) {
1535
+ const name = slots[i];
1536
+ if (name) body = replaceParam(body, name, valueVars[i]);
1537
+ }
1538
+ return hasFreeIdentifiers(body, allowed) ? null : body;
1539
+ }
1540
+
870
1541
  // src/compiler/codegen.ts
871
1542
  function v(ctx, prefix = "_v") {
872
1543
  return `${prefix}${ctx.vars++}`;
@@ -874,12 +1545,42 @@ function v(ctx, prefix = "_v") {
874
1545
  function ind(ctx) {
875
1546
  return " ".repeat(ctx.indent);
876
1547
  }
1548
+ var NAMED_FN_FAIL = "_pfFail";
1549
+ var NAMED_FN_END = "_pfEnd";
1550
+ function namedFnPrelude() {
1551
+ return [`const ${NAMED_FN_FAIL} = {}`, `let ${NAMED_FN_END}`];
1552
+ }
1553
+ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
1554
+ ctx.namedFnDecls.push([
1555
+ `function ${fnName}(input, _pos, _ctx) {`,
1556
+ ` let _pfok = false, _pfv, _pfe = _pos`,
1557
+ ` _pfail: {`,
1558
+ ...bodyStmts,
1559
+ ` _pfv = ${valueVar}; _pfe = ${endVar}; _pfok = true`,
1560
+ ` }`,
1561
+ ` if (!_pfok) return ${NAMED_FN_FAIL}`,
1562
+ ` ${NAMED_FN_END} = _pfe`,
1563
+ ` return _pfv`,
1564
+ `}`
1565
+ ].join("\n"));
1566
+ }
1567
+ function emitNamedFnCall(ctx, fnName, pos, failExpected) {
1568
+ const vv = v(ctx, "_pfv");
1569
+ const ev = v(ctx, "_pfe");
1570
+ return {
1571
+ stmts: [
1572
+ `${ind(ctx)}const ${vv} = ${fnName}(input, ${pos}, _ctx)`,
1573
+ `${ind(ctx)}if (${vv} === ${NAMED_FN_FAIL}) ${failStmt({ ...ctx, indent: 0 }, failExpected, pos).trim()}`,
1574
+ `${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
1575
+ ],
1576
+ valueVar: vv,
1577
+ endVar: ev
1578
+ };
1579
+ }
877
1580
  function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
878
1581
  if (!ctx.capturing) return [];
1582
+ if (ctx.capAsTrivia) return [];
879
1583
  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
1584
  const lf = v(ctx, "_lf");
884
1585
  return [
885
1586
  `${i}if (_ctx._cstLeaves) {`,
@@ -889,35 +1590,84 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
889
1590
  `${i}}`
890
1591
  ];
891
1592
  }
892
- function ensureTriviaCaptureFn(ctx) {
1593
+ function ensureRegexDecl(ctx, optimizedSource, flags) {
1594
+ const f = "y" + flags.replace(/[gy]/g, "");
1595
+ const key = `${optimizedSource}/${f}`;
1596
+ let rName = ctx.regexMap.get(key);
1597
+ if (rName === void 0) {
1598
+ rName = `_re${ctx.regexDecls.length}`;
1599
+ ctx.regexDecls.push(`const ${rName} = /${optimizedSource}/${f}`);
1600
+ ctx.regexMap.set(key, rName);
1601
+ }
1602
+ return rName;
1603
+ }
1604
+ function ensureTriviaFn(ctx) {
893
1605
  const trivia2 = ctx.activeTrivia;
894
- const existing = ctx.triviaCaptureNames.get(trivia2);
1606
+ const existing = ctx.triviaFnNames.get(trivia2);
895
1607
  if (existing) return existing;
896
- const fnName = `_tc${ctx.triviaCaptureNames.size}`;
1608
+ const fnName = `_tf${ctx.triviaFnNames.size}`;
1609
+ ctx.triviaFnNames.set(trivia2, fnName);
897
1610
  ctx.triviaCaptureNames.set(trivia2, fnName);
898
- const saved = { indent: ctx.indent, failLabel: ctx.failLabel, asTrivia: ctx.capAsTrivia, trivia: ctx.activeTrivia };
1611
+ const fastKind = analyzeTriviaFastPath(trivia2);
1612
+ const kindIndices = labeledTriviaKindIndices(trivia2);
1613
+ if (fastKind) {
1614
+ ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastKind, kindIndices ?? void 0));
1615
+ return fnName;
1616
+ }
1617
+ const labeledSpec = analyzeLabeledTrivia(trivia2);
1618
+ if (labeledSpec) {
1619
+ const regexSpec = labeledTriviaRegexArms(trivia2);
1620
+ if (regexSpec) {
1621
+ const reNames = [];
1622
+ for (const arm of regexSpec.arms) {
1623
+ const def = arm.parser._def;
1624
+ if (def.tag !== "regex") break;
1625
+ reNames.push(ensureRegexDecl(ctx, def.optimizedSource, def.flags));
1626
+ }
1627
+ if (reNames.length === regexSpec.arms.length) {
1628
+ ctx.namedFnDecls.push(buildLabeledRegexTriviaFnDecl(fnName, regexSpec, reNames));
1629
+ return fnName;
1630
+ }
1631
+ }
1632
+ const rpStart = ctx.runtimeParsers.length;
1633
+ for (const arm of labeledSpec.arms) {
1634
+ ctx.runtimeParsers.push(arm.parser);
1635
+ }
1636
+ ctx.namedFnDecls.push(buildLabeledRuntimeTriviaFnDecl(fnName, labeledSpec, rpStart));
1637
+ return fnName;
1638
+ }
1639
+ const savedIndent = ctx.indent;
1640
+ const savedFailLabel = ctx.failLabel;
1641
+ const savedTrivia = ctx.activeTrivia;
1642
+ const savedCapAsTrivia = ctx.capAsTrivia;
899
1643
  ctx.indent = 2;
900
- ctx.failLabel = "_cap";
1644
+ ctx.failLabel = "_triv";
901
1645
  ctx.capAsTrivia = true;
902
- delete ctx.activeTrivia;
1646
+ ctx.activeTrivia = void 0;
903
1647
  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;
1648
+ ctx.indent = savedIndent;
1649
+ ctx.failLabel = savedFailLabel;
1650
+ ctx.capAsTrivia = savedCapAsTrivia;
1651
+ ctx.activeTrivia = savedTrivia;
908
1652
  ctx.namedFnDecls.push([
909
- `function ${fnName}(input, _pos, _ctx) {`,
1653
+ `function ${fnName}(input, _pos, _ctx, _cap) {`,
910
1654
  ` let _e = _pos`,
911
- ` _cap: {`,
1655
+ ` _triv: {`,
912
1656
  ...r.stmts,
913
1657
  ` _e = ${r.endVar}`,
914
1658
  ` }`,
915
- ` if (_ctx._triviaLog !== undefined && _e > _pos) _ctx._triviaLog.push(_pos, _e)`,
1659
+ ` if (_cap && _e > _pos) {`,
1660
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
1661
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
1662
+ ` }`,
916
1663
  ` return _e`,
917
1664
  `}`
918
1665
  ].join("\n"));
919
1666
  return fnName;
920
1667
  }
1668
+ function ensureTriviaCaptureFn(ctx) {
1669
+ return ensureTriviaFn(ctx);
1670
+ }
921
1671
  function failStmt(ctx, expected, posExpr) {
922
1672
  if (ctx.failLabel) return `${ind(ctx)}break ${ctx.failLabel}`;
923
1673
  return `${ind(ctx)}return { ok: false, expected: [${expected}], span: { start: ${posExpr}, end: ${posExpr} } }`;
@@ -933,14 +1683,6 @@ function firstSetCond(codeVar, fs) {
933
1683
  (r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
934
1684
  ).join(" || ");
935
1685
  }
936
- function asIIFE(stmts, valueVar, endVar, startPos, indent) {
937
- return [
938
- `(() => {`,
939
- ...stmts,
940
- `${indent} return { ok: true, value: ${valueVar}, span: { start: ${startPos}, end: ${endVar} } }`,
941
- `${indent}})()`
942
- ].join("\n");
943
- }
944
1686
  function emitFallible(inner, ctx, pos) {
945
1687
  const lbl = v(ctx, "_lbl");
946
1688
  const okV = `${lbl}ok`;
@@ -1006,6 +1748,33 @@ function emitLit(def, ctx, pos) {
1006
1748
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
1007
1749
  return { stmts, valueVar: vv, endVar };
1008
1750
  }
1751
+ function escapeKeywordRe(s) {
1752
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1753
+ }
1754
+ function emitKeywords(def, ctx, pos) {
1755
+ const alt = def.words.map(escapeKeywordRe).join("|");
1756
+ const boundary = def.boundary ? `(?![${def.boundary}])` : "";
1757
+ const flags = def.caseInsensitive ? "iuy" : "uy";
1758
+ const source = `(?:${alt})${boundary}`;
1759
+ const key = `${source}/${flags}`;
1760
+ let rName = ctx.regexMap.get(key);
1761
+ if (rName === void 0) {
1762
+ rName = `_re${ctx.regexDecls.length}`;
1763
+ ctx.regexDecls.push(`const ${rName} = /${source}/${flags}`);
1764
+ ctx.regexMap.set(key, rName);
1765
+ }
1766
+ const mv = v(ctx, "_m");
1767
+ const vv = v(ctx);
1768
+ const stmts = [
1769
+ `${ind(ctx)}${rName}.lastIndex = ${pos}`,
1770
+ `${ind(ctx)}const ${mv} = ${rName}.exec(input)`,
1771
+ `${ind(ctx)}if (${mv} === null) ${failStmt({ ...ctx, indent: 0 }, '"keyword"', pos).trim()}`,
1772
+ `${ind(ctx)}const ${vv} = ${mv}[0]`
1773
+ ];
1774
+ const endVar = `${pos} + ${vv}.length`;
1775
+ stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
1776
+ return { stmts, valueVar: vv, endVar };
1777
+ }
1009
1778
  function emitRegex(def, ctx, pos) {
1010
1779
  const flags = "y" + def.flags.replace(/[gy]/g, "");
1011
1780
  const key = `${def.optimizedSource}/${flags}`;
@@ -1028,31 +1797,7 @@ function emitRegex(def, ctx, pos) {
1028
1797
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
1029
1798
  return { stmts, valueVar: vv, endVar };
1030
1799
  }
1031
- function ensureTriviaFn(ctx) {
1032
- const trivia2 = ctx.activeTrivia;
1033
- if (!ctx.namedParsers.has(trivia2)) {
1034
- const fnName = `_pf${ctx.namedParsers.size}`;
1035
- ctx.namedParsers.set(trivia2, fnName);
1036
- const savedIndent = ctx.indent;
1037
- const savedFailLabel = ctx.failLabel;
1038
- const savedTrivia = ctx.activeTrivia;
1039
- ctx.indent = 1;
1040
- ctx.failLabel = void 0;
1041
- delete ctx.activeTrivia;
1042
- const r = emit(trivia2, ctx, "_pos");
1043
- ctx.indent = savedIndent;
1044
- ctx.failLabel = savedFailLabel;
1045
- if (savedTrivia) ctx.activeTrivia = savedTrivia;
1046
- ctx.namedFnDecls.push([
1047
- `function ${fnName}(input, _pos, _ctx) {`,
1048
- ...r.stmts,
1049
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
1050
- `}`
1051
- ].join("\n"));
1052
- }
1053
- return ctx.namedParsers.get(trivia2);
1054
- }
1055
- function emitSeq(def, ctx, pos) {
1800
+ function emitSeqValues(def, ctx, pos) {
1056
1801
  const startV = v(ctx, "_start");
1057
1802
  const curV = v(ctx, "_cur");
1058
1803
  const stmts = [
@@ -1064,23 +1809,41 @@ function emitSeq(def, ctx, pos) {
1064
1809
  if (i > 0 && ctx.activeTrivia) {
1065
1810
  if (ctx.capturing) {
1066
1811
  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");
1812
+ const markV = v(ctx, "_mk");
1813
+ const markTl = v(ctx, "_mktl");
1814
+ const markLog = v(ctx, "_mklg");
1815
+ const scanEndV = v(ctx, "_sne");
1071
1816
  stmts.push(
1072
- `${ind(ctx)}const ${tmp} = ${trivFn}(input, ${curV}, _ctx)`,
1073
- `${ind(ctx)}if (${tmp}.ok) ${curV} = ${tmp}.span.end`
1817
+ `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
1818
+ `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
1819
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
1820
+ `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`
1074
1821
  );
1822
+ const r2 = emit(def.parsers[i], ctx, scanEndV);
1823
+ stmts.push(...r2.stmts);
1824
+ const endAfterV = v(ctx, "_sea");
1825
+ stmts.push(
1826
+ `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
1827
+ `${ind(ctx)}if (${endAfterV} > ${scanEndV}) { ${curV} = ${endAfterV} } else { if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; }`
1828
+ );
1829
+ valueVars.push(r2.valueVar);
1830
+ continue;
1831
+ } else {
1832
+ const trivFn = ensureTriviaFn(ctx);
1833
+ stmts.push(`${ind(ctx)}${curV} = ${trivFn}(input, ${curV}, _ctx)`);
1075
1834
  }
1076
1835
  }
1077
1836
  const r = emit(def.parsers[i], ctx, curV);
1078
1837
  stmts.push(...r.stmts, `${ind(ctx)}${curV} = ${r.endVar}`);
1079
1838
  valueVars.push(r.valueVar);
1080
1839
  }
1840
+ return { stmts, valueVar: valueVars[valueVars.length - 1] ?? "null", endVar: curV, valueVars };
1841
+ }
1842
+ function emitSeq(def, ctx, pos) {
1843
+ const { stmts, endVar, valueVars } = emitSeqValues(def, ctx, pos);
1081
1844
  const arrV = v(ctx, "_arr");
1082
1845
  stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
1083
- return { stmts, valueVar: arrV, endVar: curV };
1846
+ return { stmts, valueVar: arrV, endVar };
1084
1847
  }
1085
1848
  function emitChoice(def, ctx, pos) {
1086
1849
  const allExpected = JSON.stringify(
@@ -1131,29 +1894,39 @@ function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
1131
1894
  const matchV = v(ctx, "_gm");
1132
1895
  const wordV = v(ctx, "_gw");
1133
1896
  const endV = v(ctx, "_ge");
1897
+ const valV = v(ctx, "_gcv");
1134
1898
  const stmts = [
1135
1899
  `${ind(ctx)}${reVar}.lastIndex = ${pos}`,
1136
1900
  `${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
1137
1901
  `${ind(ctx)}if (${matchV} === null) ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`,
1138
1902
  `${ind(ctx)}const ${wordV} = ${matchV}[0]`,
1139
- `${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`
1903
+ `${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
1904
+ `${ind(ctx)}let ${valV}`
1140
1905
  ];
1906
+ let first = true;
1141
1907
  for (let i = 0; i < def.parsers.length; i++) {
1142
1908
  if (i === superIndex) continue;
1143
1909
  const p = def.parsers[i];
1144
1910
  const litVal = getCoreLiteralValue(p);
1145
1911
  if (litVal === null) continue;
1146
- stmts.push(`${ind(ctx)}if (${wordV} === ${JSON.stringify(litVal)}) {`);
1912
+ const kw = first ? "if" : "else if";
1913
+ first = false;
1914
+ stmts.push(`${ind(ctx)}${kw} (${wordV} === ${JSON.stringify(litVal)}) {`);
1147
1915
  ctx.indent++;
1148
1916
  const tR = emitTransformChain(p, JSON.stringify(litVal), endV, pos, ctx);
1149
- stmts.push(...tR.stmts);
1150
- stmts.push(`${ind(ctx)}return { ok: true, value: ${tR.valueVar}, span: { start: ${pos}, end: ${endV} } }`);
1917
+ stmts.push(...emitLeafCapture(ctx, JSON.stringify(litVal), pos, endV));
1918
+ stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}`);
1151
1919
  ctx.indent--;
1152
1920
  stmts.push(`${ind(ctx)}}`);
1153
1921
  }
1154
1922
  const rR = emitTransformChain(superParser, wordV, endV, pos, ctx);
1155
- stmts.push(...rR.stmts);
1156
- return { stmts, valueVar: rR.valueVar, endVar: endV };
1923
+ const regexKw = first ? "if" : "else";
1924
+ stmts.push(`${ind(ctx)}${regexKw} {`);
1925
+ ctx.indent++;
1926
+ stmts.push(...emitLeafCapture(ctx, wordV, pos, endV), ...rR.stmts, `${ind(ctx)}${valV} = ${rR.valueVar}`);
1927
+ ctx.indent--;
1928
+ stmts.push(`${ind(ctx)}}`);
1929
+ return { stmts, valueVar: valV, endVar: endV };
1157
1930
  }
1158
1931
  function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
1159
1932
  const valV = v(ctx, "_llv");
@@ -1167,10 +1940,15 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
1167
1940
  const litCond = emitLiteralCondition(litVal, pos);
1168
1941
  const kw = first ? "if" : "else if";
1169
1942
  first = false;
1943
+ const litEnd = `${pos} + ${litLen}`;
1170
1944
  stmts.push(`${ind(ctx)}${kw} (${litCond}) {`);
1171
1945
  ctx.indent++;
1172
- const tR = emitTransformChain(p, JSON.stringify(litVal), `${pos} + ${litLen}`, pos, ctx);
1173
- stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}; ${endV} = ${pos} + ${litLen}`);
1946
+ const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
1947
+ stmts.push(
1948
+ ...emitLeafCapture(ctx, JSON.stringify(litVal), pos, litEnd),
1949
+ ...tR.stmts,
1950
+ `${ind(ctx)}${valV} = ${tR.valueVar}; ${endV} = ${litEnd}`
1951
+ );
1174
1952
  ctx.indent--;
1175
1953
  stmts.push(`${ind(ctx)}}`);
1176
1954
  }
@@ -1178,67 +1956,66 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
1178
1956
  return { stmts, valueVar: valV, endVar: endV };
1179
1957
  }
1180
1958
  function emitFirstMatch(def, allExpected, ctx, pos) {
1181
- const resV = v(ctx, "_cr");
1182
- const stmts = [`${ind(ctx)}let ${resV}`];
1959
+ const resValV = v(ctx, "_crv");
1960
+ const resEndV = v(ctx, "_cre");
1961
+ const resOkV = v(ctx, "_crok");
1962
+ const ind0 = ind(ctx);
1963
+ const stmts = [
1964
+ `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`
1965
+ ];
1183
1966
  for (let i = 0; i < def.parsers.length; i++) {
1184
1967
  const p = def.parsers[i];
1185
1968
  const gate = def.gates[i];
1186
1969
  const autoNot = def.autoNot[i];
1187
- const savedIndent = ctx.indent;
1188
- const savedFailLabel = ctx.failLabel;
1189
- ctx.indent = 0;
1190
- ctx.failLabel = void 0;
1191
- const r = emit(p, ctx, pos);
1192
- ctx.indent = savedIndent;
1193
- ctx.failLabel = savedFailLabel;
1194
- const iife = asIIFE(r.stmts, r.valueVar, r.endVar, pos, ind(ctx));
1195
1970
  let gateCond = null;
1196
1971
  if (gate) {
1197
1972
  const gateIdx = ctx.mapFns.length;
1198
1973
  ctx.mapFns.push(gate);
1199
1974
  gateCond = `_mf[${gateIdx}](_ctx.state)`;
1200
1975
  }
1201
- const skipCond = gateCond ? `!${resV}?.ok && ${gateCond}` : `!${resV}?.ok`;
1976
+ const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
1202
1977
  const markLeaves = ctx.capturing ? v(ctx, "_cml") : null;
1203
1978
  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} }` : "";
1979
+ const markTl = ctx.capturing ? v(ctx, "_cmtl") : null;
1980
+ const markLog = ctx.capturing ? v(ctx, "_cmlg") : null;
1981
+ const rollback = markLeaves ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLeaves}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRaw}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}` : "";
1982
+ stmts.push(`${ind0}if (${skipCond}) {`);
1983
+ ctx.indent++;
1984
+ const ind1 = ind(ctx);
1985
+ if (markLeaves) {
1986
+ stmts.push(
1987
+ `${ind1}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
1988
+ `${ind1}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
1989
+ `${ind1}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
1990
+ `${ind1}const ${markLog} = _ctx._triviaLog?.length ?? 0`
1991
+ );
1992
+ }
1993
+ const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
1994
+ stmts.push(...armStmts);
1995
+ ctx.indent--;
1210
1996
  if (autoNot && autoNot.length > 0) {
1211
- const tmp = v(ctx, "_ct");
1212
1997
  const anCode = v(ctx, "_anc");
1213
1998
  const rejectCond = autoNot.map(
1214
- (check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${tmp}.span.end)`
1999
+ (check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
1215
2000
  ).join(" || ");
1216
- stmts.push(`${ind(ctx)}if (${skipCond}) {`);
1217
- stmts.push(...markStmts);
1218
2001
  stmts.push(
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} }`
2002
+ `${ind0} if (${okVar}) { const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1; if (!(${rejectCond})) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true } }`
1221
2003
  );
1222
- if (rollbackStmt) stmts.push(rollbackStmt);
1223
- stmts.push(`${ind(ctx)}}`);
2004
+ if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
1224
2005
  } else {
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)}}`);
2006
+ stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
2007
+ if (rollback) stmts.push(`${ind0} else { ${rollback} }`);
1230
2008
  }
2009
+ stmts.push(`${ind0}}`);
1231
2010
  }
1232
- stmts.push(`${ind(ctx)}if (!${resV}?.ok) ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
1233
- return { stmts, valueVar: `${resV}.value`, endVar: `${resV}.span.end` };
2011
+ stmts.push(`${ind0}if (!${resOkV}) ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
2012
+ return { stmts, valueVar: resValV, endVar: resEndV };
1234
2013
  }
1235
2014
  function emitNonDisjoint(def, strategy, 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
- }
2015
+ if (strategy.tag === "greedyClassify")
2016
+ return emitGreedyClassify(def, strategy.superIndex, allExpected, ctx, pos);
2017
+ if (strategy.tag === "literalsLongestFirst")
2018
+ return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
1242
2019
  return emitFirstMatch(def, allExpected, ctx, pos);
1243
2020
  }
1244
2021
  function emitTransformChain(p, baseValue, endV, startPos, ctx) {
@@ -1291,27 +2068,28 @@ function emitMany(def, ctx, pos) {
1291
2068
  if (ctx.capturing) {
1292
2069
  const capFn = ensureTriviaCaptureFn(ctx);
1293
2070
  const markV = v(ctx, "_mk");
2071
+ const markTl = v(ctx, "_mktl");
2072
+ const markLog = v(ctx, "_mklg");
1294
2073
  const npV = v(ctx, "_np");
1295
2074
  stmts.push(
1296
2075
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
1297
- `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx)`
2076
+ `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
2077
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
2078
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`
1298
2079
  );
1299
2080
  itemPos = npV;
1300
- rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; `;
2081
+ rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; `;
1301
2082
  } else {
1302
2083
  const trivFn = ensureTriviaFn(ctx);
1303
2084
  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
- );
2085
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`);
1308
2086
  itemPos = npV;
1309
2087
  }
1310
2088
  }
1311
2089
  const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
1312
2090
  stmts.push(
1313
2091
  ...iterStmts,
1314
- `${ind(ctx)}if (!${iterOk} || ${iterEnd} === ${curV}) { ${rollback}break }`,
2092
+ `${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
1315
2093
  `${ind(ctx)}${arrV}.push(${iterVal})`,
1316
2094
  `${ind(ctx)}${curV} = ${iterEnd}`
1317
2095
  );
@@ -1348,55 +2126,70 @@ function emitSepBy(_p, def, ctx, pos) {
1348
2126
  `${ind(ctx)}while (${curV} < input.length) {`
1349
2127
  );
1350
2128
  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
2129
  let sepAtPos = curV;
1358
2130
  if (ctx.activeTrivia) {
1359
2131
  if (ctx.capturing) {
1360
2132
  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);
2133
+ const markV = v(ctx, "_mk");
2134
+ const markTl = v(ctx, "_mktl");
2135
+ const markLog = v(ctx, "_mklg");
1366
2136
  const spV = v(ctx, "_sp");
1367
2137
  stmts.push(
1368
- `${ind(ctx)}let ${spV} = ${curV}`,
1369
- `${ind(ctx)}{ const _trv = ${trivFn}(input, ${spV}, _ctx); if (_trv.ok) ${spV} = _trv.span.end }`
2138
+ `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
2139
+ `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
2140
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
2141
+ `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`
1370
2142
  );
1371
2143
  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);
2144
+ const sepRollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; `;
2145
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
2146
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sepRollback}break }`);
2147
+ const mark2V = v(ctx, "_mk2");
2148
+ const mark2Tl = v(ctx, "_mktl2");
2149
+ const mark2Log = v(ctx, "_mklg2");
1380
2150
  const npV = v(ctx, "_np");
1381
- stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx)`);
1382
- nextAtPos = npV;
2151
+ stmts.push(
2152
+ `${ind(ctx)}const ${mark2V} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
2153
+ `${ind(ctx)}const ${mark2Tl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
2154
+ `${ind(ctx)}const ${mark2Log} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
2155
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`
2156
+ );
2157
+ const nextRollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mark2V}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mark2Tl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mark2Log}; `;
2158
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
2159
+ stmts.push(
2160
+ ...nextStmts,
2161
+ `${ind(ctx)}if (!${nextOk}) { ${nextRollback}break }`,
2162
+ `${ind(ctx)}${arrV}.push(${nextVal})`,
2163
+ `${ind(ctx)}${curV} = ${nextEnd}`
2164
+ );
1383
2165
  } else {
1384
2166
  const trivFn = ensureTriviaFn(ctx);
2167
+ const spV = v(ctx, "_sp");
2168
+ stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
2169
+ sepAtPos = spV;
2170
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
2171
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
1385
2172
  const npV = v(ctx, "_np");
2173
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
2174
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
1386
2175
  stmts.push(
1387
- `${ind(ctx)}let ${npV} = ${sepEnd}`,
1388
- `${ind(ctx)}{ const _trv = ${trivFn}(input, ${npV}, _ctx); if (_trv.ok) ${npV} = _trv.span.end }`
2176
+ ...nextStmts,
2177
+ `${ind(ctx)}if (!${nextOk}) break`,
2178
+ `${ind(ctx)}${arrV}.push(${nextVal})`,
2179
+ `${ind(ctx)}${curV} = ${nextEnd}`
1389
2180
  );
1390
- nextAtPos = npV;
1391
2181
  }
2182
+ } else {
2183
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
2184
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2185
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
2186
+ stmts.push(
2187
+ ...nextStmts,
2188
+ `${ind(ctx)}if (!${nextOk}) break`,
2189
+ `${ind(ctx)}${arrV}.push(${nextVal})`,
2190
+ `${ind(ctx)}${curV} = ${nextEnd}`
2191
+ );
1392
2192
  }
1393
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, nextAtPos);
1394
- stmts.push(
1395
- ...nextStmts,
1396
- `${ind(ctx)}if (!${nextOk}) { ${rollback}break }`,
1397
- `${ind(ctx)}${arrV}.push(${nextVal})`,
1398
- `${ind(ctx)}${curV} = ${nextEnd}`
1399
- );
1400
2193
  ctx.indent--;
1401
2194
  stmts.push(`${ind(ctx)}}`);
1402
2195
  ctx.indent--;
@@ -1458,27 +2251,33 @@ function emitNot(def, ctx, pos) {
1458
2251
  };
1459
2252
  }
1460
2253
  function emitNode(def, ctx, pos) {
1461
- const fnIdx = ctx.buildFns.length;
1462
- ctx.buildFns.push(def.build);
1463
- ctx.buildSrcs.push(def.buildSrc ?? null);
2254
+ const mkType = analyzeMkInlineBuild(def);
2255
+ let buildIdx = null;
2256
+ if (!mkType) {
2257
+ buildIdx = ctx.buildFns.length;
2258
+ ctx.buildFns.push(def.build);
2259
+ ctx.buildSrcs.push(def.buildSrc ?? null);
2260
+ }
1464
2261
  const i = ind(ctx);
1465
2262
  const chV = v(ctx, "_ch");
1466
2263
  const rawV = v(ctx, "_raw");
1467
- const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st");
2264
+ const tlV = v(ctx, "_tl");
2265
+ const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
1468
2266
  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`
2267
+ `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []`,
2268
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
2269
+ `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${tlV}`
1472
2270
  ];
1473
2271
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
1474
2272
  stmts.push(...innerStmts);
1475
- stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}`);
2273
+ stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
1476
2274
  stmts.push(`${i}if (!${okVar}) ${failStmt({ ...ctx, indent: 0 }, '"node"', pos).trim()}`);
2275
+ const stV = v(ctx, "_nst");
2276
+ stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
1477
2277
  const ndV = v(ctx, "_nd");
2278
+ const ndExpr = mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `_build[${buildIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV})`;
1478
2279
  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).
2280
+ `${i}const ${ndV} = ${ndExpr}`,
1482
2281
  `${i}if (${sc}) ${sc}.push(${ndV})`,
1483
2282
  `${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
2283
  );
@@ -1512,31 +2311,14 @@ function emitLazy(p, def, ctx, pos) {
1512
2311
  const savedIndent = ctx.indent;
1513
2312
  const savedFailLabel = ctx.failLabel;
1514
2313
  ctx.indent = 1;
1515
- ctx.failLabel = void 0;
2314
+ ctx.failLabel = "_pfail";
1516
2315
  const r = emit(resolved, ctx, "_pos");
1517
2316
  ctx.indent = savedIndent;
1518
2317
  ctx.failLabel = savedFailLabel;
1519
- ctx.namedFnDecls.push([
1520
- `function ${fnName2}(input, _pos, _ctx) {`,
1521
- ...r.stmts,
1522
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
1523
- `}`
1524
- ].join("\n"));
2318
+ pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
1525
2319
  }
1526
2320
  const fnName = ctx.namedParsers.get(p);
1527
- const rv = v(ctx, "_pfr");
1528
- const vv = v(ctx, "_pfv");
1529
- const ev = v(ctx, "_pfe");
1530
- return {
1531
- stmts: [
1532
- `${ind(ctx)}const ${rv} = ${fnName}(input, ${pos}, _ctx)`,
1533
- `${ind(ctx)}if (!${rv}.ok) ${failStmt({ ...ctx, indent: 0 }, '"parser"', pos).trim()}`,
1534
- `${ind(ctx)}const ${vv} = ${rv}.value`,
1535
- `${ind(ctx)}const ${ev} = ${rv}.span.end`
1536
- ],
1537
- valueVar: vv,
1538
- endVar: ev
1539
- };
2321
+ return emitNamedFnCall(ctx, fnName, pos, '"parser"');
1540
2322
  }
1541
2323
  function emitRecover(def, ctx, pos) {
1542
2324
  const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
@@ -1572,6 +2354,8 @@ function emit(p, ctx, pos) {
1572
2354
  return emitLit(def, ctx, pos);
1573
2355
  case "regex":
1574
2356
  return emitRegex(def, ctx, pos);
2357
+ case "keywords":
2358
+ return emitKeywords(def, ctx, pos);
1575
2359
  case "sequence":
1576
2360
  return emitSeq(def, ctx, pos);
1577
2361
  case "choice":
@@ -1584,7 +2368,31 @@ function emit(p, ctx, pos) {
1584
2368
  case "sepBy":
1585
2369
  return emitSepBy(p, def, ctx, pos);
1586
2370
  case "transform": {
2371
+ const fnSrc = transformFnSource(def.fn, def.fnSrc);
2372
+ if (fnSrc && def.parser._def.tag === "sequence") {
2373
+ const seqR = emitSeqValues(def.parser._def, ctx, pos);
2374
+ const inlined = tryInlineDestructureTransform(fnSrc, seqR.valueVars);
2375
+ if (inlined) {
2376
+ const mv2 = v(ctx, "_mapped");
2377
+ return {
2378
+ stmts: [...seqR.stmts, `${ind(ctx)}const ${mv2} = ${inlined}`],
2379
+ valueVar: mv2,
2380
+ endVar: seqR.endVar
2381
+ };
2382
+ }
2383
+ }
1587
2384
  const inner = emit(def.parser, ctx, pos);
2385
+ if (fnSrc) {
2386
+ const unary = tryInlineUnaryTransform(fnSrc, inner.valueVar);
2387
+ if (unary) {
2388
+ const mv2 = v(ctx, "_mapped");
2389
+ return {
2390
+ stmts: [...inner.stmts, `${ind(ctx)}const ${mv2} = ${unary}`],
2391
+ valueVar: mv2,
2392
+ endVar: inner.endVar
2393
+ };
2394
+ }
2395
+ }
1588
2396
  const fnIdx = ctx.mapFns.length;
1589
2397
  ctx.mapFns.push(def.fn);
1590
2398
  ctx.mapFnSrcs.push(def.fnSrc ?? null);
@@ -1620,12 +2428,20 @@ function emit(p, ctx, pos) {
1620
2428
  return emitLazy(p, def, ctx, pos);
1621
2429
  case "trivia":
1622
2430
  return emit(def.parser, ctx, pos);
2431
+ case "label":
2432
+ return emit(def.parser, ctx, pos);
1623
2433
  case "grammar": {
1624
2434
  const savedTrivia = ctx.activeTrivia;
1625
- if (def.triviaParser) ctx.activeTrivia = def.triviaParser;
2435
+ const savedKindLabels = ctx.triviaKindLabels;
2436
+ if (def.triviaParser) {
2437
+ ctx.activeTrivia = def.triviaParser;
2438
+ if (def.triviaParser._meta.triviaKindLabels) {
2439
+ ctx.triviaKindLabels = def.triviaParser._meta.triviaKindLabels;
2440
+ }
2441
+ }
1626
2442
  const r = emit(def.parser, ctx, pos);
1627
- if (savedTrivia === void 0) delete ctx.activeTrivia;
1628
- else ctx.activeTrivia = savedTrivia;
2443
+ ctx.activeTrivia = savedTrivia;
2444
+ ctx.triviaKindLabels = savedKindLabels;
1629
2445
  return r;
1630
2446
  }
1631
2447
  case "not":
@@ -1660,27 +2476,22 @@ function emit(p, ctx, pos) {
1660
2476
  const savedIndent = ctx.indent;
1661
2477
  const savedFailLabel = ctx.failLabel;
1662
2478
  ctx.indent = 1;
1663
- ctx.failLabel = void 0;
2479
+ ctx.failLabel = "_pfail";
1664
2480
  const innerR = emit(innerParser, ctx, "_pos");
1665
2481
  ctx.indent = savedIndent;
1666
2482
  ctx.failLabel = savedFailLabel;
1667
- ctx.namedFnDecls.push([
1668
- `function ${fnName}(input, _pos, _ctx) {`,
1669
- ...innerR.stmts,
1670
- ` return { ok: true, value: ${innerR.valueVar}, span: { start: _pos, end: ${innerR.endVar} } }`,
1671
- `}`
1672
- ].join("\n"));
2483
+ pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
1673
2484
  }
1674
2485
  const fn = ctx.namedParsers.get(innerParser);
1675
2486
  const rv = v(ctx, "_wcr");
1676
- const vv = v(ctx);
2487
+ const vv = v(ctx, "_wcv");
1677
2488
  const ev = v(ctx, "_wce");
1678
2489
  return {
1679
2490
  stmts: [
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()}`,
1682
- `${ind(ctx)}const ${vv} = ${rv}.value`,
1683
- `${ind(ctx)}const ${ev} = ${rv}.span.end`
2491
+ `${ind(ctx)}const ${rv} = { ..._ctx, state: _mf[${evIdx}]() }`,
2492
+ `${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
2493
+ `${ind(ctx)}if (${vv} === ${NAMED_FN_FAIL}) ${failStmt({ ...ctx, indent: 0 }, '"withCtx"', pos).trim()}`,
2494
+ `${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
1684
2495
  ],
1685
2496
  valueVar: vv,
1686
2497
  endVar: ev
@@ -1706,6 +2517,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
1706
2517
  }
1707
2518
  case "grammar":
1708
2519
  case "trivia":
2520
+ case "label":
1709
2521
  case "optional":
1710
2522
  case "many":
1711
2523
  case "oneOrMore":
@@ -1729,7 +2541,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
1729
2541
  return false;
1730
2542
  }
1731
2543
  }
1732
- function compile(parser2, mapFnSources) {
2544
+ function compile(combinator, mapFnSources) {
1733
2545
  const ctx = {
1734
2546
  vars: 0,
1735
2547
  indent: 1,
@@ -1743,15 +2555,18 @@ function compile(parser2, mapFnSources) {
1743
2555
  needsCollator: false,
1744
2556
  namedParsers: /* @__PURE__ */ new Map(),
1745
2557
  triviaCaptureNames: /* @__PURE__ */ new Map(),
2558
+ triviaFnNames: /* @__PURE__ */ new Map(),
1746
2559
  namedFnDecls: [],
1747
- capturing: hasNodeDef(parser2)
2560
+ capturing: hasNodeDef(combinator)
1748
2561
  };
1749
- const r = emit(parser2, ctx, "_pos");
2562
+ const r = emit(combinator, ctx, "_pos");
1750
2563
  const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
1751
2564
  ` : "";
2565
+ const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
1752
2566
  const source = [
1753
2567
  ...ctx.regexDecls,
1754
2568
  "",
2569
+ ...namedPrelude,
1755
2570
  ...ctx.namedFnDecls,
1756
2571
  `${collatorDecl}function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
1757
2572
  ` let pos = _pos`,
@@ -1762,12 +2577,16 @@ function compile(parser2, mapFnSources) {
1762
2577
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
1763
2578
  ...ctx.regexDecls,
1764
2579
  collatorDecl,
2580
+ ...namedPrelude,
1765
2581
  ...ctx.namedFnDecls,
1766
2582
  `let pos = _pos`,
1767
2583
  ...r.stmts,
1768
2584
  `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
1769
2585
  ].join("\n"));
1770
- const defaultCtx = { trackLines: false };
2586
+ const defaultCtx = {
2587
+ trackLines: false,
2588
+ ...ctx.triviaKindLabels ? { triviaKindLabels: ctx.triviaKindLabels } : {}
2589
+ };
1771
2590
  const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
1772
2591
  const effectiveSources = mapFnSources ?? derivedSrcs;
1773
2592
  const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
@@ -1781,6 +2600,9 @@ function compile(parser2, mapFnSources) {
1781
2600
  parse(input, pos = 0) {
1782
2601
  return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
1783
2602
  },
2603
+ parseWithContext(input, parseCtx, pos = 0) {
2604
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
2605
+ },
1784
2606
  parseWithErrors(input, pos = 0) {
1785
2607
  const errors = [];
1786
2608
  const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
@@ -1803,12 +2625,14 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
1803
2625
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
1804
2626
  const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl;
1805
2627
  if (!needsWrapper) return innerFn;
2628
+ const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
1806
2629
  return [
1807
2630
  `/* @__PURE__ */ (() => {`,
1808
2631
  ...ctx.regexDecls.map((d) => ` ${d}`),
1809
2632
  collatorDecl ? ` ${collatorDecl.trim()}` : "",
1810
2633
  mfDecl,
1811
2634
  buildDecl,
2635
+ ...namedPrelude.map((l) => ` ${l}`),
1812
2636
  ...ctx.namedFnDecls.flatMap((f) => f.split("\n").map((l) => ` ${l}`)),
1813
2637
  ` return ${innerFn}`,
1814
2638
  `})()`
@@ -1830,10 +2654,9 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
1830
2654
  let cur = pos;
1831
2655
  const probeCtx = { trackLines: ctx.trackLines, state: ctx.state };
1832
2656
  const emit2 = (end) => {
1833
- if (end > pos && (ctx._cstLeaves || ctx._cstRawChildren)) {
2657
+ if (end > pos && cstCaptureActive(ctx)) {
1834
2658
  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);
2659
+ pushCstLeaf(ctx, leaf);
1837
2660
  }
1838
2661
  };
1839
2662
  while (cur < input.length) {
@@ -1876,16 +2699,22 @@ function balanced(open, close, options = {}) {
1876
2699
  }
1877
2700
 
1878
2701
  // src/plugin/evaluator.ts
2702
+ function isWordFactory(v2) {
2703
+ return !!v2 && typeof v2 === "object" && v2.tag === "wordFactory";
2704
+ }
1879
2705
  var SUPPORTED = {
1880
2706
  literal: (...a) => literal(a[0], a[1]),
1881
2707
  regex: (...a) => regex(a[0], a[1]),
2708
+ keywords: (...a) => keywords(a[0], a[1]),
2709
+ word: (...a) => word(a[0], a[1]),
1882
2710
  sequence: (...a) => sequence(...a),
1883
2711
  choice: (...a) => choice(...a),
1884
2712
  many: (...a) => many(a[0]),
1885
2713
  oneOrMore: (...a) => oneOrMore(a[0]),
1886
2714
  optional: (...a) => optional(a[0]),
1887
2715
  sepBy: (...a) => sepBy(a[0], a[1]),
1888
- trivia: (...a) => trivia(a[0])
2716
+ trivia: (...a) => trivia(a[0]),
2717
+ label: (...a) => label(a[0], a[1])
1889
2718
  };
1890
2719
  function isScopeEntry(v2) {
1891
2720
  return !!v2 && typeof v2 === "object" && "combi" in v2 && "mfSrcs" in v2;
@@ -1907,6 +2736,33 @@ function exprToCombi(node2, scope, code, mfs) {
1907
2736
  if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
1908
2737
  if (node2.type !== "CallExpression") return null;
1909
2738
  const callee = node2.callee;
2739
+ if (callee.type === "CallExpression" && callee.callee.type === "Identifier" && callee.callee.name === "makeWord") {
2740
+ const boundaryArg = callee.arguments[0];
2741
+ const strArg = node2.arguments[0];
2742
+ if (!strArg || strArg.type === "SpreadElement") return null;
2743
+ const boundary = boundaryArg && boundaryArg.type !== "SpreadElement" ? anyValue(boundaryArg, scope, code, mfs) : "_0-9A-Za-z";
2744
+ const str = anyValue(strArg, scope, code, mfs);
2745
+ if (typeof boundary !== "string" || typeof str !== "string") return null;
2746
+ try {
2747
+ return word(str, boundary);
2748
+ } catch {
2749
+ return null;
2750
+ }
2751
+ }
2752
+ if (callee.type === "Identifier") {
2753
+ const factory2 = scope.get(callee.name);
2754
+ if (isWordFactory(factory2)) {
2755
+ const [strArg] = node2.arguments;
2756
+ if (!strArg || strArg.type === "SpreadElement") return null;
2757
+ const str = anyValue(strArg, scope, code, mfs);
2758
+ if (typeof str !== "string") return null;
2759
+ try {
2760
+ return word(str, factory2.boundary);
2761
+ } catch {
2762
+ return null;
2763
+ }
2764
+ }
2765
+ }
1910
2766
  if (callee.type !== "Identifier") return null;
1911
2767
  if (callee.name === "transform" && code !== void 0 && mfs !== void 0) {
1912
2768
  const [parserArg, fnArg] = node2.arguments;
@@ -2083,9 +2939,27 @@ function anyValue(node2, scope, code, mfs) {
2083
2939
  if (!propName) return null;
2084
2940
  return obj[propName] ?? null;
2085
2941
  }
2086
- if (node2.type === "CallExpression") return exprToCombi(node2, scope, code, mfs);
2942
+ if (node2.type === "CallExpression") {
2943
+ const callee = node2.callee;
2944
+ if (callee.type === "Identifier" && callee.name === "makeWord") {
2945
+ const boundaryArg = node2.arguments[0];
2946
+ const boundary = boundaryArg && boundaryArg.type !== "SpreadElement" ? anyValue(boundaryArg, scope, code, mfs) : "_0-9A-Za-z";
2947
+ if (typeof boundary !== "string") return null;
2948
+ return { tag: "wordFactory", boundary };
2949
+ }
2950
+ return exprToCombi(node2, scope, code, mfs);
2951
+ }
2087
2952
  return null;
2088
2953
  }
2954
+ function evaluateWordFactory(node2, scope, code) {
2955
+ if (node2.type !== "CallExpression") return null;
2956
+ const callee = node2.callee;
2957
+ if (callee.type !== "Identifier" || callee.name !== "makeWord") return null;
2958
+ const boundaryArg = node2.arguments[0];
2959
+ const boundary = boundaryArg && boundaryArg.type !== "SpreadElement" ? anyValue(boundaryArg, scope, code) : "_0-9A-Za-z";
2960
+ if (typeof boundary !== "string") return null;
2961
+ return { tag: "wordFactory", boundary };
2962
+ }
2089
2963
  function evaluateExpr(node2, scope, code, mapFnSources) {
2090
2964
  return exprToCombi(node2, scope, code, mapFnSources);
2091
2965
  }
@@ -2243,23 +3117,23 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
2243
3117
  anyUnresolved = true;
2244
3118
  warnings.push(`${id}:${lineOf(pos)} \u2014 ${msg} (running via the interpreter; add the plugin or simplify the declaration to compile it)`);
2245
3119
  };
2246
- const compileRulesFactory = (init, label) => {
3120
+ const compileRulesFactory = (init, label2) => {
2247
3121
  const args = init.arguments;
2248
3122
  const factoryArg = args[0];
2249
3123
  if (!factoryArg) {
2250
- warn(init.start, `${label}: rules() needs a factory argument`);
3124
+ warn(init.start, `${label2}: rules() needs a factory argument`);
2251
3125
  return null;
2252
3126
  }
2253
3127
  const ruleMap = evaluateParserFactory(factoryArg, scope, code, []);
2254
3128
  if (!ruleMap) {
2255
- warn(init.start, `${label}: rules(...) factory isn't statically evaluable`);
3129
+ warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
2256
3130
  return null;
2257
3131
  }
2258
3132
  const entries = [];
2259
3133
  for (const [ruleKey, rule] of ruleMap) {
2260
3134
  const compiled = compile(rule);
2261
3135
  if (compiled.inlineExpression === null) {
2262
- warn(init.start, `${label}: rule "${ruleKey}" couldn't be inlined`);
3136
+ warn(init.start, `${label2}: rule "${ruleKey}" couldn't be inlined`);
2263
3137
  return null;
2264
3138
  }
2265
3139
  entries.push([ruleKey, compiled.inlineExpression]);
@@ -2303,6 +3177,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
2303
3177
  const mapFnSources = [];
2304
3178
  const parser2 = evaluateExpr(init, scope, code, mapFnSources);
2305
3179
  if (parser2 === null) {
3180
+ const wordFactory = evaluateWordFactory(init, scope, code);
3181
+ if (wordFactory) {
3182
+ ;
3183
+ scope.set(varName, wordFactory);
3184
+ continue;
3185
+ }
2306
3186
  warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
2307
3187
  continue;
2308
3188
  }