1 defmodule Mix
.Tasks
.Step7Quote
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) "\nnil
)")))))
33 Mal
.Env
.set(env
, "eval", %Function
{value: fn
[ast
] ->
39 Mal
.Env
.set(env
, "*ARGV*", list(rest
))
40 load_file(file_name
, env
)
43 Mal
.Env
.set(env
, "*ARGV*", list([]))
48 IO
.write(:stdio
, "user> ")
49 IO
.read(:stdio
, :line
)
50 |
> read_eval_print(env
)
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
}
60 defp
eval_ast({:map
, ast
, meta
}, env
) do
61 map
= for {key
, value} <- ast
, into
: %{} do
62 {eval(key
, env
), eval(value, env
)}
68 defp
eval_ast({:vector
, ast
, meta
}, env
) do
69 {:vector
, Enum
.map(ast
, fn elem
-> eval(elem
, env
) end), meta
}
72 defp
eval_ast({:symbol
, symbol
}, env
) do
73 case Mal
.Env
.get(env
, symbol
) do
75 :not_found
-> throw({:error
, "'#{symbol}' not found"})
79 defp
eval_ast(ast
, _env
), do: ast
82 Mal
.Reader
.read_str(input
)
85 defp
eval_bindings([], env
), do: env
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
)
91 defp
eval_bindings(_bindings
, _env
), do: throw({:error
, "Unbalanced let* bindings"})
93 defp
quasiquote({:list
, [{:symbol
, "unquote"}, arg
], _
}), do: arg
94 defp
quasiquote({:list
, [{:symbol
, "unquote"}| _
], _
}), do: throw({:error
, "unquote: arg count"})
95 defp
quasiquote({:list
, xs
, _
}), do: qq_foldr(xs
)
96 defp
quasiquote({:vector
, xs
, _
}), do: list([{:symbol
, "vec"}, qq_foldr(xs
)])
97 defp
quasiquote({:symbol
, sym
}), do: list([{:symbol
, "quote"}, {:symbol
, sym
}])
98 defp
quasiquote({:map
, ast
, meta
}), do: list([{:symbol
, "quote"}, {:map
, ast
, meta
}])
99 defp
quasiquote(ast
), do: ast
101 defp
qq_foldr([]), do: list([])
102 defp
qq_foldr([x|xs
]), do: qq_loop(x
, qq_foldr xs
)
104 defp
qq_loop({:list
, [{:symbol
, "splice-unquote"}, arg
], _
}, acc
), do: list([{:symbol
, "concat"}, arg
, acc
])
105 defp
qq_loop({:list
, [{:symbol
, "splice-unquote"}| _
], _
}, _
), do: throw({:error
, "splice-unquote: arg count"})
106 defp
qq_loop(elt
, acc
), do: list([{:symbol
, "cons"}, quasiquote(elt
), acc
])
108 defp
eval({:list
, [], _
} = empty_ast
, _env
), do: empty_ast
109 defp
eval({:list
, ast
, meta
}, env
), do: eval_list(ast
, env
, meta
)
110 defp
eval(ast
, env
), do: eval_ast(ast
, env
)
112 defp
eval_list([{:symbol
, "if"}, condition
, if_true | if_false
], env
, _
) do
113 result
= eval(condition
, env
)
114 if result
== nil
or result
== false
do
117 [body
] -> eval(body
, env
)
124 defp
eval_list([{:symbol
, "do"} | ast
], env
, _
) do
126 |
> List
.delete_at(-1)
129 eval(List
.last(ast
), env
)
132 defp
eval_list([{:symbol
, "def!"}, {:symbol
, key
}, value], env
, _
) do
133 evaluated
= eval(value, env
)
134 Mal
.Env
.set(env
, key
, evaluated
)
138 defp
eval_list([{:symbol
, "let*"}, {list_type
, bindings
, _
}, body
], env
, _
)
139 when list_type
== :list
or list_type
== :vector
do
140 let_env
= Mal
.Env
.new(env
)
141 eval_bindings(bindings
, let_env
)
145 defp
eval_list([{:symbol
, "fn*"}, {list_type
, params
, _
}, body
], env
, _
)
146 when list_type
== :list
or list_type
== :vector
do
147 param_symbols
= for {:symbol
, symbol
} <- params
, do: symbol
150 inner
= Mal
.Env
.new(env
, param_symbols
, args
)
154 %Function
{value: closure
}
157 defp
eval_list([{:symbol
, "quote"}, arg
], _env
, _
), do: arg
159 defp
eval_list([{:symbol
, "quasiquoteexpand"}, ast
], _
, _
) do
163 defp
eval_list([{:symbol
, "quasiquote"}, ast
], env
, _
) do
168 defp
eval_list(ast
, env
, meta
) do
169 {:list
, [func | args
], _
} = eval_ast({:list
, ast
, meta
}, env
)
174 Mal
.Printer
.print_str(value)
177 defp
read_eval_print(:eof
, _env
), do: exit(:normal
)
178 defp
read_eval_print(line
, env
) do
183 {:error
, message
} -> IO
.puts("Error: #{message}")