From 1b8715cde6e0ab71bb6227d10598d6da4dd80a7d Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Sun, 11 Aug 2019 11:04:36 +0900 Subject: [PATCH] Handle complex type declarations correctly chibicc can now read complex type declarations such as below. long x; long int x; int long x; short x; short int x; int short x; In the following example, `x` is defined as an alias for `int`. typedef x; Below is valid C code where the second `t` is a local variable of type int having value 3. typedef int t; t t = 3; --- chibicc.h | 1 + parse.c | 128 +++++++++++++++++++++++++++++++++++++++++------------- tests | 8 ++++ 3 files changed, 108 insertions(+), 29 deletions(-) diff --git a/chibicc.h b/chibicc.h index 95d7f95..05e5d13 100644 --- a/chibicc.h +++ b/chibicc.h @@ -178,6 +178,7 @@ typedef enum { struct Type { TypeKind kind; + bool is_typedef; // typedef int align; // alignment Type *base; // pointer or array int array_size; // array diff --git a/parse.c b/parse.c index 2730dfa..bc0f400 100644 --- a/parse.c +++ b/parse.c @@ -170,26 +170,99 @@ Program *program() { } // type-specifier = builtin-type | struct-decl | typedef-name -// builtin-type = "void" | "_Bool" | "char" | "short" | "int" | "long" +// +// builtin-type = "void" +// | "_Bool" +// | "char" +// | "short" | "short" "int" | "int" "short" +// | "int" +// | "long" | "long" "int" | "int" "long" +// +// Note that "typedef" can appear anywhere in a type-specifier. Type *type_specifier() { if (!is_typename(token)) error_tok(token, "typename expected"); - if (consume("void")) - return void_type(); - if (consume("_Bool")) - return bool_type(); - if (consume("char")) - return char_type(); - if (consume("short")) - return short_type(); - if (consume("int")) - return int_type(); - if (consume("long")) - return long_type(); - if (consume("struct")) - return struct_decl(); - return find_var(consume_ident())->type_def; + Type *ty = NULL; + + enum { + VOID = 1 << 1, + BOOL = 1 << 3, + CHAR = 1 << 5, + SHORT = 1 << 7, + INT = 1 << 9, + LONG = 1 << 11, + }; + + int base_type = 0; + Type *user_type = NULL; + + bool is_typedef = false; + + for (;;) { + // Read one token at a time. + Token *tok = token; + if (consume("typedef")) { + is_typedef = true; + } else if (consume("void")) { + base_type += VOID; + } else if (consume("_Bool")) { + base_type += BOOL; + } else if (consume("char")) { + base_type += CHAR; + } else if (consume("short")) { + base_type += SHORT; + } else if (consume("int")) { + base_type += INT; + } else if (consume("long")) { + base_type += LONG; + } else if (peek("struct")) { + if (base_type || user_type) + break; + user_type = struct_decl(); + } else { + if (base_type || user_type) + break; + Type *ty = find_typedef(token); + if (!ty) + break; + token = token->next; + user_type = ty; + } + + switch (base_type) { + case VOID: + ty = void_type(); + break; + case BOOL: + ty = bool_type(); + break; + case CHAR: + ty = char_type(); + break; + case SHORT: + case SHORT + INT: + ty = short_type(); + break; + case INT: + ty = int_type(); + break; + case LONG: + case LONG + INT: + ty = long_type(); + break; + case 0: + // If there's no type specifier, it becomes int. + // For example, `typedef x` defines x as an alias for int. + ty = user_type ? user_type : int_type(); + break; + default: + error_tok(tok, "invalid type"); + } + } + + ty->is_typedef = is_typedef; + return ty; } // declarator = "*"* ("(" declarator ")" | ident) type-suffix @@ -231,6 +304,7 @@ void push_tag_scope(Token *tok, Type *ty) { // | "struct" ident? "{" struct-member "}" Type *struct_decl() { // Read a struct tag. + expect("struct"); Token *tag = consume_ident(); if (tag && !peek("{")) { TagScope *sc = find_tag(tag); @@ -369,6 +443,13 @@ Node *declaration() { ty = declarator(ty, &name); ty = type_suffix(ty); + if (ty->is_typedef) { + expect(";"); + ty->is_typedef = false; + push_scope(name)->type_def = ty; + return new_node(ND_NULL, tok); + } + if (ty->kind == TY_VOID) error_tok(tok, "variable declared void"); @@ -391,7 +472,8 @@ Node *read_expr_stmt() { bool is_typename() { return peek("void") || peek("_Bool") || peek("char") || peek("short") || - peek("int") || peek("long") || peek("struct") || find_typedef(token); + peek("int") || peek("long") || peek("struct") || peek("typedef") || + find_typedef(token); } // stmt = "return" expr ";" @@ -399,7 +481,6 @@ bool is_typename() { // | "while" "(" expr ")" stmt // | "for" "(" expr? ";" expr? ";" expr? ")" stmt // | "{" stmt* "}" -// | "typedef" type-specifier declarator type-suffix ";" // | declaration // | expr ";" Node *stmt() { @@ -468,17 +549,6 @@ Node *stmt() { return node; } - if (tok = consume("typedef")) { - Type *ty = type_specifier(); - char *name = NULL; - ty = declarator(ty, &name); - ty = type_suffix(ty); - expect(";"); - - push_scope(name)->type_def = ty; - return new_node(ND_NULL, tok); - } - if (is_typename()) return declaration(); diff --git a/tests b/tests index 1316b42..b848dab 100644 --- a/tests +++ b/tests @@ -279,6 +279,14 @@ int main() { assert(1, ({ _Bool x=1; x; }), "_Bool x=1; x;"); assert(1, ({ _Bool x=2; x; }), "_Bool x=2; x;"); + assert(1, ({ char x; sizeof(x); }), "char x; sizeof(x);"); + assert(2, ({ short int x; sizeof(x); }), "short int x; sizeof(x);"); + assert(2, ({ int short x; sizeof(x); }), "int short x; sizeof(x);"); + assert(4, ({ int x; sizeof(x); }), "int x; sizeof(x);"); + assert(4, ({ typedef t; t x; sizeof(x); }), "typedef t; t x; sizeof(x);"); + assert(8, ({ long int x; sizeof(x); }), "long int x; sizeof(x);"); + assert(8, ({ int long x; sizeof(x); }), "int long x; sizeof(x);"); + printf("OK\n"); return 0; } -- GitLab