; A small interpreter over dyn values: a program is nested vectors whose ; first element is a keyword naming the operation, variables are keywords, ; and environments are dyn maps chained through a :parent key. class lambda(param, body, env) generic describe(v) -> dyn method describe(f: lambda) "a function of one argument" multi kind(v) -> dyn = type-of(v) method kind(v) when :int "number" method kind(v) when :vec = "form" method kind(v) when :else "value" once steps = 0 fn lookup(env, name) -> dyn if env == nil println("unbound", name) return 0 if has-key?(env, name) then get(env, name) else lookup(get(env, :parent), name) fn extend(env, name, value) -> dyn {:parent env name value} fn eval(e, env) -> dyn steps += 1 match type-of(e) :keyword -> lookup(env, e) :vec -> eval-form(e, env) _ -> e fn eval-form(e, env) -> dyn let op = e[0] match op :+ -> eval(e[1], env) + eval(e[2], env) :- -> eval(e[1], env) - eval(e[2], env) :* -> eval(e[1], env) * eval(e[2], env) :< -> eval(e[1], env) < eval(e[2], env) :if -> if eval(e[1], env) then eval(e[2], env) else eval(e[3], env) :let -> let v = eval(e[2], env) let inner = extend(env, e[1], v) ; A function sees its own name, so it can call itself. if type-of(v) == :lambda put(v, :env, inner) eval(e[3], inner) :fn -> lambda(e[1], e[2], env) :do -> let last = nil for i in range(1, length(e)) last = eval(e[i], env) last _ -> let f = eval(op, env) arg = eval(e[1], env) eval(get(f, :body), extend(get(f, :env), get(f, :param), arg)) fn run(program) -> () steps = 0 let result = eval(program, {:parent nil}) println(kind(program), "=>", result, "in", steps, "steps") fn main() -> i32 run(42) run([:+ 1 [:* 2 3]]) run([:let :x 5 [:if [:< :x 3] :small [:* :x :x]]]) let fact = [:let :fact [:fn :n [:if [:< :n 2] 1 [:* :n [:fact [:- :n 1]]]]] [:fact 6]] run(fact) run([:let :k 10 [:let :add-k [:fn :y [:+ :y :k]] [:do [:add-k 1] [:add-k 32]]]]) let f = eval([:fn :x :x], {:parent nil}) println(describe(f), "/", kind(f), "/", type-of(f)) run([:let :greeting "hello" :greeting]) 0