From b253dbf95cf5b75be4ca1f2925dbc2e06d8b8c18 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Sun, 11 Aug 2019 19:59:27 +0900 Subject: [PATCH] Add enum --- chibi.h | 2 ++ parse.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++------ tests | 12 ++++++++ tokenize.c | 2 +- type.c | 4 +++ 5 files changed, 94 insertions(+), 10 deletions(-) diff --git a/chibi.h b/chibi.h index 70fbc13..2fdec6d 100644 --- a/chibi.h +++ b/chibi.h @@ -172,6 +172,7 @@ typedef enum { TY_SHORT, TY_INT, TY_LONG, + TY_ENUM, TY_PTR, TY_ARRAY, TY_STRUCT, @@ -208,6 +209,7 @@ int align_to(int n, int align); Type *pointer_to(Type *base); Type *array_of(Type *base, int size); Type *func_type(Type *return_ty); +Type *enum_type(void); void add_type(Node *node); // diff --git a/parse.c b/parse.c index 2c2a710..a2ee539 100644 --- a/parse.c +++ b/parse.c @@ -1,15 +1,19 @@ #include "chibi.h" -// Scope for local variables, global variables or typedefs +// Scope for local variables, global variables, typedefs +// or enum constants typedef struct VarScope VarScope; struct VarScope { VarScope *next; char *name; + Var *var; Type *type_def; + Type *enum_ty; + int enum_val; }; -// Scope for struct tags +// Scope for struct or enum tags typedef struct TagScope TagScope; struct TagScope { TagScope *next; @@ -30,7 +34,7 @@ static VarList *locals; static VarList *globals; // C has two block scopes; one is for variables/typedefs and -// the other is for struct tags. +// the other is for struct/union/enum tags. static VarScope *var_scope; static TagScope *tag_scope; @@ -158,6 +162,7 @@ static Type *abstract_declarator(Type *ty); static Type *type_suffix(Type *ty); static Type *type_name(void); static Type *struct_decl(void); +static Type *enum_specifier(void); static Member *struct_member(void); static void global_var(void); static Node *declaration(void); @@ -215,7 +220,7 @@ Program *program(void) { return prog; } -// basetype = builtin-type | struct-decl | typedef-name +// basetype = builtin-type | struct-decl | typedef-name | enum-specifier // // builtin-type = "void" | "_Bool" | "char" | "short" | "int" // | "long" | "long" "long" @@ -261,6 +266,8 @@ static Type *basetype(bool *is_typedef) { if (peek("struct")) { ty = struct_decl(); + } else if (peek("enum")) { + ty = enum_specifier(); } else { ty = find_typedef(token); assert(ty); @@ -383,6 +390,8 @@ static Type *struct_decl(void) { TagScope *sc = find_tag(tag); if (!sc) error_tok(tag, "unknown struct type"); + if (sc->ty->kind != TY_STRUCT) + error_tok(tag, "not a struct tag"); return sc->ty; } @@ -419,6 +428,59 @@ static Type *struct_decl(void) { return ty; } +// Some types of list can end with an optional "," followed by "}" +// to allow a trailing comma. This function returns true if it looks +// like we are at the end of such list. +static bool consume_end(void) { + Token *tok = token; + if (consume("}") || (consume(",") && consume("}"))) + return true; + token = tok; + return false; +} + +// enum-specifier = "enum" ident +// | "enum" ident? "{" enum-list? "}" +// +// enum-list = ident ("=" num)? ("," ident ("=" num)?)* ","? +static Type *enum_specifier(void) { + expect("enum"); + Type *ty = enum_type(); + + // Read an enum tag. + Token *tag = consume_ident(); + if (tag && !peek("{")) { + 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"); + return sc->ty; + } + + expect("{"); + + // Read enum-list. + int cnt = 0; + for (;;) { + char *name = expect_ident(); + if (consume("=")) + cnt = expect_number(); + + VarScope *sc = push_scope(name); + sc->enum_ty = ty; + sc->enum_val = cnt++; + + if (consume_end()) + break; + expect(","); + } + + if (tag) + push_tag_scope(tag, ty); + return ty; +} + // struct-member = basetype declarator type-suffix ";" static Member *struct_member(void) { Type *ty = basetype(NULL); @@ -558,8 +620,8 @@ static Node *read_expr_stmt(void) { // Returns true if the next token represents a type. static bool is_typename(void) { return peek("void") || peek("_Bool") || peek("char") || peek("short") || - peek("int") || peek("long") || peek("struct") || peek("typedef") || - find_typedef(token); + peek("int") || peek("long") || peek("enum") || peek("struct") || + peek("typedef") || find_typedef(token); } static Node *stmt(void) { @@ -927,10 +989,14 @@ static Node *primary(void) { return node; } - // Variable + // Variable or enum constant VarScope *sc = find_var(tok); - if (sc && sc->var) - return new_var_node(sc->var, tok); + if (sc) { + if (sc->var) + return new_var_node(sc->var, tok); + if (sc->enum_ty) + return new_num(sc->enum_val, tok); + } error_tok(tok, "undefined variable"); } diff --git a/tests b/tests index 1218b5e..a0855c1 100644 --- a/tests +++ b/tests @@ -332,6 +332,18 @@ int main() { assert(97, 'a', "'a'"); assert(10, '\n', "\'\\n\'"); + assert(0, ({ enum { zero, one, two }; zero; }), "enum { zero, one, two }; zero;"); + assert(1, ({ enum { zero, one, two }; one; }), "enum { zero, one, two }; one;"); + assert(2, ({ enum { zero, one, two }; two; }), "enum { zero, one, two }; two;"); + assert(5, ({ enum { five=5, six, seven }; five; }), "enum { five=5, six, seven }; five;"); + assert(6, ({ enum { five=5, six, seven }; six; }), "enum { five=5, six, seven }; six;"); + assert(0, ({ enum { zero, five=5, three=3, four }; zero; }), "enum { zero, five=5, three=3, four }; zero;"); + assert(5, ({ enum { zero, five=5, three=3, four }; five; }), "enum { zero, five=5, three=3, four }; five;"); + assert(3, ({ enum { zero, five=5, three=3, four }; three; }), "enum { zero, five=5, three=3, four }; three;"); + assert(4, ({ enum { zero, five=5, three=3, four }; four; }), "enum { zero, five=5, three=3, four }; four;"); + assert(4, ({ enum { zero, one, two } x; sizeof(x); }), "enum { zero, one, two } x; sizeof(x);"); + assert(4, ({ enum t { zero, one, two }; enum t y; sizeof(y); }), "enum t { zero, one, two }; enum t y; sizeof(y);"); + printf("OK\n"); return 0; } diff --git a/tokenize.c b/tokenize.c index 7fcdcce..ff3bb2d 100644 --- a/tokenize.c +++ b/tokenize.c @@ -149,7 +149,7 @@ static char *starts_with_reserved(char *p) { // Keyword static char *kw[] = {"return", "if", "else", "while", "for", "int", "char", "sizeof", "struct", "typedef", "short", - "long", "void", "_Bool"}; + "long", "void", "_Bool", "enum"}; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) { int len = strlen(kw[i]); diff --git a/type.c b/type.c index c9f866f..ad56d74 100644 --- a/type.c +++ b/type.c @@ -44,6 +44,10 @@ Type *func_type(Type *return_ty) { return ty; } +Type *enum_type(void) { + return new_type(TY_ENUM, 4, 4); +} + void add_type(Node *node) { if (!node || node->ty) return;