lambda-calculus-with-js
Version:
Using JS' anonymous functions to perform lambda calculus
189 lines (188 loc) • 5.22 kB
JavaScript
export class Tested {
toKfc(num) {
const arr = [this.kfcify(num, getIndenter(), {}, 0)].flat(Infinity);
return arr.join('');
}
}
function getIndenter(indented = 0) {
const space = '| '.repeat(indented);
const indenter = (context) => `${space}${context}\n`;
indenter.next = () => getIndenter(indented + 1);
return indenter;
}
export class TestedFunc extends Tested {
arg;
value;
constructor(arg, value) {
super();
this.arg = arg;
this.value = value;
}
rebuild(ids = {}) {
return n => this.value.rebuild({ ...ids, [this.arg.id]: n });
}
toJs() {
return `p${this.arg.id} => ${this.value.toJs()}`;
}
toLambda(std) {
return `λp${this.arg.id}.${this.value.toLambda(std)}`;
}
kfcify(num, indenter, table, depth) {
return [
num ? indenter('func') : 'F',
this.value.kfcify(num, indenter.next(), { ...table, [this.arg.id]: depth }, depth + 1),
];
}
}
export class TestedCall extends Tested {
caller;
arg;
constructor(caller, arg) {
super();
this.caller = caller;
this.arg = arg;
}
rebuild(ids = {}) {
return this.caller.rebuild(ids)(this.arg.rebuild(ids));
}
toJs() {
let caller = this.caller.toJs();
if (this.caller instanceof TestedFunc)
caller = `(${caller})`;
return `${caller}(${this.arg.toJs()})`;
}
toLambda(std) {
const caller = this.caller.toLambda(std);
const arg = this.arg.toLambda(std);
return std ? `(${caller} ${arg})` : `((${caller}) (${arg}))`;
}
kfcify(num, indenter = getIndenter(), table = {}, depth = 0) {
return [
num ? indenter('call') : 'C',
this.caller.kfcify(num, indenter.next(), table, depth),
this.arg.kfcify(num, indenter.next(), table, depth),
];
}
}
export class TestedArg extends Tested {
id;
constructor(id) {
super();
this.id = id;
}
rebuild(ids = {}) {
return ids[this.id];
}
toJs() {
return `p${this.id}`;
}
toLambda(_) {
return `p${this.id}`;
}
kfcify(num, indenter = getIndenter(), table = {}, depth = 0) {
const argDepth = depth - table[this.id];
return num
? indenter(argDepth.toString())
: 'K'.repeat(argDepth) + 'F';
}
}
export class TestedConst extends Tested {
inner;
constructor(inner) {
super();
this.inner = inner;
}
rebuild(_) {
return fI[this.inner];
}
toJs() {
return `fI[${this.inner}]`;
}
toLambda(_) {
return this.inner.toString();
}
kfcify(num, indenter = getIndenter()) {
return num
? indenter(this.inner.toString())
: this.inner.toString();
}
}
/**getLambda */
export function gl(lambda) {
return lambda;
}
function getCatcher(argThis, argTotal, testTag = new TestedArg(argThis)) {
const catcher = (n) => getCatcher(argThis, argTotal, new TestedCall(catcher.testTag, n.testTag ?? test(n, argTotal)));
catcher.testTag = testTag;
return catcher;
}
export function test(lambda, argTotal = { id: 1 }) {
lambda = solve(lambda);
return lambda.testTag ?? new TestedFunc(new TestedArg(argTotal.id), test(lambda(getCatcher(argTotal.id++, argTotal)), argTotal));
}
export var Log;
(function (Log) {
/**普通 Lambda 表达式 */
Log[Log["Std"] = 0] = "Std";
/**JavaScript 代码 */
Log[Log["Js"] = 1] = "Js";
/**大部分 Lambda 演算网站可以正确识别的形式 */
Log[Log["Exable"] = 2] = "Exable";
/**KFC 语言 */
Log[Log["Kfc"] = 3] = "Kfc";
/**使用数字的易懂的 KFC 语言 */
Log[Log["KfcNum"] = 4] = "KfcNum";
})(Log || (Log = {}));
const logMap = [
t => t.toLambda(true),
t => t.toJs(),
t => t.toLambda(false),
t => t.toKfc(false),
t => t.toKfc(true),
];
export function log(n, level = Log.Js) {
console.log(logMap[level](test(n)));
}
function getRecursion(lastRecursing) {
return n => {
let recursing;
if (recursionConfig.boldlyReceiving) {
const recursed = lastRecursing();
recursing = () => recursed(n);
}
else {
recursing = () => lastRecursing()(n);
}
const t0 = getRecursion(recursing);
t0.recursing = recursing;
return t0;
};
}
/**Y Combinator */
export const yC = gl(p => {
const n = f => p(getRecursion(() => f(f)));
return n(n);
});
export const recursionConfig = {
/**递归次数限制,超过就被认为是死循环 */
max: 999999,
/**实验性,如果为 true ,递归函数的最大参数数量不受栈空间影响 */
boldlyReceiving: false,
};
export function solve(n) {
let i = 0;
while (n.recursing) {
n = n.recursing();
if (i++ > recursionConfig.max)
throw Error('Endless Recursion');
}
return n;
}
/**free identifier */
export const fI = Array(999)
.fill(0)
.map((_, inner) => {
const t = n => n;
t.testTag = new TestedConst(inner);
return t;
});