Packages

Handles is a templating language written in pure Gleam. Heavily inspired by Mustache and Handlebars.js

Current section

Files

Jump to
handles src handles@lexer.erl
Raw

src/handles@lexer.erl

-module(handles@lexer).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([run/1]).
-export_type([token/0, lex_error/0, syntax_error/0, lexer_state/0]).
-type token() :: {constant, integer(), integer(), binary()} |
{property, integer(), integer(), list(binary())} |
{block_start, integer(), integer(), binary(), list(binary())} |
{block_end, integer(), integer(), binary()}.
-type lex_error() :: {unbalanced_tag, integer(), integer()} |
{syntax_error, list(syntax_error())}.
-type syntax_error() :: {missing_body, integer(), integer()} |
{missing_block_kind, integer(), integer()} |
{unexpected_block_argument, integer(), integer()}.
-type lexer_state() :: {static, integer(), integer(), binary()} |
{tag, integer(), integer(), binary()}.
-spec to_block_or_property(integer(), integer(), binary()) -> {ok, token()} |
{error, syntax_error()}.
to_block_or_property(Start, End, Value) ->
Val = gleam@string:trim(Value),
case gleam@string:first(Val) of
{ok, <<"#"/utf8>>} ->
case gleam@string:split_once(
gleam@string:drop_left(Val, 1),
<<" "/utf8>>
) of
{ok, {Kind, Body}} ->
{ok,
{block_start,
Start,
End,
Kind,
gleam@string:split(Body, <<"."/utf8>>)}};
{error, _} ->
{error, {missing_block_kind, Start, End}}
end;
{ok, <<"/"/utf8>>} ->
case gleam@string:split_once(
gleam@string:drop_left(Val, 1),
<<" "/utf8>>
) of
{ok, {_, _}} ->
{error, {unexpected_block_argument, Start, End}};
{error, _} ->
{ok,
{block_end, Start, End, gleam@string:drop_left(Val, 1)}}
end;
{ok, _} ->
{ok,
{property, Start, End, gleam@string:split(Value, <<"."/utf8>>)}};
{error, _} ->
{error, {missing_body, Start, End}}
end.
-spec step(
lexer_state(),
list({ok, token()} | {error, syntax_error()}),
binary()
) -> {ok, list({ok, token()} | {error, syntax_error()})} | {error, lex_error()}.
step(State, Acc, Input) ->
case State of
{static, Start, End, Str} ->
case gleam@string:starts_with(Input, <<"{{"/utf8>>) of
true ->
step(
{tag, End + 2, End + 2, <<""/utf8>>},
[{ok, {constant, Start, End, Str}} | Acc],
gleam@string:drop_left(Input, 2)
);
false ->
case gleam@string:first(Input) of
{ok, Char} ->
step(
{static,
Start,
End + 1,
gleam@string:append(Str, Char)},
Acc,
gleam@string:drop_left(Input, 1)
);
{error, _} ->
{ok, [{ok, {constant, Start, End, Str}} | Acc]}
end
end;
{tag, Start@1, End@1, Value} ->
case gleam@string:starts_with(Input, <<"}}"/utf8>>) of
true ->
step(
{static, End@1 + 2, End@1 + 2, <<""/utf8>>},
[to_block_or_property(Start@1, End@1, Value) | Acc],
gleam@string:drop_left(Input, 2)
);
false ->
case gleam@string:first(Input) of
{ok, Char@1} ->
step(
{tag,
Start@1,
End@1 + 1,
gleam@string:append(Value, Char@1)},
Acc,
gleam@string:drop_left(Input, 1)
);
{error, _} ->
{error, {unbalanced_tag, Start@1, End@1}}
end
end
end.
-spec run(binary()) -> {ok, list(token())} | {error, lex_error()}.
run(Template) ->
case step({static, 0, 0, <<""/utf8>>}, [], Template) of
{ok, Tokens} ->
case begin
_pipe = Tokens,
gleam@result:partition(_pipe)
end of
{Ok, []} ->
{ok, Ok};
{_, Err} ->
{error, {syntax_error, Err}}
end;
{error, Err@1} ->
{error, Err@1}
end.