-module(ephp_parser_class). -author('manuel@altenwald.com'). -compile([warnings_as_errors]). -export([st_class/3, st_interface/3]). -include("ephp.hrl"). -include("ephp_parser.hrl"). -import(ephp_parser, [ add_pos/2, new_line/1, remove_spaces/2 ]). st_interface(<>, Pos, Interface) when ?IS_SPACE(SP) -> st_interface(Rest, add_pos(Pos, 1), Interface); st_interface(<>, Pos, Interface) when ?IS_NEWLINE(SP) -> st_interface(Rest, new_line(Pos), Interface); st_interface(<>, Pos, #class{name=undefined}=I) when ?IS_ALPHA(A) orelse A =:= $_ -> {Rest0, Pos0, Name} = ephp_parser_func:funct_name(<>, Pos, []), st_interface(Rest0, Pos0, I#class{name = Name}); st_interface(<>, Pos, Interface) when ?OR(E,$E,$e) andalso ?OR(X,$X,$x) andalso ?OR(T,$T,$t) andalso ?OR(N,$N,$n) andalso ?OR(D,$D,$d) andalso ?OR(S,$S,$s) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> {Rest0, Pos0} = remove_spaces(<>, add_pos(Pos, 5)), {Rest1, Pos1, Extends} = ephp_parser_func:funct_name(Rest0, Pos0, []), st_interface(Rest1, Pos1, Interface#class{extends = Extends}); st_interface(<<"{", Rest/binary>>, Pos, Interface) -> st_interface_content(Rest, normal_public_level(add_pos(Pos, 1)), Interface). st_class(<>, Pos, Class) when ?IS_SPACE(SP) -> st_class(Rest, add_pos(Pos,1), Class); st_class(<>, Pos, Class) when ?IS_NEWLINE(SP) -> st_class(Rest, new_line(Pos), Class); st_class(<>, Pos, #class{name=undefined}=C) when ?IS_ALPHA(A) orelse A =:= $_ -> {Rest0, Pos0, Name} = ephp_parser_func:funct_name(<>, Pos, []), st_class(Rest0, Pos0, C#class{name=Name}); st_class(<>, Pos, Class) when ?OR(E,$E,$e) andalso ?OR(X,$X,$x) andalso ?OR(T,$T,$t) andalso ?OR(N,$N,$n) andalso ?OR(D,$D,$d) andalso ?OR(S,$S,$s) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> {Rest0, Pos0} = remove_spaces(<>, add_pos(Pos,5)), {Rest1, Pos1, Extends} = ephp_parser_func:funct_name(Rest0, Pos0, []), st_class(Rest1, Pos1, Class#class{extends=Extends}); st_class(<>, Pos, Class) when ?OR(I,$I,$i) andalso ?OR(M,$M,$m) andalso ?OR(P,$P,$p) andalso ?OR(L,$L,$l) andalso ?OR(E,$E,$e) andalso ?OR(N,$N,$n) andalso ?OR(T,$T,$t) andalso ?OR(S,$S,$s) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> {Rest0, Pos0} = remove_spaces(<>, add_pos(Pos,5)), {Rest1, Pos1, Implements} = st_implements(Rest0, Pos0, []), st_class(Rest1, Pos1, Class#class{implements=Implements}); st_class(<<"{",Rest/binary>>, Pos, Class) -> st_class_content(Rest, normal_public_level(add_pos(Pos,1)), Class). st_interface_content(<>, Pos, Interface) when ?IS_SPACE(SP) -> st_interface_content(Rest, add_pos(Pos, 1), Interface); st_interface_content(<>, Pos, Interface) when ?IS_NEWLINE(SP) -> st_interface_content(Rest, new_line(Pos), Interface); st_interface_content(<<"}", Rest/binary>>, Pos, Class) -> {Rest, add_pos(Pos, 1), Class}; st_interface_content(<<";", Rest/binary>>, Pos, Interface) -> st_interface_content(Rest, normal_public_level(add_pos(Pos, 1)), Interface); st_interface_content(<<"//",Rest/binary>>, Pos, Parsed) -> {Rest0, Pos0, _} = ephp_parser:comment_line(Rest, Pos, Parsed), st_interface_content(Rest0, Pos0, Parsed); st_interface_content(<<"#",Rest/binary>>, Pos, Parsed) -> {Rest0, Pos0, _} = ephp_parser:comment_line(Rest, Pos, Parsed), st_interface_content(Rest0, Pos0, Parsed); st_interface_content(<<"/*",Rest/binary>>, Pos, Parsed) -> {Rest0, Pos0, _} = ephp_parser:comment_block(Rest, Pos, Parsed), st_interface_content(Rest0, Pos0, Parsed); st_interface_content(<>, Pos, Interface) when ?OR(P,$P,$p) andalso ?OR(U,$U,$u) andalso ?OR(B,$B,$b) andalso ?OR(L,$L,$l) andalso ?OR(I,$I,$i) andalso ?OR(C,$C,$c) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> st_interface_content(<>, public_level(add_pos(Pos, 6)), Interface); st_interface_content(<>, Pos, Interface) when ?OR(P,$P,$p) andalso ?OR(R,$R,$r) andalso ?OR(I,$I,$i) andalso ?OR(V,$V,$v) andalso ?OR(A,$A,$a) andalso ?OR(T,$T,$t) andalso ?OR(E,$E,$e) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> st_interface_content(<>, private_level(add_pos(Pos, 7)), Interface); st_interface_content(<>, Pos, Interface) when ?OR(P,$P,$p) andalso ?OR(R,$R,$r) andalso ?OR(O,$O,$o) andalso ?OR(T,$T,$t) andalso ?OR(E,$E,$e) andalso ?OR(C,$C,$c) andalso ?OR(D,$D,$d) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> st_interface_content(<>, protected_level(add_pos(Pos, 9)), Interface); st_interface_content(<>, Pos, Class) when ?OR(S,$S,$s) andalso ?OR(T,$T,$t) andalso ?OR(A,$A,$a) andalso ?OR(I,$I,$i) andalso ?OR(C,$C,$c) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> st_interface_content(<>, static_level(add_pos(Pos,6)), Class); st_interface_content(<>, {{RawAccess, Type, Final}, _, _} = Pos, #class{methods = Methods} = Interface) 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) -> {Rest0, Pos0, [#function{}=Fun]} = ephp_parser_func:st_function(Rest, add_pos(Pos, 9), []), % TODO: if code is defined it should to throw an error or only ignore it? Method = #class_method{ name = Fun#function.name, args = Fun#function.args, type = Type, access = access(RawAccess), final = Final}, NewInterface = Interface#class{methods=Methods ++ [Method]}, st_interface_content(Rest0, normal_public_level(Pos0), NewInterface); st_interface_content(<>, Pos, #class{constants=Constants} = Interface) when ?OR(C,$c,$C) andalso ?OR(O,$o,$O) andalso ?OR(N,$n,$N) andalso ?OR(S,$s,$S) andalso ?OR(T,$t,$T) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> % FIXME assign when a left-constant should be converted in a simple constant {Rest0, Pos0, #assign{variable=#constant{}=Cons, expression=Value}} = ephp_parser_expr:expression(<>, add_pos(Pos,5), []), Constant = #class_const{name = Cons#constant.name, value = Value}, NewInterface = Interface#class{constants = Constants ++ [Constant]}, st_interface_content(Rest0, normal_public_level(Pos0), NewInterface). st_class_content(<>, Pos, Class) when ?IS_SPACE(SP) -> st_class_content(Rest, add_pos(Pos,1), Class); st_class_content(<>, Pos, Class) when ?IS_NEWLINE(SP) -> st_class_content(Rest, new_line(Pos), Class); st_class_content(<<"}",Rest/binary>>, Pos, Class) -> NewPos = add_pos(Pos, 1), {Rest, NewPos, ephp_parser:add_line(Class, NewPos)}; st_class_content(<<";",Rest/binary>>, Pos, Class) -> st_class_content(Rest, normal_public_level(add_pos(Pos, 1)), Class); st_class_content(<<"//",Rest/binary>>, Pos, Parsed) -> {Rest0, Pos0, _} = ephp_parser:comment_line(Rest, Pos, Parsed), st_class_content(Rest0, Pos0, Parsed); st_class_content(<<"#",Rest/binary>>, Pos, Parsed) -> {Rest0, Pos0, _} = ephp_parser:comment_line(Rest, Pos, Parsed), st_class_content(Rest0, Pos0, Parsed); st_class_content(<<"/*",Rest/binary>>, Pos, Parsed) -> {Rest0, Pos0, _} = ephp_parser:comment_block(Rest, Pos, Parsed), st_class_content(Rest0, Pos0, Parsed); st_class_content(<>, Pos, Class) when ?OR(P,$P,$p) andalso ?OR(U,$U,$u) andalso ?OR(B,$B,$b) andalso ?OR(L,$L,$l) andalso ?OR(I,$I,$i) andalso ?OR(C,$C,$c) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> st_class_content(<>, public_level(add_pos(Pos,6)), Class); st_class_content(<>, Pos, Class) when ?OR(P,$P,$p) andalso ?OR(R,$R,$r) andalso ?OR(I,$I,$i) andalso ?OR(V,$V,$v) andalso ?OR(A,$A,$a) andalso ?OR(T,$T,$t) andalso ?OR(E,$E,$e) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> st_class_content(<>, private_level(add_pos(Pos,7)), Class); st_class_content(<>, Pos, Class) when ?OR(P,$P,$p) andalso ?OR(R,$R,$r) andalso ?OR(O,$O,$o) andalso ?OR(T,$T,$t) andalso ?OR(E,$E,$e) andalso ?OR(C,$C,$c) andalso ?OR(D,$D,$d) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> st_class_content(<>, protected_level(add_pos(Pos,9)), Class); st_class_content(<>, Pos, Class) when ?OR(F,$F,$f) andalso ?OR(I,$I,$i) andalso ?OR(N,$N,$n) andalso ?OR(A,$A,$a) andalso ?OR(L,$L,$l) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> st_class_content(<>, final_level(add_pos(Pos,5)), Class); st_class_content(<>, Pos, Class) when ?OR(S,$S,$s) andalso ?OR(T,$T,$t) andalso ?OR(A,$A,$a) andalso ?OR(I,$I,$i) andalso ?OR(C,$C,$c) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> st_class_content(<>, static_level(add_pos(Pos,6)), Class); st_class_content(<>, Pos, Class) when ?OR(V,$V,$v) andalso ?OR(A,$A,$a) andalso ?OR(R,$R,$r) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> st_class_content(<>, public_level(add_pos(Pos,3)), Class); st_class_content(<<"$",_/binary>> = Rest, {{Access,Type,Final},_,_}=Pos, #class{attrs=Attrs}=Class) when Access =/= unset orelse Type =:= static -> Attr = case ephp_parser_expr:expression(Rest, Pos, []) of {Rest0, Pos0, #assign{variable=#variable{name=VarName}, expression=Expr}} -> #class_attr{ access = access(Access), name = VarName, type = Type, init_value = Expr, final = Final}; {Rest0, Pos0, #variable{name = VarName}} -> #class_attr{ access = access(Access), name = VarName, type = Type, final = Final} end, NewClass = Class#class{attrs=Attrs ++ [Attr]}, st_class_content(Rest0, normal_public_level(Pos0), NewClass); st_class_content(<<"$",_/binary>> = _Rest, Pos, _Class) -> ephp_parser:throw_error(eparse, Pos, {<<"`\"function (T_FUNCTION)\"'">>}); st_class_content(<>, {{RawAccess, Type, Final},_,_} = Pos, #class{methods = Methods} = Class) 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)) -> {Rest0, Pos0, [#function{}=Fun]} = ephp_parser_func:st_function(Rest, add_pos(Pos,9), []), Method = #class_method{ name = Fun#function.name, args = Fun#function.args, code = Fun#function.code, type = Type, access = access(RawAccess), final = Final}, NewClass = Class#class{methods=Methods ++ [Method]}, st_class_content(Rest0, normal_public_level(Pos0), NewClass); st_class_content(<>, Pos, #class{constants=Constants}=Class) when ?OR(C,$c,$C) andalso ?OR(O,$o,$O) andalso ?OR(N,$n,$N) andalso ?OR(S,$s,$S) andalso ?OR(T,$t,$T) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> % FIXME assign when a left-constant should be converted in a simple constant {Rest0, Pos0, #assign{variable=#constant{}=Cons, expression=Value}} = ephp_parser_expr:expression(<>, add_pos(Pos,5), []), Constant = #class_const{ name=Cons#constant.name, value=Value}, NewClass = Class#class{constants=Constants ++ [Constant]}, st_class_content(Rest0, normal_public_level(Pos0), NewClass); st_class_content(<>, Pos, Class) when ?OR(A,$A,$a) andalso ?OR(B,$B,$b) andalso ?OR(S,$S,$s) andalso ?OR(T,$T,$t) andalso ?OR(R,$R,$r) andalso ?OR(C,$C,$c) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) -> NewPos = abstract_level(add_pos(Pos,8)), st_class_content(<>, NewPos, Class). access(unset) -> public; access(Other) -> Other. st_implements(<>, Pos, Parsed) when ?IS_SPACE(SP) -> st_implements(Rest, add_pos(Pos,1), Parsed); st_implements(<>, Pos, Parsed) when ?IS_NEWLINE(SP) -> st_implements(Rest, new_line(Pos), Parsed); st_implements(<<",",Rest/binary>>, Pos, Parsed) -> st_implements(Rest, add_pos(Pos,1), Parsed); st_implements(<> = Rest, Pos, Parsed) when ?IS_ALPHA(A) -> {Rest0, Pos0, Name} = ephp_parser_func:funct_name(Rest, Pos, []), st_implements(Rest0, Pos0, [Name|Parsed]); st_implements(<<"{",_/binary>> = Rest, Pos, Parsed) -> {Rest, Pos, Parsed}. normal_public_level({_,Row,Col}) -> {{unset,normal,false},Row,Col}. public_level({{_,Type,Final},Row,Col}) -> {{public,Type,Final},Row,Col}. protected_level({{_,Type,Final},Row,Col}) -> {{protected,Type,Final},Row,Col}. private_level({{_,Type,Final},Row,Col}) -> {{private,Type,Final},Row,Col}. static_level({{Access,_,Final},Row,Col}) -> {{Access,static,Final},Row,Col}. abstract_level({{Access,_,Final},Row,Col}) -> {{Access,abstract,Final},Row,Col}. final_level({{Access,Type,_},Row,Col}) -> {{Access,Type,true},Row,Col}.