ada.2: typo
[jackhill/mal.git] / impls / elixir / lib / mix / tasks / step7_quote.ex
CommitLineData
4115c430 1defmodule Mix.Tasks.Step7Quote do
eccae82a 2 import Mal.Types
3 alias Mal.Function
4
4115c430 5 def run(args) do
e497903f 6 env = Mal.Env.new()
4115c430 7 Mal.Env.merge(env, Mal.Core.namespace)
8 bootstrap(args, env)
bd0c3504 9 loop(env)
4115c430 10 end
11
230dc1aa 12 defp load_file(file_name, env) do
4115c430 13 read_eval_print("""
14 (load-file "#{file_name}")
15 """, env)
16 exit(:normal)
17 end
18
19 defp bootstrap(args, env) do
20 # not:
21 read_eval_print("""
22 (def! not
23 (fn* (a) (if a false true)))
24 """, env)
25
26 # load-file:
27 read_eval_print("""
28 (def! load-file
29 (fn* (f)
e6d41de4 30 (eval (read-string (str "(do " (slurp f) "\nnil)")))))
4115c430 31 """, env)
32
6c7c9e6f 33 Mal.Env.set(env, "eval", %Function{value: fn [ast] ->
4115c430 34 eval(ast, env)
6c7c9e6f 35 end})
4115c430 36
37 case args do
230dc1aa 38 [file_name | rest] ->
39 Mal.Env.set(env, "*ARGV*", list(rest))
40 load_file(file_name, env)
41
42 [] ->
43 Mal.Env.set(env, "*ARGV*", list([]))
4115c430 44 end
45 end
46
bd0c3504 47 defp loop(env) do
eccae82a 48 IO.write(:stdio, "user> ")
49 IO.read(:stdio, :line)
4115c430 50 |> read_eval_print(env)
bd0c3504 51 |> IO.puts
4115c430 52
bd0c3504 53 loop(env)
4115c430 54 end
55
eccae82a 56 defp eval_ast({:list, ast, meta}, env) when is_list(ast) do
57 {:list, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
4115c430 58 end
59
eccae82a 60 defp eval_ast({:map, ast, meta}, env) do
61 map = for {key, value} <- ast, into: %{} do
4f16e21e 62 {eval(key, env), eval(value, env)}
63 end
eccae82a 64
65 {:map, map, meta}
4f16e21e 66 end
67
eccae82a 68 defp eval_ast({:vector, ast, meta}, env) do
69 {:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
1bc4ac2b 70 end
71
1cd421f4 72 defp eval_ast({:symbol, symbol}, env) do
4115c430 73 case Mal.Env.get(env, symbol) do
74 {:ok, value} -> value
eccae82a 75 :not_found -> throw({:error, "'#{symbol}' not found"})
4115c430 76 end
77 end
78
1cd421f4 79 defp eval_ast(ast, _env), do: ast
4115c430 80
1cd421f4 81 defp read(input) do
4115c430 82 Mal.Reader.read_str(input)
83 end
84
e68e138f 85 defp eval_bindings([], env), do: env
4115c430 86 defp eval_bindings([{:symbol, key}, binding | tail], env) do
87 evaluated = eval(binding, env)
88 Mal.Env.set(env, key, evaluated)
89 eval_bindings(tail, env)
90 end
91 defp eval_bindings(_bindings, _env), do: throw({:error, "Unbalanced let* bindings"})
92
eccae82a 93 defp quasi_list([], _env), do: list([{:symbol, "quote"}, list([])])
94 defp quasi_list([{:symbol, "unquote"}, arg], _env), do: arg
95 defp quasi_list([{:list, [{:symbol, "splice-unquote"}, first], _meta} | tail], env) do
96 right = tail
97 |> list
98 |> quasiquote(env)
99
100 list([{:symbol, "concat"}, first, right])
0568566f 101 end
eccae82a 102 defp quasi_list([head | tail], env) do
103 left = quasiquote(head, env)
104 right = tail
105 |> list
106 |> quasiquote(env)
107
108 list([{:symbol, "cons"}, left, right])
109 end
110
111 defp quasiquote({list_type, ast, _}, env)
112 when list_type in [:list, :vector] do
113 quasi_list(ast, env)
0568566f 114 end
eccae82a 115 defp quasiquote(ast, _env), do: list([{:symbol, "quote"}, ast])
0568566f 116
e68e138f 117 defp eval({:list, [], _} = empty_ast, _env), do: empty_ast
eccae82a 118 defp eval({:list, ast, meta}, env), do: eval_list(ast, env, meta)
119 defp eval(ast, env), do: eval_ast(ast, env)
120
121 defp eval_list([{:symbol, "if"}, condition, if_true | if_false], env, _) do
4115c430 122 result = eval(condition, env)
123 if result == nil or result == false do
124 case if_false do
125 [] -> nil
126 [body] -> eval(body, env)
127 end
128 else
129 eval(if_true, env)
130 end
131 end
132
eccae82a 133 defp eval_list([{:symbol, "do"} | ast], env, _) do
134 ast
135 |> List.delete_at(-1)
136 |> list
137 |> eval_ast(env)
4115c430 138 eval(List.last(ast), env)
139 end
140
eccae82a 141 defp eval_list([{:symbol, "def!"}, {:symbol, key}, value], env, _) do
4115c430 142 evaluated = eval(value, env)
143 Mal.Env.set(env, key, evaluated)
144 evaluated
145 end
146
eccae82a 147 defp eval_list([{:symbol, "let*"}, {list_type, bindings, _}, body], env, _)
148 when list_type == :list or list_type == :vector do
e497903f 149 let_env = Mal.Env.new(env)
4115c430 150 eval_bindings(bindings, let_env)
151 eval(body, let_env)
152 end
153
eccae82a 154 defp eval_list([{:symbol, "fn*"}, {list_type, params, _}, body], env, _)
155 when list_type == :list or list_type == :vector do
4115c430 156 param_symbols = for {:symbol, symbol} <- params, do: symbol
157
158 closure = fn args ->
e497903f 159 inner = Mal.Env.new(env, param_symbols, args)
4115c430 160 eval(body, inner)
161 end
162
eccae82a 163 %Function{value: closure}
4115c430 164 end
165
eccae82a 166 defp eval_list([{:symbol, "quote"}, arg], _env, _), do: arg
0568566f 167
eccae82a 168 defp eval_list([{:symbol, "quasiquote"}, ast], env, _) do
0568566f 169 quasiquote(ast, env)
170 |> eval(env)
171 end
172
eccae82a 173 defp eval_list(ast, env, meta) do
174 {:list, [func | args], _} = eval_ast({:list, ast, meta}, env)
6c7c9e6f 175 func.value.(args)
4115c430 176 end
177
1cd421f4 178 defp print(value) do
bd0c3504 179 Mal.Printer.print_str(value)
4115c430 180 end
181
1cd421f4 182 defp read_eval_print(:eof, _env), do: exit(:normal)
183 defp read_eval_print(line, env) do
4115c430 184 read(line)
185 |> eval(env)
186 |> print
187 catch
188 {:error, message} -> IO.puts("Error: #{message}")
189 end
190end