Current section
Files
Jump to
Current section
Files
src/cel@parser.erl
-module(cel@parser).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([tokenize/1, parse_/1, parse/1]).
-export_type([binary_op/0, arithmetic/0, relation/0, logical/0, unary_op/0, atom_/0, member/0, expression/0, error/0]).
-type binary_op() :: {arithmetic, arithmetic()} |
{relation, relation()} |
{logical, logical()}.
-type arithmetic() :: add | sub | mul | 'div' | mod.
-type relation() :: less_than |
less_than_eq |
greater_than |
greater_than_eq |
equals |
not_equals |
in.
-type logical() :: 'and' | 'or'.
-type unary_op() :: 'not' | unary_sub.
-type atom_() :: {int, integer()} |
{u_int, integer()} |
{float, float()} |
{string, binary()} |
{bytes, bitstring()} |
{bool, boolean()} |
null.
-type member() :: {attribute, binary()} | {index, expression()}.
-type expression() :: {binary_operation,
expression(),
binary_op(),
expression()} |
{unary, unary_op(), expression()} |
{ternary, expression(), expression(), expression()} |
{list, list(expression())} |
{map, list({expression(), expression()})} |
{member, expression(), member()} |
{function_call,
binary(),
gleam@option:option(expression()),
list(expression())} |
{atom, atom_()} |
{ident, binary()}.
-type error() :: unexpected_end_of_file |
{unexpected_source_str, binary(), integer()} |
{unexpected_token, cel@parser@lexer:token(), integer()} |
{dead_end, list({cel@parser@lexer:token(), cel@parser@lexer:position()})}.
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 79).
-spec binary_operator(cel@parser@lexer:token()) -> {ok, binary_op()} |
{error, nil}.
binary_operator(Token) ->
case Token of
plus ->
{ok, {arithmetic, add}};
minus ->
{ok, {arithmetic, sub}};
star ->
{ok, {arithmetic, mul}};
slash ->
{ok, {arithmetic, 'div'}};
percent ->
{ok, {arithmetic, mod}};
less_than ->
{ok, {relation, less_than}};
less_than_eq ->
{ok, {relation, less_than_eq}};
greater_than ->
{ok, {relation, greater_than}};
greater_than_eq ->
{ok, {relation, greater_than_eq}};
equals ->
{ok, {relation, equals}};
not_equals ->
{ok, {relation, not_equals}};
in ->
{ok, {relation, in}};
'and' ->
{ok, {logical, 'and'}};
'or' ->
{ok, {logical, 'or'}};
_ ->
{error, nil}
end.
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 197).
-spec precedence(binary_op()) -> integer().
precedence(Operator) ->
case Operator of
{arithmetic, mul} ->
3;
{arithmetic, 'div'} ->
3;
{arithmetic, mod} ->
3;
{arithmetic, add} ->
4;
{arithmetic, sub} ->
4;
{relation, _} ->
5;
{logical, 'and'} ->
6;
{logical, 'or'} ->
7
end.
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 166).
-spec handle_operator(
gleam@option:option(binary_op()),
list(binary_op()),
list(expression())
) -> {gleam@option:option(expression()), list(binary_op()), list(expression())}.
handle_operator(Next, Operators, Values) ->
case {Next, Operators, Values} of
{{some, Operator}, [], _} ->
{none, [Operator], Values};
{{some, Next@1}, [Previous | Operators@1], [A, B | Rest_values]} ->
case precedence(Previous) =< precedence(Next@1) of
true ->
Values@1 = [{binary_operation, B, Previous, A} |
Rest_values],
handle_operator({some, Next@1}, Operators@1, Values@1);
false ->
{none, [Next@1, Previous | Operators@1], Values}
end;
{none, [Operator@1 | Operators@2], [A@1, B@1 | Values@2]} ->
Values@3 = [{binary_operation, B@1, Operator@1, A@1} | Values@2],
handle_operator(none, Operators@2, Values@3);
{none, [], [Expression]} ->
{{some, Expression}, Operators, Values};
{none, [], []} ->
{none, Operators, Values};
{_, _, _} ->
erlang:error(#{gleam_error => panic,
message => <<"parser bug, operator expression not full reduced"/utf8>>,
module => <<"cel/parser"/utf8>>,
function => <<"handle_operator"/utf8>>,
line => 193})
end.
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 318).
-spec list(
fun((list({cel@parser@lexer:token(), cel@parser@lexer:position()})) -> {ok,
{GAH, list({cel@parser@lexer:token(), cel@parser@lexer:position()})}} |
{error, error()}),
gleam@option:option(GAH),
list(GAH),
list({cel@parser@lexer:token(), cel@parser@lexer:position()})
) -> {ok,
{list(GAH),
list({cel@parser@lexer:token(), cel@parser@lexer:position()})}} |
{error, error()}.
list(Parser, Discard, Acc, Tokens) ->
case Tokens of
[{right_square, _} | Tokens@1] ->
{ok, {lists:reverse(Acc), Tokens@1}};
[{comma, _}, {right_square, _} | Tokens@2] when Acc =/= [] ->
{ok, {lists:reverse(Acc), Tokens@2}};
_ ->
gleam@result:'try'(
Parser(Tokens),
fun(_use0) ->
{Element, Tokens@3} = _use0,
Acc@1 = [Element | Acc],
case Tokens@3 of
[{right_square, _} | Tokens@4] ->
{ok, {lists:reverse(Acc@1), Tokens@4}};
[{comma, _}, {right_square, _} | Tokens@4] ->
{ok, {lists:reverse(Acc@1), Tokens@4}};
[{comma, _} | Tokens@5] ->
list(Parser, Discard, Acc@1, Tokens@5);
[{Other, Position} | _] ->
{error,
{unexpected_token,
Other,
erlang:element(2, Position)}};
[] ->
{error, unexpected_end_of_file}
end
end
)
end.
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 424).
-spec unexpected(list({cel@parser@lexer:token(), cel@parser@lexer:position()})) -> {ok,
any()} |
{error, error()}.
unexpected(Tokens) ->
case Tokens of
[{Token, Pos} | _] ->
{error, {unexpected_token, Token, erlang:element(2, Pos)}};
[] ->
{error, unexpected_end_of_file}
end.
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 280).
-spec comma_delimited(
list(GAA),
list({cel@parser@lexer:token(), cel@parser@lexer:position()}),
fun((list({cel@parser@lexer:token(), cel@parser@lexer:position()})) -> {ok,
{GAA, list({cel@parser@lexer:token(), cel@parser@lexer:position()})}} |
{error, error()}),
cel@parser@lexer:token()
) -> {ok,
{list(GAA),
list({cel@parser@lexer:token(), cel@parser@lexer:position()})}} |
{error, error()}.
comma_delimited(Items, Tokens, Parser, Final) ->
case Tokens of
[] ->
{error, unexpected_end_of_file};
[{Token, _} | Tokens@1] when Token =:= Final ->
{ok, {lists:reverse(Items), Tokens@1}};
_ ->
gleam@result:'try'(
Parser(Tokens),
fun(_use0) ->
{Element, Tokens@2} = _use0,
case Tokens@2 of
[{comma, _} | Tokens@3] ->
comma_delimited(
[Element | Items],
Tokens@3,
Parser,
Final
);
[{Token@1, _} | Tokens@4] when Token@1 =:= Final ->
{ok, {lists:reverse([Element | Items]), Tokens@4}};
_ ->
unexpected(Tokens@2)
end
end
)
end.
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 431).
-spec tokenize(binary()) -> {ok,
list({cel@parser@lexer:token(), cel@parser@lexer:position()})} |
{error, error()}.
tokenize(Source) ->
Lexed = begin
_pipe = cel@parser@lexer:new(Source),
_pipe@1 = cel@parser@lexer:discard_comments(_pipe),
_pipe@2 = cel@parser@lexer:discard_whitespace(_pipe@1),
cel@parser@lexer:lex(_pipe@2)
end,
Check_last_token = case gleam@list:last(Lexed) of
{ok, {{unexpected_grapheme, S}, {position, Offset, _}}} ->
{error, {unexpected_source_str, S, Offset}};
{ok, {{unterminated_string, S@1}, {position, Offset@1, _}}} ->
{error, {unexpected_source_str, S@1, Offset@1}};
{error, nil} ->
{error, unexpected_end_of_file};
_ ->
{ok, nil}
end,
gleam@result:map(Check_last_token, fun(_) -> Lexed end).
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 109).
-spec expression_loop(
list({cel@parser@lexer:token(), cel@parser@lexer:position()}),
list(binary_op()),
list(expression())
) -> {ok,
{expression(),
list({cel@parser@lexer:token(), cel@parser@lexer:position()})}} |
{error, error()}.
expression_loop(Tokens, Operators, Values) ->
gleam@result:'try'(
expression_unit(Tokens),
fun(_use0) ->
{Expr, Tokens@1} = _use0,
Expr@1 = case Expr of
none ->
{error, {dead_end, Tokens@1}};
{some, E} ->
Values@1 = [E | Values],
Binop = case Tokens@1 of
[{Token, _} | Tokens@2] ->
gleam@result:map(
binary_operator(Token),
fun(Op) -> {Op, Tokens@2} end
);
[] ->
{error, nil}
end,
case Binop of
{ok, {Operator, Tokens@3}} ->
case handle_operator(
{some, Operator},
Operators,
Values@1
) of
{{some, Expression}, _, _} ->
{ok, {Expression, Tokens@3}};
{none, Operators@1, Values@2} ->
expression_loop(
Tokens@3,
Operators@1,
Values@2
)
end;
_ ->
case erlang:element(
1,
handle_operator(none, Operators, Values@1)
) of
none ->
{error, {dead_end, Tokens@1}};
{some, Expression@1} ->
{ok, {Expression@1, Tokens@1}}
end
end
end,
gleam@result:'try'(
Expr@1,
fun(_use0@1) ->
{Expr@2, Tokens@4} = _use0@1,
case Tokens@4 of
[{question_mark, _} | Tokens@5] ->
gleam@result:'try'(
expression(Tokens@5),
fun(_use0@2) ->
{Then, Tokens@6} = _use0@2,
case Tokens@6 of
[{colon, _} | Tokens@7] ->
gleam@result:map(
expression(Tokens@7),
fun(_use0@3) ->
{Otherwise, Tokens@8} = _use0@3,
{{ternary,
Expr@2,
Then,
Otherwise},
Tokens@8}
end
);
_ ->
unexpected(Tokens@6)
end
end
);
_ ->
{ok, {Expr@2, Tokens@4}}
end
end
)
end
).
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 105).
-spec expression(list({cel@parser@lexer:token(), cel@parser@lexer:position()})) -> {ok,
{expression(),
list({cel@parser@lexer:token(), cel@parser@lexer:position()})}} |
{error, error()}.
expression(Tokens) ->
expression_loop(Tokens, [], []).
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 304).
-spec map_field(list({cel@parser@lexer:token(), cel@parser@lexer:position()})) -> {ok,
{{expression(), expression()},
list({cel@parser@lexer:token(), cel@parser@lexer:position()})}} |
{error, error()}.
map_field(Tokens) ->
gleam@result:'try'(
expression(Tokens),
fun(_use0) ->
{Key_expression, Tokens@1} = _use0,
case Tokens@1 of
[{colon, _} | Tokens@2] ->
gleam@result:map(
expression(Tokens@2),
fun(_use0@1) ->
{Value_expression, Tokens@3} = _use0@1,
{{Key_expression, Value_expression}, Tokens@3}
end
);
_ ->
unexpected(Tokens@1)
end
end
).
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 449).
-spec parse_(list({cel@parser@lexer:token(), cel@parser@lexer:position()})) -> {ok,
expression()} |
{error, error()}.
parse_(Tokens) ->
gleam@result:'try'(
expression(Tokens),
fun(_use0) ->
{Expr, Rest} = _use0,
case Rest of
[] ->
{ok, Expr};
_ ->
{error, {dead_end, Rest}}
end
end
).
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 458).
-spec parse(binary()) -> {ok, expression()} | {error, error()}.
parse(Source) ->
_pipe = tokenize(Source),
gleam@result:then(_pipe, fun parse_/1).
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 386).
-spec call(
list(expression()),
binary(),
gleam@option:option(expression()),
list({cel@parser@lexer:token(), cel@parser@lexer:position()})
) -> {ok,
{expression(),
list({cel@parser@lexer:token(), cel@parser@lexer:position()})}} |
{error, error()}.
call(Arguments, Ident, This, Tokens) ->
case Tokens of
[] ->
{error, unexpected_end_of_file};
[{right_paren, _} | Tokens@1] ->
Call = {function_call, Ident, This, lists:reverse(Arguments)},
after_expression(Call, Tokens@1);
_ ->
gleam@result:'try'(
expression(Tokens),
fun(_use0) ->
{Argument, Tokens@2} = _use0,
Arguments@1 = [Argument | Arguments],
case Tokens@2 of
[{comma, _} | Tokens@3] ->
call(Arguments@1, Ident, This, Tokens@3);
[{right_paren, _} | Tokens@4] ->
Call@1 = {function_call,
Ident,
This,
lists:reverse(Arguments@1)},
after_expression(Call@1, Tokens@4);
_ ->
unexpected(Tokens@2)
end
end
)
end.
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 349).
-spec after_expression(
expression(),
list({cel@parser@lexer:token(), cel@parser@lexer:position()})
) -> {ok,
{expression(),
list({cel@parser@lexer:token(), cel@parser@lexer:position()})}} |
{error, error()}.
after_expression(Parsed, Tokens) ->
case Tokens of
[{dot, _}, {{ident, Label}, _} | Tokens@1] ->
after_expression({member, Parsed, {attribute, Label}}, Tokens@1);
[{left_square, _} | Tokens@2] ->
gleam@result:'try'(
expression(Tokens@2),
fun(_use0) ->
{Expression, Tokens@3} = _use0,
case Tokens@3 of
[{right_square, _} | Tokens@4] ->
{ok,
{{member, Parsed, {index, Expression}},
Tokens@4}};
_ ->
unexpected(Tokens@3)
end
end
);
[{left_paren, Pos} | Tokens@5] ->
case Parsed of
{ident, Ident} ->
call([], Ident, none, Tokens@5);
{member, This, {attribute, Ident@1}} ->
call([], Ident@1, {some, This}, Tokens@5);
_ ->
{error,
{unexpected_token, left_paren, erlang:element(2, Pos)}}
end;
_ ->
{ok, {Parsed, Tokens}}
end.
-file("/Users/jonas/src/personal/cel-gleam/src/cel/parser.gleam", 207).
-spec expression_unit(
list({cel@parser@lexer:token(), cel@parser@lexer:position()})
) -> {ok,
{gleam@option:option(expression()),
list({cel@parser@lexer:token(), cel@parser@lexer:position()})}} |
{error, error()}.
expression_unit(Tokens) ->
gleam@result:'try'(case Tokens of
[{dot, _}, {{ident, Name}, _} | Tokens@1] ->
{ok, {{some, {ident, Name}}, Tokens@1}};
[{{ident, Name}, _} | Tokens@1] ->
{ok, {{some, {ident, Name}}, Tokens@1}};
[{{bool, Value}, _} | Tokens@2] ->
{ok, {{some, {atom, {bool, Value}}}, Tokens@2}};
[{{int, Value@1}, _} | Tokens@3] ->
_assert_subject = gleam_stdlib:parse_int(Value@1),
{ok, N} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail,
module => <<"cel/parser"/utf8>>,
function => <<"expression_unit"/utf8>>,
line => 219})
end,
{ok, {{some, {atom, {int, N}}}, Tokens@3}};
[{{u_int, Value@2}, _} | Tokens@4] ->
Excl_suffix = gleam@string:drop_end(Value@2, 1),
_assert_subject@1 = gleam_stdlib:parse_int(Excl_suffix),
{ok, N@1} = case _assert_subject@1 of
{ok, _} -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail@1,
module => <<"cel/parser"/utf8>>,
function => <<"expression_unit"/utf8>>,
line => 224})
end,
{ok, {{some, {atom, {u_int, N@1}}}, Tokens@4}};
[{{float, Value@3}, _} | Tokens@5] ->
_assert_subject@2 = gleam_stdlib:parse_float(Value@3),
{ok, F} = case _assert_subject@2 of
{ok, _} -> _assert_subject@2;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail@2,
module => <<"cel/parser"/utf8>>,
function => <<"expression_unit"/utf8>>,
line => 228})
end,
{ok, {{some, {atom, {float, F}}}, Tokens@5}};
[{{string, Value@4}, _} | Tokens@6] ->
{ok, {{some, {atom, {string, Value@4}}}, Tokens@6}};
[{{bytes, Value@5}, _} | Tokens@7] ->
{ok, {{some, {atom, {bytes, Value@5}}}, Tokens@7}};
[{left_square, _} | Tokens@8] ->
Result = list(fun expression/1, none, [], Tokens@8),
gleam@result:map(
Result,
fun(_use0) ->
{Elements, Tokens@9} = _use0,
{{some, {list, Elements}}, Tokens@9}
end
);
[{left_curly, _} | Tokens@10] ->
Result@1 = comma_delimited(
[],
Tokens@10,
fun map_field/1,
right_curly
),
gleam@result:map(
Result@1,
fun(_use0@1) ->
{Fields, Tokens@11} = _use0@1,
{{some, {map, Fields}}, Tokens@11}
end
);
[{exclamation_mark, _} | Tokens@12] ->
gleam@result:map(
expression(Tokens@12),
fun(_use0@2) ->
{Expression, Tokens@13} = _use0@2,
{{some, {unary, 'not', Expression}}, Tokens@13}
end
);
[{minus, _} | Tokens@14] ->
gleam@result:map(
expression(Tokens@14),
fun(_use0@3) ->
{Expression@1, Tokens@15} = _use0@3,
{{some, {unary, unary_sub, Expression@1}}, Tokens@15}
end
);
[{left_paren, _} | Tokens@16] ->
gleam@result:'try'(
expression(Tokens@16),
fun(_use0@4) ->
{Expression@2, Tokens@17} = _use0@4,
case Tokens@17 of
[{right_paren, _} | Tokens@18] ->
{ok, {{some, Expression@2}, Tokens@18}};
_ ->
unexpected(Tokens@17)
end
end
);
_ ->
{ok, {none, Tokens}}
end, fun(_use0@5) ->
{Parsed, Tokens@19} = _use0@5,
case Parsed of
{some, Expression@3} ->
case after_expression(Expression@3, Tokens@19) of
{ok, {Expression@4, Tokens@20}} ->
{ok, {{some, Expression@4}, Tokens@20}};
{error, Error} ->
{error, Error}
end;
none ->
{ok, {none, Tokens@19}}
end
end).