Cython has moved to github.
cython-devel
view Cython/Compiler/ParseTreeTransforms.py @ 1241:b23f31007194
fix the declaration/initilization/cleanup of module global cdef Python objects
| author | Lisandro Dalcin <dalcinl@gmail.com> |
|---|---|
| date | Thu Oct 16 20:26:34 2008 -0300 (3 years ago) |
| parents | 3c17d33ba344 |
| children | c327100e0a20 |
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
215 """
216 ERR_FUNCDEF_NOT_ALLOWED = 'function definition not allowed here'
218 def __call__(self, node):
219 self.scope_type = 'pxd'
220 return super(PxdPostParse, self).__call__(node)
222 def visit_CClassDefNode(self, node):
223 old = self.scope_type
224 self.scope_type = 'cclass'
225 self.visitchildren(node)
226 self.scope_type = old
227 return node
229 def visit_FuncDefNode(self, node):
230 # FuncDefNode always come with an implementation (without
231 # an imp they are CVarDefNodes..)
232 ok = False
234 if (isinstance(node, DefNode) and self.scope_type == 'cclass'
235 and node.name in ('__getbuffer__', '__releasebuffer__')):
236 ok = True
238 if not ok:
239 self.context.nonfatal_error(PostParseError(node.pos,
240 self.ERR_FUNCDEF_NOT_ALLOWED))
241 return None
242 else:
243 return node
245 class InterpretCompilerDirectives(CythonTransform):
246 """
247 After parsing, options can be stored in a number of places:
248 - #cython-comments at the top of the file (stored in ModuleNode)
249 - Command-line arguments overriding these
250 - @cython.optionname decorators
251 - with cython.optionname: statements
253 This transform is responsible for interpreting these various sources
254 and store the option in two ways:
255 - Set the directives attribute of the ModuleNode for global directives.
256 - Use a CompilerDirectivesNode to override directives for a subtree.
258 (The first one is primarily to not have to modify with the tree
259 structure, so that ModuleNode stay on top.)
261 The directives are stored in dictionaries from name to value in effect.
262 Each such dictionary is always filled in for all possible directives,
263 using default values where no value is given by the user.
265 The available directives are controlled in Options.py.
267 Note that we have to run this prior to analysis, and so some minor
268 duplication of functionality has to occur: We manually track cimports
269 and which names the "cython" module may have been imported to.
270 """
271 special_methods = set(['declare', 'union', 'struct', 'typedef', 'sizeof', 'cast', 'address', 'pointer', 'compiled', 'NULL'])
273 def __init__(self, context, compilation_option_overrides):
274 super(InterpretCompilerDirectives, self).__init__(context)
275 self.compilation_option_overrides = compilation_option_overrides
276 self.cython_module_names = set()
277 self.option_names = {}
279 # Set up processing and handle the cython: comments.
280 def visit_ModuleNode(self, node):
281 options = copy.copy(Options.option_defaults)
282 options.update(node.option_comments)
283 options.update(self.compilation_option_overrides)
284 self.options = options
285 node.directives = options
286 self.visitchildren(node)
287 node.cython_module_names = self.cython_module_names
288 return node
290 # Track cimports of the cython module.
291 def visit_CImportStatNode(self, node):
292 if node.module_name == u"cython":
293 if node.as_name:
294 modname = node.as_name
295 else:
296 modname = u"cython"
297 self.cython_module_names.add(modname)
298 return node
300 def visit_FromCImportStatNode(self, node):
301 if node.module_name == u"cython":
302 newimp = []
303 for pos, name, as_name, kind in node.imported_names:
304 if (name in Options.option_types or
305 name in self.special_methods or
306 PyrexTypes.parse_basic_type(name)):
307 if as_name is None:
308 as_name = name
309 self.option_names[as_name] = name
310 if kind is not None:
311 self.context.nonfatal_error(PostParseError(pos,
312 "Compiler option imports must be plain imports"))
313 else:
314 newimp.append((pos, name, as_name, kind))
315 if not newimp:
316 return None
317 node.imported_names = newimp
318 return node
320 def visit_FromImportStatNode(self, node):
321 if node.module.module_name.value == u"cython":
322 newimp = []
323 for name, name_node in node.items:
324 if (name in Options.option_types or
325 name in self.special_methods or
326 PyrexTypes.parse_basic_type(name)):
327 self.option_names[name_node.name] = name
328 else:
329 newimp.append((name, name_node))
330 if not newimp:
331 return None
332 node.items = newimp
333 return node
335 def visit_SingleAssignmentNode(self, node):
336 if (isinstance(node.rhs, ImportNode) and
337 node.rhs.module_name.value == u'cython'):
338 node = CImportStatNode(node.pos,
339 module_name = u'cython',
340 as_name = node.lhs.name)
341 self.visit_CImportStatNode(node)
342 else:
343 self.visitchildren(node)
344 return node
346 def visit_NameNode(self, node):
347 if node.name in self.cython_module_names:
348 node.is_cython_module = True
349 else:
350 node.cython_attribute = self.option_names.get(node.name)
351 return node
353 def visit_Node(self, node):
354 self.visitchildren(node)
355 return node
357 def try_to_parse_option(self, node):
358 # If node is the contents of an option (in a with statement or
359 # decorator), returns (optionname, value).
360 # Otherwise, returns None
361 optname = None
362 if isinstance(node, CallNode):
363 self.visit(node.function)
364 optname = node.function.as_cython_attribute()
366 if optname:
367 optiontype = Options.option_types.get(optname)
368 if optiontype:
369 args, kwds = node.explicit_args_kwds()
370 if optiontype is bool:
371 if kwds is not None or len(args) != 1 or not isinstance(args[0], BoolNode):
372 raise PostParseError(dec.function.pos,
373 'The %s option takes one compile-time boolean argument' % optname)
374 return (optname, args[0].value)
375 elif optiontype is dict:
376 if len(args) != 0:
377 raise PostParseError(dec.function.pos,
378 'The %s option takes no prepositional arguments' % optname)
379 return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
380 else:
381 assert False
383 return None
385 def visit_with_options(self, body, options):
386 oldoptions = self.options
387 newoptions = copy.copy(oldoptions)
388 newoptions.update(options)
389 self.options = newoptions
390 assert isinstance(body, StatListNode), body
391 retbody = self.visit_Node(body)
392 directive = CompilerDirectivesNode(pos=retbody.pos, body=retbody,
393 directives=newoptions)
394 self.options = oldoptions
395 return directive
397 # Handle decorators
398 def visit_DefNode(self, node):
399 options = []
401 if node.decorators:
402 # Split the decorators into two lists -- real decorators and options
403 realdecs = []
404 for dec in node.decorators:
405 option = self.try_to_parse_option(dec.decorator)
406 if option is not None:
407 options.append(option)
408 else:
409 realdecs.append(dec)
410 node.decorators = realdecs
412 if options:
413 optdict = {}
414 options.reverse() # Decorators coming first take precedence
415 for option in options:
416 name, value = option
417 optdict[name] = value
418 body = StatListNode(node.pos, stats=[node])
419 return self.visit_with_options(body, optdict)
420 else:
421 return self.visit_Node(node)
423 # Handle with statements
424 def visit_WithStatNode(self, node):
425 option = self.try_to_parse_option(node.manager)
426 if option is not None:
427 if node.target is not None:
428 raise PostParseError(node.pos, "Compiler option with statements cannot contain 'as'")
429 name, value = option
430 return self.visit_with_options(node.body, {name:value})
431 else:
432 return self.visit_Node(node)
434 class WithTransform(CythonTransform):
436 # EXCINFO is manually set to a variable that contains
437 # the exc_info() tuple that can be generated by the enclosing except
438 # statement.
439 template_without_target = TreeFragment(u"""
440 MGR = EXPR
441 EXIT = MGR.__exit__
442 MGR.__enter__()
443 EXC = True
444 try:
445 try:
446 BODY
447 except:
448 EXC = False
449 if not EXIT(*EXCINFO):
450 raise
451 finally:
452 if EXC:
453 EXIT(None, None, None)
454 """, temps=[u'MGR', u'EXC', u"EXIT"],
455 pipeline=[NormalizeTree(None)])
457 template_with_target = TreeFragment(u"""
458 MGR = EXPR
459 EXIT = MGR.__exit__
460 VALUE = MGR.__enter__()
461 EXC = True
462 try:
463 try:
464 TARGET = VALUE
465 BODY
466 except:
467 EXC = False
468 if not EXIT(*EXCINFO):
469 raise
470 finally:
471 if EXC:
472 EXIT(None, None, None)
473 """, temps=[u'MGR', u'EXC', u"EXIT", u"VALUE"],
474 pipeline=[NormalizeTree(None)])
476 def visit_WithStatNode(self, node):
477 excinfo_temp = TempHandle(PyrexTypes.py_object_type)
478 if node.target is not None:
479 result = self.template_with_target.substitute({
480 u'EXPR' : node.manager,
481 u'BODY' : node.body,
482 u'TARGET' : node.target,
483 u'EXCINFO' : excinfo_temp.ref(node.pos)
484 }, pos=node.pos)
485 # Set except excinfo target to EXCINFO
486 result.body.stats[4].body.stats[0].except_clauses[0].excinfo_target = (
487 excinfo_temp.ref(node.pos))
488 else:
489 result = self.template_without_target.substitute({
490 u'EXPR' : node.manager,
491 u'BODY' : node.body,
492 u'EXCINFO' : excinfo_temp.ref(node.pos)
493 }, pos=node.pos)
494 # Set except excinfo target to EXCINFO
495 result.body.stats[4].body.stats[0].except_clauses[0].excinfo_target = (
496 excinfo_temp.ref(node.pos))
498 return TempsBlockNode(node.pos, temps=[excinfo_temp], body=result)
500 class DecoratorTransform(CythonTransform):
502 def visit_DefNode(self, func_node):
503 if not func_node.decorators:
504 return func_node
506 decorator_result = NameNode(func_node.pos, name = func_node.name)
507 for decorator in func_node.decorators[::-1]:
508 decorator_result = SimpleCallNode(
509 decorator.pos,
510 function = decorator.decorator,
511 args = [decorator_result])
513 func_name_node = NameNode(func_node.pos, name = func_node.name)
514 reassignment = SingleAssignmentNode(
515 func_node.pos,
516 lhs = func_name_node,
517 rhs = decorator_result)
518 return [func_node, reassignment]
520 class AnalyseDeclarationsTransform(CythonTransform):
522 basic_property = TreeFragment(u"""
523 property NAME:
524 def __get__(self):
525 return ATTR
526 def __set__(self, value):
527 ATTR = value
528 """, level='c_class')
530 def __call__(self, root):
531 self.env_stack = [root.scope]
532 return super(AnalyseDeclarationsTransform, self).__call__(root)
534 def visit_ModuleNode(self, node):
535 node.analyse_declarations(self.env_stack[-1])
536 self.visitchildren(node)
537 return node
539 def visit_FuncDefNode(self, node):
540 lenv = node.create_local_scope(self.env_stack[-1])
541 node.body.analyse_control_flow(lenv) # this will be totally refactored
542 node.declare_arguments(lenv)
543 for var, type_node in node.directive_locals.items():
544 if not lenv.lookup_here(var): # don't redeclare args
545 type = type_node.analyse_as_type(lenv)
546 if type:
547 lenv.declare_var(var, type, type_node.pos)
548 else:
549 error(type_node.pos, "Not a type")
550 node.body.analyse_declarations(lenv)
551 self.env_stack.append(lenv)
552 self.visitchildren(node)
553 self.env_stack.pop()
554 return node
556 # Some nodes are no longer needed after declaration
557 # analysis and can be dropped. The analysis was performed
558 # on these nodes in a seperate recursive process from the
559 # enclosing function or module, so we can simply drop them.
560 def visit_CVarDefNode(self, node):
561 if node.need_properties:
562 # cdef public attributes may need type testing on
563 # assignment, so we create a property accesss
564 # mechanism for them.
565 stats = []
566 for entry in node.need_properties:
567 property = self.create_Property(entry)
568 property.analyse_declarations(node.dest_scope)
569 self.visit(property)
570 stats.append(property)
571 return StatListNode(pos=node.pos, stats=stats)
572 else:
573 return None
575 def create_Property(self, entry):
576 property = self.basic_property.substitute({
577 u"ATTR": AttributeNode(pos=entry.pos,
578 obj=NameNode(pos=entry.pos, name="self"),
579 attribute=entry.name),
580 }, pos=entry.pos).stats[0]
581 property.name = entry.name
582 return property
584 class AnalyseExpressionsTransform(CythonTransform):
586 def visit_ModuleNode(self, node):
587 node.body.analyse_expressions(node.scope)
588 self.visitchildren(node)
589 return node
591 def visit_FuncDefNode(self, node):
592 node.body.analyse_expressions(node.local_scope)
593 self.visitchildren(node)
594 return node
596 class AlignFunctionDefinitions(CythonTransform):
597 """
598 This class takes the signatures from a .pxd file and applies them to
599 the def methods in a .py file.
600 """
602 def visit_ModuleNode(self, node):
603 self.scope = node.scope
604 self.visitchildren(node)
605 return node
607 def visit_PyClassDefNode(self, node):
608 pxd_def = self.scope.lookup(node.name)
609 if pxd_def:
610 if pxd_def.is_cclass:
611 return self.visit_CClassDefNode(node.as_cclass(), pxd_def)
612 else:
613 error(node.pos, "'%s' redeclared" % node.name)
614 error(pxd_def.pos, "previous declaration here")
615 return None
616 self.visitchildren(node)
617 return node
619 def visit_CClassDefNode(self, node, pxd_def=None):
620 if pxd_def is None:
621 pxd_def = self.scope.lookup(node.class_name)
622 if pxd_def:
623 outer_scope = self.scope
624 self.scope = pxd_def.type.scope
625 self.visitchildren(node)
626 if pxd_def:
627 self.scope = outer_scope
628 return node
630 def visit_DefNode(self, node):
631 pxd_def = self.scope.lookup(node.name)
632 if pxd_def:
633 if pxd_def.is_cfunction:
634 node = node.as_cfunction(pxd_def)
635 else:
636 error(node.pos, "'%s' redeclared" % node.name)
637 error(pxd_def.pos, "previous declaration here")
638 return None
639 # Enable this when internal def functions are allowed.
640 # self.visitchildren(node)
641 return node
644 class MarkClosureVisitor(CythonTransform):
646 needs_closure = False
648 def visit_FuncDefNode(self, node):
649 self.needs_closure = False
650 self.visitchildren(node)
651 node.needs_closure = self.needs_closure
652 self.needs_closure = True
653 return node
655 def visit_ClassDefNode(self, node):
656 self.visitchildren(node)
657 self.needs_closure = True
658 return node
660 def visit_YieldNode(self, node):
661 self.needs_closure = True
663 class CreateClosureClasses(CythonTransform):
664 # Output closure classes in module scope for all functions
665 # that need it.
667 def visit_ModuleNode(self, node):
668 self.module_scope = node.scope
669 self.visitchildren(node)
670 return node
672 def create_class_from_scope(self, node, target_module_scope):
673 as_name = temp_name_handle("closure")
674 func_scope = node.local_scope
676 entry = target_module_scope.declare_c_class(name = as_name,
677 pos = node.pos, defining = True, implementing = True)
678 class_scope = entry.type.scope
679 for entry in func_scope.entries.values():
680 class_scope.declare_var(pos=node.pos,
681 name=entry.name,
682 cname=entry.cname,
683 type=entry.type,
684 is_cdef=True)
686 def visit_FuncDefNode(self, node):
687 self.create_class_from_scope(node, self.module_scope)
688 return node
691 class EnvTransform(CythonTransform):
692 """
693 This transformation keeps a stack of the environments.
694 """
695 def __call__(self, root):
696 self.env_stack = [root.scope]
697 return super(EnvTransform, self).__call__(root)
699 def visit_FuncDefNode(self, node):
700 self.env_stack.append(node.local_scope)
701 self.visitchildren(node)
702 self.env_stack.pop()
703 return node
706 class TransformBuiltinMethods(EnvTransform):
708 def visit_SingleAssignmentNode(self, node):
709 if node.declaration_only:
710 return None
711 else:
712 self.visitchildren(node)
713 return node
715 def visit_AttributeNode(self, node):
716 return self.visit_cython_attribute(node)
718 def visit_NameNode(self, node):
719 return self.visit_cython_attribute(node)
721 def visit_cython_attribute(self, node):
722 attribute = node.as_cython_attribute()
723 if attribute:
724 if attribute == u'compiled':
725 node = BoolNode(node.pos, value=True)
726 elif attribute == u'NULL':
727 node = NullNode(node.pos)
728 elif not PyrexTypes.parse_basic_type(attribute):
729 error(node.pos, u"'%s' not a valid cython attribute or is being used incorrectly" % attribute)
730 return node
732 def visit_SimpleCallNode(self, node):
734 # locals builtin
735 if isinstance(node.function, ExprNodes.NameNode):
736 if node.function.name == 'locals':
737 pos = node.pos
738 lenv = self.env_stack[-1]
739 items = [ExprNodes.DictItemNode(pos,
740 key=ExprNodes.IdentifierStringNode(pos, value=var),
741 value=ExprNodes.NameNode(pos, name=var)) for var in lenv.entries]
742 return ExprNodes.DictNode(pos, key_value_pairs=items)
744 # cython.foo
745 function = node.function.as_cython_attribute()
746 if function:
747 if function == u'cast':
748 if len(node.args) != 2:
749 error(node.function.pos, u"cast takes exactly two arguments")
750 else:
751 type = node.args[0].analyse_as_type(self.env_stack[-1])
752 if type:
753 node = TypecastNode(node.function.pos, type=type, operand=node.args[1])
754 else:
755 error(node.args[0].pos, "Not a type")
756 elif function == u'sizeof':
757 if len(node.args) != 1:
758 error(node.function.pos, u"sizeof takes exactly one argument" % function)
759 else:
760 type = node.args[0].analyse_as_type(self.env_stack[-1])
761 if type:
762 node = SizeofTypeNode(node.function.pos, arg_type=type)
763 else:
764 node = SizeofVarNode(node.function.pos, operand=node.args[0])
765 elif function == 'address':
766 if len(node.args) != 1:
767 error(node.function.pos, u"sizeof takes exactly one argument" % function)
768 else:
769 node = AmpersandNode(node.function.pos, operand=node.args[0])
770 else:
771 error(node.function.pos, u"'%s' not a valid cython language construct" % function)
773 self.visitchildren(node)
774 return node
