Current section

Files

Jump to
zigler lib zig _parameter.ex
Raw

lib/zig/_parameter.ex

defmodule Zig.Parameter do
@moduledoc false
@enforce_keys [:cleanup]
defstruct @enforce_keys ++ ~w[in_out type sentinel]a
alias Zig.Options
alias Zig.Type
alias Zig.Type.Cpointer
import Type, only: [sigil_t: 2]
# information supplied by the user.
@type unmerged :: %__MODULE__{
cleanup: boolean,
in_out: boolean
}
# information obtained by semantic analysis. Cleanup must be present
# as the cleanup clause is inherited by the module rules cleanup.
@type sema :: %__MODULE__{
cleanup: boolean,
type: Type.t()
}
# type as merged after semantic analysis.
@type t :: %__MODULE__{
type: Type.t(),
cleanup: boolean,
in_out: boolean,
sentinel: boolean
}
@spec new(Zig.parameter_options(), Options.context()) :: unmerged
def new(options, context) do
options
|> List.wrap()
|> Options.normalize(:cleanup, Options.boolean_normalizer(noclean: false), context)
|> Options.normalize(:in_out, Options.boolean_normalizer(in_out: true), context)
|> Options.normalize(:sentinel, Options.boolean_normalizer(sentinel: true), context)
|> Options.scrub_non_keyword(context)
|> force_in_out_no_cleanup()
|> Keyword.put_new(:cleanup, true)
|> then(&struct!(__MODULE__, &1))
rescue
e in KeyError ->
Options.raise_with("was supplied the invalid option `#{e.key}`", context)
end
def force_in_out_no_cleanup(options) do
if options[:in_out] do
Keyword.put(options, :cleanup, false)
else
Keyword.put(options, :in_out, false)
end
end
# merging semantic analysis with unmerged options
@spec merge(sema, unmerged) :: t
def merge(sema, specified) do
%{merge_sentinel(sema, specified) | cleanup: specified.cleanup, in_out: specified.in_out}
end
defp merge_sentinel(sema, %{sentinel: nil}), do: sema
defp merge_sentinel(sema, %{sentinel: sentinel}) do
%{sema | type: set_sentinel(sema.type, sentinel)}
end
defp set_sentinel(type, sentinel) do
case {type, sentinel} do
{%Cpointer{child: ~t[u8]}, true} ->
%{type | sentinel: 0}
{%Cpointer{child: %Cpointer{}}, true} ->
%{type | sentinel: :null}
{type, false} ->
type
{type, sentinel} ->
raise CompileError,
description: "sentinel #{sentinel} not supported for type `#{Type.render_zig(type)}`"
end
end
# code rendering
alias Zig.Type.Pointer
def render_in_out_type(%{type: %Pointer{child: child}}), do: Type.render_zig(child)
def render_in_out_type(%{type: type}), do: Type.render_zig(type)
def needs_size?(parameter), do: Type.needs_size?(parameter.type)
def render_payload_options(parameter, index) do
parameter.type
|> Type.payload_options("arg#{index}")
|> then(fn
options when parameter.in_out -> Keyword.put(options, :in_out, "&arg#{index}")
options -> options
end)
|> assemble_payload_options
end
defp assemble_payload_options(options) do
[".{", Enum.map(options, fn {k, v} -> ~s(.#{k} = #{v},) end), "},"]
end
def render_cleanup(parameter, index) do
cond do
parameter.cleanup ->
Type.render_cleanup(parameter.type, index)
:else ->
".{.cleanup = false},"
end
end
end