-module(graded@internal@extract). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/graded/internal/extract.gleam"). -export([module_path_for_source/2, build_import_context/1, extract_calls/2]). -export_type([import_context/0, extract_result/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(false). -type import_context() :: {import_context, gleam@dict:dict(binary(), binary()), gleam@dict:dict(binary(), graded@internal@types:qualified_name())}. -type extract_result() :: {extract_result, list(graded@internal@types:resolved_call()), list(graded@internal@types:local_call()), list(graded@internal@types:field_call()), list(graded@internal@types:resolved_call()), gleam@dict:dict(integer(), list(graded@internal@types:call_argument()))}. -file("src/graded/internal/extract.gleam", 22). ?DOC(false). -spec module_path_for_source(binary(), binary()) -> binary(). module_path_for_source(Gleam_path, Source_directory) -> Prefix = <>, Relative = case gleam_stdlib:string_starts_with(Gleam_path, Prefix) of true -> gleam@string:drop_start(Gleam_path, string:length(Prefix)); false -> Gleam_path end, filepath:strip_extension(Relative). -file("src/graded/internal/extract.gleam", 110). ?DOC(false). -spec is_constructor_name(binary()) -> boolean(). is_constructor_name(Name) -> case gleam@string:first(Name) of {ok, Char} -> (Char =:= string:uppercase(Char)) andalso (Char /= string:lowercase( Char )); {error, nil} -> false end. -file("src/graded/internal/extract.gleam", 162). ?DOC(false). -spec qualified_call_lookup(binary(), binary(), glance:span(), import_context()) -> extract_result(). qualified_call_lookup(Alias, Function_name, Span, Context) -> case gleam_stdlib:map_get(erlang:element(2, Context), Alias) of {ok, Module_path} -> {extract_result, [{resolved_call, {qualified_name, Module_path, Function_name}, Span}], [], [], [], maps:new()}; {error, nil} -> {extract_result, [], [], [{field_call, Alias, Function_name, Span}], [], maps:new()} end. -file("src/graded/internal/extract.gleam", 225). ?DOC(false). -spec last_segment(binary()) -> binary(). last_segment(Module_path) -> _pipe = Module_path, _pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>), _pipe@2 = gleam@list:last(_pipe@1), gleam@result:unwrap(_pipe@2, Module_path). -file("src/graded/internal/extract.gleam", 58). ?DOC(false). -spec build_import_context(glance:module_()) -> import_context(). build_import_context(Module) -> {Aliases, Unqualified} = gleam@list:fold( erlang:element(2, Module), {maps:new(), maps:new()}, fun(State, Definition) -> Import_ = erlang:element(3, Definition), Module_path = erlang:element(3, Import_), Alias = case erlang:element(4, Import_) of {some, {named, Name}} -> Name; {some, {discarded, _}} -> last_segment(Module_path); none -> last_segment(Module_path) end, New_aliases = gleam@dict:insert( erlang:element(1, State), Alias, Module_path ), New_unqualified = gleam@list:fold( erlang:element(6, Import_), erlang:element(2, State), fun(Unqualified_map, Unqualified_import) -> Name@1 = case erlang:element(3, Unqualified_import) of {some, Alias_name} -> Alias_name; none -> erlang:element(2, Unqualified_import) end, gleam@dict:insert( Unqualified_map, Name@1, {qualified_name, Module_path, erlang:element(2, Unqualified_import)} ) end ), {New_aliases, New_unqualified} end ), {import_context, Aliases, Unqualified}. -file("src/graded/internal/extract.gleam", 493). ?DOC(false). -spec classify_expression(glance:expression(), import_context()) -> graded@internal@types:argument_value(). classify_expression(Expression, Context) -> case Expression of {field_access, _, {variable, _, Alias}, Function_name} -> case is_constructor_name(Function_name) of true -> constructor_ref; false -> case gleam_stdlib:map_get(erlang:element(2, Context), Alias) of {ok, Module_path} -> {function_ref, {qualified_name, Module_path, Function_name}}; {error, nil} -> other_expression end end; {variable, _, Name} -> case is_constructor_name(Name) of true -> constructor_ref; false -> case gleam_stdlib:map_get(erlang:element(3, Context), Name) of {ok, Qualified_name} -> {function_ref, Qualified_name}; {error, nil} -> {local_ref, Name} end end; _ -> other_expression end. -file("src/graded/internal/extract.gleam", 471). ?DOC(false). -spec classify_arguments( list(glance:field(glance:expression())), import_context(), integer() ) -> list(graded@internal@types:call_argument()). classify_arguments(Arguments, Context, Position_offset) -> gleam@list:index_map( Arguments, fun(Field, I) -> {Label@2, Expression} = case Field of {labelled_field, Label, _, Item} -> {{some, Label}, {some, Item}}; {shorthand_field, Label@1, _} -> {{some, Label@1}, none}; {unlabelled_field, Item@1} -> {none, {some, Item@1}} end, Value = case Expression of none -> other_expression; {some, Expr} -> classify_expression(Expr, Context) end, {call_argument, I + Position_offset, Label@2, Value} end ). -file("src/graded/internal/extract.gleam", 528). ?DOC(false). -spec attach_pipe_args( extract_result(), glance:span(), list(graded@internal@types:call_argument()) ) -> extract_result(). attach_pipe_args(Base, Span, Pipe_args) -> {extract_result, erlang:element(2, Base), erlang:element(3, Base), erlang:element(4, Base), erlang:element(5, Base), gleam@dict:insert( erlang:element(6, Base), erlang:element(2, Span), Pipe_args )}. -file("src/graded/internal/extract.gleam", 592). ?DOC(false). -spec empty() -> extract_result(). empty() -> {extract_result, [], [], [], [], maps:new()}. -file("src/graded/internal/extract.gleam", 117). ?DOC(false). -spec resolve_unqualified_call(binary(), glance:span(), import_context()) -> extract_result(). resolve_unqualified_call(Name, Span, Context) -> case is_constructor_name(Name) of true -> empty(); false -> case gleam_stdlib:map_get(erlang:element(3, Context), Name) of {ok, Qualified_name} -> {extract_result, [{resolved_call, Qualified_name, Span}], [], [], [], maps:new()}; {error, nil} -> {extract_result, [], [{local_call, Name, Span}], [], [], maps:new()} end end. -file("src/graded/internal/extract.gleam", 150). ?DOC(false). -spec resolve_qualified_call( binary(), binary(), glance:span(), import_context() ) -> extract_result(). resolve_qualified_call(Alias, Function_name, Span, Context) -> case is_constructor_name(Function_name) of true -> empty(); false -> qualified_call_lookup(Alias, Function_name, Span, Context) end. -file("src/graded/internal/extract.gleam", 204). ?DOC(false). -spec qualified_reference_lookup( binary(), binary(), glance:span(), import_context() ) -> extract_result(). qualified_reference_lookup(Alias, Function_name, Span, Context) -> case gleam_stdlib:map_get(erlang:element(2, Context), Alias) of {ok, Module_path} -> {extract_result, [], [], [], [{resolved_call, {qualified_name, Module_path, Function_name}, Span}], maps:new()}; {error, nil} -> empty() end. -file("src/graded/internal/extract.gleam", 192). ?DOC(false). -spec resolve_qualified_reference( binary(), binary(), glance:span(), import_context() ) -> extract_result(). resolve_qualified_reference(Alias, Function_name, Span, Context) -> case is_constructor_name(Function_name) of true -> empty(); false -> qualified_reference_lookup(Alias, Function_name, Span, Context) end. -file("src/graded/internal/extract.gleam", 602). ?DOC(false). -spec merge(extract_result(), extract_result()) -> extract_result(). merge(Left, Right) -> {extract_result, lists:append(erlang:element(2, Left), erlang:element(2, Right)), lists:append(erlang:element(3, Left), erlang:element(3, Right)), lists:append(erlang:element(4, Left), erlang:element(4, Right)), lists:append(erlang:element(5, Left), erlang:element(5, Right)), maps:merge(erlang:element(6, Left), erlang:element(6, Right))}. -file("src/graded/internal/extract.gleam", 541). ?DOC(false). -spec merge_with_args( extract_result(), extract_result(), glance:span(), list(graded@internal@types:call_argument()) ) -> extract_result(). merge_with_args(Call_result, Inner, Span, Args) -> Merged = merge(Call_result, Inner), {extract_result, erlang:element(2, Merged), erlang:element(3, Merged), erlang:element(4, Merged), erlang:element(5, Merged), gleam@dict:insert( erlang:element(6, Merged), erlang:element(2, Span), Args )}. -file("src/graded/internal/extract.gleam", 257). ?DOC(false). -spec extract_from_expression(glance:expression(), import_context()) -> extract_result(). extract_from_expression(Expression, Context) -> case Expression of {call, Span, {field_access, _, {variable, _, Alias}, Function_name}, Arguments} -> merge_with_args( resolve_qualified_call(Alias, Function_name, Span, Context), extract_from_arguments(Arguments, Context), Span, classify_arguments(Arguments, Context, 0) ); {call, Span@1, {variable, _, Name}, Arguments@1} -> merge_with_args( resolve_unqualified_call(Name, Span@1, Context), extract_from_arguments(Arguments@1, Context), Span@1, classify_arguments(Arguments@1, Context, 0) ); {call, _, Function_expression, Arguments@2} -> merge( extract_from_expression(Function_expression, Context), extract_from_arguments(Arguments@2, Context) ); {binary_operator, _, pipe, Left, Right} -> Pipe_arg = {call_argument, 0, none, classify_expression(Left, Context)}, merge( extract_from_expression(Left, Context), extract_pipe_target(Right, Context, [Pipe_arg]) ); {binary_operator, _, _, Left@1, Right@1} -> merge( extract_from_expression(Left@1, Context), extract_from_expression(Right@1, Context) ); {fn, _, _, _, Statements} -> extract_calls(Statements, Context); {block, _, Statements@1} -> extract_calls(Statements@1, Context); {'case', _, Subjects, Clauses} -> merge( fold_expressions(Subjects, Context), gleam@list:fold( Clauses, empty(), fun(Accumulated, Clause) -> merge(Accumulated, extract_from_clause(Clause, Context)) end ) ); {tuple, _, Elements} -> fold_expressions(Elements, Context); {list, _, Elements@1, Rest} -> merge_optional(fold_expressions(Elements@1, Context), Rest, Context); {negate_int, _, Value} -> extract_from_expression(Value, Context); {negate_bool, _, Value@1} -> extract_from_expression(Value@1, Context); {record_update, _, _, _, Record, _} -> extract_from_expression(Record, Context); {field_access, Span@2, {variable, _, Alias@1}, Function_name@1} -> resolve_qualified_reference( Alias@1, Function_name@1, Span@2, Context ); {field_access, _, Container, _} -> extract_from_expression(Container, Context); {tuple_index, _, Tuple, _} -> extract_from_expression(Tuple, Context); {fn_capture, _, _, Function_expression@1, Arguments_before, Arguments_after} -> merge( extract_from_expression(Function_expression@1, Context), merge( extract_from_arguments(Arguments_before, Context), extract_from_arguments(Arguments_after, Context) ) ); {echo, _, {some, Inner}, _} -> extract_from_expression(Inner, Context); {echo, _, none, _} -> empty(); {variable, Span@3, Name@1} -> case gleam_stdlib:map_get(erlang:element(3, Context), Name@1) of {ok, Qualified_name} -> {extract_result, [], [], [], [{resolved_call, Qualified_name, Span@3}], maps:new()}; {error, nil} -> empty() end; {int, _, _} -> empty(); {float, _, _} -> empty(); {string, _, _} -> empty(); {panic, _, _} -> empty(); {todo, _, _} -> empty(); {bit_string, _, _} -> empty() end. -file("src/graded/internal/extract.gleam", 99). ?DOC(false). -spec extract_calls(list(glance:statement()), import_context()) -> extract_result(). extract_calls(Statements, Context) -> gleam@list:fold( Statements, empty(), fun(Accumulated, Statement) -> merge(Accumulated, extract_from_statement(Statement, Context)) end ). -file("src/graded/internal/extract.gleam", 232). ?DOC(false). -spec extract_from_statement(glance:statement(), import_context()) -> extract_result(). extract_from_statement(Statement, Context) -> case Statement of {expression, Expression} -> extract_from_expression(Expression, Context); {assignment, _, _, _, _, Expression@1} -> extract_from_expression(Expression@1, Context); {use, _, _, Expression@2} -> extract_from_expression(Expression@2, Context); {assert, _, Expression@3, Message} -> Expression_result = extract_from_expression(Expression@3, Context), case Message of {some, Message_expression} -> merge( Expression_result, extract_from_expression(Message_expression, Context) ); none -> Expression_result end end. -file("src/graded/internal/extract.gleam", 461). ?DOC(false). -spec extract_from_clause(glance:clause(), import_context()) -> extract_result(). extract_from_clause(Clause, Context) -> Body_result = extract_from_expression(erlang:element(4, Clause), Context), case erlang:element(3, Clause) of {some, Guard} -> merge(Body_result, extract_from_expression(Guard, Context)); none -> Body_result end. -file("src/graded/internal/extract.gleam", 554). ?DOC(false). -spec extract_from_arguments( list(glance:field(glance:expression())), import_context() ) -> extract_result(). extract_from_arguments(Arguments, Context) -> gleam@list:fold( Arguments, empty(), fun(Accumulated, Field) -> Expression = case Field of {labelled_field, _, _, Item} -> {some, Item}; {shorthand_field, _, _} -> none; {unlabelled_field, Item@1} -> {some, Item@1} end, case Expression of {some, Inner} -> merge(Accumulated, extract_from_expression(Inner, Context)); none -> Accumulated end end ). -file("src/graded/internal/extract.gleam", 406). ?DOC(false). -spec extract_pipe_target( glance:expression(), import_context(), list(graded@internal@types:call_argument()) ) -> extract_result(). extract_pipe_target(Expression, Context, Pipe_args) -> case Expression of {field_access, Span, {variable, _, Alias}, Function_name} -> attach_pipe_args( resolve_qualified_call(Alias, Function_name, Span, Context), Span, Pipe_args ); {variable, Span@1, Name} -> attach_pipe_args( resolve_unqualified_call(Name, Span@1, Context), Span@1, Pipe_args ); {call, Span@2, {field_access, _, {variable, _, Alias@1}, Function_name@1}, Arguments} -> merge_with_args( resolve_qualified_call( Alias@1, Function_name@1, Span@2, Context ), extract_from_arguments(Arguments, Context), Span@2, lists:append( Pipe_args, classify_arguments(Arguments, Context, 1) ) ); {call, Span@3, {variable, _, Name@1}, Arguments@1} -> merge_with_args( resolve_unqualified_call(Name@1, Span@3, Context), extract_from_arguments(Arguments@1, Context), Span@3, lists:append( Pipe_args, classify_arguments(Arguments@1, Context, 1) ) ); _ -> extract_from_expression(Expression, Context) end. -file("src/graded/internal/extract.gleam", 571). ?DOC(false). -spec fold_expressions(list(glance:expression()), import_context()) -> extract_result(). fold_expressions(Expressions, Context) -> gleam@list:fold( Expressions, empty(), fun(Accumulated, Expression) -> merge(Accumulated, extract_from_expression(Expression, Context)) end ). -file("src/graded/internal/extract.gleam", 580). ?DOC(false). -spec merge_optional( extract_result(), gleam@option:option(glance:expression()), import_context() ) -> extract_result(). merge_optional(Base, Optional_expression, Context) -> case Optional_expression of {some, Expression} -> merge(Base, extract_from_expression(Expression, Context)); none -> Base end.