diff --git a/parse.c b/parse.c index b838c12db92e2398294d906441a0e91b147fc802..0bd19f0a66e70a109d7a5975df7c72c862e977b9 100644 --- a/parse.c +++ b/parse.c @@ -1,8 +1,18 @@ #include "chibicc.h" +// Scope for struct tags +typedef struct TagScope TagScope; +struct TagScope { + TagScope *next; + char *name; + Type *ty; +}; + VarList *locals; VarList *globals; + VarList *scope; +TagScope *tag_scope; // Find a variable by name. Var *find_var(Token *tok) { @@ -14,6 +24,13 @@ Var *find_var(Token *tok) { return NULL; } +TagScope *find_tag(Token *tok) { + for (TagScope *sc = tag_scope; sc; sc = sc->next) + if (strlen(sc->name) == tok->len && !memcmp(tok->str, sc->name, tok->len)) + return sc; + return NULL; +} + Node *new_node(NodeKind kind, Token *tok) { Node *node = calloc(1, sizeof(Node)); node->kind = kind; @@ -152,12 +169,31 @@ Type *read_type_suffix(Type *base) { return array_of(base, sz); } -// struct-decl = "struct" "{" struct-member "}" +void push_tag_scope(Token *tok, Type *ty) { + TagScope *sc = calloc(1, sizeof(TagScope)); + sc->next = tag_scope; + sc->name = strndup(tok->str, tok->len); + sc->ty = ty; + tag_scope = sc; +} + +// struct-decl = "struct" ident +// | "struct" ident? "{" struct-member "}" Type *struct_decl() { - // Read struct members. expect("struct"); + + // Read a struct tag. + Token *tag = consume_ident(); + if (tag && !peek("{")) { + TagScope *sc = find_tag(tag); + if (!sc) + error_tok(tag, "unknown struct type"); + return sc->ty; + } + expect("{"); + // Read struct members. Member head; head.next = NULL; Member *cur = &head; @@ -182,6 +218,9 @@ Type *struct_decl() { ty->align = mem->ty->align; } + // Register the struct type if a name was given. + if (tag) + push_tag_scope(tag, ty); return ty; } @@ -257,9 +296,13 @@ void global_var() { } // declaration = basetype ident ("[" num "]")* ("=" expr) ";" +// | basetype ";" Node *declaration() { Token *tok = token; Type *ty = basetype(); + if (consume(";")) + return new_node(ND_NULL, tok); + char *name = expect_ident(); ty = read_type_suffix(ty); Var *var = push_var(name, ty, true); @@ -343,12 +386,14 @@ Node *stmt() { head.next = NULL; Node *cur = &head; - VarList *sc = scope; + VarList *sc1 = scope; + TagScope *sc2 = tag_scope; while (!consume("}")) { cur->next = stmt(); cur = cur->next; } - scope = sc; + scope = sc1; + tag_scope = sc2; Node *node = new_node(ND_BLOCK, tok); node->body = head.next; @@ -484,7 +529,8 @@ Node *postfix() { // // Statement expression is a GNU C extension. Node *stmt_expr(Token *tok) { - VarList *sc = scope; + VarList *sc1 = scope; + TagScope *sc2 = tag_scope; Node *node = new_node(ND_STMT_EXPR, tok); node->body = stmt(); @@ -496,7 +542,8 @@ Node *stmt_expr(Token *tok) { } expect(")"); - scope = sc; + scope = sc1; + tag_scope = sc2; if (cur->kind != ND_EXPR_STMT) error_tok(cur->tok, "stmt expr returning void is not supported"); diff --git a/tests b/tests index b3972e98eb97f9bbd188921e73c155af3bcf62ab..002af42ecfbc8cf6dd9356a2a6d3b0e2985f3765 100644 --- a/tests +++ b/tests @@ -231,6 +231,11 @@ int main() { assert(15, ({ int x; char y; int a=&x; int b=&y; b-a; }), "int x; char y; int a=&x; int b=&y; b-a;"); assert(1, ({ char x; int y; int a=&x; int b=&y; b-a; }), "char x; int y; int a=&x; int b=&y; b-a;"); + assert(16, ({ struct t {int a; int b;} x; struct t y; sizeof(y); }), "struct t {int a; int b;} x; struct t y; sizeof(y);"); + assert(16, ({ struct t {int a; int b;}; struct t y; sizeof(y); }), "struct t {int a; int b;}; struct t y; sizeof(y);"); + assert(2, ({ struct t {char a[2];}; { struct t {char a[4];}; } struct t y; sizeof(y); }), "struct t {char a[2];}; { struct t {char a[4];}; } struct t y; sizeof(y);"); + assert(3, ({ struct t {int x;}; int t=1; struct t y; y.x=2; t+y.x; }), "struct t {int x;}; int t=1; struct t y; y.x=2; t+y.x;"); + printf("OK\n"); return 0; }