Skip to content

Commit

Permalink
Allow tagged types to be redefined in nested scopes.
Browse files Browse the repository at this point in the history
  • Loading branch information
windelbouwman committed Jun 6, 2020
1 parent 5338bb0 commit 2ac2d76
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 48 deletions.
9 changes: 7 additions & 2 deletions ppci/lang/c/nodes/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,12 @@ def __repr__(self):
return "Pointer-type"


class EnumType(CType):
class TaggedType(CType):
""" A Type which can be inserted in the tag namespace.
"""


class EnumType(TaggedType):
""" Enum type """

def __init__(self, constants=None):
Expand All @@ -200,7 +205,7 @@ def __repr__(self):
return "Enum-type"


class StructOrUnionType(CType):
class StructOrUnionType(TaggedType):
""" Common base for struct and union types """

def __init__(self, tag=None, fields=None):
Expand Down
13 changes: 10 additions & 3 deletions ppci/lang/c/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,13 +299,17 @@ def parse_struct_or_union(self):
keyword.loc,
)

is_definition = (self.peek == "{") or (
(tag is not None) and self.peek == ";"
)

if self.peek == "{":
fields = self.parse_struct_fields()
else:
fields = None

return self.semantics.on_struct_or_union(
keyword.val, tag, fields, keyword.loc
keyword.val, tag, is_definition, fields, keyword.loc
)

def parse_struct_fields(self):
Expand Down Expand Up @@ -363,7 +367,11 @@ def parse_enum(self):
keyword.loc,
)

ctyp = self.semantics.on_enum(tag, keyword.loc)
is_definition = (self.peek == "{") or (
(tag is not None) and self.peek == ";"
)

ctyp = self.semantics.on_enum(tag, is_definition, keyword.loc)

# If we have a body, either after tag or directly, parse it:
if self.peek == "{":
Expand All @@ -374,7 +382,6 @@ def parse_enum(self):
def parse_enum_fields(self, ctyp, location):
""" Parse enum declarations """
self.consume("{")
self.semantics.enter_enum_values(ctyp, location)
constants = []
while self.peek != "}":
name = self.consume("ID")
Expand Down
38 changes: 22 additions & 16 deletions ppci/lang/c/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,11 @@ class Scope:

def __init__(self, parent=None):
self.parent = parent

# Different namespaces in this scope:
self.var_map = OrderedDict()
self.tags = {}
self.labels = {}
self._tags = OrderedDict()
self.labels = OrderedDict()

def get_defined_names(self):
""" Get all defined symbols in this scope, and scopes above. """
Expand All @@ -160,7 +161,7 @@ def is_defined(self, name: str, all_scopes=True):
def is_definition(self, name: str):
""" Check if this name is a definition. """
if self.is_defined(name, all_scopes=False):
sym = self.get(name)
sym = self.get_identifier(name)
return sym.declaration.is_definition()
else:
return False
Expand Down Expand Up @@ -190,29 +191,34 @@ def get_declarations(self):
r.append(d)
return r

def has_tag(self, name: str):
if self.parent:
# TODO: make tags a flat space?
def has_tag(self, name: str, all_scopes=True):
""" Check the tag namespace for the given name. """
if name in self._tags:
return True
elif self.parent and all_scopes:
return self.parent.has_tag(name)
return name in self.tags
else:
return False

def get_tag(self, name: str):
def get_tag(self, name: str, all_scopes=True):
""" Get a struct by tag """
if self.parent:
if name in self._tags:
return self._tags[name]
elif self.parent and all_scopes:
return self.parent.get_tag(name)
return self.tags[name]
else:
raise KeyError(name)

def add_tag(self, name: str, o):
if self.parent:
return self.parent.add_tag(name, o)
self.tags[name] = o
def add_tag(self, name: str, item):
""" Add the given item to the tag namespace. """
self._tags[name] = item

def get(self, name: str):
def get_identifier(self, name: str):
""" Get the symbol with the given name """
if name in self.var_map:
return self.var_map[name]
elif self.parent:
return self.parent.get(name)
return self.parent.get_identifier(name)
else:
raise KeyError(name)

Expand Down
79 changes: 52 additions & 27 deletions ppci/lang/c/semantics.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def on_typedef(self, typ, name, modifiers, location):
typ = self.apply_type_modifiers(modifiers, typ)
declaration = declarations.Typedef(typ, name, location)
if self.scope.is_defined(name, all_scopes=False):
sym = self.scope.get(name)
sym = self.scope.get_identifier(name)
self.check_redeclaration_type(sym, declaration)
sym.add_redeclaration(declaration)
else:
Expand All @@ -275,7 +275,7 @@ def register_declaration(self, declaration):
# Check if the declared name is already defined:
if self.scope.is_defined(declaration.name, all_scopes=False):
# Get the already declared name and figure out what now!
sym = self.scope.get(declaration.name)
sym = self.scope.get_identifier(declaration.name)
self.check_redeclaration_type(sym, declaration)

