1 defmodule Mix
.Tasks
.Step8Macros
do
7 Mal
.Env
.merge(env
, Mal
.Core
.namespace
)
12 defp
load_file(file_name
, env
) do
14 (load-file "#
{file_name
}")
19 defp
bootstrap(args
, env
) do
23 (fn* (a) (if a false true)))
30 (eval (read-string (str "(do " (slurp f) ")")))))
41 (throw \"odd number of forms to cond\"))
42 (cons 'cond (rest (rest xs)))))))"
53 `
(let
* (or_FIXME ~
(first xs
)) (if or_FIXME
or_FIXME (or ~@
(rest xs
))))))))
56 Mal.Env.set(env, "eval
", %Function{value: fn [ast] ->
62 Mal.Env.set(env, "*ARGV
*", list(rest))
63 load_file(file_name, env)
66 Mal.Env.set(env, "*ARGV
*", list([]))
71 IO.write(:stdio, "user
> ")
72 IO.read(:stdio, :line)
73 |> read_eval_print(env)
79 defp eval_ast({:list, ast, meta}, env) when is_list(ast) do
80 {:list, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
83 defp eval_ast({:map, ast, meta}, env) do
84 map = for {key, value} <- ast, into: %{} do
85 {eval(key, env), eval(value, env)}
91 defp eval_ast({:vector, ast, meta}, env) do
92 {:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
95 defp eval_ast({:symbol, symbol}, env) do
96 case Mal.Env.get(env, symbol) do
98 :not_found -> throw({:error, "'#{symbol}' not found
"})
102 defp eval_ast(ast, _env), do: ast
105 Mal.Reader.read_str(input)
108 defp eval_bindings([], _env), do: _env
109 defp eval_bindings([{:symbol, key}, binding | tail], env) do
110 evaluated = eval(binding, env)
111 Mal.Env.set(env, key, evaluated)
112 eval_bindings(tail, env)
114 defp eval_bindings(_bindings, _env), do: throw({:error, "Unbalanced let
* bindings
"})
116 defp quasi_list([], _env), do: list([{:symbol, "quote
"}, list([])])
117 defp quasi_list([{:symbol, "unquote
"}, arg], _env), do: arg
118 defp quasi_list([{:list, [{:symbol, "splice
-unquote
"}, first], _meta} | tail], env) do
123 list([{:symbol, "concat
"}, first, right])
125 defp quasi_list([head | tail], env) do
126 left = quasiquote(head, env)
131 list([{:symbol, "cons
"}, left, right])
134 defp quasiquote({list_type, ast, _}, env)
135 when list_type in [:list, :vector] do
138 defp quasiquote(ast, _env), do: list([{:symbol, "quote
"}, ast])
140 defp macro_call?({:list, [{:symbol, key} | _tail], _}, env) do
141 case Mal.Env.get(env, key) do
142 {:ok, %Function{macro: true}} -> true
146 defp macro_call?(_ast, _env), do: false
148 defp do_macro_call({:list, [{:symbol, key} | tail], _}, env) do
149 {:ok, %Function{value: macro, macro: true}} = Mal.Env.get(env, key)
154 defp macroexpand(ast, env) do
155 if macro_call?(ast, env) do
156 do_macro_call(ast, env)
162 defp eval({:list, _list, _meta} = ast, env) do
163 case macroexpand(ast, env) do
164 {:list, list, meta} -> eval_list(list, env, meta)
168 defp eval(ast, env), do: eval_ast(ast, env)
170 defp eval_list([{:symbol, "macroexpand
"}, ast], env, _), do: macroexpand(ast, env)
172 defp eval_list([{:symbol, "if"}, condition, if_true | if_false], env, _) do
173 result = eval(condition, env)
174 if result == nil or result == false do
177 [body] -> eval(body, env)
184 defp eval_list([{:symbol, "do"} | ast], env, _) do
186 |> List.delete_at(-1)
189 eval(List.last(ast), env)
192 defp eval_list([{:symbol, "def
!"}, {:symbol, key}, value], env, _) do
193 evaluated = eval(value, env)
194 Mal.Env.set(env, key, evaluated)
198 defp eval_list([{:symbol, "defmacro
!"}, {:symbol, key}, function], env, _) do
199 macro = %{eval(function, env) | macro: true}
200 Mal.Env.set(env, key, macro)
204 defp eval_list([{:symbol, "let
*"}, {list_type, bindings, _}, body], env, _)
205 when list_type == :list or list_type == :vector do
206 let_env = Mal.Env.new(env)
207 eval_bindings(bindings, let_env)
211 defp eval_list([{:symbol, "fn
*"}, {list_type, params, _}, body], env, _)
212 when list_type == :list or list_type == :vector do
213 param_symbols = for {:symbol, symbol} <- params, do: symbol
216 inner = Mal.Env.new(env, param_symbols, args)
220 %Function{value: closure}
223 defp eval_list([{:symbol, "quote
"}, arg], _env, _), do: arg
225 defp eval_list([{:symbol, "quasiquote
"}, ast], env, _) do
230 defp eval_list(ast, env, meta) do
231 {:list, [func | args], _} = eval_ast({:list, ast, meta}, env)
236 Mal.Printer.print_str(value)
239 defp read_eval_print(:eof, _env), do: exit(:normal)
240 defp read_eval_print(line, env) do
245 {:error, message} -> IO.puts("Error
: #
{message
}")