@@ 1,33 1,41 @@
defmodule Lixp do
def eval(str) do
{:ok, prog, _, _, _, _} = Lixp.Parser.program(str)
-
+
{val, _} =
Enum.reduce(prog, {nil, %{}}, fn p, {_, e} ->
eval(p, e)
end)
-
+
val
end
+
defp eval(num, env) when is_number(num) do
{num, env}
end
+
defp eval(atom, env) when is_atom(atom) do
{atom, env}
end
+
defp eval({:symbol, sym}, env) do
{env[sym], env}
end
+
defp eval([{:symbol, "quote"} | [rest]], env) do
- rest = Enum.map(rest, fn
- [{:symbol, "unquote"} | [body]] ->
- {val, _} = eval(body, env)
- val
- x -> x
- end)
-
+ rest =
+ Enum.map(rest, fn
+ [{:symbol, "unquote"} | [body]] ->
+ {val, _} = eval(body, env)
+ val
+
+ x ->
+ x
+ end)
+
{rest, env}
end
+
defp eval([{:symbol, "ffi"} | rest], env) do
[m, f, a] = rest
{m, env} = eval(m, env)
@@ 35,36 43,40 @@ defmodule Lixp do
{a, env} = eval(a, env)
{apply(m, f, a), env}
end
+
defp eval([{:symbol, "define"} | rest], env) do
[{:symbol, name}, body] = rest
{val, env} = eval(body, env)
env = Map.put(env, name, val)
-
+
{val, env}
end
- defp eval([{:symbol, "lambda"} | rest ], env) do
- [ arg_names | [body] ] = rest
- {fn args ->
- arg_names = Enum.map(arg_names, fn {:symbol, name} -> name end)
- combination = Enum.zip(arg_names, args)
- {val, _} = eval(body, extend(combination, env))
- val
- end, env}
- end
- defp eval([h | t], env) do
- {f, env} = eval(h, env)
- args = Enum.map(t, fn x ->
- {val, _} = eval(x, env)
- val
- end)
-
- {f.(args), env}
+
+ defp eval([{:symbol, "lambda"} | rest], env) do
+ [arg_names | [body]] = rest
+
+ {fn arg_values ->
+ arg_names = Enum.map(arg_names, fn {:symbol, name} -> name end)
+ args = Enum.zip(arg_names, arg_values)
+
+ env =
+ Enum.reduce(args, env, fn {key, val}, e ->
+ Map.put(e, key, val)
+ end)
+
+ eval(body, env) |> elem(0)
+ end, env}
end
- defp extend([{name, val} | rest], env) do
- extend(rest, Map.put(env, name, val))
- end
-
- defp extend([], env) do
- env
+
+ defp eval([function | args], env) do
+ {function, _} = eval(function, env)
+
+ args =
+ Enum.map(args, fn arg ->
+ {val, _} = eval(arg, env)
+ val
+ end)
+
+ {function.(args), env}
end
end
@@ 1,72 1,95 @@
-defmodule Lixp.Parser.Help do
+defmodule Lixp.Parser do
import NimbleParsec
- def digit do
- ascii_char([?0..?9]) |> map(:char_to_string)
+
+ defp parse_float(str) do
+ Float.parse(str) |> elem(0)
end
- def dot do
- ascii_char([?.]) |> map(:char_to_string)
+
+ defp wrap_symbol(sym) do
+ {:symbol, sym}
end
- def number do
- digit()
- |> optional(repeat(digit()))
- |> optional(dot())
- |> optional(repeat(digit()))
+
+ digit = ascii_string([?0..?9], min: 1)
+ dot = string(".")
+
+ number =
+ digit
+ |> optional(dot |> concat(digit))
|> reduce({Enum, :join, [""]})
|> map(:parse_float)
- |> label("number")
- end
-end
-defmodule Lixp.Parser do
- import NimbleParsec
- import Lixp.Parser.Help
- def parse_float(str) do
- {n, _} = Float.parse(str)
- n
- end
- def char_to_string(char) do
- List.to_string([char])
- end
+
colon = string(":")
- valid_atom_chars = ascii_string([?^..?z, ?!..?', ?*..?., ?0..?Z], min: 1)
-
+
+ symbol_characters =
+ ascii_string(
+ [
+ # space
+ {:not, 32},
+ {:not, ?(},
+ {:not, ?)},
+ {:not, ?:},
+ {:not, ?0..9},
+ {:not, ?.}
+ ],
+ min: 1
+ )
+
+ symbol =
+ symbol_characters
+ |> map(:wrap_symbol)
+ |> label("symbol")
+
atom =
ignore(colon)
- |> concat(valid_atom_chars)
+ |> concat(symbol_characters)
|> map({String, :to_atom, []})
|> label("atom")
-
- def wrap_symbol(sym) do
- {:symbol, sym}
- end
-
- symbol =
- valid_atom_chars
- |> map({:wrap_symbol, []})
- |> label("symbol")
- open_list = ascii_char([?(]) |> map(:char_to_string)
- close_list = ascii_char([?)]) |> map(:char_to_string)
-
+
+ open_list = string("(")
+ close_list = string(")")
+
list =
ignore(open_list)
- |> optional(repeat(choice([
- atom,
- number(),
- parsec(:list),
- symbol,
- ignore(string(" "))])))
- |> concat(ignore(close_list))
+ |> optional(repeat(parsec(:term)))
+ |> ignore(close_list)
|> reduce({Enum, :into, [[]]})
|> label("list")
-
- defparsec(
- :list,
- list
- )
+
+ q = string("'")
+ comma = string(",")
+
+ defp quote_wrap(arg) do
+ [{:symbol, "quote"}, arg]
+ end
+
+ defp unquote_wrap(arg) do
+ [{:symbol, "unquote"}, arg]
+ end
+
+ quoted =
+ ignore(q)
+ |> parsec(:term)
+ |> map(:quote_wrap)
+
+ unquoted =
+ ignore(comma)
+ |> parsec(:term)
+ |> map(:unquote_wrap)
+
defparsec(
:term,
- choice([list, number(), atom, symbol, ignore(string(" ")), ignore(string("\n"))])
+ choice([
+ number,
+ list,
+ symbol,
+ atom,
+ quoted,
+ unquoted,
+ ignore(string(" ")),
+ ignore(string("\n"))
+ ])
)
-
+
defparsec(
:program,
repeat(parsec(:term))