Add union

This commit is contained in:
Rui Ueyama 2020-04-02 21:54:47 +09:00
parent c38f99488f
commit 198f0e8f1d
4 changed files with 54 additions and 11 deletions

View File

@ -145,6 +145,7 @@ typedef enum {
TY_FUNC,
TY_ARRAY,
TY_STRUCT,
TY_UNION,
} TypeKind;
struct Type {

50
parse.c
View File

@ -27,7 +27,7 @@ struct VarScope {
Var *var;
};
// Scope for struct tags
// Scope for struct or union tags
typedef struct TagScope TagScope;
struct TagScope {
TagScope *next;
@ -72,6 +72,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);
@ -225,6 +226,9 @@ static Type *typespec(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");
}
@ -310,7 +314,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 ";"
@ -626,9 +631,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;
@ -649,6 +654,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) {
@ -660,10 +676,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;
}
@ -677,8 +707,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);

12
test/union.c Normal file
View File

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

View File

@ -117,7 +117,7 @@ static int from_hex(char c) {
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++)