blob: 8b7ad9fe0647bc7b73f18c1823bef94bf35de12f [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
5from ..pgen2 import token
6from ..pytree import Leaf, Node
7from ..pygram import python_symbols as syms
8
9
10###########################################################
11### Common node-construction "macros"
12###########################################################
13
14def KeywordArg(keyword, value):
15 return Node(syms.argument,
16 [keyword, Leaf(token.EQUAL, '='), value])
17
18def LParen():
19 return Leaf(token.LPAR, "(")
20
21def RParen():
22 return Leaf(token.RPAR, ")")
23
24def Assign(target, source):
25 """Build an assignment statement"""
26 if not isinstance(target, list):
27 target = [target]
28 if not isinstance(source, list):
29 source.set_prefix(" ")
30 source = [source]
31
32 return Node(syms.atom,
33 target + [Leaf(token.EQUAL, "=", prefix=" ")] + source)
34
35def Name(name, prefix=None):
36 """Return a NAME leaf"""
37 return Leaf(token.NAME, name, prefix=prefix)
38
39def Attr(obj, attr):
40 """A node tuple for obj.attr"""
41 return [obj, Node(syms.trailer, [Dot(), attr])]
42
43def Comma():
44 """A comma leaf"""
45 return Leaf(token.COMMA, ",")
46
47def Dot():
48 """A period (.) leaf"""
49 return Leaf(token.DOT, ".")
50
51def ArgList(args, lparen=LParen(), rparen=RParen()):
52 """A parenthesised argument list, used by Call()"""
53 return Node(syms.trailer,
54 [lparen.clone(),
55 Node(syms.arglist, args),
56 rparen.clone()])
57
58def Call(func_name, args, prefix=None):
59 """A function call"""
60 node = Node(syms.power, [func_name, ArgList(args)])
61 if prefix is not None:
62 node.set_prefix(prefix)
63 return node
64
65def Newline():
66 """A newline literal"""
67 return Leaf(token.NEWLINE, "\n")
68
69def BlankLine():
70 """A blank line"""
71 return Leaf(token.NEWLINE, "")
72
73def Number(n, prefix=None):
74 return Leaf(token.NUMBER, n, prefix=prefix)
75
76def Subscript(index_node):
77 """A numeric or string subscript"""
78 return Node(syms.trailer, [Leaf(token.LBRACE, '['),
79 index_node,
80 Leaf(token.RBRACE, ']')])
81
82def String(string, prefix=None):
83 """A string leaf"""
84 return Leaf(token.STRING, string, prefix=prefix)
85
86def ListComp(xp, fp, it, test=None):
87 """A list comprehension of the form [xp for fp in it if test].
88
89 If test is None, the "if test" part is omitted.
90 """
91 xp.set_prefix("")
92 fp.set_prefix(" ")
93 it.set_prefix(" ")
94 for_leaf = Leaf(token.NAME, "for")
95 for_leaf.set_prefix(" ")
96 in_leaf = Leaf(token.NAME, "in")
97 in_leaf.set_prefix(" ")
98 inner_args = [for_leaf, fp, in_leaf, it]
99 if test:
100 test.set_prefix(" ")
101 if_leaf = Leaf(token.NAME, "if")
102 if_leaf.set_prefix(" ")
103 inner_args.append(Node(syms.comp_if, [if_leaf, test]))
104 inner = Node(syms.listmaker, [xp, Node(syms.comp_for, inner_args)])
105 return Node(syms.atom,
106 [Leaf(token.LBRACE, "["),
107 inner,
108 Leaf(token.RBRACE, "]")])
109
110###########################################################
111### Determine whether a node represents a given literal
112###########################################################
113
114def is_tuple(node):
115 """Does the node represent a tuple literal?"""
116 if isinstance(node, Node) and node.children == [LParen(), RParen()]:
117 return True
118 return (isinstance(node, Node)
119 and len(node.children) == 3
120 and isinstance(node.children[0], Leaf)
121 and isinstance(node.children[1], Node)
122 and isinstance(node.children[2], Leaf)
123 and node.children[0].value == "("
124 and node.children[2].value == ")")
125
126def is_list(node):
127 """Does the node represent a list literal?"""
128 return (isinstance(node, Node)
129 and len(node.children) > 1
130 and isinstance(node.children[0], Leaf)
131 and isinstance(node.children[-1], Leaf)
132 and node.children[0].value == "["
133 and node.children[-1].value == "]")
134
135###########################################################
136### Common portability code. This allows fixers to do, eg,
137### "from .util import set" and forget about it.
138###########################################################
139
140try:
141 any = any
142except NameError:
143 def any(l):
144 for o in l:
145 if o:
146 return True
147 return False
148
149try:
150 set = set
151except NameError:
152 from sets import Set as set
153
154try:
155 reversed = reversed
156except NameError:
157 def reversed(l):
158 return l[::-1]
159
160###########################################################
161### Misc
162###########################################################
163
164def attr_chain(obj, attr):
165 """Follow an attribute chain.
166
167 If you have a chain of objects where a.foo -> b, b.foo-> c, etc,
168 use this to iterate over all objects in the chain. Iteration is
169 terminated by getattr(x, attr) is None.
170
171 Args:
172 obj: the starting object
173 attr: the name of the chaining attribute
174
175 Yields:
176 Each successive object in the chain.
177 """
178 next = getattr(obj, attr)
179 while next:
180 yield next
181 next = getattr(next, attr)
182
183###########################################################
184### The following functions are to find bindings in a suite
185###########################################################
186
187def make_suite(node):
188 if node.type == syms.suite:
189 return node
190 node = node.clone()
191 parent, node.parent = node.parent, None
192 suite = Node(syms.suite, [node])
193 suite.parent = parent
194 return suite
195
196def does_tree_import(package, name, node):
197 """ Returns true if name is imported from package at the
198 top level of the tree which node belongs to.
199 To cover the case of an import like 'import foo', use
200 Null for the package and 'foo' for the name. """
201 # Scamper up to the top level namespace
202 while node.type != syms.file_input:
203 assert node.parent, "Tree is insane! root found before "\
204 "file_input node was found."
205 node = node.parent
206
207 binding = find_binding(name, node, package)
208 return bool(binding)
209
210_def_syms = set([syms.classdef, syms.funcdef])
211def find_binding(name, node, package=None):
212 """ Returns the node which binds variable name, otherwise None.
213 If optional argument package is supplied, only imports will
214 be returned.
215 See test cases for examples."""
216 for child in node.children:
217 ret = None
218 if child.type == syms.for_stmt:
219 if _find(name, child.children[1]):
220 return child
221 n = find_binding(name, make_suite(child.children[-1]), package)
222 if n: ret = n
223 elif child.type in (syms.if_stmt, syms.while_stmt):
224 n = find_binding(name, make_suite(child.children[-1]), package)
225 if n: ret = n
226 elif child.type == syms.try_stmt:
227 n = find_binding(name, make_suite(child.children[2]), package)
228 if n:
229 ret = n
230 else:
231 for i, kid in enumerate(child.children[3:]):
232 if kid.type == token.COLON and kid.value == ":":
233 # i+3 is the colon, i+4 is the suite
234 n = find_binding(name, make_suite(child.children[i+4]), package)
235 if n: ret = n
236 elif child.type in _def_syms and child.children[1].value == name:
237 ret = child
238 elif _is_import_binding(child, name, package):
239 ret = child
240 elif child.type == syms.simple_stmt:
241 ret = find_binding(name, child, package)
242 elif child.type == syms.expr_stmt:
243 if _find(name, child.children[0]):
244 ret = child
245
246 if ret:
247 if not package:
248 return ret
249 if ret.type in (syms.import_name, syms.import_from):
250 return ret
251 return None
252
253_block_syms = set([syms.funcdef, syms.classdef, syms.trailer])
254def _find(name, node):
255 nodes = [node]
256 while nodes:
257 node = nodes.pop()
258 if node.type > 256 and node.type not in _block_syms:
259 nodes.extend(node.children)
260 elif node.type == token.NAME and node.value == name:
261 return node
262 return None
263
264def _is_import_binding(node, name, package=None):
265 """ Will reuturn node if node will import name, or node
266 will import * from package. None is returned otherwise.
267 See test cases for examples. """
268
269 if node.type == syms.import_name and not package:
270 imp = node.children[1]
271 if imp.type == syms.dotted_as_names:
272 for child in imp.children:
273 if child.type == syms.dotted_as_name:
274 if child.children[2].value == name:
275 return node
276 elif child.type == token.NAME and child.value == name:
277 return node
278 elif imp.type == syms.dotted_as_name:
279 last = imp.children[-1]
280 if last.type == token.NAME and last.value == name:
281 return node
282 elif imp.type == token.NAME and imp.value == name:
283 return node
284 elif node.type == syms.import_from:
285 # unicode(...) is used to make life easier here, because
286 # from a.b import parses to ['import', ['a', '.', 'b'], ...]
287 if package and unicode(node.children[1]).strip() != package:
288 return None
289 n = node.children[3]
290 if package and _find('as', n):
291 # See test_from_import_as for explanation
292 return None
293 elif n.type == syms.import_as_names and _find(name, n):
294 return node
295 elif n.type == syms.import_as_name:
296 child = n.children[2]
297 if child.type == token.NAME and child.value == name:
298 return node
299 elif n.type == token.NAME and n.value == name:
300 return node
301 elif package and n.type == token.STAR:
302 return node
303 return None