#
#   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;
}
"""]
