defmodule Kinda.CodeGen do
@moduledoc """
Behavior for customizing your source code generation.
"""
alias Kinda.CodeGen.{KindDecl, NIFDecl}
defmacro __using__(opts) do
quote do
mod = Keyword.fetch!(unquote(opts), :with)
root = Keyword.fetch!(unquote(opts), :root)
forward = Keyword.fetch!(unquote(opts), :forward)
Kinda.CodeGen.nif_ast(mod.kinds(), mod.nifs(), root, forward)
|> then(&Module.eval_quoted(__MODULE__, &1))
end
end
@callback kinds() :: KindDecl.t()
@callback nifs() :: [{atom(), integer()}]
def kinds(), do: []
def nif_ast(kinds, nifs, root_module, forward_module) do
# generate stubs for generated NIFs
extra_kind_nifs =
kinds
|> Enum.map(&NIFDecl.from_resource_kind/1)
|> List.flatten()
for nif <- nifs ++ extra_kind_nifs do
nif =
case nif do
{wrapper_name, arity} when is_atom(wrapper_name) and is_integer(arity) ->
%NIFDecl{
wrapper_name: wrapper_name,
nif_name: Module.concat(root_module, wrapper_name),
arity: arity
}
%NIFDecl{} ->
nif
end
args_ast = Macro.generate_unique_arguments(nif.arity, __MODULE__)
%NIFDecl{wrapper_name: wrapper_name, nif_name: nif_name} = nif
wrapper_name =
if is_bitstring(wrapper_name) do
String.to_atom(wrapper_name)
else
wrapper_name
end
quote do
@doc false
def unquote(nif_name)(unquote_splicing(args_ast)),
do:
raise(
"NIF for resource kind is not implemented, or failed to load NIF library. Function: :\"#{unquote(nif_name)}\"/#{unquote(nif.arity)}"
)
def unquote(wrapper_name)(unquote_splicing(args_ast)) do
refs = Kinda.unwrap_ref([unquote_splicing(args_ast)])
ret = apply(__MODULE__, unquote(nif_name), refs)
unquote(forward_module).check!(ret)
end
end
end
|> List.flatten()
end
end