diff options
| author | Slávek Banko <slavek.banko@axis.cz> | 2021-03-26 13:52:33 +0100 |
|---|---|---|
| committer | Slávek Banko <slavek.banko@axis.cz> | 2021-03-26 13:52:33 +0100 |
| commit | 0f27805eedcc40ae34009aa31a4dc08cb949f867 (patch) | |
| tree | 8b1c8995d7fdab97acde4bd7c63f96d378c34d02 /debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/ExprNodes.py | |
| parent | bad411472a12b93f8bfca6b7ca52d89488a8d8ce (diff) | |
| download | extra-dependencies-0f27805eedcc40ae34009aa31a4dc08cb949f867.tar.gz extra-dependencies-0f27805eedcc40ae34009aa31a4dc08cb949f867.zip | |
DEB pyrex: Added to repository.
Signed-off-by: Slávek Banko <slavek.banko@axis.cz>
Diffstat (limited to 'debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/ExprNodes.py')
| -rw-r--r-- | debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/ExprNodes.py | 3954 |
1 files changed, 3954 insertions, 0 deletions
diff --git a/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/ExprNodes.py b/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/ExprNodes.py new file mode 100644 index 00000000..c2848286 --- /dev/null +++ b/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/ExprNodes.py @@ -0,0 +1,3954 @@ +# +# Pyrex - Parse tree nodes for expressions +# + +import operator +from string import join + +from Errors import error, InternalError +import Naming +from Nodes import Node +import PyrexTypes +from PyrexTypes import py_object_type, c_long_type, typecast, error_type, \ + CPtrType, CFuncType, COverloadedFuncType +import Symtab +import Options + +from Pyrex.Debugging import print_call_chain +from DebugFlags import debug_disposal_code, debug_temp_alloc, \ + debug_coercion + +class ExprNode(Node): + # subexprs [string] Class var holding names of subexpr node attrs + # type PyrexType Type of the result + # result_code string Code fragment + # result_ctype string C type of result_code if different from type + # inplace_result string Temp var holding in-place operation result + # is_temp boolean Result is in a temporary variable + # is_sequence_constructor + # boolean Is a list or tuple constructor expression + # saved_subexpr_nodes + # [ExprNode or [ExprNode or None] or None] + # Cached result of subexpr_nodes() + + result_ctype = None + + # The Analyse Expressions phase for expressions is split + # into two sub-phases: + # + # Analyse Types + # Determines the result type of the expression based + # on the types of its sub-expressions, and inserts + # coercion nodes into the expression tree where needed. + # Marks nodes which will need to have temporary variables + # allocated. + # + # Allocate Temps + # Allocates temporary variables where needed, and fills + # in the result_code field of each node. + # + # ExprNode provides some convenience routines which + # perform both of the above phases. These should only + # be called from statement nodes, and only when no + # coercion nodes need to be added around the expression + # being analysed. If coercion is needed, the above two phases + # should be invoked separately. + # + # Framework code in ExprNode provides much of the common + # processing for the various phases. It makes use of the + # 'subexprs' class attribute of ExprNodes, which should + # contain a list of the names of attributes which can + # hold sub-nodes or sequences of sub-nodes. + # + # The framework makes use of a number of abstract methods. + # Their responsibilities are as follows. + # + # Declaration Analysis phase + # + # analyse_target_declaration + # Called during the Analyse Declarations phase to analyse + # the LHS of an assignment or argument of a del statement. + # Nodes which cannot be the LHS of an assignment need not + # implement it. + # + # Expression Analysis phase + # + # analyse_types + # - Call analyse_types on all sub-expressions. + # - Check operand types, and wrap coercion nodes around + # sub-expressions where needed. + # - Set the type of this node. + # - If a temporary variable will be required for the + # result, set the is_temp flag of this node. + # + # analyse_target_types + # Called during the Analyse Types phase to analyse + # the LHS of an assignment or argument of a del + # statement. Similar responsibilities to analyse_types. + # + # allocate_temps + # - Call allocate_temps for all sub-nodes. + # - Call allocate_temp for this node. + # - If a temporary was allocated, call release_temp on + # all sub-expressions. + # + # allocate_target_temps + # - Call allocate_temps on sub-nodes and allocate any other + # temps used during assignment. + # - Fill in result_code with a C lvalue if needed. + # - If a rhs node is supplied, call release_temp on it. + # - Call release_temp on sub-nodes and release any other + # temps used during assignment. + # + # #calculate_result_code + # # - Called during the Allocate Temps phase. Should return a + # # C code fragment evaluating to the result. This is only + # # called when the result is not a temporary. + # + # target_code + # Called by the default implementation of allocate_target_temps. + # Should return a C lvalue for assigning to the node. The default + # implementation calls calculate_result_code. + # + # check_const + # - Check that this node and its subnodes form a + # legal constant expression. If so, do nothing, + # otherwise call not_const. + # + # The default implementation of check_const + # assumes that the expression is not constant. + # + # check_const_addr + # - Same as check_const, except check that the + # expression is a C lvalue whose address is + # constant. Otherwise, call addr_not_const. + # + # The default implementation of calc_const_addr + # assumes that the expression is not a constant + # lvalue. + # + # Code Generation phase + # + # generate_evaluation_code + # 1. Call generate_evaluation_code for sub-expressions. + # 2. Generate any C statements necessary to calculate + # the result of this node from the results of its + # sub-expressions. If result is not in a temporary, record + # any information that will be needed by this node's + # implementation of calculate_result_code(). + # 4. If result is in a temporary, call generate_disposal_code + # on all sub-expressions. + # + # A default implementation of generate_evaluation_code + # is provided which uses the folling abstract methods: + # generate_result_code (for no. 2) + # + # generate_assignment_code + # Called on the LHS of an assignment. + # - Call generate_evaluation_code for sub-expressions. + # - Generate code to perform the assignment. + # - If the assignment absorbed a reference, call + # generate_post_assignment_code on the RHS, + # otherwise call generate_disposal_code on it. + # + # generate_deletion_code + # Called on an argument of a del statement. + # - Call generate_evaluation_code for sub-expressions. + # - Generate code to perform the deletion. + # - Call generate_disposal_code on all sub-expressions. + # + # calculate_result_code + # Return a C code fragment representing the result of this node. + # This is only called if the result is not in a temporary. + # + + is_sequence_constructor = 0 + is_attribute = 0 + + saved_subexpr_nodes = None + is_temp = 0 + + def not_implemented(self, method_name): + print_call_chain(method_name, "not implemented") ### + raise InternalError( + "%s.%s not implemented" % + (self.__class__.__name__, method_name)) + + def is_lvalue(self): + return 0 + + def is_inplace_lvalue(self): + return 0 + + def is_ephemeral(self): + # An ephemeral node is one whose result is in + # a Python temporary and we suspect there are no + # other references to it. Certain operations are + # disallowed on such values, since they are + # likely to result in a dangling pointer. + return self.type.is_pyobject and self.is_temp + + def subexpr_nodes(self): + # Extract a list of subexpression nodes based + # on the contents of the subexprs class attribute. + if self.saved_subexpr_nodes is None: + nodes = [] + for name in self.subexprs: + item = getattr(self, name) + if item: + if isinstance(item, ExprNode): + nodes.append(item) + else: + nodes.extend(item) + self.saved_subexpr_nodes = nodes + return self.saved_subexpr_nodes + + def result(self): + # Return a C code fragment for the result of this node. + if self.is_temp: + result_code = self.result_code + else: + result_code = self.calculate_result_code() + return result_code + + def result_as(self, type = None): + # Return the result code cast to the specified C type. + return typecast(type, self.ctype(), self.result()) + + def py_result(self): + # Return the result code cast to PyObject *. + return self.result_as(py_object_type) + + def ctype(self): + # Return the native C type of the result. + return self.result_ctype or self.type + + def compile_time_value(self, denv): + # Return value of compile-time expression, or report error. + error(self.pos, "Invalid compile-time expression") + + def compile_time_value_error(self, e): + error(self.pos, "Error in compile-time expression: %s: %s" % ( + e.__class__.__name__, e)) + + # ------------- Declaration Analysis ---------------- + + def analyse_target_declaration(self, env): + error(self.pos, "Cannot assign to or delete this") + + # ------------- Expression Analysis ---------------- + + def analyse_const_expression(self, env): + # Called during the analyse_declarations phase of a + # constant expression. Analyses the expression's type, + # checks whether it is a legal const expression, + # and determines its value. + self.analyse_types(env) + self.allocate_temps(env) + self.check_const() + + def analyse_expressions(self, env): + # Convenience routine performing both the Type + # Analysis and Temp Allocation phases for a whole + # expression. + self.analyse_types(env) + self.allocate_temps(env) + + def analyse_target_expression(self, env, rhs): + # Convenience routine performing both the Type + # Analysis and Temp Allocation phases for the LHS of + # an assignment. + self.analyse_target_types(env) + self.allocate_target_temps(env, rhs) + + def analyse_boolean_expression(self, env): + # Analyse expression and coerce to a boolean. + self.analyse_types(env) + bool = self.coerce_to_boolean(env) + bool.allocate_temps(env) + return bool + + def analyse_temp_boolean_expression(self, env): + # Analyse boolean expression and coerce result into + # a temporary. This is used when a branch is to be + # performed on the result and we won't have an + # opportunity to ensure disposal code is executed + # afterwards. By forcing the result into a temporary, + # we ensure that all disposal has been done by the + # time we get the result. + self.analyse_types(env) + bool = self.coerce_to_boolean(env) + temp_bool = bool.coerce_to_temp(env) + temp_bool.allocate_temps(env) + return temp_bool + + # --------------- Type Analysis ------------------ + + def analyse_as_function(self, env): + # Analyse types for an expression that is to be called. + self.analyse_types(env) + + def analyse_as_module(self, env): + # If this node can be interpreted as a reference to a + # cimported module, return its scope, else None. + return None + + def analyse_as_extension_type(self, env): + # If this node can be interpreted as a reference to an + # extension type, return its type, else None. + return None + + def analyse_as_cimported_attribute(self, env, *args, **kwds): + # If this node can be interpreted as a cimported name, + # finish type analysis and return true, else return false. + return 0 + + def analyse_types(self, env): + self.not_implemented("analyse_types") + + def analyse_target_types(self, env): + self.analyse_types(env) + + def analyse_inplace_types(self, env): + if self.is_inplace_lvalue(): + self.analyse_types(env) + else: + error(self.pos, "Invalid target for in-place operation") + self.type = error_type + + def gil_assignment_check(self, env): + if env.nogil and self.type.is_pyobject: + error(self.pos, "Assignment of Python object not allowed without gil") + + def check_const(self): + self.not_const() + + def not_const(self): + error(self.pos, "Not allowed in a constant expression") + + def check_const_addr(self): + self.addr_not_const() + + def addr_not_const(self): + error(self.pos, "Address is not constant") + + def gil_check(self, env): + if env.nogil and self.type.is_pyobject: + self.gil_error() + + # ----------------- Result Allocation ----------------- + + def result_in_temp(self): + # Return true if result is in a temporary owned by + # this node or one of its subexpressions. Overridden + # by certain nodes which can share the result of + # a subnode. + return self.is_temp + + def allocate_target_temps(self, env, rhs, inplace = 0): + # Perform temp allocation for the LHS of an assignment. + if debug_temp_alloc: + print self, "Allocating target temps" + self.allocate_subexpr_temps(env) + #self.result_code = self.target_code() + if rhs: + rhs.release_temp(env) + self.release_subexpr_temps(env) + + def allocate_inplace_target_temps(self, env, rhs): + if debug_temp_alloc: + print self, "Allocating inplace target temps" + self.allocate_subexpr_temps(env) + #self.result_code = self.target_code() + py_inplace = self.type.is_pyobject + if py_inplace: + self.allocate_temp(env) + self.inplace_result = env.allocate_temp(py_object_type) + self.release_temp(env) + rhs.release_temp(env) + if py_inplace: + env.release_temp(self.inplace_result) + self.release_subexpr_temps(env) + + def allocate_temps(self, env, result = None): + # Allocate temporary variables for this node and + # all its sub-expressions. If a result is specified, + # this must be a temp node and the specified variable + # is used as the result instead of allocating a new + # one. + if debug_temp_alloc: + print self, "Allocating temps" + self.allocate_subexpr_temps(env) + self.allocate_temp(env, result) + if self.is_temp: + self.release_subexpr_temps(env) + + def allocate_subexpr_temps(self, env): + # Allocate temporary variables for all sub-expressions + # of this node. + if debug_temp_alloc: + print self, "Allocating temps for:", self.subexprs + for node in self.subexpr_nodes(): + if node: + if debug_temp_alloc: + print self, "Allocating temps for", node + node.allocate_temps(env) + + def allocate_temp(self, env, result = None): + # If this node requires a temporary variable for its + # result, allocate one. If a result is specified, + # this must be a temp node and the specified variable + # is used as the result instead of allocating a new + # one. + if debug_temp_alloc: + print self, "Allocating temp" + if result: + if not self.is_temp: + raise InternalError("Result forced on non-temp node") + self.result_code = result + elif self.is_temp: + type = self.type + if not type.is_void: + if type.is_pyobject: + type = PyrexTypes.py_object_type + self.result_code = env.allocate_temp(type) + else: + self.result_code = None + if debug_temp_alloc: + print self, "Allocated result", self.result_code + #else: + # self.result_code = self.calculate_result_code() + + def target_code(self): + # Return code fragment for use as LHS of a C assignment. + return self.calculate_result_code() + + def calculate_result_code(self): + self.not_implemented("calculate_result_code") + + def release_temp(self, env): + # If this node owns a temporary result, release it, + # otherwise release results of its sub-expressions. + if self.is_temp: + if debug_temp_alloc: + print self, "Releasing result", self.result_code + env.release_temp(self.result_code) + else: + self.release_subexpr_temps(env) + + def release_subexpr_temps(self, env): + # Release the results of all sub-expressions of + # this node. + for node in self.subexpr_nodes(): + if node: + node.release_temp(env) + + # ---------------- Code Generation ----------------- + + def mark_vars_used(self): + for node in self.subexpr_nodes(): + node.mark_vars_used() + + def make_owned_reference(self, code): + # If result is a pyobject, make sure we own + # a reference to it. + if self.type.is_pyobject and not self.result_in_temp(): + code.put_incref(self.py_result()) + + def generate_evaluation_code(self, code): + # Generate code to evaluate this node and + # its sub-expressions, and dispose of any + # temporary results of its sub-expressions. + self.generate_subexpr_evaluation_code(code) + self.generate_result_code(code) + if self.is_temp: + self.generate_subexpr_disposal_code(code) + + def generate_subexpr_evaluation_code(self, code): + for node in self.subexpr_nodes(): + node.generate_evaluation_code(code) + + def generate_result_code(self, code): + self.not_implemented("generate_result_code") + + inplace_functions = { + "+=": "PyNumber_InPlaceAdd", + "-=": "PyNumber_InPlaceSubtract", + "*=": "PyNumber_InPlaceMultiply", + "/=": "PyNumber_InPlaceDivide", + "%=": "PyNumber_InPlaceRemainder", + "**=": "PyNumber_InPlacePower", + "<<=": "PyNumber_InPlaceLshift", + ">>=": "PyNumber_InPlaceRshift", + "&=": "PyNumber_InPlaceAnd", + "^=": "PyNumber_InPlaceXor", + "|=": "PyNumber_InPlaceOr", + } + + def generate_inplace_operation_code(self, operator, rhs, code): + args = (self.py_result(), rhs.py_result()) + if operator == "**=": + arg_code = "%s, %s, Py_None" % args + else: + arg_code = "%s, %s" % args + code.putln("%s = %s(%s); if (!%s) %s" % ( + self.inplace_result, + self.inplace_functions[operator], + arg_code, + self.inplace_result, + code.error_goto(self.pos))) + if self.is_temp: + code.put_decref_clear(self.py_result()) + rhs.generate_disposal_code(code) + if self.type.is_extension_type: + code.putln( + "if (!__Pyx_TypeTest(%s, %s)) %s" % ( + self.inplace_result, + self.type.typeptr_cname, + code.error_goto(self.pos))) + + def generate_disposal_code(self, code): + # If necessary, generate code to dispose of + # temporary Python reference. + if self.is_temp: + if self.type.is_pyobject: + code.put_decref_clear(self.py_result(), self.ctype()) + else: + self.generate_subexpr_disposal_code(code) + + def generate_subexpr_disposal_code(self, code): + # Generate code to dispose of temporary results + # of all sub-expressions. + for node in self.subexpr_nodes(): + node.generate_disposal_code(code) + + def generate_post_assignment_code(self, code): + # Same as generate_disposal_code except that + # assignment will have absorbed a reference to + # the result if it is a Python object. + if self.is_temp: + if self.type.is_pyobject: + code.putln("%s = 0;" % self.result()) + else: + self.generate_subexpr_disposal_code(code) + + def generate_inplace_result_disposal_code(self, code): + code.put_decref_clear(self.inplace_result, py_object_type) + + def generate_assignment_code(self, rhs, code): + # Stub method for nodes which are not legal as + # the LHS of an assignment. An error will have + # been reported earlier. + pass + + def generate_deletion_code(self, code): + # Stub method for nodes that are not legal as + # the argument of a del statement. An error + # will have been reported earlier. + pass + + # ----------------- Coercion ---------------------- + + def coerce_to(self, dst_type, env): + # Coerce the result so that it can be assigned to + # something of type dst_type. If processing is necessary, + # wraps this node in a coercion node and returns that. + # Otherwise, returns this node unchanged. + # + # This method is called during the analyse_expressions + # phase of the src_node's processing. + src = self + src_type = self.type + src_is_py_type = src_type.is_pyobject + dst_is_py_type = dst_type.is_pyobject + + if dst_type.is_pyobject: + if not src.type.is_pyobject: + src = CoerceToPyTypeNode(src, env) + if not src.type.subtype_of(dst_type): + if not isinstance(src, NoneNode): + src = PyTypeTestNode(src, dst_type, env) + elif src.type.is_pyobject: + src = CoerceFromPyTypeNode(dst_type, src, env) + else: # neither src nor dst are py types + if not dst_type.assignable_from(src_type): + error(self.pos, "Cannot assign type '%s' to '%s'" % + (src.type, dst_type)) + return src + + def coerce_to_pyobject(self, env): + return self.coerce_to(PyrexTypes.py_object_type, env) + + def coerce_to_boolean(self, env): + # Coerce result to something acceptable as + # a boolean value. + type = self.type + if type.is_pyobject or type.is_ptr or type.is_float: + return CoerceToBooleanNode(self, env) + else: + if not type.is_int and not type.is_error: + error(self.pos, + "Type '%s' not acceptable as a boolean" % type) + return self + + def coerce_to_integer(self, env): + # If not already some C integer type, coerce to longint. + if self.type.is_int: + return self + else: + return self.coerce_to(PyrexTypes.c_long_type, env) + + def coerce_to_temp(self, env): + # Ensure that the result is in a temporary. + if self.result_in_temp(): + return self + else: + return CoerceToTempNode(self, env) + + def coerce_to_simple(self, env): + # Ensure that the result is simple (see is_simple). + if self.is_simple(): + return self + else: + return self.coerce_to_temp(env) + + def is_simple(self): + # A node is simple if its result is something that can + # be referred to without performing any operations, e.g. + # a constant, local var, C global var, struct member + # reference, or temporary. + return self.result_in_temp() + + +class AtomicExprNode(ExprNode): + # Abstract base class for expression nodes which have + # no sub-expressions. + + subexprs = [] + + +class PyConstNode(AtomicExprNode): + # Abstract base class for constant Python values. + + def is_simple(self): + return 1 + + def analyse_types(self, env): + self.type = py_object_type + + def calculate_result_code(self): + return self.value + + def generate_result_code(self, code): + pass + + +class NoneNode(PyConstNode): + # The constant value None + + value = "Py_None" + + def compile_time_value(self, denv): + return None + + +class EllipsisNode(PyConstNode): + # '...' in a subscript list. + + value = "Py_Ellipsis" + + def compile_time_value(self, denv): + return Ellipsis + + +class ConstNode(AtomicExprNode): + # Abstract base type for literal constant nodes. + # + # value string C code fragment + + is_literal = 1 + + def is_simple(self): + return 1 + + def analyse_types(self, env): + pass # Types are held in class variables + + def check_const(self): + pass + + def calculate_result_code(self): + return str(self.value) + + def generate_result_code(self, code): + pass + + +class NullNode(ConstNode): + type = PyrexTypes.c_null_ptr_type + value = "NULL" + + +class CharNode(ConstNode): + type = PyrexTypes.c_char_type + + def compile_time_value(self, denv): + return ord(self.value) + + def calculate_result_code(self): + return "'%s'" % self.value + + +class IntNode(ConstNode): + type = PyrexTypes.c_long_type + + def compile_time_value(self, denv): + return int(self.value, 0) + + +class FloatNode(ConstNode): + type = PyrexTypes.c_double_type + + def compile_time_value(self, denv): + return float(self.value) + + def calculate_result_code(self): + strval = str(self.value) + if strval == 'nan': + return "NAN" + elif strval == 'inf': + return "INFINITY" + elif strval == '-inf': + return "(-INFINITY)" + else: + return strval + + +class StringNode(ConstNode): + # #entry Symtab.Entry + + type = PyrexTypes.c_char_ptr_type + + def compile_time_value(self, denv): + return eval('"%s"' % self.value) + +# def analyse_types(self, env): +# self.entry = env.add_string_const(self.value) + + def coerce_to(self, dst_type, env): + # Arrange for a Python version of the string to be pre-allocated + # when coercing to a Python type. + if dst_type.is_pyobject and not self.type.is_pyobject: + node = self.as_py_string_node(env) + else: + node = self + # We still need to perform normal coerce_to processing on the + # result, because we might be coercing to an extension type, + # in which case a type test node will be needed. + return ConstNode.coerce_to(node, dst_type, env) + + def as_py_string_node(self, env): + # Return a new StringNode with the same value as this node + # but whose type is a Python type instead of a C type. + #entry = self.entry + #env.add_py_string(entry) + return StringNode(self.pos, type = py_object_type, value = self.value) + + def generate_evaluation_code(self, code): + if self.type.is_pyobject: + self.result_code = code.get_py_string_const(self.value) + else: + self.result_code = code.get_string_const(self.value) + + def calculate_result_code(self): + return self.result_code + + +class LongNode(AtomicExprNode): + # Python long integer literal + # + # value string + + def compile_time_value(self, denv): + return long(self.value) + + gil_message = "Constructing Python long int" + + def analyse_types(self, env): + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + + def generate_evaluation_code(self, code): + result = self.result() + code.putln( + '%s = PyLong_FromString("%s", 0, 0); if (!%s) %s' % ( + self.result(), + self.value, + self.result(), + code.error_goto(self.pos))) + + +class ImagNode(AtomicExprNode): + # Imaginary number literal + # + # value float imaginary part + + def compile_time_value(self, denv): + return complex(0.0, self.value) + + gil_message = "Constructing complex number" + + def analyse_types(self, env): + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + + def generate_evaluation_code(self, code): + result = self.result() + code.putln( + "%s = PyComplex_FromDoubles(0.0, %s); if (!%s) %s" % ( + self.result(), + self.value, + self.result(), + code.error_goto(self.pos))) + + +class NameNode(AtomicExprNode): + # Reference to a local or global variable name. + # + # name string Python name of the variable + # + # entry Entry Symbol table entry + # type_entry Entry For extension type names, the original type entry + # interned_cname string + + is_name = 1 + entry = None + type_entry = None + + def compile_time_value(self, denv): + try: + return denv.lookup(self.name) + except KeyError: + error(self.pos, "Compile-time name '%s' not defined" % self.name) + + def coerce_to(self, dst_type, env): + # If coercing to a generic pyobject and this is a builtin + # C function with a Python equivalent, manufacture a NameNode + # referring to the Python builtin. + #print "NameNode.coerce_to:", self.name, dst_type ### + if dst_type is py_object_type: + entry = self.entry + if entry.is_cfunction: + var_entry = entry.as_variable + if var_entry: + node = NameNode(self.pos, name = self.name) + node.entry = var_entry + node.analyse_rvalue_entry(env) + return node + return AtomicExprNode.coerce_to(self, dst_type, env) + + def analyse_as_module(self, env): + # Try to interpret this as a reference to a cimported module. + # Returns the module scope, or None. + entry = env.lookup(self.name) + if entry and entry.as_module: + return entry.as_module + return None + + def analyse_as_extension_type(self, env): + # Try to interpret this as a reference to an extension type. + # Returns the extension type, or None. + entry = env.lookup(self.name) + if entry and entry.is_type and entry.type.is_extension_type: + return entry.type + else: + return None + + def analyse_target_declaration(self, env): + self.entry = env.lookup_here(self.name) + if not self.entry: + self.entry = env.declare_var(self.name, py_object_type, self.pos) + + def analyse_types(self, env): + self.lookup_entry(env) + self.analyse_rvalue_entry(env) + + def lookup_entry(self, env): + self.entry = env.lookup(self.name) + if not self.entry: + self.entry = env.declare_builtin(self.name, self.pos) + + def analyse_target_types(self, env): + self.analyse_entry(env) + self.finish_analysing_lvalue() + + def analyse_inplace_types(self, env): + self.analyse_rvalue_entry(env) + self.finish_analysing_lvalue() + + def finish_analysing_lvalue(self): + if self.entry.is_readonly: + error(self.pos, "Assignment to read-only name '%s'" + % self.name) + elif not self.is_lvalue(): + error(self.pos, "Assignment to non-lvalue '%s'" + % self.name) + self.type = PyrexTypes.error_type + self.entry.used = 1 + + def analyse_as_function(self, env): + self.lookup_entry(env) + if self.entry.is_type: + self.analyse_constructor_entry(env) + else: + self.analyse_rvalue_entry(env) + + def analyse_constructor_entry(self, env): + entry = self.entry + type = entry.type + if type.is_struct_or_union: + self.type = entry.type.cplus_constructor_type + elif type.is_pyobject: + self.analyse_rvalue_entry(env) + else: + error(self.pos, "Type '%s' not callable as a C++ constructor" % type) + self.type = error_type + + def analyse_rvalue_entry(self, env): + #print "NameNode.analyse_rvalue_entry:", self.name ### + #print "Entry:", self.entry.__dict__ ### + self.analyse_entry(env) + entry = self.entry + if entry.is_declared_generic: + self.result_ctype = py_object_type + if entry.is_pyglobal or entry.is_builtin: + self.is_temp = 1 + self.gil_check(env) + + gil_message = "Accessing Python global or builtin" + + def analyse_entry(self, env): + #print "NameNode.analyse_entry:", self.name ### + self.check_identifier_kind() + entry = self.entry + type = entry.type + ctype = entry.ctype + self.type = type + if ctype: + self.result_ctype = ctype + if entry.is_pyglobal or entry.is_builtin: + assert type.is_pyobject, "Python global or builtin not a Python object" + #self.interned_cname = env.intern(self.entry.name) + + def check_identifier_kind(self): + # Check that this is an appropriate kind of name for use in an expression. + # Also finds the variable entry associated with an extension type. + entry = self.entry + if entry.is_type and entry.type.is_extension_type: + self.type_entry = entry + if not (entry.is_const or entry.is_variable + or entry.is_builtin or entry.is_cfunction): + if self.entry.as_variable: + self.entry = self.entry.as_variable + else: + error(self.pos, + "'%s' is not a constant, variable or function identifier" % self.name) + + def is_simple(self): + # If it's not a C variable, it'll be in a temp. + return 1 + + def calculate_target_results(self, env): + pass + + def check_const(self): + entry = self.entry + if not (entry.is_const or entry.is_cfunction): + self.not_const() + + def check_const_addr(self): + entry = self.entry + if not (entry.is_cglobal or entry.is_cfunction): + self.addr_not_const() + + def is_lvalue(self): + entry = self.entry + return entry.is_variable and \ + not entry.type.is_array and \ + not entry.is_readonly + + def is_inplace_lvalue(self): + return self.is_lvalue() + + def is_ephemeral(self): + # Name nodes are never ephemeral, even if the + # result is in a temporary. + return 0 + + def allocate_temp(self, env, result = None): + AtomicExprNode.allocate_temp(self, env, result) + entry = self.entry + if entry: + entry.used = 1 + + def calculate_result_code(self): + entry = self.entry + if not entry: + return "<error>" # There was an error earlier + return entry.cname + + def generate_result_code(self, code): + assert hasattr(self, 'entry') + entry = self.entry + if entry is None: + return # There was an error earlier + if entry.utility_code: + code.use_utility_code(entry.utility_code) + if entry.is_pyglobal or entry.is_builtin: + if entry.is_builtin: + namespace = Naming.builtins_cname + else: # entry.is_pyglobal + namespace = entry.namespace_cname + result = self.result() + cname = code.intern(self.entry.name) + code.use_utility_code(get_name_interned_utility_code) + code.putln( + '%s = __Pyx_GetName(%s, %s); if (!%s) %s' % ( + result, + namespace, + cname, + result, + code.error_goto(self.pos))) + + def generate_setattr_code(self, value_code, code): + entry = self.entry + namespace = self.entry.namespace_cname + cname = code.intern(self.entry.name) + code.putln( + 'if (PyObject_SetAttr(%s, %s, %s) < 0) %s' % ( + namespace, + cname, + value_code, + code.error_goto(self.pos))) + + def generate_assignment_code(self, rhs, code): + #print "NameNode.generate_assignment_code:", self.name ### + entry = self.entry + if entry is None: + return # There was an error earlier + if entry.is_pyglobal: + self.generate_setattr_code(rhs.py_result(), code) + if debug_disposal_code: + print "NameNode.generate_assignment_code:" + print "...generating disposal code for", rhs + rhs.generate_disposal_code(code) + else: + if self.type.is_pyobject: + rhs.make_owned_reference(code) + code.put_decref(self.py_result()) + code.putln('%s = %s;' % (self.result(), rhs.result_as(self.ctype()))) + if debug_disposal_code: + print "NameNode.generate_assignment_code:" + print "...generating post-assignment code for", rhs + rhs.generate_post_assignment_code(code) + + def generate_inplace_assignment_code(self, operator, rhs, code): + entry = self.entry + if entry is None: + return # There was an error earlier + if self.type.is_pyobject: + self.generate_result_code(code) + self.generate_inplace_operation_code(operator, rhs, code) + if entry.is_pyglobal: + self.generate_setattr_code(self.inplace_result, code) + self.generate_inplace_result_disposal_code(code) + else: + code.put_decref(self.py_result()) + cast_inplace_result = typecast(self.ctype(), py_object_type, self.inplace_result) + code.putln('%s = %s;' % (self.result(), cast_inplace_result)) + else: + code.putln("%s %s %s;" % (self.result(), operator, rhs.result())) + rhs.generate_disposal_code(code) + + def generate_deletion_code(self, code): + if self.entry is None: + return # There was an error earlier + if not self.entry.is_pyglobal: + error(self.pos, "Deletion of local or C global name not supported") + return + cname = code.intern(self.entry.name) + code.putln( + 'if (PyObject_DelAttr(%s, %s) < 0) %s' % ( + Naming.module_cname, + cname, + code.error_goto(self.pos))) + + def mark_vars_used(self): + if self.entry: + self.entry.used = 1 + + +class BackquoteNode(ExprNode): + # `expr` + # + # arg ExprNode + + subexprs = ['arg'] + + def analyse_types(self, env): + self.arg.analyse_types(env) + self.arg = self.arg.coerce_to_pyobject(env) + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + + gil_message = "Backquote expression" + + def generate_result_code(self, code): + result = self.result() + code.putln( + "%s = PyObject_Repr(%s); if (!%s) %s" % ( + self.result(), + self.arg.py_result(), + self.result(), + code.error_goto(self.pos))) + + +class ImportNode(ExprNode): + # Used as part of import statement implementation. + # Implements result = + # __import__(module_name, globals(), None, name_list) + # + # module_name StringNode dotted name of module + # name_list ListNode or None list of names to be imported + + subexprs = ['module_name', 'name_list'] + + def analyse_types(self, env): + self.module_name.analyse_types(env) + self.module_name = self.module_name.coerce_to_pyobject(env) + if self.name_list: + self.name_list.analyse_types(env) + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 +# env.use_utility_code(import_utility_code) + + gil_message = "Python import" + + def generate_result_code(self, code): + if self.name_list: + name_list_code = self.name_list.py_result() + else: + name_list_code = "0" + code.use_utility_code(import_utility_code) + result = self.result() + code.putln( + "%s = __Pyx_Import(%s, %s); if (!%s) %s" % ( + result, + self.module_name.py_result(), + name_list_code, + result, + code.error_goto(self.pos))) + + +class IteratorNode(ExprNode): + # Used as part of for statement implementation. + # Implements result = iter(sequence) + # + # sequence ExprNode + + subexprs = ['sequence'] + + def analyse_types(self, env): + self.sequence.analyse_types(env) + self.sequence = self.sequence.coerce_to_pyobject(env) + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + + gil_message = "Iterating over Python object" + + def generate_result_code(self, code): + result = self.result() + code.putln( + "%s = PyObject_GetIter(%s); if (!%s) %s" % ( + result, + self.sequence.py_result(), + result, + code.error_goto(self.pos))) + + +class NextNode(AtomicExprNode): + # Used as part of for statement implementation. + # Implements result = iterator.next() + # Created during analyse_types phase. + # The iterator is not owned by this node. + # + # iterator ExprNode + + def __init__(self, iterator, env): + self.pos = iterator.pos + self.iterator = iterator + self.type = py_object_type + self.is_temp = 1 + + def generate_result_code(self, code): + result = self.result() + code.putln( + "%s = PyIter_Next(%s);" % ( + result, + self.iterator.py_result())) + code.putln( + "if (!%s) {" % + result) + code.putln( + "if (PyErr_Occurred()) %s" % + code.error_goto(self.pos)) + code.putln( + "break;") + code.putln( + "}") + + +class ExcValueNode(AtomicExprNode): + # Node created during analyse_types phase + # of an ExceptClauseNode to fetch the current + # exception or traceback value. + + def __init__(self, pos, env, var): + ExprNode.__init__(self, pos) + self.type = py_object_type + self.var = var + + def calculate_result_code(self): + return self.var + + def generate_result_code(self, code): + pass + + +class TempNode(AtomicExprNode): + # Node created during analyse_types phase + # of some nodes to hold a temporary value. + + def __init__(self, pos, type, env): + ExprNode.__init__(self, pos) + self.type = type + if type.is_pyobject: + self.result_ctype = py_object_type + self.is_temp = 1 + + def generate_result_code(self, code): + pass + + +class PyTempNode(TempNode): + # TempNode holding a Python value. + + def __init__(self, pos, env): + TempNode.__init__(self, pos, PyrexTypes.py_object_type, env) + + +#------------------------------------------------------------------- +# +# Trailer nodes +# +#------------------------------------------------------------------- + +class IndexNode(ExprNode): + # Sequence indexing. + # + # base ExprNode + # index ExprNode + + subexprs = ['base', 'index'] + + def compile_time_value(self, denv): + base = self.base.compile_time_value(denv) + index = self.index.compile_time_value(denv) + try: + return base[index] + except Exception, e: + self.compile_time_value_error(e) + + def is_ephemeral(self): + return self.base.is_ephemeral() + + def analyse_target_declaration(self, env): + pass + + def analyse_types(self, env): + self.analyse_base_and_index_types(env, getting = 1) + + def analyse_target_types(self, env): + self.analyse_base_and_index_types(env, setting = 1) + + def analyse_inplace_types(self, env): + self.analyse_base_and_index_types(env, getting = 1, setting = 1) + + def analyse_base_and_index_types(self, env, getting = 0, setting = 0): + self.base.analyse_types(env) + self.index.analyse_types(env) + btype = self.base.type + if btype.is_pyobject: + itype = self.index.type + if not (btype.is_sequence and itype.is_int and itype.signed): + self.index = self.index.coerce_to_pyobject(env) + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + else: + if self.base.type.is_ptr or self.base.type.is_array: + self.type = self.base.type.base_type + else: + error(self.pos, + "Attempting to index non-array type '%s'" % + self.base.type) + self.type = PyrexTypes.error_type + if self.index.type.is_pyobject: + self.index = self.index.coerce_to( + PyrexTypes.c_py_ssize_t_type, env) + if not self.index.type.is_int: + error(self.pos, + "Invalid index type '%s'" % + self.index.type) + + gil_message = "Indexing Python object" + + def check_const_addr(self): + self.base.check_const_addr() + self.index.check_const() + + def is_lvalue(self): + return 1 + + def is_inplace_lvalue(self): + return 1 + + def calculate_result_code(self): + return "(%s[%s])" % ( + self.base.result(), self.index.result()) + + def generate_result_code(self, code): + if self.type.is_pyobject: + itype = self.index.type + if itype.is_int and itype.signed: + code.use_utility_code(getitem_int_utility_code) + function = "__Pyx_GetItemInt" + index_code = self.index.result() + else: + function = "PyObject_GetItem" + index_code = self.index.py_result() + result = self.result() + code.putln( + "%s = %s(%s, %s); if (!%s) %s" % ( + result, + function, + self.base.py_result(), + index_code, + result, + code.error_goto(self.pos))) + + def generate_setitem_code(self, value_code, code): + itype = self.index.type + if itype.is_int and itype.signed: + code.use_utility_code(setitem_int_utility_code) + function = "__Pyx_SetItemInt" + index_code = self.index.result() + else: + function = "PyObject_SetItem" + index_code = self.index.py_result() + code.putln( + "if (%s(%s, %s, %s) < 0) %s" % ( + function, + self.base.py_result(), + index_code, + value_code, + code.error_goto(self.pos))) + + def generate_assignment_code(self, rhs, code): + self.generate_subexpr_evaluation_code(code) + if self.type.is_pyobject: + self.generate_setitem_code(rhs.py_result(), code) + else: + code.putln( + "%s = %s;" % ( + self.result(), rhs.result())) + self.generate_subexpr_disposal_code(code) + rhs.generate_disposal_code(code) + + def generate_inplace_assignment_code(self, operator, rhs, code): + self.generate_subexpr_evaluation_code(code) + if self.type.is_pyobject: + self.generate_result_code(code) + self.generate_inplace_operation_code(operator, rhs, code) + self.generate_setitem_code(self.inplace_result, code) + self.generate_inplace_result_disposal_code(code) + else: + code.putln("%s %s %s;" % (self.result(), operator, rhs.result())) + rhs.generate_disposal_code(code) + self.generate_subexpr_disposal_code(code) + + def generate_deletion_code(self, code): + self.generate_subexpr_evaluation_code(code) + if self.base.type.is_sequence and self.index.type.is_int: + function = "PySequence_DelItem" + index_code = self.index.result() + else: + function = "PyObject_DelItem" + index_code = self.index.py_result() + code.putln( + "if (%s(%s, %s) < 0) %s" % ( + function, + self.base.py_result(), + index_code, + code.error_goto(self.pos))) + #else: + # error(self.pos, "Cannot delete non-Python variable") + self.generate_subexpr_disposal_code(code) + + +class SliceIndexNode(ExprNode): + # 2-element slice indexing + # + # base ExprNode + # start ExprNode or None + # stop ExprNode or None + + subexprs = ['base', 'start', 'stop'] + + def is_inplace_lvalue(self): + return 1 + + def compile_time_value(self, denv): + base = self.base.compile_time_value(denv) + start = self.start.compile_time_value(denv) + stop = self.stop.compile_time_value(denv) + try: + return base[start:stop] + except Exception, e: + self.compile_time_value_error(e) + + def analyse_target_declaration(self, env): + pass + + def analyse_types(self, env): + self.base.analyse_types(env) + if self.start: + self.start.analyse_types(env) + if self.stop: + self.stop.analyse_types(env) + self.base = self.base.coerce_to_pyobject(env) + c_int = PyrexTypes.c_py_ssize_t_type + if self.start: + self.start = self.start.coerce_to(c_int, env) + if self.stop: + self.stop = self.stop.coerce_to(c_int, env) + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + + gil_message = "Slicing Python object" + + def generate_result_code(self, code): + result = self.result() + code.putln( + "%s = PySequence_GetSlice(%s, %s, %s); if (!%s) %s" % ( + result, + self.base.py_result(), + self.start_code(), + self.stop_code(), + result, + code.error_goto(self.pos))) + + def generate_setslice_code(self, value_code, code): + code.putln( + "if (PySequence_SetSlice(%s, %s, %s, %s) < 0) %s" % ( + self.base.py_result(), + self.start_code(), + self.stop_code(), + value_code, + code.error_goto(self.pos))) + + def generate_assignment_code(self, rhs, code): + self.generate_subexpr_evaluation_code(code) + self.generate_setslice_code(rhs.result(), code) + self.generate_subexpr_disposal_code(code) + rhs.generate_disposal_code(code) + + def generate_inplace_assignment_code(self, operator, rhs, code): + self.generate_subexpr_evaluation_code(code) + self.generate_result_code(code) + self.generate_inplace_operation_code(operator, rhs, code) + self.generate_setslice_code(self.inplace_result, code) + self.generate_inplace_result_disposal_code(code) + self.generate_subexpr_disposal_code(code) + + def generate_deletion_code(self, code): + self.generate_subexpr_evaluation_code(code) + code.putln( + "if (PySequence_DelSlice(%s, %s, %s) < 0) %s" % ( + self.base.py_result(), + self.start_code(), + self.stop_code(), + code.error_goto(self.pos))) + self.generate_subexpr_disposal_code(code) + + def start_code(self): + if self.start: + return self.start.result() + else: + return "0" + + def stop_code(self): + if self.stop: + return self.stop.result() + else: + return "PY_SSIZE_T_MAX" + +# def calculate_result_code(self): +# # self.result_code is not used, but this method must exist +# return "<unused>" + + +class SliceNode(ExprNode): + # start:stop:step in subscript list + # + # start ExprNode + # stop ExprNode + # step ExprNode + + def compile_time_value(self, denv): + start = self.start.compile_time_value(denv) + stop = self.stop.compile_time_value(denv) + step = step.step.compile_time_value(denv) + try: + return slice(start, stop, step) + except Exception, e: + self.compile_time_value_error(e) + + subexprs = ['start', 'stop', 'step'] + + def analyse_types(self, env): + self.start.analyse_types(env) + self.stop.analyse_types(env) + self.step.analyse_types(env) + self.start = self.start.coerce_to_pyobject(env) + self.stop = self.stop.coerce_to_pyobject(env) + self.step = self.step.coerce_to_pyobject(env) + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + + gil_message = "Constructing Python slice object" + + def generate_result_code(self, code): + result = self.result() + code.putln( + "%s = PySlice_New(%s, %s, %s); if (!%s) %s" % ( + result, + self.start.py_result(), + self.stop.py_result(), + self.step.py_result(), + result, + code.error_goto(self.pos))) + + +class CallNode(ExprNode): + + def gil_check(self, env): + # Make sure we're not in a nogil environment + if env.nogil: + error(self.pos, "Calling gil-requiring function without gil") + + +class SimpleCallNode(CallNode): + # Function call without keyword, * or ** args. + # + # function ExprNode + # args [ExprNode] + # arg_tuple ExprNode or None used internally + # self ExprNode or None used internally + # coerced_self ExprNode or None used internally + # function_type PyrexType used internally + + subexprs = ['self', 'coerced_self', 'function', 'args', 'arg_tuple'] + + self = None + coerced_self = None + arg_tuple = None + is_new = False + + cplus_argless_constr_type = CFuncType(None, []) + + def compile_time_value(self, denv): + function = self.function.compile_time_value(denv) + args = [arg.compile_time_value(denv) for arg in self.args] + try: + return function(*args) + except Exception, e: + self.compile_time_value_error(e) + + def analyse_types(self, env): + #print "SimpleCallNode.analyse_types:", self.pos ### + function = self.function + function.is_called = 1 + function.analyse_as_function(env) + if function.is_name or function.is_attribute: + #print "SimpleCallNode.analyse_types:", self.pos, "is name or attribute" ### + func_entry = function.entry + if func_entry: + if func_entry.is_cmethod or func_entry.is_builtin_method: + # Take ownership of the object from which the attribute + # was obtained, because we need to pass it as 'self'. + #print "SimpleCallNode: Snarfing self argument" ### + self.self = function.obj + function.obj = CloneNode(self.self) + elif self.is_new: + if not (func_entry.is_type and func_entry.type.is_struct_or_union + and func_entry.type.scope.is_cplus): + error(self.pos, "'new' operator can only be used on a C++ struct type") + self.type = error_type + return + else: + #print "SimpleCallNode.analyse_types:", self.pos, "not name or attribute" ### + if self.is_new: + error(self.pos, "Invalid use of 'new' operator") + self.type = error_type + return + func_type = self.function.type + if func_type.is_ptr: + func_type = func_type.base_type + self.function_type = func_type + if func_type.is_pyobject: + #print "SimpleCallNode: Python call" ### + if self.args: + self.arg_tuple = TupleNode(self.pos, args = self.args) + self.arg_tuple.analyse_types(env) + else: + self.arg_tuple = None + self.args = None + if function.is_name and function.type_entry: + # We are calling an extension type constructor + self.type = function.type_entry.type + self.result_ctype = py_object_type + else: + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + else: + #print "SimpleCallNode: C call" ### + for arg in self.args: + arg.analyse_types(env) + if func_type.is_cfunction: + self.type = func_type.return_type + if self.is_new: + self.type = CPtrType(self.type) + if func_type.is_overloaded: + func_type = self.resolve_overloading() + if not func_type: + self.type = error_type + return + if self.self and func_type.args: + #print "SimpleCallNode: Inserting self into argument list" ### + # Coerce 'self' to the type expected by the method. + expected_type = func_type.args[0].type + self.coerced_self = CloneNode(self.self).coerce_to( + expected_type, env) + # Insert coerced 'self' argument into argument list. + self.args.insert(0, self.coerced_self) + self.analyse_c_function_call(env) + + def resolve_overloading(self): + func_type = self.function_type + arg_types = [arg.type for arg in self.args] + signatures = func_type.signatures or [self.cplus_argless_constr_type] + for signature in signatures: + if signature.callable_with(arg_types): + signature.return_type = func_type.return_type + self.function_type = signature + return signature + def display_types(types): + return ", ".join([str(type) for type in types]) + error(self.pos, "No matching signature found for argument types (%s)" + % display_types(arg_types)) + if signatures: + error(self.pos, "Candidates are:") + for signature in signatures: + error(signature.pos, "(%s)" % display_types(signature.args)) + + def analyse_c_function_call(self, env): + func_type = self.function_type + # Check function type + if not func_type.is_cfunction: + if not func_type.is_error: + error(self.pos, "Calling non-function type '%s'" % + func_type) + self.type = PyrexTypes.error_type + return + # Check no. of args + expected_nargs = len(func_type.args) + actual_nargs = len(self.args) + if actual_nargs < expected_nargs \ + or (not func_type.has_varargs and actual_nargs > expected_nargs): + expected_str = str(expected_nargs) + if func_type.has_varargs: + expected_str = "at least " + expected_str + error(self.pos, + "Call with wrong number of arguments (expected %s, got %s)" + % (expected_str, actual_nargs)) + self.args = None + self.type = PyrexTypes.error_type + return + # Coerce arguments + for i in range(expected_nargs): + formal_type = func_type.args[i].type + self.args[i] = self.args[i].coerce_to(formal_type, env) + for i in range(expected_nargs, actual_nargs): + if self.args[i].type.is_pyobject: + error(self.args[i].pos, + "Python object cannot be passed as a varargs parameter") + # Calc result code fragment + #print "SimpleCallNode.analyse_c_function_call: self.type =", self.type ### + if self.type.is_pyobject \ + or func_type.exception_value is not None \ + or func_type.exception_check: + self.is_temp = 1 + if self.type.is_pyobject: + self.result_ctype = py_object_type + # Check gil + if not func_type.nogil: + self.gil_check(env) + if func_type.exception_check and env.nogil: + self.gil_error("Calling 'except ?' or 'except *' function") + + def calculate_result_code(self): + return self.c_call_code() + + def c_call_code(self): + if self.type.is_error or self.args is None or not self.function_type.is_cfunction: + return "<error>" + func_type = self.function_type + formal_args = func_type.args + arg_list_code = [] + for (formal_arg, actual_arg) in zip(formal_args, self.args): + arg_code = actual_arg.result_as(formal_arg.type) + arg_list_code.append(arg_code) + for actual_arg in self.args[len(formal_args):]: + arg_list_code.append(actual_arg.result()) + result = "%s(%s)" % (self.function.result(), + join(arg_list_code, ",")) + if self.is_new: + result = "new " + result + return result + + def generate_result_code(self, code): + if self.type.is_error: + return + func_type = self.function_type + result = self.result() + if func_type.is_pyobject: + if self.arg_tuple: + arg_code = self.arg_tuple.py_result() + else: + arg_code = "0" + code.putln( + "%s = PyObject_CallObject(%s, %s); if (!%s) %s" % ( + result, + self.function.py_result(), + arg_code, + result, + code.error_goto(self.pos))) + elif func_type.is_cfunction: + exc_checks = [] + if self.type.is_pyobject: + exc_checks.append("!%s" % result) + else: + exc_val = func_type.exception_value + exc_check = func_type.exception_check + if exc_val is not None: + exc_checks.append("%s == %s" % (self.result(), exc_val)) + if exc_check: + exc_checks.append("PyErr_Occurred()") + if self.is_temp or exc_checks: + rhs = self.c_call_code() + result = self.result() + if result: + lhs = "%s = " % result + if self.is_temp and self.type.is_pyobject: + #return_type = self.type # func_type.return_type + #print "SimpleCallNode.generate_result_code: casting", rhs, \ + # "from", return_type, "to pyobject" ### + rhs = typecast(py_object_type, self.type, rhs) + else: + lhs = "" + code.putln( + "%s%s; if (%s) %s" % ( + lhs, + rhs, + " && ".join(exc_checks), + code.error_goto(self.pos))) + + +class GeneralCallNode(CallNode): + # General Python function call, including keyword, + # * and ** arguments. + # + # function ExprNode + # positional_args ExprNode Tuple of positional arguments + # keyword_args ExprNode or None Dict of keyword arguments + # starstar_arg ExprNode or None Dict of extra keyword args + + subexprs = ['function', 'positional_args', 'keyword_args', 'starstar_arg'] + + def compile_time_value(self, denv): + function = self.function.compile_time_value(denv) + positional_args = self.positional_args.compile_time_value(denv) + keyword_args = self.keyword_args.compile_time_value(denv) + starstar_arg = self.starstar_arg.compile_time_value(denv) + try: + keyword_args.update(starstar_arg) + return function(*positional_args, **keyword_args) + except Exception, e: + self.compile_time_value_error(e) + + def analyse_types(self, env): + function = self.function + function.analyse_types(env) + self.positional_args.analyse_types(env) + if self.keyword_args: + self.keyword_args.analyse_types(env) + if self.starstar_arg: + self.starstar_arg.analyse_types(env) + self.function = self.function.coerce_to_pyobject(env) + self.positional_args = \ + self.positional_args.coerce_to_pyobject(env) + if self.starstar_arg: + self.starstar_arg = \ + self.starstar_arg.coerce_to_pyobject(env) + if function.is_name and function.type_entry: + # We are calling an extension type constructor + self.type = function.type_entry.type + self.result_ctype = py_object_type + else: + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + + def generate_result_code(self, code): + if self.keyword_args and self.starstar_arg: + code.putln( + "if (PyDict_Update(%s, %s) < 0) %s" % ( + self.keyword_args.py_result(), + self.starstar_arg.py_result(), + code.error_goto(self.pos))) + keyword_code = self.keyword_args.py_result() + elif self.keyword_args: + keyword_code = self.keyword_args.py_result() + elif self.starstar_arg: + keyword_code = self.starstar_arg.py_result() + else: + keyword_code = None + if not keyword_code: + call_code = "PyObject_CallObject(%s, %s)" % ( + self.function.py_result(), + self.positional_args.py_result()) + else: + call_code = "PyEval_CallObjectWithKeywords(%s, %s, %s)" % ( + self.function.py_result(), + self.positional_args.py_result(), + keyword_code) + result = self.result() + code.putln( + "%s = %s; if (!%s) %s" % ( + result, + call_code, + result, + code.error_goto(self.pos))) + + +class AsTupleNode(ExprNode): + # Convert argument to tuple. Used for normalising + # the * argument of a function call. + # + # arg ExprNode + + subexprs = ['arg'] + + def compile_time_value(self, denv): + arg = self.arg.compile_time_value(denv) + try: + return tuple(arg) + except Exception, e: + self.compile_time_value_error(e) + + def analyse_types(self, env): + self.arg.analyse_types(env) + self.arg = self.arg.coerce_to_pyobject(env) + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + + gil_message = "Constructing Python tuple" + + def generate_result_code(self, code): + result = self.result() + code.putln( + "%s = PySequence_Tuple(%s); if (!%s) %s" % ( + result, + self.arg.py_result(), + result, + code.error_goto(self.pos))) + + +class AttributeNode(ExprNode): + # obj.attribute + # + # obj ExprNode + # attribute string + # + # Used internally: + # + # is_py_attr boolean Is a Python getattr operation + # member string C name of struct member + # is_called boolean Function call is being done on result + # entry Entry Symbol table entry of attribute + # interned_attr_cname string C name of interned attribute name + + is_attribute = 1 + subexprs = ['obj'] + + type = PyrexTypes.error_type + result_code = "<error>" + entry = None + is_called = 0 + + def compile_time_value(self, denv): + attr = self.attribute + if attr.startswith("__") and attr.endswith("__"): + self.error("Invalid attribute name '%s' in compile-time expression" + % attr) + return None + obj = self.obj.compile_time_value(denv) + try: + return getattr(obj, attr) + except Exception, e: + self.compile_time_value_error(e) + + def analyse_target_declaration(self, env): + pass + + def analyse_target_types(self, env): + self.analyse_types(env, target = 1) + + def analyse_as_function(self, env): + module_scope = self.obj.analyse_as_module(env) + if module_scope: + entry = module_scope.lookup_here(self.attribute) + if entry and entry.is_type: + self.mutate_into_name_node(entry) + self.analyse_constructor_entry(env) + return + self.analyse_types(env) + + def analyse_types(self, env, target = 0): + if self.analyse_as_cimported_attribute(env, target): + return + if not target and self.analyse_as_unbound_cmethod(env): + return + self.analyse_as_ordinary_attribute(env, target) + + def analyse_as_cimported_attribute(self, env, target = 0, allow_type = 0): + # Try to interpret this as a reference to an imported + # C const, type, var or function. If successful, mutates + # this node into a NameNode and returns 1, otherwise + # returns 0. + module_scope = self.obj.analyse_as_module(env) + if module_scope: + entry = module_scope.lookup_here(self.attribute) + if entry and ( + entry.is_cglobal or entry.is_cfunction + or entry.is_type or entry.is_const): + self.mutate_into_name_node(entry) + if entry.is_type and allow_type: + pass + elif target: + self.analyse_target_types(env) + else: + self.analyse_rvalue_entry(env) + return 1 + return 0 + + def analyse_as_unbound_cmethod(self, env): + # Try to interpret this as a reference to an unbound + # C method of an extension type. If successful, mutates + # this node into a NameNode and returns 1, otherwise + # returns 0. + type = self.obj.analyse_as_extension_type(env) + if type: + entry = type.scope.lookup_here(self.attribute) + if entry and entry.is_cmethod: + # Create a temporary entry describing the C method + # as an ordinary function. + ubcm_entry = Symtab.Entry(entry.name, + "%s->%s" % (type.vtabptr_cname, entry.cname), + entry.type) + ubcm_entry.is_cfunction = 1 + ubcm_entry.func_cname = entry.func_cname + self.mutate_into_name_node(ubcm_entry) + self.analyse_rvalue_entry(env) + return 1 + return 0 + + def analyse_as_extension_type(self, env): + # Try to interpret this as a reference to an extension type + # in a cimported module. Returns the extension type, or None. + module_scope = self.obj.analyse_as_module(env) + if module_scope: + entry = module_scope.lookup_here(self.attribute) + if entry and entry.is_type and entry.type.is_extension_type: + return entry.type + return None + + def analyse_as_module(self, env): + # Try to interpret this as a reference to a cimported module + # in another cimported module. Returns the module scope, or None. + module_scope = self.obj.analyse_as_module(env) + if module_scope: + entry = module_scope.lookup_here(self.attribute) + if entry and entry.as_module: + return entry.as_module + return None + + def mutate_into_name_node(self, entry): + # Turn this node into a NameNode with the given entry. + self.__class__ = NameNode + self.name = self.attribute + self.entry = entry + del self.obj + del self.attribute + + def analyse_as_ordinary_attribute(self, env, target): + self.obj.analyse_types(env) + self.analyse_attribute(env) + if self.entry and self.entry.is_cmethod and not self.is_called: + error(self.pos, "C method can only be called") + if self.is_py_attr: + if not target: + self.is_temp = 1 + self.result_ctype = py_object_type + + def analyse_attribute(self, env): + # Look up attribute and set self.type and self.member. + self.is_py_attr = 0 + self.member = self.attribute + if self.obj.type.is_string: + self.obj = self.obj.coerce_to_pyobject(env) + obj_type = self.obj.type + if obj_type.is_ptr: + obj_type = obj_type.base_type + self.op = "->" + elif obj_type.is_extension_type: + self.op = "->" + else: + self.op = "." + if obj_type.has_attributes: + entry = None + if obj_type.attributes_known(): + entry = obj_type.scope.lookup_here(self.attribute) + else: + error(self.pos, + "Cannot select attribute of incomplete type '%s'" + % obj_type) + obj_type = PyrexTypes.error_type + self.entry = entry + if entry: + if obj_type.is_extension_type and entry.name == "__weakref__": + error(self.pos, "Illegal use of special attribute __weakref__") + if entry.is_variable or entry.is_cmethod: + self.type = entry.type + self.member = entry.cname + return + if entry.is_builtin_method and self.is_called: + # Mutate into NameNode referring to C function + #print "AttributeNode: Mutating builtin method into NameNode" ### + self.type = entry.type + self.__class__ = NameNode + return + else: + # If it's not a variable or C method, it must be a Python + # method of an extension type, so we treat it like a Python + # attribute. + pass + # If we get here, the base object is not a struct/union/extension + # type, or it is an extension type and the attribute is either not + # declared or is declared as a Python method. Treat it as a Python + # attribute reference. + if obj_type.is_pyobject: + self.type = py_object_type + self.is_py_attr = 1 + #self.interned_attr_cname = env.intern(self.attribute) + self.gil_check(env) + else: + if not obj_type.is_error: + error(self.pos, + "Object of type '%s' has no attribute '%s'" % + (obj_type, self.attribute)) + + gil_message = "Accessing Python attribute" + + def is_simple(self): + if self.obj: + return self.result_in_temp() or self.obj.is_simple() + else: + return NameNode.is_simple(self) + + def is_lvalue(self): + if self.obj: + return 1 + else: + return NameNode.is_lvalue(self) + + def is_inplace_lvalue(self): + return self.is_lvalue() + + def is_ephemeral(self): + if self.obj: + return self.obj.is_ephemeral() + else: + return NameNode.is_ephemeral(self) + + def calculate_result_code(self): + obj = self.obj + obj_code = obj.result_as(obj.type) + if self.entry and self.entry.is_cmethod: + return "((struct %s *)%s%s%s)->%s" % ( + obj.type.vtabstruct_cname, obj_code, self.op, + obj.type.vtabslot_cname, self.member) + else: + return "%s%s%s" % (obj_code, self.op, self.member) + + def generate_result_code(self, code): + if self.is_py_attr: + result = self.result() + cname = code.intern(self.attribute) + code.putln( + '%s = PyObject_GetAttr(%s, %s); if (!%s) %s' % ( + result, + self.obj.py_result(), + cname, + result, + code.error_goto(self.pos))) + + def generate_setattr_code(self, value_code, code): + cname = code.intern(self.attribute) + code.putln( + 'if (PyObject_SetAttr(%s, %s, %s) < 0) %s' % ( + self.obj.py_result(), + cname, + value_code, + code.error_goto(self.pos))) + + def generate_assignment_code(self, rhs, code): + self.obj.generate_evaluation_code(code) + if self.is_py_attr: + self.generate_setattr_code(rhs.py_result(), code) + rhs.generate_disposal_code(code) + else: + select_code = self.result() + if self.type.is_pyobject: + rhs.make_owned_reference(code) + code.put_decref(select_code, self.ctype()) + code.putln( + "%s = %s;" % ( + select_code, + rhs.result_as(self.ctype()))) + rhs.generate_post_assignment_code(code) + self.obj.generate_disposal_code(code) + + def generate_inplace_assignment_code(self, operator, rhs, code): + self.obj.generate_evaluation_code(code) + select_code = self.result() + if self.type.is_pyobject: + self.generate_result_code(code) + self.generate_inplace_operation_code(operator, rhs, code) + if self.is_py_attr: + self.generate_setattr_code(self.inplace_result, code) + self.generate_inplace_result_disposal_code(code) + else: + code.put_decref(select_code, self.ctype()) + cast_inplace_result = typecast(self.ctype(), py_object_type, self.inplace_result) + code.putln("%s = %s;" % (select_code, cast_inplace_result)) + else: + code.putln("%s %s %s;" % (select_code, operator, rhs.result())) + rhs.generate_disposal_code(code) + self.obj.generate_disposal_code(code) + + def generate_deletion_code(self, code): + self.obj.generate_evaluation_code(code) + if self.is_py_attr: + cname = code.intern(self.attribute) + code.putln( + 'if (PyObject_DelAttr(%s, %s) < 0) %s' % ( + self.obj.py_result(), + cname, + code.error_goto(self.pos))) + else: + error(self.pos, "Cannot delete C attribute of extension type") + self.obj.generate_disposal_code(code) + +#------------------------------------------------------------------- +# +# Constructor nodes +# +#------------------------------------------------------------------- + +class SequenceNode(ExprNode): + # Base class for list and tuple constructor nodes. + # Contains common code for performing sequence unpacking. + # + # args [ExprNode] + # iterator ExprNode + # unpacked_items [ExprNode] or None + # coerced_unpacked_items [ExprNode] or None + + subexprs = ['args'] + + is_sequence_constructor = 1 + unpacked_items = None + + def compile_time_value_list(self, denv): + return [arg.compile_time_value(denv) for arg in self.args] + + def analyse_target_declaration(self, env): + for arg in self.args: + arg.analyse_target_declaration(env) + + def analyse_types(self, env): + for i in range(len(self.args)): + arg = self.args[i] + arg.analyse_types(env) + self.args[i] = arg.coerce_to_pyobject(env) + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + + def analyse_target_types(self, env): + self.iterator = PyTempNode(self.pos, env) + self.unpacked_items = [] + self.coerced_unpacked_items = [] + for arg in self.args: + arg.analyse_target_types(env) + unpacked_item = PyTempNode(self.pos, env) + coerced_unpacked_item = unpacked_item.coerce_to(arg.type, env) + self.unpacked_items.append(unpacked_item) + self.coerced_unpacked_items.append(coerced_unpacked_item) + self.type = py_object_type +# env.use_utility_code(unpacking_utility_code) + + def allocate_target_temps(self, env, rhs): + self.iterator.allocate_temps(env) + if rhs: + rhs.release_temp(env) + for arg, node in zip(self.args, self.coerced_unpacked_items): + node.allocate_temps(env) + arg.allocate_target_temps(env, node) + #arg.release_target_temp(env) + #node.release_temp(env) + self.iterator.release_temp(env) + +# def release_target_temp(self, env): +# #for arg in self.args: +# # arg.release_target_temp(env) +# #for node in self.coerced_unpacked_items: +# # node.release_temp(env) +# self.iterator.release_temp(env) + + def generate_result_code(self, code): + self.generate_operation_code(code) + + def generate_assignment_code(self, rhs, code): + iter_result = self.iterator.result() + code.putln( + "%s = PyObject_GetIter(%s); if (!%s) %s" % ( + iter_result, + rhs.py_result(), + iter_result, + code.error_goto(self.pos))) + rhs.generate_disposal_code(code) + for i in range(len(self.args)): + item = self.unpacked_items[i] + code.use_utility_code(unpacking_utility_code) + unpack_code = "__Pyx_UnpackItem(%s)" % ( + self.iterator.py_result()) + item_result = item.result() + code.putln( + "%s = %s; if (!%s) %s" % ( + item_result, + typecast(item.ctype(), py_object_type, unpack_code), + item_result, + code.error_goto(self.pos))) + value_node = self.coerced_unpacked_items[i] + value_node.generate_evaluation_code(code) + self.args[i].generate_assignment_code(value_node, code) + code.putln( + "if (__Pyx_EndUnpack(%s) < 0) %s" % ( + self.iterator.py_result(), + code.error_goto(self.pos))) + if debug_disposal_code: + print "UnpackNode.generate_assignment_code:" + print "...generating disposal code for", rhs + self.iterator.generate_disposal_code(code) + + +class TupleNode(SequenceNode): + # Tuple constructor. + + gil_message = "Constructing Python tuple" + + def compile_time_value(self, denv): + values = self.compile_time_value_list(denv) + try: + return tuple(values) + except Exception, e: + self.compile_time_value_error(e) + + def generate_operation_code(self, code): + result = self.result() + code.putln( + "%s = PyTuple_New(%s); if (!%s) %s" % ( + result, + len(self.args), + result, + code.error_goto(self.pos))) + for i in range(len(self.args)): + arg = self.args[i] + arg_result = arg.py_result() + # ??? Change this to use make_owned_reference? + if not arg.result_in_temp(): + code.put_incref(arg_result) + code.putln( + "PyTuple_SET_ITEM(%s, %s, %s);" % ( + result, + i, + arg_result)) + + def generate_subexpr_disposal_code(self, code): + # We call generate_post_assignment_code here instead + # of generate_disposal_code, because values were stored + # in the tuple using a reference-stealing operation. + for arg in self.args: + arg.generate_post_assignment_code(code) + + +class ListNode(SequenceNode): + # List constructor. + + gil_message = "Constructing Python list" + + def compile_time_value(self, denv): + return self.compile_time_value_list(denv) + + def generate_operation_code(self, code): + result = self.result() + code.putln("%s = PyList_New(%s); if (!%s) %s" % + (result, + len(self.args), + result, + code.error_goto(self.pos))) + for i in range(len(self.args)): + arg = self.args[i] + arg_result = arg.py_result() + #if not arg.is_temp: + if not arg.result_in_temp(): + code.put_incref(arg_result) + code.putln("PyList_SET_ITEM(%s, %s, %s);" % + (result, + i, + arg_result)) + + def generate_subexpr_disposal_code(self, code): + # We call generate_post_assignment_code here instead + # of generate_disposal_code, because values were stored + # in the list using a reference-stealing operation. + for arg in self.args: + arg.generate_post_assignment_code(code) + + +class DictNode(ExprNode): + # Dictionary constructor. + # + # key_value_pairs [(ExprNode, ExprNode)] + + def compile_time_value(self, denv): + pairs = [(key.compile_time_value(denv), value.compile_time_value(denv)) + for (key, value) in self.key_value_pairs] + try: + return dict(pairs) + except Exception, e: + self.compile_time_value_error(e) + + def analyse_types(self, env): + new_pairs = [] + for key, value in self.key_value_pairs: + key.analyse_types(env) + value.analyse_types(env) + key = key.coerce_to_pyobject(env) + value = value.coerce_to_pyobject(env) + new_pairs.append((key, value)) + self.key_value_pairs = new_pairs + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + + gil_message = "Constructing Python dict" + + def allocate_temps(self, env, result = None): + # Custom method used here because key-value + # pairs are evaluated and used one at a time. + self.allocate_temp(env, result) + for key, value in self.key_value_pairs: + key.allocate_temps(env) + value.allocate_temps(env) + key.release_temp(env) + value.release_temp(env) + + def generate_evaluation_code(self, code): + # Custom method used here because key-value + # pairs are evaluated and used one at a time. + result = self.result() + code.putln( + "%s = PyDict_New(); if (!%s) %s" % ( + result, + result, + code.error_goto(self.pos))) + for key, value in self.key_value_pairs: + key.generate_evaluation_code(code) + value.generate_evaluation_code(code) + code.putln( + "if (PyDict_SetItem(%s, %s, %s) < 0) %s" % ( + result, + key.py_result(), + value.py_result(), + code.error_goto(self.pos))) + key.generate_disposal_code(code) + value.generate_disposal_code(code) + + +class ClassNode(ExprNode): + # Helper class used in the implementation of Python + # class definitions. Constructs a class object given + # a name, tuple of bases and class dictionary. + # + # name ExprNode Name of the class + # bases ExprNode Base class tuple + # dict ExprNode Class dict (not owned by this node) + # doc ExprNode or None Doc string + # module_name string Name of defining module + + subexprs = ['name', 'bases', 'doc'] + + def analyse_types(self, env): + self.name.analyse_types(env) + self.name = self.name.coerce_to_pyobject(env) + self.bases.analyse_types(env) + if self.doc: + self.doc.analyse_types(env) + self.doc = self.doc.coerce_to_pyobject(env) + self.module_name = env.global_scope().qualified_name + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 +# env.use_utility_code(create_class_utility_code) + + gil_message = "Constructing Python class" + + def generate_result_code(self, code): + result = self.result() + if self.doc: + code.putln( + 'if (PyDict_SetItemString(%s, "__doc__", %s) < 0) %s' % ( + self.dict.py_result(), + self.doc.py_result(), + code.error_goto(self.pos))) + code.use_utility_code(create_class_utility_code) + code.putln( + '%s = __Pyx_CreateClass(%s, %s, %s, "%s"); if (!%s) %s' % ( + result, + self.bases.py_result(), + self.dict.py_result(), + self.name.py_result(), + self.module_name, + result, + code.error_goto(self.pos))) + + +class UnboundMethodNode(ExprNode): + # Helper class used in the implementation of Python + # class definitions. Constructs an unbound method + # object from a class and a function. + # + # class_cname string C var holding the class object + # function ExprNode Function object + + subexprs = ['function'] + + def analyse_types(self, env): + self.function.analyse_types(env) + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + + gil_message = "Constructing an unbound method" + + def generate_result_code(self, code): + result = self.result() + code.putln( + "%s = PyMethod_New(%s, 0, %s); if (!%s) %s" % ( + result, + self.function.py_result(), + self.class_cname, + result, + code.error_goto(self.pos))) + + +class PyCFunctionNode(AtomicExprNode): + # Helper class used in the implementation of Python + # class definitions. Constructs a PyCFunction object + # from a PyMethodDef struct. + # + # pymethdef_cname string PyMethodDef structure + # module_name string Name of defining module + + def analyse_types(self, env): + self.type = py_object_type + self.module_name = env.global_scope().module_name + self.gil_check(env) + self.is_temp = 1 + + gil_message = "Constructing Python function" + + def generate_result_code(self, code): + result = self.result() + code.putln( + "%s = PyCFunction_NewEx(&%s, 0, %s); if (!%s) %s" % ( + result, + self.pymethdef_cname, + code.get_py_string_const(self.module_name), + result, + code.error_goto(self.pos))) + +#------------------------------------------------------------------- +# +# Unary operator nodes +# +#------------------------------------------------------------------- + +compile_time_unary_operators = { + 'not': operator.not_, + '~': operator.inv, + '-': operator.neg, + '+': operator.pos, +} + +class UnopNode(ExprNode): + # operator string + # operand ExprNode + # + # Processing during analyse_expressions phase: + # + # analyse_c_operation + # Called when the operand is not a pyobject. + # - Check operand type and coerce if needed. + # - Determine result type and result code fragment. + # - Allocate temporary for result if needed. + + subexprs = ['operand'] + + def compile_time_value(self, denv): + func = compile_time_unary_operators.get(self.operator) + if not func: + error(self.pos, + "Unary '%s' not supported in compile-time expression" + % self.operator) + operand = self.operand.compile_time_value(denv) + try: + return func(operand) + except Exception, e: + self.compile_time_value_error(e) + + def analyse_types(self, env): + self.operand.analyse_types(env) + if self.is_py_operation(): + self.coerce_operand_to_pyobject(env) + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + else: + self.analyse_c_operation(env) + + def check_const(self): + self.operand.check_const() + + def is_py_operation(self): + return self.operand.type.is_pyobject + + def coerce_operand_to_pyobject(self, env): + self.operand = self.operand.coerce_to_pyobject(env) + + def generate_result_code(self, code): + if self.operand.type.is_pyobject: + self.generate_py_operation_code(code) + else: + if self.is_temp: + self.generate_c_operation_code(code) + + def generate_py_operation_code(self, code): + function = self.py_operation_function() + result = self.result() + code.putln( + "%s = %s(%s); if (!%s) %s" % ( + result, + function, + self.operand.py_result(), + result, + code.error_goto(self.pos))) + + def type_error(self): + if not self.operand.type.is_error: + error(self.pos, "Invalid operand type for '%s' (%s)" % + (self.operator, self.operand.type)) + self.type = PyrexTypes.error_type + + +class NotNode(ExprNode): + # 'not' operator + # + # operand ExprNode + + def compile_time_value(self, denv): + operand = self.operand.compile_time_value(denv) + try: + return not operand + except Exception, e: + self.compile_time_value_error(e) + + subexprs = ['operand'] + + def analyse_types(self, env): + self.operand.analyse_types(env) + self.operand = self.operand.coerce_to_boolean(env) + self.type = PyrexTypes.c_int_type + + def calculate_result_code(self): + return "(!%s)" % self.operand.result() + + def generate_result_code(self, code): + pass + + +class UnaryPlusNode(UnopNode): + # unary '+' operator + + operator = '+' + + def analyse_c_operation(self, env): + self.type = self.operand.type + + def py_operation_function(self): + return "PyNumber_Positive" + + def calculate_result_code(self): + return self.operand.result() + + +class UnaryMinusNode(UnopNode): + # unary '-' operator + + operator = '-' + + def analyse_c_operation(self, env): + if self.operand.type.is_numeric: + self.type = self.operand.type + else: + self.type_error() + + def py_operation_function(self): + return "PyNumber_Negative" + + def calculate_result_code(self): + return "(-%s)" % self.operand.result() + + +class TildeNode(UnopNode): + # unary '~' operator + + def analyse_c_operation(self, env): + if self.operand.type.is_int: + self.type = self.operand.type + else: + self.type_error() + + def py_operation_function(self): + return "PyNumber_Invert" + + def calculate_result_code(self): + return "(~%s)" % self.operand.result() + + +class AmpersandNode(ExprNode): + # The C address-of operator. + # + # operand ExprNode + + subexprs = ['operand'] + + def analyse_types(self, env): + self.operand.analyse_types(env) + argtype = self.operand.type + if not (argtype.is_cfunction or self.operand.is_lvalue()): + self.error("Taking address of non-lvalue") + return + if argtype.is_pyobject: + self.error("Cannot take address of Python variable") + return + self.type = PyrexTypes.c_ptr_type(argtype) + + def check_const(self): + self.operand.check_const_addr() + + def error(self, mess): + error(self.pos, mess) + self.type = PyrexTypes.error_type + self.result_code = "<error>" + + def calculate_result_code(self): + return "(&%s)" % self.operand.result() + + def generate_result_code(self, code): + pass + + +unop_node_classes = { + "+": UnaryPlusNode, + "-": UnaryMinusNode, + "~": TildeNode, +} + +def unop_node(pos, operator, operand): + # Construct unnop node of appropriate class for + # given operator. + return unop_node_classes[operator](pos, + operator = operator, + operand = operand) + + +class TypecastNode(ExprNode): + # C type cast + # + # base_type CBaseTypeNode + # declarator CDeclaratorNode + # operand ExprNode + + subexprs = ['operand'] + + def analyse_types(self, env): + base_type = self.base_type.analyse(env) + _, self.type = self.declarator.analyse(base_type, env) + if self.type.is_cfunction: + error(self.pos, + "Cannot cast to a function type") + self.type = PyrexTypes.error_type + self.operand.analyse_types(env) + to_py = self.type.is_pyobject + from_py = self.operand.type.is_pyobject + if from_py and not to_py and self.operand.is_ephemeral(): + error(self.pos, "Casting temporary Python object to non-Python type") + # Must do the following, so that the result can be increfed without + # the operand getting evaluated twice. + if to_py and not from_py: + #self.result_ctype = py_object_type + #self.is_temp = 1 + self.operand = self.operand.coerce_to_simple(env) + + def check_const(self): + self.operand.check_const() + + def calculate_result_code(self): + opnd = self.operand + result_code = self.type.cast_code(opnd.result()) + return result_code + + def result_as(self, type): + if not self.is_temp and type.is_pyobject and self.type.is_pyobject: + # Optimise away some unnecessary casting + return self.operand.result_as(type) + else: + return ExprNode.result_as(self, type) + + def generate_result_code(self, code): + if self.is_temp: + code.putln( + "%s = %s;" % ( + self.result(), + self.operand.py_result())) + code.put_incref(self.py_result()) + + +class SizeofNode(ExprNode): + # Base class for sizeof(x) expression nodes. + # + # sizeof_code string + + subexprs = [] + + def check_const(self): + pass + + def analyse_types(self, env): + self.analyse_argument(env) + self.type = PyrexTypes.c_size_t_type + + def analyse_type_argument(self, arg_type): + if arg_type.is_pyobject: + error(self.pos, "Cannot take sizeof Python object") + elif arg_type.is_void: + error(self.pos, "Cannot take sizeof void") + elif not arg_type.is_complete(): + error(self.pos, "Cannot take sizeof incomplete type '%s'" % arg_type) + arg_code = arg_type.declaration_code("") + self.sizeof_code = "(sizeof(%s))" % arg_code + + def calculate_result_code(self): + return self.sizeof_code + + def generate_result_code(self, code): + pass + + +class SizeofTypeNode(SizeofNode): + # C sizeof function applied to a type + # + # base_type CBaseTypeNode + # declarator CDeclaratorNode + + def analyse_argument(self, env): + base_type = self.base_type.analyse(env) + _, arg_type = self.declarator.analyse(base_type, env) + self.analyse_type_argument(arg_type) + + +class SizeofVarNode(SizeofNode): + # C sizeof function applied to a variable or qualified name + # (which may actually refer to a type) + # + # operand ExprNode + + #subexprs = ['operand'] + + def analyse_argument(self, env): + is_type = 0 + operand = self.operand + if operand.analyse_as_cimported_attribute(env, allow_type = 1): + if operand.entry.is_type: + is_type = 1 + self.analyse_type_argument(operand.entry.type) + else: + self.operand.analyse_types(env) + self.operand.mark_vars_used() + if not is_type: + self.sizeof_code = "(sizeof(%s))" % operand.result() + + +#------------------------------------------------------------------- +# +# Binary operator nodes +# +#------------------------------------------------------------------- + +compile_time_binary_operators = { + '<': operator.lt, + '<=': operator.le, + '==': operator.eq, + '!=': operator.ne, + '>=': operator.ge, + '>': operator.gt, + 'is': operator.is_, + 'is_not': operator.is_not, + '+': operator.add, + '&': operator.and_, + '/': operator.div, + '//': operator.floordiv, + '<<': operator.lshift, + '%': operator.mod, + '*': operator.mul, + '|': operator.or_, + '**': operator.pow, + '>>': operator.rshift, + '-': operator.sub, + #'/': operator.truediv, + '^': operator.xor, + 'in': lambda x, y: x in y, + 'not_in': lambda x, y: x not in y, +} + +def get_compile_time_binop(node): + func = compile_time_binary_operators.get(node.operator) + if not func: + error(node.pos, + "Binary '%s' not supported in compile-time expression" + % node.operator) + return func + +class BinopNode(ExprNode): + # operator string + # operand1 ExprNode + # operand2 ExprNode + # + # Processing during analyse_expressions phase: + # + # analyse_c_operation + # Called when neither operand is a pyobject. + # - Check operand types and coerce if needed. + # - Determine result type and result code fragment. + # - Allocate temporary for result if needed. + + subexprs = ['operand1', 'operand2'] + + def compile_time_value(self, denv): + func = get_compile_time_binop(self) + operand1 = self.operand1.compile_time_value(denv) + operand2 = self.operand2.compile_time_value(denv) + try: + return func(operand1, operand2) + except Exception, e: + self.compile_time_value_error(e) + + def analyse_types(self, env): + self.operand1.analyse_types(env) + self.operand2.analyse_types(env) + if self.is_py_operation(): + self.coerce_operands_to_pyobjects(env) + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + else: + self.analyse_c_operation(env) + + def is_py_operation(self): + return (self.operand1.type.is_pyobject + or self.operand2.type.is_pyobject) + + def coerce_operands_to_pyobjects(self, env): + self.operand1 = self.operand1.coerce_to_pyobject(env) + self.operand2 = self.operand2.coerce_to_pyobject(env) + + def check_const(self): + self.operand1.check_const() + self.operand2.check_const() + + def generate_result_code(self, code): + #print "BinopNode.generate_result_code:", self.operand1, self.operand2 ### + if self.operand1.type.is_pyobject: + function = self.py_operation_function() + if function == "PyNumber_Power": + extra_args = ", Py_None" + else: + extra_args = "" + result = self.result() + code.putln( + "%s = %s(%s, %s%s); if (!%s) %s" % ( + result, + function, + self.operand1.py_result(), + self.operand2.py_result(), + extra_args, + result, + code.error_goto(self.pos))) + else: + if self.is_temp: + self.generate_c_operation_code(code) + + def type_error(self): + if not (self.operand1.type.is_error + or self.operand2.type.is_error): + error(self.pos, "Invalid operand types for '%s' (%s; %s)" % + (self.operator, self.operand1.type, + self.operand2.type)) + self.type = PyrexTypes.error_type + + +class NumBinopNode(BinopNode): + # Binary operation taking numeric arguments. + + def analyse_c_operation(self, env): + type1 = self.operand1.type + type2 = self.operand2.type + if self.operator == "**" and type1.is_int and type2.is_int: + error(self.pos, "** with two C int types is ambiguous") + self.type = error_type + return + self.type = self.compute_c_result_type(type1, type2) + if not self.type: + self.type_error() + + def compute_c_result_type(self, type1, type2): + if self.c_types_okay(type1, type2): + return PyrexTypes.widest_numeric_type(type1, type2) + else: + return None + + def c_types_okay(self, type1, type2): + #print "NumBinopNode.c_types_okay:", type1, type2 ### + return (type1.is_numeric or type1.is_enum) \ + and (type2.is_numeric or type2.is_enum) + + def calculate_result_code(self): + return "(%s %s %s)" % ( + self.operand1.result(), + self.operator, + self.operand2.result()) + + def py_operation_function(self): + return self.py_functions[self.operator] + + py_functions = { + "|": "PyNumber_Or", + "^": "PyNumber_Xor", + "&": "PyNumber_And", + "<<": "PyNumber_Lshift", + ">>": "PyNumber_Rshift", + "+": "PyNumber_Add", + "-": "PyNumber_Subtract", + "*": "PyNumber_Multiply", + "/": "PyNumber_Divide", + "%": "PyNumber_Remainder", + "**": "PyNumber_Power" + } + + +class IntBinopNode(NumBinopNode): + # Binary operation taking integer arguments. + + def c_types_okay(self, type1, type2): + #print "IntBinopNode.c_types_okay:", type1, type2 ### + return (type1.is_int or type1.is_enum) \ + and (type2.is_int or type2.is_enum) + + +class AddNode(NumBinopNode): + # '+' operator. + + def is_py_operation(self): + if self.operand1.type.is_string \ + and self.operand2.type.is_string: + return 1 + else: + return NumBinopNode.is_py_operation(self) + + def compute_c_result_type(self, type1, type2): + #print "AddNode.compute_c_result_type:", type1, self.operator, type2 ### + if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum): + return type1 + elif (type2.is_ptr or type2.is_array) and (type1.is_int or type1.is_enum): + return type2 + else: + return NumBinopNode.compute_c_result_type( + self, type1, type2) + + +class SubNode(NumBinopNode): + # '-' operator. + + def compute_c_result_type(self, type1, type2): + if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum): + return type1 + elif (type1.is_ptr or type1.is_array) and (type2.is_ptr or type2.is_array): + return PyrexTypes.c_int_type + else: + return NumBinopNode.compute_c_result_type( + self, type1, type2) + + +class MulNode(NumBinopNode): + # '*' operator. + + def is_py_operation(self): + type1 = self.operand1.type + type2 = self.operand2.type + if (type1.is_string and type2.is_int) \ + or (type2.is_string and type1.is_int): + return 1 + else: + return NumBinopNode.is_py_operation(self) + + +class ModNode(IntBinopNode): + # '%' operator. + + def is_py_operation(self): + return (self.operand1.type.is_string + or self.operand2.type.is_string + or IntBinopNode.is_py_operation(self)) + + +class PowNode(NumBinopNode): + # '**' operator. + + def analyse_types(self, env): + env.pow_function_used = 1 + NumBinopNode.analyse_types(self, env) + + def compute_c_result_type(self, type1, type2): + if self.c_types_okay(type1, type2): + return PyrexTypes.c_double_type + else: + return None + + def calculate_result_code(self): + return "pow(%s, %s)" % ( + self.operand1.result(), self.operand2.result()) + + +class BoolBinopNode(ExprNode): + # Short-circuiting boolean operation. + # + # operator string + # operand1 ExprNode + # operand2 ExprNode + # temp_bool ExprNode used internally + + temp_bool = None + + subexprs = ['operand1', 'operand2', 'temp_bool'] + + def compile_time_value(self, denv): + if self.operator == 'and': + return self.operand1.compile_time_value(denv) \ + and self.operand2.compile_time_value(denv) + else: + return self.operand1.compile_time_value(denv) \ + or self.operand2.compile_time_value(denv) + + def analyse_types(self, env): + self.operand1.analyse_types(env) + self.operand2.analyse_types(env) + if self.operand1.type.is_pyobject or \ + self.operand2.type.is_pyobject: + self.operand1 = self.operand1.coerce_to_pyobject(env) + self.operand2 = self.operand2.coerce_to_pyobject(env) + self.temp_bool = TempNode(self.pos, + PyrexTypes.c_int_type, env) + self.type = py_object_type + self.gil_check(env) + else: + self.operand1 = self.operand1.coerce_to_boolean(env) + self.operand2 = self.operand2.coerce_to_boolean(env) + self.type = PyrexTypes.c_int_type + # For what we're about to do, it's vital that + # both operands be temp nodes. + self.operand1 = self.operand1.coerce_to_temp(env) #CTT + self.operand2 = self.operand2.coerce_to_temp(env) + self.is_temp = 1 + + gil_message = "Truth-testing Python object" + + def allocate_temps(self, env, result_code = None): + # We don't need both operands at the same time, and + # one of the operands will also be our result. So we + # use an allocation strategy here which results in + # this node and both its operands sharing the same + # result variable. This allows us to avoid some + # assignments and increfs/decrefs that would otherwise + # be necessary. + self.allocate_temp(env, result_code) + self.operand1.allocate_temps(env, self.result_code) + if self.temp_bool: + self.temp_bool.allocate_temp(env) + self.temp_bool.release_temp(env) + self.operand2.allocate_temps(env, self.result_code) + # We haven't called release_temp on either operand, + # because although they are temp nodes, they don't own + # their result variable. And because they are temp + # nodes, any temps in their subnodes will have been + # released before their allocate_temps returned. + # Therefore, they contain no temp vars that need to + # be released. + + def check_const(self): + self.operand1.check_const() + self.operand2.check_const() + + def calculate_result_code(self): + return "(%s %s %s)" % ( + self.operand1.result(), + self.py_to_c_op[self.operator], + self.operand2.result()) + + py_to_c_op = {'and': "&&", 'or': "||"} + + def generate_evaluation_code(self, code): + self.operand1.generate_evaluation_code(code) + test_result = self.generate_operand1_test(code) + if self.operator == 'and': + sense = "" + else: + sense = "!" + code.putln( + "if (%s%s) {" % ( + sense, + test_result)) + self.operand1.generate_disposal_code(code) + self.operand2.generate_evaluation_code(code) + code.putln( + "}") + + def generate_operand1_test(self, code): + # Generate code to test the truth of the first operand. + if self.type.is_pyobject: + test_result = self.temp_bool.result() + code.putln( + "%s = PyObject_IsTrue(%s); if (%s < 0) %s" % ( + test_result, + self.operand1.py_result(), + test_result, + code.error_goto(self.pos))) + else: + test_result = self.operand1.result() + return test_result + + +class CmpNode: + # Mixin class containing code common to PrimaryCmpNodes + # and CascadedCmpNodes. + + def cascaded_compile_time_value(self, operand1, denv): + func = get_compile_time_binop(self) + operand2 = self.operand2.compile_time_value(denv) + try: + result = func(operand1, operand2) + except Exception, e: + self.compile_time_value_error(e) + result = None + if result: + cascade = self.cascade + if cascade: + result = result and cascade.compile_time_value(operand2, denv) + return result + + def is_python_comparison(self): + return (self.has_python_operands() + or (self.cascade and self.cascade.is_python_comparison()) + or self.operator in ('in', 'not_in')) + + def check_types(self, env, operand1, op, operand2): + if not self.types_okay(operand1, op, operand2): + error(self.pos, "Invalid types for '%s' (%s, %s)" % + (self.operator, operand1.type, operand2.type)) + + def types_okay(self, operand1, op, operand2): + type1 = operand1.type + type2 = operand2.type + if type1.is_error or type2.is_error: + return 1 + if type1.is_pyobject: # type2 will be, too + return 1 + elif type1.is_ptr or type1.is_array: + return type1.is_null_ptr or type2.is_null_ptr \ + or ((type2.is_ptr or type2.is_array) + and type1.base_type.same_as(type2.base_type)) + elif ((type1.is_numeric and type2.is_numeric + or type1.is_enum and (type2.is_int or type1.same_as(type2)) + or type1.is_int and type2.is_enum) + and op not in ('is', 'is_not')): + return 1 + else: + return 0 + + def generate_operation_code(self, code, result, + operand1, op , operand2): + if op == 'in' or op == 'not_in': + code.putln( + "%s = PySequence_Contains(%s, %s); if (%s < 0) %s" % ( + result, + operand2.py_result(), + operand1.py_result(), + result, + code.error_goto(self.pos))) + if op == 'not_in': + code.putln( + "%s = !%s;" % ( + result, result)) + elif (operand1.type.is_pyobject + and op not in ('is', 'is_not')): + code.putln( + "if (PyObject_Cmp(%s, %s, &%s) < 0) %s" % ( + operand1.py_result(), + operand2.py_result(), + result, + code.error_goto(self.pos))) + code.putln( + "%s = %s %s 0;" % ( + result, result, op)) + else: + type1 = operand1.type + type2 = operand2.type + if (type1.is_extension_type or type2.is_extension_type) \ + and not operand1.ctype().same_as(operand2.ctype()): + code1 = operand1.result_as(py_object_type) + code2 = operand2.result_as(py_object_type) + else: + code1 = operand1.result() + code2 = operand2.result() + code.putln("%s = %s %s %s;" % ( + result, + code1, + self.c_operator(op), + code2)) + + def c_operator(self, op): + if op == 'is': + return "==" + elif op == 'is_not': + return "!=" + else: + return op + + +class PrimaryCmpNode(ExprNode, CmpNode): + # Non-cascaded comparison or first comparison of + # a cascaded sequence. + # + # operator string + # operand1 ExprNode + # operand2 ExprNode + # cascade CascadedCmpNode + + # We don't use the subexprs mechanism, because + # things here are too complicated for it to handle. + # Instead, we override all the framework methods + # which use it. + + cascade = None + + def compile_time_value(self, denv): + operand1 = self.operand1.compile_time_value(denv) + return self.cascaded_compile_time_value(operand1, denv) + + def analyse_types(self, env): + self.operand1.analyse_types(env) + self.operand2.analyse_types(env) + if self.cascade: + self.cascade.analyse_types(env, self.operand2) + self.is_pycmp = self.is_python_comparison() + if self.is_pycmp: + self.coerce_operands_to_pyobjects(env) + if self.cascade: + self.operand2 = self.operand2.coerce_to_simple(env) + self.cascade.coerce_cascaded_operands_to_temp(env) + self.check_operand_types(env) + self.type = PyrexTypes.c_int_type + if self.is_pycmp or self.cascade: + self.is_temp = 1 + + def check_operand_types(self, env): + self.check_types(env, + self.operand1, self.operator, self.operand2) + if self.cascade: + self.cascade.check_operand_types(env, self.operand2) + + def has_python_operands(self): + return (self.operand1.type.is_pyobject + or self.operand2.type.is_pyobject) + + def coerce_operands_to_pyobjects(self, env): + self.operand1 = self.operand1.coerce_to_pyobject(env) + self.operand2 = self.operand2.coerce_to_pyobject(env) + if self.cascade: + self.cascade.coerce_operands_to_pyobjects(env) + + def allocate_subexpr_temps(self, env): + self.operand1.allocate_temps(env) + self.operand2.allocate_temps(env) + if self.cascade: + self.cascade.allocate_subexpr_temps(env) + + def release_subexpr_temps(self, env): + self.operand1.release_temp(env) + self.operand2.release_temp(env) + if self.cascade: + self.cascade.release_subexpr_temps(env) + + def check_const(self): + self.operand1.check_const() + self.operand2.check_const() + if self.cascade: + self.not_const() + + def calculate_result_code(self): + return "(%s %s %s)" % ( + self.operand1.result(), + self.c_operator(self.operator), + self.operand2.result()) + + def generate_evaluation_code(self, code): + self.operand1.generate_evaluation_code(code) + self.operand2.generate_evaluation_code(code) + if self.is_temp: + result = self.result() + self.generate_operation_code(code, result, + self.operand1, self.operator, self.operand2) + if self.cascade: + self.cascade.generate_evaluation_code(code, + result, self.operand2) + self.operand1.generate_disposal_code(code) + self.operand2.generate_disposal_code(code) + + def generate_subexpr_disposal_code(self, code): + # If this is called, it is a non-cascaded cmp, + # so only need to dispose of the two main operands. + self.operand1.generate_disposal_code(code) + self.operand2.generate_disposal_code(code) + + +class CascadedCmpNode(Node, CmpNode): + # A CascadedCmpNode is not a complete expression node. It + # hangs off the side of another comparison node, shares + # its left operand with that node, and shares its result + # with the PrimaryCmpNode at the head of the chain. + # + # operator string + # operand2 ExprNode + # cascade CascadedCmpNode + + cascade = None + + def analyse_types(self, env, operand1): + self.operand2.analyse_types(env) + if self.cascade: + self.cascade.analyse_types(env, self.operand2) + + def check_operand_types(self, env, operand1): + self.check_types(env, + operand1, self.operator, self.operand2) + if self.cascade: + self.cascade.check_operand_types(env, self.operand2) + + def has_python_operands(self): + return self.operand2.type.is_pyobject + + def coerce_operands_to_pyobjects(self, env): + self.operand2 = self.operand2.coerce_to_pyobject(env) + if self.cascade: + self.cascade.coerce_operands_to_pyobjects(env) + + def coerce_cascaded_operands_to_temp(self, env): + if self.cascade: + #self.operand2 = self.operand2.coerce_to_temp(env) #CTT + self.operand2 = self.operand2.coerce_to_simple(env) + self.cascade.coerce_cascaded_operands_to_temp(env) + + def allocate_subexpr_temps(self, env): + self.operand2.allocate_temps(env) + if self.cascade: + self.cascade.allocate_subexpr_temps(env) + + def release_subexpr_temps(self, env): + self.operand2.release_temp(env) + if self.cascade: + self.cascade.release_subexpr_temps(env) + + def generate_evaluation_code(self, code, result, operand1): + code.putln("if (%s) {" % result) + self.operand2.generate_evaluation_code(code) + self.generate_operation_code(code, result, + operand1, self.operator, self.operand2) + if self.cascade: + self.cascade.generate_evaluation_code( + code, result, self.operand2) + # Cascaded cmp result is always temp + self.operand2.generate_disposal_code(code) + code.putln("}") + + +binop_node_classes = { + "or": BoolBinopNode, + "and": BoolBinopNode, + "|": IntBinopNode, + "^": IntBinopNode, + "&": IntBinopNode, + "<<": IntBinopNode, + ">>": IntBinopNode, + "+": AddNode, + "-": SubNode, + "*": MulNode, + "/": NumBinopNode, + "%": ModNode, + "**": PowNode +} + +def binop_node(pos, operator, operand1, operand2): + # Construct binop node of appropriate class for + # given operator. + return binop_node_classes[operator](pos, + operator = operator, + operand1 = operand1, + operand2 = operand2) + +#------------------------------------------------------------------- +# +# Coercion nodes +# +# Coercion nodes are special in that they are created during +# the analyse_types phase of parse tree processing. +# Their __init__ methods consequently incorporate some aspects +# of that phase. +# +#------------------------------------------------------------------- + +class CoercionNode(ExprNode): + # Abstract base class for coercion nodes. + # + # arg ExprNode node being coerced + + subexprs = ['arg'] + + def __init__(self, arg): + self.pos = arg.pos + self.arg = arg + if debug_coercion: + print self, "Coercing", self.arg + + +class CastNode(CoercionNode): + # Wrap a node in a C type cast. + + def __init__(self, arg, new_type): + CoercionNode.__init__(self, arg) + self.type = new_type + + def calculate_result_code(self): + return self.arg.result_as(self.type) + + def generate_result_code(self, code): + self.arg.generate_result_code(code) + + +class PyTypeTestNode(CoercionNode): + # This node is used to check that a generic Python + # object is an instance of a particular extension type. + # This node borrows the result of its argument node. + + def __init__(self, arg, dst_type, env): + # The arg is know to be a Python object, and + # the dst_type is known to be an extension type. + assert dst_type.is_extension_type, "PyTypeTest on non extension type" + CoercionNode.__init__(self, arg) + self.type = dst_type + self.result_ctype = arg.ctype() +# env.use_utility_code(type_test_utility_code) + self.gil_check(env) + + gil_message = "Python type test" + + def result_in_temp(self): + return self.arg.result_in_temp() + + def is_ephemeral(self): + return self.arg.is_ephemeral() + + def calculate_result_code(self): + return self.arg.result() + + def generate_result_code(self, code): + if self.type.typeobj_is_available(): + code.use_utility_code(type_test_utility_code) + code.putln( + "if (!__Pyx_TypeTest(%s, %s)) %s" % ( + self.arg.py_result(), + self.type.typeptr_cname, + code.error_goto(self.pos))) + else: + error(self.pos, "Cannot test type of extern C class " + "without type object name specification") + + def generate_post_assignment_code(self, code): + self.arg.generate_post_assignment_code(code) + + +class CoerceToPyTypeNode(CoercionNode): + # This node is used to convert a C data type + # to a Python object. + + def __init__(self, arg, env): + CoercionNode.__init__(self, arg) + self.type = py_object_type + self.gil_check(env) + self.is_temp = 1 + if not arg.type.to_py_function: + error(arg.pos, + "Cannot convert '%s' to Python object" % arg.type) + + gil_message = "Converting to Python object" + + def generate_result_code(self, code): + function = self.arg.type.to_py_function + result = self.result() + code.putln('%s = %s(%s); if (!%s) %s' % ( + result, + function, + self.arg.result(), + result, + code.error_goto(self.pos))) + + +class CoerceFromPyTypeNode(CoercionNode): + # This node is used to convert a Python object + # to a C data type. + + def __init__(self, result_type, arg, env): + CoercionNode.__init__(self, arg) + self.type = result_type + self.is_temp = 1 + if not result_type.from_py_function: + error(arg.pos, + "Cannot convert Python object to '%s'" % result_type) + if self.type.is_string and self.arg.is_ephemeral(): + error(arg.pos, + "Obtaining char * from temporary Python value") + + def generate_result_code(self, code): + function = self.type.from_py_function + operand = self.arg.py_result() + rhs = "%s(%s)" % (function, operand) + if self.type.is_enum: + rhs = typecast(self.type, c_long_type, rhs) + result = self.result() + if self.type.is_string: + err_code = "!%s" % result + else: + err_code = "PyErr_Occurred()" + code.putln('%s = %s; if (%s) %s' % ( + result, + rhs, + err_code, + code.error_goto(self.pos))) + + +class CoerceToBooleanNode(CoercionNode): + # This node is used when a result needs to be used + # in a boolean context. + + def __init__(self, arg, env): + CoercionNode.__init__(self, arg) + self.type = PyrexTypes.c_int_type + if arg.type.is_pyobject: + if env.nogil: + self.gil_error() + self.is_temp = 1 + + gil_message = "Truth-testing Python object" + + def check_const(self): + if self.is_temp: + self.not_const() + self.arg.check_const() + + def calculate_result_code(self): + return "(%s != 0)" % self.arg.result() + + def generate_result_code(self, code): + if self.arg.type.is_pyobject: + result = self.result() + code.putln( + "%s = PyObject_IsTrue(%s); if (%s < 0) %s" % ( + result, + self.arg.py_result(), + result, + code.error_goto(self.pos))) + + +class CoerceToTempNode(CoercionNode): + # This node is used to force the result of another node + # to be stored in a temporary. It is only used if the + # argument node's result is not already in a temporary. + + def __init__(self, arg, env): + CoercionNode.__init__(self, arg) + self.type = self.arg.type + self.is_temp = 1 + if self.type.is_pyobject: + self.gil_check(env) + self.result_ctype = py_object_type + + gil_message = "Creating temporary Python reference" + + + def generate_result_code(self, code): + #self.arg.generate_evaluation_code(code) # Already done + # by generic generate_subexpr_evaluation_code! + code.putln("%s = %s;" % ( + self.result(), self.arg.result_as(self.ctype()))) + if self.type.is_pyobject: + code.put_incref(self.py_result()) + + +class CloneNode(CoercionNode): + # This node is employed when the result of another node needs + # to be used multiple times. The argument node's result must + # be in a temporary. This node "borrows" the result from the + # argument node, and does not generate any evaluation or + # disposal code for it. The original owner of the argument + # node is responsible for doing those things. + + subexprs = [] # Arg is not considered a subexpr + + def __init__(self, arg): + CoercionNode.__init__(self, arg) + self.type = arg.type + self.result_ctype = arg.result_ctype + + def calculate_result_code(self): + return self.arg.result() + + def generate_evaluation_code(self, code): + pass + + def generate_result_code(self, code): + pass + +#------------------------------------------------------------------------------------ +# +# Runtime support code +# +#------------------------------------------------------------------------------------ + +get_name_utility_code = [ +""" +static PyObject *__Pyx_GetName(PyObject *dict, char *name); /*proto*/ +""",""" +static PyObject *__Pyx_GetName(PyObject *dict, char *name) { + PyObject *result; + result = PyObject_GetAttrString(dict, name); + if (!result) + PyErr_SetString(PyExc_NameError, name); + return result; +} +"""] + +get_name_interned_utility_code = [ +""" +static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name); /*proto*/ +""",""" +static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name) { + PyObject *result; + result = PyObject_GetAttr(dict, name); + if (!result) + PyErr_SetObject(PyExc_NameError, name); + return result; +} +"""] + +#------------------------------------------------------------------------------------ + +import_utility_code = [ +""" +static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list); /*proto*/ +""",""" +static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list) { + PyObject *__import__ = 0; + PyObject *empty_list = 0; + PyObject *module = 0; + PyObject *global_dict = 0; + PyObject *empty_dict = 0; + PyObject *list; + __import__ = PyObject_GetAttrString(%(BUILTINS)s, "__import__"); + if (!__import__) + goto bad; + if (from_list) + list = from_list; + else { + empty_list = PyList_New(0); + if (!empty_list) + goto bad; + list = empty_list; + } + global_dict = PyModule_GetDict(%(GLOBALS)s); + if (!global_dict) + goto bad; + empty_dict = PyDict_New(); + if (!empty_dict) + goto bad; + module = PyObject_CallFunction(__import__, "OOOO", + name, global_dict, empty_dict, list); +bad: + Py_XDECREF(empty_list); + Py_XDECREF(__import__); + Py_XDECREF(empty_dict); + return module; +} +""" % { + "BUILTINS": Naming.builtins_cname, + "GLOBALS": Naming.module_cname, +}] + +#------------------------------------------------------------------------------------ +# +#get_exception_utility_code = [ +#""" +#static PyObject *__Pyx_GetExcValue(void); /*proto*/ +#""",""" +#static PyObject *__Pyx_GetExcValue(void) { +# PyObject *type = 0, *value = 0, *tb = 0; +# PyObject *result = 0; +# PyThreadState *tstate = PyThreadState_Get(); +# PyErr_Fetch(&type, &value, &tb); +# PyErr_NormalizeException(&type, &value, &tb); +# if (PyErr_Occurred()) +# goto bad; +# if (!value) { +# value = Py_None; +# Py_INCREF(value); +# } +# Py_XDECREF(tstate->exc_type); +# Py_XDECREF(tstate->exc_value); +# Py_XDECREF(tstate->exc_traceback); +# tstate->exc_type = type; +# tstate->exc_value = value; +# tstate->exc_traceback = tb; +# result = value; +# Py_XINCREF(result); +# type = 0; +# value = 0; +# tb = 0; +#bad: +# Py_XDECREF(type); +# Py_XDECREF(value); +# Py_XDECREF(tb); +# return result; +#} +#"""] +# +#------------------------------------------------------------------------------------ + +unpacking_utility_code = [ +""" +static PyObject *__Pyx_UnpackItem(PyObject *); /*proto*/ +static int __Pyx_EndUnpack(PyObject *); /*proto*/ +""",""" +static void __Pyx_UnpackError(void) { + PyErr_SetString(PyExc_ValueError, "unpack sequence of wrong size"); +} + +static PyObject *__Pyx_UnpackItem(PyObject *iter) { + PyObject *item; + if (!(item = PyIter_Next(iter))) { + if (!PyErr_Occurred()) + __Pyx_UnpackError(); + } + return item; +} + +static int __Pyx_EndUnpack(PyObject *iter) { + PyObject *item; + if ((item = PyIter_Next(iter))) { + Py_DECREF(item); + __Pyx_UnpackError(); + return -1; + } + else if (!PyErr_Occurred()) + return 0; + else + return -1; +} +"""] + +#------------------------------------------------------------------------------------ + +type_test_utility_code = [ +""" +static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type); /*proto*/ +""",""" +static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) { + if (!type) { + PyErr_Format(PyExc_SystemError, "Missing type object"); + return 0; + } + if (obj == Py_None || PyObject_TypeCheck(obj, type)) + return 1; + PyErr_Format(PyExc_TypeError, "Cannot convert %s to %s", + obj->ob_type->tp_name, type->tp_name); + return 0; +} +"""] + +#------------------------------------------------------------------------------------ + +create_class_utility_code = [ +""" +static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name, char *modname); /*proto*/ +""",""" +static PyObject *__Pyx_CreateClass( + PyObject *bases, PyObject *dict, PyObject *name, char *modname) +{ + PyObject *py_modname; + PyObject *result = 0; + + py_modname = PyString_FromString(modname); + if (!py_modname) + goto bad; + if (PyDict_SetItemString(dict, "__module__", py_modname) < 0) + goto bad; + result = PyClass_New(bases, dict, name); +bad: + Py_XDECREF(py_modname); + return result; +} +"""] + +#------------------------------------------------------------------------------------ + +getitem_int_utility_code = [ +""" +static PyObject *__Pyx_GetItemInt(PyObject *o, Py_ssize_t i); /*proto*/ +""",""" +static PyObject *__Pyx_GetItemInt(PyObject *o, Py_ssize_t i) { + PyTypeObject *t = o->ob_type; + PyObject *r; + if (t->tp_as_sequence && t->tp_as_sequence->sq_item) + r = PySequence_GetItem(o, i); + else { + PyObject *j = PyInt_FromLong(i); + if (!j) + return 0; + r = PyObject_GetItem(o, j); + Py_DECREF(j); + } + return r; +} +"""] + +#------------------------------------------------------------------------------------ + +setitem_int_utility_code = [ +""" +static int __Pyx_SetItemInt(PyObject *o, Py_ssize_t i, PyObject *v); /*proto*/ +""",""" +static int __Pyx_SetItemInt(PyObject *o, Py_ssize_t i, PyObject *v) { + PyTypeObject *t = o->ob_type; + int r; + if (t->tp_as_sequence && t->tp_as_sequence->sq_item) + r = PySequence_SetItem(o, i, v); + else { + PyObject *j = PyInt_FromLong(i); + if (!j) + return -1; + r = PyObject_SetItem(o, j, v); + Py_DECREF(j); + } + return r; +} +"""] |
