@dallaylaen/ski-interpreter 2.8.1 → 2.8.2
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 +10 -0
- package/example/collatz.ski +30 -0
- package/example/eval.js +34 -0
- package/example/example-quests.json +83 -0
- package/example/mini-quest.html +42 -0
- package/example/reverse-linked-list.ski +14 -0
- package/example/t-foo-bar.ski +5 -0
- package/package.json +2 -1
- package/lib/types/toposort.d.ts +0 -29
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.8.2] - 2026-04-26
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `examples/` are now included in the package
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- removed stale .d.ts file from lib/
|
|
17
|
+
|
|
8
18
|
## [2.8.1] - 2026-04-26
|
|
9
19
|
|
|
10
20
|
### Added
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// This file generates a trace of a Collatz 3n+1 sequence,
|
|
2
|
+
// producing HUGE number of steps with very deep recursion,
|
|
3
|
+
// and can thus be used as a rough performance benchmark.
|
|
4
|
+
|
|
5
|
+
// `calc f n` == `f n`, provided n is a number, except that `n` is
|
|
6
|
+
// forced into a redex position before being fed to `f`,
|
|
7
|
+
// thus eliminating possible duplicate computations.
|
|
8
|
+
|
|
9
|
+
T=CI;
|
|
10
|
+
V=BCT;
|
|
11
|
+
calc=BBB(T 0)(V(CB+));
|
|
12
|
+
if=BBB(B(BW)C)C;
|
|
13
|
+
half=C(V(C(V(T(KI))(C(B+)K)))(K 0))K;
|
|
14
|
+
collatz=if(VCK)half(B(V+ 1)(B 3));
|
|
15
|
+
dec=BBBCV(C(BT)+)(K 0)I;
|
|
16
|
+
M=WI;
|
|
17
|
+
wait4=BBB(BCC)(BCC);
|
|
18
|
+
nil=KI;
|
|
19
|
+
lst=BS(C(BB));
|
|
20
|
+
fold=M(B(SI)(wait4M))(B(C(BV(BBBK))nil)(C(BBlst)));
|
|
21
|
+
L=BWB;
|
|
22
|
+
lazyy=M(B(SI)(wait4M));
|
|
23
|
+
lesseq=C(BB(VT(KK)))(VT(K(KI)));
|
|
24
|
+
P=M(C(BL(WC)));
|
|
25
|
+
R=CC;
|
|
26
|
+
v3=BBBCV;
|
|
27
|
+
|
|
28
|
+
BML(B(S(B(BB(B(Bcalc(SV))))(Cif (Knil)))) (B(S(CB)))) (C lesseq 1) (collatz) 127
|
|
29
|
+
// BML(B(S(B(BB(B((SV))))(Cif (Knil)))) (B(S(CB)))) (C lesseq 1) (collatz) 27
|
|
30
|
+
|
package/example/eval.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A minimal example of evaluating a SKI expression.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { SKI } = require('../lib/ski-interpreter.cjs');
|
|
10
|
+
// SKI is the only export and contains other goodies as subkeys
|
|
11
|
+
|
|
12
|
+
const [node, path, src] = process.argv;
|
|
13
|
+
|
|
14
|
+
if (!src) {
|
|
15
|
+
console.log(`Usage: ${node} ${path} <src>`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const ski = new SKI();
|
|
20
|
+
|
|
21
|
+
// add some extra terms
|
|
22
|
+
ski.add('M', 'SII'); // an alias
|
|
23
|
+
ski.add('L', x => y => x.apply(y.apply(y))) // ditto but with a 'Native' js impl
|
|
24
|
+
|
|
25
|
+
const expr = ski.parse(src);
|
|
26
|
+
|
|
27
|
+
// expr.step() = 1 reduction, expr.run() = reduce to the end
|
|
28
|
+
// expr.walk() = an iterator
|
|
29
|
+
// all three return an object with { steps, expr, final } where:
|
|
30
|
+
// - steps = the number of reductions performed so far
|
|
31
|
+
// - expr = the resulting expression
|
|
32
|
+
// - final = boolean indicating if the expression is in normal form (no more reductions possible)
|
|
33
|
+
for (const step of expr.walk())
|
|
34
|
+
console.log(`[${step.steps}] ${step.expr}`);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "dull6XUf",
|
|
3
|
+
"created_at": "2026-02-27T00:00:00",
|
|
4
|
+
"name": "Example quests",
|
|
5
|
+
"intro": "<p>A small template collection of combinatory logic exercises. Feel free to use this file as a starting point for your own quest chapter.</p>",
|
|
6
|
+
"content": [
|
|
7
|
+
{
|
|
8
|
+
"id": "YpgCLTl8",
|
|
9
|
+
"created_at": "2026-02-27T00:00:00",
|
|
10
|
+
"name": "I from SK",
|
|
11
|
+
"intro": [
|
|
12
|
+
"<p>The identity combinator <code>I</code> satisfies <code>I x = x</code>.",
|
|
13
|
+
"It can actually be derived from <code>S</code> and <code>K</code> alone —",
|
|
14
|
+
"no built-in <code>I</code> needed.</p>",
|
|
15
|
+
"<p>Find a term built only from <code>S</code> and <code>K</code> that behaves like <code>I</code>.</p>"
|
|
16
|
+
],
|
|
17
|
+
"hint": "Think about what <code>S K K x</code> reduces to.",
|
|
18
|
+
"allow": "SK",
|
|
19
|
+
"input": "phi",
|
|
20
|
+
"cases": [
|
|
21
|
+
[ "phi x", "x" ],
|
|
22
|
+
[ "phi (K x y)", "K x y" ]
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "vQsBd85P",
|
|
27
|
+
"created_at": "2026-02-27T00:00:00",
|
|
28
|
+
"name": "NAND for K/KI",
|
|
29
|
+
"intro": [
|
|
30
|
+
"<p>Using the Church encoding, <code>K</code> represents <b>true</b> and <code>KI</code> represents <b>false</b>.</p>",
|
|
31
|
+
"<p>Implement <code>nand</code>: it should return <code>K</code> (true) for every pair of inputs",
|
|
32
|
+
"except when <i>both</i> are <code>K</code> (true), in which case it returns <code>KI</code> (false).</p>",
|
|
33
|
+
"<p>Apply the result to <code>true</code> and <code>false</code> witnesses to observe it.</p>"
|
|
34
|
+
],
|
|
35
|
+
"input": "phi",
|
|
36
|
+
"cases": [
|
|
37
|
+
[ "phi K K true false", "false" ],
|
|
38
|
+
[ "phi (KI) K true false", "true" ],
|
|
39
|
+
[ "phi K (KI) true false", "true" ],
|
|
40
|
+
[ "phi (KI) (KI) true false", "true" ]
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "IgVco9YW",
|
|
45
|
+
"created_at": "2026-02-27T00:00:00",
|
|
46
|
+
"name": "Linear basis from I and your own combinator",
|
|
47
|
+
"intro": [
|
|
48
|
+
"<p>It can be shown that <code>I</code>, <code>B</code>, and <code>C</code> (or <code>T</code>)",
|
|
49
|
+
"form a complete basis for <i>linear</i> combinators — those that use every argument",
|
|
50
|
+
"exactly once.</p>",
|
|
51
|
+
"<p>Surprisingly, <code>B</code> and <code>C</code> can be merged into a single combinator.",
|
|
52
|
+
"Your task is to <i>declare</i> such a combinator <code>P</code> (lambdas are allowed here),",
|
|
53
|
+
"and then <i>prove</i> the basis is complete by implementing <code>B</code> and <code>T</code>",
|
|
54
|
+
"using only <code>I</code> and your <code>P</code>.</p>"
|
|
55
|
+
],
|
|
56
|
+
"input": [
|
|
57
|
+
{
|
|
58
|
+
"name": "P",
|
|
59
|
+
"lambdas": true,
|
|
60
|
+
"allow": "I-I",
|
|
61
|
+
"note": "Declare your own linear combinator (lambdas allowed, no built-ins)."
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "B",
|
|
65
|
+
"lambdas": false,
|
|
66
|
+
"allow": "I",
|
|
67
|
+
"note": "Implement <code>B a b c = a (b c)</code> using <code>P</code> and <code>I</code>."
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "T",
|
|
71
|
+
"lambdas": false,
|
|
72
|
+
"allow": "I",
|
|
73
|
+
"note": "Implement <code>T a b = b a</code> using <code>P</code> and <code>I</code>."
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"cases": [
|
|
77
|
+
[ { "caps": { "linear": true } }, "P" ],
|
|
78
|
+
[ "B a b c", "a (b c)" ],
|
|
79
|
+
[ "T a b", "b a" ]
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8"/>
|
|
5
|
+
<title>Example Quests - Simple Kombinator Interpreter</title>
|
|
6
|
+
<script src="../docs/build/js/ski-quest.min.js"></script>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: sans-serif;
|
|
10
|
+
max-width: 860px;
|
|
11
|
+
margin: 0 auto;
|
|
12
|
+
padding: 1em;
|
|
13
|
+
}
|
|
14
|
+
#inventory {
|
|
15
|
+
border: 1px solid #ccc;
|
|
16
|
+
padding: 0.5em 1em;
|
|
17
|
+
margin-bottom: 1em;
|
|
18
|
+
}
|
|
19
|
+
</style>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<h1>Example Quests</h1>
|
|
23
|
+
|
|
24
|
+
<div id="inventory">
|
|
25
|
+
<b>Inventory</b>
|
|
26
|
+
<ul id="known"></ul>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div id="quest"></div>
|
|
30
|
+
|
|
31
|
+
<script>
|
|
32
|
+
const page = new QuestPage({
|
|
33
|
+
storePrefix: 'example-quests',
|
|
34
|
+
baseUrl: '.',
|
|
35
|
+
contentBox: document.getElementById('quest'),
|
|
36
|
+
inventoryBox: document.getElementById('known'),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
page.loadChapters(['example-quests.json']);
|
|
40
|
+
</script>
|
|
41
|
+
</body>
|
|
42
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dallaylaen/ski-interpreter",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.2",
|
|
4
4
|
"description": "Simple Kombinator Interpreter - a combinatory logic & lambda calculus parser and interpreter. Supports SKI, BCKW, Church numerals, and setting up assertions ('quests') involving all of the above.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"combinatory logic",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"README.md",
|
|
41
41
|
"LICENSE",
|
|
42
42
|
"CHANGELOG.md",
|
|
43
|
+
"example",
|
|
43
44
|
"lib"
|
|
44
45
|
],
|
|
45
46
|
"repository": {
|
package/lib/types/toposort.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Expr, Named } from './expr';
|
|
2
|
-
/**
|
|
3
|
-
* Sort a list in such a way that dependent terms come after the (named) terms they depend on.
|
|
4
|
-
* If env is given, only terms listed there are taken into account.
|
|
5
|
-
* If env is omitted, it will be implied from the list.
|
|
6
|
-
* If list is omitted, it will default to values of env.
|
|
7
|
-
* If just one term is given instead of a list, it will be coerced into a list.
|
|
8
|
-
*
|
|
9
|
-
* No terms outside env + list may ever appear in the result.
|
|
10
|
-
*
|
|
11
|
-
* The terms in env must be named and their names must match their keys.
|
|
12
|
-
*
|
|
13
|
-
* @param {Expr|Expr[]} list
|
|
14
|
-
* @param {{[s:string]: Named}} env
|
|
15
|
-
* @returns {{list: Expr[], env: {[s:string]: Named}}}
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* const expr = ski.parse(src);
|
|
19
|
-
* toposort([expr], ski.getTerms()); // returns all terms appearing in Expr in correct order
|
|
20
|
-
*/
|
|
21
|
-
export type ToposortResult = {
|
|
22
|
-
list: Expr[];
|
|
23
|
-
env: {
|
|
24
|
-
[s: string]: Named;
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
export declare function toposort(list: Expr[] | Expr | undefined, env: {
|
|
28
|
-
[s: string]: Named;
|
|
29
|
-
} | undefined): ToposortResult;
|