-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).