Cython has moved to github.

cython-devel

view Cython/Compiler/ParseTreeTransforms.py @ 1661:9d0ac0d9659b

Kludge for #151
author Dag Sverre Seljebotn <dagss@student.matnat.uio.no>
date Thu Jan 29 19:19:06 2009 +0100 (3 years ago)
parents b3a305cf4a4b
children b1ca3f85761a
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
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:
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 = None
298 for stat in node.body.stats:
299 if not isinstance(stat, CVarDefNode):
300 err = self.ERR_INLINE_ONLY
301 break
302 node = CVarDefNode(node.pos,
303 visibility = node.visibility,
304 base_type = node.base_type,
305 declarators = [node.declarator],
306 in_pxd = True,
307 api = node.api,
308 overridable = node.overridable,
309 pxd_locals = node.body.stats)
310 if err:
311 self.context.nonfatal_error(PostParseError(node.pos, err))
312 return None
313 else:
314 return node
316 class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
317 """
318 After parsing, options can be stored in a number of places:
319 - #cython-comments at the top of the file (stored in ModuleNode)
320 - Command-line arguments overriding these
321 - @cython.optionname decorators
322 - with cython.optionname: statements
324 This transform is responsible for interpreting these various sources
325 and store the option in two ways:
326 - Set the directives attribute of the ModuleNode for global directives.
327 - Use a CompilerDirectivesNode to override directives for a subtree.
329 (The first one is primarily to not have to modify with the tree
330 structure, so that ModuleNode stay on top.)
332 The directives are stored in dictionaries from name to value in effect.
333 Each such dictionary is always filled in for all possible directives,
334 using default values where no value is given by the user.
336 The available directives are controlled in Options.py.
338 Note that we have to run this prior to analysis, and so some minor
339 duplication of functionality has to occur: We manually track cimports
340 and which names the "cython" module may have been imported to.
341 """
342 special_methods = set(['declare', 'union', 'struct', 'typedef', 'sizeof', 'cast', 'address', 'pointer', 'compiled', 'NULL'])
344 def __init__(self, context, compilation_option_overrides):
345 super(InterpretCompilerDirectives, self).__init__(context)
346 self.compilation_option_overrides = compilation_option_overrides
347 self.cython_module_names = set()
348 self.option_names = {}
350 # Set up processing and handle the cython: comments.
351 def visit_ModuleNode(self, node):
352 options = copy.copy(Options.option_defaults)
353 options.update(node.option_comments)
354 options.update(self.compilation_option_overrides)
355 self.options = options
356 node.directives = options
357 self.visitchildren(node)
358 node.cython_module_names = self.cython_module_names
359 return node
361 # Track cimports of the cython module.
362 def visit_CImportStatNode(self, node):
363 if node.module_name == u"cython":
364 if node.as_name:
365 modname = node.as_name
366 else:
367 modname = u"cython"
368 self.cython_module_names.add(modname)
369 return node
371 def visit_FromCImportStatNode(self, node):
372 if node.module_name == u"cython":
373 newimp = []
374 for pos, name, as_name, kind in node.imported_names:
375 if (name in Options.option_types or
376 name in self.special_methods or
377 PyrexTypes.parse_basic_type(name)):
378 if as_name is None:
379 as_name = name
380 self.option_names[as_name] = name
381 if kind is not None:
382 self.context.nonfatal_error(PostParseError(pos,
383 "Compiler option imports must be plain imports"))
384 else:
385 newimp.append((pos, name, as_name, kind))
386 if not newimp:
387 return None
388 node.imported_names = newimp
389 return node
391 def visit_FromImportStatNode(self, node):
392 if node.module.module_name.value == u"cython":
393 newimp = []
394 for name, name_node in node.items:
395 if (name in Options.option_types or
396 name in self.special_methods or
397 PyrexTypes.parse_basic_type(name)):
398 self.option_names[name_node.name] = name
399 else:
400 newimp.append((name, name_node))
401 if not newimp:
402 return None
403 node.items = newimp
404 return node
406 def visit_SingleAssignmentNode(self, node):
407 if (isinstance(node.rhs, ImportNode) and
408 node.rhs.module_name.value == u'cython'):
409 node = CImportStatNode(node.pos,
410 module_name = u'cython',
411 as_name = node.lhs.name)
412 self.visit_CImportStatNode(node)
413 else:
414 self.visitchildren(node)
415 return node
417 def visit_NameNode(self, node):
418 if node.name in self.cython_module_names:
419 node.is_cython_module = True
420 else:
421 node.cython_attribute = self.option_names.get(node.name)
422 return node
424 def try_to_parse_option(self, node):
425 # If node is the contents of an option (in a with statement or
426 # decorator), returns (optionname, value).
427 # Otherwise, returns None
428 optname = None
429 if isinstance(node, CallNode):
430 self.visit(node.function)
431 optname = node.function.as_cython_attribute()
433 if optname:
434 optiontype = Options.option_types.get(optname)
435 if optiontype:
436 args, kwds = node.explicit_args_kwds()
437 if optiontype is bool:
438 if kwds is not None or len(args) != 1 or not isinstance(args[0], BoolNode):
439 raise PostParseError(dec.function.pos,
440 'The %s option takes one compile-time boolean argument' % optname)
441 return (optname, args[0].value)
442 elif optiontype is dict:
443 if len(args) != 0:
444 raise PostParseError(dec.function.pos,
445 'The %s option takes no prepositional arguments' % optname)
446 return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
447 else:
448 assert False
450 return None
452 def visit_with_options(self, body, options):
453 oldoptions = self.options
454 newoptions = copy.copy(oldoptions)
455 newoptions.update(options)
456 self.options = newoptions
457 assert isinstance(body, StatListNode), body
458 retbody = self.visit_Node(body)
459 directive = CompilerDirectivesNode(pos=retbody.pos, body=retbody,
460 directives=newoptions)
461 self.options = oldoptions
462 return directive
464 # Handle decorators
465 def visit_DefNode(self, node):
466 options = []
468 if node.decorators:
469 # Split the decorators into two lists -- real decorators and options
470 realdecs = []
471 for dec in node.decorators:
472 option = self.try_to_parse_option(dec.decorator)
473 if option is not None:
474 options.append(option)
475 else:
476 realdecs.append(dec)
477 node.decorators = realdecs
479 if options:
480 optdict = {}
481 options.reverse() # Decorators coming first take precedence
482 for option in options:
483 name, value = option
484 optdict[name] = value
485 body = StatListNode(node.pos, stats=[node])
486 return self.visit_with_options(body, optdict)
487 else:
488 return self.visit_Node(node)
490 # Handle with statements
491 def visit_WithStatNode(self, node):
492 option = self.try_to_parse_option(node.manager)
493 if option is not None:
494 if node.target is not None:
495 raise PostParseError(node.pos, "Compiler option with statements cannot contain 'as'")
496 name, value = option
497 return self.visit_with_options(node.body, {name:value})
498 else:
499 return self.visit_Node(node)
501 class WithTransform(CythonTransform, SkipDeclarations):
503 # EXCINFO is manually set to a variable that contains
504 # the exc_info() tuple that can be generated by the enclosing except
505 # statement.
506 template_without_target = TreeFragment(u"""
507 MGR = EXPR
508 EXIT = MGR.__exit__
509 MGR.__enter__()
510 EXC = True
511 try:
512 try:
513 EXCINFO = None
514 BODY
515 except:
516 EXC = False
517 if not EXIT(*EXCINFO):
518 raise
519 finally:
520 if EXC:
521 EXIT(None, None, None)
522 """, temps=[u'MGR', u'EXC', u"EXIT"],
523 pipeline=[NormalizeTree(None)])
525 template_with_target = TreeFragment(u"""
526 MGR = EXPR
527 EXIT = MGR.__exit__
528 VALUE = MGR.__enter__()
529 EXC = True
530 try:
531 try:
532 EXCINFO = None
533 TARGET = VALUE
534 BODY
535 except:
536 EXC = False
537 if not EXIT(*EXCINFO):
538 raise
539 finally:
540 if EXC:
541 EXIT(None, None, None)
542 MGR = EXIT = VALUE = EXC = None
544 """, temps=[u'MGR', u'EXC', u"EXIT", u"VALUE"],
545 pipeline=[NormalizeTree(None)])
547 def visit_WithStatNode(self, node):
548 self.visitchildren(node, ['body'])
549 # FIXME: excinfo_temp should be more local to the except
550 # clause that uses it, to avoid the presetting to None
551 excinfo_temp = TempHandle(Builtin.tuple_type)
552 if node.target is not None:
553 result = self.template_with_target.substitute({
554 u'EXPR' : node.manager,
555 u'BODY' : node.body,
556 u'TARGET' : node.target,
557 u'EXCINFO' : excinfo_temp.ref(node.pos)
558 }, pos=node.pos)
559 else:
560 result = self.template_without_target.substitute({
561 u'EXPR' : node.manager,
562 u'BODY' : node.body,
563 u'EXCINFO' : excinfo_temp.ref(node.pos)
564 }, pos=node.pos)
566 # Set except excinfo target to EXCINFO
567 try_except = result.stats[-1].body.stats[-1]
568 try_except.except_clauses[0].excinfo_target = (
569 excinfo_temp.ref(node.pos))
571 result.stats[-1].body.stats[-1] = TempsBlockNode(
572 node.pos, temps=[excinfo_temp], body=try_except)
574 return result
576 def visit_ExprNode(self, node):
577 # With statements are never inside expressions.
578 return node
581 class ComprehensionTransform(VisitorTransform):
582 """Prevent the target of list/set/dict comprehensions from leaking by
583 moving it into a temp variable. This mimics the behaviour of all
584 comprehensions in Py3 and of generator expressions in Py2.x.
586 This must run before the IterationTransform, which might replace
587 for-loops with while-loops. We only handle for-loops here.
588 """
589 def visit_ModuleNode(self, node):
590 self.comprehension_targets = {}
591 self.visitchildren(node)
592 return node
594 visit_Node = VisitorTransform.recurse_to_children
596 def visit_ComprehensionNode(self, node):
597 if type(node.loop) not in (Nodes.ForInStatNode,
598 Nodes.ForFromStatNode):
599 # this should not happen!
600 self.visitchildren(node)
601 return node
603 outer_comprehension_targets = self.comprehension_targets
604 self.comprehension_targets = outer_comprehension_targets.copy()
606 # find all NameNodes in the loop target
607 target_name_collector = NameNodeCollector()
608 target_name_collector.visit(node.loop.target)
609 targets = target_name_collector.name_nodes
611 # create a temp variable for each target name
612 temps = []
613 for target in targets:
614 handle = TempHandle(target.type)
615 temps.append(handle)
616 self.comprehension_targets[target.entry.cname] = handle.ref(node.pos)
618 # replace name references in the loop code by their temp node
619 self.visitchildren(node, ['loop'])
621 loop = node.loop
622 if type(loop) is Nodes.ForFromStatNode and loop.target.type.is_numeric:
623 loop.loopvar_node = loop.target
625 node.loop = TempsBlockNode(node.pos, body=node.loop, temps=temps)
626 self.comprehension_targets = outer_comprehension_targets
627 return node
629 def visit_NameNode(self, node):
630 if node.entry:
631 replacement = self.comprehension_targets.get(node.entry.cname)
632 if replacement is not None:
633 return replacement
634 return node
637 class DecoratorTransform(CythonTransform, SkipDeclarations):
639 def visit_DefNode(self, func_node):
640 if not func_node.decorators:
641 return func_node
643 decorator_result = NameNode(func_node.pos, name = func_node.name)
644 for decorator in func_node.decorators[::-1]:
645 decorator_result = SimpleCallNode(
646 decorator.pos,
647 function = decorator.decorator,
648 args = [decorator_result])
650 func_name_node = NameNode(func_node.pos, name = func_node.name)
651 reassignment = SingleAssignmentNode(
652 func_node.pos,
653 lhs = func_name_node,
654 rhs = decorator_result)
655 return [func_node, reassignment]
657 class AnalyseDeclarationsTransform(CythonTransform):
659 basic_property = TreeFragment(u"""
660 property NAME:
661 def __get__(self):
662 return ATTR
663 def __set__(self, value):
664 ATTR = value
665 """, level='c_class')
667 def __call__(self, root):
668 self.env_stack = [root.scope]
669 return super(AnalyseDeclarationsTransform, self).__call__(root)
671 def visit_ModuleNode(self, node):
672 node.analyse_declarations(self.env_stack[-1])
673 self.visitchildren(node)
674 return node
676 def visit_FuncDefNode(self, node):
677 lenv = node.create_local_scope(self.env_stack[-1])
678 node.body.analyse_control_flow(lenv) # this will be totally refactored
679 node.declare_arguments(lenv)
680 for var, type_node in node.directive_locals.items():
681 if not lenv.lookup_here(var): # don't redeclare args
682 type = type_node.analyse_as_type(lenv)
683 if type:
684 lenv.declare_var(var, type, type_node.pos)
685 else:
686 error(type_node.pos, "Not a type")
687 for stat in node.pxd_locals:
688 stat.analyse_declarations(lenv)
689 node.body.analyse_declarations(lenv)
690 self.env_stack.append(lenv)
691 self.visitchildren(node)
692 self.env_stack.pop()
693 return node
695 # Some nodes are no longer needed after declaration
696 # analysis and can be dropped. The analysis was performed
697 # on these nodes in a seperate recursive process from the
698 # enclosing function or module, so we can simply drop them.
699 def visit_CDeclaratorNode(self, node):
700 return node
702 def visit_CTypeDefNode(self, node):
703 return node
705 def visit_CBaseTypeNode(self, node):
706 return None
708 def visit_CEnumDefNode(self, node):
709 return None
711 def visit_CStructOrUnionDefNode(self, node):
712 return None
714 def visit_CVarDefNode(self, node):
715 if node.need_properties:
716 # cdef public attributes may need type testing on
717 # assignment, so we create a property accesss
718 # mechanism for them.
719 stats = []
720 for entry in node.need_properties:
721 property = self.create_Property(entry)
722 property.analyse_declarations(node.dest_scope)
723 self.visit(property)
724 stats.append(property)
725 return StatListNode(pos=node.pos, stats=stats)
726 else:
727 return None
729 def create_Property(self, entry):
730 property = self.basic_property.substitute({
731 u"ATTR": AttributeNode(pos=entry.pos,
732 obj=NameNode(pos=entry.pos, name="self"),
733 attribute=entry.name),
734 }, pos=entry.pos).stats[0]
735 property.name = entry.name
736 return property
738 class AnalyseExpressionsTransform(CythonTransform):
740 def visit_ModuleNode(self, node):
741 node.body.analyse_expressions(node.scope)
742 self.visitchildren(node)
743 return node
745 def visit_FuncDefNode(self, node):
746 node.body.analyse_expressions(node.local_scope)
747 self.visitchildren(node)
748 return node
750 class AlignFunctionDefinitions(CythonTransform):
751 """
752 This class takes the signatures from a .pxd file and applies them to
753 the def methods in a .py file.
754 """
756 def visit_ModuleNode(self, node):
757 self.scope = node.scope
758 self.directives = node.directives
759 self.visitchildren(node)
760 return node
762 def visit_PyClassDefNode(self, node):
763 pxd_def = self.scope.lookup(node.name)
764 if pxd_def:
765 if pxd_def.is_cclass:
766 return self.visit_CClassDefNode(node.as_cclass(), pxd_def)
767 else:
768 error(node.pos, "'%s' redeclared" % node.name)
769 error(pxd_def.pos, "previous declaration here")
770 return None
771 else:
772 return node
774 def visit_CClassDefNode(self, node, pxd_def=None):
775 if pxd_def is None:
776 pxd_def = self.scope.lookup(node.class_name)
777 if pxd_def:
778 outer_scope = self.scope
779 self.scope = pxd_def.type.scope
780 self.visitchildren(node)
781 if pxd_def:
782 self.scope = outer_scope
783 return node
785 def visit_DefNode(self, node):
786 pxd_def = self.scope.lookup(node.name)
787 if pxd_def:
788 if pxd_def.is_cfunction:
789 node = node.as_cfunction(pxd_def)
790 else:
791 error(node.pos, "'%s' redeclared" % node.name)
792 error(pxd_def.pos, "previous declaration here")
793 return None
794 elif self.scope.is_module_scope and self.directives['auto_cpdef']:
795 node = node.as_cfunction(scope=self.scope)
796 # Enable this when internal def functions are allowed.
797 # self.visitchildren(node)
798 return node
801 class MarkClosureVisitor(CythonTransform):
803 needs_closure = False
805 def visit_FuncDefNode(self, node):
806 self.needs_closure = False
807 self.visitchildren(node)
808 node.needs_closure = self.needs_closure
809 self.needs_closure = True
810 return node
812 def visit_ClassDefNode(self, node):
813 self.visitchildren(node)
814 self.needs_closure = True
815 return node
817 def visit_YieldNode(self, node):
818 self.needs_closure = True
820 class CreateClosureClasses(CythonTransform):
821 # Output closure classes in module scope for all functions
822 # that need it.
824 def visit_ModuleNode(self, node):
825 self.module_scope = node.scope
826 self.visitchildren(node)
827 return node
829 def create_class_from_scope(self, node, target_module_scope):
830 as_name = temp_name_handle("closure")
831 func_scope = node.local_scope
833 entry = target_module_scope.declare_c_class(name = as_name,
834 pos = node.pos, defining = True, implementing = True)
835 class_scope = entry.type.scope
836 for entry in func_scope.entries.values():
837 class_scope.declare_var(pos=node.pos,
838 name=entry.name,
839 cname=entry.cname,
840 type=entry.type,
841 is_cdef=True)
843 def visit_FuncDefNode(self, node):
844 self.create_class_from_scope(node, self.module_scope)
845 return node
848 class EnvTransform(CythonTransform):
849 """
850 This transformation keeps a stack of the environments.
851 """
852 def __call__(self, root):
853 self.env_stack = [root.scope]
854 return super(EnvTransform, self).__call__(root)
856 def visit_FuncDefNode(self, node):
857 self.env_stack.append(node.local_scope)
858 self.visitchildren(node)
859 self.env_stack.pop()
860 return node
863 class TransformBuiltinMethods(EnvTransform):
865 def visit_SingleAssignmentNode(self, node):
866 if node.declaration_only:
867 return None
868 else:
869 self.visitchildren(node)
870 return node
872 def visit_AttributeNode(self, node):
873 return self.visit_cython_attribute(node)
875 def visit_NameNode(self, node):
876 return self.visit_cython_attribute(node)
878 def visit_cython_attribute(self, node):
879 attribute = node.as_cython_attribute()
880 if attribute:
881 if attribute == u'compiled':
882 node = BoolNode(node.pos, value=True)
883 elif attribute == u'NULL':
884 node = NullNode(node.pos)
885 elif not PyrexTypes.parse_basic_type(attribute):
886 error(node.pos, u"'%s' not a valid cython attribute or is being used incorrectly" % attribute)
887 return node
889 def visit_SimpleCallNode(self, node):
891 # locals builtin
892 if isinstance(node.function, ExprNodes.NameNode):
893 if node.function.name == 'locals':
894 pos = node.pos
895 lenv = self.env_stack[-1]
896 items = [ExprNodes.DictItemNode(pos,
897 key=ExprNodes.IdentifierStringNode(pos, value=var),
898 value=ExprNodes.NameNode(pos, name=var)) for var in lenv.entries]
899 return ExprNodes.DictNode(pos, key_value_pairs=items)
901 # cython.foo
902 function = node.function.as_cython_attribute()
903 if function:
904 if function == u'cast':
905 if len(node.args) != 2:
906 error(node.function.pos, u"cast takes exactly two arguments")
907 else:
908 type = node.args[0].analyse_as_type(self.env_stack[-1])
909 if type:
910 node = TypecastNode(node.function.pos, type=type, operand=node.args[1])
911 else:
912 error(node.args[0].pos, "Not a type")
913 elif function == u'sizeof':
914 if len(node.args) != 1:
915 error(node.function.pos, u"sizeof takes exactly one argument" % function)
916 else:
917 type = node.args[0].analyse_as_type(self.env_stack[-1])
918 if type:
919 node = SizeofTypeNode(node.function.pos, arg_type=type)
920 else:
921 node = SizeofVarNode(node.function.pos, operand=node.args[0])
922 elif function == 'address':
923 if len(node.args) != 1:
924 error(node.function.pos, u"sizeof takes exactly one argument" % function)
925 else:
926 node = AmpersandNode(node.function.pos, operand=node.args[0])
927 else:
928 error(node.function.pos, u"'%s' not a valid cython language construct" % function)
930 self.visitchildren(node)
931 return node