parseman 0.16.0 → 0.17.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.
package/dist/index.js CHANGED
@@ -84,6 +84,54 @@ function sequenceFirstSet(parsers) {
84
84
  }
85
85
  return fs;
86
86
  }
87
+ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
88
+ if (seen.has(p)) return any();
89
+ seen.add(p);
90
+ const fs = (c) => firstSetOf(c, seen);
91
+ const d = p._def;
92
+ switch (d.tag) {
93
+ case "literal":
94
+ case "regex":
95
+ case "keywords":
96
+ return p._meta.firstSet;
97
+ // terminals: no refs, cached set is exact
98
+ case "lazy":
99
+ try {
100
+ return fs(d.thunk());
101
+ } catch {
102
+ return any();
103
+ }
104
+ case "choice": {
105
+ let out = empty();
106
+ for (const arm of d.parsers) out = union(out, fs(arm));
107
+ return out;
108
+ }
109
+ case "sequence": {
110
+ let out = empty();
111
+ for (const term of d.parsers) {
112
+ out = union(out, fs(term));
113
+ if (!matchesEmpty(term)) return out;
114
+ }
115
+ return out;
116
+ }
117
+ case "oneOrMore":
118
+ case "many":
119
+ case "optional":
120
+ case "transform":
121
+ case "label":
122
+ case "trivia":
123
+ case "node":
124
+ case "grammar":
125
+ case "expect":
126
+ return fs(d.parser);
127
+ case "sepBy":
128
+ return fs(d.parser);
129
+ case "skip":
130
+ return fs(d.main);
131
+ default:
132
+ return p._meta.firstSet;
133
+ }
134
+ }
87
135
  function mergeRanges(ranges) {
88
136
  if (ranges.length === 0) return [];
89
137
  const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
@@ -3024,16 +3072,12 @@ function namedFnPrelude() {
3024
3072
  function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
3025
3073
  ctx.namedFnDecls.push([
3026
3074
  `function ${fnName}(input, _pos, _ctx) {`,
3027
- ` let _pfok = false, _pfv, _pfe = _pos`,
3028
3075
  ` _pfail: {`,
3029
3076
  ...reindentStmts(bodyStmts, 2),
3030
- ` _pfv = ${valueVar}`,
3031
- ` _pfe = ${endVar}`,
3032
- ` _pfok = true`,
3077
+ ` ${NAMED_FN_END} = ${endVar}`,
3078
+ ` return ${valueVar}`,
3033
3079
  ` }`,
3034
- ` if (!_pfok) return ${NAMED_FN_FAIL}`,
3035
- ` ${NAMED_FN_END} = _pfe`,
3036
- ` return _pfv`,
3080
+ ` return ${NAMED_FN_FAIL}`,
3037
3081
  `}`
3038
3082
  ].join("\n"));
3039
3083
  }
@@ -3734,11 +3778,16 @@ function emitFirstMatch(def, ctx, pos) {
3734
3778
  const atStart = failsAtStart(p);
3735
3779
  const staticFx = armStaticExpected(ctx, p);
3736
3780
  const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
3737
- const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
3781
+ const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
3782
+ if (p._meta.firstSet.kind !== "any" || matchesEmpty(p)) return null;
3783
+ const deep = firstSetOf(p);
3784
+ return deep.kind === "ranges" ? firstSetCond(codeV, deep) : null;
3785
+ })();
3738
3786
  let gateCond = null;
3739
3787
  if (gate) {
3740
3788
  const gateIdx = ctx.mapFns.length;
3741
3789
  ctx.mapFns.push(gate);
3790
+ ctx.mapFnSrcs.push(null);
3742
3791
  gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
3743
3792
  }
3744
3793
  const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
@@ -3797,13 +3846,21 @@ function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
3797
3846
  return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
3798
3847
  return emitFirstMatch(def, ctx, pos);
3799
3848
  }
