big C++ mergeback
[cython.git] / Cython / Compiler / Nodes.py
index e6b00487627784e71f92e67a22e311ad6272dfba..68dd042dc756810a87bc38c581a744a6d5e77edb 100644 (file)
@@ -1,3 +1,4 @@
+
 #
 #   Pyrex - Parse tree nodes
 #
@@ -18,7 +19,7 @@ import PyrexTypes
 import TypeSlots
 from PyrexTypes import py_object_type, error_type, CFuncType
 from Symtab import ModuleScope, LocalScope, GeneratorLocalScope, \
-    StructOrUnionScope, PyClassScope, CClassScope
+    StructOrUnionScope, PyClassScope, CClassScope, CppClassScope
 from Cython.Utils import open_new_file, replace_suffix
 from Code import UtilityCode
 from StringEncoding import EncodedString, escape_byte_string, split_docstring
@@ -143,6 +144,15 @@ class Node(object):
 
     def gil_error(self, env=None):
         error(self.pos, "%s not allowed without gil" % self.gil_message)
+    
+    cpp_message = "Operation"
+    
+    def cpp_check(self, env):
+        if not env.is_cpp():
+            self.cpp_error()
+
+    def cpp_error(self):
+        error(self.pos, "%s only allowed in c++" % self.cpp_message)
 
     def clone_node(self):
         """Clone the node. This is defined as a shallow copy, except for member lists
@@ -332,6 +342,7 @@ class StatListNode(Node):
     
     def analyse_expressions(self, env):
         #print "StatListNode.analyse_expressions" ###
+        entry = env.entries.get("cpp_sum", None)
         for stat in self.stats:
             stat.analyse_expressions(env)
     
@@ -447,7 +458,19 @@ class CPtrDeclaratorNode(CDeclaratorNode):
                 "Pointer base type cannot be a Python object")
         ptr_type = PyrexTypes.c_ptr_type(base_type)
         return self.base.analyse(ptr_type, env, nonempty = nonempty)
-        
+
+class CReferenceDeclaratorNode(CDeclaratorNode):
+    # base     CDeclaratorNode
+
+    child_attrs = ["base"]
+
+    def analyse(self, base_type, env, nonempty = 0):
+        if base_type.is_pyobject:
+            error(self.pos,
+                  "Reference base type cannot be a Python object")
+        ref_type = PyrexTypes.c_ref_type(base_type)
+        return self.base.analyse(ref_type, env, nonempty = nonempty)
+
 class CArrayDeclaratorNode(CDeclaratorNode):
     # base        CDeclaratorNode
     # dimension   ExprNode
@@ -655,6 +678,9 @@ class CBaseTypeNode(Node):
     
     pass
     
+    def analyse_as_type(self, env):
+        return self.analyse(env)
+    
 class CAnalysedBaseTypeNode(Node):
     # type            type
     
@@ -714,7 +740,12 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
                         type = py_object_type
                     self.arg_name = self.name
                 else:
-                    error(self.pos, "'%s' is not a type identifier" % self.name)
+                    if self.templates:
+                        if not self.name in self.templates:
+                            error(self.pos, "'%s' is not a type identifier" % self.name)
+                        type = PyrexTypes.TemplatePlaceholderType(self.name)
+                    else:
+                        error(self.pos, "'%s' is not a type identifier" % self.name)
         if self.complex:
             if not type.is_numeric or type.is_complex:
                 error(self.pos, "can only complexify c numeric types")
@@ -725,14 +756,14 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
         else:
             return PyrexTypes.error_type
 
-class CBufferAccessTypeNode(CBaseTypeNode):
+class TemplatedTypeNode(CBaseTypeNode):
     #  After parsing:
     #  positional_args  [ExprNode]        List of positional arguments
     #  keyword_args     DictNode          Keyword arguments
     #  base_type_node   CBaseTypeNode
 
     #  After analysis:
-    #  type             PyrexType.BufferType   ...containing the right options
+    #  type             PyrexTypes.BufferType or PyrexTypes.CppClassType  ...containing the right options
 
 
     child_attrs = ["base_type_node", "positional_args",
@@ -742,24 +773,38 @@ class CBufferAccessTypeNode(CBaseTypeNode):
 
     name = None
     
-    def analyse(self, env, could_be_name = False):
-        base_type = self.base_type_node.analyse(env)
+    def analyse(self, env, could_be_name = False, base_type = None):
+        if base_type is None:
+            base_type = self.base_type_node.analyse(env)
         if base_type.is_error: return base_type
-        import Buffer
+        
+        if base_type.is_cpp_class:
+            if len(self.keyword_args.key_value_pairs) != 0:
+                error(self.pos, "c++ templates cannot take keyword arguments");
+                self.type = PyrexTypes.error_type
+            else:
+                template_types = []
+                for template_node in self.positional_args:
+                    template_types.append(template_node.analyse_as_type(env))
+                self.type = base_type.specialize_here(self.pos, template_types)
+        
+        else:
+        
+            import Buffer
 
-        options = Buffer.analyse_buffer_options(
-            self.pos,
-            env,
-            self.positional_args,
-            self.keyword_args,
-            base_type.buffer_defaults)
+            options = Buffer.analyse_buffer_options(
+                self.pos,
+                env,
+                self.positional_args,
+                self.keyword_args,
+                base_type.buffer_defaults)
 
-        if sys.version_info[0] < 3:
-            # Py 2.x enforces byte strings as keyword arguments ...
-            options = dict([ (name.encode('ASCII'), value)
-                             for name, value in options.iteritems() ])
+            if sys.version_info[0] < 3:
+                # Py 2.x enforces byte strings as keyword arguments ...
+                options = dict([ (name.encode('ASCII'), value)
+                                 for name, value in options.iteritems() ])
 
-        self.type = PyrexTypes.BufferType(base_type, **options)
+            self.type = PyrexTypes.BufferType(base_type, **options)
         return self.type
 
 class CComplexBaseTypeNode(CBaseTypeNode):
@@ -904,6 +949,46 @@ class CStructOrUnionDefNode(StatNode):
         pass
 
 
+class CppClassNode(CStructOrUnionDefNode):
+
+    #  name          string
+    #  cname         string or None
+    #  visibility    "extern"
+    #  in_pxd        boolean
+    #  attributes    [CVarDefNode] or None
+    #  entry         Entry
+    #  base_classes  [string]
+    #  templates     [string] or None
+
+    def analyse_declarations(self, env):
+        scope = None
+        if len(self.attributes) != 0:
+            scope = CppClassScope(self.name, env)
+        else:
+            self.attributes = None
+        base_class_types = []
+        for base_class_name in self.base_classes:
+            base_class_entry = env.lookup(base_class_name)
+            if base_class_entry is None:
+                error(self.pos, "'%s' not found" % base_class_name)
+            elif not base_class_entry.is_type or not base_class_entry.type.is_cpp_class:
+                error(self.pos, "'%s' is not a cpp class type" % base_class_name)
+            else:
+                base_class_types.append(base_class_entry.type)
+        if self.templates is None:
+            template_types = None
+        else:
+            template_types = [PyrexTypes.TemplatePlaceholderType(template_name) for template_name in self.templates]
+        self.entry = env.declare_cpp_class(
+            self.name, scope, self.pos,
+            self.cname, base_class_types, visibility = self.visibility, templates = template_types)
+        self.entry.is_cpp_class = 1
+        if self.attributes is not None:
+            if self.in_pxd and not env.in_cinclude:
+                self.entry.defined_in_pxd = 1
+            for attr in self.attributes:
+                attr.analyse_declarations(scope)
+
 class CEnumDefNode(StatNode):
     #  name           string or None
     #  cname          string or None
@@ -3387,8 +3472,14 @@ class DelStatNode(StatNode):
     def analyse_expressions(self, env):
         for arg in self.args:
             arg.analyse_target_expression(env, None)
-            if not arg.type.is_pyobject:
-                error(arg.pos, "Deletion of non-Python object")
+            if arg.type.is_pyobject:
+                self.gil_check(env)
+            elif arg.type.is_ptr and arg.type.base_type.is_cpp_class:
+                self.cpp_check(env)
+            elif arg.type.is_cpp_class:
+                error(arg.pos, "Deletion of non-heap C++ object")
+            else:
+                error(arg.pos, "Deletion of non-Python, non-C++ object")
             #arg.release_target_temp(env)
 
     def nogil_check(self, env):
@@ -3402,6 +3493,9 @@ class DelStatNode(StatNode):
         for arg in self.args:
             if arg.type.is_pyobject:
                 arg.generate_deletion_code(code)
+            elif arg.type.is_ptr and arg.type.base_type.is_cpp_class:
+                arg.generate_result_code(code)
+                code.putln("delete %s;" % arg.result())
             # else error reported earlier
 
     def annotate(self, code):