/* * phc -- the open source PHP compiler * See license/README.license for licensing information * * Syntax definition (based on the PHP 5.1.0 grammar) */ %{ #include #include #include #include "generated/cmdline.h" #include #include #include #define YYERROR_VERBOSE #define YYPARSE_PARAM context #define YYLEX_PARAM context #ifdef YYSTYPE #undef YYSTYPE #endif #define YYSTYPE Object* extern struct gengetopt_args_info args_info; /* * Global yylex extracts the lexer from a parser context */ #include "parsing/PHP_parser.h" extern "C" int yylex(YYSTYPE *lvalp, void* context) { Parser_context* parserContext = static_cast(context); return parserContext->yylex(lvalp); } #define CONTEXT (static_cast(context)) #define yyerror(msg) CONTEXT->yyerror(msg) /* * We provide some macros to do conversion of synthesized attributes to * typed variables. Note that NULL arguments are left unchanged. * * CAST_AST(dest, src, type) converts "src" to "dest" of type "type". * It asserts that the conversion is successful. * * CAST_STR(dest, src, type) converts "src" to "dest" of type "type", * where type must be a Token type. It casts "src" to (String*) and then * uses the constructor of "dest" to do the conversion. * * CAST_UNSAFE is like ARG_CAST, but omits the assertion. * * CAST_STATEMENT_VECTOR(dest, src) is a specialised version of * ARG_CAST that tries to convert "src" to "dest" of type * AST_statement_list*. If this fails, it asserts that "src" is * of type AST_statement*, and creates a new vector and adds "src" * to this vector. If "src" is NULL, it returns an empty vector. */ static void assert_line_number(Object* obj) { AST_node* node = dynamic_cast(obj); printf("Internal error: source type is %s ", typeid(*obj).name()); if(node) printf("(source line %d)\n", node->get_line_number()); else printf("(source line unknown)\n"); } #define CAST_AST(dest, src, type) \ type* dest = dynamic_cast(src); \ if(src && !dest) assert_line_number(src); \ assert(!src || dest); #define CAST_STR(dest, src, type) \ type* dest; { \ String* temp = dynamic_cast(src); \ assert(temp != NULL); \ dest = new type(temp); } #define CAST_UNSAFE(dest, src, type) \ type* dest = dynamic_cast(src); #define CAST_STATEMENT_VECTOR(dest, src) \ AST_statement_list* dest; \ if(!src) \ dest = new AST_statement_list; \ else if(dynamic_cast(src)) \ { \ dest = new AST_statement_list; \ dest->push_back(dynamic_cast(src)); \ } \ else \ { \ assert(dynamic_cast(src)); \ dest = dynamic_cast(src); \ } /* * Some semantic checks */ #define EXPECT_VARIABLE(attr) \ if(!var) \ { \ AST_node* node = dynamic_cast(attr); \ error(ERR_EXPECT_VAR, node->get_filename(), node->get_line_number()); \ } /* * Define macro to add line numbers and comments to nodes */ #define NEW(class, args)\ dynamic_cast(copy_state(new class args, context)) static AST_node* copy_state(AST_node* node, void* context) { PHP_parser* ctxt = (PHP_parser*) context; node->attrs->set("phc.line_number", new Integer(ctxt->php_lexer->source_line)); node->attrs->set("phc.filename", ctxt->filename->deep_clone()); AST_commented_node* cnode; cnode = dynamic_cast(node); if(cnode) { List* cmnts = cnode->get_comments(); cmnts->push_back_all(&ctxt->php_lexer->last_comments); ctxt->php_lexer->last_comments.clear(); ctxt->php_lexer->last_commented_node = cnode; } return node; } /* * PHT */ #define MAKE_ELEMENT_NAME(dest,src) CAST_STR(name,src,Token_tag_name); \ dest = NEW(AST_xml_element_name, (false,NULL,false,name)); %} %pure_parser /* * Define token types */ // Keywords %token K_AND %token K_OR %token K_XOR %token K___FILE__ %token K_EXCEPTION %token K___LINE__ %token K_ARRAY %token K_AS %token K_BREAK %token K_CASE %token K_CLASS %token K_CONST %token K_CONTINUE %token K_DECLARE %token K_DEFAULT %token K_DIE %token K_DO %token K_ECHO %token K_ELSE %token K_ELSEIF %token K_EMPTY %token K_ENDDECLARE %token K_ENDFOR %token K_ENDFOREACH %token K_ENDIF %token K_ENDSWITCH %token K_ENDWHILE %token K_EVAL %token K_EXIT %token K_EXTENDS %token K_FOR %token K_FOREACH %token K_FUNCTION %token K_GLOBAL %token K_IF %token K_INCLUDE %token K_INCLUDE_ONCE %token K_INSTANCEOF %token K_ISSET %token K_LIST %token K_NEW %token K_PRINT %token K_REQUIRE %token K_REQUIRE_ONCE %token K_RETURN %token K_STATIC %token K_SWITCH %token K_UNSET %token K_USE %token K_VAR %token K_WHILE %token K___FUNCTION__ %token K___CLASS__ %token K___METHOD__ %token K_FINAL %token K_PHP_USER_FILTER %token K_INTERFACE %token K_IMPLEMENTS %token K_PUBLIC %token K_PRIVATE %token K_PROTECTED %token K_ABSTRACT %token K_CLONE %token K_TRY %token K_CATCH %token K_THROW %token K_CFUNCTION %token K_OLD_FUNCTION %token K_HALT_COMPILER // Constants %token C_TRUE %token C_FALSE %token C_NULL // Operators %token O_EQEQ %token O_EQEQEQ %token O_NOTEQ %token O_NOTEQEQ %token O_LE %token O_GE %token O_INC %token O_DEC %token O_DOUBLEARROW %token O_SINGLEARROW %token O_SL %token O_SR %token O_COLONCOLON %token O_LOGICOR %token O_LOGICAND %token O_PLUSEQ %token O_MINUSEQ %token O_MULEQ %token O_DIVEQ %token O_CONCATEQ %token O_MODEQ %token O_ANDEQ %token O_OREQ %token O_XOREQ %token O_SLEQ %token O_SREQ %token O_MAGIC_CONCAT // Casts %token CAST_INT %token CAST_REAL %token CAST_STRING %token CAST_ARRAY %token CAST_OBJECT %token CAST_BOOL %token CAST_UNSET // Other %token STRING %token IDENT %token XML_IDENT %token VARIABLE %token INT %token REAL %token INLINE_HTML %token INVALID_TOKEN /* * Resolve conflicts */ %expect 2 %left K_INCLUDE K_INCLUDE_ONCE K_EVAL K_REQUIRE K_REQUIRE_ONCE %left ',' %left K_OR K_XOR K_AND %right K_PRINT %left '=' O_PLUSEQ O_MINUSEQ O_MULEQ O_DIVEQ O_CONCATEQ O_MODEQ O_ANDEQ O_OREQ O_XOREQ O_SLEQ O_SREQ %left '?' ':' %left O_LOGICOR O_LOGICAND %left '|' '^' '&' %nonassoc O_EQEQ O_NOTEQ O_EQEQEQ O_NOTEQEQ %nonassoc '<' O_LE '>' O_GE %left O_SL O_SR %left '+' '-' '.' O_MAGIC_CONCAT %left '*' '/' '%' %right '!' %nonassoc K_INSTANCEOF %right '~' O_INC O_DEC CAST_INT CAST_REAL CAST_STRING CAST_ARRAY CAST_OBJECT CAST_BOOL CAST_UNSET '@' %right '[' %nonassoc K_NEW K_CLONE %left K_ELSEIF %left K_ELSE %left K_ENDIF %right K_STATIC K_ABSTRACT K_FINAL K_PRIVATE K_PROTECTED K_PUBLIC %% /* * Synthesises an AST_php_script */ start: top_statement_list { CAST_AST(body, $1, AST_statement_list); AST_signature* signature = NEW(AST_signature, ("%run%")); signature->method_mod = AST_method_mod::new_STATIC(); CONTEXT->php_script->get_class_def("%MAIN%")->add_member(NEW(AST_method, (signature, body))); // Make %MAIN% the last class definition AST_class_def* main = CONTEXT->php_script->class_defs->front(); CONTEXT->php_script->class_defs->pop_front(); CONTEXT->php_script->class_defs->push_back(main); } ; /* * Synthesises a AST_statement_list */ top_statement_list: top_statement_list top_statement { CAST_AST(statement_list, $1, AST_statement_list); // Ignore null statements if($2) { CAST_UNSAFE(statement, $2, AST_statement); if(statement == NULL) { CAST_AST(statements, $2, AST_statement_list); statement_list->push_back_all(statements); } else { statement_list->push_back(statement); } } $$ = statement_list; } | /* empty */ { $$ = new AST_statement_list; } ; /* * Synthesises a AST_statement_list, an AST_statement or NULL * * Mimicks inner_statement */ top_statement: statement { $$ = $1; } | function_declaration_statement { $$ = $1; } | class_declaration_statement { $$ = $1; } | K_HALT_COMPILER '(' ')' ';' { // Note: we never generate K_HALT_COMPILER $$ = NULL; } ; /* * Synthesises a AST_statement_list * * Mimicks top_statement_list */ inner_statement_list: inner_statement_list inner_statement { CAST_AST(statement_list, $1, AST_statement_list); // Ignore null statements if($2) { CAST_UNSAFE(statement, $2, AST_statement); if(statement == NULL) { CAST_AST(statements, $2, AST_statement_list); statement_list->push_back_all(statements); } else { statement_list->push_back(statement); } } $$ = statement_list; } | /* empty */ { $$ = new AST_statement_list; } ; /* * Synthesises a AST_statement_list, an AST_statement or NULL */ inner_statement: statement { $$ = $1; } | function_declaration_statement { $$ = $1; } | class_declaration_statement { $$ = $1; } | K_HALT_COMPILER '(' ')' ';' { // Note: we never generate K_HALT_COMPILER $$ = NULL; } ; /* * Synthesises a AST_statement_list or an AST_statement */ statement: unticked_statement { $$ = $1; } ; /* * Synthesises an AST_if */ keyword_if: K_IF { $$ = NEW(AST_if, ()); } ; /* * Synthesises an AST_foreach */ keyword_foreach: K_FOREACH { $$ = NEW(AST_foreach, ()); } ; /* * Synthesises a AST_statement_list or an AST_statement */ unticked_statement: '{' inner_statement_list '}' { $$ = $2; } | keyword_if '(' expr ')' statement elseif_list else_single { CAST_AST(new_if, $1, AST_if); CAST_AST(expr, $3, AST_expr); CAST_STATEMENT_VECTOR(iftrue, $5); CAST_AST(elseif, $6, AST_if); CAST_STATEMENT_VECTOR(iffalse, $7); new_if->expr = expr; new_if->iftrue = iftrue; if(elseif) { AST_statement_list* new_false = new AST_statement_list; new_false->push_back(elseif); /* * Nest the elseif part * * We must add it to the most deeply nested if we can find * Note that _if_ any of the nested ifs (elseifs) has a false * branch, then this consists of a single instruction, an AST_if*. * So, the dynamic_cast is guaranteed to succeed. * * This is similar to the code in elseif_list. */ while(!elseif->iffalse->empty()) elseif = dynamic_cast(elseif->iffalse->front()); elseif->iffalse = iffalse; new_if->iffalse = new_false; } else { new_if->iffalse = iffalse; } $$ = new_if; } | keyword_if '(' expr ')' ':' inner_statement_list new_elseif_list new_else_single K_ENDIF ';' { // Duplication of logic above for K_IF. CAST_AST(new_if, $1, AST_if); CAST_AST(expr, $3, AST_expr); CAST_STATEMENT_VECTOR(iftrue, $6); CAST_AST(elseif, $7, AST_if); CAST_STATEMENT_VECTOR(iffalse, $8); new_if->expr = expr; new_if->iftrue = iftrue; if(elseif) { AST_statement_list* new_false = new AST_statement_list; new_false->push_back(elseif); while(!elseif->iffalse->empty()) elseif = dynamic_cast(elseif->iffalse->front()); elseif->iffalse = iffalse; new_if->iffalse = new_false; } else { new_if->iffalse = iffalse; } $$ = new_if; } | K_WHILE {$1 = NEW(AST_while, ());} '(' expr ')' while_statement { CAST_AST(new_while, $1, AST_while); CAST_AST(expr, $4, AST_expr); CAST_AST(body, $6, AST_statement_list); new_while->expr = expr; new_while->statements = body; $$ = new_while; } | K_DO {$1 = NEW(AST_do, ());} statement K_WHILE '(' expr ')' ';' { CAST_AST(new_do, $1, AST_do); CAST_STATEMENT_VECTOR(body, $3); CAST_AST(expr, $6, AST_expr); new_do->statements = body; new_do->expr = expr; $$ = new_do; } | K_FOR {$1 = NEW(AST_for, ());} '(' for_expr ';' for_expr ';' for_expr ')' for_statement { CAST_AST(new_for, $1, AST_for); CAST_AST(init, $4, AST_expr); CAST_AST(cond, $6, AST_expr); CAST_AST(incr, $8, AST_expr); CAST_STATEMENT_VECTOR(stmts, $10); new_for->init = init; new_for->cond = cond; new_for->incr = incr; new_for->statements = stmts; $$ = new_for; } | K_SWITCH {$1 = NEW(AST_switch, ());} '(' expr ')' switch_case_list { CAST_AST(new_switch, $1, AST_switch); CAST_AST(expr, $4, AST_expr); CAST_AST(cases, $6, AST_switch_case_list); new_switch->expr = expr; new_switch->switch_cases = cases; $$ = new_switch; } | K_BREAK ';' { $$ = NEW(AST_break, (NULL)); } | K_BREAK expr ';' { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_break, (expr)); } | K_CONTINUE ';' { $$ = NEW(AST_continue, (NULL)); } | K_CONTINUE expr ';' { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_continue, (expr)); } | K_RETURN ';' { $$ = NEW(AST_return, (NULL)); } | K_RETURN expr_without_variable ';' { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_return, (expr)); } | K_RETURN variable ';' { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_return, (expr)); } | K_GLOBAL global_var_list ';' { CAST_AST(list, $2, List); AST_statement_list* globals = new AST_statement_list; List::const_iterator i; for(i = list->begin(); i != list->end(); i++) { AST_variable* gv = NEW(AST_variable, (*i)); // global var gv->target = NEW(Token_class_name, (new String("%MAIN%"))); AST_variable_name* iclone = (*i)->deep_clone(); AST_variable* lv = NEW(AST_variable, (iclone)); // local var AST_assignment* gd = NEW(AST_assignment, (lv, true, gv)); // global declaration gd->attrs->set("phc.unparser.is_global_stmt", new Boolean(true)); globals->push_back(NEW(AST_eval_expr, (gd))); } $$ = globals; } | K_STATIC static_var_list ';' { $$ = $2; } | K_ECHO echo_expr_list ';' { $$ = $2; } | INLINE_HTML { CAST_AST(str, $1, String); Token_string* scalar = NEW(Token_string, (str, str)); AST_expr* fn = NEW(AST_method_invocation, ("echo", scalar)); $$ = NEW(AST_eval_expr, (fn)); } | expr ';' { CAST_AST(expr, $1, AST_expr); $$ = NEW(AST_eval_expr, (expr)); } | K_USE use_filename ';' { CAST_AST(expr, $2, Token_string); AST_method_invocation* fn = NEW(AST_method_invocation, ("use", expr)); $$ = NEW(AST_eval_expr, (fn)); } | K_UNSET '(' unset_variables ')' ';' { CAST_AST(vars, $3, List); AST_statement_list* statements = new AST_statement_list; List::const_iterator i; for(i = vars->begin(); i != vars->end(); i++) statements->push_back(NEW(AST_unset, (*i))); $$ = statements; } | keyword_foreach '(' variable K_AS foreach_variable foreach_optional_arg ')' foreach_statement { CAST_AST(new_foreach, $1, AST_foreach); CAST_AST(expr, $3, AST_expr); CAST_AST(var1, $5, AST_variable); CAST_AST(var2, $6, AST_variable); CAST_STATEMENT_VECTOR(body, $8); new_foreach->expr = expr; new_foreach->statements = body; if(var2) { if(var1->attrs->get_boolean("phc.parser.is_ref")->value()) { error(ERR_FOREACH_KEY_REF, var1->get_filename(), var1->get_line_number()); } new_foreach->key = var1; new_foreach->is_ref = var2->attrs->get_boolean("phc.parser.is_ref")->value(); new_foreach->val = var2; } else { new_foreach->key = NULL; new_foreach->is_ref = var1->attrs->get_boolean("phc.parser.is_ref")->value(); new_foreach->val = var1; } $$ = new_foreach; } | keyword_foreach '(' expr_without_variable K_AS w_variable foreach_optional_arg ')' foreach_statement { // Duplication of the logic in the previous rule CAST_AST(new_foreach, $1, AST_foreach); CAST_AST(expr, $3, AST_expr); CAST_AST(var1, $5, AST_variable); CAST_AST(var2, $6, AST_variable); CAST_STATEMENT_VECTOR(body, $8); new_foreach->expr = expr; new_foreach->statements = body; if(var2) { if(var1->attrs->get_boolean("phc.parser.is_ref")->value()) { error(ERR_FOREACH_KEY_REF, var1->get_filename(), var1->get_line_number()); } new_foreach->key = var1; new_foreach->is_ref = var2->attrs->get_boolean("phc.parser.is_ref")->value(); new_foreach->val = var2; } else { new_foreach->key = NULL; new_foreach->is_ref = var1->attrs->get_boolean("phc.parser.is_ref")->value(); new_foreach->val = var1; } $$ = new_foreach; } | K_DECLARE {$1 = NEW(AST_declare, ());} '(' declare_list ')' declare_statement { CAST_AST(new_declare, $1, AST_declare); CAST_AST(directives, $4, AST_directive_list); CAST_STATEMENT_VECTOR(statements, $6); new_declare->directives = directives; new_declare->statements = statements; $$ = new_declare; } | ';' { $$ = NULL; } | K_TRY {$1 = NEW(AST_try, ());} '{' inner_statement_list '}' K_CATCH {$6 = NEW(AST_catch, ());} '(' fully_qualified_class_name VARIABLE ')' '{' inner_statement_list '}' additional_catches { CAST_AST(new_try, $1, AST_try); CAST_STATEMENT_VECTOR(try_stat, $4); CAST_AST(new_catch, $6, AST_catch); CAST_AST(class_name, $9, Token_class_name); CAST_STR(var, $10, Token_variable_name); CAST_STATEMENT_VECTOR(catch_stat, $13); CAST_AST(additional_catches, $15, AST_catch_list); new_catch->class_name = class_name; new_catch->variable_name = var; new_catch->statements = catch_stat; AST_catch_list* all_catches = new AST_catch_list; all_catches->push_back(new_catch); all_catches->push_back_all(additional_catches); new_try->statements = try_stat; new_try->catches = all_catches; $$ = new_try; } | K_THROW expr ';' { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_throw, (expr)); } | '<' {$1 = NEW(AST_xml_element,());} xml_element_name statement { CAST_AST(new_element,$1,AST_xml_element); CAST_AST(xml_element_name, $3, AST_xml_element_name); CAST_STATEMENT_VECTOR(tag_stat, $4); new_element->xml_element_name = xml_element_name; new_element->statements = tag_stat; $$ = new_element; } | '<' '?' {$2 = NEW(AST_xml_processing_instruction,());} xml_element_name statement { CAST_AST(new_element,$2,AST_xml_processing_instruction); CAST_AST(xml_element_name, $4, AST_xml_element_name); CAST_STATEMENT_VECTOR(tag_stat, $5); new_element->xml_element_name = xml_element_name; new_element->statements = tag_stat; $$ = new_element; } | '&' {$1 = NEW(AST_xml_element_attribute,());} xml_element_name atribute_assignment expr ';' { CAST_AST(new_att,$1,AST_xml_element_attribute); CAST_AST(xml_element_name, $3, AST_xml_element_name); CAST_AST(expr, $5, AST_expr); new_att->xml_element_name = xml_element_name; new_att->expr = expr; $$ = new_att; } | '?' escaped_echo_expr_list ';' { $$ = $2; } ; /* * */ atribute_assignment: /* empty */ | '=' ; xml_element_name: xml_element_name_fragment { $$ = $1; } | xml_element_name_fragment ':' xml_element_name_fragment { CAST_AST(xml_namespace, $1, AST_xml_element_name); CAST_AST(xml_name, $3, AST_xml_element_name); $$ = NEW(AST_xml_element_name , ( xml_namespace->is_var , xml_namespace->tag_name , xml_name->is_var , xml_name->tag_name ) ); } ; xml_element_name_fragment: VARIABLE { CAST_STR(name,$1,Token_tag_name); $$ = NEW(AST_xml_element_name, (false,NULL,true,name)); } | IDENT { MAKE_ELEMENT_NAME($$,$1) } | XML_IDENT { MAKE_ELEMENT_NAME($$,$1) } | K_AND { MAKE_ELEMENT_NAME($$,$1) } | K_OR { MAKE_ELEMENT_NAME($$,$1) } | K_XOR { MAKE_ELEMENT_NAME($$,$1) } | K___FILE__ { MAKE_ELEMENT_NAME($$,$1) } | K___LINE__ { MAKE_ELEMENT_NAME($$,$1) } | K_ARRAY { MAKE_ELEMENT_NAME($$,$1) } | K_AS { MAKE_ELEMENT_NAME($$,$1) } | K_BREAK { MAKE_ELEMENT_NAME($$,$1) } | K_CASE { MAKE_ELEMENT_NAME($$,$1) } | K_CLASS { MAKE_ELEMENT_NAME($$,$1) } | K_CONST { MAKE_ELEMENT_NAME($$,$1) } | K_CONTINUE { MAKE_ELEMENT_NAME($$,$1) } | K_DECLARE { MAKE_ELEMENT_NAME($$,$1) } | K_DEFAULT { MAKE_ELEMENT_NAME($$,$1) } | K_DO { MAKE_ELEMENT_NAME($$,$1) } | K_ECHO { MAKE_ELEMENT_NAME($$,$1) } | K_ELSE { MAKE_ELEMENT_NAME($$,$1) } | K_ELSEIF { MAKE_ELEMENT_NAME($$,$1) } | K_EMPTY { MAKE_ELEMENT_NAME($$,$1) } | K_ENDDECLARE { MAKE_ELEMENT_NAME($$,$1) } | K_ENDFOR { MAKE_ELEMENT_NAME($$,$1) } | K_ENDFOREACH { MAKE_ELEMENT_NAME($$,$1) } | K_ENDIF { MAKE_ELEMENT_NAME($$,$1) } | K_ENDSWITCH { MAKE_ELEMENT_NAME($$,$1) } | K_ENDWHILE { MAKE_ELEMENT_NAME($$,$1) } | K_EVAL { MAKE_ELEMENT_NAME($$,$1) } | K_EXIT { MAKE_ELEMENT_NAME($$,$1) } | K_EXTENDS { MAKE_ELEMENT_NAME($$,$1) } | K_FOR { MAKE_ELEMENT_NAME($$,$1) } | K_FOREACH { MAKE_ELEMENT_NAME($$,$1) } | K_FUNCTION { MAKE_ELEMENT_NAME($$,$1) } | K_GLOBAL { MAKE_ELEMENT_NAME($$,$1) } | K_IF { MAKE_ELEMENT_NAME($$,$1) } | K_INCLUDE { MAKE_ELEMENT_NAME($$,$1) } | K_INCLUDE_ONCE { MAKE_ELEMENT_NAME($$,$1) } | K_INSTANCEOF { MAKE_ELEMENT_NAME($$,$1) } | K_ISSET { MAKE_ELEMENT_NAME($$,$1) } | K_LIST { MAKE_ELEMENT_NAME($$,$1) } | K_NEW { MAKE_ELEMENT_NAME($$,$1) } | K_PRINT { MAKE_ELEMENT_NAME($$,$1) } | K_REQUIRE { MAKE_ELEMENT_NAME($$,$1) } | K_REQUIRE_ONCE { MAKE_ELEMENT_NAME($$,$1) } | K_RETURN { MAKE_ELEMENT_NAME($$,$1) } | K_STATIC { MAKE_ELEMENT_NAME($$,$1) } | K_SWITCH { MAKE_ELEMENT_NAME($$,$1) } | K_UNSET { MAKE_ELEMENT_NAME($$,$1) } | K_USE { MAKE_ELEMENT_NAME($$,$1) } | K_VAR { MAKE_ELEMENT_NAME($$,$1) } | K_WHILE { MAKE_ELEMENT_NAME($$,$1) } | K___FUNCTION__ { MAKE_ELEMENT_NAME($$,$1) } | K___CLASS__ { MAKE_ELEMENT_NAME($$,$1) } | K___METHOD__ { MAKE_ELEMENT_NAME($$,$1) } | K_FINAL { MAKE_ELEMENT_NAME($$,$1) } | K_INTERFACE { MAKE_ELEMENT_NAME($$,$1) } | K_IMPLEMENTS { MAKE_ELEMENT_NAME($$,$1) } | K_PUBLIC { MAKE_ELEMENT_NAME($$,$1) } | K_PRIVATE { MAKE_ELEMENT_NAME($$,$1) } | K_PROTECTED { MAKE_ELEMENT_NAME($$,$1) } | K_ABSTRACT { MAKE_ELEMENT_NAME($$,$1) } | K_CLONE { MAKE_ELEMENT_NAME($$,$1) } | K_TRY { MAKE_ELEMENT_NAME($$,$1) } | K_CATCH { MAKE_ELEMENT_NAME($$,$1) } | K_THROW { MAKE_ELEMENT_NAME($$,$1) } | K_CFUNCTION { MAKE_ELEMENT_NAME($$,$1) } | K_OLD_FUNCTION { MAKE_ELEMENT_NAME($$,$1) } | C_TRUE { MAKE_ELEMENT_NAME($$,$1) } | C_FALSE { MAKE_ELEMENT_NAME($$,$1) } | C_NULL { MAKE_ELEMENT_NAME($$,$1) } ; /* * Synthesises a AST_statement_list * * "echo foo, bar;" is translated into "echo(foo); echo(bar);" */ escaped_echo_expr_list: escaped_echo_expr_list ',' expr { CAST_AST(echo_list, $1, AST_statement_list); CAST_AST(param, $3, AST_expr); AST_method_invocation* fn = NEW(AST_method_invocation, ("?", param)); echo_list->push_back(NEW(AST_eval_expr, (fn))); $$ = echo_list; } | expr { CAST_AST(param, $1, AST_expr); AST_statement_list* echo_list = new AST_statement_list; AST_method_invocation* fn = NEW(AST_method_invocation, ("?", param)); echo_list->push_back(NEW(AST_eval_expr, (fn))); $$ = echo_list; } ; /* * Synthesises a AST_catch_list */ additional_catches: non_empty_additional_catches { $$ = $1; } | /* empty */ { AST_catch_list* catches = new AST_catch_list; $$ = catches; } ; /* * Synthesises a AST_catch_list */ non_empty_additional_catches: additional_catch { CAST_AST(c, $1, AST_catch); AST_catch_list* catches = new AST_catch_list; catches->push_back(c); $$ = catches; } | non_empty_additional_catches additional_catch { CAST_AST(catches, $1, AST_catch_list); CAST_AST(c, $2, AST_catch); catches->push_back(c); $$ = catches; } ; /* * Synthesises an AST_catch* */ additional_catch: K_CATCH {$1 = NEW(AST_catch, ());} '(' fully_qualified_class_name VARIABLE ')' '{' inner_statement_list '}' { CAST_AST(new_catch, $1, AST_catch); CAST_AST(class_name, $4, Token_class_name); CAST_STR(var, $5, Token_variable_name); CAST_STATEMENT_VECTOR(stat, $8); new_catch->class_name = class_name; new_catch->variable_name = var; new_catch->statements = stat; $$ = new_catch; } ; /* * Synthesises a List */ unset_variables: unset_variable { CAST_AST(var, $1, AST_variable); List* vars = new List; vars->push_back(var); $$ = vars; } | unset_variables ',' unset_variable { CAST_AST(vars, $1, List); CAST_AST(var, $3, AST_variable); vars->push_back(var); $$ = vars; } ; /* * Synthesises an AST_variable */ unset_variable: variable { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); $$ = var; } ; /* * Synthesises a Token_string */ use_filename: STRING { CAST_AST(str, $1, String); $$ = NEW(Token_string, (str, str)); } | '(' STRING ')' { CAST_AST(str, $2, String); $$ = NEW(Token_string, (str, str)) } ; /* * Synthesises nothing * * See unticked_function_declaration_statement */ function_declaration_statement: unticked_function_declaration_statement { $$ = $1; } ; /* * Synthesises nothing * * See unticked_class_declaration_statement */ class_declaration_statement: unticked_class_declaration_statement { $$ = $1; } ; /* * Synthesises an Integer (0 for no "&" or 1 for "&") */ is_reference: /* empty */ { $$ = new Integer(0); } | '&' { $$ = new Integer(1); } ; /* * Synthesises nothing. * * The function is added directly to the %MAIN% class. This is necessary, * because the structure of the tree as we build it (consisting of classes * only) disagrees with the structure of the PHP grammar here. */ unticked_function_declaration_statement: K_FUNCTION {$1 = NEW(AST_method, ());} is_reference IDENT {CONTEXT->current_method = dynamic_cast($4);} '(' parameter_list ')' '{' inner_statement_list '}' { CAST_AST(fn, $1, AST_method); CAST_AST(is_ref, $3, Integer); CAST_STR(name, $4, Token_method_name); CAST_AST(params, $7, AST_formal_parameter_list); CAST_AST(body, $10, AST_statement_list); AST_signature* signature = NEW(AST_signature, (AST_method_mod::new_STATIC(), is_ref->value(), name, params)); fn->signature = signature; fn->statements = body; CONTEXT->php_script->get_class_def("%MAIN%")->add_member(fn); $$ = NULL; } ; /* * Synthesises nothing * * The class or interface declaration is added directly to the script, * because as fas as PHP is concerned, this is a statement (but we disagree) */ unticked_class_declaration_statement: class_entry_type IDENT {CONTEXT->current_class = dynamic_cast($2);} extends_from implements_list '{' class_statement_list '}' { CAST_AST(new_class, $1, AST_class_def); CAST_STR(name, $2, Token_class_name); CAST_AST(extends, $4, Token_class_name); CAST_AST(implements, $5, Token_interface_name_list); CAST_AST(members, $7, AST_member_list); new_class->class_name = name; new_class->extends = extends; new_class->implements = implements; new_class->members = members; CONTEXT->php_script->class_defs->push_back(new_class); $$ = NULL; } | interface_entry IDENT interface_extends_list '{' class_statement_list '}' { CAST_AST(idef, $1, AST_interface_def); CAST_STR(name, $2, Token_interface_name); CAST_AST(extends, $3, Token_interface_name_list); CAST_AST(members, $5, AST_member_list); idef->interface_name = name; idef->extends = extends; idef->members = members; CONTEXT->php_script->interface_defs->push_back(idef); $$ = NULL; } ; /* * Synthesises an AST_class_def * * We generate the AST_class_def here to that it gets generated immediately * after having seen the "class" keyword, rather than after having seen the * closing curly brace. * * This means that the line number for the AST_class_def object will be * recorded as the _starting_ line number of the class, rather than the * final line number; moreover, any class comments will also be recorded * correctly. */ class_entry_type: K_CLASS { AST_class_mod* mod = NEW(AST_class_mod, (false, false)); $$ = NEW(AST_class_def, (mod)); } | K_ABSTRACT K_CLASS { AST_class_mod* mod = NEW(AST_class_mod, (true, false)); $$ = NEW(AST_class_def, (mod)); } | K_FINAL K_CLASS { AST_class_mod* mod = NEW(AST_class_mod, (false, true)); $$ = NEW(AST_class_def, (mod)); } ; /* * Synthesises a Token_class_name or NULL */ extends_from: /* empty */ { $$ = NULL; } | K_EXTENDS fully_qualified_class_name { $$ = $2; } ; /* * Synthesises nothing */ interface_entry: K_INTERFACE { $$ = NEW(AST_interface_def, ()); } ; /* * Synthesises a Token_interface_name_list */ interface_extends_list: /* empty */ { $$ = new Token_interface_name_list; } | K_EXTENDS interface_list { $$ = $2; } ; /* * Syntesises a Token_interface_name_list * * Vector contains no elements if the class implements no interfaces */ implements_list: /* empty */ { $$ = new Token_interface_name_list; } | K_IMPLEMENTS interface_list { $$ = $2; } ; /* * Synthesises a Token_interface_name_list */ interface_list: fully_qualified_class_name { CAST_AST(class_name, $1, Token_class_name); Token_interface_name* ifn = NEW(Token_interface_name, (class_name->value)); Token_interface_name_list* names; names = new Token_interface_name_list; names->push_back(ifn); $$ = names; } | interface_list ',' fully_qualified_class_name { CAST_AST(names, $1, Token_interface_name_list); CAST_AST(class_name, $3, Token_class_name); Token_interface_name* ifn = NEW(Token_interface_name, (class_name->value)); names->push_back(ifn); $$ = names; } ; /* * Synthesises an AST_variable or NULL */ foreach_optional_arg: /* empty */ { $$ = NULL; } | O_DOUBLEARROW foreach_variable { $$ = $2; } ; /* * Synthesises an AST_variable */ foreach_variable: w_variable { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); $$ = var; } | '&' w_variable { CAST_UNSAFE(var, $2, AST_variable); EXPECT_VARIABLE($2); var->attrs->set("phc.parser.is_ref", new Boolean(true)); $$ = var; } ; /* * Synthesises an AST_statement or AST_statement_list */ for_statement: statement { $$ = $1; } | ':' inner_statement_list K_ENDFOR ';' { $$ = $2; } ; /* * Synthesises an AST_statement* or AST_statement_list */ foreach_statement: statement { $$ = $1; } | ':' inner_statement_list K_ENDFOREACH ';' { $$ = $2; } ; /* * Synthesises an AST_statement* or AST_statement_list */ declare_statement: statement { $$ = $1; } | ':' inner_statement_list K_ENDDECLARE ';' { $$ = $2; } ; /* * Synthesises a AST_directive_list */ declare_list: IDENT '=' static_scalar { CAST_STR(name, $1, Token_directive_name); CAST_AST(value, $3, AST_expr); AST_directive_list* dirs = new AST_directive_list; AST_directive* dir = NEW(AST_directive, (name, value)); dirs->push_back(dir); $$ = dirs; } | declare_list ',' IDENT '=' static_scalar { CAST_AST(dirs, $1, AST_directive_list); CAST_STR(name, $3, Token_directive_name); CAST_AST(value, $5, AST_expr); AST_directive* dir = NEW(AST_directive, (name, value)); dirs->push_back(dir); $$ = dirs; } ; /* * Synthesises a AST_switch_case_list */ switch_case_list: '{' case_list '}' { $$ = $2; } | '{' ';' case_list '}' { $$ = $3; } | ':' case_list K_ENDSWITCH ';' { $$ = $2; } | ':' ';' case_list K_ENDSWITCH ';' { $$ = $3; } ; /* * Synthesises a AST_switch_case_list */ case_list: /* empty */ { $$ = new AST_switch_case_list; } | case_list K_CASE expr case_separator {$2 = NEW(AST_switch_case, ());} inner_statement_list { CAST_AST(switch_case, $2, AST_switch_case); CAST_AST(cases, $1, AST_switch_case_list); CAST_AST(expr, $3, AST_expr); CAST_AST(body, $6, AST_statement_list); switch_case->statements = body; switch_case->expr = expr; cases->push_back(switch_case); $$ = cases; } | case_list K_DEFAULT case_separator {$2 = NEW(AST_switch_case, ());} inner_statement_list { CAST_AST(switch_case, $2, AST_switch_case); CAST_AST(cases, $1, AST_switch_case_list); CAST_AST(body, $5, AST_statement_list); switch_case->statements = body; switch_case->expr = NULL; cases->push_back(switch_case); $$ = cases; } ; /* * No semantic value */ case_separator: ':' { $$ = NULL; } | ';' { $$ = NULL; } ; /* * Synthesises a AST_statement_list */ while_statement: statement { CAST_STATEMENT_VECTOR(body, $1); $$ = body; } | ':' inner_statement_list K_ENDWHILE ';' { $$ = $2; } ; /* * Synthesises an AST_if */ elseif_list: /* empty */ { $$ = NULL; } | elseif_list K_ELSEIF {$2 = NEW(AST_if, ());} '(' expr ')' statement { CAST_AST(elseif, $1, AST_if); CAST_AST(nested_if, $2, AST_if); CAST_AST(expr, $5, AST_expr); CAST_STATEMENT_VECTOR(stat, $7); AST_statement_list* empty = new AST_statement_list; nested_if->expr = expr; nested_if->iftrue = stat; nested_if->iffalse = empty; nested_if->attrs->set("phc.unparser.is_elseif", new Boolean(true)); if(elseif) { AST_statement_list* iffalse = new AST_statement_list; iffalse->push_back(nested_if); /* * Nest the elseif. * This is similar to the code in unticked_statement for K_IF. */ AST_if* p = elseif; while(!p->iffalse->empty()) p = dynamic_cast(p->iffalse->front()); p->iffalse = iffalse; $$ = elseif; } else { $$ = nested_if; } } ; /* * Synthesises an AST_if * * Duplication of elseif_list functionality */ new_elseif_list: /* empty */ { $$ = NULL; } | new_elseif_list K_ELSEIF {$2 = NEW(AST_if, ());} '(' expr ')' ':' inner_statement_list { CAST_AST(elseif, $1, AST_if); CAST_AST(nested_if, $2, AST_if); CAST_AST(expr, $5, AST_expr); CAST_STATEMENT_VECTOR(stat, $8); AST_statement_list* empty = new AST_statement_list; nested_if->expr = expr; nested_if->iftrue = stat; nested_if->iffalse = empty; nested_if->attrs->set("phc.unparser.is_elseif", new Boolean(true)); if(elseif) { AST_statement_list* iffalse = new AST_statement_list; iffalse->push_back(nested_if); AST_if* p = elseif; while(!p->iffalse->empty()) p = dynamic_cast(p->iffalse->front()); p->iffalse = iffalse; $$ = elseif; } else { $$ = nested_if; } } ; /* * Synthesises a AST_statement_list or NULL */ else_single: /* empty */ { $$ = NULL; } | K_ELSE statement { $$ = $2; } ; /* * Synthesises a AST_statement_list or NULL * * Mimicks else_single */ new_else_single: /* empty */ { $$ = NULL; } | K_ELSE ':' inner_statement_list { $$ = $3; } ; /* * Synthesises a AST_formal_parameter_list */ parameter_list: non_empty_parameter_list { $$ = $1; } | /* empty */ { AST_formal_parameter_list* params = new AST_formal_parameter_list; $$ = params; } ; /* * Synthesises a AST_formal_parameter_list * * Lots of duplication in these rules, but what can you do? */ non_empty_parameter_list: optional_class_type VARIABLE { CAST_AST(class_type, $1, AST_type); CAST_STR(name, $2, Token_variable_name); AST_formal_parameter_list* params; params = new AST_formal_parameter_list; params->push_back(NEW(AST_formal_parameter, (class_type, name))); $$ = params; } | optional_class_type '&' VARIABLE { CAST_AST(class_type, $1, AST_type); CAST_STR(name, $3, Token_variable_name); AST_formal_parameter_list* params; params = new AST_formal_parameter_list; params->push_back(NEW(AST_formal_parameter, (class_type, true, name))); $$ = params; } | optional_class_type '&' VARIABLE '=' static_scalar { CAST_AST(class_type, $1, AST_type); CAST_STR(name, $3, Token_variable_name); CAST_AST(default_val, $5, AST_expr); AST_formal_parameter_list* params; params = new AST_formal_parameter_list; params->push_back(NEW(AST_formal_parameter, (class_type, true, name, default_val))); $$ = params; } | optional_class_type VARIABLE '=' static_scalar { CAST_AST(class_type, $1, AST_type); CAST_STR(name, $2, Token_variable_name); CAST_AST(default_val, $4, AST_expr); AST_formal_parameter_list* params; params = new AST_formal_parameter_list; params->push_back(NEW(AST_formal_parameter, (class_type, false, name, default_val))); $$ = params; } | non_empty_parameter_list ',' optional_class_type VARIABLE { CAST_AST(params, $1, AST_formal_parameter_list); CAST_AST(class_type, $3, AST_type); CAST_STR(name, $4, Token_variable_name); params->push_back(NEW(AST_formal_parameter, (class_type, name))); $$ = params; } | non_empty_parameter_list ',' optional_class_type '&' VARIABLE { CAST_AST(params, $1, AST_formal_parameter_list); CAST_AST(class_type, $3, AST_type); CAST_STR(name, $5, Token_variable_name); params->push_back(NEW(AST_formal_parameter, (class_type, true, name))); $$ = params; } | non_empty_parameter_list ',' optional_class_type '&' VARIABLE '=' static_scalar { CAST_AST(params, $1, AST_formal_parameter_list); CAST_AST(class_type, $3, AST_type); CAST_STR(name, $5, Token_variable_name); CAST_AST(default_val, $7, AST_expr); params->push_back(NEW(AST_formal_parameter, (class_type, true, name, default_val))); $$ = params; } | non_empty_parameter_list ',' optional_class_type VARIABLE '=' static_scalar { CAST_AST(params, $1, AST_formal_parameter_list); CAST_AST(class_type, $3, AST_type); CAST_STR(name, $4, Token_variable_name); CAST_AST(default_val, $6, AST_expr); params->push_back(NEW(AST_formal_parameter, (class_type, false, name, default_val))); $$ = params; } ; /* * Synthesises an AST_type */ optional_class_type: /* empty */ { $$ = NEW(AST_type, (false, NULL)); } | IDENT { CAST_STR(class_name, $1, Token_class_name); $$ = NEW(AST_type, (false, class_name)); } | K_ARRAY { $$ = NEW(AST_type, (true, NULL)); } ; /* * Synthesises a AST_actual_parameter_list */ function_call_parameter_list: non_empty_function_call_parameter_list { $$ = $1; } | /* empty */ { $$ = new AST_actual_parameter_list; } ; /* * Synthesises a AST_actual_parameter_list */ non_empty_function_call_parameter_list: expr_without_variable { CAST_AST(arg, $1, AST_expr); AST_actual_parameter_list* list = new AST_actual_parameter_list; list->push_back(NEW(AST_actual_parameter, (false, arg))); $$ = list; } | variable { CAST_AST(arg, $1, AST_expr); AST_actual_parameter_list* list = new AST_actual_parameter_list; list->push_back(NEW(AST_actual_parameter, (false, arg))); $$ = list; } | '&' w_variable { CAST_AST(expr, $2, AST_expr); AST_actual_parameter_list* list = new AST_actual_parameter_list; list->push_back(NEW(AST_actual_parameter, (true, expr))); $$ = list; } | non_empty_function_call_parameter_list ',' expr_without_variable { CAST_AST(list, $1, AST_actual_parameter_list); CAST_AST(arg, $3, AST_expr); list->push_back(NEW(AST_actual_parameter, (false, arg))); $$ = list; } | non_empty_function_call_parameter_list ',' variable { CAST_AST(list, $1, AST_actual_parameter_list); CAST_AST(arg, $3, AST_expr); list->push_back(NEW(AST_actual_parameter, (false, arg))); $$ = list; } | non_empty_function_call_parameter_list ',' '&' w_variable { CAST_AST(list, $1, AST_actual_parameter_list); CAST_AST(arg, $4, AST_expr); list->push_back(NEW(AST_actual_parameter, (true, arg))); $$ = list; } ; /* * Synthesises a List */ global_var_list: global_var_list ',' global_var { CAST_AST(list, $1, List); CAST_AST(var_name, $3, AST_variable_name); list->push_back(var_name); $$ = list; } | global_var { CAST_AST(var_name, $1, AST_variable_name); List* list = new List; list->push_back(var_name); $$ = list; } ; /* * Synthesises an AST_variable_name (Token_variable_name or AST_reflection) */ global_var: VARIABLE { CAST_STR(var, $1, Token_variable_name); $$ = var; } | '$' r_variable { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_reflection, (expr)); } | '$' '{' expr '}' { CAST_AST(expr, $3, AST_expr); $$ = NEW(AST_reflection, (expr)); } ; /* * Synthesises a List */ static_var_list: static_var_list ',' VARIABLE { CAST_AST(list, $1, AST_statement_list); CAST_STR(name, $3, Token_variable_name); AST_static_declaration* v = NEW(AST_static_declaration, (name, NULL)); list->push_back(v); $$ = list; } | static_var_list ',' VARIABLE '=' static_scalar { CAST_AST(list, $1, AST_statement_list); CAST_STR(name, $3, Token_variable_name); CAST_AST(value, $5, AST_expr); AST_static_declaration* v = NEW(AST_static_declaration, (name, value)); list->push_back(v); $$ = list; } | VARIABLE { CAST_STR(name, $1, Token_variable_name); AST_statement_list* list = new AST_statement_list; AST_static_declaration* v = NEW(AST_static_declaration, (name, NULL)); list->push_back(v); $$ = list; } | VARIABLE '=' static_scalar { CAST_STR(name, $1, Token_variable_name); CAST_AST(value, $3, AST_expr); AST_statement_list* list = new AST_statement_list; AST_static_declaration* v = NEW(AST_static_declaration, (name, value)); list->push_back(v); $$ = list; } ; /* * Synthesises a AST_member_list */ class_statement_list: class_statement_list class_statement { CAST_AST(member_list, $1, AST_member_list); CAST_AST(members, $2, AST_member_list); member_list->push_back_all(members); $$ = member_list; } | /* empty */ { $$ = new AST_member_list; } ; /* * Synthesises a AST_member_list */ class_statement: variable_modifiers class_variable_declaration ';' { CAST_AST(modifiers, $1, AST_attr_mod); CAST_AST(members, $2, AST_member_list); AST_member_list::iterator i; for(i = members->begin(); i != members->end(); i++) { CAST_AST(attribute, *i, AST_attribute); attribute->attr_mod = modifiers->deep_clone(); } $$ = members; } | class_constant_declaration ';' { $$ = $1; } | method_modifiers K_FUNCTION {$2 = NEW(AST_method, ());} is_reference IDENT {CONTEXT->current_method = dynamic_cast($5);} '(' parameter_list ')' method_body { CAST_AST(mod, $1, AST_method_mod); CAST_AST(fn, $2, AST_method); CAST_AST(is_ref, $4, Integer); CAST_STR(name, $5, Token_method_name); CAST_AST(params, $8, AST_formal_parameter_list); CAST_AST(body, $10, AST_statement_list); AST_signature* signature = NEW(AST_signature, (mod, is_ref->value(), name, params)); AST_member_list* members = new AST_member_list; fn->signature = signature; fn->statements = body; members->push_back(fn); $$ = members; } ; /* * Synthesises a AST_statement_list or NULL (for abstract methods) */ method_body: ';' { // Abstract method $$ = NULL; } | '{' inner_statement_list '}' { $$ = $2; } ; /* * Synthesises an AST_attr_mod */ variable_modifiers: non_empty_member_modifiers { CAST_AST(method_mod, $1, AST_method_mod); if(method_mod->is_abstract) { error(ERR_ABSTRACT_VARS, method_mod->get_filename(), method_mod->get_line_number()); } $$ = NEW(AST_attr_mod, (method_mod)); } | K_VAR { $$ = NEW(AST_attr_mod, (true, false, false, false, false)); } ; /* * Synthesises an AST_method_mod */ method_modifiers: /* empty */ { $$ = AST_method_mod::new_PUBLIC(); } | non_empty_member_modifiers { $$ = $1; } ; /* * Synthesises an AST_method_mod */ non_empty_member_modifiers: member_modifier { $$ = $1; } | non_empty_member_modifiers member_modifier { CAST_AST(i, $1, AST_method_mod); CAST_AST(j, $2, AST_method_mod); $$ = NEW(AST_method_mod, (i, j)); } ; /* * Synthesises an AST_method_mod */ member_modifier: K_PUBLIC { $$ = AST_method_mod::new_PUBLIC(); } | K_PROTECTED { $$ = AST_method_mod::new_PROTECTED(); } | K_PRIVATE { $$ = AST_method_mod::new_PRIVATE(); } | K_STATIC { $$ = AST_method_mod::new_STATIC(); } | K_ABSTRACT { $$ = AST_method_mod::new_ABSTRACT(); } | K_FINAL { $$ = AST_method_mod::new_FINAL(); } ; /* * Synthesises a AST_member_list */ class_variable_declaration: class_variable_declaration ',' VARIABLE { CAST_AST(members, $1, AST_member_list); CAST_STR(name, $3, Token_variable_name); AST_attribute* attr = NEW(AST_attribute, (AST_attr_mod::new_PUBLIC(), name, NULL)); members->push_back(attr); $$ = members; } | class_variable_declaration ',' VARIABLE '=' static_scalar { CAST_AST(members, $1, AST_member_list); CAST_STR(name, $3, Token_variable_name); CAST_AST(value, $5, AST_expr); AST_attribute* attr = NEW(AST_attribute, (AST_attr_mod::new_PUBLIC(), name, value)); members->push_back(attr); $$ = members; } | VARIABLE { CAST_STR(name, $1, Token_variable_name); AST_member_list* members = new AST_member_list; AST_attribute* attr = NEW(AST_attribute, (AST_attr_mod::new_PUBLIC(), name, NULL)); members->push_back(attr); $$ = members; } | VARIABLE '=' static_scalar { CAST_STR(name, $1, Token_variable_name); CAST_AST(value, $3, AST_expr); AST_member_list* members = new AST_member_list; AST_attribute* attr = NEW(AST_attribute, (AST_attr_mod::new_PUBLIC(), name, value)); members->push_back(attr); $$ = members; } ; /* * Synthesises a AST_member_list */ class_constant_declaration: class_constant_declaration ',' IDENT '=' static_scalar { CAST_AST(members, $1, AST_member_list); CAST_STR(name, $3, Token_variable_name); CAST_AST(value, $5, AST_expr); AST_attribute* attr = NEW(AST_attribute, (AST_attr_mod::new_CONST(), name, value)); members->push_back(attr); $$ = members; } | K_CONST IDENT '=' static_scalar { CAST_STR(name, $2, Token_variable_name); CAST_AST(value, $4, AST_expr); AST_member_list* members = new AST_member_list; AST_attribute* attr = NEW(AST_attribute, (AST_attr_mod::new_CONST(), name, value)); members->push_back(attr); $$ = members; } ; /* * Synthesises a AST_statement_list * * "echo foo, bar;" is translated into "echo(foo); echo(bar);" */ echo_expr_list: echo_expr_list ',' expr { CAST_AST(echo_list, $1, AST_statement_list); CAST_AST(param, $3, AST_expr); AST_method_invocation* fn = NEW(AST_method_invocation, ("echo", param)); echo_list->push_back(NEW(AST_eval_expr, (fn))); $$ = echo_list; } | expr { CAST_AST(param, $1, AST_expr); AST_statement_list* echo_list = new AST_statement_list; AST_method_invocation* fn = NEW(AST_method_invocation, ("echo", param)); echo_list->push_back(NEW(AST_eval_expr, (fn))); $$ = echo_list; } ; /* * Synthesises a AST_expr */ for_expr: /* empty */ { $$ = NULL; } | non_empty_for_expr { $$ = $1 } ; /* * Synthesises a AST_expr * Commas are treated as binops */ non_empty_for_expr: non_empty_for_expr ',' expr { CAST_AST(a, $1, AST_expr); CAST_AST(b, $3, AST_expr); $$ = new AST_bin_op(a, b, ","); } | expr { $$ = $1; } ; /* * Synthesises an AST_expr */ expr_without_variable: K_LIST '(' assignment_list ')' '=' expr { CAST_AST(elements, $3, AST_list_elements); CAST_AST(expr, $6, AST_expr); $$ = NEW(AST_list_assignment, (elements, expr)); } | variable '=' expr { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(value, $3, AST_expr); $$ = NEW(AST_assignment, (var, false, value)); } | variable '=' '&' variable { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(value, $4, AST_expr); $$ = NEW(AST_assignment, (var, true, value)); } | variable '=' '&' K_NEW class_name_reference ctor_arguments { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(name, $5, AST_class_name); CAST_AST(args, $6, AST_actual_parameter_list); $$ = NEW(AST_assignment, (var, true, NEW(AST_new, (name, args)))); } | K_NEW class_name_reference ctor_arguments { CAST_AST(name, $2, AST_class_name); CAST_AST(args, $3, AST_actual_parameter_list); $$ = NEW(AST_new, (name, args)); } | K_CLONE expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_clone, (expr)); } | variable O_PLUSEQ expr { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(expr, $3, AST_expr); AST_expr* var_c; var_c = var->deep_clone(); expr->attrs->set("phc.unparser.needs_brackets", new Boolean(true)); $$ = NEW(AST_assignment, (var, false, NEW(AST_bin_op, (var_c, expr, "+")))); } | variable O_MINUSEQ expr { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(expr, $3, AST_expr); AST_expr* var_c; var_c = var->deep_clone(); expr->attrs->set("phc.unparser.needs_brackets", new Boolean(true)); $$ = NEW(AST_assignment, (var, false, NEW(AST_bin_op, (var_c, expr, "-")))); } | variable O_MULEQ expr { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(expr, $3, AST_expr); AST_expr* var_c; var_c = var->deep_clone(); expr->attrs->set("phc.unparser.needs_brackets", new Boolean(true)); $$ = NEW(AST_assignment, (var, false, NEW(AST_bin_op, (var_c, expr, "*")))); } | variable O_DIVEQ expr { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(expr, $3, AST_expr); AST_expr* var_c; var_c = var->deep_clone(); expr->attrs->set("phc.unparser.needs_brackets", new Boolean(true)); $$ = NEW(AST_assignment, (var, false, NEW(AST_bin_op, (var_c, expr, "/")))); } | variable O_CONCATEQ expr { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(expr, $3, AST_expr); AST_expr* var_c; var_c = var->deep_clone(); expr->attrs->set("phc.unparser.needs_brackets", new Boolean(true)); $$ = NEW(AST_assignment, (var, false, NEW(AST_bin_op, (var_c, expr, ".")))); } | variable O_MODEQ expr { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(expr, $3, AST_expr); AST_expr* var_c; var_c = var->deep_clone(); expr->attrs->set("phc.unparser.needs_brackets", new Boolean(true)); $$ = NEW(AST_assignment, (var, false, NEW(AST_bin_op, (var_c, expr, "%")))); } | variable O_ANDEQ expr { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(expr, $3, AST_expr); AST_expr* var_c; var_c = var->deep_clone(); expr->attrs->set("phc.unparser.needs_brackets", new Boolean(true)); $$ = NEW(AST_assignment, (var, false, NEW(AST_bin_op, (var_c, expr, "&")))); } | variable O_OREQ expr { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(expr, $3, AST_expr); AST_expr* var_c; var_c = var->deep_clone(); expr->attrs->set("phc.unparser.needs_brackets", new Boolean(true)); $$ = NEW(AST_assignment, (var, false, NEW(AST_bin_op, (var_c, expr, "|")))); } | variable O_XOREQ expr { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(expr, $3, AST_expr); AST_expr* var_c; var_c = var->deep_clone(); expr->attrs->set("phc.unparser.needs_brackets", new Boolean(true)); $$ = NEW(AST_assignment, (var, false, NEW(AST_bin_op, (var_c, expr, "^")))); } | variable O_SLEQ expr { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(expr, $3, AST_expr); AST_expr* var_c; var_c = var->deep_clone(); expr->attrs->set("phc.unparser.needs_brackets", new Boolean(true)); $$ = NEW(AST_assignment, (var, false, NEW(AST_bin_op, (var_c, expr, "<<")))); } | variable O_SREQ expr { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); CAST_AST(expr, $3, AST_expr); AST_expr* var_c; var_c = var->deep_clone(); expr->attrs->set("phc.unparser.needs_brackets", new Boolean(true)); $$ = NEW(AST_assignment, (var, false, NEW(AST_bin_op, (var_c, expr, ">>")))); } | rw_variable O_INC { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); $$ = NEW(AST_post_op, (var, "++")); } | O_INC rw_variable { CAST_UNSAFE(var, $2, AST_variable); EXPECT_VARIABLE($2); $$ = NEW(AST_pre_op, (var, "++")); } | rw_variable O_DEC { CAST_UNSAFE(var, $1, AST_variable); EXPECT_VARIABLE($1); $$ = NEW(AST_post_op, (var, "--")); } | O_DEC rw_variable { CAST_UNSAFE(var, $2, AST_variable); EXPECT_VARIABLE($2); $$ = NEW(AST_pre_op, (var, "--")); } | expr O_LOGICOR expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "||")); } | expr O_LOGICAND expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "&&")); } | expr K_OR expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "or")); } | expr K_AND expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "and")); } | expr K_XOR expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "xor")); } | expr '|' expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "|")); } | expr '&' expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "&")); } | expr '^' expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "^")); } | expr '.' expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, ".")); } | expr O_MAGIC_CONCAT expr '}' { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, ".")); /* * Interface with the lexer: return to in-string state * (either DQ_STR or HD_MAIN, depending on the semantic value * of O_MAGIC_CONCAT) */ PHP_parser* ctxt = static_cast(context); ctxt->php_lexer->return_state = dynamic_cast($2)->value(); ctxt->php_lexer->return_to_complex_syntax(); } | expr '+' expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "+")); } | expr '-' expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "-")); } | expr '*' expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "*")); } | expr '/' expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "/")); } | expr '%' expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "%")); } | expr O_SL expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "<<")); } | expr O_SR expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, ">>")); } | '+' expr { // We ignore unary plus $$ = $2; } | '-' expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_unary_op, (expr, "-")); } | '!' expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_unary_op, (expr, "!")); } | '~' expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_unary_op, (expr, "~")); } | expr O_EQEQEQ expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "===")); } | expr O_NOTEQEQ expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "!==")); } | expr O_EQEQ expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "==")); } | expr O_NOTEQ expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "!=")); } | expr '<' expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "<")); } | expr O_LE expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, "<=")); } | expr '>' expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, ">")); } | expr O_GE expr { CAST_AST(left, $1, AST_expr); CAST_AST(right, $3, AST_expr); $$ = NEW(AST_bin_op, (left, right, ">=")); } | expr K_INSTANCEOF class_name_reference { CAST_AST(expr, $1, AST_expr); CAST_AST(class_name, $3, AST_class_name); $$ = NEW(AST_instanceof, (expr, class_name)); } | '(' expr ')' { CAST_AST(expr, $2, AST_expr); expr->attrs->set("phc.unparser.needs_brackets", new Boolean(true)); $$ = expr; } | expr '?' expr ':' expr { CAST_AST(cond, $1, AST_expr); CAST_AST(iftrue, $3, AST_expr); CAST_AST(iffalse, $5, AST_expr); $$ = NEW(AST_conditional_expr, (cond, iftrue, iffalse)); } | internal_functions_in_yacc { $$ = $1; } | CAST_INT expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_cast, ("int", expr)); } | CAST_REAL expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_cast, ("real", expr)); } | CAST_STRING expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_cast, ("string", expr)); } | CAST_ARRAY expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_cast, ("array", expr)); } | CAST_OBJECT expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_cast, ("object", expr)); } | CAST_BOOL expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_cast, ("bool", expr)); } | CAST_UNSET expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_cast, ("unset", expr)); } | K_EXIT exit_expr { CAST_AST(param, $2, AST_expr); $$ = NEW(AST_method_invocation, ("exit", param)); } | '@' expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_ignore_errors, (expr)); } | scalar { $$ = $1; } | K_ARRAY '(' array_pair_list ')' { CAST_AST(list, $3, AST_array_elem_list); $$ = NEW(AST_array, (list)); } | K_PRINT expr { CAST_AST(param, $2, AST_expr); $$ = NEW(AST_method_invocation, ("print", param)); } ; /* * Synthesises an AST_method_invocation */ function_call: IDENT '(' function_call_parameter_list ')' { CAST_AST(params, $3, AST_actual_parameter_list); CAST_STR(fn, $1, Token_method_name); $$ = NEW(AST_method_invocation, (NULL, fn, params)); } | fully_qualified_class_name O_COLONCOLON IDENT '(' function_call_parameter_list ')' { CAST_AST(class_name, $1, Token_class_name); CAST_STR(fn, $3, Token_method_name); CAST_AST(params, $5, AST_actual_parameter_list); $$ = NEW(AST_method_invocation, (class_name, fn, params)); } | fully_qualified_class_name O_COLONCOLON variable_without_objects '(' function_call_parameter_list ')' { CAST_AST(class_name, $1, Token_class_name); CAST_AST(expr, $3, AST_expr); CAST_AST(params, $5, AST_actual_parameter_list); $$ = NEW(AST_method_invocation, (class_name, NEW(AST_reflection, (expr)), params)); } | variable_without_objects '(' function_call_parameter_list ')' { CAST_AST(expr, $1, AST_expr); CAST_AST(params, $3, AST_actual_parameter_list); $$ = NEW(AST_method_invocation, (NULL, NEW(AST_reflection, (expr)), params)); } ; /* * Synthesises a Token_class_name */ fully_qualified_class_name: IDENT { CAST_STR(name, $1, Token_class_name); $$ = name; } ; /* * Synthesises an AST_class_name */ class_name_reference: IDENT { CAST_STR(name, $1, Token_class_name); $$ = name; } | dynamic_class_name_reference { CAST_AST(expr, $1, AST_expr); $$ = NEW(AST_reflection, (expr)); } ; /* * Synthesises up an AST_variable or whatever base_variable synthesises * * Note: the grammar is right recursive, whereas invocation is left recursive * "$a->b->c" is "($a->b)->c"; must be careful to generate the correct tree. */ dynamic_class_name_reference: base_variable O_SINGLEARROW object_property dynamic_class_name_variable_properties { CAST_AST(object, $1, AST_expr); CAST_AST(result, $3, AST_variable); CAST_AST(vars, $4, List); result->target = object; List::const_iterator i; for(i = vars->begin(); i != vars->end(); i++) { (*i)->target = result; result = *i; } $$ = result; } | base_variable { $$ = $1; } ; /* * Synthesises a List */ dynamic_class_name_variable_properties: dynamic_class_name_variable_properties dynamic_class_name_variable_property { CAST_AST(vars, $1, List); CAST_AST(var, $2, AST_variable); vars->push_back(var); $$ = vars; } | /* empty */ { List* vars = new List; $$ = vars; } ; /* * Synthesises an AST_variable */ dynamic_class_name_variable_property: O_SINGLEARROW object_property { $$ = $2; } ; /* * Synthesises an AST_expr * * If no argument is specified (exit; or exit();), assume 0. */ exit_expr: /* empty */ { $$ = NEW(Token_int, (0L, new String("0"))); } | '(' ')' { $$ = NEW(Token_int, (0L, new String("0"))); } | '(' expr ')' { $$ = $2; } ; /* * Synthesises a AST_actual_parameter_list */ ctor_arguments: /* empty */ { AST_actual_parameter_list* args = new AST_actual_parameter_list; $$ = args; } | '(' function_call_parameter_list ')' { $$ = $2; } ; /* * Synthesises an AST_expr */ common_scalar: INT { CAST_AST(str, $1, String); $$ = NEW(Token_int, (strtol(str->c_str(), 0, 0), str)); } | REAL { CAST_AST(str, $1, String); $$ = NEW(Token_real, (atof(str->c_str()), str)); } | STRING { CAST_AST(str, $1, String); $$ = NEW(Token_string, (str, str)); } | C_FALSE { CAST_AST(str, $1, String); $$ = NEW(Token_bool, (false, str)); } | C_TRUE { CAST_AST(str, $1, String); $$ = NEW(Token_bool, (true, str)); } | C_NULL { CAST_AST(str, $1, String); $$ = NEW(Token_null, (str)); } | K___LINE__ { PHP_lexer* lex = static_cast(context)->php_lexer; $$ = NEW(Token_int, (lex->source_line, new String("__LINE__"))); } | K___FILE__ { $$ = NEW(Token_string, (static_cast(context)->filename, new String("__FILE__"))); } | K___CLASS__ { $$ = NEW(Token_string, (CONTEXT->current_class, new String("__CLASS__"))); } | K___METHOD__ { $$ = NEW(Token_string, (CONTEXT->current_method, new String("__METHOD__"))); } | K___FUNCTION__ { $$ = NEW(Token_string, (CONTEXT->current_method, new String("__FUNCTION__"))); } ; /* * Synthesises an AST_expr */ static_scalar: common_scalar { $$ = $1; } | IDENT { CAST_STR(name, $1, Token_constant_name); $$ = NEW(AST_constant, ("%MAIN%", name)); } | '+' static_scalar { // We simply ignore the + $$ = $2; } | '-' static_scalar { CAST_AST(val, $2, AST_expr); $$ = NEW(AST_unary_op, (val, "-")); } | K_ARRAY '(' static_array_pair_list ')' { CAST_AST(list, $3, AST_array_elem_list); $$ = NEW(AST_array, (list)); } | static_class_constant { $$ = $1; } ; /* * Synthesises an AST_constant */ static_class_constant: IDENT O_COLONCOLON IDENT { CAST_STR(class_name, $1, Token_class_name); CAST_STR(constant, $3, Token_constant_name); $$ = NEW(AST_constant, (class_name, constant)); } ; /* * Synthesises an AST_expr */ scalar: IDENT { CAST_STR(name, $1, Token_constant_name); $$ = NEW(AST_constant, ("%MAIN%", name)); } | class_constant { $$ = $1; } | common_scalar { $$ = $1; } ; /* * Synthesises a AST_array_elem_list */ static_array_pair_list: /* empty */ { $$ = new AST_array_elem_list; } | non_empty_static_array_pair_list possible_comma { $$ = $1; } ; /* * No semantic value */ possible_comma: /* empty */ { $$ = NULL; } | ',' { $$ = NULL; } ; /* * Synthesises a AST_array_elem_list */ non_empty_static_array_pair_list: non_empty_static_array_pair_list ',' static_scalar O_DOUBLEARROW static_scalar { CAST_AST(list, $1, AST_array_elem_list); CAST_AST(key, $3, AST_expr); CAST_AST(value, $5, AST_expr); AST_array_elem* elem = NEW(AST_array_elem, (key, false, value)); list->push_back(elem); $$ = list; } | non_empty_static_array_pair_list ',' static_scalar { CAST_AST(list, $1, AST_array_elem_list); CAST_AST(value, $3, AST_expr); AST_array_elem* elem = NEW(AST_array_elem, (NULL, false, value)); list->push_back(elem); $$ = list; } | static_scalar O_DOUBLEARROW static_scalar { CAST_AST(key, $1, AST_expr); CAST_AST(value, $3, AST_expr); AST_array_elem_list* list = new AST_array_elem_list; AST_array_elem* elem = NEW(AST_array_elem, (key, false, value)); list->push_back(elem); $$ = list; } | static_scalar { CAST_AST(value, $1, AST_expr); AST_array_elem_list* list = new AST_array_elem_list; AST_array_elem* elem = NEW(AST_array_elem, (NULL, false, value)); list->push_back(elem); $$ = list; } ; /* * Synthesises an AST_expr */ expr: r_variable { $$ = $1; } | expr_without_variable { $$ = $1; } ; /* * Synthesises an AST_expr */ r_variable: variable { $$ = $1; } ; /* * Synthesises an AST_expr */ w_variable: variable { $$ = $1; } ; /* * Synthesises an AST_expr */ rw_variable: variable { $$ = $1; } ; /* * Synthesises an AST_expr * * NOTE: the original rule read * * variable ::= base_variable_with_function_calls O_SINGLEARROW object_property * method_or_not variable_properties * * However, this duplicates work done in variable_properties, because * variable_properties is a list of variable_property's, and * * variable_property ::= O_SINGLEARROW object_property method_or_not * * Now, in the original grammar, variable_properties allows for an empty list; * that's now changed, so that it requires at least one variable_property. * * We don't normally change the grammar, but this rule is difficult enough * as it is, so that we don't want to be duplicating code. */ variable: base_variable_with_function_calls variable_properties { CAST_AST(object, $1, AST_expr); CAST_AST(props, $2, AST_expr_list); AST_expr_list::iterator i; for(i = props->begin(); i != props->end(); i++) { CAST_UNSAFE(var, *i, AST_variable); CAST_UNSAFE(fn, *i, AST_method_invocation); if(var) { var->target = object; AST_actual_parameter_list* function_params = dynamic_cast(var->attrs->get("phc.parser.function_params")); if(function_params) { object = NEW(AST_method_invocation, (NULL, NEW(AST_reflection,(var)), function_params)); } else { object = var; } } else { fn->target = object; object = fn; } } $$ = object; } | base_variable_with_function_calls { $$ = $1; } ; /* * Synthesises a AST_expr_list (AST_variable/AST_method_invocation) * * NOTE: The second rule goes to empty in the original grammar; see the * comment for "variable ::=" why this is changed. */ variable_properties: variable_properties variable_property { CAST_AST(props, $1, AST_expr_list); CAST_AST(prop, $2, AST_expr); props->push_back(prop); $$ = props; } | variable_property { CAST_AST(prop, $1, AST_expr); AST_expr_list* props = new AST_expr_list; props->push_back(prop); $$ = props; } ; /* * Synthesises an AST_variable or an AST_method_invocation * * NOTE: We decide to synthesise an AST_variable or an AST_method_invocation * based on the absence or presence of a parameter list (method_or_not). * If there is a parameter list, we _try_ to generate a method invocation. * * To do this, we take the name of the variable synthesised by * object_property, and use it for the name of the method invocation. That is, * if the name of the variable is VarName[x], we convert it to FnName[x]; * otherwise, it must be an expression and we use the name as-is. * * However, this fails to work if the variable has array indices. This is * the case, for example, in "$x->f[]()" (i.e., "f[]()" as far as * variable_property is concerned). In this case, the name of the method * is "$x->f[]"; however, we cannot generate this here because we don't * know the "$x" part. Instead, we synthesise up "f[]" (AST_variable), and * we set a private attribute in AST_variable, called "function_params", to * the parameters of the method. The rule "variable ::= " must check for * this attribute, and generate the correct method invocation if set. */ variable_property: O_SINGLEARROW object_property method_or_not { CAST_AST(var, $2, AST_variable); CAST_AST(params, $3, AST_actual_parameter_list); if(params) { if(var->array_indices->empty()) { CAST_UNSAFE(vn, var->variable_name, Token_variable_name); if(vn) { Token_method_name* fn; fn = NEW(Token_method_name, (vn->value)); $$ = NEW(AST_method_invocation, (NULL, fn, params)); } else { AST_reflection* fn; fn = dynamic_cast(var->variable_name); assert(fn); $$ = NEW(AST_method_invocation, (NULL, fn, params)); } } else { var->attrs->set("phc.parser.function_params", params); $$ = var; } } else { $$ = var; } } ; /* * Synthesises a AST_actual_parameter_list or NULL */ method_or_not: '(' function_call_parameter_list ')' { $$ = $2; } | /* empty */ { $$ = NULL; } ; /* * Synthesises an AST_variable */ variable_without_objects: reference_variable { $$ = $1; } | simple_indirect_reference reference_variable { CAST_AST(num_indirections, $1, Integer); CAST_AST(var, $2, AST_variable); for(long i = 0; i < num_indirections->value(); i++) { var = NEW(AST_variable, (NEW(AST_reflection, (var)))); } $$ = var; } ; /* * Synthesises an AST_variable */ static_member: fully_qualified_class_name O_COLONCOLON variable_without_objects { CAST_AST(class_name, $1, Token_class_name); CAST_AST(var, $3, AST_variable); var->target = class_name; $$ = var; } ; /* * Synthesises an AST_variable or AST_method_invocation */ base_variable_with_function_calls: base_variable { $$ = $1; } | function_call { $$ = $1; } ; /* * Synthesises an AST_variable */ base_variable: reference_variable { $$ = $1; } | simple_indirect_reference reference_variable { CAST_AST(num_indirections, $1, Integer); CAST_AST(var, $2, AST_variable); for(long i = 0; i < num_indirections->value(); i++) { var = NEW(AST_variable, (NEW(AST_reflection, (var)))); } $$ = var; } | static_member { $$ = $1; } ; /* * Synthesises an AST_variable */ reference_variable: reference_variable '[' dim_offset ']' { CAST_AST(var, $1, AST_variable); CAST_AST(index, $3, AST_expr); // string index is only valid after *all* array indices. assert(var->string_index == NULL); var->array_indices->push_back(index); $$ = var; } | reference_variable '{' expr '}' { CAST_AST(var, $1, AST_variable); CAST_AST(index, $3, AST_expr); // we only accept *one* string index. assert(var->string_index == NULL); var->string_index = index; $$ = var; } | compound_variable { CAST_AST(name, $1, AST_variable_name); $$ = NEW(AST_variable, (name)); } ; /* * Synthesises an AST_variable_name */ compound_variable: VARIABLE { CAST_STR(var, $1, Token_variable_name); $$ = var; } | '$' '{' expr '}' { CAST_AST(expr, $3, AST_expr); $$ = NEW(AST_reflection, (expr)); } ; /* * Synthesises an AST_expr or NULL */ dim_offset: /* empty */ { $$ = NULL; } | expr { $$ = $1; } ; /* * Synthesises an AST_variable */ object_property: object_dim_list { $$ = $1; } | variable_without_objects { // This is a "normal" variable (which includes a $), i.e. $x->$y // So, we need to add a level of indirection CAST_AST(var, $1, AST_variable); $$ = NEW(AST_variable, (NEW(AST_reflection, (var)))); } ; /* * Synthesises an AST_variable * * Mimics reference_variable */ object_dim_list: object_dim_list '[' dim_offset ']' { CAST_AST(op, $1, AST_variable); CAST_AST(index, $3, AST_expr); // string index is only valid after *all* array indices assert(op->string_index == NULL); op->array_indices->push_back(index); $$ = op; } | object_dim_list '{' expr '}' { CAST_AST(op, $1, AST_variable); CAST_AST(index, $3, AST_expr); // we only accept *one* string index. assert(op->string_index == NULL); op->string_index = index; $$ = op; } | variable_name { CAST_AST(name, $1, AST_variable_name); $$ = NEW(AST_variable, (name)); } ; /* * Synthesises an AST_variable_name * * Mimics compound_variable */ variable_name: IDENT { CAST_STR(var, $1, Token_variable_name); $$ = var; } | '{' expr '}' { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_reflection, (expr)); } ; /* * Synthesises an Integer * * Counts the number of dollar signs (indirections) */ simple_indirect_reference: '$' { $$ = new Integer(1); } | simple_indirect_reference '$' { CAST_AST(i, $1, Integer); (*i)++; $$ = i; } ; /* * Synthesises an AST_list_elements */ assignment_list: assignment_list ',' assignment_list_element { CAST_AST(elements, $1, AST_list_elements); CAST_AST(element, $3, AST_list_element); elements->list_elements->push_back(element); $$ = elements; } | assignment_list_element { CAST_AST(element, $1, AST_list_element); AST_list_element_list* elements = new AST_list_element_list; elements->push_back(element); $$ = NEW(AST_list_elements, (elements)); } ; /* * Synthesises an AST_list_element * * Can be NULL for an empty list element, list($x, , $y) */ assignment_list_element: variable { $$ = $1; } | K_LIST '(' assignment_list ')' { $$ = $3; } | /* empty */ { $$ = NULL; } ; /* * Synthesises a AST_array_elem_list */ array_pair_list: /* emtpy */ { $$ = new AST_array_elem_list; } | non_empty_array_pair_list possible_comma { $$ = $1; } ; /* * Synthesises a AST_array_elem_list * * Code duplicated in non_empty_static_array_pair_list */ non_empty_array_pair_list: non_empty_array_pair_list ',' expr O_DOUBLEARROW expr { CAST_AST(list, $1, AST_array_elem_list); CAST_AST(key, $3, AST_expr); CAST_AST(value, $5, AST_expr); AST_array_elem* elem = NEW(AST_array_elem, (key, false, value)); list->push_back(elem); $$ = list; } | non_empty_array_pair_list ',' expr { CAST_AST(list, $1, AST_array_elem_list); CAST_AST(value, $3, AST_expr); AST_array_elem* elem = NEW(AST_array_elem, (NULL, false, value)); list->push_back(elem); $$ = list; } | expr O_DOUBLEARROW expr { CAST_AST(key, $1, AST_expr); CAST_AST(value, $3, AST_expr); AST_array_elem_list* list = new AST_array_elem_list; AST_array_elem* elem = NEW(AST_array_elem, (key, false, value)); list->push_back(elem); $$ = list; } | expr { CAST_AST(value, $1, AST_expr); AST_array_elem_list* list = new AST_array_elem_list; AST_array_elem* elem = NEW(AST_array_elem, (NULL, false, value)); list->push_back(elem); $$ = list; } | non_empty_array_pair_list ',' expr O_DOUBLEARROW '&' w_variable { CAST_AST(list, $1, AST_array_elem_list); CAST_AST(key, $3, AST_expr); CAST_AST(value, $6, AST_expr); AST_array_elem* elem = NEW(AST_array_elem, (key, true, value)); list->push_back(elem); $$ = list; } | non_empty_array_pair_list ',' '&' w_variable { CAST_AST(list, $1, AST_array_elem_list); CAST_AST(value, $4, AST_expr); AST_array_elem* elem = NEW(AST_array_elem, (NULL, true, value)); list->push_back(elem); $$ = list; } | expr O_DOUBLEARROW '&' w_variable { CAST_AST(key, $1, AST_expr); CAST_AST(value, $4, AST_expr); AST_array_elem_list* list = new AST_array_elem_list; AST_array_elem* elem = NEW(AST_array_elem, (key, true, value)); list->push_back(elem); $$ = list; } | '&' w_variable { CAST_AST(value, $2, AST_expr); AST_array_elem_list* list = new AST_array_elem_list; AST_array_elem* elem = NEW(AST_array_elem, (NULL, true, value)); list->push_back(elem); $$ = list; } ; /* * Synthesises an AST_method_invocation */ internal_functions_in_yacc: K_ISSET '(' isset_variables ')' { CAST_AST(params, $3, AST_actual_parameter_list); Token_method_name* fn = NEW(Token_method_name, (new String("isset"))); $$ = NEW(AST_method_invocation, (NULL, fn, params)); } | K_EMPTY '(' variable ')' { CAST_AST(variable, $3, AST_expr); $$ = NEW(AST_method_invocation, ("empty", variable)); } | K_INCLUDE expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_method_invocation, ("include", expr)); } | K_INCLUDE_ONCE expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_method_invocation, ("include_once", expr)); } | K_EVAL '(' expr ')' { CAST_AST(expr, $3, AST_expr); $$ = NEW(AST_method_invocation, ("eval", expr)); } | K_REQUIRE expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_method_invocation, ("require", expr)); } | K_REQUIRE_ONCE expr { CAST_AST(expr, $2, AST_expr); $$ = NEW(AST_method_invocation, ("require_once", expr)); } ; /* * Synthesises a AST_actual_parameter_list */ isset_variables: variable { CAST_AST(variable, $1, AST_expr); AST_actual_parameter_list* params = new AST_actual_parameter_list; params->push_back(NEW(AST_actual_parameter, (false, variable))); $$ = params; } | isset_variables ',' variable { CAST_AST(params, $1, AST_actual_parameter_list); CAST_AST(variable, $3, AST_expr); params->push_back(NEW(AST_actual_parameter, (false, variable))); $$ = params; } ; /* * Synthesises an AST_constant */ class_constant: fully_qualified_class_name O_COLONCOLON IDENT { CAST_AST(class_name, $1, Token_class_name); CAST_STR(constant, $3, Token_constant_name); $$ = NEW(AST_constant, (class_name, constant)); } ;