This commit is contained in:
Rui Ueyama 2019-08-11 19:59:27 +09:00
parent aa0accc75e
commit 48ba2656fe
5 changed files with 103 additions and 11 deletions

View File

@ -161,6 +161,7 @@ typedef enum {
TY_SHORT,
TY_INT,
TY_LONG,
TY_ENUM,
TY_PTR,
TY_FUNC,
TY_ARRAY,
@ -219,6 +220,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);
//

87
parse.c
View File

@ -18,16 +18,19 @@
#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;
Obj *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;
@ -40,8 +43,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;
};
@ -65,6 +68,7 @@ static Obj *current_fn;
static bool is_typename(Token *tok);
static Type *declspec(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);
@ -238,7 +242,8 @@ static void push_tag_scope(Token *tok, Type *ty) {
// declspec = ("void" | "_Bool" | "char" | "short" | "int" | "long"
// | "typedef"
// | struct-decl | union-decl | typedef-name)+
// | struct-decl | union-decl | typedef-name
// | enum-specifier)+
//
// The order of typenames in a type-specifier doesn't matter. For
// example, `int long static` means the same as `static long int`.
@ -281,7 +286,7 @@ static Type *declspec(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;
@ -289,6 +294,8 @@ static Type *declspec(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;
@ -432,6 +439,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, "{")) {
Type *ty = find_tag(tag);
if (!ty)
error_tok(tag, "unknown enum type");
if (ty->kind != TY_ENUM)
error_tok(tag, "not an enum tag");
*rest = tok;
return 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 = declspec (declarator ("=" expr)? ("," declarator ("=" expr)?)*)? ";"
static Node *declaration(Token **rest, Token *tok, Type *basety) {
Node head = {};
@ -467,7 +527,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++)
@ -1017,12 +1077,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) {

18
test/enum.c Normal file
View File

@ -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;
}

View File

@ -128,6 +128,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++)

6
type.c
View File

@ -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);