Current section

Files

Jump to
graded src graded@internal@annotation.erl
Raw

src/graded@internal@annotation.erl

-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, <<Cur/binary, Char/binary>>, Depth + 1};
<<"]"/utf8>> ->
{Segs, <<Cur/binary, Char/binary>>, Depth - 1};
_ ->
{Segs, <<Cur/binary, Char/binary>>, 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)),
<<<<<<<<<<Prefix/binary, " "/utf8>>/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) -> <<Content/binary, "\n"/utf8>> end)(_pipe@13).