Current section
Files
Jump to
Current section
Files
lib/macpato.ex
defmodule Macpato do
@moduledoc ~S"""
iex> import Macpato
...> expr = quote do
...> fn a, b, c -> a + b + c end
...> end
...> case expr do
...> macpato(fn _, b, _ -> _ end) -> :b_is_second_arg
...> _ -> :dunno
...> end
:b_is_second_arg
iex> import Macpato
...> expr = quote do
...> fn a, b, c -> a + b + c end
...> end
...> case expr do
...> macpato(fn _, _, _({name, _, _}) -> _ end) -> name
...> end
:c
iex> import Macpato
...> expr = quote do
...> fn a -> a + 22 end
...> end
...> x = 22
...> case expr do
...> macpato(fn _ -> _ + _(^x) end) -> :good
...> end
:good
iex> import Macpato
...> expr = quote do
...> fn a, b, c -> x end
...> end
...> case expr do
...> macpato(fn _(@args) -> _ end) -> length(args)
...> end
3
"""
defmodule Pre do
def walk({:_, _, [expr]}, _), do: [macpato: expr]
def walk({a, b, c}, opts) when length(c) > 0 do
{walk(a, opts), walk(b, opts), Enum.map(c, &walk(&1, opts))} |> step(opts)
end
def walk(expr, opts) when is_list(expr) do
Enum.map(expr, &walk(&1, opts))
end
def walk(expr, opts) do
expr |> step(opts)
end
defp step(expr, opts) do
expr
|> lowd()
|> meta(Keyword.get(opts, :meta, false))
|> context(Keyword.get(opts, :meta, false))
end
defp lowd({:_, _, nil}), do: wildcard
defp lowd(any), do: any
defp meta({a, _, c}, false), do: {a, wildcard, c}
defp meta(any, _), do: any
defp context({a, b, c}, false) when is_atom(a) and is_atom(c), do: {a, b, wildcard}
defp context(any, _), do: any
defp wildcard do
[macpato: {:_, [], Elixir}]
end
end
defmodule Post do
def walk([[macpato: {:{}, _, [:@, _, [expr]]}]]), do: walk(macpato: expr)
def walk(macpato: expr), do: Macro.prewalk(expr, &unscape/1)
def walk({:{}, _, x}) when is_list(x), do: {:{}, [], walk(x)}
def walk(expr) when is_list(expr), do: Enum.map(expr, &walk/1)
def walk(expr), do: expr
defp unscape({:{}, _, [a, b, c]}), do: {a, b, c}
defp unscape(expr), do: expr
end
defmacro macpato(expr, opts \\ []) do
macpato_expr(expr, opts)
end
def macpato_expr(ast, opts \\ []) do
ast
|> Pre.walk(opts)
|> Macro.escape
|> Post.walk
end
end