blob: 2b1cea5843edd159a29b55c35847292775e03e87 [file] [log] [blame]
Martin v. Löwisef04c442008-03-19 05:04:44 +00001"""Utility functions, node construction macros, etc."""
2# Author: Collin Winter
3
4# Local imports
Benjamin Petersondf6dc8f2008-06-15 02:57:40 +00005from .pgen2 import token
6from .pytree import Leaf, Node
7from .pygram import python_symbols as syms
8from . import patcomp
Martin v. Löwisef04c442008-03-19 05:04:44 +00009
10
11###########################################################
12### Common node-construction "macros"
13###########################################################
14
15def KeywordArg(keyword, value):
16 return Node(syms.argument,
17 [keyword, Leaf(token.EQUAL, '='), value])
18
19def LParen():
20 return Leaf(token.LPAR, "(")
21
22def RParen():
23 return Leaf(token.RPAR, ")")
24
25def Assign(target, source):
26 """Build an assignment statement"""
27 if not isinstance(target, list):
28 target = [target]
29 if not isinstance(source, list):
30 source.set_prefix(" ")
31 source = [source]
32
33 return Node(syms.atom,
34 target + [Leaf(token.EQUAL, "=", prefix=" ")] + source)
35
36def Name(name, prefix=None):
37 """Return a NAME leaf"""
38 return Leaf(token.NAME, name, prefix=prefix)
39
40def Attr(obj, attr):
41 """A node tuple for obj.attr"""
42 return [obj, Node(syms.trailer, [Dot(), attr])]
43
44def Comma():
45 """A comma leaf"""
46 return Leaf(token.COMMA, ",")
47
48def Dot():
49 """A period (.) leaf"""
50 return Leaf(token.DOT, ".")
51
52def ArgList(args, lparen=LParen(), rparen=RParen()):
53 """A parenthesised argument list, used by Call()"""
54 return Node(syms.trailer,
55 [lparen.clone(),
56 Node(syms.arglist, args),
57 rparen.clone()])
58
59def Call(func_name, args, prefix=None):
60 """A function call"""
61 node = Node(syms.power, [func_name, ArgList(args)])
62 if prefix is not None:
63 node.set_prefix(prefix)
64 return node
65
66def Newline():
67 """A newline literal"""
68 return Leaf(token.NEWLINE, "\n")
69
70def BlankLine():
71 """A blank line"""
72 return Leaf(token.NEWLINE, "")
73
74def Number(n, prefix=None):
75 return Leaf(token.NUMBER, n, prefix=prefix)
76
77def Subscript(index_node):
78 """A numeric or string subscript"""
79 return Node(syms.trailer, [Leaf(token.LBRACE, '['),
80 index_node,
81 Leaf(token.RBRACE, ']')])
82
83def String(string, prefix=None):
84 """A string leaf"""
85 return Leaf(token.STRING, string, prefix=prefix)
86
87def ListComp(xp, fp, it, test=None):
88 """A list comprehension of the form [xp for fp in it if test].
89
90 If test is None, the "if test" part is omitted.
91 """
92 xp.set_prefix("")
93 fp.set_prefix(" ")
94 it.set_prefix(" ")
95 for_leaf = Leaf(token.NAME, "for")
96 for_leaf.set_prefix(" ")
97 in_leaf = Leaf(token.NAME, "in")
98 in_leaf.set_prefix(" ")
99 inner_args = [for_leaf, fp, in_leaf, it]
100 if test:
101 test.set_prefix(" ")
102 if_leaf = Leaf(token.NAME, "if")
103 if_leaf.set_prefix(" ")
104 inner_args.append(Node(syms.comp_if, [if_leaf, test]))
105 inner = Node(syms.listmaker, [xp, Node(syms.comp_for, inner_args)])
106 return Node(syms.atom,
107 [Leaf(token.LBRACE, "["),
108 inner,
109 Leaf(token.RBRACE, "]")])
110
Martin v. Löwisa675ef12008-03-24 00:50:58 +0000111def FromImport(package_name, name_leafs):
112 """ Return an import statement in the form:
113 from package import name_leafs"""
114 # XXX: May not handle dotted imports properly (eg, package_name='foo.bar')
Benjamin Petersond613bb42008-07-16 18:44:47 +0000115 #assert package_name == '.' or '.' not in package_name, "FromImport has "\
116 # "not been tested with dotted package names -- use at your own "\
117 # "peril!"
Martin v. Löwisa675ef12008-03-24 00:50:58 +0000118
119 for leaf in name_leafs:
120 # Pull the leaves out of their old tree
121 leaf.remove()
122
123 children = [Leaf(token.NAME, 'from'),
124 Leaf(token.NAME, package_name, prefix=" "),
125 Leaf(token.NAME, 'import', prefix=" "),
126 Node(syms.import_as_names, name_leafs)]
127 imp = Node(syms.import_from, children)
128 return imp
129
130
Martin v. Löwisef04c442008-03-19 05:04:44 +0000131###########################################################
132### Determine whether a node represents a given literal
133###########################################################
134
135def is_tuple(node):
136 """Does the node represent a tuple literal?"""
137 if isinstance(node, Node) and node.children == [LParen(), RParen()]:
138 return True
139 return (isinstance(node, Node)
140 and len(node.children) == 3
141 and isinstance(node.children[0], Leaf)
142 and isinstance(node.children[1], Node)
143 and isinstance(node.children[2], Leaf)
144 and node.children[0].value == "("
145 and node.children[2].value == ")")
146
147def is_list(node):
148 """Does the node represent a list literal?"""
149 return (isinstance(node, Node)
150 and len(node.children) > 1
151 and isinstance(node.children[0], Leaf)
152 and isinstance(node.children[-1], Leaf)
153 and node.children[0].value == "["
154 and node.children[-1].value == "]")
155
156###########################################################
157### Common portability code. This allows fixers to do, eg,
158### "from .util import set" and forget about it.
159###########################################################
160
161try:
162 any = any
163except NameError:
164 def any(l):
165 for o in l:
166 if o:
167 return True
168 return False
169
170try:
171 set = set
172except NameError:
173 from sets import Set as set
174
175try:
176 reversed = reversed
177except NameError:
178 def reversed(l):
179 return l[::-1]
180
181###########################################################
182### Misc
183###########################################################
184
Martin v. Löwis3de92bf2008-04-10 02:50:50 +0000185
186consuming_calls = set(["sorted", "list", "set", "any", "all", "tuple", "sum",
187 "min", "max"])
188
Martin v. Löwisef04c442008-03-19 05:04:44 +0000189def attr_chain(obj, attr):
190 """Follow an attribute chain.
Martin v. Löwisf733c602008-03-19 05:26:18 +0000191
Martin v. Löwisef04c442008-03-19 05:04:44 +0000192 If you have a chain of objects where a.foo -> b, b.foo-> c, etc,
193 use this to iterate over all objects in the chain. Iteration is
194 terminated by getattr(x, attr) is None.
Martin v. Löwisf733c602008-03-19 05:26:18 +0000195
Martin v. Löwisef04c442008-03-19 05:04:44 +0000196 Args:
197 obj: the starting object
198 attr: the name of the chaining attribute
Martin v. Löwisf733c602008-03-19 05:26:18 +0000199
Martin v. Löwisef04c442008-03-19 05:04:44 +0000200 Yields:
201 Each successive object in the chain.
202 """
203 next = getattr(obj, attr)
204 while next:
205 yield next
206 next = getattr(next, attr)
207
Martin v. Löwisf733c602008-03-19 05:26:18 +0000208p0 = """for_stmt< 'for' any 'in' node=any ':' any* >
209 | comp_for< 'for' any 'in' node=any any* >
210 """
211p1 = """
212power<
213 ( 'iter' | 'list' | 'tuple' | 'sorted' | 'set' | 'sum' |
214 'any' | 'all' | (any* trailer< '.' 'join' >) )
215 trailer< '(' node=any ')' >
216 any*
217>
218"""
219p2 = """
220power<
221 'sorted'
222 trailer< '(' arglist<node=any any*> ')' >
223 any*
224>
225"""
226pats_built = False
227def in_special_context(node):
228 """ Returns true if node is in an environment where all that is required
229 of it is being itterable (ie, it doesn't matter if it returns a list
230 or an itterator).
231 See test_map_nochange in test_fixers.py for some examples and tests.
232 """
233 global p0, p1, p2, pats_built
234 if not pats_built:
235 p1 = patcomp.compile_pattern(p1)
236 p0 = patcomp.compile_pattern(p0)
237 p2 = patcomp.compile_pattern(p2)
238 pats_built = True
239 patterns = [p0, p1, p2]
240 for pattern, parent in zip(patterns, attr_chain(node, "parent")):
241 results = {}
242 if pattern.match(parent, results) and results["node"] is node:
243 return True
244 return False
245
Martin v. Löwisef04c442008-03-19 05:04:44 +0000246###########################################################
247### The following functions are to find bindings in a suite
248###########################################################
249
250def make_suite(node):
251 if node.type == syms.suite:
252 return node
253 node = node.clone()
254 parent, node.parent = node.parent, None
255 suite = Node(syms.suite, [node])
256 suite.parent = parent
257 return suite
258
259def does_tree_import(package, name, node):
260 """ Returns true if name is imported from package at the
261 top level of the tree which node belongs to.
262 To cover the case of an import like 'import foo', use
263 Null for the package and 'foo' for the name. """
264 # Scamper up to the top level namespace
265 while node.type != syms.file_input:
266 assert node.parent, "Tree is insane! root found before "\
267 "file_input node was found."
268 node = node.parent
269
270 binding = find_binding(name, node, package)
271 return bool(binding)
272
273_def_syms = set([syms.classdef, syms.funcdef])
274def find_binding(name, node, package=None):
275 """ Returns the node which binds variable name, otherwise None.
276 If optional argument package is supplied, only imports will
277 be returned.
278 See test cases for examples."""
279 for child in node.children:
280 ret = None
281 if child.type == syms.for_stmt:
282 if _find(name, child.children[1]):
283 return child
284 n = find_binding(name, make_suite(child.children[-1]), package)
285 if n: ret = n
286 elif child.type in (syms.if_stmt, syms.while_stmt):
287 n = find_binding(name, make_suite(child.children[-1]), package)
288 if n: ret = n
289 elif child.type == syms.try_stmt:
290 n = find_binding(name, make_suite(child.children[2]), package)
291 if n:
292 ret = n
293 else:
294 for i, kid in enumerate(child.children[3:]):
295 if kid.type == token.COLON and kid.value == ":":
296 # i+3 is the colon, i+4 is the suite
297 n = find_binding(name, make_suite(child.children[i+4]), package)
298 if n: ret = n
299 elif child.type in _def_syms and child.children[1].value == name:
300 ret = child
301 elif _is_import_binding(child, name, package):
302 ret = child
303 elif child.type == syms.simple_stmt:
304 ret = find_binding(name, child, package)
305 elif child.type == syms.expr_stmt:
Martin v. Löwisf733c602008-03-19 05:26:18 +0000306 if _find(name, child.children[0]):
307 ret = child
Martin v. Löwisef04c442008-03-19 05:04:44 +0000308
309 if ret:
310 if not package:
311 return ret
312 if ret.type in (syms.import_name, syms.import_from):
313 return ret
314 return None
315
316_block_syms = set([syms.funcdef, syms.classdef, syms.trailer])
317def _find(name, node):
318 nodes = [node]
319 while nodes:
320 node = nodes.pop()
321 if node.type > 256 and node.type not in _block_syms:
322 nodes.extend(node.children)
323 elif node.type == token.NAME and node.value == name:
324 return node
325 return None
326
327def _is_import_binding(node, name, package=None):
328 """ Will reuturn node if node will import name, or node
329 will import * from package. None is returned otherwise.
330 See test cases for examples. """
331
332 if node.type == syms.import_name and not package:
333 imp = node.children[1]
334 if imp.type == syms.dotted_as_names:
335 for child in imp.children:
336 if child.type == syms.dotted_as_name:
337 if child.children[2].value == name:
338 return node
339 elif child.type == token.NAME and child.value == name:
340 return node
341 elif imp.type == syms.dotted_as_name:
342 last = imp.children[-1]
343 if last.type == token.NAME and last.value == name:
344 return node
345 elif imp.type == token.NAME and imp.value == name:
346 return node
347 elif node.type == syms.import_from:
Benjamin Petersondf6dc8f2008-06-15 02:57:40 +0000348 # str(...) is used to make life easier here, because
Martin v. Löwisef04c442008-03-19 05:04:44 +0000349 # from a.b import parses to ['import', ['a', '.', 'b'], ...]
Martin v. Löwis8a5f8ca2008-03-19 05:33:36 +0000350 if package and str(node.children[1]).strip() != package:
Martin v. Löwisef04c442008-03-19 05:04:44 +0000351 return None
352 n = node.children[3]
353 if package and _find('as', n):
354 # See test_from_import_as for explanation
355 return None
356 elif n.type == syms.import_as_names and _find(name, n):
357 return node
358 elif n.type == syms.import_as_name:
359 child = n.children[2]
360 if child.type == token.NAME and child.value == name:
361 return node
362 elif n.type == token.NAME and n.value == name:
363 return node
364 elif package and n.type == token.STAR:
365 return node
366 return None