Packages
caffeine_lang
5.4.3
6.3.1
6.3.0
6.2.2
6.2.1
6.2.0
6.1.2
6.1.1
6.1.0
6.0.0
5.6.0
5.5.0
5.4.4
5.4.3
5.4.2
5.4.1
5.4.0
5.3.0
5.2.0
5.1.1
5.1.0
5.0.12
5.0.11
5.0.10
5.0.8
5.0.7
5.0.6
5.0.5
5.0.4
5.0.1
5.0.0
4.10.0
4.9.0
4.8.3
4.8.2
4.8.1
4.8.0
4.7.9
4.7.8
4.7.7
4.7.6
4.7.5
4.6.7
4.6.6
4.6.5
4.6.4
4.6.3
4.6.2
4.6.0
4.5.1
4.5.0
4.4.4
4.4.3
4.4.1
4.4.0
4.3.7
4.3.6
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.0.2
1.0.1
0.1.0
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.2
0.0.1
A compiler for generating reliability artifacts from service expectation definitions.
Current section
Files
Jump to
Current section
Files
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", 144).
?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", 136).
?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", 111).
-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", 107).
?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", 427).
-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", 403).
?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", 169).
?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", 822).
-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", 794).
-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", 769).
-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", 710).
?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(KXV),
fun((parser_state()) -> {ok, {KXV, parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()})
) -> {ok, {list(KXV), 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", 1146).
-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", 1134).
-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", 1125).
-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", 1102).
-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_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);
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", 754).
?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", 744).
?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", 867).
?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", 1169).
?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_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>>
end.
-file("src/caffeine_lang/frontend/parser.gleam", 1063).
-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", 1051).
-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", 1037).
?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", 852).
?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", 975).
?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", 1028).
?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", 912).
?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 => 919,
value => _assert_fail,
start => 29362,
'end' => 29403,
pattern_start => 29373,
pattern_end => 29389})
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", 1000).
-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", 990).
-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", 949).
-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", 928).
?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", 959).
-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", 877).
-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", 734).
?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", 693).
-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", 681).
-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", 666).
-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", 651).
-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", 346).
-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_extends(State@1),
fun(_use0@1) ->
{Extends, State@2} = _use0@1,
gleam@result:'try'(
expect(State@2, symbol_colon, <<":"/utf8>>),
fun(State@3) ->
gleam@result:'try'(
expect(
State@3,
keyword_requires,
<<"Requires"/utf8>>
),
fun(State@4) ->
gleam@result:'try'(
parse_type_struct(State@4),
fun(_use0@2) ->
{Requires, State@5} = _use0@2,
gleam@result:'try'(
expect(
State@5,
keyword_provides,
<<"Provides"/utf8>>
),
fun(State@6) ->
gleam@result:'try'(
parse_literal_struct(
State@6
),
fun(_use0@3) ->
{Provides, State@7} = _use0@3,
{ok,
{{measurement_item,
Name,
Extends,
Requires,
Provides,
Leading_comments},
State@7}}
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 576).
?DOC(" Recovery loop for measurement items, detecting boundaries by string literal tokens.\n").
-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", 568).
?DOC(
" Parse measurement items with recovery between items.\n"
" Measurement items start with a string literal name (no `*` prefix).\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", 326).
-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", 308).
-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", 289).
-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", 282).
?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", 238).
-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", 226).
?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", 199).
-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", 192).
?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", 127).
?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", 96).
?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", 39).
?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", 410).
-spec at_block_boundary(caffeine_lang@frontend@token:token()) -> boolean().
at_block_boundary(Tok) ->
case Tok of
keyword_expectations ->
true;
keyword_unmeasured ->
true;
e_o_f ->
true;
_ ->
false
end.
-file("src/caffeine_lang/frontend/parser.gleam", 373).
-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'(
expect(State, symbol_star, <<"*"/utf8>>),
fun(State@1) ->
gleam@result:'try'(
parse_string_literal(State@1),
fun(_use0) ->
{Name, State@2} = _use0,
gleam@result:'try'(
parse_optional_extends(State@2),
fun(_use0@1) ->
{Extends, State@3} = _use0@1,
gleam@result:'try'(
expect(State@3, symbol_colon, <<":"/utf8>>),
fun(State@4) -> case peek(State@4) of
keyword_requires ->
{error,
{unexpected_token,
<<"Provides"/utf8>>,
<<"Requires"/utf8>>,
erlang:element(3, State@4),
erlang:element(4, State@4)}};
_ ->
gleam@result:'try'(
expect(
State@4,
keyword_provides,
<<"Provides"/utf8>>
),
fun(State@5) ->
gleam@result:'try'(
parse_literal_struct(
State@5
),
fun(_use0@2) ->
{Provides, State@6} = _use0@2,
{ok,
{{expect_item,
Name,
Extends,
Provides,
Leading_comments},
State@6}}
end
)
end
)
end end
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 417).
-spec at_item_boundary(caffeine_lang@frontend@token:token()) -> boolean().
at_item_boundary(Tok) ->
case Tok of
symbol_star ->
true;
keyword_expectations ->
true;
keyword_unmeasured ->
true;
e_o_f ->
true;
_ ->
false
end.
-file("src/caffeine_lang/frontend/parser.gleam", 609).
?DOC(" Generic item-level recovery loop parameterized by item parser.\n").
-spec items_recovering_loop(
parser_state(),
list(KXB),
list(caffeine_lang@frontend@parser_error:parser_error()),
list(caffeine_lang@frontend@ast:comment()),
fun((parser_state(), list(caffeine_lang@frontend@ast:comment())) -> {ok,
{KXB, parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()})
) -> {list(KXB),
list(caffeine_lang@frontend@parser_error:parser_error()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}.
items_recovering_loop(State, Item_acc, Error_acc, Pending, Parse_item) ->
case peek(State) of
symbol_star ->
case Parse_item(State, Pending) of
{ok, {Item, State@1}} ->
{Next_pending, State@2} = consume_comments(State@1),
items_recovering_loop(
State@2,
[Item | Item_acc],
Error_acc,
Next_pending,
Parse_item
);
{error, Err} ->
State@3 = skip_until(advance(State), fun at_item_boundary/1),
{Next_pending@1, State@4} = consume_comments(State@3),
items_recovering_loop(
State@4,
Item_acc,
[Err | Error_acc],
Next_pending@1,
Parse_item
)
end;
_ ->
{lists:reverse(Item_acc), lists:reverse(Error_acc), Pending, State}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 601).
?DOC(" Parse expect items with recovery between items.\n").
-spec parse_expect_items_recovering(parser_state()) -> {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, State@1} = consume_comments(State),
items_recovering_loop(State@1, [], [], Pending, fun parse_expect_item/2).
-file("src/caffeine_lang/frontend/parser.gleam", 513).
?DOC(
" Parse just the header of an expects block.\n"
" Handles both `Expectations measured by \"name\"` and `Unmeasured Expectations`.\n"
).
-spec parse_expects_block_header(parser_state()) -> {ok,
{gleam@option:option(binary()), parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}.
parse_expects_block_header(State) ->
case peek(State) of
keyword_unmeasured ->
State@1 = advance(State),
gleam@result:'try'(
expect(State@1, keyword_expectations, <<"Expectations"/utf8>>),
fun(State@2) -> {ok, {none, State@2}} end
);
_ ->
gleam@result:'try'(
expect(State, keyword_expectations, <<"Expectations"/utf8>>),
fun(State@3) ->
gleam@result:'try'(
expect(State@3, keyword_measured, <<"measured"/utf8>>),
fun(State@4) ->
gleam@result:'try'(
expect(State@4, keyword_by, <<"by"/utf8>>),
fun(State@5) ->
gleam@result:'try'(
parse_string_literal(State@5),
fun(_use0) ->
{Measurement, State@6} = _use0,
{ok, {{some, Measurement}, State@6}}
end
)
end
)
end
)
end
)
end.
-file("src/caffeine_lang/frontend/parser.gleam", 541).
?DOC(" Generic single-block recovery parameterized by header parser, item parser, and block constructor.\n").
-spec block_recovering(
parser_state(),
list(caffeine_lang@frontend@ast:comment()),
fun((parser_state()) -> {ok, {KWA, parser_state()}} |
{error, caffeine_lang@frontend@parser_error:parser_error()}),
fun((parser_state()) -> {list(KWD),
list(caffeine_lang@frontend@parser_error:parser_error()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}),
fun((KWA, list(KWD), list(caffeine_lang@frontend@ast:comment())) -> KWJ)
) -> {{ok, {KWJ, list(caffeine_lang@frontend@ast:comment())}} |
{error, caffeine_lang@frontend@parser_error:parser_error()},
list(caffeine_lang@frontend@parser_error:parser_error()),
parser_state()}.
block_recovering(
State,
Leading_comments,
Parse_header,
Parse_items,
Build_block
) ->
case Parse_header(State) of
{error, Err} ->
{{error, Err}, [], State};
{ok, {Header_data, State@1}} ->
{Items, Item_errors, Trailing, State@2} = Parse_items(State@1),
{{ok, {Build_block(Header_data, Items, Leading_comments), Trailing}},
Item_errors,
State@2}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 492).
?DOC(" Parse a single expects block, recovering from item-level errors.\n").
-spec parse_expects_block_recovering(
parser_state(),
list(caffeine_lang@frontend@ast:comment())
) -> {{ok,
{caffeine_lang@frontend@ast:expects_block(),
list(caffeine_lang@frontend@ast:comment())}} |
{error, caffeine_lang@frontend@parser_error:parser_error()},
list(caffeine_lang@frontend@parser_error:parser_error()),
parser_state()}.
parse_expects_block_recovering(State, Leading_comments) ->
block_recovering(
State,
Leading_comments,
fun parse_expects_block_header/1,
fun parse_expect_items_recovering/1,
fun(Measurement, Items, Comments) ->
{expects_block, Measurement, Items, Comments}
end
).
-file("src/caffeine_lang/frontend/parser.gleam", 444).
?DOC(" Custom block-level recovery loop that matches either KeywordExpectations or KeywordUnmeasured.\n").
-spec expects_blocks_loop(
parser_state(),
list(caffeine_lang@frontend@ast:expects_block()),
list(caffeine_lang@frontend@parser_error:parser_error()),
list(caffeine_lang@frontend@ast:comment())
) -> {list(caffeine_lang@frontend@ast:expects_block()),
list(caffeine_lang@frontend@parser_error:parser_error()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}.
expects_blocks_loop(State, Block_acc, Error_acc, Pending) ->
case peek(State) of
e_o_f ->
{lists:reverse(Block_acc), Error_acc, Pending, State};
keyword_expectations ->
{Block_result, Item_errors, State@1} = parse_expects_block_recovering(
State,
Pending
),
{More_comments, State@2} = consume_comments(State@1),
case Block_result of
{ok, {Block, Trailing}} ->
Next_pending = lists:append(Trailing, More_comments),
expects_blocks_loop(
State@2,
[Block | Block_acc],
lists:append(Error_acc, Item_errors),
Next_pending
);
{error, Err} ->
State@3 = skip_until(
advance(State@2),
fun at_block_boundary/1
),
{Next_pending@1, State@4} = consume_comments(State@3),
expects_blocks_loop(
State@4,
Block_acc,
lists:append(Error_acc, [Err | Item_errors]),
Next_pending@1
)
end;
keyword_unmeasured ->
{Block_result, Item_errors, State@1} = parse_expects_block_recovering(
State,
Pending
),
{More_comments, State@2} = consume_comments(State@1),
case Block_result of
{ok, {Block, Trailing}} ->
Next_pending = lists:append(Trailing, More_comments),
expects_blocks_loop(
State@2,
[Block | Block_acc],
lists:append(Error_acc, Item_errors),
Next_pending
);
{error, Err} ->
State@3 = skip_until(
advance(State@2),
fun at_block_boundary/1
),
{Next_pending@1, State@4} = consume_comments(State@3),
expects_blocks_loop(
State@4,
Block_acc,
lists:append(Error_acc, [Err | Item_errors]),
Next_pending@1
)
end;
Tok ->
Err@1 = {unexpected_token,
<<"Expectations or Unmeasured Expectations"/utf8>>,
caffeine_lang@frontend@token:to_string(Tok),
erlang:element(3, State),
erlang:element(4, State)},
{lists:reverse(Block_acc),
lists:append(Error_acc, [Err@1]),
Pending,
State}
end.
-file("src/caffeine_lang/frontend/parser.gleam", 436).
?DOC(
" Parse expects blocks with error recovery between blocks and items.\n"
" Recognizes both `Expectations measured by \"...\"` and `Unmeasured Expectations` headers.\n"
).
-spec parse_expects_blocks_recovering(
parser_state(),
list(caffeine_lang@frontend@ast:comment())
) -> {list(caffeine_lang@frontend@ast:expects_block()),
list(caffeine_lang@frontend@parser_error:parser_error()),
list(caffeine_lang@frontend@ast:comment()),
parser_state()}.
parse_expects_blocks_recovering(State, Pending) ->
expects_blocks_loop(State, [], [], Pending).
-file("src/caffeine_lang/frontend/parser.gleam", 73).
?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,
{Blocks, Errors, Pending@2, _} = parse_expects_blocks_recovering(
State@2,
Pending@1
),
case Errors of
[] ->
{ok, {expects_file, Extendables, Blocks, Pending@2}};
Errs ->
{error, Errs}
end
end
)
end
).