Current section

Files

Jump to
mex lib mex.ex
Raw

lib/mex.ex

defmodule Mex do
alias Mex.CLI
defdelegate set_width(num_chars), to: Mex.CLI
@name [ none: &Mex.no_expansion/2, once: &Macro.expand_once/2,
expand: &Macro.expand/2, all: &Mex.expand_all/2 ]
@pos [[:none, :once, :expand, :all],
[:all], [:expand, :all], [:once, :expand, :all]]
def by_name(atom), do: @name[atom]
def by_column_count(i) do
Enum.map (Enum.at(@pos,i)||List.first(@pos)), &by_name/1
end
@doc """
Call with a `do..end` block, preceded by an optional parameter:
- a number 1-3, to limit number of expansions to show side by side
- a single named expander, one of `:none, :once, :expand, :all`
If you'd like to compare the output of your own expanders side by side,
see `Mex.pp_compare`.
"""
defmacro mex( num \\ 0, rest )
defmacro mex( num, do: node ) when is_integer(num) do
pp_compare node, __CALLER__, by_column_count(num)
quote do: :ok
end
defmacro mex( atom, do: node ) when is_atom(atom) do
pp_compare node, __CALLER__, [by_name(atom)]
quote do: :ok
end
@doc """
Display the expansion of a `node` side-by-side in `env`,
expanded by `expanders`, a list of anonymous expansion functions.
"""
def pp_compare node, env, expanders do
{texts, errors} = try_expansions( node, env, expanders)
IO.puts expanders |> Enum.map(&inspect/1) |> CLI.headers
IO.puts texts |> CLI.side_by_side
IO.puts errors |> Enum.reject(&is_nil/1) |> Enum.map_join("\n", &(" "<>&1))
end
def expand_all(n, env) do
Macro.prewalk n, &Macro.expand( &1, env )
end
def no_expansion( node, _env), do: node
# error handling
def try_expansions( node, env, fns ) do
fns
|> Enum.map( &try_expand( &1, node, env ))
|> Enum.reduce( {[],[]}, &accum_expansion/2 )
end
defp accum_expansion {_status, txt, err}, {pages, errs} do
{pages ++ [txt], errs ++ [err]}
end
defp try_expand f, node, env do
import IO.ANSI
try do
{:ok, (f.(node, env) |> Macro.to_string), nil}
rescue
e ->
{ :err, "ExpansionError", "#{bright}#{inspect(f)}#{normal} - #{e.message}" }
end
end
end