Current section

Files

Jump to
glemplate src glemplate@parser.erl
Raw

src/glemplate@parser.erl

-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_tree:string_tree(),
list(glemplate@ast:node_()),
gleam@option:option(incomplete_tag()),
gleam@option:option(state())}.
-file("/Users/nicd/svn/glemplate/src/glemplate/parser.gleam", 136).
-spec lex(binary(), nibble@lexer:lexer(JLE, lexer_mode())) -> {ok,
list(nibble@lexer:token(JLE))} |
{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,
(erlang:integer_to_binary(
erlang:element(2, Error)
))/binary>>/binary,
":"/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(3, Error)))/binary>>/binary,
"), last lexeme: "/utf8>>/binary,
(erlang:element(4, Error))/binary>>}
end
).
-file("/Users/nicd/svn/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("/Users/nicd/svn/glemplate/src/glemplate/parser.gleam", 279).
-spec do_parse(
list(nibble@lexer:token(JPG)),
nibble:parser(state(), JPG, 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,
(erlang:integer_to_binary(
erlang:element(
2,
erlang:element(
2,
Deadend
)
)
))/binary>>/binary,
":"/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(
3,
erlang:element(
2,
Deadend
)
)
))/binary>>/binary,
") and ("/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(
4,
erlang:element(2, Deadend)
)
))/binary>>/binary,
":"/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(
5,
erlang:element(2, Deadend)
)
))/binary>>/binary,
"): "/utf8>>/binary,
(gleam@string:inspect(erlang:element(3, Deadend)))/binary>>
end
)}
end
).
-file("/Users/nicd/svn/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("/Users/nicd/svn/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("/Users/nicd/svn/glemplate/src/glemplate/parser.gleam", 300).
-spec empty_state() -> state().
empty_state() ->
{state, [], gleam@string_tree:new(), [], none, none}.
-file("/Users/nicd/svn/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("/Users/nicd/svn/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("/Users/nicd/svn/glemplate/src/glemplate/parser.gleam", 521).
-spec stringify_current_text(state()) -> state().
stringify_current_text(State) ->
erlang:setelement(
3,
erlang:setelement(
4,
State,
[{text, unicode:characters_to_binary(erlang:element(3, State))} |
erlang:element(4, State)]
),
gleam@string_tree:new()
).
-file("/Users/nicd/svn/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("/Users/nicd/svn/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("/Users/nicd/svn/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("/Users/nicd/svn/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("/Users/nicd/svn/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("/Users/nicd/svn/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("/Users/nicd/svn/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("/Users/nicd/svn/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("/Users/nicd/svn/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_tree:new(),
fun(Acc, Token@1) ->
gleam@string_tree:append(Acc, stringify_token(Token@1))
end
),
_pipe@1 = unicode:characters_to_binary(_pipe),
nibble:return(_pipe@1)
end
).
-file("/Users/nicd/svn/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("/Users/nicd/svn/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("/Users/nicd/svn/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("/Users/nicd/svn/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_tree: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_tree:new()
)
end
).
-file("/Users/nicd/svn/glemplate/src/glemplate/parser.gleam", 103).
-spec new() -> parser().
new() ->
{parser, gen_lexer(), base_parser(empty_state())}.