diff options
| author | Michele Calgaro <michele.calgaro@yahoo.it> | 2024-09-24 22:32:48 +0900 |
|---|---|---|
| committer | Michele Calgaro <michele.calgaro@yahoo.it> | 2024-09-24 22:32:48 +0900 |
| commit | 6d225e65e158e6547cb863d5558e955b3355449d (patch) | |
| tree | 5fa5a62db33edc47242886a852b82336d53bf686 /debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/ExprNodes.py | |
| parent | 2950a7d84514b9b2d9916afb06ff05fdabab3be7 (diff) | |
| download | extra-dependencies-6d225e65e158e6547cb863d5558e955b3355449d.tar.gz extra-dependencies-6d225e65e158e6547cb863d5558e955b3355449d.zip | |
Removed pyrex, which is no longer required
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
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, 0 insertions, 3954 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 deleted file mode 100644 index c2848286..00000000 --- a/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/ExprNodes.py +++ /dev/null @@ -1,3954 +0,0 @@ -# -# 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; -} -"""] |
