Add typedef

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;
This commit is contained in:
Rui Ueyama 2020-09-04 17:23:26 +09:00
parent f46370ef98
commit a6b82da1ae
3 changed files with 110 additions and 30 deletions

121
parse.c
View File

@ -18,12 +18,13 @@
#include "chibicc.h"
// Scope for local or global variables.
// Scope for local, global variables or typedefs.
typedef struct VarScope VarScope;
struct VarScope {
VarScope *next;
char *name;
Obj *var;
Type *type_def;
};
// Scope for struct or union tags
@ -45,6 +46,11 @@ struct Scope {
TagScope *tags;
};
// Variable attributes such as typedef or extern.
typedef struct {
bool is_typedef;
} VarAttr;
// All local variable instances created during parsing are
// accumulated to this list.
static Obj *locals;
@ -55,9 +61,9 @@ static Obj *globals;
static Scope *scope = &(Scope){};
static bool is_typename(Token *tok);
static Type *declspec(Token **rest, Token *tok);
static Type *declspec(Token **rest, Token *tok, VarAttr *attr);
static Type *declarator(Token **rest, Token *tok, Type *ty);
static Node *declaration(Token **rest, Token *tok);
static Node *declaration(Token **rest, Token *tok, Type *basety);
static Node *compound_stmt(Token **rest, Token *tok);
static Node *stmt(Token **rest, Token *tok);
static Node *expr_stmt(Token **rest, Token *tok);
@ -72,6 +78,7 @@ static Type *union_decl(Token **rest, Token *tok);
static Node *postfix(Token **rest, Token *tok);
static Node *unary(Token **rest, Token *tok);
static Node *primary(Token **rest, Token *tok);
static Token *parse_typedef(Token *tok, Type *basety);
static void enter_scope(void) {
Scope *sc = calloc(1, sizeof(Scope));
@ -84,11 +91,11 @@ static void leave_scope(void) {
}
// Find a variable by name.
static Obj *find_var(Token *tok) {
static VarScope *find_var(Token *tok) {
for (Scope *sc = scope; sc; sc = sc->next)
for (VarScope *sc2 = sc->vars; sc2; sc2 = sc2->next)
if (equal(tok, sc2->name))
return sc2->var;
return sc2;
return NULL;
}
@ -132,10 +139,9 @@ static Node *new_var_node(Obj *var, Token *tok) {
return node;
}
static VarScope *push_scope(char *name, Obj *var) {
static VarScope *push_scope(char *name) {
VarScope *sc = calloc(1, sizeof(VarScope));
sc->name = name;
sc->var = var;
sc->next = scope->vars;
scope->vars = sc;
return sc;
@ -145,7 +151,7 @@ static Obj *new_var(char *name, Type *ty) {
Obj *var = calloc(1, sizeof(Obj));
var->name = name;
var->ty = ty;
push_scope(name, var);
push_scope(name)->var = var;
return var;
}
@ -185,7 +191,16 @@ static char *get_ident(Token *tok) {
return strndup(tok->loc, tok->len);
}
static int get_number(Token *tok) {
static Type *find_typedef(Token *tok) {
if (tok->kind == TK_IDENT) {
VarScope *sc = find_var(tok);
if (sc)
return sc->type_def;
}
return NULL;
}
static long get_number(Token *tok) {
if (tok->kind != TK_NUM)
error_tok(tok, "expected a number");
return tok->val;
@ -200,7 +215,8 @@ static void push_tag_scope(Token *tok, Type *ty) {
}
// declspec = ("void" | "char" | "short" | "int" | "long"
// | struct-decl | union-decl)+
// | "typedef"
// | struct-decl | union-decl | typedef-name)+
//
// The order of typenames in a type-specifier doesn't matter. For
// example, `int long static` means the same as `static long int`.
@ -213,7 +229,7 @@ static void push_tag_scope(Token *tok, Type *ty) {
// 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 *declspec(Token **rest, Token *tok) {
static Type *declspec(Token **rest, Token *tok, VarAttr *attr) {
// 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
@ -231,12 +247,30 @@ static Type *declspec(Token **rest, Token *tok) {
int counter = 0;
while (is_typename(tok)) {
// Handle "typedef" keyword
if (equal(tok, "typedef")) {
if (!attr)
error_tok(tok, "storage class specifier is not allowed in this context");
attr->is_typedef = true;
tok = tok->next;
continue;
}
// Handle user-defined types.
if (equal(tok, "struct") || equal(tok, "union")) {
if (equal(tok, "struct"))
Type *ty2 = find_typedef(tok);
if (equal(tok, "struct") || equal(tok, "union") || ty2) {
if (counter)
break;
if (equal(tok, "struct")) {
ty = struct_decl(&tok, tok->next);
else
} else if (equal(tok, "union")) {
ty = union_decl(&tok, tok->next);
} else {
ty = ty2;
tok = tok->next;
}
counter += OTHER;
continue;
}
@ -295,7 +329,7 @@ static Type *func_params(Token **rest, Token *tok, Type *ty) {
while (!equal(tok, ")")) {
if (cur != &head)
tok = skip(tok, ",");
Type *basety = declspec(&tok, tok);
Type *basety = declspec(&tok, tok, NULL);
Type *ty = declarator(&tok, tok, basety);
cur = cur->next = copy_type(ty);
}
@ -346,9 +380,7 @@ static Type *declarator(Token **rest, Token *tok, Type *ty) {
}
// declaration = declspec (declarator ("=" expr)? ("," declarator ("=" expr)?)*)? ";"
static Node *declaration(Token **rest, Token *tok) {
Type *basety = declspec(&tok, tok);
static Node *declaration(Token **rest, Token *tok, Type *basety) {
Node head = {};
Node *cur = &head;
int i = 0;
@ -382,12 +414,13 @@ static Node *declaration(Token **rest, Token *tok) {
static bool is_typename(Token *tok) {
static char *kw[] = {
"void", "char", "short", "int", "long", "struct", "union",
"typedef",
};
for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)
if (equal(tok, kw[i]))
return true;
return false;
return find_typedef(tok);
}
// stmt = "return" expr ";"
@ -449,7 +482,7 @@ static Node *stmt(Token **rest, Token *tok) {
return expr_stmt(rest, tok);
}
// compound-stmt = (declaration | stmt)* "}"
// compound-stmt = (typedef | declaration | stmt)* "}"
static Node *compound_stmt(Token **rest, Token *tok) {
Node *node = new_node(ND_BLOCK, tok);
Node head = {};
@ -458,10 +491,19 @@ static Node *compound_stmt(Token **rest, Token *tok) {
enter_scope();
while (!equal(tok, "}")) {
if (is_typename(tok))
cur = cur->next = declaration(&tok, tok);
else
if (is_typename(tok)) {
VarAttr attr = {};
Type *basety = declspec(&tok, tok, &attr);
if (attr.is_typedef) {
tok = parse_typedef(tok, basety);
continue;
}
cur = cur->next = declaration(&tok, tok, basety);
} else {
cur = cur->next = stmt(&tok, tok);
}
add_type(cur);
}
@ -685,7 +727,7 @@ static void struct_members(Token **rest, Token *tok, Type *ty) {
Member *cur = &head;
while (!equal(tok, "}")) {
Type *basety = declspec(&tok, tok);
Type *basety = declspec(&tok, tok, NULL);
int i = 0;
while (!consume(&tok, tok, ";")) {
@ -875,11 +917,11 @@ static Node *primary(Token **rest, Token *tok) {
return funcall(rest, tok);
// Variable
Obj *var = find_var(tok);
if (!var)
VarScope *sc = find_var(tok);
if (!sc || !sc->var)
error_tok(tok, "undefined variable");
*rest = tok->next;
return new_var_node(var, tok);
return new_var_node(sc->var, tok);
}
if (tok->kind == TK_STR) {
@ -897,6 +939,20 @@ static Node *primary(Token **rest, Token *tok) {
error_tok(tok, "expected an expression");
}
static Token *parse_typedef(Token *tok, Type *basety) {
bool first = true;
while (!consume(&tok, tok, ";")) {
if (!first)
tok = skip(tok, ",");
first = false;
Type *ty = declarator(&tok, tok, basety);
push_scope(get_ident(ty->name))->type_def = ty;
}
return tok;
}
static void create_param_lvars(Type *param) {
if (param) {
create_param_lvars(param->next);
@ -951,12 +1007,19 @@ static bool is_function(Token *tok) {
return ty->kind == TY_FUNC;
}
// program = (function-definition | global-variable)*
// program = (typedef | function-definition | global-variable)*
Obj *parse(Token *tok) {
globals = NULL;
while (tok->kind != TK_EOF) {
Type *basety = declspec(&tok, tok);
VarAttr attr = {};
Type *basety = declspec(&tok, tok, &attr);
// Typedef
if (attr.is_typedef) {
tok = parse_typedef(tok, basety);
continue;
}
// Function
if (is_function(tok)) {

17
test/typedef.c Normal file
View File

@ -0,0 +1,17 @@
#include "test.h"
typedef int MyInt, MyInt2[4];
typedef int;
int main() {
ASSERT(1, ({ typedef int t; t x=1; x; }));
ASSERT(1, ({ typedef struct {int a;} t; t x; x.a=1; x.a; }));
ASSERT(1, ({ typedef int t; t t=1; t; }));
ASSERT(2, ({ typedef struct {int a;} t; { typedef int t; } t x; x.a=2; x.a; }));
ASSERT(4, ({ typedef t; t x; sizeof(x); }));
ASSERT(3, ({ MyInt x=3; x; }));
ASSERT(16, ({ MyInt2 x; sizeof(x); }));
printf("OK\n");
return 0;
}

View File

@ -126,7 +126,7 @@ static int read_punct(char *p) {
static bool is_keyword(Token *tok) {
static char *kw[] = {
"return", "if", "else", "for", "while", "int", "sizeof", "char",
"struct", "union", "short", "long", "void",
"struct", "union", "short", "long", "void", "typedef",
};
for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)