mirror of https://github.com/rui314/chibicc
1588 lines
36 KiB
C
1588 lines
36 KiB
C
#include "chibicc.h"
|
|
|
|
// Scope for local variables, global variables or typedefs
|
|
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 tags
|
|
typedef struct TagScope TagScope;
|
|
struct TagScope {
|
|
TagScope *next;
|
|
char *name;
|
|
int depth;
|
|
Type *ty;
|
|
};
|
|
|
|
typedef struct {
|
|
VarScope *var_scope;
|
|
TagScope *tag_scope;
|
|
} Scope;
|
|
|
|
VarList *locals;
|
|
VarList *globals;
|
|
|
|
VarScope *var_scope;
|
|
TagScope *tag_scope;
|
|
int scope_depth;
|
|
|
|
Node *current_switch;
|
|
|
|
Scope *enter_scope() {
|
|
Scope *sc = calloc(1, sizeof(Scope));
|
|
sc->var_scope = var_scope;
|
|
sc->tag_scope = tag_scope;
|
|
++scope_depth;
|
|
return sc;
|
|
}
|
|
|
|
void leave_scope(Scope *sc) {
|
|
var_scope = sc->var_scope;
|
|
tag_scope = sc->tag_scope;
|
|
--scope_depth;
|
|
}
|
|
|
|
// Find a variable or a typedef by name.
|
|
VarScope *find_var(Token *tok) {
|
|
for (VarScope *sc = var_scope; sc; sc = sc->next) {
|
|
if (strlen(sc->name) == tok->len && !memcmp(tok->str, sc->name, tok->len))
|
|
return sc;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
TagScope *find_tag(Token *tok) {
|
|
for (TagScope *sc = tag_scope; sc; sc = sc->next)
|
|
if (strlen(sc->name) == tok->len && !memcmp(tok->str, sc->name, tok->len))
|
|
return sc;
|
|
return NULL;
|
|
}
|
|
|
|
Node *new_node(NodeKind kind, Token *tok) {
|
|
Node *node = calloc(1, sizeof(Node));
|
|
node->kind = kind;
|
|
node->tok = tok;
|
|
return node;
|
|
}
|
|
|
|
Node *new_binary(NodeKind kind, Node *lhs, Node *rhs, Token *tok) {
|
|
Node *node = new_node(kind, tok);
|
|
node->lhs = lhs;
|
|
node->rhs = rhs;
|
|
return node;
|
|
}
|
|
|
|
Node *new_unary(NodeKind kind, Node *expr, Token *tok) {
|
|
Node *node = new_node(kind, tok);
|
|
node->lhs = expr;
|
|
return node;
|
|
}
|
|
|
|
Node *new_num(long val, Token *tok) {
|
|
Node *node = new_node(ND_NUM, tok);
|
|
node->val = val;
|
|
return node;
|
|
}
|
|
|
|
Node *new_var(Var *var, Token *tok) {
|
|
Node *node = new_node(ND_VAR, tok);
|
|
node->var = var;
|
|
return node;
|
|
}
|
|
|
|
VarScope *push_scope(char *name) {
|
|
VarScope *sc = calloc(1, sizeof(VarScope));
|
|
sc->name = name;
|
|
sc->next = var_scope;
|
|
sc->depth = scope_depth;
|
|
var_scope = sc;
|
|
return sc;
|
|
}
|
|
|
|
Var *push_var(char *name, Type *ty, bool is_local, Token *tok) {
|
|
Var *var = calloc(1, sizeof(Var));
|
|
var->name = name;
|
|
var->ty = ty;
|
|
var->is_local = is_local;
|
|
var->tok = tok;
|
|
|
|
VarList *vl = calloc(1, sizeof(VarList));
|
|
vl->var = var;
|
|
if (is_local) {
|
|
vl->next = locals;
|
|
locals = vl;
|
|
} else if (ty->kind != TY_FUNC) {
|
|
vl->next = globals;
|
|
globals = vl;
|
|
}
|
|
return var;
|
|
}
|
|
|
|
Type *find_typedef(Token *tok) {
|
|
if (tok->kind == TK_IDENT) {
|
|
VarScope *sc = find_var(token);
|
|
if (sc)
|
|
return sc->type_def;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *new_label() {
|
|
static int cnt = 0;
|
|
char buf[20];
|
|
sprintf(buf, ".L.data.%d", cnt++);
|
|
return strndup(buf, 20);
|
|
}
|
|
|
|
Function *function();
|
|
Type *type_specifier();
|
|
Type *declarator(Type *ty, char **name);
|
|
Type *abstract_declarator(Type *ty);
|
|
Type *type_suffix(Type *ty);
|
|
Type *type_name();
|
|
Type *struct_decl();
|
|
Type *enum_specifier();
|
|
Member *struct_member();
|
|
void global_var();
|
|
Node *declaration();
|
|
bool is_typename();
|
|
Node *stmt();
|
|
Node *expr();
|
|
long eval(Node *node);
|
|
long eval2(Node *node, Var **var);
|
|
long const_expr();
|
|
Node *assign();
|
|
Node *conditional();
|
|
Node *logor();
|
|
Node *logand();
|
|
Node *bitand();
|
|
Node *bitor();
|
|
Node *bitxor();
|
|
Node *equality();
|
|
Node *relational();
|
|
Node *shift();
|
|
Node *add();
|
|
Node *mul();
|
|
Node *cast();
|
|
Node *unary();
|
|
Node *postfix();
|
|
Node *primary();
|
|
|
|
bool is_function() {
|
|
Token *tok = token;
|
|
|
|
Type *ty = type_specifier();
|
|
char *name = NULL;
|
|
declarator(ty, &name);
|
|
bool isfunc = name && consume("(");
|
|
|
|
token = tok;
|
|
return isfunc;
|
|
}
|
|
|
|
// program = (global-var | function)*
|
|
Program *program() {
|
|
Function head;
|
|
head.next = NULL;
|
|
Function *cur = &head;
|
|
globals = NULL;
|
|
|
|
while (!at_eof()) {
|
|
if (is_function()) {
|
|
Function *fn = function();
|
|
if (!fn)
|
|
continue;
|
|
cur->next = fn;
|
|
cur = cur->next;
|
|
continue;
|
|
}
|
|
|
|
global_var();
|
|
}
|
|
|
|
Program *prog = calloc(1, sizeof(Program));
|
|
prog->globals = globals;
|
|
prog->fns = head.next;
|
|
return prog;
|
|
}
|
|
|
|
// type-specifier = builtin-type | struct-decl | typedef-name | enum-specifier
|
|
//
|
|
// builtin-type = "void"
|
|
// | "_Bool"
|
|
// | "char"
|
|
// | "short" | "short" "int" | "int" "short"
|
|
// | "int"
|
|
// | "long" | "long" "int" | "int" "long"
|
|
//
|
|
// Note that "typedef" and "static" can appear anywhere in a type-specifier.
|
|
Type *type_specifier() {
|
|
if (!is_typename(token))
|
|
error_tok(token, "typename expected");
|
|
|
|
Type *ty = NULL;
|
|
|
|
enum {
|
|
VOID = 1 << 1,
|
|
BOOL = 1 << 3,
|
|
CHAR = 1 << 5,
|
|
SHORT = 1 << 7,
|
|
INT = 1 << 9,
|
|
LONG = 1 << 11,
|
|
};
|
|
|
|
int base_type = 0;
|
|
Type *user_type = NULL;
|
|
|
|
bool is_typedef = false;
|
|
bool is_static = false;
|
|
|
|
for (;;) {
|
|
// Read one token at a time.
|
|
Token *tok = token;
|
|
if (consume("typedef")) {
|
|
is_typedef = true;
|
|
} else if (consume("static")) {
|
|
is_static = true;
|
|
} else if (consume("void")) {
|
|
base_type += VOID;
|
|
} else if (consume("_Bool")) {
|
|
base_type += BOOL;
|
|
} else if (consume("char")) {
|
|
base_type += CHAR;
|
|
} else if (consume("short")) {
|
|
base_type += SHORT;
|
|
} else if (consume("int")) {
|
|
base_type += INT;
|
|
} else if (consume("long")) {
|
|
base_type += LONG;
|
|
} else if (peek("struct")) {
|
|
if (base_type || user_type)
|
|
break;
|
|
user_type = struct_decl();
|
|
} else if (peek("enum")) {
|
|
if (base_type || user_type)
|
|
break;
|
|
user_type = enum_specifier();
|
|
} else {
|
|
if (base_type || user_type)
|
|
break;
|
|
Type *ty = find_typedef(token);
|
|
if (!ty)
|
|
break;
|
|
token = token->next;
|
|
user_type = ty;
|
|
}
|
|
|
|
switch (base_type) {
|
|
case VOID:
|
|
ty = void_type();
|
|
break;
|
|
case BOOL:
|
|
ty = bool_type();
|
|
break;
|
|
case CHAR:
|
|
ty = char_type();
|
|
break;
|
|
case SHORT:
|
|
case SHORT + INT:
|
|
ty = short_type();
|
|
break;
|
|
case INT:
|
|
ty = int_type();
|
|
break;
|
|
case LONG:
|
|
case LONG + INT:
|
|
ty = long_type();
|
|
break;
|
|
case 0:
|
|
// If there's no type specifier, it becomes int.
|
|
// For example, `typedef x` defines x as an alias for int.
|
|
ty = user_type ? user_type : int_type();
|
|
break;
|
|
default:
|
|
error_tok(tok, "invalid type");
|
|
}
|
|
}
|
|
|
|
ty->is_typedef = is_typedef;
|
|
ty->is_static = is_static;
|
|
return ty;
|
|
}
|
|
|
|
// declarator = "*"* ("(" declarator ")" | ident) type-suffix
|
|
Type *declarator(Type *ty, char **name) {
|
|
while (consume("*"))
|
|
ty = pointer_to(ty);
|
|
|
|
if (consume("(")) {
|
|
Type *placeholder = calloc(1, sizeof(Type));
|
|
Type *new_ty = declarator(placeholder, name);
|
|
expect(")");
|
|
*placeholder = *type_suffix(ty);
|
|
return new_ty;
|
|
}
|
|
|
|
*name = expect_ident();
|
|
return type_suffix(ty);
|
|
}
|
|
|
|
// abstract-declarator = "*"* ("(" abstract-declarator ")")? type-suffix
|
|
Type *abstract_declarator(Type *ty) {
|
|
while (consume("*"))
|
|
ty = pointer_to(ty);
|
|
|
|
if (consume("(")) {
|
|
Type *placeholder = calloc(1, sizeof(Type));
|
|
Type *new_ty = abstract_declarator(placeholder);
|
|
expect(")");
|
|
*placeholder = *type_suffix(ty);
|
|
return new_ty;
|
|
}
|
|
return type_suffix(ty);
|
|
}
|
|
|
|
// type-suffix = ("[" const-expr? "]" type-suffix)?
|
|
Type *type_suffix(Type *ty) {
|
|
if (!consume("["))
|
|
return ty;
|
|
|
|
int sz = 0;
|
|
bool is_incomplete = true;
|
|
if (!consume("]")) {
|
|
sz = const_expr();
|
|
is_incomplete = false;
|
|
expect("]");
|
|
}
|
|
|
|
ty = type_suffix(ty);
|
|
ty = array_of(ty, sz);
|
|
ty->is_incomplete = is_incomplete;
|
|
return ty;
|
|
}
|
|
|
|
// type-name = type-specifier abstract-declarator type-suffix
|
|
Type *type_name() {
|
|
Type *ty = type_specifier();
|
|
ty = abstract_declarator(ty);
|
|
return type_suffix(ty);
|
|
}
|
|
|
|
void push_tag_scope(Token *tok, Type *ty) {
|
|
TagScope *sc = calloc(1, sizeof(TagScope));
|
|
sc->next = tag_scope;
|
|
sc->name = strndup(tok->str, tok->len);
|
|
sc->depth = scope_depth;
|
|
sc->ty = ty;
|
|
tag_scope = sc;
|
|
}
|
|
|
|
// struct-decl = "struct" ident? ("{" struct-member "}")?
|
|
Type *struct_decl() {
|
|
// Read a struct tag.
|
|
expect("struct");
|
|
Token *tag = consume_ident();
|
|
if (tag && !peek("{")) {
|
|
TagScope *sc = find_tag(tag);
|
|
|
|
if (!sc) {
|
|
Type *ty = struct_type();
|
|
push_tag_scope(tag, ty);
|
|
return ty;
|
|
}
|
|
|
|
if (sc->ty->kind != TY_STRUCT)
|
|
error_tok(tag, "not a struct tag");
|
|
return sc->ty;
|
|
}
|
|
|
|
// Although it looks weird, "struct *foo" is legal C that defines
|
|
// foo as a pointer to an unnamed incomplete struct type.
|
|
if (!consume("{"))
|
|
return struct_type();
|
|
|
|
TagScope *sc = find_tag(tag);
|
|
Type *ty;
|
|
|
|
if (sc && sc->depth == scope_depth) {
|
|
// If there's an existing struct type having the same tag name in
|
|
// the same block scope, this is a redefinition.
|
|
if (sc->ty->kind != TY_STRUCT)
|
|
error_tok(tag, "not a struct tag");
|
|
ty = sc->ty;
|
|
} else {
|
|
// Register a struct type as an incomplete type early, so that you
|
|
// can write recursive structs such as
|
|
// "struct T { struct T *next; }".
|
|
ty = struct_type();
|
|
if (tag)
|
|
push_tag_scope(tag, ty);
|
|
}
|
|
|
|
// Read struct members.
|
|
Member head;
|
|
head.next = NULL;
|
|
Member *cur = &head;
|
|
|
|
while (!consume("}")) {
|
|
cur->next = struct_member();
|
|
cur = cur->next;
|
|
}
|
|
|
|
ty->members = head.next;
|
|
|
|
// Assign offsets within the struct to members.
|
|
int offset = 0;
|
|
for (Member *mem = ty->members; mem; mem = mem->next) {
|
|
offset = align_to(offset, mem->ty->align);
|
|
mem->offset = offset;
|
|
offset += size_of(mem->ty, mem->tok);
|
|
|
|
if (ty->align < mem->ty->align)
|
|
ty->align = mem->ty->align;
|
|
}
|
|
|
|
ty->is_incomplete = false;
|
|
return ty;
|
|
}
|
|
|
|
// enum-specifier = "enum" ident
|
|
// | "enum" ident? "{" enum-list? "}"
|
|
//
|
|
// enum-list = enum-elem ("," enum-elem)* ","?
|
|
// enum-elem = ident ("=" const-expr)?
|
|
Type *enum_specifier() {
|
|
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 = const_expr();
|
|
|
|
VarScope *sc = push_scope(name);
|
|
sc->enum_ty = ty;
|
|
sc->enum_val = cnt++;
|
|
|
|
if (consume(",")) {
|
|
if (consume("}"))
|
|
break;
|
|
continue;
|
|
}
|
|
expect("}");
|
|
break;
|
|
}
|
|
|
|
if (tag)
|
|
push_tag_scope(tag, ty);
|
|
return ty;
|
|
}
|
|
|
|
// struct-member = type-specifier declarator type-suffix ";"
|
|
Member *struct_member() {
|
|
Type *ty = type_specifier();
|
|
Token *tok = token;
|
|
char *name = NULL;
|
|
ty = declarator(ty, &name);
|
|
ty = type_suffix(ty);
|
|
expect(";");
|
|
|
|
Member *mem = calloc(1, sizeof(Member));
|
|
mem->name = name;
|
|
mem->ty = ty;
|
|
mem->tok = tok;
|
|
return mem;
|
|
}
|
|
|
|
VarList *read_func_param() {
|
|
Type *ty = type_specifier();
|
|
char *name = NULL;
|
|
Token *tok = token;
|
|
ty = declarator(ty, &name);
|
|
ty = type_suffix(ty);
|
|
|
|
// "array of T" is converted to "pointer to T" only in the parameter
|
|
// context. For example, *argv[] is converted to **argv by this.
|
|
if (ty->kind == TY_ARRAY)
|
|
ty = pointer_to(ty->base);
|
|
|
|
Var *var = push_var(name, ty, true, tok);
|
|
push_scope(name)->var = var;
|
|
|
|
VarList *vl = calloc(1, sizeof(VarList));
|
|
vl->var = var;
|
|
return vl;
|
|
}
|
|
|
|
VarList *read_func_params() {
|
|
if (consume(")"))
|
|
return NULL;
|
|
|
|
Token *tok = token;
|
|
if (consume("void") && consume(")"))
|
|
return NULL;
|
|
token = tok;
|
|
|
|
VarList *head = read_func_param();
|
|
VarList *cur = head;
|
|
|
|
while (!consume(")")) {
|
|
expect(",");
|
|
cur->next = read_func_param();
|
|
cur = cur->next;
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
// function = type-specifier declarator "(" params? ")" ("{" stmt* "}" | ";")
|
|
// params = param ("," param)* | "void"
|
|
// param = type-specifier declarator type-suffix
|
|
Function *function() {
|
|
locals = NULL;
|
|
|
|
Type *ty = type_specifier();
|
|
char *name = NULL;
|
|
Token *tok = token;
|
|
ty = declarator(ty, &name);
|
|
|
|
// Add a function type to the scope
|
|
Var *var = push_var(name, func_type(ty), false, tok);
|
|
push_scope(name)->var = var;
|
|
|
|
// Construct a function object
|
|
Function *fn = calloc(1, sizeof(Function));
|
|
fn->name = name;
|
|
fn->is_static = ty->is_static;
|
|
expect("(");
|
|
fn->params = read_func_params();
|
|
|
|
if (consume(";"))
|
|
return NULL;
|
|
|
|
// Read function body
|
|
Node head;
|
|
head.next = NULL;
|
|
Node *cur = &head;
|
|
expect("{");
|
|
while (!consume("}")) {
|
|
cur->next = stmt();
|
|
cur = cur->next;
|
|
}
|
|
|
|
fn->node = head.next;
|
|
fn->locals = locals;
|
|
return fn;
|
|
}
|
|
|
|
// Initializer list can end either with "}" or "," followed by "}"
|
|
// to allow a trailing comma. This function returns true if it looks
|
|
// like we are at the end of an initializer list.
|
|
bool peek_end() {
|
|
Token *tok = token;
|
|
bool ret = consume("}") || (consume(",") && consume("}"));
|
|
token = tok;
|
|
return ret;
|
|
}
|
|
|
|
bool consume_end() {
|
|
Token *tok = token;
|
|
if (consume("}") || (consume(",") && consume("}")))
|
|
return true;
|
|
token = tok;
|
|
return false;
|
|
}
|
|
|
|
// global-var = type-specifier declarator type-suffix ";"
|
|
Initializer *new_init_val(Initializer *cur, int sz, int val) {
|
|
Initializer *init = calloc(1, sizeof(Initializer));
|
|
init->sz = sz;
|
|
init->val = val;
|
|
cur->next = init;
|
|
return init;
|
|
}
|
|
|
|
Initializer *new_init_label(Initializer *cur, char *label, long addend) {
|
|
Initializer *init = calloc(1, sizeof(Initializer));
|
|
init->label = label;
|
|
init->addend = addend;
|
|
cur->next = init;
|
|
return init;
|
|
}
|
|
|
|
Initializer *new_init_zero(Initializer *cur, int nbytes) {
|
|
for (int i = 0; i < nbytes; i++)
|
|
cur = new_init_val(cur, 1, 0);
|
|
return cur;
|
|
}
|
|
|
|
Initializer *gvar_init_string(char *p, int len) {
|
|
Initializer head;
|
|
head.next = NULL;
|
|
Initializer *cur = &head;
|
|
for (int i = 0; i < len; i++)
|
|
cur = new_init_val(cur, 1, p[i]);
|
|
return head.next;
|
|
}
|
|
|
|
Initializer *emit_struct_padding(Initializer *cur, Type *parent, Member *mem) {
|
|
int end = mem->offset + size_of(mem->ty, token);
|
|
|
|
int padding;
|
|
if (mem->next)
|
|
padding = mem->next->offset - end;
|
|
else
|
|
padding = size_of(parent, token) - end;
|
|
|
|
if (padding)
|
|
cur = new_init_zero(cur, padding);
|
|
return cur;
|
|
}
|
|
|
|
void skip_excess_elements2() {
|
|
for (;;) {
|
|
if (consume("{"))
|
|
skip_excess_elements2();
|
|
else
|
|
assign();
|
|
|
|
if (consume_end())
|
|
return;
|
|
expect(",");
|
|
}
|
|
}
|
|
|
|
void skip_excess_elements() {
|
|
expect(",");
|
|
warn_tok(token, "excess elements in initializer");
|
|
skip_excess_elements2();
|
|
}
|
|
|
|
Initializer *gvar_initializer(Initializer *cur, Type *ty) {
|
|
Token *tok = token;
|
|
|
|
if (ty->kind == TY_ARRAY && ty->base->kind == TY_CHAR &&
|
|
token->kind == TK_STR) {
|
|
token = token->next;
|
|
|
|
if (ty->is_incomplete) {
|
|
ty->array_size = tok->cont_len;
|
|
ty->is_incomplete = false;
|
|
}
|
|
|
|
int len = (ty->array_size < tok->cont_len)
|
|
? ty->array_size : tok->cont_len;
|
|
|
|
for (int i = 0; i < len; i++)
|
|
cur = new_init_val(cur, 1, tok->contents[i]);
|
|
return new_init_zero(cur, ty->array_size - len);
|
|
}
|
|
|
|
if (ty->kind == TY_ARRAY) {
|
|
bool open = consume("{");
|
|
int i = 0;
|
|
int limit = ty->is_incomplete ? INT_MAX : ty->array_size;
|
|
|
|
do {
|
|
cur = gvar_initializer(cur, ty->base);
|
|
i++;
|
|
} while (i < limit && !peek_end() && consume(","));
|
|
|
|
if (open && !consume_end())
|
|
skip_excess_elements();
|
|
|
|
// Set excess array elements to zero.
|
|
cur = new_init_zero(cur, size_of(ty->base, tok) * (ty->array_size - i));
|
|
|
|
if (ty->is_incomplete) {
|
|
ty->array_size = i;
|
|
ty->is_incomplete = false;
|
|
}
|
|
|
|
return cur;
|
|
}
|
|
|
|
if (ty->kind == TY_STRUCT) {
|
|
bool open = consume("{");
|
|
Member *mem = ty->members;
|
|
|
|
do {
|
|
cur = gvar_initializer(cur, mem->ty);
|
|
cur = emit_struct_padding(cur, ty, mem);
|
|
mem = mem->next;
|
|
} while (mem && !peek_end() && consume(","));
|
|
|
|
if (open && !consume_end())
|
|
skip_excess_elements();
|
|
|
|
// Set excess struct elements to zero.
|
|
if (mem) {
|
|
int sz = size_of(ty, tok) - mem->offset;
|
|
if (sz)
|
|
cur = new_init_zero(cur, sz);
|
|
}
|
|
return cur;
|
|
}
|
|
|
|
bool open = consume("{");
|
|
Node *expr = conditional();
|
|
if (open)
|
|
expect("}");
|
|
|
|
Var *var = NULL;
|
|
long addend = eval2(expr, &var);
|
|
|
|
if (var)
|
|
return new_init_label(cur, var->name, addend);
|
|
return new_init_val(cur, size_of(ty, token), addend);
|
|
}
|
|
|
|
// global-var = type-specifier declarator type-suffix ("=" gvar-initializer)? ";"
|
|
void global_var() {
|
|
Type *ty = type_specifier();
|
|
char *name = NULL;
|
|
Token *tok = token;
|
|
ty = declarator(ty, &name);
|
|
ty = type_suffix(ty);
|
|
|
|
Var *var = push_var(name, ty, false, tok);
|
|
push_scope(name)->var = var;
|
|
|
|
if (consume("=")) {
|
|
Initializer head;
|
|
head.next = NULL;
|
|
gvar_initializer(&head, ty);
|
|
var->initializer = head.next;
|
|
}
|
|
expect(";");
|
|
}
|
|
|
|
typedef struct Designator Designator;
|
|
struct Designator {
|
|
Designator *next;
|
|
int idx; // array
|
|
Member *mem; // struct
|
|
};
|
|
|
|
// Creates a node for an array access. For example, if var represents
|
|
// a variable x and desg represents indices 3 and 4, this function
|
|
// returns a node representing x[3][4].
|
|
Node *new_desg_node2(Var *var, Designator *desg) {
|
|
Token *tok = var->tok;
|
|
if (!desg)
|
|
return new_var(var, tok);
|
|
|
|
Node *node = new_desg_node2(var, desg->next);
|
|
|
|
if (desg->mem) {
|
|
node = new_unary(ND_MEMBER, node, desg->mem->tok);
|
|
node->member_name = desg->mem->name;
|
|
return node;
|
|
}
|
|
|
|
node = new_binary(ND_ADD, node, new_num(desg->idx, tok), tok);
|
|
return new_unary(ND_DEREF, node, tok);
|
|
}
|
|
|
|
Node *new_desg_node(Var *var, Designator *desg, Node *rhs) {
|
|
Node *lhs = new_desg_node2(var, desg);
|
|
Node *node = new_binary(ND_ASSIGN, lhs, rhs, rhs->tok);
|
|
return new_unary(ND_EXPR_STMT, node, rhs->tok);
|
|
}
|
|
|
|
Node *lvar_init_zero(Node *cur, Var *var, Type *ty, Designator *desg) {
|
|
if (ty->kind == TY_ARRAY) {
|
|
for (int i = 0; i < ty->array_size; i++) {
|
|
Designator desg2 = {desg, i++, NULL};
|
|
cur = lvar_init_zero(cur, var, ty->base, &desg2);
|
|
}
|
|
return cur;
|
|
}
|
|
|
|
cur->next = new_desg_node(var, desg, new_num(0, token));
|
|
return cur->next;
|
|
}
|
|
|
|
// lvar-initializer = assign
|
|
// | "{" lvar-initializer ("," lvar-initializer)* ","? "}"
|
|
//
|
|
// An initializer for a local variable is expanded to multiple
|
|
// assignments. For example, this function creates the following
|
|
// nodes for x[2][3]={{1,2,3},{4,5,6}}.
|
|
//
|
|
// x[0][0]=1;
|
|
// x[0][1]=2;
|
|
// x[0][2]=3;
|
|
// x[1][0]=4;
|
|
// x[1][1]=5;
|
|
// x[1][2]=6;
|
|
//
|
|
// Struct members are initialized in declaration order. For example,
|
|
// `struct { int a; int b; } x = {1, 2}` sets x.a to 1 and x.b to 2.
|
|
//
|
|
// There are a few special rules for ambiguous initializers and
|
|
// shorthand notations:
|
|
//
|
|
// - If an initializer list is shorter than an array, excess array
|
|
// elements are initialized with 0.
|
|
//
|
|
// - A char array can be initialized by a string literal. For example,
|
|
// `char x[4] = "foo"` is equivalent to `char x[4] = {'f', 'o', 'o',
|
|
// '\0'}`.
|
|
//
|
|
// - If a rhs is an incomplete array, its size is set by counting the
|
|
// number of items on the rhs. For example, `x` in `int x[]={1,2,3}`
|
|
// has type `int[3]`.
|
|
Node *lvar_initializer(Node *cur, Var *var, Type *ty, Designator *desg) {
|
|
if (ty->kind == TY_ARRAY && ty->base->kind == TY_CHAR &&
|
|
token->kind == TK_STR) {
|
|
// Initialize a char array with a string literal.
|
|
Token *tok = token;
|
|
token = token->next;
|
|
|
|
if (ty->is_incomplete) {
|
|
ty->array_size = tok->cont_len;
|
|
ty->is_incomplete = false;
|
|
}
|
|
|
|
int len = (ty->array_size < tok->cont_len)
|
|
? ty->array_size : tok->cont_len;
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
Designator desg2 = {desg, i, NULL};
|
|
Node *rhs = new_num(tok->contents[i], tok);
|
|
cur->next = new_desg_node(var, &desg2, rhs);
|
|
cur = cur->next;
|
|
}
|
|
|
|
for (; i < ty->array_size; i++) {
|
|
Designator desg2 = {desg, i, NULL};
|
|
cur = lvar_init_zero(cur, var, ty->base, &desg2);
|
|
}
|
|
return cur;
|
|
}
|
|
|
|
if (ty->kind == TY_ARRAY) {
|
|
bool open = consume("{");
|
|
int i = 0;
|
|
int limit = ty->is_incomplete ? INT_MAX : ty->array_size;
|
|
|
|
do {
|
|
Designator desg2 = {desg, i++, NULL};
|
|
cur = lvar_initializer(cur, var, ty->base, &desg2);
|
|
} while (i < limit && !peek_end() && consume(","));
|
|
|
|
if (open && !consume_end())
|
|
skip_excess_elements();
|
|
|
|
// Set excess array elements to zero.
|
|
while (i < ty->array_size) {
|
|
Designator desg2 = {desg, i++, NULL};
|
|
cur = lvar_init_zero(cur, var, ty->base, &desg2);
|
|
}
|
|
|
|
if (ty->is_incomplete) {
|
|
ty->array_size = i;
|
|
ty->is_incomplete = false;
|
|
}
|
|
return cur;
|
|
}
|
|
|
|
if (ty->kind == TY_STRUCT) {
|
|
bool open = consume("{");
|
|
Member *mem = ty->members;
|
|
|
|
do {
|
|
Designator desg2 = {desg, 0, mem};
|
|
cur = lvar_initializer(cur, var, mem->ty, &desg2);
|
|
mem = mem->next;
|
|
} while (mem && !peek_end() && consume(","));
|
|
|
|
if (open && !consume_end())
|
|
skip_excess_elements();
|
|
|
|
// Set excess struct elements to zero.
|
|
for (; mem; mem = mem->next) {
|
|
Designator desg2 = {desg, 0, mem};
|
|
cur = lvar_init_zero(cur, var, mem->ty, &desg2);
|
|
}
|
|
return cur;
|
|
}
|
|
|
|
bool open = consume("{");
|
|
cur->next = new_desg_node(var, desg, assign());
|
|
if (open)
|
|
expect("}");
|
|
return cur->next;
|
|
}
|
|
|
|
// declaration = type-specifier declarator type-suffix ("=" lvar-initializer)? ";"
|
|
// | type-specifier ";"
|
|
Node *declaration() {
|
|
Token *tok;
|
|
Type *ty = type_specifier();
|
|
if (tok = consume(";"))
|
|
return new_node(ND_NULL, tok);
|
|
|
|
tok = token;
|
|
char *name = NULL;
|
|
ty = declarator(ty, &name);
|
|
ty = type_suffix(ty);
|
|
|
|
if (ty->is_typedef) {
|
|
expect(";");
|
|
ty->is_typedef = false;
|
|
push_scope(name)->type_def = ty;
|
|
return new_node(ND_NULL, tok);
|
|
}
|
|
|
|
if (ty->kind == TY_VOID)
|
|
error_tok(tok, "variable declared void");
|
|
|
|
Var *var;
|
|
if (ty->is_static)
|
|
var = push_var(new_label(), ty, false, tok);
|
|
else
|
|
var = push_var(name, ty, true, tok);
|
|
push_scope(name)->var = var;
|
|
|
|
if (consume(";"))
|
|
return new_node(ND_NULL, tok);
|
|
|
|
expect("=");
|
|
|
|
Node head;
|
|
head.next = NULL;
|
|
lvar_initializer(&head, var, var->ty, NULL);
|
|
expect(";");
|
|
|
|
Node *node = new_node(ND_BLOCK, tok);
|
|
node->body = head.next;
|
|
return node;
|
|
}
|
|
|
|
Node *read_expr_stmt() {
|
|
Token *tok = token;
|
|
return new_unary(ND_EXPR_STMT, expr(), tok);
|
|
}
|
|
|
|
bool is_typename() {
|
|
return peek("void") || peek("_Bool") || peek("char") || peek("short") ||
|
|
peek("int") || peek("long") || peek("enum") || peek("struct") ||
|
|
peek("typedef") || peek("static") || find_typedef(token);
|
|
}
|
|
|
|
// stmt = "return" expr ";"
|
|
// | "if" "(" expr ")" stmt ("else" stmt)?
|
|
// | "switch" "(" expr ")" stmt
|
|
// | "case" const-expr ":" stmt
|
|
// | "default" ":" stmt
|
|
// | "while" "(" expr ")" stmt
|
|
// | "for" "(" (expr? ";" | declaration) expr? ";" expr? ")" stmt
|
|
// | "{" stmt* "}"
|
|
// | "break" ";"
|
|
// | "continue" ";"
|
|
// | "goto" ident ";"
|
|
// | ident ":" stmt
|
|
// | declaration
|
|
// | expr ";"
|
|
Node *stmt() {
|
|
Token *tok;
|
|
if (tok = consume("return")) {
|
|
Node *node = new_unary(ND_RETURN, expr(), tok);
|
|
expect(";");
|
|
return node;
|
|
}
|
|
|
|
if (tok = consume("if")) {
|
|
Node *node = new_node(ND_IF, tok);
|
|
expect("(");
|
|
node->cond = expr();
|
|
expect(")");
|
|
node->then = stmt();
|
|
if (consume("else"))
|
|
node->els = stmt();
|
|
return node;
|
|
}
|
|
|
|
if (tok = consume("switch")) {
|
|
Node *node = new_node(ND_SWITCH, tok);
|
|
expect("(");
|
|
node->cond = expr();
|
|
expect(")");
|
|
|
|
Node *sw = current_switch;
|
|
current_switch = node;
|
|
node->then = stmt();
|
|
current_switch = sw;
|
|
return node;
|
|
}
|
|
|
|
if (tok = consume("case")) {
|
|
if (!current_switch)
|
|
error_tok(tok, "stray case");
|
|
int val = const_expr();
|
|
expect(":");
|
|
|
|
Node *node = new_unary(ND_CASE, stmt(), tok);
|
|
node->val = val;
|
|
node->case_next = current_switch->case_next;
|
|
current_switch->case_next = node;
|
|
return node;
|
|
}
|
|
|
|
if (tok = consume("default")) {
|
|
if (!current_switch)
|
|
error_tok(tok, "stray default");
|
|
expect(":");
|
|
|
|
Node *node = new_unary(ND_CASE, stmt(), tok);
|
|
current_switch->default_case = node;
|
|
return node;
|
|
}
|
|
|
|
if (tok = consume("while")) {
|
|
Node *node = new_node(ND_WHILE, tok);
|
|
expect("(");
|
|
node->cond = expr();
|
|
expect(")");
|
|
node->then = stmt();
|
|
return node;
|
|
}
|
|
|
|
if (tok = consume("for")) {
|
|
Node *node = new_node(ND_FOR, tok);
|
|
expect("(");
|
|
Scope *sc = enter_scope();
|
|
|
|
if (!consume(";")) {
|
|
if (is_typename()) {
|
|
node->init = declaration();
|
|
} else {
|
|
node->init = read_expr_stmt();
|
|
expect(";");
|
|
}
|
|
}
|
|
if (!consume(";")) {
|
|
node->cond = expr();
|
|
expect(";");
|
|
}
|
|
if (!consume(")")) {
|
|
node->inc = read_expr_stmt();
|
|
expect(")");
|
|
}
|
|
node->then = stmt();
|
|
|
|
leave_scope(sc);
|
|
return node;
|
|
}
|
|
|
|
if (tok = consume("{")) {
|
|
Node head;
|
|
head.next = NULL;
|
|
Node *cur = &head;
|
|
|
|
Scope *sc = enter_scope();
|
|
while (!consume("}")) {
|
|
cur->next = stmt();
|
|
cur = cur->next;
|
|
}
|
|
leave_scope(sc);
|
|
|
|
Node *node = new_node(ND_BLOCK, tok);
|
|
node->body = head.next;
|
|
return node;
|
|
}
|
|
|
|
if (tok = consume("break")) {
|
|
expect(";");
|
|
return new_node(ND_BREAK, tok);
|
|
}
|
|
|
|
if (tok = consume("continue")) {
|
|
expect(";");
|
|
return new_node(ND_CONTINUE, tok);
|
|
}
|
|
|
|
if (tok = consume("goto")) {
|
|
Node *node = new_node(ND_GOTO, tok);
|
|
node->label_name = expect_ident();
|
|
expect(";");
|
|
return node;
|
|
}
|
|
|
|
if (tok = consume_ident()) {
|
|
if (consume(":")) {
|
|
Node *node = new_unary(ND_LABEL, stmt(), tok);
|
|
node->label_name = strndup(tok->str, tok->len);
|
|
return node;
|
|
}
|
|
token = tok;
|
|
}
|
|
|
|
if (is_typename())
|
|
return declaration();
|
|
|
|
Node *node = read_expr_stmt();
|
|
expect(";");
|
|
return node;
|
|
}
|
|
|
|
// expr = assign ("," assign)*
|
|
Node *expr() {
|
|
Node *node = assign();
|
|
Token *tok;
|
|
while (tok = consume(",")) {
|
|
node = new_unary(ND_EXPR_STMT, node, node->tok);
|
|
node = new_binary(ND_COMMA, node, assign(), tok);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
long eval(Node *node) {
|
|
return eval2(node, NULL);
|
|
}
|
|
|
|
long eval2(Node *node, Var **var) {
|
|
switch (node->kind) {
|
|
case ND_ADD: {
|
|
long lhs = eval2(node->lhs, var);
|
|
return lhs + eval2(node->rhs, var);
|
|
}
|
|
case ND_SUB: {
|
|
long lhs = eval2(node->lhs, var);
|
|
return lhs - eval(node->rhs);
|
|
}
|
|
case ND_MUL:
|
|
return eval(node->lhs) * eval(node->rhs);
|
|
case ND_DIV:
|
|
return eval(node->lhs) / eval(node->rhs);
|
|
case ND_BITAND:
|
|
return eval(node->lhs) & eval(node->rhs);
|
|
case ND_BITOR:
|
|
return eval(node->lhs) | eval(node->rhs);
|
|
case ND_BITXOR:
|
|
return eval(node->lhs) | eval(node->rhs);
|
|
case ND_SHL:
|
|
return eval(node->lhs) << eval(node->rhs);
|
|
case ND_SHR:
|
|
return eval(node->lhs) >> eval(node->rhs);
|
|
case ND_EQ:
|
|
return eval(node->lhs) == eval(node->rhs);
|
|
case ND_NE:
|
|
return eval(node->lhs) != eval(node->rhs);
|
|
case ND_LT:
|
|
return eval(node->lhs) < eval(node->rhs);
|
|
case ND_LE:
|
|
return eval(node->lhs) <= eval(node->rhs);
|
|
case ND_TERNARY:
|
|
return eval(node->cond) ? eval(node->then) : eval(node->els);
|
|
case ND_COMMA:
|
|
return eval(node->rhs);
|
|
case ND_NOT:
|
|
return !eval(node->lhs);
|
|
case ND_BITNOT:
|
|
return ~eval(node->lhs);
|
|
case ND_LOGAND:
|
|
return eval(node->lhs) && eval(node->rhs);
|
|
case ND_LOGOR:
|
|
return eval(node->lhs) || eval(node->rhs);
|
|
case ND_NUM:
|
|
return node->val;
|
|
case ND_ADDR:
|
|
if (!var || *var || node->lhs->kind != ND_VAR)
|
|
error_tok(node->tok, "invalid initializer");
|
|
*var = node->lhs->var;
|
|
return 0;
|
|
case ND_VAR:
|
|
if (!var || *var || node->var->ty->kind != TY_ARRAY)
|
|
error_tok(node->tok, "invalid initializer");
|
|
*var = node->var;
|
|
return 0;
|
|
}
|
|
|
|
error_tok(node->tok, "not a constant expression");
|
|
}
|
|
|
|
long const_expr() {
|
|
return eval(conditional());
|
|
}
|
|
|
|
// assign = conditional (assign-op assign)?
|
|
// assign-op = "=" | "+=" | "-=" | "*=" | "/=" | "<<=" | ">>="
|
|
Node *assign() {
|
|
Node *node = conditional();
|
|
Token *tok;
|
|
if (tok = consume("="))
|
|
node = new_binary(ND_ASSIGN, node, assign(), tok);
|
|
if (tok = consume("+="))
|
|
node = new_binary(ND_A_ADD, node, assign(), tok);
|
|
if (tok = consume("-="))
|
|
node = new_binary(ND_A_SUB, node, assign(), tok);
|
|
if (tok = consume("*="))
|
|
node = new_binary(ND_A_MUL, node, assign(), tok);
|
|
if (tok = consume("/="))
|
|
node = new_binary(ND_A_DIV, node, assign(), tok);
|
|
if (tok = consume("<<="))
|
|
node = new_binary(ND_A_SHL, node, assign(), tok);
|
|
if (tok = consume(">>="))
|
|
node = new_binary(ND_A_SHR, node, assign(), tok);
|
|
return node;
|
|
}
|
|
|
|
// conditional = logor ("?" expr ":" conditional)?
|
|
Node *conditional() {
|
|
Node *node = logor();
|
|
Token *tok = consume("?");
|
|
if (!tok)
|
|
return node;
|
|
|
|
Node *ternary = new_node(ND_TERNARY, tok);
|
|
ternary->cond = node;
|
|
ternary->then = expr();
|
|
expect(":");
|
|
ternary->els = conditional();
|
|
return ternary;
|
|
}
|
|
|
|
// logor = logand ("||" logand)*
|
|
Node *logor() {
|
|
Node *node = logand();
|
|
Token *tok;
|
|
while (tok = consume("||"))
|
|
node = new_binary(ND_LOGOR, node, logand(), tok);
|
|
return node;
|
|
}
|
|
|
|
// logand = bitor ("&&" bitor)*
|
|
Node *logand() {
|
|
Node *node = bitor();
|
|
Token *tok;
|
|
while (tok = consume("&&"))
|
|
node = new_binary(ND_LOGAND, node, bitor(), tok);
|
|
return node;
|
|
}
|
|
|
|
// bitor = bitxor ("|" bitxor)*
|
|
Node *bitor() {
|
|
Node *node = bitxor();
|
|
Token *tok;
|
|
while (tok = consume("|"))
|
|
node = new_binary(ND_BITOR, node, bitxor(), tok);
|
|
return node;
|
|
}
|
|
|
|
// bitxor = bitand ("^" bitand)*
|
|
Node *bitxor() {
|
|
Node *node = bitand();
|
|
Token *tok;
|
|
while (tok = consume("^"))
|
|
node = new_binary(ND_BITXOR, node, bitxor(), tok);
|
|
return node;
|
|
}
|
|
|
|
// bitand = equality ("&" equality)*
|
|
Node *bitand() {
|
|
Node *node = equality();
|
|
Token *tok;
|
|
while (tok = consume("&"))
|
|
node = new_binary(ND_BITAND, node, equality(), tok);
|
|
return node;
|
|
}
|
|
|
|
// equality = relational ("==" relational | "!=" relational)*
|
|
Node *equality() {
|
|
Node *node = relational();
|
|
Token *tok;
|
|
|
|
for (;;) {
|
|
if (tok = consume("=="))
|
|
node = new_binary(ND_EQ, node, relational(), tok);
|
|
else if (tok = consume("!="))
|
|
node = new_binary(ND_NE, node, relational(), tok);
|
|
else
|
|
return node;
|
|
}
|
|
}
|
|
|
|
// relational = shift ("<" shift | "<=" shift | ">" shift | ">=" shift)*
|
|
Node *relational() {
|
|
Node *node = shift();
|
|
Token *tok;
|
|
|
|
for (;;) {
|
|
if (tok = consume("<"))
|
|
node = new_binary(ND_LT, node, shift(), tok);
|
|
else if (tok = consume("<="))
|
|
node = new_binary(ND_LE, node, shift(), tok);
|
|
else if (tok = consume(">"))
|
|
node = new_binary(ND_LT, shift(), node, tok);
|
|
else if (tok = consume(">="))
|
|
node = new_binary(ND_LE, shift(), node, tok);
|
|
else
|
|
return node;
|
|
}
|
|
}
|
|
|
|
// shift = add ("<<" add | ">>" add)*
|
|
Node *shift() {
|
|
Node *node = add();
|
|
Token *tok;
|
|
|
|
for (;;) {
|
|
if (tok = consume("<<"))
|
|
node = new_binary(ND_SHL, node, add(), tok);
|
|
else if (tok = consume(">>"))
|
|
node = new_binary(ND_SHR, node, add(), tok);
|
|
else
|
|
return node;
|
|
}
|
|
}
|
|
|
|
// add = mul ("+" mul | "-" mul)*
|
|
Node *add() {
|
|
Node *node = mul();
|
|
Token *tok;
|
|
|
|
for (;;) {
|
|
if (tok = consume("+"))
|
|
node = new_binary(ND_ADD, node, mul(), tok);
|
|
else if (tok = consume("-"))
|
|
node = new_binary(ND_SUB, node, mul(), tok);
|
|
else
|
|
return node;
|
|
}
|
|
}
|
|
|
|
// mul = cast ("*" cast | "/" cast)*
|
|
Node *mul() {
|
|
Node *node = cast();
|
|
Token *tok;
|
|
|
|
for (;;) {
|
|
if (tok = consume("*"))
|
|
node = new_binary(ND_MUL, node, cast(), tok);
|
|
else if (tok = consume("/"))
|
|
node = new_binary(ND_DIV, node, cast(), tok);
|
|
else
|
|
return node;
|
|
}
|
|
}
|
|
|
|
// cast = "(" type-name ")" cast | unary
|
|
Node *cast() {
|
|
Token *tok = token;
|
|
|
|
if (consume("(")) {
|
|
if (is_typename()) {
|
|
Type *ty = type_name();
|
|
expect(")");
|
|
Node *node = new_unary(ND_CAST, cast(), tok);
|
|
node->ty = ty;
|
|
return node;
|
|
}
|
|
token = tok;
|
|
}
|
|
|
|
return unary();
|
|
}
|
|
|
|
// unary = ("+" | "-" | "*" | "&" | "!" | "~")? cast
|
|
// | ("++" | "--") unary
|
|
// | postfix
|
|
Node *unary() {
|
|
Token *tok;
|
|
if (consume("+"))
|
|
return cast();
|
|
if (tok = consume("-"))
|
|
return new_binary(ND_SUB, new_num(0, tok), cast(), tok);
|
|
if (tok = consume("&"))
|
|
return new_unary(ND_ADDR, cast(), tok);
|
|
if (tok = consume("*"))
|
|
return new_unary(ND_DEREF, cast(), tok);
|
|
if (tok = consume("!"))
|
|
return new_unary(ND_NOT, cast(), tok);
|
|
if (tok = consume("~"))
|
|
return new_unary(ND_BITNOT, cast(), tok);
|
|
if (tok = consume("++"))
|
|
return new_unary(ND_PRE_INC, unary(), tok);
|
|
if (tok = consume("--"))
|
|
return new_unary(ND_PRE_DEC, unary(), tok);
|
|
return postfix();
|
|
}
|
|
|
|
// postfix = primary ("[" expr "]" | "." ident | "->" ident | "++" | "--")*
|
|
Node *postfix() {
|
|
Node *node = primary();
|
|
Token *tok;
|
|
|
|
for (;;) {
|
|
if (tok = consume("[")) {
|
|
// x[y] is short for *(x+y)
|
|
Node *exp = new_binary(ND_ADD, node, expr(), tok);
|
|
expect("]");
|
|
node = new_unary(ND_DEREF, exp, tok);
|
|
continue;
|
|
}
|
|
|
|
if (tok = consume(".")) {
|
|
node = new_unary(ND_MEMBER, node, tok);
|
|
node->member_name = expect_ident();
|
|
continue;
|
|
}
|
|
|
|
if (tok = consume("->")) {
|
|
// x->y is short for (*x).y
|
|
node = new_unary(ND_DEREF, node, tok);
|
|
node = new_unary(ND_MEMBER, node, tok);
|
|
node->member_name = expect_ident();
|
|
continue;
|
|
}
|
|
|
|
if (tok = consume("++")) {
|
|
node = new_unary(ND_POST_INC, node, tok);
|
|
continue;
|
|
}
|
|
|
|
if (tok = consume("--")) {
|
|
node = new_unary(ND_POST_DEC, node, tok);
|
|
continue;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
}
|
|
|
|
// stmt-expr = "(" "{" stmt stmt* "}" ")"
|
|
//
|
|
// Statement expression is a GNU C extension.
|
|
Node *stmt_expr(Token *tok) {
|
|
Scope *sc = enter_scope();
|
|
Node *node = new_node(ND_STMT_EXPR, tok);
|
|
node->body = stmt();
|
|
Node *cur = node->body;
|
|
|
|
while (!consume("}")) {
|
|
cur->next = stmt();
|
|
cur = cur->next;
|
|
}
|
|
expect(")");
|
|
leave_scope(sc);
|
|
|
|
if (cur->kind != ND_EXPR_STMT)
|
|
error_tok(cur->tok, "stmt expr returning void is not supported");
|
|
*cur = *cur->lhs;
|
|
return node;
|
|
}
|
|
|
|
// func-args = "(" (assign ("," assign)*)? ")"
|
|
Node *func_args() {
|
|
if (consume(")"))
|
|
return NULL;
|
|
|
|
Node *head = assign();
|
|
Node *cur = head;
|
|
while (consume(",")) {
|
|
cur->next = assign();
|
|
cur = cur->next;
|
|
}
|
|
expect(")");
|
|
return head;
|
|
}
|
|
|
|
// primary = "(" "{" stmt-expr-tail
|
|
// | "(" expr ")"
|
|
// | "sizeof" "(" type-name ")"
|
|
// | "sizeof" unary
|
|
// | ident func-args?
|
|
// | str
|
|
// | num
|
|
Node *primary() {
|
|
Token *tok;
|
|
|
|
if (tok = consume("(")) {
|
|
if (consume("{"))
|
|
return stmt_expr(tok);
|
|
|
|
Node *node = expr();
|
|
expect(")");
|
|
return node;
|
|
}
|
|
|
|
if (tok = consume("sizeof")) {
|
|
if (consume("(")) {
|
|
if (is_typename()) {
|
|
Type *ty = type_name();
|
|
expect(")");
|
|
return new_num(size_of(ty, tok), tok);
|
|
}
|
|
token = tok->next;
|
|
}
|
|
return new_unary(ND_SIZEOF, unary(), tok);
|
|
}
|
|
|
|
if (tok = consume_ident()) {
|
|
if (consume("(")) {
|
|
Node *node = new_node(ND_FUNCALL, tok);
|
|
node->funcname = strndup(tok->str, tok->len);
|
|
node->args = func_args();
|
|
|
|
VarScope *sc = find_var(tok);
|
|
if (sc) {
|
|
if (!sc->var || sc->var->ty->kind != TY_FUNC)
|
|
error_tok(tok, "not a function");
|
|
node->ty = sc->var->ty->return_ty;
|
|
} else {
|
|
node->ty = int_type();
|
|
}
|
|
return node;
|
|
}
|
|
|
|
VarScope *sc = find_var(tok);
|
|
if (sc) {
|
|
if (sc->var)
|
|
return new_var(sc->var, tok);
|
|
if (sc->enum_ty)
|
|
return new_num(sc->enum_val, tok);
|
|
}
|
|
error_tok(tok, "undefined variable");
|
|
}
|
|
|
|
tok = token;
|
|
if (tok->kind == TK_STR) {
|
|
token = token->next;
|
|
|
|
Type *ty = array_of(char_type(), tok->cont_len);
|
|
Var *var = push_var(new_label(), ty, false, NULL);
|
|
var->initializer = gvar_init_string(tok->contents, tok->cont_len);
|
|
return new_var(var, tok);
|
|
}
|
|
|
|
if (tok->kind != TK_NUM)
|
|
error_tok(tok, "expected expression");
|
|
return new_num(expect_number(), tok);
|
|
}
|