Commit ccda96cb authored by Robert Bradshaw's avatar Robert Bradshaw

Allow nested C++ types.

parent da38498e
...@@ -3062,6 +3062,10 @@ class AttributeNode(ExprNode): ...@@ -3062,6 +3062,10 @@ class AttributeNode(ExprNode):
module_scope = self.obj.analyse_as_module(env) module_scope = self.obj.analyse_as_module(env)
if module_scope: if module_scope:
return module_scope.lookup_type(self.attribute) 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 return None
def analyse_as_extension_type(self, env): def analyse_as_extension_type(self, env):
......
...@@ -779,6 +779,27 @@ class CSimpleBaseTypeNode(CBaseTypeNode): ...@@ -779,6 +779,27 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
else: else:
return PyrexTypes.error_type 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): class TemplatedTypeNode(CBaseTypeNode):
# After parsing: # After parsing:
# positional_args [ExprNode] List of positional arguments # positional_args [ExprNode] List of positional arguments
......
...@@ -1869,11 +1869,15 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None): ...@@ -1869,11 +1869,15 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
complex = complex, longness = longness, complex = complex, longness = longness,
is_self_arg = self_flag, templates = templates) is_self_arg = self_flag, templates = templates)
if s.sy == '[': if s.sy == '[':
return p_buffer_or_template(s, type_node, templates) type_node = p_buffer_or_template(s, type_node, templates)
else:
return type_node 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): def p_buffer_or_template(s, base_type_node, templates):
# s.sy == '[' # s.sy == '['
...@@ -2729,7 +2733,10 @@ def p_cpp_class_definition(s, pos, ctx): ...@@ -2729,7 +2733,10 @@ def p_cpp_class_definition(s, pos, ctx):
body_ctx = Ctx(visibility = ctx.visibility) body_ctx = Ctx(visibility = ctx.visibility)
body_ctx.templates = templates body_ctx.templates = templates
while s.sy != 'DEDENT': 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( attributes.append(
p_c_func_or_var_declaration(s, s.position(), body_ctx)) p_c_func_or_var_declaration(s, s.position(), body_ctx))
else: else:
......
...@@ -1821,6 +1821,7 @@ class CppClassType(CType): ...@@ -1821,6 +1821,7 @@ class CppClassType(CType):
is_cpp_class = 1 is_cpp_class = 1
has_attributes = 1 has_attributes = 1
exception_check = True exception_check = True
namespace = None
def __init__(self, name, scope, cname, base_classes, templates = None, template_type = None): def __init__(self, name, scope, cname, base_classes, templates = None, template_type = None):
self.name = name self.name = name
...@@ -1843,18 +1844,22 @@ class CppClassType(CType): ...@@ -1843,18 +1844,22 @@ class CppClassType(CType):
return self.specialize(dict(zip(self.templates, template_values))) return self.specialize(dict(zip(self.templates, template_values)))
def specialize(self, values): def specialize(self, values):
if not self.templates: if not self.templates and not self.namespace:
return self return self
if self.templates is None:
self.templates = []
key = tuple(values.items()) key = tuple(values.items())
if key in self.specializations: if key in self.specializations:
return self.specializations[key] return self.specializations[key]
template_values = [t.specialize(values) for t in self.templates] template_values = [t.specialize(values) for t in self.templates]
specialized = self.specializations[key] = \ specialized = self.specializations[key] = \
CppClassType(self.name, None, self.cname, [], template_values, template_type=self) CppClassType(self.name, None, self.cname, [], template_values, template_type=self)
# Need to do these *after* self.specializations[key] is set to # Need to do these *after* self.specializations[key] is set
# avoid infinite recursion on circular references. # to avoid infinite recursion on circular references.
specialized.base_classes = [b.specialize(values) for b in self.base_classes] specialized.base_classes = [b.specialize(values) for b in self.base_classes]
specialized.scope = self.scope.specialize(values) specialized.scope = self.scope.specialize(values)
if self.namespace is not None:
specialized.namespace = self.namespace.specialize(values)
return specialized return specialized
def declaration_code(self, entity_code, for_display = 0, dll_linkage = None, pyrex = 0): def declaration_code(self, entity_code, for_display = 0, dll_linkage = None, pyrex = 0):
...@@ -1866,7 +1871,10 @@ class CppClassType(CType): ...@@ -1866,7 +1871,10 @@ class CppClassType(CType):
if for_display or pyrex: if for_display or pyrex:
name = self.name name = self.name
else: 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) return "%s%s %s" % (name, templates, entity_code)
def is_subclass(self, other_type): def is_subclass(self, other_type):
......
...@@ -219,6 +219,7 @@ class Scope(object): ...@@ -219,6 +219,7 @@ class Scope(object):
is_py_class_scope = 0 is_py_class_scope = 0
is_c_class_scope = 0 is_c_class_scope = 0
is_cpp_class_scope = 0
is_module_scope = 0 is_module_scope = 0
scope_prefix = "" scope_prefix = ""
in_cinclude = 0 in_cinclude = 0
...@@ -399,6 +400,44 @@ class Scope(object): ...@@ -399,6 +400,44 @@ class Scope(object):
self.check_for_illegal_incomplete_ctypedef(typedef_flag, pos) self.check_for_illegal_incomplete_ctypedef(typedef_flag, pos)
return entry 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): def check_previous_typedef_flag(self, entry, typedef_flag, pos):
if typedef_flag != entry.type.typedef_flag: if typedef_flag != entry.type.typedef_flag:
error(pos, "'%s' previously declared using '%s'" % ( error(pos, "'%s' previously declared using '%s'" % (
...@@ -1004,42 +1043,6 @@ class ModuleScope(Scope): ...@@ -1004,42 +1043,6 @@ class ModuleScope(Scope):
if typedef_flag and not self.in_cinclude: if typedef_flag and not self.in_cinclude:
error(pos, "Forward-referenced type must use 'cdef', not 'ctypedef'") 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): def allocate_vtable_names(self, entry):
# If extension type has a vtable, allocate vtable struct and # If extension type has a vtable, allocate vtable struct and
# slot names for it. # slot names for it.
...@@ -1510,13 +1513,15 @@ class CClassScope(ClassScope): ...@@ -1510,13 +1513,15 @@ class CClassScope(ClassScope):
class CppClassScope(Scope): class CppClassScope(Scope):
# Namespace of a C++ class. # Namespace of a C++ class.
inherited_var_entries = []
is_cpp_class_scope = 1
default_constructor = None default_constructor = None
def __init__(self, name, outer_scope): def __init__(self, name, outer_scope):
Scope.__init__(self, name, outer_scope, None) Scope.__init__(self, name, outer_scope, None)
self.directives = outer_scope.directives self.directives = outer_scope.directives
self.inherited_var_entries = []
def declare_var(self, name, type, pos, def declare_var(self, name, type, pos,
cname = None, visibility = 'extern', is_cdef = 0, allow_pyobject = 0): cname = None, visibility = 'extern', is_cdef = 0, allow_pyobject = 0):
...@@ -1598,11 +1603,17 @@ class CppClassScope(Scope): ...@@ -1598,11 +1603,17 @@ class CppClassScope(Scope):
def specialize(self, values): def specialize(self, values):
scope = CppClassScope(self.name, self.outer_scope) scope = CppClassScope(self.name, self.outer_scope)
for entry in self.entries.values(): for entry in self.entries.values():
scope.declare_var(entry.name, if entry.is_type:
entry.type.specialize(values), scope.declare_type(entry.name,
entry.pos, entry.type.specialize(values),
entry.cname, entry.pos,
entry.visibility) entry.cname)
else:
scope.declare_var(entry.name,
entry.type.specialize(values),
entry.pos,
entry.cname,
entry.visibility)
return scope return scope
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment