Skip to content

Instantly share code, notes, and snippets.

@tk0miya
Last active July 6, 2020 14:19
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tk0miya/4379646 to your computer and use it in GitHub Desktop.
Save tk0miya/4379646 to your computer and use it in GitHub Desktop.
sys.path.insert(0, 'lib/')
extensions += ['sphinxcontrib_phpautodoc']
<?php
/* this is bar function! */
function bar() {
}
/*
* Class for foo
*
* * hello world
* * good bye world
*/
class Foo {
// Hello *Sphinx* world!
function buz() {
}
}
?>
.. test documentation master file, created by
sphinx-quickstart on Wed Dec 26 19:08:50 2012.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to test's documentation!
================================
Contents:
.. phpautodoc:: hello.php
.. toctree::
:maxdepth: 2
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
# ----------------------------------------------------------------------
# phplex.py
#
# A lexer for PHP.
# ----------------------------------------------------------------------
import ply.lex as lex
import re
# todo: nowdocs
# todo: backticks
# todo: binary string literals and casts
# todo: BAD_CHARACTER
# todo: <script> syntax (does anyone use this?)
states = (
('php', 'exclusive'),
('quoted', 'exclusive'),
('quotedvar', 'exclusive'),
('varname', 'exclusive'),
('offset', 'exclusive'),
('property', 'exclusive'),
('heredoc', 'exclusive'),
('heredocvar', 'exclusive'),
)
# Reserved words
reserved = (
'ARRAY', 'AS', 'BREAK', 'CASE', 'CLASS', 'CONST', 'CONTINUE', 'DECLARE',
'DEFAULT', 'DO', 'ECHO', 'ELSE', 'ELSEIF', 'EMPTY', 'ENDDECLARE',
'ENDFOR', 'ENDFOREACH', 'ENDIF', 'ENDSWITCH', 'ENDWHILE', 'EVAL', 'EXIT',
'EXTENDS', 'FOR', 'FOREACH', 'FUNCTION', 'GLOBAL', 'IF', 'INCLUDE',
'INCLUDE_ONCE', 'INSTANCEOF', 'ISSET', 'LIST', 'NEW', 'PRINT', 'REQUIRE',
'REQUIRE_ONCE', 'RETURN', 'STATIC', 'SWITCH', 'UNSET', 'USE', 'VAR',
'WHILE', 'FINAL', 'INTERFACE', 'IMPLEMENTS', 'PUBLIC', 'PRIVATE',
'PROTECTED', 'ABSTRACT', 'CLONE', 'TRY', 'CATCH', 'THROW', 'NAMESPACE',
)
# Not used by parser
unparsed = (
# Invisible characters
'WHITESPACE',
# Open and close tags
'OPEN_TAG', 'OPEN_TAG_WITH_ECHO', 'CLOSE_TAG',
)
tokens = reserved + unparsed + (
# Operators
'PLUS', 'MINUS', 'MUL', 'DIV', 'MOD', 'AND', 'OR', 'NOT', 'XOR', 'SL',
'SR', 'BOOLEAN_AND', 'BOOLEAN_OR', 'BOOLEAN_NOT', 'IS_SMALLER',
'IS_GREATER', 'IS_SMALLER_OR_EQUAL', 'IS_GREATER_OR_EQUAL', 'IS_EQUAL',
'IS_NOT_EQUAL', 'IS_IDENTICAL', 'IS_NOT_IDENTICAL',
# Assignment operators
'EQUALS', 'MUL_EQUAL', 'DIV_EQUAL', 'MOD_EQUAL', 'PLUS_EQUAL',
'MINUS_EQUAL', 'SL_EQUAL', 'SR_EQUAL', 'AND_EQUAL', 'OR_EQUAL',
'XOR_EQUAL', 'CONCAT_EQUAL',
# Increment/decrement
'INC', 'DEC',
# Arrows
'OBJECT_OPERATOR', 'DOUBLE_ARROW', 'DOUBLE_COLON',
# Delimiters
'LPAREN', 'RPAREN', 'LBRACKET', 'RBRACKET', 'LBRACE', 'RBRACE', 'DOLLAR',
'COMMA', 'CONCAT', 'QUESTION', 'COLON', 'SEMI', 'AT', 'NS_SEPARATOR',
# Casts
'ARRAY_CAST', 'BOOL_CAST', 'DOUBLE_CAST', 'INT_CAST', 'OBJECT_CAST',
'STRING_CAST', 'UNSET_CAST',
# Escaping from HTML
'INLINE_HTML',
# Identifiers and reserved words
'DIR', 'FILE', 'LINE', 'FUNC_C', 'CLASS_C', 'METHOD_C', 'NS_C',
'LOGICAL_AND', 'LOGICAL_OR', 'LOGICAL_XOR',
'HALT_COMPILER',
'STRING', 'VARIABLE',
'LNUMBER', 'DNUMBER', 'NUM_STRING',
'CONSTANT_ENCAPSED_STRING', 'ENCAPSED_AND_WHITESPACE', 'QUOTE',
'DOLLAR_OPEN_CURLY_BRACES', 'STRING_VARNAME', 'CURLY_OPEN',
# Heredocs
'START_HEREDOC', 'END_HEREDOC',
# Comments
'COMMENT', 'DOC_COMMENT',
)
# Newlines
def t_php_WHITESPACE(t):
r'[ \t\r\n]+'
t.lexer.lineno += t.value.count("\n")
return t
# Operators
t_php_PLUS = r'\+'
t_php_MINUS = r'-'
t_php_MUL = r'\*'
t_php_DIV = r'/'
t_php_MOD = r'%'
t_php_AND = r'&'
t_php_OR = r'\|'
t_php_NOT = r'~'
t_php_XOR = r'\^'
t_php_SL = r'<<'
t_php_SR = r'>>'
t_php_BOOLEAN_AND = r'&&'
t_php_BOOLEAN_OR = r'\|\|'
t_php_BOOLEAN_NOT = r'!'
t_php_IS_SMALLER = r'<'
t_php_IS_GREATER = r'>'
t_php_IS_SMALLER_OR_EQUAL = r'<='
t_php_IS_GREATER_OR_EQUAL = r'>='
t_php_IS_EQUAL = r'=='
t_php_IS_NOT_EQUAL = r'(!=(?!=))|(<>)'
t_php_IS_IDENTICAL = r'==='
t_php_IS_NOT_IDENTICAL = r'!=='
# Assignment operators
t_php_EQUALS = r'='
t_php_MUL_EQUAL = r'\*='
t_php_DIV_EQUAL = r'/='
t_php_MOD_EQUAL = r'%='
t_php_PLUS_EQUAL = r'\+='
t_php_MINUS_EQUAL = r'-='
t_php_SL_EQUAL = r'<<='
t_php_SR_EQUAL = r'>>='
t_php_AND_EQUAL = r'&='
t_php_OR_EQUAL = r'\|='
t_php_XOR_EQUAL = r'\^='
t_php_CONCAT_EQUAL = r'\.='
# Increment/decrement
t_php_INC = r'\+\+'
t_php_DEC = r'--'
# Arrows
t_php_DOUBLE_ARROW = r'=>'
t_php_DOUBLE_COLON = r'::'
def t_php_OBJECT_OPERATOR(t):
r'->'
if re.match(r'[A-Za-z_]', peek(t.lexer)):
t.lexer.push_state('property')
return t
# Delimeters
t_php_LPAREN = r'\('
t_php_RPAREN = r'\)'
t_php_DOLLAR = r'\$'
t_php_COMMA = r','
t_php_CONCAT = r'\.(?!\d|=)'
t_php_QUESTION = r'\?'
t_php_COLON = r':'
t_php_SEMI = r';'
t_php_AT = r'@'
t_php_NS_SEPARATOR = r'\\'
def t_php_LBRACKET(t):
r'\['
t.lexer.push_state('php')
return t
def t_php_RBRACKET(t):
r'\]'
t.lexer.pop_state()
return t
def t_php_LBRACE(t):
r'\{'
t.lexer.push_state('php')
return t
def t_php_RBRACE(t):
r'\}'
t.lexer.pop_state()
return t
# Casts
t_php_ARRAY_CAST = r'\([ \t]*[Aa][Rr][Rr][Aa][Yy][ \t]*\)'
t_php_BOOL_CAST = r'\([ \t]*[Bb][Oo][Oo][Ll]([Ee][Aa][Nn])?[ \t]*\)'
t_php_DOUBLE_CAST = r'\([ \t]*([Rr][Ee][Aa][Ll]|[Dd][Oo][Uu][Bb][Ll][Ee]|[Ff][Ll][Oo][Aa][Tt])[ \t]*\)'
t_php_INT_CAST = r'\([ \t]*[Ii][Nn][Tt]([Ee][Gg][Ee][Rr])?[ \t]*\)'
t_php_OBJECT_CAST = r'\([ \t]*[Oo][Bb][Jj][Ee][Cc][Tt][ \t]*\)'
t_php_STRING_CAST = r'\([ \t]*[Ss][Tt][Rr][Ii][Nn][Gg][ \t]*\)'
t_php_UNSET_CAST = r'\([ \t]*[Uu][Nn][Ss][Ee][Tt][ \t]*\)'
# Comments
def t_php_DOC_COMMENT(t):
r'/\*\*(.|\n)*?\*/'
t.lexer.lineno += t.value.count("\n")
return t
def t_php_COMMENT(t):
r'/\*(.|\n)*?\*/ | //([^?%\n]|[?%](?!>))*\n? | \#([^?%\n]|[?%](?!>))*\n?'
t.lexer.lineno += t.value.count("\n")
return t
# Escaping from HTML
def t_OPEN_TAG(t):
r'<[?%]((php[ \t\r\n]?)|=)?'
if '=' in t.value: t.type = 'OPEN_TAG_WITH_ECHO'
t.lexer.lineno += t.value.count("\n")
t.lexer.begin('php')
return t
def t_php_CLOSE_TAG(t):
r'[?%]>\r?\n?'
t.lexer.lineno += t.value.count("\n")
t.lexer.begin('INITIAL')
return t
def t_INLINE_HTML(t):
r'([^<]|<(?![?%]))+'
t.lexer.lineno += t.value.count("\n")
return t
# Identifiers and reserved words
reserved_map = {
'__DIR__': 'DIR',
'__FILE__': 'FILE',
'__LINE__': 'LINE',
'__FUNCTION__': 'FUNC_C',
'__CLASS__': 'CLASS_C',
'__METHOD__': 'METHOD_C',
'__NAMESPACE__': 'NS_C',
'AND': 'LOGICAL_AND',
'OR': 'LOGICAL_OR',
'XOR': 'LOGICAL_XOR',
'DIE': 'EXIT',
'__HALT_COMPILER': 'HALT_COMPILER',
}
for r in reserved:
reserved_map[r] = r
# Identifier
def t_php_STRING(t):
r'[A-Za-z_][\w_]*'
t.type = reserved_map.get(t.value.upper(), 'STRING')
return t
# Variable
def t_php_VARIABLE(t):
r'\$[A-Za-z_][\w_]*'
return t
# Floating literal
def t_php_DNUMBER(t):
r'(\d*\.\d+|\d+\.\d*)([Ee][+-]?\d+)? | (\d+[Ee][+-]?\d+)'
return t
# Integer literal
def t_php_LNUMBER(t):
r'(0x[0-9A-Fa-f]+)|\d+'
return t
# String literal
def t_php_CONSTANT_ENCAPSED_STRING(t):
r"'([^\\']|\\(.|\n))*'"
t.lexer.lineno += t.value.count("\n")
return t
def t_php_QUOTE(t):
r'"'
t.lexer.push_state('quoted')
return t
def t_quoted_QUOTE(t):
r'"'
t.lexer.pop_state()
return t
def t_quoted_ENCAPSED_AND_WHITESPACE(t):
r'( [^"\\${] | \\(.|\n) | \$(?![A-Za-z_{]) | \{(?!\$) )+'
t.lexer.lineno += t.value.count("\n")
return t
def t_quoted_VARIABLE(t):
r'\$[A-Za-z_][\w_]*'
t.lexer.push_state('quotedvar')
return t
def t_quoted_CURLY_OPEN(t):
r'\{(?=\$)'
t.lexer.push_state('php')
return t
def t_quoted_DOLLAR_OPEN_CURLY_BRACES(t):
r'\$\{'
if re.match(r'[A-Za-z_]', peek(t.lexer)):
t.lexer.push_state('varname')
else:
t.lexer.push_state('php')
return t
def t_quotedvar_QUOTE(t):
r'"'
t.lexer.pop_state()
t.lexer.pop_state()
return t
def t_quotedvar_LBRACKET(t):
r'\['
t.lexer.begin('offset')
return t
def t_quotedvar_OBJECT_OPERATOR(t):
r'->(?=[A-Za-z])'
t.lexer.begin('property')
return t
def t_quotedvar_ENCAPSED_AND_WHITESPACE(t):
r'( [^"\\${] | \\(.|\n) | \$(?![A-Za-z_{]) | \{(?!\$) )+'
t.lexer.lineno += t.value.count("\n")
t.lexer.pop_state()
return t
t_quotedvar_VARIABLE = t_php_VARIABLE
def t_quotedvar_CURLY_OPEN(t):
r'\{(?=\$)'
t.lexer.begin('php')
return t
def t_quotedvar_DOLLAR_OPEN_CURLY_BRACES(t):
r'\$\{'
if re.match(r'[A-Za-z_]', peek(t.lexer)):
t.lexer.begin('varname')
else:
t.lexer.begin('php')
return t
def t_varname_STRING_VARNAME(t):
r'[A-Za-z_][\w_]*'
return t
t_varname_RBRACE = t_php_RBRACE
t_varname_LBRACKET = t_php_LBRACKET
def t_offset_STRING(t):
r'[A-Za-z_][\w_]*'
return t
def t_offset_NUM_STRING(t):
r'\d+'
return t
t_offset_VARIABLE = t_php_VARIABLE
t_offset_RBRACKET = t_php_RBRACKET
def t_property_STRING(t):
r'[A-Za-z_][\w_]*'
t.lexer.pop_state()
return t
# Heredocs
def t_php_START_HEREDOC(t):
r'<<<[ \t]*(?P<label>[A-Za-z_][\w_]*)\n'
t.lexer.lineno += t.value.count("\n")
t.lexer.push_state('heredoc')
t.lexer.heredoc_label = t.lexer.lexmatch.group('label')
return t
def t_heredoc_END_HEREDOC(t):
r'(?<=\n)[A-Za-z_][\w_]*'
if t.value == t.lexer.heredoc_label:
del t.lexer.heredoc_label
t.lexer.pop_state()
else:
t.type = 'ENCAPSED_AND_WHITESPACE'
return t
def t_heredoc_ENCAPSED_AND_WHITESPACE(t):
r'( [^\n\\${] | \\. | \$(?![A-Za-z_{]) | \{(?!\$) )+\n? | \\?\n'
t.lexer.lineno += t.value.count("\n")
return t
def t_heredoc_VARIABLE(t):
r'\$[A-Za-z_][\w_]*'
t.lexer.push_state('heredocvar')
return t
t_heredoc_CURLY_OPEN = t_quoted_CURLY_OPEN
t_heredoc_DOLLAR_OPEN_CURLY_BRACES = t_quoted_DOLLAR_OPEN_CURLY_BRACES
def t_heredocvar_ENCAPSED_AND_WHITESPACE(t):
r'( [^\n\\${] | \\. | \$(?![A-Za-z_{]) | \{(?!\$) )+\n? | \\?\n'
t.lexer.lineno += t.value.count("\n")
t.lexer.pop_state()
return t
t_heredocvar_LBRACKET = t_quotedvar_LBRACKET
t_heredocvar_OBJECT_OPERATOR = t_quotedvar_OBJECT_OPERATOR
t_heredocvar_VARIABLE = t_quotedvar_VARIABLE
t_heredocvar_CURLY_OPEN = t_quotedvar_CURLY_OPEN
t_heredocvar_DOLLAR_OPEN_CURLY_BRACES = t_quotedvar_DOLLAR_OPEN_CURLY_BRACES
def t_ANY_error(t):
raise SyntaxError('illegal character', (None, t.lineno, None, t.value))
def peek(lexer):
try:
return lexer.lexdata[lexer.lexpos]
except IndexError:
return ''
class FilteredLexer(object):
def __init__(self, lexer):
self.lexer = lexer
self.last_token = None
@property
def lineno(self):
return self.lexer.lineno
@lineno.setter
def lineno(self, value):
self.lexer.lineno = value
@property
def lexpos(self):
return self.lexer.lexpos
@lexpos.setter
def lexpos(self, value):
self.lexer.lexpos = value
def clone(self):
return FilteredLexer(self.lexer.clone())
def current_state(self):
return self.lexer.current_state()
def input(self, input):
self.lexer.input(input)
def token(self):
t = self.lexer.token()
# Filter out tokens that the parser is not expecting.
while t and t.type in unparsed:
# Skip over open tags, but keep track of when we see them.
if t.type == 'OPEN_TAG':
self.last_token = t
t = self.lexer.token()
continue
# Rewrite <?= to yield an "echo" statement.
if t.type == 'OPEN_TAG_WITH_ECHO':
t.type = 'ECHO'
break
# Insert semicolons in place of close tags where necessary.
if t.type == 'CLOSE_TAG':
if self.last_token and \
self.last_token.type in ('OPEN_TAG', 'SEMI', 'COLON',
'LBRACE', 'RBRACE'):
# Dont insert semicolons after these tokens.
pass
else:
# Rewrite close tag as a semicolon.
t.type = 'SEMI'
break
t = self.lexer.token()
self.last_token = t
return t
# Iterator interface
def __iter__(self):
return self
def next(self):
t = self.token()
if t is None:
raise StopIteration
return t
__next__ = next
full_lexer = lex.lex()
lexer = FilteredLexer(full_lexer)
full_tokens = tokens
tokens = filter(lambda token: token not in unparsed, tokens)
if __name__ == "__main__":
lex.runmain(full_lexer)
# -----------------------------------------------------------------------------
# phpparse.py
#
# A parser for PHP.
# -----------------------------------------------------------------------------
import os
import sys
import phplex
from phply import phpast as ast
import ply.yacc as yacc
# Get the token map
tokens = phplex.tokens
ast.Comment = ast.node('Comment', ['text'])
precedence = (
('left', 'INCLUDE', 'INCLUDE_ONCE', 'EVAL', 'REQUIRE', 'REQUIRE_ONCE'),
('left', 'COMMA'),
('left', 'LOGICAL_OR'),
('left', 'LOGICAL_XOR'),
('left', 'LOGICAL_AND'),
('right', 'PRINT'),
('left', 'EQUALS', 'PLUS_EQUAL', 'MINUS_EQUAL', 'MUL_EQUAL', 'DIV_EQUAL', 'CONCAT_EQUAL', 'MOD_EQUAL', 'AND_EQUAL', 'OR_EQUAL', 'XOR_EQUAL', 'SL_EQUAL', 'SR_EQUAL'),
('left', 'QUESTION', 'COLON'),
('left', 'BOOLEAN_OR'),
('left', 'BOOLEAN_AND'),
('left', 'OR'),
('left', 'XOR'),
('left', 'AND'),
('nonassoc', 'IS_EQUAL', 'IS_NOT_EQUAL', 'IS_IDENTICAL', 'IS_NOT_IDENTICAL'),
('nonassoc', 'IS_SMALLER', 'IS_SMALLER_OR_EQUAL', 'IS_GREATER', 'IS_GREATER_OR_EQUAL'),
('left', 'SL', 'SR'),
('left', 'PLUS', 'MINUS', 'CONCAT'),
('left', 'MUL', 'DIV', 'MOD'),
('right', 'BOOLEAN_NOT'),
('nonassoc', 'INSTANCEOF'),
('right', 'NOT', 'INC', 'DEC', 'INT_CAST', 'DOUBLE_CAST', 'STRING_CAST', 'ARRAY_CAST', 'OBJECT_CAST', 'BOOL_CAST', 'UNSET_CAST', 'AT'),
('right', 'LBRACKET'),
('nonassoc', 'NEW', 'CLONE'),
# ('left', 'ELSEIF'),
# ('left', 'ELSE'),
('left', 'ENDIF'),
('right', 'STATIC', 'ABSTRACT', 'FINAL', 'PRIVATE', 'PROTECTED', 'PUBLIC'),
)
def p_start(p):
'start : top_statement_list'
p[0] = p[1]
def p_top_statement_list(p):
'''top_statement_list : top_statement_list top_statement
| empty'''
if len(p) == 3:
p[0] = p[1] + [p[2]]
else:
p[0] = []
def p_top_statement(p):
'''top_statement : statement
| comment
| function_declaration_statement
| class_declaration_statement
| HALT_COMPILER LPAREN RPAREN SEMI'''
if len(p) == 2:
p[0] = p[1]
else:
# ???
pass
def p_top_statement_namespace(p):
'''top_statement : NAMESPACE namespace_name SEMI
| NAMESPACE LBRACE top_statement_list RBRACE
| NAMESPACE namespace_name LBRACE top_statement_list RBRACE'''
if len(p) == 4:
p[0] = ast.Namespace(p[2], [], lineno=p.lineno(1))
elif len(p) == 5:
p[0] = ast.Namespace(None, p[3], lineno=p.lineno(1))
else:
p[0] = ast.Namespace(p[2], p[4], lineno=p.lineno(1))
def p_top_statement_constant(p):
'top_statement : CONST constant_declarations SEMI'
p[0] = ast.ConstantDeclarations(p[2], lineno=p.lineno(1))
def p_top_statement_use(p):
'top_statement : USE use_declarations SEMI'
p[0] = ast.UseDeclarations(p[2], lineno=p.lineno(1))
def p_use_declarations(p):
'''use_declarations : use_declarations COMMA use_declaration
| use_declaration'''
if len(p) == 4:
p[0] = p[1] + [p[3]]
else:
p[0] = [p[1]]
def p_use_declaration(p):
'''use_declaration : namespace_name
| NS_SEPARATOR namespace_name
| namespace_name AS STRING
| NS_SEPARATOR namespace_name AS STRING'''
if len(p) == 2:
p[0] = ast.UseDeclaration(p[1], None, lineno=p.lineno(1))
elif len(p) == 3:
p[0] = ast.UseDeclaration(p[1] + p[2], None, lineno=p.lineno(1))
elif len(p) == 4:
p[0] = ast.UseDeclaration(p[1], p[3], lineno=p.lineno(2))
else:
p[0] = ast.UseDeclaration(p[1] + p[2], p[4], lineno=p.lineno(1))
def p_constant_declarations(p):
'''constant_declarations : constant_declarations COMMA constant_declaration
| constant_declaration'''
if len(p) == 4:
p[0] = p[1] + [p[3]]
else:
p[0] = [p[1]]
def p_constant_declaration(p):
'constant_declaration : STRING EQUALS static_scalar'
p[0] = ast.ConstantDeclaration(p[1], p[3], lineno=p.lineno(1))
def p_inner_statement_list(p):
'''inner_statement_list : inner_statement_list inner_statement
| empty'''
if len(p) == 3:
p[0] = p[1] + [p[2]]
else:
p[0] = []
def p_inner_statement(p):
'''inner_statement : statement
| function_declaration_statement
| class_declaration_statement
| HALT_COMPILER LPAREN RPAREN SEMI'''
assert len(p) == 2, "__HALT_COMPILER() can only be used from the outermost scope"
p[0] = p[1]
def p_statement_block(p):
'statement : LBRACE inner_statement_list RBRACE'
p[0] = ast.Block(p[2], lineno=p.lineno(1))
def p_statement_if(p):
'''statement : IF LPAREN expr RPAREN statement elseif_list else_single
| IF LPAREN expr RPAREN COLON inner_statement_list new_elseif_list new_else_single ENDIF SEMI'''
if len(p) == 8:
p[0] = ast.If(p[3], p[5], p[6], p[7], lineno=p.lineno(1))
else:
p[0] = ast.If(p[3], ast.Block(p[6], lineno=p.lineno(5)),
p[7], p[8], lineno=p.lineno(1))
def p_statement_while(p):
'statement : WHILE LPAREN expr RPAREN while_statement'
p[0] = ast.While(p[3], p[5], lineno=p.lineno(1))
def p_statement_do_while(p):
'statement : DO statement WHILE LPAREN expr RPAREN SEMI'
p[0] = ast.DoWhile(p[2], p[5], lineno=p.lineno(1))
def p_statement_for(p):
'statement : FOR LPAREN for_expr SEMI for_expr SEMI for_expr RPAREN for_statement'
p[0] = ast.For(p[3], p[5], p[7], p[9], lineno=p.lineno(1))
def p_statement_foreach(p):
'statement : FOREACH LPAREN expr AS foreach_variable foreach_optional_arg RPAREN foreach_statement'
if p[6] is None:
p[0] = ast.Foreach(p[3], None, p[5], p[8], lineno=p.lineno(1))
else:
p[0] = ast.Foreach(p[3], p[5], p[6], p[8], lineno=p.lineno(1))
def p_statement_switch(p):
'statement : SWITCH LPAREN expr RPAREN switch_case_list'
p[0] = ast.Switch(p[3], p[5], lineno=p.lineno(1))
def p_statement_break(p):
'''statement : BREAK SEMI
| BREAK expr SEMI'''
if len(p) == 3:
p[0] = ast.Break(None, lineno=p.lineno(1))
else:
p[0] = ast.Break(p[2], lineno=p.lineno(1))
def p_statement_continue(p):
'''statement : CONTINUE SEMI
| CONTINUE expr SEMI'''
if len(p) == 3:
p[0] = ast.Continue(None, lineno=p.lineno(1))
else:
p[0] = ast.Continue(p[2], lineno=p.lineno(1))
def p_statement_return(p):
'''statement : RETURN SEMI
| RETURN expr SEMI'''
if len(p) == 3:
p[0] = ast.Return(None, lineno=p.lineno(1))
else:
p[0] = ast.Return(p[2], lineno=p.lineno(1))
def p_statement_global(p):
'statement : GLOBAL global_var_list SEMI'
p[0] = ast.Global(p[2], lineno=p.lineno(1))
def p_statement_static(p):
'statement : STATIC static_var_list SEMI'
p[0] = ast.Static(p[2], lineno=p.lineno(1))
def p_statement_echo(p):
'statement : ECHO echo_expr_list SEMI'
p[0] = ast.Echo(p[2], lineno=p.lineno(1))
def p_statement_inline_html(p):
'statement : INLINE_HTML'
p[0] = ast.InlineHTML(p[1], lineno=p.lineno(1))
def p_statement_expr(p):
'statement : expr SEMI'
p[0] = p[1]
def p_statement_unset(p):
'statement : UNSET LPAREN unset_variables RPAREN SEMI'
p[0] = ast.Unset(p[3], lineno=p.lineno(1))
def p_statement_empty(p):
'statement : SEMI'
pass
def p_statement_try(p):
'statement : TRY LBRACE inner_statement_list RBRACE CATCH LPAREN fully_qualified_class_name VARIABLE RPAREN LBRACE inner_statement_list RBRACE additional_catches'
p[0] = ast.Try(p[3], [ast.Catch(p[7], ast.Variable(p[8], lineno=p.lineno(8)),
p[11], lineno=p.lineno(5))] + p[13],
lineno=p.lineno(1))
def p_additional_catches(p):
'''additional_catches : additional_catches CATCH LPAREN fully_qualified_class_name VARIABLE RPAREN LBRACE inner_statement_list RBRACE
| empty'''
if len(p) == 10:
p[0] = p[1] + [ast.Catch(p[4], ast.Variable(p[5], lineno=p.lineno(5)),
p[8], lineno=p.lineno(2))]
else:
p[0] = []
def p_statement_throw(p):
'statement : THROW expr SEMI'
p[0] = ast.Throw(p[2], lineno=p.lineno(1))
def p_statement_declare(p):
'statement : DECLARE LPAREN declare_list RPAREN declare_statement'
p[0] = ast.Declare(p[3], p[5], lineno=p.lineno(1))
def p_declare_list(p):
'''declare_list : STRING EQUALS static_scalar
| declare_list COMMA STRING EQUALS static_scalar'''
if len(p) == 4:
p[0] = [ast.Directive(p[1], p[3], lineno=p.lineno(1))]
else:
p[0] = p[1] + [ast.Directive(p[3], p[5], lineno=p.lineno(2))]
def p_declare_statement(p):
'''declare_statement : statement
| COLON inner_statement_list ENDDECLARE SEMI'''
if len(p) == 2:
p[0] = p[1]
else:
p[0] = ast.Block(p[2], lineno=p.lineno(1))
def p_elseif_list(p):
'''elseif_list : empty
| elseif_list ELSEIF LPAREN expr RPAREN statement'''
if len(p) == 2:
p[0] = []
else:
p[0] = p[1] + [ast.ElseIf(p[4], p[6], lineno=p.lineno(2))]
def p_else_single(p):
'''else_single : empty
| ELSE statement'''
if len(p) == 3:
p[0] = ast.Else(p[2], lineno=p.lineno(1))
def p_new_elseif_list(p):
'''new_elseif_list : empty
| new_elseif_list ELSEIF LPAREN expr RPAREN COLON inner_statement_list'''
if len(p) == 2:
p[0] = []
else:
p[0] = p[1] + [ast.ElseIf(p[4], ast.Block(p[7], lineo=p.lineno(6)),
lineno=p.lineno(2))]
def p_new_else_single(p):
'''new_else_single : empty
| ELSE COLON inner_statement_list'''
if len(p) == 4:
p[0] = ast.Else(ast.Block(p[3], lineno=p.lineno(2)),
lineno=p.lineno(1))
def p_while_statement(p):
'''while_statement : statement
| COLON inner_statement_list ENDWHILE SEMI'''
if len(p) == 2:
p[0] = p[1]
else:
p[0] = ast.Block(p[2], lineno=p.lineno(1))
def p_for_expr(p):
'''for_expr : empty
| non_empty_for_expr'''
p[0] = p[1]
def p_non_empty_for_expr(p):
'''non_empty_for_expr : non_empty_for_expr COMMA expr
| expr'''
if len(p) == 4:
p[0] = p[1] + [p[3]]
else:
p[0] = [p[1]]
def p_for_statement(p):
'''for_statement : statement
| COLON inner_statement_list ENDFOR SEMI'''
if len(p) == 2:
p[0] = p[1]
else:
p[0] = ast.Block(p[2], lineno=p.lineno(1))
def p_foreach_variable(p):
'''foreach_variable : VARIABLE
| AND VARIABLE'''
if len(p) == 2:
p[0] = ast.ForeachVariable(p[1], False, lineno=p.lineno(1))
else:
p[0] = ast.ForeachVariable(p[2], True, lineno=p.lineno(1))
def p_foreach_optional_arg(p):
'''foreach_optional_arg : empty
| DOUBLE_ARROW foreach_variable'''
if len(p) == 3:
p[0] = p[2]
def p_foreach_statement(p):
'''foreach_statement : statement
| COLON inner_statement_list ENDFOREACH SEMI'''
if len(p) == 2:
p[0] = p[1]
else:
p[0] = ast.Block(p[2], lineno=p.lineno(1))
def p_switch_case_list(p):
'''switch_case_list : LBRACE case_list RBRACE
| LBRACE SEMI case_list RBRACE'''
if len(p) == 4:
p[0] = p[2]
else:
p[0] = p[3]
def p_switch_case_list_colon(p):
'''switch_case_list : COLON case_list ENDSWITCH SEMI
| COLON SEMI case_list ENDSWITCH SEMI'''
if len(p) == 5:
p[0] = p[2]
else:
p[0] = p[3]
def p_case_list(p):
'''case_list : empty
| case_list CASE expr case_separator inner_statement_list
| case_list DEFAULT case_separator inner_statement_list'''
if len(p) == 6:
p[0] = p[1] + [ast.Case(p[3], p[5], lineno=p.lineno(2))]
elif len(p) == 5:
p[0] = p[1] + [ast.Default(p[4], lineno=p.lineno(2))]
else:
p[0] = []
def p_case_separator(p):
'''case_separator : COLON
| SEMI'''
pass
def p_global_var_list(p):
'''global_var_list : global_var_list COMMA global_var
| global_var'''
if len(p) == 4:
p[0] = p[1] + [p[3]]
else:
p[0] = [p[1]]
def p_global_var(p):
'''global_var : VARIABLE
| DOLLAR variable
| DOLLAR LBRACE expr RBRACE'''
if len(p) == 2:
p[0] = ast.Variable(p[1], lineno=p.lineno(1))
elif len(p) == 3:
p[0] = ast.Variable(p[2], lineno=p.lineno(1))
else:
p[0] = ast.Variable(p[3], lineno=p.lineno(1))
def p_static_var_list(p):
'''static_var_list : static_var_list COMMA static_var
| static_var'''
if len(p) == 4:
p[0] = p[1] + [p[3]]
else:
p[0] = [p[1]]
def p_static_var(p):
'''static_var : VARIABLE EQUALS static_scalar
| VARIABLE'''
if len(p) == 4:
p[0] = ast.StaticVariable(p[1], p[3], lineno=p.lineno(1))
else:
p[0] = ast.StaticVariable(p[1], None, lineno=p.lineno(1))
def p_echo_expr_list(p):
'''echo_expr_list : echo_expr_list COMMA expr
| expr'''
if len(p) == 4:
p[0] = p[1] + [p[3]]
else:
p[0] = [p[1]]
def p_unset_variables(p):
'''unset_variables : unset_variables COMMA unset_variable
| unset_variable'''
if len(p) == 4:
p[0] = p[1] + [p[3]]
else:
p[0] = [p[1]]
def p_unset_variable(p):
'unset_variable : variable'
p[0] = p[1]
def p_function_declaration_statement(p):
'function_declaration_statement : FUNCTION is_reference STRING LPAREN parameter_list RPAREN LBRACE inner_statement_list RBRACE'
p[0] = ast.Function(p[3], p[5], p[8], p[2], lineno=p.lineno(1))
def p_class_declaration_statement(p):
'''class_declaration_statement : class_entry_type STRING extends_from implements_list LBRACE class_statement_list RBRACE
| INTERFACE STRING interface_extends_list LBRACE class_statement_list RBRACE'''
if len(p) == 8:
p[0] = ast.Class(p[2], p[1], p[3], p[4], p[6], lineno=p.lineno(2))
else:
p[0] = ast.Interface(p[2], p[3], p[5], lineno=p.lineno(1))
def p_class_entry_type(p):
'''class_entry_type : CLASS
| ABSTRACT CLASS
| FINAL CLASS'''
if len(p) == 3:
p[0] = p[1].lower()
def p_extends_from(p):
'''extends_from : empty
| EXTENDS fully_qualified_class_name'''
if len(p) == 3:
p[0] = p[2]
def p_fully_qualified_class_name(p):
'''fully_qualified_class_name : namespace_name
| NS_SEPARATOR namespace_name
| NAMESPACE NS_SEPARATOR namespace_name'''
if len(p) == 2:
p[0] = p[1]
elif len(p) == 3:
p[0] = p[1] + p[2]
else:
p[0] = p[1] + p[2] + p[3]
def p_implements_list(p):
'''implements_list : IMPLEMENTS interface_list
| empty'''
if len(p) == 3:
p[0] = p[2]
else:
p[0] = []
def p_class_statement_list(p):
'''class_statement_list : class_statement_list class_statement
| empty'''
if len(p) == 3:
p[0] = p[1] + [p[2]]
else:
p[0] = []
def p_class_statement(p):
'''class_statement : method_modifiers FUNCTION is_reference STRING LPAREN parameter_list RPAREN method_body
| comment
| variable_modifiers class_variable_declaration SEMI
| class_constant_declaration SEMI'''
if len(p) == 9:
p[0] = ast.Method(p[4], p[1], p[6], p[8], p[3], lineno=p.lineno(2))
elif len(p) == 4:
p[0] = ast.ClassVariables(p[1], p[2], lineno=p.lineno(3))
elif len(p) == 2:
p[0] = p[1]
else:
p[0] = ast.ClassConstants(p[1], lineno=p.lineno(2))
def p_class_variable_declaration_initial(p):
'''class_variable_declaration : class_variable_declaration COMMA VARIABLE EQUALS static_scalar
| VARIABLE EQUALS static_scalar'''
if len(p) == 6:
p[0] = p[1] + [ast.ClassVariable(p[3], p[5], lineno=p.lineno(2))]
else:
p[0] = [ast.ClassVariable(p[1], p[3], lineno=p.lineno(1))]
def p_class_variable_declaration_no_initial(p):
'''class_variable_declaration : class_variable_declaration COMMA VARIABLE
| VARIABLE'''
if len(p) == 4:
p[0] = p[1] + [ast.ClassVariable(p[3], None, lineno=p.lineno(2))]
else:
p[0] = [ast.ClassVariable(p[1], None, lineno=p.lineno(1))]
def p_class_constant_declaration(p):
'''class_constant_declaration : class_constant_declaration COMMA STRING EQUALS static_scalar
| CONST STRING EQUALS static_scalar'''
if len(p) == 6:
p[0] = p[1] + [ast.ClassConstant(p[3], p[5], lineno=p.lineno(2))]
else:
p[0] = [ast.ClassConstant(p[2], p[4], lineno=p.lineno(1))]
def p_interface_list(p):
'''interface_list : interface_list COMMA fully_qualified_class_name
| fully_qualified_class_name'''
if len(p) == 4:
p[0] = p[1] + [p[3]]
else:
p[0] = [p[1]]
def p_interface_extends_list(p):
'''interface_extends_list : EXTENDS interface_list
| empty'''
if len(p) == 3:
p[0] = p[2]
def p_variable_modifiers_non_empty(p):
'variable_modifiers : non_empty_member_modifiers'
p[0] = p[1]
def p_variable_modifiers_var(p):
'variable_modifiers : VAR'
p[0] = []
def p_method_modifiers_non_empty(p):
'method_modifiers : non_empty_member_modifiers'
p[0] = p[1]
def p_method_modifiers_empty(p):
'method_modifiers : empty'
p[0] = []
def p_method_body(p):
'''method_body : LBRACE inner_statement_list RBRACE
| SEMI'''
if len(p) == 4:
p[0] = p[2]
else:
p[0] = []
def p_non_empty_member_modifiers(p):
'''non_empty_member_modifiers : non_empty_member_modifiers member_modifier
| member_modifier'''
if len(p) == 3:
p[0] = p[1] + [p[2]]
else:
p[0] = [p[1]]
def p_member_modifier(p):
'''member_modifier : PUBLIC
| PROTECTED
| PRIVATE
| STATIC
| ABSTRACT
| FINAL'''
p[0] = p[1].lower()
def p_is_reference(p):
'''is_reference : AND
| empty'''
p[0] = p[1] is not None
def p_parameter_list(p):
'''parameter_list : parameter_list COMMA parameter
| parameter'''
if len(p) == 4:
p[0] = p[1] + [p[3]]
else:
p[0] = [p[1]]
def p_parameter_list_empty(p):
'parameter_list : empty'
p[0] = []
def p_parameter(p):
'''parameter : VARIABLE
| class_name VARIABLE
| AND VARIABLE
| class_name AND VARIABLE
| VARIABLE EQUALS static_scalar
| class_name VARIABLE EQUALS static_scalar
| AND VARIABLE EQUALS static_scalar
| class_name AND VARIABLE EQUALS static_scalar'''
if len(p) == 2: # VARIABLE
p[0] = ast.FormalParameter(p[1], None, False, None, lineno=p.lineno(1))
elif len(p) == 3 and p[1] == '&': # AND VARIABLE
p[0] = ast.FormalParameter(p[2], None, True, None, lineno=p.lineno(1))
elif len(p) == 3 and p[1] != '&': # STRING VARIABLE
p[0] = ast.FormalParameter(p[2], None, False, p[1], lineno=p.lineno(1))
elif len(p) == 4 and p[2] != '&': # VARIABLE EQUALS static_scalar
p[0] = ast.FormalParameter(p[1], p[3], False, None, lineno=p.lineno(1))
elif len(p) == 4 and p[2] == '&': # STRING AND VARIABLE
p[0] = ast.FormalParameter(p[3], None, True, p[1], lineno=p.lineno(1))
elif len(p) == 5 and p[1] == '&': # AND VARIABLE EQUALS static_scalar
p[0] = ast.FormalParameter(p[2], p[4], True, None, lineno=p.lineno(1))
elif len(p) == 5 and p[1] != '&': # class_name VARIABLE EQUALS static_scalar
p[0] = ast.FormalParameter(p[2], p[4], False, p[1], lineno=p.lineno(1))
else: # STRING AND VARIABLE EQUALS static_scalar
p[0] = ast.FormalParameter(p[3], p[5], True, p[1], lineno=p.lineno(1))
def p_expr_variable(p):
'expr : variable'
p[0] = p[1]
def p_expr_assign(p):
'''expr : variable EQUALS expr
| variable EQUALS AND expr'''
if len(p) == 5:
p[0] = ast.Assignment(p[1], p[4], True, lineno=p.lineno(2))
else:
p[0] = ast.Assignment(p[1], p[3], False, lineno=p.lineno(2))
def p_expr_new(p):
'expr : NEW class_name_reference ctor_arguments'
p[0] = ast.New(p[2], p[3], lineno=p.lineno(1))
def p_class_name_reference(p):
'''class_name_reference : class_name
| dynamic_class_name_reference'''
p[0] = p[1]
def p_class_name(p):
'''class_name : namespace_name
| NS_SEPARATOR namespace_name
| NAMESPACE NS_SEPARATOR namespace_name'''
if len(p) == 2:
p[0] = p[1]
elif len(p) == 3:
p[0] = p[1] + p[2]
else:
p[0] = p[1] + p[2] + p[3]
def p_class_name_static(p):
'class_name : STATIC'
p[0] = p[1].lower()
def p_dynamic_class_name_reference(p):
'''dynamic_class_name_reference : base_variable OBJECT_OPERATOR object_property dynamic_class_name_variable_properties
| base_variable'''
if len(p) == 5:
name, dims = p[3]
p[0] = ast.ObjectProperty(p[1], name, lineno=p.lineno(2))
for class_, dim, lineno in dims:
p[0] = class_(p[0], dim, lineno=lineno)
for name, dims in p[4]:
p[0] = ast.ObjectProperty(p[0], name, lineno=p.lineno(2))
for class_, dim, lineno in dims:
p[0] = class_(p[0], dim, lineno=lineno)
else:
p[0] = p[1]
def p_dynamic_class_name_variable_properties(p):
'''dynamic_class_name_variable_properties : dynamic_class_name_variable_properties dynamic_class_name_variable_property
| empty'''
if len(p) == 3:
p[0] = p[1] + [p[2]]
else:
p[0] = []
def p_dynamic_class_name_variable_property(p):
'dynamic_class_name_variable_property : OBJECT_OPERATOR object_property'
p[0] = p[2]
def p_ctor_arguments(p):
'''ctor_arguments : LPAREN function_call_parameter_list RPAREN
| empty'''
if len(p) == 4:
p[0] = p[2]
else:
p[0] = []
def p_expr_clone(p):
'expr : CLONE expr'
p[0] = ast.Clone(p[2], lineno=p.lineno(1))
def p_expr_list_assign(p):
'expr : LIST LPAREN assignment_list RPAREN EQUALS expr'
p[0] = ast.ListAssignment(p[3], p[6], lineno=p.lineno(1))
def p_assignment_list(p):
'''assignment_list : assignment_list COMMA assignment_list_element
| assignment_list_element'''
if len(p) == 4:
p[0] = p[1] + [p[3]]
else:
p[0] = [p[1]]
def p_assignment_list_element(p):
'''assignment_list_element : variable
| empty
| LIST LPAREN assignment_list RPAREN'''
if len(p) == 2:
p[0] = p[1]
else:
p[0] = p[3]
def p_variable(p):
'''variable : base_variable_with_function_calls OBJECT_OPERATOR object_property method_or_not variable_properties
| base_variable_with_function_calls'''
if len(p) == 6:
name, dims = p[3]
params = p[4]
if params is not None:
p[0] = ast.MethodCall(p[1], name, params, lineno=p.lineno(2))
else:
p[0] = ast.ObjectProperty(p[1], name, lineno=p.lineno(2))
for class_, dim, lineno in dims:
p[0] = class_(p[0], dim, lineno=lineno)
for (name, dims), params in p[5]:
if params is not None:
p[0] = ast.MethodCall(p[0], name, params, lineno=p.lineno(2))
else:
p[0] = ast.ObjectProperty(p[0], name, lineno=p.lineno(2))
for class_, dim, lineno in dims:
p[0] = class_(p[0], dim, lineno=lineno)
else:
p[0] = p[1]
def p_base_variable_with_function_calls(p):
'''base_variable_with_function_calls : base_variable
| function_call'''
p[0] = p[1]
def p_function_call(p):
'''function_call : namespace_name LPAREN function_call_parameter_list RPAREN
| NS_SEPARATOR namespace_name LPAREN function_call_parameter_list RPAREN
| NAMESPACE NS_SEPARATOR namespace_name LPAREN function_call_parameter_list RPAREN'''
if len(p) == 5:
p[0] = ast.FunctionCall(p[1], p[3], lineno=p.lineno(2))
elif len(p) == 6:
p[0] = ast.FunctionCall(p[1] + p[2], p[4], lineno=p.lineno(1))
else:
p[0] = ast.FunctionCall(p[1] + p[2] + p[3], p[5], lineno=p.lineno(1))
def p_function_call_static(p):
'''function_call : class_name DOUBLE_COLON STRING LPAREN function_call_parameter_list RPAREN
| class_name DOUBLE_COLON variable_without_objects LPAREN function_call_parameter_list RPAREN
| variable_class_name DOUBLE_COLON STRING LPAREN function_call_parameter_list RPAREN
| variable_class_name DOUBLE_COLON variable_without_objects LPAREN function_call_parameter_list RPAREN'''
p[0] = ast.StaticMethodCall(p[1], p[3], p[5], lineno=p.lineno(2))
def p_function_call_variable(p):
'function_call : variable_without_objects LPAREN function_call_parameter_list RPAREN'
p[0] = ast.FunctionCall(p[1], p[3], lineno=p.lineno(2))
def p_method_or_not(p):
'''method_or_not : LPAREN function_call_parameter_list RPAREN
| empty'''
if len(p) == 4:
p[0] = p[2]
def p_variable_properties(p):
'''variable_properties : variable_properties variable_property
| empty'''
if len(p) == 3:
p[0] = p[1] + [p[2]]
else:
p[0] = []
def p_variable_property(p):
'variable_property : OBJECT_OPERATOR object_property method_or_not'
p[0] = (p[2], p[3])
def p_base_variable(p):
'''base_variable : simple_indirect_reference
| static_member'''
p[0] = p[1]
def p_simple_indirect_reference(p):
'''simple_indirect_reference : DOLLAR simple_indirect_reference
| reference_variable'''
if len(p) == 3:
p[0] = ast.Variable(p[2], lineno=p.lineno(1))
else:
p[0] = p[1]
def p_static_member(p):
'''static_member : class_name DOUBLE_COLON variable_without_objects
| variable_class_name DOUBLE_COLON variable_without_objects'''
p[0] = ast.StaticProperty(p[1], p[3], lineno=p.lineno(2))
def p_variable_class_name(p):
'variable_class_name : reference_variable'
p[0] = p[1]
def p_reference_variable_array_offset(p):
'reference_variable : reference_variable LBRACKET dim_offset RBRACKET'
p[0] = ast.ArrayOffset(p[1], p[3], lineno=p.lineno(2))
def p_reference_variable_string_offset(p):
'reference_variable : reference_variable LBRACE expr RBRACE'
p[0] = ast.StringOffset(p[1], p[3], lineno=p.lineno(2))
def p_reference_variable_compound_variable(p):
'reference_variable : compound_variable'
p[0] = p[1]
def p_compound_variable(p):
'''compound_variable : VARIABLE
| DOLLAR LBRACE expr RBRACE'''
if len(p) == 2:
p[0] = ast.Variable(p[1], lineno=p.lineno(1))
else:
p[0] = ast.Variable(p[3], lineno=p.lineno(1))
def p_dim_offset(p):
'''dim_offset : expr
| empty'''
p[0] = p[1]
def p_object_property(p):
'''object_property : variable_name object_dim_list
| variable_without_objects'''
if len(p) == 3:
p[0] = (p[1], p[2])
else:
p[0] = (p[1], [])
def p_object_dim_list_empty(p):
'object_dim_list : empty'
p[0] = []
def p_object_dim_list_array_offset(p):
'object_dim_list : object_dim_list LBRACKET dim_offset RBRACKET'
p[0] = p[1] + [(ast.ArrayOffset, p[3], p.lineno(2))]
def p_object_dim_list_string_offset(p):
'object_dim_list : object_dim_list LBRACE expr RBRACE'
p[0] = p[1] + [(ast.StringOffset, p[3], p.lineno(2))]
def p_variable_name(p):
'''variable_name : STRING
| LBRACE expr RBRACE'''
if len(p) == 2:
p[0] = p[1]
else:
p[0] = p[2]
def p_variable_without_objects(p):
'variable_without_objects : simple_indirect_reference'
p[0] = p[1]
def p_expr_scalar(p):
'expr : scalar'
p[0] = p[1]
def p_expr_array(p):
'expr : ARRAY LPAREN array_pair_list RPAREN'
p[0] = ast.Array(p[3], lineno=p.lineno(1))
def p_array_pair_list(p):
'''array_pair_list : empty
| non_empty_array_pair_list possible_comma'''
if len(p) == 2:
p[0] = []
else:
p[0] = p[1]
def p_non_empty_array_pair_list_item(p):
'''non_empty_array_pair_list : non_empty_array_pair_list COMMA AND variable
| non_empty_array_pair_list COMMA expr
| AND variable
| expr'''
if len(p) == 5:
p[0] = p[1] + [ast.ArrayElement(None, p[4], True, lineno=p.lineno(2))]
elif len(p) == 4:
p[0] = p[1] + [ast.ArrayElement(None, p[3], False, lineno=p.lineno(2))]
elif len(p) == 3:
p[0] = [ast.ArrayElement(None, p[2], True, lineno=p.lineno(1))]
else:
p[0] = [ast.ArrayElement(None, p[1], False, lineno=p.lineno(1))]
def p_non_empty_array_pair_list_pair(p):
'''non_empty_array_pair_list : non_empty_array_pair_list COMMA expr DOUBLE_ARROW AND variable
| non_empty_array_pair_list COMMA expr DOUBLE_ARROW expr
| expr DOUBLE_ARROW AND variable
| expr DOUBLE_ARROW expr'''
if len(p) == 7:
p[0] = p[1] + [ast.ArrayElement(p[3], p[6], True, lineno=p.lineno(2))]
elif len(p) == 6:
p[0] = p[1] + [ast.ArrayElement(p[3], p[5], False, lineno=p.lineno(2))]
elif len(p) == 5:
p[0] = [ast.ArrayElement(p[1], p[4], True, lineno=p.lineno(2))]
else:
p[0] = [ast.ArrayElement(p[1], p[3], False, lineno=p.lineno(2))]
def p_possible_comma(p):
'''possible_comma : empty
| COMMA'''
pass
def p_function_call_parameter_list(p):
'''function_call_parameter_list : function_call_parameter_list COMMA function_call_parameter
| function_call_parameter'''
if len(p) == 4:
p[0] = p[1] + [p[3]]
else:
p[0] = [p[1]]
def p_function_call_parameter_list_empty(p):
'function_call_parameter_list : empty'
p[0] = []
def p_function_call_parameter(p):
'''function_call_parameter : expr
| AND variable'''
if len(p) == 2:
p[0] = ast.Parameter(p[1], False, lineno=p.lineno(1))
else:
p[0] = ast.Parameter(p[2], True, lineno=p.lineno(1))
def p_expr_function(p):
'expr : FUNCTION is_reference LPAREN parameter_list RPAREN lexical_vars LBRACE inner_statement_list RBRACE'
p[0] = ast.Closure(p[4], p[6], p[8], p[2], lineno=p.lineno(1))
def p_lexical_vars(p):
'''lexical_vars : USE LPAREN lexical_var_list RPAREN
| empty'''
if len(p) == 5:
p[0] = p[3]
else:
p[0] = []
def p_lexical_var_list(p):
'''lexical_var_list : lexical_var_list COMMA AND VARIABLE
| lexical_var_list COMMA VARIABLE
| AND VARIABLE
| VARIABLE'''
if len(p) == 5:
p[0] = p[1] + [ast.LexicalVariable(p[4], True, lineno=p.lineno(2))]
elif len(p) == 4:
p[0] = p[1] + [ast.LexicalVariable(p[3], False, lineno=p.lineno(2))]
elif len(p) == 3:
p[0] = [ast.LexicalVariable(p[2], True, lineno=p.lineno(1))]
else:
p[0] = [ast.LexicalVariable(p[1], False, lineno=p.lineno(1))]
def p_expr_assign_op(p):
'''expr : variable PLUS_EQUAL expr
| variable MINUS_EQUAL expr
| variable MUL_EQUAL expr
| variable DIV_EQUAL expr
| variable CONCAT_EQUAL expr
| variable MOD_EQUAL expr
| variable AND_EQUAL expr
| variable OR_EQUAL expr
| variable XOR_EQUAL expr
| variable SL_EQUAL expr
| variable SR_EQUAL expr'''
p[0] = ast.AssignOp(p[2], p[1], p[3], lineno=p.lineno(2))
def p_expr_binary_op(p):
'''expr : expr BOOLEAN_AND expr
| expr BOOLEAN_OR expr
| expr LOGICAL_AND expr
| expr LOGICAL_OR expr
| expr LOGICAL_XOR expr
| expr AND expr
| expr OR expr
| expr XOR expr
| expr CONCAT expr
| expr PLUS expr
| expr MINUS expr
| expr MUL expr
| expr DIV expr
| expr SL expr
| expr SR expr
| expr MOD expr
| expr IS_IDENTICAL expr
| expr IS_NOT_IDENTICAL expr
| expr IS_EQUAL expr
| expr IS_NOT_EQUAL expr
| expr IS_SMALLER expr
| expr IS_SMALLER_OR_EQUAL expr
| expr IS_GREATER expr
| expr IS_GREATER_OR_EQUAL expr
| expr INSTANCEOF expr'''
p[0] = ast.BinaryOp(p[2].lower(), p[1], p[3], lineno=p.lineno(2))
def p_expr_unary_op(p):
'''expr : PLUS expr
| MINUS expr
| NOT expr
| BOOLEAN_NOT expr'''
p[0] = ast.UnaryOp(p[1], p[2], lineno=p.lineno(1))
def p_expr_ternary_op(p):
'expr : expr QUESTION expr COLON expr'
p[0] = ast.TernaryOp(p[1], p[3], p[5], lineno=p.lineno(2))
def p_expr_pre_incdec(p):
'''expr : INC variable
| DEC variable'''
p[0] = ast.PreIncDecOp(p[1], p[2], lineno=p.lineno(1))
def p_expr_post_incdec(p):
'''expr : variable INC
| variable DEC'''
p[0] = ast.PostIncDecOp(p[2], p[1], lineno=p.lineno(2))
def p_expr_cast_int(p):
'expr : INT_CAST expr'
p[0] = ast.Cast('int', p[2], lineno=p.lineno(1))
def p_expr_cast_double(p):
'expr : DOUBLE_CAST expr'
p[0] = ast.Cast('double', p[2], lineno=p.lineno(1))
def p_expr_cast_string(p):
'expr : STRING_CAST expr'
p[0] = ast.Cast('string', p[2], lineno=p.lineno(1))
def p_expr_cast_array(p):
'expr : ARRAY_CAST expr'
p[0] = ast.Cast('array', p[2], lineno=p.lineno(1))
def p_expr_cast_object(p):
'expr : OBJECT_CAST expr'
p[0] = ast.Cast('object', p[2], lineno=p.lineno(1))
def p_expr_cast_bool(p):
'expr : BOOL_CAST expr'
p[0] = ast.Cast('bool', p[2], lineno=p.lineno(1))
def p_expr_cast_unset(p):
'expr : UNSET_CAST expr'
p[0] = ast.Cast('unset', p[2], lineno=p.lineno(1))
def p_expr_isset(p):
'expr : ISSET LPAREN isset_variables RPAREN'
p[0] = ast.IsSet(p[3], lineno=p.lineno(1))
def p_isset_variables(p):
'''isset_variables : isset_variables COMMA variable
| variable'''
if len(p) == 4:
p[0] = p[1] + [p[3]]
else:
p[0] = [p[1]]
def p_expr_empty(p):
'expr : EMPTY LPAREN expr RPAREN'
p[0] = ast.Empty(p[3], lineno=p.lineno(1))
def p_expr_eval(p):
'expr : EVAL LPAREN expr RPAREN'
p[0] = ast.Eval(p[3], lineno=p.lineno(1))
def p_expr_include(p):
'expr : INCLUDE expr'
p[0] = ast.Include(p[2], False, lineno=p.lineno(1))
def p_expr_include_once(p):
'expr : INCLUDE_ONCE expr'
p[0] = ast.Include(p[2], True, lineno=p.lineno(1))
def p_expr_require(p):
'expr : REQUIRE expr'
p[0] = ast.Require(p[2], False, lineno=p.lineno(1))
def p_expr_require_once(p):
'expr : REQUIRE_ONCE expr'
p[0] = ast.Require(p[2], True, lineno=p.lineno(1))
def p_expr_exit(p):
'''expr : EXIT
| EXIT LPAREN RPAREN
| EXIT LPAREN expr RPAREN'''
if len(p) == 5:
p[0] = ast.Exit(p[3], lineno=p.lineno(1))
else:
p[0] = ast.Exit(None, lineno=p.lineno(1))
def p_expr_print(p):
'expr : PRINT expr'
p[0] = ast.Print(p[2], lineno=p.lineno(1))
def p_expr_silence(p):
'expr : AT expr'
p[0] = ast.Silence(p[2], lineno=p.lineno(1))
def p_expr_group(p):
'expr : LPAREN expr RPAREN'
p[0] = p[2]
def p_scalar(p):
'''scalar : class_constant
| common_scalar
| QUOTE encaps_list QUOTE
| START_HEREDOC encaps_list END_HEREDOC'''
if len(p) == 4:
p[0] = p[2]
else:
p[0] = p[1]
def p_scalar_string_varname(p):
'scalar : STRING_VARNAME'
p[0] = ast.Variable('$' + p[1], lineno=p.lineno(1))
def p_scalar_namespace_name(p):
'''scalar : namespace_name
| NS_SEPARATOR namespace_name
| NAMESPACE NS_SEPARATOR namespace_name'''
if len(p) == 2:
p[0] = ast.Constant(p[1], lineno=p.lineno(1))
elif len(p) == 3:
p[0] = ast.Constant(p[1] + p[2], lineno=p.lineno(1))
else:
p[0] = ast.Constant(p[1] + p[2] + p[3], lineno=p.lineno(1))
def p_class_constant(p):
'''class_constant : class_name DOUBLE_COLON STRING
| variable_class_name DOUBLE_COLON STRING'''
p[0] = ast.StaticProperty(p[1], p[3], lineno=p.lineno(2))
def p_common_scalar_lnumber(p):
'common_scalar : LNUMBER'
if p[1].startswith('0x'):
p[0] = int(p[1], 16)
elif p[1].startswith('0'):
p[0] = int(p[1], 8)
else:
p[0] = int(p[1])
def p_common_scalar_dnumber(p):
'common_scalar : DNUMBER'
p[0] = float(p[1])
def p_common_scalar_string(p):
'common_scalar : CONSTANT_ENCAPSED_STRING'
p[0] = p[1][1:-1].replace("\\'", "'").replace('\\\\', '\\')
def p_common_scalar_magic_line(p):
'common_scalar : LINE'
p[0] = ast.MagicConstant(p[1].upper(), p.lineno(1), lineno=p.lineno(1))
def p_common_scalar_magic_file(p):
'common_scalar : FILE'
value = getattr(p.lexer, 'filename', None)
p[0] = ast.MagicConstant(p[1].upper(), value, lineno=p.lineno(1))
def p_common_scalar_magic_dir(p):
'common_scalar : DIR'
value = getattr(p.lexer, 'filename', None)
if value is not None:
value = os.path.dirname(value)
p[0] = ast.MagicConstant(p[1].upper(), value, lineno=p.lineno(1))
def p_common_scalar_magic_class(p):
'common_scalar : CLASS_C'
p[0] = ast.MagicConstant(p[1].upper(), None, lineno=p.lineno(1))
def p_common_scalar_magic_method(p):
'common_scalar : METHOD_C'
p[0] = ast.MagicConstant(p[1].upper(), None, lineno=p.lineno(1))
def p_common_scalar_magic_func(p):
'common_scalar : FUNC_C'
p[0] = ast.MagicConstant(p[1].upper(), None, lineno=p.lineno(1))
def p_common_scalar_magic_ns(p):
'common_scalar : NS_C'
p[0] = ast.MagicConstant(p[1].upper(), None, lineno=p.lineno(1))
def p_static_scalar(p):
'''static_scalar : common_scalar
| QUOTE QUOTE
| QUOTE ENCAPSED_AND_WHITESPACE QUOTE'''
if len(p) == 2:
p[0] = p[1]
elif len(p) == 3:
p[0] = ''
else:
p[0] = p[2].decode('string_escape')
def p_static_scalar_namespace_name(p):
'''static_scalar : namespace_name
| NS_SEPARATOR namespace_name
| NAMESPACE NS_SEPARATOR namespace_name'''
if len(p) == 2:
p[0] = ast.Constant(p[1], lineno=p.lineno(1))
elif len(p) == 3:
p[0] = ast.Constant(p[1] + p[2], lineno=p.lineno(1))
else:
p[0] = ast.Constant(p[1] + p[2] + p[3], lineno=p.lineno(1))
def p_static_scalar_unary_op(p):
'''static_scalar : PLUS static_scalar
| MINUS static_scalar'''
p[0] = ast.UnaryOp(p[1], p[2], lineno=p.lineno(1))
def p_static_scalar_array(p):
'static_scalar : ARRAY LPAREN static_array_pair_list RPAREN'
p[0] = ast.Array(p[3], lineno=p.lineno(1))
def p_static_array_pair_list(p):
'''static_array_pair_list : empty
| static_non_empty_array_pair_list possible_comma'''
if len(p) == 2:
p[0] = []
else:
p[0] = p[1]
def p_static_non_empty_array_pair_list_item(p):
'''static_non_empty_array_pair_list : static_non_empty_array_pair_list COMMA static_scalar
| static_scalar'''
if len(p) == 4:
p[0] = p[1] + [ast.ArrayElement(None, p[3], False, lineno=p.lineno(2))]
else:
p[0] = [ast.ArrayElement(None, p[1], False, lineno=p.lineno(1))]
def p_static_non_empty_array_pair_list_pair(p):
'''static_non_empty_array_pair_list : static_non_empty_array_pair_list COMMA static_scalar DOUBLE_ARROW static_scalar
| static_scalar DOUBLE_ARROW static_scalar'''
if len(p) == 6:
p[0] = p[1] + [ast.ArrayElement(p[3], p[5], False, lineno=p.lineno(2))]
else:
p[0] = [ast.ArrayElement(p[1], p[3], False, lineno=p.lineno(2))]
def p_namespace_name(p):
'''namespace_name : namespace_name NS_SEPARATOR STRING
| STRING'''
if len(p) == 4:
p[0] = p[1] + p[2] + p[3]
else:
p[0] = p[1]
def p_encaps_list(p):
'''encaps_list : encaps_list encaps_var
| empty'''
if len(p) == 3:
if p[1] == '':
p[0] = p[2]
else:
p[0] = ast.BinaryOp('.', p[1], p[2], lineno=p.lineno(2))
else:
p[0] = ''
def p_encaps_list_string(p):
'encaps_list : encaps_list ENCAPSED_AND_WHITESPACE'
if p[1] == '':
p[0] = p[2].decode('string_escape')
else:
p[0] = ast.BinaryOp('.', p[1], p[2].decode('string_escape'),
lineno=p.lineno(2))
def p_encaps_var(p):
'encaps_var : VARIABLE'
p[0] = ast.Variable(p[1], lineno=p.lineno(1))
def p_encaps_var_array_offset(p):
'encaps_var : VARIABLE LBRACKET encaps_var_offset RBRACKET'
p[0] = ast.ArrayOffset(ast.Variable(p[1], lineno=p.lineno(1)), p[3],
lineno=p.lineno(2))
def p_encaps_var_object_property(p):
'encaps_var : VARIABLE OBJECT_OPERATOR STRING'
p[0] = ast.ObjectProperty(ast.Variable(p[1], lineno=p.lineno(1)), p[3],
lineno=p.lineno(2))
def p_encaps_var_dollar_curly_expr(p):
'encaps_var : DOLLAR_OPEN_CURLY_BRACES expr RBRACE'
p[0] = p[2]
def p_encaps_var_dollar_curly_array_offset(p):
'encaps_var : DOLLAR_OPEN_CURLY_BRACES STRING_VARNAME LBRACKET expr RBRACKET RBRACE'
p[0] = ast.ArrayOffset(ast.Variable('$' + p[2], lineno=p.lineno(2)), p[4],
lineno=p.lineno(3))
def p_encaps_var_curly_variable(p):
'encaps_var : CURLY_OPEN variable RBRACE'
p[0] = p[2]
def p_encaps_var_offset_string(p):
'encaps_var_offset : STRING'
p[0] = p[1]
def p_encaps_var_offset_num_string(p):
'encaps_var_offset : NUM_STRING'
p[0] = int(p[1])
def p_encaps_var_offset_variable(p):
'encaps_var_offset : VARIABLE'
p[0] = ast.Variable(p[1], lineno=p.lineno(1))
def p_comment(p):
'''comment : COMMENT
| DOC_COMMENT'''
p[0] = ast.Comment(p[1])
def p_empty(p):
'empty : '
pass
# Error rule for syntax errors
def p_error(t):
if t:
raise SyntaxError('invalid syntax', (None, t.lineno, None, t.value))
else:
raise SyntaxError('unexpected EOF while parsing', (None, None, None, None))
# Build the grammar
parser = yacc.yacc()
if __name__ == '__main__':
import readline
import pprint
s = ''
lexer = phplex.lexer
while True:
try:
if s:
prompt = ' '
else:
prompt = lexer.current_state()
if prompt == 'INITIAL': prompt = 'html'
prompt += '> '
s += raw_input(prompt)
except EOFError:
break
if not s: continue
s += '\n'
try:
lexer.lineno = 1
result = parser.parse(s, lexer=lexer)
except SyntaxError, e:
if e.lineno is not None:
print e, 'near', repr(e.text)
s = ''
continue
if result:
for item in result:
if hasattr(item, 'generic'):
item = item.generic()
pprint.pprint(item)
s = ''
# -*- coding: utf-8 -*-
"""
sphinxcontrib_wikitable
~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2012 by Takeshi KOMIYA
:license: BSD, see LICENSE for details.
"""
import os
import re
from docutils import nodes
from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
class PHPAutodocDirective(Directive):
has_content = False
optional_arguments = 1
def run(self):
self.indent = u''
self.result = ViewList()
srcdir = self.state.document.settings.env.srcdir
filename = os.path.join(srcdir, self.arguments[0])
self.parse(filename)
node = nodes.paragraph()
node.document = self.state.document
self.state.nested_parse(self.result, 0, node)
return node.children
def add_line(self, line, *lineno):
self.result.append(self.indent + line, '<phpautodoc>', *lineno)
def add_directive_header(self, directive, name):
domain = getattr(self, 'domain', 'py')
self.add_line(u'.. %s:%s:: %s' % (domain, directive, name))
self.add_line('')
def add_comment(self, comment):
text = comment.text
text = re.sub('^//', '', text)
text = re.sub('^/\*\s*', '', text)
text = re.sub('\s*\*/$', '', text)
r = re.compile('^\s*\*[ ]*', re.M)
text = re.sub(r, '', text)
for line in text.splitlines():
self.add_line(u' ' + line)
self.add_line('')
def parse(self, filename):
from phplex import lexer
from phpparse import parser
try:
with open(filename) as f:
tree = parser.parse(f.read(), lexer=lexer)
self._parse(tree)
except:
pass # FIXME: ignore any errors
def _parse(self, tree):
from phply import phpast as ast
last_node = None
for node in tree:
if isinstance(node, ast.Function):
self.add_directive_header('function', node.name)
if isinstance(last_node, ast.Comment):
self.add_comment(last_node)
elif isinstance(node, ast.Class):
self.add_directive_header('class', node.name)
if isinstance(last_node, ast.Comment):
self.add_comment(last_node)
self._parse(node.nodes)
elif isinstance(node, ast.Method):
self.add_directive_header('method', node.name)
if isinstance(last_node, ast.Comment):
self.add_comment(last_node)
last_node = node
def setup(app):
app.add_directive('phpautodoc', PHPAutodocDirective)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment