1 defmodule Mix
.Tasks
.StepAMal
do
5 #
for escript execution
12 Mal
.Env
.merge(env
, Mal
.Core
.namespace
)
17 defp
load_file(file_name
, env
) do
19 (load-file "#
{file_name
}")
24 defp
bootstrap(args
, env
) do
26 read_eval_print("(def! *host-language* \"Elixir\")", env
)
31 (fn* (a) (if a false true)))
38 (eval (read-string (str "(do " (slurp f) "\nnil
)")))))
49 (throw \"odd number of forms to cond\"))
50 (cons 'cond (rest (rest xs)))))))"
53 Mal.Env.set(env, "eval
", %Function{value: fn [ast] ->
59 Mal.Env.set(env, "*ARGV
*", list(rest))
60 load_file(file_name, env)
63 Mal.Env.set(env, "*ARGV
*", list([]))
64 read_eval_print("(println (str
\"Mal
[\" *host
-language
* \"]\"))", env)
69 IO.write(:stdio, "user
> ")
70 IO.read(:stdio, :line)
71 |> read_eval_print(env)
77 defp eval_ast({:list, ast, meta}, env) when is_list(ast) do
78 {:list, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
81 defp eval_ast({:map, ast, meta}, env) do
82 map = for {key, value} <- ast, into: %{} do
83 {eval(key, env), eval(value, env)}
89 defp eval_ast({:vector, ast, meta}, env) do
90 {:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
93 defp eval_ast({:symbol, symbol}, env) do
94 case Mal.Env.get(env, symbol) do
96 :not_found -> throw({:error, "'#{symbol}' not found
"})
100 defp eval_ast(ast, _env), do: ast
103 Mal.Reader.read_str(input)
106 defp eval_bindings([], env), do: env
107 defp eval_bindings([{:symbol, key}, binding | tail], env) do
108 evaluated = eval(binding, env)
109 Mal.Env.set(env, key, evaluated)
110 eval_bindings(tail, env)
112 defp eval_bindings(_bindings, _env), do: throw({:error, "Unbalanced let
* bindings
"})
114 defp quasi_list([], _env), do: list([{:symbol, "quote
"}, list([])])
115 defp quasi_list([{:symbol, "unquote
"}, arg], _env), do: arg
116 defp quasi_list([{:list, [{:symbol, "splice
-unquote
"}, first], _meta} | tail], env) do
121 list([{:symbol, "concat
"}, first, right])
123 defp quasi_list([head | tail], env) do
124 left = quasiquote(head, env)
129 list([{:symbol, "cons
"}, left, right])
132 defp quasiquote({list_type, ast, _}, env)
133 when list_type in [:list, :vector] do
136 defp quasiquote(ast, _env), do: list([{:symbol, "quote
"}, ast])
138 defp macro_call?({:list, [{:symbol, key} | _tail], _}, env) do
139 case Mal.Env.get(env, key) do
140 {:ok, %Function{macro: true}} -> true
144 defp macro_call?(_ast, _env), do: false
146 defp do_macro_call({:list, [{:symbol, key} | tail], _}, env) do
147 {:ok, %Function{value: macro, macro: true}} = Mal.Env.get(env, key)
152 defp macroexpand(ast, env) do
153 if macro_call?(ast, env) do
154 do_macro_call(ast, env)
160 defp eval({:list, [], _} = empty_ast, _env), do: empty_ast
161 defp eval({:list, _list, _meta} = ast, env) do
162 case macroexpand(ast, env) do
163 {:list, list, meta} -> eval_list(list, env, meta)
164 result -> eval_ast(result, env)
167 defp eval(ast, env), do: eval_ast(ast, env)
169 defp eval_list([{:symbol, "macroexpand
"}, ast], env, _), do: macroexpand(ast, env)
171 defp eval_list([{:symbol, "if"}, condition, if_true | if_false], env, _) do
172 result = eval(condition, env)
173 if result == nil or result == false do
176 [body] -> eval(body, env)
183 defp eval_list([{:symbol, "do"} | ast], env, _) do
185 |> List.delete_at(-1)
188 eval(List.last(ast), env)
191 defp eval_list([{:symbol, "def
!"}, {:symbol, key}, value], env, _) do
192 evaluated = eval(value, env)
193 Mal.Env.set(env, key, evaluated)
197 defp eval_list([{:symbol, "defmacro
!"}, {:symbol, key}, function], env, _) do
198 macro = %{eval(function, env) | macro: true}
199 Mal.Env.set(env, key, macro)
203 defp eval_list([{:symbol, "let
*"}, {list_type, bindings, _}, body], env, _)
204 when list_type == :list or list_type == :vector do
205 let_env = Mal.Env.new(env)
206 eval_bindings(bindings, let_env)
210 defp eval_list([{:symbol, "fn
*"}, {list_type, params, _}, body], env, _)
211 when list_type == :list or list_type == :vector do
212 param_symbols = for {:symbol, symbol} <- params, do: symbol
215 inner = Mal.Env.new(env, param_symbols, args)
219 %Function{value: closure}
222 defp eval_list([{:symbol, "quote
"}, arg], _env, _), do: arg
224 defp eval_list([{:symbol, "quasiquote
"}, ast], env, _) do
229 # (try* A (catch* B C))
230 defp eval_list([{:symbol, "try
*"}, try_form, {:list, catch_list, _meta}], env, _) do
231 eval_try(try_form, catch_list, env)
233 defp eval_list([{:symbol, "try
*"}, try_form], env, _) do
236 defp eval_list([{:symbol, "try
*"}, _try_form, _], _env, _) do
237 throw({:error, "try
* requires a list as the second parameter
"})
240 defp eval_list(ast, env, meta) do
241 {:list, [func | args], _} = eval_ast({:list, ast, meta}, env)
245 defp eval_try(try_form,
246 [{:symbol, "catch
*"}, {:symbol, exception}, catch_form], env) do
251 catch_env = Mal.Env.new(env)
252 Mal.Env.set(catch_env, exception, {:exception, message})
253 eval(catch_form, catch_env)
256 defp eval_try(_try_form, _catch_list, _env) do
257 throw({:error, "catch
* requires two arguments
"})
261 Mal.Printer.print_str(value)
264 defp read_eval_print(:eof, _env), do: exit(:normal)
265 defp read_eval_print(line, env) do
270 {:error, exception} ->
271 IO.puts("Error
: #
{Mal
.Printer
.print_str(exception
)}")