Allow nested C++ types.
authorRobert Bradshaw <robertwb@math.washington.edu>
Sun, 21 Feb 2010 04:17:58 +0000 (20:17 -0800)
committerRobert Bradshaw <robertwb@math.washington.edu>
Sun, 21 Feb 2010 04:17:58 +0000 (20:17 -0800)
Cython/Compiler/ExprNodes.py
Cython/Compiler/Nodes.py
Cython/Compiler/Parsing.py
Cython/Compiler/PyrexTypes.py
Cython/Compiler/Symtab.py

index c7de6d077fea93760063cc6b1f5d6b99a3c85af3..d9e2117692969fe15679ee66a5c5b7e8327ef75c 100755 (executable)
@@ -3062,6 +3062,10 @@ class AttributeNode(ExprNode):
         module_scope = self.obj.analyse_as_module(env)
         if module_scope:
             return module_scope.lookup_type(self.attribute)
+        if not isinstance(self.obj, (UnicodeNode, StringNode, BytesNode)):
+            base_type = self.obj.analyse_as_type(env)
+            if base_type and hasattr(base_type, 'scope'):
+                return base_type.scope.lookup_type(self.attribute)
         return None
     
     def analyse_as_extension_type(self, env):
index 4302b999a7683e345cb07892e0411141cc6a6c27..1eb531e0079e8c38fff97d8f526db0dbd45e36c1 100644 (file)
@@ -779,6 +779,27 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
         else:
             return PyrexTypes.error_type
 
+class CNestedBaseTypeNode(CBaseTypeNode):
+    # For C++ classes that live inside other C++ classes. 
+
+    # name             string
+    # base_type        CBaseTypeNode
+
+    child_attrs = ['base_type']
+
+    def analyse(self, env, could_be_name = None):
+        base_type = self.base_type.analyse(env)
+        if base_type is PyrexTypes.error_type:
+            return PyrexTypes.error_type
+        if not base_type.is_cpp_class:
+            error(self.pos, "'%s' is not a valid type scope" % base_type)
+            return PyrexTypes.error_type
+        type_entry = base_type.scope.lookup_here(self.name)
+        if not type_entry or not type_entry.is_type:
+            error(self.pos, "'%s.%s' is not a type identifier" % (base_type, self.name))
+            return PyrexTypes.error_type
+        return type_entry.type
+
 class TemplatedTypeNode(CBaseTypeNode):
     #  After parsing:
     #  positional_args  [ExprNode]        List of positional arguments
index 8ca741baa4243a91a9e71cf7f4b08c1c0dcf390b..f35498f1e259bd259fd5e80379b28134b6e9165b 100644 (file)
@@ -1869,11 +1869,15 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
         complex = complex, longness = longness, 
         is_self_arg = self_flag, templates = templates)
 
-
     if s.sy == '[':
-        return p_buffer_or_template(s, type_node, templates)
-    else:
-        return type_node
+        type_node = p_buffer_or_template(s, type_node, templates)
+    
+    if s.sy == '.':
+        s.next()
+        name = p_ident(s)
+        type_node = Nodes.CNestedBaseTypeNode(pos, base_type = type_node, name = name)
+    
+    return type_node
 
 def p_buffer_or_template(s, base_type_node, templates):
     # s.sy == '['
@@ -2729,7 +2733,10 @@ def p_cpp_class_definition(s, pos,  ctx):
         body_ctx = Ctx(visibility = ctx.visibility)
         body_ctx.templates = templates
         while s.sy != 'DEDENT':
-            if s.sy != 'pass':
+            if s.systring == 'cppclass':
+                attributes.append(
+                    p_cpp_class_definition(s, s.position(), body_ctx))
+            elif s.sy != 'pass':
                 attributes.append(
                     p_c_func_or_var_declaration(s, s.position(), body_ctx))
             else:
index 5f86de2f1e898aad28d5bec85ed8a9d5b62bca11..2e6bdc05dd5bbb464219743f3bdd5940bde439d9 100755 (executable)
@@ -1821,6 +1821,7 @@ class CppClassType(CType):
     is_cpp_class = 1
     has_attributes = 1
     exception_check = True
+    namespace = None
     
     def __init__(self, name, scope, cname, base_classes, templates = None, template_type = None):
         self.name = name
@@ -1843,18 +1844,22 @@ class CppClassType(CType):
         return self.specialize(dict(zip(self.templates, template_values)))
     
     def specialize(self, values):
-        if not self.templates:
+        if not self.templates and not self.namespace:
             return self
+        if self.templates is None:
+            self.templates = []
         key = tuple(values.items())
         if key in self.specializations:
             return self.specializations[key]
         template_values = [t.specialize(values) for t in self.templates]
         specialized = self.specializations[key] = \
             CppClassType(self.name, None, self.cname, [], template_values, template_type=self)
-        # Need to do these *after* self.specializations[key] is set to 
-        # avoid infinite recursion on circular references.
+        # Need to do these *after* self.specializations[key] is set
+        # to avoid infinite recursion on circular references.
         specialized.base_classes = [b.specialize(values) for b in self.base_classes]
         specialized.scope = self.scope.specialize(values)
+        if self.namespace is not None:
+            specialized.namespace = self.namespace.specialize(values)
         return specialized
 
     def declaration_code(self, entity_code, for_display = 0, dll_linkage = None, pyrex = 0):
@@ -1866,7 +1871,10 @@ class CppClassType(CType):
         if for_display or pyrex:
             name = self.name
         else:
-            name = self.cname
+            if self.namespace is not None:
+                name = "%s::%s" % (self.namespace.declaration_code(''), self.cname)
+            else:
+                name = self.cname
         return "%s%s %s" % (name, templates, entity_code)
 
     def is_subclass(self, other_type):
