diff --git a/chibicc.h b/chibicc.h index 8f6e637591c8669ab50a626d3e519321398f3ad6..adbd80e263fa13ed57ec239dc8da984ca37e20dd 100644 --- a/chibicc.h +++ b/chibicc.h @@ -153,6 +153,7 @@ typedef enum { TY_FUNC, TY_ARRAY, TY_STRUCT, + TY_UNION, } TypeKind; struct Type { diff --git a/parse.c b/parse.c index 31319be81869c87e0b169f3c85adf9075aff1e57..f1c4b20398cac577c4aa848def670bfbf541d063 100644 --- a/parse.c +++ b/parse.c @@ -26,7 +26,7 @@ struct VarScope { Obj *var; }; -// Scope for struct tags +// Scope for struct or union tags typedef struct TagScope TagScope; struct TagScope { TagScope *next; @@ -67,6 +67,7 @@ static Node *relational(Token **rest, Token *tok); static Node *add(Token **rest, Token *tok); static Node *mul(Token **rest, Token *tok); static Type *struct_decl(Token **rest, Token *tok); +static Type *union_decl(Token **rest, Token *tok); static Node *postfix(Token **rest, Token *tok); static Node *unary(Token **rest, Token *tok); static Node *primary(Token **rest, Token *tok); @@ -212,6 +213,9 @@ static Type *declspec(Token **rest, Token *tok) { if (equal(tok, "struct")) return struct_decl(rest, tok->next); + if (equal(tok, "union")) + return union_decl(rest, tok->next); + error_tok(tok, "typename expected"); } @@ -297,7 +301,8 @@ static Node *declaration(Token **rest, Token *tok) { // Returns true if a given token represents a type. static bool is_typename(Token *tok) { - return equal(tok, "char") || equal(tok, "int") || equal(tok, "struct"); + return equal(tok, "char") || equal(tok, "int") || equal(tok, "struct") || + equal(tok, "union"); } // stmt = "return" expr ";" @@ -613,9 +618,9 @@ static void struct_members(Token **rest, Token *tok, Type *ty) { ty->members = head.next; } -// struct-decl = ident? "{" struct-members -static Type *struct_decl(Token **rest, Token *tok) { - // Read a struct tag. +// struct-union-decl = ident? ("{" struct-members)? +static Type *struct_union_decl(Token **rest, Token *tok) { + // Read a tag. Token *tag = NULL; if (tok->kind == TK_IDENT) { tag = tok; @@ -636,6 +641,17 @@ static Type *struct_decl(Token **rest, Token *tok) { struct_members(rest, tok->next, ty); ty->align = 1; + // Register the struct type if a name was given. + if (tag) + push_tag_scope(tag, ty); + return ty; +} + +// struct-decl = struct-union-decl +static Type *struct_decl(Token **rest, Token *tok) { + Type *ty = struct_union_decl(rest, tok); + ty->kind = TY_STRUCT; + // Assign offsets within the struct to members. int offset = 0; for (Member *mem = ty->members; mem; mem = mem->next) { @@ -647,10 +663,24 @@ static Type *struct_decl(Token **rest, Token *tok) { ty->align = mem->ty->align; } ty->size = align_to(offset, ty->align); + return ty; +} - // Register the struct type if a name was given. - if (tag) - push_tag_scope(tag, ty); +// union-decl = struct-union-decl +static Type *union_decl(Token **rest, Token *tok) { + Type *ty = struct_union_decl(rest, tok); + ty->kind = TY_UNION; + + // If union, we don't have to assign offsets because they + // are already initialized to zero. We need to compute the + // alignment and the size though. + for (Member *mem = ty->members; mem; mem = mem->next) { + if (ty->align < mem->ty->align) + ty->align = mem->ty->align; + if (ty->size < mem->ty->size) + ty->size = mem->ty->size; + } + ty->size = align_to(ty->size, ty->align); return ty; } @@ -664,8 +694,8 @@ static Member *get_struct_member(Type *ty, Token *tok) { static Node *struct_ref(Node *lhs, Token *tok) { add_type(lhs); - if (lhs->ty->kind != TY_STRUCT) - error_tok(lhs->tok, "not a struct"); + if (lhs->ty->kind != TY_STRUCT && lhs->ty->kind != TY_UNION) + error_tok(lhs->tok, "not a struct nor a union"); Node *node = new_unary(ND_MEMBER, lhs, tok); node->member = get_struct_member(lhs->ty, tok); diff --git a/test/union.c b/test/union.c new file mode 100644 index 0000000000000000000000000000000000000000..f891e8c46cb8c65fc393945afd84ba46385c763c --- /dev/null +++ b/test/union.c @@ -0,0 +1,12 @@ +#include "test.h" + +int main() { + ASSERT(8, ({ union { int a; char b[6]; } x; sizeof(x); })); + ASSERT(3, ({ union { int a; char b[4]; } x; x.a = 515; x.b[0]; })); + ASSERT(2, ({ union { int a; char b[4]; } x; x.a = 515; x.b[1]; })); + ASSERT(0, ({ union { int a; char b[4]; } x; x.a = 515; x.b[2]; })); + ASSERT(0, ({ union { int a; char b[4]; } x; x.a = 515; x.b[3]; })); + + printf("OK\n"); + return 0; +} diff --git a/tokenize.c b/tokenize.c index cf30b6ed6939d6c1fb80b204fea380980c6f5292..4c29bc285bf99b30f66776261b2a80e9740cf9bd 100644 --- a/tokenize.c +++ b/tokenize.c @@ -126,7 +126,7 @@ static int read_punct(char *p) { static bool is_keyword(Token *tok) { static char *kw[] = { "return", "if", "else", "for", "while", "int", "sizeof", "char", - "struct", + "struct", "union", }; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)