diff --git a/parse.c b/parse.c index 9627a99..d89b2c1 100644 --- a/parse.c +++ b/parse.c @@ -18,12 +18,13 @@ #include "chibicc.h" -// Scope for local or global variables. +// Scope for local, global variables or typedefs. typedef struct VarScope VarScope; struct VarScope { VarScope *next; char *name; Obj *var; + Type *type_def; }; // Scope for struct or union tags @@ -45,6 +46,11 @@ struct Scope { TagScope *tags; }; +// Variable attributes such as typedef or extern. +typedef struct { + bool is_typedef; +} VarAttr; + // All local variable instances created during parsing are // accumulated to this list. static Obj *locals; @@ -55,9 +61,9 @@ static Obj *globals; static Scope *scope = &(Scope){}; static bool is_typename(Token *tok); -static Type *declspec(Token **rest, Token *tok); +static Type *declspec(Token **rest, Token *tok, VarAttr *attr); static Type *declarator(Token **rest, Token *tok, Type *ty); -static Node *declaration(Token **rest, Token *tok); +static Node *declaration(Token **rest, Token *tok, Type *basety); static Node *compound_stmt(Token **rest, Token *tok); static Node *stmt(Token **rest, Token *tok); static Node *expr_stmt(Token **rest, Token *tok); @@ -72,6 +78,7 @@ 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); +static Token *parse_typedef(Token *tok, Type *basety); static void enter_scope(void) { Scope *sc = calloc(1, sizeof(Scope)); @@ -84,11 +91,11 @@ static void leave_scope(void) { } // Find a variable by name. -static Obj *find_var(Token *tok) { +static VarScope *find_var(Token *tok) { for (Scope *sc = scope; sc; sc = sc->next) for (VarScope *sc2 = sc->vars; sc2; sc2 = sc2->next) if (equal(tok, sc2->name)) - return sc2->var; + return sc2; return NULL; } @@ -132,10 +139,9 @@ static Node *new_var_node(Obj *var, Token *tok) { return node; } -static VarScope *push_scope(char *name, Obj *var) { +static VarScope *push_scope(char *name) { VarScope *sc = calloc(1, sizeof(VarScope)); sc->name = name; - sc->var = var; sc->next = scope->vars; scope->vars = sc; return sc; @@ -145,7 +151,7 @@ static Obj *new_var(char *name, Type *ty) { Obj *var = calloc(1, sizeof(Obj)); var->name = name; var->ty = ty; - push_scope(name, var); + push_scope(name)->var = var; return var; } @@ -185,7 +191,16 @@ static char *get_ident(Token *tok) { return strndup(tok->loc, tok->len); } -static int get_number(Token *tok) { +static Type *find_typedef(Token *tok) { + if (tok->kind == TK_IDENT) { + VarScope *sc = find_var(tok); + if (sc) + return sc->type_def; + } + return NULL; +} + +static long get_number(Token *tok) { if (tok->kind != TK_NUM) error_tok(tok, "expected a number"); return tok->val; @@ -200,7 +215,8 @@ static void push_tag_scope(Token *tok, Type *ty) { } // declspec = ("void" | "char" | "short" | "int" | "long" -// | struct-decl | union-decl)+ +// | "typedef" +// | struct-decl | union-decl | typedef-name)+ // // The order of typenames in a type-specifier doesn't matter. For // example, `int long static` means the same as `static long int`. @@ -213,7 +229,7 @@ static void push_tag_scope(Token *tok, Type *ty) { // while keeping the "current" type object that the typenames up // until that point represent. When we reach a non-typename token, // we returns the current type object. -static Type *declspec(Token **rest, Token *tok) { +static Type *declspec(Token **rest, Token *tok, VarAttr *attr) { // We use a single integer as counters for all typenames. // For example, bits 0 and 1 represents how many times we saw the // keyword "void" so far. With this, we can use a switch statement @@ -231,12 +247,30 @@ static Type *declspec(Token **rest, Token *tok) { int counter = 0; while (is_typename(tok)) { + // Handle "typedef" keyword + if (equal(tok, "typedef")) { + if (!attr) + error_tok(tok, "storage class specifier is not allowed in this context"); + attr->is_typedef = true; + tok = tok->next; + continue; + } + // Handle user-defined types. - if (equal(tok, "struct") || equal(tok, "union")) { - if (equal(tok, "struct")) + Type *ty2 = find_typedef(tok); + if (equal(tok, "struct") || equal(tok, "union") || ty2) { + if (counter) + break; + + if (equal(tok, "struct")) { ty = struct_decl(&tok, tok->next); - else + } else if (equal(tok, "union")) { ty = union_decl(&tok, tok->next); + } else { + ty = ty2; + tok = tok->next; + } + counter += OTHER; continue; } @@ -295,7 +329,7 @@ static Type *func_params(Token **rest, Token *tok, Type *ty) { while (!equal(tok, ")")) { if (cur != &head) tok = skip(tok, ","); - Type *basety = declspec(&tok, tok); + Type *basety = declspec(&tok, tok, NULL); Type *ty = declarator(&tok, tok, basety); cur = cur->next = copy_type(ty); } @@ -346,9 +380,7 @@ static Type *declarator(Token **rest, Token *tok, Type *ty) { } // declaration = declspec (declarator ("=" expr)? ("," declarator ("=" expr)?)*)? ";" -static Node *declaration(Token **rest, Token *tok) { - Type *basety = declspec(&tok, tok); - +static Node *declaration(Token **rest, Token *tok, Type *basety) { Node head = {}; Node *cur = &head; int i = 0; @@ -382,12 +414,13 @@ static Node *declaration(Token **rest, Token *tok) { static bool is_typename(Token *tok) { static char *kw[] = { "void", "char", "short", "int", "long", "struct", "union", + "typedef", }; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) if (equal(tok, kw[i])) return true; - return false; + return find_typedef(tok); } // stmt = "return" expr ";" @@ -449,7 +482,7 @@ static Node *stmt(Token **rest, Token *tok) { return expr_stmt(rest, tok); } -// compound-stmt = (declaration | stmt)* "}" +// compound-stmt = (typedef | declaration | stmt)* "}" static Node *compound_stmt(Token **rest, Token *tok) { Node *node = new_node(ND_BLOCK, tok); Node head = {}; @@ -458,10 +491,19 @@ static Node *compound_stmt(Token **rest, Token *tok) { enter_scope(); while (!equal(tok, "}")) { - if (is_typename(tok)) - cur = cur->next = declaration(&tok, tok); - else + if (is_typename(tok)) { + VarAttr attr = {}; + Type *basety = declspec(&tok, tok, &attr); + + if (attr.is_typedef) { + tok = parse_typedef(tok, basety); + continue; + } + + cur = cur->next = declaration(&tok, tok, basety); + } else { cur = cur->next = stmt(&tok, tok); + } add_type(cur); } @@ -685,7 +727,7 @@ static void struct_members(Token **rest, Token *tok, Type *ty) { Member *cur = &head; while (!equal(tok, "}")) { - Type *basety = declspec(&tok, tok); + Type *basety = declspec(&tok, tok, NULL); int i = 0; while (!consume(&tok, tok, ";")) { @@ -875,11 +917,11 @@ static Node *primary(Token **rest, Token *tok) { return funcall(rest, tok); // Variable - Obj *var = find_var(tok); - if (!var) + VarScope *sc = find_var(tok); + if (!sc || !sc->var) error_tok(tok, "undefined variable"); *rest = tok->next; - return new_var_node(var, tok); + return new_var_node(sc->var, tok); } if (tok->kind == TK_STR) { @@ -897,6 +939,20 @@ static Node *primary(Token **rest, Token *tok) { error_tok(tok, "expected an expression"); } +static Token *parse_typedef(Token *tok, Type *basety) { + bool first = true; + + while (!consume(&tok, tok, ";")) { + if (!first) + tok = skip(tok, ","); + first = false; + + Type *ty = declarator(&tok, tok, basety); + push_scope(get_ident(ty->name))->type_def = ty; + } + return tok; +} + static void create_param_lvars(Type *param) { if (param) { create_param_lvars(param->next); @@ -951,12 +1007,19 @@ static bool is_function(Token *tok) { return ty->kind == TY_FUNC; } -// program = (function-definition | global-variable)* +// program = (typedef | function-definition | global-variable)* Obj *parse(Token *tok) { globals = NULL; while (tok->kind != TK_EOF) { - Type *basety = declspec(&tok, tok); + VarAttr attr = {}; + Type *basety = declspec(&tok, tok, &attr); + + // Typedef + if (attr.is_typedef) { + tok = parse_typedef(tok, basety); + continue; + } // Function if (is_function(tok)) { diff --git a/test/typedef.c b/test/typedef.c new file mode 100644 index 0000000..24cf448 --- /dev/null +++ b/test/typedef.c @@ -0,0 +1,17 @@ +#include "test.h" + +typedef int MyInt, MyInt2[4]; +typedef int; + +int main() { + ASSERT(1, ({ typedef int t; t x=1; x; })); + ASSERT(1, ({ typedef struct {int a;} t; t x; x.a=1; x.a; })); + ASSERT(1, ({ typedef int t; t t=1; t; })); + ASSERT(2, ({ typedef struct {int a;} t; { typedef int t; } t x; x.a=2; x.a; })); + ASSERT(4, ({ typedef t; t x; sizeof(x); })); + ASSERT(3, ({ MyInt x=3; x; })); + ASSERT(16, ({ MyInt2 x; sizeof(x); })); + + printf("OK\n"); + return 0; +} diff --git a/tokenize.c b/tokenize.c index a6849ba..a194ada 100644 --- a/tokenize.c +++ b/tokenize.c @@ -126,7 +126,7 @@ static int read_punct(char *p) { static bool is_keyword(Token *tok) { static char *kw[] = { "return", "if", "else", "for", "while", "int", "sizeof", "char", - "struct", "union", "short", "long", "void", + "struct", "union", "short", "long", "void", "typedef", }; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)