Mercurial > genshi > genshi-test
annotate genshi/template/astutil.py @ 902:09cc3627654c experimental-inline
Sync `experimental/inline` branch with [source:trunk@1126].
author | cmlenz |
---|---|
date | Fri, 23 Apr 2010 21:08:26 +0000 |
parents | 1837f39efd6f |
children | 95d62e239f60 |
rev | line source |
---|---|
820 | 1 # -*- coding: utf-8 -*- |
2 # | |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
3 # Copyright (C) 2008-2010 Edgewall Software |
820 | 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 | |
16 try: | |
17 import _ast | |
18 except ImportError: | |
19 from genshi.template.ast24 import _ast, parse | |
20 else: | |
21 def parse(source, mode): | |
22 return compile(source, '', mode, _ast.PyCF_ONLY_AST) | |
23 | |
24 | |
25 __docformat__ = 'restructuredtext en' | |
26 | |
27 | |
28 class 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, | |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
133 # stmt* body, expr* decorator_list) |
820 | 134 def visit_FunctionDef(self, node): |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
135 decarators = () |
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
136 if hasattr(node, 'decorator_list'): |
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
137 decorators = getattr(node, 'decorator_list') |
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
138 else: # different name in earlier Python versions |
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
139 decorators = getattr(node, 'decorators', ()) |
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
140 for decorator in decorators: |
820 | 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: | |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
659 raise NotImplemented('Slice type not implemented') |
820 | 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 | |
684 class 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 | |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
745 visit_ExceptHandler = _clone |
820 | 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 |