A .formatter.exs => .formatter.exs +4 -0
@@ 0,0 1,4 @@
+# Used by "mix format"
+[
+ inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
+]
A .gitignore => .gitignore +27 -0
@@ 0,0 1,27 @@
+# The directory Mix will write compiled artifacts to.
+/_build/
+
+# If you run "mix test --cover", coverage assets end up here.
+/cover/
+
+# The directory Mix downloads your dependencies sources to.
+/deps/
+
+# Where third-party dependencies like ExDoc output generated docs.
+/doc/
+
+# Ignore .fetch files in case you like to edit your project deps locally.
+/.fetch
+
+# If the VM crashes, it generates a dump, let's ignore it too.
+erl_crash.dump
+
+# Also ignore archive artifacts (built via "mix archive.build").
+*.ez
+
+# Ignore package tarball (built via "mix hex.build").
+lixp-*.tar
+
+
+# Temporary files for e.g. tests
+/tmp
A README.md => README.md +21 -0
@@ 0,0 1,21 @@
+# Lixp
+
+**TODO: Add description**
+
+## Installation
+
+If [available in Hex](https://hex.pm/docs/publish), the package can be installed
+by adding `lixp` to your list of dependencies in `mix.exs`:
+
+```elixir
+def deps do
+ [
+ {:lixp, "~> 0.1.0"}
+ ]
+end
+```
+
+Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
+and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
+be found at [https://hexdocs.pm/lixp](https://hexdocs.pm/lixp).
+
A lib/lixp.ex => lib/lixp.ex +36 -0
@@ 0,0 1,36 @@
+defmodule Lixp do
+ def eval(str) do
+ {:ok, [res], _, _, _, _} = Lixp.Parser.program(str)
+ eval(res, %{})
+ end
+ defp eval(num, _env) when is_number(num) do
+ num
+ end
+ defp eval(atom, _env) when is_atom(atom) do
+ atom
+ end
+ defp eval({:symbol, sym}, env) do
+ env[sym]
+ 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)
+ eval(body, extend(combination, env))
+ end
+ end
+ defp eval([h | t], env) do
+ f = eval(h, env)
+ args = Enum.map(t, &eval(&1, env))
+
+ f.(args)
+ end
+ defp extend([{name, val} | rest], env) do
+ extend(rest, Map.put(env, name, val))
+ end
+
+ defp extend([], env) do
+ env
+ end
+end
A lib/lixp/parser.ex => lib/lixp/parser.ex +74 -0
@@ 0,0 1,74 @@
+defmodule Lixp.Parser.Help do
+ import NimbleParsec
+ def digit do
+ ascii_char([?0..?9]) |> map(:char_to_string)
+ end
+ def dot do
+ ascii_char([?.]) |> map(:char_to_string)
+ end
+ def number do
+ digit()
+ |> optional(repeat(digit()))
+ |> optional(dot())
+ |> optional(repeat(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)
+
+ atom =
+ ignore(colon)
+ |> concat(valid_atom_chars)
+ |> 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)
+
+ list =
+ ignore(open_list)
+ |> optional(repeat(choice([
+ atom,
+ number(),
+ parsec(:list),
+ symbol,
+ ignore(string(" "))])))
+ |> concat(ignore(close_list))
+ |> reduce({Enum, :into, [[]]})
+ |> label("list")
+
+ defparsec(
+ :list,
+ list
+ )
+ defparsec(
+ :term,
+ choice([list, number(), atom, symbol, ignore(string(" "))])
+ )
+
+ defparsec(
+ :program,
+ repeat(parsec(:term))
+ )
+end
A mix.exs => mix.exs +29 -0
@@ 0,0 1,29 @@
+defmodule Lixp.MixProject do
+ use Mix.Project
+
+ def project do
+ [
+ app: :lixp,
+ version: "0.1.0",
+ elixir: "~> 1.11",
+ start_permanent: Mix.env() == :prod,
+ deps: deps()
+ ]
+ end
+
+ # Run "mix help compile.app" to learn about applications.
+ def application do
+ [
+ extra_applications: [:logger]
+ ]
+ end
+
+ # Run "mix help deps" to learn about dependencies.
+ defp deps do
+ [
+ # {:dep_from_hexpm, "~> 0.3.0"},
+ # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
+ {:nimble_parsec, "~> 1.0"}
+ ]
+ end
+end
A mix.lock => mix.lock +3 -0
@@ 0,0 1,3 @@
+%{
+ "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},
+}
A test/lixp_test.exs => test/lixp_test.exs +8 -0
@@ 0,0 1,8 @@
+defmodule LixpTest do
+ use ExUnit.Case
+ doctest Lixp
+
+ test "greets the world" do
+ assert Lixp.hello() == :world
+ end
+end
A test/test_helper.exs => test/test_helper.exs +1 -0
@@ 0,0 1,1 @@
+ExUnit.start()