-module(graded@internal@annotation). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/graded/internal/annotation.gleam"). -export([extract_annotations/1, extract_checks/1, extract_type_fields/1, extract_externals/1, merge_inferred/2, parse_file/1, parse/1, format_annotation/1, format_type_field/1, format_external/1, format_file/1, format_sorted/1]). -export_type([parse_error/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 parse_error() :: {invalid_line, integer(), binary()}. -file("src/graded/internal/annotation.gleam", 39). ?DOC(false). -spec extract_annotations(graded@internal@types:graded_file()) -> list(graded@internal@types:effect_annotation()). extract_annotations(File) -> gleam@list:filter_map(erlang:element(2, File), fun(Line) -> case Line of {annotation_line, Annotation} -> {ok, Annotation}; {type_field_line, _} -> {error, nil}; {external_line, _} -> {error, nil}; {comment_line, _} -> {error, nil}; blank_line -> {error, nil} end end). -file("src/graded/internal/annotation.gleam", 52). ?DOC(false). -spec extract_checks(graded@internal@types:graded_file()) -> list(graded@internal@types:effect_annotation()). extract_checks(File) -> _pipe = extract_annotations(File), gleam@list:filter( _pipe, fun(Annotation) -> erlang:element(2, Annotation) =:= check end ). -file("src/graded/internal/annotation.gleam", 95). ?DOC(false). -spec extract_type_fields(graded@internal@types:graded_file()) -> list(graded@internal@types:type_field_annotation()). extract_type_fields(File) -> gleam@list:filter_map(erlang:element(2, File), fun(Line) -> case Line of {type_field_line, Tf} -> {ok, Tf}; _ -> {error, nil} end end). -file("src/graded/internal/annotation.gleam", 105). ?DOC(false). -spec external_sort_key(graded@internal@types:external_annotation()) -> binary(). external_sort_key(External_annotation) -> case erlang:element(3, External_annotation) of module_external -> erlang:element(2, External_annotation); {function_external, Function} -> <<<<(erlang:element(2, External_annotation))/binary, "."/utf8>>/binary, Function/binary>> end. -file("src/graded/internal/annotation.gleam", 124). ?DOC(false). -spec extract_externals(graded@internal@types:graded_file()) -> list(graded@internal@types:external_annotation()). extract_externals(File) -> gleam@list:filter_map(erlang:element(2, File), fun(Line) -> case Line of {external_line, External_annotation} -> {ok, External_annotation}; _ -> {error, nil} end end). -file("src/graded/internal/annotation.gleam", 155). ?DOC(false). -spec merge_inferred( graded@internal@types:graded_file(), list(graded@internal@types:effect_annotation()) ) -> graded@internal@types:graded_file(). merge_inferred(File, Inferred) -> Inferred_map = begin _pipe = Inferred, _pipe@1 = gleam@list:map( _pipe, fun(Annotation) -> {erlang:element(3, Annotation), Annotation} end ), maps:from_list(_pipe@1) end, {New_lines, Placed} = gleam@list:fold( erlang:element(2, File), {[], gleam@set:new()}, fun(State, Line) -> {Lines, Placed_set} = State, case Line of {annotation_line, Annotation@1} -> case erlang:element(2, Annotation@1) of effects -> case gleam_stdlib:map_get( Inferred_map, erlang:element(3, Annotation@1) ) of {ok, New_annotation} -> {[{annotation_line, New_annotation} | Lines], gleam@set:insert( Placed_set, erlang:element(3, Annotation@1) )}; {error, nil} -> {Lines, Placed_set} end; check -> {[Line | Lines], Placed_set} end; {type_field_line, _} -> {[Line | Lines], Placed_set}; {external_line, _} -> {[Line | Lines], Placed_set}; {comment_line, _} -> {[Line | Lines], Placed_set}; blank_line -> {[Line | Lines], Placed_set} end end ), Remaining = begin _pipe@2 = Inferred, _pipe@3 = gleam@list:filter( _pipe@2, fun(Annotation@2) -> not gleam@set:contains(Placed, erlang:element(3, Annotation@2)) end ), gleam@list:map(_pipe@3, fun(Field@0) -> {annotation_line, Field@0} end) end, {graded_file, lists:append(lists:reverse(New_lines), Remaining)}. -file("src/graded/internal/annotation.gleam", 421). ?DOC(false). -spec split_at_top_level_commas(binary()) -> list(binary()). split_at_top_level_commas(Input) -> {Segments, Current, _} = gleam@list:fold( gleam@string:to_graphemes(Input), {[], <<""/utf8>>, 0}, fun(State, Char) -> {Segs, Cur, Depth} = State, case Char of <<","/utf8>> when Depth =:= 0 -> {[Cur | Segs], <<""/utf8>>, Depth}; <<"["/utf8>> -> {Segs, <>, Depth + 1}; <<"]"/utf8>> -> {Segs, <>, Depth - 1}; _ -> {Segs, <>, Depth} end end ), lists:reverse([Current | Segments]). -file("src/graded/internal/annotation.gleam", 435). ?DOC(false). -spec parse_effect_set(binary()) -> {ok, graded@internal@types:effect_set()} | {error, nil}. parse_effect_set(Input) -> Trimmed = gleam@string:trim(Input), Has_brackets = gleam_stdlib:string_starts_with(Trimmed, <<"["/utf8>>) andalso gleam_stdlib:string_ends_with(Trimmed, <<"]"/utf8>>), gleam@bool:guard( not Has_brackets, {error, nil}, fun() -> Inner = begin _pipe = Trimmed, _pipe@1 = gleam@string:drop_start(_pipe, 1), _pipe@2 = gleam@string:drop_end(_pipe@1, 1), gleam@string:trim(_pipe@2) end, case Inner of <<"_"/utf8>> -> {ok, wildcard}; <<""/utf8>> -> {ok, {specific, gleam@set:new()}}; _ -> _pipe@3 = Inner, _pipe@4 = gleam@string:split(_pipe@3, <<","/utf8>>), _pipe@5 = gleam@list:map(_pipe@4, fun gleam@string:trim/1), _pipe@6 = gleam@list:filter( _pipe@5, fun(Label) -> Label /= <<""/utf8>> end ), _pipe@7 = gleam@set:from_list(_pipe@6), _pipe@8 = {specific, _pipe@7}, {ok, _pipe@8} end end ). -file("src/graded/internal/annotation.gleam", 408). ?DOC(false). -spec parse_name_colon_effects(binary()) -> {ok, {binary(), graded@internal@types:effect_set()}} | {error, nil}. parse_name_colon_effects(Input) -> case gleam@string:split(gleam@string:trim(Input), <<":"/utf8>>) of [Name_part, Effects_part] -> Name = gleam@string:trim(Name_part), gleam@bool:guard( Name =:= <<""/utf8>>, {error, nil}, fun() -> gleam@result:'try'( parse_effect_set(gleam@string:trim(Effects_part)), fun(Effects) -> {ok, {Name, Effects}} end ) end ); _ -> {error, nil} end. -file("src/graded/internal/annotation.gleam", 363). ?DOC(false). -spec parse_type_field_line(binary()) -> {ok, graded@internal@types:type_field_annotation()} | {error, nil}. parse_type_field_line(Rest) -> gleam@result:'try'( parse_name_colon_effects(Rest), fun(_use0) -> {Qualified, Effects} = _use0, case gleam@string:split(Qualified, <<"."/utf8>>) of [Type_name, Field] when (Type_name =/= <<""/utf8>>) andalso (Field =/= <<""/utf8>>) -> {ok, {type_field_annotation, Type_name, Field, Effects}}; _ -> {error, nil} end end ). -file("src/graded/internal/annotation.gleam", 375). ?DOC(false). -spec parse_external_line(binary()) -> {ok, graded@internal@types:external_annotation()} | {error, nil}. parse_external_line(Rest) -> gleam@result:'try'( parse_name_colon_effects(Rest), fun(_use0) -> {Qualified, Effects} = _use0, Segments = gleam@string:split(Qualified, <<"."/utf8>>), Len = erlang:length(Segments), case Len of 1 -> {ok, {external_annotation, Qualified, module_external, Effects}}; _ -> gleam@result:'try'( gleam@list:last(Segments), fun(Function) -> Module = begin _pipe = Segments, _pipe@1 = gleam@list:take(_pipe, Len - 1), gleam@string:join(_pipe@1, <<"."/utf8>>) end, {ok, {external_annotation, Module, {function_external, Function}, Effects}} end ) end end ). -file("src/graded/internal/annotation.gleam", 402). ?DOC(false). -spec parse_single_param(binary()) -> {ok, graded@internal@types:param_bound()} | {error, nil}. parse_single_param(Input) -> gleam@result:'try'( parse_name_colon_effects(Input), fun(_use0) -> {Name, Effects} = _use0, {ok, {param_bound, Name, Effects}} end ). -file("src/graded/internal/annotation.gleam", 394). ?DOC(false). -spec parse_params_section(binary()) -> {ok, list(graded@internal@types:param_bound())} | {error, nil}. parse_params_section(Input) -> case gleam@string:trim(Input) of <<""/utf8>> -> {ok, []}; Trimmed -> gleam@list:try_map( split_at_top_level_commas(Trimmed), fun parse_single_param/1 ) end. -file("src/graded/internal/annotation.gleam", 342). ?DOC(false). -spec parse_params_suffix( graded@internal@types:annotation_kind(), binary(), binary(), binary() ) -> {ok, graded@internal@types:effect_annotation()} | {error, nil}. parse_params_suffix(Kind, Name, Params_str, Suffix) -> Suffix_trimmed = gleam@string:trim(Suffix), case gleam_stdlib:string_starts_with(Suffix_trimmed, <<":"/utf8>>) of false -> {error, nil}; true -> Effects_str = gleam@string:trim( gleam@string:drop_start(Suffix_trimmed, 1) ), case {parse_effect_set(Effects_str), parse_params_section(Params_str)} of {{ok, Effects}, {ok, Params}} -> {ok, {effect_annotation, Kind, Name, Params, Effects}}; {_, _} -> {error, nil} end end. -file("src/graded/internal/annotation.gleam", 326). ?DOC(false). -spec parse_params_annotation( graded@internal@types:annotation_kind(), binary(), list(binary()) ) -> {ok, graded@internal@types:effect_annotation()} | {error, nil}. parse_params_annotation(Kind, Name_part, Rest_parts) -> Name = gleam@string:trim(Name_part), gleam@bool:guard( Name =:= <<""/utf8>>, {error, nil}, fun() -> Rejoined = gleam@string:join(Rest_parts, <<"("/utf8>>), case gleam@string:split(Rejoined, <<")"/utf8>>) of [Params_str, Suffix | _] -> parse_params_suffix(Kind, Name, Params_str, Suffix); _ -> {error, nil} end end ). -file("src/graded/internal/annotation.gleam", 301). ?DOC(false). -spec parse_annotation_rest( graded@internal@types:annotation_kind(), binary(), integer(), binary() ) -> {ok, graded@internal@types:effect_annotation()} | {error, parse_error()}. parse_annotation_rest(Kind, Rest, Line_number, Original) -> Err = {error, {invalid_line, Line_number, Original}}, case gleam@string:split(Rest, <<"("/utf8>>) of [] -> Err; [No_params] -> case parse_name_colon_effects(No_params) of {error, nil} -> Err; {ok, {Name, Effects}} -> {ok, {effect_annotation, Kind, Name, [], Effects}} end; [Name_part | Rest_parts] -> _pipe = parse_params_annotation(Kind, Name_part, Rest_parts), gleam@result:replace_error( _pipe, {invalid_line, Line_number, Original} ) end. -file("src/graded/internal/annotation.gleam", 284). ?DOC(false). -spec parse_annotation_line(binary(), integer(), binary()) -> {ok, graded@internal@types:effect_annotation()} | {error, parse_error()}. parse_annotation_line(Trimmed, Line_number, Original) -> {Kind, Rest} = case Trimmed of <<"effects "/utf8, Remaining/binary>> -> {{ok, effects}, Remaining}; <<"check "/utf8, Remaining@1/binary>> -> {{ok, check}, Remaining@1}; _ -> {{error, nil}, <<""/utf8>>} end, case Kind of {error, nil} -> {error, {invalid_line, Line_number, Original}}; {ok, Parsed_kind} -> parse_annotation_rest(Parsed_kind, Rest, Line_number, Original) end. -file("src/graded/internal/annotation.gleam", 257). ?DOC(false). -spec parse_structured_line(binary(), integer()) -> {ok, graded@internal@types:graded_line()} | {error, parse_error()}. parse_structured_line(Line, Line_number) -> Trimmed = gleam@string:trim(Line), case Trimmed of <<""/utf8>> -> {ok, blank_line}; <<"//"/utf8, _/binary>> -> {ok, {comment_line, Line}}; <<"effects "/utf8, _/binary>> -> case parse_annotation_line(Trimmed, Line_number, Line) of {ok, Annotation} -> {ok, {annotation_line, Annotation}}; {error, Parse_error} -> {error, Parse_error} end; <<"check "/utf8, _/binary>> -> case parse_annotation_line(Trimmed, Line_number, Line) of {ok, Annotation} -> {ok, {annotation_line, Annotation}}; {error, Parse_error} -> {error, Parse_error} end; <<"type "/utf8, Rest/binary>> -> case parse_type_field_line(Rest) of {ok, Tf} -> {ok, {type_field_line, Tf}}; {error, nil} -> {error, {invalid_line, Line_number, Line}} end; <<"external effects "/utf8, Rest@1/binary>> -> case parse_external_line(Rest@1) of {ok, Ext} -> {ok, {external_line, Ext}}; {error, nil} -> {error, {invalid_line, Line_number, Line}} end; _ -> {error, {invalid_line, Line_number, Line}} end. -file("src/graded/internal/annotation.gleam", 21). ?DOC(false). -spec parse_file(binary()) -> {ok, graded@internal@types:graded_file()} | {error, parse_error()}. parse_file(Input) -> _pipe = Input, _pipe@1 = gleam@string:split(_pipe, <<"\n"/utf8>>), _pipe@2 = gleam@list:index_map( _pipe@1, fun(Line, Index) -> {Index + 1, Line} end ), _pipe@3 = gleam@list:try_map( _pipe@2, fun(Pair) -> {Line_number, Line@1} = Pair, parse_structured_line(Line@1, Line_number) end ), gleam@result:map(_pipe@3, fun(Lines) -> {graded_file, Lines} end). -file("src/graded/internal/annotation.gleam", 33). ?DOC(false). -spec parse(binary()) -> {ok, list(graded@internal@types:effect_annotation())} | {error, parse_error()}. parse(Input) -> gleam@result:'try'( parse_file(Input), fun(File) -> {ok, extract_annotations(File)} end ). -file("src/graded/internal/annotation.gleam", 459). ?DOC(false). -spec collect_comments(list(graded@internal@types:graded_line())) -> list(binary()). collect_comments(Lines) -> gleam@list:filter_map(Lines, fun(Line) -> case Line of {comment_line, Text} -> {ok, Text}; _ -> {error, nil} end end). -file("src/graded/internal/annotation.gleam", 468). ?DOC(false). -spec format_effect_set(graded@internal@types:effect_set()) -> binary(). format_effect_set(Effect_set) -> case Effect_set of wildcard -> <<"[_]"/utf8>>; {specific, Labels} -> case begin _pipe = gleam@set:to_list(Labels), gleam@list:sort(_pipe, fun gleam@string:compare/2) end of [] -> <<"[]"/utf8>>; Sorted -> <<<<"["/utf8, (gleam@string:join(Sorted, <<", "/utf8>>))/binary>>/binary, "]"/utf8>> end end. -file("src/graded/internal/annotation.gleam", 58). ?DOC(false). -spec format_annotation(graded@internal@types:effect_annotation()) -> binary(). format_annotation(Annotation) -> Prefix = case erlang:element(2, Annotation) of effects -> <<"effects"/utf8>>; check -> <<"check"/utf8>> end, Params_string = case erlang:element(4, Annotation) of [] -> <<""/utf8>>; Params -> <<<<"("/utf8, (gleam@string:join( gleam@list:map( Params, fun(Param) -> <<<<(erlang:element(2, Param))/binary, ": "/utf8>>/binary, (format_effect_set(erlang:element(3, Param)))/binary>> end ), <<", "/utf8>> ))/binary>>/binary, ")"/utf8>> end, Effects_string = format_effect_set(erlang:element(5, Annotation)), <<<<<<<<<>/binary, (erlang:element(3, Annotation))/binary>>/binary, Params_string/binary>>/binary, " : "/utf8>>/binary, Effects_string/binary>>. -file("src/graded/internal/annotation.gleam", 85). ?DOC(false). -spec format_type_field(graded@internal@types:type_field_annotation()) -> binary(). format_type_field(Tf) -> <<<<<<<<<<"type "/utf8, (erlang:element(2, Tf))/binary>>/binary, "."/utf8>>/binary, (erlang:element(3, Tf))/binary>>/binary, " : "/utf8>>/binary, (format_effect_set(erlang:element(4, Tf)))/binary>>. -file("src/graded/internal/annotation.gleam", 112). ?DOC(false). -spec format_external(graded@internal@types:external_annotation()) -> binary(). format_external(External_annotation) -> Name = case erlang:element(3, External_annotation) of module_external -> erlang:element(2, External_annotation); {function_external, Function} -> <<<<(erlang:element(2, External_annotation))/binary, "."/utf8>>/binary, Function/binary>> end, <<<<<<"external effects "/utf8, Name/binary>>/binary, " : "/utf8>>/binary, (format_effect_set(erlang:element(4, External_annotation)))/binary>>. -file("src/graded/internal/annotation.gleam", 134). ?DOC(false). -spec format_file(graded@internal@types:graded_file()) -> binary(). format_file(File) -> _pipe = erlang:element(2, File), _pipe@1 = gleam@list:map(_pipe, fun(Line) -> case Line of {annotation_line, Annotation} -> format_annotation(Annotation); {type_field_line, Tf} -> format_type_field(Tf); {external_line, Ext} -> format_external(Ext); {comment_line, Text} -> Text; blank_line -> <<""/utf8>> end end), gleam@string:join(_pipe@1, <<"\n"/utf8>>). -file("src/graded/internal/annotation.gleam", 203). ?DOC(false). -spec format_sorted(graded@internal@types:graded_file()) -> binary(). format_sorted(File) -> Comments = collect_comments(erlang:element(2, File)), Annotations = extract_annotations(File), Check_lines = begin _pipe = Annotations, _pipe@1 = gleam@list:filter( _pipe, fun(Annotation) -> erlang:element(2, Annotation) =:= check end ), _pipe@2 = gleam@list:sort( _pipe@1, fun(Left, Right) -> gleam@string:compare( erlang:element(3, Left), erlang:element(3, Right) ) end ), gleam@list:map(_pipe@2, fun format_annotation/1) end, Effects_lines = begin _pipe@3 = Annotations, _pipe@4 = gleam@list:filter( _pipe@3, fun(Annotation@1) -> erlang:element(2, Annotation@1) =:= effects end ), _pipe@5 = gleam@list:sort( _pipe@4, fun(Left@1, Right@1) -> gleam@string:compare( erlang:element(3, Left@1), erlang:element(3, Right@1) ) end ), gleam@list:map(_pipe@5, fun format_annotation/1) end, Type_field_lines = begin _pipe@6 = extract_type_fields(File), _pipe@7 = gleam@list:sort( _pipe@6, fun(Left@2, Right@2) -> gleam@string:compare( <<<<(erlang:element(2, Left@2))/binary, "."/utf8>>/binary, (erlang:element(3, Left@2))/binary>>, <<<<(erlang:element(2, Right@2))/binary, "."/utf8>>/binary, (erlang:element(3, Right@2))/binary>> ) end ), gleam@list:map(_pipe@7, fun format_type_field/1) end, External_lines = begin _pipe@8 = extract_externals(File), _pipe@9 = gleam@list:sort( _pipe@8, fun(Left@3, Right@3) -> gleam@string:compare( external_sort_key(Left@3), external_sort_key(Right@3) ) end ), gleam@list:map(_pipe@9, fun format_external/1) end, Sections = [Comments, External_lines, Type_field_lines, Check_lines, Effects_lines], _pipe@10 = Sections, _pipe@11 = gleam@list:filter(_pipe@10, fun(Section) -> Section /= [] end), _pipe@12 = gleam@list:map( _pipe@11, fun(Section@1) -> gleam@string:join(Section@1, <<"\n"/utf8>>) end ), _pipe@13 = gleam@string:join(_pipe@12, <<"\n\n"/utf8>>), (fun(Content) -> <> end)(_pipe@13).