mirror of
https://github.com/rui314/chibicc
synced 2024-11-22 06:11:18 +03:00
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:
parent
f46370ef98
commit
a6b82da1ae
121
parse.c
121
parse.c
@ -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
17
test/typedef.c
Normal 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;
|
||||
}
|
@ -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++)
|
||||
|
Loading…
Reference in New Issue
Block a user