Edgewall Software

source: branches/stable/0.6.x/genshi/template/astutil.py

Last change on this file was 1120, checked in by cmlenz, 13 years ago

Update changelog and copyright years.

  • Property svn:eol-style set to native
File size: 23.1 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2008-2010 Edgewall Software
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution. The terms
8# are also available at http://genshi.edgewall.org/wiki/License.
9#
10# This software consists of voluntary contributions made by many
11# individuals. For the exact contribution history, see the revision
12# history and logs, available at http://genshi.edgewall.org/log/.
13
14"""Support classes for generating code from abstract syntax trees."""
15
16try:
17    import _ast
18except ImportError:
19    from genshi.template.ast24 import _ast, parse
20else:
21    def parse(source, mode):
22        return compile(source, '', mode, _ast.PyCF_ONLY_AST)
23
24
25__docformat__ = 'restructuredtext en'
26
27
28class ASTCodeGenerator(object):
29    """General purpose base class for AST transformations.
30
31    Every visitor method can be overridden to return an AST node that has been
32    altered or replaced in some way.
33    """
34    def __init__(self, tree):
35        self.lines_info = []
36        self.line_info = None
37        self.code = ''
38        self.line = None
39        self.last = None
40        self.indent = 0
41        self.blame_stack = []
42        self.visit(tree)
43        if self.line.strip():
44            self.code += self.line + '\n'
45            self.lines_info.append(self.line_info)
46        self.line = None
47        self.line_info = None
48
49    def _change_indent(self, delta):
50        self.indent += delta
51
52    def _new_line(self):
53        if self.line is not None:
54            self.code += self.line + '\n'
55            self.lines_info.append(self.line_info)
56        self.line = ' '*4*self.indent
57        if len(self.blame_stack) == 0:
58            self.line_info = []
59            self.last = None
60        else:
61            self.line_info = [(0, self.blame_stack[-1],)]
62            self.last = self.blame_stack[-1]
63
64    def _write(self, s):
65        if len(s) == 0:
66            return
67        if len(self.blame_stack) == 0:
68            if self.last is not None:
69                self.last = None
70                self.line_info.append((len(self.line), self.last))
71        else:
72            if self.last != self.blame_stack[-1]:
73                self.last = self.blame_stack[-1]
74                self.line_info.append((len(self.line), self.last))
75        self.line += s
76
77    def visit(self, node):
78        if node is None:
79            return None
80        if type(node) is tuple:
81            return tuple([self.visit(n) for n in node])
82        try:
83            self.blame_stack.append((node.lineno, node.col_offset,))
84            info = True
85        except AttributeError:
86            info = False
87        visitor = getattr(self, 'visit_%s' % node.__class__.__name__, None)
88        if visitor is None:
89            raise Exception('Unhandled node type %r' % type(node))
90        ret = visitor(node)
91        if info:
92            self.blame_stack.pop()
93        return ret
94
95    def visit_Module(self, node):
96        for n in node.body:
97            self.visit(n)
98    visit_Interactive = visit_Module
99    visit_Suite = visit_Module
100
101    def visit_Expression(self, node):
102        self._new_line()
103        return self.visit(node.body)
104
105    # arguments = (expr* args, identifier? vararg,
106    #              identifier? kwarg, expr* defaults)
107    def visit_arguments(self, node):
108        first = True
109        no_default_count = len(node.args) - len(node.defaults)
110        for i, arg in enumerate(node.args):
111            if not first:
112                self._write(', ')
113            else:
114                first = False
115            self.visit(arg)
116            if i >= no_default_count:
117                self._write('=')
118                self.visit(node.defaults[i - no_default_count])
119        if getattr(node, 'vararg', None):
120            if not first:
121                self._write(', ')
122            else:
123                first = False
124            self._write('*' + node.vararg)
125        if getattr(node, 'kwarg', None):
126            if not first:
127                self._write(', ')
128            else:
129                first = False
130            self._write('**' + node.kwarg)
131
132    # FunctionDef(identifier name, arguments args,
133    #                           stmt* body, expr* decorator_list)
134    def visit_FunctionDef(self, node):
135        decarators = ()
136        if hasattr(node, 'decorator_list'):
137            decorators = getattr(node, 'decorator_list')
138        else: # different name in earlier Python versions
139            decorators = getattr(node, 'decorators', ())
140        for decorator in decorators:
141            self._new_line()
142            self._write('@')
143            self.visit(decorator)
144        self._new_line()
145        self._write('def ' + node.name + '(')
146        self.visit(node.args)
147        self._write('):')
148        self._change_indent(1)
149        for statement in node.body:
150            self.visit(statement)
151        self._change_indent(-1)
152
153    # ClassDef(identifier name, expr* bases, stmt* body)
154    def visit_ClassDef(self, node):
155        self._new_line()
156        self._write('class ' + node.name)
157        if node.bases:
158            self._write('(')
159            self.visit(node.bases[0])
160            for base in node.bases[1:]:
161                self._write(', ')
162                self.visit(base)
163            self._write(')')
164        self._write(':')
165        self._change_indent(1)
166        for statement in node.body:
167            self.visit(statement)
168        self._change_indent(-1)
169
170    # Return(expr? value)
171    def visit_Return(self, node):
172        self._new_line()
173        self._write('return')
174        if getattr(node, 'value', None):
175            self._write(' ')
176            self.visit(node.value)
177
178    # Delete(expr* targets)
179    def visit_Delete(self, node):
180        self._new_line()
181        self._write('del ')
182        self.visit(node.targets[0])
183        for target in node.targets[1:]:
184            self._write(', ')
185            self.visit(target)
186
187    # Assign(expr* targets, expr value)
188    def visit_Assign(self, node):
189        self._new_line()
190        for target in node.targets:
191            self.visit(target)
192            self._write(' = ')
193        self.visit(node.value)
194
195    # AugAssign(expr target, operator op, expr value)
196    def visit_AugAssign(self, node):
197        self._new_line()
198        self.visit(node.target)
199        self._write(' ' + self.binary_operators[node.op.__class__] + '= ')
200        self.visit(node.value)
201
202    # Print(expr? dest, expr* values, bool nl)
203    def visit_Print(self, node):
204        self._new_line()
205        self._write('print')
206        if getattr(node, 'dest', None):
207            self._write(' >> ')
208            self.visit(node.dest)
209            if getattr(node, 'values', None):
210                self._write(', ')
211        else:
212            self._write(' ')
213        if getattr(node, 'values', None):
214            self.visit(node.values[0])
215            for value in node.values[1:]:
216                self._write(', ')
217                self.visit(value)
218        if not node.nl:
219            self._write(',')
220
221    # For(expr target, expr iter, stmt* body, stmt* orelse)
222    def visit_For(self, node):
223        self._new_line()
224        self._write('for ')
225        self.visit(node.target)
226        self._write(' in ')
227        self.visit(node.iter)
228        self._write(':')
229        self._change_indent(1)
230        for statement in node.body:
231            self.visit(statement)
232        self._change_indent(-1)
233        if getattr(node, 'orelse', None):
234            self._new_line()
235            self._write('else:')
236            self._change_indent(1)
237            for statement in node.orelse:
238                self.visit(statement)
239            self._change_indent(-1)
240
241    # While(expr test, stmt* body, stmt* orelse)
242    def visit_While(self, node):
243        self._new_line()
244        self._write('while ')
245        self.visit(node.test)
246        self._write(':')
247        self._change_indent(1)
248        for statement in node.body:
249            self.visit(statement)
250        self._change_indent(-1)
251        if getattr(node, 'orelse', None):
252            self._new_line()
253            self._write('else:')
254            self._change_indent(1)
255            for statement in node.orelse:
256                self.visit(statement)
257            self._change_indent(-1)
258
259    # If(expr test, stmt* body, stmt* orelse)
260    def visit_If(self, node):
261        self._new_line()
262        self._write('if ')
263        self.visit(node.test)
264        self._write(':')
265        self._change_indent(1)
266        for statement in node.body:
267            self.visit(statement)
268        self._change_indent(-1)
269        if getattr(node, 'orelse', None):
270            self._new_line()
271            self._write('else:')
272            self._change_indent(1)
273            for statement in node.orelse:
274                self.visit(statement)
275            self._change_indent(-1)
276
277    # With(expr context_expr, expr? optional_vars, stmt* body)
278    def visit_With(self, node):
279        self._new_line()
280        self._write('with ')
281        self.visit(node.context_expr)
282        if getattr(node, 'optional_vars', None):
283            self._write(' as ')
284            self.visit(node.optional_vars)
285        self._write(':')
286        self._change_indent(1)
287        for statement in node.body:
288            self.visit(statement)
289        self._change_indent(-1)
290
291
292    # Raise(expr? type, expr? inst, expr? tback)
293    def visit_Raise(self, node):
294        self._new_line()
295        self._write('raise')
296        if not node.type:
297            return
298        self._write(' ')
299        self.visit(node.type)
300        if not node.inst:
301            return
302        self._write(', ')
303        self.visit(node.inst)
304        if not node.tback:
305            return
306        self._write(', ')
307        self.visit(node.tback)
308
309    # TryExcept(stmt* body, excepthandler* handlers, stmt* orelse)
310    def visit_TryExcept(self, node):
311        self._new_line()
312        self._write('try:')
313        self._change_indent(1)
314        for statement in node.body:
315            self.visit(statement)
316        self._change_indent(-1)
317        if getattr(node, 'handlers', None):
318            for handler in node.handlers:
319                self.visit(handler)
320        self._new_line()
321        if getattr(node, 'orelse', None):
322            self._write('else:')
323            self._change_indent(1)
324            for statement in node.orelse:
325                self.visit(statement)
326            self._change_indent(-1)
327
328    # excepthandler = (expr? type, expr? name, stmt* body)
329    def visit_ExceptHandler(self, node):
330        self._new_line()
331        self._write('except')
332        if getattr(node, 'type', None):
333            self._write(' ')
334            self.visit(node.type)
335        if getattr(node, 'name', None):
336            self._write(', ')
337            self.visit(node.name)
338        self._write(':')
339        self._change_indent(1)
340        for statement in node.body:
341            self.visit(statement)
342        self._change_indent(-1)
343    visit_excepthandler = visit_ExceptHandler
344
345    # TryFinally(stmt* body, stmt* finalbody)
346    def visit_TryFinally(self, node):
347        self._new_line()
348        self._write('try:')
349        self._change_indent(1)
350        for statement in node.body:
351            self.visit(statement)
352        self._change_indent(-1)
353
354        if getattr(node, 'finalbody', None):
355            self._new_line()
356            self._write('finally:')
357            self._change_indent(1)
358            for statement in node.finalbody:
359                self.visit(statement)
360            self._change_indent(-1)
361
362    # Assert(expr test, expr? msg)
363    def visit_Assert(self, node):
364        self._new_line()
365        self._write('assert ')
366        self.visit(node.test)
367        if getattr(node, 'msg', None):
368            self._write(', ')
369            self.visit(node.msg)
370
371    def visit_alias(self, node):
372        self._write(node.name)
373        if getattr(node, 'asname', None):
374            self._write(' as ')
375            self._write(node.asname)
376
377    # Import(alias* names)
378    def visit_Import(self, node):
379        self._new_line()
380        self._write('import ')
381        self.visit(node.names[0])
382        for name in node.names[1:]:
383            self._write(', ')
384            self.visit(name)
385
386    # ImportFrom(identifier module, alias* names, int? level)
387    def visit_ImportFrom(self, node):
388        self._new_line()
389        self._write('from ')
390        if node.level:
391            self._write('.' * node.level)
392        self._write(node.module)
393        self._write(' import ')
394        self.visit(node.names[0])
395        for name in node.names[1:]:
396            self._write(', ')
397            self.visit(name)
398
399    # Exec(expr body, expr? globals, expr? locals)
400    def visit_Exec(self, node):
401        self._new_line()
402        self._write('exec ')
403        self.visit(node.body)
404        if not node.globals:
405            return
406        self._write(', ')
407        self.visit(node.globals)
408        if not node.locals:
409            return
410        self._write(', ')
411        self.visit(node.locals)
412
413    # Global(identifier* names)
414    def visit_Global(self, node):
415        self._new_line()
416        self._write('global ')
417        self.visit(node.names[0])
418        for name in node.names[1:]:
419            self._write(', ')
420            self.visit(name)
421
422    # Expr(expr value)
423    def visit_Expr(self, node):
424        self._new_line()
425        self.visit(node.value)
426
427    # Pass
428    def visit_Pass(self, node):
429        self._new_line()
430        self._write('pass')
431
432    # Break
433    def visit_Break(self, node):
434        self._new_line()
435        self._write('break')
436
437    # Continue
438    def visit_Continue(self, node):
439        self._new_line()
440        self._write('continue')
441
442    ### EXPRESSIONS
443    def with_parens(f):
444        def _f(self, node):
445            self._write('(')
446            f(self, node)
447            self._write(')')
448        return _f
449
450    bool_operators = {_ast.And: 'and', _ast.Or: 'or'}
451
452    # BoolOp(boolop op, expr* values)
453    @with_parens
454    def visit_BoolOp(self, node):
455        joiner = ' ' + self.bool_operators[node.op.__class__] + ' '
456        self.visit(node.values[0])
457        for value in node.values[1:]:
458            self._write(joiner)
459            self.visit(value)
460
461    binary_operators = {
462        _ast.Add: '+',
463        _ast.Sub: '-',
464        _ast.Mult: '*',
465        _ast.Div: '/',
466        _ast.Mod: '%',
467        _ast.Pow: '**',
468        _ast.LShift: '<<',
469        _ast.RShift: '>>',
470        _ast.BitOr: '|',
471        _ast.BitXor: '^',
472        _ast.BitAnd: '&',
473        _ast.FloorDiv: '//'
474    }
475
476    # BinOp(expr left, operator op, expr right)
477    @with_parens
478    def visit_BinOp(self, node):
479        self.visit(node.left)
480        self._write(' ' + self.binary_operators[node.op.__class__] + ' ')
481        self.visit(node.right)
482
483    unary_operators = {
484        _ast.Invert: '~',
485        _ast.Not: 'not',
486        _ast.UAdd: '+',
487        _ast.USub: '-',
488    }
489
490    # UnaryOp(unaryop op, expr operand)
491    def visit_UnaryOp(self, node):
492        self._write(self.unary_operators[node.op.__class__] + ' ')
493        self.visit(node.operand)
494
495    # Lambda(arguments args, expr body)
496    @with_parens
497    def visit_Lambda(self, node):
498        self._write('lambda ')
499        self.visit(node.args)
500        self._write(': ')
501        self.visit(node.body)
502
503    # IfExp(expr test, expr body, expr orelse)
504    @with_parens
505    def visit_IfExp(self, node):
506        self.visit(node.body)
507        self._write(' if ')
508        self.visit(node.test)
509        self._write(' else ')
510        self.visit(node.orelse)
511
512    # Dict(expr* keys, expr* values)
513    def visit_Dict(self, node):
514        self._write('{')
515        for key, value in zip(node.keys, node.values):
516            self.visit(key)
517            self._write(': ')
518            self.visit(value)
519            self._write(', ')
520        self._write('}')
521
522    # ListComp(expr elt, comprehension* generators)
523    def visit_ListComp(self, node):
524        self._write('[')
525        self.visit(node.elt)
526        for generator in node.generators:
527            # comprehension = (expr target, expr iter, expr* ifs)
528            self._write(' for ')
529            self.visit(generator.target)
530            self._write(' in ')
531            self.visit(generator.iter)
532            for ifexpr in generator.ifs:
533                self._write(' if ')
534                self.visit(ifexpr)
535        self._write(']')
536
537    # GeneratorExp(expr elt, comprehension* generators)
538    def visit_GeneratorExp(self, node):
539        self._write('(')
540        self.visit(node.elt)
541        for generator in node.generators:
542            # comprehension = (expr target, expr iter, expr* ifs)
543            self._write(' for ')
544            self.visit(generator.target)
545            self._write(' in ')
546            self.visit(generator.iter)
547            for ifexpr in generator.ifs:
548                self._write(' if ')
549                self.visit(ifexpr)
550        self._write(')')
551
552    # Yield(expr? value)
553    def visit_Yield(self, node):
554        self._write('yield')
555        if getattr(node, 'value', None):
556            self._write(' ')
557            self.visit(node.value)
558
559    comparision_operators = {
560        _ast.Eq: '==',
561        _ast.NotEq: '!=',
562        _ast.Lt: '<',
563        _ast.LtE: '<=',
564        _ast.Gt: '>',
565        _ast.GtE: '>=',
566        _ast.Is: 'is',
567        _ast.IsNot: 'is not',
568        _ast.In: 'in',
569        _ast.NotIn: 'not in',
570    }
571
572    # Compare(expr left, cmpop* ops, expr* comparators)
573    @with_parens
574    def visit_Compare(self, node):
575        self.visit(node.left)
576        for op, comparator in zip(node.ops, node.comparators):
577            self._write(' ' + self.comparision_operators[op.__class__] + ' ')
578            self.visit(comparator)
579
580    # Call(expr func, expr* args, keyword* keywords,
581    #                         expr? starargs, expr? kwargs)
582    def visit_Call(self, node):
583        self.visit(node.func)
584        self._write('(')
585        first = True
586        for arg in node.args:
587            if not first:
588                self._write(', ')
589            first = False
590            self.visit(arg)
591
592        for keyword in node.keywords:
593            if not first:
594                self._write(', ')
595            first = False
596            # keyword = (identifier arg, expr value)
597            self._write(keyword.arg)
598            self._write('=')
599            self.visit(keyword.value)
600        if getattr(node, 'starargs', None):
601            if not first:
602                self._write(', ')
603            first = False
604            self._write('*')
605            self.visit(node.starargs)
606
607        if getattr(node, 'kwargs', None):
608            if not first:
609                self._write(', ')
610            first = False
611            self._write('**')
612            self.visit(node.kwargs)
613        self._write(')')
614
615    # Repr(expr value)
616    def visit_Repr(self, node):
617        self._write('`')
618        self.visit(node.value)
619        self._write('`')
620
621    # Num(object n)
622    def visit_Num(self, node):
623        self._write(repr(node.n))
624
625    # Str(string s)
626    def visit_Str(self, node):
627        self._write(repr(node.s))
628
629    # Attribute(expr value, identifier attr, expr_context ctx)
630    def visit_Attribute(self, node):
631        self.visit(node.value)
632        self._write('.')
633        self._write(node.attr)
634
635    # Subscript(expr value, slice slice, expr_context ctx)
636    def visit_Subscript(self, node):
637        self.visit(node.value)
638        self._write('[')
639        def _process_slice(node):
640            if isinstance(node, _ast.Ellipsis):
641                self._write('...')
642            elif isinstance(node, _ast.Slice):
643                if getattr(node, 'lower', 'None'):
644                    self.visit(node.lower)
645                self._write(':')
646                if getattr(node, 'upper', None):
647                    self.visit(node.upper)
648                if getattr(node, 'step', None):
649                    self._write(':')
650                    self.visit(node.step)
651            elif isinstance(node, _ast.Index):
652                self.visit(node.value)
653            elif isinstance(node, _ast.ExtSlice):
654                self.visit(node.dims[0])
655                for dim in node.dims[1:]:
656                    self._write(', ')
657                    self.visit(dim)
658            else:
659                raise NotImplemented('Slice type not implemented')
660        _process_slice(node.slice)
661        self._write(']')
662
663    # Name(identifier id, expr_context ctx)
664    def visit_Name(self, node):
665        self._write(node.id)
666
667    # List(expr* elts, expr_context ctx)
668    def visit_List(self, node):
669        self._write('[')
670        for elt in node.elts:
671            self.visit(elt)
672            self._write(', ')
673        self._write(']')
674
675    # Tuple(expr *elts, expr_context ctx)
676    def visit_Tuple(self, node):
677        self._write('(')
678        for elt in node.elts:
679            self.visit(elt)
680            self._write(', ')
681        self._write(')')
682
683
684class ASTTransformer(object):
685    """General purpose base class for AST transformations.
686   
687    Every visitor method can be overridden to return an AST node that has been
688    altered or replaced in some way.
689    """
690
691    def visit(self, node):
692        if node is None:
693            return None
694        if type(node) is tuple:
695            return tuple([self.visit(n) for n in node])
696        visitor = getattr(self, 'visit_%s' % node.__class__.__name__, None)
697        if visitor is None:
698            return node
699        return visitor(node)
700
701    def _clone(self, node):
702        clone = node.__class__()
703        for name in getattr(clone, '_attributes', ()):
704            try:
705                setattr(clone, 'name', getattr(node, name))
706            except AttributeError:
707                pass
708        for name in clone._fields:
709            try:
710                value = getattr(node, name)
711            except AttributeError:
712                pass
713            else:
714                if value is None:
715                    pass
716                elif isinstance(value, list):
717                    value = [self.visit(x) for x in value]
718                elif isinstance(value, tuple):
719                    value = tuple(self.visit(x) for x in value)
720                else: 
721                    value = self.visit(value)
722                setattr(clone, name, value)
723        return clone
724
725    visit_Module = _clone
726    visit_Interactive = _clone
727    visit_Expression = _clone
728    visit_Suite = _clone
729
730    visit_FunctionDef = _clone
731    visit_ClassDef = _clone
732    visit_Return = _clone
733    visit_Delete = _clone
734    visit_Assign = _clone
735    visit_AugAssign = _clone
736    visit_Print = _clone
737    visit_For = _clone
738    visit_While = _clone
739    visit_If = _clone
740    visit_With = _clone
741    visit_Raise = _clone
742    visit_TryExcept = _clone
743    visit_TryFinally = _clone
744    visit_Assert = _clone
745    visit_ExceptHandler = _clone
746
747    visit_Import = _clone
748    visit_ImportFrom = _clone
749    visit_Exec = _clone
750    visit_Global = _clone
751    visit_Expr = _clone
752    # Pass, Break, Continue don't need to be copied
753
754    visit_BoolOp = _clone
755    visit_BinOp = _clone
756    visit_UnaryOp = _clone
757    visit_Lambda = _clone
758    visit_IfExp = _clone
759    visit_Dict = _clone
760    visit_ListComp = _clone
761    visit_GeneratorExp = _clone
762    visit_Yield = _clone
763    visit_Compare = _clone
764    visit_Call = _clone
765    visit_Repr = _clone
766    # Num, Str don't need to be copied
767
768    visit_Attribute = _clone
769    visit_Subscript = _clone
770    visit_Name = _clone
771    visit_List = _clone
772    visit_Tuple = _clone
773
774    visit_comprehension = _clone
775    visit_excepthandler = _clone
776    visit_arguments = _clone
777    visit_keyword = _clone
778    visit_alias = _clone
779
780    visit_Slice = _clone
781    visit_ExtSlice = _clone
782    visit_Index = _clone
783
784    del _clone
Note: See TracBrowser for help on using the repository browser.