diff --git a/chibicc.h b/chibicc.h index d3a8d19ce7e1d93c9d1ab7d029345dba6f149b31..d907855710f54740e4d80066190675ddd6bd9f1a 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 e7ab20384a105a6652cca56397a603fc3f8328da..bf656c2d53a6964b7bd52d326d516fbac4f19937 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 e7ad3b4d4dd16eda455134182d4b34a7913f2203..31bddfbaf392f0cca0ed6dec77fac92d2c86c9c0 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 48e08f179b3268e23c2d7bd713427491efbcab5b..177539c148fdff667b6992035f17c8f56a1c9379 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 e8a610b54acb28d08125b40444196fd13efb82e0..699a9a91ac6775a8f01eb39e468fe9842fa7a4c8 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 964413b8a639ed1b1dd21f299f6b483e28ff7b7b..02d716e59db84eae7b1e6c6893513abe2470419a 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);