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