Current section
Files
Jump to
Current section
Files
src/ephp_parser.erl
-module(ephp_parser).
-author('manuel@altenwald.com').
-compile([warnings_as_errors]).
-export([parse/1, parse/2, file/1]).
-export([
add_pos/2, new_line/1, copy_rowcol/2, add_line/2, remove_spaces/2,
throw_error/3, code_block/3, code/3, new_line/2, inc_pos/1,
get_ns/2,
array_def_level/1, code_statement_level/1, arg_level/1,
code_block_level/1, enclosed_level/1, unclosed_level/1,
get_line/1,
variable/3, comment_line/3, comment_block/3, constant/3
]).
-include("ephp.hrl").
-include("ephp_parser.hrl").
-import(ephp_parser_expr, [
expression/3, add_op/2, precedence/1
]).
file(File) ->
{ok, Content} = file:read_file(File),
parse(File, Content).
parse(Document) ->
parse(<<"-">>, Document).
parse(File, Document) when is_list(Document) ->
parse(File, list_to_binary(Document));
parse(File, Document) ->
AbsFilename = filename:absname(File),
Parser = #parser{filename = AbsFilename},
{_Rest, _Parser, Parsed} = document(Document, Parser, []),
lists:reverse(Parsed).
document(<<>>, Parser, Parsed) ->
{<<>>, Parser, Parsed};
document(<<"<?php",Rest/binary>>, #parser{level = literal} = Parser, Parsed) ->
{Rest, add_pos(Parser, 5), Parsed};
document(<<"<?php",Rest/binary>>, Parser, Parsed) ->
Parser0 = normal_level(add_pos(Parser, 5)),
{Rest1, Parser1, NParsed} = code_ns(Rest, Parser0, []),
case NParsed of
[] ->
document(Rest1, Parser1, Parsed);
_ ->
Eval = add_line(#eval{statements = lists:reverse(NParsed)}, Parser),
document(Rest1, Parser1, [Eval|Parsed])
end;
document(<<"<?=", Rest/binary>>, Parser, Parsed) ->
NewParser = code_value_level(add_pos(Parser, 3)),
{Rest0, Parser0, Text} = code_ns(Rest, NewParser, []),
document(Rest0, copy_rowcol(Parser0, Parser), [get_print(Text, NewParser)|Parsed]);
document(<<"<?", Rest/binary>>, #parser{level = literal} = Parser, Parsed) ->
%% TODO: if short is not permitted, use as text
{Rest, add_pos(Parser, 2), Parsed};
document(<<"<?", Rest/binary>>, Parser, Parsed) ->
%% TODO: if short is not permitted, use as text
{Rest0, Parser0, NParsed} = code_ns(Rest, normal_level(add_pos(Parser, 2)), []),
case NParsed of
[] ->
document(Rest0, Parser0, Parsed);
_ ->
Eval = add_line(#eval{statements = lists:reverse(NParsed)}, Parser),
document(Rest0, Parser0, [Eval|Parsed])
end;
document(<<"\n",Rest/binary>>, Parser, Parsed) ->
Parser1 = new_line(maybe_set_namespace(Parser)),
document(Rest, Parser1, add_to_text(<<"\n">>, Parser1, Parsed));
document(<<L:1/binary,Rest/binary>>, Parser, Parsed) ->
Parser1 = inc_pos(maybe_set_namespace(Parser)),
document(Rest, Parser1, add_to_text(L, Parser1, Parsed)).
maybe_set_namespace(#parser{namespace_can_be = true} = Parser) ->
Parser#parser{namespace_can_be = false};
maybe_set_namespace(#parser{namespace_can_be = only_block} = Parser) ->
throw_error(enamespaceblock, Parser, ?E_ERROR, undefined);
maybe_set_namespace(Parser) -> Parser.
code_ns(<<N:8,A:8,M:8,E:8,S:8,P:8,A:8,C:8,E:8,SP:8,Rest/binary>>,
#parser{namespace = [], namespace_can_be = NScanBe} = Parser,
Parsed) when
?OR(N,$N,$n) andalso ?OR(A,$A,$a) andalso ?OR(M,$M,$m) andalso
?OR(E,$E,$e) andalso ?OR(S,$S,$s) andalso ?OR(P,$P,$p) andalso
?OR(C,$C,$c) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) andalso
NScanBe =/= false ->
{Rest0, Parser0, NameSpace} = namespace(<<SP:8, Rest/binary>>,
add_pos(Parser, 9), []),
case remove_spaces(Rest0, Parser0#parser{namespace = NameSpace,
namespace_can_be = false}) of
{<<";", _/binary>> = Rest1, Parser1} when NScanBe =:= true
orelse NScanBe =:= only_statement ->
code(Rest1,
Parser1#parser{namespace_can_be = only_statement},
Parsed);
{<<";", _/binary>>, Parser1} when NScanBe =:= only_block ->
throw_error(enamespaceblock, Parser1, ?E_ERROR, undefined);
{<<"{", _/binary>>, Parser1} when NScanBe =:= only_statement ->
throw_error(enamespacemix, Parser1, ?E_ERROR, undefined);
{<<"{", _/binary>> = Rest1, Parser1} when NScanBe =:= only_block
orelse NScanBe =:= true ->
{Rest2, Parser2, CodeBlock} = code_block(Rest1, Parser1, []),
Parser2_1 = Parser2#parser{namespace = [],
namespace_can_be = only_block},
code_ns(Rest2, Parser2_1, lists:reverse(CodeBlock) ++ Parsed)
end;
code_ns(<<"//", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = comment_line(Rest, add_pos(Parser, 2), Parsed),
code_ns(Rest0, Parser0, Parsed);
code_ns(<<"#", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = comment_line(Rest, inc_pos(Parser), Parsed),
code_ns(Rest0, Parser0, Parsed);
code_ns(<<"/*", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = comment_block(Rest, add_pos(Parser, 2), Parsed),
code_ns(Rest0, Parser0, Parsed);
code_ns(<<Space:8,Rest/binary>>, Parser, Parsed) when ?IS_SPACE(Space) ->
code_ns(Rest, inc_pos(Parser), Parsed);
code_ns(<<NewLine:8,Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(NewLine) ->
code_ns(Rest, new_line(Parser), Parsed);
code_ns(Rest, Parser, Parsed) ->
code(Rest, Parser, Parsed).
code(<<"{", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, Parsed0} = code(Rest, code_block_level(add_pos(Parser, 2)), []),
code(Rest0, Parser0, Parsed0 ++ Parsed);
code(<<B:8,R:8,E:8,A:8,K:8,SP:8,Rest/binary>>, Parser, Parsed) when
?OR(B,$B,$b) andalso ?OR(R,$R,$r) andalso ?OR(E,$E,$e) andalso
?OR(A,$A,$a) andalso ?OR(K,$K,$k) andalso
(not (?IS_SPACE(SP) orelse ?IS_NUMBER(SP) orelse SP =:= $_)) ->
code(<<SP:8,Rest/binary>>, add_pos(Parser, 5), [break|Parsed]);
code(<<C:8,O:8,N:8,T:8,I:8,N:8,U:8,E:8,SP:8,Rest/binary>>, Parser, Parsed) when
?OR(C,$C,$c) andalso ?OR(O,$O,$o) andalso ?OR(N,$N,$n) andalso
?OR(T,$T,$t) andalso ?OR(I,$I,$i) andalso ?OR(U,$U,$u) andalso
?OR(E,$E,$e) andalso (not (?IS_SPACE(SP) orelse ?IS_NUMBER(SP))) ->
code(<<SP:8,Rest/binary>>, add_pos(Parser, 8), [continue|Parsed]);
code(<<R:8,E:8,T:8,U:8,R:8,N:8,SP:8,Rest/binary>>, Parser, Parsed) when
?OR(R,$R,$r) andalso ?OR(E,$E,$e) andalso ?OR(T,$T,$t) andalso
?OR(U,$U,$u) andalso ?OR(N,$N,$n) andalso
(not (?IS_ALPHA(SP) orelse ?IS_NUMBER(SP))) ->
{Rest0, Parser0, Return} = expression(<<SP:8,Rest/binary>>, add_pos(Parser, 6), []),
case Return of
[] -> code(Rest0, Parser0, [add_line(#return{}, Parser)|Parsed]);
_ -> code(Rest0, Parser0, [add_line(#return{value = Return}, Parser)|Parsed])
end;
code(<<T:8,H:8,R:8,O:8,W:8,SP:8,Rest/binary>>, Parser, Parsed) when
?OR(T,$T,$t) andalso ?OR(H,$H,$h) andalso ?OR(R,$R,$r) andalso
?OR(O,$O,$o) andalso ?OR(W,$W,$w) andalso
(not (?IS_ALPHA(SP) orelse ?IS_NUMBER(SP) orelse SP =:= $_)) ->
{Rest0, Parser0, Throw} = expression(<<SP:8, Rest/binary>>, add_pos(Parser, 5), []),
case Throw of
[] -> throw_error(eparse, Parser0, Rest);
_ -> code(Rest0, Parser0, [add_line(#throw{value = Throw}, Parser)|Parsed])
end;
code(<<T:8,R:8,Y:8,SP:8,Rest/binary>>, Parser, Parsed) when
?OR(T,$T,$t) andalso ?OR(R,$R,$r) andalso ?OR(Y,$Y,$y) andalso
(not (?IS_ALPHA(SP) orelse ?IS_NUMBER(SP) orelse SP =:= $_)) ->
{Rest0, Parser0, Code} = code_block(<<SP:8, Rest/binary>>, add_pos(Parser, 3), []),
code(Rest0, copy_rowcol(Parser0, Parser),
[add_line(#try_catch{code_block = Code}, Parser)|Parsed]);
%% TODO catch must be part of try, couldn't be separate!
code(<<C:8,A:8,T:8,C:8,H:8,SP:8,Rest/binary>>, Parser, [#try_catch{} = Try|Parsed])
when ?OR(C,$C,$c) andalso ?OR(A,$A,$a) andalso ?OR(T,$T,$t)
andalso ?OR(H,$H,$h) andalso
(not (?IS_ALPHA(SP) orelse ?IS_NUMBER(SP) orelse SP =:= $_)) ->
{<<"(", Rest0/binary>>, Parser0} =
remove_spaces(<<SP:8,Rest/binary>>, add_pos(Parser, 5)),
{Rest1, Parser1, [Arg]} = ephp_parser_func:funct_args(Rest0, inc_pos(Parser0), []),
{Rest2, Parser2, Code} = code_block(Rest1, Parser1, []),
#try_catch{catches = Catches} = Try,
NewCatch = add_line(#catch_block{exception = Arg, code_block = Code}, Parser),
NewTry = Try#try_catch{catches = Catches ++ [NewCatch]},
code(Rest2, copy_rowcol(Parser2, Parser), [NewTry|Parsed]);
%% TODO finally must be part of try, couldn't be separate!
code(<<F:8,I:8,N:8,A:8,L:8,L:8,Y:8,SP:8,Rest/binary>>, Parser,
[#try_catch{} = Try|Parsed]) 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 ?OR(Y,$Y,$y) andalso
(not (?IS_ALPHA(SP) orelse ?IS_NUMBER(SP) orelse SP =:= $_)) ->
{Rest0, Parser0, Code} = code_block(<<SP:8, Rest/binary>>, add_pos(Parser, 7), []),
code(Rest0, copy_rowcol(Parser0, Parser), [Try#try_catch{finally = Code}|Parsed]);
code(<<"@", _/binary>> = Rest, Parser, Parsed) ->
{Rest0, Parser0, Exp} = expression(Rest, code_statement_level(Parser), []),
code(Rest0, copy_rowcol(Parser0, Parser), [Exp|Parsed]);
code(<<G:8,L:8,O:8,B:8,A:8,L:8,SP:8,Rest/binary>>, Parser, Parsed) when
?OR(G,$G,$g) andalso ?OR(L,$L,$l) andalso ?OR(O,$O,$o) andalso
?OR(B,$B,$b) andalso ?OR(A,$A,$a) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
{Rest0, Parser0} = remove_spaces(Rest, add_pos(Parser, 7)),
{Rest1, Parser1, [Global]} = st_global(Rest0, Parser0, []),
code(Rest1, copy_rowcol(Parser1, Parser), [Global|Parsed]);
code(<<"}",Rest/binary>>, #parser{level = code_block} = Parser, Parsed) ->
{Rest, inc_pos(Parser), lists:reverse(Parsed)};
code(<<"}",Rest/binary>>, #parser{level = switch_block} = Parser, Parsed) ->
{Rest, inc_pos(Parser), lists:reverse(switch_case_block(Parsed))};
code(<<E:8,N:8,D:8,I:8,F:8,SP:8,Rest/binary>>, #parser{level = if_old_block} = Parser, Parsed)
when
?OR(E,$E,$e) andalso ?OR(N,$N,$n) andalso ?OR(D,$D,$d) andalso
?OR(I,$I,$i) andalso ?OR(F,$F,$f) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $;) ->
{Rest0, Parser0} = remove_spaces(<<SP:8,Rest/binary>>, add_pos(Parser, 5)),
{Rest0, Parser0, lists:reverse(Parsed)};
code(<<E:8,N:8,D:8,S:8,W:8,I:8,T:8,C:8,H:8,SP:8,Rest/binary>>,
#parser{level = switch_old_block} = Parser, Parsed) when
?OR(E,$E,$e) andalso ?OR(N,$N,$n) andalso ?OR(D,$D,$d) andalso
?OR(S,$S,$s) andalso ?OR(W,$W,$w) andalso ?OR(I,$I,$i) andalso
?OR(T,$T,$t) andalso ?OR(C,$C,$c) andalso ?OR(H,$H,$h) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $;) ->
{Rest0, Parser0} = remove_spaces(<<SP:8, Rest/binary>>, add_pos(Parser, 9)),
{Rest0, Parser0, lists:reverse(switch_case_block(Parsed))};
code(<<E:8,N:8,D:8,F:8,O:8,R:8,E:8,A:8,C:8,H:8,SP:8,Rest/binary>>,
#parser{level = foreach_old_block} = Parser, Parsed) when
?OR(E,$E,$e) andalso ?OR(N,$N,$n) andalso ?OR(D,$D,$d) andalso
?OR(F,$F,$f) andalso ?OR(O,$O,$o) andalso ?OR(R,$R,$r) andalso
?OR(A,$A,$a) andalso ?OR(C,$C,$c) andalso ?OR(H,$H,$h) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $;) ->
{Rest0, Parser0} = remove_spaces(<<SP:8,Rest/binary>>, add_pos(Parser, 10)),
{Rest0, Parser0, lists:reverse(Parsed)};
code(<<E:8,N:8,D:8,F:8,O:8,R:8,SP:8,Rest/binary>>,
#parser{level = for_old_block} = Parser, Parsed) when
?OR(E,$E,$e) andalso ?OR(N,$N,$n) andalso ?OR(D,$D,$d) andalso
?OR(F,$F,$f) andalso ?OR(O,$O,$o) andalso ?OR(R,$R,$r) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $;) ->
{Rest0, Parser0} = remove_spaces(<<SP:8,Rest/binary>>, add_pos(Parser, 6)),
{Rest0, Parser0, lists:reverse(Parsed)};
code(<<E:8,N:8,D:8,W:8,H:8,I:8,L:8,E:8,SP:8,Rest/binary>>,
#parser{level = while_old_block} = Parser, Parsed) when
?OR(E,$E,$e) andalso ?OR(N,$N,$n) andalso ?OR(D,$D,$d) andalso
?OR(W,$W,$w) andalso ?OR(H,$H,$h) andalso ?OR(I,$I,$i) andalso
?OR(L,$L,$l) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $;) ->
{Rest0, Parser0} = remove_spaces(<<SP:8,Rest/binary>>, add_pos(Parser, 8)),
{Rest0, Parser0, lists:reverse(Parsed)};
code(<<A:8,_/binary>> = Rest, #parser{level = code_statement} = Parser, Parsed)
when A =:= $; orelse A =:= $} ->
{Rest, Parser, Parsed};
code(<<T:8,R:8,U:8,E:8,SP:8,Rest/binary>>, Parser, Parsed)
when ?OR(T,$t,$T) andalso ?OR(R,$r,$R) andalso ?OR(U,$u,$U)
andalso ?OR(E,$e,$E) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
{Rest0, Parser0, Exp} = expression(Rest, Parser, add_op(true, [])),
code(Rest0, copy_rowcol(Parser0, Parser), [Exp|Parsed]);
code(<<F:8,A:8,L:8,S:8,E:8,SP:8,Rest/binary>>, 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 (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
{Rest0, Parser0, Exp} = expression(Rest, Parser, add_op(false, [])),
code(Rest0, copy_rowcol(Parser0, Parser), [Exp|Parsed]);
code(<<I:8,F:8,SP:8,Rest/binary>>, Parser, Parsed)
when ?OR(I,$i,$I) andalso ?OR(F,$f,$F)
andalso (?IS_SPACE(SP) orelse SP =:= $() ->
{Rest0, Parser0} = remove_spaces(<<SP:8,Rest/binary>>, Parser),
{Rest1, Parser1, [If]} = st_if(Rest0, Parser0, []),
code(Rest1, copy_rowcol(Parser1, Parser), [If|Parsed]);
code(<<W:8,H:8,I:8,L:8,E:8,SP:8,Rest/binary>>, Parser, Parsed)
when ?OR(W,$w,$W) andalso ?OR(H,$h,$H) andalso ?OR(I,$i,$I)
andalso ?OR(L,$l,$L) andalso ?OR(E,$e,$E)
andalso (?IS_SPACE(SP) orelse SP =:= $() ->
{Rest0, Parser0} = remove_spaces(<<SP:8,Rest/binary>>, add_pos(Parser, 5)),
{Rest1, Parser1, NewParsed} = st_while(Rest0, Parser0, Parsed),
code(Rest1, copy_rowcol(Parser1, Parser), NewParsed);
code(<<D:8,O:8,SP:8,Rest/binary>>, Parser, Parsed)
when ?OR(D,$d,$D) andalso ?OR(O,$o,$O) andalso
(?IS_SPACE(SP) orelse ?OR(SP,${,$:) orelse ?IS_NEWLINE(SP)) ->
{Rest0, Parser0, [DoWhile]} = st_do_while(Rest, add_pos(Parser, 3), []),
code(Rest0, copy_rowcol(Parser0, Parser), [DoWhile|Parsed]);
code(<<F:8,O:8,R:8,E:8,A:8,C:8,H:8,SP:8,Rest/binary>>, Parser, Parsed)
when ?OR(F,$f,$F) andalso ?OR(O,$o,$O) andalso ?OR(R,$r,$R)
andalso ?OR(E,$e,$E) andalso ?OR(A,$a,$A) andalso ?OR(C,$c,$C)
andalso ?OR(H,$h,$H)
andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $() ->
{Rest0, Parser0} = remove_spaces(<<SP:8, Rest/binary>>, Parser),
{Rest1, Parser1, NewParsed} = st_foreach(Rest0, Parser0, Parsed),
code(Rest1, copy_rowcol(Parser1, Parser), NewParsed);
code(<<F:8,O:8,R:8,SP:8,Rest/binary>>, Parser, Parsed)
when ?OR(F,$f,$F) andalso ?OR(O,$o,$O) andalso ?OR(R,$r,$R)
andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $() ->
{Rest0, Parser0} = remove_spaces(<<SP:8,Rest/binary>>, Parser),
{Rest1, Parser1, NewParsed} = st_for(Rest0, Parser0, Parsed),
code(Rest1, copy_rowcol(Parser1, Parser), NewParsed);
code(<<E:8,L:8,S:8,E:8,SP:8,_/binary>> = Rest, #parser{level = if_old_block} = Parser, Parsed)
when ?OR(E,$e,$E) andalso ?OR(L,$l,$L) andalso ?OR(S,$s,$S)
andalso (SP =:= $: orelse ?IS_SPACE(SP) orelse ?OR(SP,$i,$I)) ->
{Rest, Parser, Parsed};
code(<<S:8,W:8,I:8,T:8,C:8,H:8,SP:8,Rest/binary>>, Parser, Parsed) when
?OR(S,$S,$s) andalso ?OR(W,$W,$w) andalso ?OR(I,$I,$i) andalso
?OR(T,$T,$t) andalso ?OR(C,$C,$c) andalso ?OR(H,$H,$h) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $() ->
{<<"(",_/binary>> = Rest0, Parser0} = remove_spaces(<<SP:8,Rest/binary>>, Parser),
{Rest1, Parser1, NewParsed} = st_switch(Rest0, add_pos(Parser0, 6), Parsed),
code(Rest1, copy_rowcol(Parser1, Parser), NewParsed);
code(<<C:8,A:8,S:8,E:8,SP:8,Rest/binary>>, #parser{level = Level} = Parser, Parsed) when
(Level =:= switch_block orelse Level =:= switch_old_block) andalso
?OR(C,$C,$c) andalso ?OR(A,$A,$a) andalso ?OR(S,$S,$s) andalso
?OR(E,$E,$e) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
{Rest0, Parser0} = remove_spaces(<<SP:8,Rest/binary>>, add_pos(Parser, 4)),
NewParser = switch_label_level(Parser0),
case expression(Rest0, NewParser, []) of
{<<":", Rest1/binary>>, Parser1, Exp} -> ok;
{<<";", Rest1/binary>>, Parser1, Exp} -> ok
end,
NewParsed = [add_line(#switch_case{label = Exp, code_block = []}, Parser)|
switch_case_block(Parsed)],
code(Rest1, copy_rowcol(inc_pos(Parser1), Parser), NewParsed);
code(<<C:8,A:8,S:8,E:8,SP:8,Rest/binary>>, #parser{level = code_statement} = Parser, Parsed) when
?OR(C,$C,$c) andalso ?OR(A,$A,$a) andalso ?OR(S,$S,$s) andalso
?OR(E,$E,$e) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
{<<"case",SP:8,Rest/binary>>, Parser, Parsed};
code(<<D:8,E:8,F:8,A:8,U:8,L:8,T:8,SP:8,Rest/binary>>,
#parser{level = Level} = Parser, Parsed) when
(Level =:= switch_block orelse Level =:= switch_old_block) andalso
?OR(D,$D,$d) andalso ?OR(E,$E,$e) andalso ?OR(F,$F,$f) andalso
?OR(A,$A,$a) andalso ?OR(U,$U,$u) andalso ?OR(L,$L,$l) andalso
?OR(T,$T,$t) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $:) ->
case remove_spaces(<<SP:8, Rest/binary>>, add_pos(Parser, 4)) of
{<<":", Rest0/binary>>, Parser0} -> ok;
{<<";", Rest0/binary>>, Parser0} -> ok
end,
NewParsed = [add_line(#switch_case{label = default,
code_block = []},
Parser)|switch_case_block(Parsed)],
code(Rest0, copy_rowcol(inc_pos(Parser0), Parser), NewParsed);
code(<<D:8,E:8,F:8,A:8,U:8,L:8,T:8,SP:8,Rest/binary>>,
#parser{level = code_statement} = Parser, Parsed) when
?OR(D,$D,$d) andalso ?OR(E,$E,$e) andalso ?OR(F,$F,$f) andalso
?OR(A,$A,$a) andalso ?OR(U,$U,$u) andalso ?OR(L,$L,$l) andalso
?OR(T,$T,$t) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP) orelse SP =:= $:) ->
{<<"default",SP:8,Rest/binary>>, Parser, Parsed};
code(<<A:8,B:8,S:8,T:8,R:8,A:8,C:8,T:8,SP:8,Rest/binary>>, Parser, Parsed) 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)) ->
{Rest0, Parser0, Parsed0} = code(Rest, add_pos(Parser, 9), []),
Class = lists:last(Parsed0),
{Rest0, Parser0, Parsed0 ++ [Class#class{type = abstract}] ++ Parsed};
code(<<F:8,I:8,N:8,A:8,L:8,SP:8,Rest/binary>>, Parser, Parsed) 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)) ->
Class = add_line(#class{final = true}, Parser),
code(<<SP:8, Rest/binary>>, add_pos(Parser, 5), [Class|Parsed]);
code(<<C:8,L:8,A:8,S:8,S:8,SP:8,Rest/binary>>, Parser, Parsed) when
?OR(C,$C,$c) andalso ?OR(L,$L,$l) andalso ?OR(A,$A,$a) andalso
?OR(S,$S,$s) andalso (?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
{Rest0, Parser0, Class0} = case Parsed of
[#class{name = undefined} = Class|_] ->
ephp_parser_class:st_class(<<SP:8, Rest/binary>>, add_pos(Parser, 5),
Class);
_ ->
Class = add_line(#class{}, Parser),
ephp_parser_class:st_class(<<SP:8, Rest/binary>>, add_pos(Parser, 5),
Class)
end,
code(Rest0, copy_rowcol(Parser0, Parser), [Class0|Parsed]);
code(<<U:8,S:8,E:8,SP:8,Rest/binary>>, Parser, Parsed) when
?OR(U,$U,$u) andalso ?OR(S,$S,$s) andalso ?OR(E,$E,$e) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
case remove_spaces(<<SP:8, Rest/binary>>, add_pos(Parser, 3)) of
{<<C:8,O:8,N:8,S:8,T:8,SP:8,Rest0/binary>>, Parser0} 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)) ->
use_const(<<SP:8,Rest0/binary>>, add_pos(Parser0, 5), Parsed);
{<<F:8,U:8,N:8,C:8,T:8,I:8,O:8,N:8,SP:8,Rest0/binary>>, Parser0} 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)) ->
use_function(<<SP:8,Rest0/binary>>, add_pos(Parser0, 8), Parsed);
{Rest0, Parser0} ->
use_list(Rest0, Parser0#parser{use_ns = []}, Parsed)
end;
code(<<N:8,A:8,M:8,E:8,S:8,P:8,A:8,C:8,E:8,SP:8,_/binary>> = Rest,
#parser{namespace_can_be = only_statement} = Parser, Parsed) when
?OR(N,$N,$n) andalso ?OR(A,$A,$a) andalso ?OR(M,$M,$m) andalso
?OR(E,$E,$e) andalso ?OR(S,$S,$s) andalso ?OR(P,$P,$p) andalso
?OR(C,$C,$c) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
code_ns(Rest, Parser#parser{namespace = []}, Parsed);
code(<<N:8,A:8,M:8,E:8,S:8,P:8,A:8,C:8,E:8,SP:8,_/binary>>, Parser, _Parsed) when
?OR(N,$N,$n) andalso ?OR(A,$A,$a) andalso ?OR(M,$M,$m) andalso
?OR(E,$E,$e) andalso ?OR(S,$S,$s) andalso ?OR(P,$P,$p) andalso
?OR(C,$C,$c) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
throw_error(enamespace, Parser, ?E_ERROR, undefined);
code(<<I:8,N:8,T:8,E:8,R:8,F:8,A:8,C:8,E:8,SP:8,Rest/binary>>, Parser, Parsed) when
?OR(I,$I,$i) andalso ?OR(N,$N,$n) andalso ?OR(T,$T,$t) andalso
?OR(E,$E,$e) andalso ?OR(R,$R,$r) andalso ?OR(F,$F,$f) andalso
?OR(A,$A,$a) andalso ?OR(C,$C,$c) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
Interface = add_line(#class{type = interface}, Parser),
{Rest0, Parser0, Interface0} =
ephp_parser_class:st_interface(<<SP:8, Rest/binary>>, add_pos(Parser, 9),
Interface),
code(Rest0, copy_rowcol(Parser0, Parser), [Interface0|Parsed]);
code(<<E:8,C:8,H:8,O:8,SP:8,Rest/binary>>, Parser, Parsed) when
?OR(E,$e,$E) andalso ?OR(C,$c,$C) andalso ?OR(H,$h,$H) andalso
?OR(O,$o,$O) andalso
(SP =:= $( orelse ?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
Call = add_line(#call{name = <<"print">>}, Parser),
{Rest0, Parser0, [Call0]} =
ephp_parser_func:echo(<<SP:8,Rest/binary>>, add_pos(Parser, 4), [Call]),
% FIXME if we detect an OR or AND expression, we put around print
case Call0#call.args of
[#operation{type = Type} = A1] when Type =:= 'or' orelse Type =:= 'and' ->
Print = A1#operation{
expression_left = get_print(A1#operation.expression_left, Parser)
},
code(Rest0, copy_rowcol(Parser0, Parser), [Print|Parsed]);
_ ->
code(Rest0, copy_rowcol(Parser0, Parser), [Call0|Parsed])
end;
code(<<P:8,R:8,I:8,N:8,T:8,SP:8,Rest/binary>>, Parser, Parsed)
when ?OR(P,$p,$P) andalso ?OR(R,$r,$R) andalso ?OR(I,$i,$I)
andalso ?OR(N,$n,$N) andalso ?OR(T,$t,$T)
andalso (?IS_SPACE(SP) orelse SP =:= $() ->
Call = add_line(#call{name = <<"print">>}, Parser),
{Rest0, Parser0, [Call0]} =
ephp_parser_func:echo(<<SP:8,Rest/binary>>, add_pos(Parser, 5), [Call]),
% FIXME if we detect an OR or AND expression, we put around print
case Call0#call.args of
[#operation{type = Type} = A1] when Type =:= 'or' orelse Type =:= 'and' ->
Print = A1#operation{
expression_left = get_print(A1#operation.expression_left, Parser)
},
code(Rest0, copy_rowcol(Parser0, Parser), [Print|Parsed]);
_ ->
code(Rest0, copy_rowcol(Parser0, Parser), [Call0|Parsed])
end;
code(<<C:8,O:8,N:8,S:8,T:8,SP:8,Rest/binary>>, Parser, Parsed)
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) ->
case expression(Rest, add_pos(Parser, 6), []) of
{Rest0, Parser0, #assign{variable = #constant{} = Const, expression = Value}} ->
Constant = Const#constant{type = define, value = Value},
code(Rest0, copy_rowcol(Parser0, Parser), [Constant|Parsed]);
{_, _, _} -> throw_error(eparse, Parser, Rest)
end;
code(<<F:8,U:8,N:8,C:8,T:8,I:8,O:8,N:8,SP:8,Rest/binary>>, 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) ->
{Rest0, Parser0, [#function{}=Function]} =
ephp_parser_func:st_function(Rest, add_pos(Parser, 9), []),
code(Rest0, copy_rowcol(Parser0, Parser), Parsed ++ [Function]);
code(<<F:8,U:8,N:8,C:8,T:8,I:8,O:8,N:8,SP:8,Rest/binary>>, 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_NEWLINE(SP) ->
{Rest0, Parser0, [#function{} = Function]} =
ephp_parser_func:st_function(Rest, new_line(Parser), []),
code(Rest0, copy_rowcol(Parser0, Parser), Parsed ++ [Function]);
code(<<"?>\n",Rest/binary>>, #parser{level = code_value} = Parser, [Parsed]) ->
{Rest, new_line(Parser), Parsed};
code(<<"?>",Rest/binary>>, #parser{level = code_value} = Parser, [Parsed]) ->
{Rest, add_pos(Parser, 2), Parsed};
code(<<"?>\n",Rest/binary>>, #parser{level = L} = Parser, Parsed) when
L =:= code_block orelse L =:= if_old_block orelse
L =:= while_old_block orelse L =:= for_old_block orelse
L =:= foreach_old_block orelse L =:= switch_block orelse
L =:= switch_old_block ->
NewParser = new_line(literal_level(Parser)),
{Rest0, Parser0, Text} = document(Rest, NewParser, []),
code(Rest0, copy_rowcol(Parser0, Parser), Text ++ Parsed);
code(<<"?>",Rest/binary>>, #parser{level = L} = Parser, Parsed) when
L =:= code_block orelse L =:= if_old_block orelse
L =:= while_old_block orelse L =:= for_old_block orelse
L =:= foreach_old_block orelse L =:= switch_block orelse
L =:= switch_old_block ->
{Rest0, Parser0, Text} = document(Rest, literal_level(add_pos(Parser, 2)), []),
code(Rest0, copy_rowcol(Parser0, Parser), Text ++ Parsed);
code(<<"?>", _/binary>> = Rest, #parser{level = code_statement} = Parser, Parsed) ->
{Rest, Parser, Parsed};
code(<<"?>\n", Rest/binary>>, Parser, Parsed) ->
{Rest, new_line(Parser), Parsed};
code(<<"?>", Rest/binary>>, Parser, Parsed) ->
{Rest, add_pos(Parser, 2), Parsed};
code(<<"//", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = comment_line(Rest, add_pos(Parser, 2), Parsed),
code(Rest0, Parser0, Parsed);
code(<<"#", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = comment_line(Rest, inc_pos(Parser), Parsed),
code(Rest0, Parser0, Parsed);
code(<<"/*", Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = comment_block(Rest, add_pos(Parser, 2), Parsed),
code(Rest0, Parser0, Parsed);
code(<<"<<<", _/binary>> = Rest, Parser, Parsed) ->
{Rest0, Parser0, S} = ephp_parser_string:string(Rest,Parser,[]),
code(Rest0, copy_rowcol(Parser0, Parser), [S|Parsed]);
code(<<I:8,N:8,C:8,L:8,U:8,D:8,E:8,SP:8,Rest/binary>>, 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 (?IS_SPACE(SP) orelse SP =:= $() ->
{Rest0, Parser0} = remove_spaces(<<SP:8, Rest/binary>>, add_pos(Parser, 7)),
{Rest1, Parser1, Exp} = expression(Rest0, Parser0, []),
Include = add_line(#call{name = <<"include">>, args = [Exp]}, Parser),
code(Rest1, Parser1, [Include|Parsed]);
code(<<I:8,N:8,C:8,L:8,U:8,D:8,E:8,$_,O:8,N:8,C:8,E:8,SP:8,Rest/binary>>,
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
(?IS_SPACE(SP) orelse SP =:= $() ->
{Rest0, Parser0} = remove_spaces(<<SP:8, Rest/binary>>, add_pos(Parser, 12)),
{Rest1, Parser1, Exp} = expression(Rest0, Parser0, []),
Include = add_line(#call{name = <<"include_once">>, args = [Exp]}, Parser),
code(Rest1, Parser1, [Include|Parsed]);
code(<<R:8,E:8,Q:8,U:8,I:8,R:8,E:8,SP:8,Rest/binary>>, 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
(?IS_SPACE(SP) orelse SP =:= $() ->
{Rest0, Parser0} = remove_spaces(<<SP:8, Rest/binary>>, add_pos(Parser, 7)),
{Rest1, Parser1, Exp} = expression(Rest0, Parser0, []),
Include = add_line(#call{name = <<"require">>, args = [Exp]}, Parser),
code(Rest1, Parser1, [Include|Parsed]);
code(<<R:8,E:8,Q:8,U:8,I:8,R:8,E:8,$_,O:8,N:8,C:8,E:8,SP:8,Rest/binary>>,
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
(?IS_SPACE(SP) orelse SP =:= $() ->
{Rest0, Parser0} = remove_spaces(<<SP:8, Rest/binary>>, add_pos(Parser, 12)),
{Rest1, Parser1, Exp} = expression(Rest0, Parser0, []),
Include = add_line(#call{name = <<"require_once">>, args = [Exp]}, Parser),
code(Rest1, Parser1, [Include|Parsed]);
code(<<S:8,T:8,A:8,T:8,I:8,C:8,SP:8,Rest/binary>>, Parser, Parsed) 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)) ->
{Rest0, Parser0, Parsed0} = static(Rest, add_pos(Parser, 7), []),
code(Rest0, copy_rowcol(Parser0, Parser), Parsed0 ++ Parsed);
code(<<A:8,_/binary>> = Rest, Parser, [#constant{}|_])
when ?IS_ALPHA(A) orelse A =:= $_ orelse A =:= $\\ ->
throw_error(eparse, Parser, Rest);
code(<<A:8,_/binary>> = Rest, Parser, Parsed) when
?IS_ALPHA(A) orelse A =:= $_ orelse A =:= $\\ ->
case expression(Rest, Parser, []) of
{<<>>, _Parser0, []} ->
{<<>>, Parser, Parsed};
{Rest0, Parser0, Parsed0} ->
code(Rest0, copy_rowcol(Parser0, Parser), [Parsed0] ++ Parsed)
end;
code(<<A:8,_/binary>> = Rest, Parser, Parsed) when ?IS_NUMBER(A)
orelse A =:= $- orelse A =:= $(
orelse A =:= $" orelse A =:= $'
orelse A =:= $$ orelse A =:= $+
orelse A =:= 126 orelse A =:= $! ->
{Rest0, Parser0, Exp} = expression(Rest, Parser, []),
code(Rest0, copy_rowcol(Parser0, Parser), [Exp|Parsed]);
code(<<Space:8,Rest/binary>>, Parser, Parsed) when ?IS_SPACE(Space) ->
code(Rest, inc_pos(Parser), Parsed);
code(<<NewLine:8,Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(NewLine) ->
code(Rest, new_line(Parser), Parsed);
code(<<";",Rest/binary>>, Parser, Parsed) ->
code(Rest, inc_pos(Parser), Parsed);
code(Rest, Parser, [#try_catch{catches = C, finally = F}|_Parsed])
when C =:= [] andalso F =:= [] ->
throw_error(enocatch, Parser, Rest);
code(<<>>, Parser, Parsed) ->
{<<>>, Parser, Parsed};
code(Text, Parser, _Parsed) ->
throw_error(eparse, Parser, Text).
code_block(<<"//",Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = comment_line(Rest, Parser, Parsed),
code_block(Rest0, Parser0, Parsed);
code_block(<<"#",Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = comment_line(Rest, Parser, Parsed),
code_block(Rest0, Parser0, Parsed);
code_block(<<"/*",Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = comment_block(Rest, Parser, Parsed),
code_block(Rest0, Parser0, Parsed);
%% TODO change this to use always 'code/3' as one statement and block calls
%% several times until '}' is received. Same for old block but given known
%% constants instead.
code_block(<<"{", Rest/binary>>, Parser, Parsed) ->
code(Rest, code_block_level(inc_pos(Parser)), Parsed);
code_block(<<":", Rest/binary>>, #parser{level = if_block} = Parser, Parsed) ->
code(Rest, if_old_block_level(inc_pos(Parser)), Parsed);
code_block(<<":", Rest/binary>>, #parser{level = foreach_block} = Parser, Parsed) ->
code(Rest, foreach_old_block_level(inc_pos(Parser)), Parsed);
code_block(<<":", Rest/binary>>, #parser{level = for_block} = Parser, Parsed) ->
code(Rest, for_old_block_level(inc_pos(Parser)), Parsed);
code_block(<<":", Rest/binary>>, #parser{level = while_block} = Parser, Parsed) ->
code(Rest, while_old_block_level(inc_pos(Parser)), Parsed);
code_block(<<SP:8, Rest/binary>>, Parser, Parsed) when ?IS_SPACE(SP) ->
code_block(Rest, inc_pos(Parser), Parsed);
code_block(<<SP:8, Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(SP) ->
code_block(Rest, new_line(Parser), Parsed);
code_block(<<>>, Parser, Parsed) ->
{<<>>, Parser, Parsed};
code_block(Rest, Parser, Parsed) ->
code(Rest, code_statement_level(Parser), Parsed).
get_ns([], #parser{namespace = BaseNS}) -> BaseNS;
get_ns([<<>>], _Parser) -> [];
get_ns([FirstName|RestNames] = NS,
#parser{use_list = UseList, namespace = BaseNS}) ->
case ephp_string:to_lower(FirstName) of
<<"namespace">> ->
ephp_ns:join(BaseNS, RestNames);
_ ->
FullNS = ephp_ns:join(BaseNS, NS),
case ephp_ns:find(NS, UseList) of
{MatchNS, RealNS} -> ephp_ns:translate(NS, MatchNS, RealNS);
false -> FullNS
end
end.
use_const(Rest, #parser{use_const_list = ConstList,
use_ns = UseNS} = Parser, Parsed) ->
{Rest0, Parser0, NameSpace} = namespace(Rest, Parser, []),
{NS, BaseName} = ephp_ns:split(NameSpace),
BaseNS = ephp_ns:join(UseNS, NS),
case remove_spaces(Rest0, Parser0) of
{<<"{", Rest1/binary>>, #parser{use_ns = []} = Parser1} ->
RealBaseNS = ephp_ns:join(BaseNS, [BaseName]),
use_const(Rest1, inc_pos(Parser1#parser{use_ns = RealBaseNS}), Parsed);
{<<"}", Rest1/binary>>, #parser{use_ns = UseNS} = Parser1}
when UseNS =/= [] ->
use_const(Rest1, inc_pos(Parser1#parser{use_ns = []}), Parsed);
%% TODO
% {<<$;, _/binary>>, Parser1} when UseNS =/= [] ->
% throw_error(...);
% {<<$,, _/binary>>, #parser{use_ns = []} = Parser1} ->
% throw_error(...);
{<<SEP:8, Rest1/binary>>, Parser1} when ?OR(SEP, $;, $,) ->
NewUseConstList = [{BaseName, BaseNS}|ConstList],
Parser2 = inc_pos(Parser1#parser{use_const_list = NewUseConstList}),
case SEP of
$; -> code(Rest1, Parser2, Parsed);
$, -> use_const(Rest1, Parser2, Parsed)
end
end.
use_function(Rest, #parser{use_func_list = FuncList,
use_ns = UseNS} = Parser, Parsed) ->
{Rest0, Parser0, NameSpace} = namespace(Rest, Parser, []),
{NS, BaseName} = ephp_ns:split(NameSpace),
BaseNS = ephp_ns:join(UseNS, NS),
case remove_spaces(Rest0, Parser0) of
{<<"{", Rest1/binary>>, #parser{use_ns = []} = Parser1} ->
RealBaseNS = ephp_ns:join(BaseNS, [BaseName]),
use_function(Rest1, inc_pos(Parser1#parser{use_ns = RealBaseNS}), Parsed);
{<<"}", Rest1/binary>>, #parser{use_ns = UseNS} = Parser1}
when UseNS =/= [] ->
use_function(Rest1, inc_pos(Parser1#parser{use_ns = []}), Parsed);
%% TODO
% {<<$;, _/binary>>, Parser1} when UseNS =/= [] ->
% throw_error(...);
% {<<$,, _/binary>>, #parser{use_ns = []} = Parser1} ->
% throw_error(...);
{<<SEP:8, Rest1/binary>>, Parser1} when ?OR(SEP, $;, $,) ->
NewUseConstList = [{BaseName, BaseNS}|FuncList],
Parser2 = inc_pos(Parser1#parser{use_func_list = NewUseConstList}),
case SEP of
$; -> code(Rest1, Parser2, Parsed);
$, -> use_function(Rest1, Parser2, Parsed)
end
end.
use_list(Rest, #parser{use_ns = UseNS, use_list = UseList} = Parser, Parsed) ->
{Rest0, Parser0, NameSpace} = namespace(Rest, Parser, []),
{NS, Name} = ephp_ns:split(NameSpace),
BaseNS = ephp_ns:join(UseNS, NS ++ [Name]),
case remove_spaces(Rest0, Parser0) of
{<<"{", Rest1/binary>>, #parser{use_ns = []} = Parser1} ->
use_list(Rest1, inc_pos(Parser1#parser{use_ns = BaseNS}), Parsed);
{<<"}", Rest1/binary>>, #parser{use_ns = UseNS} = Parser1}
when UseNS =/= [] ->
use_list(Rest1, inc_pos(Parser1#parser{use_ns = []}), Parsed);
%% TODO
% {<<$;, _/binary>>, Parser1} when UseNS =/= [] ->
% throw_error(...);
% {<<$,, _/binary>>, #parser{use_ns = []} = Parser1} ->
% throw_error(...);
{<<SEP:8, Rest1/binary>>, Parser1} when ?OR(SEP, $;, $,) ->
AliasNS = [lists:last(BaseNS)],
NewUseList = [{AliasNS, BaseNS}|UseList],
Parser2 = inc_pos(Parser1#parser{use_list = NewUseList}),
case SEP of
$; -> code(Rest1, Parser2, Parsed);
$, -> use_list(Rest1, Parser2, Parsed)
end;
{<<A:8,S:8,SP:8,Rest1/binary>>, Parser1} when
?OR(A,$A,$a) andalso ?OR(S,$S,$s) andalso
(?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
{Rest2, Parser2, AliasNS} = namespace(<<SP:8, Rest1/binary>>,
add_pos(Parser1, 2), []),
NewUseList = [{AliasNS, BaseNS}|UseList],
Parser3 = Parser2#parser{use_list = NewUseList},
case remove_spaces(Rest2, Parser3) of
{<<";", _/binary>> = Rest4, Parser4} ->
code(Rest4, Parser4, Parsed);
{<<",", Rest4/binary>>, Parser4} ->
use_list(Rest4, inc_pos(Parser4), Parsed)
end
end.
namespace(<<SP:8, Rest/binary>>, Parser, []) when ?IS_SPACE(SP) ->
namespace(Rest, inc_pos(Parser), []);
namespace(<<SP:8, Rest/binary>>, Parser, []) when ?IS_NEWLINE(SP) ->
namespace(Rest, new_line(Parser), []);
namespace(<<"\\", Rest/binary>>, Parser, Parsed) ->
namespace(Rest, inc_pos(Parser), [<<>>|Parsed]);
namespace(<<A:8, _/binary>> = Rest, Parser, []) when
?IS_ALPHANUM(A) orelse A =:= $_ ->
namespace(Rest, Parser, [<<>>]);
namespace(<<A:8, Rest/binary>>, Parser, [NamePart|Parsed]) when
?IS_ALPHANUM(A) orelse A =:= $_ ->
namespace(Rest, inc_pos(Parser), [<<NamePart/binary, A:8>>|Parsed]);
namespace(Rest, Parser, Parsed) ->
{Rest, Parser, lists:reverse(Parsed)}.
static(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_SPACE(SP) ->
static(Rest, inc_pos(Parser), Parsed);
static(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(SP) ->
static(Rest, new_line(Parser), Parsed);
static(<<",",Rest/binary>>, Parser, Parsed) when Parsed =/= [] ->
static(Rest, inc_pos(Parser), Parsed);
static(<<",", _/binary>>, Parser, _Parsed) ->
throw_error(eparse, Parser, <<",">>);
static(<<";", _/binary>> = Rest, Parser, Parsed) ->
{Rest, Parser, Parsed};
static(<<>>, Parser, _Parsed) ->
throw_error(eparse, Parser, <<>>);
static(Rest, Parser, Parsed) ->
case expression(Rest, arg_level(Parser), []) of
{Rest0, Parser0, #assign{variable = Var} = Assign} ->
NewAssign = Assign#assign{variable = Var#variable{type = static}},
static(Rest0, copy_rowcol(Parser0, Parser), Parsed ++ [NewAssign]);
{Rest0, Parser0, #variable{} = Var} ->
NewVar = Var#variable{type = static},
static(Rest0, copy_rowcol(Parser0, Parser), Parsed ++ [NewVar])
end.
variable(<<SP:8,Rest/binary>>, Parser, []) when ?IS_SPACE(SP) ->
variable(Rest, inc_pos(Parser), []);
variable(<<SP:8,Rest/binary>>, #parser{level = enclosed} = Parser, Var) when ?IS_SPACE(SP) ->
variable(Rest, inc_pos(Parser), Var);
variable(<<SP:8,Rest/binary>>, Parser, []) when ?IS_NEWLINE(SP) ->
variable(Rest, new_line(Parser), []);
variable(<<SP:8,Rest/binary>>, #parser{level = enclosed} = Parser, Var) when ?IS_NEWLINE(SP) ->
variable(Rest, new_line(Parser), Var);
variable(<<"$",Rest/binary>>, Parser, []) ->
variable(Rest, inc_pos(Parser), []);
variable(<<A:8,Rest/binary>>, Parser, [])
when ?IS_ALPHA(A) orelse A =:= $_ orelse A >= 16#7f ->
Var = add_line(#variable{name = <<A:8>>}, Parser),
variable(Rest, inc_pos(Parser), [Var]);
variable(<<A:8, Rest/binary>>, #parser{} = Parser, [#variable{name = N} = V])
when ?IS_NUMBER(A) orelse ?IS_ALPHA(A) orelse A =:= $_
orelse A >= 16#7f ->
variable(Rest, inc_pos(Parser), [V#variable{name = <<N/binary, A:8>>}]);
variable(<<SP:8, _/binary>> = Rest, #parser{level = unclosed} = Parser, Var)
when ?IS_SPACE(SP) ->
{Rest, inc_pos(Parser), Var};
variable(<<SP:8, _/binary>> = Rest, #parser{level = unclosed} = Parser, Var)
when ?IS_NEWLINE(SP) ->
{Rest, new_line(Parser), Var};
variable(Rest, Parser, []) ->
throw_error(eparse, Parser, Rest);
variable(Rest, Parser, Var) ->
var_access(Rest, Parser, Var).
var_access(<<"}", _/binary>> = Rest, #parser{level = enclosed} = Parser, Var) ->
{Rest, Parser, Var};
var_access(<<"[", Rest/binary>>, Parser, [#variable{idx = Indexes} = Var]) ->
{Rest1, Parser1, RawIdx} = expression(Rest, array_level(inc_pos(Parser)), []),
Idx = case RawIdx of
[] -> auto;
_ -> RawIdx
end,
NewVar = Var#variable{idx = Indexes ++ [Idx]},
var_access(Rest1, copy_rowcol(Parser1, Parser), [NewVar]);
var_access(<<"{", Rest/binary>>, Parser, [#variable{idx = Indexes} = Var]) ->
NewParser = array_curly_level(inc_pos(Parser)),
{Rest1, Parser1, RawIdx} = expression(Rest, NewParser, []),
Idx = case RawIdx of
[] -> auto;
_ -> RawIdx
end,
NewVar = Var#variable{idx = Indexes ++ [Idx]},
var_access(Rest1, copy_rowcol(Parser1, Parser), [NewVar]);
var_access(<<"->",Rest/binary>>, #parser{level = L} = Parser, [#variable{} = Var])
when is_number(L) ->
% TODO move this code to ephp_parser_expr
OpL = <<"->">>,
Op = add_op({OpL, precedence(OpL), Parser}, add_op(Var, [])),
{Rest0, Parser0, [Exp]} = accessor(Rest, arg_level(add_pos(Parser, 2)), []),
var_access(Rest0, copy_rowcol(Parser0, Parser), [add_op('end', add_op(Exp, Op))]);
var_access(<<"->",Rest/binary>>, Parser, [#variable{} = Var]) ->
% TODO move this code to ephp_parser_expr
OpL = <<"->">>,
Op = add_op({OpL, precedence(OpL), Parser}, add_op(Var, [])),
{Rest0, Parser0, [Exp]} = accessor(Rest, add_pos(Parser, 2), []),
var_access(Rest0, copy_rowcol(Parser0, Parser), [add_op('end', add_op(Exp, Op))]);
var_access(Rest, Parser, Parsed) ->
{Rest, Parser, Parsed}.
accessor(<<A:8, Rest/binary>>, Parser, Parsed) when ?IS_SPACE(A) ->
accessor(Rest, inc_pos(Parser), Parsed);
accessor(<<A:8, Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(A) ->
accessor(Rest, new_line(Parser), Parsed);
accessor(<<A:8, _/binary>> = Rest, Parser, []) when ?IS_ALPHA(A) orelse A =:= $_ ->
constant(Rest, Parser, []);
accessor(<<"$", Rest/binary>>, Parser, []) ->
variable(Rest, Parser, []);
accessor(<<"{", Rest/binary>>, Parser, []) ->
NewParser = inc_pos(enclosed_level(Parser)),
{Rest0, Parser0, Acc} = expression(Rest, NewParser, []),
case remove_spaces(Rest0, Parser0) of
{<<"(", Rest1/binary>>, Parser1} ->
NParser1 = inc_pos(Parser1),
{Rest2, Parser2, Args} = ephp_parser_func:call_args(Rest1, NParser1, []),
{Rest2, copy_rowcol(Parser2, Parser),
[add_line(#call{name = Acc, args = Args,
namespace = Parser#parser.namespace}, Parser1)]};
{Rest1, Parser1} ->
{Rest1, copy_rowcol(Parser1, Parser), [Acc]}
end;
accessor(<<>>, Parser, Parsed) ->
{<<>>, Parser, Parsed};
accessor(_Rest, Parser, []) ->
throw_error(eparse, Parser, {<<"`\"identifier (T_STRING)\"' or "
"`\"variable (T_VARIABLE)\"' or "
"`'{'' or `'$''">>}).
constant(<<A:8,Rest/binary>>, Parser, [])
when (?IS_ALPHA(A) orelse ?IS_NUMBER(A) orelse A =:= $_ orelse A =:= $\\)
andalso Parser#parser.level =/= unclosed ->
constant(Rest, inc_pos(Parser), [add_line(#constant{name = <<A:8>>}, Parser)]);
constant(<<A:8,Rest/binary>>, Parser, []) when ?IS_ALPHA(A) orelse A =:= $_ ->
constant(Rest, inc_pos(Parser), [add_line(#constant{name = <<A:8>>}, Parser)]);
constant(<<A:8,Rest/binary>>, Parser, [#constant{name = N} = C])
when (?IS_ALPHA(A) orelse ?IS_NUMBER(A) orelse A =:= $_ orelse A =:= $\\)
andalso Parser#parser.level =/= unclosed ->
constant(Rest, inc_pos(Parser), [C#constant{name = <<N/binary, A:8>>}]);
constant(<<A:8,Rest/binary>>, Parser, [#constant{name = N} = C])
when ?IS_ALPHA(A) orelse ?IS_NUMBER(A) orelse A =:= $_ ->
constant(Rest, inc_pos(Parser), [C#constant{name = <<N/binary, A:8>>}]);
constant(<<SP:8,_/binary>> = Rest, #parser{level = unclosed} = Parser, [#constant{}] = Parsed)
when ?IS_SPACE(SP) ->
{Rest, Parser, Parsed};
constant(<<SP:8,Rest/binary>>, Parser, [#constant{}] = Parsed)
when ?IS_SPACE(SP) ->
constant_wait(Rest, inc_pos(Parser), Parsed);
constant(<<SP:8,_/binary>> = Rest, #parser{level = unclosed} = Parser, [#constant{}] = Parsed)
when ?IS_NEWLINE(SP) ->
{Rest, Parser, Parsed};
constant(<<SP:8,Rest/binary>>, Parser, [#constant{}] = Parsed)
when ?IS_NEWLINE(SP) ->
constant_wait(Rest, new_line(Parser), Parsed);
constant(<<"(",_/binary>> = Rest, Parser, Parsed) ->
constant_wait(Rest, Parser, Parsed);
% TODO fail when unclosed is used?
constant(<<"::",_/binary>> = Rest, Parser, Parsed) ->
constant_wait(Rest, Parser, Parsed);
constant(Rest, Parser, Parsed) ->
{Rest, Parser, constant_known(Parsed, Parser)}.
%% if after one or several spaces there are a parens, it's a function
%% but if not, it should returns
constant_wait(<<"(", Rest/binary>>, Parser, [#constant{} = C]) ->
{NS, Name} = ephp_parser_func:get_ns(ephp_ns:parse(C#constant.name), Parser),
case ephp_string:to_lower(Name) of
<<"__halt_compiler">> ->
{<<>>, Parser, []};
_ ->
Call = #call{name = Name, line = C#constant.line, namespace = NS},
ephp_parser_func:function(Rest, inc_pos(Parser), [Call])
end;
constant_wait(<<"::$", Rest/binary>>, Parser, [#constant{} = C]) ->
NewParser = arg_level(add_pos(Parser, 2)),
{Rest1, Parser1, [Var]} = variable(<<"$", Rest/binary>>, NewParser, []),
{RawNS, RealClassName} = ephp_ns:parse(C#constant.name),
NS = ephp_parser:get_ns(RawNS, Parser),
NewVar = Var#variable{type = class, class = RealClassName, class_ns = NS},
{Rest1, copy_rowcol(Parser1, Parser), [NewVar]};
constant_wait(<<"::",Rest/binary>>, Parser, [#constant{} = Cons]) ->
case constant(Rest, add_pos(Parser, 2), []) of
{Rest1, Parser1, [#constant{name = <<"class">>}]} ->
%% syntax: MyClass::class
{Rest1, Parser1, [add_line(#text{text = Cons#constant.name}, Parser)]};
{Rest1, Parser1, [#constant{} = C]} ->
{RawConsNS, ConstName} = ephp_ns:parse(Cons#constant.name),
ConsNS = ephp_parser:get_ns(RawConsNS, Parser),
{Rest1, Parser1, [C#constant{type = class,
class = ConstName,
namespace = ConsNS}]};
{Rest1, Parser1, [#call{} = C]} ->
{RawConsNS, ConstName} = ephp_ns:parse(Cons#constant.name),
ConsNS = ephp_parser:get_ns(RawConsNS, Parser),
{Rest1, Parser1, [C#call{type = class,
class = ConstName,
namespace = ConsNS}]}
end;
constant_wait(<<SP:8,Rest/binary>>, Parser, [#constant{}] = Parsed)
when ?IS_SPACE(SP) ->
constant_wait(Rest, inc_pos(Parser), Parsed);
constant_wait(<<SP:8,Rest/binary>>, Parser, [#constant{}] = Parsed)
when ?IS_NEWLINE(SP) ->
constant_wait(Rest, new_line(Parser), Parsed);
constant_wait(Rest, Parser, Parsed) ->
{Rest, Parser, constant_known(Parsed, Parser)}.
constant_known([#constant{name = <<"__DIR__">>}|Parsed],
#parser{filename = Filename} = Parser) ->
[add_line(#text{text = filename:dirname(Filename)}, Parser)|Parsed];
constant_known([#constant{name = <<"__FILE__">>}|Parsed],
#parser{filename = Filename} = Parser) ->
[add_line(#text{text = Filename}, Parser)|Parsed];
constant_known([#constant{name = <<"__LINE__">>}|Parsed],
#parser{row = R} = Parser) ->
[add_line(#int{int = R}, Parser)|Parsed];
constant_known([#constant{name = <<"__NAMESPACE__">>}|Parsed],
#parser{namespace = NS} = Parser) ->
[add_line(#text{text = ephp_ns:to_bin(NS)}, Parser)|Parsed];
constant_known([#constant{name = <<"exit">>}|Parsed], Parser) ->
[add_line(#call{name = <<"exit">>}, Parser)|Parsed];
constant_known([#constant{name = RawName} = C|Parsed], Parser) ->
{RawNS, Name} = ephp_ns:parse(RawName),
NS = ephp_parser:get_ns(RawNS, Parser),
case lists:member(C#constant.name, ephp_const:special_consts()) of
true -> [C|Parsed];
false -> [C#constant{namespace = NS, name = Name}|Parsed]
end.
st_global(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_SPACE(SP) ->
st_global(Rest, inc_pos(Parser), Parsed);
st_global(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(SP) ->
st_global(Rest, new_line(Parser), Parsed);
st_global(<<",",Rest/binary>>, Parser, Parsed) ->
st_global(Rest, inc_pos(Parser), Parsed);
st_global(<<";", _/binary>> = Rest, Parser, Parsed) ->
Global = add_line(#global{vars = Parsed}, Parser),
{Rest, Parser, [Global]};
st_global(<<"$",_/binary>> = Rest, Parser, Parsed) ->
{Rest0, Parser0, [Var]} = variable(Rest, Parser, []),
st_global(Rest0, Parser0, [Var|Parsed]).
st_while(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_SPACE(SP) ->
st_while(Rest, inc_pos(Parser), Parsed);
st_while(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(SP) ->
st_while(Rest, new_line(Parser), Parsed);
st_while(<<"(",Rest/binary>>, Parser, Parsed) ->
NewParser = inc_pos(Parser),
{<<")",Rest1/binary>>, Parser1, Conditions} =
expression(Rest, arg_level(NewParser), []),
{Rest2, Parser2, CodeBlock} = code_block(Rest1, while_block_level(Parser1), []),
While = add_line(#while{type = pre,
conditions = Conditions,
loop_block = CodeBlock}, Parser),
{Rest2, copy_rowcol(Parser2, Parser), [While|Parsed]};
st_while(<<>>, Parser, _Parsed) ->
throw_error(eparse, Parser, <<>>).
st_do_while(Rest, Parser, Parsed) ->
case code_block(Rest, Parser, []) of
{<<";", Rest0/binary>>, Parser0, CodeBlock} -> ok;
{Rest0, Parser0, CodeBlock} -> ok
end,
{<<WhileRaw:5/binary,Rest1/binary>>, Parser1} = remove_spaces(Rest0, Parser0),
<<"while">> = ephp_string:to_lower(WhileRaw),
{Rest2, Parser2, [While]} = st_while(Rest1, Parser1, []),
DoWhile = add_line(While#while{type = post,
loop_block = CodeBlock}, Parser),
{Rest2, copy_rowcol(Parser2, Parser), [DoWhile|Parsed]}.
-spec st_if(binary(), parser(), [if_block()]) -> {binary(), parser(), [if_block()]}.
st_if(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_SPACE(SP) ->
st_if(Rest, inc_pos(Parser), Parsed);
st_if(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(SP) ->
st_if(Rest, new_line(Parser), Parsed);
st_if(<<";",Rest/binary>>, Parser, [#if_block{}] = Parsed) ->
st_if(Rest, inc_pos(Parser), Parsed);
st_if(<<"//",Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = comment_line(Rest, Parser, Parsed),
st_if(Rest0, Parser0, Parsed);
st_if(<<"#",Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = comment_line(Rest, Parser, Parsed),
st_if(Rest0, Parser0, Parsed);
st_if(<<"/*",Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, _} = comment_block(Rest, Parser, Parsed),
st_if(Rest0, Parser0, Parsed);
st_if(<<"(",Rest/binary>>, Parser, []) ->
NewParser = inc_pos(Parser),
{<<")",Rest1/binary>>, Parser1, Conditions} =
expression(Rest, arg_level(NewParser), []),
{Rest2, Parser2, CodeBlock} = code_block(Rest1, if_block_level(Parser1), []),
If = add_line(#if_block{conditions = Conditions,
true_block = CodeBlock}, Parser),
st_if(Rest2, copy_rowcol(Parser2, Parser), [If]);
st_if(<<E:8,L:8,S:8,E:8,SP:8,Rest/binary>>, Parser, [#if_block{} = If|Parsed]) when
?OR(E,$e,$E) andalso ?OR(L,$l,$L) andalso ?OR(S,$s,$S) andalso
(?OR(SP,${,$:) orelse ?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
{Rest0, Parser0} = remove_spaces(<<SP:8,Rest/binary>>, add_pos(Parser, 4)),
BlockParser = if_block_level(Parser0),
{Rest1, Parser1, CodeBlock} = code_block(Rest0, BlockParser, []),
IfWithElse = If#if_block{false_block = CodeBlock},
{Rest1, copy_rowcol(Parser1, Parser), [IfWithElse|Parsed]};
st_if(<<E:8,L:8,S:8,E:8,I:8,F:8,SP:8,Rest/binary>>, Parser,
[#if_block{} = If|Parsed]) when
?OR(E,$e,$E) andalso ?OR(L,$l,$L) andalso ?OR(S,$s,$S) andalso
?OR(I,$i,$I) andalso ?OR(F,$f,$F) andalso
(SP =:= $( orelse ?IS_SPACE(SP) orelse ?IS_NEWLINE(SP)) ->
{Rest1, Parser1, CodeBlock} = st_if(<<SP:8,Rest/binary>>, add_pos(Parser, 6), []),
IfWithElse = If#if_block{false_block = CodeBlock},
{Rest1, copy_rowcol(Parser1, Parser), [IfWithElse|Parsed]};
st_if(<<>>, Parser, [#if_block{}] = Parsed) ->
{<<>>, Parser, Parsed};
st_if(<<>>, Parser, _Parsed) ->
throw_error(eparse, Parser, <<>>);
st_if(Rest, Parser, [#if_block{}] = Parsed) ->
{Rest, Parser, Parsed}.
args(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_SPACE(SP) ->
args(Rest, inc_pos(Parser), Parsed);
args(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(SP) ->
args(Rest, new_line(Parser), Parsed);
args(Rest, Parser, Args) when Rest =/= <<>> ->
case expression(Rest, arg_level(Parser), []) of
{<<")",_/binary>> = Rest0, Parser0, Arg} ->
{Rest0, inc_pos(Parser0), Args ++ [Arg]};
{<<";",_/binary>> = Rest0, Parser0, Arg} ->
{Rest0, inc_pos(Parser0), Args ++ [Arg]};
{<<",", Rest0/binary>>, Parser0, Arg} ->
args(Rest0, inc_pos(Parser0), Args ++ [Arg]);
{Rest0, Parser0, Arg} ->
args(Rest0, Parser0, Args ++ [Arg])
end.
st_foreach(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_SPACE(SP) ->
st_foreach(Rest, inc_pos(Parser), Parsed);
st_foreach(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(SP) ->
st_foreach(Rest, new_line(Parser), Parsed);
st_foreach(<<"(",Rest/binary>>, Parser, Parsed) ->
{Rest0, Parser0, Exp} = expression(Rest, foreach_block_level(Parser), []),
{<<AS:2/binary,Rest1/binary>>, Parser1} = remove_spaces(Rest0, Parser0),
%% TODO add case for match this and trigger an eparse otherwise
<<"as">> = ephp_string:to_lower(AS),
NewParser = array_def_level(add_pos(Parser1, 2)),
%% TODO add case for match this and trigger an eparse otherwise
{<<")",Rest2/binary>>, Parser2, ExpIter} = expression(Rest1, NewParser, []),
BlockParser = foreach_block_level(inc_pos(Parser2)),
{Rest3, Parser3, CodeBlock} = code_block(Rest2, BlockParser, []),
RawFor = add_line(#foreach{iter = ExpIter,
elements = Exp,
loop_block = CodeBlock}, Parser),
For = case ExpIter of
#ref{} ->
RawFor;
#variable{} ->
RawFor;
[KIter, Iter] ->
RawFor#foreach{kiter = KIter, iter = Iter}
end,
{Rest3, copy_rowcol(Parser3, Parser), [For|Parsed]}.
switch_case_block([]) ->
[];
switch_case_block(Blocks) ->
{Block, [Switch|Rest]} = lists:splitwith(fun
(#switch_case{}) -> false;
(_) -> true
end, Blocks),
[Switch#switch_case{code_block = lists:reverse(Block)}|Rest].
st_switch(<<"(",Rest/binary>>, Parser, Parsed) ->
{<<")", Rest0/binary>>, Parser0, Cond} = expression(Rest, inc_pos(Parser), []),
NewParser = case remove_spaces(Rest0, inc_pos(Parser0)) of
{<<"{", Rest1/binary>>, Parser1} ->
switch_block_level(inc_pos(Parser1));
{<<":", Rest1/binary>>, Parser1} ->
switch_old_block_level(inc_pos(Parser1))
end,
{Rest2, Parser2, CodeBlock} = code(Rest1, NewParser, []),
Switch = add_line(#switch{condition = Cond, cases = CodeBlock}, Parser),
{Rest2, copy_rowcol(Parser2, Parser), [Switch|Parsed]}.
st_for(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_SPACE(SP) ->
st_for(Rest, inc_pos(Parser), Parsed);
st_for(<<SP:8,Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(SP) ->
st_for(Rest, new_line(Parser), Parsed);
st_for(<<"(", Rest/binary>>, Parser, Parsed) ->
{<<";", Rest0/binary>>, Parser0, Init} = args(Rest, inc_pos(Parser), []),
{<<";", Rest1/binary>>, Parser1, [Cond]} = args(Rest0, inc_pos(Parser0), []),
{<<")", Rest2/binary>>, Parser2, Upda} = args(Rest1, inc_pos(Parser1), []),
{Rest3, Parser3, CodeBlock} = code_block(Rest2,
for_block_level(inc_pos(Parser2)), []),
ForCodeBlock = CodeBlock ++ Upda,
For = add_line(#while{type = post,
conditions = Cond,
loop_block = ForCodeBlock}, Parser),
{Rest3, copy_rowcol(Parser3, Parser), [For|Init] ++ Parsed}.
comment_line(<<>>, Parser, Parsed) ->
{<<>>, Parser, Parsed};
comment_line(<<"?>", _/binary>> = Rest, Parser, Parsed) ->
{Rest, Parser, Parsed};
comment_line(<<A:8, Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(A) ->
{Rest, new_line(Parser), Parsed};
comment_line(<<_/utf8, Rest/binary>>, Parser, Parsed) ->
comment_line(Rest, inc_pos(Parser), Parsed).
comment_block(<<>>, Parser, _Parsed) ->
%% TODO: throw parse error
throw_error(eparse, Parser, missing_comment_end);
comment_block(<<"*/", Rest/binary>>, Parser, Parsed) ->
{Rest, add_pos(Parser, 2), Parsed};
comment_block(<<A:8, Rest/binary>>, Parser, Parsed) when ?IS_NEWLINE(A) ->
comment_block(Rest, new_line(Parser), Parsed);
comment_block(<<_/utf8, Rest/binary>>, Parser, Parsed) ->
comment_block(Rest, inc_pos(Parser), Parsed).
%%------------------------------------------------------------------------------
%% helper functions
%%------------------------------------------------------------------------------
copy_rowcol(#parser{row = Row, col = Col}, Parser) ->
Parser#parser{row = Row, col = Col}.
set_level(Level, Parser) -> Parser#parser{level = Level}.
add_to_text(L, _Parser, [#print_text{text=Text}=P|Parsed]) ->
NewText = <<Text/binary, L/binary>>,
[P#print_text{text=NewText}|Parsed];
add_to_text(L, Parser, Parsed) ->
[add_line(#print_text{text=L}, Parser)|Parsed].
inc_pos(Parser) ->
add_pos(Parser, 1).
add_pos(#parser{col = Col} = Parser, Offset) ->
Parser#parser{col = Col + Offset}.
new_line(#parser{row = Row} = Parser, N) ->
Parser#parser{row = Row + N, col = 1}.
new_line(Parser) ->
new_line(Parser, 1).
if_old_block_level(Parser) -> set_level(if_old_block, Parser).
for_old_block_level(Parser) -> set_level(for_old_block, Parser).
foreach_old_block_level(Parser) -> set_level(foreach_old_block, Parser).
while_old_block_level(Parser) -> set_level(while_old_block, Parser).
switch_old_block_level(Parser) -> set_level(switch_old_block, Parser).
if_block_level(Parser) -> set_level(if_block, Parser).
for_block_level(Parser) -> set_level(for_block, Parser).
foreach_block_level(Parser) -> set_level(foreach_block, Parser).
while_block_level(Parser) -> set_level(while_block, Parser).
switch_block_level(Parser) -> set_level(switch_block, Parser).
switch_label_level(Parser) -> set_level(switch_label, Parser).
normal_level(Parser) -> set_level(code, Parser).
code_block_level(Parser) -> set_level(code_block, Parser).
code_value_level(Parser) -> set_level(code_value, Parser).
code_statement_level(Parser) -> set_level(code_statement, Parser).
arg_level(Parser) -> set_level(arg, Parser).
array_level(Parser) -> set_level(array, Parser).
array_curly_level(Parser) -> set_level(array_curly, Parser).
array_def_level(Parser) -> (set_level(array_def, Parser))#parser{array_type = old}.
literal_level(Parser) -> set_level(literal, Parser).
enclosed_level(Parser) -> set_level(enclosed, Parser).
unclosed_level(Parser) -> set_level(unclosed, Parser).
get_line(#parser{row = Row, col = Col}) -> {{line, Row}, {column, Col}};
get_line(#array{line = Line}) -> Line;
get_line(#eval{line = Line}) -> Line;
get_line(#print{line = Line}) -> Line;
get_line(#print_text{line = Line}) -> Line;
get_line(#variable{line = Line}) -> Line;
get_line(#constant{line = Line}) -> Line;
get_line(#int{line = Line}) -> Line;
get_line(#float{line = Line}) -> Line;
get_line(#text_to_process{line = Line}) -> Line;
get_line(#text{line = Line}) -> Line;
get_line(#if_block{line = Line}) -> Line;
get_line(#assign{line = Line}) -> Line;
get_line(#array_element{line = Line}) -> Line;
get_line(#foreach{line = Line}) -> Line;
get_line(#operation{line = Line}) -> Line;
get_line(#concat{line = Line}) -> Line;
get_line(#while{line = Line}) -> Line;
get_line(#return{line = Line}) -> Line;
get_line(#function{line = Line}) -> Line;
get_line(#global{line = Line}) -> Line;
get_line(#ref{line = Line}) -> Line;
get_line(#switch{line = Line}) -> Line;
get_line(#switch_case{line = Line}) -> Line;
get_line(#call{line = Line}) -> Line;
get_line(#class{line = Line}) -> Line;
get_line(#class_method{line = Line}) -> Line;
get_line(#class_const{line = Line}) -> Line;
get_line(#class_attr{line = Line}) -> Line;
get_line(#instance{line = Line}) -> Line;
get_line(#cast{line = Line}) -> Line;
get_line(#throw{line = Line}) -> Line;
get_line(#try_catch{line = Line}) -> Line;
get_line(#catch_block{line = Line}) -> Line;
get_line(#clone{line = Line}) -> Line;
get_line(#command{line = Line}) -> Line.
add_line(true, _) -> true;
add_line(false, _) -> false;
add_line(X, {{line,Row},{column,Col}}) -> add_line(X, #parser{row = Row, col = Col});
add_line(#array{}=A, Parser) -> A#array{line = get_line(Parser)};
add_line(#eval{}=E, Parser) -> E#eval{line = get_line(Parser)};
add_line(#print{}=P, Parser) -> P#print{line = get_line(Parser)};
add_line(#print_text{}=P, Parser) -> P#print_text{line = get_line(Parser)};
add_line(#variable{}=V, Parser) -> V#variable{line = get_line(Parser)};
add_line(#constant{}=O, Parser) -> O#constant{line = get_line(Parser)};
add_line(#int{}=I, Parser) -> I#int{line = get_line(Parser)};
add_line(#float{}=F, Parser) -> F#float{line = get_line(Parser)};
add_line(#text_to_process{}=T, Parser) ->
T#text_to_process{line = get_line(Parser)};
add_line(#text{}=T, Parser) -> T#text{line = get_line(Parser)};
add_line(#if_block{}=I, Parser) -> I#if_block{line = get_line(Parser)};
add_line(#assign{}=A, Parser) -> A#assign{line = get_line(Parser)};
add_line(#array_element{}=A, Parser) ->
A#array_element{line = get_line(Parser)};
add_line(#foreach{}=F, Parser) -> F#foreach{line = get_line(Parser)};
add_line(#operation{}=O, Parser) -> O#operation{line = get_line(Parser)};
add_line(#concat{}=O, Parser) -> O#concat{line = get_line(Parser)};
add_line(#while{}=W, Parser) -> W#while{line = get_line(Parser)};
add_line(#return{}=Rt, Parser) -> Rt#return{line = get_line(Parser)};
add_line(#function{}=F, Parser) -> F#function{line = get_line(Parser)};
add_line(#global{}=G, Parser) -> G#global{line = get_line(Parser)};
add_line(#ref{}=Rf, Parser) -> Rf#ref{line = get_line(Parser)};
add_line(#switch{}=S, Parser) -> S#switch{line = get_line(Parser)};
add_line(#switch_case{}=S, Parser) -> S#switch_case{line = get_line(Parser)};
add_line(#call{}=Cl, Parser) -> Cl#call{line = get_line(Parser)};
add_line(#class{}=Cl, Parser) -> Cl#class{line = get_line(Parser)};
add_line(#class_method{}=CM, Parser) ->
CM#class_method{line = get_line(Parser)};
add_line(#class_const{}=CC, Parser) ->
CC#class_const{line = get_line(Parser)};
add_line(#class_attr{}=CA, Parser) ->
CA#class_attr{line = get_line(Parser)};
add_line({object, Expr}, Parser) -> {object, Expr, get_line(Parser)};
add_line({class, Expr}, Parser) -> {class, Expr, get_line(Parser)};
add_line(#instance{}=I, Parser) -> I#instance{line = get_line(Parser)};
add_line(#cast{}=Cs, Parser) -> Cs#cast{line = get_line(Parser)};
add_line(#throw{}=T, Parser) -> T#throw{line = get_line(Parser)};
add_line(#try_catch{}=T, Parser) -> T#try_catch{line = get_line(Parser)};
add_line(#catch_block{}=B, Parser) -> B#catch_block{line = get_line(Parser)};
add_line(#clone{}=L, Parser) -> L#clone{line = get_line(Parser)};
add_line(#command{}=Co, Parser) -> Co#command{line = get_line(Parser)}.
remove_spaces(<<SP:8, Rest/binary>>, Parser) when ?IS_SPACE(SP) ->
remove_spaces(Rest, inc_pos(Parser));
remove_spaces(<<SP:8, Rest/binary>>, Parser) when ?IS_NEWLINE(SP) ->
remove_spaces(Rest, new_line(Parser));
remove_spaces(<<>>, Parser) -> {<<>>, Parser};
remove_spaces(Rest, Parser) -> {Rest, Parser}.
get_print({Type, Value, _}, Parser) when
Type =:= int; Type =:= float; Type =:= text ->
add_line(#print_text{text = ephp_data:to_bin(Value)}, Parser);
get_print(Value, Parser) when is_atom(Value) ->
add_line(#print_text{text = ephp_data:to_bin(Value)}, Parser);
get_print(Expr, Parser) ->
add_line(#print{expression = Expr}, Parser).
-spec throw_error(atom(), parser(), errorlevel(), any()) -> no_return().
throw_error(Error, #parser{row = Row, col = Col}, ErrorLevel, Data) ->
Index = {{line, Row}, {column, Col}},
ephp_error:error({error, Error, Index, ErrorLevel, Data}).
throw_error(Error, Parser, Data) when is_binary(Data) ->
Output = iolist_to_binary(Data),
Size = min(byte_size(Output), 20),
LimitedData = <<Output:Size/binary, "...">>,
throw_error(Error, Parser, ?E_PARSE, LimitedData);
throw_error(Error, Parser, Data) when is_tuple(Data) ->
throw_error(Error, Parser, ?E_PARSE, Data).