summaryrefslogtreecommitdiffstats
path: root/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/ExprNodes.py
diff options
context:
space:
mode:
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.py3954
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;
-}
-"""]