phply-0.9.1/0000775000175000017500000000000012340747411013076 5ustar pablopablo00000000000000phply-0.9.1/setup.py0000664000175000017500000000203112340747172014610 0ustar pablopablo00000000000000""" PHPLY -------------------- PHP lexer and parser in Python """ from setuptools import setup, find_packages setup(name="phply", version="0.9.1", packages=find_packages(), namespace_packages=['phply'], include_package_data=True, author='Ramen', author_email='', description='PHP lexer and parser in Python', long_description=__doc__, zip_safe=False, platforms='any', license='BSD', url='http://www.github.com/ramen/phply', classifiers=[ 'Development Status :: 2 - Pre-Alpha', 'Environment :: Console', 'Intended Audience :: Education', 'License :: OSI Approved :: BSD License', 'Programming Language :: Python', 'Programming Language :: PHP', 'Operating System :: Unix', ], # entry_points={ # 'console_scripts': [ # ], # }, install_requires=[ 'ply', ], test_suite='nose.collector', tests_require=[ 'nose', ], ) phply-0.9.1/tools/0000775000175000017500000000000012340747411014236 5ustar pablopablo00000000000000phply-0.9.1/tools/php2python.py0000775000175000017500000000072312331717437016735 0ustar pablopablo00000000000000#!/usr/bin/env python # php2python.py - Converts PHP to Python using unparse.py # Usage: php2python.py < input.php > output.py import sys sys.path.append('..') from phply.phplex import lexer from phply.phpparse import parser from phply import pythonast from ast import Module from unparse import Unparser input = sys.stdin output = sys.stdout body = [pythonast.from_phpast(ast) for ast in parser.parse(input.read(), lexer=lexer)] Unparser(body, output) phply-0.9.1/tools/php2jinja.py0000775000175000017500000001531112331717437016506 0ustar pablopablo00000000000000#!/usr/bin/env python # php2jinja.py - Converts PHP to Jinja2 templates (experimental) # Usage: php2jinja.py < input.php > output.html import sys sys.path.append('..') from phply.phplex import lexer from phply.phpparse import parser from phply.phpast import * input = sys.stdin output = sys.stdout op_map = { '&&': 'and', '||': 'or', '!': 'not', '!==': '!=', '===': '==', '.': '~', } def unparse(nodes): result = [] for node in nodes: result.append(unparse_node(node)) return ''.join(result) def unparse_node(node, is_expr=False): if isinstance(node, (basestring, int, float)): return repr(node) if isinstance(node, InlineHTML): return str(node.data) if isinstance(node, Constant): if node.name.lower() == 'null': return 'None' return str(node.name) if isinstance(node, Variable): return str(node.name[1:]) if isinstance(node, Echo): return '{{ %s }}' % (''.join(unparse_node(x, True) for x in node.nodes)) if isinstance(node, (Include, Require)): return '{%% include %s -%%}' % (unparse_node(node.expr, True)) if isinstance(node, Block): return ''.join(unparse_node(x) for x in node.nodes) if isinstance(node, ArrayOffset): return '%s[%s]' % (unparse_node(node.node, True), unparse_node(node.expr, True)) if isinstance(node, ObjectProperty): return '%s.%s' % (unparse_node(node.node, True), node.name) if isinstance(node, Array): elems = [] for elem in node.nodes: elems.append(unparse_node(elem, True)) if node.nodes and node.nodes[0].key is not None: return '{%s}' % ', '.join(elems) else: return '[%s]' % ', '.join(elems) if isinstance(node, ArrayElement): if node.key: return '%s: %s' % (unparse_node(node.key, True), unparse_node(node.value, True)) else: return unparse_node(node.value, True) if isinstance(node, Assignment): if isinstance(node.node, ArrayOffset) and node.node.expr is None: return '{%% do %s.append(%s) -%%}' % (unparse_node(node.node.node, None), unparse_node(node.expr, True)) else: return '{%% set %s = %s -%%}' % (unparse_node(node.node, True), unparse_node(node.expr, True)) if isinstance(node, UnaryOp): op = op_map.get(node.op, node.op) return '(%s %s)' % (op, unparse_node(node.expr, True)) if isinstance(node, BinaryOp): op = op_map.get(node.op, node.op) return '(%s %s %s)' % (unparse_node(node.left, True), op, unparse_node(node.right, True)) if isinstance(node, TernaryOp): return '(%s if %s else %s)' % (unparse_node(node.iftrue, True), unparse_node(node.expr, True), unparse_node(node.iffalse, True)) if isinstance(node, IsSet): if len(node.nodes) == 1: return '(%s is defined)' % unparse_node(node.nodes[0], True) else: tests = ['(%s is defined)' % unparse_node(n, True) for n in node.nodes] return '(' + ' and '.join(tests) + ')' if isinstance(node, Empty): return '(not %s)' % (unparse_node(node.expr, True)) if isinstance(node, Silence): return unparse_node(node.expr, True) if isinstance(node, Cast): filter = '' if node.type in ('int', 'float', 'string'): filter = '|%s' % node.type return '%s%s' % (unparse_node(node.expr, True), filter) if isinstance(node, If): body = unparse_node(node.node) for elseif in node.elseifs: body += '{%% elif %s -%%}%s' % (unparse_node(elseif.expr, True), unparse_node(elseif.node)) if node.else_: body += '{%% else -%%}%s' % (unparse_node(node.else_.node)) return '{%% if %s -%%}%s{%% endif -%%}' % (unparse_node(node.expr, True), body) if isinstance(node, While): dummy = Foreach(node.expr, None, ForeachVariable('$XXX', False), node.node) return unparse_node(dummy) if isinstance(node, Foreach): var = node.valvar.name[1:] if node.keyvar: var = '%s, %s' % (node.keyvar.name[1:], var) return '{%% for %s in %s -%%}%s{%% endfor -%%}' % (var, unparse_node(node.expr, True), unparse_node(node.node)) if isinstance(node, Function): name = node.name params = [] for param in node.params: params.append(param.name[1:]) # if param.default is not None: # params.append('%s=%s' % (param.name[1:], # unparse_node(param.default, True))) # else: # params.append(param.name[1:]) params = ', '.join(params) body = '\n '.join(unparse_node(node) for node in node.nodes) return '{%% macro %s(%s) -%%}\n %s\n{%%- endmacro -%%}\n\n' % (name, params, body) if isinstance(node, Return): return '{{ %s }}' % unparse_node(node.node, True) if isinstance(node, FunctionCall): if node.name.endswith('printf'): params = [unparse_node(x.node, True) for x in node.params[1:]] if is_expr: return '%s %% (%s,)' % (unparse_node(node.params[0].node, True), ', '.join(params)) else: return '{{ %s %% (%s,) }}' % (unparse_node(node.params[0].node, True), ', '.join(params)) params = ', '.join(unparse_node(param.node, True) for param in node.params) if is_expr: return '%s(%s)' % (node.name, params) else: return '{{ %s(%s) }}' % (node.name, params) if isinstance(node, MethodCall): params = ', '.join(unparse_node(param.node, True) for param in node.params) if is_expr: return '%s.%s(%s)' % (unparse_node(node.node, True), node.name, params) else: return '{{ %s.%s(%s) }}' % (unparse_node(node.node, True), node.name, params) if is_expr: return 'XXX(%r)' % str(node) else: return '{# XXX %s #}' % node output.write(unparse(parser.parse(input.read(), lexer=lexer))) phply-0.9.1/tools/tokenize.php0000664000175000017500000000126312331717437016606 0ustar pablopablo00000000000000 output.json import sys sys.path.append('..') from phply.phplex import lexer from phply.phpparse import parser import simplejson input = sys.stdin output = sys.stdout with_lineno = True def export(items): result = [] if items: for item in items: if hasattr(item, 'generic'): item = item.generic(with_lineno=with_lineno) result.append(item) return result simplejson.dump(export(parser.parse(input.read(), lexer=lexer, tracking=with_lineno)), output, indent=2) output.write('\n') phply-0.9.1/tools/unparse.py0000664000175000017500000003306612331717437016302 0ustar pablopablo00000000000000"Usage: unparse.py " import sys import _ast import cStringIO import os def interleave(inter, f, seq): """Call f on each item in seq, calling inter() in between. """ seq = iter(seq) try: f(seq.next()) except StopIteration: pass else: for x in seq: inter() f(x) class Unparser: """Methods in this class recursively traverse an AST and output source code for the abstract syntax; original formatting is disregarged. """ def __init__(self, tree, file = sys.stdout): """Unparser(tree, file=sys.stdout) -> None. Print the source for tree to file.""" self.f = file self._indent = 0 self.dispatch(tree) print >>self.f,"" self.f.flush() def fill(self, text = ""): "Indent a piece of text, according to the current indentation level" self.f.write("\n"+" "*self._indent + text) def write(self, text): "Append a piece of text to the current line." self.f.write(text) def enter(self): "Print ':', and increase the indentation." self.write(":") self._indent += 1 def leave(self): "Decrease the indentation level." self._indent -= 1 def dispatch(self, tree): "Dispatcher function, dispatching tree type T to method _T." if isinstance(tree, list): for t in tree: self.dispatch(t) return meth = getattr(self, "_"+tree.__class__.__name__) meth(tree) ############### Unparsing methods ###################### # There should be one method per concrete grammar type # # Constructors should be grouped by sum type. Ideally, # # this would follow the order in the grammar, but # # currently doesn't. # ######################################################## def _Module(self, tree): for stmt in tree.body: self.dispatch(stmt) # stmt def _Expr(self, tree): self.fill() self.dispatch(tree.value) def _Import(self, t): self.fill("import ") interleave(lambda: self.write(", "), self.dispatch, t.names) def _ImportFrom(self, t): self.fill("from ") self.write(t.module) self.write(" import ") interleave(lambda: self.write(", "), self.dispatch, t.names) # XXX(jpe) what is level for? def _Assign(self, t): self.fill() for target in t.targets: self.dispatch(target) self.write(" = ") self.dispatch(t.value) def _AugAssign(self, t): self.fill() self.dispatch(t.target) self.write(" "+self.binop[t.op.__class__.__name__]+"= ") self.dispatch(t.value) def _Return(self, t): self.fill("return") if t.value: self.write(" ") self.dispatch(t.value) def _Pass(self, t): self.fill("pass") def _Break(self, t): self.fill("break") def _Continue(self, t): self.fill("continue") def _Delete(self, t): self.fill("del ") self.dispatch(t.targets) def _Assert(self, t): self.fill("assert ") self.dispatch(t.test) if t.msg: self.write(", ") self.dispatch(t.msg) def _Exec(self, t): self.fill("exec ") self.dispatch(t.body) if t.globals: self.write(" in ") self.dispatch(t.globals) if t.locals: self.write(", ") self.dispatch(t.locals) def _Print(self, t): self.fill("print ") do_comma = False if t.dest: self.write(">>") self.dispatch(t.dest) do_comma = True for e in t.values: if do_comma:self.write(", ") else:do_comma=True self.dispatch(e) if not t.nl: self.write(",") def _Global(self, t): self.fill("global ") interleave(lambda: self.write(", "), self.write, t.names) def _Yield(self, t): self.write("(") self.write("yield") if t.value: self.write(" ") self.dispatch(t.value) self.write(")") def _Raise(self, t): self.fill('raise ') if t.type: self.dispatch(t.type) if t.inst: self.write(", ") self.dispatch(t.inst) if t.tback: self.write(", ") self.dispatch(t.tback) def _TryExcept(self, t): self.fill("try") self.enter() self.dispatch(t.body) self.leave() for ex in t.handlers: self.dispatch(ex) if t.orelse: self.fill("else") self.enter() self.dispatch(t.orelse) self.leave() def _TryFinally(self, t): self.fill("try") self.enter() self.dispatch(t.body) self.leave() self.fill("finally") self.enter() self.dispatch(t.finalbody) self.leave() def _ExceptHandler(self, t): self.fill("except") if t.type: self.write(" ") self.dispatch(t.type) if t.name: self.write(", ") self.dispatch(t.name) self.enter() self.dispatch(t.body) self.leave() def _ClassDef(self, t): self.write("\n") self.fill("class "+t.name) if t.bases: self.write("(") for a in t.bases: self.dispatch(a) self.write(", ") self.write(")") self.enter() self.dispatch(t.body) self.leave() def _FunctionDef(self, t): self.write("\n") for deco in t.decorator_list: self.fill("@") self.dispatch(deco) self.fill("def "+t.name + "(") self.dispatch(t.args) self.write(")") self.enter() self.dispatch(t.body) self.leave() def _For(self, t): self.fill("for ") self.dispatch(t.target) self.write(" in ") self.dispatch(t.iter) self.enter() self.dispatch(t.body) self.leave() if t.orelse: self.fill("else") self.enter() self.dispatch(t.orelse) self.leave def _If(self, t): self.fill("if ") self.dispatch(t.test) self.enter() # XXX elif? self.dispatch(t.body) self.leave() if t.orelse: self.fill("else") self.enter() self.dispatch(t.orelse) self.leave() def _While(self, t): self.fill("while ") self.dispatch(t.test) self.enter() self.dispatch(t.body) self.leave() if t.orelse: self.fill("else") self.enter() self.dispatch(t.orelse) self.leave def _With(self, t): self.fill("with ") self.dispatch(t.context_expr) if t.optional_vars: self.write(" as ") self.dispatch(t.optional_vars) self.enter() self.dispatch(t.body) self.leave() # expr def _Str(self, tree): self.write(repr(tree.s)) def _Name(self, t): self.write(t.id) def _Repr(self, t): self.write("`") self.dispatch(t.value) self.write("`") def _Num(self, t): self.write(repr(t.n)) def _List(self, t): self.write("[") interleave(lambda: self.write(", "), self.dispatch, t.elts) self.write("]") def _ListComp(self, t): self.write("[") self.dispatch(t.elt) for gen in t.generators: self.dispatch(gen) self.write("]") def _GeneratorExp(self, t): self.write("(") self.dispatch(t.elt) for gen in t.generators: self.dispatch(gen) self.write(")") def _comprehension(self, t): self.write(" for ") self.dispatch(t.target) self.write(" in ") self.dispatch(t.iter) for if_clause in t.ifs: self.write(" if ") self.dispatch(if_clause) def _IfExp(self, t): self.write("(") self.dispatch(t.body) self.write(" if ") self.dispatch(t.test) self.write(" else ") self.dispatch(t.orelse) self.write(")") def _Dict(self, t): self.write("{") def writem((k, v)): self.dispatch(k) self.write(": ") self.dispatch(v) interleave(lambda: self.write(", "), writem, zip(t.keys, t.values)) self.write("}") def _Tuple(self, t): self.write("(") if len(t.elts) == 1: (elt,) = t.elts self.dispatch(elt) self.write(",") else: interleave(lambda: self.write(", "), self.dispatch, t.elts) self.write(")") unop = {"Invert":"~", "Not": "not", "UAdd":"+", "USub":"-"} def _UnaryOp(self, t): self.write(self.unop[t.op.__class__.__name__]) self.write("(") self.dispatch(t.operand) self.write(")") binop = { "Add":"+", "Sub":"-", "Mult":"*", "Div":"/", "Mod":"%", "LShift":">>", "RShift":"<<", "BitOr":"|", "BitXor":"^", "BitAnd":"&", "FloorDiv":"//", "Pow": "**"} def _BinOp(self, t): self.write("(") self.dispatch(t.left) self.write(" " + self.binop[t.op.__class__.__name__] + " ") self.dispatch(t.right) self.write(")") cmpops = {"Eq":"==", "NotEq":"!=", "Lt":"<", "LtE":"<=", "Gt":">", "GtE":">=", "Is":"is", "IsNot":"is not", "In":"in", "NotIn":"not in"} def _Compare(self, t): self.write("(") self.dispatch(t.left) for o, e in zip(t.ops, t.comparators): self.write(" " + self.cmpops[o.__class__.__name__] + " ") self.dispatch(e) self.write(")") boolops = {_ast.And: 'and', _ast.Or: 'or'} def _BoolOp(self, t): self.write("(") s = " %s " % self.boolops[t.op.__class__] interleave(lambda: self.write(s), self.dispatch, t.values) self.write(")") def _Attribute(self,t): self.dispatch(t.value) self.write(".") self.write(t.attr) def _Call(self, t): self.dispatch(t.func) self.write("(") comma = False for e in t.args: if comma: self.write(", ") else: comma = True self.dispatch(e) for e in t.keywords: if comma: self.write(", ") else: comma = True self.dispatch(e) if t.starargs: if comma: self.write(", ") else: comma = True self.write("*") self.dispatch(t.starargs) if t.kwargs: if comma: self.write(", ") else: comma = True self.write("**") self.dispatch(t.kwargs) self.write(")") def _Subscript(self, t): self.dispatch(t.value) self.write("[") self.dispatch(t.slice) self.write("]") # slice def _Ellipsis(self, t): self.write("...") def _Index(self, t): self.dispatch(t.value) def _Slice(self, t): if t.lower: self.dispatch(t.lower) self.write(":") if t.upper: self.dispatch(t.upper) if t.step: self.write(":") self.dispatch(t.step) def _ExtSlice(self, t): interleave(lambda: self.write(', '), self.dispatch, t.dims) # others def _arguments(self, t): first = True nonDef = len(t.args)-len(t.defaults) for a in t.args[0:nonDef]: if first:first = False else: self.write(", ") self.dispatch(a) for a,d in zip(t.args[nonDef:], t.defaults): if first:first = False else: self.write(", ") self.dispatch(a), self.write("=") self.dispatch(d) if t.vararg: if first:first = False else: self.write(", ") self.write("*"+t.vararg) if t.kwarg: if first:first = False else: self.write(", ") self.write("**"+t.kwarg) def _keyword(self, t): self.write(t.arg) self.write("=") self.dispatch(t.value) def _Lambda(self, t): self.write("lambda ") self.dispatch(t.args) self.write(": ") self.dispatch(t.body) def _alias(self, t): self.write(t.name) if t.asname: self.write(" as "+t.asname) def roundtrip(filename, output=sys.stdout): source = open(filename).read() tree = compile(source, filename, "exec", _ast.PyCF_ONLY_AST) Unparser(tree, output) def testdir(a): try: names = [n for n in os.listdir(a) if n.endswith('.py')] except OSError: print >> sys.stderr, "Directory not readable: %s" % a else: for n in names: fullname = os.path.join(a, n) if os.path.isfile(fullname): output = cStringIO.StringIO() print 'Testing %s' % fullname try: roundtrip(fullname, output) except Exception, e: print ' Failed to compile, exception is %s' % repr(e) elif os.path.isdir(fullname): testdir(fullname) def main(args): if args[0] == '--testdir': for a in args[1:]: testdir(a) else: for a in args: roundtrip(a) if __name__=='__main__': main(sys.argv[1:]) phply-0.9.1/tools/phpshell.py0000775000175000017500000000425212331717437016442 0ustar pablopablo00000000000000#!/usr/bin/env python # phpshell.py - PHP interactive interpreter import sys sys.path.append('..') import ast import pprint import readline import traceback from phply import pythonast, phplex from phply.phpparse import parser def echo(*objs): for obj in objs: sys.stdout.write(str(obj)) def inline_html(obj): sys.stdout.write(obj) def XXX(obj): print 'Not implemented:\n ', obj def ast_dump(code): print 'AST dump:' print ' ', ast.dump(code, include_attributes=True) def php_eval(nodes): body = [] for node in nodes: stmt = pythonast.to_stmt(pythonast.from_phpast(node)) body.append(stmt) code = ast.Module(body) # ast_dump(code) eval(compile(code, '', mode='exec'), globals()) s = '' lexer = phplex.lexer parser.parse(' ' try: s += raw_input(prompt) except EOFError: break if not s: continue s += '\n' # Catch all exceptions and print tracebacks. try: # Try parsing the input normally. try: lexer.lineno = 1 result = parser.parse(s, lexer=lexer) php_eval(result) except SyntaxError, e: # Parsing failed. See if it can be parsed as an expression. try: lexer.lineno = 1 result = parser.parse('print ' + s + ';', lexer=lexer) php_eval(result) except (SyntaxError, TypeError): # That also failed. Try adding a semicolon. try: lexer.lineno = 1 result = parser.parse(s + ';', lexer=lexer) php_eval(result) except SyntaxError: # Did we get an EOF? If so, we're still waiting for input. # If not, it's a syntax error for sure. if e.lineno is None: continue else: print e, 'near', repr(e.text) s = '' except: traceback.print_exc() s = '' phply-0.9.1/tests/0000775000175000017500000000000012340747411014240 5ustar pablopablo00000000000000phply-0.9.1/tests/test_parser.py0000664000175000017500000005547512331717437017172 0ustar pablopablo00000000000000from phply import phplex from phply.phpparse import parser from phply.phpast import * import nose.tools import pprint def eq_ast(input, expected, filename=None): lexer = phplex.lexer.clone() lexer.filename = filename output = parser.parse(input, lexer=lexer) resolve_magic_constants(output) print 'Parser output:' pprint.pprint(output) print print 'Node by node:' for out, exp in zip(output, expected): print '\tgot:', out, '\texpected:', exp nose.tools.eq_(out, exp) assert len(output) == len(expected), \ 'output length was %d, expected %s' % (len(output), len(expected)) def test_inline_html(): input = 'html more html' expected = [InlineHTML('html '), InlineHTML(' more html')] eq_ast(input, expected) def test_echo(): input = '' expected = [Echo(["hello, world!"])] eq_ast(input, expected) def test_open_tag_with_echo(): input = '' expected = [ Echo(["hello, world!"]), Echo(["test"]), Constant('EXTRA'), ] eq_ast(input, expected) def test_exit(): input = '' expected = [ Exit(None), Exit(None), Exit(123), Exit(None), Exit(None), Exit(456), ] eq_ast(input, expected) def test_isset(): input = r"""c); isset($d['e']); isset($f, $g); ?>""" expected = [ IsSet([Variable('$a')]), IsSet([ObjectProperty(Variable('$b'), 'c')]), IsSet([ArrayOffset(Variable('$d'), 'e')]), IsSet([Variable('$f'), Variable('$g')]), ] eq_ast(input, expected) def test_namespace_names(): input = r"""""" expected = [ Constant(r'foo'), Constant(r'bar\baz'), Constant(r'one\too\tree'), Constant(r'\top'), Constant(r'\top\level'), Constant(r'namespace\level'), ] eq_ast(input, expected) def test_unary_ops(): input = r"""""" expected = [ Assignment(Variable('$a'), UnaryOp('-', 5), False), Assignment(Variable('$b'), UnaryOp('+', 6), False), Assignment(Variable('$c'), UnaryOp('!', Variable('$d')), False), Assignment(Variable('$e'), UnaryOp('~', Variable('$f')), False), ] eq_ast(input, expected) def test_assignment_ops(): input = r"""""" expected = [ AssignOp('+=', Variable('$a'), 5), AssignOp('-=', Variable('$b'), 6), AssignOp('.=', Variable('$c'), Variable('$d')), AssignOp('^=', Variable('$e'), Variable('$f')), ] eq_ast(input, expected) def test_object_properties(): input = r"""property; $object->foreach; $object->$variable; $object->$variable->schmariable; $object->$variable->$schmariable; ?>""" expected = [ ObjectProperty(Variable('$object'), 'property'), ObjectProperty(Variable('$object'), 'foreach'), ObjectProperty(Variable('$object'), Variable('$variable')), ObjectProperty(ObjectProperty(Variable('$object'), Variable('$variable')), 'schmariable'), ObjectProperty(ObjectProperty(Variable('$object'), Variable('$variable')), Variable('$schmariable')), ] eq_ast(input, expected) def test_string_unescape(): input = r"""""" expected = [ r"\r\n\t\'", "\r\n\t\\\"", ] eq_ast(input, expected) def test_string_offset_lookups(): input = r"""property"; "$too->many->properties"; "$adjacent->object$lookup"; "$two->$variables"; "stray -> [ ]"; "not[array]"; "non->object"; ?>""" expected = [ ArrayOffset(Variable('$array'), 'offset'), ArrayOffset(Variable('$array'), 42), ArrayOffset(Variable('$array'), Variable('$variable')), ArrayOffset(Variable('$curly'), 'offset'), BinaryOp('.', ArrayOffset(Variable('$too'), 'many'), '[offsets]'), BinaryOp('.', ArrayOffset(Variable('$next'), 'to'), Variable('$array')), ObjectProperty(Variable('$object'), 'property'), BinaryOp('.', ObjectProperty(Variable('$too'), 'many'), '->properties'), BinaryOp('.', ObjectProperty(Variable('$adjacent'), 'object'), Variable('$lookup')), BinaryOp('.', BinaryOp('.', Variable('$two'), '->'), Variable('$variables')), 'stray -> [ ]', 'not[array]', 'non->object', ] eq_ast(input, expected) def test_string_curly_dollar_expressions(): input = r"""items[4]->five}"; "{${$nasty}}"; "{${funcall()}}"; "{${$object->method()}}"; "{$object->$variable}"; "{$object->$variable[1]}"; "{${static_class::constant}}"; "{${static_class::$variable}}"; ?>""" expected = [ BinaryOp('.', BinaryOp('.', 'a', Variable('$dollar_curly')), 'b'), BinaryOp('.', BinaryOp('.', 'c', Variable('$curly_dollar')), 'd'), BinaryOp('.', BinaryOp('.', 'e', Variable('$dollar_curly_dollar')), 'f'), ArrayOffset(ArrayOffset(Variable('$array'), 0), 1), ArrayOffset(ArrayOffset(Variable('$array'), 'two'), 3), ObjectProperty(ArrayOffset(ObjectProperty(Variable('$object'), 'items'), 4), 'five'), Variable(Variable('$nasty')), Variable(FunctionCall('funcall', [])), Variable(MethodCall(Variable('$object'), 'method', [])), ObjectProperty(Variable('$object'), Variable('$variable')), ObjectProperty(Variable('$object'), ArrayOffset(Variable('$variable'), 1)), Variable(StaticProperty('static_class', 'constant')), Variable(StaticProperty('static_class', Variable('$variable'))), ] eq_ast(input, expected) def test_heredoc(): input = r"""variables. This is not the EOT; this is: EOT; ?>""" expected = [ Echo([BinaryOp('.', BinaryOp('.', BinaryOp('.', BinaryOp('.', BinaryOp('.', BinaryOp('.', BinaryOp('.', 'This', ' is a "'), Variable('$heredoc')), '" with some '), ObjectProperty(Variable('$embedded'), 'variables')), '.\n'), 'This'), ' is not the EOT; this is:\n')]), ] eq_ast(input, expected) def test_function_calls(): input = r"""""" expected = [ FunctionCall('f', []), FunctionCall('doit', [Parameter(Variable('$arg1'), False), Parameter(Variable('$arg2'), True), Parameter(BinaryOp('+', 3, 4), False)]), FunctionCall('name\\spaced', []), FunctionCall('\\name\\spaced', []), FunctionCall('namespace\\d', []), ] eq_ast(input, expected) def test_method_calls(): input = r"""meth($a, &$b, $c . $d); $chain->one($x)->two(&$y); ?>""" expected = [ MethodCall(Variable('$obj'), 'meth', [Parameter(Variable('$a'), False), Parameter(Variable('$b'), True), Parameter(BinaryOp('.', Variable('$c'), Variable('$d')), False)]), MethodCall(MethodCall(Variable('$chain'), 'one', [Parameter(Variable('$x'), False)]), 'two', [Parameter(Variable('$y'), True)]), ] eq_ast(input, expected) def test_if(): input = r""" $b) { return 1; } elseif ($a == $b) { return 0; } else { return 'firetruck'; } if ($if): echo 'a'; elseif ($elseif): echo 'b'; else: echo 'c'; endif; ?>""" expected = [ If(1, If(2, Echo([3]), [], Else(Echo([4]))), [], Else(Echo([5]))), If(BinaryOp('<', Variable('$a'), Variable('$b')), Block([Return(UnaryOp('-', 1))]), [ElseIf(BinaryOp('>', Variable('$a'), Variable('$b')), Block([Return(1)])), ElseIf(BinaryOp('==', Variable('$a'), Variable('$b')), Block([Return(0)]))], Else(Block([Return('firetruck')]))), If(Variable('$if'), Block([Echo(['a'])]), [ElseIf(Variable('$elseif'), Block([Echo(['b'])]))], Else(Block([Echo(['c'])]))), ] eq_ast(input, expected) def test_foreach(): input = r""" $eggs) { echo "$ham: $eggs"; } foreach (complex($expression) as &$ref) $ref++; foreach ($what as &$de => &$dealy): yo(); yo(); endforeach; ?>""" expected = [ Foreach(Variable('$foo'), None, ForeachVariable('$bar', False), Block([Echo([Variable('$bar')])])), Foreach(Variable('$spam'), ForeachVariable('$ham', False), ForeachVariable('$eggs', False), Block([Echo([BinaryOp('.', BinaryOp('.', Variable('$ham'), ': '), Variable('$eggs'))])])), Foreach(FunctionCall('complex', [Parameter(Variable('$expression'), False)]), None, ForeachVariable('$ref', True), PostIncDecOp('++', Variable('$ref'))), Foreach(Variable('$what'), ForeachVariable('$de', True), ForeachVariable('$dealy', True), Block([FunctionCall('yo', []), FunctionCall('yo', [])])), ] eq_ast(input, expected) def test_global_variables(): input = r"""prop}; ?>""" expected = [ Global([Variable('$foo'), Variable('$bar')]), Global([Variable(Variable('$yo'))]), Global([Variable(Variable('$dawg'))]), Global([Variable(ObjectProperty(Variable('$obj'), 'prop'))]), ] eq_ast(input, expected) def test_variable_variables(): input = r"""b; $$$triple; ?>""" expected = [ Assignment(Variable(Variable('$a')), Variable(Variable('$b')), False), Assignment(Variable(Variable('$a')), Variable(Variable('$b')), True), Assignment(Variable(Variable('$a')), Variable(Variable('$b')), False), Assignment(Variable(Variable('$a')), Variable(Variable('$b')), True), ObjectProperty(Variable(Variable('$a')), 'b'), Variable(Variable(Variable('$triple'))), ] eq_ast(input, expected) def test_classes(): input = r"""""" expected = [ Class('Clown', 'final', 'Unicycle', ['RedNose', 'FacePaint'], [ ClassConstants([ClassConstant('the', 'only'), ClassConstant('constant', 'is')]), ClassConstants([ClassConstant('change', 'chump')]), ClassVariables([], [ClassVariable('$iable', 999), ClassVariable('$nein', None)]), ClassVariables(['protected', 'static'], [ClassVariable('$x', None)]), Method('conjunction_junction', ['public'], [FormalParameter('$arg1', None, False, None), FormalParameter('$arg2', None, False, None)], [Return(BinaryOp('.', Variable('$arg1'), Variable('$arg2')))], False), ]), Class('Stub', None, None, [], []), ] eq_ast(input, expected) def test_new(): input = r"""""" expected = [ New('Foo', []), New('Foo', []), New('Bar', [Parameter(1, False), Parameter(2, False), Parameter(3, False)]), Assignment(Variable('$crusty'), New('OldSyntax', []), True), New('name\\Spaced', []), New('\\name\\Spaced', []), New('namespace\\D', []), ] eq_ast(input, expected) def test_exceptions(): input = r"""""" expected = [ Try([ Assignment(Variable('$a'), BinaryOp('+', Variable('$b'), Variable('$c')), False), Throw(New('Food', [Parameter(Variable('$a'), False)])), ], [ Catch('Food', Variable('$f'), [ Echo([BinaryOp('.', 'Received food: ', Variable('$f'))]) ]), Catch('\\Bar\\Food', Variable('$f'), [ Echo([BinaryOp('.', 'Received bar food: ', Variable('$f'))]) ]), Catch('namespace\\Food', Variable('$f'), [ Echo([BinaryOp('.', 'Received namespace food: ', Variable('$f'))]) ]), Catch('Exception', Variable('$e'), [ Echo(['Problem?']), ]), ]) ] eq_ast(input, expected) def test_declare(): input = r"""""" expected = [ Declare([Directive('ticks', 1)], Block([ Echo(['hi']), ])), Declare([Directive('ticks', 2)], None), Declare([Directive('ticks', 3)], Block([ Echo(['bye']), ])), ] eq_ast(input, expected) def test_instanceof(): input = r"""""" expected = [ If(BinaryOp('instanceof', Variable('$foo'), Constant('Bar')), Block([Echo(['$foo is a bar'])]), [], None), BinaryOp('instanceof', Variable('$foo'), Variable('$bar')), ] eq_ast(input, expected) def test_static_members(): input = r"""""" expected = [ StaticProperty('Ztatic', 'constant'), StaticProperty('Ztatic', Variable('$variable')), StaticMethodCall('Ztatic', 'method', []), StaticMethodCall('Ztatic', Variable('$variable_method'), []), StaticProperty('static', 'late_binding'), StaticProperty('static', Variable('$late_binding')), StaticMethodCall('static', 'late_binding', []), ] eq_ast(input, expected) def test_casts(): input = r"""""" expected = [ Cast('array', Variable('$x')), Cast('bool', Variable('$x')), Cast('bool', Variable('$x')), Cast('double', Variable('$x')), Cast('double', Variable('$x')), Cast('double', Variable('$x')), Cast('int', Variable('$x')), Cast('int', Variable('$x')), Cast('string', Variable('$x')), Cast('unset', Variable('$x')), ] eq_ast(input, expected) def test_namespaces(): input = r"""""" expected = [ Namespace('my\\name', []), Namespace('my\\name', [FunctionCall('foo', []), FunctionCall('bar', [])]), Namespace(None, [FunctionCall('foo', []), FunctionCall('bar', [])]), ] eq_ast(input, expected) def test_use_declarations(): input = r"""""" expected = [ UseDeclarations([UseDeclaration('me', None)]), UseDeclarations([UseDeclaration('\\me', None)]), UseDeclarations([UseDeclaration('\\me\\please', None)]), UseDeclarations([UseDeclaration('my\\name', 'foo')]), UseDeclarations([UseDeclaration('a', None), UseDeclaration('b', None)]), UseDeclarations([UseDeclaration('a', 'b'), UseDeclaration('\\c\\d\\e', 'f')]), ] eq_ast(input, expected) def test_constant_declarations(): input = r"""""" expected = [ ConstantDeclarations([ConstantDeclaration('foo', 42)]), ConstantDeclarations([ConstantDeclaration('bar', 'baz'), ConstantDeclaration('wat', Constant('\\DOO'))]), ConstantDeclarations([ConstantDeclaration('ant', Constant('namespace\\level'))]), ConstantDeclarations([ConstantDeclaration('dq1', '')]), ConstantDeclarations([ConstantDeclaration('dq2', 'nothing fancy')]), ] eq_ast(input, expected) def test_closures(): input = r"""""" expected = [ Assignment(Variable('$greet'), Closure([FormalParameter('$name', None, False, None)], [], [FunctionCall('printf', [Parameter('Hello %s\r\n', False), Parameter(Variable('$name'), False)])], False), False), FunctionCall(Variable('$greet'), [Parameter('World', False)]), Assignment(Variable('$cb'), Closure([FormalParameter('$a', None, False, None), FormalParameter('$b', None, True, None)], [LexicalVariable('$c', False), LexicalVariable('$d', True)], [], True), False), ] eq_ast(input, expected) def test_magic_constants(): input = r"""""" expected = [ Namespace('Shmamespace', []), Function('p', [FormalParameter('$x', None, False, None)], [ Echo([BinaryOp('.', BinaryOp('.', BinaryOp('.', MagicConstant('__FUNCTION__', 'Shmamespace\\p'), ': '), Variable('$x')), '\n')]) ], False), Class('Bar', None, None, [], [Method('__construct', [], [], [FunctionCall('p', [Parameter(MagicConstant('__LINE__', 10), False)]), FunctionCall('p', [Parameter(MagicConstant('__DIR__', '/my/dir'), False)]), FunctionCall('p', [Parameter(MagicConstant('__FILE__', '/my/dir/file.php'), False)]), FunctionCall('p', [Parameter(MagicConstant('__NAMESPACE__', 'Shmamespace'), False)]), FunctionCall('p', [Parameter(MagicConstant('__CLASS__', 'Shmamespace\\Bar'), False)]), FunctionCall('p', [Parameter(MagicConstant('__METHOD__', 'Shmamespace\\Bar::__construct'), False)])], False)]), New('Bar', []), ] eq_ast(input, expected, filename='/my/dir/file.php') def test_type_hinting(): input = r""""""; expected = [ Function('foo', [FormalParameter('$var1', None, False, 'Foo'), FormalParameter('$var2', 1, False, 'Bar'), FormalParameter('$var3', None, True, 'Quux'), FormalParameter('$var4', 1, True, 'Corge')], [], False)] eq_ast(input, expected) phply-0.9.1/tests/__init__.py0000664000175000017500000000000012331717437016344 0ustar pablopablo00000000000000phply-0.9.1/tests/test_lexer.py0000664000175000017500000002657012331717437017007 0ustar pablopablo00000000000000from phply import phplex import nose.tools import pprint def eq_tokens(input, expected, ignore=('WHITESPACE', 'OPEN_TAG', 'CLOSE_TAG')): output = [] lexer = phplex.full_lexer.clone() lexer.input(input) while True: tok = lexer.token() if not tok: break if tok.type in ignore: continue output.append((tok.type, tok.value)) print 'Lexer output:' pprint.pprint(output) print print 'Token by token:' for out, exp in zip(output, expected): print '\tgot:', out, '\texpected:', exp nose.tools.eq_(out, exp) assert len(output) == len(expected), \ 'output length was %d, expected %s' % (len(output), len(expected)) def test_whitespace(): input = ' \t\t ' expected = [ ('INLINE_HTML', ' '), ('OPEN_TAG', ''), ('INLINE_HTML', '\t\t '), ('OPEN_TAG', ''), ('INLINE_HTML', ' '), ('OPEN_TAG', ''), ] eq_tokens(input, expected, ignore=()) def test_open_close_tags(): input = ' <% %> <%= %>' expected = [ ('OPEN_TAG', ''), ('INLINE_HTML', ' '), ('OPEN_TAG', '<%'), ('WHITESPACE', ' '), ('CLOSE_TAG', '%>'), ('INLINE_HTML', ' '), ('OPEN_TAG', ''), ('INLINE_HTML', ' '), ('OPEN_TAG_WITH_ECHO', ''), ('INLINE_HTML', ' '), ('OPEN_TAG_WITH_ECHO', '<%='), ('WHITESPACE', ' '), ('CLOSE_TAG', '%>'), ] eq_tokens(input, expected, ignore=()) def test_numbers(): input = """""" expected = [ ('LNUMBER', '0'), ('LNUMBER', '12'), ('DNUMBER', '34.56'), ('DNUMBER', '7e8'), ('DNUMBER', '9.01e23'), ('DNUMBER', '4.5E+6'), ('DNUMBER', '.78e-9'), ('DNUMBER', '1.e+2'), ('DNUMBER', '34.'), ('DNUMBER', '.56'), ('LNUMBER', '0xdEcAfBaD'), ('LNUMBER', '0x123456789abcdef'), ('LNUMBER', '0666'), ] eq_tokens(input, expected) def test_strings(): input = r"""""" expected = [ ('CONSTANT_ENCAPSED_STRING', "''"), ('CONSTANT_ENCAPSED_STRING', "'hello'"), ('CONSTANT_ENCAPSED_STRING', "'what\\'s up'"), ('CONSTANT_ENCAPSED_STRING', "'newlines\n'"), ('QUOTE', '"'), ('QUOTE', '"'), ('QUOTE', '"'), ('ENCAPSED_AND_WHITESPACE', 'hello'), ('QUOTE', '"'), ('QUOTE', '"'), ('VARIABLE', '$world'), ('QUOTE', '"'), ('QUOTE', '"'), ('ENCAPSED_AND_WHITESPACE', 'hello '), ('VARIABLE', '$cruel'), ('ENCAPSED_AND_WHITESPACE', ' \\"world\\"'), ('QUOTE', '"'), ('QUOTE', '"'), ('ENCAPSED_AND_WHITESPACE', 'end$'), ('QUOTE', '"'), ('QUOTE', '"'), ('ENCAPSED_AND_WHITESPACE', 'newlines\n'), ('QUOTE', '"'), ] eq_tokens(input, expected) def test_string_backslash_escapes(): input = r"""""" expected = [ ('QUOTE', '"'), ('ENCAPSED_AND_WHITESPACE', "\n \\$escape\n \\{"), ('VARIABLE', "$escape"), ('ENCAPSED_AND_WHITESPACE', "}\n \\${escape}\n "), ('QUOTE', '"'), ] eq_tokens(input, expected) def test_string_offset_lookups(): input = r"""property $too->many->properties $adjacent->object$lookup stray -> [ ] not[array] non->object " ?>""" expected = [ ('QUOTE', '"'), ('ENCAPSED_AND_WHITESPACE', '\n '), ('VARIABLE', '$array'), ('LBRACKET', '['), ('STRING', 'offset'), ('RBRACKET', ']'), ('ENCAPSED_AND_WHITESPACE', '\n '), ('VARIABLE', '$too'), ('LBRACKET', '['), ('STRING', 'many'), ('RBRACKET', ']'), ('ENCAPSED_AND_WHITESPACE', '[offsets]\n '), ('VARIABLE', '$next'), ('LBRACKET', '['), ('STRING', 'to'), ('RBRACKET', ']'), ('VARIABLE', '$array'), ('ENCAPSED_AND_WHITESPACE', '\n '), ('DOLLAR_OPEN_CURLY_BRACES', '${'), ('STRING_VARNAME', 'curly'), ('LBRACKET', '['), ('CONSTANT_ENCAPSED_STRING', "'offset'"), ('RBRACKET', ']'), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', '\n '), ('VARIABLE', '$object'), ('OBJECT_OPERATOR', '->'), ('STRING', 'property'), ('ENCAPSED_AND_WHITESPACE', '\n '), ('VARIABLE', '$too'), ('OBJECT_OPERATOR', '->'), ('STRING', 'many'), ('ENCAPSED_AND_WHITESPACE', '->properties\n '), ('VARIABLE', '$adjacent'), ('OBJECT_OPERATOR', '->'), ('STRING', 'object'), ('VARIABLE', '$lookup'), ('ENCAPSED_AND_WHITESPACE', '\n stray -> [ ]\n not[array]\n non->object\n '), ('QUOTE', '"'), ] eq_tokens(input, expected) def test_string_curly_dollar_expressions(): input = r"""items[4]->five} {${$nasty}} {${funcall()}} {${$object->method()}} {$object->$variable} {$object->$variable[1]} {${static_class::variable}} {${static_class::$variable}} " ?>""" expected = [ ('QUOTE', '"'), ('ENCAPSED_AND_WHITESPACE', "\n a"), ('DOLLAR_OPEN_CURLY_BRACES', "${"), ('STRING_VARNAME', "dollar_curly"), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "b\n c"), ('CURLY_OPEN', "{"), ('VARIABLE', "$curly_dollar"), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "d\n e"), ('DOLLAR_OPEN_CURLY_BRACES', "${"), ('VARIABLE', "$dollar_curly_dollar"), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "f\n "), ('CURLY_OPEN', "{"), ('VARIABLE', "$array"), ('LBRACKET', '['), ('LNUMBER', "0"), ('RBRACKET', ']'), ('LBRACKET', '['), ('LNUMBER', "1"), ('RBRACKET', ']'), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "\n "), ('CURLY_OPEN', "{"), ('VARIABLE', "$array"), ('LBRACKET', '['), ('CONSTANT_ENCAPSED_STRING', "'two'"), ('RBRACKET', ']'), ('LBRACKET', '['), ('LNUMBER', "3"), ('RBRACKET', ']'), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "\n "), ('CURLY_OPEN', "{"), ('VARIABLE', "$object"), ('OBJECT_OPERATOR', "->"), ('STRING', "items"), ('LBRACKET', '['), ('LNUMBER', "4"), ('RBRACKET', ']'), ('OBJECT_OPERATOR', "->"), ('STRING', "five"), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "\n "), ('CURLY_OPEN', "{"), ('DOLLAR', '$'), ('LBRACE', '{'), ('VARIABLE', "$nasty"), ('RBRACE', '}'), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "\n "), ('CURLY_OPEN', "{"), ('DOLLAR', "$"), ('LBRACE', "{"), ('STRING', "funcall"), ('LPAREN', "("), ('RPAREN', ")"), ('RBRACE', '}'), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "\n "), ('CURLY_OPEN', "{"), ('DOLLAR', "$"), ('LBRACE', "{"), ('VARIABLE', "$object"), ('OBJECT_OPERATOR', "->"), ('STRING', "method"), ('LPAREN', "("), ('RPAREN', ")"), ('RBRACE', '}'), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "\n "), ('CURLY_OPEN', "{"), ('VARIABLE', "$object"), ('OBJECT_OPERATOR', "->"), ('VARIABLE', "$variable"), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "\n "), ('CURLY_OPEN', "{"), ('VARIABLE', "$object"), ('OBJECT_OPERATOR', "->"), ('VARIABLE', "$variable"), ('LBRACKET', '['), ('LNUMBER', "1"), ('RBRACKET', ']'), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "\n "), ('CURLY_OPEN', "{"), ('DOLLAR', "$"), ('LBRACE', "{"), ('STRING', "static_class"), ('DOUBLE_COLON', "::"), ('STRING', "variable"), ('RBRACE', '}'), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "\n "), ('CURLY_OPEN', "{"), ('DOLLAR', "$"), ('LBRACE', "{"), ('STRING', "static_class"), ('DOUBLE_COLON', "::"), ('VARIABLE', "$variable"), ('RBRACE', '}'), ('RBRACE', '}'), ('ENCAPSED_AND_WHITESPACE', "\n "), ('QUOTE', '"'), ] eq_tokens(input, expected) def test_heredoc(): input = r"""variables This is not the EOT; this is: EOT; ?>""" expected = [ ('OPEN_TAG', ''), ('STRING', 'variables'), ('ENCAPSED_AND_WHITESPACE', '\n'), ('ENCAPSED_AND_WHITESPACE', 'This'), ('ENCAPSED_AND_WHITESPACE', ' is not the EOT; this is:\n'), ('END_HEREDOC', 'EOT'), ('SEMI', ';'), ('WHITESPACE', '\n '), ('CLOSE_TAG', '?>'), ] eq_tokens(input, expected, ignore=()) def test_heredoc_backslash_newline(): input = r"""""" expected = [ ('OPEN_TAG', ''), ] eq_tokens(input, expected, ignore=()) def test_commented_close_tag(): input = '\n' expected = [ ('OPEN_TAG', '\n'), # PHP seems inconsistent regarding ('OPEN_TAG', ''), ] eq_tokens(input, expected, ignore=()) def test_punctuation(): input = '' expected = [ ('LPAREN', '('), ('LBRACKET', '['), ('LBRACE', '{'), ('RBRACE', '}'), ('RBRACKET', ']'), ('RPAREN', ')'), ('COLON', ':'), ('SEMI', ';'), ('COMMA', ','), ('CONCAT', '.'), ('AT', '@'), ] eq_tokens(input, expected) phply-0.9.1/LICENSE0000664000175000017500000000305512331717437014113 0ustar pablopablo00000000000000Copyright (c) 2010 by Dave Benjamin and contributors. See AUTHORS for more details. Some rights reserved. Redistribution and use in source and binary forms of the software as well as documentation, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. phply-0.9.1/README.md0000664000175000017500000000326012331717437014363 0ustar pablopablo00000000000000# phply phply is a parser for the PHP programming language written using PLY, a Lex/YACC-style parser generator toolkit for Python. ## Why? Good question. Because I'm crazy. Because it seemed possible. Things I'm interested in doing with it: * Converting PHP code to Python * Running PHP templates in a Python environment * Learning more about parsing "industrial" languages, warts and all ## What does it stand for? * phply -> PHP PLY * phply -> PHP Hypertext Preprocessor Python Lex YACC * phply -> PHP Hypertext Preprocessor Hypertext Preprocessor Python Lex Yet Another Compiler Compiler * (... to be completed ...) ## How do you pronounce it? If you're conservative, it's pronounced "pee aich ply". If you're liberal, it's "fiply". And if you're anarchist, pronounce it however you want. Who am I to tell you what to do? ## What's working? * Lexer matching the standard PHP lexer token-for-token * Parser and abstract syntax tree for most of the PHP grammar * Script to convert PHP source to JSON-based ASTs * Script to convert PHP source to Jinja2 source (experimental) ## What's not? Some things can't be parsed yet. They are getting fewer by the day, but there is still a fair amount of work to do: * Nowdocs * Backticks * Binary string literals and casts * Labels and goto * Some other stuff, probably ## Who's working on it? See the [AUTHORS](https://github.com/ramen/phply/blob/master/AUTHORS) file. ## How do I use it? * Lexer test: python phply/phplex.py * Parser test: python phply/phpparse.py * JSON dump: cd tools; python php2json.py < input.php > output.json * Jinja2 conversion: cd tools; python php2jinja.py < input.php > output.html * Fork me on GitHub and start hacking :) phply-0.9.1/.gitignore0000664000175000017500000000005412331717437015072 0ustar pablopablo00000000000000*.pyc parser.out parsetab.py phply.egg-info phply-0.9.1/PKG-INFO0000664000175000017500000000112712340747411014174 0ustar pablopablo00000000000000Metadata-Version: 1.1 Name: phply Version: 0.9.1 Summary: PHP lexer and parser in Python Home-page: http://www.github.com/ramen/phply Author: Ramen Author-email: UNKNOWN License: BSD Description: PHPLY -------------------- PHP lexer and parser in Python Platform: any Classifier: Development Status :: 2 - Pre-Alpha Classifier: Environment :: Console Classifier: Intended Audience :: Education Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python Classifier: Programming Language :: PHP Classifier: Operating System :: Unix phply-0.9.1/phply.egg-info/0000775000175000017500000000000012340747411015724 5ustar pablopablo00000000000000phply-0.9.1/phply.egg-info/SOURCES.txt0000664000175000017500000000100012340747411017577 0ustar pablopablo00000000000000.gitignore AUTHORS LICENSE README.md setup.py phply/__init__.py phply/phpast.py phply/phplex.py phply/phpparse.py phply/pythonast.py phply.egg-info/PKG-INFO phply.egg-info/SOURCES.txt phply.egg-info/dependency_links.txt phply.egg-info/namespace_packages.txt phply.egg-info/not-zip-safe phply.egg-info/requires.txt phply.egg-info/top_level.txt tests/__init__.py tests/test_lexer.py tests/test_parser.py tools/php2jinja.py tools/php2json.py tools/php2python.py tools/phpshell.py tools/tokenize.php tools/unparse.pyphply-0.9.1/phply.egg-info/not-zip-safe0000664000175000017500000000000112331720767020157 0ustar pablopablo00000000000000 phply-0.9.1/phply.egg-info/requires.txt0000664000175000017500000000000312340747411020315 0ustar pablopablo00000000000000plyphply-0.9.1/phply.egg-info/namespace_packages.txt0000664000175000017500000000000612340747411022253 0ustar pablopablo00000000000000phply phply-0.9.1/phply.egg-info/PKG-INFO0000664000175000017500000000112712340747411017022 0ustar pablopablo00000000000000Metadata-Version: 1.1 Name: phply Version: 0.9.1 Summary: PHP lexer and parser in Python Home-page: http://www.github.com/ramen/phply Author: Ramen Author-email: UNKNOWN License: BSD Description: PHPLY -------------------- PHP lexer and parser in Python Platform: any Classifier: Development Status :: 2 - Pre-Alpha Classifier: Environment :: Console Classifier: Intended Audience :: Education Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python Classifier: Programming Language :: PHP Classifier: Operating System :: Unix phply-0.9.1/phply.egg-info/dependency_links.txt0000664000175000017500000000000112340747411021772 0ustar pablopablo00000000000000 phply-0.9.1/phply.egg-info/top_level.txt0000664000175000017500000000001412340747411020451 0ustar pablopablo00000000000000phply tests phply-0.9.1/phply/0000775000175000017500000000000012340747411014232 5ustar pablopablo00000000000000phply-0.9.1/phply/__init__.py0000664000175000017500000000007012331717437016345 0ustar pablopablo00000000000000__import__('pkg_resources').declare_namespace(__name__) phply-0.9.1/phply/phplex.py0000664000175000017500000003137312331717437016120 0ustar pablopablo00000000000000# ---------------------------------------------------------------------- # 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: