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
package/dist/index.cjs CHANGED
@@ -30,7 +30,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- Parser: () => Parser,
34
33
  annotateSpan: () => annotateSpan,
35
34
  balanced: () => balanced,
36
35
  buildLineIndex: () => buildLineIndex,
@@ -41,8 +40,10 @@ __export(index_exports, {
41
40
  guard: () => guard,
42
41
  isParseError: () => isParseError,
43
42
  keywords: () => keywords,
43
+ label: () => label,
44
44
  literal: () => literal,
45
45
  makeFunctionalDoc: () => makeFunctionalDoc,
46
+ makeWord: () => makeWord,
46
47
  many: () => many,
47
48
  node: () => node,
48
49
  not: () => not,
@@ -61,7 +62,9 @@ __export(index_exports, {
61
62
  skip: () => skip,
62
63
  transform: () => transform,
63
64
  trivia: () => trivia,
64
- withCtx: () => withCtx
65
+ triviaEntries: () => triviaEntries,
66
+ withCtx: () => withCtx,
67
+ word: () => word
65
68
  });
66
69
  module.exports = __toCommonJS(index_exports);
67
70
 
@@ -125,6 +128,151 @@ function failAt(ctx, expected, pos) {
125
128
  return r;
126
129
  }
127
130
 
131
+ // src/cst/capture-buffer.ts
132
+ var EMPTY = [];
133
+ var EMPTY_TL = [];
134
+ function cstCaptureActive(ctx) {
135
+ return ctx._cstBuf !== void 0 || ctx._cstLeaves !== void 0;
136
+ }
137
+ function beginCstNodeCapture(ctx) {
138
+ const saved = {
139
+ ch: ctx._cstChildren,
140
+ lv: ctx._cstLeaves,
141
+ raw: ctx._cstRawChildren,
142
+ tl: ctx._cstTriviaLog,
143
+ cap: ctx.captureTrivia,
144
+ buf: ctx._cstBuf
145
+ };
146
+ ctx._cstBuf = {};
147
+ ctx._cstChildren = void 0;
148
+ ctx._cstLeaves = void 0;
149
+ ctx._cstRawChildren = void 0;
150
+ ctx._cstTriviaLog = void 0;
151
+ ctx.captureTrivia = true;
152
+ return saved;
153
+ }
154
+ function finishCstBuf(buf) {
155
+ if (!buf) return { children: EMPTY, rawChildren: EMPTY, triviaLog: EMPTY_TL };
156
+ const children = buf.ch ?? (buf.single !== void 0 ? [buf.single] : EMPTY);
157
+ const rawChildren = buf.raw ?? (buf.rawSingle !== void 0 ? [buf.rawSingle] : EMPTY);
158
+ const triviaLog = buf.tl ?? EMPTY_TL;
159
+ return { children, rawChildren, triviaLog };
160
+ }
161
+ function endCstNodeCapture(ctx, saved) {
162
+ const materialized = finishCstBuf(ctx._cstBuf);
163
+ ctx._cstBuf = saved.buf;
164
+ ctx._cstChildren = saved.ch;
165
+ ctx._cstLeaves = saved.lv;
166
+ ctx._cstRawChildren = saved.raw;
167
+ ctx._cstTriviaLog = saved.tl;
168
+ ctx.captureTrivia = saved.cap;
169
+ return materialized;
170
+ }
171
+ function pushCstLeaf(ctx, leaf) {
172
+ pushCstChild(ctx, leaf, leaf);
173
+ }
174
+ function pushCstChild(ctx, built, rawEntry) {
175
+ const b = ctx._cstBuf;
176
+ if (b) {
177
+ if (b.ch) b.ch.push(built);
178
+ else if (b.single !== void 0) {
179
+ b.ch = [b.single, built];
180
+ b.single = void 0;
181
+ } else b.single = built;
182
+ if (b.raw) b.raw.push(rawEntry);
183
+ else if (b.rawSingle !== void 0) {
184
+ b.raw = [b.rawSingle, rawEntry];
185
+ b.rawSingle = void 0;
186
+ } else b.rawSingle = rawEntry;
187
+ return;
188
+ }
189
+ if (ctx._cstChildren) ctx._cstChildren.push(built);
190
+ else if (ctx._cstLeaves) ctx._cstLeaves.push(built);
191
+ if (ctx._cstRawChildren) ctx._cstRawChildren.push(rawEntry);
192
+ }
193
+ function cstRawLen(ctx) {
194
+ const b = ctx._cstBuf;
195
+ if (b) {
196
+ if (b.raw) return b.raw.length;
197
+ return b.rawSingle !== void 0 ? 1 : 0;
198
+ }
199
+ return ctx._cstRawChildren?.length ?? 0;
200
+ }
201
+ function cstLeavesLen(ctx) {
202
+ const b = ctx._cstBuf;
203
+ if (b) {
204
+ if (b.ch) return b.ch.length;
205
+ return b.single !== void 0 ? 1 : 0;
206
+ }
207
+ return ctx._cstLeaves?.length ?? 0;
208
+ }
209
+ function cstTlLen(ctx) {
210
+ const b = ctx._cstBuf;
211
+ if (b) return b.tl?.length ?? 0;
212
+ return ctx._cstTriviaLog?.length ?? 0;
213
+ }
214
+ function saveCstMark(ctx) {
215
+ return {
216
+ raw: cstRawLen(ctx),
217
+ tlog: cstTlLen(ctx),
218
+ leaves: cstLeavesLen(ctx)
219
+ };
220
+ }
221
+ function rollbackBufList(b, keyMulti, keySingle, len) {
222
+ const arr = b[keyMulti];
223
+ if (arr) {
224
+ if (len === 0) b[keyMulti] = void 0;
225
+ else if (len === 1) {
226
+ b[keySingle] = arr[0];
227
+ b[keyMulti] = void 0;
228
+ } else arr.length = len;
229
+ return;
230
+ }
231
+ if (len === 0) b[keySingle] = void 0;
232
+ }
233
+ function rollbackCstCapture(ctx, mark) {
234
+ const b = ctx._cstBuf;
235
+ if (b) {
236
+ rollbackBufList(b, "raw", "rawSingle", mark.raw);
237
+ rollbackBufList(b, "ch", "single", mark.leaves);
238
+ if (b.tl) {
239
+ if (mark.tlog === 0) b.tl = void 0;
240
+ else b.tl.length = mark.tlog;
241
+ }
242
+ return;
243
+ }
244
+ if (ctx._cstRawChildren) ctx._cstRawChildren.length = mark.raw;
245
+ if (ctx._cstTriviaLog) ctx._cstTriviaLog.length = mark.tlog;
246
+ if (ctx._cstLeaves) ctx._cstLeaves.length = mark.leaves;
247
+ }
248
+ function pushCstTriviaEntry(ctx, start, end, kindIndex) {
249
+ const insertIdx = cstRawLen(ctx);
250
+ const b = ctx._cstBuf;
251
+ const withKind = ctx.triviaKindLabels !== void 0 && kindIndex !== void 0;
252
+ if (b) {
253
+ if (!b.tl) {
254
+ b.tl = withKind ? [start, end, insertIdx, kindIndex] : [start, end, insertIdx];
255
+ } else if (withKind) {
256
+ b.tl.push(start, end, insertIdx, kindIndex);
257
+ } else {
258
+ b.tl.push(start, end, insertIdx);
259
+ }
260
+ return;
261
+ }
262
+ if (ctx._cstTriviaLog) {
263
+ if (withKind) ctx._cstTriviaLog.push(start, end, insertIdx, kindIndex);
264
+ else ctx._cstTriviaLog.push(start, end, insertIdx);
265
+ }
266
+ }
267
+ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
268
+ if (!ctx._triviaLog) return;
269
+ if (ctx.triviaKindLabels !== void 0 && kindIndex !== void 0) {
270
+ ctx._triviaLog.push(start, end, kindIndex);
271
+ } else {
272
+ ctx._triviaLog.push(start, end);
273
+ }
274
+ }
275
+
128
276
  // src/combinators/literal.ts
129
277
  var _collatorCache = null;
130
278
  function collator() {
@@ -164,8 +312,7 @@ function literal(value, opts = {}) {
164
312
  if (matched) {
165
313
  const span = { start: pos, end };
166
314
  const leaf = { _tag: "leaf", value: slice, span };
167
- if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
168
- if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
315
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
169
316
  return { ok: true, value: slice, span };
170
317
  }
171
318
  return failAt(ctx, [JSON.stringify(value)], pos);
@@ -307,14 +454,19 @@ function regex(pattern, flags = "") {
307
454
  }
308
455
  const span = { start: pos, end: pos + m[0].length };
309
456
  const leaf = { _tag: "leaf", value: m[0], span };
310
- if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
311
- if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
457
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
312
458
  return { ok: true, value: m[0], span };
313
459
  }
314
460
  };
315
461
  }
316
462
 
317
463
  // src/combinators/keywords.ts
464
+ function word(str, boundary = "_0-9A-Za-z") {
465
+ return keywords([str], { boundary });
466
+ }
467
+ function makeWord(boundary = "_0-9A-Za-z") {
468
+ return (str) => keywords([str], { boundary });
469
+ }
318
470
  function escapeRe(s) {
319
471
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
320
472
  }
@@ -350,109 +502,15 @@ function keywords(words, opts = {}) {
350
502
  const value = m[0];
351
503
  const span = { start: pos, end: pos + value.length };
352
504
  const leaf = { _tag: "leaf", value, span };
353
- if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
354
- if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
505
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
355
506
  return { ok: true, value, span };
356
507
  }
357
508
  };
358
509
  }
359
510
 
360
- // src/combinators/trivia-skip.ts
361
- var NOOP_COMMIT = () => {
362
- };
363
- function scanTrivia(input, cur, ctx) {
364
- const triviaP = ctx.trivia;
365
- if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
366
- const log = ctx._triviaLog;
367
- if (log !== void 0) {
368
- const tr2 = triviaP.parse(input, cur, { trackLines: false, state: ctx.state });
369
- if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
370
- const end = tr2.span.end;
371
- return {
372
- end,
373
- commit: () => {
374
- log.push(cur, end);
375
- }
376
- };
377
- }
378
- if (ctx.captureTrivia && ctx._cstRawChildren) {
379
- const sink = [];
380
- const tr2 = triviaP.parse(input, cur, {
381
- trackLines: ctx.trackLines,
382
- state: ctx.state,
383
- _cstLeaves: sink
384
- });
385
- if (tr2.ok && tr2.span.end > cur) {
386
- const out = ctx._cstRawChildren;
387
- return {
388
- end: tr2.span.end,
389
- commit: () => {
390
- for (const tok of sink) {
391
- if (tok._tag === "leaf" && tok.span.end > tok.span.start) {
392
- out.push({ _tag: "trivia", value: tok.value, span: tok.span });
393
- }
394
- }
395
- }
396
- };
397
- }
398
- return { end: cur, commit: NOOP_COMMIT };
399
- }
400
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
401
- return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
402
- }
403
- function consumeTrivia(input, cur, ctx) {
404
- const scan = scanTrivia(input, cur, ctx);
405
- scan.commit();
406
- return scan.end;
407
- }
408
-
409
- // src/combinators/sequence.ts
410
- function sequence(...parsers) {
411
- const meta = {
412
- firstSet: parsers[0]?._meta.firstSet ?? empty(),
413
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
414
- isTrivia: false
415
- };
416
- return {
417
- _tag: "sequence",
418
- _meta: meta,
419
- _def: { tag: "sequence", parsers },
420
- parse(input, pos, ctx) {
421
- const values = [];
422
- let cur = pos;
423
- for (let i = 0; i < parsers.length; i++) {
424
- if (ctx.trivia && i > 0) {
425
- const scan = scanTrivia(input, cur, ctx);
426
- const raw = ctx._cstRawChildren;
427
- const mark = raw ? raw.length : 0;
428
- scan.commit();
429
- const result2 = parsers[i].parse(input, scan.end, ctx);
430
- if (!result2.ok) return result2;
431
- if (result2.span.end > scan.end) {
432
- cur = result2.span.end;
433
- } else if (raw) {
434
- raw.length = mark;
435
- }
436
- values.push(result2.value);
437
- continue;
438
- }
439
- const result = parsers[i].parse(input, cur, ctx);
440
- if (!result.ok) return result;
441
- values.push(result.value);
442
- cur = result.span.end;
443
- }
444
- return {
445
- ok: true,
446
- value: values,
447
- span: { start: pos, end: cur }
448
- };
449
- }
450
- };
451
- }
452
-
453
511
  // src/combinators/choice.ts
454
512
  function choice(...args) {
455
- const parsers = args.map((a) => "gate" in a ? a.parser : a);
513
+ const parsers = args.map((a) => "gate" in a ? a.combinator : a);
456
514
  const gates = args.map((a) => "gate" in a ? a.gate : null);
457
515
  const hasGates = gates.some((g) => g !== null);
458
516
  const disjoint = !hasGates && areDisjoint(parsers.map((p) => p._meta.firstSet));
@@ -536,19 +594,19 @@ function choice(...args) {
536
594
  }
537
595
  for (let i = 0; i < parsers.length; i++) {
538
596
  if (gates[i] && !gates[i](ctx.state)) continue;
539
- const leavesLen = ctx._cstLeaves?.length;
540
- const rawLen = ctx._cstRawChildren?.length;
597
+ const mark = saveCstMark(ctx);
598
+ const logLen = ctx._triviaLog?.length;
541
599
  const result = parsers[i].parse(input, pos, ctx);
542
600
  if (!result.ok) {
543
- if (leavesLen !== void 0 && ctx._cstLeaves) ctx._cstLeaves.length = leavesLen;
544
- if (rawLen !== void 0 && ctx._cstRawChildren) ctx._cstRawChildren.length = rawLen;
601
+ rollbackCstCapture(ctx, mark);
602
+ if (logLen !== void 0 && ctx._triviaLog) ctx._triviaLog.length = logLen;
545
603
  expected.push(...result.expected);
546
604
  continue;
547
605
  }
548
606
  const checks = autoNot[i];
549
607
  if (checks && autoNotFires(input, result.span.end, checks)) {
550
- if (leavesLen !== void 0 && ctx._cstLeaves) ctx._cstLeaves.length = leavesLen;
551
- if (rawLen !== void 0 && ctx._cstRawChildren) ctx._cstRawChildren.length = rawLen;
608
+ rollbackCstCapture(ctx, mark);
609
+ if (logLen !== void 0 && ctx._triviaLog) ctx._triviaLog.length = logLen;
552
610
  continue;
553
611
  }
554
612
  return result;
@@ -627,6 +685,7 @@ function getCoreRegexDef(p) {
627
685
  const def = p._def;
628
686
  if (def.tag === "regex") return { source: def.source, flags: def.flags };
629
687
  if (def.tag === "transform") return getCoreRegexDef(def.parser);
688
+ if (def.tag === "label") return getCoreRegexDef(def.parser);
630
689
  return null;
631
690
  }
632
691
  function applyTransforms(p, value, span) {
@@ -680,46 +739,275 @@ function areDisjoint(sets) {
680
739
  return true;
681
740
  }
682
741
 
742
+ // src/cst/trivia-kinds.ts
743
+ function unwrapTrivia(p) {
744
+ let cur = p;
745
+ while (cur._def.tag === "trivia") cur = cur._def.parser;
746
+ return cur;
747
+ }
748
+ function peelLabel(p) {
749
+ if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
750
+ return null;
751
+ }
752
+ function analyzeLabeledTrivia(trivia2) {
753
+ let core = unwrapTrivia(trivia2);
754
+ let minRepeats = 1;
755
+ if (core._def.tag === "oneOrMore") {
756
+ core = core._def.parser;
757
+ minRepeats = 1;
758
+ } else if (core._def.tag === "many") {
759
+ core = core._def.parser;
760
+ minRepeats = 0;
761
+ }
762
+ const arms = [];
763
+ if (core._def.tag === "choice") {
764
+ for (let i = 0; i < core._def.parsers.length; i++) {
765
+ const peeled = peelLabel(core._def.parsers[i]);
766
+ if (!peeled) return null;
767
+ arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
768
+ }
769
+ } else {
770
+ const peeled = peelLabel(core);
771
+ if (!peeled) return null;
772
+ arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
773
+ }
774
+ return { labels: arms.map((a) => a.label), arms, minRepeats };
775
+ }
776
+ function matchArmAt(input, pos, arm) {
777
+ const r = arm.parse(input, pos, { trackLines: false });
778
+ if (!r.ok || r.span.end <= pos) return null;
779
+ return { end: r.span.end };
780
+ }
781
+ function scanLabeledTriviaChunks(input, cur, spec) {
782
+ const chunks = [];
783
+ let pos = cur;
784
+ while (pos < input.length) {
785
+ let matched = null;
786
+ for (const arm of spec.arms) {
787
+ const m = matchArmAt(input, pos, arm.parser);
788
+ if (m) {
789
+ matched = { end: m.end, kindIndex: arm.kindIndex };
790
+ break;
791
+ }
792
+ }
793
+ if (!matched) break;
794
+ chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
795
+ pos = matched.end;
796
+ }
797
+ if (chunks.length < spec.minRepeats) {
798
+ return { end: cur, chunks: [] };
799
+ }
800
+ return { end: pos, chunks };
801
+ }
802
+ function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
803
+ const chunks = [];
804
+ let pos = cur;
805
+ while (pos < input.length) {
806
+ const c = input.charCodeAt(pos);
807
+ if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {
808
+ const start = pos;
809
+ pos++;
810
+ while (pos < input.length) {
811
+ const c2 = input.charCodeAt(pos);
812
+ if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) pos++;
813
+ else break;
814
+ }
815
+ chunks.push({ start, end: pos, kindIndex: wsKind });
816
+ continue;
817
+ }
818
+ if (c === 47 && input.charCodeAt(pos + 1) === 42) {
819
+ const start = pos;
820
+ let j = pos + 2;
821
+ while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++;
822
+ pos = j + 2 <= input.length ? j + 2 : input.length;
823
+ chunks.push({ start, end: pos, kindIndex: commentKind });
824
+ continue;
825
+ }
826
+ break;
827
+ }
828
+ return { end: pos, chunks };
829
+ }
830
+ function tryFastLabeledScan(input, cur, trivia2) {
831
+ const spec = analyzeLabeledTrivia(trivia2);
832
+ if (!spec || spec.arms.length !== 2) return null;
833
+ const wsArm = spec.arms.find((a) => {
834
+ const src = getCoreRegexDef(a.parser)?.source;
835
+ return src != null && !src.includes("\\*");
836
+ });
837
+ const commentArm = spec.arms.find((a) => {
838
+ const src = getCoreRegexDef(a.parser)?.source;
839
+ return src != null && src.includes("\\*");
840
+ });
841
+ if (!wsArm || !commentArm) return null;
842
+ const { chunks } = scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
843
+ if (chunks.length < spec.minRepeats) return { end: cur, chunks: [] };
844
+ return scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
845
+ }
846
+ function recordTriviaChunks(ctx, chunks) {
847
+ const kinds = ctx.triviaKindLabels;
848
+ for (const ch of chunks) {
849
+ pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
850
+ if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
851
+ pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
852
+ }
853
+ }
854
+ }
855
+
856
+ // src/combinators/trivia-skip.ts
857
+ var NOOP_COMMIT = () => {
858
+ };
859
+ function needsDeferredTriviaCommit(ctx) {
860
+ return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
861
+ }
862
+ function saveTriviaMark(ctx) {
863
+ const m = saveCstMark(ctx);
864
+ return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
865
+ }
866
+ function rollbackTrivia(ctx, mark) {
867
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves });
868
+ if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
869
+ }
870
+ function scanWithLabels(input, cur, ctx) {
871
+ const triviaP = ctx.trivia;
872
+ const spec = analyzeLabeledTrivia(triviaP);
873
+ if (!spec) return { end: cur, commit: NOOP_COMMIT };
874
+ const fast = tryFastLabeledScan(input, cur, triviaP);
875
+ const { end, chunks } = fast ?? scanLabeledTriviaChunks(input, cur, spec);
876
+ if (end === cur) return { end: cur, commit: NOOP_COMMIT };
877
+ return {
878
+ end,
879
+ commit: () => recordTriviaChunks(ctx, chunks)
880
+ };
881
+ }
882
+ function advanceTrivia(input, cur, ctx) {
883
+ const triviaP = ctx.trivia;
884
+ if (!triviaP) return cur;
885
+ if (ctx.triviaKindLabels) {
886
+ const scan = scanWithLabels(input, cur, ctx);
887
+ return scan.end;
888
+ }
889
+ const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
890
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
891
+ }
892
+ function scanTrivia(input, cur, ctx) {
893
+ const triviaP = ctx.trivia;
894
+ if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
895
+ const log = ctx._triviaLog;
896
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
897
+ if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
898
+ return scanWithLabels(input, cur, ctx);
899
+ }
900
+ if (log !== void 0 || captureTl) {
901
+ const tr2 = triviaP.parse(input, cur, {
902
+ trackLines: log !== void 0 ? false : ctx.trackLines,
903
+ state: ctx.state
904
+ });
905
+ if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
906
+ const end = tr2.span.end;
907
+ return {
908
+ end,
909
+ commit: () => {
910
+ pushTriviaLogEntry(ctx, cur, end);
911
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
912
+ }
913
+ };
914
+ }
915
+ const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
916
+ return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
917
+ }
918
+
919
+ // src/combinators/sequence.ts
920
+ function sequence(...parsers) {
921
+ const meta = {
922
+ firstSet: parsers[0]?._meta.firstSet ?? empty(),
923
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
924
+ isTrivia: false
925
+ };
926
+ return {
927
+ _tag: "sequence",
928
+ _meta: meta,
929
+ _def: { tag: "sequence", parsers },
930
+ parse(input, pos, ctx) {
931
+ const values = [];
932
+ let cur = pos;
933
+ for (let i = 0; i < parsers.length; i++) {
934
+ if (ctx.trivia && i > 0) {
935
+ let scanEnd;
936
+ let mark = saveTriviaMark(ctx);
937
+ if (needsDeferredTriviaCommit(ctx)) {
938
+ const scan = scanTrivia(input, cur, ctx);
939
+ scan.commit();
940
+ scanEnd = scan.end;
941
+ } else {
942
+ scanEnd = advanceTrivia(input, cur, ctx);
943
+ }
944
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
945
+ if (!result2.ok) return result2;
946
+ if (result2.span.end > scanEnd) {
947
+ cur = result2.span.end;
948
+ } else {
949
+ rollbackTrivia(ctx, mark);
950
+ }
951
+ values.push(result2.value);
952
+ continue;
953
+ }
954
+ const result = parsers[i].parse(input, cur, ctx);
955
+ if (!result.ok) return result;
956
+ values.push(result.value);
957
+ cur = result.span.end;
958
+ }
959
+ return {
960
+ ok: true,
961
+ value: values,
962
+ span: { start: pos, end: cur }
963
+ };
964
+ }
965
+ };
966
+ }
967
+
683
968
  // src/combinators/repeat.ts
