diff --git a/parse.c b/parse.c index 57caf7179167d2638697f9ec94657f7c8141168c..e35e6169bed2ddd08e56204167db91045a3186af 100644 --- a/parse.c +++ b/parse.c @@ -26,16 +26,30 @@ struct VarScope { Obj *var; }; +// Scope for struct tags +typedef struct TagScope TagScope; +struct TagScope { + TagScope *next; + char *name; + Type *ty; +}; + // Represents a block scope. typedef struct Scope Scope; struct Scope { Scope *next; + + // C has two block scopes; one is for variables and the other is + // for struct tags. VarScope *vars; + TagScope *tags; }; // All local variable instances created during parsing are // accumulated to this list. static Obj *locals; + +// Likewise, global variables are accumulated to this list. static Obj *globals; static Scope *scope = &(Scope){}; @@ -76,6 +90,14 @@ static Obj *find_var(Token *tok) { return NULL; } +static Type *find_tag(Token *tok) { + for (Scope *sc = scope; sc; sc = sc->next) + for (TagScope *sc2 = sc->tags; sc2; sc2 = sc2->next) + if (equal(tok, sc2->name)) + return sc2->ty; + return NULL; +} + static Node *new_node(NodeKind kind, Token *tok) { Node *node = calloc(1, sizeof(Node)); node->kind = kind; @@ -167,6 +189,14 @@ static int get_number(Token *tok) { return tok->val; } +static void push_tag_scope(Token *tok, Type *ty) { + TagScope *sc = calloc(1, sizeof(TagScope)); + sc->name = strndup(tok->loc, tok->len); + sc->ty = ty; + sc->next = scope->tags; + scope->tags = sc; +} + // declspec = "char" | "int" | struct-decl static Type *declspec(Token **rest, Token *tok) { if (equal(tok, "char")) { @@ -583,14 +613,27 @@ static void struct_members(Token **rest, Token *tok, Type *ty) { ty->members = head.next; } -// struct-decl = "{" struct-members +// struct-decl = ident? "{" struct-members static Type *struct_decl(Token **rest, Token *tok) { - tok = skip(tok, "{"); + // Read a struct tag. + Token *tag = NULL; + if (tok->kind == TK_IDENT) { + tag = tok; + tok = tok->next; + } + + if (tag && !equal(tok, "{")) { + Type *ty = find_tag(tag); + if (!ty) + error_tok(tag, "unknown struct type"); + *rest = tok; + return ty; + } // Construct a struct object. Type *ty = calloc(1, sizeof(Type)); ty->kind = TY_STRUCT; - struct_members(rest, tok, ty); + struct_members(rest, tok->next, ty); ty->align = 1; // Assign offsets within the struct to members. @@ -605,6 +648,9 @@ static Type *struct_decl(Token **rest, Token *tok) { } ty->size = align_to(offset, ty->align); + // Register the struct type if a name was given. + if (tag) + push_tag_scope(tag, ty); return ty; } diff --git a/test/struct.c b/test/struct.c index 2877951ac548fa4d7f8980f2ae5178f21e0fc66c..0719b8b9eefd4842ce0d2e612de3a92d3616a69c 100644 --- a/test/struct.c +++ b/test/struct.c @@ -28,6 +28,11 @@ int main() { ASSERT(16, ({ struct {char a; int b;} x; sizeof(x); })); ASSERT(16, ({ struct {int a; char b;} x; sizeof(x); })); + ASSERT(16, ({ 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); })); + ASSERT(2, ({ 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; })); + printf("OK\n"); return 0; }