Current section

Files

Jump to
type_resolver lib parse_helpers.ex
Raw

lib/parse_helpers.ex

defmodule TypeResolver.ParseHelpers do
alias TypeResolver.Env
alias TypeResolver.ParseHelpers.AnnotatedType
alias TypeResolver.ParseHelpers.Literals
alias TypeResolver.ParseHelpers.ParametrizedType
alias TypeResolver.ParseHelpers.RemoteTypes
alias TypeResolver.ParseHelpers.SimpleType
alias TypeResolver.ParseHelpers.UserTypes
alias TypeResolver.ParseHelpers.Vars
def parse(expr, env) do
with {:error, :cannot_parse} <- SimpleType.parse(expr),
{:error, :cannot_parse} <- AnnotatedType.parse(expr, env),
{:error, :cannot_parse} <- ParametrizedType.parse(expr, env),
{:error, :cannot_parse} <- Literals.parse(expr, env),
{:error, :cannot_parse} <- RemoteTypes.parse(expr, env),
{:error, :cannot_parse} <- RemoteTypes.parse_user_defined(expr, env),
{:error, :cannot_parse} <- Vars.parse(expr, env),
{:error, :cannot_parse} <- UserTypes.parse(expr, env),
{:error, :cannot_parse} <- catch_struct(expr) do
{:error, :cannot_parse}
end
end
def resolve(env, type, args \\ []) do
Code.ensure_compiled(env.target_module)
{:type, {_name, t, vars}} =
case Code.Typespec.fetch_types(env.target_module) do
:error ->
exported_module = Module.concat(env.target_module, ExportedTypes)
case Code.ensure_compiled(exported_module) do
{:module, _} ->
exported_module.types()
|> Enum.find(fn {:type, {t, _, _}} -> t == type end)
{:error, _} ->
raise "no types can be found for type #{type} in module #{env.target_module}. Env: #{inspect(env)}"
end
{:ok, specs} ->
specs
|> Enum.find(fn {:type, {t, _, _}} -> t == type end)
end
t |> parse(env |> Env.with_args(prepare_args(vars, args)))
end
def parse_user_types(types) do
Enum.map(types, &parse_user_type/1)
end
def parse_user_type({:type, {:"::", _, [{name, _, params}, t]}, _}) do
{name, {t, params}}
end
def prepare_args(nil, _values), do: %{}
def prepare_args(vars, values) do
if Enum.count(vars) != Enum.count(values) do
raise "Error when counting var and args"
end
vars
|> Enum.map(fn
{:var, _, name} -> name
{name, _, nil} -> name
end)
|> Enum.zip(values)
|> Map.new()
end
def parse_args(args, env) when is_list(args) do
Enum.reduce(args, {:ok, []}, fn
arg, {:ok, ret} ->
with {:ok, parsed} <- parse(arg, env) do
{:ok, [parsed | ret]}
end
_arg, {:error, _} = err ->
err
end)
end
def parse_args(_, _env), do: {:error, :cannot_parse}
@doc !"""
We catch structs since already parsed parts can end up in the
expression. Only already parsed sub expressions are structs, else
there are none in AST.
"""
defp catch_struct(%_{} = s), do: {:ok, s}
defp catch_struct(_), do: {:error, :cannot_parse}
end