aboutsummaryrefslogtreecommitdiffstats
path: root/lib/mako/pyparser.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mako/pyparser.py')
-rw-r--r--lib/mako/pyparser.py369
1 files changed, 369 insertions, 0 deletions
diff --git a/lib/mako/pyparser.py b/lib/mako/pyparser.py
new file mode 100644
index 0000000..bd20c67
--- /dev/null
+++ b/lib/mako/pyparser.py
@@ -0,0 +1,369 @@
+# ast.py
+# Copyright (C) Mako developers
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""Handles parsing of Python code.
+
+Parsing to AST is done via _ast on Python > 2.5, otherwise the compiler
+module is used.
+"""
+import sys
+from StringIO import StringIO
+from mako import exceptions, util
+
+new_ast = sys.version_info > (2, 5)
+
+if new_ast:
+ import _ast
+ util.restore__ast(_ast)
+ import _ast_util
+else:
+ from compiler import parse as compiler_parse
+ from compiler import visitor
+
+
+def parse(code, mode='exec', **exception_kwargs):
+ """Parse an expression into AST"""
+ try:
+ if new_ast:
+ return _ast_util.parse(code, '<unknown>', mode)
+ else:
+ return compiler_parse(code, mode)
+ except Exception, e:
+ raise exceptions.SyntaxException("(%s) %s (%s)" % (e.__class__.__name__, str(e), repr(code[0:50])), **exception_kwargs)
+
+
+if new_ast:
+ class FindIdentifiers(_ast_util.NodeVisitor):
+ def __init__(self, listener, **exception_kwargs):
+ self.in_function = False
+ self.in_assign_targets = False
+ self.local_ident_stack = {}
+ self.listener = listener
+ self.exception_kwargs = exception_kwargs
+ def _add_declared(self, name):
+ if not self.in_function:
+ self.listener.declared_identifiers.add(name)
+ def visit_ClassDef(self, node):
+ self._add_declared(node.name)
+ def visit_Assign(self, node):
+ # flip around the visiting of Assign so the expression gets evaluated first,
+ # in the case of a clause like "x=x+5" (x is undeclared)
+ self.visit(node.value)
+ in_a = self.in_assign_targets
+ self.in_assign_targets = True
+ for n in node.targets:
+ self.visit(n)
+ self.in_assign_targets = in_a
+ def visit_FunctionDef(self, node):
+ self._add_declared(node.name)
+ # push function state onto stack. dont log any
+ # more identifiers as "declared" until outside of the function,
+ # but keep logging identifiers as "undeclared".
+ # track argument names in each function header so they arent counted as "undeclared"
+ saved = {}
+ inf = self.in_function
+ self.in_function = True
+ for arg in node.args.args:
+ if arg.id in self.local_ident_stack:
+ saved[arg.id] = True
+ else:
+ self.local_ident_stack[arg.id] = True
+ for n in node.body:
+ self.visit(n)
+ self.in_function = inf
+ for arg in node.args.args:
+ if arg.id not in saved:
+ del self.local_ident_stack[arg.id]
+ def visit_For(self, node):
+ # flip around visit
+ self.visit(node.iter)
+ self.visit(node.target)
+ for statement in node.body:
+ self.visit(statement)
+ for statement in node.orelse:
+ self.visit(statement)
+ def visit_Name(self, node):
+ if isinstance(node.ctx, _ast.Store):
+ self._add_declared(node.id)
+ if node.id not in __builtins__ and node.id not in self.listener.declared_identifiers and node.id not in self.local_ident_stack:
+ self.listener.undeclared_identifiers.add(node.id)
+ def visit_Import(self, node):
+ for name in node.names:
+ if name.asname is not None:
+ self._add_declared(name.asname)
+ else:
+ self._add_declared(name.name.split('.')[0])
+ def visit_ImportFrom(self, node):
+ for name in node.names:
+ if name.asname is not None:
+ self._add_declared(name.asname)
+ else:
+ if name.name == '*':
+ raise exceptions.CompileException("'import *' is not supported, since all identifier names must be explicitly declared. Please use the form 'from <modulename> import <name1>, <name2>, ...' instead.", **self.exception_kwargs)
+ self._add_declared(name.name)
+
+ class FindTuple(_ast_util.NodeVisitor):
+ def __init__(self, listener, code_factory, **exception_kwargs):
+ self.listener = listener
+ self.exception_kwargs = exception_kwargs
+ self.code_factory = code_factory
+ def visit_Tuple(self, node):
+ for n in node.elts:
+ p = self.code_factory(n, **self.exception_kwargs)
+ self.listener.codeargs.append(p)
+ self.listener.args.append(ExpressionGenerator(n).value())
+ self.listener.declared_identifiers = self.listener.declared_identifiers.union(p.declared_identifiers)
+ self.listener.undeclared_identifiers = self.listener.undeclared_identifiers.union(p.undeclared_identifiers)
+
+ class ParseFunc(_ast_util.NodeVisitor):
+ def __init__(self, listener, **exception_kwargs):
+ self.listener = listener
+ self.exception_kwargs = exception_kwargs
+ def visit_FunctionDef(self, node):
+ self.listener.funcname = node.name
+ argnames = [arg.id for arg in node.args.args]
+ if node.args.vararg:
+ argnames.append(node.args.vararg)
+ if node.args.kwarg:
+ argnames.append(node.args.kwarg)
+ self.listener.argnames = argnames
+ self.listener.defaults = node.args.defaults # ast
+ self.listener.varargs = node.args.vararg
+ self.listener.kwargs = node.args.kwarg
+
+ class ExpressionGenerator(object):
+ def __init__(self, astnode):
+ self.generator = _ast_util.SourceGenerator(' ' * 4)
+ self.generator.visit(astnode)
+ def value(self):
+ return ''.join(self.generator.result)
+else:
+ class FindIdentifiers(object):
+ def __init__(self, listener, **exception_kwargs):
+ self.in_function = False
+ self.local_ident_stack = {}
+ self.listener = listener
+ self.exception_kwargs = exception_kwargs
+ def _add_declared(self, name):
+ if not self.in_function:
+ self.listener.declared_identifiers.add(name)
+ def visitClass(self, node, *args):
+ self._add_declared(node.name)
+ def visitAssName(self, node, *args):
+ self._add_declared(node.name)
+ def visitAssign(self, node, *args):
+ # flip around the visiting of Assign so the expression gets evaluated first,
+ # in the case of a clause like "x=x+5" (x is undeclared)
+ self.visit(node.expr, *args)
+ for n in node.nodes:
+ self.visit(n, *args)
+ def visitFunction(self,node, *args):
+ self._add_declared(node.name)
+ # push function state onto stack. dont log any
+ # more identifiers as "declared" until outside of the function,
+ # but keep logging identifiers as "undeclared".
+ # track argument names in each function header so they arent counted as "undeclared"
+ saved = {}
+ inf = self.in_function
+ self.in_function = True
+ for arg in node.argnames:
+ if arg in self.local_ident_stack:
+ saved[arg] = True
+ else:
+ self.local_ident_stack[arg] = True
+ for n in node.getChildNodes():
+ self.visit(n, *args)
+ self.in_function = inf
+ for arg in node.argnames:
+ if arg not in saved:
+ del self.local_ident_stack[arg]
+ def visitFor(self, node, *args):
+ # flip around visit
+ self.visit(node.list, *args)
+ self.visit(node.assign, *args)
+ self.visit(node.body, *args)
+ def visitName(self, node, *args):
+ if node.name not in __builtins__ and node.name not in self.listener.declared_identifiers and node.name not in self.local_ident_stack:
+ self.listener.undeclared_identifiers.add(node.name)
+ def visitImport(self, node, *args):
+ for (mod, alias) in node.names:
+ if alias is not None:
+ self._add_declared(alias)
+ else:
+ self._add_declared(mod.split('.')[0])
+ def visitFrom(self, node, *args):
+ for (mod, alias) in node.names:
+ if alias is not None:
+ self._add_declared(alias)
+ else:
+ if mod == '*':
+ raise exceptions.CompileException("'import *' is not supported, since all identifier names must be explicitly declared. Please use the form 'from <modulename> import <name1>, <name2>, ...' instead.", **self.exception_kwargs)
+ self._add_declared(mod)
+ def visit(self, expr):
+ visitor.walk(expr, self) #, walker=walker())
+
+ class FindTuple(object):
+ def __init__(self, listener, code_factory, **exception_kwargs):
+ self.listener = listener
+ self.exception_kwargs = exception_kwargs
+ self.code_factory = code_factory
+ def visitTuple(self, node, *args):
+ for n in node.nodes:
+ p = self.code_factory(n, **self.exception_kwargs)
+ self.listener.codeargs.append(p)
+ self.listener.args.append(ExpressionGenerator(n).value())
+ self.listener.declared_identifiers = self.listener.declared_identifiers.union(p.declared_identifiers)
+ self.listener.undeclared_identifiers = self.listener.undeclared_identifiers.union(p.undeclared_identifiers)
+ def visit(self, expr):
+ visitor.walk(expr, self) #, walker=walker())
+
+ class ParseFunc(object):
+ def __init__(self, listener, **exception_kwargs):
+ self.listener = listener
+ self.exception_kwargs = exception_kwargs
+ def visitFunction(self, node, *args):
+ self.listener.funcname = node.name
+ self.listener.argnames = node.argnames
+ self.listener.defaults = node.defaults
+ self.listener.varargs = node.varargs
+ self.listener.kwargs = node.kwargs
+ def visit(self, expr):
+ visitor.walk(expr, self)
+
+ class ExpressionGenerator(object):
+ """given an AST node, generates an equivalent literal Python expression."""
+ def __init__(self, astnode):
+ self.buf = StringIO()
+ visitor.walk(astnode, self) #, walker=walker())
+ def value(self):
+ return self.buf.getvalue()
+ def operator(self, op, node, *args):
+ self.buf.write("(")
+ self.visit(node.left, *args)
+ self.buf.write(" %s " % op)
+ self.visit(node.right, *args)
+ self.buf.write(")")
+ def booleanop(self, op, node, *args):
+ self.visit(node.nodes[0])
+ for n in node.nodes[1:]:
+ self.buf.write(" " + op + " ")
+ self.visit(n, *args)
+ def visitConst(self, node, *args):
+ self.buf.write(repr(node.value))
+ def visitAssName(self, node, *args):
+ # TODO: figure out OP_ASSIGN, other OP_s
+ self.buf.write(node.name)
+ def visitName(self, node, *args):
+ self.buf.write(node.name)
+ def visitMul(self, node, *args):
+ self.operator("*", node, *args)
+ def visitAnd(self, node, *args):
+ self.booleanop("and", node, *args)
+ def visitOr(self, node, *args):
+ self.booleanop("or", node, *args)
+ def visitBitand(self, node, *args):
+ self.booleanop("&", node, *args)
+ def visitBitor(self, node, *args):
+ self.booleanop("|", node, *args)
+ def visitBitxor(self, node, *args):
+ self.booleanop("^", node, *args)
+ def visitAdd(self, node, *args):
+ self.operator("+", node, *args)
+ def visitGetattr(self, node, *args):
+ self.visit(node.expr, *args)
+ self.buf.write(".%s" % node.attrname)
+ def visitSub(self, node, *args):
+ self.operator("-", node, *args)
+ def visitNot(self, node, *args):
+ self.buf.write("not ")
+ self.visit(node.expr)
+ def visitDiv(self, node, *args):
+ self.operator("/", node, *args)
+ def visitFloorDiv(self, node, *args):
+ self.operator("//", node, *args)
+ def visitSubscript(self, node, *args):
+ self.visit(node.expr)
+ self.buf.write("[")
+ [self.visit(x) for x in node.subs]
+ self.buf.write("]")
+ def visitUnarySub(self, node, *args):
+ self.buf.write("-")
+ self.visit(node.expr)
+ def visitUnaryAdd(self, node, *args):
+ self.buf.write("-")
+ self.visit(node.expr)
+ def visitSlice(self, node, *args):
+ self.visit(node.expr)
+ self.buf.write("[")
+ if node.lower is not None:
+ self.visit(node.lower)
+ self.buf.write(":")
+ if node.upper is not None:
+ self.visit(node.upper)
+ self.buf.write("]")
+ def visitDict(self, node):
+ self.buf.write("{")
+ c = node.getChildren()
+ for i in range(0, len(c), 2):
+ self.visit(c[i])
+ self.buf.write(": ")
+ self.visit(c[i+1])
+ if i<len(c) -2:
+ self.buf.write(", ")
+ self.buf.write("}")
+ def visitTuple(self, node):
+ self.buf.write("(")
+ c = node.getChildren()
+ for i in range(0, len(c)):
+ self.visit(c[i])
+ if i<len(c) - 1:
+ self.buf.write(", ")
+ self.buf.write(")")
+ def visitList(self, node):
+ self.buf.write("[")
+ c = node.getChildren()
+ for i in range(0, len(c)):
+ self.visit(c[i])
+ if i<len(c) - 1:
+ self.buf.write(", ")
+ self.buf.write("]")
+ def visitListComp(self, node):
+ self.buf.write("[")
+ self.visit(node.expr)
+ self.buf.write(" ")
+ for n in node.quals:
+ self.visit(n)
+ self.buf.write("]")
+ def visitListCompFor(self, node):
+ self.buf.write(" for ")
+ self.visit(node.assign)
+ self.buf.write(" in ")
+ self.visit(node.list)
+ for n in node.ifs:
+ self.visit(n)
+ def visitListCompIf(self, node):
+ self.buf.write(" if ")
+ self.visit(node.test)
+ def visitCompare(self, node):
+ self.visit(node.expr)
+ for tup in node.ops:
+ self.buf.write(tup[0])
+ self.visit(tup[1])
+ def visitCallFunc(self, node, *args):
+ self.visit(node.node)
+ self.buf.write("(")
+ if len(node.args):
+ self.visit(node.args[0])
+ for a in node.args[1:]:
+ self.buf.write(", ")
+ self.visit(a)
+ self.buf.write(")")
+
+ class walker(visitor.ASTVisitor):
+ def dispatch(self, node, *args):
+ print "Node:", str(node)
+ #print "dir:", dir(node)
+ return visitor.ASTVisitor.dispatch(self, node, *args)