index a031e97ce7e641d638019dbc35bb76562e02aaa8..13395144e41bf4ea6f52ba400feeb55e9faf930e 100644 (file)
@@ -219,6 +219,7 @@ class Scope(object):
 
     is_py_class_scope = 0
     is_c_class_scope = 0
+    is_cpp_class_scope = 0
     is_module_scope = 0
     scope_prefix = ""
     in_cinclude = 0
@@ -399,6 +400,44 @@ class Scope(object):
             self.check_for_illegal_incomplete_ctypedef(typedef_flag, pos)
         return entry
     
+    def declare_cpp_class(self, name, scope,
+            pos, cname = None, base_classes = [],
+            visibility = 'extern', templates = None):
+        if visibility != 'extern':
+            error(pos, "C++ classes may only be extern")
+        if cname is None:
+            cname = name
+        entry = self.lookup(name)
+        if not entry:
+            type = PyrexTypes.CppClassType(
+                name, scope, cname, base_classes, templates = templates)
+            entry = self.declare_type(name, type, pos, cname,
+                visibility = visibility, defining = scope is not None)
+        else:
+            if not (entry.is_type and entry.type.is_cpp_class):
+                warning(pos, "'%s' redeclared  " % name, 0)
+            elif scope and entry.type.scope:
+                warning(pos, "'%s' already defined  (ignoring second definition)" % name, 0)
+            else:
+                if scope:
+                    entry.type.scope = scope
+                    self.type_entries.append(entry)
+        if not scope and not entry.type.scope:
+            entry.type.scope = CppClassScope(name, self)
+        if templates is not None:
+            for T in templates:
+                template_entry = entry.type.scope.declare(T.name, T.name, T, None, 'extern')
+                template_entry.is_type = 1
+        
+        def declare_inherited_attributes(entry, base_classes):
+            for base_class in base_classes:
+                declare_inherited_attributes(entry, base_class.base_classes)
+                entry.type.scope.declare_inherited_cpp_attributes(base_class.scope)                 
+        declare_inherited_attributes(entry, base_classes)
+        if self.is_cpp_class_scope:
+            entry.type.namespace = self.outer_scope.lookup(self.name).type
+        return entry
+
     def check_previous_typedef_flag(self, entry, typedef_flag, pos):
         if typedef_flag != entry.type.typedef_flag:
             error(pos, "'%s' previously declared using '%s'" % (
@@ -1004,42 +1043,6 @@ class ModuleScope(Scope):
         if typedef_flag and not self.in_cinclude:
             error(pos, "Forward-referenced type must use 'cdef', not 'ctypedef'")
     
-    def declare_cpp_class(self, name, scope,
-            pos, cname = None, base_classes = [],
-            visibility = 'extern', templates = None):
-        if visibility != 'extern':
-            error(pos, "C++ classes may only be extern")
-        if cname is None:
-            cname = name
-        entry = self.lookup(name)
-        if not entry:
-            type = PyrexTypes.CppClassType(
-                name, scope, cname, base_classes, templates = templates)
-            entry = self.declare_type(name, type, pos, cname,
-                visibility = visibility, defining = scope is not None)
-        else:
-            if not (entry.is_type and entry.type.is_cpp_class):
-                warning(pos, "'%s' redeclared  " % name, 0)
-            elif scope and entry.type.scope:
-                warning(pos, "'%s' already defined  (ignoring second definition)" % name, 0)
-            else:
-                if scope:
-                    entry.type.scope = scope
-                    self.type_entries.append(entry)
-        if not scope and not entry.type.scope:
-            entry.type.scope = CppClassScope(name, self)
-        if templates is not None:
-            for T in templates:
-                template_entry = entry.type.scope.declare(T.name, T.name, T, None, 'extern')
-                template_entry.is_type = 1
-        
-        def declare_inherited_attributes(entry, base_classes):
-            for base_class in base_classes:
-                declare_inherited_attributes(entry, base_class.base_classes)
-                entry.type.scope.declare_inherited_cpp_attributes(base_class.scope)                 
-        declare_inherited_attributes(entry, base_classes)
-        return entry
-    
     def allocate_vtable_names(self, entry):
         #  If extension type has a vtable, allocate vtable struct and
         #  slot names for it.
@@ -1510,13 +1513,15 @@ class CClassScope(ClassScope):
         
 class CppClassScope(Scope):
     #  Namespace of a C++ class.
-    inherited_var_entries = []
+    
+    is_cpp_class_scope = 1
     
     default_constructor = None
     
     def __init__(self, name, outer_scope):
         Scope.__init__(self, name, outer_scope, None)
         self.directives = outer_scope.directives
+        self.inherited_var_entries = []
 
     def declare_var(self, name, type, pos, 
             cname = None, visibility = 'extern', is_cdef = 0, allow_pyobject = 0):
@@ -1598,11 +1603,17 @@ class CppClassScope(Scope):
     def specialize(self, values):
         scope = CppClassScope(self.name, self.outer_scope)
         for entry in self.entries.values():
-            scope.declare_var(entry.name,
-                                entry.type.specialize(values),
-                                entry.pos,
-                                entry.cname,
-                                entry.visibility)
+            if entry.is_type:
+                scope.declare_type(entry.name,
+                                    entry.type.specialize(values),
+                                    entry.pos,
+                                    entry.cname)
+            else:
+                scope.declare_var(entry.name,
+                                    entry.type.specialize(values),
+                                    entry.pos,
+                                    entry.cname,
+                                    entry.visibility)
         return scope