parseman 0.28.0 → 0.29.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
@@ -4140,6 +4140,61 @@ function tryInlineDestructureTransform(src, valueVars) {
4140
4140
  }
4141
4141
 
4142
4142
  // src/compiler/codegen.ts
4143
+ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
4144
+ if (seen.has(p)) return { concrete: empty(), refs: [] };
4145
+ seen.add(p);
4146
+ const d = p._def;
4147
+ const rec = (c) => leadingFirstSetRecipe(c, seen);
4148
+ const merge2 = (a, b) => ({ concrete: union(a.concrete, b.concrete), refs: [...a.refs, ...b.refs] });
4149
+ switch (d.tag) {
4150
+ case "lazy": {
4151
+ const name = p._ruleName;
4152
+ if (name !== void 0) return { concrete: empty(), refs: [name] };
4153
+ try {
4154
+ return rec(d.thunk());
4155
+ } catch {
4156
+ return { concrete: any(), refs: [] };
4157
+ }
4158
+ }
4159
+ case "literal":
4160
+ case "regex":
4161
+ case "keywords":
4162
+ return { concrete: p._meta.firstSet, refs: [] };
4163
+ case "choice": {
4164
+ let out = { concrete: empty(), refs: [] };
4165
+ for (const arm of d.parsers) out = merge2(out, rec(arm));
4166
+ return out;
4167
+ }
4168
+ case "sequence": {
4169
+ let out = { concrete: empty(), refs: [] };
4170
+ for (const term of d.parsers) {
4171
+ out = merge2(out, rec(term));
4172
+ if (!matchesEmpty(term)) return out;
4173
+ }
4174
+ return out;
4175
+ }
4176
+ case "oneOrMore":
4177
+ case "many":
4178
+ case "optional":
4179
+ case "transform":
4180
+ case "label":
4181
+ case "field":
4182
+ case "trivia":
4183
+ case "token":
4184
+ case "leaf":
4185
+ case "node":
4186
+ case "grammar":
4187
+ case "expect":
4188
+ return rec(d.parser);
4189
+ case "sepBy":
4190
+ return rec(d.parser);
4191
+ case "skip":
4192
+ return rec(d.main);
4193
+ // not / scanTo / guard / withCtx / recover / unknown → shallow set (safe).
4194
+ default:
4195
+ return { concrete: p._meta.firstSet, refs: [] };
4196
+ }
4197
+ }
4143
4198
  var HOST_READS_DECL = "const _hostReads = (b, n) => { if (b === undefined) return false; let s; try { s = Function.prototype.toString.call(b) } catch (e) { return true } if (/\\barguments\\b/.test(s)) return true; const m = /^[^(]*\\(([\\s\\S]*?)\\)/.exec(s); if (m && /\\.\\.\\.|=/.test(m[1])) return true; return b.length > n }";
4144
4199
  var _loweringSink = null;
4145
4200
  function v(ctx, prefix = "_v") {
@@ -5186,6 +5241,13 @@ function emitMany(def, ctx, pos) {
5186
5241
  itemPos = npV;
5187
5242
  }
5188
5243
  }
5244
+ if (!ctx.recovery && !ctx.coverage && !failsAtStart(def.parser) && needsFirstSetGuard(def.parser)) {
5245
+ const lcV = v(ctx, "_lc");
5246
+ stmts.push(
5247
+ `${ind(ctx)}const ${lcV} = ${itemPos} < input.length ? (input.codePointAt(${itemPos}) ?? -1) : -1`,
5248
+ `${ind(ctx)}if (!(${firstSetCond(lcV, def.parser._meta.firstSet)})) { ${rollback}break }`
5249
+ );
5250
+ }
5189
5251
  const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
5190
5252
  stmts.push(...iterStmts);
