Eeeeyup, it is yet another language, this time it is just a lispy syntax of the elixir ast, it was pretty quick to whip up, basically just another way of messing around with the Elixir AST, the quite tests for it:
test "Parsing" do
assert %{rest: "", result: {:integer, _, 1}} = parse_expression "1"
assert %{rest: "", result: {:float, _, 6.28}} = parse_expression "6.28"
assert %{rest: "", result: {:name, _, "test"}} = parse_expression "test"
assert %{rest: "", result: {:name, _, "test"}} = parse_expression " test "
assert %{rest: "fail", result: {:name, _, "test"}} = parse_expression "test fail"
assert %{rest: "", result: {:name, _, "test pass"}} = parse_expression "test\\ pass"
assert %{rest: "", result: {:cmd, _, [{:integer, _, 1}]}} = parse_expression "(1)"
assert %{rest: "", result: {:cmd, _, [{:integer, _, 1}, {:integer, _, 2}]}} = parse_expression "(1 2)"
assert %{rest: "", result: {:cmd, _, [{:name, _, "+"}, {:integer, _, 1}, {:integer, _, 2}]}} = parse_expression "(+ 1 2)"
assert %{rest: "", result: {:name, _, ":add"}} = parse_expression ":add"
assert %{rest: "", result: {:name, _, ":+"}} = parse_expression ":+"
assert %{rest: "", result: {:cmd, _, [{:name, _, ":+"}, {:integer, _, 1}, {:integer, _, 2}]}} = parse_expression "(:+ 1 2)"
assert %{rest: "", result: {:name, _, "add"}} = parse_expression "add"
assert %{rest: "", result: {:name, _, "A string"}} = parse_expression "A\\ string"
assert 2 = 1 + 1
end
def testcall(), do: 42
def testcall(a), do: 42 + a
test "Sigil" do
# Direct values
assert 1 = ~L{1}u
assert 6.28 = ~L{6.28}u
# Atoms
assert :test = ~L{atom test}
assert :"split test" = ~L{atom split\ test}
# Strings
assert "test" = ~L{string test}
assert "split test" = ~L{string split\ test}
# Lists
assert [] = ~L{list}
assert [1] = ~L{list 1}
assert [1, 2] = ~L{list 1 2}
# Tuples
assert {} = ~L{tuple}
assert {1} = ~L{tuple 1}
assert {1, 2} = ~L{tuple 1 2}
# Maps
assert %{} = ~L{map}
assert %{1 => 2} = ~L{map (1 2)}
assert %{1 => 2, 3 => 4} = ~L{map (1 2) (3 4)}
# Mixed map
assert %{{1, 2} => [3, 4]} = ~L{map ((tuple 1 2) (list 3 4))}
# Local call
assert 42 = ~L{testcall}
assert 43 = ~L{testcall 1}
assert 3 = ~L{+ 1 2}
# Remote call
assert "42" = ~L{Elixir.Kernel.inspect 42}
# Anonymous function 0-arg
assert 42 = ~L{fn (() 42)}.()
assert 42 = ~L{fn (() () 42)}.()
# Anonymous function 1-arg
assert 42 = ~L{fn ((x) x)}.(42)
assert 42 = ~L{fn ((x) (* x 2))}.(21)
# Anonymous function 1-arg pattern matching
assert 42 = ~L{fn ((0) 42) ((x) x)}.(0)
# Anonymous function 1-arg guarded
assert 42 = ~L{fn ((x) () x)}.(42)
assert 42 = ~L{fn ((x) ((> x 0)) x)}.(42)
assert 42 = ~L{fn ((x) ((> x 0)) x) ((x) ((< x 0)) (- x))}.(-42)
# Quote
assert {:name, _, "x"} = ~L{quote x}
assert {:cmd, _, [{:name, _, "blah"}, {:integer, _, 1}]} = ~L{quote (blah 1)}
end
~L{Elixir.Kernel.defmodule (atom TestGenModule0) (list (do))}
~L{Elixir.Kernel.defmodule (atom TestGenModule1) (list (do
(def get (list (do 42)))
(def (id x) (list (do x)))
(def (idq _x) (list (do (quote _x))))
))}
test "Generated module tests" do
assert :TestGenModule0 = :TestGenModule0.module_info()[:module]
assert :TestGenModule1 = :TestGenModule1.module_info()[:module]
assert 42 = :TestGenModule1.get()
assert 42 = :TestGenModule1.id(42)
assert {:name, _, "_x"} = :TestGenModule1.idq(42)
end
However, I can make macroâs inside the lispy thing that operate over the lispy stuff in a not quite lispy way (they are lisp, but they are tagged with some metadata like column and line numbers and such), so this is an easy way to make little embedded DSELâs. ^.^
And not only macroâs, but read macroâs too! Only ones so far is `, which just parses the next expression and wraps it up in a (quote <expression>)
and a ,
, which will unquote within a quote (technically it just parses the next expression and wraps it in a (unquote <expression>)
that can be detected by the main quote
, not entirely done yet, but the read macroâs exist). And have some starter ones that grab out {
and [
(the implementations are not done yet) that parse lists of expressions until the termination }
and ]
(recursive is fine as it just re-enters the parser as normal) and puts it in a (tuple <stuff>)
or (list <stuff>)
as appropriate (those of which are âspecial formsâ, but even special forms are user-definable and overridable if you are wanting to do some crazy stuff that even macros and read-macros cannot do).
Barely started, I had some time today but it was only about an hour so this is about all I got done so far, but it was easy to do.