-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, _, _} -> <<<<">/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(LUE), fun((parser_state()) -> {ok, {LUE, parser_state()}} | {error, caffeine_lang@frontend@parser_error:parser_error()}) ) -> {ok, {list(LUE), 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 ` = `. 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 ` 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 => <>, 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 }` 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 }` 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: as ` 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: [value: as ] }`. 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 ...` 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): \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" " \"\" (extends [...])?:\n" " (Assumes:\n" " (hard|soft) dependency on \"\"\n" " ...)?\n" " Guarantees % (below )? over window\n" " (as measured by \"\" with: { })?\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 ).