diff options
Diffstat (limited to 'debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Nodes.py')
| -rw-r--r-- | debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Nodes.py | 3249 |
1 files changed, 3249 insertions, 0 deletions
diff --git a/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Nodes.py b/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Nodes.py new file mode 100644 index 00000000..fb974df0 --- /dev/null +++ b/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Nodes.py @@ -0,0 +1,3249 @@ +# +# Pyrex - Parse tree nodes +# + +import string, sys + +import Code +from Errors import error, one_time_warning, InternalError +import Naming +import PyrexTypes +from PyrexTypes import py_object_type, c_int_type, error_type, \ + CTypedefType, CFuncType +from Symtab import ModuleScope, LocalScope, \ + StructOrUnionScope, PyClassScope, CClassScope +from Pyrex.Utils import open_new_file, replace_suffix +import Options + +from DebugFlags import debug_disposal_code + +class Node: + # pos (string, int, int) Source file position + # is_name boolean Is a NameNode + # is_literal boolean Is a ConstNode + + is_name = 0 + is_literal = 0 + + def __init__(self, pos, **kw): + self.pos = pos + self.__dict__.update(kw) + + gil_message = "Operation" + + def gil_check(self, env): + if env.nogil: + self.gil_error() + + def gil_error(self, message = None): + error(self.pos, "%s not allowed without gil" % (message or self.gil_message)) + + # + # There are 3 phases of parse tree processing, applied in order to + # all the statements in a given scope-block: + # + # (1) analyse_declarations + # Make symbol table entries for all declarations at the current + # level, both explicit (def, cdef, etc.) and implicit (assignment + # to an otherwise undeclared name). + # + # (2) analyse_expressions + # Determine the result types of expressions and fill in the + # 'type' attribute of each ExprNode. Insert coercion nodes into the + # tree where needed to convert to and from Python objects. + # Allocate temporary locals for intermediate results. + # + # (3) generate_code + # Emit C code for all declarations, statements and expressions. + # Recursively applies the 3 processing phases to the bodies of + # functions. + # + + def analyse_declarations(self, env): + pass + + def analyse_expressions(self, env): + raise InternalError("analyse_expressions not implemented for %s" % \ + self.__class__.__name__) + + def generate_code(self, code): + raise InternalError("generate_code not implemented for %s" % \ + self.__class__.__name__) + + +class BlockNode: + # Mixin class for nodes representing a declaration block. + pass + +# def generate_const_definitions(self, env, code): +# if env.const_entries: +# code.putln("") +# for entry in env.const_entries: +# if not entry.is_interned: +# code.put_var_declaration(entry, static = 1) + +# def generate_interned_name_decls(self, env, code): +# # Flush accumulated interned names from the global scope +# # and generate declarations for them. +# genv = env.global_scope() +# intern_map = genv.intern_map +# names = genv.interned_names +# if names: +# code.putln("") +# for name in names: +# code.putln( +# "static PyObject *%s;" % intern_map[name]) +# del names[:] + +# def generate_py_string_decls(self, env, code): +# entries = env.pystring_entries +# if entries: +# code.putln("") +# for entry in entries: +# code.putln( +# "static PyObject *%s;" % entry.pystring_cname) + + +class StatListNode(Node): + # stats a list of StatNode + + def analyse_declarations(self, env): + #print "StatListNode.analyse_declarations" ### + for stat in self.stats: + stat.analyse_declarations(env) + + def analyse_expressions(self, env): + #print "StatListNode.analyse_expressions" ### + for stat in self.stats: + stat.analyse_expressions(env) + + def generate_function_definitions(self, env, code): + #print "StatListNode.generate_function_definitions" ### + for stat in self.stats: + stat.generate_function_definitions(env, code) + + def generate_execution_code(self, code): + #print "StatListNode.generate_execution_code" ### + for stat in self.stats: + code.mark_pos(stat.pos) + stat.generate_execution_code(code) + + +class StatNode(Node): + # + # Code generation for statements is split into the following subphases: + # + # (1) generate_function_definitions + # Emit C code for the definitions of any structs, + # unions, enums and functions defined in the current + # scope-block. + # + # (2) generate_execution_code + # Emit C code for executable statements. + # + + def generate_function_definitions(self, env, code): + pass + + def generate_execution_code(self, code): + raise InternalError("generate_execution_code not implemented for %s" % \ + self.__class__.__name__) + + +class CDefExternNode(StatNode): + # include_file string or None + # body StatNode + + def analyse_declarations(self, env): + if self.include_file: + env.add_include_file(self.include_file) + old_cinclude_flag = env.in_cinclude + env.in_cinclude = 1 + self.body.analyse_declarations(env) + env.in_cinclude = old_cinclude_flag + + def analyse_expressions(self, env): + pass + + def generate_execution_code(self, code): + pass + + +class CDeclaratorNode(Node): + # Part of a C declaration. + # + # Processing during analyse_declarations phase: + # + # analyse + # Returns (name, type) pair where name is the + # CNameDeclaratorNode of the name being declared + # and type is the type it is being declared as. + # + # calling_convention string Calling convention of CFuncDeclaratorNode + # for which this is a base + + calling_convention = "" + + +class CNameDeclaratorNode(CDeclaratorNode): + # name string The Pyrex name being declared + # cname string or None C name, if specified + + def analyse(self, base_type, env): + return self, base_type + + +class CPtrDeclaratorNode(CDeclaratorNode): + # base CDeclaratorNode + + def analyse(self, base_type, env): + if base_type.is_pyobject: + error(self.pos, + "Pointer base type cannot be a Python object") + ptr_type = PyrexTypes.c_ptr_type(base_type) + return self.base.analyse(ptr_type, env) + + +class CArrayDeclaratorNode(CDeclaratorNode): + # base CDeclaratorNode + # dimension ExprNode + + def analyse(self, base_type, env): + if self.dimension: + self.dimension.analyse_const_expression(env) + if not self.dimension.type.is_int: + error(self.dimension.pos, "Array dimension not integer") + size = self.dimension.result() + else: + size = None + if not base_type.is_complete(): + error(self.pos, + "Array element type '%s' is incomplete" % base_type) + if base_type.is_pyobject: + error(self.pos, + "Array element cannot be a Python object") + if base_type.is_cfunction: + error(self.pos, + "Array element cannot be a function") + array_type = PyrexTypes.c_array_type(base_type, size) + return self.base.analyse(array_type, env) + + +class CFuncDeclaratorNode(CDeclaratorNode): + # base CDeclaratorNode + # args [CArgDeclNode] + # has_varargs boolean + # exception_value ConstNode + # exception_check boolean True if PyErr_Occurred check needed + # nogil boolean Can be called without gil + # with_gil boolean Acquire gil around function body + + def analyse(self, return_type, env): + func_type_args = [] + for arg_node in self.args: + name_declarator, type = arg_node.analyse(env) + name = name_declarator.name + if name_declarator.cname: + error(self.pos, + "Function argument cannot have C name specification") + # Turn *[] argument into ** + if type.is_array: + type = PyrexTypes.c_ptr_type(type.base_type) + # Catch attempted C-style func(void) decl + if type.is_void: + error(arg_node.pos, "Function argument cannot be void") + func_type_args.append( + PyrexTypes.CFuncTypeArg(name, type, arg_node.pos)) + if arg_node.default: + error(arg_node.pos, "C function argument cannot have default value") + exc_val = None + exc_check = 0 + if return_type.is_pyobject \ + and (self.exception_value or self.exception_check): + error(self.pos, + "Exception clause not allowed for function returning Python object") + else: + if self.exception_value: + self.exception_value.analyse_const_expression(env) + exc_val = self.exception_value.result() + if not return_type.assignable_from(self.exception_value.type): + error(self.exception_value.pos, + "Exception value incompatible with function return type") + exc_check = self.exception_check + if return_type.is_array: + error(self.pos, + "Function cannot return an array") + if return_type.is_cfunction: + error(self.pos, + "Function cannot return a function") + func_type = PyrexTypes.CFuncType( + return_type, func_type_args, self.has_varargs, + exception_value = exc_val, exception_check = exc_check, + calling_convention = self.base.calling_convention, + nogil = self.nogil, with_gil = self.with_gil) + return self.base.analyse(func_type, env) + + +class CArgDeclNode(Node): + # Item in a function declaration argument list. + # + # base_type CBaseTypeNode + # declarator CDeclaratorNode + # #not_none boolean Tagged with 'not None' + # allow_none tristate True == 'or None', False == 'not None', None = unspecified + # default ExprNode or None + # default_entry Symtab.Entry Entry for the variable holding the default value + # is_self_arg boolean Is the "self" arg of an extension type method + # is_kw_only boolean Is a keyword-only argument + + is_self_arg = 0 + + def analyse(self, env): + #print "CArgDeclNode.analyse: is_self_arg =", self.is_self_arg ### + base_type = self.base_type.analyse(env) + return self.declarator.analyse(base_type, env) + + +class CBaseTypeNode(Node): + # Abstract base class for C base type nodes. + # + # Processing during analyse_declarations phase: + # + # analyse + # Returns the type. + + pass + + +class CSimpleBaseTypeNode(CBaseTypeNode): + # name string + # module_path [string] Qualifying name components + # is_basic_c_type boolean + # signed boolean + # longness integer + # is_self_arg boolean Is self argument of C method + + def analyse(self, env): + # Return type descriptor. + #print "CSimpleBaseTypeNode.analyse: is_self_arg =", self.is_self_arg ### + type = None + if self.is_basic_c_type: + type = PyrexTypes.simple_c_type(self.signed, self.longness, self.name) + if not type: + error(self.pos, "Unrecognised type modifier combination") + elif self.name == "object" and not self.module_path: + type = py_object_type + elif self.name is None: + if self.is_self_arg and env.is_c_class_scope: + #print "CSimpleBaseTypeNode.analyse: defaulting to parent type" ### + type = env.parent_type + else: + type = py_object_type + else: + scope = env.find_imported_module(self.module_path, self.pos) + if scope: + entry = scope.find(self.name, self.pos) + if entry and entry.is_type: + type = entry.type + else: + error(self.pos, "'%s' is not a type identifier" % self.name) + if type: + return type + else: + return PyrexTypes.error_type + + +class CComplexBaseTypeNode(CBaseTypeNode): + # base_type CBaseTypeNode + # declarator CDeclaratorNode + + def analyse(self, env): + base = self.base_type.analyse(env) + _, type = self.declarator.analyse(base, env) + return type + + +class CVarDefNode(StatNode): + # C variable definition or forward/extern function declaration. + # + # visibility 'private' or 'public' or 'extern' + # base_type CBaseTypeNode + # declarators [CDeclaratorNode] + # in_pxd boolean + # api boolean + + def analyse_declarations(self, env, dest_scope = None): + if not dest_scope: + dest_scope = env + base_type = self.base_type.analyse(env) + for declarator in self.declarators: + name_declarator, type = declarator.analyse(base_type, env) + if not type.is_complete(): + if not (self.visibility == 'extern' and type.is_array): + error(declarator.pos, + "Variable type '%s' is incomplete" % type) + if self.visibility == 'extern' and type.is_pyobject: + error(declarator.pos, + "Python object cannot be declared extern") + name = name_declarator.name + cname = name_declarator.cname + if type.is_cfunction: + entry = dest_scope.declare_cfunction(name, type, declarator.pos, + cname = cname, visibility = self.visibility, in_pxd = self.in_pxd, + api = self.api) + else: + if self.in_pxd and self.visibility <> 'extern': + error(self.pos, + "Only 'extern' C variable declaration allowed in .pxd file") + dest_scope.declare_var(name, type, declarator.pos, + cname = cname, visibility = self.visibility, is_cdef = 1) + + def analyse_expressions(self, env): + pass + + def generate_execution_code(self, code): + pass + + +class CStructOrUnionDefNode(StatNode): + # name string + # cname string or None + # module_path [string] + # kind "struct" or "union" + # typedef_flag boolean + # cplus_flag boolean + # visibility "public" or "private" + # in_pxd boolean + # attributes [CVarDefNode] or None + # entry Entry + # bases [([name, ...], name), ...] + + def analyse_declarations(self, env): + scope = None + base_scopes = [] + for base in self.bases: + base_entry = env.find_qualified_name(base, self.pos) + if base_entry: + if base_entry.is_type and base_entry.type.is_struct_or_union \ + and base_entry.type.scope.is_cplus: + base_scopes.append(base_entry.type.scope) + else: + error(self.pos, "Base type '%s' is not a C++ struct" % + ".".join(base[0] + [base[1]])) + if self.attributes is not None: + scope = StructOrUnionScope(base_scopes = base_scopes, is_cplus = self.cplus_flag) + if self.module_path: + home_scope = env.find_imported_module(self.module_path, self.pos) + if not home_scope: + return + else: + home_scope = env + def declare(): + self.entry = home_scope.declare_struct_or_union( + self.name, self.kind, scope, self.typedef_flag, self.pos, + self.cname, visibility = self.visibility) + if self.attributes is not None: + if self.in_pxd and not env.in_cinclude: + self.entry.defined_in_pxd = 1 + if not self.typedef_flag: + declare() + if self.attributes is not None: + for attr in self.attributes: + attr.analyse_declarations(env, scope) + if self.typedef_flag: + declare() + + def analyse_expressions(self, env): + pass + + def generate_execution_code(self, code): + pass + + +class CEnumDefNode(StatNode): + # name string or None + # cname string or None + # items [CEnumDefItemNode] + # typedef_flag boolean + # visibility "public" or "private" + # in_pxd boolean + # entry Entry + + def analyse_declarations(self, env): + self.entry = env.declare_enum(self.name, self.pos, + cname = self.cname, typedef_flag = self.typedef_flag, + visibility = self.visibility) + if self.items is not None: + if self.in_pxd and not env.in_cinclude: + self.entry.defined_in_pxd = 1 + for item in self.items: + item.analyse_declarations(env, self.entry) + + def analyse_expressions(self, env): + pass + + def generate_execution_code(self, code): + pass + + +class CEnumDefItemNode(StatNode): + # name string + # cname string or None + # value ExprNode or None + + def analyse_declarations(self, env, enum_entry): + value_node = self.value + if value_node: + value_node.analyse_const_expression(env) + type = value_node.type + if type.is_int or type.is_enum: + value = value_node.result() + else: + error(self.pos, + "Type '%s' is not a valid enum value" % type) + value = "<error>" + else: + value = self.name + entry = env.declare_const(self.name, enum_entry.type, + value, self.pos, cname = self.cname) + enum_entry.enum_values.append(entry) + + +class CTypeDefNode(StatNode): + # base_type CBaseTypeNode + # declarator CDeclaratorNode + # visibility "public" or "private" + # in_pxd boolean + + def analyse_declarations(self, env): + base = self.base_type.analyse(env) + name_declarator, type = self.declarator.analyse(base, env) + name = name_declarator.name + cname = name_declarator.cname + entry = env.declare_typedef(name, type, self.pos, + cname = cname, visibility = self.visibility) + if self.in_pxd and not env.in_cinclude: + entry.defined_in_pxd = 1 + + def analyse_expressions(self, env): + pass + + def generate_execution_code(self, code): + pass + + +class FuncDefNode(StatNode, BlockNode): + # Base class for function definition nodes. + # + # return_type PyrexType + # #filename string C name of filename string const + # entry Symtab.Entry + + def analyse_expressions(self, env): + pass + + def need_gil_acquisition(self, lenv): + return 0 + + def generate_function_definitions(self, env, code): + # Generate C code for header and body of function + genv = env.global_scope() + lenv = LocalScope(name = self.entry.name, outer_scope = genv) + lenv.return_type = self.return_type + type = self.entry.type + if type.is_cfunction: + lenv.nogil = type.nogil and not type.with_gil + code.init_labels() + self.declare_arguments(lenv) + self.body.analyse_declarations(lenv) + self.body.analyse_expressions(lenv) + # Code for nested function definitions would go here + # if we supported them, which we probably won't. + # ----- Function header + code.putln("") + self.generate_function_header(code, + with_pymethdef = env.is_py_class_scope) + # ----- Local variable declarations + self.generate_argument_declarations(lenv, code) + code.put_var_declarations(lenv.var_entries) + init = "" + if not self.return_type.is_void: + code.putln( + "%s%s;" % + (self.return_type.declaration_code( + Naming.retval_cname), + init)) + code.put_var_declarations(lenv.temp_entries) + self.generate_keyword_list(code) + # ----- Extern library function declarations + lenv.generate_library_function_declarations(code) + # ----- GIL acquisition + acquire_gil = self.need_gil_acquisition(lenv) + if acquire_gil: + lenv.global_scope().gil_used = 1 + code.putln("PyGILState_STATE _save = PyGILState_Ensure();") + # ----- Fetch arguments + self.generate_argument_parsing_code(code) + self.generate_argument_increfs(lenv, code) + # ----- Initialise local variables + for entry in lenv.var_entries: + if entry.type.is_pyobject and entry.init_to_none and entry.used: + code.put_init_var_to_py_none(entry) + # ----- Check and convert arguments + self.generate_argument_conversion_code(code) + self.generate_argument_type_tests(code) + # ----- Function body + self.body.generate_execution_code(code) + # ----- Default return value + code.putln("") + if self.return_type.is_pyobject: + #if self.return_type.is_extension_type: + # lhs = "(PyObject *)%s" % Naming.retval_cname + #else: + lhs = Naming.retval_cname + code.put_init_to_py_none(lhs, self.return_type) + else: + val = self.return_type.default_value + if val: + code.putln("%s = %s;" % (Naming.retval_cname, val)) + #code.putln("goto %s;" % code.return_label) + # ----- Error cleanup + if code.error_label in code.labels_used: + code.put_goto(code.return_label) + code.put_label(code.error_label) + code.put_var_xdecrefs(lenv.temp_entries) + default_retval = self.return_type.default_value + err_val = self.error_value() + exc_check = self.caller_will_check_exceptions() + if err_val or exc_check: + code.putln( + '__Pyx_AddTraceback("%s");' % + self.entry.qualified_name) + val = err_val or default_retval + if val: + code.putln( + "%s = %s;" % ( + Naming.retval_cname, + val)) + else: + code.use_utility_code(unraisable_exception_utility_code) + code.putln( + '__Pyx_WriteUnraisable("%s");' % + self.entry.qualified_name) + #if not self.return_type.is_void: + if default_retval: + code.putln( + "%s = %s;" % ( + Naming.retval_cname, + default_retval)) + #self.return_type.default_value)) + # ----- Return cleanup + code.put_label(code.return_label) + code.put_var_decrefs(lenv.var_entries, used_only = 1) + #code.put_var_decrefs(lenv.arg_entries) + self.generate_argument_decrefs(lenv, code) + self.put_stararg_decrefs(code) + if acquire_gil: + code.putln("PyGILState_Release(_save);") + if not self.return_type.is_void: + code.putln("return %s;" % Naming.retval_cname) + code.putln("}") + + def put_stararg_decrefs(self, code): + pass + + def declare_argument(self, env, arg, readonly = 0): + if arg.type.is_void: + error(arg.pos, "Invalid use of 'void'") + elif not arg.type.is_complete() and not arg.type.is_array: + error(arg.pos, + "Argument type '%s' is incomplete" % arg.type) + return env.declare_arg(arg.name, arg.type, arg.pos, + readonly = readonly) + + def generate_argument_increfs(self, env, code): + # Turn writable borrowed argument refs into owned refs. + # This is necessary, because if the argument is assigned to, + # it will be decrefed. + for entry in env.arg_entries: + if not entry.is_readonly: + code.put_var_incref(entry) + + def generate_argument_decrefs(self, env, code): + for entry in env.arg_entries: + if not entry.is_readonly: + code.put_var_decref(entry) + + def generate_execution_code(self, code): + pass + + +class CFuncDefNode(FuncDefNode): + # C function definition. + # + # visibility 'private' or 'public' or 'extern' + # base_type CBaseTypeNode + # declarator CDeclaratorNode + # body StatListNode + # api boolean + # + # with_gil boolean Acquire GIL around body + # type CFuncType + + def unqualified_name(self): + return self.entry.name + + def analyse_declarations(self, env): + base_type = self.base_type.analyse(env) + name_declarator, type = self.declarator.analyse(base_type, env) + if not type.is_cfunction: + error(self.pos, + "Suite attached to non-function declaration") + # Remember the actual type according to the function header + # written here, because the type in the symbol table entry + # may be different if we're overriding a C method inherited + # from the base type of an extension type. + self.type = type + name = name_declarator.name + cname = name_declarator.cname + self.entry = env.declare_cfunction( + name, type, self.pos, + cname = cname, visibility = self.visibility, + defining = self.body is not None, + api = self.api) + self.return_type = type.return_type + + def declare_arguments(self, env): + type = self.type + without_gil = type.nogil and not type.with_gil + for arg in type.args: + if not arg.name: + error(arg.pos, "Missing argument name") + self.declare_argument(env, arg, + readonly = without_gil and arg.type.is_pyobject) + + def need_gil_acquisition(self, lenv): + type = self.type + with_gil = type.with_gil + if type.nogil and not with_gil: +# for arg in type.args: +# if arg.type.is_pyobject: +# error(self.pos, +# "Function with Python argument cannot be declared nogil") + if type.return_type.is_pyobject: + error(self.pos, + "Function with Python return type cannot be declared nogil") + for entry in lenv.var_entries + lenv.temp_entries: + #print "CFuncDefNode.need_gil_acquisition:", entry.name, entry.cname, "readonly =", entry.is_readonly ### + if entry.type.is_pyobject and not entry.is_readonly: + error(self.pos, "Function declared nogil has Python locals or temporaries") + return with_gil + + def generate_function_header(self, code, with_pymethdef): + arg_decls = [] + type = self.type + visibility = self.entry.visibility + for arg in type.args: + arg_decls.append(arg.declaration_code()) + if type.has_varargs: + arg_decls.append("...") + if not arg_decls: + arg_decls = ["void"] + entity = type.function_header_code(self.entry.func_cname, + string.join(arg_decls, ",")) + if visibility == 'public': + dll_linkage = "DL_EXPORT" + else: + dll_linkage = None + header = self.return_type.declaration_code(entity, + dll_linkage = dll_linkage) + if visibility <> 'private': + storage_class = "%s " % Naming.extern_c_macro + else: + storage_class = "static " + code.putln("%s%s {" % ( + storage_class, + header)) + + def generate_argument_declarations(self, env, code): + # Arguments already declared in function header + pass + + def generate_keyword_list(self, code): + pass + + def generate_argument_parsing_code(self, code): + pass + + def generate_argument_conversion_code(self, code): + pass + + def generate_argument_type_tests(self, code): + pass + + def error_value(self): + if self.return_type.is_pyobject: + return "0" + else: + #return None + return self.entry.type.exception_value + + def caller_will_check_exceptions(self): + return self.entry.type.exception_check + + +class PyArgDeclNode(Node): + # Argument which must be a Python object (used + # for * and ** arguments). + # + # name string + # entry Symtab.Entry + + pass + + +class DefNode(FuncDefNode): + # A Python function definition. + # + # name string the Python name of the function + # args [CArgDeclNode] formal arguments + # star_arg PyArgDeclNode or None * argument + # starstar_arg PyArgDeclNode or None ** argument + # doc string or None + # body StatListNode + # + # The following subnode is constructed internally + # when the def statement is inside a Python class definition. + # + # assmt AssignmentNode Function construction/assignment + + assmt = None + num_kwonly_args = 0 + reqd_kw_flags_cname = "0" + has_star_or_kwonly_args = 0 + + def __init__(self, pos, **kwds): + FuncDefNode.__init__(self, pos, **kwds) + n = 0 + for arg in self.args: + if arg.kw_only: + n += 1 + self.num_kwonly_args = n + if self.star_arg or self.starstar_arg or n > 0: + self.has_star_or_kwonly_args = 1 + + def analyse_declarations(self, env): + for arg in self.args: + base_type = arg.base_type.analyse(env) + name_declarator, type = \ + arg.declarator.analyse(base_type, env) + arg.name = name_declarator.name + if name_declarator.cname: + error(self.pos, + "Python function argument cannot have C name specification") + arg.type = type.as_argument_type() + arg.hdr_type = None + arg.needs_conversion = 0 + arg.needs_type_test = 0 + arg.is_generic = 1 + if arg.allow_none is not None and not arg.type.is_extension_type: + error(self.pos, + "Only extension type arguments can have 'or None' or 'not None'") + self.declare_pyfunction(env) + self.analyse_signature(env) + self.return_type = self.entry.signature.return_type() +# if self.has_star_or_kwonly_args: +# env.use_utility_code(get_starargs_utility_code) + + def analyse_signature(self, env): + any_type_tests_needed = 0 + sig = self.entry.signature + nfixed = sig.num_fixed_args() + for i in range(nfixed): + if i < len(self.args): + arg = self.args[i] + arg.is_generic = 0 + if sig.is_self_arg(i): + arg.is_self_arg = 1 + arg.hdr_type = arg.type = env.parent_type + arg.needs_conversion = 0 + else: + arg.hdr_type = sig.fixed_arg_type(i) + if not arg.type.same_as(arg.hdr_type): + if arg.hdr_type.is_pyobject and arg.type.is_pyobject: + arg.needs_type_test = 1 + any_type_tests_needed = 1 + else: + arg.needs_conversion = 1 + if arg.needs_conversion: + arg.hdr_cname = Naming.arg_prefix + arg.name + else: + arg.hdr_cname = Naming.var_prefix + arg.name + else: + self.bad_signature() + return + if nfixed < len(self.args): + if not sig.has_generic_args: + self.bad_signature() + for arg in self.args: + if arg.is_generic and arg.type.is_extension_type: + arg.needs_type_test = 1 + any_type_tests_needed = 1 +# if any_type_tests_needed: +# env.use_utility_code(arg_type_test_utility_code) + + def bad_signature(self): + sig = self.entry.signature + expected_str = "%d" % sig.num_fixed_args() + if sig.has_generic_args: + expected_str = expected_str + " or more" + name = self.name + if name.startswith("__") and name.endswith("__"): + desc = "Special method" + else: + desc = "Method" + error(self.pos, + "%s %s has wrong number of arguments " + "(%d declared, %s expected)" % ( + desc, self.name, len(self.args), expected_str)) + + def declare_pyfunction(self, env): + #print "DefNode.declare_pyfunction:", self.name, "in", env ### + name = self.name + entry = env.declare_pyfunction(self.name, self.pos) + self.entry = entry + prefix = env.scope_prefix + entry.func_cname = \ + Naming.func_prefix + prefix + name + entry.pymethdef_cname = \ + Naming.pymethdef_prefix + prefix + name + if not entry.is_special: + entry.doc = self.doc + entry.doc_cname = \ + Naming.funcdoc_prefix + prefix + name + + def declare_arguments(self, env): + for arg in self.args: + if not arg.name: + error(arg.pos, "Missing argument name") + if arg.needs_conversion: + arg.entry = env.declare_var(arg.name, arg.type, arg.pos) + if arg.type.is_pyobject: + arg.entry.init = "0" + arg.entry.init_to_none = 0 + else: + arg.entry = self.declare_argument(env, arg) + arg.entry.used = 1 + arg.entry.is_self_arg = arg.is_self_arg + if arg.hdr_type: + if arg.is_self_arg or \ + (arg.type.is_extension_type and not arg.hdr_type.is_extension_type): + arg.entry.is_declared_generic = 1 + self.declare_python_arg(env, self.star_arg) + self.declare_python_arg(env, self.starstar_arg) + + def declare_python_arg(self, env, arg): + if arg: + entry = env.declare_var(arg.name, + PyrexTypes.py_object_type, arg.pos) + entry.used = 1 + entry.init = "0" + entry.init_to_none = 0 + entry.xdecref_cleanup = 1 + arg.entry = entry + + def analyse_expressions(self, env): + self.analyse_default_values(env) + if env.is_py_class_scope: + self.synthesize_assignment_node(env) + + def analyse_default_values(self, env): + for arg in self.args: + if arg.default: + if arg.is_generic: + arg.default.analyse_types(env) + arg.default = arg.default.coerce_to(arg.type, env) + arg.default.allocate_temps(env) + arg.default_entry = env.add_default_value(arg.type) + arg.default_entry.used = 1 + else: + error(arg.pos, + "This argument cannot have a default value") + arg.default = None + + def synthesize_assignment_node(self, env): + import ExprNodes + self.assmt = SingleAssignmentNode(self.pos, + lhs = ExprNodes.NameNode(self.pos, name = self.name), + rhs = ExprNodes.UnboundMethodNode(self.pos, + class_cname = env.class_obj_cname, + function = ExprNodes.PyCFunctionNode(self.pos, + pymethdef_cname = self.entry.pymethdef_cname))) + self.assmt.analyse_declarations(env) + self.assmt.analyse_expressions(env) + + def generate_function_header(self, code, with_pymethdef): + arg_code_list = [] + sig = self.entry.signature + if sig.has_dummy_arg: + arg_code_list.append( + "PyObject *%s" % Naming.self_cname) + for arg in self.args: + if not arg.is_generic: + if arg.is_self_arg: + arg_code_list.append("PyObject *%s" % arg.hdr_cname) + else: + arg_code_list.append( + arg.hdr_type.declaration_code(arg.hdr_cname)) + if sig.has_generic_args: + arg_code_list.append( + "PyObject *%s, PyObject *%s" + % (Naming.args_cname, Naming.kwds_cname)) + arg_code = ", ".join(arg_code_list) + dc = self.return_type.declaration_code(self.entry.func_cname) + header = "static %s(%s)" % (dc, arg_code) + code.putln("%s; /*proto*/" % header) + if self.entry.doc: + code.putln( + 'static char %s[] = "%s";' % ( + self.entry.doc_cname, + self.entry.doc)) + if with_pymethdef: + code.put( + "static PyMethodDef %s = " % + self.entry.pymethdef_cname) + code.put_pymethoddef(self.entry, ";") + code.putln("%s {" % header) + + def generate_argument_declarations(self, env, code): + for arg in self.args: + if arg.is_generic: # or arg.needs_conversion: + code.put_var_declaration(arg.entry) + + def generate_keyword_list(self, code): + if self.entry.signature.has_generic_args: + reqd_kw_flags = [] + has_reqd_kwds = False + code.put( + "static char *%s[] = {" % + Naming.kwdlist_cname) + for arg in self.args: + if arg.is_generic: + code.put( + '"%s",' % + arg.name) + if arg.kw_only and not arg.default: + has_reqd_kwds = 1 + flag = "1" + else: + flag = "0" + reqd_kw_flags.append(flag) + code.putln( + "0};") + if has_reqd_kwds: + flags_name = Naming.reqd_kwds_cname + self.reqd_kw_flags_cname = flags_name + code.putln( + "static char %s[] = {%s};" % ( + flags_name, + ",".join(reqd_kw_flags))) + + def generate_argument_parsing_code(self, code): + # Generate PyArg_ParseTuple call for generic + # arguments, if any. + has_kwonly_args = self.num_kwonly_args > 0 + has_star_or_kw_args = self.star_arg is not None \ + or self.starstar_arg is not None or has_kwonly_args + if not self.entry.signature.has_generic_args: + if has_star_or_kw_args: + error(self.pos, "This method cannot have * or keyword arguments") + else: + arg_addrs = [] + arg_formats = [] + default_seen = 0 + for arg in self.args: + arg_entry = arg.entry + if arg.is_generic: + if arg.default: + code.putln( + "%s = %s;" % ( + arg_entry.cname, + arg.default_entry.cname)) + if not default_seen: + arg_formats.append("|") + default_seen = 1 + elif default_seen and not arg.kw_only: + error(arg.pos, "Non-default argument following default argument") + arg_addrs.append("&" + arg_entry.cname) + format = arg_entry.type.parsetuple_format + if format: + arg_formats.append(format) + else: + error(arg.pos, + "Cannot convert Python object argument to type '%s'" + % arg.type) + error_return_code = "return %s;" % self.error_value() + argformat = '"%s"' % string.join(arg_formats, "") + if has_star_or_kw_args: + self.generate_stararg_getting_code(code) + pt_arglist = [Naming.args_cname, Naming.kwds_cname, argformat, + Naming.kwdlist_cname] + arg_addrs + pt_argstring = string.join(pt_arglist, ", ") + code.put( + 'if (!PyArg_ParseTupleAndKeywords(%s)) ' % + pt_argstring) + if has_star_or_kw_args: + code.putln("{") + code.put_xdecref(Naming.args_cname, py_object_type) + code.put_xdecref(Naming.kwds_cname, py_object_type) + self.generate_arg_xdecref(self.star_arg, code) + self.generate_arg_xdecref(self.starstar_arg, code) + code.putln(error_return_code) + code.putln("}") + else: + code.putln(error_return_code) + + def put_stararg_decrefs(self, code): + if self.has_star_or_kwonly_args: + code.put_xdecref(Naming.args_cname, py_object_type) + code.put_xdecref(Naming.kwds_cname, py_object_type) + + def generate_arg_xdecref(self, arg, code): + if arg: + code.put_var_xdecref(arg.entry) + + def arg_address(self, arg): + if arg: + return "&%s" % arg.entry.cname + else: + return 0 + + def generate_stararg_getting_code(self, code): + num_kwonly = self.num_kwonly_args + nargs = len(self.args) - num_kwonly - self.entry.signature.num_fixed_args() + star_arg_addr = self.arg_address(self.star_arg) + starstar_arg_addr = self.arg_address(self.starstar_arg) + code.use_utility_code(get_starargs_utility_code) + code.putln( + "if (__Pyx_GetStarArgs(&%s, &%s, %s, %s, %s, %s, %s) < 0) return %s;" % ( + Naming.args_cname, + Naming.kwds_cname, + Naming.kwdlist_cname, + nargs, + star_arg_addr, + starstar_arg_addr, + self.reqd_kw_flags_cname, + self.error_value())) + + def generate_argument_conversion_code(self, code): + # Generate code to convert arguments from + # signature type to declared type, if needed. + for arg in self.args: + if arg.needs_conversion: + self.generate_arg_conversion(arg, code) + + def generate_arg_conversion(self, arg, code): + # Generate conversion code for one argument. + old_type = arg.hdr_type + new_type = arg.type + if old_type.is_pyobject: + self.generate_arg_conversion_from_pyobject(arg, code) + elif new_type.is_pyobject: + self.generate_arg_conversion_to_pyobject(arg, code) + else: + if new_type.assignable_from(old_type): + code.putln( + "%s = %s;" % (arg.entry.cname, arg.hdr_cname)) + else: + error(arg.pos, + "Cannot convert argument from '%s' to '%s'" % + (old_type, new_type)) + + def generate_arg_conversion_from_pyobject(self, arg, code): + new_type = arg.type + func = new_type.from_py_function + if func: + code.putln("%s = %s(%s); if (PyErr_Occurred()) %s" % ( + arg.entry.cname, + func, + arg.hdr_cname, + code.error_goto(arg.pos))) + else: + error(arg.pos, + "Cannot convert Python object argument to type '%s'" + % new_type) + + def generate_arg_conversion_to_pyobject(self, arg, code): + old_type = arg.hdr_type + func = old_type.to_py_function + if func: + code.putln("%s = %s(%s); if (!%s) %s" % ( + arg.entry.cname, + func, + arg.hdr_cname, + arg.entry.cname, + code.error_goto(arg.pos))) + else: + error(arg.pos, + "Cannot convert argument of type '%s' to Python object" + % old_type) + + def generate_argument_type_tests(self, code): + # Generate type tests for args whose signature + # type is PyObject * and whose declared type is + # a subtype thereof. + for arg in self.args: + if arg.needs_type_test: + self.generate_arg_type_test(arg, code) + + def generate_arg_type_test(self, arg, code): + # Generate type test for one argument. + if arg.type.typeobj_is_available(): + typeptr_cname = arg.type.typeptr_cname + arg_code = "((PyObject *)%s)" % arg.entry.cname + code.use_utility_code(arg_type_test_utility_code) + code.putln( + 'if (!__Pyx_ArgTypeTest(%s, %s, %d, "%s")) %s' % ( + arg_code, + typeptr_cname, + #not arg.not_none, + arg.allow_none <> False, + arg.name, + code.error_goto(arg.pos))) + if arg.allow_none is None: + one_time_warning(arg.pos, 'or_none', + "'not None' will become the default in a future version of Pyrex. " + "Use 'or None' to allow passing None.") + else: + error(arg.pos, "Cannot test type of extern C class " + "without type object name specification") + + def generate_execution_code(self, code): + # Evaluate and store argument default values + for arg in self.args: + default = arg.default + if default: + default.generate_evaluation_code(code) + default.make_owned_reference(code) + code.putln( + "%s = %s;" % ( + arg.default_entry.cname, + default.result_as(arg.default_entry.type))) + default.generate_post_assignment_code(code) +# if default.is_temp and default.type.is_pyobject: +# code.putln( +# "%s = 0;" % +# default.result()) + # For Python class methods, create and store function object + if self.assmt: + self.assmt.generate_execution_code(code) + + def error_value(self): + return self.entry.signature.error_value + + def caller_will_check_exceptions(self): + return 1 + + +class PyClassDefNode(StatNode, BlockNode): + # A Python class definition. + # + # name string Name of the class + # doc string or None + # body StatNode Attribute definition code + # entry Symtab.Entry + # scope PyClassScope + # + # The following subnodes are constructed internally: + # + # dict DictNode Class dictionary + # classobj ClassNode Class object + # target NameNode Variable to assign class object to + + def __init__(self, pos, name, bases, doc, body): + StatNode.__init__(self, pos) + self.name = name + self.doc = doc + self.body = body + import ExprNodes + self.dict = ExprNodes.DictNode(pos, key_value_pairs = []) + if self.doc: + doc_node = ExprNodes.StringNode(pos, value = self.doc) + else: + doc_node = None + self.classobj = ExprNodes.ClassNode(pos, + name = ExprNodes.StringNode(pos, value = name), + bases = bases, dict = self.dict, doc = doc_node) + self.target = ExprNodes.NameNode(pos, name = name) + + def analyse_declarations(self, env): + self.target.analyse_target_declaration(env) + + def analyse_expressions(self, env): + self.dict.analyse_expressions(env) + self.classobj.analyse_expressions(env) + genv = env.global_scope() + cenv = PyClassScope(name = self.name, outer_scope = genv) + cenv.class_dict_cname = self.dict.result() + cenv.class_obj_cname = self.classobj.result() + self.scope = cenv + self.body.analyse_declarations(cenv) + self.body.analyse_expressions(cenv) + self.target.analyse_target_expression(env, self.classobj) + self.dict.release_temp(env) + #self.classobj.release_temp(env) + #self.target.release_target_temp(env) + + def generate_function_definitions(self, env, code): + #self.generate_py_string_decls(self.scope, code) + self.body.generate_function_definitions( + self.scope, code) + + def generate_execution_code(self, code): + self.dict.generate_evaluation_code(code) + self.classobj.generate_evaluation_code(code) + self.body.generate_execution_code(code) + self.target.generate_assignment_code(self.classobj, code) + self.dict.generate_disposal_code(code) + + +class CClassDefNode(StatNode): + # An extension type definition. + # + # visibility 'private' or 'public' or 'extern' + # typedef_flag boolean + # api boolean + # module_name string or None For import of extern type objects + # class_name string Unqualified name of class + # as_name string or None Name to declare as in this scope + # base_class_module string or None Module containing the base class + # base_class_name string or None Name of the base class + # options CClassOptions: + # objstruct_name string or None Specified C name of object struct + # typeobj_name string or None Specified C name of type object + # no_gc boolean Suppress GC support + # in_pxd boolean Is in a .pxd file + # doc string or None + # body StatNode or None + # entry Symtab.Entry + # base_type PyExtensionType or None + + entry = None + + def analyse_declarations(self, env): + #print "CClassDefNode.analyse_declarations:", self.class_name + #print "...visibility =", self.visibility + #print "...module_name =", self.module_name + if env.in_cinclude and not self.options.objstruct_cname: + error(self.pos, "Object struct name specification required for " + "C class defined in 'extern from' block") + self.base_type = None + has_body = self.body is not None + if self.base_class_name: + if self.base_class_module: + base_class_scope = env.find_module(self.base_class_module, self.pos) + else: + base_class_scope = env + if base_class_scope: + base_class_entry = base_class_scope.find(self.base_class_name, self.pos) + if base_class_entry: + if not base_class_entry.is_type: + error(self.pos, "'%s' is not a type name" % self.base_class_name) + elif not base_class_entry.type.is_extension_type: + error(self.pos, "'%s' is not an extension type" % self.base_class_name) + elif has_body and base_class_entry.visibility <> 'extern' and not base_class_entry.type.is_defined(): + error(self.pos, "Base class '%s' is incomplete" % self.base_class_name) + else: + self.base_type = base_class_entry.type + if self.module_name and self.visibility <> 'extern': + module_path = self.module_name.split(".") + home_scope = env.find_imported_module(module_path, self.pos) + if not home_scope: + return + else: + home_scope = env + self.entry = home_scope.declare_c_class( + name = self.class_name, + pos = self.pos, + defining = has_body and self.in_pxd, + implementing = has_body and not self.in_pxd, + module_name = self.module_name, + base_type = self.base_type, + visibility = self.visibility, + typedef_flag = self.typedef_flag, + api = self.api, + options = self.options) + if home_scope is not env and self.visibility == 'extern': + env.add_imported_entry(self.class_name, self.entry, pos) + scope = self.entry.type.scope + if self.doc: + scope.doc = self.doc + if has_body: + self.body.analyse_declarations(scope) + if self.in_pxd: + scope.defined = 1 + else: + scope.implemented = 1 + env.allocate_vtable_names(self.entry) + + def analyse_expressions(self, env): + if self.body: + self.body.analyse_expressions(env) + + def generate_function_definitions(self, env, code): + if self.entry and self.body: +# self.body.generate_function_definitions( +# self.entry.type.scope, code) + self.body.generate_function_definitions(env, code) + + def generate_execution_code(self, code): + # This is needed to generate evaluation code for + # default values of method arguments. + if self.body: + self.body.generate_execution_code(code) + + +class PropertyNode(StatNode): + # Definition of a property in an extension type. + # + # name string + # doc string or None Doc string + # body StatListNode + + def analyse_declarations(self, env): + #print "PropertyNode.analyse_declarations:", env ### + entry = env.declare_property(self.name, self.doc, self.pos) + if entry: + #if self.doc: + # doc_entry = env.get_string_const(self.doc) + # entry.doc_cname = doc_entry.cname + self.body.analyse_declarations(entry.scope) + + def analyse_expressions(self, env): + self.body.analyse_expressions(env) + + def generate_function_definitions(self, env, code): + self.body.generate_function_definitions(env, code) + + def generate_execution_code(self, code): + pass + + +class GlobalNode(StatNode): + # Global variable declaration. + # + # names [string] + + def analyse_declarations(self, env): + for name in self.names: + env.declare_global(name, self.pos) + + def analyse_expressions(self, env): + pass + + def generate_execution_code(self, code): + pass + + +class ExprStatNode(StatNode): + # Expression used as a statement. + # + # expr ExprNode + + def analyse_expressions(self, env): + self.expr.analyse_expressions(env) + self.expr.release_temp(env) + + def generate_execution_code(self, code): + self.expr.generate_evaluation_code(code) + if not self.expr.is_temp and self.expr.result(): + code.putln("%s;" % self.expr.result()) + self.expr.generate_disposal_code(code) + + +class AssignmentNode(StatNode): + # Abstract base class for assignment nodes. + # + # The analyse_expressions and generate_execution_code + # phases of assignments are split into two sub-phases + # each, to enable all the right hand sides of a + # parallel assignment to be evaluated before assigning + # to any of the left hand sides. + + def analyse_expressions(self, env): + self.analyse_types(env) + self.allocate_rhs_temps(env) + self.allocate_lhs_temps(env) + + def generate_execution_code(self, code): + self.generate_rhs_evaluation_code(code) + self.generate_assignment_code(code) + + +class SingleAssignmentNode(AssignmentNode): + # The simplest case: + # + # a = b + # + # lhs ExprNode Left hand side + # rhs ExprNode Right hand side + + def analyse_declarations(self, env): + self.lhs.analyse_target_declaration(env) + + def analyse_types(self, env, use_temp = 0): + self.rhs.analyse_types(env) + self.lhs.analyse_target_types(env) + self.lhs.gil_assignment_check(env) + self.rhs = self.rhs.coerce_to(self.lhs.type, env) + if use_temp: + self.rhs = self.rhs.coerce_to_temp(env) + + def allocate_rhs_temps(self, env): + self.rhs.allocate_temps(env) + + def allocate_lhs_temps(self, env): + self.lhs.allocate_target_temps(env, self.rhs) + + def generate_rhs_evaluation_code(self, code): + self.rhs.generate_evaluation_code(code) + + def generate_assignment_code(self, code): + self.lhs.generate_assignment_code(self.rhs, code) + + +class AugmentedAssignmentNode(SingleAssignmentNode): + # An in-place operation: + # + # a op= b + # + # lhs ExprNode Left hand side + # operator string + # rhs ExprNode Right hand side + + def analyse_types(self, env): + op = self.operator + self.rhs.analyse_types(env) + self.lhs.analyse_inplace_types(env) + type = self.lhs.type + if type.is_pyobject: + type = py_object_type + else: + if type.is_ptr and (op == '+=' or op == '-='): + type = c_int_type + elif op == "**=": + error(self.pos, "**= operator not supported for non-Python types") + return + self.rhs = self.rhs.coerce_to(type, env) + + def allocate_lhs_temps(self, env): + self.lhs.allocate_inplace_target_temps(env, self.rhs) + + def generate_assignment_code(self, code): + self.lhs.generate_inplace_assignment_code(self.operator, self.rhs, code) + + +class CascadedAssignmentNode(AssignmentNode): + # An assignment with multiple left hand sides: + # + # a = b = c + # + # lhs_list [ExprNode] Left hand sides + # rhs ExprNode Right hand sides + # + # Used internally: + # + # coerced_rhs_list [ExprNode] RHS coerced to type of each LHS + + def analyse_declarations(self, env): + for lhs in self.lhs_list: + lhs.analyse_target_declaration(env) + + def analyse_types(self, env, use_temp = 0): + self.rhs.analyse_types(env) + if use_temp: + self.rhs = self.rhs.coerce_to_temp(env) + else: + self.rhs = self.rhs.coerce_to_simple(env) + from ExprNodes import CloneNode + self.coerced_rhs_list = [] + for lhs in self.lhs_list: + lhs.analyse_target_types(env) + lhs.gil_assignment_check(env) + rhs = CloneNode(self.rhs) + rhs = rhs.coerce_to(lhs.type, env) + self.coerced_rhs_list.append(rhs) + + def allocate_rhs_temps(self, env): + self.rhs.allocate_temps(env) + + def allocate_lhs_temps(self, env): + for lhs, rhs in zip(self.lhs_list, self.coerced_rhs_list): + rhs.allocate_temps(env) + lhs.allocate_target_temps(env, rhs) + #lhs.release_target_temp(env) + #rhs.release_temp(env) + self.rhs.release_temp(env) + + def generate_rhs_evaluation_code(self, code): + self.rhs.generate_evaluation_code(code) + + def generate_assignment_code(self, code): + for i in range(len(self.lhs_list)): + lhs = self.lhs_list[i] + rhs = self.coerced_rhs_list[i] + rhs.generate_evaluation_code(code) + lhs.generate_assignment_code(rhs, code) + # Assignment has disposed of the cloned RHS + self.rhs.generate_disposal_code(code) + +class ParallelAssignmentNode(AssignmentNode): + # A combined packing/unpacking assignment: + # + # a, b, c = d, e, f + # + # This has been rearranged by the parser into + # + # a = d ; b = e ; c = f + # + # but we must evaluate all the right hand sides + # before assigning to any of the left hand sides. + # + # stats [AssignmentNode] The constituent assignments + + def analyse_declarations(self, env): + for stat in self.stats: + stat.analyse_declarations(env) + + def analyse_expressions(self, env): + for stat in self.stats: + stat.analyse_types(env, use_temp = 1) + stat.allocate_rhs_temps(env) + for stat in self.stats: + stat.allocate_lhs_temps(env) + + def generate_execution_code(self, code): + for stat in self.stats: + stat.generate_rhs_evaluation_code(code) + for stat in self.stats: + stat.generate_assignment_code(code) + + +class PrintStatNode(StatNode): + # print statement + # + # args [ExprNode] + # ends_with_comma boolean + + def analyse_expressions(self, env): + for i in range(len(self.args)): + arg = self.args[i] + arg.analyse_types(env) + arg = arg.coerce_to_pyobject(env) + arg.allocate_temps(env) + arg.release_temp(env) + self.args[i] = arg +# env.use_utility_code(printing_utility_code) + self.gil_check(env) + + gil_message = "Python print statement" + + def generate_execution_code(self, code): + for arg in self.args: + arg.generate_evaluation_code(code) + code.use_utility_code(printing_utility_code) + code.putln( + "if (__Pyx_PrintItem(%s) < 0) %s" % ( + arg.py_result(), + code.error_goto(self.pos))) + arg.generate_disposal_code(code) + if not self.ends_with_comma: + code.use_utility_code(printing_utility_code) + code.putln( + "if (__Pyx_PrintNewline() < 0) %s" % + code.error_goto(self.pos)) + + +class DelStatNode(StatNode): + # del statement + # + # args [ExprNode] + + def analyse_declarations(self, env): + for arg in self.args: + arg.analyse_target_declaration(env) + + def analyse_expressions(self, env): + for arg in self.args: + arg.analyse_target_expression(env, None) + type = arg.type + if not (type.is_pyobject + or (type.is_ptr and type.base_type.is_struct_or_union + and type.base_type.scope.is_cplus)): + error(arg.pos, "'del' can only be applied to Python object or pointer to C++ type") + if type.is_pyobject: + self.gil_check(env) + + gil_message = "Deleting Python object" + + def generate_execution_code(self, code): + for arg in self.args: + if arg.type.is_pyobject: + arg.generate_deletion_code(code) + else: + arg.generate_evaluation_code(code) + code.putln("delete %s;" % arg.result()) + arg.generate_disposal_code(code) + + +class PassStatNode(StatNode): + # pass statement + + def analyse_expressions(self, env): + pass + + def generate_execution_code(self, code): + pass + + +class BreakStatNode(StatNode): + + def analyse_expressions(self, env): + pass + + def generate_execution_code(self, code): + if not code.break_label: + error(self.pos, "break statement not inside loop") + else: + #code.putln( + # "goto %s;" % + # code.break_label) + code.put_goto(code.break_label) + + +class ContinueStatNode(StatNode): + + def analyse_expressions(self, env): + pass + + def generate_execution_code(self, code): + if code.in_try_finally: + error(self.pos, "continue statement inside try of try...finally") + elif not code.continue_label: + error(self.pos, "continue statement not inside loop") + else: + #code.putln( + # "goto %s;" % + # code.continue_label) + code.put_goto(code.continue_label) + + +class ReturnStatNode(StatNode): + # return statement + # + # value ExprNode or None + # return_type PyrexType + # temps_in_use [Entry] Temps in use at time of return + + def analyse_expressions(self, env): + return_type = env.return_type + self.return_type = return_type + self.temps_in_use = env.temps_in_use() + if not return_type: + error(self.pos, "Return not inside a function body") + return + if self.value: + self.value.analyse_types(env) + if return_type.is_void or return_type.is_returncode: + error(self.value.pos, + "Return with value in void function") + else: + self.value = self.value.coerce_to(env.return_type, env) + self.value.allocate_temps(env) + self.value.release_temp(env) + else: + if (not return_type.is_void + and not return_type.is_pyobject + and not return_type.is_returncode): + error(self.pos, "Return value required") + if return_type.is_pyobject: + self.gil_check(env) + + gil_message = "Returning Python object" + + def generate_execution_code(self, code): + if not self.return_type: + # error reported earlier + return + if self.value: + self.value.generate_evaluation_code(code) + self.value.make_owned_reference(code) + code.putln( + "%s = %s;" % ( + Naming.retval_cname, + self.value.result_as(self.return_type))) + self.value.generate_post_assignment_code(code) + else: + if self.return_type.is_pyobject: + code.put_init_to_py_none(Naming.retval_cname, self.return_type) + elif self.return_type.is_returncode: + code.putln( + "%s = %s;" % ( + Naming.retval_cname, + self.return_type.default_value)) + for entry in self.temps_in_use: + code.put_var_decref_clear(entry) + #code.putln( + # "goto %s;" % + # code.return_label) + code.put_goto(code.return_label) + + +class RaiseStatNode(StatNode): + # raise statement + # + # exc_type ExprNode or None + # exc_value ExprNode or None + # exc_tb ExprNode or None + + def analyse_expressions(self, env): + if self.exc_type: + self.exc_type.analyse_types(env) + self.exc_type = self.exc_type.coerce_to_pyobject(env) + self.exc_type.allocate_temps(env) + if self.exc_value: + self.exc_value.analyse_types(env) + self.exc_value = self.exc_value.coerce_to_pyobject(env) + self.exc_value.allocate_temps(env) + if self.exc_tb: + self.exc_tb.analyse_types(env) + self.exc_tb = self.exc_tb.coerce_to_pyobject(env) + self.exc_tb.allocate_temps(env) + if self.exc_type: + self.exc_type.release_temp(env) + if self.exc_value: + self.exc_value.release_temp(env) + if self.exc_tb: + self.exc_tb.release_temp(env) + self.gil_check(env) + + gil_message = "Raising exception" + + def generate_execution_code(self, code): + if self.exc_type: + self.exc_type.generate_evaluation_code(code) + type_code = self.exc_type.py_result() + else: + type_code = 0 + if self.exc_value: + self.exc_value.generate_evaluation_code(code) + value_code = self.exc_value.py_result() + else: + value_code = "0" + if self.exc_tb: + self.exc_tb.generate_evaluation_code(code) + tb_code = self.exc_tb.py_result() + else: + tb_code = "0" + code.use_utility_code(raise_utility_code) + code.putln( + "__Pyx_Raise(%s, %s, %s);" % ( + type_code, + value_code, + tb_code)) + if self.exc_type: + self.exc_type.generate_disposal_code(code) + if self.exc_value: + self.exc_value.generate_disposal_code(code) + if self.exc_tb: + self.exc_tb.generate_disposal_code(code) + code.putln( + code.error_goto(self.pos)) + + +class ReraiseStatNode(StatNode): + + def analyse_expressions(self, env): + env.reraise_used = 1 + self.gil_check(env) + + gil_message = "Raising exception" + + def generate_execution_code(self, code): + vars = code.exc_vars + if vars: + tvars = tuple(vars) + code.putln("PyErr_Restore(%s, %s, %s);" % tvars) + code.putln("%s = %s = %s = 0;" % tvars) + code.putln(code.error_goto(self.pos)) + else: + error(self.pos, "Reraise not inside except clause") + + +class AssertStatNode(StatNode): + # assert statement + # + # cond ExprNode + # value ExprNode or None + + def analyse_expressions(self, env): + self.cond = self.cond.analyse_boolean_expression(env) + if self.value: + self.value.analyse_types(env) + self.value = self.value.coerce_to_pyobject(env) + self.value.allocate_temps(env) + self.cond.release_temp(env) + if self.value: + self.value.release_temp(env) + self.gil_check(env) + + gil_message = "Raising exception" + + def generate_execution_code(self, code): + code.putln("#ifndef PYREX_WITHOUT_ASSERTIONS") + self.cond.generate_evaluation_code(code) + code.putln( + "if (!%s) {" % + self.cond.result()) + if self.value: + self.value.generate_evaluation_code(code) + if self.value: + code.putln( + "PyErr_SetObject(PyExc_AssertionError, %s);" % + self.value.py_result()) + else: + code.putln( + "PyErr_SetNone(PyExc_AssertionError);") + code.putln( + code.error_goto(self.pos)) + code.putln( + "}") + self.cond.generate_disposal_code(code) + # Disposal code for value not needed because exception always raised + #if self.value: + # self.value.generate_disposal_code(code) + code.putln("#endif") + +class IfStatNode(StatNode): + # if statement + # + # if_clauses [IfClauseNode] + # else_clause StatNode or None + + def analyse_declarations(self, env): + for if_clause in self.if_clauses: + if_clause.analyse_declarations(env) + if self.else_clause: + self.else_clause.analyse_declarations(env) + + def analyse_expressions(self, env): + for if_clause in self.if_clauses: + if_clause.analyse_expressions(env) + if self.else_clause: + self.else_clause.analyse_expressions(env) + + def generate_execution_code(self, code): + end_label = code.new_label() + for if_clause in self.if_clauses: + if_clause.generate_execution_code(code, end_label) + if self.else_clause: + code.putln("/*else*/ {") + self.else_clause.generate_execution_code(code) + code.putln("}") + code.put_label(end_label) + + +class IfClauseNode(Node): + # if or elif clause in an if statement + # + # condition ExprNode + # body StatNode + + def analyse_declarations(self, env): + self.condition.analyse_declarations(env) + self.body.analyse_declarations(env) + + def analyse_expressions(self, env): + self.condition = \ + self.condition.analyse_temp_boolean_expression(env) + self.condition.release_temp(env) + self.body.analyse_expressions(env) + + def generate_execution_code(self, code, end_label): + self.condition.generate_evaluation_code(code) + code.putln( + "if (%s) {" % + self.condition.result()) + self.body.generate_execution_code(code) + #code.putln( + # "goto %s;" % + # end_label) + code.put_goto(end_label) + code.putln("}") + + +class WhileStatNode(StatNode): + # while statement + # + # condition ExprNode + # body StatNode + # else_clause StatNode + + def analyse_declarations(self, env): + self.body.analyse_declarations(env) + if self.else_clause: + self.else_clause.analyse_declarations(env) + + def analyse_expressions(self, env): + self.condition = \ + self.condition.analyse_temp_boolean_expression(env) + self.condition.release_temp(env) + #env.recycle_pending_temps() # TEMPORARY + self.body.analyse_expressions(env) + if self.else_clause: + self.else_clause.analyse_expressions(env) + + def generate_execution_code(self, code): + old_loop_labels = code.new_loop_labels() + code.putln( + "while (1) {") + self.condition.generate_evaluation_code(code) + code.putln( + "if (!%s) break;" % + self.condition.result()) + self.body.generate_execution_code(code) + code.put_label(code.continue_label) + code.putln("}") + break_label = code.break_label + code.set_loop_labels(old_loop_labels) + if self.else_clause: + code.putln("/*else*/ {") + self.else_clause.generate_execution_code(code) + code.putln("}") + code.put_label(break_label) + + +class ForInStatNode(StatNode): + # for statement + # + # target ExprNode + # iterator IteratorNode + # body StatNode + # else_clause StatNode + # item NextNode used internally + + def analyse_declarations(self, env): + self.target.analyse_target_declaration(env) + self.body.analyse_declarations(env) + if self.else_clause: + self.else_clause.analyse_declarations(env) + + def analyse_expressions(self, env): + import ExprNodes + self.iterator.analyse_expressions(env) + self.target.analyse_target_types(env) + self.item = ExprNodes.NextNode(self.iterator, env) + self.item = self.item.coerce_to(self.target.type, env) + self.item.allocate_temps(env) + self.target.allocate_target_temps(env, self.item) + #self.item.release_temp(env) + #self.target.release_target_temp(env) + self.body.analyse_expressions(env) + if self.else_clause: + self.else_clause.analyse_expressions(env) + self.iterator.release_temp(env) + + def generate_execution_code(self, code): + old_loop_labels = code.new_loop_labels() + self.iterator.generate_evaluation_code(code) + code.putln( + "for (;;) {") + self.item.generate_evaluation_code(code) + self.target.generate_assignment_code(self.item, code) + self.body.generate_execution_code(code) + code.put_label(code.continue_label) + code.putln( + "}") + break_label = code.break_label + code.set_loop_labels(old_loop_labels) + if self.else_clause: + code.putln("/*else*/ {") + self.else_clause.generate_execution_code(code) + code.putln("}") + code.put_label(break_label) + self.iterator.generate_disposal_code(code) + + +class IntegerForStatNode(StatNode): + # for expr rel name rel expr + # + # bound1 ExprNode + # relation1 string + # target NameNode + # relation2 string + # bound2 ExprNode + # body StatNode + # else_clause StatNode or None + # + # Used internally: + # + # is_py_target bool + # loopvar_name string + # py_loopvar_node PyTempNode or None + + def analyse_declarations(self, env): + self.target.analyse_target_declaration(env) + self.body.analyse_declarations(env) + if self.else_clause: + self.else_clause.analyse_declarations(env) + + def analyse_expressions(self, env): + import ExprNodes + self.target.analyse_target_types(env) + self.bound1.analyse_types(env) + self.bound2.analyse_types(env) + self.bound1 = self.bound1.coerce_to_integer(env) + self.bound2 = self.bound2.coerce_to_integer(env) + if not (self.bound2.is_name or self.bound2.is_literal): + self.bound2 = self.bound2.coerce_to_temp(env) + target_type = self.target.type + if not (target_type.is_pyobject or target_type.is_int): + error(self.target.pos, + "Integer for-loop variable must be of type int or Python object") + #if not (target_type.is_pyobject + # or target_type.assignable_from(PyrexTypes.c_int_type)): + # error(self.target.pos, + # "Cannot assign integer to variable of type '%s'" % target_type) + if target_type.is_int: + self.is_py_target = 0 + self.loopvar_name = self.target.entry.cname + self.py_loopvar_node = None + else: + self.is_py_target = 1 + c_loopvar_node = ExprNodes.TempNode(self.pos, + PyrexTypes.c_long_type, env) + c_loopvar_node.allocate_temps(env) + self.loopvar_name = c_loopvar_node.result() + self.py_loopvar_node = \ + ExprNodes.CloneNode(c_loopvar_node).coerce_to_pyobject(env) + self.bound1.allocate_temps(env) + self.bound2.allocate_temps(env) + if self.is_py_target: + self.py_loopvar_node.allocate_temps(env) + self.target.allocate_target_temps(env, self.py_loopvar_node) + #self.target.release_target_temp(env) + #self.py_loopvar_node.release_temp(env) + self.body.analyse_expressions(env) + if self.is_py_target: + c_loopvar_node.release_temp(env) + if self.else_clause: + self.else_clause.analyse_expressions(env) + self.bound1.release_temp(env) + self.bound2.release_temp(env) + + def generate_execution_code(self, code): + old_loop_labels = code.new_loop_labels() + self.bound1.generate_evaluation_code(code) + self.bound2.generate_evaluation_code(code) + offset, incop = self.relation_table[self.relation1] + code.putln( + "for (%s = %s%s; %s %s %s; %s%s) {" % ( + self.loopvar_name, + self.bound1.result(), offset, + self.loopvar_name, self.relation2, self.bound2.result(), + incop, self.loopvar_name)) + if self.py_loopvar_node: + self.py_loopvar_node.generate_evaluation_code(code) + self.target.generate_assignment_code(self.py_loopvar_node, code) + self.body.generate_execution_code(code) + code.put_label(code.continue_label) + code.putln("}") + break_label = code.break_label + code.set_loop_labels(old_loop_labels) + if self.else_clause: + code.putln("/*else*/ {") + self.else_clause.generate_execution_code(code) + code.putln("}") + code.put_label(break_label) + self.bound1.generate_disposal_code(code) + self.bound2.generate_disposal_code(code) + + relation_table = { + # {relop : (initial offset, increment op)} + '<=': ("", "++"), + '<' : ("+1", "++"), + '>=': ("", "--"), + '>' : ("-1", "--") + } + + +class TryExceptStatNode(StatNode): + # try .. except statement + # + # body StatNode + # except_clauses [ExceptClauseNode] + # else_clause StatNode or None + # cleanup_list [Entry] temps to clean up on error + + def analyse_declarations(self, env): + self.body.analyse_declarations(env) + for except_clause in self.except_clauses: + except_clause.analyse_declarations(env) + if self.else_clause: + self.else_clause.analyse_declarations(env) + self.gil_check(env) + + def analyse_expressions(self, env): + self.body.analyse_expressions(env) + self.cleanup_list = env.free_temp_entries[:] + for except_clause in self.except_clauses: + except_clause.analyse_expressions(env) + if self.else_clause: + self.else_clause.analyse_expressions(env) + self.gil_check(env) + + gil_message = "Try-except statement" + + def generate_execution_code(self, code): + old_error_label = code.new_error_label() + our_error_label = code.error_label + end_label = code.new_label() + code.putln( + "/*try:*/ {") + self.body.generate_execution_code(code) + code.putln( + "}") + code.error_label = old_error_label + if self.else_clause: + code.putln( + "/*else:*/ {") + self.else_clause.generate_execution_code(code) + code.putln( + "}") + code.put_goto(end_label) + code.put_label(our_error_label) + code.put_var_xdecrefs_clear(self.cleanup_list) + default_clause_seen = 0 + for except_clause in self.except_clauses: + if not except_clause.pattern: + default_clause_seen = 1 + else: + if default_clause_seen: + error(except_clause.pos, "Default except clause not last") + except_clause.generate_handling_code(code, end_label) + if not default_clause_seen: + code.put_goto(code.error_label) + code.put_label(end_label) + + +class ExceptClauseNode(Node): + # Part of try ... except statement. + # + # pattern ExprNode + # exc_target ExprNode or None + # tb_target ExprNode or None + # body StatNode + # match_flag string result of exception match + # exc_value ExcValueNode used internally + # tb_value ExcValueNode used internally + # function_name string qualified name of enclosing function + # exc_vars (string * 3) local exception variables + # reraise_used boolean body contains reraise statement + + def analyse_declarations(self, env): + if self.exc_target: + self.exc_target.analyse_target_declaration(env) + if self.tb_target: + self.tb_target.analyse_target_declaration(env) + self.body.analyse_declarations(env) + + def analyse_expressions(self, env): + genv = env.global_scope() + self.function_name = env.qualified_name + if self.pattern: + self.pattern.analyse_expressions(env) + self.pattern = self.pattern.coerce_to_pyobject(env) + self.match_flag = env.allocate_temp(PyrexTypes.c_int_type) + self.pattern.release_temp(env) + env.release_temp(self.match_flag) + self.exc_vars = [env.allocate_temp(py_object_type) for i in xrange(3)] + self.exc_value = self.analyse_target(env, self.exc_target, 1) + self.tb_value = self.analyse_target(env, self.tb_target, 2) + old_reraise_used = env.reraise_used + env.reraise_used = False + self.body.analyse_expressions(env) + self.reraise_used = env.reraise_used + env.reraise_used = old_reraise_used + for var in self.exc_vars: + env.release_temp(var) + + def analyse_target(self, env, target, var_no): + if target: + import ExprNodes + value = ExprNodes.ExcValueNode(self.pos, env, self.exc_vars[var_no]) + value.allocate_temps(env) + target.analyse_target_expression(env, value) + return value + + def generate_handling_code(self, code, end_label): + code.mark_pos(self.pos) + if self.pattern: + self.pattern.generate_evaluation_code(code) + code.putln( + "%s = PyErr_ExceptionMatches(%s);" % ( + self.match_flag, + self.pattern.py_result())) + self.pattern.generate_disposal_code(code) + code.putln( + "if (%s) {" % + self.match_flag) + else: + code.putln( + "/*except:*/ {") + any_bindings = self.exc_target or self.tb_target + exc_vars_used = any_bindings or self.reraise_used + if exc_vars_used: + if any_bindings: + code.putln( + '%s; __Pyx_AddTraceback("%s");' % ( + code.error_setup(self.pos), + self.function_name)) + exc_args = "&%s, &%s, &%s" % tuple(self.exc_vars) + code.putln("PyErr_Fetch(%s);" % exc_args) + if any_bindings: + code.use_utility_code(normalize_exception_utility_code) + code.putln("if (__Pyx_NormalizeException(%s) < 0) %s" % (exc_args, + code.error_goto(self.pos))) + if self.exc_target: + self.exc_value.generate_evaluation_code(code) + self.exc_target.generate_assignment_code(self.exc_value, code) + if self.tb_target: + self.tb_value.generate_evaluation_code(code) + self.tb_target.generate_assignment_code(self.tb_value, code) + old_exc_vars = code.exc_vars + code.exc_vars = self.exc_vars + self.body.generate_execution_code(code) + code.exc_vars = old_exc_vars + if exc_vars_used: + for var in self.exc_vars: + code.putln("Py_XDECREF(%s); %s = 0;" % (var, var)) + code.put_goto(end_label) + code.putln( + "}") + + +class TryFinallyStatNode(StatNode): + # try ... finally statement + # + # body StatNode + # finally_clause StatNode + # + # cleanup_list [Entry] temps to clean up on error + # + # The plan is that we funnel all continue, break + # return and error gotos into the beginning of the + # finally block, setting a variable to remember which + # one we're doing. At the end of the finally block, we + # switch on the variable to figure out where to go. + # In addition, if we're doing an error, we save the + # exception on entry to the finally block and restore + # it on exit. + + preserve_exception = 1 + + disallow_continue_in_try_finally = 0 + # There doesn't seem to be any point in disallowing + # continue in the try block, since we have no problem + # handling it. + + def analyse_declarations(self, env): + self.body.analyse_declarations(env) + self.finally_clause.analyse_declarations(env) + + def analyse_expressions(self, env): + self.body.analyse_expressions(env) + self.cleanup_list = env.free_temp_entries[:] + self.finally_clause.analyse_expressions(env) + self.gil_check(env) + + gil_message = "Try-finally statement" + + def generate_execution_code(self, code): + old_error_label = code.error_label + old_labels = code.all_new_labels() + new_labels = code.get_all_labels() + new_error_label = code.error_label + catch_label = code.new_label() + code.putln( + "/*try:*/ {") + if self.disallow_continue_in_try_finally: + was_in_try_finally = code.in_try_finally + code.in_try_finally = 1 + self.body.generate_execution_code(code) + if self.disallow_continue_in_try_finally: + code.in_try_finally = was_in_try_finally + code.putln( + "}") + code.putln( + "/*finally:*/ {") + cases_used = [] + error_label_used = 0 + for i, new_label in enumerate(new_labels): + if new_label in code.labels_used: + cases_used.append(i) + if new_label == new_error_label: + error_label_used = 1 + error_label_case = i + if cases_used: + code.putln( + "int __pyx_why;") + if error_label_used and self.preserve_exception: + code.putln( + "PyObject *%s, *%s, *%s;" % Naming.exc_vars) + code.putln( + "int %s;" % Naming.exc_lineno_name) + code.use_label(catch_label) + code.putln( + "__pyx_why = 0; goto %s;" % catch_label) + for i in cases_used: + new_label = new_labels[i] + #if new_label and new_label <> "<try>": + if new_label == new_error_label and self.preserve_exception: + self.put_error_catcher(code, + new_error_label, i+1, catch_label) + else: + code.putln( + "%s: __pyx_why = %s; goto %s;" % ( + new_label, + i+1, + catch_label)) + code.put_label(catch_label) + code.set_all_labels(old_labels) + if error_label_used: + code.new_error_label() + finally_error_label = code.error_label + self.finally_clause.generate_execution_code(code) + if error_label_used: + if finally_error_label in code.labels_used and self.preserve_exception: + over_label = code.new_label() + code.put_goto(over_label); + code.put_label(finally_error_label) + code.putln("if (__pyx_why == %d) {" % (error_label_case + 1)) + for var in Naming.exc_vars: + code.putln("Py_XDECREF(%s);" % var) + code.putln("}") + code.put_goto(old_error_label) + code.put_label(over_label) + code.error_label = old_error_label + if cases_used: + code.putln( + "switch (__pyx_why) {") + for i in cases_used: + old_label = old_labels[i] + if old_label == old_error_label and self.preserve_exception: + self.put_error_uncatcher(code, i+1, old_error_label) + else: + code.use_label(old_label) + code.putln( + "case %s: goto %s;" % ( + i+1, + old_label)) + code.putln( + "}") + code.putln( + "}") + + def put_error_catcher(self, code, error_label, i, catch_label): + code.putln( + "%s: {" % + error_label) + code.putln( + "__pyx_why = %s;" % + i) + code.put_var_xdecrefs_clear(self.cleanup_list) + code.putln( + "PyErr_Fetch(&%s, &%s, &%s);" % + Naming.exc_vars) + code.putln( + "%s = %s;" % ( + Naming.exc_lineno_name, Naming.lineno_cname)) + #code.putln( + # "goto %s;" % + # catch_label) + code.put_goto(catch_label) + code.putln( + "}") + + def put_error_uncatcher(self, code, i, error_label): + code.putln( + "case %s: {" % + i) + code.putln( + "PyErr_Restore(%s, %s, %s);" % + Naming.exc_vars) + code.putln( + "%s = %s;" % ( + Naming.lineno_cname, Naming.exc_lineno_name)) + for var in Naming.exc_vars: + code.putln( + "%s = 0;" % + var) + code.put_goto(error_label) + code.putln( + "}") + + +class GILStatNode(TryFinallyStatNode): + # 'with gil' or 'with nogil' statement + # + # state string 'gil' or 'nogil' + + preserve_exception = 0 + + def __init__(self, pos, state, body): + self.state = state + TryFinallyStatNode.__init__(self, pos, + body = body, + finally_clause = GILExitNode(pos, state = state)) + + def analyse_expressions(self, env): + env.global_scope().gil_used = 1 + was_nogil = env.nogil + env.nogil = 1 + TryFinallyStatNode.analyse_expressions(self, env) + env.nogil = was_nogil + + def gil_check(self, env): + pass + + def generate_execution_code(self, code): + code.putln("/*with %s:*/ {" % self.state) + if self.state == 'gil': + code.putln("PyGILState_STATE _save = PyGILState_Ensure();") + else: + code.putln("PyThreadState *_save;") + code.putln("Py_UNBLOCK_THREADS") + TryFinallyStatNode.generate_execution_code(self, code) + code.putln("}") + + +class GILExitNode(StatNode): + # Used as the 'finally' block in a GILStatNode + # + # state string 'gil' or 'nogil' + + def analyse_expressions(self, env): + pass + + def generate_execution_code(self, code): + if self.state == 'gil': + code.putln("PyGILState_Release();") + else: + code.putln("Py_BLOCK_THREADS") + + +class CImportStatNode(StatNode): + # cimport statement + # + # module_name string Qualified name of module being imported + # as_name string or None Name specified in "as" clause, if any + + def analyse_declarations(self, env): + module_scope = env.find_module(self.module_name, self.pos) + if "." in self.module_name: + names = self.module_name.split(".") + top_name = names[0] + top_module_scope = env.context.find_submodule(top_name) + module_scope = top_module_scope + for name in names[1:]: + submodule_scope = module_scope.find_submodule(name) + module_scope.declare_module(name, submodule_scope, self.pos) + if not self.as_name: + env.add_imported_module(submodule_scope) + module_scope = submodule_scope + if self.as_name: + env.declare_module(self.as_name, module_scope, self.pos) + env.add_imported_module(module_scope) + else: + env.declare_module(top_name, top_module_scope, self.pos) + env.add_imported_module(top_module_scope) + else: + name = self.as_name or self.module_name + env.declare_module(name, module_scope, self.pos) + env.add_imported_module(module_scope) + + def analyse_expressions(self, env): + pass + + def generate_execution_code(self, code): + pass + + +class FromCImportStatNode(StatNode): + # from ... cimport statement + # + # module_name string Qualified name of module + # imported_names Parsing.ImportedName Names to be imported + + def analyse_declarations(self, env): + module_scope = env.find_module(self.module_name, self.pos) + env.add_imported_module(module_scope) + for imp in self.imported_names: + kind = imp.kind + #entry = module_scope.find(imp.name, imp.pos) + entry = module_scope.lookup(imp.name) + if entry: + if kind and not self.declaration_matches(entry, kind): + entry.redeclared(pos) + else: + if kind == 'struct' or kind == 'union': + entry = module_scope.declare_struct_or_union(imp.name, + kind = kind, scope = None, typedef_flag = 0, pos = imp.pos) + elif kind == 'class': + entry = module_scope.declare_c_class(imp.name, pos = imp.pos, + module_name = self.module_name) + else: + error(imp.pos, "Name '%s' not declared in module '%s'" + % (imp.name, self.module_name)) + if entry: + local_name = imp.as_name or imp.name + env.add_imported_entry(local_name, entry, imp.pos) + + def declaration_matches(self, entry, kind): + if not entry.is_type: + return 0 + type = entry.type + if kind == 'class': + if not type.is_extension_type: + return 0 + else: + if not type.is_struct_or_union: + return 0 + if kind <> type.kind: + return 0 + return 1 + + def analyse_expressions(self, env): + pass + + def generate_execution_code(self, code): + pass + + +class FromImportStatNode(StatNode): + # from ... import statement + # + # module ImportNode + # items [(string, NameNode)] + # #interned_items [(string, NameNode)] + # item PyTempNode used internally + + def analyse_declarations(self, env): + for _, target in self.items: + target.analyse_target_declaration(env) + + def analyse_expressions(self, env): + import ExprNodes + self.module.analyse_expressions(env) + self.item = ExprNodes.PyTempNode(self.pos, env) + self.item.allocate_temp(env) + #self.interned_items = [] + for name, target in self.items: + #self.interned_items.append((env.intern(name), target)) + target.analyse_target_expression(env, None) + self.module.release_temp(env) + self.item.release_temp(env) + + def generate_execution_code(self, code): + self.module.generate_evaluation_code(code) + #for cname, target in self.interned_items: + for name, target in self.items: + cname = code.intern(name) + code.putln( + '%s = PyObject_GetAttr(%s, %s); if (!%s) %s' % ( + self.item.result(), + self.module.py_result(), + cname, + self.item.result(), + code.error_goto(self.pos))) + target.generate_assignment_code(self.item, code) + self.module.generate_disposal_code(code) + +#------------------------------------------------------------------------------------ +# +# Runtime support code +# +#------------------------------------------------------------------------------------ + +#utility_function_predeclarations = \ +#""" +#typedef struct {PyObject **p; char *s;} __Pyx_InternTabEntry; /*proto*/ +#typedef struct {PyObject **p; char *s; long n;} __Pyx_StringTabEntry; /*proto*/ +#""" + +utility_function_predeclarations = \ +""" +typedef struct {PyObject **p; int i; char *s; long n;} __Pyx_StringTabEntry; /*proto*/ +""" + +#get_name_predeclaration = \ +#"static PyObject *__Pyx_GetName(PyObject *dict, char *name); /*proto*/" + +#get_name_interned_predeclaration = \ +#"static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name); /*proto*/" + +#------------------------------------------------------------------------------------ + +printing_utility_code = [ +""" +static int __Pyx_PrintItem(PyObject *); /*proto*/ +static int __Pyx_PrintNewline(void); /*proto*/ +""",r""" +static PyObject *__Pyx_GetStdout(void) { + PyObject *f = PySys_GetObject("stdout"); + if (!f) { + PyErr_SetString(PyExc_RuntimeError, "lost sys.stdout"); + } + return f; +} + +static int __Pyx_PrintItem(PyObject *v) { + PyObject *f; + + if (!(f = __Pyx_GetStdout())) + return -1; + if (PyFile_SoftSpace(f, 1)) { + if (PyFile_WriteString(" ", f) < 0) + return -1; + } + if (PyFile_WriteObject(v, f, Py_PRINT_RAW) < 0) + return -1; + if (PyString_Check(v)) { + char *s = PyString_AsString(v); + Py_ssize_t len = PyString_Size(v); + if (len > 0 && + isspace(Py_CHARMASK(s[len-1])) && + s[len-1] != ' ') + PyFile_SoftSpace(f, 0); + } + return 0; +} + +static int __Pyx_PrintNewline(void) { + PyObject *f; + + if (!(f = __Pyx_GetStdout())) + return -1; + if (PyFile_WriteString("\n", f) < 0) + return -1; + PyFile_SoftSpace(f, 0); + return 0; +} +"""] + +#------------------------------------------------------------------------------------ + +# The following function is based on do_raise() from ceval.c. + +raise_utility_code = [ +""" +static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb); /*proto*/ +""",""" +static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb) { + if (value == Py_None) + value = NULL; + if (tb == Py_None) + tb = NULL; + Py_XINCREF(type); + Py_XINCREF(value); + Py_XINCREF(tb); + if (tb && !PyTraceBack_Check(tb)) { + PyErr_SetString(PyExc_TypeError, + "raise: arg 3 must be a traceback or None"); + goto raise_error; + } + #if PY_VERSION_HEX < 0x02050000 + if (!PyClass_Check(type)) + #else + if (!PyType_Check(type)) + #endif + { + /* Raising an instance. The value should be a dummy. */ + if (value) { + PyErr_SetString(PyExc_TypeError, + "instance exception may not have a separate value"); + goto raise_error; + } + /* Normalize to raise <class>, <instance> */ + value = type; + #if PY_VERSION_HEX < 0x02050000 + if (PyInstance_Check(type)) { + type = (PyObject*) ((PyInstanceObject*)type)->in_class; + Py_INCREF(type); + } + else { + PyErr_SetString(PyExc_TypeError, + "raise: exception must be an old-style class or instance"); + goto raise_error; + } + #else + type = (PyObject*) type->ob_type; + Py_INCREF(type); + if (!PyType_IsSubtype((PyTypeObject *)type, (PyTypeObject *)PyExc_BaseException)) { + PyErr_SetString(PyExc_TypeError, + "raise: exception class must be a subclass of BaseException"); + goto raise_error; + } + #endif + } + PyErr_Restore(type, value, tb); + return; +raise_error: + Py_XDECREF(value); + Py_XDECREF(type); + Py_XDECREF(tb); + return; +} +"""] + +#------------------------------------------------------------------------------------ + +#reraise_utility_code = [ +#""" +#static void __Pyx_ReRaise(void); /*proto*/ +#""",""" +#static void __Pyx_ReRaise(void) { +# PyThreadState *tstate = PyThreadState_Get(); +# PyObject *type = tstate->exc_type; +# PyObject *value = tstate->exc_value; +# PyObject *tb = tstate->exc_traceback; +# Py_XINCREF(type); +# Py_XINCREF(value); +# Py_XINCREF(tb); +# PyErr_Restore(type, value, tb); +#} +#"""] + +#------------------------------------------------------------------------------------ + +arg_type_test_utility_code = [ +""" +static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed, char *name); /*proto*/ +""",""" +static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed, char *name) { + if (!type) { + PyErr_Format(PyExc_SystemError, "Missing type object"); + return 0; + } + if ((none_allowed && obj == Py_None) || PyObject_TypeCheck(obj, type)) + return 1; + PyErr_Format(PyExc_TypeError, + "Argument '%s' has incorrect type (expected %s, got %s)", + name, type->tp_name, obj->ob_type->tp_name); + return 0; +} +"""] + +#------------------------------------------------------------------------------------ +# +# __Pyx_GetStarArgs splits the args tuple and kwds dict into two parts +# each, one part suitable for passing to PyArg_ParseTupleAndKeywords, +# and the other containing any extra arguments. On success, replaces +# the borrowed references *args and *kwds with references to a new +# tuple and dict, and passes back new references in *args2 and *kwds2. +# Does not touch any of its arguments on failure. +# +# Any of *kwds, args2 and kwds2 may be 0 (but not args or kwds). If +# *kwds == 0, it is not changed. If kwds2 == 0 and *kwds != 0, a new +# reference to the same dictionary is passed back in *kwds. +# +# If rqd_kwds is not 0, it is an array of booleans corresponding to the +# names in kwd_list, indicating required keyword arguments. If any of +# these are not present in kwds, an exception is raised. +# + +get_starargs_utility_code = [ +""" +static int __Pyx_GetStarArgs(PyObject **args, PyObject **kwds, char *kwd_list[], \ + Py_ssize_t nargs, PyObject **args2, PyObject **kwds2, char rqd_kwds[]); /*proto*/ +""",""" +static int __Pyx_GetStarArgs( + PyObject **args, + PyObject **kwds, + char *kwd_list[], + Py_ssize_t nargs, + PyObject **args2, + PyObject **kwds2, + char rqd_kwds[]) +{ + PyObject *x = 0, *args1 = 0, *kwds1 = 0; + int i; + char **p; + + if (args2) + *args2 = 0; + if (kwds2) + *kwds2 = 0; + + if (args2) { + args1 = PyTuple_GetSlice(*args, 0, nargs); + if (!args1) + goto bad; + *args2 = PyTuple_GetSlice(*args, nargs, PyTuple_GET_SIZE(*args)); + if (!*args2) + goto bad; + } + else if (PyTuple_GET_SIZE(*args) > nargs) { + int m = nargs; + int n = PyTuple_GET_SIZE(*args); + PyErr_Format(PyExc_TypeError, + "function takes at most %d positional arguments (%d given)", + m, n); + goto bad; + } + else { + args1 = *args; + Py_INCREF(args1); + } + + if (rqd_kwds && !*kwds) + for (i = 0, p = kwd_list; *p; i++, p++) + if (rqd_kwds[i]) + goto missing_kwarg; + + if (kwds2) { + if (*kwds) { + kwds1 = PyDict_New(); + if (!kwds1) + goto bad; + *kwds2 = PyDict_Copy(*kwds); + if (!*kwds2) + goto bad; + for (i = 0, p = kwd_list; *p; i++, p++) { + x = PyDict_GetItemString(*kwds, *p); + if (x) { + if (PyDict_SetItemString(kwds1, *p, x) < 0) + goto bad; + if (PyDict_DelItemString(*kwds2, *p) < 0) + goto bad; + } + else if (rqd_kwds && rqd_kwds[i]) + goto missing_kwarg; + } + } + else { + *kwds2 = PyDict_New(); + if (!*kwds2) + goto bad; + } + } + else { + kwds1 = *kwds; + Py_XINCREF(kwds1); + if (rqd_kwds && *kwds) + for (i = 0, p = kwd_list; *p; i++, p++) + if (rqd_kwds[i] && !PyDict_GetItemString(*kwds, *p)) + goto missing_kwarg; + } + + *args = args1; + *kwds = kwds1; + return 0; +missing_kwarg: + PyErr_Format(PyExc_TypeError, + "required keyword argument '%s' is missing", *p); +bad: + Py_XDECREF(args1); + Py_XDECREF(kwds1); + if (args2) { + Py_XDECREF(*args2); + } + if (kwds2) { + Py_XDECREF(*kwds2); + } + return -1; +} +"""] + +#------------------------------------------------------------------------------------ + +unraisable_exception_utility_code = [ +""" +static void __Pyx_WriteUnraisable(char *name); /*proto*/ +""",""" +static void __Pyx_WriteUnraisable(char *name) { + PyObject *old_exc, *old_val, *old_tb; + PyObject *ctx; + PyGILState_STATE state = PyGILState_Ensure(); + PyErr_Fetch(&old_exc, &old_val, &old_tb); + ctx = PyString_FromString(name); + PyErr_Restore(old_exc, old_val, old_tb); + if (!ctx) + ctx = Py_None; + PyErr_WriteUnraisable(ctx); + PyGILState_Release(state); +} +"""] + +#------------------------------------------------------------------------------------ + +traceback_utility_code = [ +""" +static void __Pyx_AddTraceback(char *funcname); /*proto*/ +""",""" +#include "compile.h" +#include "frameobject.h" +#include "traceback.h" + +static void __Pyx_AddTraceback(char *funcname) { + PyObject *py_srcfile = 0; + PyObject *py_funcname = 0; + PyObject *py_globals = 0; + PyObject *empty_tuple = 0; + PyObject *empty_string = 0; + PyCodeObject *py_code = 0; + PyFrameObject *py_frame = 0; + + py_srcfile = PyString_FromString(%(FILENAME)s); + if (!py_srcfile) goto bad; + py_funcname = PyString_FromString(funcname); + if (!py_funcname) goto bad; + py_globals = PyModule_GetDict(%(GLOBALS)s); + if (!py_globals) goto bad; + empty_tuple = PyTuple_New(0); + if (!empty_tuple) goto bad; + empty_string = PyString_FromString(""); + if (!empty_string) goto bad; + py_code = PyCode_New( + 0, /*int argcount,*/ + 0, /*int nlocals,*/ + 0, /*int stacksize,*/ + 0, /*int flags,*/ + empty_string, /*PyObject *code,*/ + empty_tuple, /*PyObject *consts,*/ + empty_tuple, /*PyObject *names,*/ + empty_tuple, /*PyObject *varnames,*/ + empty_tuple, /*PyObject *freevars,*/ + empty_tuple, /*PyObject *cellvars,*/ + py_srcfile, /*PyObject *filename,*/ + py_funcname, /*PyObject *name,*/ + %(LINENO)s, /*int firstlineno,*/ + empty_string /*PyObject *lnotab*/ + ); + if (!py_code) goto bad; + py_frame = PyFrame_New( + PyThreadState_Get(), /*PyThreadState *tstate,*/ + py_code, /*PyCodeObject *code,*/ + py_globals, /*PyObject *globals,*/ + 0 /*PyObject *locals*/ + ); + if (!py_frame) goto bad; + py_frame->f_lineno = %(LINENO)s; + PyTraceBack_Here(py_frame); +bad: + Py_XDECREF(py_srcfile); + Py_XDECREF(py_funcname); + Py_XDECREF(empty_tuple); + Py_XDECREF(empty_string); + Py_XDECREF(py_code); + Py_XDECREF(py_frame); +} +""" % { + 'FILENAME': Naming.filename_cname, + 'LINENO': Naming.lineno_cname, + 'GLOBALS': Naming.module_cname +}] + +#------------------------------------------------------------------------------------ + +set_vtable_utility_code = [ +""" +static int __Pyx_SetVtable(PyObject *dict, void *vtable); /*proto*/ +""",""" +static int __Pyx_SetVtable(PyObject *dict, void *vtable) { + PyObject *pycobj = 0; + int result; + + pycobj = PyCObject_FromVoidPtr(vtable, 0); + if (!pycobj) + goto bad; + if (PyDict_SetItemString(dict, "__pyx_vtable__", pycobj) < 0) + goto bad; + result = 0; + goto done; + +bad: + result = -1; +done: + Py_XDECREF(pycobj); + return result; +} +"""] + +#------------------------------------------------------------------------------------ + +get_vtable_utility_code = [ +""" +static int __Pyx_GetVtable(PyObject *dict, void *vtabptr); /*proto*/ +""",r""" +static int __Pyx_GetVtable(PyObject *dict, void *vtabptr) { + int result; + PyObject *pycobj; + + pycobj = PyMapping_GetItemString(dict, "__pyx_vtable__"); + if (!pycobj) + goto bad; + *(void **)vtabptr = PyCObject_AsVoidPtr(pycobj); + if (!*(void **)vtabptr) + goto bad; + result = 0; + goto done; + +bad: + result = -1; +done: + Py_XDECREF(pycobj); + return result; +} +"""] + +#------------------------------------------------------------------------------------ + +#init_intern_tab_utility_code = [ +#""" +#static int __Pyx_InternStrings(__Pyx_InternTabEntry *t); /*proto*/ +#""",""" +#static int __Pyx_InternStrings(__Pyx_InternTabEntry *t) { +# while (t->p) { +# *t->p = PyString_InternFromString(t->s); +# if (!*t->p) +# return -1; +# ++t; +# } +# return 0; +#} +#"""] + +#init_intern_tab_utility_code = [ +#""" +#static int __Pyx_InternStrings(PyObject **t[]); /*proto*/ +#""",""" +#static int __Pyx_InternStrings(PyObject **t[]) { +# while (*t) { +# PyString_InternInPlace(*t); +# if (!**t) +# return -1; +# ++t; +# } +# return 0; +#} +#"""] + +#------------------------------------------------------------------------------------ + +init_string_tab_utility_code = [ +""" +static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /*proto*/ +""",""" +static int __Pyx_InitStrings(__Pyx_StringTabEntry *t) { + while (t->p) { + *t->p = PyString_FromStringAndSize(t->s, t->n - 1); + if (!*t->p) + return -1; + if (t->i) + PyString_InternInPlace(t->p); + ++t; + } + return 0; +} +"""] + +#------------------------------------------------------------------------------------ + +#get_exception_utility_code = [ +#""" +#static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb); /*proto*/ +#""",""" +#static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb) { +# PyThreadState *tstate = PyThreadState_Get(); +# PyObject *old_type, *old_value, *old_tb; +# PyErr_Fetch(type, value, tb); +# PyErr_NormalizeException(type, value, tb); +# if (PyErr_Occurred()) +# goto bad; +# if (!*tb) { +# printf("no traceback\n"); +# *tb = Py_None; +# Py_INCREF(*tb); +# } +##if 1 +# Py_INCREF(*type); +# Py_INCREF(*value); +# Py_INCREF(*tb); +# old_type = tstate->exc_type; +# old_value = tstate->exc_value; +# old_tb = tstate->exc_traceback; +# tstate->exc_type = *type; +# tstate->exc_value = *value; +# tstate->exc_traceback = *tb; +# Py_XDECREF(old_type); +# Py_XDECREF(old_value); +# Py_XDECREF(old_tb); +##endif +# return 0; +#bad: +# Py_XDECREF(*type); +# Py_XDECREF(*value); +# Py_XDECREF(*tb); +# return -1; +#} +#"""] + +#------------------------------------------------------------------------------------ + +#get_exception_utility_code = [ +#""" +#static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb); /*proto*/ +#""",""" +#static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb) { +# PyErr_Fetch(type, value, tb); +# PyErr_NormalizeException(type, value, tb); +# if (PyErr_Occurred()) +# goto bad; +# if (!*tb) { +# *tb = Py_None; +# Py_INCREF(*tb); +# } +# return 0; +#bad: +# Py_XDECREF(*type); +# Py_XDECREF(*value); +# Py_XDECREF(*tb); +# return -1; +#} +#"""] + +#------------------------------------------------------------------------------------ + +normalize_exception_utility_code = [ +""" +static int __Pyx_NormalizeException(PyObject **type, PyObject **value, PyObject **tb); /*proto*/ +""",""" +static int __Pyx_NormalizeException(PyObject **type, PyObject **value, PyObject **tb) { + PyErr_NormalizeException(type, value, tb); + if (PyErr_Occurred()) + goto bad; + if (!*tb) { + *tb = Py_None; + Py_INCREF(*tb); + } + return 0; +bad: + Py_XDECREF(*type); + Py_XDECREF(*value); + Py_XDECREF(*tb); + return -1; +} +"""] + +#------------------------------------------------------------------------------------ |
