Skip to content

Commit

Permalink
Add anonymous struct and union
Browse files Browse the repository at this point in the history
  • Loading branch information
rui314 committed Sep 30, 2020
1 parent 9d5a1cf commit ad378f1
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 8 deletions.
62 changes: 54 additions & 8 deletions parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -2072,6 +2072,18 @@ static void struct_members(Token **rest, Token *tok, Type *ty) {
Type *basety = typespec(&tok, tok, &attr);
bool first = true;

// Anonymous struct member
if ((basety->kind == TY_STRUCT || basety->kind == TY_UNION) &&
consume(&tok, tok, ";")) {
Member *mem = calloc(1, sizeof(Member));
mem->ty = basety;
mem->idx = idx++;
mem->align = attr.align ? attr.align : mem->ty->align;
cur = cur->next = mem;
continue;
}

// Regular struct members
while (!consume(&tok, tok, ";")) {
if (!first)
tok = skip(tok, ",");
Expand Down Expand Up @@ -2206,21 +2218,55 @@ static Type *union_decl(Token **rest, Token *tok) {
return ty;
}

// Find a struct member by name.
static Member *get_struct_member(Type *ty, Token *tok) {
for (Member *mem = ty->members; mem; mem = mem->next)
for (Member *mem = ty->members; mem; mem = mem->next) {
// Anonymous struct member
if ((mem->ty->kind == TY_STRUCT || mem->ty->kind == TY_UNION) &&
!mem->name) {
if (get_struct_member(mem->ty, tok))
return mem;
continue;
}

// Regular struct member
if (mem->name->len == tok->len &&
!strncmp(mem->name->loc, tok->loc, tok->len))
return mem;
error_tok(tok, "no such member");
}
return NULL;
}

static Node *struct_ref(Node *lhs, Token *tok) {
add_type(lhs);
if (lhs->ty->kind != TY_STRUCT && lhs->ty->kind != TY_UNION)
error_tok(lhs->tok, "not a struct nor a union");
// Create a node representing a struct member access, such as foo.bar
// where foo is a struct and bar is a member name.
//
// C has a feature called "anonymous struct" which allows a struct to
// have another unnamed struct as a member like this:
//
// struct { struct { int a; }; int b; } x;
//
// The members of an anonymous struct belong to the outer struct's
// member namespace. Therefore, in the above example, you can access
// member "a" of the anonymous struct as "x.a".
//
// This function takes care of anonymous structs.
static Node *struct_ref(Node *node, Token *tok) {
add_type(node);
if (node->ty->kind != TY_STRUCT && node->ty->kind != TY_UNION)
error_tok(node->tok, "not a struct nor a union");

Type *ty = node->ty;

Node *node = new_unary(ND_MEMBER, lhs, tok);
node->member = get_struct_member(lhs->ty, tok);
for (;;) {
Member *mem = get_struct_member(ty, tok);
if (!mem)
error_tok(tok, "no such member");
node = new_unary(ND_MEMBER, node, tok);
node->member = mem;
if (mem->name)
break;
ty = mem->ty;
}
return node;
}

Expand Down
8 changes: 8 additions & 0 deletions test/union.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ int main() {
ASSERT(3, ({ union {int a,b;} x,y; x.a=3; y.a=5; y=x; y.a; }));
ASSERT(3, ({ union {struct {int a,b;} c;} x,y; x.c.b=3; y.c.b=5; y=x; y.c.b; }));

ASSERT(0xef, ({ union { struct { unsigned char a,b,c,d; }; long e; } x; x.e=0xdeadbeef; x.a; }));
ASSERT(0xbe, ({ union { struct { unsigned char a,b,c,d; }; long e; } x; x.e=0xdeadbeef; x.b; }));
ASSERT(0xad, ({ union { struct { unsigned char a,b,c,d; }; long e; } x; x.e=0xdeadbeef; x.c; }));
ASSERT(0xde, ({ union { struct { unsigned char a,b,c,d; }; long e; } x; x.e=0xdeadbeef; x.d; }));

ASSERT(3, ({struct { union { int a,b; }; union { int c,d; }; } x; x.a=3; x.b; }));
ASSERT(5, ({struct { union { int a,b; }; union { int c,d; }; } x; x.d=5; x.c; }));

printf("OK\n");
return 0;
}

0 comments on commit ad378f1

Please sign in to comment.