diff --git a/chibicc.h b/chibicc.h index 9d9131f..17385e9 100644 --- a/chibicc.h +++ b/chibicc.h @@ -154,6 +154,7 @@ typedef enum { TY_SHORT, TY_INT, TY_LONG, + TY_ENUM, TY_PTR, TY_FUNC, TY_ARRAY, @@ -212,6 +213,7 @@ Type *copy_type(Type *ty); Type *pointer_to(Type *base); Type *func_type(Type *return_ty); Type *array_of(Type *base, int size); +Type *enum_type(void); void add_type(Node *node); // diff --git a/parse.c b/parse.c index e02b568..1ebb7c4 100644 --- a/parse.c +++ b/parse.c @@ -18,17 +18,21 @@ #include "chibicc.h" -// Scope for local, global variables or typedefs. +// Scope for local variables, global variables, typedefs +// or enum constants typedef struct VarScope VarScope; struct VarScope { VarScope *next; char *name; int depth; + Var *var; Type *type_def; + Type *enum_ty; + int enum_val; }; -// Scope for struct or union tags +// Scope for struct, union or enum tags typedef struct TagScope TagScope; struct TagScope { TagScope *next; @@ -41,8 +45,8 @@ typedef struct Scope Scope; struct Scope { Scope *next; - // C has two block scopes; one is for variables and the other is - // for struct tags. + // C has two block scopes; one is for variables/typedefs and + // the other is for struct/union/enum tags. VarScope *vars; TagScope *tags; }; @@ -70,6 +74,7 @@ static Var *current_fn; static bool is_typename(Token *tok); static Type *typespec(Token **rest, Token *tok, VarAttr *attr); +static Type *enum_specifier(Token **rest, Token *tok); static Type *declarator(Token **rest, Token *tok, Type *ty); static Node *declaration(Token **rest, Token *tok, Type *basety); static Node *compound_stmt(Token **rest, Token *tok); @@ -243,7 +248,7 @@ static void push_tag_scope(Token *tok, Type *ty) { } // typespec = typename typename* -// typename = "void" | "char" | "short" | "int" | "long" +// typename = "void" | "_Bool" | "char" | "short" | "int" | "long" // | struct-decl | union-decl | typedef-name // // The order of typenames in a type-specifier doesn't matter. For @@ -287,7 +292,7 @@ static Type *typespec(Token **rest, Token *tok, VarAttr *attr) { // Handle user-defined types. Type *ty2 = find_typedef(tok); - if (equal(tok, "struct") || equal(tok, "union") || ty2) { + if (equal(tok, "struct") || equal(tok, "union") || equal(tok, "enum") || ty2) { if (counter) break; @@ -295,6 +300,8 @@ static Type *typespec(Token **rest, Token *tok, VarAttr *attr) { ty = struct_decl(&tok, tok->next); } else if (equal(tok, "union")) { ty = union_decl(&tok, tok->next); + } else if (equal(tok, "enum")) { + ty = enum_specifier(&tok, tok->next); } else { ty = ty2; tok = tok->next; @@ -438,6 +445,59 @@ static Type *typename(Token **rest, Token *tok) { return abstract_declarator(rest, tok, ty); } +// enum-specifier = ident? "{" enum-list? "}" +// | ident ("{" enum-list? "}")? +// +// enum-list = ident ("=" num)? ("," ident ("=" num)?)* +static Type *enum_specifier(Token **rest, Token *tok) { + Type *ty = enum_type(); + + // Read a struct tag. + Token *tag = NULL; + if (tok->kind == TK_IDENT) { + tag = tok; + tok = tok->next; + } + + if (tag && !equal(tok, "{")) { + TagScope *sc = find_tag(tag); + if (!sc) + error_tok(tag, "unknown enum type"); + if (sc->ty->kind != TY_ENUM) + error_tok(tag, "not an enum tag"); + *rest = tok; + return sc->ty; + } + + tok = skip(tok, "{"); + + // Read an enum-list. + int i = 0; + int val = 0; + while (!equal(tok, "}")) { + if (i++ > 0) + tok = skip(tok, ","); + + char *name = get_ident(tok); + tok = tok->next; + + if (equal(tok, "=")) { + val = get_number(tok->next); + tok = tok->next->next; + } + + VarScope *sc = push_scope(name); + sc->enum_ty = ty; + sc->enum_val = val++; + } + + *rest = tok->next; + + if (tag) + push_tag_scope(tag, ty); + return ty; +} + // declaration = typespec (declarator ("=" expr)? ("," declarator ("=" expr)?)*)? ";" static Node *declaration(Token **rest, Token *tok, Type *basety) { Node head = {}; @@ -473,7 +533,7 @@ static Node *declaration(Token **rest, Token *tok, Type *basety) { static bool is_typename(Token *tok) { static char *kw[] = { "void", "_Bool", "char", "short", "int", "long", "struct", "union", - "typedef", + "typedef", "enum", }; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) @@ -1023,12 +1083,19 @@ static Node *primary(Token **rest, Token *tok) { if (equal(tok->next, "(")) return funcall(rest, tok); - // Variable + // Variable or enum constant VarScope *sc = find_var(tok); - if (!sc || !sc->var) + if (!sc || (!sc->var && !sc->enum_ty)) error_tok(tok, "undefined variable"); + + Node *node; + if (sc->var) + node = new_var_node(sc->var, tok); + else + node = new_num(sc->enum_val, tok); + *rest = tok->next; - return new_var_node(sc->var, tok); + return node; } if (tok->kind == TK_STR) { diff --git a/test/enum.c b/test/enum.c new file mode 100644 index 0000000..ba580be --- /dev/null +++ b/test/enum.c @@ -0,0 +1,18 @@ +#include "test.h" + +int main() { + ASSERT(0, ({ enum { zero, one, two }; zero; })); + ASSERT(1, ({ enum { zero, one, two }; one; })); + ASSERT(2, ({ enum { zero, one, two }; two; })); + ASSERT(5, ({ enum { five=5, six, seven }; five; })); + ASSERT(6, ({ enum { five=5, six, seven }; six; })); + ASSERT(0, ({ enum { zero, five=5, three=3, four }; zero; })); + ASSERT(5, ({ enum { zero, five=5, three=3, four }; five; })); + ASSERT(3, ({ enum { zero, five=5, three=3, four }; three; })); + ASSERT(4, ({ enum { zero, five=5, three=3, four }; four; })); + ASSERT(4, ({ enum { zero, one, two } x; sizeof(x); })); + ASSERT(4, ({ enum t { zero, one, two }; enum t y; sizeof(y); })); + + printf("OK\n"); + return 0; +} diff --git a/tokenize.c b/tokenize.c index eb7715f..463d89a 100644 --- a/tokenize.c +++ b/tokenize.c @@ -125,6 +125,7 @@ static bool is_keyword(Token *tok) { static char *kw[] = { "return", "if", "else", "for", "while", "int", "sizeof", "char", "struct", "union", "short", "long", "void", "typedef", "_Bool", + "enum", }; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) diff --git a/type.c b/type.c index 9c8b170..81e581a 100644 --- a/type.c +++ b/type.c @@ -19,7 +19,7 @@ static Type *new_type(TypeKind kind, int size, int align) { bool is_integer(Type *ty) { TypeKind k = ty->kind; return k == TY_BOOL || k == TY_CHAR || k == TY_SHORT || - k == TY_INT || k == TY_LONG; + k == TY_INT || k == TY_LONG || k == TY_ENUM; } Type *copy_type(Type *ty) { @@ -48,6 +48,10 @@ Type *array_of(Type *base, int len) { return ty; } +Type *enum_type(void) { + return new_type(TY_ENUM, 4, 4); +} + static Type *get_common_type(Type *ty1, Type *ty2) { if (ty1->base) return pointer_to(ty1->base);