Current section

Files

Jump to
libero src libero@glance_type_resolver.erl
Raw

src/libero@glance_type_resolver.erl

-module(libero@glance_type_resolver).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/libero/glance_type_resolver.gleam").
-export([resolver_from_imports/1, type_to_field_type/4]).
-export_type([unsupported_type_policy/0, type_resolver/0, type_binding/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Convert parsed `glance.Type` values into Libero's shared `FieldType`.\n"
"\n"
" Seed discovery and codegen both need the same type-resolution rules for\n"
" imports, aliases, builtins, tuples, and unsupported syntax. This module\n"
" owns that conversion so codegen sees one structured representation\n"
" regardless of where a type reference appears in discovered source.\n"
).
-type unsupported_type_policy() :: {reject_unsupported, binary()} |
preserve_unsupported.
-opaque type_resolver() :: {type_resolver,
gleam@dict:dict(binary(), type_binding()),
gleam@dict:dict(binary(), binary())}.
-type type_binding() :: {type_binding, binary(), binary()}.
-file("src/libero/glance_type_resolver.gleam", 173).
-spec build_aliased(list(glance:definition(glance:import()))) -> gleam@dict:dict(binary(), binary()).
build_aliased(Imports) ->
gleam@list:fold(
Imports,
maps:new(),
fun(Acc, Def) ->
{definition, _, Imp} = Def,
Last_seg = libero@field_type:last_segment(erlang:element(3, Imp)),
Alias = case erlang:element(4, Imp) of
{some, {named, Name}} ->
Name;
_ ->
Last_seg
end,
gleam@dict:insert(Acc, Alias, erlang:element(3, Imp))
end
).
-file("src/libero/glance_type_resolver.gleam", 135).
-spec build_unqualified(list(glance:definition(glance:import()))) -> {ok,
gleam@dict:dict(binary(), type_binding())} |
{error, binary()}.
build_unqualified(Imports) ->
gleam@list:try_fold(
Imports,
maps:new(),
fun(Acc, Def) ->
{definition, _, Imp} = Def,
gleam@list:try_fold(
erlang:element(5, Imp),
Acc,
fun(Inner_acc, Uq) ->
Local_name = case erlang:element(3, Uq) of
{some, Alias} ->
Alias;
none ->
erlang:element(2, Uq)
end,
Binding = {type_binding,
erlang:element(3, Imp),
erlang:element(2, Uq)},
case gleam_stdlib:map_get(Inner_acc, Local_name) of
{error, nil} ->
{ok,
gleam@dict:insert(
Inner_acc,
Local_name,
Binding
)};
{ok, Existing} ->
case (erlang:element(2, Existing) =:= erlang:element(
2,
Binding
))
andalso (erlang:element(3, Existing) =:= erlang:element(
3,
Binding
)) of
true ->
{ok, Inner_acc};
false ->
{error,
<<<<<<<<<<<<<<<<<<"Ambiguous import: \""/utf8,
Local_name/binary>>/binary,
"\" is bound to "/utf8>>/binary,
(erlang:element(
2,
Existing
))/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(
3,
Existing
))/binary>>/binary,
" and "/utf8>>/binary,
(erlang:element(2, Binding))/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(3, Binding))/binary>>}
end
end
end
)
end
).
-file("src/libero/glance_type_resolver.gleam", 34).
-spec resolver_from_imports(list(glance:definition(glance:import()))) -> {ok,
type_resolver()} |
{error, binary()}.
resolver_from_imports(Imports) ->
gleam@result:'try'(
build_unqualified(Imports),
fun(Unqualified) ->
{ok, {type_resolver, Unqualified, build_aliased(Imports)}}
end
).
-file("src/libero/glance_type_resolver.gleam", 131).
-spec is_stdlib_module_path(binary()) -> boolean().
is_stdlib_module_path(Path) ->
(Path =:= <<"gleam"/utf8>>) orelse gleam_stdlib:string_starts_with(
Path,
<<"gleam/"/utf8>>
).
-file("src/libero/glance_type_resolver.gleam", 83).
-spec resolve_unqualified(
binary(),
list(glance:type()),
type_resolver(),
binary(),
unsupported_type_policy()
) -> {ok, libero@field_type:field_type()} | {error, binary()}.
resolve_unqualified(Name, Parameters, Resolver, Current_module, Policy) ->
Recurse = fun(Inner) ->
type_to_field_type(Inner, Resolver, Current_module, Policy)
end,
gleam@result:'try'(
gleam@list:try_map(Parameters, Recurse),
fun(Args) ->
Identity = fun(X) -> X end,
case gleam_stdlib:map_get(erlang:element(2, Resolver), Name) of
{ok, {type_binding, Module_path, Type_name}} ->
case is_stdlib_module_path(Module_path) of
true ->
case libero@field_type:builtin_field_type(
Name,
Args,
Identity
) of
{ok, Ft} ->
{ok, Ft};
{error, nil} ->
{ok,
{user_type,
Module_path,
Type_name,
Args}}
end;
false ->
{ok, {user_type, Module_path, Type_name, Args}}
end;
{error, nil} ->
case libero@field_type:builtin_field_type(
Name,
Args,
Identity
) of
{ok, Ft@1} ->
{ok, Ft@1};
{error, nil} ->
{ok, {user_type, Current_module, Name, Args}}
end
end
end
).
-file("src/libero/glance_type_resolver.gleam", 41).
-spec type_to_field_type(
glance:type(),
type_resolver(),
binary(),
unsupported_type_policy()
) -> {ok, libero@field_type:field_type()} | {error, binary()}.
type_to_field_type(T, Resolver, Current_module, Policy) ->
Recurse = fun(Inner) ->
type_to_field_type(Inner, Resolver, Current_module, Policy)
end,
case T of
{named_type, _, Name, none, Parameters} ->
resolve_unqualified(
Name,
Parameters,
Resolver,
Current_module,
Policy
);
{named_type, _, Name@1, {some, M}, Parameters@1} ->
Module_path = begin
_pipe = gleam_stdlib:map_get(erlang:element(3, Resolver), M),
gleam@result:unwrap(_pipe, M)
end,
gleam@result:'try'(
gleam@list:try_map(Parameters@1, Recurse),
fun(Args) -> {ok, {user_type, Module_path, Name@1, Args}} end
);
{tuple_type, _, Elements} ->
gleam@result:'try'(
gleam@list:try_map(Elements, Recurse),
fun(Elements@1) -> {ok, {tuple_of, Elements@1}} end
);
{variable_type, _, Name@2} ->
{ok, {type_var, Name@2}};
{function_type, _, _, _} ->
case Policy of
{reject_unsupported, Path} ->
{error,
<<"Unsupported function type in "/utf8, Path/binary>>};
preserve_unsupported ->
{ok, {type_var, <<"_fn"/utf8>>}}
end;
{hole_type, _, _} ->
case Policy of
{reject_unsupported, Path@1} ->
{error, <<"Unsupported hole type in "/utf8, Path@1/binary>>};
preserve_unsupported ->
{ok, {type_var, <<"_"/utf8>>}}
end
end.