//apifn static void core_free_compiler() { deallocate(pctx->heap, pctx->type_map.data); deallocate(pctx->heap, pctx->interns.map.data); arena_release(pctx->stage_arena); arena_release(pctx->scratch); arena_release(pctx->perm); } //apifn static void core_init_compiler(Core_Ctx *ctx, Allocator *allocator) { ctx->time.init_context = os_time(); pctx = ctx; ctx->emit_type_info = true; ctx->emit_line_directives = true; ctx->debugger_break_on_compiler_error = true; ctx->color_codes_enabled = true; ctx->same_scope_token = { SAME_SCOPE }; arena_init(&ctx->perm_push_only, "Perm Push Only"_s); arena_init(&ctx->scratch_, "Scratch"_s); arena_init(&ctx->stage_arena_, "Stage Arena"_s); ctx->scratch = &ctx->scratch_; ctx->stage_arena = &ctx->stage_arena_; ctx->perm = &ctx->perm_push_only; ctx->heap = allocator; ctx->type_map = map_make(ctx->heap, 2048); ctx->gen = {ctx->perm}; ctx->helper_builder = {ctx->perm}; ctx->scope_ids = 1; bigint_allocator = ctx->perm; ctx->interns = intern_table_make(ctx->perm, ctx->heap, 2048); /*#import meta for i in meta.keywords: print(f'pctx->keyword_{i.lower()} = pctx->intern("{i}"_s);') print(f'pctx->interns.first_keyword = pctx->keyword_{meta.keywords[0].lower()}.str;') print(f'pctx->interns.last_keyword = pctx->keyword_{meta.keywords[-1].lower()}.str;') for i in meta.interns: print(f'pctx->intern_{i.lower()} = pctx->intern("{i}"_s);') index = 0 for i in meta.token_simple_expr: if i[1] != "SPECIAL": print(f'pctx->op_info_table[{index}] = {{pctx->intern("{i[1]}"_s), "{i[0].upper()}"_s, TK_{i[0]}, {int(i[2]&meta.BINARY_EXPR>0)}, {int(i[2]&meta.UNARY_EXPR>0)}}};') index += 1 */ pctx->keyword_struct = pctx->intern("struct"_s); pctx->keyword_union = pctx->intern("union"_s); pctx->keyword_true = pctx->intern("true"_s); pctx->keyword_default = pctx->intern("default"_s); pctx->keyword_break = pctx->intern("break"_s); pctx->keyword_false = pctx->intern("false"_s); pctx->keyword_return = pctx->intern("return"_s); pctx->keyword_switch = pctx->intern("switch"_s); pctx->keyword_assert = pctx->intern("Assert"_s); pctx->keyword_if = pctx->intern("if"_s); pctx->keyword_elif = pctx->intern("elif"_s); pctx->keyword_pass = pctx->intern("pass"_s); pctx->keyword_else = pctx->intern("else"_s); pctx->keyword_for = pctx->intern("for"_s); pctx->keyword_enum = pctx->intern("enum"_s); pctx->interns.first_keyword = pctx->keyword_struct.str; pctx->interns.last_keyword = pctx->keyword_enum.str; pctx->intern_typeof = pctx->intern("TypeOf"_s); pctx->intern_sizeof = pctx->intern("SizeOf"_s); pctx->intern_len = pctx->intern("Len"_s); pctx->intern_alignof = pctx->intern("AlignOf"_s); pctx->intern_foreign = pctx->intern("foreign"_s); pctx->intern_strict = pctx->intern("strict"_s); pctx->intern_void = pctx->intern("void"_s); pctx->intern_flag = pctx->intern("flag"_s); pctx->intern_it = pctx->intern("it"_s); pctx->intern_load = pctx->intern("load"_s); pctx->intern_import = pctx->intern("import"_s); pctx->intern_link = pctx->intern("link"_s); pctx->op_info_table[0] = {pctx->intern("*"_s), "MUL"_s, TK_Mul, 1, 0}; pctx->op_info_table[1] = {pctx->intern("/"_s), "DIV"_s, TK_Div, 1, 0}; pctx->op_info_table[2] = {pctx->intern("%"_s), "MOD"_s, TK_Mod, 1, 0}; pctx->op_info_table[3] = {pctx->intern("<<"_s), "LEFTSHIFT"_s, TK_LeftShift, 1, 0}; pctx->op_info_table[4] = {pctx->intern(">>"_s), "RIGHTSHIFT"_s, TK_RightShift, 1, 0}; pctx->op_info_table[5] = {pctx->intern("+"_s), "ADD"_s, TK_Add, 1, 1}; pctx->op_info_table[6] = {pctx->intern("-"_s), "SUB"_s, TK_Sub, 1, 1}; pctx->op_info_table[7] = {pctx->intern("=="_s), "EQUALS"_s, TK_Equals, 1, 0}; pctx->op_info_table[8] = {pctx->intern("<="_s), "LESSERTHENOREQUAL"_s, TK_LesserThenOrEqual, 1, 0}; pctx->op_info_table[9] = {pctx->intern(">="_s), "GREATERTHENOREQUAL"_s, TK_GreaterThenOrEqual, 1, 0}; pctx->op_info_table[10] = {pctx->intern("<"_s), "LESSERTHEN"_s, TK_LesserThen, 1, 0}; pctx->op_info_table[11] = {pctx->intern(">"_s), "GREATERTHEN"_s, TK_GreaterThen, 1, 0}; pctx->op_info_table[12] = {pctx->intern("!="_s), "NOTEQUALS"_s, TK_NotEquals, 1, 0}; pctx->op_info_table[13] = {pctx->intern("&"_s), "BITAND"_s, TK_BitAnd, 1, 0}; pctx->op_info_table[14] = {pctx->intern("|"_s), "BITOR"_s, TK_BitOr, 1, 0}; pctx->op_info_table[15] = {pctx->intern("^"_s), "BITXOR"_s, TK_BitXor, 1, 0}; pctx->op_info_table[16] = {pctx->intern("&&"_s), "AND"_s, TK_And, 1, 0}; pctx->op_info_table[17] = {pctx->intern("||"_s), "OR"_s, TK_Or, 1, 0}; pctx->op_info_table[18] = {pctx->intern("~"_s), "NEG"_s, TK_Neg, 0, 1}; pctx->op_info_table[19] = {pctx->intern("!"_s), "NOT"_s, TK_Not, 0, 1}; /*END*/ // Init types pctx->type__void = {TYPE_VOID}; // pctx->type__string = {TYPE_STRING, sizeof(String), __alignof(String)}; pctx->type__bool = {TYPE_BOOL, sizeof(bool), __alignof(bool)}; pctx->type__type = {TYPE_TYPE, sizeof(S64), __alignof(S64)}; pctx->type__f32 = {TYPE_F32, sizeof(F32), __alignof(F32)}; pctx->type__f64 = {TYPE_F64, sizeof(F64), __alignof(F64)}; pctx->type__s8 = {TYPE_S8, sizeof(S8), __alignof(S8)}; pctx->type__s16 = {TYPE_S16, sizeof(S16), __alignof(S16)}; pctx->type__s32 = {TYPE_S32, sizeof(S32), __alignof(S32)}; pctx->type__s64 = {TYPE_S64, sizeof(S64), __alignof(S64)}; pctx->type__u8 = {TYPE_U8, sizeof(U8), __alignof(U8), true}; pctx->type__u16 = {TYPE_U16, sizeof(U16), __alignof(U16), true}; pctx->type__u32 = {TYPE_U32, sizeof(U32), __alignof(U32), true}; pctx->type__u64 = {TYPE_U64, sizeof(U64), __alignof(U64), true}; pctx->type__untyped_bool = {TYPE_UNTYPED_BOOL, sizeof(bool), __alignof(bool)}; pctx->type__untyped_int = {TYPE_UNTYPED_INT, sizeof(S64), __alignof(S64)}; pctx->type__untyped_string = {TYPE_UNTYPED_STRING, sizeof(String), __alignof(String)}; pctx->type__untyped_float = {TYPE_UNTYPED_FLOAT, sizeof(double), __alignof(double)}; pctx->type__char = {TYPE_CHAR, sizeof(char), __alignof(char)}; pctx->type__int = {TYPE_INT, sizeof(int), __alignof(int)}; pctx->type_char = &pctx->type__char; pctx->type_int = &pctx->type__int; pctx->type_void = &pctx->type__void; //pctx->type_any; // Needs to be inited at runtime pctx->type_type = &pctx->type__type; // pctx->type_string = &pctx->type__string; pctx->type_bool = &pctx->type__bool; pctx->type_f32 = &pctx->type__f32; pctx->type_f64 = &pctx->type__f64; pctx->type_s8 = &pctx->type__s8 ; pctx->type_s16 = &pctx->type__s16; pctx->type_s32 = &pctx->type__s32; pctx->type_s64 = &pctx->type__s64; pctx->type_u8 = &pctx->type__u8 ; pctx->type_u16 = &pctx->type__u16; pctx->type_u32 = &pctx->type__u32; pctx->type_u64 = &pctx->type__u64; pctx->untyped_string = &pctx->type__untyped_string; pctx->untyped_bool = &pctx->type__untyped_bool; pctx->untyped_int = &pctx->type__untyped_int; pctx->untyped_float = &pctx->type__untyped_float; pctx->type_pointer_to_char = type_pointer(pctx->type_char); pctx->type_pointer_to_void = type_pointer(pctx->type_void); // Init paths ctx->exe_folder = os_get_exe_dir(ctx->perm); ctx->working_folder = os_get_working_dir(ctx->perm); String main_module = string_fmt(ctx->perm, "%Q/modules", ctx->exe_folder); add(ctx->perm, &ctx->module_folders, main_module); ctx->time.init_context = os_time() - ctx->time.init_context; } //apifn CORE_Static void core_bootstrap_compiler(Allocator *allocator) { Core_Ctx *ctx = allocate_struct(allocator, Core_Ctx); core_init_compiler(ctx, allocator); } CORE_Static void insert_builtin_type_into_scope(Ast_Scope *p, String name, Ast_Type *type) { Intern_String string = pctx->intern(name); Ast_Decl * decl = ast_type(0, string, type); type->type_id = pctx->type_ids++; decl->parent_scope = p; decl->state = DECL_RESOLVED; insert_into_scope(p, decl); add(pctx->perm, &pctx->all_types, type); } CORE_Static void parse_all_modules() { pctx->time.parsing = os_time(); For_Named(pctx->modules, module) { if (module->state != MODULE_REGISTERED) continue; For(module->all_loaded_files) { parse_file(it); } if (module != pctx->language_base_module) { add(pctx->perm, &module->implicit_imports, (Ast_Scope *)pctx->language_base_module); } module->state = MODULE_PARSED; } pctx->time.parsing = os_time() - pctx->time.parsing; } CORE_Static Ast_Module * add_module(Token *pos, Intern_String filename, B32 command_line_module, bool string_only_module) { Arena *scratch = pctx->scratch; Scratch_Scope _scope(scratch); String absolute_file_path = {}; String absolute_base_folder = {}; if (string_only_module) { absolute_file_path = filename.s; absolute_base_folder = filename.s; } else { // // Find in working directory // if (command_line_module) { if (os_does_file_exist(filename.s)) { String path = os_get_absolute_path(scratch, filename.s); string_path_normalize(path); absolute_file_path = string_copy(scratch, path); absolute_base_folder = string_chop_last_slash(path); } } // // Find in module folder // else { For(pctx->module_folders) { String path = string_fmt(scratch, "%Q/%Q", it, filename); if (os_does_file_exist(path)) { absolute_file_path = path; absolute_base_folder = string_chop_last_slash(path); break; } } } if (absolute_file_path.len == 0) { compiler_error(pos, "Couldn't find the module with name %Q", filename); } } For(pctx->modules) { if (string_compare(it->absolute_file_path, absolute_file_path)) { log_trace("Returning registered module: %Q\n", absolute_file_path); return it; } } log_trace("Adding module: %Q\n", filename); Ast_Module *result = ast_new(Ast_Module, AST_MODULE, pos, 0); result->absolute_file_path = string_copy(pctx->perm, absolute_file_path); result->absolute_base_folder = string_copy(pctx->perm, absolute_base_folder); result->debug_name = string_skip_to_last_slash(result->absolute_file_path); result->module = result; // @warning: self referential result->file = result; // @warning: self referential result->parent_scope = 0; result->scope_id = pctx->scope_ids++; register_ast_file(pos, result->absolute_file_path, result, GLOBAL_IMPLICIT_LOAD); add(pctx->perm, &pctx->modules, result); return result; } CORE_Static Ast_File * custom_parse_string(String string) { Ast_Module *module = pctx->custom_module; Ast_File *file = get(&module->all_loaded_files, 0); file->filecontent = string; parse_file(file); return file; } CORE_Static void resolve_everything_in_module(Ast_Module *module) { if (module->state == MODULE_RESOLVED) return; pctx->time.typechecking = os_time(); For_Named(module->all_loaded_files, file) { For_Named(file->decls, decl) { resolve_name(file, decl->pos, decl->name); if (decl->kind == AST_STRUCT) { type_complete(decl->type_val); } } } module->state = MODULE_RESOLVED; pctx->time.typechecking = os_time() - pctx->time.typechecking; } CORE_Static void init_language_core() { pctx->custom_module = add_module(0, pctx->intern("Custom.core"_s), false, true); pctx->custom_module->state = MODULE_RESOLVED; Ast_Module *module = add_module(0, pctx->intern("Language.core"_s), false, true); get(&module->all_loaded_files, 0)->filecontent = R"( Any :: struct data: *void type: Type String :: struct data: *U8 len: S64 Type_Info_Kind :: enum S64 // FIRST_NUMERIC S32 S16 S8 INT CHAR U64 U32 U16 U8 F32 F64 POINTER BOOL // LAST_NUMERIC STRING VOID ARRAY LAMBDA STRUCT UNION ENUM TYPE SLICE TUPLE Type_Info_Struct_Member :: struct name: String type: Type offset: S64 Type_Info :: struct kind: Type_Info_Kind size: S64 align: S64 is_unsigned: Bool type: Type base_type: Type array_size: S64 struct_member_count: S64 struct_members: *Type_Info_Struct_Member lambda_argument_count: S64 lambda_arguments: *Type_Info lambda_return: Type type_infos_len: S64 #foreign type_infos : *Type_Info #foreign GetTypeInfo :: (type: Type): *Type_Info id := type->S64 if id >= type_infos_len return 0 return type_infos + id )"_s; { insert_builtin_type_into_scope(module, "S64"_s, pctx->type_s64); insert_builtin_type_into_scope(module, "S32"_s, pctx->type_s32); insert_builtin_type_into_scope(module, "S16"_s, pctx->type_s16); insert_builtin_type_into_scope(module, "S8"_s, pctx->type_s8); insert_builtin_type_into_scope(module, "int"_s, pctx->type_int); insert_builtin_type_into_scope(module, "char"_s, pctx->type_char); insert_builtin_type_into_scope(module, "U64"_s, pctx->type_u64); insert_builtin_type_into_scope(module, "U32"_s, pctx->type_u32); insert_builtin_type_into_scope(module, "U16"_s, pctx->type_u16); insert_builtin_type_into_scope(module, "U8"_s, pctx->type_u8); insert_builtin_type_into_scope(module, "F64"_s, pctx->type_f64); insert_builtin_type_into_scope(module, "F32"_s, pctx->type_f32); insert_builtin_type_into_scope(module, "void"_s, pctx->type_void); insert_builtin_type_into_scope(module, "Bool"_s, pctx->type_bool); // insert_builtin_type_into_scope(module, "String"_s, pctx->type_string); insert_builtin_type_into_scope(module, "Type"_s, pctx->type_type); } { Ast_Scope *scope = ast_decl_scope(&pctx->null_token, pctx->perm, get(&module->all_loaded_files, 0)); Ast_Decl * decl = ast_namespace(&pctx->null_token, scope, pctx->intern("Const"_s)); decl->state = DECL_RESOLVED; Value v1 = {}; v1.type = pctx->untyped_string; v1.intern_val = pctx->intern(OS_NAME); Ast_Decl *const_os1 = ast_const(&pctx->null_token, pctx->intern("OSName"_s), v1); const_os1->state = DECL_RESOLVED; insert_into_scope(scope, const_os1); Value v2 = {}; v1.type = pctx->untyped_string; v1.intern_val = pctx->intern(OS_NAME_LOWER); Ast_Decl *const_os2 = ast_const(&pctx->null_token, pctx->intern("OSNameLower"_s), v2); const_os2->state = DECL_RESOLVED; insert_into_scope(scope, const_os2); insert_into_scope(module, decl); } pctx->language_base_module = module; parse_all_modules(); resolve_everything_in_module(module); // @note: language stuff needs to be declared before type_info data // so we mark where it ends pctx->base_language_ordered_decl_len = length(&pctx->ordered_decls); Ast_Decl *any_decl = search_for_single_decl(module, pctx->intern("Any"_s)); assert(any_decl->type == pctx->type_type); pctx->type_any = any_decl->type_val; Ast_Decl *string_decl = search_for_single_decl(module, pctx->intern("String"_s)); assert(string_decl->type == pctx->type_type); pctx->type_string = string_decl->type_val; } typedef void Ast_Visit_Callback(Ast *ast); void ast_visit(uint32_t visit_id, Ast_Visit_Callback *callback, Ast *ast) { if (!ast) return; callback(ast); switch(ast->kind) { CASE(MODULE, Module) { if (node->visit_id == visit_id) return; node->visit_id = visit_id; For(node->decls) ast_visit(visit_id, callback, it); For(node->implicit_imports) ast_visit(visit_id, callback, it); For(node->stmts) ast_visit(visit_id, callback, it); For(node->all_loaded_files) ast_visit(visit_id, callback, it); BREAK(); } CASE(FILE, File) { if (node->visit_id == visit_id) return; node->visit_id = visit_id; For(node->decls) ast_visit(visit_id, callback, it); For(node->implicit_imports) ast_visit(visit_id, callback, it); For(node->stmts) ast_visit(visit_id, callback, it); BREAK(); } CASE(SCOPE, Scope) { if (node->visit_id == visit_id) return; node->visit_id = visit_id; For(node->decls) ast_visit(visit_id, callback, it); For(node->implicit_imports) ast_visit(visit_id, callback, it); For(node->stmts) ast_visit(visit_id, callback, it); BREAK(); } CASE(LAMBDA, Decl){ Ast_Lambda *lambda = node->lambda; For(lambda->ret) ast_visit(visit_id, callback, it); For(lambda->args) ast_visit(visit_id, callback, it); ast_visit(visit_id, callback, node->typespec); ast_visit(visit_id, callback, node->scope); ast_visit(visit_id, callback, lambda->scope); BREAK(); } CASE(CONST, Decl){ ast_visit(visit_id, callback, node->expr); ast_visit(visit_id, callback, node->typespec); ast_visit(visit_id, callback, node->scope); BREAK(); } CASE(VAR, Decl){ ast_visit(visit_id, callback, node->expr); ast_visit(visit_id, callback, node->typespec); ast_visit(visit_id, callback, node->scope); BREAK(); } case AST_NAMESPACE: CASE(ENUM, Decl){ ast_visit(visit_id, callback, node->typespec); For(node->scope->decls) ast_visit(visit_id, callback, it); BREAK(); } CASE(INDEX, Index) { ast_visit(visit_id, callback, node->expr); ast_visit(visit_id, callback, node->index); BREAK(); } CASE(STRUCT, Decl) { // @todo BREAK(); } CASE(ARRAY, Array) { ast_visit(visit_id, callback, node->base); ast_visit(visit_id, callback, node->expr); BREAK(); } CASE(BINARY, Binary) { ast_visit(visit_id, callback, node->left); ast_visit(visit_id, callback, node->right); BREAK(); } CASE(VAR_UNPACK, Var_Unpack) { For(node->vars) ast_visit(visit_id, callback, it); ast_visit(visit_id, callback, node->expr); BREAK(); } CASE(UNARY, Unary) { ast_visit(visit_id, callback, node->expr); BREAK(); } case AST_COMPOUND: CASE(CALL, Call){ ast_visit(visit_id, callback, node->name); // @union For(node->exprs) ast_visit(visit_id, callback, it); BREAK(); } CASE(CALL_ITEM, Call_Item){ ast_visit(visit_id, callback, node->item); ast_visit(visit_id, callback, node->name); // @union BREAK(); } case AST_CONSTANT_ASSERT: CASE(RUNTIME_ASSERT, Builtin){ ast_visit(visit_id, callback, node->expr); BREAK(); } CASE(RETURN, Return){ For(node->expr) ast_visit(visit_id, callback, it); BREAK(); } CASE(FOR, For) { ast_visit(visit_id, callback, node->init); ast_visit(visit_id, callback, node->cond); ast_visit(visit_id, callback, node->iter); ast_visit(visit_id, callback, node->scope); ast_visit(visit_id, callback, node->array_traversal_var); BREAK(); } CASE(IF, If) { For(node->ifs) ast_visit(visit_id, callback, it); BREAK(); } CASE(IF_NODE, If_Node) { ast_visit(visit_id, callback, node->expr); ast_visit(visit_id, callback, node->scope); ast_visit(visit_id, callback, node->init); BREAK(); } CASE(SWITCH_CASE, Switch_Case) { For(node->labels) ast_visit(visit_id, callback, it); ast_visit(visit_id, callback, node->scope); BREAK(); } CASE(SWITCH, Switch) { ast_visit(visit_id, callback, node->value); For(node->cases) ast_visit(visit_id, callback, it); ast_visit(visit_id, callback, node->default_scope); BREAK(); } case AST_VALUE: case AST_IDENT: case AST_PASS: case AST_BREAK: break; invalid_default_case; } } CORE_Static String compile_file_to_string(Allocator *allocator, String filename) { F64 total_time = os_time(); core_bootstrap_compiler(allocator); pctx->time.total = total_time; pctx->time.start = total_time; init_language_core(); Ast_Module *module = add_module(0, pctx->intern(filename), true); parse_all_modules(); assert(module); resolve_everything_in_module(module); String result = compile_to_c_code(); pctx->time.total = os_time() - pctx->time.total; return result; }