Cython has moved to github.
cython-devel
view Cython/Compiler/ParseTreeTransforms.py @ 1870:478a5cfba350
Bug 158 -- raise error if cdef variable declared after it's used.
| author | Kurt Smith <kwsmith1@wisc.edu> |
|---|---|
| date | Tue Mar 17 00:02:46 2009 -0500 (3 years ago) |
| parents | 569ed4c256db |
| children | e2365a6d00b8 |
line source
1 from Cython.Compiler.Visitor import VisitorTransform, CythonTransform, TreeVisitor
2 from Cython.Compiler.ModuleNode import ModuleNode
3 from Cython.Compiler.Nodes import *
4 from Cython.Compiler.ExprNodes import *
5 from Cython.Compiler.UtilNodes import *
6 from Cython.Compiler.TreeFragment import TreeFragment, TemplateTransform
7 from Cython.Compiler.StringEncoding import EncodedString
8 from Cython.Compiler.Errors import CompileError
9 try:
10 set
11 except NameError:
12 from sets import Set as set
13 import copy
16 class NameNodeCollector(TreeVisitor):
17 """Collect all NameNodes of a (sub-)tree in the ``name_nodes``
18 attribute.
19 """
20 def __init__(self):
21 super(NameNodeCollector, self).__init__()
22 self.name_nodes = []
24 visit_Node = TreeVisitor.visitchildren
26 def visit_NameNode(self, node):
27 self.name_nodes.append(node)
30 class SkipDeclarations(object):
31 """
32 Variable and function declarations can often have a deep tree structure,
33 and yet most transformations don't need to descend to this depth.
35 Declaration nodes are removed after AnalyseDeclarationsTransform, so there
36 is no need to use this for transformations after that point.
37 """
38 def visit_CTypeDefNode(self, node):
39 return node
41 def visit_CVarDefNode(self, node):
42 return node
44 def visit_CDeclaratorNode(self, node):
45 return node
47 def visit_CBaseTypeNode(self, node):
48 return node
50 def visit_CEnumDefNode(self, node):
51 return node
53 def visit_CStructOrUnionDefNode(self, node):
54 return node
57 class NormalizeTree(CythonTransform):
58 """
59 This transform fixes up a few things after parsing
60 in order to make the parse tree more suitable for
61 transforms.
63 a) After parsing, blocks with only one statement will
64 be represented by that statement, not by a StatListNode.
65 When doing transforms this is annoying and inconsistent,
66 as one cannot in general remove a statement in a consistent
67 way and so on. This transform wraps any single statements
68 in a StatListNode containing a single statement.
70 b) The PassStatNode is a noop and serves no purpose beyond
71 plugging such one-statement blocks; i.e., once parsed a
72 ` "pass" can just as well be represented using an empty
73 StatListNode. This means less special cases to worry about
74 in subsequent transforms (one always checks to see if a
75 StatListNode has no children to see if the block is empty).
76 """
78 def __init__(self, context):
79 super(NormalizeTree, self).__init__(context)
80 self.is_in_statlist = False
81 self.is_in_expr = False
83 def visit_ExprNode(self, node):
84 stacktmp = self.is_in_expr
85 self.is_in_expr = True
86 self.visitchildren(node)
87 self.is_in_expr = stacktmp
88 return node
90 def visit_StatNode(self, node, is_listcontainer=False):
91 stacktmp = self.is_in_statlist
92 self.is_in_statlist = is_listcontainer
93 self.visitchildren(node)
94 self.is_in_statlist = stacktmp
95 if not self.is_in_statlist and not self.is_in_expr:
96 return StatListNode(pos=node.pos, stats=[node])
97 else:
98 return node
100 def visit_StatListNode(self, node):
101 self.is_in_statlist = True
102 self.visitchildren(node)
103 self.is_in_statlist = False
104 return node
106 def visit_ParallelAssignmentNode(self, node):
107 return self.visit_StatNode(node, True)
109 def visit_CEnumDefNode(self, node):
110 return self.visit_StatNode(node, True)
112 def visit_CStructOrUnionDefNode(self, node):
113 return self.visit_StatNode(node, True)
115 # Eliminate PassStatNode
116 def visit_PassStatNode(self, node):
117 if not self.is_in_statlist:
118 return StatListNode(pos=node.pos, stats=[])
119 else:
120 return []
122 def visit_CDeclaratorNode(self, node):
123 return node
126 class PostParseError(CompileError): pass
128 # error strings checked by unit tests, so define them
129 ERR_CDEF_INCLASS = 'Cannot assign default value to fields in cdef classes, structs or unions'
130 ERR_BUF_LOCALONLY = 'Buffer types only allowed as function local variables'
131 ERR_BUF_DEFAULTS = 'Invalid buffer defaults specification (see docs)'
132 ERR_INVALID_SPECIALATTR_TYPE = 'Special attributes must not have a type declared'
133 class PostParse(CythonTransform):
134 """
135 Basic interpretation of the parse tree, as well as validity
136 checking that can be done on a very basic level on the parse
137 tree (while still not being a problem with the basic syntax,
138 as such).
140 Specifically:
141 - Default values to cdef assignments are turned into single
142 assignments following the declaration (everywhere but in class
143 bodies, where they raise a compile error)
145 - Interpret some node structures into Python runtime values.
146 Some nodes take compile-time arguments (currently:
147 CBufferAccessTypeNode[args] and __cythonbufferdefaults__ = {args}),
148 which should be interpreted. This happens in a general way
149 and other steps should be taken to ensure validity.
151 Type arguments cannot be interpreted in this way.
153 - For __cythonbufferdefaults__ the arguments are checked for
154 validity.
156 CBufferAccessTypeNode has its options interpreted:
157 Any first positional argument goes into the "dtype" attribute,
158 any "ndim" keyword argument goes into the "ndim" attribute and
159 so on. Also it is checked that the option combination is valid.
160 - __cythonbufferdefaults__ attributes are parsed and put into the
161 type information.
163 Note: Currently Parsing.py does a lot of interpretation and
164 reorganization that can be refactored into this transform
165 if a more pure Abstract Syntax Tree is wanted.
166 """
168 # Track our context.
169 scope_type = None # can be either of 'module', 'function', 'class'
171 def __init__(self, context):
172 super(PostParse, self).__init__(context)
173 self.specialattribute_handlers = {
174 '__cythonbufferdefaults__' : self.handle_bufferdefaults
175 }
177 def visit_ModuleNode(self, node):
178 self.scope_type = 'module'
179 self.scope_node = node
180 self.visitchildren(node)
181 return node
183 def visit_scope(self, node, scope_type):
184 prev = self.scope_type, self.scope_node
185 self.scope_type = scope_type
186 self.scope_node = node
187 self.visitchildren(node)
188 self.scope_type, self.scope_node = prev
189 return node
191 def visit_ClassDefNode(self, node):
192 return self.visit_scope(node, 'class')
194 def visit_FuncDefNode(self, node):
195 return self.visit_scope(node, 'function')
197 def visit_CStructOrUnionDefNode(self, node):
198 return self.visit_scope(node, 'struct')
200 # cdef variables
201 def handle_bufferdefaults(self, decl):
202 if not isinstance(decl.default, DictNode):
203 raise PostParseError(decl.pos, ERR_BUF_DEFAULTS)
204 self.scope_node.buffer_defaults_node = decl.default
205 self.scope_node.buffer_defaults_pos = decl.pos
207 def visit_CVarDefNode(self, node):
208 # This assumes only plain names and pointers are assignable on
209 # declaration. Also, it makes use of the fact that a cdef decl
210 # must appear before the first use, so we don't have to deal with
211 # "i = 3; cdef int i = i" and can simply move the nodes around.
212 try:
213 self.visitchildren(node)
214 stats = [node]
215 newdecls = []
216 for decl in node.declarators:
217 declbase = decl
218 while isinstance(declbase, CPtrDeclaratorNode):
219 declbase = declbase.base
220 if isinstance(declbase, CNameDeclaratorNode):
221 if declbase.default is not None:
222 if self.scope_type in ('class', 'struct'):
223 if isinstance(self.scope_node, CClassDefNode):
224 handler = self.specialattribute_handlers.get(decl.name)
225 if handler:
226 if decl is not declbase:
227 raise PostParseError(decl.pos, ERR_INVALID_SPECIALATTR_TYPE)
228 handler(decl)
229 continue # Remove declaration
230 raise PostParseError(decl.pos, ERR_CDEF_INCLASS)
231 first_assignment = self.scope_type != 'module'
232 stats.append(SingleAssignmentNode(node.pos,
233 lhs=NameNode(node.pos, name=declbase.name),
234 rhs=declbase.default, first=first_assignment))
235 declbase.default = None
236 newdecls.append(decl)
237 node.declarators = newdecls
238 return stats
239 except PostParseError, e:
240 # An error in a cdef clause is ok, simply remove the declaration
241 # and try to move on to report more errors
242 self.context.nonfatal_error(e)
243 return None
245 def visit_CBufferAccessTypeNode(self, node):
246 if not self.scope_type == 'function':
247 raise PostParseError(node.pos, ERR_BUF_LOCALONLY)
248 return node
250 class PxdPostParse(CythonTransform, SkipDeclarations):
251 """
252 Basic interpretation/validity checking that should only be
253 done on pxd trees.
255 A lot of this checking currently happens in the parser; but
256 what is listed below happens here.
258 - "def" functions are let through only if they fill the
259 getbuffer/releasebuffer slots
261 - cdef functions are let through only if they are on the
262 top level and are declared "inline"
263 """
264 ERR_INLINE_ONLY = "function definition in pxd file must be declared 'cdef inline'"
265 ERR_NOGO_WITH_INLINE = "inline function definition in pxd file cannot be '%s'"
267 def __call__(self, node):
268 self.scope_type = 'pxd'
269 return super(PxdPostParse, self).__call__(node)
271 def visit_CClassDefNode(self, node):
272 old = self.scope_type
273 self.scope_type = 'cclass'
274 self.visitchildren(node)
275 self.scope_type = old
276 return node
278 def visit_FuncDefNode(self, node):
279 # FuncDefNode always come with an implementation (without
280 # an imp they are CVarDefNodes..)
281 err = self.ERR_INLINE_ONLY
283 if (isinstance(node, DefNode) and self.scope_type == 'cclass'
284 and node.name in ('__getbuffer__', '__releasebuffer__')):
285 err = None # allow these slots
287 if isinstance(node, CFuncDefNode):
288 if u'inline' in node.modifiers and self.scope_type == 'pxd':
289 node.inline_in_pxd = True
290 if node.visibility != 'private':
291 err = self.ERR_NOGO_WITH_INLINE % node.visibility
292 elif node.api:
293 err = self.ERR_NOGO_WITH_INLINE % 'api'
294 else:
295 err = None # allow inline function
296 else:
297 err = self.ERR_INLINE_ONLY
299 if err:
300 self.context.nonfatal_error(PostParseError(node.pos, err))
301 return None
302 else:
303 return node
305 class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
306 """
307 After parsing, options can be stored in a number of places:
308 - #cython-comments at the top of the file (stored in ModuleNode)
309 - Command-line arguments overriding these
310 - @cython.optionname decorators
311 - with cython.optionname: statements
313 This transform is responsible for interpreting these various sources
314 and store the option in two ways:
315 - Set the directives attribute of the ModuleNode for global directives.
316 - Use a CompilerDirectivesNode to override directives for a subtree.
318 (The first one is primarily to not have to modify with the tree
319 structure, so that ModuleNode stay on top.)
321 The directives are stored in dictionaries from name to value in effect.
322 Each such dictionary is always filled in for all possible directives,
323 using default values where no value is given by the user.
325 The available directives are controlled in Options.py.
327 Note that we have to run this prior to analysis, and so some minor
328 duplication of functionality has to occur: We manually track cimports
329 and which names the "cython" module may have been imported to.
330 """
331 special_methods = set(['declare', 'union', 'struct', 'typedef', 'sizeof', 'cast', 'address', 'pointer', 'compiled', 'NULL'])
333 def __init__(self, context, compilation_option_overrides):
334 super(InterpretCompilerDirectives, self).__init__(context)
335 self.compilation_option_overrides = compilation_option_overrides
336 self.cython_module_names = set()
337 self.option_names = {}
339 # Set up processing and handle the cython: comments.
340 def visit_ModuleNode(self, node):
341 options = copy.copy(Options.option_defaults)
342 options.update(node.option_comments)
343 options.update(self.compilation_option_overrides)
344 self.options = options
345 node.directives = options
346 self.visitchildren(node)
347 node.cython_module_names = self.cython_module_names
348 return node
350 # Track cimports of the cython module.
351 def visit_CImportStatNode(self, node):
352 if node.module_name == u"cython":
353 if node.as_name:
354 modname = node.as_name
355 else:
356 modname = u"cython"
357 self.cython_module_names.add(modname)
358 return node
360 def visit_FromCImportStatNode(self, node):
361 if node.module_name == u"cython":
362 newimp = []
363 for pos, name, as_name, kind in node.imported_names:
364 if (name in Options.option_types or
365 name in self.special_methods or
366 PyrexTypes.parse_basic_type(name)):
367 if as_name is None:
368 as_name = name
369 self.option_names[as_name] = name
370 if kind is not None:
371 self.context.nonfatal_error(PostParseError(pos,
372 "Compiler option imports must be plain imports"))
373 else:
374 newimp.append((pos, name, as_name, kind))
375 if not newimp:
376 return None
377 node.imported_names = newimp
378 return node
380 def visit_FromImportStatNode(self, node):
381 if node.module.module_name.value == u"cython":
382 newimp = []
383 for name, name_node in node.items:
384 if (name in Options.option_types or
385 name in self.special_methods or
386 PyrexTypes.parse_basic_type(name)):
387 self.option_names[name_node.name] = name
388 else:
389 newimp.append((name, name_node))
390 if not newimp:
391 return None
392 node.items = newimp
393 return node
395 def visit_SingleAssignmentNode(self, node):
396 if (isinstance(node.rhs, ImportNode) and
397 node.rhs.module_name.value == u'cython'):
398 node = CImportStatNode(node.pos,
399 module_name = u'cython',
400 as_name = node.lhs.name)
401 self.visit_CImportStatNode(node)
402 else:
403 self.visitchildren(node)
404 return node
406 def visit_NameNode(self, node):
407 if node.name in self.cython_module_names:
408 node.is_cython_module = True
409 else:
410 node.cython_attribute = self.option_names.get(node.name)
411 return node
413 def try_to_parse_option(self, node):
414 # If node is the contents of an option (in a with statement or
415 # decorator), returns (optionname, value).
416 # Otherwise, returns None
417 optname = None
418 if isinstance(node, CallNode):
419 self.visit(node.function)
420 optname = node.function.as_cython_attribute()
422 if optname:
423 optiontype = Options.option_types.get(optname)
424 if optiontype:
425 args, kwds = node.explicit_args_kwds()
426 if optiontype is bool:
427 if kwds is not None or len(args) != 1 or not isinstance(args[0], BoolNode):
428 raise PostParseError(dec.function.pos,
429 'The %s option takes one compile-time boolean argument' % optname)
430 return (optname, args[0].value)
431 elif optiontype is dict:
432 if len(args) != 0:
433 raise PostParseError(dec.function.pos,
434 'The %s option takes no prepositional arguments' % optname)
435 return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
436 else:
437 assert False
439 return None
441 def visit_with_options(self, body, options):
442 oldoptions = self.options
443 newoptions = copy.copy(oldoptions)
444 newoptions.update(options)
445 self.options = newoptions
446 assert isinstance(body, StatListNode), body
447 retbody = self.visit_Node(body)
448 directive = CompilerDirectivesNode(pos=retbody.pos, body=retbody,
449 directives=newoptions)
450 self.options = oldoptions
451 return directive
453 # Handle decorators
454 def visit_FuncDefNode(self, node):
455 options = []
457 if node.decorators:
458 # Split the decorators into two lists -- real decorators and options
459 realdecs = []
460 for dec in node.decorators:
461 option = self.try_to_parse_option(dec.decorator)
462 if option is not None:
463 options.append(option)
464 else:
465 realdecs.append(dec)
466 if realdecs and isinstance(node, CFuncDefNode):
467 raise PostParseError(realdecs[0].pos, "Cdef functions cannot take arbitrary decorators.")
468 else:
469 node.decorators = realdecs
471 if options:
472 optdict = {}
473 options.reverse() # Decorators coming first take precedence
474 for option in options:
475 name, value = option
476 optdict[name] = value
477 body = StatListNode(node.pos, stats=[node])
478 return self.visit_with_options(body, optdict)
479 else:
480 return self.visit_Node(node)
482 def visit_CVarDefNode(self, node):
483 if node.decorators:
484 for dec in node.decorators:
485 option = self.try_to_parse_option(dec.decorator)
486 if option is not None and option[0] == u'locals':
487 node.directive_locals = option[1]
488 else:
489 raise PostParseError(dec.pos, "Cdef functions can only take cython.locals() decorator.")
490 return node
492 # Handle with statements
493 def visit_WithStatNode(self, node):
494 option = self.try_to_parse_option(node.manager)
495 if option is not None:
496 if node.target is not None:
497 raise PostParseError(node.pos, "Compiler option with statements cannot contain 'as'")
498 name, value = option
499 return self.visit_with_options(node.body, {name:value})
500 else:
501 return self.visit_Node(node)
503 class WithTransform(CythonTransform, SkipDeclarations):
505 # EXCINFO is manually set to a variable that contains
506 # the exc_info() tuple that can be generated by the enclosing except
507 # statement.
508 template_without_target = TreeFragment(u"""
509 MGR = EXPR
510 EXIT = MGR.__exit__
511 MGR.__enter__()
512 EXC = True
513 try:
514 try:
515 EXCINFO = None
516 BODY
517 except:
518 EXC = False
519 if not EXIT(*EXCINFO):
520 raise
521 finally:
522 if EXC:
523 EXIT(None, None, None)
524 """, temps=[u'MGR', u'EXC', u"EXIT"],
525 pipeline=[NormalizeTree(None)])
527 template_with_target = TreeFragment(u"""
528 MGR = EXPR
529 EXIT = MGR.__exit__
530 VALUE = MGR.__enter__()
531 EXC = True
532 try:
533 try:
534 EXCINFO = None
535 TARGET = VALUE
536 BODY
537 except:
538 EXC = False
539 if not EXIT(*EXCINFO):
540 raise
541 finally:
542 if EXC:
543 EXIT(None, None, None)
544 MGR = EXIT = VALUE = EXC = None
546 """, temps=[u'MGR', u'EXC', u"EXIT", u"VALUE"],
547 pipeline=[NormalizeTree(None)])
549 def visit_WithStatNode(self, node):
550 # TODO: Cleanup badly needed
551 TemplateTransform.temp_name_counter += 1
552 handle = "__tmpvar_%d" % TemplateTransform.temp_name_counter
554 self.visitchildren(node, ['body'])
555 excinfo_temp = NameNode(node.pos, name=handle)#TempHandle(Builtin.tuple_type)
556 if node.target is not None:
557 result = self.template_with_target.substitute({
558 u'EXPR' : node.manager,
559 u'BODY' : node.body,
560 u'TARGET' : node.target,
561 u'EXCINFO' : excinfo_temp
562 }, pos=node.pos)
563 else:
564 result = self.template_without_target.substitute({
565 u'EXPR' : node.manager,
566 u'BODY' : node.body,
567 u'EXCINFO' : excinfo_temp
568 }, pos=node.pos)
570 # Set except excinfo target to EXCINFO
571 try_except = result.stats[-1].body.stats[-1]
572 try_except.except_clauses[0].excinfo_target = NameNode(node.pos, name=handle)
573 # excinfo_temp.ref(node.pos))
575 # result.stats[-1].body.stats[-1] = TempsBlockNode(
576 # node.pos, temps=[excinfo_temp], body=try_except)
578 return result
580 def visit_ExprNode(self, node):
581 # With statements are never inside expressions.
582 return node
585 class ComprehensionTransform(VisitorTransform):
586 """Prevent the target of list/set/dict comprehensions from leaking by
587 moving it into a temp variable. This mimics the behaviour of all
588 comprehensions in Py3 and of generator expressions in Py2.x.
590 This must run before the IterationTransform, which might replace
591 for-loops with while-loops. We only handle for-loops here.
592 """
593 def visit_ModuleNode(self, node):
594 self.comprehension_targets = {}
595 self.visitchildren(node)
596 return node
598 visit_Node = VisitorTransform.recurse_to_children
600 def visit_ComprehensionNode(self, node):
601 if type(node.loop) not in (Nodes.ForInStatNode,
602 Nodes.ForFromStatNode):
603 # this should not happen!
604 self.visitchildren(node)
605 return node
607 outer_comprehension_targets = self.comprehension_targets
608 self.comprehension_targets = outer_comprehension_targets.copy()
610 # find all NameNodes in the loop target
611 target_name_collector = NameNodeCollector()
612 target_name_collector.visit(node.loop.target)
613 targets = target_name_collector.name_nodes
615 # create a temp variable for each target name
616 temps = []
617 for target in targets:
618 handle = TempHandle(target.type)
619 temps.append(handle)
620 self.comprehension_targets[target.entry.cname] = handle.ref(node.pos)
622 # replace name references in the loop code by their temp node
623 self.visitchildren(node, ['loop'])
625 loop = node.loop
626 if type(loop) is Nodes.ForFromStatNode and loop.target.type.is_numeric:
627 loop.loopvar_node = loop.target
629 node.loop = TempsBlockNode(node.pos, body=node.loop, temps=temps)
630 self.comprehension_targets = outer_comprehension_targets
631 return node
633 def visit_NameNode(self, node):
634 if node.entry:
635 replacement = self.comprehension_targets.get(node.entry.cname)
636 if replacement is not None:
637 return replacement
638 return node
641 class DecoratorTransform(CythonTransform, SkipDeclarations):
643 def visit_DefNode(self, func_node):
644 if not func_node.decorators:
645 return func_node
647 decorator_result = NameNode(func_node.pos, name = func_node.name)
648 for decorator in func_node.decorators[::-1]:
649 decorator_result = SimpleCallNode(
650 decorator.pos,
651 function = decorator.decorator,
652 args = [decorator_result])
654 func_name_node = NameNode(func_node.pos, name = func_node.name)
655 reassignment = SingleAssignmentNode(
656 func_node.pos,
657 lhs = func_name_node,
658 rhs = decorator_result)
659 return [func_node, reassignment]
662 ERR_DEC_AFTER = "cdef variable '%s' declared after it's used."
663 class AnalyseDeclarationsTransform(CythonTransform):
665 basic_property = TreeFragment(u"""
666 property NAME:
667 def __get__(self):
668 return ATTR
669 def __set__(self, value):
670 ATTR = value
671 """, level='c_class')
673 def __call__(self, root):
674 self.env_stack = [root.scope]
675 # needed to determine if a cdef var is declared after it's used.
676 self.local_scope_stack = []
677 return super(AnalyseDeclarationsTransform, self).__call__(root)
679 def visit_NameNode(self, node):
680 self.local_scope_stack[-1].add(node.name)
681 return node
683 def visit_ModuleNode(self, node):
684 self.local_scope_stack.append(set())
685 node.analyse_declarations(self.env_stack[-1])
686 self.visitchildren(node)
687 self.local_scope_stack.pop()
688 return node
690 def visit_FuncDefNode(self, node):
691 self.local_scope_stack.append(set())
692 lenv = node.create_local_scope(self.env_stack[-1])
693 node.body.analyse_control_flow(lenv) # this will be totally refactored
694 node.declare_arguments(lenv)
695 for var, type_node in node.directive_locals.items():
696 if not lenv.lookup_here(var): # don't redeclare args
697 type = type_node.analyse_as_type(lenv)
698 if type:
699 lenv.declare_var(var, type, type_node.pos)
700 else:
701 error(type_node.pos, "Not a type")
702 node.body.analyse_declarations(lenv)
703 self.env_stack.append(lenv)
704 self.visitchildren(node)
705 self.env_stack.pop()
706 self.local_scope_stack.pop()
707 return node
709 # Some nodes are no longer needed after declaration
710 # analysis and can be dropped. The analysis was performed
711 # on these nodes in a seperate recursive process from the
712 # enclosing function or module, so we can simply drop them.
713 def visit_CDeclaratorNode(self, node):
714 # necessary to ensure that all CNameDeclaratorNodes are visited.
715 self.visitchildren(node)
716 return node
718 def visit_CTypeDefNode(self, node):
719 return node
721 def visit_CBaseTypeNode(self, node):
722 return None
724 def visit_CEnumDefNode(self, node):
725 return None
727 def visit_CStructOrUnionDefNode(self, node):
728 return None
730 def visit_CNameDeclaratorNode(self, node):
731 if node.name in self.local_scope_stack[-1]:
732 # cdef variable declared after it's used.
733 error(node.pos, ERR_DEC_AFTER % node.name)
734 self.visitchildren(node)
735 return node
737 def visit_CVarDefNode(self, node):
739 # to ensure all CNameDeclaratorNodes are visited.
740 self.visitchildren(node)
742 if node.need_properties:
743 # cdef public attributes may need type testing on
744 # assignment, so we create a property accesss
745 # mechanism for them.
746 stats = []
747 for entry in node.need_properties:
748 property = self.create_Property(entry)
749 property.analyse_declarations(node.dest_scope)
750 self.visit(property)
751 stats.append(property)
752 return StatListNode(pos=node.pos, stats=stats)
753 else:
754 return None
756 def create_Property(self, entry):
757 property = self.basic_property.substitute({
758 u"ATTR": AttributeNode(pos=entry.pos,
759 obj=NameNode(pos=entry.pos, name="self"),
760 attribute=entry.name),
761 }, pos=entry.pos).stats[0]
762 property.name = entry.name
763 return property
765 class AnalyseExpressionsTransform(CythonTransform):
767 def visit_ModuleNode(self, node):
768 node.body.analyse_expressions(node.scope)
769 self.visitchildren(node)
770 return node
772 def visit_FuncDefNode(self, node):
773 node.body.analyse_expressions(node.local_scope)
774 self.visitchildren(node)
775 return node
777 class AlignFunctionDefinitions(CythonTransform):
778 """
779 This class takes the signatures from a .pxd file and applies them to
780 the def methods in a .py file.
781 """
783 def visit_ModuleNode(self, node):
784 self.scope = node.scope
785 self.directives = node.directives
786 self.visitchildren(node)
787 return node
789 def visit_PyClassDefNode(self, node):
790 pxd_def = self.scope.lookup(node.name)
791 if pxd_def:
792 if pxd_def.is_cclass:
793 return self.visit_CClassDefNode(node.as_cclass(), pxd_def)
794 else:
795 error(node.pos, "'%s' redeclared" % node.name)
796 error(pxd_def.pos, "previous declaration here")
797 return None
798 else:
799 return node
801 def visit_CClassDefNode(self, node, pxd_def=None):
802 if pxd_def is None:
803 pxd_def = self.scope.lookup(node.class_name)
804 if pxd_def:
805 outer_scope = self.scope
806 self.scope = pxd_def.type.scope
807 self.visitchildren(node)
808 if pxd_def:
809 self.scope = outer_scope
810 return node
812 def visit_DefNode(self, node):
813 pxd_def = self.scope.lookup(node.name)
814 if pxd_def:
815 if pxd_def.is_cfunction:
816 node = node.as_cfunction(pxd_def)
817 else:
818 error(node.pos, "'%s' redeclared" % node.name)
819 error(pxd_def.pos, "previous declaration here")
820 return None
821 elif self.scope.is_module_scope and self.directives['auto_cpdef']:
822 node = node.as_cfunction(scope=self.scope)
823 # Enable this when internal def functions are allowed.
824 # self.visitchildren(node)
825 return node
828 class MarkClosureVisitor(CythonTransform):
830 needs_closure = False
832 def visit_FuncDefNode(self, node):
833 self.needs_closure = False
834 self.visitchildren(node)
835 node.needs_closure = self.needs_closure
836 self.needs_closure = True
837 return node
839 def visit_ClassDefNode(self, node):
840 self.visitchildren(node)
841 self.needs_closure = True
842 return node
844 def visit_YieldNode(self, node):
845 self.needs_closure = True
847 class CreateClosureClasses(CythonTransform):
848 # Output closure classes in module scope for all functions
849 # that need it.
851 def visit_ModuleNode(self, node):
852 self.module_scope = node.scope
853 self.visitchildren(node)
854 return node
856 def create_class_from_scope(self, node, target_module_scope):
857 as_name = temp_name_handle("closure")
858 func_scope = node.local_scope
860 entry = target_module_scope.declare_c_class(name = as_name,
861 pos = node.pos, defining = True, implementing = True)
862 class_scope = entry.type.scope
863 for entry in func_scope.entries.values():
864 class_scope.declare_var(pos=node.pos,
865 name=entry.name,
866 cname=entry.cname,
867 type=entry.type,
868 is_cdef=True)
870 def visit_FuncDefNode(self, node):
871 self.create_class_from_scope(node, self.module_scope)
872 return node
875 class EnvTransform(CythonTransform):
876 """
877 This transformation keeps a stack of the environments.
878 """
879 def __call__(self, root):
880 self.env_stack = [root.scope]
881 return super(EnvTransform, self).__call__(root)
883 def visit_FuncDefNode(self, node):
884 self.env_stack.append(node.local_scope)
885 self.visitchildren(node)
886 self.env_stack.pop()
887 return node
890 class TransformBuiltinMethods(EnvTransform):
892 def visit_SingleAssignmentNode(self, node):
893 if node.declaration_only:
894 return None
895 else:
896 self.visitchildren(node)
897 return node
899 def visit_AttributeNode(self, node):
900 return self.visit_cython_attribute(node)
902 def visit_NameNode(self, node):
903 return self.visit_cython_attribute(node)
905 def visit_cython_attribute(self, node):
906 attribute = node.as_cython_attribute()
907 if attribute:
908 if attribute == u'compiled':
909 node = BoolNode(node.pos, value=True)
910 elif attribute == u'NULL':
911 node = NullNode(node.pos)
912 elif not PyrexTypes.parse_basic_type(attribute):
913 error(node.pos, u"'%s' not a valid cython attribute or is being used incorrectly" % attribute)
914 return node
916 def visit_SimpleCallNode(self, node):
918 # locals builtin
919 if isinstance(node.function, ExprNodes.NameNode):
920 if node.function.name == 'locals':
921 pos = node.pos
922 lenv = self.env_stack[-1]
923 items = [ExprNodes.DictItemNode(pos,
924 key=ExprNodes.IdentifierStringNode(pos, value=var),
925 value=ExprNodes.NameNode(pos, name=var)) for var in lenv.entries]
926 return ExprNodes.DictNode(pos, key_value_pairs=items)
928 # cython.foo
929 function = node.function.as_cython_attribute()
930 if function:
931 if function == u'cast':
932 if len(node.args) != 2:
933 error(node.function.pos, u"cast takes exactly two arguments")
934 else:
935 type = node.args[0].analyse_as_type(self.env_stack[-1])
936 if type:
937 node = TypecastNode(node.function.pos, type=type, operand=node.args[1])
938 else:
939 error(node.args[0].pos, "Not a type")
940 elif function == u'sizeof':
941 if len(node.args) != 1:
942 error(node.function.pos, u"sizeof takes exactly one argument" % function)
943 else:
944 type = node.args[0].analyse_as_type(self.env_stack[-1])
945 if type:
946 node = SizeofTypeNode(node.function.pos, arg_type=type)
947 else:
948 node = SizeofVarNode(node.function.pos, operand=node.args[0])
949 elif function == 'address':
950 if len(node.args) != 1:
951 error(node.function.pos, u"sizeof takes exactly one argument" % function)
952 else:
953 node = AmpersandNode(node.function.pos, operand=node.args[0])
954 else:
955 error(node.function.pos, u"'%s' not a valid cython language construct" % function)
957 self.visitchildren(node)
958 return node
