Current section

Files

Jump to
caffeine_lang src caffeine_lang@frontend@parser.erl
Raw

src/caffeine_lang@frontend@parser.erl

-module(caffeine_lang@frontend@parser).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/caffeine_lang/frontend/parser.gleam").
-export([parse_measurements_file/1, parse_expects_file/1]).
-export_type([parser_state/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.
-type parser_state() :: {parser_state,
list(caffeine_lang@frontend@token:positioned_token()),
integer(),
integer(),
integer(),
integer()}.
-file("src/caffeine_lang/frontend/parser.gleam", 143).
?DOC(" Consume current token and advance state.\n").
-spec advance(parser_state()) -> parser_state().
advance(State) ->
case erlang:element(2, State) of
[_ | Rest] ->
case Rest of
[{positioned_token, _, Line, Column} | _] ->
{parser_state,
Rest,
Line,
Column,
erlang:element(3, State),
erlang:element(4, State)};
[] ->
{parser_state,
Rest,
erlang:element(3, State),
erlang:element(4, State),
erlang:element(3, State),
erlang:element(4, State)}
end;
[] ->
State
end.
-file("src/caffeine_lang/frontend/parser.gleam", 135).
?DOC(" Peek at the current token without consuming it.\n").
-spec peek(parser_state()) -> caffeine_lang@frontend@token:token().
peek(State) ->
case erlang:element(2, State) of
[{positioned_token, Tok, _, _} | _] ->
Tok;
[] ->
e_o_f
end.
-file("src/caffeine_lang/frontend/parser.gleam", 110).
-spec consume_comments_loop(
parser_state(),
list(caffeine_lang@frontend@ast:comment())
) -> {list(caffeine_lang@frontend@ast:comment()), parser_state()}.
consume_comments_loop(State, Acc) ->
case peek(State) of
{comment_line, Text} ->
consume_comments_loop(advance(State), [{line_comment, Text} | Acc]);
{comment_section, Text@1} ->
consume_comments_loop(
advance(State),
[{section_comment, Text@1} | Acc]
);
{comment_doc, Text@2} ->
consume_comments_loop(advance(State), [{doc_comment, Text@2} | Acc]);
_ ->
{lists:reverse(Acc), State}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 106).
?DOC(" Consume consecutive comment tokens from the stream, returning them as Comment list.\n").
-spec consume_comments(parser_state()) -> {list(caffeine_lang@frontend@ast:comment()),
parser_state()}.
consume_comments(State) ->
consume_comments_loop(State, []).
-file("src/caffeine_lang/frontend/parser.gleam", 572).
-spec at_measurement_item_boundary(caffeine_lang@frontend@token:token()) -> boolean().
at_measurement_item_boundary(Tok) ->
case Tok of
{literal_string, _} ->
true;
e_o_f ->
true;
_ ->
false
end.
-file("src/caffeine_lang/frontend/parser.gleam", 558).
?DOC(
" Advance the state until `peek(state)` satisfies `predicate`.\n"
" Used by recovery loops to resync to the next item/block boundary.\n"
).
-spec skip_until(
parser_state(),
fun((caffeine_lang@frontend@token:token()) -> boolean())
) -> parser_state().
skip_until(State, Predicate) ->
case Predicate(peek(State)) of
true ->
State;
false ->
skip_until(advance(State), Predicate)
end.
-file("src/caffeine_lang/frontend/parser.gleam", 168).
?DOC(" Expect a specific token, consuming it if matched.\n").
-spec expect(parser_state(), caffeine_lang@frontend@token:token(), binary()) -> {ok,
parser_state()} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
expect(State, Expected, Expected_name) ->
case peek(State) of
Tok when Tok =:= Expected ->
{ok, advance(State)};
Tok@1 ->
{error,
{unexpected_token,
Expected_name,
caffeine_lang@frontend@token:to_string(Tok@1),
erlang:element(5, State),
erlang:element(6, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 691).
-spec parse_identifier(parser_state()) -> {ok, {binary(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_identifier(State) ->
case peek(State) of
{identifier, Name} ->
{ok, {Name, advance(State)}};
Tok ->
{error,
{unexpected_token,
<<"identifier"/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 865).
?DOC(" Parse a type-alias reference (`_name`) or surface UnknownType for a bare identifier.\n").
-spec parse_type_alias_ref_or_error(parser_state(), binary()) -> {ok,
{caffeine_lang@types:parsed_type(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_type_alias_ref_or_error(State, Name) ->
case gleam_stdlib:string_starts_with(Name, <<"_"/utf8>>) of
true ->
{ok, {{parsed_type_alias_ref, Name}, advance(State)}};
false ->
{error,
{unknown_type,
Name,
erlang:element(3, State),
erlang:element(4, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 820).
-spec parse_field(
parser_state(),
list(caffeine_lang@frontend@ast:comment()),
fun((parser_state()) -> {ok,
{caffeine_lang@frontend@ast:value(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()})
) -> {ok, {caffeine_lang@frontend@ast:field(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_field(State, Leading_comments, Parse_value) ->
case peek(State) of
{identifier, Name} ->
State@1 = advance(State),
gleam@result:'try'(
expect(State@1, symbol_colon, <<":"/utf8>>),
fun(State@2) ->
gleam@result:'try'(
Parse_value(State@2),
fun(_use0) ->
{Value, State@3} = _use0,
{ok,
{{field, Name, Value, Leading_comments},
State@3}}
end
)
end
);
{literal_string, Name@1} ->
{error,
{quoted_field_name,
Name@1,
erlang:element(3, State),
erlang:element(4, State)}};
Tok ->
{error,
{unexpected_token,
<<"field name"/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 792).
-spec parse_fields_loop(
parser_state(),
list(caffeine_lang@frontend@ast:field()),
list(caffeine_lang@frontend@ast:comment()),
fun((parser_state()) -> {ok,
{caffeine_lang@frontend@ast:value(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()})
) -> {ok,
{list(caffeine_lang@frontend@ast:field()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_fields_loop(State, Acc, Pending, Parse_value) ->
case peek(State) of
symbol_comma ->
State@1 = advance(State),
{Next_pending, State@2} = consume_comments(State@1),
case peek(State@2) of
symbol_right_brace ->
{ok, {lists:reverse(Acc), Next_pending, State@2}};
_ ->
gleam@result:'try'(
parse_field(State@2, Next_pending, Parse_value),
fun(_use0) ->
{Field, State@3} = _use0,
{Next_pending@1, State@4} = consume_comments(
State@3
),
parse_fields_loop(
State@4,
[Field | Acc],
Next_pending@1,
Parse_value
)
end
)
end;
_ ->
{ok, {lists:reverse(Acc), Pending, State}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 767).
-spec parse_fields(
parser_state(),
list(caffeine_lang@frontend@ast:comment()),
fun((parser_state()) -> {ok,
{caffeine_lang@frontend@ast:value(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()})
) -> {ok,
{list(caffeine_lang@frontend@ast:field()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_fields(State, Pending, Parse_value) ->
case peek(State) of
symbol_right_brace ->
{ok, {[], Pending, State}};
{identifier, _} ->
gleam@result:'try'(
parse_field(State, Pending, Parse_value),
fun(_use0) ->
{Field, State@1} = _use0,
{Next_pending, State@2} = consume_comments(State@1),
parse_fields_loop(
State@2,
[Field],
Next_pending,
Parse_value
)
end
);
{literal_string, Name} ->
{error,
{quoted_field_name,
Name,
erlang:element(3, State),
erlang:element(4, State)}};
Tok ->
{error,
{unexpected_token,
<<"field name or }"/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 752).
?DOC(" Generic struct parser parameterized by field value parser.\n").
-spec parse_struct(
parser_state(),
fun((parser_state()) -> {ok,
{caffeine_lang@frontend@ast:value(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()})
) -> {ok, {caffeine_lang@frontend@ast:struct(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_struct(State, Parse_value) ->
gleam@result:'try'(
expect(State, symbol_left_brace, <<"{"/utf8>>),
fun(State@1) ->
{Pending, State@2} = consume_comments(State@1),
gleam@result:'try'(
parse_fields(State@2, Pending, Parse_value),
fun(_use0) ->
{Fields, Trailing_comments, State@3} = _use0,
gleam@result:'try'(
expect(State@3, symbol_right_brace, <<"}"/utf8>>),
fun(State@4) ->
{ok, {{struct, Fields, Trailing_comments}, State@4}}
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 1295).
?DOC(
" Convert a Literal to its string representation for use in refinements/defaults.\n"
" List literals are serialized as \"[a, b, c]\" using the same element format as\n"
" types.gleam's value-to-string, so list defaults can be round-tripped at the\n"
" resolver. Round-trip via comma-split does not preserve commas embedded inside\n"
" individual string elements.\n"
).
-spec literal_to_string(caffeine_lang@frontend@ast:literal()) -> binary().
literal_to_string(Literal) ->
case Literal of
{literal_string, S} ->
S;
{literal_integer, N} ->
erlang:integer_to_binary(N);
{literal_float, F} ->
gleam_stdlib:float_to_string(F);
{literal_percentage, F@1} ->
<<(gleam_stdlib:float_to_string(F@1))/binary, "%"/utf8>>;
{literal_duration, Amount, Unit} ->
<<(gleam_stdlib:float_to_string(Amount))/binary, Unit/binary>>;
literal_true ->
<<"true"/utf8>>;
literal_false ->
<<"false"/utf8>>;
{literal_list, Elements} ->
<<<<"["/utf8,
(begin
_pipe = Elements,
_pipe@1 = gleam@list:map(_pipe, fun literal_to_string/1),
gleam@string:join(_pipe@1, <<", "/utf8>>)
end)/binary>>/binary,
"]"/utf8>>;
{literal_struct, _, _} ->
<<"{}"/utf8>>;
{literal_external_indicator, Source, _, _} ->
<<<<"<from "/utf8, Source/binary>>/binary, ">"/utf8>>
end.
-file("src/caffeine_lang/frontend/parser.gleam", 708).
?DOC(
" Consume `, item , item ...` until a non-comma token. Does not allow\n"
" trailing commas (the parse_one callback runs after every comma).\n"
).
-spec sep_by_comma(
parser_state(),
list(LTW),
fun((parser_state()) -> {ok, {LTW, parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()})
) -> {ok, {list(LTW), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
sep_by_comma(State, Acc, Parse_one) ->
case peek(State) of
symbol_comma ->
State@1 = advance(State),
gleam@result:'try'(
Parse_one(State@1),
fun(_use0) ->
{Item, State@2} = _use0,
sep_by_comma(State@2, [Item | Acc], Parse_one)
end
);
_ ->
{ok, {lists:reverse(Acc), State}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 1049).
-spec expect_x(parser_state()) -> {ok, parser_state()} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
expect_x(State) ->
case peek(State) of
keyword_x ->
{ok, advance(State)};
Tok ->
{error,
{invalid_refinement,
<<"expected 'x', got "/utf8,
(caffeine_lang@frontend@token:to_string(Tok))/binary>>,
erlang:element(3, State),
erlang:element(4, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 850).
?DOC(" Match a primitive type keyword and consume it. Returns Error(Nil) on miss.\n").
-spec try_parse_primitive_keyword(parser_state()) -> {ok,
{caffeine_lang@types:primitive_types(), parser_state()}} |
{error, nil}.
try_parse_primitive_keyword(State) ->
case peek(State) of
keyword_string ->
{ok, {string, advance(State)}};
keyword_integer ->
{ok, {{numeric_type, integer}, advance(State)}};
keyword_float ->
{ok, {{numeric_type, float}, advance(State)}};
keyword_boolean ->
{ok, {boolean, advance(State)}};
keyword_u_r_l ->
{ok, {{semantic_type, u_r_l}, advance(State)}};
keyword_percentage ->
{ok, {{numeric_type, percentage}, advance(State)}};
_ ->
{error, nil}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 973).
?DOC(
" Parses types valid as Dict keys: String primitive or type alias ref.\n"
" Only String is allowed as a primitive key (JSON keys must be strings).\n"
).
-spec parse_dict_key_type(parser_state()) -> {ok,
{caffeine_lang@types:parsed_type(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_dict_key_type(State) ->
case peek(State) of
keyword_string ->
{ok, {{parsed_primitive, string}, advance(State)}};
{identifier, Name} ->
parse_type_alias_ref_or_error(State, Name);
Tok ->
{error,
{unknown_type,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 1223).
?DOC(
" Parse `<field> = <literal>`. The field name is an identifier; the value\n"
" is a literal that supports `$$var$$` template interpolation via the\n"
" normal literal parser.\n"
).
-spec parse_match_clause(parser_state()) -> {ok,
{caffeine_lang@frontend@ast:match_clause(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_match_clause(State) ->
gleam@result:'try'(
parse_identifier(State),
fun(_use0) ->
{Field, State@1} = _use0,
gleam@result:'try'(
expect(State@1, symbol_equals, <<"="/utf8>>),
fun(State@2) ->
gleam@result:'try'(
parse_literal(State@2),
fun(_use0@1) ->
{Value, State@3} = _use0@1,
{ok, {{match_clause, Field, Value}, State@3}}
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 1206).
-spec parse_match_chain_loop(
parser_state(),
list(caffeine_lang@frontend@ast:match_clause())
) -> {ok, {list(caffeine_lang@frontend@ast:match_clause()), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_match_chain_loop(State, Acc) ->
case peek(State) of
keyword_and ->
State@1 = advance(State),
gleam@result:'try'(
parse_match_clause(State@1),
fun(_use0) ->
{Clause, State@2} = _use0,
parse_match_chain_loop(State@2, [Clause | Acc])
end
);
_ ->
{ok, {lists:reverse(Acc), State}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 1199).
?DOC(
" Parse a chain of one or more match clauses separated by `and`. Stops at\n"
" the first non-`and` token. Returns the chain in source order.\n"
).
-spec parse_match_chain(parser_state()) -> {ok,
{list(caffeine_lang@frontend@ast:match_clause()), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_match_chain(State) ->
gleam@result:'try'(
parse_match_clause(State),
fun(_use0) ->
{First, State@1} = _use0,
parse_match_chain_loop(State@1, [First])
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 1190).
?DOC(
" Parse a `where <chain>` clause: consumes `where`, then one or more\n"
" `and`-separated match clauses. Used by the single-line surface form.\n"
).
-spec parse_where_chain(parser_state()) -> {ok,
{list(caffeine_lang@frontend@ast:match_clause()), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_where_chain(State) ->
gleam@result:'try'(
expect(State, keyword_where, <<"where"/utf8>>),
fun(State@1) -> parse_match_chain(State@1) end
).
-file("src/caffeine_lang/frontend/parser.gleam", 732).
?DOC(" Parses a struct with typed fields (for Requires blocks).\n").
-spec parse_type_struct(parser_state()) -> {ok,
{caffeine_lang@frontend@ast:struct(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_type_struct(State) ->
parse_struct(
State,
fun(S) ->
gleam@result:'try'(
parse_type(S),
fun(_use0) ->
{Type_, S@1} = _use0,
{ok, {{type_value, Type_}, S@1}}
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 910).
?DOC(
" Parses a record type: `{ field: Type, ... }`.\n"
" Reuses parse_type_struct to parse the struct, then converts fields to a dict.\n"
).
-spec parse_record_type(parser_state()) -> {ok,
{caffeine_lang@types:parsed_type(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_record_type(State) ->
gleam@result:'try'(
parse_type_struct(State),
fun(_use0) ->
{S, State@1} = _use0,
Fields = begin
_pipe = erlang:element(2, S),
_pipe@1 = gleam@list:map(
_pipe,
fun(Field) ->
T@1 = case erlang:element(3, Field) of
{type_value, T} -> T;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"caffeine_lang/frontend/parser"/utf8>>,
function => <<"parse_record_type"/utf8>>,
line => 917,
value => _assert_fail,
start => 30056,
'end' => 30097,
pattern_start => 30067,
pattern_end => 30083})
end,
{erlang:element(2, Field), T@1}
end
),
maps:from_list(_pipe@1)
end,
{ok, {{parsed_record, Fields}, State@1}}
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 1260).
-spec parse_literal_list_contents(parser_state()) -> {ok,
{list(caffeine_lang@frontend@ast:literal()), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_literal_list_contents(State) ->
case peek(State) of
symbol_right_bracket ->
{ok, {[], State}};
symbol_right_brace ->
{ok, {[], State}};
_ ->
gleam@result:'try'(
parse_literal(State),
fun(_use0) ->
{First, State@1} = _use0,
sep_by_comma(State@1, [First], fun parse_literal/1)
end
)
end.
-file("src/caffeine_lang/frontend/parser.gleam", 1061).
-spec parse_refinement_body(parser_state(), caffeine_lang@types:parsed_type()) -> {ok,
{caffeine_lang@types:refinement_types(caffeine_lang@types:parsed_type()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_refinement_body(State, Inner) ->
case peek(State) of
symbol_left_brace ->
State@1 = advance(State),
gleam@result:'try'(
parse_literal_list_contents(State@1),
fun(_use0) ->
{Values, State@2} = _use0,
gleam@result:'try'(
expect(State@2, symbol_right_brace, <<"}"/utf8>>),
fun(State@3) ->
String_values = gleam@list:map(
Values,
fun literal_to_string/1
),
{ok,
{{one_of,
Inner,
gleam@set:from_list(String_values)},
State@3}}
end
)
end
);
symbol_left_paren ->
State@4 = advance(State),
gleam@result:'try'(
parse_literal(State@4),
fun(_use0@1) ->
{Min, State@5} = _use0@1,
gleam@result:'try'(
expect(State@5, symbol_dot_dot, <<".."/utf8>>),
fun(State@6) ->
gleam@result:'try'(
parse_literal(State@6),
fun(_use0@2) ->
{Max, State@7} = _use0@2,
gleam@result:'try'(
expect(
State@7,
symbol_right_paren,
<<")"/utf8>>
),
fun(State@8) ->
{ok,
{{inclusive_range,
Inner,
literal_to_string(Min),
literal_to_string(Max)},
State@8}}
end
)
end
)
end
)
end
);
Tok ->
{error,
{unexpected_token,
<<"{ or ("/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 1035).
?DOC(
" Parse `{ x | x in <body> }` wrapping an arbitrary inner type. Shared\n"
" implementation for primitive refinements and Defaulted-with-refinement.\n"
).
-spec parse_refinement_with_inner(
parser_state(),
caffeine_lang@types:parsed_type()
) -> {ok,
{caffeine_lang@types:refinement_types(caffeine_lang@types:parsed_type()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_refinement_with_inner(State, Inner) ->
gleam@result:'try'(
expect(State, symbol_left_brace, <<"{"/utf8>>),
fun(State@1) ->
gleam@result:'try'(
expect_x(State@1),
fun(State@2) ->
gleam@result:'try'(
expect(State@2, symbol_pipe, <<"|"/utf8>>),
fun(State@3) ->
gleam@result:'try'(
expect_x(State@3),
fun(State@4) ->
gleam@result:'try'(
expect(
State@4,
keyword_in,
<<"in"/utf8>>
),
fun(State@5) ->
gleam@result:'try'(
parse_refinement_body(
State@5,
Inner
),
fun(_use0) ->
{Refinement, State@6} = _use0,
gleam@result:'try'(
expect(
State@6,
symbol_right_brace,
<<"}"/utf8>>
),
fun(State@7) ->
{ok,
{Refinement,
State@7}}
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 998).
-spec parse_defaulted_type(parser_state()) -> {ok,
{caffeine_lang@types:parsed_type(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_defaulted_type(State) ->
State@1 = advance(State),
gleam@result:'try'(
expect(State@1, symbol_left_paren, <<"("/utf8>>),
fun(State@2) ->
gleam@result:'try'(
parse_type(State@2),
fun(_use0) ->
{Inner, State@3} = _use0,
gleam@result:'try'(
expect(State@3, symbol_comma, <<","/utf8>>),
fun(State@4) ->
gleam@result:'try'(
parse_literal(State@4),
fun(_use0@1) ->
{Default, State@5} = _use0@1,
gleam@result:'try'(
expect(
State@5,
symbol_right_paren,
<<")"/utf8>>
),
fun(State@6) ->
Defaulted = {parsed_modifier,
{defaulted,
Inner,
literal_to_string(Default)}},
case peek(State@6) of
symbol_left_brace ->
gleam@result:'try'(
parse_refinement_with_inner(
State@6,
Defaulted
),
fun(_use0@2) ->
{Refinement,
State@7} = _use0@2,
{ok,
{{parsed_refinement,
Refinement},
State@7}}
end
);
_ ->
{ok, {Defaulted, State@6}}
end
end
)
end
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 988).
-spec parse_optional_type(parser_state()) -> {ok,
{caffeine_lang@types:parsed_type(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_optional_type(State) ->
State@1 = advance(State),
gleam@result:'try'(
expect(State@1, symbol_left_paren, <<"("/utf8>>),
fun(State@2) ->
gleam@result:'try'(
parse_type(State@2),
fun(_use0) ->
{Inner, State@3} = _use0,
gleam@result:'try'(
expect(State@3, symbol_right_paren, <<")"/utf8>>),
fun(State@4) ->
{ok,
{{parsed_modifier, {optional, Inner}}, State@4}}
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 947).
-spec parse_list_type(parser_state()) -> {ok,
{caffeine_lang@types:parsed_type(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_list_type(State) ->
State@1 = advance(State),
gleam@result:'try'(
expect(State@1, symbol_left_paren, <<"("/utf8>>),
fun(State@2) ->
gleam@result:'try'(
parse_collection_inner_type(State@2),
fun(_use0) ->
{Element, State@3} = _use0,
gleam@result:'try'(
expect(State@3, symbol_right_paren, <<")"/utf8>>),
fun(State@4) ->
{ok,
{{parsed_collection, {list, Element}}, State@4}}
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 926).
?DOC(
" Parses types valid inside collections: primitives, nested collections, or type alias refs.\n"
" Does not allow modifiers (Optional/Defaulted) or refinements directly.\n"
).
-spec parse_collection_inner_type(parser_state()) -> {ok,
{caffeine_lang@types:parsed_type(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_collection_inner_type(State) ->
case try_parse_primitive_keyword(State) of
{ok, {Primitive, State@1}} ->
{ok, {{parsed_primitive, Primitive}, State@1}};
{error, _} ->
case peek(State) of
keyword_list ->
parse_list_type(State);
keyword_dict ->
parse_dict_type(State);
symbol_left_brace ->
parse_record_type(State);
{identifier, Name} ->
parse_type_alias_ref_or_error(State, Name);
Tok ->
{error,
{unknown_type,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end
end.
-file("src/caffeine_lang/frontend/parser.gleam", 957).
-spec parse_dict_type(parser_state()) -> {ok,
{caffeine_lang@types:parsed_type(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_dict_type(State) ->
State@1 = advance(State),
gleam@result:'try'(
expect(State@1, symbol_left_paren, <<"("/utf8>>),
fun(State@2) ->
gleam@result:'try'(
parse_dict_key_type(State@2),
fun(_use0) ->
{Key, State@3} = _use0,
gleam@result:'try'(
expect(State@3, symbol_comma, <<","/utf8>>),
fun(State@4) ->
gleam@result:'try'(
parse_collection_inner_type(State@4),
fun(_use0@1) ->
{Value, State@5} = _use0@1,
gleam@result:'try'(
expect(
State@5,
symbol_right_paren,
<<")"/utf8>>
),
fun(State@6) ->
{ok,
{{parsed_collection,
{dict, Key, Value}},
State@6}}
end
)
end
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 1026).
?DOC(" Parse `{ x | x in <body> }` for a primitive base type.\n").
-spec parse_refinement(parser_state(), caffeine_lang@types:primitive_types()) -> {ok,
{caffeine_lang@types:refinement_types(caffeine_lang@types:parsed_type()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_refinement(State, Primitive) ->
parse_refinement_with_inner(State, {parsed_primitive, Primitive}).
-file("src/caffeine_lang/frontend/parser.gleam", 875).
-spec parse_type(parser_state()) -> {ok,
{caffeine_lang@types:parsed_type(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_type(State) ->
case try_parse_primitive_keyword(State) of
{ok, {Primitive, State@1}} ->
case peek(State@1) of
symbol_left_brace ->
gleam@result:'try'(
parse_refinement(State@1, Primitive),
fun(_use0) ->
{Refinement, State@2} = _use0,
{ok, {{parsed_refinement, Refinement}, State@2}}
end
);
_ ->
{ok, {{parsed_primitive, Primitive}, State@1}}
end;
{error, _} ->
case peek(State) of
keyword_list ->
parse_list_type(State);
keyword_dict ->
parse_dict_type(State);
keyword_optional ->
parse_optional_type(State);
keyword_defaulted ->
parse_defaulted_type(State);
symbol_left_brace ->
parse_record_type(State);
{identifier, Name} ->
parse_type_alias_ref_or_error(State, Name);
Tok ->
{error,
{unknown_type,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end
end.
-file("src/caffeine_lang/frontend/parser.gleam", 1235).
?DOC(
" Parse `value: <path> as <type>` if present (block form only). Returns\n"
" None if the next token isn't an Identifier opening the value-extraction\n"
" line, so callers can fall through to `}`.\n"
).
-spec parse_optional_value_extraction(parser_state()) -> {ok,
{gleam@option:option(caffeine_lang@frontend@ast:value_extraction()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_optional_value_extraction(State) ->
case peek(State) of
{identifier, <<"value"/utf8>>} ->
State@1 = advance(State),
gleam@result:'try'(
expect(State@1, symbol_colon, <<":"/utf8>>),
fun(State@2) ->
gleam@result:'try'(
parse_identifier(State@2),
fun(_use0) ->
{Path, State@3} = _use0,
gleam@result:'try'(
expect(State@3, keyword_as, <<"as"/utf8>>),
fun(State@4) ->
gleam@result:'try'(
parse_type(State@4),
fun(_use0@1) ->
{Type_, State@5} = _use0@1,
{ok,
{{some,
{value_extraction,
Path,
Type_}},
State@5}}
end
)
end
)
end
)
end
);
_ ->
{ok, {none, State}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 1173).
?DOC(
" Parse `{ where: <chain> [value: <path> as <type>] }`. Requires `where:`\n"
" to come first; `value:` is optional. The block form is what enables\n"
" value extraction; the single-line form is sugar for the where-only case.\n"
).
-spec parse_external_indicator_block(parser_state(), binary()) -> {ok,
{caffeine_lang@frontend@ast:literal(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_external_indicator_block(State, Source) ->
gleam@result:'try'(
expect(State, symbol_left_brace, <<"{"/utf8>>),
fun(State@1) ->
gleam@result:'try'(
expect(State@1, keyword_where, <<"where"/utf8>>),
fun(State@2) ->
gleam@result:'try'(
expect(State@2, symbol_colon, <<":"/utf8>>),
fun(State@3) ->
gleam@result:'try'(
parse_match_chain(State@3),
fun(_use0) ->
{Match, State@4} = _use0,
gleam@result:'try'(
parse_optional_value_extraction(State@4),
fun(_use0@1) ->
{Value_extraction, State@5} = _use0@1,
gleam@result:'try'(
expect(
State@5,
symbol_right_brace,
<<"}"/utf8>>
),
fun(State@6) ->
{ok,
{{literal_external_indicator,
Source,
Match,
Value_extraction},
State@6}}
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 1149).
?DOC(
" Parse a `from <source> ...` external-indicator literal. Dispatches on the\n"
" token after the source identifier: `{` opens the block form, `where`\n"
" starts the single-line form.\n"
).
-spec parse_external_indicator(parser_state()) -> {ok,
{caffeine_lang@frontend@ast:literal(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_external_indicator(State) ->
gleam@result:'try'(
expect(State, keyword_from, <<"from"/utf8>>),
fun(State@1) ->
gleam@result:'try'(
parse_identifier(State@1),
fun(_use0) ->
{Source, State@2} = _use0,
case peek(State@2) of
symbol_left_brace ->
parse_external_indicator_block(State@2, Source);
keyword_where ->
gleam@result:'try'(
parse_where_chain(State@2),
fun(_use0@1) ->
{Match, State@3} = _use0@1,
{ok,
{{literal_external_indicator,
Source,
Match,
none},
State@3}}
end
);
Tok ->
{error,
{unexpected_token,
<<<<"`where` or `{` after `from "/utf8,
Source/binary>>/binary,
"`"/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State@2),
erlang:element(4, State@2)}}
end
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 1272).
-spec parse_literal_struct_value(parser_state()) -> {ok,
{caffeine_lang@frontend@ast:literal(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_literal_struct_value(State) ->
Parse_value = fun(S) ->
gleam@result:'try'(
parse_literal(S),
fun(_use0) ->
{Literal, S@1} = _use0,
{ok, {{literal_value, Literal}, S@1}}
end
)
end,
gleam@result:'try'(
expect(State, symbol_left_brace, <<"{"/utf8>>),
fun(State@1) ->
{Pending, State@2} = consume_comments(State@1),
gleam@result:'try'(
parse_fields(State@2, Pending, Parse_value),
fun(_use0@1) ->
{Fields, Trailing_comments, State@3} = _use0@1,
gleam@result:'try'(
expect(State@3, symbol_right_brace, <<"}"/utf8>>),
fun(State@4) ->
{ok,
{{literal_struct, Fields, Trailing_comments},
State@4}}
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 1251).
-spec parse_literal_list(parser_state()) -> {ok,
{caffeine_lang@frontend@ast:literal(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_literal_list(State) ->
gleam@result:'try'(
expect(State, symbol_left_bracket, <<"["/utf8>>),
fun(State@1) ->
gleam@result:'try'(
parse_literal_list_contents(State@1),
fun(_use0) ->
{Elements, State@2} = _use0,
gleam@result:'try'(
expect(State@2, symbol_right_bracket, <<"]"/utf8>>),
fun(State@3) ->
{ok, {{literal_list, Elements}, State@3}}
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 1100).
-spec parse_literal(parser_state()) -> {ok,
{caffeine_lang@frontend@ast:literal(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_literal(State) ->
case peek(State) of
{literal_string, S} ->
{ok, {{literal_string, S}, advance(State)}};
{literal_integer, N} ->
{ok, {{literal_integer, N}, advance(State)}};
{literal_float, F} ->
{ok, {{literal_float, F}, advance(State)}};
{literal_percentage, F@1} ->
{ok, {{literal_percentage, F@1}, advance(State)}};
{literal_duration, Amount, Unit} ->
{ok, {{literal_duration, Amount, Unit}, advance(State)}};
literal_true ->
{ok, {literal_true, advance(State)}};
literal_false ->
{ok, {literal_false, advance(State)}};
symbol_left_bracket ->
parse_literal_list(State);
symbol_left_brace ->
parse_literal_struct_value(State);
keyword_from ->
parse_external_indicator(State);
Tok ->
{error,
{unexpected_token,
<<"literal value"/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 742).
?DOC(" Parses a struct with literal fields (for Provides blocks).\n").
-spec parse_literal_struct(parser_state()) -> {ok,
{caffeine_lang@frontend@ast:struct(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_literal_struct(State) ->
parse_struct(
State,
fun(S) ->
gleam@result:'try'(
parse_literal(S),
fun(_use0) ->
{Literal, S@1} = _use0,
{ok, {{literal_value, Literal}, S@1}}
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 679).
-spec parse_extends_list(parser_state()) -> {ok,
{list(binary()), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_extends_list(State) ->
case peek(State) of
symbol_right_bracket ->
{ok, {[], State}};
_ ->
gleam@result:'try'(
parse_identifier(State),
fun(_use0) ->
{First, State@1} = _use0,
sep_by_comma(State@1, [First], fun parse_identifier/1)
end
)
end.
-file("src/caffeine_lang/frontend/parser.gleam", 664).
-spec parse_optional_extends(parser_state()) -> {ok,
{list(binary()), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_optional_extends(State) ->
case peek(State) of
keyword_extends ->
State@1 = advance(State),
gleam@result:'try'(
expect(State@1, symbol_left_bracket, <<"["/utf8>>),
fun(State@2) ->
gleam@result:'try'(
parse_extends_list(State@2),
fun(_use0) ->
{Extends, State@3} = _use0,
gleam@result:'try'(
expect(
State@3,
symbol_right_bracket,
<<"]"/utf8>>
),
fun(State@4) -> {ok, {Extends, State@4}} end
)
end
)
end
);
_ ->
{ok, {[], State}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 384).
?DOC(
" Recognises an optional `success_rate` or `time_slice` keyword in the\n"
" measurement header, e.g. `\"api\" success_rate:` or `\"api\" time_slice:`.\n"
" Returns None if the next token is `:` or `extends` (legacy untyped header).\n"
).
-spec parse_optional_expectation_type(parser_state()) -> {ok,
{gleam@option:option(caffeine_lang@frontend@ast:expectation_type()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_optional_expectation_type(State) ->
case peek(State) of
keyword_success_rate ->
{ok, {{some, success_rate_type}, advance(State)}};
keyword_time_slice ->
{ok, {{some, time_slice_type}, advance(State)}};
_ ->
{ok, {none, State}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 649).
-spec parse_string_literal(parser_state()) -> {ok, {binary(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_string_literal(State) ->
case peek(State) of
{literal_string, S} ->
{ok, {S, advance(State)}};
Tok ->
{error,
{unexpected_token,
<<"string"/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 345).
-spec parse_measurement_item(
parser_state(),
list(caffeine_lang@frontend@ast:comment())
) -> {ok, {caffeine_lang@frontend@ast:measurement_item(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_measurement_item(State, Leading_comments) ->
gleam@result:'try'(
parse_string_literal(State),
fun(_use0) ->
{Name, State@1} = _use0,
gleam@result:'try'(
parse_optional_expectation_type(State@1),
fun(_use0@1) ->
{Expectation_type, State@2} = _use0@1,
gleam@result:'try'(
parse_optional_extends(State@2),
fun(_use0@2) ->
{Extends, State@3} = _use0@2,
gleam@result:'try'(
expect(State@3, symbol_colon, <<":"/utf8>>),
fun(State@4) ->
{_, State@5} = consume_comments(State@4),
gleam@result:'try'(case peek(State@5) of
keyword_requires ->
State@6 = advance(State@5),
parse_type_struct(State@6);
_ ->
{ok,
{{struct, [], []}, State@5}}
end, fun(_use0@3) ->
{Requires, State@7} = _use0@3,
{_, State@8} = consume_comments(
State@7
),
gleam@result:'try'(
expect(
State@8,
keyword_provides,
<<"Provides"/utf8>>
),
fun(State@9) ->
gleam@result:'try'(
parse_literal_struct(
State@9
),
fun(_use0@4) ->
{Provides, State@10} = _use0@4,
{ok,
{{measurement_item,
Name,
Expectation_type,
Extends,
Requires,
Provides,
Leading_comments},
State@10}}
end
)
end
)
end)
end
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 588).
-spec measurement_items_loop(
parser_state(),
list(caffeine_lang@frontend@ast:measurement_item()),
list(caffeine_lang@frontend@parser_error:parser_error()),
list(caffeine_lang@frontend@ast:comment())
) -> {list(caffeine_lang@frontend@ast:measurement_item()),
list(caffeine_lang@frontend@parser_error:parser_error()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}.
measurement_items_loop(State, Items, Errors, Pending) ->
case peek(State) of
{literal_string, _} ->
case parse_measurement_item(State, Pending) of
{ok, {Item, State@1}} ->
{Next_pending, State@2} = consume_comments(State@1),
measurement_items_loop(
State@2,
[Item | Items],
Errors,
Next_pending
);
{error, Err} ->
State@3 = skip_until(
advance(State),
fun at_measurement_item_boundary/1
),
{Next_pending@1, State@4} = consume_comments(State@3),
measurement_items_loop(
State@4,
Items,
[Err | Errors],
Next_pending@1
)
end;
_ ->
{lists:reverse(Items), lists:reverse(Errors), Pending, State}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 581).
?DOC(
" Parse measurement items with recovery between items.\n"
" Measurement items start with a string literal name.\n"
).
-spec parse_measurement_items_recovering(
parser_state(),
list(caffeine_lang@frontend@ast:comment())
) -> {list(caffeine_lang@frontend@ast:measurement_item()),
list(caffeine_lang@frontend@parser_error:parser_error()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}.
parse_measurement_items_recovering(State, Pending) ->
measurement_items_loop(State, [], [], Pending).
-file("src/caffeine_lang/frontend/parser.gleam", 325).
-spec parse_extendable_kind(parser_state()) -> {ok,
{caffeine_lang@frontend@ast:extendable_kind(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_extendable_kind(State) ->
case peek(State) of
keyword_requires ->
{ok, {extendable_requires, advance(State)}};
keyword_provides ->
{ok, {extendable_provides, advance(State)}};
Tok ->
{error,
{unexpected_token,
<<"Type, Requires, or Provides"/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 307).
-spec parse_extendable(
parser_state(),
binary(),
list(caffeine_lang@frontend@ast:comment())
) -> {ok, {caffeine_lang@frontend@ast:extendable(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_extendable(State, Name, Leading_comments) ->
State@1 = advance(State),
gleam@result:'try'(
expect(State@1, symbol_left_paren, <<"("/utf8>>),
fun(State@2) ->
gleam@result:'try'(
parse_extendable_kind(State@2),
fun(_use0) ->
{Kind, State@3} = _use0,
gleam@result:'try'(
expect(State@3, symbol_right_paren, <<")"/utf8>>),
fun(State@4) ->
gleam@result:'try'(
expect(State@4, symbol_colon, <<":"/utf8>>),
fun(State@5) -> gleam@result:'try'(case Kind of
extendable_requires ->
parse_type_struct(State@5);
extendable_provides ->
parse_literal_struct(State@5)
end, fun(_use0@1) ->
{Body, State@6} = _use0@1,
{ok,
{{extendable,
Name,
Kind,
Body,
Leading_comments},
State@6}}
end) end
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 288).
-spec parse_extendables_loop(
parser_state(),
list(caffeine_lang@frontend@ast:extendable()),
list(caffeine_lang@frontend@ast:comment())
) -> {ok,
{list(caffeine_lang@frontend@ast:extendable()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_extendables_loop(State, Acc, Pending) ->
case peek(State) of
{identifier, Name} ->
gleam@result:'try'(
parse_extendable(State, Name, Pending),
fun(_use0) ->
{Extendable, State@1} = _use0,
{Next_pending, State@2} = consume_comments(State@1),
parse_extendables_loop(
State@2,
[Extendable | Acc],
Next_pending
)
end
);
_ ->
{ok, {lists:reverse(Acc), Pending, State}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 281).
?DOC(" Parse zero or more extendables at file start.\n").
-spec parse_extendables(
parser_state(),
list(caffeine_lang@frontend@ast:comment())
) -> {ok,
{list(caffeine_lang@frontend@ast:extendable()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_extendables(State, Pending) ->
parse_extendables_loop(State, [], Pending).
-file("src/caffeine_lang/frontend/parser.gleam", 237).
-spec parse_type_alias(
parser_state(),
binary(),
list(caffeine_lang@frontend@ast:comment())
) -> {ok, {caffeine_lang@frontend@ast:type_alias(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_type_alias(State, Name, Leading_comments) ->
case Name of
<<"_"/utf8>> ->
{error,
{invalid_type_alias_name,
Name,
<<"type alias name must have at least one character after the underscore"/utf8>>,
erlang:element(3, State),
erlang:element(4, State)}};
_ ->
State@1 = advance(State),
gleam@result:'try'(
expect(State@1, symbol_left_paren, <<"("/utf8>>),
fun(State@2) -> case peek(State@2) of
keyword_type ->
State@3 = advance(State@2),
gleam@result:'try'(
expect(
State@3,
symbol_right_paren,
<<")"/utf8>>
),
fun(State@4) ->
gleam@result:'try'(
expect(
State@4,
symbol_colon,
<<":"/utf8>>
),
fun(State@5) ->
gleam@result:'try'(
parse_type(State@5),
fun(_use0) ->
{Type_, State@6} = _use0,
{ok,
{{type_alias,
Name,
Type_,
Leading_comments},
State@6}}
end
)
end
)
end
);
Tok ->
{error,
{unexpected_token,
<<"Type"/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State@2),
erlang:element(4, State@2)}}
end end
)
end.
-file("src/caffeine_lang/frontend/parser.gleam", 225).
?DOC(
" Check if current position is a type alias definition.\n"
" Looks for pattern: Identifier ( KeywordType )\n"
).
-spec is_type_alias(parser_state()) -> boolean().
is_type_alias(State) ->
case erlang:element(2, State) of
[{positioned_token, {identifier, _}, _, _},
{positioned_token, symbol_left_paren, _, _},
{positioned_token, keyword_type, _, _} |
_] ->
true;
_ ->
false
end.
-file("src/caffeine_lang/frontend/parser.gleam", 198).
-spec parse_type_aliases_loop(
parser_state(),
list(caffeine_lang@frontend@ast:type_alias()),
list(caffeine_lang@frontend@ast:comment())
) -> {ok,
{list(caffeine_lang@frontend@ast:type_alias()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_type_aliases_loop(State, Acc, Pending) ->
case peek(State) of
{identifier, Name} ->
case is_type_alias(State) of
true ->
gleam@result:'try'(
parse_type_alias(State, Name, Pending),
fun(_use0) ->
{Type_alias, State@1} = _use0,
{Next_pending, State@2} = consume_comments(State@1),
parse_type_aliases_loop(
State@2,
[Type_alias | Acc],
Next_pending
)
end
);
false ->
{ok, {lists:reverse(Acc), Pending, State}}
end;
_ ->
{ok, {lists:reverse(Acc), Pending, State}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 191).
?DOC(
" Parse zero or more type aliases at file start.\n"
" Type alias syntax: _name (Type): <refinement_type>\n"
).
-spec parse_type_aliases(
parser_state(),
list(caffeine_lang@frontend@ast:comment())
) -> {ok,
{list(caffeine_lang@frontend@ast:type_alias()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_type_aliases(State, Pending) ->
parse_type_aliases_loop(State, [], Pending).
-file("src/caffeine_lang/frontend/parser.gleam", 126).
?DOC(" Initialize parser state from a list of positioned tokens.\n").
-spec init_state(list(caffeine_lang@frontend@token:positioned_token())) -> parser_state().
init_state(Tokens) ->
case Tokens of
[{positioned_token, _, Line, Column} | _] ->
{parser_state, Tokens, Line, Column, Line, Column};
[] ->
{parser_state, Tokens, 1, 1, 1, 1}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 95).
?DOC(" Filter out whitespace tokens (keep comments in stream).\n").
-spec filter_whitespace(list(caffeine_lang@frontend@token:positioned_token())) -> list(caffeine_lang@frontend@token:positioned_token()).
filter_whitespace(Tokens) ->
gleam@list:filter(Tokens, fun(Ptok) -> case Ptok of
{positioned_token, whitespace_newline, _, _} ->
false;
{positioned_token, {whitespace_indent, _}, _, _} ->
false;
_ ->
true
end end).
-file("src/caffeine_lang/frontend/parser.gleam", 38).
?DOC(
" Parses a measurements file from source text.\n"
" Returns all recoverable parse errors rather than stopping at the first one.\n"
).
-spec parse_measurements_file(binary()) -> {ok,
caffeine_lang@frontend@ast:measurements_file(caffeine_lang@frontend@ast:parsed())} |
{error, list(caffeine_lang@frontend@parser_error:parser_error())}.
parse_measurements_file(Source) ->
gleam@result:'try'(
begin
_pipe = caffeine_lang@frontend@tokenizer:tokenize(Source),
gleam@result:map_error(_pipe, fun(E) -> [{tokenizer_error, E}] end)
end,
fun(Tokens) ->
Filtered = filter_whitespace(Tokens),
State = init_state(Filtered),
{Pending, State@1} = consume_comments(State),
gleam@result:'try'(
begin
_pipe@1 = parse_type_aliases(State@1, Pending),
gleam@result:map_error(_pipe@1, fun(E@1) -> [E@1] end)
end,
fun(_use0) ->
{Type_aliases, Pending@1, State@2} = _use0,
gleam@result:'try'(
begin
_pipe@2 = parse_extendables(State@2, Pending@1),
gleam@result:map_error(
_pipe@2,
fun(E@2) -> [E@2] end
)
end,
fun(_use0@1) ->
{Extendables, Pending@2, State@3} = _use0@1,
{Items, Errors, Pending@3, _} = parse_measurement_items_recovering(
State@3,
Pending@2
),
case Errors of
[] ->
{ok,
{measurements_file,
Type_aliases,
Extendables,
Items,
Pending@3}};
Errs ->
{error, Errs}
end
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 565).
-spec at_expect_item_boundary(caffeine_lang@frontend@token:token()) -> boolean().
at_expect_item_boundary(Tok) ->
case Tok of
{literal_string, _} ->
true;
e_o_f ->
true;
_ ->
false
end.
-file("src/caffeine_lang/frontend/parser.gleam", 503).
-spec parse_optional_measured_by(parser_state()) -> {ok,
{gleam@option:option(caffeine_lang@frontend@ast:measured_by()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_optional_measured_by(State) ->
case peek(State) of
keyword_as ->
State@1 = advance(State),
gleam@result:'try'(
expect(State@1, keyword_measured, <<"measured"/utf8>>),
fun(State@2) ->
gleam@result:'try'(
expect(State@2, keyword_by, <<"by"/utf8>>),
fun(State@3) ->
gleam@result:'try'(
parse_string_literal(State@3),
fun(_use0) ->
{Measurement, State@4} = _use0,
gleam@result:'try'(
expect(
State@4,
keyword_with,
<<"with"/utf8>>
),
fun(State@5) ->
gleam@result:'try'(
expect(
State@5,
symbol_colon,
<<":"/utf8>>
),
fun(State@6) ->
gleam@result:'try'(
parse_literal_struct(
State@6
),
fun(_use0@1) ->
{With_args, State@7} = _use0@1,
{ok,
{{some,
{measured_by,
Measurement,
With_args}},
State@7}}
end
)
end
)
end
)
end
)
end
)
end
);
_ ->
{ok, {none, State}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 536).
-spec parse_duration_literal(parser_state()) -> {ok,
{caffeine_lang@frontend@ast:duration_literal(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_duration_literal(State) ->
case peek(State) of
{literal_duration, Amount, Unit} ->
{ok, {{duration_literal, Amount, Unit}, advance(State)}};
Tok ->
{error,
{unexpected_token,
<<"duration (e.g. 10d, 50ms)"/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 490).
-spec parse_optional_below(parser_state()) -> {ok,
{gleam@option:option(caffeine_lang@frontend@ast:duration_literal()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_optional_below(State) ->
case peek(State) of
keyword_below ->
State@1 = advance(State),
gleam@result:'try'(
parse_duration_literal(State@1),
fun(_use0) ->
{Dur, State@2} = _use0,
{ok, {{some, Dur}, State@2}}
end
);
_ ->
{ok, {none, State}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 521).
-spec parse_percentage_literal(parser_state()) -> {ok,
{float(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_percentage_literal(State) ->
case peek(State) of
{literal_percentage, F} ->
{ok, {F, advance(State)}};
Tok ->
{error,
{unexpected_token,
<<"percentage (e.g. 99.9%)"/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 477).
-spec parse_guarantees(parser_state()) -> {ok,
{caffeine_lang@frontend@ast:guarantees(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_guarantees(State) ->
gleam@result:'try'(
expect(State, keyword_guarantees, <<"Guarantees"/utf8>>),
fun(State@1) ->
gleam@result:'try'(
parse_percentage_literal(State@1),
fun(_use0) ->
{Threshold, State@2} = _use0,
gleam@result:'try'(
parse_optional_below(State@2),
fun(_use0@1) ->
{Below, State@3} = _use0@1,
gleam@result:'try'(
expect(State@3, keyword_over, <<"over"/utf8>>),
fun(State@4) ->
gleam@result:'try'(
parse_duration_literal(State@4),
fun(_use0@2) ->
{Window, State@5} = _use0@2,
gleam@result:'try'(
expect(
State@5,
keyword_window,
<<"window"/utf8>>
),
fun(State@6) ->
gleam@result:'try'(
parse_optional_measured_by(
State@6
),
fun(_use0@3) ->
{Measured_by,
State@7} = _use0@3,
{ok,
{{guarantees,
Threshold,
Below,
Window,
Measured_by},
State@7}}
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 456).
-spec parse_dependency_line(
parser_state(),
list(caffeine_lang@frontend@ast:comment())
) -> {ok, {caffeine_lang@frontend@ast:dependency(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_dependency_line(State, Leading_comments) ->
gleam@result:'try'(case peek(State) of
keyword_hard ->
{ok, {hard_dep, advance(State)}};
keyword_soft ->
{ok, {soft_dep, advance(State)}};
Tok ->
{error,
{unexpected_token,
<<"hard or soft"/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)}}
end, fun(_use0) ->
{Kind, State@1} = _use0,
gleam@result:'try'(
expect(State@1, keyword_dependency, <<"dependency"/utf8>>),
fun(State@2) ->
gleam@result:'try'(
expect(State@2, keyword_on, <<"on"/utf8>>),
fun(State@3) ->
gleam@result:'try'(
parse_string_literal(State@3),
fun(_use0@1) ->
{Target, State@4} = _use0@1,
{ok,
{{dependency,
Kind,
Target,
Leading_comments},
State@4}}
end
)
end
)
end
)
end).
-file("src/caffeine_lang/frontend/parser.gleam", 441).
-spec parse_dependency_lines(
parser_state(),
list(caffeine_lang@frontend@ast:comment()),
list(caffeine_lang@frontend@ast:dependency())
) -> {ok,
{list(caffeine_lang@frontend@ast:dependency()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_dependency_lines(State, Leading, Acc) ->
case peek(State) of
keyword_hard ->
gleam@result:'try'(
parse_dependency_line(State, Leading),
fun(_use0) ->
{Dep, State@1} = _use0,
{Next_pending, State@2} = consume_comments(State@1),
parse_dependency_lines(State@2, Next_pending, [Dep | Acc])
end
);
keyword_soft ->
gleam@result:'try'(
parse_dependency_line(State, Leading),
fun(_use0) ->
{Dep, State@1} = _use0,
{Next_pending, State@2} = consume_comments(State@1),
parse_dependency_lines(State@2, Next_pending, [Dep | Acc])
end
);
_ ->
{ok, {lists:reverse(Acc), Leading, State}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 424).
-spec parse_optional_assumes(parser_state()) -> {ok,
{gleam@option:option(caffeine_lang@frontend@ast:assumes()),
parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_optional_assumes(State) ->
case peek(State) of
keyword_assumes ->
State@1 = advance(State),
gleam@result:'try'(
expect(State@1, symbol_colon, <<":"/utf8>>),
fun(State@2) ->
{Pending, State@3} = consume_comments(State@2),
gleam@result:'try'(
parse_dependency_lines(State@3, Pending, []),
fun(_use0) ->
{Deps, Trailing, State@4} = _use0,
{ok, {{some, {assumes, Deps, Trailing}}, State@4}}
end
)
end
);
_ ->
{ok, {none, State}}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 408).
?DOC(
" Parses a single standalone expectation:\n"
"\n"
" \"<name>\" (extends [...])?:\n"
" (Assumes:\n"
" (hard|soft) dependency on \"<target>\"\n"
" ...)?\n"
" Guarantees <pct>% (below <dur>)? over <dur> window\n"
" (as measured by \"<measurement>\" with: { <fields> })?\n"
).
-spec parse_expect_item(
parser_state(),
list(caffeine_lang@frontend@ast:comment())
) -> {ok, {caffeine_lang@frontend@ast:expect_item(), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_expect_item(State, Leading_comments) ->
gleam@result:'try'(
parse_string_literal(State),
fun(_use0) ->
{Name, State@1} = _use0,
gleam@result:'try'(
parse_optional_extends(State@1),
fun(_use0@1) ->
{Extends, State@2} = _use0@1,
gleam@result:'try'(
expect(State@2, symbol_colon, <<":"/utf8>>),
fun(State@3) ->
{_, State@4} = consume_comments(State@3),
gleam@result:'try'(
parse_optional_assumes(State@4),
fun(_use0@2) ->
{Assumes, State@5} = _use0@2,
gleam@result:'try'(
parse_guarantees(State@5),
fun(_use0@3) ->
{Guarantees, State@6} = _use0@3,
{ok,
{{expect_item,
Name,
Extends,
Assumes,
Guarantees,
Leading_comments},
State@6}}
end
)
end
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 621).
-spec expect_items_loop(
parser_state(),
list(caffeine_lang@frontend@ast:expect_item()),
list(caffeine_lang@frontend@parser_error:parser_error()),
list(caffeine_lang@frontend@ast:comment())
) -> {list(caffeine_lang@frontend@ast:expect_item()),
list(caffeine_lang@frontend@parser_error:parser_error()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}.
expect_items_loop(State, Items, Errors, Pending) ->
case peek(State) of
{literal_string, _} ->
case parse_expect_item(State, Pending) of
{ok, {Item, State@1}} ->
{Next_pending, State@2} = consume_comments(State@1),
expect_items_loop(
State@2,
[Item | Items],
Errors,
Next_pending
);
{error, Err} ->
State@3 = skip_until(
advance(State),
fun at_expect_item_boundary/1
),
{Next_pending@1, State@4} = consume_comments(State@3),
expect_items_loop(
State@4,
Items,
[Err | Errors],
Next_pending@1
)
end;
_ ->
{lists:reverse(Items), lists:reverse(Errors), Pending, State}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 614).
?DOC(
" Parse expect items with recovery between items.\n"
" Each expect item starts with a string literal name (top-level, no grouping).\n"
).
-spec parse_expect_items_recovering(
parser_state(),
list(caffeine_lang@frontend@ast:comment())
) -> {list(caffeine_lang@frontend@ast:expect_item()),
list(caffeine_lang@frontend@parser_error:parser_error()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}.
parse_expect_items_recovering(State, Pending) ->
expect_items_loop(State, [], [], Pending).
-file("src/caffeine_lang/frontend/parser.gleam", 72).
?DOC(
" Parses an expects file from source text.\n"
" Returns all recoverable parse errors rather than stopping at the first one.\n"
).
-spec parse_expects_file(binary()) -> {ok,
caffeine_lang@frontend@ast:expects_file(caffeine_lang@frontend@ast:parsed())} |
{error, list(caffeine_lang@frontend@parser_error:parser_error())}.
parse_expects_file(Source) ->
gleam@result:'try'(
begin
_pipe = caffeine_lang@frontend@tokenizer:tokenize(Source),
gleam@result:map_error(_pipe, fun(E) -> [{tokenizer_error, E}] end)
end,
fun(Tokens) ->
Filtered = filter_whitespace(Tokens),
State = init_state(Filtered),
{Pending, State@1} = consume_comments(State),
gleam@result:'try'(
begin
_pipe@1 = parse_extendables(State@1, Pending),
gleam@result:map_error(_pipe@1, fun(E@1) -> [E@1] end)
end,
fun(_use0) ->
{Extendables, Pending@1, State@2} = _use0,
{Items, Errors, Pending@2, _} = parse_expect_items_recovering(
State@2,
Pending@1
),
case Errors of
[] ->
{ok, {expects_file, Extendables, Items, Pending@2}};
Errs ->
{error, Errs}
end
end
)
end
).