Current section
Files
Jump to
Current section
Files
lib/makina/state/attribute.ex
defmodule Makina.State.Attribute do
@moduledoc false
##################################################################################################
# Macros
##################################################################################################
@doc """
This macro imports the macros defined in this module.
## Examples
iex> defmodule Example do
...> use Makina.State.Attribute
...> end
"""
@spec __using__(Keyword.t()) :: Macro.t()
defmacro __using__(_options) do
quote do
import unquote(__MODULE__), only: :macros
end
end
@doc """
This macro populates the module with attribute information.
## Examples
iex> defmodule Example do
...> use Makina.State.Attribute
...> attribute :attr1, 0, integer()
...> end
"""
@doc_template ~S"""
Defines the initial value of the attribute `<%= name %>`.
"""
@spec attribute(atom(), Macro.t(), Macro.t()) :: Macro.t()
defmacro attribute(name, init, type) do
docs = EEx.eval_string(@doc_template, name: name)
symbolic = Macro.postwalk(type, &symbolic_type/1)
dynamic = Macro.postwalk(type, &dynamic_type/1)
quote do
@type symbolic() :: unquote(symbolic)
@type dynamic() :: unquote(dynamic)
@type symbolic_expr() :: Makina.Types.symbolic_expr()
@doc unquote(docs)
@spec init() :: symbolic()
def init(), do: unquote(init)
@spec __makina_info__() :: Makina.State.attribute_info()
def __makina_info__() do
%{
name: unquote(name),
type: unquote(Macro.escape(type)),
symbolic: unquote(Macro.escape(symbolic)),
dynamic: unquote(Macro.escape(dynamic)),
module: unquote(__CALLER__.module)
}
end
end
end
##################################################################################################
# AST Helpers
##################################################################################################
@doc """
This function removes top-level `symbolic` annotations from a type definition. It must be used
with function that traverses the AST, like `Macro.postwalk` to remove all the `symbolic`
annotations from the given type.
## Examples
iex> type = quote do: [symbolic(integer())]
iex> Macro.postwalk(type, &dynamic_type/1)
quote do: [integer()]
"""
@spec dynamic_type(Macro.t()) :: Macro.t()
def dynamic_type({:symbolic, _, [type]}), do: type
def dynamic_type(ast), do: ast
@doc """
This function replaces top-level `symbolic` annotations by the `symbolic_expr` type. It must be
used with a function that traverses the AST, like `Macro.prewalk` to remove all the `symbolic`
annotations from the given type.
## Examples
iex> type = quote do: [symbolic(integer())]
iex> Macro.postwalk(type, &symbolic_type/1)
quote do: [symbolic_expr()]
"""
@spec symbolic_type(Macro.t()) :: Macro.t()
def symbolic_type({:symbolic, _, [_type]}), do: quote(do: symbolic_expr())
def symbolic_type(ast), do: ast
end