@dallaylaen/ski-interpreter 2.7.0 → 2.8.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.
@@ -85,10 +85,13 @@ var control = {
85
85
  var native = {};
86
86
  var Expr = class _Expr {
87
87
  /**
88
+ * Add metadata based on user-supplied values and/or the properties of the term itself.
88
89
  *
89
- * Define properties of the term based on user supplied options and/or inference results.
90
- * Typically useful for declaring Native and Alias terms.
91
- * @protected
90
+ * Typically applied to named terms shortly after instantiation.
91
+ *
92
+ * Experimental. Name and meaning may change in the future.
93
+ *
94
+ * @experimental
92
95
  * @param {Object} options
93
96
  * @param {string} [options.note] - a brief description what the term does
94
97
  * @param {number} [options.arity] - number of arguments the term is waiting for (if known)
@@ -98,7 +101,7 @@ var Expr = class _Expr {
98
101
  * @param {number} [options.maxArgs] - maximum number of arguments for inference, if canonize is true
99
102
  * @return {this}
100
103
  */
101
- _setup(options = {}) {
104
+ annotate(options = {}) {
102
105
  if (options.fancy !== void 0 && this instanceof Named)
103
106
  this.fancyName = options.fancy;
104
107
  if (options.note !== void 0)
@@ -402,7 +405,9 @@ var Expr = class _Expr {
402
405
  */
403
406
  *toLambda(options = {}) {
404
407
  let expr = this.traverse((e) => {
405
- if (e instanceof FreeVar || e instanceof App || e instanceof Lambda || e instanceof Alias)
408
+ if (e instanceof FreeVar)
409
+ return e;
410
+ if (e instanceof App || e instanceof Lambda || e instanceof Alias)
406
411
  return null;
407
412
  const guess = e.infer({ max: options.max, maxArgs: options.maxArgs });
408
413
  if (!guess.normal)
@@ -768,16 +773,26 @@ var Expr = class _Expr {
768
773
  diag(indent = "") {
769
774
  return indent + this.constructor.name + ": " + this;
770
775
  }
776
+ declare(options = {}) {
777
+ const { declaration: d = ["", "=", "; "], ...format } = options;
778
+ const res = toposort({ list: [this], env: format.inventory });
779
+ return res.list.map((s) => {
780
+ if (s instanceof Alias)
781
+ return d[0] + s.name + d[1] + s.impl.format({ ...format, inventory: res.env });
782
+ if (s instanceof FreeVar)
783
+ return d[0] + s.name + d[1];
784
+ return s.format({ ...format, inventory: res.env });
785
+ }).join(d[2]);
786
+ }
771
787
  /**
772
788
  * Convert the expression to a JSON-serializable format.
773
789
  * Sadly the format is not yet finalized and may change in the future.
774
790
  *
775
791
  * @experimental
776
- * @returns {string}
777
792
  * @sealed
778
793
  */
779
794
  toJSON() {
780
- return this.format();
795
+ return this.declare();
781
796
  }
782
797
  };
783
798
  var App = class _App extends Expr {
@@ -944,7 +959,7 @@ var Native = class extends Named {
944
959
  constructor(name, impl, opt = {}) {
945
960
  super(name);
946
961
  this.invoke = impl;
947
- this._setup({ canonize: true, ...opt });
962
+ this.annotate({ canonize: true, ...opt });
948
963
  }
949
964
  };
950
965
  var Lambda = class _Lambda extends Expr {
@@ -1051,7 +1066,7 @@ var Alias = class extends Named {
1051
1066
  if (!(impl instanceof Expr))
1052
1067
  throw new Error("Attempt to create an alias for a non-expression: " + impl);
1053
1068
  this.impl = impl;
1054
- this._setup(options);
1069
+ this.annotate(options);
1055
1070
  this.invoke = waitn(options.inline ? 0 : this.arity ?? 0)(impl);
1056
1071
  this.size = impl.size;
1057
1072
  if (options.inline)
@@ -1189,40 +1204,43 @@ function maybeLambda(args, expr, caps) {
1189
1204
  function nthvar(n) {
1190
1205
  return new FreeVar("abcdefgh"[n] ?? "x" + n);
1191
1206
  }
1192
- var classes = { Expr, App, Named, FreeVar, Native, Lambda, Church, Alias };
1193
-
1194
- // src/toposort.ts
1195
- function toposort(list, env) {
1196
- if (list instanceof Expr)
1197
- list = [list];
1198
- if (env) {
1199
- if (!list)
1200
- list = Object.keys(env).sort().map((k) => env[k]);
1201
- } else {
1202
- if (!list)
1203
- return { list: [], env: {} };
1204
- env = {};
1205
- for (const item of list) {
1206
- if (!(item instanceof Named))
1207
- continue;
1208
- if (env[item.name])
1209
- throw new Error("duplicate name " + item);
1210
- env[item.name] = item;
1207
+ function toposort(options) {
1208
+ if (typeof options !== "object" || options === null || Array.isArray(options) || options instanceof Expr)
1209
+ throw new Error("positional arguments to toposort are deprecated, use { list: ..., env: ... } instead");
1210
+ const allow = options.allow ? { ...options.allow } : null;
1211
+ const env = { ...options.env ?? {} };
1212
+ const list = options.list instanceof Expr ? [options.list] : options.list ?? [];
1213
+ if (allow) {
1214
+ for (const term of list) {
1215
+ if (term instanceof Named)
1216
+ allow[term.name] = term;
1211
1217
  }
1212
1218
  }
1219
+ for (const term of list) {
1220
+ if (term instanceof Named && env[term.name] === term)
1221
+ delete env[term.name];
1222
+ }
1213
1223
  const out = [];
1214
- const seen = /* @__PURE__ */ new Set();
1224
+ const seen = new Set(Object.values(env));
1215
1225
  const rec = (term) => {
1216
1226
  if (seen.has(term))
1217
1227
  return;
1218
- term.fold(false, (acc, e) => {
1219
- if (e !== term && e instanceof Named && env[e.name] === e) {
1228
+ term.fold(void 0, (_, e) => {
1229
+ if (!(e instanceof Named))
1230
+ return;
1231
+ if (allow && allow[e.name] !== e)
1232
+ return;
1233
+ if (!allow && e instanceof Alias && e.inline)
1234
+ return;
1235
+ if (e !== term) {
1220
1236
  rec(e);
1221
- return control.prune(false);
1237
+ return control.prune();
1222
1238
  }
1223
1239
  });
1224
1240
  out.push(term);
1225
1241
  seen.add(term);
1242
+ if (term instanceof Named)
1243
+ env[term.name] ||= term;
1226
1244
  };
1227
1245
  for (const term of list)
1228
1246
  rec(term);
@@ -1231,6 +1249,7 @@ function toposort(list, env) {
1231
1249
  env
1232
1250
  };
1233
1251
  }
1252
+ var classes = { Expr, App, Named, FreeVar, Native, Lambda, Church, Alias };
1234
1253
 
1235
1254
  // src/parser.ts
1236
1255
  var Empty = class extends Expr {
@@ -1333,7 +1352,7 @@ var Parser = class {
1333
1352
  add(term, impl, options) {
1334
1353
  const named = this._named(term, impl);
1335
1354
  const opts = typeof options === "string" ? { note: options, canonize: false } : options ?? {};
1336
- named._setup({ canonize: this.annotate, ...opts });
1355
+ named.annotate({ canonize: this.annotate, ...opts });
1337
1356
  const old = this.known[named.name];
1338
1357
  if (old instanceof Alias)
1339
1358
  old.makeInline();
@@ -1478,7 +1497,7 @@ var Parser = class {
1478
1497
  env[temp.name] = temp;
1479
1498
  delete env[name];
1480
1499
  }
1481
- const list = toposort(void 0, env).list;
1500
+ const list = toposort({ list: Object.values(env), allow: {} }).list;
1482
1501
  const detour = /* @__PURE__ */ new Map();
1483
1502
  if (Object.keys(needDetour).length) {
1484
1503
  const rework = (expr) => {
@@ -1505,14 +1524,15 @@ var Parser = class {
1505
1524
  return out;
1506
1525
  }
1507
1526
  /**
1508
- * @template T
1509
1527
  * @param {string} source
1510
1528
  * @param {Object} [options]
1511
- * @param {{[keys: string]: Expr}} [options.env]
1512
- * @param {T} [options.scope]
1513
- * @param {boolean} [options.numbers]
1514
- * @param {boolean} [options.lambdas]
1515
- * @param {string} [options.allow]
1529
+ * @param [options.env] - additional
1530
+ * @param [options.scope] - assign this scope to unknown free variables
1531
+ * @param {boolean} [options.numbers] - whether numbers are allowed
1532
+ * @param {boolean} [options.lambdas] - whether lambdas are allowed
1533
+ * @param {string} [options.allow] - restrict known terms
1534
+ * @param [options.canonize] - whether to calculate canonical form, arity, and properties
1535
+ * of intermediate aliases
1516
1536
  * @return {Expr}
1517
1537
  */
1518
1538
  parse(source, options = {}) {
@@ -1522,18 +1542,19 @@ var Parser = class {
1522
1542
  const jar = { ...options.env };
1523
1543
  let expr = new Empty();
1524
1544
  for (const item of lines) {
1525
- if (expr instanceof Alias)
1526
- expr.makeInline();
1527
- const def = item.match(/^([A-Z]|[a-z][a-z_0-9]*)\s*=(.*)$/s);
1528
- if (def && def[2] === "")
1529
- expr = new FreeVar(def[1], options.scope ?? FreeVar.global);
1545
+ const [_, name, def] = item.match(/^([A-Z]|[a-z][a-z_0-9]*)\s*=(.*)$/s) || [];
1546
+ if (name !== void 0) {
1547
+ if (jar[name] instanceof Alias && jar[name] !== options.env?.[name]) {
1548
+ jar[name].makeInline();
1549
+ }
1550
+ delete jar[name];
1551
+ }
1552
+ if (def === "")
1553
+ expr = new FreeVar(name, options.scope ?? FreeVar.global);
1530
1554
  else
1531
1555
  expr = this.parseLine(item, jar, options);
1532
- if (def) {
1533
- if (jar[def[1]] !== void 0)
1534
- throw new Error("Attempt to redefine a known term: " + def[1]);
1535
- jar[def[1]] = expr;
1536
- }
1556
+ if (name)
1557
+ jar[name] = expr;
1537
1558
  }
1538
1559
  if (this.addContext) {
1539
1560
  if (expr instanceof Named)
@@ -1565,7 +1586,7 @@ var Parser = class {
1565
1586
  parseLine(source, env = {}, options = {}) {
1566
1587
  const aliased = source.match(/^\s*([A-Z]|[a-z][a-z_0-9]*)\s*=\s*(.*)$/s);
1567
1588
  if (aliased)
1568
- return new Alias(aliased[1], this.parseLine(aliased[2], env, options));
1589
+ return new Alias(aliased[1], this.parseLine(aliased[2], env, options), { canonize: options.canonize });
1569
1590
  const opt = {
1570
1591
  numbers: options.numbers ?? this.hasNumbers,
1571
1592
  lambdas: options.lambdas ?? this.hasLambdas,
@@ -2119,15 +2140,8 @@ function deepFormat(obj, options = {}) {
2119
2140
  out[key] = deepFormat(obj[key], options);
2120
2141
  return out;
2121
2142
  }
2122
- function declare(expr, env = {}) {
2123
- const res = toposort([expr], env);
2124
- return res.list.map((s) => {
2125
- if (s instanceof Alias)
2126
- return s.name + "=" + s.impl.format({ inventory: res.env });
2127
- if (s instanceof FreeVar)
2128
- return s.name + "=";
2129
- return s.format({ inventory: res.env });
2130
- }).join("; ");
2143
+ function declare(expr, env) {
2144
+ return expr.declare({ inventory: env });
2131
2145
  }
2132
2146
  var isStringPair = (x) => Array.isArray(x) && x.length === 2 && typeof x[0] === "string" && typeof x[1] === "string" ? void 0 : "must be a pair of strings";
2133
2147
  var isStringTriple = (x) => Array.isArray(x) && x.length === 3 && typeof x[0] === "string" && typeof x[1] === "string" && typeof x[2] === "string" ? void 0 : "must be a triplet of strings";