defmodule Moon.Convert.Ast do @moduledoc "Some helper functions for work with elixir module AST" # import Macro @doc "try Macro.to_string(function_ast(__MODULE__, :function_name))" def function_ast(mod, fun) do mod |> module_ast() |> Macro.prewalk(fn result = {:def, _, [{^fun, _, _} | _]} -> throw(result) other -> other end) :error_fetchig_ast catch result -> {:ok, result} end def module_ast(mod) do mod.__info__(:compile)[:source] |> to_string() |> File.read!() |> Code.string_to_quoted!() end def merge_ast( {:defmodule, m1, [ aliases, [ do: {:__block__, [], children} ] ]}, {:defmodule, _, [ _aliases, [ do: {:__block__, [], children2} ] ]} ) do {:defmodule, m1, [ aliases, [ do: {:__block__, [], (children ++ children2) |> Enum.sort_by(fn {:@, _, [{:moduledoc, _, _}]} -> 0 {:use, _, _} -> 1 {:alias, _, _} -> 2 {:import, _, _} -> 3 {:require, _, _} -> 4 _ -> 100 end)} ] ]} end def ast_aliases(ast, %{module: module}) do {_, aliases} = ast |> Macro.prewalk([], fn result = {:alias, _, [{:__aliases__, _, a} | _]}, acc -> {result, acc ++ [a]} result = {:alias, _, [{:__MODULE__, _, _}]}, acc -> {result, acc ++ [ module |> to_string() |> String.replace("Elixir.", "") |> String.split(".") |> Enum.map(&String.to_atom/1) ]} result = {:alias, _, [{{:., _, [{:__aliases__, _, root_part}, :{}]}, _, children}]}, acc -> {result, acc ++ (children |> Enum.map(fn {:__aliases__, _, sub_part} -> root_part ++ sub_part end))} other, acc -> {other, acc} end) aliases |> Enum.reduce(%{}, fn items, acc -> Map.put( acc, Enum.at(items, -1) |> to_string(), [:"Elixir" | items] |> Enum.map_join(".", &to_string/1) |> String.to_atom() ) end) end def parent_module(module) do module |> to_string() |> String.replace(~r/\.[A-Za-z]*$/, "") |> String.to_atom() end end