evaluate = {
function evaluate(exp, env = globalEnv, verbose = false) {
if (verbose) console.log("EVAL EXP", exp);
switch (typeOf(exp)) {
case "number": return exp;
case "string": return exp.slice(1, -1);
case "symbol": return searchEnv(exp, env).table[exp];
case "list": { const [operator, ...args] = exp; return apply(operator, args, env);}
default: throw SyntaxError("Unexpected Expression");
}
}
function apply(operator, args, env, verbose=false) {
if (verbose) console.log("APPLY OPERATOR", operator, "TO ARGS", args);
switch (operator) {
case "quote":
return args[0];
case "if": {
const [test, conseq, alt] = args;
const predicate = evaluate(test, env);
const exp = predicate ? conseq : alt;
return evaluate(exp, env);
}
case "define": {
const [defined, definition] = args;
if (Array.isArray(defined)) return createProc(defined.slice(1), definition, env);
else return env.table[defined] = evaluate(definition, env);
}
case "set!": {
const [symbol, definition] = args;
searchEnv(symbol, env).table[symbol] = evaluate(definition, env);
return null;
}
case "lambda": {
const [parms, body] = args;
return createProc(parms, body, env);
}
default: {
const proc = evaluate(operator, env);
const vals = args.map((arg) => evaluate(arg, env));
return proc(...vals);
}
}
}
function createProc(parms, body, env) {
const zip = (a1, a2) => a1.reduce((acc, k, i) => (acc[k] = a2[i], acc), {});
function call(...args) {
const newEnv = createEnv(zip(parms, args), env);
return evaluate(body, newEnv);
}
return call;
}
return evaluate
}