684
- function repItem(parser2, input, cur, ctx) {
685
- const raw = ctx._cstRawChildren;
686
- const mark = raw ? raw.length : 0;
969
+ function repItem(combinator, input, cur, ctx) {
970
+ const mark = saveTriviaMark(ctx);
687
971
  let pos = cur;
688
972
  if (ctx.trivia) {
689
- const scan = scanTrivia(input, cur, ctx);
690
- scan.commit();
691
- pos = scan.end;
973
+ if (needsDeferredTriviaCommit(ctx)) {
974
+ const scan = scanTrivia(input, cur, ctx);
975
+ scan.commit();
976
+ pos = scan.end;
977
+ } else {
978
+ pos = advanceTrivia(input, cur, ctx);
979
+ }
692
980
  }
693
981
  if (pos >= input.length) {
694
- if (raw) raw.length = mark;
982
+ rollbackTrivia(ctx, mark);
695
983
  return "stop";
696
984
  }
697
- const result = parser2.parse(input, pos, ctx);
985
+ const result = combinator.parse(input, pos, ctx);
698
986
  if (!result.ok) {
699
- if (raw) raw.length = mark;
987
+ rollbackTrivia(ctx, mark);
700
988
  return { fail: result };
701
989
  }
702
990
  if (result.span.end === pos) {
703
- if (raw) raw.length = mark;
991
+ rollbackTrivia(ctx, mark);
704
992
  return "stop";
705
993
  }
706
994
  return { value: result.value, end: result.span.end };
707
995
  }
708
- function many(parser2) {
996
+ function many(combinator) {
709
997
  const meta = {
710
- firstSet: parser2._meta.firstSet,
711
- canMatchNewline: parser2._meta.canMatchNewline,
998
+ firstSet: combinator._meta.firstSet,
999
+ canMatchNewline: combinator._meta.canMatchNewline,
712
1000
  isTrivia: false
713
1001
  };
714
1002
  return {
715
1003
  _tag: "many",
716
1004
  _meta: meta,
717
- _def: { tag: "many", parser: parser2, min: 0 },
1005
+ _def: { tag: "many", parser: combinator, min: 0 },
718
1006
  parse(input, pos, ctx) {
719
1007
  const values = [];
720
1008
  let cur = pos;
721
1009
  while (cur < input.length) {
722
- const item = repItem(parser2, input, cur, ctx);
1010
+ const item = repItem(combinator, input, cur, ctx);
723
1011
  if (item === "stop" || "fail" in item) break;
724
1012
  values.push(item.value);
725
1013
  cur = item.end;
@@ -728,23 +1016,23 @@ function many(parser2) {
728
1016
  }
729
1017
  };
730
1018
  }
731
- function oneOrMore(parser2) {
1019
+ function oneOrMore(combinator) {
732
1020
  const meta = {
733
- firstSet: parser2._meta.firstSet,
734
- canMatchNewline: parser2._meta.canMatchNewline,
1021
+ firstSet: combinator._meta.firstSet,
1022
+ canMatchNewline: combinator._meta.canMatchNewline,
735
1023
  isTrivia: false
736
1024
  };
737
1025
  return {
738
1026
  _tag: "oneOrMore",
739
1027
  _meta: meta,
740
- _def: { tag: "oneOrMore", parser: parser2, min: 1 },
1028
+ _def: { tag: "oneOrMore", parser: combinator, min: 1 },
741
1029
  parse(input, pos, ctx) {
742
- const first = parser2.parse(input, pos, ctx);
1030
+ const first = combinator.parse(input, pos, ctx);
743
1031
  if (!first.ok) return first;
744
1032
  const values = [first.value];
745
1033
  let cur = first.span.end;
746
1034
  while (cur < input.length) {
747
- const item = repItem(parser2, input, cur, ctx);
1035
+ const item = repItem(combinator, input, cur, ctx);
748
1036
  if (item === "stop" || "fail" in item) break;
749
1037
  values.push(item.value);
750
1038
  cur = item.end;
@@ -753,45 +1041,71 @@ function oneOrMore(parser2) {
753
1041
  }
754
1042
  };
755
1043
  }
756
- function optional(parser2) {
1044
+ function optional(combinator) {
757
1045
  const meta = {
758
- firstSet: parser2._meta.firstSet,
759
- canMatchNewline: parser2._meta.canMatchNewline,
1046
+ firstSet: combinator._meta.firstSet,
1047
+ canMatchNewline: combinator._meta.canMatchNewline,
760
1048
  isTrivia: false
761
1049
  };
762
1050
  return {
763
1051
  _tag: "optional",
764
1052
  _meta: meta,
765
- _def: { tag: "optional", parser: parser2 },
1053
+ _def: { tag: "optional", parser: combinator },
766
1054
  parse(input, pos, ctx) {
767
- const result = parser2.parse(input, pos, ctx);
1055
+ const result = combinator.parse(input, pos, ctx);
768
1056
  if (result.ok) return result;
769
1057
  return { ok: true, value: null, span: { start: pos, end: pos } };
770
1058
  }
771
1059
  };
772
1060
  }
773
- function sepBy(parser2, separator) {
1061
+ function sepBy(combinator, separator) {
774
1062
  const meta = {
775
- firstSet: parser2._meta.firstSet,
776
- canMatchNewline: parser2._meta.canMatchNewline || separator._meta.canMatchNewline,
1063
+ firstSet: combinator._meta.firstSet,
1064
+ canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
777
1065
  isTrivia: false
778
1066
  };
779
1067
  return {
780
1068
  _tag: "sepBy",
781
1069
  _meta: meta,
782
- _def: { tag: "sepBy", parser: parser2, separator },
1070
+ _def: { tag: "sepBy", parser: combinator, separator },
783
1071
  parse(input, pos, ctx) {
784
- const first = parser2.parse(input, pos, ctx);
1072
+ const first = combinator.parse(input, pos, ctx);
785
1073
  if (!first.ok) return { ok: true, value: [], span: { start: pos, end: pos } };
786
1074
  const values = [first.value];
787
1075
  let cur = first.span.end;
788
1076
  while (cur < input.length) {
789
- const sepPos = ctx.trivia ? consumeTrivia(input, cur, ctx) : cur;
1077
+ let mark = saveTriviaMark(ctx);
1078
+ let sepPos = cur;
1079
+ if (ctx.trivia) {
1080
+ if (needsDeferredTriviaCommit(ctx)) {
1081
+ const scan = scanTrivia(input, cur, ctx);
1082
+ scan.commit();
1083
+ sepPos = scan.end;
1084
+ } else {
1085
+ sepPos = advanceTrivia(input, cur, ctx);
1086
+ }
1087
+ }
790
1088
  const sep = separator.parse(input, sepPos, ctx);
791
- if (!sep.ok) break;
792
- const nextPos = ctx.trivia ? consumeTrivia(input, sep.span.end, ctx) : sep.span.end;
793
- const next = parser2.parse(input, nextPos, ctx);
794
- if (!next.ok) break;
1089
+ if (!sep.ok) {
1090
+ rollbackTrivia(ctx, mark);
1091
+ break;
1092
+ }
1093
+ mark = saveTriviaMark(ctx);
1094
+ let nextPos = sep.span.end;
1095
+ if (ctx.trivia) {
1096
+ if (needsDeferredTriviaCommit(ctx)) {
1097
+ const scan = scanTrivia(input, sep.span.end, ctx);
1098
+ scan.commit();
1099
+ nextPos = scan.end;
1100
+ } else {
1101
+ nextPos = advanceTrivia(input, sep.span.end, ctx);
1102
+ }
1103
+ }
1104
+ const next = combinator.parse(input, nextPos, ctx);
1105
+ if (!next.ok) {
1106
+ rollbackTrivia(ctx, mark);
1107
+ break;
1108
+ }
795
1109
  values.push(next.value);
796
1110
  cur = next.span.end;
797
1111
  }
@@ -861,7 +1175,7 @@ function rules(factory) {
861
1175
  }
862
1176
 
863
1177
  // src/combinators/not.ts
864
- function not(parser2) {
1178
+ function not(combinator) {
865
1179
  const meta = {
866
1180
  firstSet: any(),
867
1181
  // we don't know what NOT matches
@@ -871,11 +1185,11 @@ function not(parser2) {
871
1185
  return {
872
1186
  _tag: "not",
873
1187
  _meta: meta,
874
- _def: { tag: "not", parser: parser2 },
1188
+ _def: { tag: "not", parser: combinator },
875
1189
  parse(input, pos, ctx) {
876
- const result = parser2.parse(input, pos, ctx);
1190
+ const result = combinator.parse(input, pos, ctx);
877
1191
  if (result.ok) {
878
- return { ok: false, expected: [`not(${parser2._tag})`], span: { start: pos, end: pos } };
1192
+ return { ok: false, expected: [`not(${combinator._tag})`], span: { start: pos, end: pos } };
879
1193
  }
880
1194
  return { ok: true, value: null, span: { start: pos, end: pos } };
881
1195
  }
@@ -883,37 +1197,27 @@ function not(parser2) {
883
1197
  }
884
1198
 
885
1199
  // src/combinators/node.ts
886
- function node(type, parser2, build) {
1200
+ function node(type, combinator, build) {
887
1201
  const meta = {
888
- firstSet: parser2._meta.firstSet,
889
- canMatchNewline: parser2._meta.canMatchNewline,
1202
+ firstSet: combinator._meta.firstSet,
1203
+ canMatchNewline: combinator._meta.canMatchNewline,
890
1204
  isTrivia: false
891
1205
  };
892
1206
  return {
893
1207
  _tag: "node",
894
1208
  _meta: meta,
895
- _def: { tag: "node", type, parser: parser2, build },
1209
+ _def: { tag: "node", type, parser: combinator, build },
896
1210
  parse(input, pos, ctx) {
897
- const children = [];
898
- const rawChildren = [];
899
- const innerCtx = {
900
- ...ctx,
901
- // A node() always records the trivia between its terms — the library owns
902
- // capture so grammar authors don't reconstruct it. (Matches the compiler.)
903
- captureTrivia: true,
904
- _cstChildren: children,
905
- _cstLeaves: children,
906
- _cstRawChildren: rawChildren
907
- };
908
- const r = parser2.parse(input, pos, innerCtx);
1211
+ const saved = beginCstNodeCapture(ctx);
1212
+ const r = combinator.parse(input, pos, ctx);
1213
+ const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
909
1214
  if (!r.ok) return r;
910
- const built = build(children, rawChildren, r.span);
1215
+ const state = ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
1216
+ const built = build(children, rawChildren, r.span, triviaLog, state);
911
1217
  const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
912
- if (ctx._cstChildren) ctx._cstChildren.push(built);
913
- if (ctx._cstRawChildren) {
914
- ctx._cstRawChildren.push(
915
- isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span }
916
- );
1218
+ const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1219
+ if (saved.buf !== void 0 || saved.ch !== void 0) {
1220
+ pushCstChild(ctx, built, rawEntry);
917
1221
  }
918
1222
  return { ok: true, value: built, span: r.span };
919
1223
  }
@@ -921,13 +1225,13 @@ function node(type, parser2, build) {
921
1225
  }
922
1226
 
923
1227
  // src/combinators/map.ts
924
- function transform(parser2, fn) {
1228
+ function transform(combinator, fn) {
925
1229
  return {
926
1230
  _tag: "transform",
927
- _meta: parser2._meta,
928
- _def: { tag: "transform", parser: parser2, fn },
1231
+ _meta: combinator._meta,
1232
+ _def: { tag: "transform", parser: combinator, fn },
929
1233
  parse(input, pos, ctx) {
930
- const result = parser2.parse(input, pos, ctx);
1234
+ const result = combinator.parse(input, pos, ctx);
931
1235
  if (!result.ok) return result;
932
1236
  return { ...result, value: fn(result.value, result.span) };
933
1237
  }
@@ -947,12 +1251,25 @@ function skip(main, skipped) {
947
1251
  }
948
1252
  };
949
1253
  }
950
- function trivia(parser2) {
1254
+ function trivia(combinator) {
1255
+ const kindLabels = analyzeLabeledTrivia(combinator)?.labels;
951
1256
  return {
952
- _tag: parser2._tag,
953
- _meta: { ...parser2._meta, isTrivia: true },
954
- _def: { tag: "trivia", parser: parser2 },
955
- parse: parser2.parse.bind(parser2)
1257
+ _tag: combinator._tag,
1258
+ _meta: {
1259
+ ...combinator._meta,
1260
+ isTrivia: true,
1261
+ ...kindLabels ? { triviaKindLabels: kindLabels } : {}
1262
+ },
1263
+ _def: { tag: "trivia", parser: combinator },
1264
+ parse: combinator.parse.bind(combinator)
1265
+ };
1266
+ }
1267
+ function label(name, combinator) {
1268
+ return {
1269
+ _tag: combinator._tag,
1270
+ _meta: combinator._meta,
1271
+ _def: { tag: "label", label: name, parser: combinator },
1272
+ parse: combinator.parse.bind(combinator)
956
1273
  };
957
1274
  }
958
1275
 
@@ -1003,7 +1320,10 @@ function parser(opts, root) {
1003
1320
  const ctx = {
1004
1321
  ..._ctx,
1005
1322
  trackLines,
1006
- ...opts.trivia !== void 0 ? { trivia: opts.trivia } : {},
1323
+ ...opts.trivia !== void 0 ? {
1324
+ trivia: opts.trivia,
1325
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
1326
+ } : {},
1007
1327
  ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {}
1008
1328
  };
1009
1329
  const result = root.parse(input, pos ?? 0, ctx);
@@ -1032,6 +1352,320 @@ function parse(combinator, input, opts = {}) {
1032
1352
  return withErrors;
1033
1353
  }
1034
1354
 
1355
+ // src/compiler/trivia-fast-path.ts
1356
+ var BLOCK_COMMENT = String.raw`\/\*(?:[^*]|\*(?!\/))*\*\/`;
1357
+ var WS_CLASS_SOURCES = /* @__PURE__ */ new Set([
1358
+ String.raw`[ \t\n\r\f]+`,
1359
+ String.raw`[ \t\n]+`,
1360
+ String.raw`[ \t]+`,
1361
+ String.raw`[\t\n\f\r ]+`
1362
+ ]);
1363
+ function unwrapTrivia2(p) {
1364
+ let cur = p;
1365
+ while (cur._def.tag === "trivia") cur = cur._def.parser;
1366
+ return cur;
1367
+ }
1368
+ function oneOrMoreInner(p) {
1369
+ const d = p._def;
1370
+ if (d.tag === "oneOrMore") return d.parser;
1371
+ if (d.tag === "many" && d.min >= 1) return d.parser;
1372
+ return null;
1373
+ }
1374
+ function choiceArms(p) {
1375
+ if (p._def.tag === "choice") return p._def.parsers;
1376
+ return null;
1377
+ }
1378
+ function isWsClassSource(source) {
1379
+ return WS_CLASS_SOURCES.has(source);
1380
+ }
1381
+ function isBlockCommentSource(source) {
1382
+ return source === BLOCK_COMMENT;
1383
+ }
1384
+ function analyzeTriviaFastPath(trivia2) {
1385
+ const core = unwrapTrivia2(trivia2);
1386
+ const direct = getCoreRegexDef(core)?.source;
1387
+ if (direct) {
1388
+ if (isWsClassSource(direct)) return "wsOnly";
1389
+ return null;
1390
+ }
1391
+ const inner = oneOrMoreInner(core);
1392
+ if (!inner) return null;
1393
+ const innerSrc = getCoreRegexDef(inner)?.source;
1394
+ if (innerSrc && isWsClassSource(innerSrc)) return "wsOnly";
1395
+ const arms = choiceArms(inner);
1396
+ if (!arms || arms.length !== 2) return null;
1397
+ let hasWs = false;
1398
+ let hasComment = false;
1399
+ for (const arm of arms) {
1400
+ const src = getCoreRegexDef(arm)?.source;
1401
+ if (!src) return null;
1402
+ if (isWsClassSource(src)) hasWs = true;
1403
+ else if (isBlockCommentSource(src)) hasComment = true;
1404
+ else return null;
1405
+ }
1406
+ if (hasWs && hasComment) return "wsComments";
1407
+ return null;
1408
+ }
1409
+ var CAP_RECORD = [
1410
+ ` if (_cap && _e > _pos) {`,
1411
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
1412
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
1413
+ ` }`
1414
+ ].join("\n");
1415
+ var WS_ONLY_LOOP = [
1416
+ ` while (_e < input.length) {`,
1417
+ ` const c = input.charCodeAt(_e)`,
1418
+ ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) { _e++; continue }`,
1419
+ ` break`,
1420
+ ` }`
1421
+ ].join("\n");
1422
+ var WS_COMMENTS_LOOP = [
1423
+ ` while (_e < input.length) {`,
1424
+ ` const c = input.charCodeAt(_e)`,
1425
+ ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) { _e++; continue }`,
1426
+ ` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
1427
+ ` let j = _e + 2`,
1428
+ ` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
1429
+ ` _e = j + 2 <= input.length ? j + 2 : input.length`,
1430
+ ` continue`,
1431
+ ` }`,
1432
+ ` break`,
1433
+ ` }`
1434
+ ].join("\n");
1435
+ var CAP_CHUNK = (wsKind, commentKind) => [
1436
+ ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {`,
1437
+ ` const _cs = _e`,
1438
+ ` _e++`,
1439
+ ` while (_e < input.length) {`,
1440
+ ` const c2 = input.charCodeAt(_e)`,
1441
+ ` if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) _e++`,
1442
+ ` else break`,
1443
+ ` }`,
1444
+ ` if (_cap) {`,
1445
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${wsKind})`,
1446
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${wsKind})`,
1447
+ ` }`,
1448
+ ` continue`,
1449
+ ` }`,
1450
+ ` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
1451
+ ` const _cs = _e`,
1452
+ ` let j = _e + 2`,
1453
+ ` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
1454
+ ` _e = j + 2 <= input.length ? j + 2 : input.length`,
1455
+ ` if (_cap) {`,
1456
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${commentKind})`,
1457
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${commentKind})`,
1458
+ ` }`,
1459
+ ` continue`,
1460
+ ` }`
1461
+ ].join("\n");
1462
+ var WS_COMMENTS_LOOP_LABELED = (wsKind, commentKind) => [
1463
+ ` while (_e < input.length) {`,
1464
+ ` const c = input.charCodeAt(_e)`,
1465
+ CAP_CHUNK(wsKind, commentKind),
1466
+ ` break`,
1467
+ ` }`
1468
+ ].join("\n");
1469
+ function buildFastTriviaFnDecl(fnName, kind, kindIndices) {
1470
+ const loop = kind === "wsComments" && kindIndices ? WS_COMMENTS_LOOP_LABELED(kindIndices.ws, kindIndices.comment) : kind === "wsComments" ? WS_COMMENTS_LOOP : WS_ONLY_LOOP;
1471
+ const cap = kindIndices ? "" : CAP_RECORD;
1472
+ const lines = [
1473
+ `function ${fnName}(input, _pos, _ctx, _cap) {`,
1474
+ ` let _e = _pos`,
1475
+ loop
1476
+ ];
1477
+ if (cap) lines.push(cap);
1478
+ lines.push(` return _e`, `}`);
1479
+ return lines.join("\n");
1480
+ }
1481
+ function regexArmIndex(spec, isComment) {
1482
+ for (const arm of spec.arms) {
1483
+ const src = getCoreRegexDef(arm.parser)?.source;
1484
+ if (!src) return null;
1485
+ const comment = src.includes("\\*") || src.startsWith("\\/\\/");
1486
+ if (comment === isComment) return arm.kindIndex;
1487
+ }
1488
+ return null;
1489
+ }
1490
+ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
1491
+ const tryArms = spec.arms.map((arm, i) => {
1492
+ const re = reNames[i];
1493
+ const k = arm.kindIndex;
1494
+ return [
1495
+ ` ${re}.lastIndex = _e`,
1496
+ ` const _m${i} = ${re}.exec(input)`,
1497
+ ` if (_m${i} && _m${i}.index === _e) {`,
1498
+ ` const _ce = _e + _m${i}[0].length`,
1499
+ ` if (_cap) {`,
1500
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
1501
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
1502
+ ` }`,
1503
+ ` _e = _ce`,
1504
+ ` _matched = true`,
1505
+ ` continue`,
1506
+ ` }`
1507
+ ].join("\n");
1508
+ }).join("\n");
1509
+ return [
1510
+ `function ${fnName}(input, _pos, _ctx, _cap) {`,
1511
+ ` let _e = _pos`,
1512
+ ` while (_e < input.length) {`,
1513
+ ` let _matched = false`,
1514
+ tryArms,
1515
+ ` if (!_matched) break`,
1516
+ ` }`,
1517
+ ` return _e`,
1518
+ `}`
1519
+ ].join("\n");
1520
+ }
1521
+ function labeledTriviaKindIndices(trivia2) {
1522
+ const spec = analyzeLabeledTrivia(trivia2);
1523
+ if (!spec || spec.arms.length !== 2) return null;
1524
+ const ws = regexArmIndex(spec, false);
1525
+ const comment = regexArmIndex(spec, true);
1526
+ if (ws === null || comment === null) return null;
1527
+ return { ws, comment };
1528
+ }
1529
+ function labeledTriviaRegexArms(trivia2) {
1530
+ const spec = analyzeLabeledTrivia(trivia2);
1531
+ if (!spec) return null;
1532
+ for (const arm of spec.arms) {
1533
+ if (!getCoreRegexDef(arm.parser)) return null;
1534
+ }
1535
+ return spec;
1536
+ }
1537
+ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
1538
+ const tryArms = spec.arms.map((arm, i) => {
1539
+ const k = arm.kindIndex;
1540
+ const ri = rpStartIndex + i;
1541
+ return [
1542
+ ` const _r${i} = _rp[${ri}].parse(input, _e, _ctx)`,
1543
+ ` if (_r${i}.ok && _r${i}.span.end > _e) {`,
1544
+ ` const _ce = _r${i}.span.end`,
1545
+ ` if (_cap) {`,
1546
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
1547
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
1548
+ ` }`,
1549
+ ` _e = _ce`,
1550
+ ` _matched = true`,
1551
+ ` continue`,
1552
+ ` }`
1553
+ ].join("\n");
1554
+ }).join("\n");
1555
+ return [
1556
+ `function ${fnName}(input, _pos, _ctx, _cap) {`,
1557
+ ` let _e = _pos`,
1558
+ ` while (_e < input.length) {`,
1559
+ ` let _matched = false`,
1560
+ tryArms,
1561
+ ` if (!_matched) break`,
1562
+ ` }`,
1563
+ ` return _e`,
1564
+ `}`
1565
+ ].join("\n");
1566
+ }
1567
+
1568
+ // src/compiler/inline-build.ts
1569
+ 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*$/;
1570
+ function escapeRegExp(s) {
1571
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1572
+ }
1573
+ function looseMkBuildRe(type) {
1574
+ return new RegExp(
1575
+ 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*$`
1576
+ );
1577
+ }
1578
+ function analyzeMkInlineBuild(def) {
1579
+ const src = (def.buildSrc ?? def.build.toString()).trim();
1580
+ const strict = src.match(MK_BUILD_RE);
1581
+ if (strict) {
1582
+ const mkType = strict[2];
1583
+ return mkType === def.type ? def.type : null;
1584
+ }
1585
+ if (looseMkBuildRe(def.type).test(src)) return def.type;
1586
+ return null;
1587
+ }
1588
+ function emitInlineMkNodeExpr(type, chV, rawV, pos, endVar, tlV) {
1589
+ return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: { start: ${pos}, end: ${endVar} }, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
1590
+ }
1591
+
1592
+ // src/compiler/inline-callback.ts
1593
+ var INLINE_BUILTINS = /* @__PURE__ */ new Set([
1594
+ "parseInt",
1595
+ "parseFloat",
1596
+ "String",
1597
+ "Number",
1598
+ "Boolean",
1599
+ "Object",
1600
+ "Array",
1601
+ "Math",
1602
+ "JSON",
1603
+ "undefined",
1604
+ "null",
1605
+ "true",
1606
+ "false",
1607
+ "NaN",
1608
+ "Infinity"
1609
+ ]);
1610
+ function escapeRegExp2(s) {
1611
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1612
+ }
1613
+ function transformFnSource(fn, fnSrc) {
1614
+ if (fnSrc) return fnSrc.trim();
1615
+ const s = fn.toString().trim();
1616
+ if (s.includes("[native code]")) return null;
1617
+ return s;
1618
+ }
1619
+ function replaceParam(body, param, valueVar) {
1620
+ return body.replace(new RegExp(`\\b${escapeRegExp2(param)}\\b`, "g"), valueVar);
1621
+ }
1622
+ function stripForIdCheck(body) {
1623
+ return body.replace(/'[^'\\]*(?:\\.[^'\\]*)*'|"[^"\\]*(?:\\.[^"\\]*)*"/g, '""').replace(/\bas\s+const\b/g, "").replace(/\b[A-Za-z_$][\w$]*\s*:/g, ":");
1624
+ }
1625
+ function hasFreeIdentifiers(body, allowed) {
1626
+ const stripped = stripForIdCheck(body);
1627
+ const ids = stripped.match(/\b[A-Za-z_$][\w$]*\b/g) ?? [];
1628
+ for (const id of ids) {
1629
+ if (INLINE_BUILTINS.has(id)) continue;
1630
+ if (allowed.has(id)) continue;
1631
+ return true;
1632
+ }
1633
+ return false;
1634
+ }
1635
+ function tryInlineUnaryTransform(src, valueVar) {
1636
+ const allowed = /* @__PURE__ */ new Set([valueVar]);
1637
+ const nullary = src.match(/^\(\s*\)\s*=>\s*(.+)$/);
1638
+ if (nullary) {
1639
+ const body2 = nullary[1].trim();
1640
+ return hasFreeIdentifiers(body2, allowed) ? null : body2;
1641
+ }
1642
+ const m = src.match(/^(\w+)\s*=>\s*(.+)$/);
1643
+ if (!m) return null;
1644
+ const body = replaceParam(m[2].trim(), m[1], valueVar);
1645
+ return hasFreeIdentifiers(body, allowed) ? null : body;
1646
+ }
1647
+ function parseArrayDestructure(pattern, arity) {
1648
+ const slots = [];
1649
+ for (const part of pattern.split(",")) {
1650
+ const name = part.trim();
1651
+ slots.push(name === "" || name === "_" ? null : name);
1652
+ }
1653
+ while (slots.length < arity) slots.push(null);
1654
+ return slots.slice(0, arity);
1655
+ }
1656
+ function tryInlineDestructureTransform(src, valueVars) {
1657
+ const m = src.match(/^\(\s*\[([^\]]*)\]\s*\)\s*=>\s*(.+)$/s);
1658
+ if (!m) return null;
1659
+ const slots = parseArrayDestructure(m[1], valueVars.length);
1660
+ let body = m[2].trim();
1661
+ const allowed = new Set(valueVars);
1662
+ for (let i = 0; i < slots.length; i++) {
1663
+ const name = slots[i];
1664
+ if (name) body = replaceParam(body, name, valueVars[i]);
1665
+ }
1666
+ return hasFreeIdentifiers(body, allowed) ? null : body;
1667
+ }
1668
+
1035
1669
  // src/compiler/codegen.ts
1036
1670
  function v(ctx, prefix = "_v") {
1037
1671
  return `${prefix}${ctx.vars++}`;
@@ -1039,12 +1673,42 @@ function v(ctx, prefix = "_v") {
1039
1673
  function ind(ctx) {
1040
1674
  return " ".repeat(ctx.indent);
1041
1675
  }
1676
+ var NAMED_FN_FAIL = "_pfFail";
1677
+ var NAMED_FN_END = "_pfEnd";
1678
+ function namedFnPrelude() {
1679
+ return [`const ${NAMED_FN_FAIL} = {}`, `let ${NAMED_FN_END}`];
1680
+ }
1681
+ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
1682
+ ctx.namedFnDecls.push([
1683
+ `function ${fnName}(input, _pos, _ctx) {`,
1684
+ ` let _pfok = false, _pfv, _pfe = _pos`,
1685
+ ` _pfail: {`,
1686
+ ...bodyStmts,
1687
+ ` _pfv = ${valueVar}; _pfe = ${endVar}; _pfok = true`,
1688
+ ` }`,
1689
+ ` if (!_pfok) return ${NAMED_FN_FAIL}`,
1690
+ ` ${NAMED_FN_END} = _pfe`,
1691
+ ` return _pfv`,
1692
+ `}`
1693
+ ].join("\n"));
1694
+ }
1695
+ function emitNamedFnCall(ctx, fnName, pos, failExpected) {
1696
+ const vv = v(ctx, "_pfv");
1697
+ const ev = v(ctx, "_pfe");
1698
+ return {
1699
+ stmts: [
1700
+ `${ind(ctx)}const ${vv} = ${fnName}(input, ${pos}, _ctx)`,
1701
+ `${ind(ctx)}if (${vv} === ${NAMED_FN_FAIL}) ${failStmt({ ...ctx, indent: 0 }, failExpected, pos).trim()}`,
1702
+ `${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
1703
+ ],
1704
+ valueVar: vv,
1705
+ endVar: ev
1706
+ };
1707
+ }
1042
1708
  function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
1043
1709
  if (!ctx.capturing) return [];
1710
+ if (ctx.capAsTrivia) return [];
1044
1711
  const i = ind(ctx);
1045
- if (ctx.capAsTrivia) {
1046
- return [`${i}if (_ctx._cstRawChildren) _ctx._cstRawChildren.push({ _tag: 'trivia', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } })`];
1047
- }
1048
1712
  const lf = v(ctx, "_lf");
1049
1713
  return [
1050
1714
  `${i}if (_ctx._cstLeaves) {`,
@@ -1054,35 +1718,84 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
1054
1718
  `${i}}`
1055
1719
  ];
1056
1720
  }
1057
- function ensureTriviaCaptureFn(ctx) {
1721
+ function ensureRegexDecl(ctx, optimizedSource, flags) {
1722
+ const f = "y" + flags.replace(/[gy]/g, "");
1723
+ const key = `${optimizedSource}/${f}`;
1724
+ let rName = ctx.regexMap.get(key);
1725
+ if (rName === void 0) {
1726
+ rName = `_re${ctx.regexDecls.length}`;
1727
+ ctx.regexDecls.push(`const ${rName} = /${optimizedSource}/${f}`);
1728
+ ctx.regexMap.set(key, rName);
1729
+ }
1730
+ return rName;
1731
+ }
1732
+ function ensureTriviaFn(ctx) {
1058
1733
  const trivia2 = ctx.activeTrivia;
1059
- const existing = ctx.triviaCaptureNames.get(trivia2);
1734
+ const existing = ctx.triviaFnNames.get(trivia2);
1060
1735
  if (existing) return existing;
1061
- const fnName = `_tc${ctx.triviaCaptureNames.size}`;
1736
+ const fnName = `_tf${ctx.triviaFnNames.size}`;
1737
+ ctx.triviaFnNames.set(trivia2, fnName);
1062
1738
  ctx.triviaCaptureNames.set(trivia2, fnName);
1063
- const saved = { indent: ctx.indent, failLabel: ctx.failLabel, asTrivia: ctx.capAsTrivia, trivia: ctx.activeTrivia };
1739
+ const fastKind = analyzeTriviaFastPath(trivia2);
1740
+ const kindIndices = labeledTriviaKindIndices(trivia2);
1741
+ if (fastKind) {
1742
+ ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastKind, kindIndices ?? void 0));
1743
+ return fnName;
1744
+ }
1745
+ const labeledSpec = analyzeLabeledTrivia(trivia2);
1746
+ if (labeledSpec) {
1747
+ const regexSpec = labeledTriviaRegexArms(trivia2);
1748
+ if (regexSpec) {
1749
+ const reNames = [];
1750
+ for (const arm of regexSpec.arms) {
1751
+ const def = arm.parser._def;
1752
+ if (def.tag !== "regex") break;
1753
+ reNames.push(ensureRegexDecl(ctx, def.optimizedSource, def.flags));
1754
+ }
1755
+ if (reNames.length === regexSpec.arms.length) {
1756
+ ctx.namedFnDecls.push(buildLabeledRegexTriviaFnDecl(fnName, regexSpec, reNames));
1757
+ return fnName;
1758
+ }
1759
+ }
1760
+ const rpStart = ctx.runtimeParsers.length;
1761
+ for (const arm of labeledSpec.arms) {
1762
+ ctx.runtimeParsers.push(arm.parser);
1763
+ }
1764
+ ctx.namedFnDecls.push(buildLabeledRuntimeTriviaFnDecl(fnName, labeledSpec, rpStart));
1765
+ return fnName;
1766
+ }
1767
+ const savedIndent = ctx.indent;
1768
+ const savedFailLabel = ctx.failLabel;
1769
+ const savedTrivia = ctx.activeTrivia;
1770
+ const savedCapAsTrivia = ctx.capAsTrivia;
1064
1771
  ctx.indent = 2;
1065
- ctx.failLabel = "_cap";
1772
+ ctx.failLabel = "_triv";
1066
1773
  ctx.capAsTrivia = true;
1067
- delete ctx.activeTrivia;
1774
+ ctx.activeTrivia = void 0;
1068
1775
  const r = emit(trivia2, ctx, "_pos");
1069
- ctx.indent = saved.indent;
1070
- ctx.failLabel = saved.failLabel;
1071
- ctx.capAsTrivia = saved.asTrivia;
1072
- if (saved.trivia) ctx.activeTrivia = saved.trivia;
1776
+ ctx.indent = savedIndent;
1777
+ ctx.failLabel = savedFailLabel;
1778
+ ctx.capAsTrivia = savedCapAsTrivia;
1779
+ ctx.activeTrivia = savedTrivia;
1073
1780
  ctx.namedFnDecls.push([
1074
- `function ${fnName}(input, _pos, _ctx) {`,
1781
+ `function ${fnName}(input, _pos, _ctx, _cap) {`,
1075
1782
  ` let _e = _pos`,
1076
- ` _cap: {`,
1783
+ ` _triv: {`,
1077
1784
  ...r.stmts,
1078
1785
  ` _e = ${r.endVar}`,
1079
1786
  ` }`,
1080
- ` if (_ctx._triviaLog !== undefined && _e > _pos) _ctx._triviaLog.push(_pos, _e)`,
1787
+ ` if (_cap && _e > _pos) {`,
1788
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
1789
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
1790
+ ` }`,
1081
1791
  ` return _e`,
1082
1792
  `}`
1083
1793
  ].join("\n"));
1084
1794
  return fnName;
1085
1795
  }
1796
+ function ensureTriviaCaptureFn(ctx) {
1797
+ return ensureTriviaFn(ctx);
1798
+ }
1086
1799
  function failStmt(ctx, expected, posExpr) {
1087
1800
  if (ctx.failLabel) return `${ind(ctx)}break ${ctx.failLabel}`;
1088
1801
  return `${ind(ctx)}return { ok: false, expected: [${expected}], span: { start: ${posExpr}, end: ${posExpr} } }`;
@@ -1098,14 +1811,6 @@ function firstSetCond(codeVar, fs) {
1098
1811
  (r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
1099
1812
  ).join(" || ");
1100
1813
  }
1101
- function asIIFE(stmts, valueVar, endVar, startPos, indent) {
1102
- return [
1103
- `(() => {`,
1104
- ...stmts,
1105
- `${indent} return { ok: true, value: ${valueVar}, span: { start: ${startPos}, end: ${endVar} } }`,
1106
- `${indent}})()`
1107
- ].join("\n");
1108
- }
1109
1814
  function emitFallible(inner, ctx, pos) {
1110
1815
  const lbl = v(ctx, "_lbl");
1111
1816
  const okV = `${lbl}ok`;
@@ -1171,6 +1876,33 @@ function emitLit(def, ctx, pos) {
1171
1876
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
1172
1877
  return { stmts, valueVar: vv, endVar };
1173
1878
  }
1879
+ function escapeKeywordRe(s) {
1880
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1881
+ }
1882
+ function emitKeywords(def, ctx, pos) {
1883
+ const alt = def.words.map(escapeKeywordRe).join("|");
1884
+ const boundary = def.boundary ? `(?![${def.boundary}])` : "";
1885
+ const flags = def.caseInsensitive ? "iuy" : "uy";
1886
+ const source = `(?:${alt})${boundary}`;
1887
+ const key = `${source}/${flags}`;
1888
+ let rName = ctx.regexMap.get(key);
1889
+ if (rName === void 0) {
1890
+ rName = `_re${ctx.regexDecls.length}`;
1891
+ ctx.regexDecls.push(`const ${rName} = /${source}/${flags}`);
1892
+ ctx.regexMap.set(key, rName);
1893
+ }
1894
+ const mv = v(ctx, "_m");
1895
+ const vv = v(ctx);
1896
+ const stmts = [
1897
+ `${ind(ctx)}${rName}.lastIndex = ${pos}`,
1898
+ `${ind(ctx)}const ${mv} = ${rName}.exec(input)`,
1899
+ `${ind(ctx)}if (${mv} === null) ${failStmt({ ...ctx, indent: 0 }, '"keyword"', pos).trim()}`,
1900
+ `${ind(ctx)}const ${vv} = ${mv}[0]`
1901
+ ];
1902
+ const endVar = `${pos} + ${vv}.length`;
1903
+ stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
1904
+ return { stmts, valueVar: vv, endVar };
1905
+ }
1174
1906
  function emitRegex(def, ctx, pos) {
1175
1907
  const flags = "y" + def.flags.replace(/[gy]/g, "");
1176
1908
  const key = `${def.optimizedSource}/${flags}`;
@@ -1193,31 +1925,7 @@ function emitRegex(def, ctx, pos) {
1193
1925
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
1194
1926
  return { stmts, valueVar: vv, endVar };
1195
1927
  }
1196
- function ensureTriviaFn(ctx) {
1197
- const trivia2 = ctx.activeTrivia;
1198
- if (!ctx.namedParsers.has(trivia2)) {
1199
- const fnName = `_pf${ctx.namedParsers.size}`;
1200
- ctx.namedParsers.set(trivia2, fnName);
1201
- const savedIndent = ctx.indent;
1202
- const savedFailLabel = ctx.failLabel;
1203
- const savedTrivia = ctx.activeTrivia;
1204
- ctx.indent = 1;
1205
- ctx.failLabel = void 0;
1206
- delete ctx.activeTrivia;
1207
- const r = emit(trivia2, ctx, "_pos");
1208
- ctx.indent = savedIndent;
1209
- ctx.failLabel = savedFailLabel;
1210
- if (savedTrivia) ctx.activeTrivia = savedTrivia;
1211
- ctx.namedFnDecls.push([
1212
- `function ${fnName}(input, _pos, _ctx) {`,
1213
- ...r.stmts,
1214
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
1215
- `}`
1216
- ].join("\n"));
1217
- }
1218
- return ctx.namedParsers.get(trivia2);
1219
- }
1220
- function emitSeq(def, ctx, pos) {
1928
+ function emitSeqValues(def, ctx, pos) {
1221
1929
  const startV = v(ctx, "_start");
1222
1930
  const curV = v(ctx, "_cur");
1223
1931
  const stmts = [
@@ -1229,23 +1937,41 @@ function emitSeq(def, ctx, pos) {
1229
1937
  if (i > 0 && ctx.activeTrivia) {
1230
1938
  if (ctx.capturing) {
1231
1939
  const capFn = ensureTriviaCaptureFn(ctx);
1232
- stmts.push(`${ind(ctx)}${curV} = ${capFn}(input, ${curV}, _ctx)`);
1233
- } else {
1234
- const trivFn = ensureTriviaFn(ctx);
1235
- const tmp = v(ctx, "_trv");
1940
+ const markV = v(ctx, "_mk");
1941
+ const markTl = v(ctx, "_mktl");
1942
+ const markLog = v(ctx, "_mklg");
1943
+ const scanEndV = v(ctx, "_sne");
1944
+ stmts.push(
1945
+ `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
1946
+ `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
1947
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
1948
+ `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`
1949
+ );
1950
+ const r2 = emit(def.parsers[i], ctx, scanEndV);
1951
+ stmts.push(...r2.stmts);
1952
+ const endAfterV = v(ctx, "_sea");
1236
1953
  stmts.push(
1237
- `${ind(ctx)}const ${tmp} = ${trivFn}(input, ${curV}, _ctx)`,
1238
- `${ind(ctx)}if (${tmp}.ok) ${curV} = ${tmp}.span.end`
1954
+ `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
1955
+ `${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}; }`
1239
1956
  );
1957
+ valueVars.push(r2.valueVar);
1958
+ continue;
1959
+ } else {
1960
+ const trivFn = ensureTriviaFn(ctx);
1961
+ stmts.push(`${ind(ctx)}${curV} = ${trivFn}(input, ${curV}, _ctx)`);
1240
1962
  }
1241
1963
  }
1242
1964
  const r = emit(def.parsers[i], ctx, curV);
1243
1965
  stmts.push(...r.stmts, `${ind(ctx)}${curV} = ${r.endVar}`);
1244
1966
  valueVars.push(r.valueVar);
1245
1967
  }
1968
+ return { stmts, valueVar: valueVars[valueVars.length - 1] ?? "null", endVar: curV, valueVars };
1969
+ }
1970
+ function emitSeq(def, ctx, pos) {
1971
+ const { stmts, endVar, valueVars } = emitSeqValues(def, ctx, pos);
1246
1972
  const arrV = v(ctx, "_arr");
1247
1973
  stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
1248
- return { stmts, valueVar: arrV, endVar: curV };
1974
+ return { stmts, valueVar: arrV, endVar };
1249
1975
  }
1250
1976
  function emitChoice(def, ctx, pos) {
1251
1977
  const allExpected = JSON.stringify(
@@ -1296,29 +2022,39 @@ function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
1296
2022
  const matchV = v(ctx, "_gm");
1297
2023
  const wordV = v(ctx, "_gw");
1298
2024
  const endV = v(ctx, "_ge");
2025
+ const valV = v(ctx, "_gcv");
1299
2026
  const stmts = [
1300
2027
  `${ind(ctx)}${reVar}.lastIndex = ${pos}`,
1301
2028
  `${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
1302
2029
  `${ind(ctx)}if (${matchV} === null) ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`,
1303
2030
  `${ind(ctx)}const ${wordV} = ${matchV}[0]`,
1304
- `${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`
2031
+ `${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
2032
+ `${ind(ctx)}let ${valV}`
1305
2033
  ];
2034
+ let first = true;
1306
2035
  for (let i = 0; i < def.parsers.length; i++) {
1307
2036
  if (i === superIndex) continue;
1308
2037
  const p = def.parsers[i];
1309
2038
  const litVal = getCoreLiteralValue(p);
1310
2039
  if (litVal === null) continue;
1311
- stmts.push(`${ind(ctx)}if (${wordV} === ${JSON.stringify(litVal)}) {`);
2040
+ const kw = first ? "if" : "else if";
2041
+ first = false;
2042
+ stmts.push(`${ind(ctx)}${kw} (${wordV} === ${JSON.stringify(litVal)}) {`);
1312
2043
  ctx.indent++;
1313
2044
  const tR = emitTransformChain(p, JSON.stringify(litVal), endV, pos, ctx);
1314
- stmts.push(...tR.stmts);
1315
- stmts.push(`${ind(ctx)}return { ok: true, value: ${tR.valueVar}, span: { start: ${pos}, end: ${endV} } }`);
2045
+ stmts.push(...emitLeafCapture(ctx, JSON.stringify(litVal), pos, endV));
2046
+ stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}`);
1316
2047
  ctx.indent--;
1317
2048
  stmts.push(`${ind(ctx)}}`);
1318
2049
  }
1319
2050
  const rR = emitTransformChain(superParser, wordV, endV, pos, ctx);
1320
- stmts.push(...rR.stmts);
1321
- return { stmts, valueVar: rR.valueVar, endVar: endV };
2051
+ const regexKw = first ? "if" : "else";
2052
+ stmts.push(`${ind(ctx)}${regexKw} {`);
2053
+ ctx.indent++;
2054
+ stmts.push(...emitLeafCapture(ctx, wordV, pos, endV), ...rR.stmts, `${ind(ctx)}${valV} = ${rR.valueVar}`);
2055
+ ctx.indent--;
2056
+ stmts.push(`${ind(ctx)}}`);
2057
+ return { stmts, valueVar: valV, endVar: endV };
1322
2058
  }
1323
2059
  function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
1324
2060
  const valV = v(ctx, "_llv");
@@ -1332,10 +2068,15 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
1332
2068
  const litCond = emitLiteralCondition(litVal, pos);
1333
2069
  const kw = first ? "if" : "else if";
1334
2070
  first = false;
2071
+ const litEnd = `${pos} + ${litLen}`;
1335
2072
  stmts.push(`${ind(ctx)}${kw} (${litCond}) {`);
1336
2073
  ctx.indent++;
1337
- const tR = emitTransformChain(p, JSON.stringify(litVal), `${pos} + ${litLen}`, pos, ctx);
1338
- stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}; ${endV} = ${pos} + ${litLen}`);
2074
+ const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
2075
+ stmts.push(
2076
+ ...emitLeafCapture(ctx, JSON.stringify(litVal), pos, litEnd),
2077
+ ...tR.stmts,
2078
+ `${ind(ctx)}${valV} = ${tR.valueVar}; ${endV} = ${litEnd}`
2079
+ );
1339
2080
  ctx.indent--;
1340
2081
  stmts.push(`${ind(ctx)}}`);
1341
2082
  }
@@ -1343,67 +2084,66 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
1343
2084
  return { stmts, valueVar: valV, endVar: endV };
1344
2085
  }
1345
2086
  function emitFirstMatch(def, allExpected, ctx, pos) {
1346
- const resV = v(ctx, "_cr");
1347
- const stmts = [`${ind(ctx)}let ${resV}`];
2087
+ const resValV = v(ctx, "_crv");
2088
+ const resEndV = v(ctx, "_cre");
2089
+ const resOkV = v(ctx, "_crok");
2090
+ const ind0 = ind(ctx);
2091
+ const stmts = [
2092
+ `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`
2093
+ ];
1348
2094
  for (let i = 0; i < def.parsers.length; i++) {
1349
2095
  const p = def.parsers[i];
1350
2096
  const gate = def.gates[i];
1351
2097
  const autoNot = def.autoNot[i];
1352
- const savedIndent = ctx.indent;
1353
- const savedFailLabel = ctx.failLabel;
1354
- ctx.indent = 0;
1355
- ctx.failLabel = void 0;
1356
- const r = emit(p, ctx, pos);
1357
- ctx.indent = savedIndent;
1358
- ctx.failLabel = savedFailLabel;
1359
- const iife = asIIFE(r.stmts, r.valueVar, r.endVar, pos, ind(ctx));
1360
2098
  let gateCond = null;
1361
2099
  if (gate) {
1362
2100
  const gateIdx = ctx.mapFns.length;
1363
2101
  ctx.mapFns.push(gate);
1364
2102
  gateCond = `_mf[${gateIdx}](_ctx.state)`;
1365
2103
  }
1366
- const skipCond = gateCond ? `!${resV}?.ok && ${gateCond}` : `!${resV}?.ok`;
2104
+ const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
1367
2105
  const markLeaves = ctx.capturing ? v(ctx, "_cml") : null;
1368
2106
  const markRaw = ctx.capturing ? v(ctx, "_cmr") : null;
1369
- const rollback = markLeaves ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLeaves}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRaw}` : "";
1370
- const markStmts = markLeaves ? [
1371
- `${ind(ctx)} const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
1372
- `${ind(ctx)} const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`
1373
- ] : [];
1374
- const rollbackStmt = rollback ? `${ind(ctx)} if (!${resV}?.ok) { ${rollback} }` : "";
2107
+ const markTl = ctx.capturing ? v(ctx, "_cmtl") : null;
2108
+ const markLog = ctx.capturing ? v(ctx, "_cmlg") : null;
2109
+ 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}` : "";
2110
+ stmts.push(`${ind0}if (${skipCond}) {`);
2111
+ ctx.indent++;
2112
+ const ind1 = ind(ctx);
2113
+ if (markLeaves) {
2114
+ stmts.push(
2115
+ `${ind1}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
2116
+ `${ind1}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
2117
+ `${ind1}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
2118
+ `${ind1}const ${markLog} = _ctx._triviaLog?.length ?? 0`
2119
+ );
2120
+ }
2121
+ const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
2122
+ stmts.push(...armStmts);
2123
+ ctx.indent--;
1375
2124
  if (autoNot && autoNot.length > 0) {
1376
- const tmp = v(ctx, "_ct");
1377
2125
  const anCode = v(ctx, "_anc");
1378
2126
  const rejectCond = autoNot.map(
1379
- (check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${tmp}.span.end)`
2127
+ (check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
1380
2128
  ).join(" || ");
1381
- stmts.push(`${ind(ctx)}if (${skipCond}) {`);
1382
- stmts.push(...markStmts);
1383
2129
  stmts.push(
1384
- `${ind(ctx)} const ${tmp} = (() => { try { return ${iife} } catch {} })()`,
1385
- `${ind(ctx)} if (${tmp}?.ok) { const ${anCode} = ${tmp}.span.end < input.length ? input.charCodeAt(${tmp}.span.end) : -1; if (!(${rejectCond})) ${resV} = ${tmp} }`
2130
+ `${ind0} if (${okVar}) { const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1; if (!(${rejectCond})) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true } }`
1386
2131
  );
1387
- if (rollbackStmt) stmts.push(rollbackStmt);
1388
- stmts.push(`${ind(ctx)}}`);
2132
+ if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
1389
2133
  } else {
1390
- stmts.push(`${ind(ctx)}if (${skipCond}) {`);
1391
- stmts.push(...markStmts);
1392
- stmts.push(`${ind(ctx)} try { ${resV} = ${iife} } catch {}`);
1393
- if (rollbackStmt) stmts.push(rollbackStmt);
1394
- stmts.push(`${ind(ctx)}}`);
2134
+ stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
2135
+ if (rollback) stmts.push(`${ind0} else { ${rollback} }`);
1395
2136
  }
2137
+ stmts.push(`${ind0}}`);
1396
2138
  }
1397
- stmts.push(`${ind(ctx)}if (!${resV}?.ok) ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
1398
- return { stmts, valueVar: `${resV}.value`, endVar: `${resV}.span.end` };
2139
+ stmts.push(`${ind0}if (!${resOkV}) ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
2140
+ return { stmts, valueVar: resValV, endVar: resEndV };
1399
2141
  }
1400
2142
  function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
1401
- if (!ctx.capturing) {
1402
- if (strategy.tag === "greedyClassify")
1403
- return emitGreedyClassify(def, strategy.superIndex, allExpected, ctx, pos);
1404
- if (strategy.tag === "literalsLongestFirst")
1405
- return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
1406
- }
2143
+ if (strategy.tag === "greedyClassify")
2144
+ return emitGreedyClassify(def, strategy.superIndex, allExpected, ctx, pos);
2145
+ if (strategy.tag === "literalsLongestFirst")
2146
+ return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
1407
2147
  return emitFirstMatch(def, allExpected, ctx, pos);
1408
2148
  }
1409
2149
  function emitTransformChain(p, baseValue, endV, startPos, ctx) {
@@ -1456,27 +2196,28 @@ function emitMany(def, ctx, pos) {
1456
2196
  if (ctx.capturing) {
1457
2197
  const capFn = ensureTriviaCaptureFn(ctx);
1458
2198
  const markV = v(ctx, "_mk");
2199
+ const markTl = v(ctx, "_mktl");
2200
+ const markLog = v(ctx, "_mklg");
1459
2201
  const npV = v(ctx, "_np");
1460
2202
  stmts.push(
1461
2203
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
1462
- `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx)`
2204
+ `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
2205
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
2206
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`
1463
2207
  );
1464
2208
  itemPos = npV;
1465
- rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; `;
2209
+ rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; `;
1466
2210
  } else {
1467
2211
  const trivFn = ensureTriviaFn(ctx);
1468
2212
  const npV = v(ctx, "_np");
1469
- stmts.push(
1470
- `${ind(ctx)}let ${npV} = ${curV}`,
1471
- `${ind(ctx)}{ const _trv = ${trivFn}(input, ${npV}, _ctx); if (_trv.ok) ${npV} = _trv.span.end }`
1472
- );
2213
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`);
1473
2214
  itemPos = npV;
1474
2215
  }
1475
2216
  }
1476
2217
  const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
1477
2218
  stmts.push(
1478
2219
  ...iterStmts,
1479
- `${ind(ctx)}if (!${iterOk} || ${iterEnd} === ${curV}) { ${rollback}break }`,
2220
+ `${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
1480
2221
  `${ind(ctx)}${arrV}.push(${iterVal})`,
1481
2222
  `${ind(ctx)}${curV} = ${iterEnd}`
1482
2223
  );
@@ -1513,55 +2254,70 @@ function emitSepBy(_p, def, ctx, pos) {
1513
2254
  `${ind(ctx)}while (${curV} < input.length) {`
1514
2255
  );
1515
2256
  ctx.indent++;
1516
- let rollback = "";
1517
- if (ctx.capturing) {
1518
- const markV = v(ctx, "_mk");
1519
- stmts.push(`${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`);
1520
- rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; `;
1521
- }
1522
2257
  let sepAtPos = curV;
1523
2258
  if (ctx.activeTrivia) {
1524
2259
  if (ctx.capturing) {
1525
2260
  const capFn = ensureTriviaCaptureFn(ctx);
1526
- const spV = v(ctx, "_sp");
1527
- stmts.push(`${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx)`);
1528
- sepAtPos = spV;
1529
- } else {
1530
- const trivFn = ensureTriviaFn(ctx);
2261
+ const markV = v(ctx, "_mk");
2262
+ const markTl = v(ctx, "_mktl");
2263
+ const markLog = v(ctx, "_mklg");
1531
2264
  const spV = v(ctx, "_sp");
1532
2265
  stmts.push(
1533
- `${ind(ctx)}let ${spV} = ${curV}`,
1534
- `${ind(ctx)}{ const _trv = ${trivFn}(input, ${spV}, _ctx); if (_trv.ok) ${spV} = _trv.span.end }`
2266
+ `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
2267
+ `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
2268
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
2269
+ `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`
1535
2270
  );
1536
2271
  sepAtPos = spV;
1537
- }
1538
- }
1539
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
1540
- stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollback}break }`);
1541
- let nextAtPos = sepEnd;
1542
- if (ctx.activeTrivia) {
1543
- if (ctx.capturing) {
1544
- const capFn = ensureTriviaCaptureFn(ctx);
2272
+ const sepRollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; `;
2273
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
2274
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sepRollback}break }`);
2275
+ const mark2V = v(ctx, "_mk2");
2276
+ const mark2Tl = v(ctx, "_mktl2");
2277
+ const mark2Log = v(ctx, "_mklg2");
1545
2278
  const npV = v(ctx, "_np");
1546
- stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx)`);
1547
- nextAtPos = npV;
2279
+ stmts.push(
2280
+ `${ind(ctx)}const ${mark2V} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
2281
+ `${ind(ctx)}const ${mark2Tl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
2282
+ `${ind(ctx)}const ${mark2Log} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
2283
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`
2284
+ );
2285
+ const nextRollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mark2V}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mark2Tl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mark2Log}; `;
2286
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
2287
+ stmts.push(
2288
+ ...nextStmts,
2289
+ `${ind(ctx)}if (!${nextOk}) { ${nextRollback}break }`,
2290
+ `${ind(ctx)}${arrV}.push(${nextVal})`,
2291
+ `${ind(ctx)}${curV} = ${nextEnd}`
2292
+ );
1548
2293
  } else {
1549
2294
  const trivFn = ensureTriviaFn(ctx);
2295
+ const spV = v(ctx, "_sp");
2296
+ stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
2297
+ sepAtPos = spV;
2298
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
2299
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
1550
2300
  const npV = v(ctx, "_np");
2301
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
2302
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
1551
2303
  stmts.push(
1552
- `${ind(ctx)}let ${npV} = ${sepEnd}`,
1553
- `${ind(ctx)}{ const _trv = ${trivFn}(input, ${npV}, _ctx); if (_trv.ok) ${npV} = _trv.span.end }`
2304
+ ...nextStmts,
2305
+ `${ind(ctx)}if (!${nextOk}) break`,
2306
+ `${ind(ctx)}${arrV}.push(${nextVal})`,
2307
+ `${ind(ctx)}${curV} = ${nextEnd}`
1554
2308
  );
1555
- nextAtPos = npV;
1556
2309
  }
2310
+ } else {
2311
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
2312
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2313
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
2314
+ stmts.push(
2315
+ ...nextStmts,
2316
+ `${ind(ctx)}if (!${nextOk}) break`,
2317
+ `${ind(ctx)}${arrV}.push(${nextVal})`,
2318
+ `${ind(ctx)}${curV} = ${nextEnd}`
2319
+ );
1557
2320
  }
1558
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, nextAtPos);
1559
- stmts.push(
1560
- ...nextStmts,
1561
- `${ind(ctx)}if (!${nextOk}) { ${rollback}break }`,
1562
- `${ind(ctx)}${arrV}.push(${nextVal})`,
1563
- `${ind(ctx)}${curV} = ${nextEnd}`
1564
- );
1565
2321
  ctx.indent--;
1566
2322
  stmts.push(`${ind(ctx)}}`);
1567
2323
  ctx.indent--;
@@ -1623,27 +2379,33 @@ function emitNot(def, ctx, pos) {
1623
2379
  };
1624
2380
  }
1625
2381
  function emitNode(def, ctx, pos) {
1626
- const fnIdx = ctx.buildFns.length;
1627
- ctx.buildFns.push(def.build);
1628
- ctx.buildSrcs.push(def.buildSrc ?? null);
2382
+ const mkType = analyzeMkInlineBuild(def);
2383
+ let buildIdx = null;
2384
+ if (!mkType) {
2385
+ buildIdx = ctx.buildFns.length;
2386
+ ctx.buildFns.push(def.build);
2387
+ ctx.buildSrcs.push(def.buildSrc ?? null);
2388
+ }
1629
2389
  const i = ind(ctx);
1630
2390
  const chV = v(ctx, "_ch");
1631
2391
  const rawV = v(ctx, "_raw");
1632
- const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st");
2392
+ const tlV = v(ctx, "_tl");
2393
+ const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
1633
2394
  const stmts = [
1634
- `${i}const ${chV} = [], ${rawV} = []`,
1635
- `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia`,
1636
- `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true`
2395
+ `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []`,
2396
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
2397
+ `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${tlV}`
1637
2398
  ];
1638
2399
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
1639
2400
  stmts.push(...innerStmts);
1640
- stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}`);
2401
+ stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
1641
2402
  stmts.push(`${i}if (!${okVar}) ${failStmt({ ...ctx, indent: 0 }, '"node"', pos).trim()}`);
2403
+ const stV = v(ctx, "_nst");
2404
+ stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
1642
2405
  const ndV = v(ctx, "_nd");
2406
+ const ndExpr = mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `_build[${buildIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV})`;
1643
2407
  stmts.push(
1644
- `${i}const ${ndV} = _build[${fnIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} })`,
1645
- // Record into the caller's collectors (the node in children; the node, or a
1646
- // spanned leaf if build collapsed to a non-node value, in rawChildren).
2408
+ `${i}const ${ndV} = ${ndExpr}`,
1647
2409
  `${i}if (${sc}) ${sc}.push(${ndV})`,
1648
2410
  `${i}if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && ${ndV}._tag === 'node') ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : '', span: { start: ${pos}, end: ${endVar} } })`
1649
2411
  );
@@ -1677,31 +2439,14 @@ function emitLazy(p, def, ctx, pos) {
1677
2439
  const savedIndent = ctx.indent;
1678
2440
  const savedFailLabel = ctx.failLabel;
1679
2441
  ctx.indent = 1;
1680
- ctx.failLabel = void 0;
2442
+ ctx.failLabel = "_pfail";
1681
2443
  const r = emit(resolved, ctx, "_pos");
1682
2444
  ctx.indent = savedIndent;
1683
2445
  ctx.failLabel = savedFailLabel;
1684
- ctx.namedFnDecls.push([
1685
- `function ${fnName2}(input, _pos, _ctx) {`,
1686
- ...r.stmts,
1687
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
1688
- `}`
1689
- ].join("\n"));
2446
+ pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
1690
2447
  }
1691
2448
  const fnName = ctx.namedParsers.get(p);
1692
- const rv = v(ctx, "_pfr");
1693
- const vv = v(ctx, "_pfv");
1694
- const ev = v(ctx, "_pfe");
1695
- return {
1696
- stmts: [
1697
- `${ind(ctx)}const ${rv} = ${fnName}(input, ${pos}, _ctx)`,
1698
- `${ind(ctx)}if (!${rv}.ok) ${failStmt({ ...ctx, indent: 0 }, '"parser"', pos).trim()}`,
1699
- `${ind(ctx)}const ${vv} = ${rv}.value`,
1700
- `${ind(ctx)}const ${ev} = ${rv}.span.end`
1701
- ],
1702
- valueVar: vv,
1703
- endVar: ev
1704
- };
2449
+ return emitNamedFnCall(ctx, fnName, pos, '"parser"');
1705
2450
  }
1706
2451
  function emitRecover(def, ctx, pos) {
1707
2452
  const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
@@ -1737,6 +2482,8 @@ function emit(p, ctx, pos) {
1737
2482
  return emitLit(def, ctx, pos);
1738
2483
  case "regex":
1739
2484
  return emitRegex(def, ctx, pos);
2485
+ case "keywords":
2486
+ return emitKeywords(def, ctx, pos);
1740
2487
  case "sequence":
1741
2488
  return emitSeq(def, ctx, pos);
1742
2489
  case "choice":
@@ -1749,7 +2496,31 @@ function emit(p, ctx, pos) {
1749
2496
  case "sepBy":
1750
2497
  return emitSepBy(p, def, ctx, pos);
1751
2498
  case "transform": {
2499
+ const fnSrc = transformFnSource(def.fn, def.fnSrc);
2500
+ if (fnSrc && def.parser._def.tag === "sequence") {
2501
+ const seqR = emitSeqValues(def.parser._def, ctx, pos);
2502
+ const inlined = tryInlineDestructureTransform(fnSrc, seqR.valueVars);
2503
+ if (inlined) {
2504
+ const mv2 = v(ctx, "_mapped");
2505
+ return {
2506
+ stmts: [...seqR.stmts, `${ind(ctx)}const ${mv2} = ${inlined}`],
2507
+ valueVar: mv2,
2508
+ endVar: seqR.endVar
2509
+ };
2510
+ }
2511
+ }
1752
2512
  const inner = emit(def.parser, ctx, pos);
2513
+ if (fnSrc) {
2514
+ const unary = tryInlineUnaryTransform(fnSrc, inner.valueVar);
2515
+ if (unary) {
2516
+ const mv2 = v(ctx, "_mapped");
2517
+ return {
2518
+ stmts: [...inner.stmts, `${ind(ctx)}const ${mv2} = ${unary}`],
2519
+ valueVar: mv2,
2520
+ endVar: inner.endVar
2521
+ };
2522
+ }
2523
+ }
1753
2524
  const fnIdx = ctx.mapFns.length;
1754
2525
  ctx.mapFns.push(def.fn);
1755
2526
  ctx.mapFnSrcs.push(def.fnSrc ?? null);
@@ -1785,12 +2556,20 @@ function emit(p, ctx, pos) {
1785
2556
  return emitLazy(p, def, ctx, pos);
1786
2557
  case "trivia":
1787
2558
  return emit(def.parser, ctx, pos);
2559
+ case "label":
2560
+ return emit(def.parser, ctx, pos);
1788
2561
  case "grammar": {
1789
2562
  const savedTrivia = ctx.activeTrivia;
1790
- if (def.triviaParser) ctx.activeTrivia = def.triviaParser;
2563
+ const savedKindLabels = ctx.triviaKindLabels;
2564
+ if (def.triviaParser) {
2565
+ ctx.activeTrivia = def.triviaParser;
2566
+ if (def.triviaParser._meta.triviaKindLabels) {
2567
+ ctx.triviaKindLabels = def.triviaParser._meta.triviaKindLabels;
2568
+ }
2569
+ }
1791
2570
  const r = emit(def.parser, ctx, pos);
1792
- if (savedTrivia === void 0) delete ctx.activeTrivia;
1793
- else ctx.activeTrivia = savedTrivia;
2571
+ ctx.activeTrivia = savedTrivia;
2572
+ ctx.triviaKindLabels = savedKindLabels;
1794
2573
  return r;
1795
2574
  }
1796
2575
  case "not":
@@ -1825,27 +2604,22 @@ function emit(p, ctx, pos) {
1825
2604
  const savedIndent = ctx.indent;
1826
2605
  const savedFailLabel = ctx.failLabel;
1827
2606
  ctx.indent = 1;
1828
- ctx.failLabel = void 0;
2607
+ ctx.failLabel = "_pfail";
1829
2608
  const innerR = emit(innerParser, ctx, "_pos");
1830
2609
  ctx.indent = savedIndent;
1831
2610
  ctx.failLabel = savedFailLabel;
1832
- ctx.namedFnDecls.push([
1833
- `function ${fnName}(input, _pos, _ctx) {`,
1834
- ...innerR.stmts,
1835
- ` return { ok: true, value: ${innerR.valueVar}, span: { start: _pos, end: ${innerR.endVar} } }`,
1836
- `}`
1837
- ].join("\n"));
2611
+ pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
1838
2612
  }
1839
2613
  const fn = ctx.namedParsers.get(innerParser);
1840
2614
  const rv = v(ctx, "_wcr");
1841
- const vv = v(ctx);
2615
+ const vv = v(ctx, "_wcv");
1842
2616
  const ev = v(ctx, "_wce");
1843
2617
  return {
1844
2618
  stmts: [
1845
- `${ind(ctx)}const ${rv} = ${fn}(input, ${pos}, { ..._ctx, state: _mf[${evIdx}]() })`,
1846
- `${ind(ctx)}if (!${rv}.ok) ${failStmt({ ...ctx, indent: 0 }, '"withCtx"', pos).trim()}`,
1847
- `${ind(ctx)}const ${vv} = ${rv}.value`,
1848
- `${ind(ctx)}const ${ev} = ${rv}.span.end`
2619
+ `${ind(ctx)}const ${rv} = { ..._ctx, state: _mf[${evIdx}]() }`,
2620
+ `${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
2621
+ `${ind(ctx)}if (${vv} === ${NAMED_FN_FAIL}) ${failStmt({ ...ctx, indent: 0 }, '"withCtx"', pos).trim()}`,
2622
+ `${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
1849
2623
  ],
1850
2624
  valueVar: vv,
1851
2625
  endVar: ev
@@ -1871,6 +2645,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
1871
2645
  }
1872
2646
  case "grammar":
1873
2647
  case "trivia":
2648
+ case "label":
1874
2649
  case "optional":
1875
2650
  case "many":
1876
2651
  case "oneOrMore":
@@ -1894,7 +2669,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
1894
2669
  return false;
1895
2670
  }
1896
2671
  }
1897
- function compile(parser2, mapFnSources) {
2672
+ function compile(combinator, mapFnSources) {
1898
2673
  const ctx = {
1899
2674
  vars: 0,
1900
2675
  indent: 1,
@@ -1908,15 +2683,18 @@ function compile(parser2, mapFnSources) {
1908
2683
  needsCollator: false,
1909
2684
  namedParsers: /* @__PURE__ */ new Map(),
1910
2685
  triviaCaptureNames: /* @__PURE__ */ new Map(),
2686
+ triviaFnNames: /* @__PURE__ */ new Map(),
1911
2687
  namedFnDecls: [],
1912
- capturing: hasNodeDef(parser2)
2688
+ capturing: hasNodeDef(combinator)
1913
2689
  };
1914
- const r = emit(parser2, ctx, "_pos");
2690
+ const r = emit(combinator, ctx, "_pos");
1915
2691
  const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
1916
2692
  ` : "";
2693
+ const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
1917
2694
  const source = [
1918
2695
  ...ctx.regexDecls,
1919
2696
  "",
2697
+ ...namedPrelude,
1920
2698
  ...ctx.namedFnDecls,
1921
2699
  `${collatorDecl}function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
1922
2700
  ` let pos = _pos`,
@@ -1927,12 +2705,16 @@ function compile(parser2, mapFnSources) {
1927
2705
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
1928
2706
  ...ctx.regexDecls,
1929
2707
  collatorDecl,
2708
+ ...namedPrelude,
1930
2709
  ...ctx.namedFnDecls,
1931
2710
  `let pos = _pos`,
1932
2711
  ...r.stmts,
1933
2712
  `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
1934
2713
  ].join("\n"));
1935
- const defaultCtx = { trackLines: false };
2714
+ const defaultCtx = {
2715
+ trackLines: false,
2716
+ ...ctx.triviaKindLabels ? { triviaKindLabels: ctx.triviaKindLabels } : {}
2717
+ };
1936
2718
  const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
1937
2719
  const effectiveSources = mapFnSources ?? derivedSrcs;
1938
2720
  const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
@@ -1946,6 +2728,9 @@ function compile(parser2, mapFnSources) {
1946
2728
  parse(input, pos = 0) {
1947
2729
  return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
1948
2730
  },
2731
+ parseWithContext(input, parseCtx, pos = 0) {
2732
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
2733
+ },
1949
2734
  parseWithErrors(input, pos = 0) {
1950
2735
  const errors = [];
1951
2736
  const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
@@ -1968,12 +2753,14 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
1968
2753
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
1969
2754
  const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl;
1970
2755
  if (!needsWrapper) return innerFn;
2756
+ const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
1971
2757
  return [
1972
2758
  `/* @__PURE__ */ (() => {`,
1973
2759
  ...ctx.regexDecls.map((d) => ` ${d}`),
1974
2760
  collatorDecl ? ` ${collatorDecl.trim()}` : "",
1975
2761
  mfDecl,
1976
2762
  buildDecl,
2763
+ ...namedPrelude.map((l) => ` ${l}`),
1977
2764
  ...ctx.namedFnDecls.flatMap((f) => f.split("\n").map((l) => ` ${l}`)),
1978
2765
  ` return ${innerFn}`,
1979
2766
  `})()`
@@ -2000,24 +2787,24 @@ function guard(predicate) {
2000
2787
  }
2001
2788
 
2002
2789
  // src/combinators/withCtx.ts
2003
- function withCtx(extra, parser2) {
2790
+ function withCtx(extra, combinator) {
2004
2791
  const meta = {
2005
- firstSet: parser2._meta.firstSet,
2006
- canMatchNewline: parser2._meta.canMatchNewline,
2792
+ firstSet: combinator._meta.firstSet,
2793
+ canMatchNewline: combinator._meta.canMatchNewline,
2007
2794
  isTrivia: false
2008
2795
  };
2009
2796
  return {
2010
2797
  _tag: "withCtx",
2011
2798
  _meta: meta,
2012
- _def: { tag: "withCtx", extra, parser: parser2 },
2799
+ _def: { tag: "withCtx", extra, parser: combinator },
2013
2800
  parse(input, pos, ctx) {
2014
- return parser2.parse(input, pos, { ...ctx, state: extra });
2801
+ return combinator.parse(input, pos, { ...ctx, state: extra });
2015
2802
  }
2016
2803
  };
2017
2804
  }
2018
2805
 
2019
2806
  // src/combinators/recover.ts
2020
- function recover(parser2, sentinel) {
2807
+ function recover(combinator, sentinel) {
2021
2808
  const meta = {
2022
2809
  firstSet: { kind: "any" },
2023
2810
  canMatchNewline: true,
@@ -2026,9 +2813,9 @@ function recover(parser2, sentinel) {
2026
2813
  return {
2027
2814
  _tag: "recover",
2028
2815
  _meta: meta,
2029
- _def: { tag: "recover", parser: parser2, sentinel },
2816
+ _def: { tag: "recover", parser: combinator, sentinel },
2030
2817
  parse(input, pos, ctx) {
2031
- const result = parser2.parse(input, pos, ctx);
2818
+ const result = combinator.parse(input, pos, ctx);
2032
2819
  if (result.ok) return result;
2033
2820
  let scanPos = pos;
2034
2821
  while (scanPos < input.length) {
@@ -2050,10 +2837,10 @@ function isParseError(value) {
2050
2837
  }
2051
2838
 
2052
2839
  // src/combinators/completions.ts
2053
- function completionsAt(parser2, input, offset) {
2840
+ function completionsAt(combinator, input, offset) {
2054
2841
  const probe = { offset, best: null };
2055
2842
  const ctx = { trackLines: false, _probe: probe };
2056
- const result = parser2.parse(input.slice(0, offset), 0, ctx);
2843
+ const result = combinator.parse(input.slice(0, offset), 0, ctx);
2057
2844
  if (result.ok) return [];
2058
2845
  const best = deeperFail(probe.best, result);
2059
2846
  return best?.expected ?? [];
@@ -2079,10 +2866,9 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
2079
2866
  let cur = pos;
2080
2867
  const probeCtx = { trackLines: ctx.trackLines, state: ctx.state };
2081
2868
  const emit2 = (end) => {
2082
- if (end > pos && (ctx._cstLeaves || ctx._cstRawChildren)) {
2869
+ if (end > pos && cstCaptureActive(ctx)) {
2083
2870
  const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
2084
- if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
2085
- if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
2871
+ pushCstLeaf(ctx, leaf);
2086
2872
  }
2087
2873
  };
2088
2874
  while (cur < input.length) {
@@ -2124,7 +2910,7 @@ function balanced(open, close, options = {}) {
2124
2910
  return combi;
2125
2911
  }
2126
2912
 
2127
- // src/cst/incremental.ts
2913
+ // src/functional/doc.ts
2128
2914
  function isNode(x) {
2129
2915
  return typeof x === "object" && x !== null && x._tag === "node";
2130
2916
  }
@@ -2133,7 +2919,8 @@ function findContaining(node2, pos, path = []) {
2133
2919
  const child = node2.children[i];
2134
2920
  if (!isNode(child)) continue;
2135
2921
  if (child.span.start <= pos && pos < child.span.end) {
2136
- return findContaining(child, pos, [...path, i]) ?? { node: child, path: [...path, i] };
2922
+ const inner = findContaining(child, pos, [...path, i]);
2923
+ return inner ?? { node: child, path: [...path, i] };
2137
2924
  }
2138
2925
  }
2139
2926
  return null;
@@ -2149,249 +2936,11 @@ function ancestorsAt(root, path) {
2149
2936
  }
2150
2937
  return ancestors;
2151
2938
  }
2152
- function replaceAtPath(grammar, root, path, newNode) {
2153
- if (path.length === 0) return newNode;
2154
- const [idx, ...rest] = path;
2155
- const newChildren = [...root.children];
2156
- newChildren[idx] = rest.length === 0 ? newNode : replaceAtPath(grammar, root.children[idx], rest, newNode);
2157
- return grammar.rebuild(root, newChildren);
2158
- }
2159
- var ParseDocImpl = class _ParseDocImpl {
2160
- _parser;
2161
- _ruleName;
2162
- _trivia;
2163
- _captureTrivia;
2164
- tree;
2165
- errors;
2166
- input;
2167
- /** Offset reached by the top-level rule. < input.length means input was left unconsumed. */
2168
- consumedEnd;
2169
- constructor(parser2, ruleName, tree, errors, input, trivia2, captureTrivia, consumedEnd = 0) {
2170
- this._parser = parser2;
2171
- this._ruleName = ruleName;
2172
- this._trivia = trivia2;
2173
- this._captureTrivia = captureTrivia;
2174
- this.tree = tree;
2175
- this.errors = errors;
2176
- this.input = input;
2177
- this.consumedEnd = consumedEnd;
2178
- }
2179
- edit(from, to, replacement) {
2180
- const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
2181
- if (!this.tree) return makeParseDoc(this._parser, this._ruleName, newInput, this._trivia, this._captureTrivia);
2182
- const delta = replacement.length - (to - from);
2183
- const found = findContaining(this.tree, from);
2184
- if (!found) return makeParseDoc(this._parser, this._ruleName, newInput, this._trivia, this._captureTrivia);
2185
- const ancestors = ancestorsAt(this.tree, found.path);
2186
- const candidates = [found];
2187
- const pathCopy = [...found.path];
2188
- for (let i = ancestors.length - 2; i >= 0; i--) {
2189
- pathCopy.pop();
2190
- candidates.push({ node: ancestors[i + 1], path: [...pathCopy] });
2191
- }
2192
- for (const candidate of candidates) {
2193
- const { node: node2, path } = candidate;
2194
- const expectedEnd = node2.span.end + delta;
2195
- const parser2 = this._parser.rule(node2.type);
2196
- const ctx = { trackLines: false, state: node2.state, ...this._trivia !== void 0 ? { trivia: this._trivia } : {}, ...this._captureTrivia ? { captureTrivia: true } : {} };
2197
- const r = parser2.parse(newInput, node2.span.start, ctx);
2198
- if (!r.ok) continue;
2199
- if (r.span.end === expectedEnd) {
2200
- const newTree = replaceAtPath(this._parser, this.tree, path, r.value);
2201
- return new _ParseDocImpl(this._parser, this._ruleName, newTree, [], newInput, this._trivia, this._captureTrivia);
2202
- }
2203
- }
2204
- return makeParseDoc(this._parser, this._ruleName, newInput, this._trivia, this._captureTrivia);
2205
- }
2206
- };
2207
- function makeParseDoc(parser2, ruleName, input, trivia2, captureTrivia, triviaLog) {
2208
- const ctx = {
2209
- trackLines: false,
2210
- ...trivia2 !== void 0 ? { trivia: trivia2 } : {},
2211
- ...captureTrivia ? { captureTrivia: true } : {},
2212
- ...triviaLog !== void 0 ? { _triviaLog: triviaLog } : {}
2213
- };
2214
- const r = parser2.rule(ruleName).parse(input, 0, ctx);
2215
- if (r.ok) {
2216
- return new ParseDocImpl(parser2, ruleName, r.value, [], input, trivia2, captureTrivia, r.span.end);
2217
- }
2218
- return new ParseDocImpl(parser2, ruleName, null, [{ ok: false, expected: r.expected, span: r.span }], input, trivia2, captureTrivia, r.span.start);
2219
- }
2220
-
2221
- // src/cst/grammar.ts
2222
- var Parser = class {
2223
- _built = false;
2224
- /** Set in a subclass to enable automatic trivia skipping between sequence terms. */
2225
- _trivia;
2226
- /**
2227
- * Set in a subclass to make the rule name optional in `parse()`.
2228
- * When defined, `parse(input)` is equivalent to `parse(defaultRule, input)`.
2229
- */
2230
- _defaultRule;
2231
- /**
2232
- * Set `true` in a subclass to record trivia (whitespace/comments) consumed
2233
- * between terms into each node's rawChildren as separate CSTTrivia tokens.
2234
- * Requires `_trivia` to be set. Default: trivia is skipped without recording.
2235
- */
2236
- _captureTrivia;
2237
- /**
2238
- * When set, makeParseDoc injects this array as ctx._triviaLog so each
2239
- * scanTrivia.commit() pushes [runStart, runEnd] pairs into it.
2240
- * Reset to [] before each parse() call in any subclass that uses it.
2241
- */
2242
- _triviaLog;
2243
- /**
2244
- * Optional map of public entry-point aliases → actual rule names. Lets callers
2245
- * `parse('value', src)` when the grammar's rule is `ValueList`, removing
2246
- * per-adapter name-translation tables.
2247
- */
2248
- _aliases;
2249
- /** Resolve an alias to its target rule name (identity if not aliased). */
2250
- _resolveRule(name) {
2251
- return this._aliases?.[name] ?? name;
2252
- }
2253
- _build() {
2254
- if (this._built) return;
2255
- this._built = true;
2256
- const keys = Object.keys(this).filter((k) => !k.startsWith("_"));
2257
- const slots = /* @__PURE__ */ new Map();
2258
- const g = {};
2259
- for (const key of keys) {
2260
- const val = this[key];
2261
- if (isCombinator(val)) {
2262
- const isRule = /^[A-Z]/.test(key);
2263
- const final = isRule ? this._makeNodeParser(key, val) : val;
2264
- g[key] = final;
2265
- Object.defineProperty(this, key, { value: final, writable: false, configurable: false });
2266
- } else if (typeof val === "function") {
2267
- const slot = ref();
2268
- slots.set(key, slot);
2269
- g[key] = slot;
2270
- }
2271
- }
2272
- for (const [key, slot] of slots) {
2273
- const thunk = this[key];
2274
- const inner = thunk(g);
2275
- const isRule = /^[A-Z]/.test(key);
2276
- const final = isRule ? this._makeNodeParser(key, inner) : inner;
2277
- slot.define(final);
2278
- slot._meta.firstSet = final._meta.firstSet;
2279
- slot._meta.canMatchNewline = final._meta.canMatchNewline;
2280
- Object.defineProperty(this, key, { value: final, writable: false, configurable: false });
2281
- }
2282
- }
2283
- /**
2284
- * Override to produce a custom AST node instead of a plain CSTNode.
2285
- * The returned object must satisfy NodeLike for IncrementalParser to work.
2286
- *
2287
- * `rawChildren` is `children` plus any trivia tokens (whitespace/comments)
2288
- * consumed between terms, in parse order. Use it to inspect trivia when the
2289
- * grammar is whitespace-sensitive (e.g. CSS descendant vs adjacent combinators).
2290
- * The default implementation ignores `rawChildren`.
2291
- */
2292
- buildNode(type, span, children, state, _rawChildren) {
2293
- return { _tag: "node", type, span, children, state };
2294
- }
2295
- /** Wrap an inner combinator so it produces a CSTNode on each match. */
2296
- _makeNodeParser(type, inner) {
2297
- const self = this;
2298
- const meta = {
2299
- firstSet: inner._meta.firstSet,
2300
- canMatchNewline: inner._meta.canMatchNewline,
2301
- isTrivia: false
2302
- };
2303
- return {
2304
- _tag: "cstNode",
2305
- _meta: meta,
2306
- _def: { tag: "unknown" },
2307
- parse(input, pos, ctx) {
2308
- const state = ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2309
- const children = [];
2310
- const rawChildren = [];
2311
- const innerCtx = {
2312
- ...ctx,
2313
- _cstChildren: children,
2314
- _cstLeaves: children,
2315
- _cstRawChildren: rawChildren
2316
- };
2317
- const r = inner.parse(input, pos, innerCtx);
2318
- if (!r.ok) return r;
2319
- const node2 = self.buildNode(type, r.span, children, state, rawChildren);
2320
- const isNodeLike = typeof node2 === "object" && node2 !== null && node2._tag === "node";
2321
- if (ctx._cstChildren) ctx._cstChildren.push(node2);
2322
- if (ctx._cstRawChildren) ctx._cstRawChildren.push(
2323
- isNodeLike ? node2 : { _tag: "leaf", value: typeof node2 === "string" ? node2 : "", span: r.span }
2324
- );
2325
- return { ok: true, value: node2, span: r.span };
2326
- }
2327
- };
2328
- }
2329
- /** Reconstruct a node with a new children array (used by IncrementalParser). */
2330
- rebuild(node2, newChildren) {
2331
- return this.buildNode(node2.type, node2.span, newChildren, node2.state, []);
2332
- }
2333
- parse(ruleNameOrInput, input) {
2334
- let ruleName;
2335
- let src;
2336
- if (input === void 0) {
2337
- if (this._defaultRule === void 0) {
2338
- throw new Error("parse(input) requires a _defaultRule to be set on the grammar");
2339
- }
2340
- ruleName = this._defaultRule;
2341
- src = ruleNameOrInput;
2342
- } else {
2343
- ruleName = ruleNameOrInput;
2344
- src = input;
2345
- }
2346
- return makeParseDoc(this, this._resolveRule(ruleName), src, this._trivia, this._captureTrivia, this._triviaLog);
2347
- }
2348
- /**
2349
- * Get the compiled Combinator for a named rule.
2350
- * Triggers lazy initialization on first call.
2351
- */
2352
- rule(name) {
2353
- this._build();
2354
- const resolved = this._resolveRule(name);
2355
- const p = this[resolved];
2356
- if (!p) throw new Error(`No rule '${String(name)}' on this parser`);
2357
- return p;
2358
- }
2359
- };
2360
- function isCombinator(val) {
2361
- return val !== null && typeof val === "object" && "_tag" in val;
2362
- }
2363
-
2364
- // src/functional/doc.ts
2365
- function isNode2(x) {
2366
- return typeof x === "object" && x !== null && x._tag === "node";
2367
- }
2368
- function findContaining2(node2, pos, path = []) {
2369
- for (let i = 0; i < node2.children.length; i++) {
2370
- const child = node2.children[i];
2371
- if (!isNode2(child)) continue;
2372
- if (child.span.start <= pos && pos < child.span.end) {
2373
- const inner = findContaining2(child, pos, [...path, i]);
2374
- return inner ?? { node: child, path: [...path, i] };
2375
- }
2376
- }
2377
- return null;
2378
- }
2379
- function ancestorsAt2(root, path) {
2380
- const ancestors = [root];
2381
- let cur = root;
2382
- for (const idx of path.slice(0, -1)) {
2383
- const child = cur.children[idx];
2384
- if (!child || !isNode2(child)) break;
2385
- ancestors.push(child);
2386
- cur = child;
2387
- }
2388
- return ancestors;
2389
- }
2390
- function replaceAtPath2(rebuild, root, path, newNode) {
2939
+ function replaceAtPath(rebuild, root, path, newNode) {
2391
2940
  if (path.length === 0) return newNode;
2392
2941
  const [idx, ...rest] = path;
2393
2942
  const newChildren = [...root.children];
2394
- newChildren[idx] = rest.length === 0 ? newNode : replaceAtPath2(rebuild, root.children[idx], rest, newNode);
2943
+ newChildren[idx] = rest.length === 0 ? newNode : replaceAtPath(rebuild, root.children[idx], rest, newNode);
2395
2944
  return rebuild(root, newChildren);
2396
2945
  }
2397
2946
  function defaultRebuild(node2, children) {
@@ -2417,9 +2966,9 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
2417
2966
  const reparse = () => makeFunctionalDoc(this._registry, this._rootRule, newInput, this._opts);
2418
2967
  if (!this.tree) return reparse();
2419
2968
  const delta = replacement.length - (to - from);
2420
- const found = findContaining2(this.tree, from);
2969
+ const found = findContaining(this.tree, from);
2421
2970
  if (!found) return reparse();
2422
- const ancestors = ancestorsAt2(this.tree, found.path);
2971
+ const ancestors = ancestorsAt(this.tree, found.path);
2423
2972
  const candidates = [found];
2424
2973
  const pathCopy = [...found.path];
2425
2974
  for (let i = ancestors.length - 2; i >= 0; i--) {
@@ -2434,7 +2983,7 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
2434
2983
  const r = ruleFn(newInput, node2.span.start, ctx);
2435
2984
  if (!r.ok) continue;
2436
2985
  if (r.span.end === node2.span.end + delta) {
2437
- const newTree = replaceAtPath2(rebuild, this.tree, path, r.value);
2986
+ const newTree = replaceAtPath(rebuild, this.tree, path, r.value);
2438
2987
  return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
2439
2988
  }
2440
2989
  }
@@ -2453,59 +3002,52 @@ function makeFunctionalDoc(registry, rootRule, input, opts = {}) {
2453
3002
  }
2454
3003
 
2455
3004
  // src/cst/trivia-index.ts
2456
- function spanOf(c) {
2457
- const s = c.span;
2458
- return s && typeof s.start === "number" ? s : void 0;
2459
- }
2460
3005
  function merge(map, key, run) {
2461
3006
  if (run.length === 0) return;
2462
3007
  const existing = map.get(key);
2463
3008
  if (existing) existing.push(...run);
2464
3009
  else map.set(key, [...run]);
2465
3010
  }
2466
- function buildTriviaIndex(root, opts) {
3011
+ function buildTriviaIndex(root, input = "", opts) {
2467
3012
  const before = /* @__PURE__ */ new Map();
2468
3013
  const after = /* @__PURE__ */ new Map();
2469
3014
  const visit = (node2) => {
2470
- const raw = node2.children;
2471
- if (!raw) return;
2472
- let prevEnd;
2473
- let i = 0;
2474
- while (i < raw.length) {
2475
- const c = raw[i];
2476
- if (c._tag === "trivia") {
2477
- const run = [];
2478
- while (i < raw.length && raw[i]._tag === "trivia") {
2479
- const t = raw[i];
2480
- const sp2 = spanOf(t);
2481
- if (sp2 && sp2.end > sp2.start) run.push({ value: t.value ?? "", span: sp2 });
2482
- i++;
3015
+ const log = node2.triviaLog;
3016
+ const raw = node2.rawChildren;
3017
+ if (log && log.length && raw) {
3018
+ for (let i = 0; i < log.length; i += 3) {
3019
+ const tStart = log[i], tEnd = log[i + 1], insertIdx = log[i + 2];
3020
+ if (tEnd <= tStart) continue;
3021
+ const run = [{ value: input.slice(tStart, tEnd), span: { start: tStart, end: tEnd } }];
3022
+ const next = raw[insertIdx];
3023
+ if (next) merge(before, next.span.start, run);
3024
+ const prev = insertIdx > 0 ? raw[insertIdx - 1] : void 0;
3025
+ if (prev) merge(after, prev.span.end, run);
3026
+ }
3027
+ }
3028
+ const ch = node2.children;
3029
+ if (ch) {
3030
+ for (const c of ch) {
3031
+ if (typeof c === "object" && c !== null && c.children !== void 0) {
3032
+ visit(c);
2483
3033
  }
2484
- const next = i < raw.length ? spanOf(raw[i]) : void 0;
2485
- if (next) merge(before, next.start, run);
2486
- if (prevEnd !== void 0) merge(after, prevEnd, run);
2487
- continue;
2488
3034
  }
2489
- const sp = spanOf(c);
2490
- if (sp) prevEnd = sp.end;
2491
- if (c._tag === "node") visit(c);
2492
- i++;
2493
3035
  }
2494
3036
  };
2495
- if (root && typeof root === "object") visit(root);
2496
- if (opts) {
2497
- const rootSpan = spanOf(root);
3037
+ if (root) visit(root);
3038
+ if (opts && input) {
3039
+ const rootSpan = root?.span;
2498
3040
  const re = new RegExp(opts.trivia.source, opts.trivia.flags.replace(/[gy]/g, "") + "y");
2499
3041
  const scanFrom = (from) => {
2500
3042
  const run = [];
2501
3043
  let pos = from;
2502
3044
  re.lastIndex = pos;
2503
- let m = re.exec(opts.input);
3045
+ let m = re.exec(input);
2504
3046
  while (m && m.index === pos && m[0].length > 0) {
2505
3047
  run.push({ value: m[0], span: { start: pos, end: pos + m[0].length } });
2506
3048
  pos += m[0].length;
2507
3049
  re.lastIndex = pos;
2508
- m = re.exec(opts.input);
3050
+ m = re.exec(input);
2509
3051
  }
2510
3052
  return run;
2511
3053
  };
@@ -2514,7 +3056,7 @@ function buildTriviaIndex(root, opts) {
2514
3056
  if (lead.length) merge(before, rootSpan.start, lead);
2515
3057
  }
2516
3058
  const end = rootSpan ? rootSpan.end : 0;
2517
- if (end < opts.input.length) {
3059
+ if (end < input.length) {
2518
3060
  const trail = scanFrom(end);
2519
3061
  if (trail.length) {
2520
3062
  merge(after, end, trail);
@@ -2524,4 +3066,38 @@ function buildTriviaIndex(root, opts) {
2524
3066
  }
2525
3067
  return { before, after };
2526
3068
  }
3069
+
3070
+ // src/cst/trivia-entries.ts
3071
+ function entryOffset(i, stride) {
3072
+ return i * stride;
3073
+ }
3074
+ function triviaEntries(log, labels, opts) {
3075
+ const baseStride = opts?.nodeLog ? 3 : 2;
3076
+ const stride = labels ? baseStride + 1 : baseStride;
3077
+ const length = Math.floor(log.length / stride);
3078
+ return {
3079
+ length,
3080
+ labels,
3081
+ stride,
3082
+ start(i) {
3083
+ return log[entryOffset(i, stride)];
3084
+ },
3085
+ end(i) {
3086
+ return log[entryOffset(i, stride) + 1];
3087
+ },
3088
+ kindIndex(i) {
3089
+ if (!labels) return void 0;
3090
+ return log[entryOffset(i, stride) + baseStride];
3091
+ },
3092
+ kind(i) {
3093
+ const ki = labels ? log[entryOffset(i, stride) + baseStride] : void 0;
3094
+ return ki !== void 0 ? labels[ki] : void 0;
3095
+ },
3096
+ text(i, input) {
3097
+ const s = log[entryOffset(i, stride)];
3098
+ const e = log[entryOffset(i, stride) + 1];
3099
+ return input.slice(s, e);
3100
+ }
3101
+ };
3102
+ }
2527
3103
  //# sourceMappingURL=index.cjs.map