--- lam.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 local function Env (inner, outer) local mt = { __type = "Environment", __index = outer, __newindex = function (self, key, value) if rawget(self, key) then -- Set the current environment's value rawset(self, key, value) else -- Set the outer value getmetatable(self).__index[key] = value end end, } return setmetatable(inner, mt) end local function 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 = Env(inner, self.env) while not isNull(b.cdr) do eval(b.car, e) b = b.cdr end return eval(b.car, e) end, } return setmetatable(v, mt) end local specials = { quote = function (args, env) return args.car end, define = function (args, env) rawset(env, args.car, eval(args.cdr.car, env)) return nil end, lambda = function (args, env) return Proc(args.car, args.cdr, env) end, ["set!"] = function (args, env) env[args.car] = eval(args.cdr.car, env) return nil end, ["if"] = function (args, env) local test, conseq, alt = args.car, args.cdr.car, args.cdr.cdr.car if eval(test) then return eval(conseq) else return eval(alt) end end, } -- Aliases specials.lam = specials.lambda specials.def = specials.define local function 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 specials[op] then return specials[op](args, env) else -- procedure local proc = eval(op, env) local params = {} local a = args while a.cdr do table.insert(params, eval(a.car, env)) a = a.cdr end return proc(List(params)) end end end --- return { Env = Env, Proc = Proc, eval = eval, }