From 0360086bab822d837cd8e1de78dbcc8c550bc6ab Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Tue, 31 May 2022 18:29:35 +0200 Subject: [PATCH] Restructure --- lambdas.kl | 4 + main.cpp | 18 +- new_ast.cpp | 9 + new_parse.cpp | 10 +- new_type.cpp | 267 ------------------ typechecking.cpp => typecheck.cpp | 239 +++------------- typecheck.h | 448 ++++++++++++++++++++++++++++++ 7 files changed, 516 insertions(+), 479 deletions(-) rename typechecking.cpp => typecheck.cpp (81%) create mode 100644 typecheck.h diff --git a/lambdas.kl b/lambdas.kl index 81920d6..cd34c06 100644 --- a/lambdas.kl +++ b/lambdas.kl @@ -13,6 +13,10 @@ if_stmt :: (cond: int): type return CONSTANT +for_stmt :: () + for i := 0, i + 10, i + pass + add_10 :: (size: int): int add_20 :: (new_size: int): int return 20 diff --git a/main.cpp b/main.cpp index f49ee9d..dbc49cc 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,3 @@ -#include "base.cpp" -#include "base_unicode.cpp" -#include "new_lex.cpp" -#include "new_ast.cpp" -#include "new_parse.cpp" -#include "typechecking.cpp" -#include "ccodegen.cpp" - /* ------------------------------------------------------------------------------- @@ -41,6 +33,7 @@ For now I don't thing it should be overloadable. @todo [ ] - For loop +[ ] - More operators [ ] - Init statements [ ] - Fixing access to constants, in C we cant have constants inside of structs / functions so we need to rewrite the tree [ ] - Default values in structs??? Should compound stmts bring values from default values?? Maybe not? Whats the alternative @@ -73,7 +66,14 @@ For now I don't thing it should be overloadable. */ - +#include "base.cpp" +#include "base_unicode.cpp" +#include "new_lex.cpp" +#include "new_ast.cpp" +#include "new_parse.cpp" +#include "typecheck.h" +#include "typecheck.cpp" +#include "ccodegen.cpp" int main(){ test_os_memory(); diff --git a/new_ast.cpp b/new_ast.cpp index e637f28..687a02e 100644 --- a/new_ast.cpp +++ b/new_ast.cpp @@ -11,6 +11,7 @@ Intern_String keyword_return; Intern_String keyword_if; Intern_String keyword_else; Intern_String keyword_for; +Intern_String keyword_pass; Intern_String keyword_cast; Intern_String keyword_enum; @@ -59,6 +60,7 @@ struct Parse_Ctx:Lexer{ keyword_cast = intern("cast"_s); keyword_return = intern("return"_s); keyword_if = intern("if"_s); + keyword_pass = intern("pass"_s); keyword_else = intern("else"_s); keyword_for = intern("for"_s); keyword_enum = intern_string(&interns, "enum"_s); @@ -98,6 +100,7 @@ enum Ast_Kind: U32{ AST_IF_NODE, AST_RETURN, AST_BLOCK, + AST_PASS, AST_LAMBDA, AST_LAMBDA_ARG, AST_ENUM, @@ -410,6 +413,12 @@ ast_for(Token *pos, Ast_Expr *init, Ast_Expr *cond, Ast_Expr *iter, Ast_Block *b return result; } +function Ast_Pass * +ast_pass(Token *pos){ + AST_NEW(Pass, PASS, pos, AST_STMT); + return result; +} + function Ast_Return * ast_return(Token *pos, Ast_Expr *expr){ AST_NEW(Return, RETURN, pos, AST_STMT); diff --git a/new_parse.cpp b/new_parse.cpp index cfee774..faaac64 100644 --- a/new_parse.cpp +++ b/new_parse.cpp @@ -207,8 +207,11 @@ parse_block(){ if(token_match_keyword(keyword_return)){ Ast_Expr *expr = 0; if(!token_is_scope()) expr = parse_expr(); - Ast_Return *return_stmt = ast_return(token, expr); - stmts.add(return_stmt); + stmts.add(ast_return(token, expr)); + } + + else if(token_match_keyword(keyword_pass)){ + stmts.add(ast_pass(token)); } else if(token_match_keyword(keyword_for)){ @@ -225,7 +228,8 @@ parse_block(){ } } - stmts.add(ast_for(token, actual_init, cond, iter, parse_block())); + Ast_Block *for_block = parse_block(); + stmts.add(ast_for(token, actual_init, cond, iter, for_block)); } else if(token_match_keyword(keyword_if)){ diff --git a/new_type.cpp b/new_type.cpp index 6c29166..8b13789 100644 --- a/new_type.cpp +++ b/new_type.cpp @@ -1,268 +1 @@ -enum Ast_Resolved_Type_Kind{ - TYPE_NONE, - TYPE_NULL, - TYPE_COMPLETING, - TYPE_INCOMPLETE, - TYPE_INT, - TYPE_BOOL, - TYPE_UNSIGNED, - TYPE_STRING, - TYPE_VOID, - TYPE_POINTER, - TYPE_ARRAY, - TYPE_LAMBDA, - TYPE_STRUCT, - TYPE_UNION, - TYPE_ENUM, - TYPE_TYPE, -}; - -const char *type_names[] = { - "[Invalid Ast_Resolved_Type]", - "[Null]", - "[Completing]", - "[Incomplete]", - "[Int]", - "[Bool]", - "[Unsigned]", - "[String]", - "[Void]", - "[Pointer]", - "[Array]", - "[Lambda]", - "[Struct]", - "[Union]", - "[Enum]", - "[Type]", -}; - -struct Ast_Resolved_Member{ - Ast_Resolved_Type *type; - Intern_String name; - U64 offset; -}; - -struct Ast_Resolved_Type{ - Ast_Resolved_Type_Kind kind; - SizeU size; - SizeU align; - - Ast *ast; - union{ - Ast_Resolved_Type *base; - struct{ - Ast_Resolved_Type *base; - SizeU size; - }arr; - struct{ - Array members; - }agg; - struct{ - Ast_Resolved_Type *ret; - Array args; - }func; - }; -}; - -const SizeU pointer_size = sizeof(SizeU); -const SizeU pointer_align = __alignof(SizeU); - -global Ast_Resolved_Type type__null = {TYPE_NULL}; -global Ast_Resolved_Type type__void = {TYPE_VOID}; -global Ast_Resolved_Type type__int = {TYPE_INT, sizeof(int), __alignof(int)}; -global Ast_Resolved_Type type__unsigned = {TYPE_INT, sizeof(unsigned), __alignof(unsigned)}; -global Ast_Resolved_Type type__string = {TYPE_STRING, sizeof(String), __alignof(String)}; -global Ast_Resolved_Type type__bool = {TYPE_BOOL, sizeof(bool), __alignof(bool)}; -global Ast_Resolved_Type type__type = {TYPE_TYPE}; - -global Ast_Resolved_Type *type_type = &type__type; -global Ast_Resolved_Type *type_void = &type__void; -global Ast_Resolved_Type *type_int = &type__int; -global Ast_Resolved_Type *type_unsigned = &type__unsigned; -global Ast_Resolved_Type *type_string = &type__string; -global Ast_Resolved_Type *type_bool = &type__bool; -global Ast_Resolved_Type *type_null = &type__null; - -force_inline B32 is_string(Ast_Resolved_Type *type){return type->kind == TYPE_STRING;} -force_inline B32 is_int(Ast_Resolved_Type *type){return type->kind == TYPE_INT;} -force_inline B32 is_struct(Ast_Resolved_Type *type){return type->kind == TYPE_STRUCT;} -force_inline B32 is_array(Ast_Resolved_Type *type){return type->kind == TYPE_ARRAY;} -force_inline B32 is_enum(Ast_Resolved_Type *type){return type->kind == TYPE_ENUM;} -force_inline B32 is_pointer(Ast_Resolved_Type *type){return type->kind == TYPE_POINTER;} - -function Ast_Resolved_Type * -type_new(Allocator *allocator, Ast_Resolved_Type_Kind kind, SizeU size, SizeU align){ - Ast_Resolved_Type *result = exp_alloc_type(allocator, Ast_Resolved_Type, AF_ZeroMemory); - result->kind = kind; - result->size = size; - result->align = align; - return result; -} - -function Ast_Resolved_Type * -type_copy(Allocator *a, Ast_Resolved_Type *type){ - Ast_Resolved_Type *result = exp_alloc_type(a, Ast_Resolved_Type); - memory_copy(result, type, sizeof(Ast_Resolved_Type)); - return result; -} - -function Ast_Resolved_Type * -type_pointer(Ast_Resolved_Type *base){ - Ast_Resolved_Type *result = (Ast_Resolved_Type *)map_get(&pctx->type_map, (void *)base); - if(!result){ - result = type_new(pctx->perm, TYPE_POINTER, pointer_size, pointer_align); - result->base = base; - map_insert(&pctx->type_map, base, result); - } - assert(result->kind == TYPE_POINTER); - return result; -} - -function Ast_Resolved_Type * -type_array(Ast_Resolved_Type *base, SizeU size){ - U64 hash = hash_mix(hash_ptr(base), hash_u64(size)); - Ast_Resolved_Type *result = (Ast_Resolved_Type *)map_get(&pctx->type_map, hash); - if(result){ - assert(result->kind == TYPE_ARRAY); - assert(result->arr.size == size); - assert(result->arr.base == base); - return result; - } - - result = type_new(pctx->perm, TYPE_ARRAY, pointer_size, pointer_align); - result->arr.base = base; - result->arr.size = size; - map_insert(&pctx->type_map, hash, result); - return result; -} - -function Ast_Resolved_Type * -type_lambda(Ast *ast, Ast_Resolved_Type *ret, Array args){ - U64 hash = hash_ptr(ret); - For(args) hash = hash_mix(hash, hash_ptr(it)); - Ast_Resolved_Type *result = (Ast_Resolved_Type *)map_get(&pctx->type_map, hash); - - if(result){ - assert(result->kind == TYPE_LAMBDA); - assert(result->func.ret == ret); - assert(result->func.args.len == args.len); - return result; - } - - result = type_new(pctx->perm, TYPE_LAMBDA, pointer_size, pointer_align); - result->ast = ast; - result->func.ret = ret; - result->func.args = args.tight_copy(pctx->perm); - map_insert(&pctx->type_map, hash, result); - - return result; -} - -function Ast_Resolved_Type * -type_enum(Ast_Enum *ast){ - Ast_Resolved_Type *type = resolve_typespec(ast->typespec, AST_CAN_BE_NULL); - if(!type) { - type = type_int; - } - - Ast_Resolved_Type *result = type_new(pctx->perm, TYPE_ENUM, type->size, type->align); - result->base = type; - result->ast = ast; - return result; -} - -function Ast_Resolved_Type * -type_incomplete(Ast *ast){ - Ast_Resolved_Type *result = type_new(pctx->perm, TYPE_INCOMPLETE, 0, 0); - result->ast = ast; - return result; -} - -function void -type_struct_complete(Ast_Resolved_Type *type, Ast_Struct *node){ - // @todo: compute size, alignement, offset !!! - // @note: resolve all the struct members first - type->kind = TYPE_COMPLETING; - Scratch scratch; - Array members = {scratch}; - For(node->members){ - Operand op = resolve_binding(it); - Intern_String name = ast_get_name(it); - sym_new_resolved(SYM_VAR, name, op.type, {}, it); - members.add({op.type, name}); - } - type->agg.members = members.tight_copy(pctx->perm); - type->kind = TYPE_STRUCT; - - // @note: resolve constant members after the struct got resolved - // this way we avoid a problem where we start resolving the function - // and this function has parameter of type parent struct - // which is being resolved right now, cyclic dependency happens. - // constants arent required to make struct work - For(node->const_members){ - Operand op = resolve_binding(it); - Intern_String name = ast_get_name(it); - sym_new_resolved(SYM_CONST, name, op.type, op.value, it); - } -} - -function Ast_Resolved_Type * -type_struct(Ast_Struct *agg){ - Ast_Resolved_Type *result = type_new(pctx->perm, TYPE_STRUCT, 0, 0); - result->ast = agg; - type_struct_complete(result, agg); - return result; -} - -function void -type_complete(Ast_Resolved_Type *type){ - if(type->kind == TYPE_COMPLETING){ - parsing_error(type->ast->pos, "Cyclic type dependency"); - } - else if(type->kind != TYPE_INCOMPLETE){ - return; - } - - Ast_Struct *node = (Ast_Struct *)type->ast; - type_struct_complete(type, node); - pctx->resolving_package->ordered.add((Ast_Named *)node->parent); -} - -function void -test_types(){ - Scratch scratch; - Parse_Ctx ctx = {}; - ctx.init(scratch, scratch); - pctx = &ctx; - - Ast_Resolved_Type *array_type1 = type_array(type_int, 32); - Ast_Resolved_Type *array_type2 = type_array(type_int, 32); - Ast_Resolved_Type *array_type3 = type_array(type_int, 48); - assert(array_type1 == array_type2); - assert(array_type2 != array_type3); - Ast_Resolved_Type *pointer_type1 = type_pointer(type_int); - Ast_Resolved_Type *pointer_type2 = type_pointer(type_int); - assert(pointer_type2 == pointer_type1); - Ast_Resolved_Type *pointer_type3 = type_pointer(pointer_type1); - Ast_Resolved_Type *pointer_type4 = type_pointer(pointer_type2); - assert(pointer_type3 != pointer_type1); - assert(pointer_type3 == pointer_type4); - - Array types = {scratch}; - types.add(type_array(type_int, 32)); - Ast_Resolved_Type *func_type1 = type_lambda(0, types[0], types); - Ast_Resolved_Type *func_type2 = type_lambda(0, types[0], types); - assert(func_type1 == func_type2); - - Array types2 = {scratch}; - { - types2.add(type_array(type_int, 32)); - types2.add(type_int); - } - types.add(type_int); - Ast_Resolved_Type *func_type3 = type_lambda(0, types[0], types); - Ast_Resolved_Type *func_type4 = type_lambda(0, types[0], types2); - assert(func_type1 != func_type3); - assert(func_type3 == func_type4); -} diff --git a/typechecking.cpp b/typecheck.cpp similarity index 81% rename from typechecking.cpp rename to typecheck.cpp index 7926e47..f0bd36a 100644 --- a/typechecking.cpp +++ b/typecheck.cpp @@ -1,187 +1,6 @@ #define CASE(kind,type) case AST_##kind: { Ast_##type *node = (Ast_##type *)ast; #define BREAK() } break -enum Sym_Kind{ - SYM_NONE, - SYM_CONST, - SYM_VAR, -}; - -enum Sym_State{ - SYM_NOT_RESOLVED, - SYM_RESOLVING, - SYM_RESOLVED, -}; - -#define VALUE_FIELDS \ - S64 int_val; \ - Intern_String intern_val; \ - Ast_Resolved_Type *type_val; -#define INLINE_VALUE_FIELDS union{Value value; union{VALUE_FIELDS};} - -union Value{VALUE_FIELDS}; - -struct Sym{ - Intern_String name; - Sym_Kind kind; - Sym_State state; - Ast *ast; - - Ast_Resolved_Type *type; - INLINE_VALUE_FIELDS; -}; - -struct Operand{ - Ast_Resolved_Type *type; - bool is_const; - INLINE_VALUE_FIELDS; -}; - - -struct Typecheck_Ctx{ // @todo - Ast_Resolved_Type *required_type; - Sym *const_sym; - B32 expr_can_be_null; -}; - -enum{AST_CANT_BE_NULL = 0, AST_CAN_BE_NULL = 1}; -function Ast_Resolved_Type *resolve_typespec(Ast_Expr *ast, B32 ast_can_be_null = AST_CANT_BE_NULL); -function Sym *resolve_name(Token *pos, Intern_String name); -function Operand resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *compound_required_type = 0, Sym *const_sym = 0); -function Operand resolve_binding(Ast *ast, Sym *sym = 0); -global Ast_Named empty_decl = {}; - -function void -sym_insert(Sym *sym){ - U64 hash = hash_string(sym->name.s); - Sym *is_sym = (Sym *)map_get(&pctx->syms, hash); - if(is_sym){ - parsing_error(sym->ast->pos, "Symbol with name: [%s] defined multiple times", sym->name.s.str); - } - if(pctx->scope > 0){ - pctx->local_syms.add(sym); - } - - map_insert(&pctx->syms, hash, sym); -} - -function Sym * -sym_get(Intern_String name){ - Sym *result = (Sym *)map_get(&pctx->syms, hash_string(name.s)); - return result; -} - -function S64 -scope_open(){ - S64 local_sym_count = pctx->local_syms.len; - pctx->scope++; - return local_sym_count; -} - -function void -scope_close(S64 local_sym_count){ - pctx->scope--; - assert(pctx->scope >= 0); - for(S64 i = local_sym_count; i < pctx->local_syms.len; i++){ - Sym *it = pctx->local_syms.data[i]; - void *removed = map_remove(&pctx->syms, hash_string(it->name.s)); - assert(removed); - } - pctx->local_syms.len = local_sym_count; -} - -function void -sym_associate(Ast *ast, Sym *sym){ - assert(ast); - assert(sym); - map_insert(&pctx->resolved, ast, sym); -} - -function Sym * -sym_new(Sym_Kind kind, Intern_String name, Ast *ast, B32 associate = true){ - Sym *result = exp_alloc_type(pctx->perm, Sym, AF_ZeroMemory); - result->name = name; - result->kind = kind; - result->ast = ast; - if(associate) sym_associate(ast, result); - return result; -} - -function Sym * -sym_new_resolved(Sym_Kind kind, Intern_String name, Ast_Resolved_Type *type, Value value, Ast *ast, B32 associate = true){ - Sym *result = sym_new(kind, name, ast, associate); - result->type = type; - result->state = SYM_RESOLVED; - result->value = value; - return result; -} - -function Sym * -resolved_get(Ast *ast){ - Sym *result = (Sym *)map_get(&pctx->resolved, ast); - assert(result); - return result; -} -#include "new_type.cpp" - -function Ast_Resolved_Type * -resolved_type_get(Ast_Expr *ast){ - Sym *result = resolved_get(ast); - assert(result->type == type_type); - assert(result->type); - return result->type_val; -} - -function Sym * -sym_type(Ast *ast, Ast_Resolved_Type *type, Intern_String name = {}, B32 associate = true){ - Value value; value.type_val = type; - Sym *result = sym_new_resolved(SYM_CONST, name, type_type, value, ast, associate); - return result; -} - -function Sym * -sym_insert(Sym_Kind kind, Intern_String name, Ast_Resolved_Type *type, Value value, Ast *ast){ - Sym *sym = sym_new_resolved(kind, name, type, value, ast); - sym_insert(sym); - return sym; -} - -function void -sym_insert_builtin_type(String name, Ast_Resolved_Type *type){ - Intern_String string = intern_string(&pctx->interns, name); - Sym *sym = sym_type(&empty_decl, type, string, false); - sym_insert(sym); -} - -function void -sym_insert_builtins(){ - sym_insert_builtin_type("void"_s, type_void); - sym_insert_builtin_type("bool"_s, type_bool); - sym_insert_builtin_type("int"_s, type_int); - sym_insert_builtin_type("String"_s, type_string); - - { - Intern_String string = intern_string(&pctx->interns, "true"_s); - Value val; val.int_val = 1; - Sym *sym = sym_new_resolved(SYM_CONST, string, type_bool, val, &empty_decl, false); - sym_insert(sym); - } - - { - Intern_String string = intern_string(&pctx->interns, "false"_s); - Value val; val.int_val = 0; - Sym *sym = sym_new_resolved(SYM_CONST, string, type_bool, val, &empty_decl, false); - sym_insert(sym); - } - - { - Intern_String string = intern_string(&pctx->interns, "null"_s); - Value val; val.int_val = 0; - Sym *sym = sym_new_resolved(SYM_CONST, string, type_null, val, &empty_decl, false); - sym_insert(sym); - } -} - function Ast_Resolved_Type * resolve_typespec(Ast_Expr *ast, B32 ast_can_be_null){ if(ast_can_be_null && ast == 0) return 0; @@ -236,9 +55,8 @@ resolve_stmt(Ast *ast, Ast_Resolved_Type *ret){ CASE(BINARY, Binary){ switch(node->op){ - case TK_Comma:{ - Operand left = resolve_expr(node->left); // needs to be lvalue - unused(left); + case TK_Colon:{ + // Operand left = resolve_expr(node->left); // needs to be lvalue Operand right = resolve_expr(node->right); assert(node->left->kind == AST_IDENT); Ast_Atom *atom = (Ast_Atom *)node->left; // @todo use left operand @@ -265,22 +83,6 @@ resolve_stmt(Ast *ast, Ast_Resolved_Type *ret){ } } -function Operand -operand(Sym *sym){ - Operand result = {}; - result.type = sym->type; - result.is_const = sym->kind == SYM_CONST ? true : false; - result.value = sym->value; - return result; -} - -function Operand -operand_type(Ast_Resolved_Type *type){ - Operand result = {type_type, true}; - result.type_val = type; - return result; -} - function Operand require_const_int(Ast_Expr *expr, B32 ast_can_be_null){ Operand op = resolve_expr(expr); @@ -839,3 +641,40 @@ parse_file(){ return result; } +function void +test_types(){ + Scratch scratch; + Parse_Ctx ctx = {}; + ctx.init(scratch, scratch); + pctx = &ctx; + + Ast_Resolved_Type *array_type1 = type_array(type_int, 32); + Ast_Resolved_Type *array_type2 = type_array(type_int, 32); + Ast_Resolved_Type *array_type3 = type_array(type_int, 48); + assert(array_type1 == array_type2); + assert(array_type2 != array_type3); + Ast_Resolved_Type *pointer_type1 = type_pointer(type_int); + Ast_Resolved_Type *pointer_type2 = type_pointer(type_int); + assert(pointer_type2 == pointer_type1); + Ast_Resolved_Type *pointer_type3 = type_pointer(pointer_type1); + Ast_Resolved_Type *pointer_type4 = type_pointer(pointer_type2); + assert(pointer_type3 != pointer_type1); + assert(pointer_type3 == pointer_type4); + + Array types = {scratch}; + types.add(type_array(type_int, 32)); + Ast_Resolved_Type *func_type1 = type_lambda(0, types[0], types); + Ast_Resolved_Type *func_type2 = type_lambda(0, types[0], types); + assert(func_type1 == func_type2); + + Array types2 = {scratch}; + { + types2.add(type_array(type_int, 32)); + types2.add(type_int); + } + types.add(type_int); + Ast_Resolved_Type *func_type3 = type_lambda(0, types[0], types); + Ast_Resolved_Type *func_type4 = type_lambda(0, types[0], types2); + assert(func_type1 != func_type3); + assert(func_type3 == func_type4); +} \ No newline at end of file diff --git a/typecheck.h b/typecheck.h new file mode 100644 index 0000000..d4c4610 --- /dev/null +++ b/typecheck.h @@ -0,0 +1,448 @@ +//----------------------------------------------------------------------------- +// Resolved Types +//----------------------------------------------------------------------------- +enum Ast_Resolved_Type_Kind{ + TYPE_NONE, + TYPE_NULL, + TYPE_COMPLETING, + TYPE_INCOMPLETE, + TYPE_INT, + TYPE_BOOL, + TYPE_UNSIGNED, + TYPE_STRING, + TYPE_VOID, + TYPE_POINTER, + TYPE_ARRAY, + TYPE_LAMBDA, + TYPE_STRUCT, + TYPE_UNION, + TYPE_ENUM, + TYPE_TYPE, +}; + +const char *type_names[] = { + "[Invalid Ast_Resolved_Type]", + "[Null]", + "[Completing]", + "[Incomplete]", + "[Int]", + "[Bool]", + "[Unsigned]", + "[String]", + "[Void]", + "[Pointer]", + "[Array]", + "[Lambda]", + "[Struct]", + "[Union]", + "[Enum]", + "[Type]", +}; + +struct Ast_Resolved_Member{ + Ast_Resolved_Type *type; + Intern_String name; + U64 offset; +}; + +struct Ast_Resolved_Type{ + Ast_Resolved_Type_Kind kind; + SizeU size; + SizeU align; + + Ast *ast; + union{ + Ast_Resolved_Type *base; + struct{ + Ast_Resolved_Type *base; + SizeU size; + }arr; + struct{ + Array members; + }agg; + struct{ + Ast_Resolved_Type *ret; + Array args; + }func; + }; +}; + +//----------------------------------------------------------------------------- +// Type globals +//----------------------------------------------------------------------------- +const SizeU pointer_size = sizeof(SizeU); +const SizeU pointer_align = __alignof(SizeU); +global Ast_Resolved_Type type__null = {TYPE_NULL}; +global Ast_Resolved_Type type__void = {TYPE_VOID}; +global Ast_Resolved_Type type__int = {TYPE_INT, sizeof(int), __alignof(int)}; +global Ast_Resolved_Type type__unsigned = {TYPE_INT, sizeof(unsigned), __alignof(unsigned)}; +global Ast_Resolved_Type type__string = {TYPE_STRING, sizeof(String), __alignof(String)}; +global Ast_Resolved_Type type__bool = {TYPE_BOOL, sizeof(bool), __alignof(bool)}; +global Ast_Resolved_Type type__type = {TYPE_TYPE}; + +global Ast_Resolved_Type *type_type = &type__type; +global Ast_Resolved_Type *type_void = &type__void; +global Ast_Resolved_Type *type_int = &type__int; +global Ast_Resolved_Type *type_unsigned = &type__unsigned; +global Ast_Resolved_Type *type_string = &type__string; +global Ast_Resolved_Type *type_bool = &type__bool; +global Ast_Resolved_Type *type_null = &type__null; + +//----------------------------------------------------------------------------- +// Symbols +//----------------------------------------------------------------------------- +enum Sym_Kind{ + SYM_NONE, + SYM_CONST, + SYM_VAR, +}; + +enum Sym_State{ + SYM_NOT_RESOLVED, + SYM_RESOLVING, + SYM_RESOLVED, +}; + +#define VALUE_FIELDS \ + S64 int_val; \ + Intern_String intern_val; \ + Ast_Resolved_Type *type_val; +#define INLINE_VALUE_FIELDS union{Value value; union{VALUE_FIELDS};} +union Value{VALUE_FIELDS}; + +struct Sym{ + Intern_String name; + Sym_Kind kind; + Sym_State state; + Ast *ast; + + Ast_Resolved_Type *type; + INLINE_VALUE_FIELDS; +}; + +struct Operand{ + Ast_Resolved_Type *type; + bool is_const; + INLINE_VALUE_FIELDS; +}; + +struct Typecheck_Ctx{ // @todo + Ast_Resolved_Type *required_type; + Sym *const_sym; + B32 expr_can_be_null; +}; + +enum{AST_CANT_BE_NULL = 0, AST_CAN_BE_NULL = 1}; +function Ast_Resolved_Type *resolve_typespec(Ast_Expr *ast, B32 ast_can_be_null = AST_CANT_BE_NULL); +function Sym *resolve_name(Token *pos, Intern_String name); +function Operand resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *compound_required_type = 0, Sym *const_sym = 0); +function Operand resolve_binding(Ast *ast, Sym *sym = 0); +global Ast_Named empty_decl = {}; + +//----------------------------------------------------------------------------- +// Symbol constructors and utils +//----------------------------------------------------------------------------- +function void +sym_insert(Sym *sym){ + U64 hash = hash_string(sym->name.s); + Sym *is_sym = (Sym *)map_get(&pctx->syms, hash); + if(is_sym) parsing_error(sym->ast->pos, "Symbol with name: [%s] defined multiple times", sym->name.s.str); + if(pctx->scope > 0) pctx->local_syms.add(sym); + map_insert(&pctx->syms, hash, sym); +} + +function Sym * +sym_get(Intern_String name){ + Sym *result = (Sym *)map_get(&pctx->syms, hash_string(name.s)); + return result; +} + +function S64 +scope_open(){ + S64 local_sym_count = pctx->local_syms.len; + pctx->scope++; + return local_sym_count; +} + +function void +scope_close(S64 local_sym_count){ + pctx->scope--; + assert(pctx->scope >= 0); + for(S64 i = local_sym_count; i < pctx->local_syms.len; i++){ + Sym *it = pctx->local_syms.data[i]; + void *removed = map_remove(&pctx->syms, hash_string(it->name.s)); + assert(removed); + } + pctx->local_syms.len = local_sym_count; +} + +function void +sym_associate(Ast *ast, Sym *sym){ + assert(ast); + assert(sym); + map_insert(&pctx->resolved, ast, sym); +} + +function Sym * +sym_new(Sym_Kind kind, Intern_String name, Ast *ast, B32 associate = true){ + Sym *result = exp_alloc_type(pctx->perm, Sym, AF_ZeroMemory); + result->name = name; + result->kind = kind; + result->ast = ast; + if(associate) sym_associate(ast, result); + return result; +} + +function Sym * +sym_new_resolved(Sym_Kind kind, Intern_String name, Ast_Resolved_Type *type, Value value, Ast *ast, B32 associate = true){ + Sym *result = sym_new(kind, name, ast, associate); + result->type = type; + result->state = SYM_RESOLVED; + result->value = value; + return result; +} + +function Sym * +resolved_get(Ast *ast){ + Sym *result = (Sym *)map_get(&pctx->resolved, ast); + assert(result); + return result; +} + +function Ast_Resolved_Type * +resolved_type_get(Ast_Expr *ast){ + Sym *result = resolved_get(ast); + assert(result->type == type_type); + assert(result->type); + return result->type_val; +} + +function Sym * +sym_type(Ast *ast, Ast_Resolved_Type *type, Intern_String name = {}, B32 associate = true){ + Value value; value.type_val = type; + Sym *result = sym_new_resolved(SYM_CONST, name, type_type, value, ast, associate); + return result; +} + +function Sym * +sym_insert(Sym_Kind kind, Intern_String name, Ast_Resolved_Type *type, Value value, Ast *ast){ + Sym *sym = sym_new_resolved(kind, name, type, value, ast); + sym_insert(sym); + return sym; +} + +function void +sym_insert_builtin_type(String name, Ast_Resolved_Type *type){ + Intern_String string = intern_string(&pctx->interns, name); + Sym *sym = sym_type(&empty_decl, type, string, false); + sym_insert(sym); +} + +function void +sym_insert_builtins(){ + sym_insert_builtin_type("void"_s, type_void); + sym_insert_builtin_type("bool"_s, type_bool); + sym_insert_builtin_type("int"_s, type_int); + sym_insert_builtin_type("String"_s, type_string); + + { + Intern_String string = intern_string(&pctx->interns, "true"_s); + Value val; val.int_val = 1; + Sym *sym = sym_new_resolved(SYM_CONST, string, type_bool, val, &empty_decl, false); + sym_insert(sym); + } + + { + Intern_String string = intern_string(&pctx->interns, "false"_s); + Value val; val.int_val = 0; + Sym *sym = sym_new_resolved(SYM_CONST, string, type_bool, val, &empty_decl, false); + sym_insert(sym); + } + + { + Intern_String string = intern_string(&pctx->interns, "null"_s); + Value val; val.int_val = 0; + Sym *sym = sym_new_resolved(SYM_CONST, string, type_null, val, &empty_decl, false); + sym_insert(sym); + } +} + +//----------------------------------------------------------------------------- +// Operands +//----------------------------------------------------------------------------- +function Operand +operand(Sym *sym){ + Operand result = {}; + result.type = sym->type; + result.is_const = sym->kind == SYM_CONST ? true : false; + result.value = sym->value; + return result; +} + +function Operand +operand_type(Ast_Resolved_Type *type){ + Operand result = {type_type, true}; + result.type_val = type; + return result; +} + +//----------------------------------------------------------------------------- +// Type constructors and utillities +//----------------------------------------------------------------------------- +force_inline B32 is_string(Ast_Resolved_Type *type){return type->kind == TYPE_STRING;} +force_inline B32 is_int(Ast_Resolved_Type *type){return type->kind == TYPE_INT;} +force_inline B32 is_struct(Ast_Resolved_Type *type){return type->kind == TYPE_STRUCT;} +force_inline B32 is_array(Ast_Resolved_Type *type){return type->kind == TYPE_ARRAY;} +force_inline B32 is_enum(Ast_Resolved_Type *type){return type->kind == TYPE_ENUM;} +force_inline B32 is_pointer(Ast_Resolved_Type *type){return type->kind == TYPE_POINTER;} + +function Ast_Resolved_Type * +type_new(Allocator *allocator, Ast_Resolved_Type_Kind kind, SizeU size, SizeU align){ + Ast_Resolved_Type *result = exp_alloc_type(allocator, Ast_Resolved_Type, AF_ZeroMemory); + result->kind = kind; + result->size = size; + result->align = align; + return result; +} + +function Ast_Resolved_Type * +type_copy(Allocator *a, Ast_Resolved_Type *type){ + Ast_Resolved_Type *result = exp_alloc_type(a, Ast_Resolved_Type); + memory_copy(result, type, sizeof(Ast_Resolved_Type)); + return result; +} + +function Ast_Resolved_Type * +type_pointer(Ast_Resolved_Type *base){ + Ast_Resolved_Type *result = (Ast_Resolved_Type *)map_get(&pctx->type_map, (void *)base); + if(!result){ + result = type_new(pctx->perm, TYPE_POINTER, pointer_size, pointer_align); + result->base = base; + map_insert(&pctx->type_map, base, result); + } + assert(result->kind == TYPE_POINTER); + return result; +} + +function Ast_Resolved_Type * +type_array(Ast_Resolved_Type *base, SizeU size){ + U64 hash = hash_mix(hash_ptr(base), hash_u64(size)); + Ast_Resolved_Type *result = (Ast_Resolved_Type *)map_get(&pctx->type_map, hash); + if(result){ + assert(result->kind == TYPE_ARRAY); + assert(result->arr.size == size); + assert(result->arr.base == base); + return result; + } + + result = type_new(pctx->perm, TYPE_ARRAY, pointer_size, pointer_align); + result->arr.base = base; + result->arr.size = size; + map_insert(&pctx->type_map, hash, result); + return result; +} + +function Ast_Resolved_Type * +type_lambda(Ast *ast, Ast_Resolved_Type *ret, Array args){ + U64 hash = hash_ptr(ret); + For(args) hash = hash_mix(hash, hash_ptr(it)); + Ast_Resolved_Type *result = (Ast_Resolved_Type *)map_get(&pctx->type_map, hash); + + if(result){ + assert(result->kind == TYPE_LAMBDA); + assert(result->func.ret == ret); + assert(result->func.args.len == args.len); + return result; + } + + result = type_new(pctx->perm, TYPE_LAMBDA, pointer_size, pointer_align); + result->ast = ast; + result->func.ret = ret; + result->func.args = args.tight_copy(pctx->perm); + map_insert(&pctx->type_map, hash, result); + + return result; +} + +function Ast_Resolved_Type * +type_enum(Ast_Enum *ast){ + Ast_Resolved_Type *type = resolve_typespec(ast->typespec, AST_CAN_BE_NULL); + if(!type) { + type = type_int; + } + + Ast_Resolved_Type *result = type_new(pctx->perm, TYPE_ENUM, type->size, type->align); + result->base = type; + result->ast = ast; + return result; +} + +/* +2022.05.31 - Global scope structs vs nested structs +Structs exist in 2 variants, the global scope structs are a bit different +then scoped structs. They startout incomplete and when some operation +requires the actual struct size, alignment, field access etc. then it +should call complete_type. It resolves all the children, calculates the +size and makes sure there are no cyclic dependencies. This is require for +correct behaviour of order independent structs. If someone just wants a pointer +to that struct we don't need to complete the type, we know how large a pointer is. +This allows us to have cyclic dependency that is a pointer. Cause we know how large pointer is. +*/ +function Ast_Resolved_Type * +type_incomplete(Ast *ast){ + Ast_Resolved_Type *result = type_new(pctx->perm, TYPE_INCOMPLETE, 0, 0); + result->ast = ast; + return result; +} + +function void +type_struct_complete(Ast_Resolved_Type *type, Ast_Struct *node){ + // @todo: compute size, alignement, offset !!! + // @note: resolve all the struct members first + type->kind = TYPE_COMPLETING; + Scratch scratch; + Array members = {scratch}; + For(node->members){ + Operand op = resolve_binding(it); + Intern_String name = ast_get_name(it); + sym_new_resolved(SYM_VAR, name, op.type, {}, it); + members.add({op.type, name}); + } + type->agg.members = members.tight_copy(pctx->perm); + type->kind = TYPE_STRUCT; + + /* + @note: resolve constant members after the struct got resolved + this way we avoid a problem where we start resolving the function + and this function has parameter of type parent struct + which is being resolved right now, cyclic dependency happens. + constants arent required to make struct work + */ + For(node->const_members){ + Operand op = resolve_binding(it); + Intern_String name = ast_get_name(it); + sym_new_resolved(SYM_CONST, name, op.type, op.value, it); + } +} + +function Ast_Resolved_Type * +type_struct(Ast_Struct *agg){ + Ast_Resolved_Type *result = type_new(pctx->perm, TYPE_STRUCT, 0, 0); + result->ast = agg; + type_struct_complete(result, agg); + return result; +} + +function void +type_complete(Ast_Resolved_Type *type){ + if(type->kind == TYPE_COMPLETING){ + parsing_error(type->ast->pos, "Cyclic type dependency"); + } + else if(type->kind != TYPE_INCOMPLETE){ + return; + } + + Ast_Struct *node = (Ast_Struct *)type->ast; + type_struct_complete(type, node); + pctx->resolving_package->ordered.add((Ast_Named *)node->parent); +}