From 0385d25a9d58057985b27ad6f8f5bce382f61057 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Thu, 8 Aug 2019 22:43:58 +0900 Subject: [PATCH] Add struct --- chibicc.h | 20 ++++++++++++-- codegen.c | 7 +++++ parse.c | 81 +++++++++++++++++++++++++++++++++++++++++++++--------- tests | 24 ++++++++++++++++ tokenize.c | 4 +-- type.c | 26 ++++++++++++++++-- 6 files changed, 143 insertions(+), 19 deletions(-) diff --git a/chibicc.h b/chibicc.h index d3a8d19..d907855 100644 --- a/chibicc.h +++ b/chibicc.h @@ -9,6 +9,7 @@ #include typedef struct Type Type; +typedef struct Member Member; // // tokenize.c @@ -90,6 +91,7 @@ typedef enum { ND_LT, // < ND_LE, // <= ND_ASSIGN, // = + ND_MEMBER, // . (struct member access) ND_ADDR, // unary & ND_DEREF, // unary * ND_RETURN, // "return" @@ -127,6 +129,10 @@ struct Node { // Block or statement expression Node *body; + // Struct member access + char *member_name; + Member *member; + // Function call char *funcname; Node *args; @@ -162,12 +168,22 @@ typedef enum { TY_INT, TY_PTR, TY_ARRAY, + TY_STRUCT, } TypeKind; struct Type { TypeKind kind; - Type *base; - int array_size; + Type *base; // pointer or array + int array_size; // array + Member *members; // struct +}; + +// Struct member +struct Member { + Member *next; + Type *ty; + char *name; + int offset; }; Type *char_type(); diff --git a/codegen.c b/codegen.c index e7ab203..bf656c2 100644 --- a/codegen.c +++ b/codegen.c @@ -24,6 +24,12 @@ void gen_addr(Node *node) { case ND_DEREF: gen(node->lhs); return; + case ND_MEMBER: + gen_addr(node->lhs); + printf(" pop rax\n"); + printf(" add rax, %d\n", node->member->offset); + printf(" push rax\n"); + return; } error_tok(node->tok, "not an lvalue"); @@ -69,6 +75,7 @@ void gen(Node *node) { printf(" add rsp, 8\n"); return; case ND_VAR: + case ND_MEMBER: gen_addr(node); if (node->ty->kind != TY_ARRAY) load(node->ty); diff --git a/parse.c b/parse.c index e7ad3b4..31bddfb 100644 --- a/parse.c +++ b/parse.c @@ -79,8 +79,11 @@ char *new_label() { Function *function(); Type *basetype(); +Type *struct_decl(); +Member *struct_member(); void global_var(); Node *declaration(); +bool is_typename(); Node *stmt(); Node *expr(); Node *assign(); @@ -122,15 +125,18 @@ Program *program() { return prog; } -// basetype = ("char" | "int") "*"* +// basetype = ("char" | "int" | struct-decl) "*"* Type *basetype() { + if (!is_typename(token)) + error_tok(token, "typename expected"); + Type *ty; - if (consume("char")) { + if (consume("char")) ty = char_type(); - } else { - expect("int"); + else if (consume("int")) ty = int_type(); - } + else + ty = struct_decl(); while (consume("*")) ty = pointer_to(ty); @@ -146,6 +152,45 @@ Type *read_type_suffix(Type *base) { return array_of(base, sz); } +// struct-decl = "struct" "{" struct-member "}" +Type *struct_decl() { + // Read struct members. + expect("struct"); + expect("{"); + + Member head; + head.next = NULL; + Member *cur = &head; + + while (!consume("}")) { + cur->next = struct_member(); + cur = cur->next; + } + + Type *ty = calloc(1, sizeof(Type)); + ty->kind = TY_STRUCT; + ty->members = head.next; + + // Assign offsets within the struct to members. + int offset = 0; + for (Member *mem = ty->members; mem; mem = mem->next) { + mem->offset = offset; + offset += size_of(mem->ty); + } + + return ty; +} + +// struct-member = basetype ident ("[" num "]")* ";" +Member *struct_member() { + Member *mem = calloc(1, sizeof(Member)); + mem->ty = basetype(); + mem->name = expect_ident(); + mem->ty = read_type_suffix(mem->ty); + expect(";"); + return mem; +} + VarList *read_func_param() { Type *ty = basetype(); char *name = expect_ident(); @@ -232,7 +277,7 @@ Node *read_expr_stmt() { } bool is_typename() { - return peek("char") || peek("int"); + return peek("char") || peek("int") || peek("struct"); } // stmt = "return" expr ";" @@ -407,18 +452,28 @@ Node *unary() { return postfix(); } -// postfix = primary ("[" expr "]")* +// postfix = primary ("[" expr "]" | "." ident)* Node *postfix() { Node *node = primary(); Token *tok; - while (tok = consume("[")) { - // x[y] is short for *(x+y) - Node *exp = new_binary(ND_ADD, node, expr(), tok); - expect("]"); - node = new_unary(ND_DEREF, exp, tok); + for (;;) { + if (tok = consume("[")) { + // x[y] is short for *(x+y) + Node *exp = new_binary(ND_ADD, node, expr(), tok); + expect("]"); + node = new_unary(ND_DEREF, exp, tok); + continue; + } + + if (tok = consume(".")) { + node = new_unary(ND_MEMBER, node, tok); + node->member_name = expect_ident(); + continue; + } + + return node; } - return node; } // stmt-expr = "(" "{" stmt stmt* "}" ")" diff --git a/tests b/tests index 48e08f1..177539c 100644 --- a/tests +++ b/tests @@ -203,6 +203,30 @@ int main() { assert(2, ({ int x=2; { int x=3; } int y=4; x; }), "int x=2; { int x=3; } int y=4; x;"); assert(3, ({ int x=2; { x=3; } x; }), "int x=2; { x=3; } x;"); + assert(1, ({ struct {int a; int b;} x; x.a=1; x.b=2; x.a; }), "struct {int a; int b;} x; x.a=1; x.b=2; x.a;"); + assert(2, ({ struct {int a; int b;} x; x.a=1; x.b=2; x.b; }), "struct {int a; int b;} x; x.a=1; x.b=2; x.b;"); + assert(1, ({ struct {char a; int b; char c;} x; x.a=1; x.b=2; x.c=3; x.a; }), "struct {char a; int b; char c;} x; x.a=1; x.b=2; x.c=3; x.a;"); + assert(2, ({ struct {char a; int b; char c;} x; x.b=1; x.b=2; x.c=3; x.b; }), "struct {char a; int b; char c;} a; x.b=x; x.a=2; x.b=3; x.b;"); + assert(3, ({ struct {char a; int b; char c;} x; x.a=1; x.b=2; x.c=3; x.c; }), "struct {char a; int b; char c;} x; x.a=1; x.b=2; x.c=3; x.c;"); + + assert(0, ({ struct {int a; int b;} x[3]; int *p=x; p[0]=0; x[0].a; }), "struct {int a; int b;} x[3]; int *p=x; p[0]=0; x[0].a;"); + assert(1, ({ struct {int a; int b;} x[3]; int *p=x; p[1]=1; x[0].b; }), "struct {int a; int b;} x[3]; int *p=x; p[1]=1; x[0].b;"); + assert(2, ({ struct {int a; int b;} x[3]; int *p=x; p[2]=2; x[1].a; }), "struct {int a; int b;} x[3]; int *p=x; p[2]=2; x[1].a;"); + assert(3, ({ struct {int a; int b;} x[3]; int *p=x; p[3]=3; x[1].b; }), "struct {int a; int b;} x[3]; int *p=x; p[3]=3; x[1].b;"); + + assert(6, ({ struct {int a[3]; int b[5];} x; int *p=&x; x.a[0]=6; p[0]; }), "struct {int a[3]; int b[5];} x; int *p=&x; x.a[0]=6; p[0];"); + assert(7, ({ struct {int a[3]; int b[5];} x; int *p=&x; x.b[0]=7; p[3]; }), "struct {int a[3]; int b[5];} x; int *p=&x; x.b[0]=7; p[3];"); + + assert(6, ({ struct { struct { int b; } a; } x; x.a.b=6; x.a.b; }), "struct { struct { int b; } a; } x; x.a.b=6; x.a.b;"); + + assert(8, ({ struct {int a;} x; sizeof(x); }), "struct {int a;} x; sizeof(x);"); + assert(16, ({ struct {int a; int b;} x; sizeof(x); }), "struct {int a; int b;} x; sizeof(x);"); + assert(24, ({ struct {int a[3];} x; sizeof(x); }), "struct {int a[3];} x; sizeof(x);"); + assert(32, ({ struct {int a;} x[4]; sizeof(x); }), "struct {int a;} x[4]; sizeof(x);"); + assert(48, ({ struct {int a[3];} x[2]; sizeof(x); }), "struct {int a[3];} x[2]; sizeof(x)};"); + assert(2, ({ struct {char a; char b;} x; sizeof(x); }), "struct {char a; char b;} x; sizeof(x);"); + assert(9, ({ struct {char a; int b;} x; sizeof(x); }), "struct {char a; int b;} x; sizeof(x);"); + printf("OK\n"); return 0; } diff --git a/tokenize.c b/tokenize.c index e8a610b..699a9a9 100644 --- a/tokenize.c +++ b/tokenize.c @@ -152,7 +152,7 @@ bool is_alnum(char c) { char *starts_with_reserved(char *p) { // Keyword static char *kw[] = {"return", "if", "else", "while", "for", "int", - "char", "sizeof"}; + "char", "sizeof", "struct"}; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) { int len = strlen(kw[i]); @@ -255,7 +255,7 @@ Token *tokenize() { } // Single-letter punctuator - if (strchr("+-*/()<>;={},&[]", *p)) { + if (strchr("+-*/()<>;={},&[].", *p)) { cur = new_token(TK_RESERVED, cur, p++, 1); continue; } diff --git a/type.c b/type.c index 964413b..02d716e 100644 --- a/type.c +++ b/type.c @@ -34,12 +34,25 @@ int size_of(Type *ty) { case TY_INT: case TY_PTR: return 8; - default: - assert(ty->kind == TY_ARRAY); + case TY_ARRAY: return size_of(ty->base) * ty->array_size; + default: + assert(ty->kind == TY_STRUCT); + Member *mem = ty->members; + while (mem->next) + mem = mem->next; + return mem->offset + size_of(mem->ty); } } +Member *find_member(Type *ty, char *name) { + assert(ty->kind == TY_STRUCT); + for (Member *mem = ty->members; mem; mem = mem->next) + if (!strcmp(mem->name, name)) + return mem; + return NULL; +} + void visit(Node *node) { if (!node) return; @@ -89,6 +102,15 @@ void visit(Node *node) { case ND_ASSIGN: node->ty = node->lhs->ty; return; + case ND_MEMBER: { + if (node->lhs->ty->kind != TY_STRUCT) + error_tok(node->tok, "not a struct"); + node->member = find_member(node->lhs->ty, node->member_name); + if (!node->member) + error_tok(node->tok, "specified member does not exist"); + node->ty = node->member->ty; + return; + } case ND_ADDR: if (node->lhs->ty->kind == TY_ARRAY) node->ty = pointer_to(node->lhs->ty->base); -- GitLab