/* * phc -- the open source PHP compiler * See license/README.license for licensing information * * The phc grammar in .tea format. * * This is the authoritative grammar for phc. */ php_script ::= interface_def* class_def+ ; interface_def ::= INTERFACE_NAME extends:INTERFACE_NAME* member* ; class_def ::= class_mod CLASS_NAME extends:CLASS_NAME? implements:INTERFACE_NAME* member* ; class_mod ::= "abstract"? "final"? ; member ::= method | attribute ; method ::= signature statement*? ; signature ::= method_mod is_ref:"&"? METHOD_NAME formal_parameter* ; method_mod ::= "public"? "protected"? "private"? "static"? "abstract"? "final"? ; formal_parameter ::= type is_ref:"&"? VARIABLE_NAME expr? ; type ::= "array"? CLASS_NAME? ; attribute ::= attr_mod VARIABLE_NAME expr? ; attr_mod ::= "public"? "protected"? "private"? "static"? "const"? ; statement ::= if | while | do | for | foreach | switch | break | continue | return | static_declaration | unset | declare | try | throw | eval_expr | xml_element | xml_element_attribute | escaped_echo | xml_processing_instruction ; if ::= expr iftrue:statement* iffalse:statement* ; while ::= expr statement* ; do ::= statement* expr ; for ::= init:expr? cond:expr? incr:expr? statement* ; foreach ::= expr key:variable? is_ref:"&"? val:variable statement* ; switch ::= expr switch_case* ; switch_case ::= expr? statement* ; break ::= expr? ; continue ::= expr? ; return ::= expr? ; static_declaration ::= VARIABLE_NAME expr? ; unset ::= variable ; declare ::= directive+ statement* ; directive ::= DIRECTIVE_NAME expr ; try ::= statement* catches:catch* ; catch ::= CLASS_NAME VARIABLE_NAME statement* ; throw ::= expr ; eval_expr ::= expr ; xml_element ::= xml_element_name statement*; xml_processing_instruction ::= xml_element_name statement*; xml_element_attribute ::= xml_element_name expr; xml_element_name ::= ns_is_var:"$"? xml_namespace:TAG_NAME? is_var:"$"? TAG_NAME; escaped_echo ::= expr*; expr ::= assignment | list_assignment | cast | unary_op | bin_op | conditional_expr | ignore_errors | constant | instanceof | variable | pre_op | post_op | array | method_invocation | new | clone | literal ; literal ::= INT | REAL | STRING | BOOL | NULL<> ; assignment ::= variable is_ref:"&"? expr ; list_assignment ::= list_elements expr ; list_elements ::= list_element?* ; list_element ::= variable | list_elements ; cast ::= CAST expr ; unary_op ::= OP expr ; bin_op ::= left:expr OP right:expr ; conditional_expr ::= cond:expr iftrue:expr iffalse:expr ; ignore_errors ::= expr ; constant ::= CLASS_NAME CONSTANT_NAME ; instanceof ::= expr class_name ; variable ::= target? variable_name array_indices:expr?* string_index:expr? ; variable_name ::= VARIABLE_NAME | reflection ; reflection ::= expr ; target ::= expr | CLASS_NAME ; pre_op ::= OP variable ; post_op ::= variable OP ; array ::= array_elem* ; array_elem ::= key:expr? is_ref:"&"? val:expr ; method_invocation ::= target method_name actual_parameter* ; method_name ::= METHOD_NAME | reflection ; actual_parameter ::= is_ref:"&"? expr ; new ::= class_name actual_parameter* ; class_name ::= CLASS_NAME | reflection ; clone ::= expr ; /* * Additional structure */ node ::= php_script | class_mod | signature | method_mod | formal_parameter | type | attr_mod | directive | list_element | variable_name | target | array_elem | method_name | actual_parameter | class_name | commented_node | expr | identifier | formal_parameter* | directive* | array_elem* | actual_parameter* | INTERFACE_NAME* | list_element* | expr* | xml_element_name ; commented_node ::= member | statement | interface_def | class_def | switch_case | catch | interface_def* | class_def* | member* | statement* | switch_case* | catch* | xml_element | xml_processing_instruction | xml_element_attribute | escaped_echo ; identifier ::= INTERFACE_NAME | CLASS_NAME | METHOD_NAME | VARIABLE_NAME | DIRECTIVE_NAME | CAST | OP | CONSTANT_NAME | TAG_NAME ; /* * Extra attributes and methods */ #include #include #include #include #include #include #include #include #include #include class AST_node : Object { AttrMap* attrs; // Return the line number of the node (or 0 if unknown) int get_line_number() { Integer* i = dynamic_cast(attrs->get("phc.line_number")); if(i != NULL) return i->value(); else return 0; } // Return the filename of the node (or NULL if unknown) String* get_filename() { return dynamic_cast(attrs->get("phc.filename")); } AST_node() { // Constructor gets called because all classes inherit from // AST_node virtually; also, because maketea knows AST_node is // abstract, it won't add a constructor itself attrs = new AttrMap(); } }; class AST_commented_node { AST_commented_node() { attrs->set("phc.comments", new List); } // Return the comments associated with the node List* get_comments() { List* comments = dynamic_cast*>(attrs->get("phc.comments")); assert(comments); return comments; } }; class AST_signature { AST_signature(const char* name) { this->method_mod = AST_method_mod::new_PUBLIC(); this->is_ref = false; this->method_name = new Token_method_name(new String(name)); this->formal_parameters = new AST_formal_parameter_list; } }; class AST_method_mod { AST_method_mod(AST_method_mod* a, AST_method_mod* b) { this->is_public = a->is_public || b->is_public; this->is_protected = a->is_protected || b->is_protected; this->is_private = a->is_private || b->is_private; this->is_static = a->is_static || b->is_static; this->is_abstract = a->is_abstract || b->is_abstract; this->is_final = a->is_final || b->is_final; } static AST_method_mod* new_PUBLIC() { return new AST_method_mod(true, false, false, false, false, false); } static AST_method_mod* new_PROTECTED() { return new AST_method_mod(false, true, false, false, false, false); } static AST_method_mod* new_PRIVATE() { return new AST_method_mod(false, false, true, false, false, false); } static AST_method_mod* new_STATIC() { return new AST_method_mod(false, false, false, true, false, false); } static AST_method_mod* new_ABSTRACT() { return new AST_method_mod(false, false, false, false, true, false); } static AST_method_mod* new_FINAL() { return new AST_method_mod(false, false, false, false, false, true); } }; class AST_php_script { void init() { interface_defs = new AST_interface_def_list; class_defs = new AST_class_def_list; class_defs->push_back(new AST_class_def("%MAIN%")); } // Returns NULL if the class could not be found AST_class_def* get_class_def(const char* name) { AST_class_def_list::const_iterator i; for(i = class_defs->begin(); i != class_defs->end(); i++) { AST_class_def* class_def = dynamic_cast(*i); if(class_def && *class_def->class_name->value == name) return class_def; } return NULL; } }; class AST_class_def { AST_class_def(AST_class_mod* mod) { this->class_mod = mod; this->class_name = NULL; this->extends = NULL; this->implements = new Token_interface_name_list; this->members = new AST_member_list; } AST_class_def(char* name) { this->class_mod = new AST_class_mod(false, false); this->class_name = new Token_class_name(new String(name)); this->extends = NULL; this->implements = new Token_interface_name_list; this->members = new AST_member_list; } void add_member(AST_member* member) { this->members->push_back(member); } // Returns NULL if the method could not be found AST_method* get_method(const char* name) { AST_member_list::const_iterator i; for(i = members->begin(); i != members->end(); i++) { AST_method* method = dynamic_cast(*i); if(method && *method->signature->method_name->value == name) return method; } return NULL; } }; class AST_variable { AST_variable(AST_variable_name* name) { this->target = NULL; this->variable_name = name; this->array_indices = new AST_expr_list; this->string_index = NULL; } void init() { attrs->set("phc.parser.is_ref", new Boolean(false)); attrs->set("phc.parser.function_params", NULL); } }; class AST_assignment { void init() { attrs->set("phc.unparser.is_global_stmt", new Boolean(false)); } }; class AST_method_invocation { AST_method_invocation(const char* name, AST_expr* arg) { this->target = NULL; this->method_name = new Token_method_name(new String(name)); this->actual_parameters = new AST_actual_parameter_list; this->actual_parameters->push_back(new AST_actual_parameter(false, arg)); } AST_method_invocation(const char* target, const char* name, AST_expr* arg) { this->target = new Token_class_name(new String(target)); this->method_name = new Token_method_name(new String(name)); this->actual_parameters = new AST_actual_parameter_list; this->actual_parameters->push_back(new AST_actual_parameter(false, arg)); } AST_method_invocation(Token_method_name* name, AST_expr* arg) { this->target = NULL; this->method_name = name; this->actual_parameters = new AST_actual_parameter_list; this->actual_parameters->push_back(new AST_actual_parameter(false, arg)); } }; class AST_formal_parameter { AST_formal_parameter(AST_type* type, Token_variable_name* name) { this->type = type; this->is_ref = false; this->variable_name = name; this->expr = NULL; } AST_formal_parameter(AST_type* type, bool is_ref, Token_variable_name* name) { this->type = type; this->is_ref = is_ref; this->variable_name = name; this->expr = NULL; } }; class AST_attr_mod { AST_attr_mod(AST_method_mod* mm) { if(mm->is_final) error(ERR_FINAL_VARS, mm->get_filename(), mm->get_line_number()); this->is_public = mm->is_public; this->is_protected = mm->is_protected; this->is_private = mm->is_private; this->is_static = mm->is_static; this->is_const = false; } static AST_attr_mod* new_PUBLIC() { return new AST_attr_mod(true, false, false, false, false); } static AST_attr_mod* new_PROTECTED() { return new AST_attr_mod(false, true, false, false, false); } static AST_attr_mod* new_PRIVATE() { return new AST_attr_mod(false, false, true, false, false); } static AST_attr_mod* new_STATIC() { return new AST_attr_mod(false, false, false, true, false); } static AST_attr_mod* new_CONST() { return new AST_attr_mod(false, false, false, false, true); } }; class AST_bin_op { AST_bin_op(AST_expr* left, AST_expr* right, char* op) { this->left = left; this->op = new Token_op(new String(op)); this->right = right; } }; class AST_post_op { AST_post_op(AST_variable* var, char* op) { this->variable = var; this->op = new Token_op(new String(op)); } }; class AST_pre_op { AST_pre_op(AST_variable* var, char* op) { this->variable = var; this->op = new Token_op(new String(op)); } }; class AST_unary_op { AST_unary_op(AST_expr* expr, char* op) { this->expr = expr; this->op = new Token_op(new String(op)); } }; class AST_expr { AST_expr() { attrs->set("phc.unparser.needs_brackets", new Boolean(false)); } }; class AST_cast { AST_cast(char* cast, AST_expr* expr) { this->cast = new Token_cast(new String(cast)); this->expr = expr; } }; class AST_constant { AST_constant(char* class_name, Token_constant_name* constant_name) { this->class_name = new Token_class_name(new String(class_name)); this->constant_name = constant_name; } }; class AST_identifier { virtual String* get_value_as_string() = 0; }; class AST_literal { virtual String* get_value_as_string() = 0; virtual String* get_source_rep() = 0; }; class Token_int { virtual String* get_value_as_string() { std::ostringstream os; os << value; return new String(os.str()); } }; class Token_real { virtual String* get_value_as_string() { std::ostringstream os; // setprecision(20) outputs as many digits as required, with // a maximum of 20 os << setprecision(20) << value; // unfortunately, that means that if no digits are required at // all (after the decimal point), the decimal point is left out // completely; setting the "showpoint" flag fixes this, but then // the STL _always_ shows all 20 digits, which is not what we // want either. Hence, we insert the ".0" manually if necessary: string str = os.str(); if(str.find('.') >= str.size()) str.append(".0"); return new String(str); } }; class Token_bool { virtual String* get_value_as_string() { if(value) return new String("True"); else return new String("False"); } }; class Token_string { virtual String* get_value_as_string() { return value; } }; class Token_null { virtual String* get_value_as_string() { return new String("NULL"); } }; class AST_if { void init() { attrs->set("phc.unparser.is_elseif", new Boolean(false)); } };