~ubuntu-branches/ubuntu/hardy/mako/hardy-backports

« back to all changes in this revision

Viewing changes to lib/mako/ast.py

  • Committer: Bazaar Package Importer
  • Author(s): Scott Kitterman
  • Date: 2008-09-09 02:09:15 UTC
  • mfrom: (5.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080909020915-y0zgvojsponlnwx8
Tags: 0.2.2-1~hardy1
Automated backport upload; no source changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# ast.py
2
 
# Copyright (C) 2006, 2007 Michael Bayer mike_mp@zzzcomputing.com
 
2
# Copyright (C) 2006, 2007, 2008 Michael Bayer mike_mp@zzzcomputing.com
3
3
#
4
4
# This module is part of Mako and is released under
5
5
# the MIT License: http://www.opensource.org/licenses/mit-license.php
6
6
 
7
7
"""utilities for analyzing expressions and blocks of Python code, as well as generating Python from AST nodes"""
8
8
 
9
 
from compiler import ast, visitor
10
 
from compiler import parse as compiler_parse
11
 
from mako import util, exceptions
12
 
from StringIO import StringIO
 
9
from mako import exceptions, pyparser, util
13
10
import re
14
11
 
15
 
def parse(code, mode, **exception_kwargs):
16
 
    try:
17
 
        return compiler_parse(code, mode)
18
 
    except SyntaxError, e:
19
 
        raise exceptions.SyntaxException("(%s) %s (%s)" % (e.__class__.__name__, str(e), repr(code[0:50])), **exception_kwargs)
20
 
    
21
12
class PythonCode(object):
22
13
    """represents information about a string containing Python code"""
23
14
    def __init__(self, code, **exception_kwargs):
36
27
        # - AST is less likely to break with version changes (for example, the behavior of co_names changed a little bit
37
28
        # in python version 2.5)
38
29
        if isinstance(code, basestring):
39
 
            expr = parse(code.lstrip(), "exec", **exception_kwargs)
 
30
            expr = pyparser.parse(code.lstrip(), "exec", **exception_kwargs)
40
31
        else:
41
32
            expr = code
42
 
        
43
 
        class FindIdentifiers(object):
44
 
            def __init__(self):
45
 
                self.in_function = False
46
 
                self.local_ident_stack = {}
47
 
            def _add_declared(s, name):
48
 
                if not s.in_function:
49
 
                    self.declared_identifiers.add(name)
50
 
            def visitClass(self, node, *args):
51
 
                self._add_declared(node.name)
52
 
            def visitAssName(self, node, *args):
53
 
                self._add_declared(node.name)
54
 
            def visitAssign(self, node, *args):
55
 
                # flip around the visiting of Assign so the expression gets evaluated first, 
56
 
                # in the case of a clause like "x=x+5" (x is undeclared)
57
 
                self.visit(node.expr, *args)
58
 
                for n in node.nodes:
59
 
                    self.visit(n, *args)
60
 
            def visitFunction(self,node, *args):
61
 
                self._add_declared(node.name)
62
 
                # push function state onto stack.  dont log any
63
 
                # more identifiers as "declared" until outside of the function,
64
 
                # but keep logging identifiers as "undeclared".
65
 
                # track argument names in each function header so they arent counted as "undeclared"
66
 
                saved = {}
67
 
                inf = self.in_function
68
 
                self.in_function = True
69
 
                for arg in node.argnames:
70
 
                    if arg in self.local_ident_stack:
71
 
                        saved[arg] = True
72
 
                    else:
73
 
                        self.local_ident_stack[arg] = True
74
 
                for n in node.getChildNodes():
75
 
                    self.visit(n, *args)
76
 
                self.in_function = inf
77
 
                for arg in node.argnames:
78
 
                    if arg not in saved:
79
 
                        del self.local_ident_stack[arg]
80
 
            def visitFor(self, node, *args):
81
 
                # flip around visit
82
 
                self.visit(node.list, *args)
83
 
                self.visit(node.assign, *args)
84
 
                self.visit(node.body, *args)
85
 
            def visitName(s, node, *args):
86
 
                if node.name not in __builtins__ and node.name not in self.declared_identifiers and node.name not in s.local_ident_stack:
87
 
                    self.undeclared_identifiers.add(node.name)
88
 
            def visitImport(self, node, *args):
89
 
                for (mod, alias) in node.names:
90
 
                    if alias is not None:
91
 
                        self._add_declared(alias)
92
 
                    else:
93
 
                        self._add_declared(mod.split('.')[0])
94
 
            def visitFrom(self, node, *args):
95
 
                for (mod, alias) in node.names:
96
 
                    if alias is not None:
97
 
                        self._add_declared(alias)
98
 
                    else:
99
 
                        if mod == '*':
100
 
                            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.", **exception_kwargs)
101
 
                        self._add_declared(mod)
102
 
        f = FindIdentifiers()
103
 
        visitor.walk(expr, f) #, walker=walker())
 
33
 
 
34
        f = pyparser.FindIdentifiers(self, **exception_kwargs)
 
35
        f.visit(expr)
104
36
 
105
37
class ArgumentList(object):
106
38
    """parses a fragment of code as a comma-separated list of expressions"""
109
41
        self.args = []
110
42
        self.declared_identifiers = util.Set()
111
43
        self.undeclared_identifiers = util.Set()
112
 
        class FindTuple(object):
113
 
            def visitTuple(s, node, *args):
114
 
                for n in node.nodes:
115
 
                    p = PythonCode(n, **exception_kwargs)
116
 
                    self.codeargs.append(p)
117
 
                    self.args.append(ExpressionGenerator(n).value())
118
 
                    self.declared_identifiers = self.declared_identifiers.union(p.declared_identifiers)
119
 
                    self.undeclared_identifiers = self.undeclared_identifiers.union(p.undeclared_identifiers)
120
44
        if isinstance(code, basestring):
121
45
            if re.match(r"\S", code) and not re.match(r",\s*$", code):
122
46
                # if theres text and no trailing comma, insure its parsed
123
47
                # as a tuple by adding a trailing comma
124
48
                code  += ","
125
 
            expr = parse(code, "exec", **exception_kwargs)
 
49
            expr = pyparser.parse(code, "exec", **exception_kwargs)
126
50
        else:
127
51
            expr = code
128
52
 
129
 
        f = FindTuple()
130
 
        visitor.walk(expr, f)
 
53
        f = pyparser.FindTuple(self, PythonCode, **exception_kwargs)
 
54
        f.visit(expr)
131
55
        
132
56
class PythonFragment(PythonCode):
133
57
    """extends PythonCode to provide identifier lookups in partial control statements
157
81
            raise exceptions.CompileException("Unsupported control keyword: '%s'" % keyword, **exception_kwargs)
158
82
        super(PythonFragment, self).__init__(code, **exception_kwargs)
159
83
        
160
 
class walker(visitor.ASTVisitor):
161
 
    def dispatch(self, node, *args):
162
 
        print "Node:", str(node)
163
 
        #print "dir:", dir(node)
164
 
        return visitor.ASTVisitor.dispatch(self, node, *args)
165
84
        
166
85
class FunctionDecl(object):
167
86
    """function declaration"""
168
87
    def __init__(self, code, allow_kwargs=True, **exception_kwargs):
169
88
        self.code = code
170
 
        expr = parse(code, "exec", **exception_kwargs)
171
 
        class ParseFunc(object):
172
 
            def visitFunction(s, node, *args):
173
 
                self.funcname = node.name
174
 
                self.argnames = node.argnames
175
 
                self.defaults = node.defaults
176
 
                self.varargs = node.varargs
177
 
                self.kwargs = node.kwargs
 
89
        expr = pyparser.parse(code, "exec", **exception_kwargs)
178
90
                
179
 
        f = ParseFunc()
180
 
        visitor.walk(expr, f)
 
91
        f = pyparser.ParseFunc(self, **exception_kwargs)
 
92
        f.visit(expr)
181
93
        if not hasattr(self, 'funcname'):
182
94
            raise exceptions.CompileException("Code '%s' is not a function declaration" % code, **exception_kwargs)
183
95
        if not allow_kwargs and self.kwargs:
202
114
            else:
203
115
                default = len(defaults) and defaults.pop() or None
204
116
            if include_defaults and default:
205
 
                namedecls.insert(0, "%s=%s" % (arg, ExpressionGenerator(default).value()))
 
117
                namedecls.insert(0, "%s=%s" % (arg, pyparser.ExpressionGenerator(default).value()))
206
118
            else:
207
119
                namedecls.insert(0, arg)
208
120
        return namedecls
211
123
    """the argument portion of a function declaration"""
212
124
    def __init__(self, code, **kwargs):
213
125
        super(FunctionArgs, self).__init__("def ANON(%s):pass" % code, **kwargs)
214
 
        
215
 
            
216
 
class ExpressionGenerator(object):
217
 
    """given an AST node, generates an equivalent literal Python expression."""
218
 
    def __init__(self, astnode):
219
 
        self.buf = StringIO()
220
 
        visitor.walk(astnode, self) #, walker=walker())
221
 
    def value(self):
222
 
        return self.buf.getvalue()        
223
 
    def operator(self, op, node, *args):
224
 
        self.buf.write("(")
225
 
        self.visit(node.left, *args)
226
 
        self.buf.write(" %s " % op)
227
 
        self.visit(node.right, *args)
228
 
        self.buf.write(")")
229
 
    def booleanop(self, op, node, *args):
230
 
        self.visit(node.nodes[0])
231
 
        for n in node.nodes[1:]:
232
 
            self.buf.write(" " + op + " ")
233
 
            self.visit(n, *args)
234
 
    def visitConst(self, node, *args):
235
 
        self.buf.write(repr(node.value))
236
 
    def visitAssName(self, node, *args):
237
 
        # TODO: figure out OP_ASSIGN, other OP_s
238
 
        self.buf.write(node.name)
239
 
    def visitName(self, node, *args):
240
 
        self.buf.write(node.name)
241
 
    def visitMul(self, node, *args):
242
 
        self.operator("*", node, *args)
243
 
    def visitAnd(self, node, *args):
244
 
        self.booleanop("and", node, *args)
245
 
    def visitOr(self, node, *args):
246
 
        self.booleanop("or", node, *args)
247
 
    def visitBitand(self, node, *args):
248
 
        self.booleanop("&", node, *args)
249
 
    def visitBitor(self, node, *args):
250
 
        self.booleanop("|", node, *args)
251
 
    def visitBitxor(self, node, *args):
252
 
        self.booleanop("^", node, *args)
253
 
    def visitAdd(self, node, *args):
254
 
        self.operator("+", node, *args)
255
 
    def visitGetattr(self, node, *args):
256
 
        self.visit(node.expr, *args)
257
 
        self.buf.write(".%s" % node.attrname)
258
 
    def visitSub(self, node, *args):
259
 
        self.operator("-", node, *args)
260
 
    def visitNot(self, node, *args):
261
 
        self.buf.write("not ")
262
 
        self.visit(node.expr)
263
 
    def visitDiv(self, node, *args):
264
 
        self.operator("/", node, *args)
265
 
    def visitFloorDiv(self, node, *args):
266
 
        self.operator("//", node, *args)
267
 
    def visitSubscript(self, node, *args):
268
 
        self.visit(node.expr)
269
 
        self.buf.write("[")
270
 
        [self.visit(x) for x in node.subs]
271
 
        self.buf.write("]")
272
 
    def visitUnarySub(self, node, *args):
273
 
        self.buf.write("-")
274
 
        self.visit(node.expr)
275
 
    def visitUnaryAdd(self, node, *args):
276
 
        self.buf.write("-")
277
 
        self.visit(node.expr)
278
 
    def visitSlice(self, node, *args):
279
 
        self.visit(node.expr)
280
 
        self.buf.write("[")
281
 
        if node.lower is not None:
282
 
            self.visit(node.lower)
283
 
        self.buf.write(":")
284
 
        if node.upper is not None:
285
 
            self.visit(node.upper)
286
 
        self.buf.write("]")
287
 
    def visitDict(self, node):
288
 
        self.buf.write("{")
289
 
        c = node.getChildren()
290
 
        for i in range(0, len(c), 2):
291
 
            self.visit(c[i])
292
 
            self.buf.write(": ")
293
 
            self.visit(c[i+1])
294
 
            if i<len(c) -2:
295
 
                self.buf.write(", ")
296
 
        self.buf.write("}")
297
 
    def visitTuple(self, node):
298
 
        self.buf.write("(")
299
 
        c = node.getChildren()
300
 
        for i in range(0, len(c)):
301
 
            self.visit(c[i])
302
 
            if i<len(c) - 1:
303
 
                self.buf.write(", ")
304
 
        self.buf.write(")")
305
 
    def visitList(self, node):
306
 
        self.buf.write("[")
307
 
        c = node.getChildren()
308
 
        for i in range(0, len(c)):
309
 
            self.visit(c[i])
310
 
            if i<len(c) - 1:
311
 
                self.buf.write(", ")
312
 
        self.buf.write("]")
313
 
    def visitListComp(self, node):
314
 
        self.buf.write("[")
315
 
        self.visit(node.expr)
316
 
        self.buf.write(" ")
317
 
        for n in node.quals:
318
 
            self.visit(n)
319
 
        self.buf.write("]")
320
 
    def visitListCompFor(self, node):
321
 
        self.buf.write(" for ")
322
 
        self.visit(node.assign)
323
 
        self.buf.write(" in ")
324
 
        self.visit(node.list)
325
 
        for n in node.ifs:
326
 
            self.visit(n)
327
 
    def visitListCompIf(self, node):
328
 
        self.buf.write(" if ")
329
 
        self.visit(node.test)
330
 
    def visitCompare(self, node):
331
 
        self.visit(node.expr)
332
 
        for tup in node.ops:
333
 
            self.buf.write(tup[0])
334
 
            self.visit(tup[1])
335
 
    def visitCallFunc(self, node, *args):
336
 
        self.visit(node.node)
337
 
        self.buf.write("(")
338
 
        if len(node.args):
339
 
            self.visit(node.args[0])
340
 
            for a in node.args[1:]:
341
 
                self.buf.write(", ")
342
 
                self.visit(a)
343
 
        self.buf.write(")")
344
 
        
345
 
        
 
 
b'\\ No newline at end of file'