Cython has moved to github.

cython-devel

view Cython/Compiler/ParseTreeTransforms.py @ 1409:151d8366f329

Inline function definitions in pxd files
author Dag Sverre Seljebotn <dagss@student.matnat.uio.no>
date Thu Nov 27 19:29:12 2008 +0100 (3 years ago)
parents bfd2c54fbe62
children 918376aaff2e
line source
1 from Cython.Compiler.Visitor import VisitorTransform, CythonTransform
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
15 class NormalizeTree(CythonTransform):
16 """
17 This transform fixes up a few things after parsing
18 in order to make the parse tree more suitable for
19 transforms.
21 a) After parsing, blocks with only one statement will
22 be represented by that statement, not by a StatListNode.
23 When doing transforms this is annoying and inconsistent,
24 as one cannot in general remove a statement in a consistent
25 way and so on. This transform wraps any single statements
26 in a StatListNode containing a single statement.
28 b) The PassStatNode is a noop and serves no purpose beyond
29 plugging such one-statement blocks; i.e., once parsed a
30 ` "pass" can just as well be represented using an empty
31 StatListNode. This means less special cases to worry about
32 in subsequent transforms (one always checks to see if a
33 StatListNode has no children to see if the block is empty).
34 """
36 def __init__(self, context):
37 super(NormalizeTree, self).__init__(context)
38 self.is_in_statlist = False
39 self.is_in_expr = False
41 def visit_ExprNode(self, node):
42 stacktmp = self.is_in_expr
43 self.is_in_expr = True
44 self.visitchildren(node)
45 self.is_in_expr = stacktmp
46 return node
48 def visit_StatNode(self, node, is_listcontainer=False):
49 stacktmp = self.is_in_statlist
50 self.is_in_statlist = is_listcontainer
51 self.visitchildren(node)
52 self.is_in_statlist = stacktmp
53 if not self.is_in_statlist and not self.is_in_expr:
54 return StatListNode(pos=node.pos, stats=[node])
55 else:
56 return node
58 def visit_StatListNode(self, node):
59 self.is_in_statlist = True
60 self.visitchildren(node)
61 self.is_in_statlist = False
62 return node
64 def visit_ParallelAssignmentNode(self, node):
65 return self.visit_StatNode(node, True)
67 def visit_CEnumDefNode(self, node):
68 return self.visit_StatNode(node, True)
70 def visit_CStructOrUnionDefNode(self, node):
71 return self.visit_StatNode(node, True)
73 # Eliminate PassStatNode
74 def visit_PassStatNode(self, node):
75 if not self.is_in_statlist:
76 return StatListNode(pos=node.pos, stats=[])
77 else:
78 return []
81 class PostParseError(CompileError): pass
83 # error strings checked by unit tests, so define them
84 ERR_CDEF_INCLASS = 'Cannot assign default value to fields in cdef classes, structs or unions'
85 ERR_BUF_LOCALONLY = 'Buffer types only allowed as function local variables'
86 ERR_BUF_DEFAULTS = 'Invalid buffer defaults specification (see docs)'
87 ERR_INVALID_SPECIALATTR_TYPE = 'Special attributes must not have a type declared'
88 class PostParse(CythonTransform):
89 """
90 Basic interpretation of the parse tree, as well as validity
91 checking that can be done on a very basic level on the parse
92 tree (while still not being a problem with the basic syntax,
93 as such).
95 Specifically:
96 - Default values to cdef assignments are turned into single
97 assignments following the declaration (everywhere but in class
98 bodies, where they raise a compile error)
100 - Interpret some node structures into Python runtime values.
101 Some nodes take compile-time arguments (currently:
102 CBufferAccessTypeNode[args] and __cythonbufferdefaults__ = {args}),
103 which should be interpreted. This happens in a general way
104 and other steps should be taken to ensure validity.
106 Type arguments cannot be interpreted in this way.
108 - For __cythonbufferdefaults__ the arguments are checked for
109 validity.
111 CBufferAccessTypeNode has its options interpreted:
112 Any first positional argument goes into the "dtype" attribute,
113 any "ndim" keyword argument goes into the "ndim" attribute and
114 so on. Also it is checked that the option combination is valid.
115 - __cythonbufferdefaults__ attributes are parsed and put into the
116 type information.
118 Note: Currently Parsing.py does a lot of interpretation and
119 reorganization that can be refactored into this transform
120 if a more pure Abstract Syntax Tree is wanted.
121 """
123 # Track our context.
124 scope_type = None # can be either of 'module', 'function', 'class'
126 def __init__(self, context):
127 super(PostParse, self).__init__(context)
128 self.specialattribute_handlers = {
129 '__cythonbufferdefaults__' : self.handle_bufferdefaults
130 }
132 def visit_ModuleNode(self, node):
133 self.scope_type = 'module'
134 self.scope_node = node
135 self.visitchildren(node)
136 return node
138 def visit_scope(self, node, scope_type):
139 prev = self.scope_type, self.scope_node
140 self.scope_type = scope_type
141 self.scope_node = node
142 self.visitchildren(node)
143 self.scope_type, self.scope_node = prev
144 return node
146 def visit_ClassDefNode(self, node):
147 return self.visit_scope(node, 'class')
149 def visit_FuncDefNode(self, node):
150 return self.visit_scope(node, 'function')
152 def visit_CStructOrUnionDefNode(self, node):
153 return self.visit_scope(node, 'struct')
155 # cdef variables
156 def handle_bufferdefaults(self, decl):
157 if not isinstance(decl.default, DictNode):
158 raise PostParseError(decl.pos, ERR_BUF_DEFAULTS)
159 self.scope_node.buffer_defaults_node = decl.default
160 self.scope_node.buffer_defaults_pos = decl.pos
162 def visit_CVarDefNode(self, node):
163 # This assumes only plain names and pointers are assignable on
164 # declaration. Also, it makes use of the fact that a cdef decl
165 # must appear before the first use, so we don't have to deal with
166 # "i = 3; cdef int i = i" and can simply move the nodes around.
167 try:
168 self.visitchildren(node)
169 stats = [node]
170 newdecls = []
171 for decl in node.declarators:
172 declbase = decl
173 while isinstance(declbase, CPtrDeclaratorNode):
174 declbase = declbase.base
175 if isinstance(declbase, CNameDeclaratorNode):
176 if declbase.default is not None:
177 if self.scope_type in ('class', 'struct'):
178 if isinstance(self.scope_node, CClassDefNode):
179 handler = self.specialattribute_handlers.get(decl.name)
180 if handler:
181 if decl is not declbase:
182 raise PostParseError(decl.pos, ERR_INVALID_SPECIALATTR_TYPE)
183 handler(decl)
184 continue # Remove declaration
185 raise PostParseError(decl.pos, ERR_CDEF_INCLASS)
186 first_assignment = self.scope_type != 'module'
187 stats.append(SingleAssignmentNode(node.pos,
188 lhs=NameNode(node.pos, name=declbase.name),
189 rhs=declbase.default, first=first_assignment))
190 declbase.default = None
191 newdecls.append(decl)
192 node.declarators = newdecls
193 return stats
194 except PostParseError, e:
195 # An error in a cdef clause is ok, simply remove the declaration
196 # and try to move on to report more errors
197 self.context.nonfatal_error(e)
198 return None
200 def visit_CBufferAccessTypeNode(self, node):
201 if not self.scope_type == 'function':
202 raise PostParseError(node.pos, ERR_BUF_LOCALONLY)
203 return node
205 class PxdPostParse(CythonTransform):
206 """
207 Basic interpretation/validity checking that should only be
208 done on pxd trees.
210 A lot of this checking currently happens in the parser; but
211 what is listed below happens here.
213 - "def" functions are let through only if they fill the
214 getbuffer/releasebuffer slots
216 - cdef functions are let through only if they are on the
217 top level and are declared "inline"
218 """
219 ERR_INLINE_ONLY = "function definition in pxd file must be declared 'cdef inline'"
220 ERR_NOGO_WITH_INLINE = "inline function definition in pxd file cannot be '%s'"
222 def __call__(self, node):
223 self.scope_type = 'pxd'
224 return super(PxdPostParse, self).__call__(node)
226 def visit_CClassDefNode(self, node):
227 old = self.scope_type
228 self.scope_type = 'cclass'
229 self.visitchildren(node)
230 self.scope_type = old
231 return node
233 def visit_FuncDefNode(self, node):
234 # FuncDefNode always come with an implementation (without
235 # an imp they are CVarDefNodes..)
236 err = self.ERR_INLINE_ONLY
238 if (isinstance(node, DefNode) and self.scope_type == 'cclass'
239 and node.name in ('__getbuffer__', '__releasebuffer__')):
240 err = None # allow these slots
242 if isinstance(node, CFuncDefNode):
243 if u'inline' in node.modifiers and self.scope_type == 'pxd':
244 node.inline_in_pxd = True
245 if node.visibility != 'private':
246 err = self.ERR_NOGO_WITH_INLINE % node.visibility
247 elif node.api:
248 err = self.ERR_NOGO_WITH_INLINE % 'api'
249 else:
250 err = None # allow inline function
251 else:
252 err = None
253 for stat in node.body.stats:
254 if not isinstance(stat, CVarDefNode):
255 err = self.ERR_INLINE_ONLY
256 break
257 node = CVarDefNode(node.pos,
258 visibility = node.visibility,
259 base_type = node.base_type,
260 declarators = [node.declarator],
261 in_pxd = True,
262 api = node.api,
263 overridable = node.overridable,
264 pxd_locals = node.body.stats)
265 if err:
266 self.context.nonfatal_error(PostParseError(node.pos, err))
267 return None
268 else:
269 return node
271 class InterpretCompilerDirectives(CythonTransform):
272 """
273 After parsing, options can be stored in a number of places:
274 - #cython-comments at the top of the file (stored in ModuleNode)
275 - Command-line arguments overriding these
276 - @cython.optionname decorators
277 - with cython.optionname: statements
279 This transform is responsible for interpreting these various sources
280 and store the option in two ways:
281 - Set the directives attribute of the ModuleNode for global directives.
282 - Use a CompilerDirectivesNode to override directives for a subtree.
284 (The first one is primarily to not have to modify with the tree
285 structure, so that ModuleNode stay on top.)
287 The directives are stored in dictionaries from name to value in effect.
288 Each such dictionary is always filled in for all possible directives,
289 using default values where no value is given by the user.
291 The available directives are controlled in Options.py.
293 Note that we have to run this prior to analysis, and so some minor
294 duplication of functionality has to occur: We manually track cimports
295 and which names the "cython" module may have been imported to.
296 """
297 special_methods = set(['declare', 'union', 'struct', 'typedef', 'sizeof', 'cast', 'address', 'pointer', 'compiled', 'NULL'])
299 def __init__(self, context, compilation_option_overrides):
300 super(InterpretCompilerDirectives, self).__init__(context)
301 self.compilation_option_overrides = compilation_option_overrides
302 self.cython_module_names = set()
303 self.option_names = {}
305 # Set up processing and handle the cython: comments.
306 def visit_ModuleNode(self, node):
307 options = copy.copy(Options.option_defaults)
308 options.update(node.option_comments)
309 options.update(self.compilation_option_overrides)
310 self.options = options
311 node.directives = options
312 self.visitchildren(node)
313 node.cython_module_names = self.cython_module_names
314 return node
316 # Track cimports of the cython module.
317 def visit_CImportStatNode(self, node):
318 if node.module_name == u"cython":
319 if node.as_name:
320 modname = node.as_name
321 else:
322 modname = u"cython"
323 self.cython_module_names.add(modname)
324 return node
326 def visit_FromCImportStatNode(self, node):
327 if node.module_name == u"cython":
328 newimp = []
329 for pos, name, as_name, kind in node.imported_names:
330 if (name in Options.option_types or
331 name in self.special_methods or
332 PyrexTypes.parse_basic_type(name)):
333 if as_name is None:
334 as_name = name
335 self.option_names[as_name] = name
336 if kind is not None:
337 self.context.nonfatal_error(PostParseError(pos,
338 "Compiler option imports must be plain imports"))
339 else:
340 newimp.append((pos, name, as_name, kind))
341 if not newimp:
342 return None
343 node.imported_names = newimp
344 return node
346 def visit_FromImportStatNode(self, node):
347 if node.module.module_name.value == u"cython":
348 newimp = []
349 for name, name_node in node.items:
350 if (name in Options.option_types or
351 name in self.special_methods or
352 PyrexTypes.parse_basic_type(name)):
353 self.option_names[name_node.name] = name
354 else:
355 newimp.append((name, name_node))
356 if not newimp:
357 return None
358 node.items = newimp
359 return node
361 def visit_SingleAssignmentNode(self, node):
362 if (isinstance(node.rhs, ImportNode) and
363 node.rhs.module_name.value == u'cython'):
364 node = CImportStatNode(node.pos,
365 module_name = u'cython',
366 as_name = node.lhs.name)
367 self.visit_CImportStatNode(node)
368 else:
369 self.visitchildren(node)
370 return node
372 def visit_NameNode(self, node):
373 if node.name in self.cython_module_names:
374 node.is_cython_module = True
375 else:
376 node.cython_attribute = self.option_names.get(node.name)
377 return node
379 def visit_Node(self, node):
380 self.visitchildren(node)
381 return node
383 def try_to_parse_option(self, node):
384 # If node is the contents of an option (in a with statement or
385 # decorator), returns (optionname, value).
386 # Otherwise, returns None
387 optname = None
388 if isinstance(node, CallNode):
389 self.visit(node.function)
390 optname = node.function.as_cython_attribute()
392 if optname:
393 optiontype = Options.option_types.get(optname)
394 if optiontype:
395 args, kwds = node.explicit_args_kwds()
396 if optiontype is bool:
397 if kwds is not None or len(args) != 1 or not isinstance(args[0], BoolNode):
398 raise PostParseError(dec.function.pos,
399 'The %s option takes one compile-time boolean argument' % optname)
400 return (optname, args[0].value)
401 elif optiontype is dict:
402 if len(args) != 0:
403 raise PostParseError(dec.function.pos,
404 'The %s option takes no prepositional arguments' % optname)
405 return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
406 else:
407 assert False
409 return None
411 def visit_with_options(self, body, options):
412 oldoptions = self.options
413 newoptions = copy.copy(oldoptions)
414 newoptions.update(options)
415 self.options = newoptions
416 assert isinstance(body, StatListNode), body
417 retbody = self.visit_Node(body)
418 directive = CompilerDirectivesNode(pos=retbody.pos, body=retbody,
419 directives=newoptions)
420 self.options = oldoptions
421 return directive
423 # Handle decorators
424 def visit_DefNode(self, node):
425 options = []
427 if node.decorators:
428 # Split the decorators into two lists -- real decorators and options
429 realdecs = []
430 for dec in node.decorators:
431 option = self.try_to_parse_option(dec.decorator)
432 if option is not None:
433 options.append(option)
434 else:
435 realdecs.append(dec)
436 node.decorators = realdecs
438 if options:
439 optdict = {}
440 options.reverse() # Decorators coming first take precedence
441 for option in options:
442 name, value = option
443 optdict[name] = value
444 body = StatListNode(node.pos, stats=[node])
445 return self.visit_with_options(body, optdict)
446 else:
447 return self.visit_Node(node)
449 # Handle with statements
450 def visit_WithStatNode(self, node):
451 option = self.try_to_parse_option(node.manager)
452 if option is not None:
453 if node.target is not None:
454 raise PostParseError(node.pos, "Compiler option with statements cannot contain 'as'")
455 name, value = option
456 return self.visit_with_options(node.body, {name:value})
457 else:
458 return self.visit_Node(node)
460 class WithTransform(CythonTransform):
462 # EXCINFO is manually set to a variable that contains
463 # the exc_info() tuple that can be generated by the enclosing except
464 # statement.
465 template_without_target = TreeFragment(u"""
466 MGR = EXPR
467 EXIT = MGR.__exit__
468 MGR.__enter__()
469 EXC = True
470 EXCINFO = None
471 try:
472 try:
473 BODY
474 except:
475 EXC = False
476 if not EXIT(*EXCINFO):
477 raise
478 finally:
479 if EXC:
480 EXIT(None, None, None)
481 """, temps=[u'MGR', u'EXC', u"EXIT"],
482 pipeline=[NormalizeTree(None)])
484 template_with_target = TreeFragment(u"""
485 MGR = EXPR
486 EXIT = MGR.__exit__
487 VALUE = MGR.__enter__()
488 EXC = True
489 EXCINFO = None
490 try:
491 try:
492 TARGET = VALUE
493 BODY
494 except:
495 EXC = False
496 if not EXIT(*EXCINFO):
497 raise
498 finally:
499 if EXC:
500 EXIT(None, None, None)
501 """, temps=[u'MGR', u'EXC', u"EXIT", u"VALUE"],
502 pipeline=[NormalizeTree(None)])
504 def visit_WithStatNode(self, node):
505 # FIXME: excinfo_temp should be more local to the except
506 # clause that uses it, to avoid the presetting to None
507 excinfo_temp = TempHandle(Builtin.tuple_type)
508 if node.target is not None:
509 result = self.template_with_target.substitute({
510 u'EXPR' : node.manager,
511 u'BODY' : node.body,
512 u'TARGET' : node.target,
513 u'EXCINFO' : excinfo_temp.ref(node.pos)
514 }, pos=node.pos)
515 else:
516 result = self.template_without_target.substitute({
517 u'EXPR' : node.manager,
518 u'BODY' : node.body,
519 u'EXCINFO' : excinfo_temp.ref(node.pos)
520 }, pos=node.pos)
522 # Set except excinfo target to EXCINFO
523 result.body.stats[5].body.stats[0].except_clauses[0].excinfo_target = (
524 excinfo_temp.ref(node.pos))
526 return TempsBlockNode(node.pos, temps=[excinfo_temp], body=result)
528 class DecoratorTransform(CythonTransform):
530 def visit_DefNode(self, func_node):
531 if not func_node.decorators:
532 return func_node
534 decorator_result = NameNode(func_node.pos, name = func_node.name)
535 for decorator in func_node.decorators[::-1]:
536 decorator_result = SimpleCallNode(
537 decorator.pos,
538 function = decorator.decorator,
539 args = [decorator_result])
541 func_name_node = NameNode(func_node.pos, name = func_node.name)
542 reassignment = SingleAssignmentNode(
543 func_node.pos,
544 lhs = func_name_node,
545 rhs = decorator_result)
546 return [func_node, reassignment]
548 class AnalyseDeclarationsTransform(CythonTransform):
550 basic_property = TreeFragment(u"""
551 property NAME:
552 def __get__(self):
553 return ATTR
554 def __set__(self, value):
555 ATTR = value
556 """, level='c_class')
558 def __call__(self, root):
559 self.env_stack = [root.scope]
560 return super(AnalyseDeclarationsTransform, self).__call__(root)
562 def visit_ModuleNode(self, node):
563 node.analyse_declarations(self.env_stack[-1])
564 self.visitchildren(node)
565 return node
567 def visit_FuncDefNode(self, node):
568 lenv = node.create_local_scope(self.env_stack[-1])
569 node.body.analyse_control_flow(lenv) # this will be totally refactored
570 node.declare_arguments(lenv)
571 for var, type_node in node.directive_locals.items():
572 if not lenv.lookup_here(var): # don't redeclare args
573 type = type_node.analyse_as_type(lenv)
574 if type:
575 lenv.declare_var(var, type, type_node.pos)
576 else:
577 error(type_node.pos, "Not a type")
578 for stat in node.pxd_locals:
579 stat.analyse_declarations(lenv)
580 node.body.analyse_declarations(lenv)
581 self.env_stack.append(lenv)
582 self.visitchildren(node)
583 self.env_stack.pop()
584 return node
586 # Some nodes are no longer needed after declaration
587 # analysis and can be dropped. The analysis was performed
588 # on these nodes in a seperate recursive process from the
589 # enclosing function or module, so we can simply drop them.
590 def visit_CVarDefNode(self, node):
591 if node.need_properties:
592 # cdef public attributes may need type testing on
593 # assignment, so we create a property accesss
594 # mechanism for them.
595 stats = []
596 for entry in node.need_properties:
597 property = self.create_Property(entry)
598 property.analyse_declarations(node.dest_scope)
599 self.visit(property)
600 stats.append(property)
601 return StatListNode(pos=node.pos, stats=stats)
602 else:
603 return None
605 def create_Property(self, entry):
606 property = self.basic_property.substitute({
607 u"ATTR": AttributeNode(pos=entry.pos,
608 obj=NameNode(pos=entry.pos, name="self"),
609 attribute=entry.name),
610 }, pos=entry.pos).stats[0]
611 property.name = entry.name
612 return property
614 class AnalyseExpressionsTransform(CythonTransform):
616 def visit_ModuleNode(self, node):
617 node.body.analyse_expressions(node.scope)
618 self.visitchildren(node)
619 return node
621 def visit_FuncDefNode(self, node):
622 node.body.analyse_expressions(node.local_scope)
623 self.visitchildren(node)
624 return node
626 class AlignFunctionDefinitions(CythonTransform):
627 """
628 This class takes the signatures from a .pxd file and applies them to
629 the def methods in a .py file.
630 """
632 def visit_ModuleNode(self, node):
633 self.scope = node.scope
634 self.directives = node.directives
635 self.visitchildren(node)
636 return node
638 def visit_PyClassDefNode(self, node):
639 pxd_def = self.scope.lookup(node.name)
640 if pxd_def:
641 if pxd_def.is_cclass:
642 return self.visit_CClassDefNode(node.as_cclass(), pxd_def)
643 else:
644 error(node.pos, "'%s' redeclared" % node.name)
645 error(pxd_def.pos, "previous declaration here")
646 return None
647 else:
648 return node
650 def visit_CClassDefNode(self, node, pxd_def=None):
651 if pxd_def is None:
652 pxd_def = self.scope.lookup(node.class_name)
653 if pxd_def:
654 outer_scope = self.scope
655 self.scope = pxd_def.type.scope
656 self.visitchildren(node)
657 if pxd_def:
658 self.scope = outer_scope
659 return node
661 def visit_DefNode(self, node):
662 pxd_def = self.scope.lookup(node.name)
663 if pxd_def:
664 if pxd_def.is_cfunction:
665 node = node.as_cfunction(pxd_def)
666 else:
667 error(node.pos, "'%s' redeclared" % node.name)
668 error(pxd_def.pos, "previous declaration here")
669 return None
670 elif self.scope.is_module_scope and self.directives['auto_cpdef']:
671 node = node.as_cfunction(scope=self.scope)
672 # Enable this when internal def functions are allowed.
673 # self.visitchildren(node)
674 return node
677 class MarkClosureVisitor(CythonTransform):
679 needs_closure = False
681 def visit_FuncDefNode(self, node):
682 self.needs_closure = False
683 self.visitchildren(node)
684 node.needs_closure = self.needs_closure
685 self.needs_closure = True
686 return node
688 def visit_ClassDefNode(self, node):
689 self.visitchildren(node)
690 self.needs_closure = True
691 return node
693 def visit_YieldNode(self, node):
694 self.needs_closure = True
696 class CreateClosureClasses(CythonTransform):
697 # Output closure classes in module scope for all functions
698 # that need it.
700 def visit_ModuleNode(self, node):
701 self.module_scope = node.scope
702 self.visitchildren(node)
703 return node
705 def create_class_from_scope(self, node, target_module_scope):
706 as_name = temp_name_handle("closure")
707 func_scope = node.local_scope
709 entry = target_module_scope.declare_c_class(name = as_name,
710 pos = node.pos, defining = True, implementing = True)
711 class_scope = entry.type.scope
712 for entry in func_scope.entries.values():
713 class_scope.declare_var(pos=node.pos,
714 name=entry.name,
715 cname=entry.cname,
716 type=entry.type,
717 is_cdef=True)
719 def visit_FuncDefNode(self, node):
720 self.create_class_from_scope(node, self.module_scope)
721 return node
724 class EnvTransform(CythonTransform):
725 """
726 This transformation keeps a stack of the environments.
727 """
728 def __call__(self, root):
729 self.env_stack = [root.scope]
730 return super(EnvTransform, self).__call__(root)
732 def visit_FuncDefNode(self, node):
733 self.env_stack.append(node.local_scope)
734 self.visitchildren(node)
735 self.env_stack.pop()
736 return node
739 class TransformBuiltinMethods(EnvTransform):
741 def visit_SingleAssignmentNode(self, node):
742 if node.declaration_only:
743 return None
744 else:
745 self.visitchildren(node)
746 return node
748 def visit_AttributeNode(self, node):
749 return self.visit_cython_attribute(node)
751 def visit_NameNode(self, node):
752 return self.visit_cython_attribute(node)
754 def visit_cython_attribute(self, node):
755 attribute = node.as_cython_attribute()
756 if attribute:
757 if attribute == u'compiled':
758 node = BoolNode(node.pos, value=True)
759 elif attribute == u'NULL':
760 node = NullNode(node.pos)
761 elif not PyrexTypes.parse_basic_type(attribute):
762 error(node.pos, u"'%s' not a valid cython attribute or is being used incorrectly" % attribute)
763 return node
765 def visit_SimpleCallNode(self, node):
767 # locals builtin
768 if isinstance(node.function, ExprNodes.NameNode):
769 if node.function.name == 'locals':
770 pos = node.pos
771 lenv = self.env_stack[-1]
772 items = [ExprNodes.DictItemNode(pos,
773 key=ExprNodes.IdentifierStringNode(pos, value=var),
774 value=ExprNodes.NameNode(pos, name=var)) for var in lenv.entries]
775 return ExprNodes.DictNode(pos, key_value_pairs=items)
777 # cython.foo
778 function = node.function.as_cython_attribute()
779 if function:
780 if function == u'cast':
781 if len(node.args) != 2:
782 error(node.function.pos, u"cast takes exactly two arguments")
783 else:
784 type = node.args[0].analyse_as_type(self.env_stack[-1])
785 if type:
786 node = TypecastNode(node.function.pos, type=type, operand=node.args[1])
787 else:
788 error(node.args[0].pos, "Not a type")
789 elif function == u'sizeof':
790 if len(node.args) != 1:
791 error(node.function.pos, u"sizeof takes exactly one argument" % function)
792 else:
793 type = node.args[0].analyse_as_type(self.env_stack[-1])
794 if type:
795 node = SizeofTypeNode(node.function.pos, arg_type=type)
796 else:
797 node = SizeofVarNode(node.function.pos, operand=node.args[0])
798 elif function == 'address':
799 if len(node.args) != 1:
800 error(node.function.pos, u"sizeof takes exactly one argument" % function)
801 else:
802 node = AmpersandNode(node.function.pos, operand=node.args[0])
803 else:
804 error(node.function.pos, u"'%s' not a valid cython language construct" % function)
806 self.visitchildren(node)
807 return node