ExprNodes.py :  » Development » Pyrex » Pyrex-0.9.9 » Pyrex » Compiler » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » Pyrex 
Pyrex » Pyrex 0.9.9 » Pyrex » Compiler » ExprNodes.py
#
#   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;
}
"""]
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.