| 1 |
|
defmodule Tools.User.Ast do |
| 2 |
|
import Enum, only: [reduce: 3] |
| 3 |
|
import Macro, only: [prewalk: 3] |
| 4 |
|
|
| 5 |
|
def bound(ast) do |
| 6 |
:-( |
{_ast, bound} = prewalk(ast, [], &bind/2) |
| 7 |
:-( |
bound |
| 8 |
|
end |
| 9 |
|
|
| 10 |
|
def refs(ast) do |
| 11 |
:-( |
{_ast, refs} = prewalk(ast, [], &mark/2) |
| 12 |
:-( |
refs |
| 13 |
|
end |
| 14 |
|
|
| 15 |
:-( |
defp bind({:=, _meta, [pattern, _expr]}, bound) do |
| 16 |
|
{nil, names(pattern, bound)} |
| 17 |
|
end |
| 18 |
|
|
| 19 |
:-( |
defp bind(node, bound) do |
| 20 |
|
{node, bound} |
| 21 |
|
end |
| 22 |
|
|
| 23 |
:-( |
defp names({:_, _, nil}, acc), do: acc |
| 24 |
:-( |
defp names({nil, _, nil}, acc), do: acc |
| 25 |
:-( |
defp names({true, _, nil}, acc), do: acc |
| 26 |
:-( |
defp names({false, _, nil}, acc), do: acc |
| 27 |
|
|
| 28 |
:-( |
defp names({name, _, nil}, acc) when is_atom(name) do |
| 29 |
:-( |
[to_string(name) | acc] |
| 30 |
|
end |
| 31 |
|
|
| 32 |
|
defp names({_name, _, args}, acc) when is_list(args) do |
| 33 |
:-( |
reduce(args, acc, &names/2) |
| 34 |
|
end |
| 35 |
|
|
| 36 |
|
defp names({left, right}, acc) do |
| 37 |
:-( |
names(left, names(right, acc)) |
| 38 |
|
end |
| 39 |
|
|
| 40 |
|
defp names(list, acc) when is_list(list) do |
| 41 |
:-( |
reduce(list, acc, &names/2) |
| 42 |
|
end |
| 43 |
|
|
| 44 |
:-( |
defp names(_node, acc), do: acc |
| 45 |
|
|
| 46 |
:-( |
defp mark({nil, _meta, nil}, refs), do: {nil, refs} |
| 47 |
:-( |
defp mark({true, _meta, nil}, refs), do: {true, refs} |
| 48 |
:-( |
defp mark({false, _meta, nil}, refs), do: {false, refs} |
| 49 |
|
|
| 50 |
:-( |
defp mark({name, _meta, nil}, refs) when is_atom(name) do |
| 51 |
:-( |
{nil, [to_string(name) | refs]} |
| 52 |
|
end |
| 53 |
|
|
| 54 |
:-( |
defp mark(node, refs), do: {node, refs} |
| 55 |
|
end |