Current section

Files

Jump to
ephp src ephp_parser_class.erl
Raw

src/ephp_parser_class.erl

-module(ephp_parser_class).
-author('manuel@altenwald.com').
-export([st_class/3, st_interface/3, class_name/3]).
-include("ephp.hrl").
-include("ephp_parser.hrl").
-import(ephp_parser, [add_pos/2, new_line/1, remove_spaces/2, inc_pos/1]).
st_interface(<<SP:8, Rest/binary>>, Parser, Interface) when ?IS_SPACE(SP) ->
st_interface(Rest, inc_pos(Parser), Interface);
st_interface(<<SP:8, Rest/binary>>, Parser, Interface) when ?IS_NEWLINE(SP) ->
st_interface(Rest, new_line(Parser), Interface);
st_interface(<<A:8, Rest/binary>>, Parser, #class{name = undefined} = I)
when ?IS_ALPHA(A) orelse A =:= $_ ->
{Rest0, Parser0, {NS, Name}} = class_name(<<A:8, Rest/binary>>, Parser, []),
st_interface(Rest0, Parser0, I#class{name = Name, namespace = NS});
st_interface(<<E:8, X:8, T:8, E:8, N:8, D:8, S:8, SP:8, Rest/binary>>, Parser, 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, Parser0} = remove_spaces(<<SP:8, Rest/binary>>, add_pos(Parser, 5)),
{Rest1, Parser1, {NS, Extends}} = class_name(Rest0, Parser0, []),
st_interface(Rest1, Parser1, Interface#class{extends = Extends, extends_ns = NS});
st_interface(<<"{", Rest/binary>>, Parser, Interface) ->
st_interface_content(Rest, normal_public_level(inc_pos(Parser)), Interface).
st_class(<<SP:8, Rest/binary>>, Parser, Class) when ?IS_SPACE(SP) ->
st_class(Rest, inc_pos(Parser), Class);
st_class(<<SP:8, Rest/binary>>, Parser, Class) when ?IS_NEWLINE(SP) ->
st_class(Rest, new_line(Parser), Class);
st_class(<<A:8, Rest/binary>>, Parser, #class{name = undefined} = C)
when ?IS_ALPHA(A) orelse A =:= $_ ->
{Rest0, Parser0, {NS, Name}} = class_name(<<A:8, Rest/binary>>, Parser, []),
st_class(Rest0, Parser0, C#class{name = Name, namespace = NS});
st_class(<<$\\, _/binary>>, Parser, _Class) ->
ephp_parser:throw_error(eparse, Parser, {<<"\\">>, <<"T_NS_SEPARATOR">>, <<"{">>});
st_class(<<E:8, X:8, T:8, E:8, N:8, D:8, S:8, SP:8, Rest/binary>>, Parser, 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, Parser0} = remove_spaces(<<SP:8, Rest/binary>>, add_pos(Parser, 5)),
{Rest1, Parser1, {NS, Extends}} = class_name(Rest0, Parser0, []),
st_class(Rest1, Parser1, Class#class{extends = Extends, extends_ns = NS});
st_class(<<I:8, M:8, P:8, L:8, E:8, M:8, E:8, N:8, T:8, S:8, SP:8, Rest/binary>>,
Parser,
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, Parser0} = remove_spaces(<<SP:8, Rest/binary>>, add_pos(Parser, 5)),
{Rest1, Parser1, Implements} = st_implements(Rest0, Parser0, []),
st_class(Rest1, Parser1, Class#class{implements = Implements});
st_class(<<"{", Rest/binary>>, Parser, Class) ->
st_class_content(Rest, normal_public_level(inc_pos(Parser)), Class).
st_interface_content(<<SP:8, Rest/binary>>, Parser, Interface) when ?IS_SPACE(SP) ->
st_interface_content(Rest, inc_pos(Parser), Interface);
st_interface_content(<<SP:8, Rest/binary>>, Parser, Interface) when ?IS_NEWLINE(SP) ->
st_interface_content(Rest, new_line(Parser), Interface);
st_interface_content(<<"}", Rest/binary>>, Parser, Class) ->
{Rest, inc_pos(Parser), Class};
st_interface_content(<<";", Rest/binary>>, Parser, Interface) ->
st_interface_content(Rest, normal_public_level(inc_pos(Parser)), Interface);
st_interface_content(<<"//", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = ephp_parser:comment_line(Rest, Parser, Parsed),
st_interface_content(Rest0, Parser0, Parsed);
st_interface_content(<<"#", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = ephp_parser:comment_line(Rest, Parser, Parsed),
st_interface_content(Rest0, Parser0, Parsed);
st_interface_content(<<"/*", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = ephp_parser:comment_block(Rest, Parser, Parsed),
st_interface_content(Rest0, Parser0, Parsed);
st_interface_content(<<P:8, U:8, B:8, L:8, I:8, C:8, SP:8, Rest/binary>>,
Parser,
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(<<SP:8, Rest/binary>>, public_level(add_pos(Parser, 6)), Interface);
st_interface_content(<<P:8, R:8, I:8, V:8, A:8, T:8, E:8, SP:8, Rest/binary>>,
Parser,
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(<<SP:8, Rest/binary>>, private_level(add_pos(Parser, 7)), Interface);
st_interface_content(<<P:8, R:8, O:8, T:8, E:8, C:8, T:8, E:8, D:8, SP:8, Rest/binary>>,
Parser,
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(<<SP:8, Rest/binary>>,
protected_level(add_pos(Parser, 9)),
Interface);
st_interface_content(<<S:8, T:8, A:8, T:8, I:8, C:8, SP:8, Rest/binary>>, Parser, 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(<<SP:8, Rest/binary>>, static_level(add_pos(Parser, 6)), Class);
st_interface_content(<<F:8, U:8, N:8, C:8, T:8, I:8, O:8, N:8, SP:8, Rest/binary>>,
#parser{level = Type,
access = RawAccess,
final = Final} =
Parser,
#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, Parser0, [#function{} = Fun]} =
ephp_parser_func:st_function(Rest, add_pos(Parser, 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(Parser0), NewInterface);
st_interface_content(<<C:8, O:8, N:8, S:8, T:8, SP:8, Rest/binary>>,
Parser,
#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, Parser0, #assign{variable = #constant{} = Cons, expression = Value}} =
ephp_parser_expr:expression(<<SP:8, Rest/binary>>, add_pos(Parser, 5), []),
Constant = #class_const{name = Cons#constant.name, value = Value},
NewInterface = Interface#class{constants = Constants ++ [Constant]},
st_interface_content(Rest0, normal_public_level(Parser0), NewInterface).
st_class_content(<<SP:8, Rest/binary>>, Parser, Class) when ?IS_SPACE(SP) ->
st_class_content(Rest, inc_pos(Parser), Class);
st_class_content(<<SP:8, Rest/binary>>, Parser, Class) when ?IS_NEWLINE(SP) ->
st_class_content(Rest, new_line(Parser), Class);
st_class_content(<<"}", Rest/binary>>, Parser, Class) ->
NewParser = inc_pos(Parser),
{Rest, NewParser, ephp_parser:add_line(Class, NewParser)};
st_class_content(<<";", Rest/binary>>, Parser, Class) ->
st_class_content(Rest, normal_public_level(inc_pos(Parser)), Class);
st_class_content(<<"//", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = ephp_parser:comment_line(Rest, Parser, Parsed),
st_class_content(Rest0, Parser0, Parsed);
st_class_content(<<"#", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = ephp_parser:comment_line(Rest, Parser, Parsed),
st_class_content(Rest0, Parser0, Parsed);
st_class_content(<<"/*", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = ephp_parser:comment_block(Rest, Parser, Parsed),
st_class_content(Rest0, Parser0, Parsed);
st_class_content(<<P:8, U:8, B:8, L:8, I:8, C:8, SP:8, Rest/binary>>, Parser, 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(<<SP:8, Rest/binary>>, public_level(add_pos(Parser, 6)), Class);
st_class_content(<<P:8, R:8, I:8, V:8, A:8, T:8, E:8, SP:8, Rest/binary>>, Parser, 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(<<SP:8, Rest/binary>>, private_level(add_pos(Parser, 7)), Class);
st_class_content(<<P:8, R:8, O:8, T:8, E:8, C:8, T:8, E:8, D:8, SP:8, Rest/binary>>,
Parser,
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(<<SP:8, Rest/binary>>, protected_level(add_pos(Parser, 9)), Class);
st_class_content(<<F:8, I:8, N:8, A:8, L:8, SP:8, Rest/binary>>, Parser, 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(<<SP:8, Rest/binary>>, final_level(add_pos(Parser, 5)), Class);
st_class_content(<<S:8, T:8, A:8, T:8, I:8, C:8, SP:8, Rest/binary>>, Parser, 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(<<SP:8, Rest/binary>>, static_level(add_pos(Parser, 6)), Class);
st_class_content(<<V:8, A:8, R:8, SP:8, Rest/binary>>, Parser, 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(<<SP:8, Rest/binary>>, public_level(add_pos(Parser, 3)), Class);
st_class_content(<<"$", _/binary>> = Rest,
#parser{level = Type,
access = Access,
final = Final} =
Parser,
#class{attrs = Attrs} = Class)
when Access =/= undefined orelse Type =:= static ->
Attr =
case ephp_parser_expr:expression(Rest, Parser, []) of
{Rest0, Parser0, #assign{variable = #variable{name = VarName}, expression = Expr}} ->
#class_attr{access = access(Access),
name = VarName,
type = Type,
init_value = Expr,
final = Final,
class_name = Class#class.name,
namespace = Class#class.namespace};
{Rest0, Parser0, #variable{name = VarName}} ->
#class_attr{access = access(Access),
name = VarName,
type = Type,
final = Final,
class_name = Class#class.name,
namespace = Class#class.namespace}
end,
NewClass = Class#class{attrs = Attrs ++ [Attr]},
case remove_spaces(Rest0, Parser0) of
{<<",", Rest1/binary>>, Parser1} ->
st_class_content(Rest1, Parser1, NewClass);
{Rest1, Parser1} ->
st_class_content(Rest1, normal_public_level(Parser1), NewClass)
end;
st_class_content(<<"$", _/binary>> = _Rest, Parser, _Class) ->
ephp_parser:throw_error(eparse, Parser, {<<"`\"function (T_FUNCTION)\"'">>});
st_class_content(<<F:8, U:8, N:8, C:8, T:8, I:8, O:8, N:8, SP:8, Rest/binary>>,
#parser{level = Type,
access = RawAccess,
final = Final} =
Parser,
#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, Parser0, [#function{} = Fun]} =
ephp_parser_func:st_function(Rest, add_pos(Parser, 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(Parser0), NewClass);
st_class_content(<<C:8, O:8, N:8, S:8, T:8, SP:8, Rest/binary>>,
Parser,
#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, Parser0, #assign{variable = #constant{} = Cons, expression = Value}} =
ephp_parser_expr:expression(<<SP:8, Rest/binary>>, add_pos(Parser, 5), []),
Constant = #class_const{name = Cons#constant.name, value = Value},
NewClass = Class#class{constants = Constants ++ [Constant]},
st_class_content(Rest0, normal_public_level(Parser0), NewClass);
st_class_content(<<A:8, B:8, S:8, T:8, R:8, A:8, C:8, T:8, SP:8, Rest/binary>>,
Parser,
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)) ->
NewParser = abstract_level(add_pos(Parser, 8)),
st_class_content(<<SP:8, Rest/binary>>, NewParser, Class).
access(undefined) ->
public;
access(Other) ->
Other.
get_ns({[<<>>], Name}, _Parser) ->
{[], Name};
get_ns({NS, Name}, Parser) ->
RawNS = ephp_parser:get_ns(NS ++ [Name], Parser),
ephp_ns:split(RawNS).
class_name(<<A:8, Rest/binary>>, Parser, [])
when ?IS_ALPHA(A) orelse A =:= $_ orelse A =:= $\\ ->
class_name(Rest, inc_pos(Parser), [<<A:8>>]);
class_name(<<A:8, Rest/binary>>, Parser, [N])
when ?IS_ALPHA(A) orelse ?IS_NUMBER(A) orelse A =:= $_ orelse A =:= $\\ ->
class_name(Rest, inc_pos(Parser), [<<N/binary, A:8>>]);
class_name(Rest, Parser, [Parsed]) ->
ProcessedParsed = get_ns(ephp_ns:parse(Parsed), Parser),
{Rest, Parser, ProcessedParsed}.
st_implements(<<SP:8, Rest/binary>>, Parser, Parsed) when ?IS_SPACE(SP) ->
st_implements(Rest, inc_pos(Parser), Parsed);
st_implements(<<SP:8, Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(SP) ->
st_implements(Rest, new_line(Parser), Parsed);
st_implements(<<",", Rest/binary>>, Parser, Parsed) ->
st_implements(Rest, inc_pos(Parser), Parsed);
st_implements(<<A:8, _/binary>> = Rest, Parser, Parsed) when ?IS_ALPHA(A) ->
{Rest0, Parser0, {NS, Name}} = class_name(Rest, Parser, []),
st_implements(Rest0, Parser0, [{NS, Name} | Parsed]);
st_implements(<<"{", _/binary>> = Rest, Parser, Parsed) ->
{Rest, Parser, Parsed}.
normal_public_level(Parser) ->
Parser#parser{level = normal,
final = false,
access = undefined}.
public_level(Parser) ->
Parser#parser{access = public}.
protected_level(Parser) ->
Parser#parser{access = protected}.
private_level(Parser) ->
Parser#parser{access = private}.
static_level(Parser) ->
Parser#parser{level = static}.
abstract_level(Parser) ->
Parser#parser{level = abstract}.
final_level(Parser) ->
Parser#parser{final = true}.