-module(ephp_parser_expr). -author('manuel@altenwald.com'). -compile([warnings_as_errors]). -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_level/2, add_line/2, remove_spaces/2, throw_error/3, array_def_level/1, code_statement_level/1, arg_level/1, enclosed_level/1, variable/3, comment_line/3, comment_block/3, constant/3 ]). -import(ephp_parser_func, [ st_use_or_block/3, funct_name/3 ]). array_def_54_level({_,Row,Col}) -> {{array_def,54},Row,Col}. add_op('end', []) -> []; add_op('end', [{op, [#constant{name = Name, line = Pos} = 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), Pos}; <> 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), Pos}; <> 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), Pos}; <> 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), Pos}; <> 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), Pos}; % 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), Pos}; <> 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), Pos}; <> when ?OR(B,$B,$b) andalso ?OR(O,$O,$o) andalso ?OR(L,$L,$l) -> OpL = <<"(bool)">>, {OpL, precedence(OpL), Pos}; <> 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), Pos}; _ -> 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([{<<"++">>,_,Pos},#variable{}=V|Rest], Processed) -> process_incr_decr(Rest, Processed ++ [{pre_incr, V, Pos}]); process_incr_decr([{<<"--">>,_,Pos},#variable{}=V|Rest], Processed) -> process_incr_decr(Rest, Processed ++ [{pre_decr, V, Pos}]); process_incr_decr([#variable{}=V,{<<"++">>,_,Pos}|Rest], Processed) -> process_incr_decr(Rest, Processed ++ [{post_incr, V, Pos}]); process_incr_decr([#variable{}=V,{<<"--">>,_,Pos}|Rest], Processed) -> process_incr_decr(Rest, Processed ++ [{post_decr, V, Pos}]); process_incr_decr([A|Rest], Processed) -> process_incr_decr(Rest, Processed ++ [A]). number(<<"0", X:8, Rest/binary>>, Pos, []) when ?OR(X, $X, $x) -> hexa(Rest, add_pos(Pos, 2), []); number(<<"0", N:8, Rest/binary>>, Pos, []) when ?IS_OCT(N) -> octa(<>, add_pos(Pos, 2), []); number(<>, Pos, []) when ?IS_NUMBER(A) orelse A =:= $- -> number(Rest, add_pos(Pos, 1), [add_line(#int{int = <>}, Pos)]); number(<>, Pos, [#int{int = N} = I]) when ?IS_NUMBER(A) -> number(Rest, add_pos(Pos, 1), [I#int{int = <>}]); number(<<".", Rest/binary>>, Pos, []) -> number(Rest, add_pos(Pos, 1), [add_line(#float{float = <<"0.">>}, Pos)]); number(<<".", Rest/binary>>, Pos, [#int{int = N, line = Line}]) -> number(Rest, add_pos(Pos, 1), [#float{float = <>, line = Line}]); number(<>, Pos, [#float{float = N} = F]) when ?IS_NUMBER(A) -> number(Rest, add_pos(Pos, 1), [F#float{float = <>}]); number(Rest, Pos, [#int{int = N} = I]) -> {Rest, Pos, [I#int{int = binary_to_integer(N)}]}; number(Rest, Pos, [#float{float = N} = F]) -> {Rest, Pos, [F#float{float = binary_to_float(N)}]}. hexa(<>, Pos, []) when ?IS_HEX(A) -> hexa(Rest, add_pos(Pos, 1), [add_line(#int{int = <>}, Pos)]); hexa(<>, Pos, [#int{int=N}=I]) when ?IS_HEX(A) -> hexa(Rest, add_pos(Pos, 1), [I#int{int = <>}]); hexa(Rest, Pos, [#int{int = N} = I]) -> {Rest, Pos, [I#int{int = binary_to_integer(N, 16)}]}. octa(<>, Pos, []) when ?IS_OCT(A) -> octa(Rest, add_pos(Pos, 1), [add_line(#int{int = <>}, Pos)]); octa(<>, Pos, [#int{int=N}=I]) when ?IS_OCT(A) -> octa(Rest, add_pos(Pos, 1), [I#int{int = <>}]); octa(Rest, Pos, [#int{int = N} = I]) -> {Rest, Pos, [I#int{int = binary_to_integer(N, 8)}]}. array_def(<>, Pos, Args) when ?IS_SPACE(SP) -> array_def(Rest, add_pos(Pos,1), Args); array_def(<>, Pos, Args) when ?IS_NEWLINE(SP) -> array_def(Rest, new_line(Pos), Args); array_def(<<")",Rest/binary>>, {{array_def,0},_,_}=Pos, Args) -> {Rest,add_pos(Pos,1),Args}; array_def(<<"]",Rest/binary>>, {{array_def,54},_,_}=Pos, Args) -> {Rest,add_pos(Pos,1),Args}; %% TODO add error missing closing params array_def(Rest, Pos, Args) when Rest =/= <<>> -> case expression(Rest, Pos, []) of {<<")",Rest0/binary>>, {{array_def,0},_,_}=Pos0, [Idx,Arg]} -> NewArg = add_line(#array_element{idx=Idx, element=Arg}, Pos), {Rest0, add_pos(Pos0,1), Args ++ [NewArg]}; {<<")",Rest0/binary>>, {{array_def,0},_,_}=Pos0, undefined} -> {Rest0, add_pos(Pos0,1), Args}; {<<")",Rest0/binary>>, {{array_def,0},_,_}=Pos0, Arg} -> NewArg = add_line(#array_element{element=Arg}, Pos), {Rest0, add_pos(Pos0,1), Args ++ [NewArg]}; {<<"]",Rest0/binary>>, {{array_def,54},_,_}=Pos0, [Idx,Arg]} -> NewArg = add_line(#array_element{idx=Idx, element=Arg}, Pos), {Rest0, add_pos(Pos0,1), Args ++ [NewArg]}; {<<"]",Rest0/binary>>, {{array_def,54},_,_}=Pos0, undefined} -> {Rest0, add_pos(Pos0,1), Args}; {<<"]",Rest0/binary>>, {{array_def,54},_,_}=Pos0, Arg} -> NewArg = add_line(#array_element{element=Arg}, Pos), {Rest0, add_pos(Pos0,1), Args ++ [NewArg]}; {<<",",Rest0/binary>>, Pos0, [Idx,Arg]} -> NewArg = add_line(#array_element{idx=Idx, element=Arg}, Pos), array_def(Rest0, add_pos(Pos0, 1), Args ++ [NewArg]); {<<",",Rest0/binary>>, Pos0, Arg} -> NewArg = add_line(#array_element{element=Arg}, Pos), array_def(Rest0, add_pos(Pos0, 1), Args ++ [NewArg]) end. % CONSTANT / FUNCTION when -> is used expression(<> = Rest, Pos, [{op,[_,{<<"->">>,_,_}]}|_]=Parsed) when ?IS_ALPHA(A) orelse A =:= $_ -> {Rest0, Pos0, [Constant]} = constant(Rest, Pos, []), expression(Rest0, copy_level(Pos, Pos0), add_op(Constant, Parsed)); % ARRAY(...) -old- expression(<>, Pos, 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(Pos, 5)) of {<<")", _/binary>> = Rest0, Pos0} -> %% (array) cast OpL = <<"(array)">>, NewParsed = add_op({OpL, precedence(OpL), Pos}, Parsed), expression(Rest0, Pos0, NewParsed); {<<"(", Rest0/binary>>, Pos0} -> NewPos = array_def_level(add_pos(Pos0, 1)), {Rest1, Pos1, Content} = array_def(Rest0, NewPos, []), NewParsed = add_op(add_line(#array{elements=Content}, Pos), Parsed), expression(Rest1, copy_level(Pos, Pos1), NewParsed) end; % [...] -array new- expression(<<"[",Rest/binary>>, Pos, []) -> NewPos = array_def_54_level(add_pos(Pos, 1)), {Rest1, Pos1, Content} = array_def(Rest, NewPos, []), NewParsed = add_op(add_line(#array{elements=Content}, Pos), []), expression(Rest1, copy_level(Pos, Pos1), NewParsed); expression(<<"[",Rest/binary>>, Pos, [{op,[]}|_]=Parsed) -> % ARRAY DEF NewPos = array_def_54_level(add_pos(Pos, 1)), {Rest1, Pos1, Content} = array_def(Rest, NewPos, []), NewParsed = add_op(add_line(#array{elements=Content}, Pos), Parsed), expression(Rest1, copy_level(Pos, Pos1), NewParsed); expression(<<"[",Rest/binary>>, Pos, [{op,Op}|_]=Parsed) -> case lists:last(Op) of {_,{RightOrLeft,_},_} when RightOrLeft =:= right orelse RightOrLeft =:= left -> % ARRAY DEF NewPos = array_def_54_level(add_pos(Pos, 1)), {Rest1, Pos1, Content} = array_def(Rest, NewPos, []), NewParsed = add_op(add_line(#array{elements=Content}, Pos), Parsed), expression(Rest1, copy_level(Pos, Pos1), NewParsed); _ -> % ARRAY INDEX NewParsed = [#variable{name = add_op('end', Parsed)}], {Rest0, Pos0, [Parsed0]} = variable(<<"[",Rest/binary>>, Pos, NewParsed), expression(Rest0, Pos0, add_op(Parsed0, [])) end; % NULL expression(<>, Pos, 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(Pos,4), add_op(undefined, Parsed)); % TRUE expression(<>, Pos, 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(Pos,4), add_op(true, Parsed)); % FALSE expression(<>, Pos, 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(Pos,5), add_op(false, Parsed)); % FUNCTION(...) -closure- expression(<>, Pos, 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>>, Pos0} = remove_spaces(<>, add_pos(Pos, 9)), {Rest1, Pos1, Args} = ephp_parser_func:funct_args(Rest0, Pos0, []), BaseFunction = add_line(#function{args = Args}, Pos), {Rest2, Pos2, Function} = st_use_or_block(Rest1, Pos1, BaseFunction), expression(Rest2, copy_level(Pos, Pos2), add_op(Function, Parsed)); % INSTANCEOF expression(<>, Pos, 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, Pos0} = remove_spaces(<>, Pos), OpL = <<"instanceof">>, expression(Rest0, Pos0, add_op({instanceof, precedence(OpL), Pos}, Parsed)); % NEW ... expression(<>, Pos, 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, Pos1, ObjName} = case remove_spaces(<>, Pos) of {<<"$", _/binary>> = Rest0, Pos0} -> expression(Rest0, Pos0, []); {<> = Rest0, Pos0} when ?IS_ALPHA(A) orelse A =:= $_ -> funct_name(Rest0, Pos0, []) end, Instance = case remove_spaces(Rest1, Pos1) of {<<"(",Rest2/binary>>, Pos2} -> {Rest3, Pos3, Args} = ephp_parser_func:call_args(Rest2, Pos2, []), add_line(#instance{name = ObjName, args = Args}, Pos); {Rest3, Pos3} -> add_line(#instance{name = ObjName}, Pos) end, expression(Rest3, copy_level(Pos, Pos3), add_op(Instance,Parsed)); % CLONE ... expression(<>, Pos, 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">>, Pos1 = add_pos(Pos, 5), Rest1 = <>, expression(Rest1, Pos1, add_op({OpL, precedence(OpL), Pos}, Parsed)); % FINAL -enclosed- expression(<<"}",Rest/binary>>, {enclosed,_,_}=Pos, [Exp]) -> {Rest, add_pos(Pos,1), add_op('end', [Exp])}; % FINAL -unclosed- expression(<> = Rest, {unclosed,_,_}=Pos, [Exp]) when ?IS_SPACE(SP) orelse SP =:= $" -> {Rest, Pos, add_op('end', [Exp])}; % SPACE -all- expression(<>, Pos, Parsed) when ?IS_SPACE(SP) -> expression(Rest, add_pos(Pos,1), Parsed); % NEWLINE -all- expression(<>, Pos, Parsed) when ?IS_NEWLINE(SP) -> expression(Rest, new_line(Pos), Parsed); % COMMENTS... expression(<<"//",Rest/binary>>, Pos, Parsed) -> {Rest0, Pos0, _} = comment_line(Rest, Pos, Parsed), expression(Rest0, Pos0, Parsed); expression(<<"#",Rest/binary>>, Pos, Parsed) -> {Rest0, Pos0, _} = comment_line(Rest, Pos, Parsed), expression(Rest0, Pos0, Parsed); expression(<<"/*",Rest/binary>>, Pos, Parsed) -> {Rest0, Pos0, _} = comment_block(Rest, Pos, Parsed), expression(Rest0, Pos0, Parsed); % FUNCTION CALL expression(<<"(",Rest/binary>>, Pos, [{op, Op}|Parsed]) -> {Op1, Op2} = case length(Op) of Size when Size >= 2 -> lists:split(Size - 1, Op); 1 -> {[], Op}; 0 -> {[], []} end, case Op2 of [#variable{} = V] -> Call = #call{name = V, line = V#variable.line}, {Rest0, Pos0, [Function]} = ephp_parser_func:function(Rest, add_pos(Pos,1), [Call|Parsed]), NewOp = {op, Op1 ++ [Function]}, expression(Rest0, copy_level(Pos, Pos0), [NewOp|Parsed]); _ -> exp_parens(Rest, add_pos(Pos, 1), [{op, Op}|Parsed]) end; % PARENS expression(<<"(", Rest/binary>>, Pos, Parsed) -> % FIXME: this is inconsistent, sometimes is expecting to remove ")" % and sometimes is not. exp_parens(Rest, Pos, Parsed); % FINAL -arg- expression(<<",", _/binary>> = Rest, {array, _, _} = Pos, Parsed) -> {Rest, Pos, add_op('end', Parsed)}; expression(<<",", _/binary>> = Rest, {array_curly, _, _} = Pos, Parsed) -> {Rest, Pos, add_op('end', Parsed)}; expression(<> = Rest, {arg,_,_}=Pos, [{op,_},#if_block{}|_]) when A =:= $, orelse A =:= $) -> throw_error(eparse, Pos, Rest); expression(<> = Rest, {arg,_,_}=Pos, Parsed) when A =:= $, orelse A =:= $) -> case add_op('end', Parsed) of #operation{type = <<"?">>, line={{_,R},{_,C}}} -> throw_error(eparse, {arg,R,C}, Rest); Op -> {Rest, Pos, Op} end; % FINAL -array definition array(...)- expression(<> = Rest, {{array_def,0},_,_}=Pos, [{op,_},#if_block{}|_]) when A =:= $, orelse A =:= $) -> throw_error(eparse, Pos, Rest); expression(<> = Rest, {{array_def,0},_,_}=Pos, [Parsed]) when A =:= $, orelse A =:= $) -> {Rest, Pos, add_op('end', [Parsed])}; expression(<> = Rest, {{array_def,0},_,_}=Pos, Parsed) when A =:= $, orelse A =:= $) -> case Parsed of [Arg, Idx] -> {Rest, Pos, [Idx, add_op('end', [Arg])]}; [] -> {Rest, Pos, undefined} end; % FINAL -array definition [...]- expression(<> = Rest, {{array_def,54},_,_}=Pos, [{op,_},#if_block{}|_]) when A =:= $, orelse A =:= $] -> throw_error(eparse, Pos, Rest); expression(<> = Rest, {{array_def,54},_,_}=Pos, [Parsed]) when A =:= $, orelse A =:= $] -> {Rest, Pos, add_op('end', [Parsed])}; expression(<> = Rest, {{array_def,54},_,_}=Pos, Parsed) when A =:= $, orelse A =:= $] -> case Parsed of [Arg,Idx] -> {Rest, Pos, [Idx,add_op('end', [Arg])]}; [] -> {Rest, Pos, undefined} end; % KEY & VALUE -array_def old and new- expression(<<"=>",Rest/binary>>, {{array_def,_},_,_}=Pos, [{op,_}=Op|Parser]) -> expression(Rest, add_pos(Pos,2), [{op,[]},add_op('end', [Op])|Parser]); % FINAL -all but parens- expression(<> = Rest, {L,_,_}=Pos, [{op,_},#if_block{}|_]) when not is_number(L) andalso (A =:= $) orelse A =:= $;) -> throw_error(eparse, Pos, Rest); expression(<> = Rest, {L,_,_}=Pos, Parsed) when not is_number(L) andalso (A =:= $) orelse A =:= $;) -> {Rest, Pos, add_op('end', Parsed)}; % FOREACH DATA expression(<> = Rest, {foreach_block,_,_} = Pos, [{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, Pos, Rest); expression(<> = Rest, {foreach_block,_,_} = Pos, Parsed) when ?OR(A,$a,$A) andalso ?OR(S,$s,$S) andalso not (?IS_ALPHANUM(SP) orelse SP =:= $_ orelse SP =:= $() -> {Rest, Pos, add_op('end', Parsed)}; % FINAL -parens- expression(<<")",_/binary>> = Rest, {L,_Row,_Col}=Pos, [{op,_},#if_block{}|_]) when is_number(L) -> throw_error(eparse, Pos, Rest); expression(<<")",Rest/binary>>, {L,_Row,_Col}=Pos, Parsed) when is_number(L) -> {Rest, add_pos(Pos,1), add_op('end', Parsed)}; % FINAL -array- expression(<<"]",_/binary>> = Rest, {array,_,_} = Pos, [{op,_},#if_block{}|_]) -> throw_error(eparse, Pos, Rest); expression(<<"]", Rest/binary>>, {array,_,_} = Pos, Parsed) -> {Rest, add_pos(Pos,1), add_op('end', Parsed)}; expression(<<"}",_/binary>> = Rest, {array_curly,_,_} = Pos, [{op, _}, #if_block{}|_]) -> throw_error(eparse, Pos, Rest); expression(<<"}", Rest/binary>>, {array_curly,_,_} = Pos, Parsed) -> {Rest, add_pos(Pos,1), add_op('end', Parsed)}; % FINAL -all but parens- expression(<<"?>\n",_/binary>> = Rest, {L,_,_} = Pos, [{op,_},#if_block{}|_]) when not is_number(L) -> throw_error(eparse, Pos, Rest); expression(<<"?>\n",_/binary>> = Rest, {L,_,_}=Pos, Parsed) when not is_number(L) -> case add_op('end', Parsed) of #operation{type = <<"?">>, line={{_,R},{_,C}}} -> throw_error(eparse, {L,R,C}, Rest); Op -> {Rest, Pos, Op} end; expression(<<"?>",_/binary>> = Rest, {L,_,_}=Pos, [{op,_},#if_block{}|_]) when not is_number(L) -> throw_error(eparse, Pos, Rest); expression(<<"?>",_/binary>> = Rest, {L,_,_}=Pos, Parsed) when not is_number(L) -> {Rest, Pos, add_op('end', Parsed)}; % VARIABLE expression(<<"$",Rest/binary>>, Pos, Parsed) -> {Rest0, Pos0, [Var]} = variable(Rest, add_pos(Pos,1), []), expression(Rest0, copy_level(Pos, Pos0), add_op(Var, Parsed)); % NUMBER expression(<> = Rest, Pos, Parsed) when ?IS_NUMBER(A) -> {Rest0, Pos0, [Number]} = number(Rest, Pos, []), expression(Rest0, copy_level(Pos, Pos0), add_op(Number, Parsed)); expression(<<".", A:8, _/binary>> = Rest, Pos, Parsed) when ?IS_NUMBER(A) -> {Rest0, Pos0, [Number]} = number(Rest, Pos, []), expression(Rest0, copy_level(Pos, Pos0), add_op(Number, Parsed)); % STRING expression(<> = Rest, Pos, Parsed) when A =:= $" orelse A =:= $' orelse A =:= $` -> {Rest0, Pos0, String} = ephp_parser_string:string(Rest, Pos, []), expression(Rest0, Pos0, add_op(String, Parsed)); % HEREDOC & NOWDOC expression(<<"<<<",_/binary>> = Rest, Pos, Parsed) -> {Rest0, Pos0, String} = ephp_parser_string:string(Rest, Pos, []), expression(Rest0, Pos0, add_op(String, Parsed)); % INCLUDE expression(<>, Pos, 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, Pos0} = case remove_spaces(<>, add_pos(Pos, 7)) of {<<"(",R0/binary>>, P0} -> {R0, add_pos(P0, 1)}; {R0, P0} -> {R0, P0} end, {Rest1, Pos1, Exp} = expression(Rest0, Pos0, []), Include = add_line(#call{name = <<"include">>, args=[Exp]}, Pos), expression(Rest1, Pos1, add_op(Include, Parsed)); % INCLUDE_ONCE expression(<>, Pos, 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, Pos0} = case remove_spaces(<>, add_pos(Pos, 12)) of {<<"(",R0/binary>>, P0} -> {R0, add_pos(P0, 1)}; {R0, P0} -> {R0, P0} end, {Rest1, Pos1, Exp} = expression(Rest0, Pos0, []), Include = add_line(#call{name = <<"include_once">>, args=[Exp]}, Pos), expression(Rest1, Pos1, add_op(Include, Parsed)); % REQUIRE expression(<>, Pos, 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, Pos0} = case remove_spaces(<>, add_pos(Pos, 7)) of {<<"(",R0/binary>>, P0} -> {R0, add_pos(P0, 1)}; {R0, P0} -> {R0, P0} end, {Rest1, Pos1, Exp} = expression(Rest0, Pos0, []), Include = add_line(#call{name = <<"require">>, args=[Exp]}, Pos), expression(Rest1, Pos1, add_op(Include, Parsed)); % REQUIRE_ONCE expression(<>, Pos, 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, Pos0} = case remove_spaces(<>, add_pos(Pos, 12)) of {<<"(",R0/binary>>, P0} -> {R0, add_pos(P0, 1)}; {R0, P0} -> {R0, P0} end, {Rest1, Pos1, Exp} = expression(Rest0, Pos0, []), Include = add_line(#call{name = <<"require_once">>, args=[Exp]}, Pos), expression(Rest1, Pos1, add_op(Include, Parsed)); % AND expression(<>, Pos, 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(Pos,3), add_op({OpL,precedence(OpL),Pos}, Parsed)); % XOR expression(<>, Pos, 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(Pos,3), add_op({OpL,precedence(OpL),Pos}, Parsed)); % OPERATOR 3 LETTERS expression(<>, Pos, Parsed) when ?IS_OP3(Op) -> expression(Rest, add_pos(Pos,3), add_op({Op,precedence(Op),Pos}, Parsed)); % OR expression(<>, Pos, 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(Pos,2), add_op({OpL,precedence(OpL),Pos}, Parsed)); % OPERATORS 2 LETTERS expression(<>, Pos, Parsed) when ?IS_OP2(Op) -> expression(Rest, add_pos(Pos,2), add_op({Op,precedence(Op),Pos}, Parsed)); % OPERATORS 1 LETTER expression(<>, Pos, Parsed) when ?IS_OP1(Op) -> expression(Rest, add_pos(Pos,1), add_op({Op,precedence(Op),Pos}, Parsed)); % CONSTANT / FUNCTION expression(<> = Rest, {L,_,_}=Pos, [{op,[]}|_]=Parsed) when ?IS_ALPHA(A) orelse A =:= $_ -> {Rest0, {_,R,C}, [Constant]} = constant(Rest, Pos, []), expression(Rest0, {L,R,C}, add_op(Constant, Parsed)); expression(<> = Rest, {L,_,_}=Pos, [{op,Ops}|_]=Parsed) when ?IS_ALPHA(A) orelse A =:= $_ -> {Rest0, {_,R,C}, [Constant]} = constant(Rest, Pos, []), case lists:last(Ops) of #constant{} -> throw_error(eparse, Pos, {Constant#constant.name, <<"T_STRING">>}); _ -> expression(Rest0, {L,R,C}, add_op(Constant, Parsed)) end; expression(<> = Rest, Pos, Parsed) when ?IS_ALPHA(A) orelse A =:= $_ -> {Rest0, Pos0, [Constant]} = constant(Rest, Pos, []), expression(Rest0, copy_level(Pos, Pos0), add_op(Constant, Parsed)); % FINAL -switch- expression(<<":",_/binary>> = Rest, {switch_label,_,_}=Pos, [Exp]) -> {Rest, Pos, add_op('end', [Exp])}; % TERNARY OPERATOR expression(<<"?", Rest/binary>>, Pos, Parsed) -> Op = <<"?">>, NewPos = add_pos(Pos, 1), QParsed = add_op({Op, precedence(Op), Pos}, Parsed), case expression(Rest, NewPos, []) of {<<":", Rest0/binary>>, Pos0, Parsed0} -> Op0 = <<":">>, {Rest1, Pos1, Parsed1} = expression(Rest0, add_pos(Pos0, 1), []), ToAdd = [Parsed0, [{Op0, precedence(Op0), Pos0}], Parsed1], XParsed = lists:foldl(fun add_op/2, QParsed, ToAdd), {Rest1, Pos1, add_op('end', XParsed)}; {Rest0, _Pos0, _Parsed0} -> throw_error(eparse, Pos, Rest0) end; expression(<<":",Rest/binary>>, Pos, Parsed) -> case lists:keyfind(<<"?">>, 1, Parsed) of {<<"?">>, _, _} -> Op = <<":">>, expression(Rest, add_pos(Pos, 1), add_op({Op, precedence(Op), Pos}, Parsed)); false -> {<<":", Rest/binary>>, Pos, add_op('end', Parsed)} end; % FINAL -unclosed- expression(Rest, {unclosed,_,_}=Pos, [Exp]) -> {Rest, Pos, add_op('end', [Exp])}; % PARSE ERROR expression(<<>>, Pos, _Parsed) -> throw_error(eparse, Pos, <<>>). exp_parens(Rest, {L, R, C} = Pos, Parsed) when not is_number(L) -> {Rest0, Pos0, Op} = expression(Rest, {1, R, C + 1}, []), expression(Rest0, copy_level(Pos, Pos0), add_op(Op, Parsed)); exp_parens(Rest, {L, R, C} = Pos, Parsed) -> {Rest0, Pos0, Op} = expression(Rest, {L + 1, R, C + 1}, []), expression(Rest0, copy_level(Pos, Pos0), 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), Res = N1 / N2, if is_integer(Res) -> #int{int=Res}; is_float(Res) -> #float{float=Res} 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; <<"%">> -> 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), [], []), [Operation] = gen_op(Postfix, []), Operation. gen_op([], Stack) -> Stack; gen_op([{<<"=">>,{_,_},Pos}|Rest], [B,{operation_not, A, Line}|Stack]) -> Assign = add_line(#assign{variable=A, expression=B}, Pos), gen_op(Rest, [{operation_not, Assign, Line}|Stack]); gen_op([{<<"=">>,{_,_},Pos}|Rest], [B,A|Stack]) -> Assign = add_line(#assign{variable=A, expression=B}, Pos), gen_op(Rest, [Assign|Stack]); gen_op([{<>, _, Pos}|Rest], []) -> gen_op(Rest, [add_line(#ref{var = V}, Pos)]); gen_op([#variable{} = V, {<<"&">>, _, Pos}, {<<"=">>, _, _} = A|Rest], Stack) -> gen_op([A|Rest], [add_line(#ref{var=V}, Pos)|Stack]); gen_op([#call{} = C, {<<"&">>, _, _Pos}|Rest], []) -> gen_op(Rest, [C]); gen_op([#call{} = C, {<<"&">>, _, _Pos}, {<<"=">>, _, _} = A|Rest], Stack) -> gen_op([A|Rest], [C|Stack]); gen_op([{<<"@">>,{right,_},{_,_,_}}|Rest], [A|Stack]) -> gen_op(Rest, [{silent, A}|Stack]); gen_op([{<<126>>,{_,_},{_,R,C}}|Rest], [A|Stack]) -> gen_op(Rest, [{operation_bnot, A, {{line,R},{column,C}}}|Stack]); gen_op([{<<"!">>,{_,_},{_,R,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([{<<"->">>,{_,_},Pos}|Rest], [B,#variable{idx=Idx}=A|Stack]) -> Object = case B of #int{int = I} -> add_line({object, ephp_data:to_bin(I)}, Pos); #float{float = F} -> add_line({object, ephp_data:to_bin(F)}, Pos); #constant{name = Name} -> add_line({object, Name}, Pos); #variable{} -> add_line({object, B}, Pos); #call{} -> add_line({object, B}, Pos); #text_to_process{} -> add_line({object, B}, Pos); _ -> throw_error(eparse, Pos, {<<"`\"identifier (T_STRING)\"' or " "`\"variable (T_VARIABLE)\"' or " "`'{'' or `'$''">>}) end, gen_op(Rest, [A#variable{idx = Idx ++ [Object]}|Stack]); gen_op([{<<"++">>, {_,_}, Pos}|Rest], [V|Stack]) -> gen_op(Rest, [{post_incr, V, Pos}|Stack]); gen_op([{<<"--">>, {_,_}, Pos}|Rest], [V|Stack]) -> gen_op(Rest, [{post_decr, V, Pos}|Stack]); gen_op([{<<"::">>,{_,_},_Pos}|Rest], [#constant{} = A,B|Stack]) -> gen_op(Rest, [A#constant{type = class, class = B}|Stack]); gen_op([{<<"(int)">>,{_,_},_Pos}|Rest], [#int{}=I|Stack]) -> gen_op(Rest, [I|Stack]); gen_op([{<<"(int)">>,{_,_},Pos}|Rest], [#float{float=F}|Stack]) -> gen_op(Rest, [add_line(#int{int=ephp_data:flooring(F)}, Pos)|Stack]); gen_op([{<<"(int)">>,{_,_},Pos}|Rest], [#text{text=T}|Stack]) -> gen_op(Rest, [add_line(#int{int=ephp_data:bin_to_number(T)}, Pos)|Stack]); gen_op([{<<"(int)">>,{_,_},Pos}|Rest], [A|Stack]) -> gen_op(Rest, [add_line(#cast{type=int, content=A}, Pos)|Stack]); gen_op([{<<"(float)">>,{_,_},Pos}|Rest], [#int{int=I}|Stack]) -> gen_op(Rest, [add_line(#float{float=erlang:float(I)}, Pos)|Stack]); gen_op([{<<"(float)">>,{_,_},_Pos}|Rest], [#float{}=F|Stack]) -> gen_op(Rest, [F|Stack]); gen_op([{<<"(float)">>,{_,_},Pos}|Rest], [#text{text=T}|Stack]) -> Float = erlang:float(ephp_data:bin_to_number(T)), gen_op(Rest, [add_line(#int{int=Float}, Pos)|Stack]); gen_op([{<<"(float)">>,{_,_},Pos}|Rest], [A|Stack]) -> gen_op(Rest, [add_line(#cast{type=float, content=A}, Pos)|Stack]); gen_op([{<<"(string)">>,{_,_},Pos}|Rest], [#int{int=I}|Stack]) -> gen_op(Rest, [add_line(#text{text=ephp_data:to_bin(I)}, Pos)|Stack]); gen_op([{<<"(string)">>,{_,_},_Pos}|Rest], [#text{}=T|Stack]) -> gen_op(Rest, [T|Stack]); gen_op([{<<"(string)">>,{_,_},Pos}|Rest], [#float{float=F}|Stack]) -> gen_op(Rest, [add_line(#text{text=ephp_data:to_bin(F)}, Pos)|Stack]); gen_op([{<<"(string)">>,{_,_},Pos}|Rest], [A|Stack]) -> gen_op(Rest, [add_line(#cast{type=string, content=A}, Pos)|Stack]); gen_op([{<<"(bool)">>,{_,_},_Pos}|Rest], [#int{int=I}|Stack]) -> gen_op(Rest, [ephp_data:to_bool(I)|Stack]); gen_op([{<<"(bool)">>,{_,_},_Pos}|Rest], [#text{text=T}|Stack]) -> gen_op(Rest, [ephp_data:to_bool(T)|Stack]); gen_op([{<<"(bool)">>,{_,_},_Pos}|Rest], [#float{float=F}|Stack]) -> gen_op(Rest, [ephp_data:to_bool(F)|Stack]); gen_op([{<<"(bool)">>,{_,_},Pos}|Rest], [A|Stack]) -> gen_op(Rest, [add_line(#cast{type=bool, content=A}, Pos)|Stack]); gen_op([{<<"(array)">>,{_,_},Pos}|Rest], [#int{line=DPos}=D|Stack]) -> gen_op(Rest, [add_to_array(#array{line=DPos}, Pos, D)|Stack]); gen_op([{<<"(array)">>,{_,_},Pos}|Rest], [#text{line=DPos}=D|Stack]) -> gen_op(Rest, [add_to_array(#array{line=DPos}, Pos, D)|Stack]); gen_op([{<<"(array)">>,{_,_},Pos}|Rest], [#float{line=DPos}=D|Stack]) -> gen_op(Rest, [add_to_array(#array{line=DPos}, Pos, D)|Stack]); gen_op([{<<"(array)">>,{_,_},Pos}|Rest], [A|Stack]) -> gen_op(Rest, [add_line(#cast{type=array, content=A}, Pos)|Stack]); gen_op([{<<"(object)">>,{_,_},Pos}|Rest], [A|Stack]) -> gen_op(Rest, [add_line(#cast{type=object, content=A}, Pos)|Stack]); gen_op([{<<"(unset)">>,{_,_},_Pos}|Rest], [_|Stack]) -> gen_op(Rest, [undefined|Stack]); gen_op([{<<"clone">>, {_, _}, Pos}|Rest], [A|Stack]) -> gen_op(Rest, [add_line(#clone{var = A}, Pos)|Stack]); % TODO add the rest of casting operators gen_op([{<<"?">>,{_,_},Pos}|Rest], [#operation{type = <<":">>}=OpElse,Cond|Stack]) -> #operation{ expression_left = TrueBlock, expression_right = FalseBlock } = OpElse, IfBlock = #if_block{ conditions = Cond, true_block = TrueBlock, false_block = FalseBlock, line = Pos }, gen_op(Rest, [IfBlock|Stack]); gen_op([{<<"?">>,{_,_},Pos}|_Rest], _Stack) -> throw_error(eparse, Pos, <<>>); gen_op([{Op,{_,_},Pos}|Rest], [B,A|Stack]) -> gen_op(Rest, [add_line(operator(Op,A,B),Pos)|Stack]); gen_op([A|Rest], Stack) -> gen_op(Rest, [A|Stack]). add_to_array(#array{elements=E}=Array, Pos, Element) -> Array#array{elements=E ++ [ add_line(#array_element{element=Element}, Pos) ]}. 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]).