defmodule Kinda.CodeGen.KindDecl do require Logger @primitive_types ~w{ bool c_int c_uint f32 f64 i16 i32 i64 i8 isize u16 u32 u64 u8 usize }a @opaque_ptr {:optional_type, {:ref, [:*, :anyopaque]}} @opaque_array {:optional_type, {:ref, [:*, :const, :anyopaque]}} def primitive_types do @primitive_types end @primitive_types_set MapSet.new(@primitive_types) def is_primitive_type?(t) do MapSet.member?(@primitive_types_set, t) end def is_opaque_ptr?(t) do t == @opaque_ptr or t == @opaque_array end # @type t() :: %__MODULE__{ kind_name: atom(), zig_t: String.t(), module_name: atom(), fields: list(atom()), kind_functions: list({atom(), integer()}) } defstruct kind_name: nil, zig_t: nil, module_name: nil, fields: [], kind_functions: [] defp module_basename(%__MODULE__{module_name: module_name}) do module_name |> Module.split() |> List.last() |> String.to_atom() end defp module_basename("c.struct_" <> struct_name) do struct_name |> String.to_atom() end defp module_basename("isize") do :ISize end defp module_basename("usize") do :USize end defp module_basename("c_int") do :CInt end defp module_basename("c_uint") do :CUInt end defp module_basename("[*c]const u8") do :CString end defp module_basename(@opaque_ptr) do :OpaquePtr end defp module_basename(@opaque_array) do :OpaqueArray end # zig 0.9 defp module_basename("?fn(" <> _ = fn_name) do raise "need module name for function type: #{fn_name}" end # zig 0.10 defp module_basename("?*const fn(" <> _ = fn_name) do raise "need module name for function type: #{fn_name}" end defp module_basename(type) when is_atom(type) do type |> Atom.to_string() |> module_basename() end defp module_basename(type) when is_binary(type) do type |> Macro.camelize() |> String.to_atom() end def default(root_module, type) when is_atom(type) or type in [@opaque_ptr, @opaque_array] do {:ok, %__MODULE__{zig_t: type, module_name: Module.concat(root_module, module_basename(type))}} end def default(root_module, type) do Logger.error( "Code generation for #{inspect(root_module)} not implemented for type:\n#{inspect(type, pretty: true)}" ) raise "Code gen not implemented" end defp dump_zig_type(@opaque_ptr) do {:ok, "?*anyopaque"} end defp dump_zig_type(@opaque_array) do {:ok, "?*const anyopaque"} end defp dump_zig_type(t) when t in @primitive_types do {:ok, Atom.to_string(t)} end defp dump_zig_type(t) when is_atom(t) do {:ok, "c." <> Atom.to_string(t)} end defp dump_zig_type(t) when is_atom(t) do {:ok, "c." <> Atom.to_string(t)} end defp dump_zig_type(_t) do :error end defp zig_type(%__MODULE__{ zig_t: zig_t, kind_name: kind_name }) do with {:ok, t} <- dump_zig_type(zig_t) do t else _ -> "c.#{kind_name}" end end def gen_resource_kind(%__MODULE__{module_name: module_name, kind_name: kind_name} = k) do """ pub const #{kind_name} = kinda.ResourceKind(#{zig_type(k)}, "#{module_name}"); """ end end