1 defmodule Mix
.Tasks
.Step8Macros
do
3 env
= Mal
.Env
.initialize()
4 Mal
.Env
.merge(env
, Mal
.Core
.namespace
)
10 defp
load_file([], _env
), do: nil
11 defp
load_file([file_name | _args
], env
) do
13 (load-file "#
{file_name
}")
18 defp
bootstrap(args
, env
) do
22 (fn* (a) (if a false true)))
29 (eval (read-string (str "(do " (slurp f) ")")))))
40 (throw \"odd number of forms to cond\"))
41 (cons 'cond (rest (rest xs)))))))"
52 `
(let
* (or_FIXME ~
(first xs
)) (if or_FIXME
or_FIXME (or ~@
(rest xs
))))))))
55 Mal.Env.set(env, "eval
", fn [ast] ->
60 [_file_name | rest] -> Mal.Env.set(env, "*ARGV
*", rest)
61 [] -> Mal.Env.set(env, "*ARGV
*", [])
66 IO.write(:stdio, "user
> ")
67 IO.read(:stdio, :line)
68 |> read_eval_print(env)
73 def eval_ast(ast, env) when is_list(ast) do
74 Enum.map(ast, fn elem -> eval(elem, env) end)
77 def eval_ast({:vector, ast}, env) do
78 {:vector, Enum.map(ast, fn elem -> eval(elem, env) end)}
81 def eval_ast({:symbol, symbol}, env) do
82 case Mal.Env.get(env, symbol) do
84 :not_found -> throw({:error, "invalid symbol #
{symbol
}"})
88 def eval_ast(ast, _env), do: ast
91 Mal.Reader.read_str(input)
94 defp eval_bindings({:vector, vector}, env), do: eval_bindings(vector, env)
95 defp eval_bindings([], _env), do: _env
96 defp eval_bindings([{:symbol, key}, binding | tail], env) do
97 evaluated = eval(binding, env)
98 Mal.Env.set(env, key, evaluated)
99 eval_bindings(tail, env)
101 defp eval_bindings(_bindings, _env), do: throw({:error, "Unbalanced let
* bindings
"})
103 defp quasiquote({:vector, list}, _env), do: quasiquote(list, _env)
104 defp quasiquote(ast, _env) when not is_list(ast), do: [{:symbol, "quote
"}, ast]
105 defp quasiquote([], _env), do: [{:symbol, "quote
"}, []]
106 defp quasiquote([{:symbol, "unquote
"}, arg], _env), do: arg
107 defp quasiquote([[{:symbol, "splice
-unquote
"}, first] | tail], env) do
108 [{:symbol, "concat
"}, first, quasiquote(tail, env)]
110 defp quasiquote([head | tail], env) do
111 [{:symbol, "cons
"}, quasiquote(head, env), quasiquote(tail, env)]
114 defp macro_call?([{:symbol, key} | _tail], env) do
115 case Mal.Env.get(env, key) do
116 {:ok, {:macro, _}} -> true
120 defp macro_call?(_ast, _env), do: false
122 defp do_macro_call([{:symbol, key} | tail], env) do
123 {:ok, {:macro, macro}} = Mal.Env.get(env, key)
128 def macroexpand(ast, env) do
129 if macro_call?(ast, env) do
130 do_macro_call(ast, env)
136 def eval(ast, env) when not is_list(ast), do: eval_ast(ast, env)
137 def eval(ast, env) when is_list(ast) do
138 case macroexpand(ast, env) do
139 result when is_list(result) -> eval_list(result, env)
144 def eval_list([{:symbol, "macroexpand
"}, ast], env), do: macroexpand(ast, env)
146 def eval_list([{:symbol, "if"}, condition, if_true | if_false], env) do
147 result = eval(condition, env)
148 if result == nil or result == false do
151 [body] -> eval(body, env)
158 def eval_list([{:symbol, "do"} | ast], env) do
159 eval_ast(List.delete_at(ast, -1), env)
160 eval(List.last(ast), env)
163 def eval_list([{:symbol, "def
!"}, {:symbol, key}, value], env) do
164 evaluated = eval(value, env)
165 Mal.Env.set(env, key, evaluated)
169 def eval_list([{:symbol, "defmacro
!"}, {:symbol, key}, function], env) do
170 {:closure, evaluated} = eval(function, env)
171 macro = {:macro, evaluated}
172 Mal.Env.set(env, key, macro)
176 def eval_list([{:symbol, "let
*"}, bindings, body], env) do
177 let_env = Mal.Env.initialize(env)
178 eval_bindings(bindings, let_env)
182 def eval_list([{:symbol, "fn
*"}, {:vector, params}, body], env) do
183 eval_list([{:symbol, "fn
*"}, params, body], env)
185 def eval_list([{:symbol, "fn
*"}, params, body], env) do
186 param_symbols = for {:symbol, symbol} <- params, do: symbol
189 inner = Mal.Env.initialize(env, param_symbols, args)
196 def eval_list([{:symbol, "quote
"}, arg], _env), do: arg
198 def eval_list([{:symbol, "quasiquote
"}, ast], env) do
203 def eval_list(ast, env) do
204 [func | args] = eval_ast(ast, env)
206 {:closure, closure} -> closure.(args)
212 IO.puts(Mal.Printer.print_str(value))
215 def read_eval_print(:eof, _env), do: exit(:normal)
216 def read_eval_print(line, env) do
221 {:error, message} -> IO.puts("Error
: #
{message
}")