@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.
- package/CHANGELOG.md +27 -0
- package/bin/ski.js +7 -10
- package/lib/ski-interpreter.cjs.js +74 -60
- package/lib/ski-interpreter.cjs.js.map +3 -3
- package/lib/ski-interpreter.min.js +5 -5
- package/lib/ski-interpreter.min.js.map +4 -4
- package/lib/ski-interpreter.mjs +74 -60
- package/lib/ski-interpreter.mjs.map +3 -3
- package/lib/ski-quest.min.js +5 -5
- package/lib/ski-quest.min.js.map +4 -4
- package/lib/types/expr.d.ts +33 -6
- package/lib/types/extras.d.ts +4 -10
- package/lib/types/index.d.ts +2 -5
- package/lib/types/parser.d.ts +11 -13
- package/package.json +1 -1
package/lib/ski-interpreter.mjs
CHANGED
|
@@ -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
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
if (
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
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 =
|
|
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(
|
|
1219
|
-
if (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(
|
|
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.
|
|
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(
|
|
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
|
|
1512
|
-
* @param
|
|
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
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
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 (
|
|
1533
|
-
|
|
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
|
-
|
|
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";
|