From ccda96cb797f8437cd727fdb46e76d2929868740 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Sat, 20 Feb 2010 20:17:58 -0800 Subject: [PATCH] Allow nested C++ types. --- Cython/Compiler/ExprNodes.py | 4 ++ Cython/Compiler/Nodes.py | 21 ++++++++ Cython/Compiler/Parsing.py | 17 +++++-- Cython/Compiler/PyrexTypes.py | 16 ++++-- Cython/Compiler/Symtab.py | 95 +++++++++++++++++++---------------- 5 files changed, 102 insertions(+), 51 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index c7de6d07..d9e21176 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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): diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 4302b999..1eb531e0 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -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 diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index 8ca741ba..f35498f1 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -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: diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 5f86de2f..2e6bdc05 100755 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -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): diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index a031e97c..13395144 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -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 -- 2.26.2