diff --git a/ast.h b/ast.h index 175ce6f..ec2d0fa 100644 --- a/ast.h +++ b/ast.h @@ -45,6 +45,7 @@ struct AstStmt { typedef enum : unsigned int { AstSpec_Int = 1 << 0, AstSpec_Char = 1 << 1, + AstSpec_Void = 1 << 2, AstSpec_DefTy = 1 << 6, AstSpec_Unsigned = 1 << 7, AstSpec_Static = 1 << 8, diff --git a/ast_print.c b/ast_print.c index 76f5f16..fd4334a 100644 --- a/ast_print.c +++ b/ast_print.c @@ -262,7 +262,7 @@ void ast_expr_print(FILE* f, const AstExpr* node, int depth) } indent(f, depth + 1); } - fprintf(f, ",]\n"); + fprintf(f, "],\n"); indent(f, depth); fprintf(f, "}"); diff --git a/parse.c b/parse.c index 88b891a..0de34b9 100644 --- a/parse.c +++ b/parse.c @@ -328,7 +328,7 @@ static AstDecl* parse_decl(Parser* p, bool is_param) if (!is_param && first->ty == AstDeclar_Function && p->curr.ty == '{') { - sema_function_enter(p->sema, declars[0]); + sema_function_enter(p->sema, spec, declars[0]); function_body = parse_stmt_block(p); sema_function_leave(p->sema); diff --git a/sema.c b/sema.c index 5ef46f6..38e352f 100644 --- a/sema.c +++ b/sema.c @@ -2,8 +2,68 @@ #include "ast.h" #include "collection.h" #include +#include +#include #include +static void type_spec_print(FILE* file, TypeSpec spec) +{ + + struct { + TypeSpec spec; + const char* ident; + } spec_idents[] = { + { TypeSpec_Const, "const" }, + { TypeSpec_Short, "short" }, + { TypeSpec_Long, "long" }, + { TypeSpec_Long2, "long" }, + { TypeSpec_Unsigned, "unsigned" }, + }; + + size_t spec_idents_size = sizeof(spec_idents) / sizeof(spec_idents[0]); + + for (size_t i = 0; i < spec_idents_size; ++i) { + if (spec & spec_idents[i].spec) { + fprintf(file, "%s ", spec_idents[i].ident); + } + } +} + +void type_print(FILE* file, const Type* type) +{ + type_spec_print(file, type->spec); + + struct { + TypeTy ty; + const char* ident; + } ty_idents[] = { + { Type_Int, "int" }, + { Type_Char, "char" }, + { Type_Float, "float" }, + { Type_Double, "double" }, + { Type_Bool, "bool" }, + { Type_Void, "void" }, + }; + + size_t ty_idents_size = sizeof(ty_idents) / sizeof(ty_idents[0]); + + for (size_t i = 0; i < ty_idents_size; ++i) { + if (type->ty == ty_idents[i].ty) { + fprintf(file, "%s", ty_idents[i].ident); + return; + } + } + + if (type->ty == Type_Pointer) { + fprintf(file, " * "); + type_spec_print(file, type->pointer.spec); + type_print(file, type->pointer.inner); + return; + } + + assert(false && "not implemented"); +} + typedef struct { Hashmap ord_syms; Hashmap tag_syms; @@ -27,8 +87,10 @@ static void rib_deinit(Rib* r) struct Sema { Arena sym_arena; + Arena type_arena; ARRAY_FIELD(Rib, ribs); Hashmap labels; + Keymap expr_types; }; Sema* sema_new(void) @@ -36,16 +98,20 @@ Sema* sema_new(void) Sema* s = malloc(sizeof(Sema)); *s = (Sema) { .sym_arena = { }, + .type_arena = { }, ARRAY_FIELD_INIT(ribs), .labels = { }, + .expr_types = { }, }; arena_init(&s->sym_arena, 1024); + arena_init(&s->type_arena, 1024); ARRAY_FIELD_PUSH(s, ribs, (&(Rib) { })); rib_init(&s->ribs[0]); hashmap_init(&s->labels, sizeof(LabelSym*)); + keymap_init(&s->expr_types, sizeof(Type*)); return s; } @@ -53,11 +119,13 @@ Sema* sema_new(void) void sema_free(Sema* s) { arena_deinit(&s->sym_arena); + arena_deinit(&s->type_arena); for (size_t i = 0; i < s->ribs_size; ++i) rib_deinit(&s->ribs[i]); if (s->ribs) free(s->ribs); hashmap_deinit(&s->labels); + keymap_deinit(&s->expr_types); free(s); } @@ -74,7 +142,162 @@ void sema_scope_leave(Sema* s) s->ribs_size -= 1; } -void sema_function_enter(Sema* s, AstDeclar* declar) +static Sym* get_ord_sym(Sema* s, const char* ident) +{ + Sym* sym = nullptr; + for (size_t i = 0; i < s->ribs_size; ++i) + if ((sym = hashmap_get(&s->ribs[s->ribs_size - i - 1].ord_syms, ident))) + break; + return sym; +} + +static void error_if_multi_spec( + bool type_specced, Loc loc, const Type* first, TypeTy second_ty) +{ + if (type_specced) { + fprintf(stderr, "error: cannot combine type '"); + type_print(stderr, &(Type) { .ty = second_ty }); + fprintf(stderr, "' with type '"); + type_print(stderr, first); + fprintf(stderr, "' on line %d\n", loc.line); + exit(1); + } +} + +static void decode_decl_spec(Type* type, AstSpec as, Loc loc) +{ + bool type_specced = false; + + if (as & AstSpec_Int) { + error_if_multi_spec(type_specced, loc, type, Type_Char); + type_specced = true; + + type->ty = Type_Int; + } + if (as & AstSpec_Char) { + error_if_multi_spec(type_specced, loc, type, Type_Char); + type_specced = true; + + type->ty = Type_Char; + } + if (as & AstSpec_Void) { + error_if_multi_spec(type_specced, loc, type, Type_Void); + type_specced = true; + + type->ty = Type_Void; + } + if (as & (AstSpec_Enum | AstSpec_Struct | AstSpec_Union)) { + assert(false && "not implemented"); + } + + if (as & AstSpec_Unsigned) { + type->spec |= TypeSpec_Unsigned; + } + if (as & AstSpec_Const) { + type->spec |= TypeSpec_Const; + } +} + +static Type* check_decl_type(Sema* s, AstDecl* decl) +{ + Type* type = arena_alloc(&s->type_arena, sizeof(Type)); + *type = (Type) { }; + + decode_decl_spec(type, decl->spec, decl->loc); + + return type; +} + +static void error_if_redefinition(Hashmap* map, const char* ident, Loc loc) +{ + Sym* sym; + if ((sym = hashmap_get(map, ident))) { + fprintf(stderr, + "error: redefinition of '%s' on line %d\n", + ident, + loc.line); + exit(1); + } +} + +static void check_declar(Sema* s, + AstDeclar* declar, + Type* type, + Type** out_declar_type, + const char** out_declar_ident) +{ + switch (declar->ty) { + case AstDeclar_Ident: { + *out_declar_type = type; + *out_declar_ident = declar->ident; + return; + } + case AstDeclar_Pointer: { + Type* new_type = arena_alloc(&s->type_arena, sizeof(Type)); + *new_type = (Type) { + .ty = Type_Pointer, + .spec = 0, + .pointer = { + .inner = type, + .spec = 0, + }, + }; + decode_decl_spec(new_type, declar->pointer.spec, declar->loc); + check_declar(s, + declar->pointer.declar, + new_type, + out_declar_type, + out_declar_ident); + return; + } + case AstDeclar_Array: { + assert(false && "not implemented"); + break; + } + case AstDeclar_Function: { + Type* new_type = arena_alloc(&s->type_arena, sizeof(Type)); + *new_type = (Type) { + .ty = Type_Function, + .spec = 0, + .function = { + .return_type = type, + ARRAY_FIELD_INIT(params), + }, + }; + + for (size_t i = 0; i < declar->function.params_size; ++i) { + AstDecl* param_decl = declar->function.params[i]; + + assert(param_decl->declars_size == 1); + AstDeclar* param_declar = param_decl->declars[0]; + + Type* decl_type = check_decl_type(s, param_decl); + + const char* declar_ident; + Type* declar_type; + check_declar( + s, param_declar, decl_type, &declar_type, &declar_ident); + + (void)declar_ident; + + ARENA_ARRAY_FIELD_PUSH( + &s->type_arena, &new_type->function, params, &declar_type); + } + + // TODO: handle forward declarations + + check_declar(s, + declar->pointer.declar, + new_type, + out_declar_type, + out_declar_ident); + return; + } + } + assert(false && "unreachable"); +} + +void sema_function_enter(Sema* s, AstSpec spec, AstDeclar* declar) { assert(declar->ty == AstDeclar_Function); @@ -82,9 +305,49 @@ void sema_function_enter(Sema* s, AstDeclar* declar) hashmap_deinit(&s->labels); hashmap_init(&s->labels, sizeof(LabelSym*)); + Type* type = arena_alloc(&s->type_arena, sizeof(Type)); + *type = (Type) { }; + decode_decl_spec(type, spec, declar->loc); + + Rib* rib = &s->ribs[s->ribs_size - 1]; + error_if_redefinition(&rib->ord_syms, declar->ident, declar->loc); + + Sym* sym = arena_alloc(&s->sym_arena, sizeof(Sym)); + *sym = (Sym) { + .ty = Sym_Declar, + .declar = { + .type = type, + }, + }; + hashmap_set(&rib->ord_syms, declar->ident, &sym); + sema_scope_enter(s); - for (size_t i = 0; i < declar->function.params_size; ++i) { } + for (size_t i = 0; i < declar->function.params_size; ++i) { + AstDecl* param_decl = declar->function.params[i]; + + assert(param_decl->declars_size == 1); + AstDeclar* param_declar = param_decl->declars[0]; + + Type* decl_type = check_decl_type(s, param_decl); + + const char* declar_ident; + Type* declar_type; + check_declar(s, param_declar, decl_type, &declar_type, &declar_ident); + + Rib* rib = &s->ribs[s->ribs_size - 1]; + error_if_redefinition(&rib->ord_syms, declar->ident, declar->loc); + + Sym* sym = arena_alloc(&s->sym_arena, sizeof(Sym)); + *sym = (Sym) { + .ty = Sym_Param, + .param = { + .type = declar_type, + .idx = i, + }, + }; + hashmap_set(&rib->ord_syms, declar_ident, &sym); + } } void sema_function_leave(Sema* s) @@ -92,15 +355,90 @@ void sema_function_leave(Sema* s) sema_scope_leave(s); } -void sema_visit_function_declar(Sema* s, AstDeclar* declar) -{ -} - void sema_visit_stmt(Sema* s, AstStmt* stmt) { - if (stmt->ty == AstStmt_Decl) { } + if (stmt->ty == AstStmt_Decl) { + Type* type = check_decl_type(s, stmt->decl); + + if (stmt->decl->function_body) { + // assume already handled + return; + } + + for (size_t i = 0; i < stmt->decl->declars_size; ++i) { + AstDeclar* declar = stmt->decl->declars[i]; + + Type* declar_type; + const char* declar_ident; + check_declar(s, declar, type, &declar_type, &declar_ident); + + Rib* rib = &s->ribs[s->ribs_size - 1]; + error_if_redefinition(&rib->ord_syms, declar->ident, declar->loc); + + Sym* sym = arena_alloc(&s->sym_arena, sizeof(Sym)); + *sym = (Sym) { + .ty = Sym_Declar, + .declar = { + .type = declar_type, + }, + }; + hashmap_set(&rib->ord_syms, declar_ident, &sym); + } + } } void sema_visit_expr(Sema* s, AstExpr* expr) { + Type* type = nullptr; + + switch (expr->ty) { + case AstExpr_Ident: { + Sym* sym; + if ((sym = get_ord_sym(s, expr->ident)) == nullptr) { + fprintf(stderr, + "error: undefined identifier '%s' on line %d\n", + expr->ident, + expr->loc.line); + exit(1); + } + + switch (sym->ty) { + case Sym_Declar: + type = sym->declar.type; + break; + case Sym_Param: + type = sym->param.type; + break; + } + + break; + } + case AstExpr_Str: { + Type* inner_type = arena_alloc(&s->type_arena, sizeof(Type)); + *inner_type = (Type) { + .ty = Type_Char, + .spec = TypeSpec_Const, + }; + + type = arena_alloc(&s->type_arena, sizeof(Type)); + *type = (Type) { + .ty = Type_Pointer, + .pointer = { + .inner = inner_type, + .spec = 0, + }, + }; + break; + }; + case AstExpr_Call: { + Type* inner_type + = keymap_get(&s->expr_types, (size_t)expr->call.expr); + assert(inner_type); + printf("inner_type->ty == %d\n", inner_type->ty); + assert(inner_type->ty == Type_Function); + break; + } + } + + keymap_set(&s->expr_types, (KeymapKey)expr, &type); } diff --git a/sema.h b/sema.h index f278eb0..c2ef09f 100644 --- a/sema.h +++ b/sema.h @@ -2,12 +2,65 @@ #define SEMA_H #include "ast.h" +#include "collection.h" +#include +#include + +typedef enum { + Type_None, + Type_Int, + Type_Char, + Type_Float, + Type_Double, + Type_Bool, + Type_Void, + Type_Pointer, + Type_Function, +} TypeTy; + +typedef enum { + TypeSpec_Const = 1 << 0, + TypeSpec_Short = 1 << 1, + TypeSpec_Long = 1 << 2, + TypeSpec_Long2 = 1 << 3, + TypeSpec_Unsigned = 1 << 4, +} TypeSpec; + +typedef struct Type Type; + +struct Type { + TypeTy ty; + TypeSpec spec; + union { + struct { + Type* inner; + TypeSpec spec; + } pointer; + struct { + Type* return_type; + ARRAY_FIELD(Type*, params); + } function; + }; +}; + +void type_print(FILE* file, const Type* type); + typedef enum { Sym_Declar, + Sym_Param, } SymTy; typedef struct { SymTy ty; + union { + struct { + Type* type; + } declar; + struct { + Type* type; + size_t idx; + } param; + }; } Sym; typedef struct { @@ -22,7 +75,7 @@ void sema_free(Sema* s); void sema_scope_enter(Sema* s); void sema_scope_leave(Sema* s); -void sema_function_enter(Sema* s, AstDeclar* declar); +void sema_function_enter(Sema* s, AstSpec spec, AstDeclar* declar); void sema_function_leave(Sema* s); void sema_visit_stmt(Sema* s, AstStmt* stmt);