-module(glemplate@parser). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([parse/2, parse_to_template/3, new/0]). -export_type([parse_error/0, parser/0, token/0, lexer_mode/0, incomplete_tag/0, state/0]). -type parse_error() :: {lexer_error, binary()} | {parser_error, list(binary())}. -opaque parser() :: {parser, nibble@lexer:lexer(token(), lexer_mode()), nibble:parser(state(), token(), nil)}. -type token() :: {text, binary()} | tag_start | output_tag_start | right_angle | percent | 'end' | 'if' | 'else' | for | in | raw | render | colon | comma | period | {variable, binary()}. -type lexer_mode() :: tag | comment_tag | content. -type incomplete_tag() :: {if_tag, glemplate@ast:var(), gleam@option:option(list(glemplate@ast:node_()))} | {iter_tag, glemplate@ast:var(), binary()}. -type state() :: {state, list(glemplate@ast:node_()), gleam@string_builder:string_builder(), list(glemplate@ast:node_()), gleam@option:option(incomplete_tag()), gleam@option:option(state())}. -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 136). -spec lex(binary(), nibble@lexer:lexer(JMR, lexer_mode())) -> {ok, list(nibble@lexer:token(JMR))} | {error, parse_error()}. lex(Input, Lexer) -> _pipe = nibble@lexer:run_advanced(Input, content, Lexer), gleam@result:map_error( _pipe, fun(Error) -> {lexer_error, <<<<<<<<<<"No match found in ("/utf8, (gleam@int:to_string( erlang:element(2, Error) ))/binary>>/binary, ":"/utf8>>/binary, (gleam@int:to_string(erlang:element(3, Error)))/binary>>/binary, "), last lexeme: "/utf8>>/binary, (erlang:element(4, Error))/binary>>} end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 150). -spec gen_lexer() -> nibble@lexer:lexer(token(), lexer_mode()). gen_lexer() -> Right_angle = nibble@lexer:token(<<">"/utf8>>, right_angle), Percent = nibble@lexer:token(<<"%"/utf8>>, percent), End = nibble@lexer:keyword(<<"end"/utf8>>, <<"\\W"/utf8>>, 'end'), If_ = nibble@lexer:keyword(<<"if"/utf8>>, <<"\\W"/utf8>>, 'if'), Else_ = nibble@lexer:keyword(<<"else"/utf8>>, <<"\\W"/utf8>>, 'else'), For = nibble@lexer:keyword(<<"for"/utf8>>, <<"\\W"/utf8>>, for), In = nibble@lexer:keyword(<<"in"/utf8>>, <<"\\W"/utf8>>, in), Raw = nibble@lexer:keyword(<<"raw"/utf8>>, <<"\\W"/utf8>>, raw), Render = nibble@lexer:keyword(<<"render"/utf8>>, <<"\\W"/utf8>>, render), Colon = nibble@lexer:token(<<":"/utf8>>, colon), Comma = nibble@lexer:token(<<","/utf8>>, comma), Period = nibble@lexer:token(<<"."/utf8>>, period), Variable = nibble@lexer:variable( gleam@set:from_list( [<<"end"/utf8>>, <<"if"/utf8>>, <<"else"/utf8>>, <<"for"/utf8>>, <<"in"/utf8>>, <<"raw"/utf8>>, <<"render"/utf8>>] ), fun(Field@0) -> {variable, Field@0} end ), Whitespace = nibble@lexer:whitespace(nil), Text = nibble@lexer:keep(fun(Lexeme, Lookahead) -> case Lookahead of <<"<"/utf8>> -> {ok, {text, Lexeme}}; <<""/utf8>> -> {ok, {text, Lexeme}}; _ -> {error, nil} end end), Tag_start = (nibble@lexer:custom( fun(Mode, Lexeme@1, Lookahead@1) -> case {Lexeme@1, Lookahead@1} of {<<""/utf8>>, <<"<"/utf8>>} -> no_match; {<<"<"/utf8>>, <<"%"/utf8>>} -> no_match; {<<"<%"/utf8>>, <<"!"/utf8>>} -> skip; {<<"<%"/utf8>>, <<"%"/utf8>>} -> skip; {<<"<%"/utf8>>, <<"="/utf8>>} -> skip; {<<"<%"/utf8>>, _} -> {keep, tag_start, tag}; {<<"<%%"/utf8>>, _} -> {keep, {text, <<"<%%"/utf8>>}, Mode}; {<<"<%="/utf8>>, _} -> {keep, output_tag_start, tag}; {<<"<%!"/utf8>>, <<"-"/utf8>>} -> skip; {<<"<%!-"/utf8>>, <<"-"/utf8>>} -> skip; {<<"<%!--"/utf8>>, _} -> {drop, comment_tag}; {_, _} -> no_match end end )), Comment_content = (nibble@lexer:custom( fun(Mode@1, Lexeme@2, Lookahead@2) -> case {Lexeme@2, Lookahead@2} of {<<""/utf8>>, <<"-"/utf8>>} -> skip; {<<"-"/utf8>>, <<"-"/utf8>>} -> skip; {<<"--"/utf8>>, <<"%"/utf8>>} -> skip; {<<"--%"/utf8>>, <<">"/utf8>>} -> skip; {<<"--%>"/utf8>>, _} -> {drop, content}; {_, _} -> {drop, Mode@1} end end )), In_tag = [Percent, End, If_, Else_, For, In, Raw, Render, Colon, Comma, Period, Variable], nibble@lexer:advanced(fun(Mode@2) -> case Mode@2 of comment_tag -> [Comment_content]; tag -> [begin _pipe = Right_angle, nibble@lexer:into(_pipe, fun(_) -> content end) end, begin _pipe@1 = Whitespace, nibble@lexer:ignore(_pipe@1) end | In_tag]; content -> [Tag_start, Text] end end). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 279). -spec do_parse( list(nibble@lexer:token(JQT)), nibble:parser(state(), JQT, any()) ) -> {ok, list(glemplate@ast:node_())} | {error, parse_error()}. do_parse(Tokens, Parser) -> _pipe = nibble:run(Tokens, Parser), _pipe@1 = gleam@result:map( _pipe, fun(State) -> erlang:element(2, State) end ), gleam@result:map_error( _pipe@1, fun(Deadends) -> {parser_error, gleam@list:map( Deadends, fun(Deadend) -> <<<<<<<<<<<<<<<<<<"Dead end between ("/utf8, (gleam@int:to_string( erlang:element( 2, erlang:element( 2, Deadend ) ) ))/binary>>/binary, ":"/utf8>>/binary, (gleam@int:to_string( erlang:element( 3, erlang:element( 2, Deadend ) ) ))/binary>>/binary, ") and ("/utf8>>/binary, (gleam@int:to_string( erlang:element( 4, erlang:element(2, Deadend) ) ))/binary>>/binary, ":"/utf8>>/binary, (gleam@int:to_string( erlang:element( 5, erlang:element(2, Deadend) ) ))/binary>>/binary, "): "/utf8>>/binary, (gleam@string:inspect(erlang:element(3, Deadend)))/binary>> end )} end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 274). -spec parse(binary(), parser()) -> {ok, list(glemplate@ast:node_())} | {error, parse_error()}. parse(Input, Parser) -> gleam@result:'try'( lex(Input, erlang:element(2, Parser)), fun(Lexed) -> do_parse(Lexed, erlang:element(3, Parser)) end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 267). -spec parse_to_template(binary(), binary(), parser()) -> {ok, glemplate@ast:template()} | {error, parse_error()}. parse_to_template(Input, Name, Parser) -> gleam@result:'try'( parse(Input, Parser), fun(Parsed) -> {ok, {template, Name, Parsed}} end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 300). -spec empty_state() -> state(). empty_state() -> {state, [], gleam@string_builder:new(), [], none, none}. -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 459). -spec end_tag(state()) -> nibble:parser(state(), token(), any()). end_tag(State) -> nibble:do( nibble:token('end'), fun(_) -> Outer_state = gleam@option:lazy_unwrap( erlang:element(6, State), fun() -> empty_state() end ), case erlang:element(5, State) of {some, {if_tag, Var, If_true}} -> {If_true@1, State@1} = case If_true of {some, If_true_nodes} -> {If_true_nodes, State}; none -> {lists:reverse(erlang:element(4, State)), erlang:setelement(4, State, [])} end, If_false = lists:reverse(erlang:element(4, State@1)), nibble:succeed( erlang:setelement( 4, Outer_state, [{dynamic, {'if', Var, If_true@1, If_false}} | erlang:element(4, Outer_state)] ) ); {some, {iter_tag, Over, Binding}} -> nibble:succeed( erlang:setelement( 4, Outer_state, [{dynamic, {iter, Over, Binding, lists:reverse(erlang:element(4, State))}} | erlang:element(4, Outer_state)] ) ); _ -> nibble:fail( <<"Expected `if` or `for` tag for `end` tag"/utf8>> ) end end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 504). -spec else_tag(state()) -> nibble:parser(state(), token(), any()). else_tag(State) -> nibble:do(nibble:token('else'), fun(_) -> case erlang:element(5, State) of {some, {if_tag, Var, none}} -> nibble:succeed( erlang:setelement( 4, erlang:setelement( 5, State, {some, {if_tag, Var, {some, lists:reverse( erlang:element(4, State) )}}} ), [] ) ); _ -> nibble:fail(<<"Expected `if` to match `else` tag"/utf8>>) end end). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 521). -spec stringify_current_text(state()) -> state(). stringify_current_text(State) -> erlang:setelement( 3, erlang:setelement( 4, State, [{text, gleam@string_builder:to_string(erlang:element(3, State))} | erlang:element(4, State)] ), gleam@string_builder:new() ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 532). -spec variable_name() -> nibble:parser(binary(), token(), any()). variable_name() -> nibble:take_map(<<"Variable name"/utf8>>, fun(Token) -> case Token of {variable, Var} -> {some, Var}; _ -> none end end). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 540). -spec accessors() -> nibble:parser(list(binary()), token(), any()). accessors() -> nibble:do( nibble:sequence(variable_name(), nibble:token(period)), fun(Accessors) -> nibble:return(Accessors) end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 548). -spec variable() -> nibble:parser(glemplate@ast:var(), token(), any()). variable() -> nibble:do( variable_name(), fun(Name) -> nibble:do( nibble:one_of( [(nibble:do( nibble:token(period), fun(_) -> nibble:do( accessors(), fun(Accs) -> nibble:return(Accs) end ) end )), nibble:return([])] ), fun(Accessors) -> nibble:return( gleam@list:fold( Accessors, {assign, Name}, fun(Acc, Accessor) -> {field_access, Acc, Accessor} end ) ) end ) end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 356). -spec output_tag(state()) -> nibble:parser(state(), token(), any()). output_tag(State) -> nibble:do( nibble:one_of( [(nibble:do( nibble:token(raw), fun(_) -> nibble:do( variable(), fun(Var) -> nibble:succeed( erlang:setelement( 4, State, [{dynamic, {raw_output, Var}} | erlang:element(4, State)] ) ) end ) end )), (nibble:do( variable(), fun(Var@1) -> nibble:succeed( erlang:setelement( 4, State, [{dynamic, {output, Var@1}} | erlang:element(4, State)] ) ) end ))] ), fun(Out) -> nibble:do( nibble:token(percent), fun(_) -> nibble:do( nibble:token(right_angle), fun(_) -> nibble:return(Out) end ) end ) end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 410). -spec if_tag(state()) -> nibble:parser(state(), token(), any()). if_tag(State) -> nibble:do( nibble:token('if'), fun(_) -> nibble:do( variable(), fun(Var) -> nibble:return( erlang:setelement( 6, erlang:setelement( 5, empty_state(), {some, {if_tag, Var, none}} ), {some, State} ) ) end ) end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 423). -spec for_tag(state()) -> nibble:parser(state(), token(), any()). for_tag(State) -> nibble:do( nibble:token(for), fun(_) -> nibble:do( variable_name(), fun(Binding) -> nibble:do( nibble:token(in), fun(_) -> nibble:do( variable(), fun(Over) -> nibble:return( erlang:setelement( 6, erlang:setelement( 5, empty_state(), {some, {iter_tag, Over, Binding}} ), {some, State} ) ) end ) end ) end ) end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 584). -spec render_binding() -> nibble:parser({glemplate@ast:var(), binary()}, token(), any()). render_binding() -> nibble:do( variable_name(), fun(To) -> nibble:do( nibble:token(colon), fun(_) -> nibble:do( variable(), fun(From) -> nibble:return({From, To}) end ) end ) end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 592). -spec stringify_token(token()) -> binary(). stringify_token(Token) -> case Token of {text, Text} -> Text; tag_start -> <<"<%"/utf8>>; output_tag_start -> <<"<%="/utf8>>; right_angle -> <<">"/utf8>>; percent -> <<"%"/utf8>>; 'end' -> <<"end"/utf8>>; 'if' -> <<"if"/utf8>>; 'else' -> <<"else"/utf8>>; for -> <<"for"/utf8>>; in -> <<"in"/utf8>>; raw -> <<"raw"/utf8>>; render -> <<"render"/utf8>>; colon -> <<":"/utf8>>; comma -> <<","/utf8>>; period -> <<"."/utf8>>; {variable, Var_name} -> Var_name end. -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 568). -spec filename() -> nibble:parser(binary(), token(), any()). filename() -> nibble:do( nibble:take_while1( <<"a filename without spaces"/utf8>>, fun(Token) -> case Token of comma -> false; percent -> false; _ -> true end end ), fun(File_name_tokens) -> _pipe = gleam@list:fold( File_name_tokens, gleam@string_builder:new(), fun(Acc, Token@1) -> gleam@string_builder:append(Acc, stringify_token(Token@1)) end ), _pipe@1 = gleam@string_builder:to_string(_pipe), nibble:return(_pipe@1) end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 438). -spec render_tag(state()) -> nibble:parser(state(), token(), any()). render_tag(State) -> nibble:do( nibble:token(render), fun(_) -> nibble:do( filename(), fun(Filename) -> nibble:do( nibble:optional(nibble:token(comma)), fun(Maybe_comma) -> nibble:do(case Maybe_comma of {some, _} -> nibble:sequence( render_binding(), nibble:token(comma) ); none -> nibble:return([]) end, fun(Bindings) -> nibble:return( erlang:setelement( 4, State, [{dynamic, {render, Filename, Bindings}} | erlang:element(4, State)] ) ) end) end ) end ) end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 393). -spec other_tag(state()) -> nibble:parser(state(), token(), any()). other_tag(State) -> nibble:do( nibble:one_of( [if_tag(State), for_tag(State), render_tag(State), else_tag(State), end_tag(State)] ), fun(Tag) -> nibble:do( nibble:token(percent), fun(_) -> nibble:do( nibble:token(right_angle), fun(_) -> nibble:return(Tag) end ) end ) end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 341). -spec tag_parser(state()) -> nibble:parser(state(), token(), any()). tag_parser(State) -> With_updated_text = stringify_current_text(State), nibble:one_of( [(nibble:do( nibble:token(tag_start), fun(_) -> other_tag(With_updated_text) end )), (nibble:do( nibble:token(output_tag_start), fun(_) -> output_tag(With_updated_text) end ))] ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 310). -spec base_parser(state()) -> nibble:parser(state(), token(), any()). base_parser(Old_state) -> _pipe@5 = nibble:loop( Old_state, fun(State) -> nibble:one_of( [begin _pipe = tag_parser(State), nibble:map( _pipe, fun(Field@0) -> {continue, Field@0} end ) end, begin _pipe@1 = nibble:any(), _pipe@2 = nibble:map( _pipe@1, fun(Token) -> erlang:setelement( 3, State, gleam@string_builder:append( erlang:element(3, State), stringify_token(Token) ) ) end ), nibble:map( _pipe@2, fun(Field@0) -> {continue, Field@0} end ) end, begin _pipe@3 = nibble:eof(), _pipe@4 = nibble:replace( _pipe@3, stringify_current_text(State) ), nibble:map( _pipe@4, fun(Field@0) -> {break, Field@0} end ) end] ) end ), nibble:map( _pipe@5, fun(State@1) -> erlang:setelement( 3, erlang:setelement( 4, erlang:setelement( 2, State@1, [{nodes, erlang:element(2, State@1)} | lists:reverse(erlang:element(4, State@1))] ), [] ), gleam@string_builder:new() ) end ). -file("/home/nicd/glemplate/src/glemplate/parser.gleam", 103). -spec new() -> parser(). new() -> {parser, gen_lexer(), base_parser(empty_state())}.