98ea77cf |
1 | defmodule Mix.Tasks.Step4IfFnDo do |
98ea77cf |
2 | def run(_) do |
a72291eb |
3 | env = Mal.Env.initialize() |
fffde7d7 |
4 | Mal.Env.merge(env, Mal.Core.namespace) |
a355b219 |
5 | bootstrap(env) |
98ea77cf |
6 | main(env) |
7 | end |
8 | |
a355b219 |
9 | def bootstrap(env) do |
10 | read_eval_print("(def! not (fn* (a) (if a false true)))", env) |
11 | end |
12 | |
98ea77cf |
13 | def main(env) do |
14 | IO.write(:stdio, "user> ") |
15 | IO.read(:stdio, :line) |
16 | |> read_eval_print(env) |
17 | |
18 | main(env) |
19 | end |
20 | |
21 | def eval_ast(ast, env) when is_list(ast) do |
22 | Enum.map(ast, fn elem -> eval(elem, env) end) |
23 | end |
24 | |
25 | def eval_ast({:symbol, symbol}, env) do |
26 | case Mal.Env.get(env, symbol) do |
27 | {:ok, value} -> value |
28 | :not_found -> throw({:error, "invalid symbol #{symbol}"}) |
29 | end |
30 | end |
31 | |
32 | def eval_ast(ast, _env), do: ast |
33 | |
34 | def read(input) do |
35 | Mal.Reader.read_str(input) |
36 | end |
37 | |
38 | defp eval_bindings([], _env), do: _env |
39 | defp eval_bindings([{:symbol, key}, binding | tail], env) do |
40 | evaluated = eval(binding, env) |
41 | Mal.Env.set(env, key, evaluated) |
42 | eval_bindings(tail, env) |
43 | end |
44 | defp eval_bindings(_bindings, _env), do: throw({:error, "Unbalanced let* bindings"}) |
45 | |
880cc6f4 |
46 | def eval([{:symbol, "if"}, condition, if_true | if_false], env) do |
47 | result = eval(condition, env) |
48 | if result == nil or result == false do |
d88bc831 |
49 | case if_false do |
50 | [] -> nil |
51 | [body] -> eval(body, env) |
52 | end |
880cc6f4 |
53 | else |
54 | eval(if_true, env) |
55 | end |
56 | end |
57 | |
98ea77cf |
58 | def eval([{:symbol, "do"} | ast], env) do |
d88bc831 |
59 | eval_ast(List.delete_at(ast, -1), env) |
60 | eval(List.last(ast), env) |
98ea77cf |
61 | end |
62 | |
63 | def eval([{:symbol, "def!"}, {:symbol, key}, value], env) do |
64 | evaluated = eval(value, env) |
65 | Mal.Env.set(env, key, evaluated) |
66 | evaluated |
67 | end |
68 | |
69 | def eval([{:symbol, "let*"}, bindings, body], env) do |
a72291eb |
70 | let_env = Mal.Env.initialize(env) |
98ea77cf |
71 | eval_bindings(bindings, let_env) |
72 | eval(body, let_env) |
73 | end |
74 | |
a72291eb |
75 | def eval([{:symbol, "fn*"}, params, body], env) do |
76 | param_symbols = for {:symbol, symbol} <- params, do: symbol |
77 | |
78 | closure = fn args -> |
79 | inner = Mal.Env.initialize(env, param_symbols, args) |
80 | eval(body, inner) |
81 | end |
82 | |
83 | {:closure, closure} |
84 | end |
85 | |
98ea77cf |
86 | def eval(ast, env) when is_list(ast) do |
87 | [func | args] = eval_ast(ast, env) |
a72291eb |
88 | case func do |
89 | {:closure, closure} -> closure.(args) |
fffde7d7 |
90 | _ -> func.(args) |
a72291eb |
91 | end |
98ea77cf |
92 | end |
93 | |
94 | def eval(ast, env), do: eval_ast(ast, env) |
95 | |
96 | def print(value) do |
97 | IO.puts(Mal.Printer.print_str(value)) |
98 | end |
99 | |
100 | def read_eval_print(:eof, _env), do: exit(0) |
101 | def read_eval_print(line, env) do |
102 | read(line) |
103 | |> eval(env) |
104 | |> print |
105 | catch |
106 | {:error, message} -> IO.puts("Error: #{message}") |
107 | end |
108 | end |