Packages

A compiler for a Scheme-ish language (coursework)

Current section

Files

Jump to
sammal lib eval.ex
Raw

lib/eval.ex

defmodule Sammal.Eval do
@moduledoc """
Evaluate parsed Lisp expressions.
"""
alias Sammal.{Env, Expr, Parser, SammalError}
@spec eval_all([Parser.form], Env) :: {any, Env}
def eval_all([], env), do: {nil, env}
def eval_all([exp], env), do: eval(exp, env)
def eval_all([exp | rest], env) do
{result, new_env} = eval(exp, env)
eval_all(rest, new_env)
end
def eval_all(_), do: throw "Expecting a list of expressions" # TODO handle
# TODO also include context here? for errors etc
@doc """
Evaluate a single expression or a form in given environment.
"""
@spec eval(Parser.form | Expr, Env) :: {any, Env}
def eval(%Expr{val: []}, env), do: {[], env}
def eval(%Expr{val: val} = expr, env) when is_atom(val) do
case Env.lookup_var(env, val) do
{:ok, res} -> {res, env}
{:error, _} -> throw SammalError.new(:unbound, expr)
end
end
def eval(%Expr{val: val}, env) when is_number(val) or is_binary(val) or is_boolean(val) do
{val, env}
end
def eval([%Expr{val: :define} = head, %Expr{val: var}, %Expr{} = val], env) when is_atom(var) do
unless Env.top_level?(env) do
# TODO allow internal definitions http://schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-8.html#%25_sec_5.2.2
throw SammalError.new(:cannot_bind, head)
end
{resolved_val, _} = eval(val, env)
{:ok, new_env} = Env.assign(env, var, resolved_val)
{nil, new_env}
end
# TODO:
# def eval([:let, ...
# def eval([:lambda, ...
# TODO lookup and handle macros
# procedure call:
def eval([%Expr{val: val} = head | rest], env) when is_atom(val) do
{proc, _} = eval(head, env)
proc_scope = %Env{parent: env}
args = Enum.map(rest, fn arg -> eval(arg, proc_scope) |> elem(0) end)
{proc.(args), env}
end
def eval([%Expr{} = expr | _], _), do: throw SammalError.new(:not_applicable, expr)
# TODO remove
def eval_raw(raw_code) do
{:ok, tokens} = Sammal.Tokenizer.tokenize(raw_code)
{:ok, {ast, _}} = Sammal.Parser.parse(tokens)
unless Mix.env == :test do
IO.inspect ast, label: "Evaluating AST"
end
eval_all(ast, Env.global)
end
end