-module(ghtml@parser). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/ghtml/parser.gleam"). -export([tokenize/1, parse/1, format_error/2, format_errors/2]). -export_type([stack_frame/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " Template parser for `.ghtml` files.\n" "\n" " Converts template source text into tokens and builds an AST representing\n" " the template structure including HTML elements, expressions, and control flow.\n" ). -type stack_frame() :: {element_frame, binary(), list(ghtml@types:attr()), list(ghtml@types:node_()), ghtml@types:span()} | {if_frame, binary(), list(ghtml@types:node_()), list(ghtml@types:node_()), boolean(), ghtml@types:span()} | {each_frame, binary(), binary(), gleam@option:option(binary()), list(ghtml@types:node_()), ghtml@types:span()} | {case_frame, binary(), gleam@option:option(binary()), list(ghtml@types:node_()), list(ghtml@types:case_branch()), ghtml@types:span()}. -file("src/ghtml/parser.gleam", 154). -spec advance_pos(ghtml@types:position(), binary()) -> ghtml@types:position(). advance_pos(Pos, Char) -> case Char of <<"\n"/utf8>> -> {position, erlang:element(2, Pos) + 1, 1}; _ -> {position, erlang:element(2, Pos), erlang:element(3, Pos) + 1} end. -file("src/ghtml/parser.gleam", 161). -spec advance_pos_str(ghtml@types:position(), binary()) -> ghtml@types:position(). advance_pos_str(Pos, Str) -> _pipe = gleam@string:to_graphemes(Str), gleam@list:fold(_pipe, Pos, fun advance_pos/2). -file("src/ghtml/parser.gleam", 232). -spec split_params(binary(), list(binary()), binary(), integer()) -> list(binary()). split_params(Input, Acc, Current, Depth) -> case gleam_stdlib:string_pop_grapheme(Input) of {error, _} -> case Current of <<""/utf8>> -> lists:reverse(Acc); _ -> lists:reverse([Current | Acc]) end; {ok, {Char, Rest}} -> case Char of <<"("/utf8>> -> split_params( Rest, Acc, <>, Depth + 1 ); <<"["/utf8>> -> split_params( Rest, Acc, <>, Depth + 1 ); <<"{"/utf8>> -> split_params( Rest, Acc, <>, Depth + 1 ); <<")"/utf8>> -> split_params( Rest, Acc, <>, Depth - 1 ); <<"]"/utf8>> -> split_params( Rest, Acc, <>, Depth - 1 ); <<"}"/utf8>> -> split_params( Rest, Acc, <>, Depth - 1 ); <<","/utf8>> when Depth =:= 0 -> split_params(Rest, [Current | Acc], <<""/utf8>>, 0); _ -> split_params( Rest, Acc, <>, Depth ) end end. -file("src/ghtml/parser.gleam", 213). -spec parse_param_list(binary()) -> list({binary(), binary()}). parse_param_list(Content) -> _pipe = Content, _pipe@1 = gleam@string:trim(_pipe), _pipe@2 = split_params(_pipe@1, [], <<""/utf8>>, 0), gleam@list:filter_map( _pipe@2, fun(Param_str) -> Param_str@1 = gleam@string:trim(Param_str), case Param_str@1 of <<""/utf8>> -> {error, nil}; _ -> case gleam@string:split_once(Param_str@1, <<":"/utf8>>) of {ok, {Name, Type_str}} -> {ok, {gleam@string:trim(Name), gleam@string:trim(Type_str)}}; {error, _} -> {error, nil} end end end ). -file("src/ghtml/parser.gleam", 358). -spec is_tag_name_char(binary()) -> boolean(). is_tag_name_char(Char) -> case Char of <<"a"/utf8>> -> true; <<"b"/utf8>> -> true; <<"c"/utf8>> -> true; <<"d"/utf8>> -> true; <<"e"/utf8>> -> true; <<"f"/utf8>> -> true; <<"g"/utf8>> -> true; <<"h"/utf8>> -> true; <<"i"/utf8>> -> true; <<"j"/utf8>> -> true; <<"k"/utf8>> -> true; <<"l"/utf8>> -> true; <<"m"/utf8>> -> true; <<"n"/utf8>> -> true; <<"o"/utf8>> -> true; <<"p"/utf8>> -> true; <<"q"/utf8>> -> true; <<"r"/utf8>> -> true; <<"s"/utf8>> -> true; <<"t"/utf8>> -> true; <<"u"/utf8>> -> true; <<"v"/utf8>> -> true; <<"w"/utf8>> -> true; <<"x"/utf8>> -> true; <<"y"/utf8>> -> true; <<"z"/utf8>> -> true; <<"A"/utf8>> -> true; <<"B"/utf8>> -> true; <<"C"/utf8>> -> true; <<"D"/utf8>> -> true; <<"E"/utf8>> -> true; <<"F"/utf8>> -> true; <<"G"/utf8>> -> true; <<"H"/utf8>> -> true; <<"I"/utf8>> -> true; <<"J"/utf8>> -> true; <<"K"/utf8>> -> true; <<"L"/utf8>> -> true; <<"M"/utf8>> -> true; <<"N"/utf8>> -> true; <<"O"/utf8>> -> true; <<"P"/utf8>> -> true; <<"Q"/utf8>> -> true; <<"R"/utf8>> -> true; <<"S"/utf8>> -> true; <<"T"/utf8>> -> true; <<"U"/utf8>> -> true; <<"V"/utf8>> -> true; <<"W"/utf8>> -> true; <<"X"/utf8>> -> true; <<"Y"/utf8>> -> true; <<"Z"/utf8>> -> true; <<"0"/utf8>> -> true; <<"1"/utf8>> -> true; <<"2"/utf8>> -> true; <<"3"/utf8>> -> true; <<"4"/utf8>> -> true; <<"5"/utf8>> -> true; <<"6"/utf8>> -> true; <<"7"/utf8>> -> true; <<"8"/utf8>> -> true; <<"9"/utf8>> -> true; <<"-"/utf8>> -> true; <<"_"/utf8>> -> true; _ -> false end. -file("src/ghtml/parser.gleam", 342). -spec extract_tag_name(binary(), binary(), ghtml@types:position()) -> {binary(), binary(), ghtml@types:position()}. extract_tag_name(Input, Acc, Pos) -> case gleam_stdlib:string_pop_grapheme(Input) of {error, _} -> {Acc, Input, Pos}; {ok, {Char, Rest}} -> case is_tag_name_char(Char) of true -> extract_tag_name( Rest, <>, advance_pos(Pos, Char) ); false -> {Acc, Input, Pos} end end. -file("src/ghtml/parser.gleam", 559). -spec is_attr_name_char(binary()) -> boolean(). is_attr_name_char(Char) -> case Char of <<"a"/utf8>> -> true; <<"b"/utf8>> -> true; <<"c"/utf8>> -> true; <<"d"/utf8>> -> true; <<"e"/utf8>> -> true; <<"f"/utf8>> -> true; <<"g"/utf8>> -> true; <<"h"/utf8>> -> true; <<"i"/utf8>> -> true; <<"j"/utf8>> -> true; <<"k"/utf8>> -> true; <<"l"/utf8>> -> true; <<"m"/utf8>> -> true; <<"n"/utf8>> -> true; <<"o"/utf8>> -> true; <<"p"/utf8>> -> true; <<"q"/utf8>> -> true; <<"r"/utf8>> -> true; <<"s"/utf8>> -> true; <<"t"/utf8>> -> true; <<"u"/utf8>> -> true; <<"v"/utf8>> -> true; <<"w"/utf8>> -> true; <<"x"/utf8>> -> true; <<"y"/utf8>> -> true; <<"z"/utf8>> -> true; <<"A"/utf8>> -> true; <<"B"/utf8>> -> true; <<"C"/utf8>> -> true; <<"D"/utf8>> -> true; <<"E"/utf8>> -> true; <<"F"/utf8>> -> true; <<"G"/utf8>> -> true; <<"H"/utf8>> -> true; <<"I"/utf8>> -> true; <<"J"/utf8>> -> true; <<"K"/utf8>> -> true; <<"L"/utf8>> -> true; <<"M"/utf8>> -> true; <<"N"/utf8>> -> true; <<"O"/utf8>> -> true; <<"P"/utf8>> -> true; <<"Q"/utf8>> -> true; <<"R"/utf8>> -> true; <<"S"/utf8>> -> true; <<"T"/utf8>> -> true; <<"U"/utf8>> -> true; <<"V"/utf8>> -> true; <<"W"/utf8>> -> true; <<"X"/utf8>> -> true; <<"Y"/utf8>> -> true; <<"Z"/utf8>> -> true; <<"0"/utf8>> -> true; <<"1"/utf8>> -> true; <<"2"/utf8>> -> true; <<"3"/utf8>> -> true; <<"4"/utf8>> -> true; <<"5"/utf8>> -> true; <<"6"/utf8>> -> true; <<"7"/utf8>> -> true; <<"8"/utf8>> -> true; <<"9"/utf8>> -> true; <<"-"/utf8>> -> true; <<"_"/utf8>> -> true; <<":"/utf8>> -> true; _ -> false end. -file("src/ghtml/parser.gleam", 543). -spec extract_attr_name(binary(), binary(), ghtml@types:position()) -> {binary(), binary(), ghtml@types:position()}. extract_attr_name(Input, Acc, Pos) -> case gleam_stdlib:string_pop_grapheme(Input) of {error, _} -> {Acc, Input, Pos}; {ok, {Char, Rest}} -> case is_attr_name_char(Char) of true -> extract_attr_name( Rest, <>, advance_pos(Pos, Char) ); false -> {Acc, Input, Pos} end end. -file("src/ghtml/parser.gleam", 630). -spec extract_quoted_value(binary(), binary(), ghtml@types:position()) -> {ok, {binary(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. extract_quoted_value(Input, Acc, Pos) -> case gleam_stdlib:string_pop_grapheme(Input) of {error, _} -> {error, {parse_error, {span, Pos, Pos}, <<"Unterminated string"/utf8>>}}; {ok, {<<"\""/utf8>>, Rest}} -> End_pos = advance_pos(Pos, <<"\""/utf8>>), {ok, {Acc, Rest, End_pos}}; {ok, {<<"\\"/utf8>>, Rest@1}} -> case gleam_stdlib:string_pop_grapheme(Rest@1) of {ok, {Escaped, Rest2}} -> New_pos = advance_pos( advance_pos(Pos, <<"\\"/utf8>>), Escaped ), extract_quoted_value( Rest2, <>, New_pos ); {error, _} -> {error, {parse_error, {span, Pos, Pos}, <<"Unterminated escape"/utf8>>}} end; {ok, {Char, Rest@2}} -> extract_quoted_value( Rest@2, <>, advance_pos(Pos, Char) ) end. -file("src/ghtml/parser.gleam", 657). -spec extract_single_quoted_value(binary(), binary(), ghtml@types:position()) -> {ok, {binary(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. extract_single_quoted_value(Input, Acc, Pos) -> case gleam_stdlib:string_pop_grapheme(Input) of {error, _} -> {error, {parse_error, {span, Pos, Pos}, <<"Unterminated string"/utf8>>}}; {ok, {<<"'"/utf8>>, Rest}} -> End_pos = advance_pos(Pos, <<"'"/utf8>>), {ok, {Acc, Rest, End_pos}}; {ok, {<<"\\"/utf8>>, Rest@1}} -> case gleam_stdlib:string_pop_grapheme(Rest@1) of {ok, {Escaped, Rest2}} -> New_pos = advance_pos( advance_pos(Pos, <<"\\"/utf8>>), Escaped ), extract_single_quoted_value( Rest2, <>, New_pos ); {error, _} -> {error, {parse_error, {span, Pos, Pos}, <<"Unterminated escape"/utf8>>}} end; {ok, {Char, Rest@2}} -> extract_single_quoted_value( Rest@2, <>, advance_pos(Pos, Char) ) end. -file("src/ghtml/parser.gleam", 815). -spec find_as_keyword(binary(), binary(), ghtml@types:position()) -> {ok, {binary(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. find_as_keyword(Input, Acc, Pos) -> case Input of <<""/utf8>> -> {error, {parse_error, {span, Pos, Pos}, <<"Expected 'as' keyword in each"/utf8>>}}; <<" as "/utf8, Rest/binary>> -> New_pos = advance_pos_str(Pos, <<" as "/utf8>>), {ok, {Acc, Rest, New_pos}}; _ -> case gleam_stdlib:string_pop_grapheme(Input) of {ok, {Char, Rest@1}} -> find_as_keyword( Rest@1, <>, advance_pos(Pos, Char) ); {error, _} -> {error, {parse_error, {span, Pos, Pos}, <<"Expected 'as' keyword in each"/utf8>>}} end end. -file("src/ghtml/parser.gleam", 844). -spec extract_until_close_brace(binary(), binary(), ghtml@types:position()) -> {binary(), binary(), ghtml@types:position()}. extract_until_close_brace(Input, Acc, Pos) -> case gleam_stdlib:string_pop_grapheme(Input) of {error, _} -> {Acc, Input, Pos}; {ok, {<<"}"/utf8>>, Rest}} -> End_pos = advance_pos(Pos, <<"}"/utf8>>), {Acc, Rest, End_pos}; {ok, {Char, Rest@1}} -> extract_until_close_brace( Rest@1, <>, advance_pos(Pos, Char) ) end. -file("src/ghtml/parser.gleam", 860). -spec parse_binding(binary()) -> {binary(), gleam@option:option(binary())}. parse_binding(Binding) -> Binding@1 = gleam@string:trim(Binding), case gleam@string:split_once(Binding@1, <<","/utf8>>) of {ok, {Item, Index}} -> {gleam@string:trim(Item), {some, gleam@string:trim(Index)}}; {error, _} -> {Binding@1, none} end. -file("src/ghtml/parser.gleam", 798). -spec parse_each_header(binary(), ghtml@types:position()) -> {ok, {binary(), binary(), gleam@option:option(binary()), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. parse_each_header(Input, Pos) -> case find_as_keyword(Input, <<""/utf8>>, Pos) of {ok, {Collection, Rest, New_pos}} -> {Binding, Rest@1, End_pos} = extract_until_close_brace( Rest, <<""/utf8>>, New_pos ), {Item, Index} = parse_binding(Binding), {ok, {gleam@string:trim(Collection), Item, Index, Rest@1, End_pos}}; {error, Err} -> {error, Err} end. -file("src/ghtml/parser.gleam", 947). -spec extract_string_literal(binary(), binary(), ghtml@types:position()) -> {ok, {binary(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. extract_string_literal(Input, Acc, Pos) -> case gleam_stdlib:string_pop_grapheme(Input) of {error, _} -> {error, {parse_error, {span, Pos, Pos}, <<"Unterminated string literal"/utf8>>}}; {ok, {<<"\\"/utf8>>, Rest}} -> case gleam_stdlib:string_pop_grapheme(Rest) of {ok, {Escaped, Rest2}} -> New_pos = advance_pos( advance_pos(Pos, <<"\\"/utf8>>), Escaped ), extract_string_literal( Rest2, <<<>/binary, Escaped/binary>>, New_pos ); {error, _} -> {error, {parse_error, {span, Pos, Pos}, <<"Unterminated escape sequence"/utf8>>}} end; {ok, {<<"\""/utf8>>, Rest@1}} -> End_pos = advance_pos(Pos, <<"\""/utf8>>), {ok, {Acc, Rest@1, End_pos}}; {ok, {Char, Rest@2}} -> extract_string_literal( Rest@2, <>, advance_pos(Pos, Char) ) end. -file("src/ghtml/parser.gleam", 897). -spec extract_expression(binary(), integer(), binary(), ghtml@types:position()) -> {ok, {binary(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. extract_expression(Input, Depth, Acc, Pos) -> case gleam_stdlib:string_pop_grapheme(Input) of {error, _} -> {error, {parse_error, {span, Pos, Pos}, <<"Unexpected end of input while parsing expression"/utf8>>}}; {ok, {<<"{"/utf8>>, Rest}} -> extract_expression( Rest, Depth + 1, <>, advance_pos(Pos, <<"{"/utf8>>) ); {ok, {<<"}"/utf8>>, Rest@1}} -> case Depth of 0 -> End_pos = advance_pos(Pos, <<"}"/utf8>>), {ok, {Acc, Rest@1, End_pos}}; _ -> extract_expression( Rest@1, Depth - 1, <>, advance_pos(Pos, <<"}"/utf8>>) ) end; {ok, {<<"\""/utf8>>, Rest@2}} -> New_pos = advance_pos(Pos, <<"\""/utf8>>), case extract_string_literal(Rest@2, <<""/utf8>>, New_pos) of {ok, {Str, Remaining, After_pos}} -> extract_expression( Remaining, Depth, <<<<<>/binary, Str/binary>>/binary, "\""/utf8>>, After_pos ); {error, Err} -> {error, Err} end; {ok, {<<"("/utf8>>, Rest@3}} -> extract_expression( Rest@3, Depth, <>, advance_pos(Pos, <<"("/utf8>>) ); {ok, {<<")"/utf8>>, Rest@4}} -> extract_expression( Rest@4, Depth, <>, advance_pos(Pos, <<")"/utf8>>) ); {ok, {Char, Rest@5}} -> extract_expression( Rest@5, Depth, <>, advance_pos(Pos, Char) ) end. -file("src/ghtml/parser.gleam", 451). -spec parse_single_attribute(binary(), ghtml@types:position()) -> {ok, {ghtml@types:attr(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. parse_single_attribute(Input, Pos) -> case Input of <<"@"/utf8, Rest/binary>> -> Pos@1 = advance_pos(Pos, <<"@"/utf8>>), {Event_name, Rest@1, New_pos} = extract_attr_name( Rest, <<""/utf8>>, Pos@1 ), case Rest@1 of <<"={"/utf8, After/binary>> -> New_pos@1 = advance_pos_str(New_pos, <<"={"/utf8>>), case extract_expression(After, 0, <<""/utf8>>, New_pos@1) of {ok, {Handler, Remaining, End_pos}} -> {ok, {{event_attr, Event_name, Handler}, Remaining, End_pos}}; {error, Err} -> {error, Err} end; _ -> {error, {parse_error, {span, New_pos, New_pos}, <<"Expected '={' after event attribute name"/utf8>>}} end; _ -> {Attr_name, Rest@2, New_pos@2} = extract_attr_name( Input, <<""/utf8>>, Pos ), case Attr_name of <<""/utf8>> -> {error, {parse_error, {span, Pos, Pos}, <<"Expected attribute name"/utf8>>}}; _ -> case Rest@2 of <<"={"/utf8, After@1/binary>> -> New_pos@3 = advance_pos_str( New_pos@2, <<"={"/utf8>> ), case extract_expression( After@1, 0, <<""/utf8>>, New_pos@3 ) of {ok, {Expr, Remaining@1, End_pos@1}} -> {ok, {{dynamic_attr, Attr_name, Expr}, Remaining@1, End_pos@1}}; {error, Err@1} -> {error, Err@1} end; <<"=\""/utf8, After@2/binary>> -> New_pos@4 = advance_pos_str( New_pos@2, <<"=\""/utf8>> ), case extract_quoted_value( After@2, <<""/utf8>>, New_pos@4 ) of {ok, {Value, Remaining@2, End_pos@2}} -> {ok, {{static_attr, Attr_name, Value}, Remaining@2, End_pos@2}}; {error, Err@2} -> {error, Err@2} end; <<"='"/utf8, After@3/binary>> -> New_pos@5 = advance_pos_str( New_pos@2, <<"='"/utf8>> ), case extract_single_quoted_value( After@3, <<""/utf8>>, New_pos@5 ) of {ok, {Value@1, Remaining@3, End_pos@3}} -> {ok, {{static_attr, Attr_name, Value@1}, Remaining@3, End_pos@3}}; {error, Err@3} -> {error, Err@3} end; _ -> {ok, {{boolean_attr, Attr_name}, Rest@2, New_pos@2}} end end end. -file("src/ghtml/parser.gleam", 983). -spec parse_text(binary(), binary(), ghtml@types:position()) -> {binary(), binary(), ghtml@types:position()}. parse_text(Input, Acc, Pos) -> case Input of <<"{{"/utf8, Rest/binary>> -> parse_text( Rest, <>, advance_pos_str(Pos, <<"{{"/utf8>>) ); <<"}}"/utf8, Rest@1/binary>> -> parse_text( Rest@1, <>, advance_pos_str(Pos, <<"}}"/utf8>>) ); <<"{"/utf8, _/binary>> -> {Acc, Input, Pos}; <<"<"/utf8, _/binary>> -> {Acc, Input, Pos}; <<"@import"/utf8, _/binary>> -> {Acc, Input, Pos}; <<"@params"/utf8, _/binary>> -> {Acc, Input, Pos}; <<""/utf8>> -> {Acc, Input, Pos}; _ -> case gleam_stdlib:string_pop_grapheme(Input) of {ok, {Char, Rest@2}} -> parse_text( Rest@2, <>, advance_pos(Pos, Char) ); {error, _} -> {Acc, Input, Pos} end end. -file("src/ghtml/parser.gleam", 1019). -spec skip_html_comment(binary(), ghtml@types:position()) -> {ok, {binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. skip_html_comment(Input, Pos) -> case Input of <<""/utf8>> -> {error, {parse_error, {span, Pos, Pos}, <<"Unterminated HTML comment"/utf8>>}}; <<"-->"/utf8, Rest/binary>> -> End_pos = advance_pos_str(Pos, <<"-->"/utf8>>), {ok, {Rest, End_pos}}; _ -> case gleam_stdlib:string_pop_grapheme(Input) of {ok, {Char, Rest@1}} -> skip_html_comment(Rest@1, advance_pos(Pos, Char)); {error, _} -> {error, {parse_error, {span, Pos, Pos}, <<"Unterminated HTML comment"/utf8>>}} end end. -file("src/ghtml/parser.gleam", 1045). -spec is_whitespace_only(binary()) -> boolean(). is_whitespace_only(Text) -> _pipe = Text, _pipe@1 = gleam@string:to_graphemes(_pipe), gleam@list:all( _pipe@1, fun(C) -> (((C =:= <<" "/utf8>>) orelse (C =:= <<"\t"/utf8>>)) orelse (C =:= <<"\n"/utf8>>)) orelse (C =:= <<"\r"/utf8>>) end ). -file("src/ghtml/parser.gleam", 1051). -spec skip_whitespace(binary(), ghtml@types:position()) -> {binary(), ghtml@types:position()}. skip_whitespace(Input, Pos) -> case gleam_stdlib:string_pop_grapheme(Input) of {ok, {<<" "/utf8>>, Rest}} -> skip_whitespace(Rest, advance_pos(Pos, <<" "/utf8>>)); {ok, {<<"\t"/utf8>>, Rest@1}} -> skip_whitespace(Rest@1, advance_pos(Pos, <<"\t"/utf8>>)); {ok, {<<"\n"/utf8>>, Rest@2}} -> skip_whitespace(Rest@2, advance_pos(Pos, <<"\n"/utf8>>)); {ok, {<<"\r"/utf8>>, Rest@3}} -> skip_whitespace(Rest@3, advance_pos(Pos, <<"\r"/utf8>>)); _ -> {Input, Pos} end. -file("src/ghtml/parser.gleam", 258). -spec parse_close_tag(binary(), ghtml@types:position(), ghtml@types:position()) -> {ok, {ghtml@types:token(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. parse_close_tag(Input, Start_pos, Pos) -> {Tag_name, Rest, New_pos} = extract_tag_name(Input, <<""/utf8>>, Pos), case Tag_name of <<""/utf8>> -> {error, {parse_error, {span, Pos, Pos}, <<"Expected tag name"/utf8>>}}; _ -> {Rest@1, New_pos@1} = skip_whitespace(Rest, New_pos), case Rest@1 of <<">"/utf8, Remaining/binary>> -> End_pos = advance_pos(New_pos@1, <<">"/utf8>>), Span = {span, Start_pos, End_pos}, {ok, {{html_close, Tag_name, Span}, Remaining, End_pos}}; _ -> {error, {parse_error, {span, New_pos@1, New_pos@1}, <<"Expected '>' in closing tag"/utf8>>}} end end. -file("src/ghtml/parser.gleam", 430). -spec parse_attributes( binary(), ghtml@types:position(), list(ghtml@types:attr()) ) -> {ok, {list(ghtml@types:attr()), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. parse_attributes(Input, Pos, Attrs) -> {Input@1, Pos@1} = skip_whitespace(Input, Pos), case Input@1 of <<">"/utf8, _/binary>> -> {ok, {Attrs, Input@1, Pos@1}}; <<"/>"/utf8, _/binary>> -> {ok, {Attrs, Input@1, Pos@1}}; <<""/utf8>> -> {ok, {Attrs, Input@1, Pos@1}}; _ -> case parse_single_attribute(Input@1, Pos@1) of {ok, {Attr, Remaining, New_pos}} -> parse_attributes(Remaining, New_pos, [Attr | Attrs]); {error, Err} -> {error, Err} end end. -file("src/ghtml/parser.gleam", 285). -spec parse_open_tag(binary(), ghtml@types:position(), ghtml@types:position()) -> {ok, {ghtml@types:token(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. parse_open_tag(Input, Start_pos, Pos) -> {Tag_name, Rest, New_pos} = extract_tag_name(Input, <<""/utf8>>, Pos), case Tag_name of <<""/utf8>> -> {error, {parse_error, {span, Pos, Pos}, <<"Expected tag name"/utf8>>}}; _ -> case parse_attributes(Rest, New_pos, []) of {ok, {Attrs, Remaining, Attr_end_pos}} -> {Remaining@1, Attr_end_pos@1} = skip_whitespace( Remaining, Attr_end_pos ), case Remaining@1 of <<"/>"/utf8, After/binary>> -> End_pos = advance_pos_str( Attr_end_pos@1, <<"/>"/utf8>> ), Span = {span, Start_pos, End_pos}, {ok, {{html_open, Tag_name, lists:reverse(Attrs), true, Span}, After, End_pos}}; <<">"/utf8, After@1/binary>> -> End_pos@1 = advance_pos( Attr_end_pos@1, <<">"/utf8>> ), Span@1 = {span, Start_pos, End_pos@1}, {ok, {{html_open, Tag_name, lists:reverse(Attrs), false, Span@1}, After@1, End_pos@1}}; _ -> {error, {parse_error, {span, Attr_end_pos@1, Attr_end_pos@1}, <<"Expected '>' or '/>' to close tag"/utf8>>}} end; {error, Err} -> {error, Err} end end. -file("src/ghtml/parser.gleam", 1061). -spec extract_balanced_parens( binary(), integer(), binary(), ghtml@types:position() ) -> {ok, {binary(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. extract_balanced_parens(Input, Depth, Acc, Pos) -> case gleam_stdlib:string_pop_grapheme(Input) of {error, _} -> {error, {parse_error, {span, Pos, Pos}, <<"Unterminated parentheses"/utf8>>}}; {ok, {<<"("/utf8>>, Rest}} -> extract_balanced_parens( Rest, Depth + 1, <>, advance_pos(Pos, <<"("/utf8>>) ); {ok, {<<")"/utf8>>, Rest@1}} -> case Depth of 0 -> End_pos = advance_pos(Pos, <<")"/utf8>>), {ok, {Acc, Rest@1, End_pos}}; _ -> extract_balanced_parens( Rest@1, Depth - 1, <>, advance_pos(Pos, <<")"/utf8>>) ) end; {ok, {<<"\""/utf8>>, Rest@2}} -> New_pos = advance_pos(Pos, <<"\""/utf8>>), case extract_string_literal(Rest@2, <<""/utf8>>, New_pos) of {ok, {Str, Remaining, After_pos}} -> extract_balanced_parens( Remaining, Depth, <<<<<>/binary, Str/binary>>/binary, "\""/utf8>>, After_pos ); {error, Err} -> {error, Err} end; {ok, {Char, Rest@3}} -> extract_balanced_parens( Rest@3, Depth, <>, advance_pos(Pos, Char) ) end. -file("src/ghtml/parser.gleam", 168). -spec parse_import(binary(), ghtml@types:position(), ghtml@types:position()) -> {ok, {ghtml@types:token(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. parse_import(Input, Start_pos, Pos) -> case Input of <<"("/utf8, Rest/binary>> -> Pos@1 = advance_pos(Pos, <<"("/utf8>>), case extract_balanced_parens(Rest, 0, <<""/utf8>>, Pos@1) of {ok, {Content, Remaining, End_pos}} -> Span = {span, Start_pos, End_pos}, {ok, {{import, Content, Span}, Remaining, End_pos}}; {error, Err} -> {error, Err} end; _ -> {error, {parse_error, {span, Pos, Pos}, <<"Expected '(' after @import"/utf8>>}} end. -file("src/ghtml/parser.gleam", 191). -spec parse_params(binary(), ghtml@types:position(), ghtml@types:position()) -> {ok, {ghtml@types:token(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. parse_params(Input, Start_pos, Pos) -> case Input of <<"("/utf8, Rest/binary>> -> Pos@1 = advance_pos(Pos, <<"("/utf8>>), case extract_balanced_parens(Rest, 0, <<""/utf8>>, Pos@1) of {ok, {Content, Remaining, End_pos}} -> Params = parse_param_list(Content), Span = {span, Start_pos, End_pos}, {ok, {{params, Params, Span}, Remaining, End_pos}}; {error, Err} -> {error, Err} end; _ -> {error, {parse_error, {span, Pos, Pos}, <<"Expected '(' after @params"/utf8>>}} end. -file("src/ghtml/parser.gleam", 868). -spec extract_pattern(binary(), binary(), ghtml@types:position()) -> {ok, {binary(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. extract_pattern(Input, Acc, Pos) -> case gleam_stdlib:string_pop_grapheme(Input) of {error, _} -> {error, {parse_error, {span, Pos, Pos}, <<"Unterminated pattern"/utf8>>}}; {ok, {<<"}"/utf8>>, Rest}} -> End_pos = advance_pos(Pos, <<"}"/utf8>>), {ok, {Acc, Rest, End_pos}}; {ok, {<<"("/utf8>>, Rest@1}} -> New_pos = advance_pos(Pos, <<"("/utf8>>), case extract_balanced_parens(Rest@1, 0, <<""/utf8>>, New_pos) of {ok, {Inner, Remaining, After_pos}} -> extract_pattern( Remaining, <<<<<>/binary, Inner/binary>>/binary, ")"/utf8>>, After_pos ); {error, Err} -> {error, Err} end; {ok, {Char, Rest@2}} -> extract_pattern( Rest@2, <>, advance_pos(Pos, Char) ) end. -file("src/ghtml/parser.gleam", 686). -spec parse_brace_content( binary(), ghtml@types:position(), ghtml@types:position() ) -> {ok, {ghtml@types:token(), binary(), ghtml@types:position()}} | {error, ghtml@types:parse_error()}. parse_brace_content(Input, Start_pos, Pos) -> case Input of <<"#if "/utf8, Rest/binary>> -> Pos@1 = advance_pos_str(Pos, <<"#if "/utf8>>), case extract_expression(Rest, 0, <<""/utf8>>, Pos@1) of {ok, {Condition, Remaining, End_pos}} -> Span = {span, Start_pos, End_pos}, {ok, {{if_start, gleam@string:trim(Condition), Span}, Remaining, End_pos}}; {error, Err} -> {error, Err} end; <<":else}"/utf8, Rest@1/binary>> -> End_pos@1 = advance_pos_str(Pos, <<":else}"/utf8>>), Span@1 = {span, Start_pos, End_pos@1}, {ok, {{'else', Span@1}, Rest@1, End_pos@1}}; <<"/if}"/utf8, Rest@2/binary>> -> End_pos@2 = advance_pos_str(Pos, <<"/if}"/utf8>>), Span@2 = {span, Start_pos, End_pos@2}, {ok, {{if_end, Span@2}, Rest@2, End_pos@2}}; <<"#each "/utf8, Rest@3/binary>> -> Pos@2 = advance_pos_str(Pos, <<"#each "/utf8>>), case parse_each_header(Rest@3, Pos@2) of {ok, {Collection, Item, Index, Remaining@1, End_pos@3}} -> Span@3 = {span, Start_pos, End_pos@3}, {ok, {{each_start, Collection, Item, Index, Span@3}, Remaining@1, End_pos@3}}; {error, Err@1} -> {error, Err@1} end; <<"/each}"/utf8, Rest@4/binary>> -> End_pos@4 = advance_pos_str(Pos, <<"/each}"/utf8>>), Span@4 = {span, Start_pos, End_pos@4}, {ok, {{each_end, Span@4}, Rest@4, End_pos@4}}; <<"#case "/utf8, Rest@5/binary>> -> Pos@3 = advance_pos_str(Pos, <<"#case "/utf8>>), case extract_expression(Rest@5, 0, <<""/utf8>>, Pos@3) of {ok, {Expr, Remaining@2, End_pos@5}} -> Span@5 = {span, Start_pos, End_pos@5}, {ok, {{case_start, gleam@string:trim(Expr), Span@5}, Remaining@2, End_pos@5}}; {error, Err@2} -> {error, Err@2} end; <<":"/utf8, Rest@6/binary>> -> Pos@4 = advance_pos(Pos, <<":"/utf8>>), case extract_pattern(Rest@6, <<""/utf8>>, Pos@4) of {ok, {Pattern, Remaining@3, End_pos@6}} -> Span@6 = {span, Start_pos, End_pos@6}, {ok, {{case_pattern, Pattern, Span@6}, Remaining@3, End_pos@6}}; {error, Err@3} -> {error, Err@3} end; <<"/case}"/utf8, Rest@7/binary>> -> End_pos@7 = advance_pos_str(Pos, <<"/case}"/utf8>>), Span@7 = {span, Start_pos, End_pos@7}, {ok, {{case_end, Span@7}, Rest@7, End_pos@7}}; _ -> case extract_expression(Input, 0, <<""/utf8>>, Pos) of {ok, {Expr@1, Remaining@4, End_pos@8}} -> Span@8 = {span, Start_pos, End_pos@8}, {ok, {{expr, Expr@1, Span@8}, Remaining@4, End_pos@8}}; {error, Err@4} -> {error, Err@4} end end. -file("src/ghtml/parser.gleam", 53). -spec tokenize_loop(binary(), ghtml@types:position(), list(ghtml@types:token())) -> {ok, list(ghtml@types:token())} | {error, list(ghtml@types:parse_error())}. tokenize_loop(Input, Pos, Tokens) -> case Input of <<""/utf8>> -> {ok, Tokens}; <<"@import"/utf8, Rest/binary>> -> Start_pos = Pos, Pos@1 = advance_pos_str(Pos, <<"@import"/utf8>>), case parse_import(Rest, Start_pos, Pos@1) of {ok, {Token, Remaining, New_pos}} -> tokenize_loop(Remaining, New_pos, [Token | Tokens]); {error, Err} -> {error, [Err]} end; <<"@params"/utf8, Rest@1/binary>> -> Start_pos@1 = Pos, Pos@2 = advance_pos_str(Pos, <<"@params"/utf8>>), case parse_params(Rest@1, Start_pos@1, Pos@2) of {ok, {Token@1, Remaining@1, New_pos@1}} -> tokenize_loop(Remaining@1, New_pos@1, [Token@1 | Tokens]); {error, Err@1} -> {error, [Err@1]} end; <<"