2 # Copyright (C) Mako developers
4 # This module is part of Mako and is released under
5 # the MIT License: http://www.opensource.org/licenses/mit-license.php
7 """Handles parsing of Python code.
9 Parsing to AST is done via _ast on Python > 2.5, otherwise the compiler
13 from StringIO import StringIO
14 from mako import exceptions, util
16 # words that cannot be assigned to (notably smaller than the total keys in __builtins__)
17 reserved = util.Set(['True', 'False', 'None'])
21 util.restore__ast(_ast)
25 from compiler import parse as compiler_parse
26 from compiler import visitor
29 def parse(code, mode='exec', **exception_kwargs):
30 """Parse an expression into AST"""
33 return _ast_util.parse(code, '<unknown>', mode)
35 return compiler_parse(code, mode)
37 raise exceptions.SyntaxException("(%s) %s (%s)" % (e.__class__.__name__, str(e), repr(code[0:50])), **exception_kwargs)
41 class FindIdentifiers(_ast_util.NodeVisitor):
42 def __init__(self, listener, **exception_kwargs):
43 self.in_function = False
44 self.in_assign_targets = False
45 self.local_ident_stack = {}
46 self.listener = listener
47 self.exception_kwargs = exception_kwargs
48 def _add_declared(self, name):
49 if not self.in_function:
50 self.listener.declared_identifiers.add(name)
51 def visit_ClassDef(self, node):
52 self._add_declared(node.name)
53 def visit_Assign(self, node):
54 # flip around the visiting of Assign so the expression gets evaluated first,
55 # in the case of a clause like "x=x+5" (x is undeclared)
56 self.visit(node.value)
57 in_a = self.in_assign_targets
58 self.in_assign_targets = True
59 for n in node.targets:
61 self.in_assign_targets = in_a
62 def visit_FunctionDef(self, node):
63 self._add_declared(node.name)
64 # push function state onto stack. dont log any
65 # more identifiers as "declared" until outside of the function,
66 # but keep logging identifiers as "undeclared".
67 # track argument names in each function header so they arent counted as "undeclared"
69 inf = self.in_function
70 self.in_function = True
71 for arg in node.args.args:
72 if arg.id in self.local_ident_stack:
75 self.local_ident_stack[arg.id] = True
78 self.in_function = inf
79 for arg in node.args.args:
80 if arg.id not in saved:
81 del self.local_ident_stack[arg.id]
82 def visit_For(self, node):
85 self.visit(node.target)
86 for statement in node.body:
88 for statement in node.orelse:
90 def visit_Name(self, node):
91 if isinstance(node.ctx, _ast.Store):
92 self._add_declared(node.id)
93 if node.id not in reserved and node.id not in self.listener.declared_identifiers and node.id not in self.local_ident_stack:
94 self.listener.undeclared_identifiers.add(node.id)
95 def visit_Import(self, node):
96 for name in node.names:
97 if name.asname is not None:
98 self._add_declared(name.asname)
100 self._add_declared(name.name.split('.')[0])
101 def visit_ImportFrom(self, node):
102 for name in node.names:
103 if name.asname is not None:
104 self._add_declared(name.asname)
107 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)
108 self._add_declared(name.name)
110 class FindTuple(_ast_util.NodeVisitor):
111 def __init__(self, listener, code_factory, **exception_kwargs):
112 self.listener = listener
113 self.exception_kwargs = exception_kwargs
114 self.code_factory = code_factory
115 def visit_Tuple(self, node):
117 p = self.code_factory(n, **self.exception_kwargs)
118 self.listener.codeargs.append(p)
119 self.listener.args.append(ExpressionGenerator(n).value())
120 self.listener.declared_identifiers = self.listener.declared_identifiers.union(p.declared_identifiers)
121 self.listener.undeclared_identifiers = self.listener.undeclared_identifiers.union(p.undeclared_identifiers)
123 class ParseFunc(_ast_util.NodeVisitor):
124 def __init__(self, listener, **exception_kwargs):
125 self.listener = listener
126 self.exception_kwargs = exception_kwargs
127 def visit_FunctionDef(self, node):
128 self.listener.funcname = node.name
129 argnames = [arg.id for arg in node.args.args]
131 argnames.append(node.args.vararg)
133 argnames.append(node.args.kwarg)
134 self.listener.argnames = argnames
135 self.listener.defaults = node.args.defaults # ast
136 self.listener.varargs = node.args.vararg
137 self.listener.kwargs = node.args.kwarg
139 class ExpressionGenerator(object):
140 def __init__(self, astnode):
141 self.generator = _ast_util.SourceGenerator(' ' * 4)
142 self.generator.visit(astnode)
144 return ''.join(self.generator.result)
146 class FindIdentifiers(object):
147 def __init__(self, listener, **exception_kwargs):
148 self.in_function = False
149 self.local_ident_stack = {}
150 self.listener = listener
151 self.exception_kwargs = exception_kwargs
152 def _add_declared(self, name):
153 if not self.in_function:
154 self.listener.declared_identifiers.add(name)
155 def visitClass(self, node, *args):
156 self._add_declared(node.name)
157 def visitAssName(self, node, *args):
158 self._add_declared(node.name)
159 def visitAssign(self, node, *args):
160 # flip around the visiting of Assign so the expression gets evaluated first,
161 # in the case of a clause like "x=x+5" (x is undeclared)
162 self.visit(node.expr, *args)
165 def visitFunction(self,node, *args):
166 self._add_declared(node.name)
167 # push function state onto stack. dont log any
168 # more identifiers as "declared" until outside of the function,
169 # but keep logging identifiers as "undeclared".
170 # track argument names in each function header so they arent counted as "undeclared"
172 inf = self.in_function
173 self.in_function = True
174 for arg in node.argnames:
175 if arg in self.local_ident_stack:
178 self.local_ident_stack[arg] = True
179 for n in node.getChildNodes():
181 self.in_function = inf
182 for arg in node.argnames:
184 del self.local_ident_stack[arg]
185 def visitFor(self, node, *args):
187 self.visit(node.list, *args)
188 self.visit(node.assign, *args)
189 self.visit(node.body, *args)
190 def visitName(self, node, *args):
191 if node.name not in reserved and node.name not in self.listener.declared_identifiers and node.name not in self.local_ident_stack:
192 self.listener.undeclared_identifiers.add(node.name)
193 def visitImport(self, node, *args):
194 for (mod, alias) in node.names:
195 if alias is not None:
196 self._add_declared(alias)
198 self._add_declared(mod.split('.')[0])
199 def visitFrom(self, node, *args):
200 for (mod, alias) in node.names:
201 if alias is not None:
202 self._add_declared(alias)
205 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)
206 self._add_declared(mod)
207 def visit(self, expr):
208 visitor.walk(expr, self) #, walker=walker())
210 class FindTuple(object):
211 def __init__(self, listener, code_factory, **exception_kwargs):
212 self.listener = listener
213 self.exception_kwargs = exception_kwargs
214 self.code_factory = code_factory
215 def visitTuple(self, node, *args):
217 p = self.code_factory(n, **self.exception_kwargs)
218 self.listener.codeargs.append(p)
219 self.listener.args.append(ExpressionGenerator(n).value())
220 self.listener.declared_identifiers = self.listener.declared_identifiers.union(p.declared_identifiers)
221 self.listener.undeclared_identifiers = self.listener.undeclared_identifiers.union(p.undeclared_identifiers)
222 def visit(self, expr):
223 visitor.walk(expr, self) #, walker=walker())
225 class ParseFunc(object):
226 def __init__(self, listener, **exception_kwargs):
227 self.listener = listener
228 self.exception_kwargs = exception_kwargs
229 def visitFunction(self, node, *args):
230 self.listener.funcname = node.name
231 self.listener.argnames = node.argnames
232 self.listener.defaults = node.defaults
233 self.listener.varargs = node.varargs
234 self.listener.kwargs = node.kwargs
235 def visit(self, expr):
236 visitor.walk(expr, self)
238 class ExpressionGenerator(object):
239 """given an AST node, generates an equivalent literal Python expression."""
240 def __init__(self, astnode):
241 self.buf = StringIO()
242 visitor.walk(astnode, self) #, walker=walker())
244 return self.buf.getvalue()
245 def operator(self, op, node, *args):
247 self.visit(node.left, *args)
248 self.buf.write(" %s " % op)
249 self.visit(node.right, *args)
251 def booleanop(self, op, node, *args):
252 self.visit(node.nodes[0])
253 for n in node.nodes[1:]:
254 self.buf.write(" " + op + " ")
256 def visitConst(self, node, *args):
257 self.buf.write(repr(node.value))
258 def visitAssName(self, node, *args):
259 # TODO: figure out OP_ASSIGN, other OP_s
260 self.buf.write(node.name)
261 def visitName(self, node, *args):
262 self.buf.write(node.name)
263 def visitMul(self, node, *args):
264 self.operator("*", node, *args)
265 def visitAnd(self, node, *args):
266 self.booleanop("and", node, *args)
267 def visitOr(self, node, *args):
268 self.booleanop("or", node, *args)
269 def visitBitand(self, node, *args):
270 self.booleanop("&", node, *args)
271 def visitBitor(self, node, *args):
272 self.booleanop("|", node, *args)
273 def visitBitxor(self, node, *args):
274 self.booleanop("^", node, *args)
275 def visitAdd(self, node, *args):
276 self.operator("+", node, *args)
277 def visitGetattr(self, node, *args):
278 self.visit(node.expr, *args)
279 self.buf.write(".%s" % node.attrname)
280 def visitSub(self, node, *args):
281 self.operator("-", node, *args)
282 def visitNot(self, node, *args):
283 self.buf.write("not ")
284 self.visit(node.expr)
285 def visitDiv(self, node, *args):
286 self.operator("/", node, *args)
287 def visitFloorDiv(self, node, *args):
288 self.operator("//", node, *args)
289 def visitSubscript(self, node, *args):
290 self.visit(node.expr)
292 [self.visit(x) for x in node.subs]
294 def visitUnarySub(self, node, *args):
296 self.visit(node.expr)
297 def visitUnaryAdd(self, node, *args):
299 self.visit(node.expr)
300 def visitSlice(self, node, *args):
301 self.visit(node.expr)
303 if node.lower is not None:
304 self.visit(node.lower)
306 if node.upper is not None:
307 self.visit(node.upper)
309 def visitDict(self, node):
311 c = node.getChildren()
312 for i in range(0, len(c), 2):
319 def visitTuple(self, node):
321 c = node.getChildren()
322 for i in range(0, len(c)):
327 def visitList(self, node):
329 c = node.getChildren()
330 for i in range(0, len(c)):
335 def visitListComp(self, node):
337 self.visit(node.expr)
342 def visitListCompFor(self, node):
343 self.buf.write(" for ")
344 self.visit(node.assign)
345 self.buf.write(" in ")
346 self.visit(node.list)
349 def visitListCompIf(self, node):
350 self.buf.write(" if ")
351 self.visit(node.test)
352 def visitCompare(self, node):
353 self.visit(node.expr)
355 self.buf.write(tup[0])
357 def visitCallFunc(self, node, *args):
358 self.visit(node.node)
361 self.visit(node.args[0])
362 for a in node.args[1:]:
367 class walker(visitor.ASTVisitor):
368 def dispatch(self, node, *args):
369 print "Node:", str(node)
370 #print "dir:", dir(node)
371 return visitor.ASTVisitor.dispatch(self, node, *args)