Current section
Files
Jump to
Current section
Files
lib/hologram/compiler/normalizer.ex
defmodule Hologram.Compiler.Normalizer do
@moduledoc false
alias Hologram.Compiler.AST
alias Hologram.Compiler.Helpers
alias Hologram.Reflection
@doc """
Normalizes Elixir AST by ensuring that:
* single expression blocks are wrapped in __block__ tuple
* alias atoms are wrapped in __aliases__ tuple
* unquote fragments are expanded
## Examples
iex> ast = Code.string_to_quoted!("cond do true -> 123 end")
{:cond, [line: 1], [[do: [{:->, [line: 1], [[true], 987]}]]]}
iex> normalize(ast)
{:cond, [line: 1], [[do: [{:->, [line: 1], [[true], {:__block__, [], [987]}]}]]]}
"""
@spec normalize(AST.t()) :: AST.t()
def normalize(ast)
def normalize({:->, meta, [pattern, block]}) do
{:->, meta, [normalize(pattern), normalize_block(block)]}
end
def normalize({:__aliases__, meta, [module]} = ast) do
maybe_normalize_alias(module, meta, ast)
end
def normalize({marker, meta, [name, [do: block]]}) when marker in [:def, :defp] do
{marker, meta, [normalize(name), [do: normalize_block(block)]]}
end
def normalize({:defmodule, meta, [name, [do: block]]}) do
{:defmodule, meta, [normalize(name), [do: normalize_block(block)]]}
end
def normalize({:for, meta, parts}) when is_list(parts) do
{:for, meta, Enum.map(parts, &normalize_comprehension_part/1)}
end
def normalize({:try, meta, [opts]}) do
{:try, meta, [Enum.map(opts, &normalize_try_opt/1)]}
end
def normalize({{:unquote, _meta_1, [marker]}, meta_2, children}) do
{marker, meta_2, normalize(children)}
end
def normalize(ast) when is_atom(ast) do
maybe_normalize_alias(ast, [alias: false], ast)
end
def normalize(ast) when is_list(ast) do
Enum.map(ast, &normalize/1)
end
def normalize(ast) when is_tuple(ast) do
ast
|> Tuple.to_list()
|> normalize()
|> List.to_tuple()
end
def normalize(ast), do: ast
defp maybe_normalize_alias(module, meta, ast) do
if Reflection.alias?(module) do
segments = Helpers.alias_segments(module)
{:__aliases__, meta, segments}
else
ast
end
end
defp normalize_block({:__block__, meta, exprs}) do
{:__block__, meta, normalize(exprs)}
end
defp normalize_block(expr) do
{:__block__, [], [normalize(expr)]}
end
defp normalize_comprehension_opt({:do, block}) do
{:do, normalize_block(block)}
end
defp normalize_comprehension_opt(opt), do: normalize(opt)
defp normalize_comprehension_part(opts) when is_list(opts) do
Enum.map(opts, &normalize_comprehension_opt/1)
end
defp normalize_comprehension_part(part), do: normalize(part)
defp normalize_try_opt({:after, block}) do
{:after, normalize_block(block)}
end
defp normalize_try_opt({:do, block}) do
{:do, normalize_block(block)}
end
defp normalize_try_opt(opt), do: normalize(opt)
end