--- lam.eval local eval = {} local base = require "base" local type = require "type" local isNull, isList, isa, List, Cons = type.isNull, type.isList, type.isa, type.List, type.Cons local unpack = table.unpack or unpack function eval.Env (inner, outer) local mt = { __type = "Environment", __index = outer, } return setmetatable(inner, mt) end function eval.Proc (params, body, env) local v = { params = params, body = body, env = env, } local mt = { __type = "Procedure", __call = function (self, args) local inner = {} local p, a = self.params, args while p.cdr and a.cdr do inner[p.car] = a.car p, a = p.cdr, a.cdr end local b = self.body local e = eval.Env(inner, self.env) while not isNull(b.cdr) do eval.eval(b.car, e) b = b.cdr end return eval.eval(b.car, e) end, } return setmetatable(v, mt) end function eval.eval (x, env) env = env or base.env if isa(x, "Symbol") then return env[x] elseif not isList(x) then return x else local op, args = x.car, x.cdr if op == "quote" then return args.car elseif op == "define" or op == "def" then env[args.car] = eval.eval(args.cdr.car, env) return nil elseif op == "lambda" or op == "lam" then return eval.Proc(args.car, args.cdr, env) elseif op == "if" then assert(not isNull(args.cdr), "Malformed 'if'") local test, conseq, alt = args.car, args.cdr.car, args.cdr.cdr.car if eval.eval(test) then return eval.eval(conseq) else return eval.eval(alt) end else -- procedure local proc = eval.eval(op, env) local params = {} local a = args while a.cdr do table.insert(params, eval.eval(a.car, env)) a = a.cdr end return proc(List(params)) end end end --- return eval