parseman 0.61.5 → 0.61.6

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.
@@ -36,6 +36,21 @@ export type EntrySupercompileResult = EmitResult & {
36
36
  * externally-addressable scan roots passed to the canonical emitter.
37
37
  */
38
38
  export declare function supercompileEntryAssembly(prog: TableProgram, cfg: EntrySupercompileCfg, extraIps?: readonly number[]): EntrySupercompileResult;
39
+ /**
40
+ * Functions that read the same are one function: `optional(x)` written in 25 places
41
+ * is 25 rows and one body, and a trivia skip can be pooled under two labels that
42
+ * lower alike. This runs after the join, so a site with one caller has
43
+ * already gone into it, the selection is untouched and every call a parse makes is
44
+ * still made. Each pass can make the callers of a merged function read alike, so it
45
+ * repeats until nothing merges.
46
+ *
47
+ * Only identifiers are compared or renamed: a grammar's own text reaches this source
48
+ * as strings and regexes, and they count as written. A temporary is local by the
49
+ * emitter's contract and is numbered per assembly, so one this function binds is
50
+ * compared in order of use. A site name that's also bound somewhere, or abbreviated
51
+ * in `{_pf1}`, can't be renamed by name, so that source stays as it is.
52
+ */
53
+ export declare function mergeSameBodies(source: string): string;
39
54
  /**
40
55
  * Plugin-only final splice for an ordinary macro replacement. The table
41
56
  * compiler emits its canonical callback-free public artifact first; this pass
@@ -1 +1 @@
1
- {"version":3,"file":"entry-supercompiler.d.ts","sourceRoot":"","sources":["../../src/plugin/entry-supercompiler.ts"],"names":[],"mappings":"AAYA,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAE/E,OAAO,EAAkC,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAKvF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IACpC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,SAAS,CAAA;IACpE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;IACpC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,eAAe,EAAE,QAAQ,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACvF,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACpD,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG;IAAE,QAAQ,CAAC,YAAY,EAAE,sBAAsB,CAAA;CAAE,CAAA;AAyKpG;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,YAAY,EAClB,GAAG,EAAE,oBAAoB,EACzB,QAAQ,GAAE,SAAS,MAAM,EAAO,GAC/B,uBAAuB,CAGzB;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,YAAY,EAClB,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,MAAM,CA0CR;AAED,8EAA8E;AAC9E,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,oBAAoB,EAC1B,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,OAAO,EAAE,UAAU,GAClB,uBAAuB,CAwRzB"}
1
+ {"version":3,"file":"entry-supercompiler.d.ts","sourceRoot":"","sources":["../../src/plugin/entry-supercompiler.ts"],"names":[],"mappings":"AAYA,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAE/E,OAAO,EAAkC,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAKvF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IACpC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,SAAS,CAAA;IACpE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;IACpC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,eAAe,EAAE,QAAQ,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACvF,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACpD,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG;IAAE,QAAQ,CAAC,YAAY,EAAE,sBAAsB,CAAA;CAAE,CAAA;AA2KpG;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,YAAY,EAClB,GAAG,EAAE,oBAAoB,EACzB,QAAQ,GAAE,SAAS,MAAM,EAAO,GAC/B,uBAAuB,CAGzB;AAgCD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA6DtD;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,YAAY,EAClB,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,MAAM,CA0CR;AAED,8EAA8E;AAC9E,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,oBAAoB,EAC1B,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,OAAO,EAAE,UAAU,GAClB,uBAAuB,CAwRzB"}
@@ -5630,7 +5630,7 @@ function sourceLeaf(root, fn) {
5630
5630
  }
5631
5631
 
5632
5632
  // src/version.ts
5633
- var PARSEMAN_VERSION = "0.61.5";
5633
+ var PARSEMAN_VERSION = "0.61.6";
5634
5634
 
5635
5635
  // src/table/lex-body.ts
5636
5636
  function buildLexBodyRecognizer(spec, constants) {
@@ -19464,21 +19464,70 @@ function runDuplicationDiagnosticRules(ruleMap, opt) {
19464
19464
  var LOOP = 8;
19465
19465
  var ROUNDS = 60;
19466
19466
  var CAP = 1e12;
19467
+ var CROSSING_WEIGHT = 4;
19468
+ var TAKEN = -1;
19467
19469
  function selectHotSites(code, entries, siteBytes, budget, profile) {
19468
19470
  const selected = /* @__PURE__ */ new Set();
19469
19471
  if (budget <= 0) return selected;
19470
- const weight = profile === void 0 ? staticWeights(code, entries) : void 0;
19471
- const w = (ip) => (weight !== void 0 ? weight.get(ip) : profile[ip]) ?? 0;
19472
- const ranked = [...siteBytes.keys()].sort((a, b) => w(b) / siteBytes.get(b) - w(a) / siteBytes.get(a) || a - b);
19473
19472
  let spent = 0;
19474
- for (const ip of ranked) {
19475
- if (w(ip) === 0) break;
19473
+ for (const ip of profile === void 0 ? staticRanking(code, entries, siteBytes) : connectedRanking(code, siteBytes, profile)) {
19476
19474
  spent += siteBytes.get(ip);
19477
19475
  if (spent > budget) break;
19478
19476
  selected.add(ip);
19479
19477
  }
19480
19478
  return selected;
19481
19479
  }
19480
+ function edgesAsLowered(code, siteBytes, ip, out) {
19481
+ out.length = 0;
19482
+ if (code[ip] !== OP_CONT) childSlots(code, ip, out);
19483
+ else if (siteBytes.has(ip)) out.push(code[ip + 1], code[ip + 3]);
19484
+ else out.push(code[ip + 2]);
19485
+ for (let i = 0; i < out.length; i++) {
19486
+ let row = out[i];
19487
+ for (; ; ) {
19488
+ const op = code[row];
19489
+ if (op === OP_GAPGATE || op === OP_SEED) row = code[row + 1];
19490
+ else if (op === OP_CONT && !siteBytes.has(row)) row = code[row + 2];
19491
+ else break;
19492
+ }
19493
+ out[i] = row;
19494
+ }
19495
+ }
19496
+ function staticRanking(code, entries, siteBytes) {
19497
+ const weight = staticWeights(code, entries, siteBytes);
19498
+ const w = (ip) => weight.get(ip) ?? 0;
19499
+ return [...siteBytes.keys()].filter((ip) => w(ip) > 0).sort((a, b) => w(b) / siteBytes.get(b) - w(a) / siteBytes.get(a) || a - b);
19500
+ }
19501
+ function* connectedRanking(code, siteBytes, calls) {
19502
+ const ran = [...siteBytes.keys()].filter((ip) => calls[ip] > 0).sort((a, b) => a - b);
19503
+ const slot = new Map(ran.map((ip, i) => [ip, i]));
19504
+ const bytes = ran.map((ip) => siteBytes.get(ip));
19505
+ const worth = ran.map((ip) => calls[ip]);
19506
+ const score = worth.map((value, i) => value / bytes[i]);
19507
+ const crossings = ran.map(() => []);
19508
+ const kids = [];
19509
+ ran.forEach((ip, from) => {
19510
+ edgesAsLowered(code, siteBytes, ip, kids);
19511
+ for (const callee of kids) {
19512
+ const to = slot.get(callee);
19513
+ if (to === void 0) continue;
19514
+ const gain = CROSSING_WEIGHT * calls[callee];
19515
+ crossings[from].push([to, gain]);
19516
+ crossings[to].push([from, gain]);
19517
+ }
19518
+ });
19519
+ for (let left = ran.length; left > 0; left--) {
19520
+ let best = 0;
19521
+ for (let i = 1; i < score.length; i++) if (score[i] > score[best]) best = i;
19522
+ score[best] = TAKEN;
19523
+ yield ran[best];
19524
+ for (const [neighbour, gain] of crossings[best]) {
19525
+ if (score[neighbour] === TAKEN) continue;
19526
+ worth[neighbour] = worth[neighbour] + gain;
19527
+ score[neighbour] = worth[neighbour] / bytes[neighbour];
19528
+ }
19529
+ }
19530
+ }
19482
19531
  function fitHotSites(code, entries, siteBytes, fixed, ceiling, sourceBytes, profile) {
19483
19532
  const ranked = [...selectHotSites(code, entries, siteBytes, ceiling - fixed, profile)];
19484
19533
  for (; ranked.length > 0; ranked.pop()) {
@@ -19488,25 +19537,17 @@ function fitHotSites(code, entries, siteBytes, fixed, ceiling, sourceBytes, prof
19488
19537
  }
19489
19538
  return void 0;
19490
19539
  }
19491
- function staticWeights(code, entries) {
19540
+ function staticWeights(code, entries, siteBytes) {
19492
19541
  let weight = /* @__PURE__ */ new Map();
19493
19542
  const kids = [];
19494
19543
  for (let round = 0; round < ROUNDS; round++) {
19495
19544
  const next = /* @__PURE__ */ new Map();
19496
19545
  for (const ip of entries) next.set(ip, 1);
19497
19546
  for (const [ip, w] of weight) {
19498
- kids.length = 0;
19499
- childSlots(code, ip, kids);
19547
+ edgesAsLowered(code, siteBytes, ip, kids);
19500
19548
  const op = code[ip];
19501
- if (op === OP_CONT && continuationGate(code, code[ip + 3]) !== void 0) {
19502
- kids.length = 0;
19503
- kids.push(code[ip + 1], code[ip + 3]);
19504
- }
19505
19549
  const scale = op === OP_REP || op === OP_REPV ? LOOP : op === OP_CHOICE || op === OP_DISPATCH ? 1 / Math.max(1, kids.length) : 1;
19506
- for (const k of kids) {
19507
- const kid = code[k] === OP_GAPGATE ? code[k + 1] : k;
19508
- next.set(kid, Math.min(CAP, (next.get(kid) ?? 0) + w * scale));
19509
- }
19550
+ for (const kid of kids) next.set(kid, Math.min(CAP, (next.get(kid) ?? 0) + w * scale));
19510
19551
  }
19511
19552
  weight = next;
19512
19553
  }
@@ -22732,6 +22773,8 @@ function findFreeIdentifiers(code, filename) {
22732
22773
  // src/plugin/entry-supercompiler.ts
22733
22774
  import { parseSync as parseSync5 } from "oxc-parser";
22734
22775
  var PIECE = /^_pf(\d+)$/;
22776
+ var MERGEABLE = /^_(?:pf|sk)\d+$/;
22777
+ var TEMP = /^_t\d+_/;
22735
22778
  var WRAP = "function __parseman_supercompile__(){\n";
22736
22779
  var SUFFIX = "\n}";
22737
22780
  function ast(value) {
@@ -22865,6 +22908,93 @@ function fallback(emitted, reason, before) {
22865
22908
  }
22866
22909
  };
22867
22910
  }
22911
+ function namesUnder(root, uses, bound) {
22912
+ let plain = true;
22913
+ const bind = (id) => {
22914
+ const name = nameOf2(id);
22915
+ if (name !== void 0) bound.add(name);
22916
+ };
22917
+ const walk = (node2, parent) => {
22918
+ if (node2.type === "VariableDeclarator") bind(ast(node2.id));
22919
+ else if (node2.type === "LabeledStatement") bind(ast(node2.label));
22920
+ else if (isFunction(node2)) {
22921
+ if (node2 !== root) bind(ast(node2.id));
22922
+ nodes(node2.params).forEach(bind);
22923
+ }
22924
+ const name = nameOf2(node2);
22925
+ if (name !== void 0 && (MERGEABLE.test(name) || TEMP.test(name))) {
22926
+ const isKey = parent?.type === "Property" && parent.key === node2 && parent.computed !== true;
22927
+ const isProperty = parent?.type === "MemberExpression" && parent.property === node2 && parent.computed !== true;
22928
+ if (parent?.type === "Property" && parent.shorthand === true) plain = false;
22929
+ if (!isKey && !isProperty) uses.push({ ...range(node2), name });
22930
+ }
22931
+ for (const child of children2(node2)) walk(child, node2);
22932
+ };
22933
+ walk(root, void 0);
22934
+ return plain;
22935
+ }
22936
+ function mergeSameBodies(source) {
22937
+ const parsed = parseSync5("parseman-merge-same-bodies.js", WRAP + source + SUFFIX);
22938
+ if (parsed.errors.length > 0) return source;
22939
+ const statements = nodes(ast(nodes(parsed.program.body)[0]?.body)?.body);
22940
+ const bodies = [];
22941
+ const others = [];
22942
+ const declared = /* @__PURE__ */ new Set();
22943
+ for (const statement of statements) {
22944
+ const name = statement.type === "FunctionDeclaration" ? nameOf2(ast(statement.id)) : void 0;
22945
+ const uses = [];
22946
+ const bound = /* @__PURE__ */ new Set();
22947
+ if (!namesUnder(statement, uses, bound)) return source;
22948
+ for (const local of bound) declared.add(local);
22949
+ if (name === void 0 || !MERGEABLE.test(name)) others.push(...uses);
22950
+ else bodies.push({ ...range(statement), name, uses: uses.sort((a, b) => a.start - b.start), local: bound });
22951
+ }
22952
+ for (const local of declared) if (MERGEABLE.test(local)) return source;
22953
+ const merged = /* @__PURE__ */ new Map();
22954
+ const current = (name) => {
22955
+ let at = name;
22956
+ for (let next = merged.get(at); next !== void 0; next = merged.get(at)) at = next;
22957
+ return at;
22958
+ };
22959
+ let live = bodies;
22960
+ for (let more = true; more; ) {
22961
+ more = false;
22962
+ const first = /* @__PURE__ */ new Map();
22963
+ live = live.filter((body) => {
22964
+ const temps = /* @__PURE__ */ new Map();
22965
+ const edits2 = body.uses.map((use) => {
22966
+ if (MERGEABLE.test(use.name)) {
22967
+ const site = current(use.name);
22968
+ return { ...use, text: site === body.name ? "#self" : site };
22969
+ }
22970
+ if (!body.local.has(use.name)) return { ...use, text: use.name };
22971
+ const seen = temps.get(use.name) ?? temps.size;
22972
+ temps.set(use.name, seen);
22973
+ return { ...use, text: `#t${seen}` };
22974
+ });
22975
+ const key = render2(source, body.start, body.end, edits2);
22976
+ const kept = first.get(key);
22977
+ if (kept === void 0) {
22978
+ first.set(key, body.name);
22979
+ return true;
22980
+ }
22981
+ merged.set(body.name, kept);
22982
+ more = true;
22983
+ return false;
22984
+ });
22985
+ }
22986
+ if (merged.size === 0) return source;
22987
+ const edits = [];
22988
+ const rename = (use) => {
22989
+ if (MERGEABLE.test(use.name) && merged.has(use.name)) edits.push({ ...use, text: current(use.name) });
22990
+ };
22991
+ others.forEach(rename);
22992
+ for (const body of bodies) {
22993
+ if (merged.has(body.name)) edits.push({ start: body.start, end: body.end, text: "" });
22994
+ else body.uses.forEach(rename);
22995
+ }
22996
+ return render2(source, 0, source.length, edits);
22997
+ }
22868
22998
  function supercompileRuleMapReplacement(prog, replacement, select) {
22869
22999
  const cfg = defaultAssemblyCfgs(prog)[0];
22870
23000
  if (cfg === void 0) return replacement;
@@ -22899,7 +23029,7 @@ function supercompileRuleMapReplacement(prog, replacement, select) {
22899
23029
  const start = body.start - prefix.length + 1;
22900
23030
  const end = body.end - prefix.length - 1;
22901
23031
  if (replacement.slice(start, end) !== FACTORY_ARITY_GUARD + canonical2.source + "\n") return replacement;
22902
- const transformed = replacement.slice(0, start) + FACTORY_ARITY_GUARD + candidate.source + "\n" + replacement.slice(end);
23032
+ const transformed = replacement.slice(0, start) + FACTORY_ARITY_GUARD + mergeSameBodies(candidate.source) + "\n" + replacement.slice(end);
22903
23033
  const checked = parseSync5("parseman-entry-supercompile-replacement-result.js", prefix + transformed);
22904
23034
  return checked.errors.length === 0 ? transformed : replacement;
22905
23035
  }