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