Add anonymous struct and union

This commit is contained in:
Rui Ueyama 2020-08-23 11:48:16 +09:00
parent 9c36dd727c
commit c3075b3030
2 changed files with 62 additions and 8 deletions

62
parse.c
View File

@ -2051,6 +2051,18 @@ static void struct_members(Token **rest, Token *tok, Type *ty) {
Type *basety = declspec(&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, ",");
@ -2186,21 +2198,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");
Node *node = new_unary(ND_MEMBER, lhs, tok);
node->member = get_struct_member(lhs->ty, tok);
Type *ty = node->ty;
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;
}

View File

@ -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;
}