3849
+ function pushMapFn(ctx, fn, src) {
3850
+ if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
3851
+ const hit = ctx.mapFnSrcs.indexOf(src);
3852
+ if (hit !== -1) return hit;
3853
+ }
3854
+ const idx = ctx.mapFns.length;
3855
+ ctx.mapFns.push(fn);
3856
+ ctx.mapFnSrcs.push(src);
3857
+ return idx;
3858
+ }
3800
3859
  function emitTransformChain(p, baseValue, endV, startPos, ctx) {
3801
3860
  const def = p._def;
3802
3861
  if (def.tag === "transform") {
3803
3862
  const innerR = emitTransformChain(def.parser, baseValue, endV, startPos, ctx);
3804
- const fnIdx = ctx.mapFns.length;
3805
- ctx.mapFns.push(def.fn);
3806
- ctx.mapFnSrcs.push(def.fnSrc ?? null);
3863
+ const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
3807
3864
  const vv = v(ctx);
3808
3865
  return {
3809
3866
  stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
@@ -3986,7 +4043,7 @@ function emitScanTo(def, ctx, pos) {
3986
4043
  `${ind(ctx)}while (${curV} < input.length) {`
3987
4044
  ];
3988
4045
  ctx.indent++;
3989
- const probeCtx = { ...ctx, capturing: false };
4046
+ const probeCtx = { ...ctx, capturing: false, noHoist: true };
3990
4047
  const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
3991
4048
  stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
3992
4049
  if (def.skip.length > 0) {
@@ -4177,6 +4234,32 @@ function emitExpect(def, ctx, pos) {
4177
4234
  return { stmts, valueVar: valVar, endVar };
4178
4235
  }
4179
4236
  function emit(p, ctx, pos) {
4237
+ const usage = ctx.lazyUsage;
4238
+ if (usage && !ctx.noHoist && !ctx.capAsTrivia && !ctx.ruleNames?.has(p) && isHoistableTag(p._def.tag) && (usage.counts.get(p) ?? 0) > 1 && // Only hoist subtrees big enough that de-duplicating them beats the call
4239
+ // overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
4240
+ // faster than it calls and barely moves the byte count; the size explosion
4241
+ // is all in LARGE choices/sequences referenced from many positions.
4242
+ (usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE) {
4243
+ const existing = ctx.namedParsers.get(p);
4244
+ if (existing !== void 0) return emitNamedFnCall(ctx, existing, pos);
4245
+ const fnName = `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
4246
+ ctx.namedParsers.set(p, fnName);
4247
+ const savedIndent = ctx.indent;
4248
+ const savedFailLabel = ctx.failLabel;
4249
+ const savedRecord = ctx.recordFail;
4250
+ ctx.indent = 1;
4251
+ ctx.failLabel = "_pfail";
4252
+ ctx.recordFail = true;
4253
+ const r = emitDispatch(p, ctx, "_pos");
4254
+ ctx.indent = savedIndent;
4255
+ ctx.failLabel = savedFailLabel;
4256
+ ctx.recordFail = savedRecord;
4257
+ pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
4258
+ return emitNamedFnCall(ctx, fnName, pos);
4259
+ }
4260
+ return emitDispatch(p, ctx, pos);
4261
+ }
4262
+ function emitDispatch(p, ctx, pos) {
4180
4263
  const def = p._def;
4181
4264
  switch (def.tag) {
4182
4265
  case "literal":
@@ -4222,9 +4305,7 @@ function emit(p, ctx, pos) {
4222
4305
  };
4223
4306
  }
4224
4307
  }
4225
- const fnIdx = ctx.mapFns.length;
4226
- ctx.mapFns.push(def.fn);
4227
- ctx.mapFnSrcs.push(def.fnSrc ?? null);
4308
+ const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
4228
4309
  const mv = v(ctx, "_mapped");
4229
4310
  return {
4230
4311
  stmts: [
@@ -4289,6 +4370,7 @@ function emit(p, ctx, pos) {
4289
4370
  case "guard": {
4290
4371
  const fnIdx = ctx.mapFns.length;
4291
4372
  ctx.mapFns.push(def.predicate);
4373
+ ctx.mapFnSrcs.push(null);
4292
4374
  const vv = v(ctx);
4293
4375
  return {
4294
4376
  stmts: [
@@ -4303,6 +4385,7 @@ function emit(p, ctx, pos) {
4303
4385
  const evIdx = ctx.mapFns.length;
4304
4386
  const extra = def.extra;
4305
4387
  ctx.mapFns.push((() => extra));
4388
+ ctx.mapFnSrcs.push(null);
4306
4389
  const innerParser = def.parser;
4307
4390
  if (!ctx.namedParsers.has(innerParser)) {
4308
4391
  const fnName = `_wcf${ctx.namedParsers.size}`;
@@ -4422,9 +4505,58 @@ function childrenOf(def) {
4422
4505
  return [];
4423
4506
  }
4424
4507
  }
4508
+ var HOISTABLE_TAGS = /* @__PURE__ */ new Set([
4509
+ "sequence",
4510
+ "choice",
4511
+ "many",
4512
+ "oneOrMore",
4513
+ "optional",
4514
+ "sepBy",
4515
+ "transform",
4516
+ "node",
4517
+ "not"
4518
+ ]);
4519
+ function isHoistableTag(tag) {
4520
+ return HOISTABLE_TAGS.has(tag);
4521
+ }
4522
+ var HOIST_MIN_SUBTREE = 3;
4425
4523
  function analyzeLazyUsage(root) {
4426
4524
  return analyzeLazyUsageMulti([root]);
4427
4525
  }
4526
+ function subtreeSizes(roots) {
4527
+ const sizes = /* @__PURE__ */ new Map();
4528
+ const sz = (p) => {
4529
+ const cached = sizes.get(p);
4530
+ if (cached !== void 0) return cached;
4531
+ const def = p._def;
4532
+ let s = 1;
4533
+ if (def.tag !== "lazy") {
4534
+ for (const child of childrenOf(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
4535
+ }
4536
+ sizes.set(p, s);
4537
+ return s;
4538
+ };
4539
+ const visitedLazy = /* @__PURE__ */ new Set();
4540
+ const visit = (p) => {
4541
+ const def = p._def;
4542
+ if (def.tag === "lazy") {
4543
+ if (visitedLazy.has(p)) return;
4544
+ visitedLazy.add(p);
4545
+ let resolved;
4546
+ try {
4547
+ resolved = def.thunk();
4548
+ } catch {
4549
+ return;
4550
+ }
4551
+ visit(resolved);
4552
+ return;
4553
+ }
4554
+ sz(p);
4555
+ for (const child of childrenOf(def)) visit(child);
4556
+ };
4557
+ for (const root of roots) visit(root);
4558
+ return sizes;
4559
+ }
4428
4560
  function analyzeLazyUsageMulti(roots) {
4429
4561
  const counts = /* @__PURE__ */ new Map();
4430
4562
  const recursive = /* @__PURE__ */ new Set();
@@ -4451,10 +4583,14 @@ function analyzeLazyUsageMulti(roots) {
4451
4583
  active.delete(p);
4452
4584
  return;
4453
4585
  }
4586
+ if (isHoistableTag(def.tag)) {
4587
+ counts.set(p, (counts.get(p) ?? 0) + 1);
4588
+ }
4454
4589
  for (const child of childrenOf(def)) walk2(child);
4455
4590
  }
4456
- for (const root of roots) walk2(root);
4457
- return { counts, recursive };
4591
+ const rootsArr = [...roots];
4592
+ for (const root of rootsArr) walk2(root);
4593
+ return { counts, recursive, sizes: subtreeSizes(rootsArr) };
4458
4594
  }
4459
4595
  function ruleDependencies(ruleMap) {
4460
4596
  const nameOf = /* @__PURE__ */ new Map();
@@ -4780,6 +4916,172 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
4780
4916
  ].join("\n");
4781
4917
  }
4782
4918
 
4919
+ // src/combinators/scanTo.ts
4920
+ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
4921
+ const meta = {
4922
+ firstSet: any(),
4923
+ canMatchNewline: true,
4924
+ isTrivia: false
4925
+ };
4926
+ return {
4927
+ _tag: "scanTo",
4928
+ _meta: meta,
4929
+ _def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
4930
+ parse(input, pos, ctx) {
4931
+ let cur = pos;
4932
+ const probeCtx = {
4933
+ trackLines: ctx.trackLines,
4934
+ state: ctx.state,
4935
+ ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
4936
+ };
4937
+ const emit2 = (end) => {
4938
+ if (end > pos && cstCaptureActive(ctx)) {
4939
+ const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
4940
+ pushCstLeaf(ctx, leaf);
4941
+ }
4942
+ };
4943
+ while (cur < input.length) {
4944
+ const s = sentinel.parse(input, cur, probeCtx);
4945
+ if (s.ok) {
4946
+ emit2(cur);
4947
+ return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
4948
+ }
4949
+ let advanced = false;
4950
+ for (const skipper of skip2) {
4951
+ const r = skipper.parse(input, cur, probeCtx);
4952
+ if (r.ok && r.span.end > cur) {
4953
+ cur = r.span.end;
4954
+ advanced = true;
4955
+ break;
4956
+ }
4957
+ }
4958
+ if (!advanced) cur++;
4959
+ }
4960
+ if (orEOF) {
4961
+ emit2(cur);
4962
+ return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
4963
+ }
4964
+ const sentDef = sentinel._def;
4965
+ const expected = sentDef.tag === "literal" ? [JSON.stringify(sentDef.value)] : ["sentinel"];
4966
+ return { ok: false, expected, span: { start: pos, end: cur } };
4967
+ }
4968
+ };
4969
+ }
4970
+ function balanced(open, close, options = {}) {
4971
+ const self = ref();
4972
+ const skips = options.skip ?? [];
4973
+ const stop = /* @__PURE__ */ new Set([open, close]);
4974
+ let bounded = true;
4975
+ for (const sk of skips) {
4976
+ const cs = firstSetClassChars(sk);
4977
+ if (cs === null) {
4978
+ bounded = false;
4979
+ break;
4980
+ }
4981
+ for (const ch of cs) stop.add(ch);
4982
+ }
4983
+ const cls = [...stop].map(escapeClassChar).join("");
4984
+ const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
4985
+ const inner = many(choice(self, ...skips, content));
4986
+ const combi = transform(
4987
+ sequence(literal(open), inner, expect(literal(close), close)),
4988
+ // parts: strings (content/self) or arrays (a sequence-shaped skip) or a
4989
+ // ParseError (recovered close). `c` is the close string or a ParseError.
4990
+ ([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
4991
+ );
4992
+ if (combi._def.tag === "transform") {
4993
+ combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
4994
+ }
4995
+ self.define(combi);
4996
+ return combi;
4997
+ }
4998
+ function firstSetClassChars(c) {
4999
+ const fs = c._meta.firstSet;
5000
+ if (fs.kind === "empty") return [];
5001
+ if (fs.kind !== "ranges") return null;
5002
+ const out = [];
5003
+ for (const { lo, hi } of fs.ranges) {
5004
+ if (hi - lo > 8) return null;
5005
+ for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
5006
+ }
5007
+ return out;
5008
+ }
5009
+ function escapeClassChar(ch) {
5010
+ return ch.replace(/[\\\]^-]/g, "\\$&");
5011
+ }
5012
+
5013
+ // src/compiler/ir-serialize.ts
5014
+ function evalRuleMapIR(ir) {
5015
+ const _tf = (child, src) => {
5016
+ let fn2;
5017
+ try {
5018
+ fn2 = (0, eval)(`(${src})`);
5019
+ } catch {
5020
+ fn2 = () => {
5021
+ throw new Error("IR transform fn not materialized");
5022
+ };
5023
+ }
5024
+ const t = transform(child, fn2);
5025
+ t._def.fnSrc = src;
5026
+ return t;
5027
+ };
5028
+ const _nd = (type, child, src, opts) => {
5029
+ const n = node(type, child, void 0, opts);
5030
+ n._def.buildSrc = src;
5031
+ return n;
5032
+ };
5033
+ const fn = new Function(
5034
+ "rules",
5035
+ "ref",
5036
+ "regex",
5037
+ "literal",
5038
+ "keywords",
5039
+ "sequence",
5040
+ "choice",
5041
+ "many",
5042
+ "oneOrMore",
5043
+ "optional",
5044
+ "sepBy",
5045
+ "not",
5046
+ "node",
5047
+ "parser",
5048
+ "scanTo",
5049
+ "transform",
5050
+ "skip",
5051
+ "trivia",
5052
+ "label",
5053
+ "expect",
5054
+ "_tf",
5055
+ "_nd",
5056
+ `return (${ir})`
5057
+ );
5058
+ const map = fn(
5059
+ rules,
5060
+ ref,
5061
+ regex,
5062
+ literal,
5063
+ keywords,
5064
+ sequence,
5065
+ choice,
5066
+ many,
5067
+ oneOrMore,
5068
+ optional,
5069
+ sepBy,
5070
+ not,
5071
+ node,
5072
+ parser,
5073
+ scanTo,
5074
+ transform,
5075
+ skip,
5076
+ trivia,
5077
+ label,
5078
+ expect,
5079
+ _tf,
5080
+ _nd
5081
+ );
5082
+ return Object.entries(map);
5083
+ }
5084
+
4783
5085
  // src/compiler/linker.ts
4784
5086
  var _nsCounter = 0;
4785
5087
  function linkable(rulesMap, ns) {
@@ -4864,9 +5166,18 @@ function fuseRules(pieces) {
4864
5166
  return new Function("_env", body)(env);
4865
5167
  }
4866
5168
  var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
5169
+ function isIRPiece(p) {
5170
+ return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
5171
+ }
5172
+ function materializePiece(p) {
5173
+ if (!isIRPiece(p)) return p;
5174
+ const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns);
5175
+ if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
5176
+ return pieces;
5177
+ }
4867
5178
  function itemPieces(item) {
4868
5179
  const carried = item[COMPOSED_PIECES];
4869
- if (Array.isArray(carried)) return carried;
5180
+ if (Array.isArray(carried)) return carried.map(materializePiece);
4870
5181
  if (item.ruleFns instanceof Map) return [item];
4871
5182
  return [linkable(item)];
4872
5183
  }
@@ -4962,100 +5273,6 @@ function deeperFail(a, b) {
4962
5273
  return a.span.start >= b.span.start ? a : b;
4963
5274
  }
4964
5275
 
4965
- // src/combinators/scanTo.ts
4966
- function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
4967
- const meta = {
4968
- firstSet: any(),
4969
- canMatchNewline: true,
4970
- isTrivia: false
4971
- };
4972
- return {
4973
- _tag: "scanTo",
4974
- _meta: meta,
4975
- _def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
4976
- parse(input, pos, ctx) {
4977
- let cur = pos;
4978
- const probeCtx = {
4979
- trackLines: ctx.trackLines,
4980
- state: ctx.state,
4981
- ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
4982
- };
4983
- const emit2 = (end) => {
4984
- if (end > pos && cstCaptureActive(ctx)) {
4985
- const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
4986
- pushCstLeaf(ctx, leaf);
4987
- }
4988
- };
4989
- while (cur < input.length) {
4990
- const s = sentinel.parse(input, cur, probeCtx);
4991
- if (s.ok) {
4992
- emit2(cur);
4993
- return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
4994
- }
4995
- let advanced = false;
4996
- for (const skipper of skip2) {
4997
- const r = skipper.parse(input, cur, probeCtx);
4998
- if (r.ok && r.span.end > cur) {
4999
- cur = r.span.end;
5000
- advanced = true;
5001
- break;
5002
- }
5003
- }
5004
- if (!advanced) cur++;
5005
- }
5006
- if (orEOF) {
5007
- emit2(cur);
5008
- return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5009
- }
5010
- const sentDef = sentinel._def;
5011
- const expected = sentDef.tag === "literal" ? [JSON.stringify(sentDef.value)] : ["sentinel"];
5012
- return { ok: false, expected, span: { start: pos, end: cur } };
5013
- }
5014
- };
5015
- }
5016
- function balanced(open, close, options = {}) {
5017
- const self = ref();
5018
- const skips = options.skip ?? [];
5019
- const stop = /* @__PURE__ */ new Set([open, close]);
5020
- let bounded = true;
5021
- for (const sk of skips) {
5022
- const cs = firstSetClassChars(sk);
5023
- if (cs === null) {
5024
- bounded = false;
5025
- break;
5026
- }
5027
- for (const ch of cs) stop.add(ch);
5028
- }
5029
- const cls = [...stop].map(escapeClassChar).join("");
5030
- const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
5031
- const inner = many(choice(self, ...skips, content));
5032
- const combi = transform(
5033
- sequence(literal(open), inner, expect(literal(close), close)),
5034
- // parts: strings (content/self) or arrays (a sequence-shaped skip) or a
5035
- // ParseError (recovered close). `c` is the close string or a ParseError.
5036
- ([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
5037
- );
5038
- if (combi._def.tag === "transform") {
5039
- combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
5040
- }
5041
- self.define(combi);
5042
- return combi;
5043
- }
5044
- function firstSetClassChars(c) {
5045
- const fs = c._meta.firstSet;
5046
- if (fs.kind === "empty") return [];
5047
- if (fs.kind !== "ranges") return null;
5048
- const out = [];
5049
- for (const { lo, hi } of fs.ranges) {
5050
- if (hi - lo > 8) return null;
5051
- for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
5052
- }
5053
- return out;
5054
- }
5055
- function escapeClassChar(ch) {
5056
- return ch.replace(/[\\\]^-]/g, "\\$&");
5057
- }
5058
-
5059
5276
  // src/combinators/recover-list.ts
5060
5277
  function tolerantElement(item, sentinel, until) {
5061
5278
  const el = transform(