diff --git a/parse.c b/parse.c index e410193893847e397d48d79424adb1402a6b7b4c..75c1707c3f3bf09082e8392fb7b688ae10ac7420 100644 --- a/parse.c +++ b/parse.c @@ -59,6 +59,7 @@ static Scope *scope = &(Scope){}; // scope and decremented by one at the end of a block scope. static int scope_depth; +static bool is_typename(Token *tok); static Type *typespec(Token **rest, Token *tok); static Type *declarator(Token **rest, Token *tok, Type *ty); static Node *declaration(Token **rest, Token *tok); @@ -211,41 +212,90 @@ static void push_tag_scope(Token *tok, Type *ty) { scope->tags = sc; } -// typespec = "void" | "char" | "short" | "int" | "long" -// | struct-decl | union-decl +// typespec = typename typename* +// typename = "void" | "char" | "short" | "int" | "long" +// | struct-decl | union-decl +// +// The order of typenames in a type-specifier doesn't matter. For +// example, `int long static` means the same as `static long int`. +// That can also be written as `static long` because you can omit +// `int` if `long` or `short` are specified. However, something like +// `char int` is not a valid type specifier. We have to accept only a +// limited combinations of the typenames. +// +// In this function, we count the number of occurrences of each typename +// 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 *typespec(Token **rest, Token *tok) { - if (equal(tok, "void")) { - *rest = tok->next; - return ty_void; - } - - if (equal(tok, "char")) { - *rest = tok->next; - return ty_char; - } + // 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 + // as you can see below. + enum { + VOID = 1 << 0, + CHAR = 1 << 2, + SHORT = 1 << 4, + INT = 1 << 6, + LONG = 1 << 8, + OTHER = 1 << 10, + }; - if (equal(tok, "short")) { - *rest = tok->next; - return ty_short; - } + Type *ty = ty_int; + int counter = 0; + + while (is_typename(tok)) { + // Handle user-defined types. + if (equal(tok, "struct") || equal(tok, "union")) { + if (equal(tok, "struct")) + ty = struct_decl(&tok, tok->next); + else + ty = union_decl(&tok, tok->next); + counter += OTHER; + continue; + } - if (equal(tok, "int")) { - *rest = tok->next; - return ty_int; - } + // Handle built-in types. + if (equal(tok, "void")) + counter += VOID; + else if (equal(tok, "char")) + counter += CHAR; + else if (equal(tok, "short")) + counter += SHORT; + else if (equal(tok, "int")) + counter += INT; + else if (equal(tok, "long")) + counter += LONG; + else + unreachable(); + + switch (counter) { + case VOID: + ty = ty_void; + break; + case CHAR: + ty = ty_char; + break; + case SHORT: + case SHORT + INT: + ty = ty_short; + break; + case INT: + ty = ty_int; + break; + case LONG: + case LONG + INT: + ty = ty_long; + break; + default: + error_tok(tok, "invalid type"); + } - if (equal(tok, "long")) { - *rest = tok->next; - return ty_long; + tok = tok->next; } - if (equal(tok, "struct")) - return struct_decl(rest, tok->next); - - if (equal(tok, "union")) - return union_decl(rest, tok->next); - - error_tok(tok, "typename expected"); + *rest = tok; + return ty; } // func-params = (param ("," param)*)? ")" diff --git a/test/decl.c b/test/decl.c new file mode 100644 index 0000000000000000000000000000000000000000..14dc267c032c252f1cf907653c54d91987673f64 --- /dev/null +++ b/test/decl.c @@ -0,0 +1,13 @@ +#include "test.h" + +int main() { + ASSERT(1, ({ char x; sizeof(x); })); + ASSERT(2, ({ short int x; sizeof(x); })); + ASSERT(2, ({ int short x; sizeof(x); })); + ASSERT(4, ({ int x; sizeof(x); })); + ASSERT(8, ({ long int x; sizeof(x); })); + ASSERT(8, ({ int long x; sizeof(x); })); + + printf("OK\n"); + return 0; +}