CORE_Static void core_init_compiler(Core_Ctx *ctx, Allocator *allocator) { ctx->init_ctx_time_begin = os_time(); pctx = ctx; ctx->emit_type_info = true; ctx->emit_line_directives = true; ctx->color_codes_enabled = true; ctx->same_scope_token = { SAME_SCOPE }; ctx->perm_push_only = make_push_arena(allocator); ctx->perm = &ctx->perm_push_only; ctx->scratch = allocate_scratch_arena(ctx->perm, mib(1)); ctx->stage_arena = allocate_scratch_arena(ctx->perm, mib(1)); 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->tokens = array_make(ctx->heap, 4096 * 4); 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_type(); // 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->init_ctx_time_end = os_time(); } 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->parsing_time_begin = os_time(); Iter_Named(&pctx->modules, mod_it) { Ast_Module *module = mod_it.item[0]; if (module->state != MODULE_REGISTERED) continue; Iter(&module->all_loaded_files) { parse_file(*it.item); } if (module != pctx->language_base_module) { add(pctx->perm, &module->implicit_imports, (Ast_Scope *)pctx->language_base_module); // module->implicit_imports.add(pctx->language_base_module); } module->state = MODULE_PARSED; } pctx->parsing_time_end = os_time(); } CORE_Static Ast_Module * add_module(Token *pos, Intern_String filename, B32 command_line_module) { Scratch_Arena *scratch = pctx->scratch; Scratch_Scope _scope(scratch); String absolute_file_path = {}; String absolute_base_folder = {}; // // 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 { Iter(&pctx->module_folders) { String path = string_fmt(scratch, "%Q/%Q", it.item[0], 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); } Iter(&pctx->modules) { if (string_compare(it.item[0]->absolute_file_path, absolute_file_path)) { log_trace("Returning registered module: %Q\n", absolute_file_path); return it.item[0]; } } 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 void resolve_everything_in_module(Ast_Module *module) { if (module->state == MODULE_RESOLVED) return; pctx->resolving_time_begin = os_time(); Iter_Named(&module->all_loaded_files, file) { Iter(&file.item[0]->decls) { Ast_Decl *decl = it.item[0]; resolve_name(file.item[0], decl->pos, decl->name); if (decl->kind == AST_STRUCT) { type_complete(decl->type_val); } } } module->state = MODULE_RESOLVED; pctx->resolving_time_end = os_time(); } CORE_Static String compile_file_to_string(Allocator *allocator, String filename) { F64 total_time = os_time(); core_bootstrap_compiler(allocator); pctx->total_time = total_time; { Ast_Module *module = add_module(0, pctx->intern("Language.core"_s)); { insert_builtin_type_into_scope(module, "S64"_s, type_s64); insert_builtin_type_into_scope(module, "S32"_s, type_s32); insert_builtin_type_into_scope(module, "S16"_s, type_s16); insert_builtin_type_into_scope(module, "S8"_s, type_s8); insert_builtin_type_into_scope(module, "int"_s, type_int); insert_builtin_type_into_scope(module, "char"_s, type_char); insert_builtin_type_into_scope(module, "U64"_s, type_u64); insert_builtin_type_into_scope(module, "U32"_s, type_u32); insert_builtin_type_into_scope(module, "U16"_s, type_u16); insert_builtin_type_into_scope(module, "U8"_s, type_u8); insert_builtin_type_into_scope(module, "F64"_s, type_f64); insert_builtin_type_into_scope(module, "F32"_s, type_f32); insert_builtin_type_into_scope(module, "void"_s, type_void); insert_builtin_type_into_scope(module, "Bool"_s, type_bool); insert_builtin_type_into_scope(module, "String"_s, type_string); insert_builtin_type_into_scope(module, "Type"_s, 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 = 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 = 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 == type_type); type_any = any_decl->type_val; } Ast_Module *module = add_module(0, pctx->intern(filename), true); parse_all_modules(); assert(module); resolve_everything_in_module(module); pctx->stage_arena->len = 0; String result = compile_to_c_code(); return result; } const U32 COMPILE_NULL = 0x0; const U32 COMPILE_PRINT_STATS = 0x1; const U32 COMPILE_PRINT_ALLOCATOR_STATS_BEFORE_DESTROY = 0x2; const U32 COMPILE_AND_RUN = 0x4; const U32 COMPILE_TESTING = 0x8; CORE_Static void compile_file(Allocator *allocator, String filename, U32 compile_flags = COMPILE_NULL) { String result = compile_file_to_string(allocator, filename); if (is_flag_set(compile_flags, COMPILE_AND_RUN)) { log_info_no_nl("%Q - ", filename); } B32 r = os_write_file("program.c"_s, result); assert(r); F64 total_compiler_time = os_time() - pctx->total_time; log_info_no_nl("%f - ", total_compiler_time); Scratch_Arena *scratch = pctx->scratch; Scratch_Scope _scope(scratch); F64 begin = os_time(); String_Builder builder = {scratch}; builder.addf("clang program.c -Wall -Wno-unused-function -Wno-parentheses-equality -g -o a" OS_EXE " "); Iter(&pctx->files_to_link) { builder.addf("-l%Q ", it.item[0]->intern_val); } String compiler_call = string_flatten(scratch, &builder); log_trace("%Q", compiler_call); system((const char *)compiler_call.str); F64 end = os_time(); if (is_flag_set(compile_flags, COMPILE_PRINT_STATS)) { log_info("total = %f", os_time() - pctx->total_time); log_info("clang = %f", end - begin); log_info("parsing = %f", pctx->parsing_time_end - pctx->parsing_time_begin); log_info("resolving = %f", pctx->resolving_time_end - pctx->resolving_time_begin); log_info("generatin = %f", pctx->generating_time_end - pctx->generating_time_begin); } if (is_flag_set(compile_flags, COMPILE_PRINT_ALLOCATOR_STATS_BEFORE_DESTROY)) { // @! allocator stats //Arena *p = (Arena *)pctx->perm; //Arena *stage = (Arena *)&pctx->stage_arena; //log_info("Pernament arena len: %llu commit: %llu", pct, p->memory.commit); //log_info("Stage arena len: %llu commit: %llu", stage->len, stage->memory.commit); //log_info("Scratch1 len: %llu commit: %llu", thread_ctx.scratch[0].len, thread_ctx.scratch[0].memory.commit); //log_info("Scratch2 len: %llu commit: %llu", thread_ctx.scratch[1].len, thread_ctx.scratch[1].memory.commit); } if (is_flag_set(compile_flags, COMPILE_AND_RUN)) { String testing = compile_flags & COMPILE_TESTING ? "testing"_s : ""_s; #if OS_WINDOWS String sys = string_fmt(scratch, "a.exe %Q", testing); #else String sys = string_fmt(scratch, "./a.out %Q", testing); #endif int result = system((char *)sys.str); assert(result != -1); if (result == 0) { log_info_no_nl(PRINTF_GREEN "OK!" PRINTF_RESET); } if (result != 0) { log_info_no_nl(PRINTF_RED "ERROR!" PRINTF_RESET); } log_info(""); } }