-module(ephp_parser_expr). -author('manuel@altenwald.com'). -export([expression/3, add_op/2, precedence/1]). -include("ephp.hrl"). -include("ephp_parser.hrl"). -import(ephp_parser, [add_pos/2, new_line/1, copy_rowcol/2, add_line/2, remove_spaces/2, throw_error/3, inc_pos/1, get_line/1, array_def_level/1, code_statement_level/1, arg_level/1, enclosed_level/1, variable/3, comment_line/3, comment_block/3, constant/3]). array_def_54_level(Parser) -> Parser#parser{level = array_def, array_type = php54}. add_op('end', []) -> []; add_op('end', [{op, [#constant{name = Name, line = RowCol} = Constant]}]) -> Op = case Name of <> when ?OR(I, $i, $I) andalso ?OR(N, $n, $N) andalso ?OR(T, $t, $T) -> OpL = <<"(int)">>, {OpL, precedence(OpL), RowCol}; <> when ?OR(I, $i, $I) andalso ?OR(N, $n, $N) andalso ?OR(T, $t, $T) andalso ?OR(E, $e, $E) andalso ?OR(G, $g, $G) andalso ?OR(R, $r, $R) -> OpL = <<"(int)">>, {OpL, precedence(OpL), RowCol}; <> when ?OR(F, $F, $f) andalso ?OR(L, $L, $l) andalso ?OR(O, $O, $o) andalso ?OR(A, $A, $a) andalso ?OR(T, $T, $t) -> OpL = <<"(float)">>, {OpL, precedence(OpL), RowCol}; <> when ?OR(D, $D, $d) andalso ?OR(O, $O, $o) andalso ?OR(U, $U, $u) andalso ?OR(B, $B, $b) andalso ?OR(L, $L, $l) andalso ?OR(E, $E, $e) -> OpL = <<"(float)">>, {OpL, precedence(OpL), RowCol}; <> when ?OR(S, $S, $s) andalso ?OR(T, $T, $t) andalso ?OR(R, $R, $r) andalso ?OR(I, $I, $i) andalso ?OR(N, $N, $n) andalso ?OR(G, $G, $g) -> OpL = <<"(string)">>, {OpL, precedence(OpL), RowCol}; % array will be captured in the array() part <> when ?OR(O, $O, $o) andalso ?OR(B, $B, $b) andalso ?OR(J, $J, $j) andalso ?OR(E, $E, $e) andalso ?OR(C, $C, $c) andalso ?OR(T, $T, $t) -> OpL = <<"(object)">>, {OpL, precedence(OpL), RowCol}; <> when ?OR(B, $B, $b) andalso ?OR(O, $O, $o) andalso ?OR(L, $L, $l) andalso ?OR(E, $E, $e) andalso ?OR(A, $A, $a) andalso ?OR(N, $N, $n) -> OpL = <<"(bool)">>, {OpL, precedence(OpL), RowCol}; <> when ?OR(B, $B, $b) andalso ?OR(O, $O, $o) andalso ?OR(L, $L, $l) -> OpL = <<"(bool)">>, {OpL, precedence(OpL), RowCol}; <> when ?OR(U, $U, $u) andalso ?OR(N, $N, $n) andalso ?OR(S, $S, $s) andalso ?OR(E, $E, $e) andalso ?OR(T, $T, $t) -> OpL = <<"(unset)">>, {OpL, precedence(OpL), RowCol}; _ -> Constant end, solve([Op]); add_op('end', [{op, Content}]) -> solve(process_incr_decr(Content)); add_op(Add, [{op, Content} | Parsed]) when is_list(Add) -> [{op, Content ++ Add} | Parsed]; add_op(Add, [{op, Content} | Parsed]) -> [{op, Content ++ [Add]} | Parsed]; add_op(Add, Parsed) -> [{op, [Add]} | Parsed]. process_incr_decr(Content) -> process_incr_decr(Content, []). process_incr_decr([], Processed) -> Processed; process_incr_decr([{<<"++">>, _, RowCol}, #variable{} = V | Rest], Processed) -> process_incr_decr(Rest, Processed ++ [{pre_incr, V, RowCol}]); process_incr_decr([{<<"--">>, _, RowCol}, #variable{} = V | Rest], Processed) -> process_incr_decr(Rest, Processed ++ [{pre_decr, V, RowCol}]); process_incr_decr([#variable{} = V, {<<"++">>, _, RowCol} | Rest], Processed) -> process_incr_decr(Rest, Processed ++ [{post_incr, V, RowCol}]); process_incr_decr([#variable{} = V, {<<"--">>, _, RowCol} | Rest], Processed) -> process_incr_decr(Rest, Processed ++ [{post_decr, V, RowCol}]); process_incr_decr([A | Rest], Processed) -> process_incr_decr(Rest, Processed ++ [A]). number(<<"0", X:8, Rest/binary>>, Parser, []) when ?OR(X, $X, $x) -> hexa(Rest, add_pos(Parser, 2), []); number(<<"0", N:8, Rest/binary>>, Parser, []) when ?IS_OCT(N) -> octa(<>, add_pos(Parser, 2), []); number(<>, Parser, []) when ?IS_NUMBER(A) orelse A =:= $- -> number(Rest, inc_pos(Parser), [add_line(#php_n{n = <>}, Parser)]); number(<>, Parser, [#php_n{n = N} = I]) when ?IS_NUMBER(A) -> number(Rest, inc_pos(Parser), [I#php_n{n = <>}]); number(<<".", Rest/binary>>, Parser, []) -> Exp = add_line(#php_n{n = <<"0.">>, type = float}, Parser), number(Rest, inc_pos(Parser), [Exp]); number(<<".", Rest/binary>>, Parser, [#php_n{n = N} = Exp0]) -> Exp = Exp0#php_n{n = <>, type = float}, number(Rest, inc_pos(Parser), [Exp]); number(Rest, Parser, [#php_n{n = N, type = int, line = L}]) -> {Rest, Parser, [#int{int = binary_to_integer(N), line = L}]}; number(Rest, Parser, [#php_n{n = N, type = float, line = L}]) -> {Rest, Parser, [#float{float = binary_to_float(N), line = L}]}. hexa(<>, Parser, []) when ?IS_HEX(A) -> hexa(Rest, inc_pos(Parser), [add_line(#php_n{n = <>}, Parser)]); hexa(<>, Parser, [#php_n{n = N} = I]) when ?IS_HEX(A) -> hexa(Rest, inc_pos(Parser), [I#php_n{n = <>}]); hexa(Rest, Parser, [#php_n{n = N, line = L}]) -> {Rest, Parser, [#int{int = binary_to_integer(N, 16), line = L}]}. octa(<>, Parser, []) when ?IS_OCT(A) -> octa(Rest, inc_pos(Parser), [add_line(#php_n{n = <>}, Parser)]); octa(<>, Parser, [#php_n{n = N} = I]) when ?IS_OCT(A) -> octa(Rest, inc_pos(Parser), [I#php_n{n = <>}]); octa(Rest, Parser, [#php_n{n = N, line = L}]) -> {Rest, Parser, [#int{int = binary_to_integer(N, 8), line = L}]}. array_def(<>, Parser, Args) when ?IS_SPACE(SP) -> array_def(Rest, inc_pos(Parser), Args); array_def(<>, Parser, Args) when ?IS_NEWLINE(SP) -> array_def(Rest, new_line(Parser), Args); array_def(<<")", Rest/binary>>, #parser{level = array_def, array_type = old} = Parser, Args) -> {Rest, inc_pos(Parser), Args}; array_def(<<"]", Rest/binary>>, #parser{level = array_def, array_type = php54} = Parser, Args) -> {Rest, inc_pos(Parser), Args}; %% TODO add error missing closing params array_def(Rest, Parser, Args) when Rest =/= <<>> -> case expression(Rest, Parser, []) of {<<")", Rest0/binary>>, #parser{level = array_def, array_type = old} = Parser0, [Idx, Arg]} -> NewArg = add_line(#array_element{idx = Idx, element = Arg}, Parser), {Rest0, inc_pos(Parser0), Args ++ [NewArg]}; {<<")", Rest0/binary>>, #parser{level = array_def, array_type = old} = Parser0, undefined} -> {Rest0, inc_pos(Parser0), Args}; {<<")", Rest0/binary>>, #parser{level = array_def, array_type = old} = Parser0, Arg} -> NewArg = add_line(#array_element{element = Arg}, Parser), {Rest0, inc_pos(Parser0), Args ++ [NewArg]}; {<<"]", Rest0/binary>>, #parser{level = array_def, array_type = php54} = Parser0, [Idx, Arg]} -> NewArg = add_line(#array_element{idx = Idx, element = Arg}, Parser), {Rest0, inc_pos(Parser0), Args ++ [NewArg]}; {<<"]", Rest0/binary>>, #parser{level = array_def, array_type = php54} = Parser0, undefined} -> {Rest0, inc_pos(Parser0), Args}; {<<"]", Rest0/binary>>, #parser{level = array_def, array_type = php54} = Parser0, Arg} -> NewArg = add_line(#array_element{element = Arg}, Parser), {Rest0, inc_pos(Parser0), Args ++ [NewArg]}; {<<",", Rest0/binary>>, Parser0, [Idx, Arg]} -> NewArg = add_line(#array_element{idx = Idx, element = Arg}, Parser), array_def(Rest0, inc_pos(Parser0), Args ++ [NewArg]); {<<",", Rest0/binary>>, Parser0, Arg} -> NewArg = add_line(#array_element{element = Arg}, Parser), array_def(Rest0, inc_pos(Parser0), Args ++ [NewArg]) end. -spec expression(Text :: binary(), parser(), [expression()]) -> {binary(), parser(), [expression()] | expression()}. % CONSTANT / FUNCTION when -> is used expression(<> = Rest, Parser, [{op, [_, {<<"->">>, _, _}]} | _] = Parsed) when ?IS_ALPHA(A) orelse A =:= $_ -> {Rest0, Parser0, [Constant]} = constant(Rest, Parser, []), expression(Rest0, copy_rowcol(Parser0, Parser), add_op(Constant, Parsed)); % ARRAY(...) -old- expression(<>, Parser, Parsed) when ?OR(A, $a, $A) andalso ?OR(R, $r, $R) andalso ?OR(Y, $y, $Y) andalso not (?IS_ALPHA(SP) orelse ?IS_NUMBER(SP) orelse SP =:= $_) -> case remove_spaces(<>, add_pos(Parser, 5)) of {<<")", _/binary>> = Rest0, Parser0} -> %% (array) cast OpL = <<"(array)">>, NewParsed = add_op({OpL, precedence(OpL), Parser}, Parsed), expression(Rest0, Parser0, NewParsed); {<<"(", Rest0/binary>>, Parser0} -> NewParser = array_def_level(inc_pos(Parser0)), {Rest1, Parser1, Content} = array_def(Rest0, NewParser, []), NewParsed = add_op(add_line(#array{elements = Content}, Parser), Parsed), expression(Rest1, copy_rowcol(Parser1, Parser), NewParsed) end; % [...] -array new- expression(<<"[", Rest/binary>>, Parser, []) -> NewParser = array_def_54_level(inc_pos(Parser)), {Rest1, Parser1, Content} = array_def(Rest, NewParser, []), NewParsed = add_op(add_line(#array{elements = Content}, Parser), []), expression(Rest1, copy_rowcol(Parser1, Parser), NewParsed); expression(<<"[", Rest/binary>>, Parser, [{op, []} | _] = Parsed) -> % ARRAY DEF NewParser = array_def_54_level(inc_pos(Parser)), {Rest1, Parser1, Content} = array_def(Rest, NewParser, []), NewParsed = add_op(add_line(#array{elements = Content}, Parser), Parsed), expression(Rest1, copy_rowcol(Parser1, Parser), NewParsed); expression(<<"[", Rest/binary>>, Parser, [{op, Op} | LastParsed] = Parsed) -> case lists:last(Op) of {_, {RightOrLeft, _}, _} when RightOrLeft =:= right orelse RightOrLeft =:= left -> % ARRAY DEF NewParser = array_def_54_level(inc_pos(Parser)), {Rest1, Parser1, Content} = array_def(Rest, NewParser, []), NewParsed = add_op(add_line(#array{elements = Content}, Parser), Parsed), expression(Rest1, copy_rowcol(Parser1, Parser), NewParsed); _ -> % ARRAY INDEX case Op of [_] -> NewParsed = [#variable{name = add_op('end', Parsed)}], {Rest0, Parser0, [Parsed0]} = variable(<<"[", Rest/binary>>, Parser, NewParsed), expression(Rest0, Parser0, add_op(Parsed0, [])); _ -> {PrevOp, [LastOp]} = lists:split(length(Op) - 1, Op), NewLastOp = [#variable{name = LastOp}], {Rest0, Parser0, Parsed0} = variable(<<"[", Rest/binary>>, Parser, NewLastOp), NewOp = [{op, PrevOp ++ Parsed0} | LastParsed], expression(Rest0, Parser0, NewOp) end end; % NULL expression(<>, Parser, Parsed) when ?OR(N, $N, $n) andalso ?OR(U, $U, $u) andalso ?OR(L, $L, $l) andalso not (?IS_ALPHA(SP) orelse ?IS_NUMBER(SP) orelse SP =:= $_) -> expression(<>, add_pos(Parser, 4), add_op(undefined, Parsed)); % TRUE expression(<>, Parser, Parsed) when ?OR(T, $t, $T) andalso ?OR(R, $r, $R) andalso ?OR(U, $u, $U) andalso ?OR(E, $e, $E) andalso not (?IS_ALPHA(SP) orelse ?IS_NUMBER(SP) orelse SP =:= $_) -> expression(<>, add_pos(Parser, 4), add_op(true, Parsed)); % FALSE expression(<>, Parser, Parsed) when ?OR(F, $f, $F) andalso ?OR(A, $a, $A) andalso ?OR(L, $l, $L) andalso ?OR(S, $s, $S) andalso ?OR(E, $e, $E) andalso not (?IS_ALPHA(SP) orelse ?IS_NUMBER(SP) orelse SP =:= $_) -> expression(<>, add_pos(Parser, 5), add_op(false, Parsed)); % FUNCTION(...) -closure- expression(<>, Parser, Parsed) when ?OR(F, $F, $f) andalso ?OR(U, $U, $u) andalso ?OR(N, $N, $n) andalso ?OR(C, $C, $c) andalso ?OR(T, $T, $t) andalso ?OR(I, $I, $i) andalso ?OR(O, $O, $o) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $() -> {<<"(", Rest0/binary>>, Parser0} = remove_spaces(<>, add_pos(Parser, 9)), {Rest1, Parser1, Args} = ephp_parser_func:funct_args(Rest0, Parser0, []), BaseFunction = add_line(#function{args = Args}, Parser), {Rest2, Parser2, Function} = ephp_parser_func:st_use_or_block(Rest1, Parser1, BaseFunction), expression(Rest2, copy_rowcol(Parser2, Parser), add_op(Function, Parsed)); % INSTANCEOF expression(<>, Parser, Parsed) when ?OR(I, $I, $i) andalso ?OR(N, $N, $n) andalso ?OR(S, $S, $s) andalso ?OR(T, $T, $t) andalso ?OR(A, $A, $a) andalso ?OR(C, $C, $c) andalso ?OR(E, $E, $e) andalso ?OR(O, $O, $o) andalso ?OR(F, $F, $f) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> {Rest0, Parser0} = remove_spaces(<>, Parser), OpL = <<"instanceof">>, expression(Rest0, Parser0, add_op({instanceof, precedence(OpL), get_line(Parser)}, Parsed)); % NEW ... expression(<>, Parser, Parsed) when ?OR(N, $N, $n) andalso ?OR(E, $E, $e) andalso ?OR(W, $W, $w) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $() -> {Rest1, Parser1, {NS, ObjName}} = case remove_spaces(<>, Parser) of {<<"$", _/binary>> = Rest0, Parser0} -> {RestEx, ParserEx, Exp} = expression(Rest0, Parser0, []), {RestEx, ParserEx, {Parser#parser.namespace, Exp}}; {<> = Rest0, Parser0} when ?IS_ALPHA(A) orelse A =:= $_ orelse A =:= $\\ -> ephp_parser_class:class_name(Rest0, Parser0, []) end, Instance = case remove_spaces(Rest1, Parser1) of {<<"(", Rest2/binary>>, Parser2} -> {Rest3, Parser3, Args} = ephp_parser_func:call_args(Rest2, Parser2, []), add_line(#instance{name = ObjName, namespace = NS, args = Args}, Parser); {Rest3, Parser3} -> add_line(#instance{name = ObjName, namespace = NS}, Parser) end, expression(Rest3, copy_rowcol(Parser3, Parser), add_op(Instance, Parsed)); % CLONE ... expression(<>, Parser, Parsed) when ?OR(C, $C, $c) andalso ?OR(L, $L, $l) andalso ?OR(O, $O, $o) andalso ?OR(N, $N, $n) andalso ?OR(E, $E, $e) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $() -> OpL = <<"clone">>, Parser1 = add_pos(Parser, 5), Rest1 = <>, expression(Rest1, Parser1, add_op({OpL, precedence(OpL), get_line(Parser)}, Parsed)); % FINAL -enclosed- expression(<<"}", Rest/binary>>, #parser{level = enclosed} = Parser, [Exp]) -> {Rest, inc_pos(Parser), add_op('end', [Exp])}; % FINAL -unclosed- expression(<> = Rest, #parser{level = unclosed} = Parser, [Exp]) when ?IS_SPACE(SP) orelse SP =:= $" -> {Rest, Parser, add_op('end', [Exp])}; % SPACE -all- expression(<>, Parser, Parsed) when ?IS_SPACE(SP) -> expression(Rest, inc_pos(Parser), Parsed); % NEWLINE -all- expression(<>, Parser, Parsed) when ?IS_NEWLINE(SP) -> expression(Rest, new_line(Parser), Parsed); % COMMENTS... expression(<<"//", Rest/binary>>, Parser, Parsed) -> {Rest0, Parser0, _} = comment_line(Rest, Parser, Parsed), expression(Rest0, Parser0, Parsed); expression(<<"#", Rest/binary>>, Parser, Parsed) -> {Rest0, Parser0, _} = comment_line(Rest, Parser, Parsed), expression(Rest0, Parser0, Parsed); expression(<<"/*", Rest/binary>>, Parser, Parsed) -> {Rest0, Parser0, _} = comment_block(Rest, Parser, Parsed), expression(Rest0, Parser0, Parsed); % FUNCTION CALL / PARENS expression(<<"(", Rest/binary>>, Parser, [{op, Op} | Parsed]) -> {Op1, Op2} = case length(Op) of 0 -> {[], []}; 1 -> {[], Op}; Size -> lists:split(Size - 1, Op) end, case Op2 of [#variable{} = V] -> % FUNCTION CALL Call = #call{name = V, line = V#variable.line}, {Rest0, Parser0, [Function]} = ephp_parser_func:function(Rest, inc_pos(Parser), [Call | Parsed]), NewOp = {op, Op1 ++ [Function]}, expression(Rest0, copy_rowcol(Parser0, Parser), [NewOp | Parsed]); _ -> % NORMAL PARENS exp_parens(Rest, inc_pos(Parser), [{op, Op} | Parsed]) end; % PARENS expression(<<"(", Rest/binary>>, Parser, Parsed) -> % FIXME: this is inconsistent, sometimes is expecting to remove ")" % and sometimes is not. exp_parens(Rest, inc_pos(Parser), Parsed); % FINAL -arg- expression(<<",", _/binary>> = Rest, #parser{level = Level, access = Access} = Parser, Parsed) when Level =:= array orelse Level =:= array_curly orelse Level =:= normal andalso (Access =:= public orelse Access =:= protected orelse Access =:= private) -> {Rest, Parser, add_op('end', Parsed)}; expression(<> = Rest, #parser{level = arg} = Parser, [{op, _}, #if_block{} | _]) when A =:= $, orelse A =:= $) -> throw_error(eparse, Parser, Rest); expression(<> = Rest, #parser{level = arg} = Parser, Parsed) when A =:= $, orelse A =:= $) -> case add_op('end', Parsed) of #operation{type = <<"?">>, line = {{_, R}, {_, C}}} -> throw_error(eparse, Parser#parser{row = R, col = C}, Rest); Op -> {Rest, Parser, Op} end; % FINAL -array definition array(...)- expression(<> = Rest, #parser{level = array_def, array_type = old} = Parser, [{op, _}, #if_block{} | _]) when A =:= $, orelse A =:= $) -> throw_error(eparse, Parser, Rest); expression(<> = Rest, #parser{level = array_def, array_type = old} = Parser, [Parsed]) when A =:= $, orelse A =:= $) -> {Rest, Parser, add_op('end', [Parsed])}; expression(<> = Rest, #parser{level = array_def, array_type = old} = Parser, Parsed) when A =:= $, orelse A =:= $) -> case Parsed of [Arg, Idx] -> {Rest, Parser, [Idx, add_op('end', [Arg])]}; [] -> {Rest, Parser, undefined} end; % FINAL -array definition [...]- expression(<> = Rest, #parser{level = array_def, array_type = php54} = Parser, [{op, _}, #if_block{} | _]) when A =:= $, orelse A =:= $] -> throw_error(eparse, Parser, Rest); expression(<> = Rest, #parser{level = array_def, array_type = php54} = Parser, [Parsed]) when A =:= $, orelse A =:= $] -> {Rest, Parser, add_op('end', [Parsed])}; expression(<> = Rest, #parser{level = array_def, array_type = php54} = Parser, Parsed) when A =:= $, orelse A =:= $] -> case Parsed of [Arg, Idx] -> {Rest, Parser, [Idx, add_op('end', [Arg])]}; [] -> {Rest, Parser, undefined} end; % KEY & VALUE -array_def old and new- expression(<<"=>", Rest/binary>>, #parser{level = array_def} = Parser, [{op, _} = Op | Parsed]) -> expression(Rest, add_pos(Parser, 2), [{op, []}, add_op('end', [Op]) | Parsed]); % FINAL -all but parens- expression(<> = Rest, #parser{level = L} = Parser, [{op, _}, #if_block{} | _]) when not is_number(L) andalso (A =:= $) orelse A =:= $;) -> throw_error(eparse, Parser, Rest); expression(<> = Rest, #parser{level = L} = Parser, Parsed) when not is_number(L) andalso (A =:= $) orelse A =:= $;) -> {Rest, Parser, add_op('end', Parsed)}; % FOREACH DATA expression(<> = Rest, #parser{level = foreach_block} = Parser, [{op, _}, #if_block{} | _]) when ?OR(A, $a, $A) andalso ?OR(S, $s, $S) andalso not (?IS_ALPHANUM(SP) orelse SP =:= $_ orelse SP =:= $() -> throw_error(eparse, Parser, Rest); expression(<> = Rest, #parser{level = foreach_block} = Parser, Parsed) when ?OR(A, $a, $A) andalso ?OR(S, $s, $S) andalso not (?IS_ALPHANUM(SP) orelse SP =:= $_ orelse SP =:= $() -> {Rest, Parser, add_op('end', Parsed)}; % FINAL -parens- expression(<<")", _/binary>> = Rest, #parser{level = L} = Parser, [{op, _}, #if_block{} | _]) when is_number(L) -> throw_error(eparse, Parser, Rest); expression(<<")", Rest/binary>>, #parser{level = L} = Parser, Parsed) when is_number(L) -> {Rest, inc_pos(Parser), add_op('end', Parsed)}; % FINAL -array- expression(<<"]", _/binary>> = Rest, #parser{level = array} = Parser, [{op, _}, #if_block{} | _]) -> throw_error(eparse, Parser, Rest); expression(<<"]", Rest/binary>>, #parser{level = array} = Parser, Parsed) -> {Rest, inc_pos(Parser), add_op('end', Parsed)}; expression(<<"}", _/binary>> = Rest, #parser{level = array_curly} = Parser, [{op, _}, #if_block{} | _]) -> throw_error(eparse, Parser, Rest); expression(<<"}", Rest/binary>>, #parser{level = array_curly} = Parser, Parsed) -> {Rest, inc_pos(Parser), add_op('end', Parsed)}; % FINAL -all but parens- expression(<<"?>\n", _/binary>> = Rest, #parser{level = L} = Parser, [{op, _}, #if_block{} | _]) when not is_number(L) -> throw_error(eparse, Parser, Rest); expression(<<"?>\n", _/binary>> = Rest, #parser{level = L} = Parser, Parsed) when not is_number(L) -> case add_op('end', Parsed) of #operation{type = <<"?">>, line = {{_, R}, {_, C}}} -> throw_error(eparse, Parser#parser{row = R, col = C}, Rest); Op -> {Rest, Parser, Op} end; expression(<<"?>", _/binary>> = Rest, #parser{level = L} = Parser, [{op, _}, #if_block{} | _]) when not is_number(L) -> throw_error(eparse, Parser, Rest); expression(<<"?>", _/binary>> = Rest, #parser{level = L} = Parser, Parsed) when not is_number(L) -> {Rest, Parser, add_op('end', Parsed)}; % VARIABLE expression(<<"$", Rest/binary>>, Parser, Parsed) -> {Rest0, Parser0, [Var]} = variable(Rest, inc_pos(Parser), []), expression(Rest0, copy_rowcol(Parser0, Parser), add_op(Var, Parsed)); % NUMBER expression(<> = Rest, Parser, Parsed) when ?IS_NUMBER(A) -> {Rest0, Parser0, [Number]} = number(Rest, Parser, []), expression(Rest0, copy_rowcol(Parser0, Parser), add_op(Number, Parsed)); expression(<<".", A:8, _/binary>> = Rest, Parser, Parsed) when ?IS_NUMBER(A) -> {Rest0, Parser0, [Number]} = number(Rest, Parser, []), expression(Rest0, copy_rowcol(Parser0, Parser), add_op(Number, Parsed)); % STRING expression(<> = Rest, Parser, Parsed) when A =:= $" orelse A =:= $' orelse A =:= $` -> {Rest0, Parser0, String} = ephp_parser_string:string(Rest, Parser, []), expression(Rest0, Parser0, add_op(String, Parsed)); % HEREDOC & NOWDOC expression(<<"<<<", _/binary>> = Rest, Parser, Parsed) -> {Rest0, Parser0, String} = ephp_parser_string:string(Rest, Parser, []), expression(Rest0, Parser0, add_op(String, Parsed)); % INCLUDE expression(<>, Parser, Parsed) when ?OR(I, $I, $i) andalso ?OR(N, $N, $n) andalso ?OR(C, $C, $c) andalso ?OR(L, $L, $l) andalso ?OR(U, $U, $u) andalso ?OR(D, $D, $d) andalso ?OR(E, $E, $e) andalso (SP =:= $) orelse ?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> {Rest0, Parser0} = case remove_spaces(<>, add_pos(Parser, 7)) of {<<"(", R0/binary>>, P0} -> {R0, inc_pos(P0)}; {R0, P0} -> {R0, P0} end, {Rest1, Parser1, Exp} = expression(Rest0, Parser0, []), Include = add_line(#call{name = <<"include">>, args = [Exp]}, Parser), expression(Rest1, Parser1, add_op(Include, Parsed)); % INCLUDE_ONCE expression(<>, Parser, Parsed) when ?OR(I, $I, $i) andalso ?OR(N, $N, $n) andalso ?OR(C, $C, $c) andalso ?OR(L, $L, $l) andalso ?OR(U, $U, $u) andalso ?OR(D, $D, $d) andalso ?OR(E, $E, $e) andalso ?OR(O, $O, $o) andalso (SP =:= $) orelse ?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> {Rest0, Parser0} = case remove_spaces(<>, add_pos(Parser, 12)) of {<<"(", R0/binary>>, P0} -> {R0, inc_pos(P0)}; {R0, P0} -> {R0, P0} end, {Rest1, Parser1, Exp} = expression(Rest0, Parser0, []), Include = add_line(#call{name = <<"include_once">>, args = [Exp]}, Parser), expression(Rest1, Parser1, add_op(Include, Parsed)); % REQUIRE expression(<>, Parser, Parsed) when ?OR(R, $R, $r) andalso ?OR(E, $E, $e) andalso ?OR(Q, $Q, $q) andalso ?OR(U, $U, $u) andalso ?OR(I, $I, $i) andalso (SP =:= $) orelse ?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> {Rest0, Parser0} = case remove_spaces(<>, add_pos(Parser, 7)) of {<<"(", R0/binary>>, P0} -> {R0, inc_pos(P0)}; {R0, P0} -> {R0, P0} end, {Rest1, Parser1, Exp} = expression(Rest0, Parser0, []), Include = add_line(#call{name = <<"require">>, args = [Exp]}, Parser), expression(Rest1, Parser1, add_op(Include, Parsed)); % REQUIRE_ONCE expression(<>, Parser, Parsed) when ?OR(R, $R, $r) andalso ?OR(E, $E, $e) andalso ?OR(Q, $Q, $q) andalso ?OR(U, $U, $u) andalso ?OR(I, $I, $i) andalso ?OR(O, $O, $o) andalso ?OR(N, $N, $n) andalso ?OR(C, $C, $c) andalso (SP =:= $) orelse ?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> {Rest0, Parser0} = case remove_spaces(<>, add_pos(Parser, 12)) of {<<"(", R0/binary>>, P0} -> {R0, inc_pos(P0)}; {R0, P0} -> {R0, P0} end, {Rest1, Parser1, Exp} = expression(Rest0, Parser0, []), Include = add_line(#call{name = <<"require_once">>, args = [Exp]}, Parser), expression(Rest1, Parser1, add_op(Include, Parsed)); % AND expression(<>, Parser, Parsed) when ?OR(A, $a, $A) andalso ?OR(N, $n, $N) andalso ?OR(D, $d, $D) andalso not (?IS_ALPHA(SP) orelse ?IS_NUMBER(SP) orelse SP =:= $_) -> OpL = <<"and">>, expression(Rest, add_pos(Parser, 3), add_op({OpL, precedence(OpL), Parser}, Parsed)); % XOR expression(<>, Parser, Parsed) when ?OR(X, $x, $X) andalso ?OR(O, $o, $O) andalso ?OR(R, $r, $R) andalso not (?IS_ALPHA(SP) orelse ?IS_NUMBER(SP) orelse SP =:= $_) -> OpL = <<"xor">>, expression(Rest, add_pos(Parser, 3), add_op({OpL, precedence(OpL), Parser}, Parsed)); % OPERATOR 3 LETTERS expression(<>, Parser, Parsed) when ?IS_OP3(Op) -> expression(Rest, add_pos(Parser, 3), add_op({Op, precedence(Op), Parser}, Parsed)); % OR expression(<>, Parser, Parsed) when ?OR(O, $o, $O) andalso ?OR(R, $r, $R) andalso not (?IS_ALPHA(SP) orelse ?IS_NUMBER(SP) orelse SP =:= $_) -> OpL = <<"or">>, expression(Rest, add_pos(Parser, 2), add_op({OpL, precedence(OpL), Parser}, Parsed)); % OPERATORS 2 LETTERS expression(<>, Parser, Parsed) when ?IS_OP2(Op) -> expression(Rest, add_pos(Parser, 2), add_op({Op, precedence(Op), Parser}, Parsed)); % OPERATORS 1 LETTER expression(<>, Parser, Parsed) when ?IS_OP1(Op) -> expression(Rest, inc_pos(Parser), add_op({Op, precedence(Op), Parser}, Parsed)); % CONSTANT / FUNCTION expression(<> = Rest, Parser, [{op, []} | _] = Parsed) when ?IS_ALPHA(A) orelse A =:= $_ orelse A =:= $\\ -> {Rest0, NewParser, [Constant]} = constant(Rest, Parser, []), expression(Rest0, copy_rowcol(NewParser, Parser), add_op(Constant, Parsed)); expression(<> = Rest, Parser, [{op, Ops} | _] = Parsed) when ?IS_ALPHA(A) orelse A =:= $_ orelse A =:= $\\ -> {Rest0, Parser0, [Constant]} = constant(Rest, Parser, []), case lists:last(Ops) of #constant{} -> throw_error(eparse, Parser, {Constant#constant.name, <<"T_STRING">>}); _ -> expression(Rest0, copy_rowcol(Parser0, Parser), add_op(Constant, Parsed)) end; expression(<> = Rest, Parser, Parsed) when ?IS_ALPHA(A) orelse A =:= $_ orelse A =:= $\\ -> case constant(Rest, Parser, []) of {Rest0, Parser0, [Constant]} -> expression(Rest0, copy_rowcol(Parser0, Parser), add_op(Constant, Parsed)); {<<>>, _Parser0, []} -> {<<>>, Parser, []} end; % FINAL -switch- expression(<<":", _/binary>> = Rest, #parser{level = switch_label} = Parser, [Exp]) -> {Rest, Parser, add_op('end', [Exp])}; % TERNARY OPERATOR expression(<<"?", Rest/binary>>, Parser, Parsed) -> Op = <<"?">>, NewParser = inc_pos(Parser), QParsed = add_op({Op, precedence(Op), Parser}, Parsed), case expression(Rest, NewParser, []) of {<<":", Rest0/binary>>, Parser0, Parsed0} -> Op0 = <<":">>, {Rest1, Parser1, Parsed1} = expression(Rest0, inc_pos(Parser0), []), ToAdd = [Parsed0, [{Op0, precedence(Op0), Parser0}], Parsed1], XParsed = lists:foldl(fun add_op/2, QParsed, ToAdd), {Rest1, Parser1, add_op('end', XParsed)}; {Rest0, _Parser0, _Parsed0} -> throw_error(eparse, Parser, Rest0) end; expression(<<":", Rest/binary>>, Parser, Parsed) -> case lists:keyfind(<<"?">>, 1, Parsed) of {<<"?">>, _, _} -> Op = <<":">>, expression(Rest, inc_pos(Parser), add_op({Op, precedence(Op), Parser}, Parsed)); false -> {<<":", Rest/binary>>, Parser, add_op('end', Parsed)} end; % FINAL -unclosed- expression(Rest, #parser{level = unclosed} = Parser, [Exp]) -> {Rest, Parser, add_op('end', [Exp])}; % PARSE ERROR expression(<<",", _/binary>>, #parser{level = code} = Parser, _Parsed) -> throw_error(eparse, Parser, {unexpected, <<",">>}); expression(<<"{", _/binary>>, Parser, _Parsed) -> throw_error(eparse, Parser, {unexpected, <<"{">>}); expression(<<>>, Parser, _Parsed) -> throw_error(eparse, Parser, <<>>). exp_parens(Rest, #parser{level = L} = Parser, Parsed) when not is_number(L) -> {Rest0, Parser0, Op} = expression(Rest, Parser#parser{level = 1}, []), expression(Rest0, copy_rowcol(Parser0, Parser), add_op(Op, Parsed)); exp_parens(Rest, #parser{level = L} = Parser, Parsed) -> {Rest0, Parser0, Op} = expression(Rest, Parser#parser{level = L + 1}, []), expression(Rest0, copy_rowcol(Parser0, Parser), add_op(Op, Parsed)). -type associativity() :: no_assoc | left | right. -spec precedence(binary()) -> {associativity(), pos_integer()} | false. %% took from http://php.net/manual/en/language.operators.precedence.php precedence(<<"clone">>) -> {no_assoc, 1}; precedence(<<"new">>) -> {no_assoc, 1}; precedence(<<"[">>) -> {left, 2}; %% array precedence(<<"->">>) -> {left, 2}; %% object precedence(<<"::">>) -> {left, 2}; %% class precedence(<<"**">>) -> {right, 3}; %% arith precedence(<<"++">>) -> {right, 4}; precedence(<<"--">>) -> {right, 4}; precedence(<<126>>) -> {right, 4}; %% ~ (types and increment/decrement) precedence(<<"(int)">>) -> {right, 4}; precedence(<<"(float)">>) -> {right, 4}; precedence(<<"(string)">>) -> {right, 4}; precedence(<<"(array)">>) -> {right, 4}; precedence(<<"(object)">>) -> {right, 4}; precedence(<<"(bool)">>) -> {right, 4}; precedence(<<"(unset)">>) -> {right, 4}; precedence(<<"@">>) -> {right, 1}; precedence(<<"instanceof">>) -> {no_assoc, 5}; precedence(<<"!">>) -> {right, 6}; %% logic precedence(<<"*">>) -> {left, 7}; precedence(<<"/">>) -> {left, 7}; precedence(<<"%">>) -> {left, 7}; precedence(<<"+">>) -> {left, 8}; precedence(<<"-">>) -> {left, 8}; precedence(<<".">>) -> {left, 8}; %% concat precedence(<<"<<">>) -> {left, 9}; %% bit by bit precedence(<<">>">>) -> {left, 9}; %% bit by bit precedence(<<"<">>) -> {no_assoc, 10}; precedence(<<"<=">>) -> {no_assoc, 10}; precedence(<<">">>) -> {no_assoc, 10}; precedence(<<">=">>) -> {no_assoc, 10}; precedence(<<"==">>) -> {no_assoc, 11}; precedence(<<"!=">>) -> {no_assoc, 11}; precedence(<<"===">>) -> {no_assoc, 11}; precedence(<<"!==">>) -> {no_assoc, 11}; precedence(<<"<>">>) -> {no_assoc, 11}; precedence(<<"<=>">>) -> {no_assoc, 11}; precedence(<<"&">>) -> {left, 12}; %% bit by bit & references precedence(<<"^">>) -> {left, 13}; %% bit by bit precedence(<<"|">>) -> {left, 14}; %% bit by bit precedence(<<"&&">>) -> {left, 15}; %% logic precedence(<<"||">>) -> {left, 16}; %% logic precedence(<<"??">>) -> {right, 17}; %% comparison precedence(<<"?">>) -> {left, 18}; %% ternary precedence(<<":">>) -> {left, 17}; %% ternary precedence(<<"=">>) -> {right, 19}; %% assign precedence(<<"+=">>) -> {right, 19}; precedence(<<"-=">>) -> {right, 19}; precedence(<<"*=">>) -> {right, 19}; precedence(<<"**=">>) -> {right, 19}; precedence(<<"/=">>) -> {right, 19}; precedence(<<".=">>) -> {right, 19}; precedence(<<"%=">>) -> {right, 19}; precedence(<<"&=">>) -> {right, 19}; precedence(<<"|=">>) -> {right, 19}; precedence(<<"^=">>) -> {right, 19}; precedence(<<"<<=">>) -> {right, 19}; precedence(<<">>=">>) -> {right, 19}; precedence(<<"and">>) -> {left, 20}; precedence(<<"xor">>) -> {left, 21}; precedence(<<"or">>) -> {left, 22}; precedence(_) -> false. operator(<<":">> = Op, Left, Right) -> #operation{type = Op, expression_left = Left, expression_right = Right}; operator(<<"?">> = Op, Left, Right) -> #operation{type = Op, expression_left = Left, expression_right = Right}; operator(<<"and">>, Left, Right) -> operator('and', Left, Right); operator(<<"or">>, Left, Right) -> operator('or', Left, Right); operator(<<"xor">>, Left, Right) -> operator('xor', Left, Right); operator(<<"&&">>, Left, Right) -> operator('and', Left, Right); operator(<<"||">>, Left, Right) -> operator('or', Left, Right); operator(<<"^^">>, Left, Right) -> operator('xor', Left, Right); operator(<<"<=">>, Left, Right) -> operator(<<"=<">>, Left, Right); operator(Op, R1, R2) when is_boolean(R1) andalso is_boolean(R2) -> case Op of 'and' -> R1 and R2; 'or' -> R1 or R2; 'xor' -> R1 xor R2; _ -> #operation{type = Op, expression_left = R1, expression_right = R2} end; operator(<<"/">>, R1, R2) when (is_record(R1, int) orelse is_record(R1, float)) andalso (is_record(R2, int) orelse is_record(R2, float)) -> N2 = element(2, R2), case N2 == 0 of true -> #operation{type = <<"/">>, expression_left = R1, expression_right = R2}; false -> N1 = element(2, R1), if N1 rem N2 =:= 0 -> #int{int = N1 div N2}; true -> #float{float = N1 / N2} end end; operator(Op, R1, R2) when (is_record(R1, int) orelse is_record(R1, float)) andalso (is_record(R2, int) orelse is_record(R2, float)) -> N1 = element(2, R1), N2 = element(2, R2), Res = case Op of <<"+">> -> N1 + N2; <<"-">> -> N1 - N2; <<"*">> -> N1 * N2; <<"**">> -> ephp_data:pow(N1, N2); <<"%">> -> N1 rem N2; <<">">> -> N1 > N2; <<"<">> -> N1 < N2; <<"==">> -> N1 == N2; <<"===">> -> N1 =:= N2; <<"=<">> -> N1 =< N2; <<">=">> -> N1 >= N2; <<"^">> -> N1 bxor N2; <<"&">> -> N1 band N2; <<"|">> -> N1 bor N2; <<"<<">> -> N1 bsl N2; <<">>">> -> N1 bsr N2; 'xor' -> ephp_data:to_bool(N1) xor ephp_data:to_bool(N2); 'or' -> ephp_data:to_bool(N1) or ephp_data:to_bool(N2); 'and' -> ephp_data:to_bool(N1) and ephp_data:to_bool(N2) end, if is_integer(Res) -> #int{int = Res}; is_float(Res) -> #float{float = Res}; is_boolean(Res) -> Res end; operator(<<".">>, #text{text = T1}, #text{text = T2}) -> #text{text = <>}; operator(<<".">>, Left, Right) -> ProcessedLeft = concat(Left), ProcessedRight = concat(Right), #concat{texts = ProcessedLeft ++ ProcessedRight}; operator(Op, Left, Right) -> #operation{type = Op, expression_left = Left, expression_right = Right}. concat(#concat{texts = T}) -> T; concat(T) -> [T]. solve(Expression) -> Postfix = shunting_yard(parse_negative(Expression), [], []), case gen_op(Postfix, []) of [Operation] -> Operation; [{UnExpected, _, Pos} | _] -> throw_error(eparse, Pos, {unexpected, UnExpected}) end. gen_op([], Stack) -> Stack; gen_op([{<<"=">>, {_, _}, Parser} | Rest], [B, {operation_not, A, Line} | Stack]) -> Assign = add_line(#assign{variable = A, expression = B}, Parser), gen_op(Rest, [{operation_not, Assign, Line} | Stack]); gen_op([{<<"=">>, {_, _}, Parser} | Rest], [B, A | Stack]) -> Assign = add_line(#assign{variable = A, expression = B}, Parser), gen_op(Rest, [Assign | Stack]); gen_op([{<>, _, Parser} | Rest], []) -> gen_op(Rest, [add_line(#ref{var = V}, Parser)]); gen_op([#variable{} = V, {<<"&">>, _, Parser}, {<<"=">>, _, _} = A | Rest], Stack) -> gen_op([A | Rest], [add_line(#ref{var = V}, Parser) | Stack]); gen_op([#call{} = C, {<<"&">>, _, _Parser} | Rest], []) -> gen_op(Rest, [C]); gen_op([#call{} = C, {<<"&">>, _, _Parser}, {<<"=">>, _, _} = A | Rest], Stack) -> gen_op([A | Rest], [C | Stack]); gen_op([{<<"@">>, {right, _}, #parser{}} | Rest], [A | Stack]) -> gen_op(Rest, [{silent, A} | Stack]); gen_op([{<<126>>, {_, _}, #parser{row = R, col = C}} | Rest], [A | Stack]) -> gen_op(Rest, [{operation_bnot, A, {{line, R}, {column, C}}} | Stack]); gen_op([{<<"!">>, {_, _}, #parser{row = R, col = C}} | Rest], [A | Stack]) -> gen_op(Rest, [{operation_not, A, {{line, R}, {column, C}}} | Stack]); gen_op([#constant{name = <<"break">>}, #int{int = I}], []) -> [{break, I}]; gen_op([#constant{name = <<"continue">>}, #int{int = I}], []) -> [{continue, I}]; gen_op([{<<"->">>, {_, _}, Parser} | Rest], [B, #variable{idx = Idx} = A | Stack]) -> Object = case B of #int{int = I} -> add_line({object, ephp_data:to_bin(I)}, Parser); #float{float = F} -> add_line({object, ephp_data:to_bin(F)}, Parser); #constant{name = Name} -> add_line({object, Name}, Parser); #variable{} -> add_line({object, B}, Parser); #call{} -> add_line({object, B}, Parser); #text_to_process{} -> add_line({object, B}, Parser); _ -> throw_error(eparse, Parser, {<<"`\"identifier (T_STRING)\"' or " "`\"variable (T_VARIABLE)\"' or " "`'{'' or `'$''">>}) end, gen_op(Rest, [A#variable{idx = Idx ++ [Object]} | Stack]); gen_op([{<<"++">>, {_, _}, Parser} | Rest], [V | Stack]) -> gen_op(Rest, [{post_incr, V, Parser} | Stack]); gen_op([{<<"--">>, {_, _}, Parser} | Rest], [V | Stack]) -> gen_op(Rest, [{post_decr, V, Parser} | Stack]); gen_op([{<<"::">>, {_, _}, _Parser} | Rest], [#constant{} = A, B | Stack]) -> gen_op(Rest, [A#constant{type = class, class = B} | Stack]); gen_op([{<<"(int)">>, {_, _}, _Parser} | Rest], [#int{} = I | Stack]) -> gen_op(Rest, [I | Stack]); gen_op([{<<"(int)">>, {_, _}, Parser} | Rest], [#float{float = F} | Stack]) -> gen_op(Rest, [add_line(#int{int = ephp_data:flooring(F)}, Parser) | Stack]); gen_op([{<<"(int)">>, {_, _}, Parser} | Rest], [#text{text = T} | Stack]) -> gen_op(Rest, [add_line(#int{int = ephp_data:bin_to_number(T)}, Parser) | Stack]); gen_op([{<<"(int)">>, {_, _}, Parser} | Rest], [A | Stack]) -> gen_op(Rest, [add_line(#cast{type = int, content = A}, Parser) | Stack]); gen_op([{<<"(float)">>, {_, _}, Parser} | Rest], [#int{int = I} | Stack]) -> gen_op(Rest, [add_line(#float{float = erlang:float(I)}, Parser) | Stack]); gen_op([{<<"(float)">>, {_, _}, _Parser} | Rest], [#float{} = F | Stack]) -> gen_op(Rest, [F | Stack]); gen_op([{<<"(float)">>, {_, _}, Parser} | Rest], [#text{text = T} | Stack]) -> Float = erlang:float( ephp_data:bin_to_number(T)), gen_op(Rest, [add_line(#float{float = Float}, Parser) | Stack]); gen_op([{<<"(float)">>, {_, _}, Parser} | Rest], [A | Stack]) -> gen_op(Rest, [add_line(#cast{type = float, content = A}, Parser) | Stack]); gen_op([{<<"(string)">>, {_, _}, Parser} | Rest], [#int{int = I} | Stack]) -> gen_op(Rest, [add_line(#text{text = ephp_data:to_bin(I)}, Parser) | Stack]); gen_op([{<<"(string)">>, {_, _}, _Parser} | Rest], [#text{} = T | Stack]) -> gen_op(Rest, [T | Stack]); gen_op([{<<"(string)">>, {_, _}, Parser} | Rest], [#float{float = F} | Stack]) -> gen_op(Rest, [add_line(#text{text = ephp_data:to_bin(F)}, Parser) | Stack]); gen_op([{<<"(string)">>, {_, _}, Parser} | Rest], [A | Stack]) -> gen_op(Rest, [add_line(#cast{type = string, content = A}, Parser) | Stack]); gen_op([{<<"(bool)">>, {_, _}, _Parser} | Rest], [#int{int = I} | Stack]) -> gen_op(Rest, [ephp_data:to_bool(I) | Stack]); gen_op([{<<"(bool)">>, {_, _}, _Parser} | Rest], [#text{text = T} | Stack]) -> gen_op(Rest, [ephp_data:to_bool(T) | Stack]); gen_op([{<<"(bool)">>, {_, _}, _Parser} | Rest], [#float{float = F} | Stack]) -> gen_op(Rest, [ephp_data:to_bool(F) | Stack]); gen_op([{<<"(bool)">>, {_, _}, Parser} | Rest], [A | Stack]) -> gen_op(Rest, [add_line(#cast{type = bool, content = A}, Parser) | Stack]); gen_op([{<<"(array)">>, {_, _}, Parser} | Rest], [#int{line = DParser} = D | Stack]) -> gen_op(Rest, [add_to_array(#array{line = DParser}, Parser, D) | Stack]); gen_op([{<<"(array)">>, {_, _}, Parser} | Rest], [#text{line = DParser} = D | Stack]) -> gen_op(Rest, [add_to_array(#array{line = DParser}, Parser, D) | Stack]); gen_op([{<<"(array)">>, {_, _}, Parser} | Rest], [#float{line = DParser} = D | Stack]) -> gen_op(Rest, [add_to_array(#array{line = DParser}, Parser, D) | Stack]); gen_op([{<<"(array)">>, {_, _}, Parser} | Rest], [A | Stack]) -> gen_op(Rest, [add_line(#cast{type = array, content = A}, Parser) | Stack]); gen_op([{<<"(object)">>, {_, _}, Parser} | Rest], [A | Stack]) -> gen_op(Rest, [add_line(#cast{type = object, content = A}, Parser) | Stack]); gen_op([{<<"(unset)">>, {_, _}, _Parser} | Rest], [_ | Stack]) -> gen_op(Rest, [undefined | Stack]); gen_op([{<<"clone">>, {_, _}, Parser} | Rest], [A | Stack]) -> gen_op(Rest, [add_line(#clone{var = A}, Parser) | Stack]); % TODO add the rest of casting operators gen_op([{<<"??">>, {_, _}, Parser} | Rest], [B, A | Stack]) -> Isset = add_line(#call{name = <<"isset">>, args = [A]}, Parser), IfBlock = add_line(#if_block{conditions = Isset, true_block = A, false_block = B}, Parser), gen_op(Rest, [IfBlock | Stack]); gen_op([{<<"?">>, {_, _}, Parser} | Rest], [#operation{type = <<":">>} = OpElse, Cond | Stack]) -> #operation{expression_left = TrueBlock, expression_right = FalseBlock} = OpElse, IfBlock = add_line(#if_block{conditions = Cond, true_block = TrueBlock, false_block = FalseBlock}, Parser), gen_op(Rest, [IfBlock | Stack]); gen_op([{<<"?">>, {_, _}, Parser} | _Rest], _Stack) -> throw_error(eparse, Parser, <<>>); gen_op([{Op, {_, _}, Parser} | Rest], [B, A | Stack]) -> gen_op(Rest, [add_line(operator(Op, A, B), Parser) | Stack]); gen_op([A | Rest], Stack) -> gen_op(Rest, [A | Stack]). -spec add_to_array(php_array(), parser(), php_integer() | php_float() | text()) -> php_array(). %% @hidden add_to_array(#array{elements = E} = Array, Parser, Element) -> Array#array{elements = E ++ [add_line(#array_element{element = Element}, Parser)]}. parse_negative(Elements) -> parse_negative(lists:reverse(Elements), []). parse_negative([#int{} = I, {<<"-">>, {_, _}, _}, {_, {_, _}, _} = Op | Rest], Stack) -> parse_negative([I#int{int = -I#int.int}, Op | Rest], Stack); parse_negative([#float{} = F, {<<"-">>, {_, _}, _}, {_, {_, _}, _} = Op | Rest], Stack) -> parse_negative([F#float{float = -F#float.float}, Op | Rest], Stack); parse_negative([A, {<<"-">>, {_, _}, _}, {_, {_, _}, _} = Op | Rest], Stack) -> parse_negative([{operation_minus, A, undefined}, Op | Rest], Stack); parse_negative([#int{} = I, {<<"-">>, {_, _}, _}], Stack) -> [I#int{int = -I#int.int} | Stack]; parse_negative([#float{} = F, {<<"-">>, {_, _}, _}], Stack) -> [F#float{float = -F#float.float} | Stack]; parse_negative([A, {<<"-">>, {_, _}, _}], Stack) -> [{operation_minus, A, undefined} | Stack]; parse_negative([A | Rest], Stack) -> parse_negative(Rest, [A | Stack]); parse_negative([], Stack) -> Stack. shunting_yard([], [], Postfix) -> Postfix; shunting_yard([], OpS, Postfix) -> Postfix ++ OpS; shunting_yard([{_, {_, _}, _} = Op | Rest], [], Postfix) -> shunting_yard(Rest, [Op], Postfix); shunting_yard([{_, {left, P1}, _} = Op | Rest], [{_, {_, P2}, _} = Op1 | OpS], Postfix) when P1 > P2 -> shunting_yard(Rest, [Op | OpS], Postfix ++ [Op1]); shunting_yard([{_, {_, P1}, _} = Op | Rest], [{_, {_, P2}, _} = Op1 | OpS], Postfix) when P1 >= P2 -> shunting_yard(Rest, [Op | OpS], Postfix ++ [Op1]); shunting_yard([{_, {left, P1}, _} = Op | Rest], [{_, {_, P2}, _} | _] = OpS, Postfix) when P1 =< P2 -> shunting_yard(Rest, [Op | OpS], Postfix); shunting_yard([{_, {_, P1}, _} = Op | Rest], [{_, {_, P2}, _} | _] = OpS, Postfix) when P1 < P2 -> shunting_yard(Rest, [Op | OpS], Postfix); shunting_yard([A | Rest], OpS, Postfix) -> shunting_yard(Rest, OpS, Postfix ++ [A]).