From 3654ce578e6fff53950874adf7e0e4ae0a6eb956 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 3 May 2026 17:29:12 +0900 Subject: refactor: organize directory structure --- src/cc1/parse.c | 3018 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3018 insertions(+) create mode 100644 src/cc1/parse.c (limited to 'src/cc1/parse.c') diff --git a/src/cc1/parse.c b/src/cc1/parse.c new file mode 100644 index 0000000..52167c1 --- /dev/null +++ b/src/cc1/parse.c @@ -0,0 +1,3018 @@ +#include "parse.h" +#include "../lib/common.h" +#include "tokenize.h" + +typedef struct { + const char* name; + Type* ty; + int stack_offset; +} LocalVar; + +typedef struct { + size_t len; + size_t capacity; + LocalVar* data; +} LocalVarArray; + +static void lvars_init(LocalVarArray* lvars) { + lvars->len = 0; + lvars->capacity = 4; + lvars->data = calloc(lvars->capacity, sizeof(LocalVar)); +} + +static void lvars_reserve(LocalVarArray* lvars, size_t size) { + if (size <= lvars->capacity) + return; + while (lvars->capacity < size) { + lvars->capacity *= 2; + } + lvars->data = realloc(lvars->data, lvars->capacity * sizeof(LocalVar)); + memset(lvars->data + lvars->len, 0, (lvars->capacity - lvars->len) * sizeof(LocalVar)); +} + +static LocalVar* lvars_push_new(LocalVarArray* lvars) { + lvars_reserve(lvars, lvars->len + 1); + return &lvars->data[lvars->len++]; +} + +typedef struct { + const char* name; + int index; +} ScopedSymbol; + +typedef struct { + size_t len; + size_t capacity; + ScopedSymbol* data; +} ScopedSymbolArray; + +static void scopedsymbols_init(ScopedSymbolArray* syms) { + syms->len = 0; + syms->capacity = 4; + syms->data = calloc(syms->capacity, sizeof(ScopedSymbol)); +} + +static void scopedsymbols_reserve(ScopedSymbolArray* syms, size_t size) { + if (size <= syms->capacity) + return; + while (syms->capacity < size) { + syms->capacity *= 2; + } + syms->data = realloc(syms->data, syms->capacity * sizeof(ScopedSymbol)); + memset(syms->data + syms->len, 0, (syms->capacity - syms->len) * sizeof(ScopedSymbol)); +} + +static ScopedSymbol* scopedsymbols_push_new(ScopedSymbolArray* syms) { + scopedsymbols_reserve(syms, syms->len + 1); + return &syms->data[syms->len++]; +} + +typedef struct Scope { + struct Scope* outer; + ScopedSymbolArray syms; +} Scope; + +typedef struct { + const char* name; + Type* ty; +} GlobalVar; + +typedef struct { + size_t len; + size_t capacity; + GlobalVar* data; +} GlobalVarArray; + +static void gvars_init(GlobalVarArray* gvars) { + gvars->len = 0; + gvars->capacity = 4; + gvars->data = calloc(gvars->capacity, sizeof(GlobalVar)); +} + +static void gvars_reserve(GlobalVarArray* gvars, size_t size) { + if (size <= gvars->capacity) + return; + while (gvars->capacity < size) { + gvars->capacity *= 2; + } + gvars->data = realloc(gvars->data, gvars->capacity * sizeof(GlobalVar)); + memset(gvars->data + gvars->len, 0, (gvars->capacity - gvars->len) * sizeof(GlobalVar)); +} + +static GlobalVar* gvars_push_new(GlobalVarArray* gvars) { + gvars_reserve(gvars, gvars->len + 1); + return &gvars->data[gvars->len++]; +} + +typedef struct { + const char* name; + Type* ty; +} Func; + +typedef struct { + size_t len; + size_t capacity; + Func* data; +} FuncArray; + +static void funcs_init(FuncArray* funcs) { + funcs->len = 0; + funcs->capacity = 32; + funcs->data = calloc(funcs->capacity, sizeof(Func)); +} + +static void funcs_reserve(FuncArray* funcs, size_t size) { + if (size <= funcs->capacity) + return; + while (funcs->capacity < size) { + funcs->capacity *= 2; + } + funcs->data = realloc(funcs->data, funcs->capacity * sizeof(Func)); + memset(funcs->data + funcs->len, 0, (funcs->capacity - funcs->len) * sizeof(Func)); +} + +static Func* funcs_push_new(FuncArray* funcs) { + funcs_reserve(funcs, funcs->len + 1); + return &funcs->data[funcs->len++]; +} + +typedef struct { + TokenArray* tokens; + int pos; + LocalVarArray lvars; + Scope* scope; + GlobalVarArray gvars; + FuncArray funcs; + AstNode* structs; + AstNode* unions; + AstNode* enums; + AstNode* typedefs; + StrArray str_literals; + int anonymous_user_type_counter; + AstNode* current_switch; +} Parser; + +static Parser* parser_new(TokenArray* tokens) { + Parser* p = calloc(1, sizeof(Parser)); + p->tokens = tokens; + gvars_init(&p->gvars); + funcs_init(&p->funcs); + p->structs = ast_new_list(4); + p->unions = ast_new_list(4); + p->enums = ast_new_list(4); + p->typedefs = ast_new_list(16); + strings_init(&p->str_literals); + + Func* va_start_func = funcs_push_new(&p->funcs); + va_start_func->name = "__ducc_va_start"; + va_start_func->ty = type_new_func(type_new(TypeKind_void), NULL); + + Func* va_arg_func = funcs_push_new(&p->funcs); + va_arg_func->name = "__ducc_va_arg"; + va_arg_func->ty = type_new_func(type_new_ptr(type_new(TypeKind_void)), NULL); + + return p; +} + +static Token* peek_token(Parser* p) { + return &p->tokens->data[p->pos]; +} + +static Token* peek_token2(Parser* p) { + return &p->tokens->data[p->pos + 1]; +} + +static SourceLocation current_location(Parser* p) { + return peek_token(p)->loc; +} + +static Token* next_token(Parser* p) { + return &p->tokens->data[p->pos++]; +} + +static Token* consume_token_if(Parser* p, TokenKind expected) { + if (peek_token(p)->kind == expected) { + return next_token(p); + } else { + return NULL; + } +} + +static bool eof(Parser* p) { + return peek_token(p)->kind != TokenKind_eof; +} + +static Token* expect(Parser* p, TokenKind expected) { + Token* t = next_token(p); + if (t->kind == expected) { + return t; + } + fatal_error("%s:%d: expected '%s', but got '%s'", t->loc.filename, t->loc.line, token_kind_stringify(expected), + token_stringify(t)); +} + +static int find_lvar_in_scope(Parser*, Scope* scope, const char* name) { + for (size_t i = 0; i < scope->syms.len; ++i) { + ScopedSymbol* sym = &scope->syms.data[i]; + if (sym->name && strcmp(sym->name, name) == 0) { + return sym->index; + } + } + return -1; +} + +static int find_lvar_in_current_scope(Parser* p, const char* name) { + return find_lvar_in_scope(p, p->scope, name); +} + +static int find_lvar(Parser* p, const char* name) { + Scope* scope = p->scope; + while (scope) { + int idx = find_lvar_in_scope(p, scope, name); + if (idx != -1) + return idx; + scope = scope->outer; + } + return -1; +} + +static int calc_lvar_stack_offset(Parser* p, Type* ty) { + int last_offset = 0; + for (int i = p->lvars.len - 1; i >= 0; i--) { + int offset = p->lvars.data[i].stack_offset; + // Skip a passed-by-stack parameter. + if (offset <= 0) + continue; + last_offset = offset; + break; + } + return to_aligned(last_offset + type_sizeof(ty), type_alignof(ty)); +} + +static int add_lvar(Parser* p, const char* name, Type* ty, int stack_offset) { + LocalVar* lvar = lvars_push_new(&p->lvars); + lvar->name = name; + lvar->ty = ty; + lvar->stack_offset = stack_offset; + ScopedSymbol* sym = scopedsymbols_push_new(&p->scope->syms); + sym->name = name; + sym->index = p->lvars.len - 1; + return stack_offset; +} + +static AstNode* generate_temporary_lvar(Parser* p, Type* ty) { + int stack_offset = add_lvar(p, NULL, ty, calc_lvar_stack_offset(p, ty)); + return ast_new_lvar(NULL, stack_offset, ty); +} + +static int find_gvar(Parser* p, const char* name) { + for (size_t i = 0; i < p->gvars.len; ++i) { + if (strcmp(p->gvars.data[i].name, name) == 0) { + return i; + } + } + return -1; +} + +static int find_func(Parser* p, const char* name) { + for (size_t i = 0; i < p->funcs.len; ++i) { + if (strcmp(p->funcs.data[i].name, name) == 0) { + return i; + } + } + return -1; +} + +static int find_struct(Parser* p, const char* name) { + for (int i = 0; i < p->structs->as.list->len; ++i) { + if (strcmp(p->structs->as.list->items[i].as.struct_def->name, name) == 0) { + return i; + } + } + return -1; +} + +static int find_union(Parser* p, const char* name) { + for (int i = 0; i < p->unions->as.list->len; ++i) { + if (strcmp(p->unions->as.list->items[i].as.union_def->name, name) == 0) { + return i; + } + } + return -1; +} + +static int find_enum(Parser* p, const char* name) { + for (int i = 0; i < p->enums->as.list->len; ++i) { + if (strcmp(p->enums->as.list->items[i].as.enum_def->name, name) == 0) { + return i; + } + } + return -1; +} + +static int find_enum_member(Parser* p, const char* name) { + for (int i = 0; i < p->enums->as.list->len; ++i) { + AstNode* members = p->enums->as.list->items[i].as.enum_def->members; + if (!members) + continue; + for (int j = 0; j < members->as.list->len; ++j) { + if (strcmp(members->as.list->items[j].as.enum_member->name, name) == 0) { + return i * 1000 + j; + } + } + } + return -1; +} + +static int find_typedef(Parser* p, const char* name) { + for (int i = 0; i < p->typedefs->as.list->len; ++i) { + if (strcmp(p->typedefs->as.list->items[i].as.typedef_decl->name, name) == 0) { + return i; + } + } + return -1; +} + +static void enter_scope(Parser* p) { + Scope* outer_scope = p->scope; + p->scope = calloc(1, sizeof(Scope)); + p->scope->outer = outer_scope; + scopedsymbols_init(&p->scope->syms); +} + +static void leave_scope(Parser* p) { + p->scope = p->scope->outer; +} + +static void enter_func(Parser* p) { + lvars_init(&p->lvars); + enter_scope(p); +} + +static void leave_func(Parser* p) { + leave_scope(p); +} + +static Token* parse_ident(Parser*); +static AstNode* parse_primary_expr(Parser*); +static AstNode* parse_postfix_expr(Parser*); +static AstNode* parse_argument_expr_list(Parser*); +static AstNode* parse_unary_expr(Parser*); +static AstNode* parse_cast_expr(Parser*); +static AstNode* parse_multiplicative_expr(Parser*); +static AstNode* parse_additive_expr(Parser*); +static AstNode* parse_shift_expr(Parser*); +static AstNode* parse_relational_expr(Parser*); +static AstNode* parse_equality_expr(Parser*); +static AstNode* parse_bitwise_and_expr(Parser*); +static AstNode* parse_bitwise_xor_expr(Parser*); +static AstNode* parse_bitwise_or_expr(Parser*); +static AstNode* parse_logical_and_expr(Parser*); +static AstNode* parse_logical_or_expr(Parser*); +static AstNode* parse_conditional_expr(Parser*); +static AstNode* parse_assignment_expr(Parser*); +static AstNode* parse_expr(Parser*); +static AstNode* parse_constant_expr(Parser*); +static Type* parse_pointer_opt(Parser*, Type*); +static void parse_type_qualifier_list_opt(Parser*); +static AstNode* parse_declarator_or_abstract_declarator_opt(Parser*, Type*); +static AstNode* parse_parameter_type_list(Parser*); +static AstNode* parse_parameter_list(Parser*); +static AstNode* parse_parameter_declaration(Parser*); +static Type* parse_array_declarator_suffix(Parser*, Type*); +static Type* parse_function_declarator_suffix(Parser*, Type*); +static AstNode* parse_direct_declarator(Parser*, Type*); +static AstNode* parse_declarator(Parser*, Type*); +static AstNode* parse_init_declarator_list(Parser*, Type*); +static AstNode* parse_declaration(Parser*); +static AstNode* parse_function_definition(Parser*, AstNode*); +static Type* parse_declaration_specifiers(Parser*); +static AstNode* parse_init_declarator(Parser*, Type*); +static Type* parse_struct_specifier(Parser*); +static Type* parse_union_specifier(Parser*); +static AstNode* parse_member_declaration_list(Parser*); +static AstNode* parse_member_declaration(Parser*); +static Type* parse_specifier_qualifier_list(Parser*); +static AstNode* parse_member_declarator_list(Parser*, Type*); +static AstNode* parse_member_declarator(Parser*, Type*); +static Type* parse_enum_specifier(Parser*); +static void parse_enum_members(Parser*, int); +static AstNode* parse_enum_member(Parser*, int); +static Type* parse_type_name(Parser*); +static Type* parse_abstract_declarator_opt(Parser*, Type*); +static AstNode* parse_braced_initializer(Parser*); +static AstNode* parse_initializer(Parser*); +static AstNode* parse_attribute_specifier_sequence_opt(Parser*); +static AstNode* parse_attribute_specifier_opt(Parser*); +static AstNode* parse_attribute_list(Parser*); +static AstNode* parse_attribute(Parser*); +static AstNode* parse_attribute_argument_clause(Parser*); +static AstNode* parse_balanced_token_sequence(Parser*); +static AstNode* parse_stmt(Parser*); +static AstNode* parse_unlabaled_stmt(Parser*); +static AstNode* parse_compound_stmt(Parser*); +static AstNode* parse_block_item(Parser*); +static AstNode* parse_expr_stmt(Parser*); +static AstNode* parse_if_stmt(Parser*); +static AstNode* parse_switch_stmt(Parser*); +static AstNode* parse_while_stmt(Parser*); +static AstNode* parse_do_while_stmt(Parser*); +static AstNode* parse_for_stmt(Parser*); +static AstNode* parse_goto_stmt(Parser*); +static AstNode* parse_continue_stmt(Parser*); +static AstNode* parse_break_stmt(Parser*); +static AstNode* parse_return_stmt(Parser*); +static AstNode* parse_static_assert_declaration(Parser*); +static AstNode* parse_external_declaration(Parser*); +static int eval(AstNode*); +static void process_declarations(Parser*, AstNode*); + +static Token* parse_ident(Parser* p) { + return expect(p, TokenKind_ident); +} + +static int register_str_literal(Parser* p, const char* s) { + return strings_push(&p->str_literals, s); +} + +static void register_params(Parser* p, AstNode* params) { + int gp_regs = 6; + int pass_by_reg_offset = 8; + int pass_by_stack_offset = -16; + for (int i = 0; i < params->as.list->len; ++i) { + AstNode* param = ¶ms->as.list->items[i]; + int ty_size = type_sizeof(param->ty); + int required_gp_regs; + if (ty_size <= 8) { + required_gp_regs = 1; + } else if (ty_size <= 16) { + required_gp_regs = 2; + } else { + required_gp_regs = 0; + } + if (required_gp_regs <= gp_regs) { + gp_regs -= required_gp_regs; + } else { + required_gp_regs = 0; + } + int stack_offset; + if (required_gp_regs == 0) { + stack_offset = pass_by_stack_offset; + pass_by_stack_offset -= to_aligned(ty_size, 8); + } else { + stack_offset = pass_by_reg_offset; + pass_by_reg_offset += to_aligned(ty_size, 8); + } + const char* name = param->as.declarator->name; + param->kind = AstNodeKind_param; + param->as.param = calloc(1, sizeof(ParamNode)); + param->as.param->name = name; + param->as.param->stack_offset = stack_offset; + add_lvar(p, name, param->ty, stack_offset); + } +} + +static void register_func(Parser* p, const char* name, Type* ty) { + Func* func = funcs_push_new(&p->funcs); + func->name = name; + func->ty = ty; +} + +typedef enum { + TypeSpecifierMask_void = 1 << 0, + TypeSpecifierMask_char = 1 << 1, + TypeSpecifierMask_short = 1 << 2, + TypeSpecifierMask_int = 1 << 3, + TypeSpecifierMask_long = 1 << 4, + // 1 << 5 is used for second 'long'. + TypeSpecifierMask_float = 1 << 6, + TypeSpecifierMask_double = 1 << 7, + TypeSpecifierMask_signed = 1 << 8, + TypeSpecifierMask_unsigned = 1 << 9, + TypeSpecifierMask__BitInt = 1 << 10, + TypeSpecifierMask_bool = 1 << 11, + TypeSpecifierMask__Complex = 1 << 12, + TypeSpecifierMask__Decimal32 = 1 << 13, + TypeSpecifierMask__Decimal64 = 1 << 14, + TypeSpecifierMask__Decimal128 = 1 << 15, + TypeSpecifierMask__Atomic = 1 << 16, + TypeSpecifierMask_struct = 1 << 17, + TypeSpecifierMask_union = 1 << 18, + TypeSpecifierMask_enum = 1 << 19, + TypeSpecifierMask_typeof = 1 << 20, + TypeSpecifierMask_typeof_unqual = 1 << 21, + TypeSpecifierMask_typedef_name = 1 << 22, +} TypeSpecifierMask; + +static Type* distinguish_type_from_type_specifiers(int type_specifiers) { + if (type_specifiers == TypeSpecifierMask_void) + return type_new(TypeKind_void); + else if (type_specifiers == TypeSpecifierMask_char) + return type_new(TypeKind_char); + else if (type_specifiers == (TypeSpecifierMask_signed + TypeSpecifierMask_char)) + return type_new(TypeKind_schar); + else if (type_specifiers == (TypeSpecifierMask_unsigned + TypeSpecifierMask_char)) + return type_new(TypeKind_uchar); + else if (type_specifiers == TypeSpecifierMask_short || + type_specifiers == (TypeSpecifierMask_signed + TypeSpecifierMask_short) || + type_specifiers == (TypeSpecifierMask_short + TypeSpecifierMask_int) || + type_specifiers == (TypeSpecifierMask_signed + TypeSpecifierMask_short + TypeSpecifierMask_int)) + return type_new(TypeKind_short); + else if (type_specifiers == (TypeSpecifierMask_unsigned + TypeSpecifierMask_short) || + type_specifiers == (TypeSpecifierMask_unsigned + TypeSpecifierMask_short + TypeSpecifierMask_int)) + return type_new(TypeKind_ushort); + else if (type_specifiers == TypeSpecifierMask_int || type_specifiers == TypeSpecifierMask_signed || + type_specifiers == (TypeSpecifierMask_signed + TypeSpecifierMask_int)) + return type_new(TypeKind_int); + else if (type_specifiers == TypeSpecifierMask_unsigned || + type_specifiers == (TypeSpecifierMask_unsigned + TypeSpecifierMask_int)) + return type_new(TypeKind_uint); + else if (type_specifiers == TypeSpecifierMask_long || + type_specifiers == (TypeSpecifierMask_signed + TypeSpecifierMask_long) || + type_specifiers == (TypeSpecifierMask_long + TypeSpecifierMask_int) || + type_specifiers == (TypeSpecifierMask_signed + TypeSpecifierMask_long + TypeSpecifierMask_int)) + return type_new(TypeKind_long); + else if (type_specifiers == (TypeSpecifierMask_unsigned + TypeSpecifierMask_long) || + type_specifiers == (TypeSpecifierMask_unsigned + TypeSpecifierMask_long + TypeSpecifierMask_int)) + return type_new(TypeKind_ulong); + else if (type_specifiers == (TypeSpecifierMask_long + TypeSpecifierMask_long) || + type_specifiers == (TypeSpecifierMask_signed + TypeSpecifierMask_long + TypeSpecifierMask_long) || + type_specifiers == (TypeSpecifierMask_long + TypeSpecifierMask_long + TypeSpecifierMask_int) || + type_specifiers == + (TypeSpecifierMask_signed + TypeSpecifierMask_long + TypeSpecifierMask_long + TypeSpecifierMask_int)) + return type_new(TypeKind_llong); + else if (type_specifiers == (TypeSpecifierMask_unsigned + TypeSpecifierMask_long + TypeSpecifierMask_long) || + type_specifiers == + (TypeSpecifierMask_unsigned + TypeSpecifierMask_long + TypeSpecifierMask_long + TypeSpecifierMask_int)) + return type_new(TypeKind_ullong); + else if (type_specifiers == TypeSpecifierMask_float) + return type_new(TypeKind_float); + else if (type_specifiers == TypeSpecifierMask_double) + return type_new(TypeKind_double); + else if (type_specifiers == TypeSpecifierMask_long + TypeSpecifierMask_double) + return type_new(TypeKind_ldouble); + else if (type_specifiers == TypeSpecifierMask_bool) + return type_new(TypeKind_bool); + else if (type_specifiers == TypeSpecifierMask_struct) + return NULL; + else if (type_specifiers == TypeSpecifierMask_union) + return NULL; + else if (type_specifiers == TypeSpecifierMask_enum) + return NULL; + else if (type_specifiers == TypeSpecifierMask_typedef_name) + return NULL; + else if (type_specifiers == 0) + return NULL; + else + unimplemented(); +} + +// e++ +// tmp1 = &e; tmp2 = *tmp1; *tmp1 += 1; tmp2 +// e-- +// tmp1 = &e; tmp2 = *tmp1; *tmp1 -= 1; tmp2 +static AstNode* create_new_postfix_inc_or_dec(Parser* p, AstNode* e, TokenKind op) { + AstNode* tmp1_lvar = generate_temporary_lvar(p, type_new_ptr(e->ty)); + AstNode* tmp2_lvar = generate_temporary_lvar(p, e->ty); + + AstNode* expr1 = ast_new_assign_expr(TokenKind_assign, tmp1_lvar, ast_new_ref_expr(e)); + AstNode* expr2 = ast_new_assign_expr(TokenKind_assign, tmp2_lvar, ast_new_deref_expr(tmp1_lvar)); + AstNode* expr3; + if (op == TokenKind_plusplus) { + expr3 = ast_new_assign_add_expr(ast_new_deref_expr(tmp1_lvar), ast_new_int(1)); + } else { + expr3 = ast_new_assign_sub_expr(ast_new_deref_expr(tmp1_lvar), ast_new_int(1)); + } + AstNode* expr4 = tmp2_lvar; + + AstNode* ret = ast_new_list(4); + ast_append(ret, expr1); + ast_append(ret, expr2); + ast_append(ret, expr3); + ast_append(ret, expr4); + ret->ty = expr4->ty; + return ret; +} + +static bool is_typedef_name(Parser* p, Token* tok) { + return tok->kind == TokenKind_ident && find_typedef(p, tok->value.string) != -1; +} + +static bool is_type_token(Parser* p, Token* tok) { + if (tok->kind == TokenKind_keyword_void || tok->kind == TokenKind_keyword_char || + tok->kind == TokenKind_keyword_short || tok->kind == TokenKind_keyword_int || + tok->kind == TokenKind_keyword_long || tok->kind == TokenKind_keyword_float || + tok->kind == TokenKind_keyword_double || tok->kind == TokenKind_keyword_signed || + tok->kind == TokenKind_keyword_unsigned || tok->kind == TokenKind_keyword__BitInt || + tok->kind == TokenKind_keyword_bool || tok->kind == TokenKind_keyword__Complex || + tok->kind == TokenKind_keyword__Decimal32 || tok->kind == TokenKind_keyword__Decimal64 || + tok->kind == TokenKind_keyword__Decimal128 || tok->kind == TokenKind_keyword__Atomic || + tok->kind == TokenKind_keyword_struct || tok->kind == TokenKind_keyword_union || + tok->kind == TokenKind_keyword_enum || tok->kind == TokenKind_keyword_typeof || + tok->kind == TokenKind_keyword_typeof_unqual || tok->kind == TokenKind_keyword_const || + tok->kind == TokenKind_keyword_restrict || tok->kind == TokenKind_keyword_volatile || + tok->kind == TokenKind_keyword__Atomic || tok->kind == TokenKind_keyword_alignas || + tok->kind == TokenKind_keyword_inline || tok->kind == TokenKind_keyword__Noreturn || + tok->kind == TokenKind_keyword_static) { + return true; + } + if (tok->kind != TokenKind_ident) { + return false; + } + return find_typedef(p, tok->value.string) != -1; +} + +// primary-expr: +// identifier +// constant +// string-literal +// '(' expr ')' +// TODO generic-selection +static AstNode* parse_primary_expr(Parser* p) { + Token* t = next_token(p); + if (t->kind == TokenKind_literal_int) { + return ast_new_int(t->value.integer); + } else if (t->kind == TokenKind_literal_double) { + return ast_new_double(t->value.floating); + } else if (t->kind == TokenKind_keyword_true) { + return ast_new_int(1); + } else if (t->kind == TokenKind_keyword_false) { + return ast_new_int(0); + } else if (t->kind == TokenKind_literal_str) { + return ast_new_str_expr(register_str_literal(p, t->value.string), + type_new_static_string(strlen(t->value.string))); + } else if (t->kind == TokenKind_paren_l) { + AstNode* e = parse_expr(p); + expect(p, TokenKind_paren_r); + return e; + } else if (t->kind == TokenKind_ident) { + const char* name = t->value.string; + + int lvar_idx = find_lvar(p, name); + if (lvar_idx == -1) { + int gvar_idx = find_gvar(p, name); + if (gvar_idx == -1) { + int enum_member_idx = find_enum_member(p, name); + if (enum_member_idx == -1) { + int func_idx = find_func(p, name); + if (func_idx == -1) { + fatal_error("%s:%d: undefined variable: %s", t->loc.filename, t->loc.line, name); + } + return ast_new_func(name, p->funcs.data[func_idx].ty); + } + int enum_idx = enum_member_idx / 1000; + int n = enum_member_idx % 1000; + AstNode* e = ast_new_int( + p->enums->as.list->items[enum_idx].as.enum_def->members->as.list->items[n].as.enum_member->value); + e->ty = type_new(TypeKind_enum); + e->ty->ref.defs = p->enums; + e->ty->ref.index = enum_idx; + return e; + } + return ast_new_gvar(name, p->gvars.data[gvar_idx].ty); + } + + return ast_new_lvar(name, p->lvars.data[lvar_idx].stack_offset, p->lvars.data[lvar_idx].ty); + } else { + fatal_error("%s:%d: expected an expression, but got '%s'", t->loc.filename, t->loc.line, token_stringify(t)); + } +} + +// postfix-expr: +// postfix-expr-stem { postfix-expr-postfix }* +// +// postfix-expr-stem: +// primary-expr +// TODO compound-literal +// +// postfix-expr-postfix: +// '[' expr ']' +// '(' argument-expr-list? ')' +// '.' identifier +// '->' identifier +// '++' +// '--' +static AstNode* parse_postfix_expr(Parser* p) { + AstNode* ret = parse_primary_expr(p); + while (1) { + if (consume_token_if(p, TokenKind_paren_l)) { + AstNode* args = parse_argument_expr_list(p); + expect(p, TokenKind_paren_r); + ret = ast_new_func_call(ret, args); + Type* func_type = ret->as.func_call->func->ty; + if (func_type->kind == TypeKind_ptr) { + func_type = func_type->base; + } + ret->ty = func_type->result; + } else if (consume_token_if(p, TokenKind_bracket_l)) { + AstNode* idx = parse_expr(p); + expect(p, TokenKind_bracket_r); + idx = ast_new_binary_expr(TokenKind_star, idx, ast_new_int(type_sizeof(ret->ty->base))); + ret = ast_new_deref_expr(ast_new_binary_expr(TokenKind_plus, ret, idx)); + } else if (consume_token_if(p, TokenKind_dot)) { + const Token* name = parse_ident(p); + ret = ast_new_member_access_expr(ast_new_ref_expr(ret), name->value.string); + } else if (consume_token_if(p, TokenKind_arrow)) { + const Token* name = parse_ident(p); + ret = ast_new_member_access_expr(ret, name->value.string); + } else if (consume_token_if(p, TokenKind_plusplus)) { + ret = create_new_postfix_inc_or_dec(p, ret, TokenKind_plusplus); + } else if (consume_token_if(p, TokenKind_minusminus)) { + ret = create_new_postfix_inc_or_dec(p, ret, TokenKind_minusminus); + } else { + break; + } + } + return ret; +} + +// argument-expr-list: +// { assignment-expr | ',' }+ +static AstNode* parse_argument_expr_list(Parser* p) { + AstNode* list = ast_new_list(6); + while (peek_token(p)->kind != TokenKind_paren_r) { + AstNode* arg = parse_assignment_expr(p); + ast_append(list, arg); + if (!consume_token_if(p, TokenKind_comma)) { + break; + } + } + return list; +} + +// unary-expr: +// postfix-expr +// '++' unary-expr +// '--' unary-expr +// unary-operator cast-expr +// 'sizeof' unary-expr +// 'sizeof' '(' type-name ')' +// TODO 'alignof' '(' type-name ')' +static AstNode* parse_unary_expr(Parser* p) { + TokenKind op = peek_token(p)->kind; + if (consume_token_if(p, TokenKind_plus)) { + return parse_cast_expr(p); + } else if (consume_token_if(p, TokenKind_minus)) { + AstNode* operand = parse_cast_expr(p); + return ast_new_binary_expr(op, ast_new_int(0), operand); + } else if (consume_token_if(p, TokenKind_not)) { + AstNode* operand = parse_cast_expr(p); + return ast_new_unary_expr(op, operand); + } else if (consume_token_if(p, TokenKind_tilde)) { + AstNode* operand = parse_cast_expr(p); + return ast_new_unary_expr(op, operand); + } else if (consume_token_if(p, TokenKind_and)) { + AstNode* operand = parse_cast_expr(p); + return ast_new_ref_expr(operand); + } else if (consume_token_if(p, TokenKind_star)) { + AstNode* operand = parse_cast_expr(p); + if (operand->ty->kind == TypeKind_func) { + // dereference operator against function pointers are no-op. + return operand; + } else { + return ast_new_deref_expr(operand); + } + } else if (consume_token_if(p, TokenKind_plusplus)) { + AstNode* operand = parse_unary_expr(p); + return ast_new_assign_add_expr(operand, ast_new_int(1)); + } else if (consume_token_if(p, TokenKind_minusminus)) { + AstNode* operand = parse_unary_expr(p); + return ast_new_assign_sub_expr(operand, ast_new_int(1)); + } else if (consume_token_if(p, TokenKind_keyword_sizeof)) { + if (peek_token(p)->kind == TokenKind_paren_l && is_type_token(p, peek_token2(p))) { + next_token(p); + Type* ty = parse_type_name(p); + expect(p, TokenKind_paren_r); + return ast_new_int(type_sizeof(ty)); + } else { + AstNode* expr = parse_unary_expr(p); + return ast_new_int(type_sizeof(expr->ty)); + } + } + return parse_postfix_expr(p); +} + +// cast-expr: +// unary-expr +// '(' type-name ')' cast-expr +static AstNode* parse_cast_expr(Parser* p) { + if (peek_token(p)->kind == TokenKind_paren_l && is_type_token(p, peek_token2(p))) { + next_token(p); + Type* ty = parse_type_name(p); + expect(p, TokenKind_paren_r); + + // TODO: check whether the original type can be casted to the result type. + AstNode* e = parse_cast_expr(p); + return ast_new_cast_expr(e, ty); + } + return parse_unary_expr(p); +} + +// multiplicative-expr: +// cast-expr { ( '*' / '/' / '%' ) cast-expr }* +static AstNode* parse_multiplicative_expr(Parser* p) { + AstNode* lhs = parse_cast_expr(p); + while (1) { + TokenKind op = peek_token(p)->kind; + if (op == TokenKind_star || op == TokenKind_slash || op == TokenKind_percent) { + next_token(p); + AstNode* rhs = parse_cast_expr(p); + lhs = ast_new_binary_expr(op, lhs, rhs); + } else { + break; + } + } + return lhs; +} + +// additive-expr: +// multiplicative-expr { ( '+' / '-' ) multiplicative-expr }* +static AstNode* parse_additive_expr(Parser* p) { + AstNode* lhs = parse_multiplicative_expr(p); + while (1) { + if (consume_token_if(p, TokenKind_plus)) { + AstNode* rhs = parse_multiplicative_expr(p); + if (lhs->ty->base) { + lhs = ast_new_binary_expr( + TokenKind_plus, lhs, + ast_new_binary_expr(TokenKind_star, rhs, ast_new_int(type_sizeof(lhs->ty->base)))); + } else if (rhs->ty->base) { + lhs = ast_new_binary_expr( + TokenKind_plus, ast_new_binary_expr(TokenKind_star, lhs, ast_new_int(type_sizeof(rhs->ty->base))), + rhs); + } else { + lhs = ast_new_binary_expr(TokenKind_plus, lhs, rhs); + } + } else if (consume_token_if(p, TokenKind_minus)) { + AstNode* rhs = parse_multiplicative_expr(p); + if (lhs->ty->base) { + if (rhs->ty->base) { + // (a - b) / sizeof(a) + lhs = ast_new_binary_expr(TokenKind_slash, ast_new_binary_expr(TokenKind_minus, lhs, rhs), + ast_new_int(type_sizeof(lhs->ty->base))); + } else { + // a - b*sizeof(a) + lhs = ast_new_binary_expr( + TokenKind_minus, lhs, + ast_new_binary_expr(TokenKind_star, rhs, ast_new_int(type_sizeof(lhs->ty->base)))); + } + } else { + lhs = ast_new_binary_expr(TokenKind_minus, lhs, rhs); + } + } else { + break; + } + } + return lhs; +} + +// shift-expr: +// additive-expr { ( '<<' / '>>' ) additive-expr }* +static AstNode* parse_shift_expr(Parser* p) { + AstNode* lhs = parse_additive_expr(p); + while (1) { + TokenKind op = peek_token(p)->kind; + if (op == TokenKind_lshift || op == TokenKind_rshift) { + next_token(p); + AstNode* rhs = parse_additive_expr(p); + lhs = ast_new_binary_expr(op, lhs, rhs); + } else { + break; + } + } + return lhs; +} + +// relational-expr: +// shift-expr { ( '<' / '>' / '<=' / '>=' ) shift-expr }* +static AstNode* parse_relational_expr(Parser* p) { + AstNode* lhs = parse_shift_expr(p); + while (1) { + TokenKind op = peek_token(p)->kind; + if (op == TokenKind_lt || op == TokenKind_le) { + next_token(p); + AstNode* rhs = parse_shift_expr(p); + lhs = ast_new_binary_expr(op, lhs, rhs); + } else if (consume_token_if(p, TokenKind_gt)) { + AstNode* rhs = parse_shift_expr(p); + lhs = ast_new_binary_expr(TokenKind_lt, rhs, lhs); + } else if (consume_token_if(p, TokenKind_ge)) { + AstNode* rhs = parse_shift_expr(p); + lhs = ast_new_binary_expr(TokenKind_le, rhs, lhs); + } else { + break; + } + } + return lhs; +} + +// equality-expr: +// relational-expr { ( '==' / '!=' ) relational-expr }* +static AstNode* parse_equality_expr(Parser* p) { + AstNode* lhs = parse_relational_expr(p); + while (1) { + TokenKind op = peek_token(p)->kind; + if (op == TokenKind_eq || op == TokenKind_ne) { + next_token(p); + AstNode* rhs = parse_relational_expr(p); + lhs = ast_new_binary_expr(op, lhs, rhs); + } else { + break; + } + } + return lhs; +} + +// bitwise-and-expr: +// equality-expr { '&' equality-expr }* +static AstNode* parse_bitwise_and_expr(Parser* p) { + AstNode* lhs = parse_equality_expr(p); + while (1) { + if (consume_token_if(p, TokenKind_and)) { + AstNode* rhs = parse_equality_expr(p); + lhs = ast_new_binary_expr(TokenKind_and, lhs, rhs); + lhs->ty = type_new(TypeKind_int); + } else { + break; + } + } + return lhs; +} + +// bitwise-xor-expr: +// bitwise-and-expr { '^' bitwise-and-expr }* +static AstNode* parse_bitwise_xor_expr(Parser* p) { + AstNode* lhs = parse_bitwise_and_expr(p); + while (1) { + if (consume_token_if(p, TokenKind_xor)) { + AstNode* rhs = parse_bitwise_and_expr(p); + lhs = ast_new_binary_expr(TokenKind_xor, lhs, rhs); + lhs->ty = type_new(TypeKind_int); + } else { + break; + } + } + return lhs; +} + +// bitwise-or-expr: +// bitwise-xor-expr { '|' bitwise-xor-expr }* +static AstNode* parse_bitwise_or_expr(Parser* p) { + AstNode* lhs = parse_bitwise_xor_expr(p); + while (1) { + if (consume_token_if(p, TokenKind_or)) { + AstNode* rhs = parse_bitwise_xor_expr(p); + lhs = ast_new_binary_expr(TokenKind_or, lhs, rhs); + lhs->ty = type_new(TypeKind_int); + } else { + break; + } + } + return lhs; +} + +// logical-and-expr: +// bitwise-or-expr { '&&' bitwise-or-expr }* +static AstNode* parse_logical_and_expr(Parser* p) { + AstNode* lhs = parse_bitwise_or_expr(p); + while (1) { + if (consume_token_if(p, TokenKind_andand)) { + AstNode* rhs = parse_bitwise_or_expr(p); + lhs = ast_new_logical_expr(TokenKind_andand, lhs, rhs); + } else { + break; + } + } + return lhs; +} + +// logical-or-expr: +// logical-and-expr { '||' logical-and-expr }* +static AstNode* parse_logical_or_expr(Parser* p) { + AstNode* lhs = parse_logical_and_expr(p); + while (1) { + if (consume_token_if(p, TokenKind_oror)) { + AstNode* rhs = parse_logical_and_expr(p); + lhs = ast_new_logical_expr(TokenKind_oror, lhs, rhs); + } else { + break; + } + } + return lhs; +} + +// conditional-expr: +// logical-or-expr ( '?' expr ':' conditional-expr )? +static AstNode* parse_conditional_expr(Parser* p) { + AstNode* e = parse_logical_or_expr(p); + if (consume_token_if(p, TokenKind_question)) { + AstNode* then_expr = parse_expr(p); + expect(p, TokenKind_colon); + AstNode* else_expr = parse_assignment_expr(p); + return ast_new_cond_expr(e, then_expr, else_expr); + } else { + return e; + } +} + +// assignment-expr: +// conditional-expr +// unary-expr assignment-operator assignment-expr +static AstNode* parse_assignment_expr(Parser* p) { + AstNode* lhs = parse_conditional_expr(p); + while (1) { + TokenKind op = peek_token(p)->kind; + // TODO: check if the lhs is unary expression. + if (op == TokenKind_assign || op == TokenKind_assign_mul || op == TokenKind_assign_div || + op == TokenKind_assign_mod || op == TokenKind_assign_or || op == TokenKind_assign_and || + op == TokenKind_assign_xor || op == TokenKind_assign_lshift || op == TokenKind_assign_rshift) { + next_token(p); + AstNode* rhs = parse_assignment_expr(p); + lhs = ast_new_assign_expr(op, lhs, rhs); + } else if (consume_token_if(p, TokenKind_assign_add)) { + AstNode* rhs = parse_assignment_expr(p); + lhs = ast_new_assign_add_expr(lhs, rhs); + } else if (consume_token_if(p, TokenKind_assign_sub)) { + AstNode* rhs = parse_assignment_expr(p); + lhs = ast_new_assign_sub_expr(lhs, rhs); + } else { + break; + } + } + return lhs; +} + +// expr: +// assignment-expr { ',' assignment-expr }* +static AstNode* parse_expr(Parser* p) { + AstNode* lhs = parse_assignment_expr(p); + while (1) { + if (consume_token_if(p, TokenKind_comma)) { + AstNode* rhs = parse_assignment_expr(p); + AstNode* list = ast_new_list(2); + ast_append(list, lhs); + ast_append(list, rhs); + list->ty = rhs->ty; + lhs = list; + } else { + break; + } + } + return lhs; +} + +// constant-expr: +// conditional-expr +static AstNode* parse_constant_expr(Parser* p) { + return parse_conditional_expr(p); +} + +// pointer: +// { '*' TODO attribute-specifier-sequence? type-qualifier-list? }+ +static Type* parse_pointer_opt(Parser* p, Type* ty) { + while (peek_token(p)->kind == TokenKind_star) { + next_token(p); + ty = type_new_ptr(ty); + parse_type_qualifier_list_opt(p); + } + return ty; +} + +// type-qualifier-list: +// { type-qualifier }+ +static void parse_type_qualifier_list_opt(Parser* p) { + while (1) { + Token* tok = peek_token(p); + if (tok->kind == TokenKind_keyword_const || tok->kind == TokenKind_keyword_restrict || + tok->kind == TokenKind_keyword_volatile || tok->kind == TokenKind_keyword__Atomic) { + // TODO: currently type qualifiers are just ignored. + next_token(p); + } else { + break; + } + } +} + +// declarator | abstract-declarator?: +// pointer? identifier TODO attribute-specifier-sequence? { direct-declarator-suffix }* +// pointer? '(' declarator ')' { direct-declarator-suffix }* +// pointer? '(' abstract-declarator ')' { direct-declarator-suffix }* +// pointer? { direct-declarator-suffix }* +// +// direct-declarator-suffix: +// array-declarator-suffix TODO attribute-specifier-sequence? +// function-declarator-suffix TODO attribute-specifier-sequence? +static AstNode* parse_declarator_or_abstract_declarator_opt(Parser* p, Type* ty) { + ty = parse_pointer_opt(p, ty); + + AstNode* decl; + if (peek_token(p)->kind == TokenKind_ident) { + decl = ast_new_declarator(parse_ident(p)->value.string, ty); + } else if (peek_token(p)->kind == TokenKind_paren_l && !is_type_token(p, peek_token2(p))) { + next_token(p); + if (peek_token(p)->kind == TokenKind_paren_r) { + Token* tok = peek_token(p); + fatal_error("%s:%d: expected declarator, but got '%s'", tok->loc.filename, tok->loc.line, + token_stringify(tok)); + } + decl = parse_declarator_or_abstract_declarator_opt(p, ty); + expect(p, TokenKind_paren_r); + } else { + decl = ast_new_declarator(NULL, ty); + } + + while (1) { + if (peek_token(p)->kind == TokenKind_bracket_l) { + decl->ty = parse_array_declarator_suffix(p, decl->ty); + } else if (peek_token(p)->kind == TokenKind_paren_l) { + decl->ty = parse_function_declarator_suffix(p, decl->ty); + } else { + break; + } + } + + return decl; +} + +// parameter-type-list: +// parameter-list +// parameter-list ',' '...' +// '...' +static AstNode* parse_parameter_type_list(Parser* p) { + AstNode* params = parse_parameter_list(p); + + bool has_void = false; + for (int i = 0; i < params->as.list->len; ++i) { + if (params->as.list->items[i].as.declarator->name && + strcmp(params->as.list->items[i].as.declarator->name, "...") == 0) { + if (i != params->as.list->len - 1) { + fatal_error("..."); + } + --params->as.list->len; + break; + } + has_void |= params->as.list->items[i].ty->kind == TypeKind_void; + } + + if (has_void) { + if (params->as.list->len != 1) { + fatal_error("invalid use of void param"); + } + params->as.list->len = 0; + } + + return params; +} + +// parameter-list: +// { parameter-declaration | ',' } +static AstNode* parse_parameter_list(Parser* p) { + AstNode* params = ast_new_list(4); + + while (1) { + AstNode* param = parse_parameter_declaration(p); + ast_append(params, param); + + if (!consume_token_if(p, TokenKind_comma)) { + break; + } + } + + return params; +} + +// parameter-declaration: +// TODO attribute-specifier-sequence? declaration-specifiers declarator +// TODO attribute-specifier-sequence? declaration-specifiers abstract-declarator? +static AstNode* parse_parameter_declaration(Parser* p) { + if (consume_token_if(p, TokenKind_ellipsis)) { + return ast_new_declarator("...", NULL); + } + + Type* base_ty = parse_declaration_specifiers(p); + return parse_declarator_or_abstract_declarator_opt(p, base_ty); +} + +// array-declarator: +// direct-declarator '[' TODO type-qualifier-list? TODO assignment-expr? ']' +// TODO direct-declarator '[' 'static' type-qualifier-list? assignment-expr? ']' +// TODO direct-declarator '[' type-qualifier-list 'static' assignment-expr? ']' +// TODO direct-declarator '[' type-qualifier-list? '*' ']' +static Type* parse_array_declarator_suffix(Parser* p, Type* ty) { + next_token(p); // skip '[' + + int size = -1; + if (peek_token(p)->kind != TokenKind_bracket_r) { + AstNode* size_expr = parse_assignment_expr(p); + size = eval(size_expr); + } + expect(p, TokenKind_bracket_r); + + return type_new_array(ty, size); +} + +// function-declarator: +// direct-declarator '(' parameter-type-list? ')' +static Type* parse_function_declarator_suffix(Parser* p, Type* ty) { + next_token(p); // skip '(' + AstNode* params; + + if (consume_token_if(p, TokenKind_paren_r)) { + params = ast_new_list(1); + } else { + params = parse_parameter_type_list(p); + expect(p, TokenKind_paren_r); + } + + return type_new_func(ty, params); +} + +// direct-declarator: +// identifier TODO attribute-specifier-sequence? +// '(' declarator ')' +// array-declarator TODO attribute-specifier-sequence? +// function-declarator TODO attribute-specifier-sequence? +static AstNode* parse_direct_declarator(Parser* p, Type* ty) { + AstNode* decl; + if (peek_token(p)->kind == TokenKind_ident) { + decl = ast_new_declarator(parse_ident(p)->value.string, ty); + } else if (peek_token(p)->kind == TokenKind_paren_l && !is_type_token(p, peek_token2(p))) { + next_token(p); + // 1st pass: skip content inside parentheses. + int saved_pos = p->pos; + Type* dummy = type_new(TypeKind_void); + parse_declarator(p, dummy); + expect(p, TokenKind_paren_r); + // Parse suffixes. + while (1) { + if (peek_token(p)->kind == TokenKind_bracket_l) { + ty = parse_array_declarator_suffix(p, ty); + } else if (peek_token(p)->kind == TokenKind_paren_l) { + ty = parse_function_declarator_suffix(p, ty); + } else { + break; + } + } + // 2nd pass: re-parse inner content. + int end_pos = p->pos; + p->pos = saved_pos; + decl = parse_declarator(p, ty); + expect(p, TokenKind_paren_r); + p->pos = end_pos; + return ast_new_declarator(decl->as.declarator->name, decl->ty); + } else { + decl = ast_new_declarator(NULL, ty); + } + + while (1) { + if (peek_token(p)->kind == TokenKind_bracket_l) { + decl->ty = parse_array_declarator_suffix(p, decl->ty); + } else if (peek_token(p)->kind == TokenKind_paren_l) { + decl->ty = parse_function_declarator_suffix(p, decl->ty); + } else { + break; + } + } + + return ast_new_declarator(decl->as.declarator->name, decl->ty); +} + +// declarator: +// pointer? direct-declarator +static AstNode* parse_declarator(Parser* p, Type* ty) { + ty = parse_pointer_opt(p, ty); + return parse_direct_declarator(p, ty); +} + +// init-declarator-list: +// init-declarator +// init-declarator-list ',' init-declarator +static AstNode* parse_init_declarator_list(Parser* p, Type* ty) { + AstNode* list = ast_new_list(1); + while (1) { + AstNode* d = parse_init_declarator(p, ty); + ast_append(list, d); + if (!consume_token_if(p, TokenKind_comma)) { + break; + } + + if (ty->storage_class == StorageClass_typedef) { + continue; + } + // Immediately declare to allow following initializer to access previous variables. For example, + // int a = 1, b = a; + process_declarations(p, &list->as.list->items[list->as.list->len - 1]); + } + return list; +} + +// declaration: +// static_assert-declaration +// TODO attribute-specifier-sequence ';' +// TODO attribute-specifier-sequence declaration-specifiers init-declarator-list ';' +// declaration-specifiers init-declarator-list ';' +static AstNode* parse_declaration(Parser* p) { + if (peek_token(p)->kind == TokenKind_keyword_static_assert) { + return parse_static_assert_declaration(p); + } + + Type* ty = parse_declaration_specifiers(p); + AstNode* decls = parse_init_declarator_list(p, ty); + expect(p, TokenKind_semicolon); + process_declarations(p, &decls->as.list->items[decls->as.list->len - 1]); + return decls; +} + +static void create_local_initializer(AstNode* list, AstNode* lhs, AstNode* init, Type* ty) { + if (init->kind == AstNodeKind_array_initializer) { + AstNode* items = init->as.array_initializer->list; + if (ty->kind == TypeKind_array) { + for (int i = 0; i < items->as.list->len; ++i) { + AstNode* idx = ast_new_binary_expr(TokenKind_star, ast_new_int(i), ast_new_int(type_sizeof(ty->base))); + AstNode* elem = ast_new_deref_expr(ast_new_binary_expr(TokenKind_plus, lhs, idx)); + create_local_initializer(list, elem, &items->as.list->items[i], ty->base); + } + } else if (ty->kind == TypeKind_struct) { + AstNode* def = &ty->ref.defs->as.list->items[ty->ref.index]; + AstNode* members = def->as.struct_def->members; + for (int i = 0; i < items->as.list->len; ++i) { + const char* member_name = members->as.list->items[i].as.struct_member->name; + AstNode* member_lhs = ast_new_member_access_expr(ast_new_ref_expr(lhs), member_name); + create_local_initializer(list, member_lhs, &items->as.list->items[i], members->as.list->items[i].ty); + } + } else if (ty->kind == TypeKind_union) { + AstNode* def = &ty->ref.defs->as.list->items[ty->ref.index]; + AstNode* members = def->as.union_def->members; + const char* member_name = members->as.list->items[0].as.struct_member->name; + AstNode* member_lhs = ast_new_member_access_expr(ast_new_ref_expr(lhs), member_name); + create_local_initializer(list, member_lhs, &items->as.list->items[0], members->as.list->items[0].ty); + } + } else { + AstNode* assign = ast_new_assign_expr(TokenKind_assign, lhs, init); + ast_append(list, ast_new_expr_stmt(assign)); + } +} + +static void process_declarations(Parser* p, AstNode* decl) { + const char* name = decl->as.declarator->name; + if (decl->ty->kind == TypeKind_func) { + // TODO: refactor + decl->ty->storage_class = decl->ty->result->storage_class; + decl->ty->result->storage_class = StorageClass_unspecified; + register_func(p, name, decl->ty); + decl->kind = AstNodeKind_func_decl; + } else { + if (type_is_unsized(decl->ty)) { + fatal_error("process_declarations: invalid type for variable"); + } + + if (p->scope) { + if (find_lvar_in_current_scope(p, name) != -1) { + // TODO: use name's location. + fatal_error("%s:%d: '%s' redeclared", peek_token(p)->loc.filename, peek_token(p)->loc.line, name); + } + int stack_offset = add_lvar(p, name, decl->ty, calc_lvar_stack_offset(p, decl->ty)); + + if (decl->as.declarator->init) { + if (decl->as.declarator->init->kind == AstNodeKind_array_initializer) { + AstNode* lhs_base = ast_new_lvar(name, stack_offset, decl->ty); + AstNode* init = decl->as.declarator->init; + AstNode* stmt_list = ast_new_list(4); + create_local_initializer(stmt_list, lhs_base, init, decl->ty); + *decl = *stmt_list; + } else { + AstNode* lhs = ast_new_lvar(name, stack_offset, decl->ty); + AstNode* assign = ast_new_assign_expr(TokenKind_assign, lhs, decl->as.declarator->init); + decl->kind = AstNodeKind_expr_stmt; + decl->as.expr_stmt = calloc(1, sizeof(ExprStmtNode)); + decl->as.expr_stmt->expr = assign; + } + } else { + decl->kind = AstNodeKind_nop; + } + } else { + if (find_gvar(p, name) != -1) { + // TODO + // fatal_error("process_declarations: %s redeclared", name); + } + // TODO: refactor + Type* base_ty = decl->ty; + while (base_ty->base) { + base_ty = base_ty->base; + } + if (base_ty != decl->ty) { + decl->ty->storage_class = base_ty->storage_class; + base_ty->storage_class = StorageClass_unspecified; + } + + // TODO: refactor + if (decl->ty->kind == TypeKind_array && decl->ty->array_size == -1 && decl->as.declarator && + decl->as.declarator->init && decl->as.declarator->init->kind == AstNodeKind_array_initializer) { + decl->ty->array_size = decl->as.declarator->init->as.array_initializer->list->as.list->len; + } + + GlobalVar* gvar = gvars_push_new(&p->gvars); + gvar->name = name; + gvar->ty = decl->ty; + + if (decl->ty->storage_class == StorageClass_extern) { + decl->kind = AstNodeKind_nop; + } else { + AstNode* init_expr = decl->as.declarator ? decl->as.declarator->init : NULL; + decl->kind = AstNodeKind_gvar_decl; + decl->as.gvar_decl = calloc(1, sizeof(GvarDeclNode)); + decl->as.gvar_decl->name = name; + decl->as.gvar_decl->expr = init_expr; + } + } + } +} + +// function-definition: +// declaration-specifiers init-declarator-list compound-stmt +static AstNode* parse_function_definition(Parser* p, AstNode* decls) { + if (decls->as.list->len != 1) { + fatal_error("parse_function_definition: invalid syntax"); + } + + Type* ty = decls->as.list->items[0].ty; + Type* base_ty = ty->result; + while (base_ty->base) { + base_ty = base_ty->base; + } + ty->storage_class = base_ty->storage_class; + base_ty->storage_class = StorageClass_unspecified; + const char* name = decls->as.list->items[0].as.declarator->name; + AstNode* params = ty->params; + + register_func(p, name, ty); + enter_func(p); + register_params(p, params); + AstNode* body = parse_compound_stmt(p); + leave_func(p); + + int stack_size = 0; + if (p->lvars.len != 0) { + stack_size = p->lvars.data[p->lvars.len - 1].stack_offset + type_sizeof(p->lvars.data[p->lvars.len - 1].ty); + if (stack_size < 0) { + stack_size = 0; + } + } + return ast_new_func_def(name, ty, params, body, stack_size); +} + +static void process_typedefs(Parser* p, AstNode* decls) { + for (int i = 0; i < decls->as.list->len; ++i) { + AstNode* decl = &decls->as.list->items[i]; + + AstNode* typedef_ = ast_new_typedef_decl(decl->as.declarator->name, decl->ty); + ast_append(p->typedefs, typedef_); + } +} + +static char* generate_anonymous_name(Parser* p) { + char* buf = calloc(32, sizeof(char)); + sprintf(buf, "__anonymous_%d__", p->anonymous_user_type_counter++); + return buf; +} + +// declaration-specifiers: +// { declaration-specifier }+ TODO attribute-specifier-sequence? +// +// declaration-specifier: +// storage-class-specifier +// type-specifier-qualifier +// function-speicifier +// +// storage-class-specifier: +// 'auto' +// 'constexpr' +// 'extern' +// 'register' +// 'static' +// 'thread_local' +// 'typedef' +// +// type-specifier-qualifier: +// type-specifier +// type-qualifier +// alignment-specifier +// +// function-specifier: +// 'inline' +// '_Noreturn' +// +// type-specifier: +// 'void' +// 'char' +// 'short' +// 'int' +// 'long' +// 'float' +// 'double' +// 'signed' +// 'unsigned' +// TODO '_BitInt' '(' constant-expr ')' +// 'bool' +// '_Complex' +// '_Decimal32' +// '_Decimal64' +// '_Decimal128' +// atomic-type-specifier +// struct-or-union-specifier +// enum-specifier +// typeof-specifier +// typedef-name +// +// type-qualifier: +// 'const' +// 'restrict' +// 'volatile' +// '_Atomic' +// +// alignment-specifier: +// TODO 'alignas' '(' type-name ')' +// TODO 'alignas' '(' constant-expr ')' +// +// atomic-type-specifier: +// TODO '_Atomic' '(' type-name ')' +// +// struct-or-union-specifier: +// 'struct' ... +// 'union' ... +// +// enum-specifier: +// 'enum' ... +// +// typeof-specifier: +// TODO 'typeof' '(' typeof-specifier-argument ')' +// TODO 'typeof_unqual' '(' typeof-specifier-argument ')' +// +// typedef-name: +// identifier +static Type* parse_declaration_specifiers(Parser* p) { + StorageClass storage_class = StorageClass_unspecified; + int type_specifiers = 0; + Type* ty = NULL; + + Token* tok; + while (1) { + tok = peek_token(p); + + // storage-class-spciefier + if (tok->kind == TokenKind_keyword_auto) { + unimplemented(); + } else if (tok->kind == TokenKind_keyword_constexpr) { + unimplemented(); + } else if (tok->kind == TokenKind_keyword_extern) { + next_token(p); + storage_class = StorageClass_extern; + } else if (tok->kind == TokenKind_keyword_register) { + unimplemented(); + } else if (tok->kind == TokenKind_keyword_static) { + next_token(p); + storage_class = StorageClass_static; + } else if (tok->kind == TokenKind_keyword_thread_local) { + unimplemented(); + } else if (tok->kind == TokenKind_keyword_typedef) { + next_token(p); + storage_class = StorageClass_typedef; + } + // type-specifier-qualifier > type-specifier + else if (tok->kind == TokenKind_keyword_void) { + if (type_specifiers & TypeSpecifierMask_void) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_void; + } else if (tok->kind == TokenKind_keyword_char) { + if (type_specifiers & TypeSpecifierMask_char) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_char; + } else if (tok->kind == TokenKind_keyword_short) { + if (type_specifiers & TypeSpecifierMask_short) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_short; + } else if (tok->kind == TokenKind_keyword_int) { + if (type_specifiers & TypeSpecifierMask_int) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_int; + } else if (tok->kind == TokenKind_keyword_long) { + if (type_specifiers & (TypeSpecifierMask_long + TypeSpecifierMask_long)) { + fatal_error("%s:%d: too looong!", tok->loc.filename, tok->loc.line); + } + next_token(p); + type_specifiers += TypeSpecifierMask_long; + } else if (tok->kind == TokenKind_keyword_float) { + if (type_specifiers & TypeSpecifierMask_float) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_float; + } else if (tok->kind == TokenKind_keyword_double) { + if (type_specifiers & TypeSpecifierMask_double) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_double; + } else if (tok->kind == TokenKind_keyword_signed) { + if (type_specifiers & TypeSpecifierMask_signed) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_signed; + } else if (tok->kind == TokenKind_keyword_unsigned) { + if (type_specifiers & TypeSpecifierMask_unsigned) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_unsigned; + } else if (tok->kind == TokenKind_keyword__BitInt) { + if (type_specifiers & TypeSpecifierMask__BitInt) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask__BitInt; + } else if (tok->kind == TokenKind_keyword_bool) { + if (type_specifiers & TypeSpecifierMask_bool) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_bool; + } else if (tok->kind == TokenKind_keyword__Complex) { + if (type_specifiers & TypeSpecifierMask__Complex) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask__Complex; + } else if (tok->kind == TokenKind_keyword__Decimal32) { + if (type_specifiers & TypeSpecifierMask__Decimal32) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask__Decimal32; + } else if (tok->kind == TokenKind_keyword__Decimal64) { + if (type_specifiers & TypeSpecifierMask__Decimal64) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask__Decimal64; + } else if (tok->kind == TokenKind_keyword__Decimal128) { + if (type_specifiers & TypeSpecifierMask__Decimal128) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask__Decimal128; + } else if (tok->kind == TokenKind_keyword__Atomic) { + if (type_specifiers & TypeSpecifierMask__Atomic) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask__Atomic; + } else if (tok->kind == TokenKind_keyword_struct) { + if (type_specifiers & TypeSpecifierMask_struct) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + ty = parse_struct_specifier(p); + type_specifiers += TypeSpecifierMask_struct; + } else if (tok->kind == TokenKind_keyword_union) { + if (type_specifiers & TypeSpecifierMask_union) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + ty = parse_union_specifier(p); + type_specifiers += TypeSpecifierMask_union; + } else if (tok->kind == TokenKind_keyword_enum) { + if (type_specifiers & TypeSpecifierMask_enum) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + ty = parse_enum_specifier(p); + type_specifiers += TypeSpecifierMask_enum; + } else if (tok->kind == TokenKind_keyword_typeof) { + unimplemented(); + } else if (tok->kind == TokenKind_keyword_typeof_unqual) { + unimplemented(); + } else if (is_typedef_name(p, tok)) { + if (type_specifiers & TypeSpecifierMask_typedef_name) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + int typedef_idx = find_typedef(p, tok->value.string); + ty = type_dup(p->typedefs->as.list->items[typedef_idx].ty); + type_specifiers += TypeSpecifierMask_typedef_name; + } + // type-specifier-qualifier > type-qualifier + else if (tok->kind == TokenKind_keyword_const) { + // TODO + next_token(p); + } else if (tok->kind == TokenKind_keyword_restrict) { + // TODO + next_token(p); + } else if (tok->kind == TokenKind_keyword_volatile) { + // TODO + next_token(p); + } else if (tok->kind == TokenKind_keyword__Atomic) { + unimplemented(); + } + // type-specifier-qualifier > alignment-specifier + else if (tok->kind == TokenKind_keyword_alignas) { + unimplemented(); + } + // function-specifier + else if (tok->kind == TokenKind_keyword_inline) { + // TODO + next_token(p); + } else if (tok->kind == TokenKind_keyword__Noreturn) { + // TODO + next_token(p); + } else { + break; + } + } + + Type* ty_ = distinguish_type_from_type_specifiers(type_specifiers); + if (ty_) { + ty = ty_; + } + if (!ty) { + fatal_error("%s:%d: no type specifiers", tok->loc.filename, tok->loc.line); + } + + ty->storage_class = storage_class; + return ty; +} + +// init-declarator: +// declarator +// declarator '=' initializer +static AstNode* parse_init_declarator(Parser* p, Type* ty) { + AstNode* decl = parse_declarator(p, ty); + if (consume_token_if(p, TokenKind_assign)) { + decl->as.declarator->init = parse_initializer(p); + } + return decl; +} + +// struct-specifier: +// 'struct' TODO attribute-specifier-sequence? identifier? '{' member-declaration-list '}' +// 'struct' TODO attribute-specifier-sequence? identifier +static Type* parse_struct_specifier(Parser* p) { + SourceLocation struct_kw_loc = current_location(p); + next_token(p); + + const Token* name; + char* anonymous_name = NULL; + if (peek_token(p)->kind == TokenKind_brace_l) { + anonymous_name = generate_anonymous_name(p); + Token* anonymous_token = calloc(1, sizeof(Token)); + anonymous_token->kind = TokenKind_ident; + anonymous_token->value.string = anonymous_name; + anonymous_token->loc = struct_kw_loc; + name = anonymous_token; + } else { + name = parse_ident(p); + } + + if (!consume_token_if(p, TokenKind_brace_l)) { + int struct_idx = find_struct(p, name->value.string); + if (struct_idx != -1) { + Type* ty = type_new(TypeKind_struct); + ty->ref.defs = p->structs; + ty->ref.index = struct_idx; + return ty; + } else { + // TODO + AstNode* new_struct = ast_new_struct_def(name->value.string); + ast_append(p->structs, new_struct); + struct_idx = p->structs->as.list->len - 1; + + Type* ty = type_new(TypeKind_struct); + ty->ref.defs = p->structs; + ty->ref.index = struct_idx; + return ty; + } + } + + int struct_idx = find_struct(p, name->value.string); + if (struct_idx != -1 && p->structs->as.list->items[struct_idx].as.struct_def->members) { + fatal_error("%s:%d: struct %s redefined", name->loc.filename, name->loc.line, name->value.string); + } + + if (struct_idx == -1) { + AstNode* new_struct = ast_new_struct_def(name->value.string); + ast_append(p->structs, new_struct); + struct_idx = p->structs->as.list->len - 1; + } + + AstNode* members = parse_member_declaration_list(p); + expect(p, TokenKind_brace_r); + p->structs->as.list->items[struct_idx].as.struct_def->members = members; + + Type* ty = type_new(TypeKind_struct); + ty->ref.defs = p->structs; + ty->ref.index = struct_idx; + return ty; +} + +// union-specifier: +// 'union' TODO attribute-specifier-sequence? identifier? '{' member-declaration-list '}' +// 'union' TODO attribute-specifier-sequence? identifier +static Type* parse_union_specifier(Parser* p) { + SourceLocation union_kw_loc = current_location(p); + next_token(p); + + const Token* name; + char* anonymous_name = NULL; + if (peek_token(p)->kind == TokenKind_brace_l) { + anonymous_name = generate_anonymous_name(p); + Token* anonymous_token = calloc(1, sizeof(Token)); + anonymous_token->kind = TokenKind_ident; + anonymous_token->value.string = anonymous_name; + anonymous_token->loc = union_kw_loc; + name = anonymous_token; + } else { + name = parse_ident(p); + } + + if (!consume_token_if(p, TokenKind_brace_l)) { + int union_idx = find_union(p, name->value.string); + if (union_idx != -1) { + Type* ty = type_new(TypeKind_union); + ty->ref.defs = p->unions; + ty->ref.index = union_idx; + return ty; + } else { + // TODO + AstNode* new_union = ast_new_union_def(name->value.string); + ast_append(p->unions, new_union); + union_idx = p->unions->as.list->len - 1; + + Type* ty = type_new(TypeKind_union); + ty->ref.defs = p->unions; + ty->ref.index = union_idx; + return ty; + } + } + + int union_idx = find_union(p, name->value.string); + if (union_idx != -1 && p->unions->as.list->items[union_idx].as.union_def->members) { + fatal_error("%s:%d: union %s redefined", name->loc.filename, name->loc.line, name->value.string); + } + + if (union_idx == -1) { + AstNode* new_union = ast_new_union_def(name->value.string); + ast_append(p->unions, new_union); + union_idx = p->unions->as.list->len - 1; + } + + AstNode* members = parse_member_declaration_list(p); + expect(p, TokenKind_brace_r); + p->unions->as.list->items[union_idx].as.union_def->members = members; + + Type* ty = type_new(TypeKind_union); + ty->ref.defs = p->unions; + ty->ref.index = union_idx; + return ty; +} + +// member-declaration-list: +// { member-declaration }+ +static AstNode* parse_member_declaration_list(Parser* p) { + AstNode* members = ast_new_list(4); + while (peek_token(p)->kind != TokenKind_brace_r) { + AstNode* decls = parse_member_declaration(p); + for (int i = 0; i < decls->as.list->len; i++) { + ast_append(members, &decls->as.list->items[i]); + } + } + return members; +} + +// member-declaration: +// TODO attribute-specifier-sequence? specifier-qualifier-list member-declaration-list? ';' +// TODO static_assert-declaration +static AstNode* parse_member_declaration(Parser* p) { + Type* base_ty = parse_specifier_qualifier_list(p); + + if (consume_token_if(p, TokenKind_semicolon)) { + char* name = generate_anonymous_name(p); + AstNode* decls = ast_new_list(1); + AstNode* member = ast_new(AstNodeKind_struct_member); + member->ty = base_ty; + member->as.struct_member = calloc(1, sizeof(StructMemberNode)); + member->as.struct_member->name = name; + ast_append(decls, member); + return decls; + } + AstNode* decls = parse_member_declarator_list(p, base_ty); + expect(p, TokenKind_semicolon); + return decls; +} + +// specifier-qualifier-list: +// { type-specifier-qualifier }+ TODO attribute-specifier-sequence? +static Type* parse_specifier_qualifier_list(Parser* p) { + int type_specifiers = 0; + Type* ty = NULL; + + while (1) { + Token* tok = peek_token(p); + + // type-specifier-qualifier > type-specifier + if (tok->kind == TokenKind_keyword_void) { + if (type_specifiers & TypeSpecifierMask_void) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_void; + } else if (tok->kind == TokenKind_keyword_char) { + if (type_specifiers & TypeSpecifierMask_char) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_char; + } else if (tok->kind == TokenKind_keyword_short) { + if (type_specifiers & TypeSpecifierMask_short) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_short; + } else if (tok->kind == TokenKind_keyword_int) { + if (type_specifiers & TypeSpecifierMask_int) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_int; + } else if (tok->kind == TokenKind_keyword_long) { + if (type_specifiers & (TypeSpecifierMask_long + TypeSpecifierMask_long)) { + fatal_error("%s:%d: too looong!", tok->loc.filename, tok->loc.line); + } + next_token(p); + type_specifiers += TypeSpecifierMask_long; + } else if (tok->kind == TokenKind_keyword_float) { + if (type_specifiers & TypeSpecifierMask_float) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_float; + } else if (tok->kind == TokenKind_keyword_double) { + if (type_specifiers & TypeSpecifierMask_double) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_double; + } else if (tok->kind == TokenKind_keyword_signed) { + if (type_specifiers & TypeSpecifierMask_signed) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_signed; + } else if (tok->kind == TokenKind_keyword_unsigned) { + if (type_specifiers & TypeSpecifierMask_unsigned) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_unsigned; + } else if (tok->kind == TokenKind_keyword__BitInt) { + if (type_specifiers & TypeSpecifierMask__BitInt) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask__BitInt; + } else if (tok->kind == TokenKind_keyword_bool) { + if (type_specifiers & TypeSpecifierMask_bool) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask_bool; + } else if (tok->kind == TokenKind_keyword__Complex) { + if (type_specifiers & TypeSpecifierMask__Complex) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask__Complex; + } else if (tok->kind == TokenKind_keyword__Decimal32) { + if (type_specifiers & TypeSpecifierMask__Decimal32) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask__Decimal32; + } else if (tok->kind == TokenKind_keyword__Decimal64) { + if (type_specifiers & TypeSpecifierMask__Decimal64) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask__Decimal64; + } else if (tok->kind == TokenKind_keyword__Decimal128) { + if (type_specifiers & TypeSpecifierMask__Decimal128) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask__Decimal128; + } else if (tok->kind == TokenKind_keyword__Atomic) { + if (type_specifiers & TypeSpecifierMask__Atomic) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + type_specifiers += TypeSpecifierMask__Atomic; + } else if (tok->kind == TokenKind_keyword_struct) { + if (type_specifiers & TypeSpecifierMask_struct) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + ty = parse_struct_specifier(p); + type_specifiers += TypeSpecifierMask_struct; + } else if (tok->kind == TokenKind_keyword_union) { + if (type_specifiers & TypeSpecifierMask_union) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + ty = parse_union_specifier(p); + type_specifiers += TypeSpecifierMask_union; + } else if (tok->kind == TokenKind_keyword_enum) { + if (type_specifiers & TypeSpecifierMask_enum) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + ty = parse_enum_specifier(p); + type_specifiers += TypeSpecifierMask_enum; + } else if (tok->kind == TokenKind_keyword_typeof) { + unimplemented(); + } else if (tok->kind == TokenKind_keyword_typeof_unqual) { + unimplemented(); + } else if (is_typedef_name(p, tok)) { + if (type_specifiers & TypeSpecifierMask_typedef_name) { + fatal_error("%s:%d: duplicate '%s'", tok->loc.filename, tok->loc.line, token_stringify(tok)); + } + next_token(p); + int typedef_idx = find_typedef(p, tok->value.string); + ty = type_dup(p->typedefs->as.list->items[typedef_idx].ty); + type_specifiers += TypeSpecifierMask_typedef_name; + } + // type-specifier-qualifier > type-qualifier + else if (tok->kind == TokenKind_keyword_const) { + // TODO + next_token(p); + } else if (tok->kind == TokenKind_keyword_restrict) { + unimplemented(); + } else if (tok->kind == TokenKind_keyword_volatile) { + // TODO + next_token(p); + } else if (tok->kind == TokenKind_keyword__Atomic) { + unimplemented(); + } + // type-specifier-qualifier > alignment-specifier + else if (tok->kind == TokenKind_keyword_alignas) { + unimplemented(); + } else { + break; + } + } + + Type* ty_ = distinguish_type_from_type_specifiers(type_specifiers); + if (ty_) { + ty = ty_; + } + + ty->storage_class = StorageClass_unspecified; + return ty; +} + +// member-declarator-list: +// { member-declarator | ',' }+ +static AstNode* parse_member_declarator_list(Parser* p, Type* base_ty) { + AstNode* list = ast_new_list(1); + while (1) { + AstNode* d = parse_member_declarator(p, base_ty); + ast_append(list, d); + if (!consume_token_if(p, TokenKind_comma)) { + break; + } + } + return list; +} + +// member-declarator: +// declarator +// declarator ':' constant-expr +// TODO ':' constant-expr +static AstNode* parse_member_declarator(Parser* p, Type* base_ty) { + AstNode* decl = parse_declarator(p, base_ty); + + const char* name = decl->as.declarator->name; + decl->kind = AstNodeKind_struct_member; + decl->as.struct_member = calloc(1, sizeof(StructMemberNode)); + decl->as.struct_member->name = name; + + if (consume_token_if(p, TokenKind_colon)) { + AstNode* bit_width_node = parse_constant_expr(p); + InitData* bit_width_evaluated = eval_init_expr(bit_width_node, type_new(TypeKind_int)); + if (bit_width_evaluated->len != 1) + fatal_error("parse_member_declarator: invalid bit-field"); + if (bit_width_evaluated->blocks[0].kind != InitDataBlockKind_bytes) + fatal_error("parse_member_declarator: invalid bit-field"); + if (bit_width_evaluated->blocks[0].as.bytes.len != sizeof(int)) + fatal_error("parse_member_declarator: invalid bit-field"); + int bit_width; + memcpy(&bit_width, bit_width_evaluated->blocks[0].as.bytes.buf, sizeof(int)); + if (bit_width < 0) + fatal_error("parse_member_declarator: invalid bit-field"); + if (bit_width % 8 != 0) + fatal_error("parse_member_declarator: unimplemented"); + decl->as.struct_member->is_bitfield = true; + decl->as.struct_member->bitfield_offset = -1; // TODO + decl->as.struct_member->bitfield_width = bit_width; + } + + return decl; +} + +// enum-specifier: +// 'enum' TODO attribute-specifier-sequence? identifier? TODO enum-type-specifier? '{' enumerator-list ','? '}' +// 'enum' identifier TODO enum-type-specifier? +static Type* parse_enum_specifier(Parser* p) { + SourceLocation enum_kw_loc = current_location(p); + next_token(p); + + const Token* name; + char* anonymous_name = NULL; + if (peek_token(p)->kind == TokenKind_brace_l) { + anonymous_name = generate_anonymous_name(p); + Token* anonymous_token = calloc(1, sizeof(Token)); + anonymous_token->kind = TokenKind_ident; + anonymous_token->value.string = anonymous_name; + anonymous_token->loc = enum_kw_loc; + name = anonymous_token; + } else { + name = parse_ident(p); + } + + if (!consume_token_if(p, TokenKind_brace_l)) { + int enum_idx = find_enum(p, name->value.string); + if (enum_idx != -1) { + Type* ty = type_new(TypeKind_enum); + ty->ref.defs = p->enums; + ty->ref.index = enum_idx; + return ty; + } else { + // TODO + AstNode* new_enum = ast_new_enum_def(name->value.string); + ast_append(p->enums, new_enum); + enum_idx = p->enums->as.list->len - 1; + + Type* ty = type_new(TypeKind_enum); + ty->ref.defs = p->enums; + ty->ref.index = enum_idx; + return ty; + } + } + + int enum_idx = find_enum(p, name->value.string); + if (enum_idx != -1 && p->enums->as.list->items[enum_idx].as.enum_def->members) { + fatal_error("%s:%d: enum %s redefined", name->loc.filename, name->loc.line, name->value.string); + } + + if (enum_idx == -1) { + AstNode* new_enum = ast_new_enum_def(name->value.string); + ast_append(p->enums, new_enum); + enum_idx = p->enums->as.list->len - 1; + } + + parse_enum_members(p, enum_idx); + expect(p, TokenKind_brace_r); + + Type* ty = type_new(TypeKind_enum); + ty->ref.defs = p->enums; + ty->ref.index = enum_idx; + return ty; +} + +// enumerator-list: +// { enumerator |? ',' }+ +static void parse_enum_members(Parser* p, int enum_idx) { + int next_value = 0; + AstNode* list = ast_new_list(16); + + if (!p->enums->as.list->items[enum_idx].as.enum_def->members) { + p->enums->as.list->items[enum_idx].as.enum_def->members = list; + } + + while (peek_token(p)->kind != TokenKind_brace_r) { + AstNode* member = parse_enum_member(p, next_value); + next_value = member->as.enum_member->value + 1; + + ast_append(list, member); + + if (!consume_token_if(p, TokenKind_comma)) { + break; + } + } +} + +// enumerator: +// enumeration-constant TODO attribute-specifier-sequence? ( '=' constant-expr )? +static AstNode* parse_enum_member(Parser* p, int next_value) { + const Token* name = parse_ident(p); + int value; + if (consume_token_if(p, TokenKind_assign)) { + AstNode* expr = parse_constant_expr(p); + value = eval(expr); + } else { + value = next_value; + } + return ast_new_enum_member(name->value.string, value); +} + +// type-name: +// specifier-qualifier-list abstract-declarator? +static Type* parse_type_name(Parser* p) { + Type* ty = parse_specifier_qualifier_list(p); + ty = parse_abstract_declarator_opt(p, ty); + return ty; +} + +// abstract-declarator: +// pointer +// pointer? TODO direct-abstract-declarator +// +// abstract-declarator?: +// pointer? TODO direct-abstract-declarator? +static Type* parse_abstract_declarator_opt(Parser* p, Type* ty) { + return parse_pointer_opt(p, ty); +} + +// braced-initializer: +// '{' { designation? initializer |? ',' }* '}' +static AstNode* parse_braced_initializer(Parser* p) { + AstNode* inits = ast_new_list(4); + expect(p, TokenKind_brace_l); + while (peek_token(p)->kind != TokenKind_brace_r) { + // TODO: support designation + AstNode* init = parse_initializer(p); + ast_append(inits, init); + if (!consume_token_if(p, TokenKind_comma)) { + break; + } + } + expect(p, TokenKind_brace_r); + return ast_new_array_initializer(inits); +} + +// initializer: +// braced-initializer +// assignment-expr +static AstNode* parse_initializer(Parser* p) { + if (peek_token(p)->kind == TokenKind_brace_l) { + return parse_braced_initializer(p); + } else { + return parse_assignment_expr(p); + } +} + +#ifndef __ducc__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +// attribute-specifier-sequence: +// { attribute-specifier }+ +static AstNode* parse_attribute_specifier_sequence_opt(Parser* p) { + AstNode* attrs = NULL; + AstNode* attr; + while ((attr = parse_attribute_specifier_opt(p))) { + unimplemented(); + } + return attrs; +} +#ifndef __ducc__ +#pragma GCC diagnostic pop +#endif + +// attribute-specifier: +// '[' '[' attribute-list ']' ']' +static AstNode* parse_attribute_specifier_opt(Parser* p) { + if (peek_token(p)->kind != TokenKind_bracket_l || peek_token2(p)->kind != TokenKind_bracket_l) { + return NULL; + } + next_token(p); // skip '[' + next_token(p); // skip '[' + AstNode* ret = parse_attribute_list(p); + expect(p, TokenKind_bracket_r); + expect(p, TokenKind_bracket_r); + return ret; +} + +// attribute-list: +// { attribute? | ',' } +static AstNode* parse_attribute_list(Parser* p) { + while (1) { + if (peek_token(p)->kind == TokenKind_ident) { + parse_attribute(p); + } + if (!consume_token_if(p, TokenKind_comma)) { + break; + } + } + unimplemented(); + return NULL; +} + +// attribute: +// attribute-token attribute-argument-clause? +// +// attribute-token: +// identifier ( '::' identifier )? +static AstNode* parse_attribute(Parser* p) { + expect(p, TokenKind_ident); + if (peek_token(p)->kind == TokenKind_colon && peek_token2(p)->kind == TokenKind_colon) { + next_token(p); + next_token(p); + expect(p, TokenKind_ident); + } + if (peek_token(p)->kind == TokenKind_paren_l) { + parse_attribute_argument_clause(p); + } + unimplemented(); + return NULL; +} + +// attribute-argument-clause: +// '(' balanced-token-sequence? ')' +static AstNode* parse_attribute_argument_clause(Parser* p) { + expect(p, TokenKind_paren_l); + if (peek_token(p)->kind != TokenKind_paren_r) { + parse_balanced_token_sequence(p); + } + expect(p, TokenKind_paren_r); + unimplemented(); + return NULL; +} + +// balanced-token-sequence: +// { balanced-token }+ +// +// balanced-token: +// '(' balanced-token-sequence? ')' +// '[' balanced-token-sequence? ']' +// '{' balanced-token-sequence? '}' +// any token other than a parenthesis, a bracket, or a brace +static AstNode* parse_balanced_token_sequence(Parser* p) { + int paren_depth = 0; + int bracket_depth = 0; + int brace_depth = 0; + while (1) { + Token* tok = peek_token(p); + if (tok->kind == TokenKind_paren_l) { + paren_depth++; + next_token(p); + } else if (tok->kind == TokenKind_paren_r) { + if (paren_depth == 0) + break; + paren_depth--; + next_token(p); + } else if (tok->kind == TokenKind_bracket_l) { + bracket_depth++; + next_token(p); + } else if (tok->kind == TokenKind_bracket_r) { + if (bracket_depth == 0) + break; + bracket_depth--; + next_token(p); + } else if (tok->kind == TokenKind_brace_l) { + brace_depth++; + next_token(p); + } else if (tok->kind == TokenKind_brace_r) { + if (brace_depth == 0) + break; + brace_depth--; + next_token(p); + } else if (tok->kind == TokenKind_eof) { + break; + } else { + next_token(p); + } + } + unimplemented(); + return NULL; +} + +// unlabaled-stmt: +// attribute-specifier-sequence? compound-stmt +// attribute-specifier-sequence? if-stmt +// attribute-specifier-sequence? switch-stmt +// attribute-specifier-sequence? while-stmt +// attribute-specifier-sequence? do-while-stmt +// attribute-specifier-sequence? for-stmt +// attribute-specifier-sequence? goto-stmt +// attribute-specifier-sequence? continue-stmt +// attribute-specifier-sequence? break-stmt +// attribute-specifier-sequence? return-stmt +// attribute-specifier-sequence? expr-stmt +static AstNode* parse_unlabaled_stmt(Parser* p) { + switch (peek_token(p)->kind) { + case TokenKind_brace_l: + return parse_compound_stmt(p); + case TokenKind_keyword_if: + return parse_if_stmt(p); + case TokenKind_keyword_switch: + return parse_switch_stmt(p); + case TokenKind_keyword_while: + return parse_while_stmt(p); + case TokenKind_keyword_do: + return parse_do_while_stmt(p); + case TokenKind_keyword_for: + return parse_for_stmt(p); + case TokenKind_keyword_goto: + return parse_goto_stmt(p); + case TokenKind_keyword_continue: + return parse_continue_stmt(p); + case TokenKind_keyword_break: + return parse_break_stmt(p); + case TokenKind_keyword_return: + return parse_return_stmt(p); + default: + return parse_expr_stmt(p); + } +} + +// label: +// TODO attribute-specifier-sequence? identifier ':' +// TODO attribute-specifier-sequence? 'case' constant-expr ':' +// TODO attribute-specifier-sequence? 'default' ':' +static AstNode* parse_label(Parser* p) { + SourceLocation loc = current_location(p); + Token* t = peek_token(p); + + AstNode* node; + if (t->kind == TokenKind_keyword_case) { + if (!p->current_switch) { + fatal_error("%s:%d: 'case' label not within a switch statement", t->loc.filename, t->loc.line); + } + expect(p, TokenKind_keyword_case); + AstNode* value = parse_constant_expr(p); + expect(p, TokenKind_colon); + node = ast_new_case_label(eval(value), ast_new_nop()); + } else if (t->kind == TokenKind_keyword_default) { + if (!p->current_switch) { + fatal_error("%s:%d: 'default' label not within a switch statement", t->loc.filename, t->loc.line); + } + expect(p, TokenKind_keyword_default); + expect(p, TokenKind_colon); + node = ast_new_default_label(ast_new_nop()); + } else { + // identifier ':' + Token* label_token = expect(p, TokenKind_ident); + expect(p, TokenKind_colon); + node = ast_new_label_stmt(label_token->value.string, ast_new_nop()); + } + node->loc = loc; + return node; +} + +static bool is_label_token(Parser* p) { + Token* t = peek_token(p); + return t->kind == TokenKind_keyword_case || t->kind == TokenKind_keyword_default || + (t->kind == TokenKind_ident && peek_token2(p)->kind == TokenKind_colon); +} + +// stmt: +// label stmt +// unlabaled-stmt +static AstNode* parse_stmt(Parser* p) { + if (is_label_token(p)) { + AstNode* label = parse_label(p); + AstNode* stmt = parse_stmt(p); + switch (label->kind) { + case AstNodeKind_case_label: + label->as.case_label->body = stmt; + break; + case AstNodeKind_default_label: + label->as.default_label->body = stmt; + break; + case AstNodeKind_label_stmt: + label->as.label_stmt->body = stmt; + break; + default: + unreachable(); + break; + } + return label; + } + return parse_unlabaled_stmt(p); +} + +// compound-stmt: +// '{' { block-item }* '}' +static AstNode* parse_compound_stmt(Parser* p) { + SourceLocation loc = current_location(p); + AstNode* list = ast_new_list(4); + expect(p, TokenKind_brace_l); + enter_scope(p); + while (peek_token(p)->kind != TokenKind_brace_r) { + AstNode* stmt = parse_block_item(p); + ast_append(list, stmt); + } + leave_scope(p); + expect(p, TokenKind_brace_r); + list->loc = loc; + return list; +} + +// block-item: +// declaration +// unlabaled-stmt +// label +static AstNode* parse_block_item(Parser* p) { + Token* t = peek_token(p); + + if (t->kind == TokenKind_keyword_static_assert || is_type_token(p, t)) { + return parse_declaration(p); + } else if (is_label_token(p)) { + return parse_label(p); + } else { + return parse_unlabaled_stmt(p); + } +} + +// expr-stmt: +// TODO attribute-specifier-sequence? expr? ';' +static AstNode* parse_expr_stmt(Parser* p) { + SourceLocation loc = current_location(p); + if (consume_token_if(p, TokenKind_semicolon)) { + AstNode* node = ast_new_nop(); + node->loc = loc; + return node; + } + + AstNode* e = parse_expr(p); + expect(p, TokenKind_semicolon); + AstNode* node = ast_new_expr_stmt(e); + node->loc = loc; + return node; +} + +// if-stmt: +// 'if' '(' expr ')' stmt ( 'else' stmt )? +static AstNode* parse_if_stmt(Parser* p) { + SourceLocation loc = current_location(p); + expect(p, TokenKind_keyword_if); + expect(p, TokenKind_paren_l); + AstNode* cond = parse_expr(p); + expect(p, TokenKind_paren_r); + AstNode* then_body = parse_stmt(p); + AstNode* else_body = NULL; + if (consume_token_if(p, TokenKind_keyword_else)) { + else_body = parse_stmt(p); + } + + AstNode* node = ast_new_if_stmt(cond, then_body, else_body); + node->loc = loc; + return node; +} + +// switch-stmt: +// 'switch' '(' expr ')' stmt +static AstNode* parse_switch_stmt(Parser* p) { + SourceLocation loc = current_location(p); + expect(p, TokenKind_keyword_switch); + expect(p, TokenKind_paren_l); + AstNode* expr = parse_expr(p); + expect(p, TokenKind_paren_r); + + AstNode* tmp_var = generate_temporary_lvar(p, expr->ty); + AstNode* assignment = ast_new_assign_expr(TokenKind_assign, tmp_var, expr); + AstNode* assign_stmt = ast_new_expr_stmt(assignment); + assign_stmt->loc = loc; + + AstNode* switch_stmt = ast_new_switch_stmt(tmp_var); + switch_stmt->loc = loc; + + AstNode* prev_switch = p->current_switch; + p->current_switch = switch_stmt; + switch_stmt->as.switch_stmt->body = parse_stmt(p); + p->current_switch = prev_switch; + + AstNode* list = ast_new_list(2); + list->loc = loc; + ast_append(list, assign_stmt); + ast_append(list, switch_stmt); + return list; +} + +// while-stmt: +// 'while' '(' expr ')' stmt +static AstNode* parse_while_stmt(Parser* p) { + SourceLocation loc = current_location(p); + expect(p, TokenKind_keyword_while); + expect(p, TokenKind_paren_l); + AstNode* cond = parse_expr(p); + expect(p, TokenKind_paren_r); + AstNode* body = parse_stmt(p); + AstNode* node = ast_new_for_stmt(NULL, cond, NULL, body); + node->loc = loc; + return node; +} + +// do-while-stmt: +// 'do' stmt 'while' '(' expr ')' ';' +static AstNode* parse_do_while_stmt(Parser* p) { + SourceLocation loc = current_location(p); + expect(p, TokenKind_keyword_do); + AstNode* body = parse_stmt(p); + expect(p, TokenKind_keyword_while); + expect(p, TokenKind_paren_l); + AstNode* cond = parse_expr(p); + expect(p, TokenKind_paren_r); + expect(p, TokenKind_semicolon); + + AstNode* node = ast_new_do_while_stmt(cond, body); + node->loc = loc; + return node; +} + +// for-stmt: +// 'for' '(' expr? ';' expr? ';' expr? ')' stmt +// 'for' '(' declaration expr? ';' expr? ')' stmt +static AstNode* parse_for_stmt(Parser* p) { + SourceLocation loc = current_location(p); + expect(p, TokenKind_keyword_for); + expect(p, TokenKind_paren_l); + AstNode* init = NULL; + AstNode* cond = NULL; + AstNode* update = NULL; + enter_scope(p); + if (peek_token(p)->kind != TokenKind_semicolon) { + if (is_type_token(p, peek_token(p))) { + AstNode* decls = parse_declaration(p); + AstNode* initializers = ast_new_list(1); + for (int i = 0; i < decls->as.list->len; i++) { + AstNode* item = &decls->as.list->items[i]; + if (item->kind == AstNodeKind_expr_stmt) { + ast_append(initializers, item->as.expr_stmt->expr); + } + } + init = initializers; + } else { + init = parse_expr(p); + expect(p, TokenKind_semicolon); + } + } else { + expect(p, TokenKind_semicolon); + } + if (peek_token(p)->kind != TokenKind_semicolon) { + cond = parse_expr(p); + } else { + cond = ast_new_int(1); + } + expect(p, TokenKind_semicolon); + if (peek_token(p)->kind != TokenKind_paren_r) { + update = parse_expr(p); + } + expect(p, TokenKind_paren_r); + AstNode* body = parse_stmt(p); + leave_scope(p); + AstNode* node = ast_new_for_stmt(init, cond, update, body); + node->loc = loc; + return node; +} + +// goto-stmt: +// 'goto' identifier ';' +static AstNode* parse_goto_stmt(Parser* p) { + SourceLocation loc = current_location(p); + expect(p, TokenKind_keyword_goto); + Token* label_token = expect(p, TokenKind_ident); + expect(p, TokenKind_semicolon); + AstNode* node = ast_new_goto_stmt(label_token->value.string); + node->loc = loc; + return node; +} + +// continue-stmt: +// 'continue' ';' +static AstNode* parse_continue_stmt(Parser* p) { + SourceLocation loc = current_location(p); + expect(p, TokenKind_keyword_continue); + expect(p, TokenKind_semicolon); + AstNode* node = ast_new_continue_stmt(); + node->loc = loc; + return node; +} + +// break-stmt: +// 'break' ';' +static AstNode* parse_break_stmt(Parser* p) { + SourceLocation loc = current_location(p); + expect(p, TokenKind_keyword_break); + expect(p, TokenKind_semicolon); + AstNode* node = ast_new_break_stmt(); + node->loc = loc; + return node; +} + +// return-stmt: +// 'return' expr? ';' +static AstNode* parse_return_stmt(Parser* p) { + SourceLocation loc = current_location(p); + expect(p, TokenKind_keyword_return); + if (consume_token_if(p, TokenKind_semicolon)) { + AstNode* node = ast_new_return_stmt(NULL); + node->loc = loc; + return node; + } + + AstNode* expr = parse_expr(p); + expect(p, TokenKind_semicolon); + AstNode* node = ast_new_return_stmt(expr); + node->loc = loc; + return node; +} + +// static_assert-declaration: +// 'static_assert' '(' constant-expr ( ',' string-literal )? ')' ';' +static AstNode* parse_static_assert_declaration(Parser* p) { + expect(p, TokenKind_keyword_static_assert); + expect(p, TokenKind_paren_l); + AstNode* predicate = parse_constant_expr(p); + Token* message = NULL; + if (consume_token_if(p, TokenKind_comma)) { + message = expect(p, TokenKind_literal_str); + } + // TODO: validate the assertion. + (void)predicate; + (void)message; + expect(p, TokenKind_paren_r); + expect(p, TokenKind_semicolon); + return NULL; +} + +// external-declaration: +// static_assert-declaration +// TODO attribute-specifier-sequence ';' +// TODO attribute-specifier-sequence function-definition-or-declaration-rest +// function-definition-or-declaration-rest +// +// function-definition-or-declaration-rest: +// declaration-specifiers init-declarator-list ';' +// declaration-specifiers init-declarator-list compound-stmt +static AstNode* parse_external_declaration(Parser* p) { + if (peek_token(p)->kind == TokenKind_keyword_static_assert) { + return parse_static_assert_declaration(p); + } + + Type* ty = parse_declaration_specifiers(p); + if (consume_token_if(p, TokenKind_semicolon)) { + // Type declaration. + return NULL; + } + + AstNode* decls = parse_init_declarator_list(p, ty); + + if (peek_token(p)->kind == TokenKind_brace_l) { + return parse_function_definition(p, decls); + } + + expect(p, TokenKind_semicolon); + if (ty->storage_class == StorageClass_typedef) { + process_typedefs(p, decls); + return NULL; + } else { + process_declarations(p, &decls->as.list->items[decls->as.list->len - 1]); + return decls; + } +} + +// translation-unit: +// { external-declaration }+ +Program* parse(TokenArray* tokens) { + Parser* p = parser_new(tokens); + AstNode* funcs = ast_new_list(32); + AstNode* vars = ast_new_list(16); + while (eof(p)) { + AstNode* n = parse_external_declaration(p); + if (!n) + continue; + if (n->kind == AstNodeKind_func_def) { + ast_append(funcs, n); + } else if (n->kind == AstNodeKind_gvar_decl) { + ast_append(vars, n); + } else if (n->kind == AstNodeKind_list) { + for (int i = 0; i < n->as.list->len; ++i) { + if (n->as.list->items[i].kind == AstNodeKind_gvar_decl) + ast_append(vars, &n->as.list->items[i]); + } + } + } + Program* prog = calloc(1, sizeof(Program)); + prog->funcs = funcs; + prog->vars = vars; + prog->str_literals = p->str_literals.data; + return prog; +} + +static int eval_binary_expr(int op, int v1, int v2) { + if (op == TokenKind_andand) { + return v1 && v2; + } else if (op == TokenKind_oror) { + return v1 || v2; + } else if (op == TokenKind_plus) { + return v1 + v2; + } else if (op == TokenKind_minus) { + return v1 - v2; + } else if (op == TokenKind_star) { + return v1 * v2; + } else if (op == TokenKind_slash) { + if (v2 == 0) { + fatal_error("eval: division by zero"); + } + return v1 / v2; + } else if (op == TokenKind_percent) { + if (v2 == 0) { + fatal_error("eval: division by zero"); + } + return v1 % v2; + } else if (op == TokenKind_eq) { + return v1 == v2; + } else if (op == TokenKind_ne) { + return v1 != v2; + } else if (op == TokenKind_lt) { + return v1 < v2; + } else if (op == TokenKind_le) { + return v1 <= v2; + } else if (op == TokenKind_lshift) { + return v1 << v2; + } else if (op == TokenKind_rshift) { + return v1 >> v2; + } else if (op == TokenKind_and) { + return v1 & v2; + } else if (op == TokenKind_or) { + return v1 | v2; + } else if (op == TokenKind_xor) { + return v1 ^ v2; + } else { + unimplemented(); + } +} + +static int eval(AstNode* e) { + if (e->kind == AstNodeKind_int_expr) { + return e->as.int_expr->value; + } else if (e->kind == AstNodeKind_unary_expr) { + int v = eval(e->as.unary_expr->operand); + if (e->as.unary_expr->op == TokenKind_not) { + return !v; + } else if (e->as.unary_expr->op == TokenKind_minus) { + return -v; + } else { + unimplemented(); + } + } else if (e->kind == AstNodeKind_binary_expr) { + int v1 = eval(e->as.binary_expr->lhs); + int v2 = eval(e->as.binary_expr->rhs); + return eval_binary_expr(e->as.binary_expr->op, v1, v2); + } else if (e->kind == AstNodeKind_logical_expr) { + int v1 = eval(e->as.logical_expr->lhs); + int v2 = eval(e->as.logical_expr->rhs); + return eval_binary_expr(e->as.logical_expr->op, v1, v2); + } else if (e->kind == AstNodeKind_cond_expr) { + int cond = eval(e->as.cond_expr->cond); + if (cond) { + return eval(e->as.cond_expr->then); + } else { + return eval(e->as.cond_expr->else_); + } + } else if (e->kind == AstNodeKind_cast_expr) { + return eval(e->as.cast_expr->operand); + } else if (e->kind == AstNodeKind_deref_expr) { + return eval(e->as.deref_expr->operand); + } else if (e->kind == AstNodeKind_ref_expr) { + return eval(e->as.ref_expr->operand); + } else { + unimplemented(); + } +} + +bool pp_eval_constant_expr(TokenArray* pp_tokens) { + TokenArray* tokens = convert_pp_tokens_to_tokens(pp_tokens); + Parser* p = parser_new(tokens); + AstNode* e = parse_constant_expr(p); + return eval(e) != 0; +} + +static InitData* initdata_new() { + InitData* init_data = calloc(1, sizeof(InitData)); + init_data->len = 0; + init_data->capacity = 1; + init_data->blocks = calloc(init_data->capacity, sizeof(InitDataBlock)); + return init_data; +} + +static void initdata_reserve(InitData* init_data, size_t size) { + if (size <= init_data->capacity) + return; + while (init_data->capacity < size) { + init_data->capacity *= 2; + } + init_data->blocks = realloc(init_data->blocks, init_data->capacity * sizeof(InitDataBlock)); + memset(init_data->blocks + init_data->len, 0, (init_data->capacity - init_data->len) * sizeof(InitDataBlock)); +} + +static InitDataBlock* initdata_push_new(InitData* init_data) { + initdata_reserve(init_data, init_data->len + 1); + return &init_data->blocks[init_data->len++]; +} + +static void initdata_append_addr(InitData* buf, const char* label) { + InitDataBlock* block = initdata_push_new(buf); + block->kind = InitDataBlockKind_addr; + block->as.addr.label = label; +} + +static void initdata_append_zeros(InitData* buf, size_t len) { + if (len == 0) + return; + char* data = calloc(len, sizeof(char)); + InitDataBlock* block = initdata_push_new(buf); + block->kind = InitDataBlockKind_bytes; + block->as.bytes.len = len; + block->as.bytes.buf = data; +} + +static void initdata_append_bytes(InitData* buf, const char* data, size_t len) { + char* copy = calloc(len, sizeof(char)); + memcpy(copy, data, len); + InitDataBlock* block = initdata_push_new(buf); + block->kind = InitDataBlockKind_bytes; + block->as.bytes.len = len; + block->as.bytes.buf = copy; +} + +static void do_eval_init_expr(InitData* buf, AstNode* expr, Type* ty) { + if (expr->kind == AstNodeKind_array_initializer) { + AstNode* list = expr->as.array_initializer->list; + if (ty->kind == TypeKind_array) { + for (int i = 0; i < list->as.list->len; ++i) { + do_eval_init_expr(buf, &list->as.list->items[i], ty->base); + } + } else if (ty->kind == TypeKind_struct) { + AstNode* def = &ty->ref.defs->as.list->items[ty->ref.index]; + AstNode* members = def->as.struct_def->members; + int offset = 0; + for (int i = 0; i < list->as.list->len; ++i) { + AstNode* member = &members->as.list->items[i]; + int align = type_alignof(member->ty); + int aligned_offset = to_aligned(offset, align); + initdata_append_zeros(buf, aligned_offset - offset); // padding + offset = aligned_offset; + do_eval_init_expr(buf, &list->as.list->items[i], member->ty); + offset += type_sizeof(member->ty); + } + int total = type_sizeof(ty); + initdata_append_zeros(buf, total - offset); // padding + } else if (ty->kind == TypeKind_union) { + AstNode* def = &ty->ref.defs->as.list->items[ty->ref.index]; + AstNode* members = def->as.union_def->members; + AstNode* member = &members->as.list->items[0]; + do_eval_init_expr(buf, &list->as.list->items[0], member->ty); + int member_size = type_sizeof(member->ty); + int total = type_sizeof(ty); + initdata_append_zeros(buf, total - member_size); // padding + } else { + unimplemented(); + } + } else if (ty->kind == TypeKind_ptr) { + if (expr->kind == AstNodeKind_str_expr) { + char label[32]; + sprintf(label, ".Lstr__%d", expr->as.str_expr->idx); + initdata_append_addr(buf, strdup(label)); + } else if (expr->kind == AstNodeKind_ref_expr) { + if (expr->as.ref_expr->operand->kind != AstNodeKind_gvar) { + unimplemented(); + } + initdata_append_addr(buf, expr->as.ref_expr->operand->as.gvar->name); + } else if (expr->kind == AstNodeKind_gvar) { + initdata_append_addr(buf, expr->as.gvar->name); + } else if (expr->kind == AstNodeKind_func) { + initdata_append_addr(buf, expr->as.func->name); + } else if (expr->kind == AstNodeKind_int_expr && expr->as.int_expr->value == 0) { + initdata_append_zeros(buf, type_sizeof(ty)); + } else { + unimplemented(); + } + } else { + int value = eval(expr); + int size = type_sizeof(ty); + char bytes[8]; + for (int i = 0; i < size; ++i) { + bytes[i] = (value >> (i * 8)) & 0xff; + } + initdata_append_bytes(buf, bytes, size); + } +} + +InitData* eval_init_expr(AstNode* expr, Type* ty) { + InitData* buf = initdata_new(); + do_eval_init_expr(buf, expr, ty); + return buf; +} -- cgit v1.3.1