mirror of
https://github.com/kanaka/mal.git
synced 2024-11-13 01:43:50 +03:00
Use the new data structures in the other steps
This commit is contained in:
parent
9dd9dd2f59
commit
eccae82ac6
@ -9,31 +9,34 @@ defmodule Mix.Tasks.Step2Eval do
|
||||
def run(_), do: loop
|
||||
|
||||
defp loop do
|
||||
Mal.Core.readline("user> ")
|
||||
IO.write(:stdio, "user> ")
|
||||
IO.read(:stdio, :line)
|
||||
|> read_eval_print
|
||||
|> IO.puts
|
||||
|
||||
loop
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_list(ast) do
|
||||
Enum.map(ast, fn elem -> eval(elem, env) end)
|
||||
defp eval_ast({:list, ast, meta}, env) when is_list(ast) do
|
||||
{:list, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:vector, ast}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end)}
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_map(ast) do
|
||||
for {key, value} <- ast, into: %{} do
|
||||
defp eval_ast({:map, ast, meta}, env) do
|
||||
map = for {key, value} <- ast, into: %{} do
|
||||
{eval(key, env), eval(value, env)}
|
||||
end
|
||||
|
||||
{:map, map, meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:vector, ast, meta}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:symbol, symbol}, env) do
|
||||
case Map.fetch(env, symbol) do
|
||||
{:ok, value} -> value
|
||||
:error -> throw({:error, "invalid symbol #{symbol}"})
|
||||
:error -> throw({:error, "'#{symbol}' not found"})
|
||||
end
|
||||
end
|
||||
|
||||
@ -43,13 +46,14 @@ defmodule Mix.Tasks.Step2Eval do
|
||||
Mal.Reader.read_str(input)
|
||||
end
|
||||
|
||||
defp eval(ast, env) when is_list(ast) do
|
||||
[func | args] = eval_ast(ast, env)
|
||||
defp eval({:list, ast, meta}, env), do: eval_list(ast, env, meta)
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp eval_list(ast, env, meta) do
|
||||
{:list, [func | args], _} = eval_ast({:list, ast, meta}, env)
|
||||
apply(func, args)
|
||||
end
|
||||
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp print(value) do
|
||||
Mal.Printer.print_str(value)
|
||||
end
|
||||
|
@ -1,4 +1,7 @@
|
||||
defmodule Mix.Tasks.Step3Env do
|
||||
import Mal.Types
|
||||
alias Mal.Function
|
||||
|
||||
@initial_env %{
|
||||
"+" => &+/2,
|
||||
"-" => &-/2,
|
||||
@ -13,31 +16,34 @@ defmodule Mix.Tasks.Step3Env do
|
||||
end
|
||||
|
||||
defp loop(env) do
|
||||
Mal.Core.readline("user> ")
|
||||
IO.write(:stdio, "user> ")
|
||||
IO.read(:stdio, :line)
|
||||
|> read_eval_print(env)
|
||||
|> IO.puts
|
||||
|
||||
loop(env)
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_list(ast) do
|
||||
Enum.map(ast, fn elem -> eval(elem, env) end)
|
||||
defp eval_ast({:list, ast, meta}, env) when is_list(ast) do
|
||||
{:list, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_map(ast) do
|
||||
for {key, value} <- ast, into: %{} do
|
||||
defp eval_ast({:map, ast, meta}, env) do
|
||||
map = for {key, value} <- ast, into: %{} do
|
||||
{eval(key, env), eval(value, env)}
|
||||
end
|
||||
|
||||
{:map, map, meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:vector, ast}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end)}
|
||||
defp eval_ast({:vector, ast, meta}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:symbol, symbol}, env) do
|
||||
case Mal.Env.get(env, symbol) do
|
||||
{:ok, value} -> value
|
||||
:not_found -> throw({:error, "invalid symbol #{symbol}"})
|
||||
:not_found -> throw({:error, "'#{symbol}' not found"})
|
||||
end
|
||||
end
|
||||
|
||||
@ -47,7 +53,6 @@ defmodule Mix.Tasks.Step3Env do
|
||||
Mal.Reader.read_str(input)
|
||||
end
|
||||
|
||||
defp eval_bindings({:vector, vector}, env), do: eval_bindings(vector, env)
|
||||
defp eval_bindings([], _env), do: _env
|
||||
defp eval_bindings([{:symbol, key}, binding | tail], env) do
|
||||
evaluated = eval(binding, env)
|
||||
@ -56,26 +61,27 @@ defmodule Mix.Tasks.Step3Env do
|
||||
end
|
||||
defp eval_bindings(_bindings, _env), do: throw({:error, "Unbalanced let* bindings"})
|
||||
|
||||
defp eval({:list, ast, meta}, env), do: eval_list(ast, env, meta)
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp eval([{:symbol, "def!"}, {:symbol, key}, value], env) do
|
||||
defp eval_list([{:symbol, "def!"}, {:symbol, key}, value], env, _) do
|
||||
evaluated = eval(value, env)
|
||||
Mal.Env.set(env, key, evaluated)
|
||||
evaluated
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "let*"}, bindings, body], env) do
|
||||
defp eval_list([{:symbol, "let*"}, {list_type, bindings, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
let_env = Mal.Env.initialize(env)
|
||||
eval_bindings(bindings, let_env)
|
||||
eval(body, let_env)
|
||||
end
|
||||
|
||||
defp eval(ast, env) when is_list(ast) do
|
||||
[func | args] = eval_ast(ast, env)
|
||||
defp eval_list(ast, env, meta) do
|
||||
{:list, [func | args], _} = eval_ast({:list, ast, meta}, env)
|
||||
apply(func, args)
|
||||
end
|
||||
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp print(value) do
|
||||
Mal.Printer.print_str(value)
|
||||
end
|
||||
|
@ -1,4 +1,7 @@
|
||||
defmodule Mix.Tasks.Step4IfFnDo do
|
||||
import Mal.Types
|
||||
alias Mal.Function
|
||||
|
||||
def run(_) do
|
||||
env = Mal.Env.initialize()
|
||||
Mal.Env.merge(env, Mal.Core.namespace)
|
||||
@ -7,35 +10,46 @@ defmodule Mix.Tasks.Step4IfFnDo do
|
||||
end
|
||||
|
||||
defp bootstrap(env) do
|
||||
read_eval_print("(def! not (fn* (a) (if a false true)))", env)
|
||||
# not:
|
||||
read_eval_print("""
|
||||
(def! not
|
||||
(fn* (a) (if a false true)))
|
||||
""", env)
|
||||
|
||||
Mal.Env.set(env, "eval", fn [ast] ->
|
||||
eval(ast, env)
|
||||
end)
|
||||
end
|
||||
|
||||
defp loop(env) do
|
||||
Mal.Core.readline("user> ")
|
||||
IO.write(:stdio, "user> ")
|
||||
IO.read(:stdio, :line)
|
||||
|> read_eval_print(env)
|
||||
|> IO.puts
|
||||
|
||||
loop(env)
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_list(ast) do
|
||||
Enum.map(ast, fn elem -> eval(elem, env) end)
|
||||
defp eval_ast({:list, ast, meta}, env) when is_list(ast) do
|
||||
{:list, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_map(ast) do
|
||||
for {key, value} <- ast, into: %{} do
|
||||
defp eval_ast({:map, ast, meta}, env) do
|
||||
map = for {key, value} <- ast, into: %{} do
|
||||
{eval(key, env), eval(value, env)}
|
||||
end
|
||||
|
||||
{:map, map, meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:vector, ast}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end)}
|
||||
defp eval_ast({:vector, ast, meta}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:symbol, symbol}, env) do
|
||||
case Mal.Env.get(env, symbol) do
|
||||
{:ok, value} -> value
|
||||
:not_found -> throw({:error, "invalid symbol #{symbol}"})
|
||||
:not_found -> throw({:error, "'#{symbol}' not found"})
|
||||
end
|
||||
end
|
||||
|
||||
@ -45,7 +59,6 @@ defmodule Mix.Tasks.Step4IfFnDo do
|
||||
Mal.Reader.read_str(input)
|
||||
end
|
||||
|
||||
defp eval_bindings({:vector, vector}, env), do: eval_bindings(vector, env)
|
||||
defp eval_bindings([], _env), do: _env
|
||||
defp eval_bindings([{:symbol, key}, binding | tail], env) do
|
||||
evaluated = eval(binding, env)
|
||||
@ -54,7 +67,10 @@ defmodule Mix.Tasks.Step4IfFnDo do
|
||||
end
|
||||
defp eval_bindings(_bindings, _env), do: throw({:error, "Unbalanced let* bindings"})
|
||||
|
||||
defp eval([{:symbol, "if"}, condition, if_true | if_false], env) do
|
||||
defp eval({:list, ast, meta}, env), do: eval_list(ast, env, meta)
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp eval_list([{:symbol, "if"}, condition, if_true | if_false], env, _) do
|
||||
result = eval(condition, env)
|
||||
if result == nil or result == false do
|
||||
case if_false do
|
||||
@ -66,27 +82,29 @@ defmodule Mix.Tasks.Step4IfFnDo do
|
||||
end
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "do"} | ast], env) do
|
||||
eval_ast(List.delete_at(ast, -1), env)
|
||||
defp eval_list([{:symbol, "do"} | ast], env, _) do
|
||||
ast
|
||||
|> List.delete_at(-1)
|
||||
|> list
|
||||
|> eval_ast(env)
|
||||
eval(List.last(ast), env)
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "def!"}, {:symbol, key}, value], env) do
|
||||
defp eval_list([{:symbol, "def!"}, {:symbol, key}, value], env, _) do
|
||||
evaluated = eval(value, env)
|
||||
Mal.Env.set(env, key, evaluated)
|
||||
evaluated
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "let*"}, bindings, body], env) do
|
||||
defp eval_list([{:symbol, "let*"}, {list_type, bindings, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
let_env = Mal.Env.initialize(env)
|
||||
eval_bindings(bindings, let_env)
|
||||
eval(body, let_env)
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "fn*"}, {:vector, params}, body], env) do
|
||||
eval([{:symbol, "fn*"}, params, body], env)
|
||||
end
|
||||
defp eval([{:symbol, "fn*"}, params, body], env) do
|
||||
defp eval_list([{:symbol, "fn*"}, {list_type, params, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
param_symbols = for {:symbol, symbol} <- params, do: symbol
|
||||
|
||||
closure = fn args ->
|
||||
@ -94,19 +112,17 @@ defmodule Mix.Tasks.Step4IfFnDo do
|
||||
eval(body, inner)
|
||||
end
|
||||
|
||||
{:closure, closure}
|
||||
%Function{value: closure}
|
||||
end
|
||||
|
||||
defp eval(ast, env) when is_list(ast) do
|
||||
[func | args] = eval_ast(ast, env)
|
||||
defp eval_list(ast, env, meta) do
|
||||
{:list, [func | args], _} = eval_ast({:list, ast, meta}, env)
|
||||
case func do
|
||||
{:closure, closure} -> closure.(args)
|
||||
%Function{value: closure} -> closure.(args)
|
||||
_ -> func.(args)
|
||||
end
|
||||
end
|
||||
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp print(value) do
|
||||
Mal.Printer.print_str(value)
|
||||
end
|
||||
|
@ -1,4 +1,7 @@
|
||||
defmodule Mix.Tasks.Step5Tco do
|
||||
import Mal.Types
|
||||
alias Mal.Function
|
||||
|
||||
def run(_) do
|
||||
env = Mal.Env.initialize()
|
||||
Mal.Env.merge(env, Mal.Core.namespace)
|
||||
@ -7,35 +10,46 @@ defmodule Mix.Tasks.Step5Tco do
|
||||
end
|
||||
|
||||
defp bootstrap(env) do
|
||||
read_eval_print("(def! not (fn* (a) (if a false true)))", env)
|
||||
# not:
|
||||
read_eval_print("""
|
||||
(def! not
|
||||
(fn* (a) (if a false true)))
|
||||
""", env)
|
||||
|
||||
Mal.Env.set(env, "eval", fn [ast] ->
|
||||
eval(ast, env)
|
||||
end)
|
||||
end
|
||||
|
||||
defp loop(env) do
|
||||
Mal.Core.readline("user> ")
|
||||
IO.write(:stdio, "user> ")
|
||||
IO.read(:stdio, :line)
|
||||
|> read_eval_print(env)
|
||||
|> IO.puts
|
||||
|
||||
loop(env)
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_list(ast) do
|
||||
Enum.map(ast, fn elem -> eval(elem, env) end)
|
||||
defp eval_ast({:list, ast, meta}, env) when is_list(ast) do
|
||||
{:list, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_map(ast) do
|
||||
for {key, value} <- ast, into: %{} do
|
||||
defp eval_ast({:map, ast, meta}, env) do
|
||||
map = for {key, value} <- ast, into: %{} do
|
||||
{eval(key, env), eval(value, env)}
|
||||
end
|
||||
|
||||
{:map, map, meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:vector, ast}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end)}
|
||||
defp eval_ast({:vector, ast, meta}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:symbol, symbol}, env) do
|
||||
case Mal.Env.get(env, symbol) do
|
||||
{:ok, value} -> value
|
||||
:not_found -> throw({:error, "invalid symbol #{symbol}"})
|
||||
:not_found -> throw({:error, "'#{symbol}' not found"})
|
||||
end
|
||||
end
|
||||
|
||||
@ -45,7 +59,6 @@ defmodule Mix.Tasks.Step5Tco do
|
||||
Mal.Reader.read_str(input)
|
||||
end
|
||||
|
||||
defp eval_bindings({:vector, vector}, env), do: eval_bindings(vector, env)
|
||||
defp eval_bindings([], _env), do: _env
|
||||
defp eval_bindings([{:symbol, key}, binding | tail], env) do
|
||||
evaluated = eval(binding, env)
|
||||
@ -54,7 +67,10 @@ defmodule Mix.Tasks.Step5Tco do
|
||||
end
|
||||
defp eval_bindings(_bindings, _env), do: throw({:error, "Unbalanced let* bindings"})
|
||||
|
||||
defp eval([{:symbol, "if"}, condition, if_true | if_false], env) do
|
||||
defp eval({:list, ast, meta}, env), do: eval_list(ast, env, meta)
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp eval_list([{:symbol, "if"}, condition, if_true | if_false], env, _) do
|
||||
result = eval(condition, env)
|
||||
if result == nil or result == false do
|
||||
case if_false do
|
||||
@ -66,27 +82,29 @@ defmodule Mix.Tasks.Step5Tco do
|
||||
end
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "do"} | ast], env) do
|
||||
eval_ast(List.delete_at(ast, -1), env)
|
||||
defp eval_list([{:symbol, "do"} | ast], env, _) do
|
||||
ast
|
||||
|> List.delete_at(-1)
|
||||
|> list
|
||||
|> eval_ast(env)
|
||||
eval(List.last(ast), env)
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "def!"}, {:symbol, key}, value], env) do
|
||||
defp eval_list([{:symbol, "def!"}, {:symbol, key}, value], env, _) do
|
||||
evaluated = eval(value, env)
|
||||
Mal.Env.set(env, key, evaluated)
|
||||
evaluated
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "let*"}, bindings, body], env) do
|
||||
defp eval_list([{:symbol, "let*"}, {list_type, bindings, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
let_env = Mal.Env.initialize(env)
|
||||
eval_bindings(bindings, let_env)
|
||||
eval(body, let_env)
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "fn*"}, {:vector, params}, body], env) do
|
||||
eval([{:symbol, "fn*"}, params, body], env)
|
||||
end
|
||||
defp eval([{:symbol, "fn*"}, params, body], env) do
|
||||
defp eval_list([{:symbol, "fn*"}, {list_type, params, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
param_symbols = for {:symbol, symbol} <- params, do: symbol
|
||||
|
||||
closure = fn args ->
|
||||
@ -94,19 +112,17 @@ defmodule Mix.Tasks.Step5Tco do
|
||||
eval(body, inner)
|
||||
end
|
||||
|
||||
{:closure, closure}
|
||||
%Function{value: closure}
|
||||
end
|
||||
|
||||
defp eval(ast, env) when is_list(ast) do
|
||||
[func | args] = eval_ast(ast, env)
|
||||
defp eval_list(ast, env, meta) do
|
||||
{:list, [func | args], _} = eval_ast({:list, ast, meta}, env)
|
||||
case func do
|
||||
{:closure, closure} -> closure.(args)
|
||||
%Function{value: closure} -> closure.(args)
|
||||
_ -> func.(args)
|
||||
end
|
||||
end
|
||||
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp print(value) do
|
||||
Mal.Printer.print_str(value)
|
||||
end
|
||||
|
@ -1,4 +1,7 @@
|
||||
defmodule Mix.Tasks.Step6File do
|
||||
import Mal.Types
|
||||
alias Mal.Function
|
||||
|
||||
def run(args) do
|
||||
env = Mal.Env.initialize()
|
||||
Mal.Env.merge(env, Mal.Core.namespace)
|
||||
@ -8,7 +11,7 @@ defmodule Mix.Tasks.Step6File do
|
||||
end
|
||||
|
||||
defp load_file([], _env), do: nil
|
||||
defp load_file([file_name | args], env) do
|
||||
defp load_file([file_name | _args], env) do
|
||||
read_eval_print("""
|
||||
(load-file "#{file_name}")
|
||||
""", env)
|
||||
@ -34,37 +37,40 @@ defmodule Mix.Tasks.Step6File do
|
||||
end)
|
||||
|
||||
case args do
|
||||
[_file_name | rest] -> Mal.Env.set(env, "*ARGV*", rest)
|
||||
[] -> Mal.Env.set(env, "*ARGV*", [])
|
||||
[_file_name | rest] -> Mal.Env.set(env, "*ARGV*", list(rest))
|
||||
[] -> Mal.Env.set(env, "*ARGV*", list([]))
|
||||
end
|
||||
end
|
||||
|
||||
defp loop(env) do
|
||||
Mal.Core.readline("user> ")
|
||||
IO.write(:stdio, "user> ")
|
||||
IO.read(:stdio, :line)
|
||||
|> read_eval_print(env)
|
||||
|> IO.puts
|
||||
|
||||
loop(env)
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_list(ast) do
|
||||
Enum.map(ast, fn elem -> eval(elem, env) end)
|
||||
defp eval_ast({:list, ast, meta}, env) when is_list(ast) do
|
||||
{:list, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_map(ast) do
|
||||
for {key, value} <- ast, into: %{} do
|
||||
defp eval_ast({:map, ast, meta}, env) do
|
||||
map = for {key, value} <- ast, into: %{} do
|
||||
{eval(key, env), eval(value, env)}
|
||||
end
|
||||
|
||||
{:map, map, meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:vector, ast}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end)}
|
||||
defp eval_ast({:vector, ast, meta}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:symbol, symbol}, env) do
|
||||
case Mal.Env.get(env, symbol) do
|
||||
{:ok, value} -> value
|
||||
:not_found -> throw({:error, "invalid symbol #{symbol}"})
|
||||
:not_found -> throw({:error, "'#{symbol}' not found"})
|
||||
end
|
||||
end
|
||||
|
||||
@ -74,7 +80,6 @@ defmodule Mix.Tasks.Step6File do
|
||||
Mal.Reader.read_str(input)
|
||||
end
|
||||
|
||||
defp eval_bindings({:vector, vector}, env), do: eval_bindings(vector, env)
|
||||
defp eval_bindings([], _env), do: _env
|
||||
defp eval_bindings([{:symbol, key}, binding | tail], env) do
|
||||
evaluated = eval(binding, env)
|
||||
@ -83,7 +88,10 @@ defmodule Mix.Tasks.Step6File do
|
||||
end
|
||||
defp eval_bindings(_bindings, _env), do: throw({:error, "Unbalanced let* bindings"})
|
||||
|
||||
defp eval([{:symbol, "if"}, condition, if_true | if_false], env) do
|
||||
defp eval({:list, ast, meta}, env), do: eval_list(ast, env, meta)
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp eval_list([{:symbol, "if"}, condition, if_true | if_false], env, _) do
|
||||
result = eval(condition, env)
|
||||
if result == nil or result == false do
|
||||
case if_false do
|
||||
@ -95,27 +103,29 @@ defmodule Mix.Tasks.Step6File do
|
||||
end
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "do"} | ast], env) do
|
||||
eval_ast(List.delete_at(ast, -1), env)
|
||||
defp eval_list([{:symbol, "do"} | ast], env, _) do
|
||||
ast
|
||||
|> List.delete_at(-1)
|
||||
|> list
|
||||
|> eval_ast(env)
|
||||
eval(List.last(ast), env)
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "def!"}, {:symbol, key}, value], env) do
|
||||
defp eval_list([{:symbol, "def!"}, {:symbol, key}, value], env, _) do
|
||||
evaluated = eval(value, env)
|
||||
Mal.Env.set(env, key, evaluated)
|
||||
evaluated
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "let*"}, bindings, body], env) do
|
||||
defp eval_list([{:symbol, "let*"}, {list_type, bindings, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
let_env = Mal.Env.initialize(env)
|
||||
eval_bindings(bindings, let_env)
|
||||
eval(body, let_env)
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "fn*"}, {:vector, params}, body], env) do
|
||||
eval([{:symbol, "fn*"}, params, body], env)
|
||||
end
|
||||
defp eval([{:symbol, "fn*"}, params, body], env) do
|
||||
defp eval_list([{:symbol, "fn*"}, {list_type, params, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
param_symbols = for {:symbol, symbol} <- params, do: symbol
|
||||
|
||||
closure = fn args ->
|
||||
@ -123,19 +133,17 @@ defmodule Mix.Tasks.Step6File do
|
||||
eval(body, inner)
|
||||
end
|
||||
|
||||
{:closure, closure}
|
||||
%Function{value: closure}
|
||||
end
|
||||
|
||||
defp eval(ast, env) when is_list(ast) do
|
||||
[func | args] = eval_ast(ast, env)
|
||||
defp eval_list(ast, env, meta) do
|
||||
{:list, [func | args], _} = eval_ast({:list, ast, meta}, env)
|
||||
case func do
|
||||
{:closure, closure} -> closure.(args)
|
||||
%Function{value: closure} -> closure.(args)
|
||||
_ -> func.(args)
|
||||
end
|
||||
end
|
||||
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp print(value) do
|
||||
Mal.Printer.print_str(value)
|
||||
end
|
||||
|
@ -1,4 +1,7 @@
|
||||
defmodule Mix.Tasks.Step7Quote do
|
||||
import Mal.Types
|
||||
alias Mal.Function
|
||||
|
||||
def run(args) do
|
||||
env = Mal.Env.initialize()
|
||||
Mal.Env.merge(env, Mal.Core.namespace)
|
||||
@ -34,37 +37,40 @@ defmodule Mix.Tasks.Step7Quote do
|
||||
end)
|
||||
|
||||
case args do
|
||||
[_file_name | rest] -> Mal.Env.set(env, "*ARGV*", rest)
|
||||
[] -> Mal.Env.set(env, "*ARGV*", [])
|
||||
[_file_name | rest] -> Mal.Env.set(env, "*ARGV*", list(rest))
|
||||
[] -> Mal.Env.set(env, "*ARGV*", list([]))
|
||||
end
|
||||
end
|
||||
|
||||
defp loop(env) do
|
||||
Mal.Core.readline("user> ")
|
||||
IO.write(:stdio, "user> ")
|
||||
IO.read(:stdio, :line)
|
||||
|> read_eval_print(env)
|
||||
|> IO.puts
|
||||
|
||||
loop(env)
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_list(ast) do
|
||||
Enum.map(ast, fn elem -> eval(elem, env) end)
|
||||
defp eval_ast({:list, ast, meta}, env) when is_list(ast) do
|
||||
{:list, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_map(ast) do
|
||||
for {key, value} <- ast, into: %{} do
|
||||
defp eval_ast({:map, ast, meta}, env) do
|
||||
map = for {key, value} <- ast, into: %{} do
|
||||
{eval(key, env), eval(value, env)}
|
||||
end
|
||||
|
||||
{:map, map, meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:vector, ast}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end)}
|
||||
defp eval_ast({:vector, ast, meta}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:symbol, symbol}, env) do
|
||||
case Mal.Env.get(env, symbol) do
|
||||
{:ok, value} -> value
|
||||
:not_found -> throw({:error, "invalid symbol #{symbol}"})
|
||||
:not_found -> throw({:error, "'#{symbol}' not found"})
|
||||
end
|
||||
end
|
||||
|
||||
@ -74,7 +80,6 @@ defmodule Mix.Tasks.Step7Quote do
|
||||
Mal.Reader.read_str(input)
|
||||
end
|
||||
|
||||
defp eval_bindings({:vector, vector}, env), do: eval_bindings(vector, env)
|
||||
defp eval_bindings([], _env), do: _env
|
||||
defp eval_bindings([{:symbol, key}, binding | tail], env) do
|
||||
evaluated = eval(binding, env)
|
||||
@ -83,18 +88,34 @@ defmodule Mix.Tasks.Step7Quote do
|
||||
end
|
||||
defp eval_bindings(_bindings, _env), do: throw({:error, "Unbalanced let* bindings"})
|
||||
|
||||
defp quasiquote({:vector, list}, _env), do: quasiquote(list, _env)
|
||||
defp quasiquote(ast, _env) when not is_list(ast), do: [{:symbol, "quote"}, ast]
|
||||
defp quasiquote([], _env), do: [{:symbol, "quote"}, []]
|
||||
defp quasiquote([{:symbol, "unquote"}, arg], _env), do: arg
|
||||
defp quasiquote([[{:symbol, "splice-unquote"}, first] | tail], env) do
|
||||
[{:symbol, "concat"}, first, quasiquote(tail, env)]
|
||||
defp quasi_list([], _env), do: list([{:symbol, "quote"}, list([])])
|
||||
defp quasi_list([{:symbol, "unquote"}, arg], _env), do: arg
|
||||
defp quasi_list([{:list, [{:symbol, "splice-unquote"}, first], _meta} | tail], env) do
|
||||
right = tail
|
||||
|> list
|
||||
|> quasiquote(env)
|
||||
|
||||
list([{:symbol, "concat"}, first, right])
|
||||
end
|
||||
defp quasiquote([head | tail], env) do
|
||||
[{:symbol, "cons"}, quasiquote(head, env), quasiquote(tail, env)]
|
||||
defp quasi_list([head | tail], env) do
|
||||
left = quasiquote(head, env)
|
||||
right = tail
|
||||
|> list
|
||||
|> quasiquote(env)
|
||||
|
||||
list([{:symbol, "cons"}, left, right])
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "if"}, condition, if_true | if_false], env) do
|
||||
defp quasiquote({list_type, ast, _}, env)
|
||||
when list_type in [:list, :vector] do
|
||||
quasi_list(ast, env)
|
||||
end
|
||||
defp quasiquote(ast, _env), do: list([{:symbol, "quote"}, ast])
|
||||
|
||||
defp eval({:list, ast, meta}, env), do: eval_list(ast, env, meta)
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp eval_list([{:symbol, "if"}, condition, if_true | if_false], env, _) do
|
||||
result = eval(condition, env)
|
||||
if result == nil or result == false do
|
||||
case if_false do
|
||||
@ -106,27 +127,29 @@ defmodule Mix.Tasks.Step7Quote do
|
||||
end
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "do"} | ast], env) do
|
||||
eval_ast(List.delete_at(ast, -1), env)
|
||||
defp eval_list([{:symbol, "do"} | ast], env, _) do
|
||||
ast
|
||||
|> List.delete_at(-1)
|
||||
|> list
|
||||
|> eval_ast(env)
|
||||
eval(List.last(ast), env)
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "def!"}, {:symbol, key}, value], env) do
|
||||
defp eval_list([{:symbol, "def!"}, {:symbol, key}, value], env, _) do
|
||||
evaluated = eval(value, env)
|
||||
Mal.Env.set(env, key, evaluated)
|
||||
evaluated
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "let*"}, bindings, body], env) do
|
||||
defp eval_list([{:symbol, "let*"}, {list_type, bindings, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
let_env = Mal.Env.initialize(env)
|
||||
eval_bindings(bindings, let_env)
|
||||
eval(body, let_env)
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "fn*"}, {:vector, params}, body], env) do
|
||||
eval([{:symbol, "fn*"}, params, body], env)
|
||||
end
|
||||
defp eval([{:symbol, "fn*"}, params, body], env) do
|
||||
defp eval_list([{:symbol, "fn*"}, {list_type, params, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
param_symbols = for {:symbol, symbol} <- params, do: symbol
|
||||
|
||||
closure = fn args ->
|
||||
@ -134,26 +157,24 @@ defmodule Mix.Tasks.Step7Quote do
|
||||
eval(body, inner)
|
||||
end
|
||||
|
||||
{:closure, closure}
|
||||
%Function{value: closure}
|
||||
end
|
||||
|
||||
defp eval([{:symbol, "quote"}, arg], _env), do: arg
|
||||
defp eval_list([{:symbol, "quote"}, arg], _env, _), do: arg
|
||||
|
||||
defp eval([{:symbol, "quasiquote"}, ast], env) do
|
||||
defp eval_list([{:symbol, "quasiquote"}, ast], env, _) do
|
||||
quasiquote(ast, env)
|
||||
|> eval(env)
|
||||
end
|
||||
|
||||
defp eval(ast, env) when is_list(ast) do
|
||||
[func | args] = eval_ast(ast, env)
|
||||
defp eval_list(ast, env, meta) do
|
||||
{:list, [func | args], _} = eval_ast({:list, ast, meta}, env)
|
||||
case func do
|
||||
{:closure, closure} -> closure.(args)
|
||||
%Function{value: closure} -> closure.(args)
|
||||
_ -> func.(args)
|
||||
end
|
||||
end
|
||||
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp print(value) do
|
||||
Mal.Printer.print_str(value)
|
||||
end
|
||||
|
@ -1,4 +1,7 @@
|
||||
defmodule Mix.Tasks.Step8Macros do
|
||||
import Mal.Types
|
||||
alias Mal.Function
|
||||
|
||||
def run(args) do
|
||||
env = Mal.Env.initialize()
|
||||
Mal.Env.merge(env, Mal.Core.namespace)
|
||||
@ -57,37 +60,40 @@ defmodule Mix.Tasks.Step8Macros do
|
||||
end)
|
||||
|
||||
case args do
|
||||
[_file_name | rest] -> Mal.Env.set(env, "*ARGV*", rest)
|
||||
[] -> Mal.Env.set(env, "*ARGV*", [])
|
||||
[_file_name | rest] -> Mal.Env.set(env, "*ARGV*", list(rest))
|
||||
[] -> Mal.Env.set(env, "*ARGV*", list([]))
|
||||
end
|
||||
end
|
||||
|
||||
defp loop(env) do
|
||||
Mal.Core.readline("user> ")
|
||||
IO.write(:stdio, "user> ")
|
||||
IO.read(:stdio, :line)
|
||||
|> read_eval_print(env)
|
||||
|> IO.puts
|
||||
|
||||
loop(env)
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_list(ast) do
|
||||
Enum.map(ast, fn elem -> eval(elem, env) end)
|
||||
defp eval_ast({:list, ast, meta}, env) when is_list(ast) do
|
||||
{:list, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_map(ast) do
|
||||
for {key, value} <- ast, into: %{} do
|
||||
defp eval_ast({:map, ast, meta}, env) do
|
||||
map = for {key, value} <- ast, into: %{} do
|
||||
{eval(key, env), eval(value, env)}
|
||||
end
|
||||
|
||||
{:map, map, meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:vector, ast}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end)}
|
||||
defp eval_ast({:vector, ast, meta}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:symbol, symbol}, env) do
|
||||
case Mal.Env.get(env, symbol) do
|
||||
{:ok, value} -> value
|
||||
:not_found -> throw({:error, "invalid symbol #{symbol}"})
|
||||
:not_found -> throw({:error, "'#{symbol}' not found"})
|
||||
end
|
||||
end
|
||||
|
||||
@ -97,7 +103,6 @@ defmodule Mix.Tasks.Step8Macros do
|
||||
Mal.Reader.read_str(input)
|
||||
end
|
||||
|
||||
defp eval_bindings({:vector, vector}, env), do: eval_bindings(vector, env)
|
||||
defp eval_bindings([], _env), do: _env
|
||||
defp eval_bindings([{:symbol, key}, binding | tail], env) do
|
||||
evaluated = eval(binding, env)
|
||||
@ -106,27 +111,40 @@ defmodule Mix.Tasks.Step8Macros do
|
||||
end
|
||||
defp eval_bindings(_bindings, _env), do: throw({:error, "Unbalanced let* bindings"})
|
||||
|
||||
defp quasiquote({:vector, list}, _env), do: quasiquote(list, _env)
|
||||
defp quasiquote(ast, _env) when not is_list(ast), do: [{:symbol, "quote"}, ast]
|
||||
defp quasiquote([], _env), do: [{:symbol, "quote"}, []]
|
||||
defp quasiquote([{:symbol, "unquote"}, arg], _env), do: arg
|
||||
defp quasiquote([[{:symbol, "splice-unquote"}, first] | tail], env) do
|
||||
[{:symbol, "concat"}, first, quasiquote(tail, env)]
|
||||
defp quasi_list([], _env), do: list([{:symbol, "quote"}, list([])])
|
||||
defp quasi_list([{:symbol, "unquote"}, arg], _env), do: arg
|
||||
defp quasi_list([{:list, [{:symbol, "splice-unquote"}, first], _meta} | tail], env) do
|
||||
right = tail
|
||||
|> list
|
||||
|> quasiquote(env)
|
||||
|
||||
list([{:symbol, "concat"}, first, right])
|
||||
end
|
||||
defp quasiquote([head | tail], env) do
|
||||
[{:symbol, "cons"}, quasiquote(head, env), quasiquote(tail, env)]
|
||||
defp quasi_list([head | tail], env) do
|
||||
left = quasiquote(head, env)
|
||||
right = tail
|
||||
|> list
|
||||
|> quasiquote(env)
|
||||
|
||||
list([{:symbol, "cons"}, left, right])
|
||||
end
|
||||
|
||||
defp macro_call?([{:symbol, key} | _tail], env) do
|
||||
defp quasiquote({list_type, ast, _}, env)
|
||||
when list_type in [:list, :vector] do
|
||||
quasi_list(ast, env)
|
||||
end
|
||||
defp quasiquote(ast, _env), do: list([{:symbol, "quote"}, ast])
|
||||
|
||||
defp macro_call?({:list, [{:symbol, key} | _tail], _}, env) do
|
||||
case Mal.Env.get(env, key) do
|
||||
{:ok, {:macro, _}} -> true
|
||||
{:ok, %Function{value: macro, macro: true}} -> true
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
defp macro_call?(_ast, _env), do: false
|
||||
|
||||
defp do_macro_call([{:symbol, key} | tail], env) do
|
||||
{:ok, {:macro, macro}} = Mal.Env.get(env, key)
|
||||
defp do_macro_call({:list, [{:symbol, key} | tail], _}, env) do
|
||||
{:ok, %Function{value: macro, macro: true}} = Mal.Env.get(env, key)
|
||||
macro.(tail)
|
||||
|> macroexpand(env)
|
||||
end
|
||||
@ -139,17 +157,17 @@ defmodule Mix.Tasks.Step8Macros do
|
||||
end
|
||||
end
|
||||
|
||||
defp eval(ast, env) when not is_list(ast), do: eval_ast(ast, env)
|
||||
defp eval(ast, env) when is_list(ast) do
|
||||
defp eval({:list, _list, _meta} = ast, env) do
|
||||
case macroexpand(ast, env) do
|
||||
result when is_list(result) -> eval_list(result, env)
|
||||
{:list, list, meta} -> eval_list(list, env, meta)
|
||||
result -> result
|
||||
end
|
||||
end
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp eval_list([{:symbol, "macroexpand"}, ast], env), do: macroexpand(ast, env)
|
||||
defp eval_list([{:symbol, "macroexpand"}, ast], env, _), do: macroexpand(ast, env)
|
||||
|
||||
defp eval_list([{:symbol, "if"}, condition, if_true | if_false], env) do
|
||||
defp eval_list([{:symbol, "if"}, condition, if_true | if_false], env, _) do
|
||||
result = eval(condition, env)
|
||||
if result == nil or result == false do
|
||||
case if_false do
|
||||
@ -161,34 +179,35 @@ defmodule Mix.Tasks.Step8Macros do
|
||||
end
|
||||
end
|
||||
|
||||
defp eval_list([{:symbol, "do"} | ast], env) do
|
||||
eval_ast(List.delete_at(ast, -1), env)
|
||||
defp eval_list([{:symbol, "do"} | ast], env, _) do
|
||||
ast
|
||||
|> List.delete_at(-1)
|
||||
|> list
|
||||
|> eval_ast(env)
|
||||
eval(List.last(ast), env)
|
||||
end
|
||||
|
||||
defp eval_list([{:symbol, "def!"}, {:symbol, key}, value], env) do
|
||||
defp eval_list([{:symbol, "def!"}, {:symbol, key}, value], env, _) do
|
||||
evaluated = eval(value, env)
|
||||
Mal.Env.set(env, key, evaluated)
|
||||
evaluated
|
||||
end
|
||||
|
||||
defp eval_list([{:symbol, "defmacro!"}, {:symbol, key}, function], env) do
|
||||
{:closure, evaluated} = eval(function, env)
|
||||
macro = {:macro, evaluated}
|
||||
defp eval_list([{:symbol, "defmacro!"}, {:symbol, key}, function], env, _) do
|
||||
macro = %{eval(function, env) | macro: true}
|
||||
Mal.Env.set(env, key, macro)
|
||||
macro
|
||||
end
|
||||
|
||||
defp eval_list([{:symbol, "let*"}, bindings, body], env) do
|
||||
defp eval_list([{:symbol, "let*"}, {list_type, bindings, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
let_env = Mal.Env.initialize(env)
|
||||
eval_bindings(bindings, let_env)
|
||||
eval(body, let_env)
|
||||
end
|
||||
|
||||
defp eval_list([{:symbol, "fn*"}, {:vector, params}, body], env) do
|
||||
eval_list([{:symbol, "fn*"}, params, body], env)
|
||||
end
|
||||
defp eval_list([{:symbol, "fn*"}, params, body], env) do
|
||||
defp eval_list([{:symbol, "fn*"}, {list_type, params, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
param_symbols = for {:symbol, symbol} <- params, do: symbol
|
||||
|
||||
closure = fn args ->
|
||||
@ -196,20 +215,20 @@ defmodule Mix.Tasks.Step8Macros do
|
||||
eval(body, inner)
|
||||
end
|
||||
|
||||
{:closure, closure}
|
||||
%Function{value: closure}
|
||||
end
|
||||
|
||||
defp eval_list([{:symbol, "quote"}, arg], _env), do: arg
|
||||
defp eval_list([{:symbol, "quote"}, arg], _env, _), do: arg
|
||||
|
||||
defp eval_list([{:symbol, "quasiquote"}, ast], env) do
|
||||
defp eval_list([{:symbol, "quasiquote"}, ast], env, _) do
|
||||
quasiquote(ast, env)
|
||||
|> eval(env)
|
||||
end
|
||||
|
||||
defp eval_list(ast, env) do
|
||||
[func | args] = eval_ast(ast, env)
|
||||
defp eval_list(ast, env, meta) do
|
||||
{:list, [func | args], _} = eval_ast({:list, ast, meta}, env)
|
||||
case func do
|
||||
{:closure, closure} -> closure.(args)
|
||||
%Function{value: closure} -> closure.(args)
|
||||
_ -> func.(args)
|
||||
end
|
||||
end
|
||||
|
@ -1,4 +1,7 @@
|
||||
defmodule Mix.Tasks.Step9Try do
|
||||
import Mal.Types
|
||||
alias Mal.Function
|
||||
|
||||
def run(args) do
|
||||
env = Mal.Env.initialize()
|
||||
Mal.Env.merge(env, Mal.Core.namespace)
|
||||
@ -57,31 +60,34 @@ defmodule Mix.Tasks.Step9Try do
|
||||
end)
|
||||
|
||||
case args do
|
||||
[_file_name | rest] -> Mal.Env.set(env, "*ARGV*", rest)
|
||||
[] -> Mal.Env.set(env, "*ARGV*", [])
|
||||
[_file_name | rest] -> Mal.Env.set(env, "*ARGV*", list(rest))
|
||||
[] -> Mal.Env.set(env, "*ARGV*", list([]))
|
||||
end
|
||||
end
|
||||
|
||||
defp loop(env) do
|
||||
Mal.Core.readline("user> ")
|
||||
IO.write(:stdio, "user> ")
|
||||
IO.read(:stdio, :line)
|
||||
|> read_eval_print(env)
|
||||
|> IO.puts
|
||||
|
||||
loop(env)
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_list(ast) do
|
||||
Enum.map(ast, fn elem -> eval(elem, env) end)
|
||||
defp eval_ast({:list, ast, meta}, env) when is_list(ast) do
|
||||
{:list, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast(ast, env) when is_map(ast) do
|
||||
for {key, value} <- ast, into: %{} do
|
||||
defp eval_ast({:map, ast, meta}, env) do
|
||||
map = for {key, value} <- ast, into: %{} do
|
||||
{eval(key, env), eval(value, env)}
|
||||
end
|
||||
|
||||
{:map, map, meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:vector, ast}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end)}
|
||||
defp eval_ast({:vector, ast, meta}, env) do
|
||||
{:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}
|
||||
end
|
||||
|
||||
defp eval_ast({:symbol, symbol}, env) do
|
||||
@ -97,7 +103,6 @@ defmodule Mix.Tasks.Step9Try do
|
||||
Mal.Reader.read_str(input)
|
||||
end
|
||||
|
||||
defp eval_bindings({:vector, vector}, env), do: eval_bindings(vector, env)
|
||||
defp eval_bindings([], _env), do: _env
|
||||
defp eval_bindings([{:symbol, key}, binding | tail], env) do
|
||||
evaluated = eval(binding, env)
|
||||
@ -106,27 +111,40 @@ defmodule Mix.Tasks.Step9Try do
|
||||
end
|
||||
defp eval_bindings(_bindings, _env), do: throw({:error, "Unbalanced let* bindings"})
|
||||
|
||||
defp quasiquote({:vector, list}, _env), do: quasiquote(list, _env)
|
||||
defp quasiquote(ast, _env) when not is_list(ast), do: [{:symbol, "quote"}, ast]
|
||||
defp quasiquote([], _env), do: [{:symbol, "quote"}, []]
|
||||
defp quasiquote([{:symbol, "unquote"}, arg], _env), do: arg
|
||||
defp quasiquote([[{:symbol, "splice-unquote"}, first] | tail], env) do
|
||||
[{:symbol, "concat"}, first, quasiquote(tail, env)]
|
||||
defp quasi_list([], _env), do: list([{:symbol, "quote"}, list([])])
|
||||
defp quasi_list([{:symbol, "unquote"}, arg], _env), do: arg
|
||||
defp quasi_list([{:list, [{:symbol, "splice-unquote"}, first], _meta} | tail], env) do
|
||||
right = tail
|
||||
|> list
|
||||
|> quasiquote(env)
|
||||
|
||||
list([{:symbol, "concat"}, first, right])
|
||||
end
|
||||
defp quasiquote([head | tail], env) do
|
||||
[{:symbol, "cons"}, quasiquote(head, env), quasiquote(tail, env)]
|
||||
defp quasi_list([head | tail], env) do
|
||||
left = quasiquote(head, env)
|
||||
right = tail
|
||||
|> list
|
||||
|> quasiquote(env)
|
||||
|
||||
list([{:symbol, "cons"}, left, right])
|
||||
end
|
||||
|
||||
defp macro_call?([{:symbol, key} | _tail], env) do
|
||||
defp quasiquote({list_type, ast, _}, env)
|
||||
when list_type in [:list, :vector] do
|
||||
quasi_list(ast, env)
|
||||
end
|
||||
defp quasiquote(ast, _env), do: list([{:symbol, "quote"}, ast])
|
||||
|
||||
defp macro_call?({:list, [{:symbol, key} | _tail], _}, env) do
|
||||
case Mal.Env.get(env, key) do
|
||||
{:ok, {:macro, _}} -> true
|
||||
{:ok, %Function{value: macro, macro: true}} -> true
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
defp macro_call?(_ast, _env), do: false
|
||||
|
||||
defp do_macro_call([{:symbol, key} | tail], env) do
|
||||
{:ok, {:macro, macro}} = Mal.Env.get(env, key)
|
||||
defp do_macro_call({:list, [{:symbol, key} | tail], _}, env) do
|
||||
{:ok, %Function{value: macro, macro: true}} = Mal.Env.get(env, key)
|
||||
macro.(tail)
|
||||
|> macroexpand(env)
|
||||
end
|
||||
@ -139,17 +157,17 @@ defmodule Mix.Tasks.Step9Try do
|
||||
end
|
||||
end
|
||||
|
||||
defp eval(ast, env) when not is_list(ast), do: eval_ast(ast, env)
|
||||
defp eval(ast, env) when is_list(ast) do
|
||||
defp eval({:list, _list, _meta} = ast, env) do
|
||||
case macroexpand(ast, env) do
|
||||
result when is_list(result) -> eval_list(result, env)
|
||||
{:list, list, meta} -> eval_list(list, env, meta)
|
||||
result -> result
|
||||
end
|
||||
end
|
||||
defp eval(ast, env), do: eval_ast(ast, env)
|
||||
|
||||
defp eval_list([{:symbol, "macroexpand"}, ast], env), do: macroexpand(ast, env)
|
||||
defp eval_list([{:symbol, "macroexpand"}, ast], env, _), do: macroexpand(ast, env)
|
||||
|
||||
defp eval_list([{:symbol, "if"}, condition, if_true | if_false], env) do
|
||||
defp eval_list([{:symbol, "if"}, condition, if_true | if_false], env, _) do
|
||||
result = eval(condition, env)
|
||||
if result == nil or result == false do
|
||||
case if_false do
|
||||
@ -161,34 +179,35 @@ defmodule Mix.Tasks.Step9Try do
|
||||
end
|
||||
end
|
||||
|
||||
defp eval_list([{:symbol, "do"} | ast], env) do
|
||||
eval_ast(List.delete_at(ast, -1), env)
|
||||
defp eval_list([{:symbol, "do"} | ast], env, _) do
|
||||
ast
|
||||
|> List.delete_at(-1)
|
||||
|> list
|
||||
|> eval_ast(env)
|
||||
eval(List.last(ast), env)
|
||||
end
|
||||
|
||||
defp eval_list([{:symbol, "def!"}, {:symbol, key}, value], env) do
|
||||
defp eval_list([{:symbol, "def!"}, {:symbol, key}, value], env, _) do
|
||||
evaluated = eval(value, env)
|
||||
Mal.Env.set(env, key, evaluated)
|
||||
evaluated
|
||||
end
|
||||
|
||||
defp eval_list([{:symbol, "defmacro!"}, {:symbol, key}, function], env) do
|
||||
{:closure, evaluated} = eval(function, env)
|
||||
macro = {:macro, evaluated}
|
||||
defp eval_list([{:symbol, "defmacro!"}, {:symbol, key}, function], env, _) do
|
||||
macro = %{eval(function, env) | macro: true}
|
||||
Mal.Env.set(env, key, macro)
|
||||
macro
|
||||
end
|
||||
|
||||
defp eval_list([{:symbol, "let*"}, bindings, body], env) do
|
||||
defp eval_list([{:symbol, "let*"}, {list_type, bindings, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
let_env = Mal.Env.initialize(env)
|
||||
eval_bindings(bindings, let_env)
|
||||
eval(body, let_env)
|
||||
end
|
||||
|
||||
defp eval_list([{:symbol, "fn*"}, {:vector, params}, body], env) do
|
||||
eval_list([{:symbol, "fn*"}, params, body], env)
|
||||
end
|
||||
defp eval_list([{:symbol, "fn*"}, params, body], env) do
|
||||
defp eval_list([{:symbol, "fn*"}, {list_type, params, _}, body], env, _)
|
||||
when list_type == :list or list_type == :vector do
|
||||
param_symbols = for {:symbol, symbol} <- params, do: symbol
|
||||
|
||||
closure = fn args ->
|
||||
@ -196,19 +215,34 @@ defmodule Mix.Tasks.Step9Try do
|
||||
eval(body, inner)
|
||||
end
|
||||
|
||||
{:closure, closure}
|
||||
%Function{value: closure}
|
||||
end
|
||||
|
||||
defp eval_list([{:symbol, "quote"}, arg], _env), do: arg
|
||||
defp eval_list([{:symbol, "quote"}, arg], _env, _), do: arg
|
||||
|
||||
defp eval_list([{:symbol, "quasiquote"}, ast], env) do
|
||||
defp eval_list([{:symbol, "quasiquote"}, ast], env, _) do
|
||||
quasiquote(ast, env)
|
||||
|> eval(env)
|
||||
end
|
||||
|
||||
# (try* A (catch* B C))
|
||||
defp eval_list([{:symbol, "try*"}, try_form,
|
||||
[{:symbol, "catch*"}, {:symbol, exception}, catch_form]], env) do
|
||||
defp eval_list([{:symbol, "try*"}, try_form, {:list, catch_list, _meta}], env, _) do
|
||||
eval_try(try_form, catch_list, env)
|
||||
end
|
||||
defp eval_list([{:symbol, "try*"}, _try_form, _], _env, _) do
|
||||
throw({:error, "try* requires a list as the second parameter"})
|
||||
end
|
||||
|
||||
defp eval_list(ast, env, meta) do
|
||||
{:list, [func | args], _} = eval_ast({:list, ast, meta}, env)
|
||||
case func do
|
||||
%Function{value: closure} -> closure.(args)
|
||||
_ -> func.(args)
|
||||
end
|
||||
end
|
||||
|
||||
defp eval_try(try_form,
|
||||
[{:symbol, "catch*"}, {:symbol, exception}, catch_form], env) do
|
||||
try do
|
||||
eval(try_form, env)
|
||||
catch
|
||||
@ -218,13 +252,8 @@ defmodule Mix.Tasks.Step9Try do
|
||||
eval(catch_form, catch_env)
|
||||
end
|
||||
end
|
||||
|
||||
defp eval_list(ast, env) do
|
||||
[func | args] = eval_ast(ast, env)
|
||||
case func do
|
||||
{:closure, closure} -> closure.(args)
|
||||
_ -> func.(args)
|
||||
end
|
||||
defp eval_try(_try_form, _catch_list, _env) do
|
||||
throw({:error, "catch* requires two arguments"})
|
||||
end
|
||||
|
||||
defp print(value) do
|
||||
|
Loading…
Reference in New Issue
Block a user