Interpreter source: function read(text) { var item, match; text = text.trimLeft(); if (text=="") throw "EOF"; else if (text[0] == "(") { var list = []; text = text.substring(1); while ([item, text] = read(text), item!=")") list.push(item); return [list, text]; } else if (text[0] == ")") return [")", text.substring(1)]; else { match = text.match(/^[^\s()]+/); if (match[0].match(/^-?\d+$/)) return [Number(match[0]), text.substring(match[0].length)]; else return [match[0], text.substring(match[0].length)]; } } function evaluate(code, env) { if (typeof(code) == "number") return code; else if (typeof(code) == "string") return lookup(code, env); else if (code.length == 0) return code; else { switch (code[0]) { case "define": return env[code[1]] = evaluate(code[2], env); case "quote": return code[1]; case "lambda": return { type: "lambda", args: code[1], body: code[2], env: env }; case "if": if (evaluate(code[1], env).length !== 0) return evaluate(code[2], env); else return evaluate(code[3], env); default: var evals = code.map(function(x) { return evaluate(x, env); }); var fun = evals[0]; var args = evals.slice(1); if (typeof(fun) == "function") return fun.apply(null, args); else if (typeof(fun) == "object" && fun.type == "lambda") { var newenv = { _parent: fun.env }; for (var i in fun.args) newenv[fun.args[i]] = args[i]; return evaluate(fun.body, newenv); } else throw "Not a function: " + fun; } } } function lookup(name, env) { if (env) { if (env[name] !== undefined) return env[name]; else return lookup(name, env._parent); } else { throw "Unbound variable " + name; } } function print(item) { if (typeof(item) == "number" || typeof(item) == "string") return item; else if (typeof(item) == "object") { if (item.type == "lambda") return ""; else { var out = "("; for (var i in item) { if (i!=0) out+=" "; out+=print(item[i]); } out+=")"; return out; } } else return "<" + item + ">"; } stdenv = { "+": function(x,y) { return x+y; }, "-": function(x,y) { return x-y; }, "*": function(x,y) { return x*y; }, "/": function(x,y) { return x/y; }, "=": function(x,y) { return x==y? "t" : []; }, "<": function(x,y) { return x<y? "t" : []; }, "first": function(ls) { return ls[0]; }, "rest" : function(ls) { return ls.slice(1); }, "cons" : function(first, rest) { return [first].concat(rest); }, "null?": function(ls) { return ls.length===0? "t": []; }, "cons?": function(ls) { return typeof(ls) == "object" && ls.length>0? "t": []; }, } function main(program, output) { var item; var env = {}; for (var v in stdenv) env[v] = stdenv[v]; while (1) { if (program.trim() == "") return; try { [item, program] = read(program); } catch (e) { alert("Error reading source to be interpreted: " + e); return; } try { output(print(evaluate(item, env))); } catch (e) { alert("Error interpreting source: " + e); return; } } }
Program to be interpreted: (define fac (lambda (n) (if (= n 0) 1 (* n (fac (- n 1)))))) (fac 5)
[output]