5191
5253
  if (ctx.recovery) {
@@ -5236,8 +5298,19 @@ function emitAttempt(p, def, ctx, pos) {
5236
5298
  const rollback = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${leaves}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${raw}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${trivia2}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${log}; if (_ctx._fields) _ctx._fields.length = ${fields}; if (_ctx._errors) _ctx._errors.length = ${errors}`;
5237
5299
  const traceId = ctx.coverage?.plan.attempts.get(p);
5238
5300
  const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
5301
+ const preGuard = [];
5302
+ if (!ctx.recovery && !ctx.coverage && needsFirstSetGuard(def.parser)) {
5303
+ const gcV = v(ctx, "_agc");
5304
+ const gExp = armStaticExpected(ctx, def.parser);
5305
+ const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(gExp, pos)} }`;
5306
+ preGuard.push(
5307
+ `${ind(ctx)}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
5308
+ `${ind(ctx)}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
5309
+ );
5310
+ }
5239
5311
  return {
5240
5312
  stmts: [
5313
+ ...preGuard,
5241
5314
  `${ind(ctx)}const ${leaves} = _ctx._cstLeaves?.length ?? 0, ${raw} = _ctx._cstRawChildren?.length ?? 0, ${trivia2} = _ctx._cstTriviaLog?.length ?? 0, ${log} = _ctx._triviaLog?.length ?? 0, ${fields} = _ctx._fields?.length ?? 0, ${errors} = _ctx._errors?.length ?? 0`,
5242
5315
  ...inner.stmts,
5243
5316
  ...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
@@ -5617,7 +5690,18 @@ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRec
5617
5690
  const saveTrivia = needsTriviaFrame ? `, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${smk ? `, ${smk} = _ctx._triviaCaptureMask` : ""}` : "";
5618
5691
  const installTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = ${capTLv ?? "false"}; _ctx._cstTriviaLog = ${innerTl}${smk ? `; _ctx._triviaCaptureMask = ${capTLv} && _ctx.build !== undefined && _ctx.build._parsemanTriviaKinds !== undefined ? _ctx.build._parsemanTriviaKinds(${JSON.stringify(def.type)}) : ${smk}` : ""}` : "";
5619
5692
  const restoreTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${smk ? `; _ctx._triviaCaptureMask = ${smk}` : ""}` : "";
5693
+ const preGuard = [];
5694
+ if (!ctx.recovery && !ctx.coverage && (capturesChildren || structural) && needsFirstSetGuard(def.parser)) {
5695
+ const gcV = v(ctx, "_ngc");
5696
+ const gExp = armStaticExpected(ctx, def.parser);
5697
+ const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(gExp, pos);
5698
+ preGuard.push(
5699
+ `${i}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
5700
+ `${i}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
5701
+ );
5702
+ }
5620
5703
  const stmts = [
5704
+ ...preGuard,
5621
5705
  profHoist,
5622
5706
  allocStmt,
5623
5707
  `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren${saveTrivia}${sf ? `, ${sf} = _ctx._fields` : ""}`,
@@ -6534,6 +6618,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
6534
6618
  ].join("\n")));
6535
6619
  }
6536
6620
  const firstSets = /* @__PURE__ */ new Map();
6621
+ const firstSetRecipes = /* @__PURE__ */ new Map();
6537
6622
  for (const [key, rule] of ruleMap) {
6538
6623
  let resolved = rule;
6539
6624
  const d = rule._def;
@@ -6545,6 +6630,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
6545
6630
  }
6546
6631
  }
6547
6632
  firstSets.set(key, canMatchEmptyAtStart(resolved) ? { kind: "any" } : resolved._meta.firstSet);
6633
+ firstSetRecipes.set(key, canMatchEmptyAtStart(resolved) ? { concrete: any(), refs: [] } : leadingFirstSetRecipe(resolved));
6548
6634
  }
6549
6635
  return {
6550
6636
  ns,
@@ -6553,6 +6639,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
6553
6639
  ruleFns,
6554
6640
  wrappers,
6555
6641
  firstSets,
6642
+ firstSetRecipes,
6556
6643
  deps: ruleDependencies(ruleMap),
6557
6644
  needsEmptyTl: !!ctx.needsEmptyTl,
6558
6645
  needsHostReads: !!ctx.needsHostReads,
@@ -7188,10 +7275,30 @@ function fusedBody(pieces) {
7188
7275
  ];
7189
7276
  const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
7190
7277
  const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
7191
- const finalFS = /* @__PURE__ */ new Map();
7278
+ const ANY2 = { kind: "any" };
7279
+ const recipes = /* @__PURE__ */ new Map();
7280
+ const shallow = /* @__PURE__ */ new Map();
7192
7281
  for (const [k, p] of winner) {
7282
+ const r = p.firstSetRecipes?.get(k);
7283
+ if (r) recipes.set(k, r);
7193
7284
  const fs = p.firstSets?.get(k);
7194
- if (fs) finalFS.set(k, fs);
7285
+ if (fs) shallow.set(k, fs);
7286
+ }
7287
+ const finalFS = /* @__PURE__ */ new Map();
7288
+ for (const [k] of winner) finalFS.set(k, recipes.get(k)?.concrete ?? shallow.get(k) ?? ANY2);
7289
+ for (let iter = 0; iter <= recipes.size; iter++) {
7290
+ let changed = false;
7291
+ for (const [k, r] of recipes) {
7292
+ let fs = r.concrete;
7293
+ for (const ref2 of r.refs) {
7294
+ fs = union(fs, recipes.has(ref2) || shallow.has(ref2) ? finalFS.get(ref2) ?? ANY2 : ANY2);
7295
+ }
7296
+ if (JSON.stringify(finalFS.get(k)) !== JSON.stringify(fs)) {
7297
+ finalFS.set(k, fs);
7298
+ changed = true;
7299
+ }
7300
+ }
7301
+ if (!changed) break;
7195
7302
  }
7196
7303
  const body = rawBody.replace(
7197
7304
  // Rule name + code-point var are both JS identifiers (rule names are validated