1# ----------------------------------------------------------------------------- 2# phpparse.py 3# 4# A parser for PHP. 5# ----------------------------------------------------------------------------- 6 7import os 8import sys 9from . import phplex 10from . import phpast as ast 11import ply.yacc as yacc 12 13if sys.version_info[0] == 3: 14 string_type = str 15else: 16 string_type = basestring 17 18# Get the token map 19tokens = phplex.tokens 20 21precedence = ( 22 ('left', 'INCLUDE', 'INCLUDE_ONCE', 'EVAL', 'REQUIRE', 'REQUIRE_ONCE'), 23 ('left', 'COMMA'), 24 ('left', 'LOGICAL_OR'), 25 ('left', 'LOGICAL_XOR'), 26 ('left', 'LOGICAL_AND'), 27 ('right', 'PRINT'), 28 ('left', 'EQUALS', 'PLUS_EQUAL', 'MINUS_EQUAL', 'MUL_EQUAL', 'DIV_EQUAL', 'CONCAT_EQUAL', 'MOD_EQUAL', 'AND_EQUAL', 'OR_EQUAL', 'XOR_EQUAL', 'SL_EQUAL', 'SR_EQUAL'), 29 ('left', 'QUESTION', 'COLON'), 30 ('left', 'BOOLEAN_OR'), 31 ('left', 'BOOLEAN_AND'), 32 ('left', 'OR'), 33 ('left', 'XOR'), 34 ('left', 'AND'), 35 ('nonassoc', 'IS_EQUAL', 'IS_NOT_EQUAL', 'IS_IDENTICAL', 'IS_NOT_IDENTICAL'), 36 ('nonassoc', 'IS_SMALLER', 'IS_SMALLER_OR_EQUAL', 'IS_GREATER', 'IS_GREATER_OR_EQUAL'), 37 ('left', 'SL', 'SR'), 38 ('left', 'PLUS', 'MINUS', 'CONCAT'), 39 ('left', 'MUL', 'DIV', 'MOD'), 40 ('right', 'BOOLEAN_NOT'), 41 ('nonassoc', 'INSTANCEOF'), 42 ('right', 'NOT', 'INC', 'DEC', 'INT_CAST', 'DOUBLE_CAST', 'STRING_CAST', 'ARRAY_CAST', 'OBJECT_CAST', 'BOOL_CAST', 'UNSET_CAST', 'AT'), 43 ('right', 'LBRACKET'), 44 ('nonassoc', 'NEW', 'CLONE'), 45 # ('left', 'ELSEIF'), 46 # ('left', 'ELSE'), 47 ('left', 'ENDIF'), 48 ('right', 'STATIC', 'ABSTRACT', 'FINAL', 'PRIVATE', 'PROTECTED', 'PUBLIC'), 49) 50 51def process_php_string_escapes(s): 52 # TODO: actual processing - turn php escape sequences into actual chars 53 res = '' 54 i = iter(s) 55 for c in i: 56 if c == '\\': 57 c = next(i) 58 if c == 'n': 59 res += '\n' 60 elif c == 'r': 61 res += '\r' 62 elif c == 't': 63 res += '\t' 64 elif c == '"': 65 res += '"' 66 elif c == "'": 67 res += "'" 68 elif c == 'x': 69 try: 70 x = next(i) 71 except StopIteration: 72 res += "\\x" 73 break 74 try: 75 x += next(i) 76 except StopIteration: 77 # one character \xH sequence is actually valid in php 78 pass 79 80 try: 81 x = int(x, 16) 82 res += chr(x) 83 except ValueError: 84 # TODO: find out what php does with broken literals 85 res += '\\x'+x 86 elif c == '\\': 87 res += '\\' 88 else: 89 res += c 90 else: 91 res += c 92 return res 93 94def p_start(p): 95 'start : top_statement_list' 96 p[0] = p[1] 97 98def p_top_statement_list(p): 99 '''top_statement_list : top_statement_list top_statement 100 | empty''' 101 if len(p) == 3: 102 p[0] = p[1] + [p[2]] 103 else: 104 p[0] = [] 105 106def p_top_statement(p): 107 '''top_statement : statement 108 | function_declaration_statement 109 | class_declaration_statement 110 | HALT_COMPILER LPAREN RPAREN SEMI''' 111 if len(p) == 2: 112 p[0] = p[1] 113 else: 114 # ??? 115 pass 116 117def p_top_statement_namespace(p): 118 '''top_statement : NAMESPACE namespace_name SEMI 119 | NAMESPACE LBRACE top_statement_list RBRACE 120 | NAMESPACE namespace_name LBRACE top_statement_list RBRACE''' 121 if len(p) == 4: 122 p[0] = ast.Namespace(p[2], [], lineno=p.lineno(1)) 123 elif len(p) == 5: 124 p[0] = ast.Namespace(None, p[3], lineno=p.lineno(1)) 125 else: 126 p[0] = ast.Namespace(p[2], p[4], lineno=p.lineno(1)) 127 128def p_top_statement_constant(p): 129 'top_statement : CONST constant_declarations SEMI' 130 p[0] = ast.ConstantDeclarations(p[2], lineno=p.lineno(1)) 131 132def p_top_statement_use(p): 133 'top_statement : USE use_declarations SEMI' 134 p[0] = ast.UseDeclarations(p[2], lineno=p.lineno(1)) 135 136def p_use_declarations(p): 137 '''use_declarations : use_declarations COMMA use_declaration 138 | use_declaration''' 139 if len(p) == 4: 140 p[0] = p[1] + [p[3]] 141 else: 142 p[0] = [p[1]] 143 144def p_use_declaration(p): 145 '''use_declaration : namespace_name 146 | NS_SEPARATOR namespace_name 147 | namespace_name AS STRING 148 | NS_SEPARATOR namespace_name AS STRING''' 149 if len(p) == 2: 150 p[0] = ast.UseDeclaration(p[1], None, lineno=p.lineno(1)) 151 elif len(p) == 3: 152 p[0] = ast.UseDeclaration(p[1] + p[2], None, lineno=p.lineno(1)) 153 elif len(p) == 4: 154 p[0] = ast.UseDeclaration(p[1], p[3], lineno=p.lineno(2)) 155 else: 156 p[0] = ast.UseDeclaration(p[1] + p[2], p[4], lineno=p.lineno(1)) 157 158def p_constant_declarations(p): 159 '''constant_declarations : constant_declarations COMMA constant_declaration 160 | constant_declaration''' 161 if len(p) == 4: 162 p[0] = p[1] + [p[3]] 163 else: 164 p[0] = [p[1]] 165 166def p_constant_declaration(p): 167 'constant_declaration : STRING EQUALS static_expr' 168 p[0] = ast.ConstantDeclaration(p[1], p[3], lineno=p.lineno(1)) 169 170def p_inner_statement_list(p): 171 '''inner_statement_list : inner_statement_list inner_statement 172 | empty''' 173 if len(p) == 3: 174 p[0] = p[1] + [p[2]] 175 else: 176 p[0] = [] 177 178def p_inner_statement(p): 179 '''inner_statement : statement 180 | function_declaration_statement 181 | class_declaration_statement 182 | HALT_COMPILER LPAREN RPAREN SEMI''' 183 assert len(p) == 2, "__HALT_COMPILER() can only be used from the outermost scope" 184 p[0] = p[1] 185 186def p_inner_statement_yield(p): 187 '''inner_statement : YIELD SEMI 188 | YIELD expr SEMI''' 189 if len(p) == 3: 190 p[0] = ast.Yield(None, lineno=p.lineno(1)) 191 else: 192 p[0] = ast.Yield(p[2], lineno=p.lineno(1)) 193 194def p_statement_block(p): 195 'statement : LBRACE inner_statement_list RBRACE' 196 p[0] = ast.Block(p[2], lineno=p.lineno(1)) 197 198def p_statement_if(p): 199 '''statement : IF LPAREN expr RPAREN statement elseif_list else_single 200 | IF LPAREN expr RPAREN COLON inner_statement_list new_elseif_list new_else_single ENDIF SEMI''' 201 if len(p) == 8: 202 p[0] = ast.If(p[3], p[5], p[6], p[7], lineno=p.lineno(1)) 203 else: 204 p[0] = ast.If(p[3], ast.Block(p[6], lineno=p.lineno(5)), 205 p[7], p[8], lineno=p.lineno(1)) 206 207def p_statement_while(p): 208 'statement : WHILE LPAREN expr RPAREN while_statement' 209 p[0] = ast.While(p[3], p[5], lineno=p.lineno(1)) 210 211def p_statement_do_while(p): 212 'statement : DO statement WHILE LPAREN expr RPAREN SEMI' 213 p[0] = ast.DoWhile(p[2], p[5], lineno=p.lineno(1)) 214 215def p_statement_for(p): 216 'statement : FOR LPAREN for_expr SEMI for_expr SEMI for_expr RPAREN for_statement' 217 p[0] = ast.For(p[3], p[5], p[7], p[9], lineno=p.lineno(1)) 218 219def p_statement_foreach(p): 220 'statement : FOREACH LPAREN expr AS foreach_variable foreach_optional_arg RPAREN foreach_statement' 221 if p[6] is None: 222 p[0] = ast.Foreach(p[3], None, p[5], p[8], lineno=p.lineno(1)) 223 else: 224 p[0] = ast.Foreach(p[3], p[5].name, p[6], p[8], lineno=p.lineno(1)) 225 226def p_statement_switch(p): 227 'statement : SWITCH LPAREN expr RPAREN switch_case_list' 228 p[0] = ast.Switch(p[3], p[5], lineno=p.lineno(1)) 229 230def p_statement_break(p): 231 '''statement : BREAK SEMI 232 | BREAK expr SEMI''' 233 if len(p) == 3: 234 p[0] = ast.Break(None, lineno=p.lineno(1)) 235 else: 236 p[0] = ast.Break(p[2], lineno=p.lineno(1)) 237 238def p_statement_continue(p): 239 '''statement : CONTINUE SEMI 240 | CONTINUE expr SEMI''' 241 if len(p) == 3: 242 p[0] = ast.Continue(None, lineno=p.lineno(1)) 243 else: 244 p[0] = ast.Continue(p[2], lineno=p.lineno(1)) 245 246def p_statement_return(p): 247 '''statement : RETURN SEMI 248 | RETURN expr SEMI''' 249 if len(p) == 3: 250 p[0] = ast.Return(None, lineno=p.lineno(1)) 251 else: 252 p[0] = ast.Return(p[2], lineno=p.lineno(1)) 253 254def p_statement_global(p): 255 'statement : GLOBAL global_var_list SEMI' 256 p[0] = ast.Global(p[2], lineno=p.lineno(1)) 257 258def p_statement_static(p): 259 'statement : STATIC static_var_list SEMI' 260 p[0] = ast.Static(p[2], lineno=p.lineno(1)) 261 262def p_statement_echo(p): 263 'statement : ECHO echo_expr_list SEMI' 264 p[0] = ast.Echo(p[2], lineno=p.lineno(1)) 265 266def p_statement_inline_html(p): 267 'statement : INLINE_HTML' 268 p[0] = ast.InlineHTML(p[1], lineno=p.lineno(1)) 269 270def p_statement_expr(p): 271 'statement : expr SEMI' 272 p[0] = p[1] 273 274def p_statement_unset(p): 275 'statement : UNSET LPAREN unset_variables RPAREN SEMI' 276 p[0] = ast.Unset(p[3], lineno=p.lineno(1)) 277 278def p_statement_empty(p): 279 'statement : SEMI' 280 pass 281 282def p_statement_try(p): 283 'statement : TRY LBRACE inner_statement_list RBRACE additional_catches maybe_finally' 284 p[0] = ast.Try(p[3], p[5], p[6], lineno=p.lineno(1)) 285 286def p_additional_catches(p): 287 '''additional_catches : additional_catches CATCH LPAREN fully_qualified_class_name VARIABLE RPAREN LBRACE inner_statement_list RBRACE 288 | empty''' 289 if len(p) == 10: 290 p[0] = p[1] + [ast.Catch(p[4], ast.Variable(p[5], lineno=p.lineno(5)), 291 p[8], lineno=p.lineno(2))] 292 else: 293 p[0] = [] 294 295def p_maybe_finally(p): 296 '''maybe_finally : FINALLY LBRACE inner_statement_list RBRACE 297 | empty''' 298 if len(p) == 5: 299 p[0] = ast.Finally(p[3], lineno=p.lineno(2)) 300 else: 301 p[0] = None 302 303def p_statement_throw(p): 304 'statement : THROW expr SEMI' 305 p[0] = ast.Throw(p[2], lineno=p.lineno(1)) 306 307def p_statement_declare(p): 308 'statement : DECLARE LPAREN declare_list RPAREN declare_statement' 309 p[0] = ast.Declare(p[3], p[5], lineno=p.lineno(1)) 310 311def p_declare_list(p): 312 '''declare_list : STRING EQUALS static_scalar 313 | declare_list COMMA STRING EQUALS static_scalar''' 314 if len(p) == 4: 315 p[0] = [ast.Directive(p[1], p[3], lineno=p.lineno(1))] 316 else: 317 p[0] = p[1] + [ast.Directive(p[3], p[5], lineno=p.lineno(2))] 318 319def p_declare_statement(p): 320 '''declare_statement : statement 321 | COLON inner_statement_list ENDDECLARE SEMI''' 322 if len(p) == 2: 323 p[0] = p[1] 324 else: 325 p[0] = ast.Block(p[2], lineno=p.lineno(1)) 326 327def p_elseif_list(p): 328 '''elseif_list : empty 329 | elseif_list ELSEIF LPAREN expr RPAREN statement''' 330 if len(p) == 2: 331 p[0] = [] 332 else: 333 p[0] = p[1] + [ast.ElseIf(p[4], p[6], lineno=p.lineno(2))] 334 335def p_else_single(p): 336 '''else_single : empty 337 | ELSE statement''' 338 if len(p) == 3: 339 p[0] = ast.Else(p[2], lineno=p.lineno(1)) 340 341def p_new_elseif_list(p): 342 '''new_elseif_list : empty 343 | new_elseif_list ELSEIF LPAREN expr RPAREN COLON inner_statement_list''' 344 if len(p) == 2: 345 p[0] = [] 346 else: 347 p[0] = p[1] + [ast.ElseIf(p[4], ast.Block(p[7], lineo=p.lineno(6)), 348 lineno=p.lineno(2))] 349 350def p_new_else_single(p): 351 '''new_else_single : empty 352 | ELSE COLON inner_statement_list''' 353 if len(p) == 4: 354 p[0] = ast.Else(ast.Block(p[3], lineno=p.lineno(2)), 355 lineno=p.lineno(1)) 356 357def p_while_statement(p): 358 '''while_statement : statement 359 | COLON inner_statement_list ENDWHILE SEMI''' 360 if len(p) == 2: 361 p[0] = p[1] 362 else: 363 p[0] = ast.Block(p[2], lineno=p.lineno(1)) 364 365def p_for_expr(p): 366 '''for_expr : empty 367 | non_empty_for_expr''' 368 p[0] = p[1] 369 370def p_non_empty_for_expr(p): 371 '''non_empty_for_expr : non_empty_for_expr COMMA expr 372 | expr''' 373 if len(p) == 4: 374 p[0] = p[1] + [p[3]] 375 else: 376 p[0] = [p[1]] 377 378def p_for_statement(p): 379 '''for_statement : statement 380 | COLON inner_statement_list ENDFOR SEMI''' 381 if len(p) == 2: 382 p[0] = p[1] 383 else: 384 p[0] = ast.Block(p[2], lineno=p.lineno(1)) 385 386def p_foreach_variable(p): 387 '''foreach_variable : LIST LPAREN assignment_list RPAREN 388 | variable 389 | AND variable''' 390 # actually the only the value supports lists, but that's a runtime error in 391 # php, so let it parse here as well 392 if len(p) == 5: 393 p[0] = ast.ForeachVariable(p[3], False, lineno=p.lineno(1)) 394 elif len(p) == 2: 395 p[0] = ast.ForeachVariable(p[1], False, lineno=p.lineno(1)) 396 else: 397 p[0] = ast.ForeachVariable(p[2], True, lineno=p.lineno(1)) 398 399def p_foreach_optional_arg(p): 400 '''foreach_optional_arg : empty 401 | DOUBLE_ARROW foreach_variable''' 402 if len(p) == 3: 403 p[0] = p[2] 404 405def p_foreach_statement(p): 406 '''foreach_statement : statement 407 | COLON inner_statement_list ENDFOREACH SEMI''' 408 if len(p) == 2: 409 p[0] = p[1] 410 else: 411 p[0] = ast.Block(p[2], lineno=p.lineno(1)) 412 413def p_switch_case_list(p): 414 '''switch_case_list : LBRACE case_list RBRACE 415 | LBRACE SEMI case_list RBRACE''' 416 if len(p) == 4: 417 p[0] = p[2] 418 else: 419 p[0] = p[3] 420 421def p_switch_case_list_colon(p): 422 '''switch_case_list : COLON case_list ENDSWITCH SEMI 423 | COLON SEMI case_list ENDSWITCH SEMI''' 424 if len(p) == 5: 425 p[0] = p[2] 426 else: 427 p[0] = p[3] 428 429def p_case_list(p): 430 '''case_list : empty 431 | case_list CASE expr case_separator inner_statement_list 432 | case_list DEFAULT case_separator inner_statement_list''' 433 if len(p) == 6: 434 p[0] = p[1] + [ast.Case(p[3], p[5], lineno=p.lineno(2))] 435 elif len(p) == 5: 436 p[0] = p[1] + [ast.Default(p[4], lineno=p.lineno(2))] 437 else: 438 p[0] = [] 439 440def p_case_separator(p): 441 '''case_separator : COLON 442 | SEMI''' 443 pass 444 445def p_global_var_list(p): 446 '''global_var_list : global_var_list COMMA global_var 447 | global_var''' 448 if len(p) == 4: 449 p[0] = p[1] + [p[3]] 450 else: 451 p[0] = [p[1]] 452 453def p_global_var(p): 454 '''global_var : VARIABLE 455 | DOLLAR variable 456 | DOLLAR LBRACE expr RBRACE''' 457 if len(p) == 2: 458 p[0] = ast.Variable(p[1], lineno=p.lineno(1)) 459 elif len(p) == 3: 460 p[0] = ast.Variable(p[2], lineno=p.lineno(1)) 461 else: 462 p[0] = ast.Variable(p[3], lineno=p.lineno(1)) 463 464def p_static_var_list(p): 465 '''static_var_list : static_var_list COMMA static_var 466 | static_var''' 467 if len(p) == 4: 468 p[0] = p[1] + [p[3]] 469 else: 470 p[0] = [p[1]] 471 472def p_static_var(p): 473 '''static_var : VARIABLE EQUALS static_scalar 474 | VARIABLE''' 475 if len(p) == 4: 476 p[0] = ast.StaticVariable(p[1], p[3], lineno=p.lineno(1)) 477 else: 478 p[0] = ast.StaticVariable(p[1], None, lineno=p.lineno(1)) 479 480def p_echo_expr_list(p): 481 '''echo_expr_list : echo_expr_list COMMA expr 482 | expr''' 483 if len(p) == 4: 484 p[0] = p[1] + [p[3]] 485 else: 486 p[0] = [p[1]] 487 488def p_unset_variables(p): 489 '''unset_variables : unset_variables COMMA unset_variable 490 | unset_variable''' 491 if len(p) == 4: 492 p[0] = p[1] + [p[3]] 493 else: 494 p[0] = [p[1]] 495 496def p_unset_variable(p): 497 'unset_variable : variable' 498 p[0] = p[1] 499 500def p_function_declaration_statement(p): 501 'function_declaration_statement : FUNCTION is_reference STRING LPAREN parameter_list RPAREN LBRACE inner_statement_list RBRACE' 502 p[0] = ast.Function(p[3], p[5], p[8], p[2], lineno=p.lineno(1)) 503 504def p_class_declaration_statement(p): 505 '''class_declaration_statement : class_entry_type STRING extends_from implements_list LBRACE class_statement_list RBRACE 506 | INTERFACE STRING interface_extends_list LBRACE class_statement_list RBRACE 507 | TRAIT STRING LBRACE trait_statement_list RBRACE''' 508 if len(p) == 8: 509 traits = [] 510 stmts = [] 511 for s in p[6]: 512 if isinstance(s, ast.TraitUse): 513 traits.append(s) 514 else: 515 stmts.append(s) 516 p[0] = ast.Class(p[2], p[1], p[3], p[4], traits, stmts, lineno=p.lineno(2)) 517 elif len(p) == 7: 518 p[0] = ast.Interface(p[2], p[3], p[5], lineno=p.lineno(1)) 519 else: 520 traits = [] 521 stmts = [] 522 for s in p[4]: 523 if isinstance(s, ast.TraitUse): 524 traits.append(s) 525 else: 526 stmts.append(s) 527 p[0] = ast.Trait(p[2], traits, stmts, lineno=p.lineno(1)) 528 529def p_class_entry_type(p): 530 '''class_entry_type : CLASS 531 | ABSTRACT CLASS 532 | FINAL CLASS''' 533 if len(p) == 3: 534 p[0] = p[1].lower() 535 536def p_extends_from(p): 537 '''extends_from : empty 538 | EXTENDS fully_qualified_class_name''' 539 if len(p) == 3: 540 p[0] = p[2] 541 542def p_fully_qualified_class_name(p): 543 '''fully_qualified_class_name : namespace_name 544 | NS_SEPARATOR namespace_name 545 | NAMESPACE NS_SEPARATOR namespace_name''' 546 if len(p) == 2: 547 p[0] = p[1] 548 elif len(p) == 3: 549 p[0] = p[1] + p[2] 550 else: 551 p[0] = p[1] + p[2] + p[3] 552 553def p_implements_list(p): 554 '''implements_list : IMPLEMENTS interface_list 555 | empty''' 556 if len(p) == 3: 557 p[0] = p[2] 558 else: 559 p[0] = [] 560 561def p_trait_modifiers_list(p): 562 '''trait_modifiers_list : trait_modifiers_list trait_modifier 563 | empty''' 564 if len(p) == 3: 565 p[0] = p[1] + [p[2]] 566 else: 567 p[0] = [] 568 569def p_trait_member(p): 570 '''trait_member : fully_qualified_class_name DOUBLE_COLON STRING 571 | STRING''' 572 if len(p) == 4: 573 p[0] = ast.StaticProperty(p[1], p[3], lineno=p.lineno(2)) 574 else: 575 p[0] = p[1] 576 577def p_trait_modifier(p): 578 'trait_modifier : trait_member AS STRING SEMI' 579 p[0] = ast.TraitModifier(p[1], p[3], None) 580 581def p_trait_modifier_with_visibility(p): 582 '''trait_modifier : trait_member AS visibility_modifier STRING SEMI 583 | trait_member AS visibility_modifier SEMI''' 584 if len(p) == 6: 585 p[0] = ast.TraitModifier(p[1], p[4], p[3]) 586 else: 587 p[0] = ast.TraitModifier(p[1], None, p[3]) 588 589def p_trait_statement_list(p): 590 '''trait_statement_list : trait_statement_list trait_statement 591 | empty''' 592 593 if len(p) == 3: 594 p[0] = p[1] + [p[2]] 595 else: 596 p[0] = [] 597 598def p_trait_statement(p): 599 '''trait_statement : method_modifiers FUNCTION is_reference STRING LPAREN parameter_list RPAREN method_body 600 | variable_modifiers class_variable_declaration SEMI 601 | USE fully_qualified_class_name LBRACE trait_modifiers_list RBRACE 602 | USE fully_qualified_class_name SEMI''' 603 if len(p) == 9: 604 p[0] = ast.Method(p[4], p[1], p[6], p[8], p[3], lineno=p.lineno(2)) 605 elif len(p) == 6: 606 p[0] = ast.TraitUse(p[2], p[4], lineno=p.lineno(1)) 607 else: 608 if p[1] == 'use': 609 p[0] = ast.TraitUse(p[2], [], lineno=p.lineno(1)) 610 else: 611 p[0] = ast.ClassVariables(p[1], p[2], lineno=p.lineno(3)) 612 613def p_class_statement_list(p): 614 '''class_statement_list : class_statement_list class_statement 615 | empty''' 616 617 if len(p) == 3: 618 p[0] = p[1] + [p[2]] 619 else: 620 p[0] = [] 621 622def p_class_statement(p): 623 '''class_statement : method_modifiers FUNCTION is_reference STRING LPAREN parameter_list RPAREN method_body 624 | variable_modifiers class_variable_declaration SEMI 625 | class_constant_declaration SEMI 626 | USE fully_qualified_class_name LBRACE trait_modifiers_list RBRACE 627 | USE fully_qualified_class_name SEMI''' 628 if len(p) == 9: 629 p[0] = ast.Method(p[4], p[1], p[6], p[8], p[3], lineno=p.lineno(2)) 630 elif len(p) == 6: 631 p[0] = ast.TraitUse(p[2], p[4], lineno=p.lineno(1)) 632 elif len(p) == 4: 633 if p[1] == 'use': 634 p[0] = ast.TraitUse(p[2], [], lineno=p.lineno(1)) 635 else: 636 p[0] = ast.ClassVariables(p[1], p[2], lineno=p.lineno(3)) 637 else: 638 p[0] = ast.ClassConstants(p[1], lineno=p.lineno(2)) 639 640def p_class_variable_declaration_initial(p): 641 '''class_variable_declaration : class_variable_declaration COMMA VARIABLE EQUALS static_scalar 642 | VARIABLE EQUALS static_scalar''' 643 if len(p) == 6: 644 p[0] = p[1] + [ast.ClassVariable(p[3], p[5], lineno=p.lineno(2))] 645 else: 646 p[0] = [ast.ClassVariable(p[1], p[3], lineno=p.lineno(1))] 647 648def p_class_variable_declaration_no_initial(p): 649 '''class_variable_declaration : class_variable_declaration COMMA VARIABLE 650 | VARIABLE''' 651 if len(p) == 4: 652 p[0] = p[1] + [ast.ClassVariable(p[3], None, lineno=p.lineno(2))] 653 else: 654 p[0] = [ast.ClassVariable(p[1], None, lineno=p.lineno(1))] 655 656def p_class_constant_declaration(p): 657 '''class_constant_declaration : class_constant_declaration COMMA STRING EQUALS static_expr 658 | CONST STRING EQUALS static_expr''' 659 if len(p) == 6: 660 p[0] = p[1] + [ast.ClassConstant(p[3], p[5], lineno=p.lineno(2))] 661 else: 662 p[0] = [ast.ClassConstant(p[2], p[4], lineno=p.lineno(1))] 663 664def p_interface_list(p): 665 '''interface_list : interface_list COMMA fully_qualified_class_name 666 | fully_qualified_class_name''' 667 if len(p) == 4: 668 p[0] = p[1] + [p[3]] 669 else: 670 p[0] = [p[1]] 671 672def p_interface_extends_list(p): 673 '''interface_extends_list : EXTENDS interface_list 674 | empty''' 675 if len(p) == 3: 676 p[0] = p[2] 677 678def p_variable_modifiers_non_empty(p): 679 'variable_modifiers : non_empty_member_modifiers' 680 p[0] = p[1] 681 682def p_variable_modifiers_var(p): 683 'variable_modifiers : VAR' 684 p[0] = [] 685 686def p_method_modifiers_non_empty(p): 687 'method_modifiers : non_empty_member_modifiers' 688 p[0] = p[1] 689 690def p_method_modifiers_empty(p): 691 'method_modifiers : empty' 692 p[0] = [] 693 694def p_method_body(p): 695 '''method_body : LBRACE inner_statement_list RBRACE 696 | SEMI''' 697 if len(p) == 4: 698 p[0] = p[2] 699 else: 700 p[0] = [] 701 702def p_non_empty_member_modifiers(p): 703 '''non_empty_member_modifiers : non_empty_member_modifiers member_modifier 704 | member_modifier''' 705 if len(p) == 3: 706 p[0] = p[1] + [p[2]] 707 else: 708 p[0] = [p[1]] 709 710def p_visibility_modifier(p): 711 '''visibility_modifier : PUBLIC 712 | PROTECTED 713 | PRIVATE''' 714 p[0] = p[1].lower() 715 716def p_member_modifier(p): 717 '''member_modifier : visibility_modifier 718 | STATIC 719 | ABSTRACT 720 | FINAL''' 721 p[0] = p[1].lower() 722 723def p_is_reference(p): 724 '''is_reference : AND 725 | empty''' 726 p[0] = p[1] is not None 727 728def p_parameter_list(p): 729 '''parameter_list : parameter_list COMMA parameter 730 | parameter''' 731 if len(p) == 4: 732 p[0] = p[1] + [p[3]] 733 else: 734 p[0] = [p[1]] 735 736def p_parameter_list_empty(p): 737 'parameter_list : empty' 738 p[0] = [] 739 740def p_parameter(p): 741 '''parameter : VARIABLE 742 | class_name VARIABLE 743 | AND VARIABLE 744 | class_name AND VARIABLE 745 | VARIABLE EQUALS static_scalar 746 | class_name VARIABLE EQUALS static_scalar 747 | AND VARIABLE EQUALS static_scalar 748 | class_name AND VARIABLE EQUALS static_scalar''' 749 if len(p) == 2: # VARIABLE 750 p[0] = ast.FormalParameter(p[1], None, False, None, lineno=p.lineno(1)) 751 elif len(p) == 3 and p[1] == '&': # AND VARIABLE 752 p[0] = ast.FormalParameter(p[2], None, True, None, lineno=p.lineno(1)) 753 elif len(p) == 3 and p[1] != '&': # STRING VARIABLE 754 p[0] = ast.FormalParameter(p[2], None, False, p[1], lineno=p.lineno(1)) 755 elif len(p) == 4 and p[2] != '&': # VARIABLE EQUALS static_scalar 756 p[0] = ast.FormalParameter(p[1], p[3], False, None, lineno=p.lineno(1)) 757 elif len(p) == 4 and p[2] == '&': # STRING AND VARIABLE 758 p[0] = ast.FormalParameter(p[3], None, True, p[1], lineno=p.lineno(1)) 759 elif len(p) == 5 and p[1] == '&': # AND VARIABLE EQUALS static_scalar 760 p[0] = ast.FormalParameter(p[2], p[4], True, None, lineno=p.lineno(1)) 761 elif len(p) == 5 and p[1] != '&': # class_name VARIABLE EQUALS static_scalar 762 p[0] = ast.FormalParameter(p[2], p[4], False, p[1], lineno=p.lineno(1)) 763 else: # STRING AND VARIABLE EQUALS static_scalar 764 p[0] = ast.FormalParameter(p[3], p[5], True, p[1], lineno=p.lineno(1)) 765 766def p_expr_variable(p): 767 'expr : variable' 768 p[0] = p[1] 769 770def p_expr_assign(p): 771 '''expr : variable EQUALS expr 772 | variable EQUALS AND expr''' 773 if len(p) == 5: 774 p[0] = ast.Assignment(p[1], p[4], True, lineno=p.lineno(2)) 775 else: 776 p[0] = ast.Assignment(p[1], p[3], False, lineno=p.lineno(2)) 777 778def p_expr_new(p): 779 'expr : NEW class_name_reference ctor_arguments' 780 p[0] = ast.New(p[2], p[3], lineno=p.lineno(1)) 781 782def p_expr_objectop(p): 783 'expr : expr OBJECT_OPERATOR object_property method_or_not' 784 name, _dims = p[3] 785 assert _dims == [] 786 params = p[4] 787 if params is not None: 788 p[0] = ast.MethodCall(p[1], name, params, lineno=p.lineno(3)) 789 else: 790 p[0] = ast.ObjectProperty(p[1], name, lineno=p.lineno(3)) 791 792def p_class_name_reference(p): 793 '''class_name_reference : class_name 794 | dynamic_class_name_reference''' 795 p[0] = p[1] 796 797def p_class_name(p): 798 '''class_name : namespace_name 799 | NS_SEPARATOR namespace_name 800 | NAMESPACE NS_SEPARATOR namespace_name''' 801 if len(p) == 2: 802 p[0] = p[1] 803 elif len(p) == 3: 804 p[0] = p[1] + p[2] 805 else: 806 p[0] = p[1] + p[2] + p[3] 807 808def p_class_name_static(p): 809 'class_name : STATIC' 810 p[0] = p[1].lower() 811 812def p_dynamic_class_name_reference(p): 813 '''dynamic_class_name_reference : base_variable OBJECT_OPERATOR object_property dynamic_class_name_variable_properties 814 | base_variable''' 815 if len(p) == 5: 816 name, dims = p[3] 817 p[0] = ast.ObjectProperty(p[1], name, lineno=p.lineno(2)) 818 for class_, dim, lineno in dims: 819 p[0] = class_(p[0], dim, lineno=lineno) 820 for name, dims in p[4]: 821 p[0] = ast.ObjectProperty(p[0], name, lineno=p.lineno(2)) 822 for class_, dim, lineno in dims: 823 p[0] = class_(p[0], dim, lineno=lineno) 824 else: 825 p[0] = p[1] 826 827def p_dynamic_class_name_variable_properties(p): 828 '''dynamic_class_name_variable_properties : dynamic_class_name_variable_properties dynamic_class_name_variable_property 829 | empty''' 830 if len(p) == 3: 831 p[0] = p[1] + [p[2]] 832 else: 833 p[0] = [] 834 835def p_dynamic_class_name_variable_property(p): 836 'dynamic_class_name_variable_property : OBJECT_OPERATOR object_property' 837 p[0] = p[2] 838 839def p_ctor_arguments(p): 840 '''ctor_arguments : LPAREN function_call_parameter_list RPAREN 841 | empty''' 842 if len(p) == 4: 843 p[0] = p[2] 844 else: 845 p[0] = [] 846 847def p_expr_clone(p): 848 'expr : CLONE expr' 849 p[0] = ast.Clone(p[2], lineno=p.lineno(1)) 850 851def p_expr_list_assign(p): 852 'expr : LIST LPAREN assignment_list RPAREN EQUALS expr' 853 p[0] = ast.ListAssignment(p[3], p[6], lineno=p.lineno(1)) 854 855def p_assignment_list(p): 856 '''assignment_list : assignment_list COMMA assignment_list_element 857 | assignment_list_element''' 858 if len(p) == 4: 859 p[0] = p[1] + [p[3]] 860 else: 861 p[0] = [p[1]] 862 863def p_assignment_list_element(p): 864 '''assignment_list_element : variable 865 | empty 866 | LIST LPAREN assignment_list RPAREN''' 867 if len(p) == 2: 868 p[0] = p[1] 869 else: 870 p[0] = p[3] 871 872def p_variable(p): 873 '''variable : base_variable_with_function_calls OBJECT_OPERATOR object_property method_or_not variable_properties 874 | base_variable_with_function_calls''' 875 if len(p) == 6: 876 name, dims = p[3] 877 params = p[4] 878 if params is not None: 879 p[0] = ast.MethodCall(p[1], name, params, lineno=p.lineno(2)) 880 else: 881 p[0] = ast.ObjectProperty(p[1], name, lineno=p.lineno(2)) 882 for class_, dim, lineno in dims: 883 p[0] = class_(p[0], dim, lineno=lineno) 884 for (name, dims), params in p[5]: 885 if params is not None: 886 p[0] = ast.MethodCall(p[0], name, params, lineno=p.lineno(2)) 887 else: 888 p[0] = ast.ObjectProperty(p[0], name, lineno=p.lineno(2)) 889 for class_, dim, lineno in dims: 890 p[0] = class_(p[0], dim, lineno=lineno) 891 else: 892 p[0] = p[1] 893 894def p_base_variable_with_function_calls(p): 895 '''base_variable_with_function_calls : base_variable 896 | function_call''' 897 p[0] = p[1] 898 899def p_function_call(p): 900 '''function_call : namespace_name LPAREN function_call_parameter_list RPAREN 901 | NS_SEPARATOR namespace_name LPAREN function_call_parameter_list RPAREN 902 | NAMESPACE NS_SEPARATOR namespace_name LPAREN function_call_parameter_list RPAREN''' 903 if len(p) == 5: 904 p[0] = ast.FunctionCall(p[1], p[3], lineno=p.lineno(2)) 905 elif len(p) == 6: 906 p[0] = ast.FunctionCall(p[1] + p[2], p[4], lineno=p.lineno(1)) 907 else: 908 p[0] = ast.FunctionCall(p[1] + p[2] + p[3], p[5], lineno=p.lineno(1)) 909 910def p_function_call_static(p): 911 '''function_call : class_name DOUBLE_COLON STRING LPAREN function_call_parameter_list RPAREN 912 | class_name DOUBLE_COLON variable_without_objects LPAREN function_call_parameter_list RPAREN 913 | variable_class_name DOUBLE_COLON STRING LPAREN function_call_parameter_list RPAREN 914 | variable_class_name DOUBLE_COLON variable_without_objects LPAREN function_call_parameter_list RPAREN''' 915 p[0] = ast.StaticMethodCall(p[1], p[3], p[5], lineno=p.lineno(2)) 916 917def p_function_call_static_dynamic_name(p): 918 '''function_call : class_name DOUBLE_COLON LBRACE expr RBRACE LPAREN function_call_parameter_list RPAREN 919 | variable_class_name DOUBLE_COLON LBRACE expr RBRACE LPAREN function_call_parameter_list RPAREN''' 920 p[0] = ast.StaticMethodCall(p[1], p[4], p[7], lineno=p.lineno(2)) 921 922def p_function_call_variable(p): 923 'function_call : variable_without_objects LPAREN function_call_parameter_list RPAREN' 924 p[0] = ast.FunctionCall(p[1], p[3], lineno=p.lineno(2)) 925 926def p_function_call_backtick_shell_exec(p): 927 'function_call : BACKTICK encaps_list BACKTICK' 928 p[0] = ast.FunctionCall('shell_exec', [ast.Parameter(p[2], False)], lineno=p.lineno(1)) 929 930def p_method_or_not(p): 931 '''method_or_not : LPAREN function_call_parameter_list RPAREN 932 | empty''' 933 if len(p) == 4: 934 p[0] = p[2] 935 936def p_variable_properties(p): 937 '''variable_properties : variable_properties variable_property 938 | empty''' 939 if len(p) == 3: 940 p[0] = p[1] + [p[2]] 941 else: 942 p[0] = [] 943 944def p_variable_property(p): 945 'variable_property : OBJECT_OPERATOR object_property method_or_not' 946 p[0] = (p[2], p[3]) 947 948def p_base_variable(p): 949 '''base_variable : simple_indirect_reference 950 | static_member''' 951 p[0] = p[1] 952 953def p_simple_indirect_reference(p): 954 '''simple_indirect_reference : DOLLAR simple_indirect_reference 955 | reference_variable''' 956 if len(p) == 3: 957 p[0] = ast.Variable(p[2], lineno=p.lineno(1)) 958 else: 959 p[0] = p[1] 960 961def p_static_member(p): 962 '''static_member : class_name DOUBLE_COLON variable_without_objects 963 | variable_class_name DOUBLE_COLON variable_without_objects 964 | class_name DOUBLE_COLON LBRACE expr RBRACE 965 | variable_class_name DOUBLE_COLON LBRACE expr RBRACE''' 966 if len(p) == 4: 967 p[0] = ast.StaticProperty(p[1], p[3], lineno=p.lineno(2)) 968 else: 969 p[0] = ast.StaticProperty(p[1], p[4], lineno=p.lineno(2)) 970 971def p_variable_class_name(p): 972 'variable_class_name : reference_variable' 973 p[0] = p[1] 974 975def p_variable_array_offset(p): 976 'variable : variable LBRACKET dim_offset RBRACKET' 977 p[0] = ast.ArrayOffset(p[1], p[3], lineno=p.lineno(2)) 978 979def p_reference_variable_array_offset(p): 980 'reference_variable : reference_variable LBRACKET dim_offset RBRACKET' 981 p[0] = ast.ArrayOffset(p[1], p[3], lineno=p.lineno(2)) 982 983def p_reference_variable_string_offset(p): 984 'reference_variable : reference_variable LBRACE expr RBRACE' 985 p[0] = ast.StringOffset(p[1], p[3], lineno=p.lineno(2)) 986 987def p_reference_variable_compound_variable(p): 988 'reference_variable : compound_variable' 989 p[0] = p[1] 990 991def p_expr_string_offset(p): 992 'expr : expr LBRACE dim_offset RBRACE' 993 p[0] = ast.StringOffset(p[1], p[3], lineno=p.lineno(2)) 994 995def p_compound_variable(p): 996 '''compound_variable : VARIABLE 997 | DOLLAR LBRACE expr RBRACE''' 998 if len(p) == 2: 999 p[0] = ast.Variable(p[1], lineno=p.lineno(1)) 1000 else: 1001 p[0] = ast.Variable(p[3], lineno=p.lineno(1)) 1002 1003def p_dim_offset(p): 1004 '''dim_offset : expr 1005 | empty''' 1006 p[0] = p[1] 1007 1008def p_object_property(p): 1009 '''object_property : variable_name object_dim_list 1010 | variable_without_objects''' 1011 if len(p) == 3: 1012 p[0] = (p[1], p[2]) 1013 else: 1014 p[0] = (p[1], []) 1015 1016def p_object_dim_list_empty(p): 1017 'object_dim_list : empty' 1018 p[0] = [] 1019 1020def p_object_dim_list_array_offset(p): 1021 'object_dim_list : object_dim_list LBRACKET dim_offset RBRACKET' 1022 p[0] = p[1] + [(ast.ArrayOffset, p[3], p.lineno(2))] 1023 1024def p_object_dim_list_string_offset(p): 1025 'object_dim_list : object_dim_list LBRACE expr RBRACE' 1026 p[0] = p[1] + [(ast.StringOffset, p[3], p.lineno(2))] 1027 1028def p_variable_name(p): 1029 '''variable_name : STRING 1030 | LBRACE expr RBRACE''' 1031 if len(p) == 2: 1032 p[0] = p[1] 1033 else: 1034 p[0] = p[2] 1035 1036def p_variable_without_objects(p): 1037 'variable_without_objects : simple_indirect_reference' 1038 p[0] = p[1] 1039 1040def p_expr_scalar(p): 1041 'expr : scalar' 1042 p[0] = p[1] 1043 1044def p_expr_array(p): 1045 '''expr : ARRAY LPAREN array_pair_list RPAREN 1046 | LBRACKET array_pair_list RBRACKET''' 1047 if len(p) == 5: 1048 contents = p[3] 1049 else: 1050 contents = p[2] 1051 1052 p[0] = ast.Array(contents, lineno=p.lineno(1)) 1053 1054def p_array_pair_list(p): 1055 '''array_pair_list : empty 1056 | non_empty_array_pair_list possible_comma''' 1057 if len(p) == 2: 1058 p[0] = [] 1059 else: 1060 p[0] = p[1] 1061 1062def p_non_empty_array_pair_list_item(p): 1063 '''non_empty_array_pair_list : non_empty_array_pair_list COMMA AND variable 1064 | non_empty_array_pair_list COMMA expr 1065 | AND variable 1066 | expr''' 1067 if len(p) == 5: 1068 p[0] = p[1] + [ast.ArrayElement(None, p[4], True, lineno=p.lineno(2))] 1069 elif len(p) == 4: 1070 p[0] = p[1] + [ast.ArrayElement(None, p[3], False, lineno=p.lineno(2))] 1071 elif len(p) == 3: 1072 p[0] = [ast.ArrayElement(None, p[2], True, lineno=p.lineno(1))] 1073 else: 1074 p[0] = [ast.ArrayElement(None, p[1], False, lineno=p.lineno(1))] 1075 1076def p_non_empty_array_pair_list_pair(p): 1077 '''non_empty_array_pair_list : non_empty_array_pair_list COMMA expr DOUBLE_ARROW AND variable 1078 | non_empty_array_pair_list COMMA expr DOUBLE_ARROW expr 1079 | expr DOUBLE_ARROW AND variable 1080 | expr DOUBLE_ARROW expr''' 1081 if len(p) == 7: 1082 p[0] = p[1] + [ast.ArrayElement(p[3], p[6], True, lineno=p.lineno(2))] 1083 elif len(p) == 6: 1084 p[0] = p[1] + [ast.ArrayElement(p[3], p[5], False, lineno=p.lineno(2))] 1085 elif len(p) == 5: 1086 p[0] = [ast.ArrayElement(p[1], p[4], True, lineno=p.lineno(2))] 1087 else: 1088 p[0] = [ast.ArrayElement(p[1], p[3], False, lineno=p.lineno(2))] 1089 1090def p_possible_comma(p): 1091 '''possible_comma : empty 1092 | COMMA''' 1093 pass 1094 1095def p_function_call_parameter_list(p): 1096 '''function_call_parameter_list : function_call_parameter_list COMMA function_call_parameter 1097 | function_call_parameter''' 1098 if len(p) == 4: 1099 p[0] = p[1] + [p[3]] 1100 else: 1101 p[0] = [p[1]] 1102 1103def p_function_call_parameter_list_empty(p): 1104 'function_call_parameter_list : empty' 1105 p[0] = [] 1106 1107def p_function_call_parameter(p): 1108 '''function_call_parameter : expr 1109 | AND variable''' 1110 if len(p) == 2: 1111 p[0] = ast.Parameter(p[1], False, lineno=p.lineno(1)) 1112 else: 1113 p[0] = ast.Parameter(p[2], True, lineno=p.lineno(1)) 1114 1115def p_expr_function(p): 1116 'expr : FUNCTION is_reference LPAREN parameter_list RPAREN lexical_vars LBRACE inner_statement_list RBRACE' 1117 p[0] = ast.Closure(p[4], p[6], p[8], p[2], lineno=p.lineno(1)) 1118 1119def p_lexical_vars(p): 1120 '''lexical_vars : USE LPAREN lexical_var_list RPAREN 1121 | empty''' 1122 if len(p) == 5: 1123 p[0] = p[3] 1124 else: 1125 p[0] = [] 1126 1127def p_lexical_var_list(p): 1128 '''lexical_var_list : lexical_var_list COMMA AND VARIABLE 1129 | lexical_var_list COMMA VARIABLE 1130 | AND VARIABLE 1131 | VARIABLE''' 1132 if len(p) == 5: 1133 p[0] = p[1] + [ast.LexicalVariable(p[4], True, lineno=p.lineno(2))] 1134 elif len(p) == 4: 1135 p[0] = p[1] + [ast.LexicalVariable(p[3], False, lineno=p.lineno(2))] 1136 elif len(p) == 3: 1137 p[0] = [ast.LexicalVariable(p[2], True, lineno=p.lineno(1))] 1138 else: 1139 p[0] = [ast.LexicalVariable(p[1], False, lineno=p.lineno(1))] 1140 1141def p_expr_assign_op(p): 1142 '''expr : variable PLUS_EQUAL expr 1143 | variable MINUS_EQUAL expr 1144 | variable MUL_EQUAL expr 1145 | variable DIV_EQUAL expr 1146 | variable CONCAT_EQUAL expr 1147 | variable MOD_EQUAL expr 1148 | variable AND_EQUAL expr 1149 | variable OR_EQUAL expr 1150 | variable XOR_EQUAL expr 1151 | variable SL_EQUAL expr 1152 | variable SR_EQUAL expr''' 1153 p[0] = ast.AssignOp(p[2], p[1], p[3], lineno=p.lineno(2)) 1154 1155def p_expr_binary_op(p): 1156 '''expr : expr BOOLEAN_AND expr 1157 | expr BOOLEAN_OR expr 1158 | expr LOGICAL_AND expr 1159 | expr LOGICAL_OR expr 1160 | expr LOGICAL_XOR expr 1161 | expr AND expr 1162 | expr OR expr 1163 | expr XOR expr 1164 | expr CONCAT expr 1165 | expr PLUS expr 1166 | expr MINUS expr 1167 | expr MUL expr 1168 | expr DIV expr 1169 | expr SL expr 1170 | expr SR expr 1171 | expr MOD expr 1172 | expr IS_IDENTICAL expr 1173 | expr IS_NOT_IDENTICAL expr 1174 | expr IS_EQUAL expr 1175 | expr IS_NOT_EQUAL expr 1176 | expr IS_SMALLER expr 1177 | expr IS_SMALLER_OR_EQUAL expr 1178 | expr IS_GREATER expr 1179 | expr IS_GREATER_OR_EQUAL expr 1180 | expr INSTANCEOF expr 1181 | expr INSTANCEOF STATIC''' 1182 p[0] = ast.BinaryOp(p[2].lower(), p[1], p[3], lineno=p.lineno(2)) 1183 1184def p_expr_unary_op(p): 1185 '''expr : PLUS expr 1186 | MINUS expr 1187 | NOT expr 1188 | BOOLEAN_NOT expr''' 1189 p[0] = ast.UnaryOp(p[1], p[2], lineno=p.lineno(1)) 1190 1191def p_expr_ternary_op(p): 1192 'expr : expr QUESTION expr COLON expr' 1193 p[0] = ast.TernaryOp(p[1], p[3], p[5], lineno=p.lineno(2)) 1194 1195def p_expr_short_ternary_op(p): 1196 'expr : expr QUESTION COLON expr' 1197 p[0] = ast.TernaryOp(p[1], p[1], p[4], lineno=p.lineno(2)) 1198 1199def p_expr_pre_incdec(p): 1200 '''expr : INC variable 1201 | DEC variable''' 1202 p[0] = ast.PreIncDecOp(p[1], p[2], lineno=p.lineno(1)) 1203 1204def p_expr_post_incdec(p): 1205 '''expr : variable INC 1206 | variable DEC''' 1207 p[0] = ast.PostIncDecOp(p[2], p[1], lineno=p.lineno(2)) 1208 1209def p_expr_cast_int(p): 1210 'expr : INT_CAST expr' 1211 p[0] = ast.Cast('int', p[2], lineno=p.lineno(1)) 1212 1213def p_expr_cast_double(p): 1214 'expr : DOUBLE_CAST expr' 1215 p[0] = ast.Cast('double', p[2], lineno=p.lineno(1)) 1216 1217def p_expr_cast_string(p): 1218 'expr : STRING_CAST expr' 1219 p[0] = ast.Cast('string', p[2], lineno=p.lineno(1)) 1220 1221def p_expr_cast_array(p): 1222 'expr : ARRAY_CAST expr' 1223 p[0] = ast.Cast('array', p[2], lineno=p.lineno(1)) 1224 1225def p_expr_cast_object(p): 1226 'expr : OBJECT_CAST expr' 1227 p[0] = ast.Cast('object', p[2], lineno=p.lineno(1)) 1228 1229def p_expr_cast_bool(p): 1230 'expr : BOOL_CAST expr' 1231 p[0] = ast.Cast('bool', p[2], lineno=p.lineno(1)) 1232 1233def p_expr_cast_unset(p): 1234 'expr : UNSET_CAST expr' 1235 p[0] = ast.Cast('unset', p[2], lineno=p.lineno(1)) 1236 1237def p_expr_cast_binary(p): 1238 'expr : BINARY_CAST expr' 1239 p[0] = ast.Cast('binary', p[2], lineno=p.lineno(1)) 1240 1241def p_expr_isset(p): 1242 'expr : ISSET LPAREN isset_variables RPAREN' 1243 p[0] = ast.IsSet(p[3], lineno=p.lineno(1)) 1244 1245def p_isset_variables(p): 1246 '''isset_variables : isset_variables COMMA variable 1247 | variable''' 1248 if len(p) == 4: 1249 p[0] = p[1] + [p[3]] 1250 else: 1251 p[0] = [p[1]] 1252 1253def p_expr_empty(p): 1254 'expr : EMPTY LPAREN expr RPAREN' 1255 p[0] = ast.Empty(p[3], lineno=p.lineno(1)) 1256 1257def p_expr_eval(p): 1258 'expr : EVAL LPAREN expr RPAREN' 1259 p[0] = ast.Eval(p[3], lineno=p.lineno(1)) 1260 1261def p_expr_include(p): 1262 'expr : INCLUDE expr' 1263 p[0] = ast.Include(p[2], False, lineno=p.lineno(1)) 1264 1265def p_expr_include_once(p): 1266 'expr : INCLUDE_ONCE expr' 1267 p[0] = ast.Include(p[2], True, lineno=p.lineno(1)) 1268 1269def p_expr_require(p): 1270 'expr : REQUIRE expr' 1271 p[0] = ast.Require(p[2], False, lineno=p.lineno(1)) 1272 1273def p_expr_require_once(p): 1274 'expr : REQUIRE_ONCE expr' 1275 p[0] = ast.Require(p[2], True, lineno=p.lineno(1)) 1276 1277def p_exit_or_die(p): 1278 '''exit_or_die : EXIT 1279 | DIE''' 1280 p[0] = p[1] 1281 p.set_lineno(0, p.lineno(1)) 1282 1283def p_expr_exit(p): 1284 '''expr : exit_or_die 1285 | exit_or_die LPAREN RPAREN 1286 | exit_or_die LPAREN expr RPAREN''' 1287 # although they're treated the same in PHP itself, annotate the exit type 1288 # (die/exit) so that apps can create better user experience 1289 if len(p) == 5: 1290 p[0] = ast.Exit(p[3], p[1], lineno=p.lineno(1)) 1291 else: 1292 p[0] = ast.Exit(None, p[1], lineno=p.lineno(1)) 1293 1294def p_expr_print(p): 1295 'expr : PRINT expr' 1296 p[0] = ast.Print(p[2], lineno=p.lineno(1)) 1297 1298def p_expr_silence(p): 1299 'expr : AT expr' 1300 p[0] = ast.Silence(p[2], lineno=p.lineno(1)) 1301 1302def p_expr_group(p): 1303 'expr : LPAREN expr RPAREN' 1304 p[0] = p[2] 1305 1306def p_scalar(p): 1307 '''scalar : class_constant 1308 | common_scalar 1309 | QUOTE encaps_list QUOTE 1310 | STRING QUOTE encaps_list QUOTE 1311 | scalar_heredoc 1312 | nowdoc 1313 | class_name_constant''' 1314 if len(p) == 4: 1315 p[0] = p[2] 1316 elif len(p) == 5: 1317 if p[1] == 'b': 1318 p[0] = p[3] 1319 else: 1320 p[0] = p[1] 1321 1322def p_scalar_heredoc(p): 1323 'scalar_heredoc : START_HEREDOC encaps_list END_HEREDOC' 1324 if isinstance(p[2], ast.BinaryOp): 1325 # due to how lexer works, the last operation is joining an unnecessary 1326 # newline character 1327 assert isinstance(p[2].right, string_type) 1328 p[2].right = p[2].right[:-1] 1329 if p[2].right: 1330 p[0] = p[2] 1331 else: 1332 p[0] = p[2].left 1333 else: 1334 # due to how lexer works, the last operation is joining an unnecessary 1335 # newline character 1336 p[0] = p[2][:-1] 1337 1338def p_nowdoc(p): 1339 'nowdoc : START_NOWDOC nowdoc_text_content END_NOWDOC' 1340 # due to how lexer works, the last operation is joining an unnecessary 1341 # newline character 1342 p[0] = p[2][:-1] 1343 1344def p_nowdoc_text_content(p): 1345 '''nowdoc_text_content : nowdoc_text_content ENCAPSED_AND_WHITESPACE 1346 | empty''' 1347 if len(p) == 3: 1348 p[0] = p[1] + p[2] 1349 else: 1350 p[0] = '' 1351 1352def p_scalar_string_varname(p): 1353 'scalar : STRING_VARNAME' 1354 p[0] = ast.Variable('$' + p[1], lineno=p.lineno(1)) 1355 1356def p_scalar_namespace_name(p): 1357 '''scalar : namespace_name 1358 | NS_SEPARATOR namespace_name 1359 | NAMESPACE NS_SEPARATOR namespace_name''' 1360 if len(p) == 2: 1361 p[0] = ast.Constant(p[1], lineno=p.lineno(1)) 1362 elif len(p) == 3: 1363 p[0] = ast.Constant(p[1] + p[2], lineno=p.lineno(1)) 1364 else: 1365 p[0] = ast.Constant(p[1] + p[2] + p[3], lineno=p.lineno(1)) 1366 1367def p_class_constant(p): 1368 '''class_constant : class_name DOUBLE_COLON STRING 1369 | variable_class_name DOUBLE_COLON STRING''' 1370 p[0] = ast.StaticProperty(p[1], p[3], lineno=p.lineno(2)) 1371 1372def p_common_scalar_lnumber(p): 1373 'common_scalar : LNUMBER' 1374 if p[1].startswith('0x'): 1375 p[0] = int(p[1], 16) 1376 elif p[1].startswith('0b'): 1377 p[0] = int(p[1], 2) 1378 elif p[1].startswith('0'): 1379 p[0] = int(p[1], 8) 1380 else: 1381 p[0] = int(p[1]) 1382 1383def p_common_scalar_dnumber(p): 1384 'common_scalar : DNUMBER' 1385 p[0] = float(p[1]) 1386 1387def p_common_scalar_string(p): 1388 '''common_scalar : CONSTANT_ENCAPSED_STRING 1389 | STRING CONSTANT_ENCAPSED_STRING''' 1390 if len(p) == 3: 1391 if p[1] == 'b': 1392 p[0] = p[2][1:-1].replace("\\'", "'").replace('\\\\', '\\') 1393 else: 1394 p[0] = p[1][1:-1].replace("\\'", "'").replace('\\\\', '\\') 1395 1396def p_common_scalar_magic_line(p): 1397 'common_scalar : LINE' 1398 p[0] = ast.MagicConstant(p[1].upper(), p.lineno(1), lineno=p.lineno(1)) 1399 1400def p_common_scalar_magic_file(p): 1401 'common_scalar : FILE' 1402 value = getattr(p.lexer, 'filename', None) 1403 p[0] = ast.MagicConstant(p[1].upper(), value, lineno=p.lineno(1)) 1404 1405def p_common_scalar_magic_dir(p): 1406 'common_scalar : DIR' 1407 value = getattr(p.lexer, 'filename', None) 1408 if value is not None: 1409 value = os.path.dirname(value) 1410 p[0] = ast.MagicConstant(p[1].upper(), value, lineno=p.lineno(1)) 1411 1412def p_common_scalar_magic_class(p): 1413 'common_scalar : CLASS_C' 1414 p[0] = ast.MagicConstant(p[1].upper(), None, lineno=p.lineno(1)) 1415 1416def p_common_scalar_magic_method(p): 1417 'common_scalar : METHOD_C' 1418 p[0] = ast.MagicConstant(p[1].upper(), None, lineno=p.lineno(1)) 1419 1420def p_common_scalar_magic_func(p): 1421 'common_scalar : FUNC_C' 1422 p[0] = ast.MagicConstant(p[1].upper(), None, lineno=p.lineno(1)) 1423 1424def p_common_scalar_magic_ns(p): 1425 'common_scalar : NS_C' 1426 p[0] = ast.MagicConstant(p[1].upper(), None, lineno=p.lineno(1)) 1427 1428def p_static_scalar(p): 1429 '''static_scalar : common_scalar 1430 | class_constant 1431 | QUOTE QUOTE 1432 | QUOTE ENCAPSED_AND_WHITESPACE QUOTE 1433 | static_heredoc 1434 | nowdoc 1435 | class_name_constant''' 1436 if len(p) == 2: 1437 p[0] = p[1] 1438 elif len(p) == 3: 1439 p[0] = '' 1440 else: 1441 p[0] = process_php_string_escapes(p[2]) 1442 1443def p_class_name_constant(p): 1444 'class_name_constant : class_name DOUBLE_COLON CLASS' 1445 # no special treatment needed - in practice php doesn't even check if the 1446 # class exists 1447 p[0] = p[1] 1448 1449def p_static_heredoc(p): 1450 'static_heredoc : START_HEREDOC multiple_encapsed END_HEREDOC' 1451 # the last character is a newline because of how the lexer works, but it 1452 # doesn't belong in the result so drop it 1453 p[0] = p[2][:-1] 1454 1455def p_multiple_encapsed(p): 1456 '''multiple_encapsed : multiple_encapsed ENCAPSED_AND_WHITESPACE 1457 | empty''' 1458 if len(p) == 3: 1459 p[0] = p[1] + p[2] 1460 else: 1461 p[0] = '' 1462 1463def p_static_scalar_namespace_name(p): 1464 '''static_scalar : namespace_name 1465 | NS_SEPARATOR namespace_name 1466 | NAMESPACE NS_SEPARATOR namespace_name''' 1467 if len(p) == 2: 1468 p[0] = ast.Constant(p[1], lineno=p.lineno(1)) 1469 elif len(p) == 3: 1470 p[0] = ast.Constant(p[1] + p[2], lineno=p.lineno(1)) 1471 else: 1472 p[0] = ast.Constant(p[1] + p[2] + p[3], lineno=p.lineno(1)) 1473 1474def p_static_scalar_unary_op(p): 1475 '''static_scalar : PLUS static_scalar 1476 | MINUS static_scalar''' 1477 p[0] = ast.UnaryOp(p[1], p[2], lineno=p.lineno(1)) 1478 1479def p_static_scalar_array(p): 1480 '''static_scalar : ARRAY LPAREN static_array_pair_list RPAREN 1481 | LBRACKET static_array_pair_list RBRACKET''' 1482 if len(p) == 5: 1483 contents = p[3] 1484 else: 1485 contents = p[2] 1486 p[0] = ast.Array(contents, lineno=p.lineno(1)) 1487 1488def p_static_array_pair_list(p): 1489 '''static_array_pair_list : empty 1490 | static_non_empty_array_pair_list possible_comma''' 1491 if len(p) == 2: 1492 p[0] = [] 1493 else: 1494 p[0] = p[1] 1495 1496def p_static_non_empty_array_pair_list_item(p): 1497 '''static_non_empty_array_pair_list : static_non_empty_array_pair_list COMMA static_expr 1498 | static_expr''' 1499 if len(p) == 4: 1500 p[0] = p[1] + [ast.ArrayElement(None, p[3], False, lineno=p.lineno(2))] 1501 else: 1502 p[0] = [ast.ArrayElement(None, p[1], False, lineno=p.lineno(1))] 1503 1504def p_static_non_empty_array_pair_list_pair(p): 1505 '''static_non_empty_array_pair_list : static_non_empty_array_pair_list COMMA static_scalar DOUBLE_ARROW static_expr 1506 | static_scalar DOUBLE_ARROW static_expr''' 1507 if len(p) == 6: 1508 p[0] = p[1] + [ast.ArrayElement(p[3], p[5], False, lineno=p.lineno(2))] 1509 else: 1510 p[0] = [ast.ArrayElement(p[1], p[3], False, lineno=p.lineno(2))] 1511 1512def p_static_expr(p): 1513 '''static_expr : static_scalar 1514 | static_expr BOOLEAN_AND static_expr 1515 | static_expr BOOLEAN_OR static_expr 1516 | static_expr LOGICAL_AND static_expr 1517 | static_expr LOGICAL_OR static_expr 1518 | static_expr LOGICAL_XOR static_expr 1519 | static_expr AND static_expr 1520 | static_expr OR static_expr 1521 | static_expr XOR static_expr 1522 | static_expr CONCAT static_expr 1523 | static_expr PLUS static_expr 1524 | static_expr MINUS static_expr 1525 | static_expr MUL static_expr 1526 | static_expr DIV static_expr 1527 | static_expr SL static_expr 1528 | static_expr SR static_expr 1529 | static_expr MOD static_expr 1530 | static_expr IS_IDENTICAL static_expr 1531 | static_expr IS_NOT_IDENTICAL static_expr 1532 | static_expr IS_EQUAL static_expr 1533 | static_expr IS_NOT_EQUAL static_expr 1534 | static_expr IS_SMALLER static_expr 1535 | static_expr IS_SMALLER_OR_EQUAL static_expr 1536 | static_expr IS_GREATER static_expr 1537 | static_expr IS_GREATER_OR_EQUAL static_expr''' 1538 if len(p) == 2: 1539 # just the scalar 1540 p[0] = p[1] 1541 else: 1542 p[0] = ast.BinaryOp(p[2].lower(), p[1], p[3], lineno=p.lineno(2)) 1543 1544def p_static_expr_group(p): 1545 'static_expr : LPAREN static_expr RPAREN' 1546 p[0] = p[2] 1547 1548def p_namespace_name(p): 1549 '''namespace_name : namespace_name NS_SEPARATOR STRING 1550 | STRING 1551 | ARRAY''' 1552 if len(p) == 4: 1553 p[0] = p[1] + p[2] + p[3] 1554 else: 1555 p[0] = p[1] 1556 1557def p_encaps_list(p): 1558 '''encaps_list : encaps_list encaps_var 1559 | empty''' 1560 if len(p) == 3: 1561 if p[1] == '': 1562 p[0] = p[2] 1563 else: 1564 p[0] = ast.BinaryOp('.', p[1], p[2], lineno=p.lineno(2)) 1565 else: 1566 p[0] = '' 1567 1568def p_encaps_list_string(p): 1569 'encaps_list : encaps_list ENCAPSED_AND_WHITESPACE' 1570 p2 = process_php_string_escapes(p[2]) 1571 if p[1] == '': 1572 p[0] = process_php_string_escapes(p[2]) 1573 else: 1574 if isinstance(p[1], string_type): 1575 # if it's only a string so far, just append the contents 1576 p[0] = p[1] + process_php_string_escapes(p[2]) 1577 elif isinstance(p[1], ast.BinaryOp) and isinstance(p[1].right, string_type): 1578 # if the last right leaf is a string, extend previous binop 1579 p[0] = ast.BinaryOp('.', p[1].left, p[1].right + process_php_string_escapes(p[2]), 1580 lineno=p[1].lineno) 1581 else: 1582 # worst case - insert a binaryop 1583 p[0] = ast.BinaryOp('.', p[1], process_php_string_escapes(p[2]), 1584 lineno=p.lineno(2)) 1585 1586def p_encaps_var(p): 1587 'encaps_var : VARIABLE' 1588 p[0] = ast.Variable(p[1], lineno=p.lineno(1)) 1589 1590def p_encaps_var_array_offset(p): 1591 'encaps_var : VARIABLE LBRACKET encaps_var_offset RBRACKET' 1592 p[0] = ast.ArrayOffset(ast.Variable(p[1], lineno=p.lineno(1)), p[3], 1593 lineno=p.lineno(2)) 1594 1595def p_encaps_var_object_property(p): 1596 'encaps_var : VARIABLE OBJECT_OPERATOR STRING' 1597 p[0] = ast.ObjectProperty(ast.Variable(p[1], lineno=p.lineno(1)), p[3], 1598 lineno=p.lineno(2)) 1599 1600def p_encaps_var_dollar_curly_expr(p): 1601 'encaps_var : DOLLAR_OPEN_CURLY_BRACES expr RBRACE' 1602 p[0] = p[2] 1603 1604def p_encaps_var_dollar_curly_array_offset(p): 1605 'encaps_var : DOLLAR_OPEN_CURLY_BRACES STRING_VARNAME LBRACKET expr RBRACKET RBRACE' 1606 p[0] = ast.ArrayOffset(ast.Variable('$' + p[2], lineno=p.lineno(2)), p[4], 1607 lineno=p.lineno(3)) 1608 1609def p_encaps_var_curly_variable(p): 1610 'encaps_var : CURLY_OPEN variable RBRACE' 1611 p[0] = p[2] 1612 1613def p_encaps_var_offset_string(p): 1614 'encaps_var_offset : STRING' 1615 p[0] = p[1] 1616 1617def p_encaps_var_offset_num_string(p): 1618 'encaps_var_offset : NUM_STRING' 1619 p[0] = int(p[1]) 1620 1621def p_encaps_var_offset_variable(p): 1622 'encaps_var_offset : VARIABLE' 1623 p[0] = ast.Variable(p[1], lineno=p.lineno(1)) 1624 1625def p_empty(p): 1626 'empty : ' 1627 pass 1628 1629# Error rule for syntax errors 1630def p_error(t): 1631 if t: 1632 raise SyntaxError('invalid syntax', (None, t.lineno, None, t.value)) 1633 else: 1634 raise SyntaxError('unexpected EOF while parsing', (None, None, None, None)) 1635 1636# Build the grammar 1637def make_parser(debug=False): 1638 return yacc.yacc(debug=debug) 1639 1640def main(): 1641 import argparse 1642 import os 1643 ap = argparse.ArgumentParser(description="Parser test tool") 1644 ap.add_argument('-g', '--generate', dest='generate', action='store_true') 1645 ap.add_argument('-r', '--recursive', dest='recursive', action='store_true') 1646 ap.add_argument('-q', '--quiet', dest='quiet', action='store_true') 1647 ap.add_argument('-d', '--debug', dest='debug', action='store_true') 1648 ap.add_argument('path', metavar='PATH', nargs='?', type=str) 1649 args = ap.parse_args() 1650 1651 if args.generate: 1652 make_parser(args.debug) 1653 return 1654 1655 parser = make_parser(args.debug) 1656 if args.path is None: 1657 run_parser(parser, sys.stdin, args.quiet, args.debug) 1658 elif os.path.isfile(args.path): 1659 with open(args.path, 'r') as f: 1660 run_parser(parser, f, args.quiet, args.debug) 1661 elif os.path.isdir(args.path): 1662 if not args.recursive: 1663 print('directory path given, use -r for recursive processing') 1664 else: 1665 for root, dirs, files in os.walk(args.path): 1666 for fpath in files: 1667 if not fpath.endswith('.php'): 1668 continue 1669 with open(os.path.join(root, fpath), 'r') as f: 1670 run_parser(parser, f, args.quiet, args.debug) 1671 1672def run_parser(parser, source, quiet, debug): 1673 s = source.read() 1674 lexer = phplex.lexer 1675 lexer.lineno = 1 1676 1677 try: 1678 result = parser.parse(s, lexer=lexer.clone(), debug=debug) 1679 except SyntaxError as e: 1680 if e.lineno is not None: 1681 print(source.name, e, 'near', repr(e.text)) 1682 else: 1683 print(source.name, e) 1684 sys.exit(1) 1685 except: 1686 print("Critical error in:", source.name) 1687 raise 1688 1689 if not quiet: 1690 import pprint 1691 for item in result: 1692 if hasattr(item, 'generic'): 1693 item = item.generic() 1694 pprint.pprint(item) 1695 1696 parser.restart() 1697