~sjm/lixp

71acb16c1635d57078e42b631e784e3fbf4c974b — Sam Marshall 4 months ago 51e0590
add define, ffi, quote and unquote
2 files changed, 48 insertions(+), 14 deletions(-)

M lib/lixp.ex
M lib/lixp/parser.ex
M lib/lixp.ex => lib/lixp.ex +47 -13
@@ 1,30 1,64 @@
defmodule Lixp do
  def eval(str) do
    {:ok, [res], _, _, _, _} = Lixp.Parser.program(str)
    eval(res, %{})
    {: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
  defp eval(num, env) when is_number(num) do
    {num, env}
  end
  defp eval(atom, _env) when is_atom(atom) do
    atom
  defp eval(atom, env) when is_atom(atom) do
    {atom, env}
  end
  defp eval({:symbol, sym}, env) do
    env[sym]
    {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, env}
  end
  defp eval([{:symbol, "ffi"} | rest], env) do
    [m, f, a] = rest
    {m, env} = eval(m, env)
    {f, env} = eval(f, env)
    {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 ->
    {fn args ->
      arg_names = Enum.map(arg_names, fn {:symbol, name} -> name end)
      combination = Enum.zip(arg_names, args)
      eval(body, extend(combination, env))
    end
      {val, _} = eval(body, extend(combination, env))
      val
    end, env}
  end
  defp eval([h | t], env) do
    f = eval(h, env)
    args = Enum.map(t, &eval(&1, env))
    {f, env} = eval(h, env)
    args = Enum.map(t, fn x ->
      {val, _} = eval(x, env)
      val
    end)
  
    f.(args)
    {f.(args), env}
  end
  defp extend([{name, val} | rest], env) do
    extend(rest, Map.put(env, name, val))

M lib/lixp/parser.ex => lib/lixp/parser.ex +1 -1
@@ 64,7 64,7 @@ defmodule Lixp.Parser do
    )
  defparsec(
    :term,
    choice([list, number(), atom, symbol, ignore(string(" "))])
    choice([list, number(), atom, symbol, ignore(string(" ")), ignore(string("\n"))])
  )
  
  defparsec(