Cython has moved to github.
cython-devel
view Cython/Compiler/ParseTreeTransforms.py @ 2054:ddaf43291b55
Fixing up flawed fix for #303
| author | Dag Sverre Seljebotn <dagss@student.matnat.uio.no> |
|---|---|
| date | Thu May 14 17:28:50 2009 +0200 (3 years ago) |
| parents | 53e4979c9b84 |
| children | 26a61c2131fd |
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 = {}
336 for key, value in compilation_option_overrides.iteritems():
337 self.compilation_option_overrides[unicode(key)] = value
338 self.cython_module_names = set()
339 self.option_names = {}
341 # Set up processing and handle the cython: comments.
342 def visit_ModuleNode(self, node):
343 options = copy.copy(Options.option_defaults)
344 for key, value in self.compilation_option_overrides.iteritems():
345 if key in node.option_comments and node.option_comments[key] != value:
346 warning(node.pos, "Compiler directive differs between environment and file header; this will change "
347 "in Cython 0.12. See http://article.gmane.org/gmane.comp.python.cython.devel/5233", 2)
348 break
349 options.update(node.option_comments)
350 options.update(self.compilation_option_overrides)
351 self.options = options
352 node.directives = options
353 self.visitchildren(node)
354 node.cython_module_names = self.cython_module_names
355 return node
357 # Track cimports of the cython module.
358 def visit_CImportStatNode(self, node):
359 if node.module_name == u"cython":
360 if node.as_name:
361 modname = node.as_name
362 else:
363 modname = u"cython"
364 self.cython_module_names.add(modname)
365 return node
367 def visit_FromCImportStatNode(self, node):
368 if node.module_name == u"cython":
369 newimp = []
370 for pos, name, as_name, kind in node.imported_names:
371 if (name in Options.option_types or
372 name in self.special_methods or
373 PyrexTypes.parse_basic_type(name)):
374 if as_name is None:
375 as_name = name
376 self.option_names[as_name] = name
377 if kind is not None:
378 self.context.nonfatal_error(PostParseError(pos,
379 "Compiler option imports must be plain imports"))
380 else:
381 newimp.append((pos, name, as_name, kind))
382 if not newimp:
383 return None
384 node.imported_names = newimp
385 return node
387 def visit_FromImportStatNode(self, node):
388 if node.module.module_name.value == u"cython":
389 newimp = []
390 for name, name_node in node.items:
391 if (name in Options.option_types or
392 name in self.special_methods or
393 PyrexTypes.parse_basic_type(name)):
394 self.option_names[name_node.name] = name
395 else:
396 newimp.append((name, name_node))
397 if not newimp:
398 return None
399 node.items = newimp
400 return node
402 def visit_SingleAssignmentNode(self, node):
403 if (isinstance(node.rhs, ImportNode) and
404 node.rhs.module_name.value == u'cython'):
405 node = CImportStatNode(node.pos,
406 module_name = u'cython',
407 as_name = node.lhs.name)
408 self.visit_CImportStatNode(node)
409 else:
410 self.visitchildren(node)
411 return node
413 def visit_NameNode(self, node):
414 if node.name in self.cython_module_names:
415 node.is_cython_module = True
416 else:
417 node.cython_attribute = self.option_names.get(node.name)
418 return node
420 def try_to_parse_option(self, node):
421 # If node is the contents of an option (in a with statement or
422 # decorator), returns (optionname, value).
423 # Otherwise, returns None
424 optname = None
425 if isinstance(node, CallNode):
426 self.visit(node.function)
427 optname = node.function.as_cython_attribute()
429 if optname:
430 optiontype = Options.option_types.get(optname)
431 if optiontype:
432 args, kwds = node.explicit_args_kwds()
433 if optiontype is bool:
434 if kwds is not None or len(args) != 1 or not isinstance(args[0], BoolNode):
435 raise PostParseError(dec.function.pos,
436 'The %s option takes one compile-time boolean argument' % optname)
437 return (optname, args[0].value)
438 elif optiontype is dict:
439 if len(args) != 0:
440 raise PostParseError(dec.function.pos,
441 'The %s option takes no prepositional arguments' % optname)
442 return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
443 else:
444 assert False
446 return None
448 def visit_with_options(self, body, options):
449 oldoptions = self.options
450 newoptions = copy.copy(oldoptions)
451 newoptions.update(options)
452 self.options = newoptions
453 assert isinstance(body, StatListNode), body
454 retbody = self.visit_Node(body)
455 directive = CompilerDirectivesNode(pos=retbody.pos, body=retbody,
456 directives=newoptions)
457 self.options = oldoptions
458 return directive
460 # Handle decorators
461 def visit_FuncDefNode(self, node):
462 options = []
464 if node.decorators:
465 # Split the decorators into two lists -- real decorators and options
466 realdecs = []
467 for dec in node.decorators:
468 option = self.try_to_parse_option(dec.decorator)
469 if option is not None:
470 options.append(option)
471 else:
472 realdecs.append(dec)
473 if realdecs and isinstance(node, CFuncDefNode):
474 raise PostParseError(realdecs[0].pos, "Cdef functions cannot take arbitrary decorators.")
475 else:
476 node.decorators = realdecs
478 if options:
479 optdict = {}
480 options.reverse() # Decorators coming first take precedence
481 for option in options:
482 name, value = option
483 optdict[name] = value
484 body = StatListNode(node.pos, stats=[node])
485 return self.visit_with_options(body, optdict)
486 else:
487 return self.visit_Node(node)
489 def visit_CVarDefNode(self, node):
490 if node.decorators:
491 for dec in node.decorators:
492 option = self.try_to_parse_option(dec.decorator)
493 if option is not None and option[0] == u'locals':
494 node.directive_locals = option[1]
495 else:
496 raise PostParseError(dec.pos, "Cdef functions can only take cython.locals() decorator.")
497 return node
499 # Handle with statements
500 def visit_WithStatNode(self, node):
501 option = self.try_to_parse_option(node.manager)
502 if option is not None:
503 if node.target is not None:
504 raise PostParseError(node.pos, "Compiler option with statements cannot contain 'as'")
505 name, value = option
506 return self.visit_with_options(node.body, {name:value})
507 else:
508 return self.visit_Node(node)
510 class WithTransform(CythonTransform, SkipDeclarations):
512 # EXCINFO is manually set to a variable that contains
513 # the exc_info() tuple that can be generated by the enclosing except
514 # statement.
515 template_without_target = TreeFragment(u"""
516 MGR = EXPR
517 EXIT = MGR.__exit__
518 MGR.__enter__()
519 EXC = True
520 try:
521 try:
522 EXCINFO = None
523 BODY
524 except:
525 EXC = False
526 if not EXIT(*EXCINFO):
527 raise
528 finally:
529 if EXC:
530 EXIT(None, None, None)
531 """, temps=[u'MGR', u'EXC', u"EXIT"],
532 pipeline=[NormalizeTree(None)])
534 template_with_target = TreeFragment(u"""
535 MGR = EXPR
536 EXIT = MGR.__exit__
537 VALUE = MGR.__enter__()
538 EXC = True
539 try:
540 try:
541 EXCINFO = None
542 TARGET = VALUE
543 BODY
544 except:
545 EXC = False
546 if not EXIT(*EXCINFO):
547 raise
548 finally:
549 if EXC:
550 EXIT(None, None, None)
551 MGR = EXIT = VALUE = EXC = None
553 """, temps=[u'MGR', u'EXC', u"EXIT", u"VALUE"],
554 pipeline=[NormalizeTree(None)])
556 def visit_WithStatNode(self, node):
557 # TODO: Cleanup badly needed
558 TemplateTransform.temp_name_counter += 1
559 handle = "__tmpvar_%d" % TemplateTransform.temp_name_counter
561 self.visitchildren(node, ['body'])
562 excinfo_temp = NameNode(node.pos, name=handle)#TempHandle(Builtin.tuple_type)
563 if node.target is not None:
564 result = self.template_with_target.substitute({
565 u'EXPR' : node.manager,
566 u'BODY' : node.body,
567 u'TARGET' : node.target,
568 u'EXCINFO' : excinfo_temp
569 }, pos=node.pos)
570 else:
571 result = self.template_without_target.substitute({
572 u'EXPR' : node.manager,
573 u'BODY' : node.body,
574 u'EXCINFO' : excinfo_temp
575 }, pos=node.pos)
577 # Set except excinfo target to EXCINFO
578 try_except = result.stats[-1].body.stats[-1]
579 try_except.except_clauses[0].excinfo_target = NameNode(node.pos, name=handle)
580 # excinfo_temp.ref(node.pos))
582 # result.stats[-1].body.stats[-1] = TempsBlockNode(
583 # node.pos, temps=[excinfo_temp], body=try_except)
585 return result
587 def visit_ExprNode(self, node):
588 # With statements are never inside expressions.
589 return node
592 class ComprehensionTransform(VisitorTransform):
593 """Prevent the target of list/set/dict comprehensions from leaking by
594 moving it into a temp variable. This mimics the behaviour of all
595 comprehensions in Py3 and of generator expressions in Py2.x.
597 This must run before the IterationTransform, which might replace
598 for-loops with while-loops. We only handle for-loops here.
599 """
600 def visit_ModuleNode(self, node):
601 self.comprehension_targets = {}
602 self.visitchildren(node)
603 return node
605 visit_Node = VisitorTransform.recurse_to_children
607 def visit_ComprehensionNode(self, node):
608 if type(node.loop) not in (Nodes.ForInStatNode,
609 Nodes.ForFromStatNode):
610 # this should not happen!
611 self.visitchildren(node)
612 return node
614 outer_comprehension_targets = self.comprehension_targets
615 self.comprehension_targets = outer_comprehension_targets.copy()
617 # find all NameNodes in the loop target
618 target_name_collector = NameNodeCollector()
619 target_name_collector.visit(node.loop.target)
620 targets = target_name_collector.name_nodes
622 # create a temp variable for each target name
623 temps = []
624 for target in targets:
625 handle = TempHandle(target.type)
626 temps.append(handle)
627 self.comprehension_targets[target.entry.cname] = handle.ref(node.pos)
629 # replace name references in the loop code by their temp node
630 self.visitchildren(node, ['loop'])
632 loop = node.loop
633 if type(loop) is Nodes.ForFromStatNode and loop.target.type.is_numeric:
634 loop.loopvar_node = loop.target
636 node.loop = TempsBlockNode(node.pos, body=node.loop, temps=temps)
637 self.comprehension_targets = outer_comprehension_targets
638 return node
640 def visit_NameNode(self, node):
641 if node.entry:
642 replacement = self.comprehension_targets.get(node.entry.cname)
643 if replacement is not None:
644 return replacement
645 return node
648 class DecoratorTransform(CythonTransform, SkipDeclarations):
650 def visit_DefNode(self, func_node):
651 if not func_node.decorators:
652 return func_node
654 decorator_result = NameNode(func_node.pos, name = func_node.name)
655 for decorator in func_node.decorators[::-1]:
656 decorator_result = SimpleCallNode(
657 decorator.pos,
658 function = decorator.decorator,
659 args = [decorator_result])
661 func_name_node = NameNode(func_node.pos, name = func_node.name)
662 reassignment = SingleAssignmentNode(
663 func_node.pos,
664 lhs = func_name_node,
665 rhs = decorator_result)
666 return [func_node, reassignment]
669 class AnalyseDeclarationsTransform(CythonTransform):
671 basic_property = TreeFragment(u"""
672 property NAME:
673 def __get__(self):
674 return ATTR
675 def __set__(self, value):
676 ATTR = value
677 """, level='c_class')
679 readonly_property = TreeFragment(u"""
680 property NAME:
681 def __get__(self):
682 return ATTR
683 """, level='c_class')
685 def __call__(self, root):
686 self.env_stack = [root.scope]
687 # needed to determine if a cdef var is declared after it's used.
688 self.seen_vars_stack = []
689 return super(AnalyseDeclarationsTransform, self).__call__(root)
691 def visit_NameNode(self, node):
692 self.seen_vars_stack[-1].add(node.name)
693 return node
695 def visit_ModuleNode(self, node):
696 self.seen_vars_stack.append(set())
697 node.analyse_declarations(self.env_stack[-1])
698 self.visitchildren(node)
699 self.seen_vars_stack.pop()
700 return node
702 def visit_FuncDefNode(self, node):
703 self.seen_vars_stack.append(set())
704 lenv = node.create_local_scope(self.env_stack[-1])
705 node.body.analyse_control_flow(lenv) # this will be totally refactored
706 node.declare_arguments(lenv)
707 for var, type_node in node.directive_locals.items():
708 if not lenv.lookup_here(var): # don't redeclare args
709 type = type_node.analyse_as_type(lenv)
710 if type:
711 lenv.declare_var(var, type, type_node.pos)
712 else:
713 error(type_node.pos, "Not a type")
714 node.body.analyse_declarations(lenv)
715 self.env_stack.append(lenv)
716 self.visitchildren(node)
717 self.env_stack.pop()
718 self.seen_vars_stack.pop()
719 return node
721 # Some nodes are no longer needed after declaration
722 # analysis and can be dropped. The analysis was performed
723 # on these nodes in a seperate recursive process from the
724 # enclosing function or module, so we can simply drop them.
725 def visit_CDeclaratorNode(self, node):
726 # necessary to ensure that all CNameDeclaratorNodes are visited.
727 self.visitchildren(node)
728 return node
730 def visit_CTypeDefNode(self, node):
731 return node
733 def visit_CBaseTypeNode(self, node):
734 return None
736 def visit_CEnumDefNode(self, node):
737 return None
739 def visit_CStructOrUnionDefNode(self, node):
740 return None
742 def visit_CNameDeclaratorNode(self, node):
743 if node.name in self.seen_vars_stack[-1]:
744 entry = self.env_stack[-1].lookup(node.name)
745 if entry is None or entry.visibility != 'extern':
746 warning(node.pos, "cdef variable '%s' declared after it is used" % node.name, 2)
747 self.visitchildren(node)
748 return node
750 def visit_CVarDefNode(self, node):
752 # to ensure all CNameDeclaratorNodes are visited.
753 self.visitchildren(node)
755 if node.need_properties:
756 # cdef public attributes may need type testing on
757 # assignment, so we create a property accesss
758 # mechanism for them.
759 stats = []
760 for entry in node.need_properties:
761 property = self.create_Property(entry, node.visibility == 'readonly')
762 property.analyse_declarations(node.dest_scope)
763 self.visit(property)
764 stats.append(property)
765 return StatListNode(pos=node.pos, stats=stats)
766 else:
767 return None
769 def create_Property(self, entry, readonly):
770 if readonly:
771 template = self.readonly_property
772 else:
773 template = self.basic_property
774 property = template.substitute({
775 u"ATTR": AttributeNode(pos=entry.pos,
776 obj=NameNode(pos=entry.pos, name="self"),
777 attribute=entry.name),
778 }, pos=entry.pos).stats[0]
779 property.name = entry.name
780 return property
782 class AnalyseExpressionsTransform(CythonTransform):
784 def visit_ModuleNode(self, node):
785 node.body.analyse_expressions(node.scope)
786 self.visitchildren(node)
787 return node
789 def visit_FuncDefNode(self, node):
790 node.body.analyse_expressions(node.local_scope)
791 self.visitchildren(node)
792 return node
794 class AlignFunctionDefinitions(CythonTransform):
795 """
796 This class takes the signatures from a .pxd file and applies them to
797 the def methods in a .py file.
798 """
800 def visit_ModuleNode(self, node):
801 self.scope = node.scope
802 self.directives = node.directives
803 self.visitchildren(node)
804 return node
806 def visit_PyClassDefNode(self, node):
807 pxd_def = self.scope.lookup(node.name)
808 if pxd_def:
809 if pxd_def.is_cclass:
810 return self.visit_CClassDefNode(node.as_cclass(), pxd_def)
811 else:
812 error(node.pos, "'%s' redeclared" % node.name)
813 error(pxd_def.pos, "previous declaration here")
814 return None
815 else:
816 return node
818 def visit_CClassDefNode(self, node, pxd_def=None):
819 if pxd_def is None:
820 pxd_def = self.scope.lookup(node.class_name)
821 if pxd_def:
822 outer_scope = self.scope
823 self.scope = pxd_def.type.scope
824 self.visitchildren(node)
825 if pxd_def:
826 self.scope = outer_scope
827 return node
829 def visit_DefNode(self, node):
830 pxd_def = self.scope.lookup(node.name)
831 if pxd_def:
832 if self.scope.is_c_class_scope and len(pxd_def.type.args) > 0:
833 # The self parameter type needs adjusting.
834 pxd_def.type.args[0].type = self.scope.parent_type
835 if pxd_def.is_cfunction:
836 node = node.as_cfunction(pxd_def)
837 else:
838 error(node.pos, "'%s' redeclared" % node.name)
839 error(pxd_def.pos, "previous declaration here")
840 return None
841 elif self.scope.is_module_scope and self.directives['auto_cpdef']:
842 node = node.as_cfunction(scope=self.scope)
843 # Enable this when internal def functions are allowed.
844 # self.visitchildren(node)
845 return node
848 class MarkClosureVisitor(CythonTransform):
850 needs_closure = False
852 def visit_FuncDefNode(self, node):
853 self.needs_closure = False
854 self.visitchildren(node)
855 node.needs_closure = self.needs_closure
856 self.needs_closure = True
857 return node
859 def visit_ClassDefNode(self, node):
860 self.visitchildren(node)
861 self.needs_closure = True
862 return node
864 def visit_YieldNode(self, node):
865 self.needs_closure = True
867 class CreateClosureClasses(CythonTransform):
868 # Output closure classes in module scope for all functions
869 # that need it.
871 def visit_ModuleNode(self, node):
872 self.module_scope = node.scope
873 self.visitchildren(node)
874 return node
876 def create_class_from_scope(self, node, target_module_scope):
877 as_name = temp_name_handle("closure")
878 func_scope = node.local_scope
880 entry = target_module_scope.declare_c_class(name = as_name,
881 pos = node.pos, defining = True, implementing = True)
882 class_scope = entry.type.scope
883 for entry in func_scope.entries.values():
884 class_scope.declare_var(pos=node.pos,
885 name=entry.name,
886 cname=entry.cname,
887 type=entry.type,
888 is_cdef=True)
890 def visit_FuncDefNode(self, node):
891 self.create_class_from_scope(node, self.module_scope)
892 return node
895 class EnvTransform(CythonTransform):
896 """
897 This transformation keeps a stack of the environments.
898 """
899 def __call__(self, root):
900 self.env_stack = [root.scope]
901 return super(EnvTransform, self).__call__(root)
903 def visit_FuncDefNode(self, node):
904 self.env_stack.append(node.local_scope)
905 self.visitchildren(node)
906 self.env_stack.pop()
907 return node
910 class TransformBuiltinMethods(EnvTransform):
912 def visit_SingleAssignmentNode(self, node):
913 if node.declaration_only:
914 return None
915 else:
916 self.visitchildren(node)
917 return node
919 def visit_AttributeNode(self, node):
920 return self.visit_cython_attribute(node)
922 def visit_NameNode(self, node):
923 return self.visit_cython_attribute(node)
925 def visit_cython_attribute(self, node):
926 attribute = node.as_cython_attribute()
927 if attribute:
928 if attribute == u'compiled':
929 node = BoolNode(node.pos, value=True)
930 elif attribute == u'NULL':
931 node = NullNode(node.pos)
932 elif not PyrexTypes.parse_basic_type(attribute):
933 error(node.pos, u"'%s' not a valid cython attribute or is being used incorrectly" % attribute)
934 return node
936 def visit_SimpleCallNode(self, node):
938 # locals builtin
939 if isinstance(node.function, ExprNodes.NameNode):
940 if node.function.name == 'locals':
941 pos = node.pos
942 lenv = self.env_stack[-1]
943 items = [ExprNodes.DictItemNode(pos,
944 key=ExprNodes.IdentifierStringNode(pos, value=var),
945 value=ExprNodes.NameNode(pos, name=var)) for var in lenv.entries]
946 return ExprNodes.DictNode(pos, key_value_pairs=items)
948 # cython.foo
949 function = node.function.as_cython_attribute()
950 if function:
951 if function == u'cast':
952 if len(node.args) != 2:
953 error(node.function.pos, u"cast takes exactly two arguments")
954 else:
955 type = node.args[0].analyse_as_type(self.env_stack[-1])
956 if type:
957 node = TypecastNode(node.function.pos, type=type, operand=node.args[1])
958 else:
959 error(node.args[0].pos, "Not a type")
960 elif function == u'sizeof':
961 if len(node.args) != 1:
962 error(node.function.pos, u"sizeof takes exactly one argument" % function)
963 else:
964 type = node.args[0].analyse_as_type(self.env_stack[-1])
965 if type:
966 node = SizeofTypeNode(node.function.pos, arg_type=type)
967 else:
968 node = SizeofVarNode(node.function.pos, operand=node.args[0])
969 elif function == 'address':
970 if len(node.args) != 1:
971 error(node.function.pos, u"sizeof takes exactly one argument" % function)
972 else:
973 node = AmpersandNode(node.function.pos, operand=node.args[0])
974 elif function == 'cmod':
975 if len(node.args) != 2:
976 error(node.function.pos, u"cmod takes exactly one argument" % function)
977 else:
978 node = binop_node(node.function.pos, '%', node.args[0], node.args[1])
979 node.cdivision = True
980 elif function == 'cdiv':
981 if len(node.args) != 2:
982 error(node.function.pos, u"cmod takes exactly one argument" % function)
983 else:
984 node = binop_node(node.function.pos, '/', node.args[0], node.args[1])
985 node.cdivision = True
986 else:
987 error(node.function.pos, u"'%s' not a valid cython language construct" % function)
989 self.visitchildren(node)
990 return node
