UNPKG

lambda-calculus-with-js

Version:

Using JS' anonymous functions to perform lambda calculus

189 lines (188 loc) 5.22 kB
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; });