# re-declarations are only allowed on top level
Expand Down Expand Up @@ -345,7 +345,7 @@ def on_basic_type(self, type_specifiers, location):
def on_typename(self, name, location):
""" Handle the case when a typedef is refered """
# Lookup typedef
typedef = self.scope.get(name).declaration
typedef = self.scope.get_identifier(name).declaration
assert isinstance(typedef, declarations.Typedef)
ctyp = typedef.typ
return ctyp # types.IdentifierType(name, ctyp)
Expand All @@ -354,30 +354,40 @@ def on_type(self, typ, modifiers, location):
""" Called when a type itself is described """
return self.apply_type_modifiers(modifiers, typ)

def on_struct_or_union(self, kind, tag, fields, location):
""" Handle struct or union definition """
def on_struct_or_union(self, kind, tag, is_definition, fields, location):
""" Handle struct or union definition.
A definition of a struct occurs when we have:
struct S;
struct S { int f; };
struct { int f; };
"""
# Layout the struct here!
assert tag or fields

mp = {"struct": types.StructType, "union": types.UnionType}
klass = mp[kind]

if tag:
# Get the tag, or register it
if self.scope.has_tag(tag):
ctyp = self.scope.get_tag(tag)
if is_definition:
# Struct definition.
if tag:
ctyp = self.define_tag_type(tag, klass, location)
else:
ctyp = klass()

if fields:
ctyp.fields = fields
else:
assert tag
# Struct usage / declaration:
if self.scope.has_tag(tag, all_scopes=True):
ctyp = self.scope.get_tag(tag, all_scopes=True)
if not isinstance(ctyp, klass):
self.error("Wrong tag kind", location)
else:
ctyp = klass()
self.scope.add_tag(tag, ctyp)
else:
ctyp = klass()

if fields:
if ctyp.complete:
self.error("Multiple definitions", location)
ctyp.fields = fields
return ctyp

def on_field_def(
Expand All @@ -400,24 +410,24 @@ def on_field_def(
field = types.Field(ctyp, name, bitsize)
return field

def on_enum(self, tag, location):
def on_enum(self, tag, is_definition, location):
""" Handle enum declaration """
if tag:
if self.scope.has_tag(tag):
ctyp = self.scope.get_tag(tag)
if is_definition:
if tag:
ctyp = self.define_tag_type(tag, types.EnumType, location)
else:
ctyp = types.EnumType()
else:
assert tag
if self.scope.has_tag(tag, all_scopes=True):
ctyp = self.scope.get_tag(tag, all_scopes=True)
if not isinstance(ctyp, types.EnumType):
self.error("This tag does not refer to an enum", location)
else:
ctyp = types.EnumType()
self.scope.add_tag(tag, ctyp)
else:
ctyp = types.EnumType()
return ctyp

def enter_enum_values(self, ctyp, location):
if ctyp.complete:
self.error("Enum defined multiple times", location)

def on_enum_value(self, ctyp, name, value, location):
""" Handle a single enum value definition """
if value:
Expand All @@ -428,7 +438,7 @@ def on_enum_value(self, ctyp, name, value, location):
)

if self.scope.is_defined(name, all_scopes=False):
sym = self.scope.get(declaration.name)
sym = self.scope.get_identifier(declaration.name)
self.invalid_redeclaration(sym, declaration)
else:
self.scope.insert(declaration)
Expand All @@ -443,6 +453,21 @@ def exit_enum_values(self, ctyp, constants, location):
# max(enum_values)
# TODO: determine storage type!

def define_tag_type(self, tag: str, klass: types.TaggedType, location):
""" Get or create a tagged type with the given tag kind. """
if self.scope.has_tag(tag, all_scopes=False):
ctyp = self.scope.get_tag(tag, all_scopes=False)
if not isinstance(ctyp, klass):
self.error("Wrong tag kind", location)

if ctyp.complete:
self.error("Multiple definitions", location)

else:
ctyp = klass()
self.scope.add_tag(tag, ctyp)
return ctyp

@staticmethod
def on_type_qualifiers(type_qualifiers, ctyp):
""" Handle type qualifiers """
Expand Down Expand Up @@ -922,7 +947,7 @@ def on_variable_access(self, name, location):
location,
hints=hints,
)
symbol = self.scope.get(name)
symbol = self.scope.get_identifier(name)
declaration = symbol.declaration
typ = declaration.typ

Expand Down
21 changes: 21 additions & 0 deletions test/lang/c/test_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,27 @@ def test_struct(self):
"""
self.do(src)

def test_tag_scoping(self):
src = """
void f(int n) {
struct S { int a; } s;
union U { int a; } u;
enum E { E1, E2 } e;
if (n == 10) {
struct S { int b; } s;
s.b = 1;
union U { int b; } u;
u.b = 1;
enum E { E3, E4 } e;
e = E3;
}
s.a = 2;
u.a = 2;
e = E1;
}
"""
self.do(src)

def test_struct_copy(self):
""" Test struct behavior when copied around. """
src = """
Expand Down

0 comments on commit 2ac2d76

Please sign in to comment.