321 lines
11 KiB
C++
321 lines
11 KiB
C++
|
|
function void
|
|
lex_init(Allocator *token_string_arena, Allocator *map_allocator, Lexer *l){
|
|
l->arena = token_string_arena;
|
|
l->tokens = array_make<Token>(token_string_arena, 1024*2);
|
|
l->interns= intern_table_make(token_string_arena, map_allocator, 1024);
|
|
|
|
keyword_struct = l->intern("struct"_s);
|
|
keyword_union = l->intern("union"_s);
|
|
keyword_true = l->intern("true"_s);
|
|
keyword_default = l->intern("default"_s);
|
|
keyword_break = l->intern("break"_s);
|
|
keyword_false = l->intern("false"_s);
|
|
keyword_return = l->intern("return"_s);
|
|
keyword_switch = l->intern("switch"_s);
|
|
keyword_assert = l->intern("Assert"_s);
|
|
keyword_if = l->intern("if"_s);
|
|
keyword_elif = l->intern("elif"_s);
|
|
keyword_pass = l->intern("pass"_s);
|
|
keyword_else = l->intern("else"_s);
|
|
keyword_for = l->intern("for"_s);
|
|
keyword_enum = intern_string(&l->interns, "enum"_s);
|
|
l->interns.first_keyword = keyword_struct.str;
|
|
l->interns.last_keyword = keyword_enum.str;
|
|
|
|
intern_sizeof = l->intern("SizeOf"_s);
|
|
intern_lengthof = l->intern("Length"_s);
|
|
intern_alignof = l->intern("AlignOf"_s);
|
|
intern_foreign = l->intern("foreign"_s);
|
|
intern_strict = l->intern("strict"_s);
|
|
intern_void = l->intern("void"_s);
|
|
intern_flag = l->intern("flag"_s);
|
|
intern_it = l->intern("it"_s);
|
|
|
|
op_add = l->intern("+"_s);
|
|
op_mul = l->intern("*"_s);
|
|
op_div = l->intern("/"_s);
|
|
op_sub = l->intern("-"_s);
|
|
op_and = l->intern("&&"_s);
|
|
op_bitand = l->intern("&"_s);
|
|
op_or = l->intern("||"_s);
|
|
op_bitor = l->intern("|"_s);
|
|
op_xor = l->intern("^"_s);
|
|
op_equals = l->intern("=="_s);
|
|
op_not_equals = l->intern("!="_s);
|
|
op_lesser_then_or_equal = l->intern("<="_s);
|
|
op_greater_then_or_equal = l->intern(">="_s);
|
|
op_lesser_then = l->intern("<"_s);
|
|
op_greater_then = l->intern(">"_s);
|
|
op_left_shift = l->intern("<<"_s);
|
|
op_right_shift = l->intern(">>"_s);
|
|
op_not = l->intern("!"_s);
|
|
op_neg = l->intern("~"_s);
|
|
op_decrement = l->intern("--"_s);
|
|
op_increment = l->intern("++"_s);
|
|
l->first_op = op_add;
|
|
l->last_op = op_increment;
|
|
}
|
|
|
|
function void
|
|
parse_init(Parse_Ctx *ctx, Allocator *perm_allocator, Allocator *heap_allocator){
|
|
pctx = ctx;
|
|
ctx->perm = perm_allocator;
|
|
ctx->heap = heap_allocator;
|
|
ctx->gen = {ctx->heap};
|
|
ctx->ordered_decls = {ctx->heap};
|
|
ctx->type_map = {ctx->heap};
|
|
ctx->modules = {ctx->heap};
|
|
ctx->all_types = {ctx->heap};
|
|
ctx->helper_builder= {ctx->heap};
|
|
ctx->files = {ctx->heap};
|
|
ctx->scope_ids = 1;
|
|
bigint_allocator = ctx->perm;
|
|
arena_init(&ctx->stage_arena, "Compiler stage arena"_s);
|
|
|
|
lex_init(ctx->perm, ctx->heap, ctx);
|
|
init_type();
|
|
|
|
// Init paths
|
|
ctx->exe_folder = os_get_exe_dir(ctx->perm);
|
|
ctx->working_folder = os_get_working_dir(ctx->perm);
|
|
|
|
ctx->module_folders = {ctx->heap};
|
|
String main_module = string_fmt(ctx->perm, "%Q/modules", ctx->exe_folder);
|
|
ctx->module_folders.add(main_module);
|
|
}
|
|
|
|
|
|
function void
|
|
begin_compilation(){
|
|
init_ctx_time_begin = os_time();
|
|
OS_Heap *heap = exp_alloc_type(&pernament_arena, OS_Heap, AF_ZeroMemory);
|
|
*heap = win32_os_heap_create(false, mib(4), 0, "Compiler heap"_s);
|
|
Parse_Ctx *ctx = exp_alloc_type(&pernament_arena, Parse_Ctx, AF_ZeroMemory);
|
|
parse_init(ctx, &pernament_arena, heap);
|
|
init_ctx_time_end = os_time();
|
|
}
|
|
|
|
function void
|
|
destroy_compiler(){
|
|
exp_destroy(pctx->heap);
|
|
exp_free_all(pctx->perm);
|
|
exp_destroy(&pctx->stage_arena);
|
|
}
|
|
|
|
function void
|
|
insert_builtin_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);
|
|
pctx->all_types.add(type);
|
|
}
|
|
|
|
function void
|
|
insert_builtin_types_into_scope(Ast_Scope *p){
|
|
insert_builtin_into_scope(p, "S64"_s, type_s64);
|
|
insert_builtin_into_scope(p, "S32"_s, type_s32);
|
|
insert_builtin_into_scope(p, "S16"_s, type_s16);
|
|
insert_builtin_into_scope(p, "S8"_s, type_s8);
|
|
insert_builtin_into_scope(p, "int"_s, type_int);
|
|
insert_builtin_into_scope(p, "char"_s, type_char);
|
|
insert_builtin_into_scope(p, "U64"_s, type_u64);
|
|
insert_builtin_into_scope(p, "U32"_s, type_u32);
|
|
insert_builtin_into_scope(p, "U16"_s, type_u16);
|
|
insert_builtin_into_scope(p, "U8"_s, type_u8);
|
|
insert_builtin_into_scope(p, "F64"_s, type_f64);
|
|
insert_builtin_into_scope(p, "F32"_s, type_f32);
|
|
|
|
insert_builtin_into_scope(p, "void"_s , type_void);
|
|
insert_builtin_into_scope(p, "Bool"_s , type_bool);
|
|
insert_builtin_into_scope(p, "String"_s, type_string);
|
|
insert_builtin_into_scope(p, "Type"_s, type_type);
|
|
}
|
|
|
|
global F64 parsing_time_begin;
|
|
global F64 parsing_time_end;
|
|
function void
|
|
parse_all_modules(){
|
|
parsing_time_begin = os_time();
|
|
for(S64 i = 0; i < pctx->modules.len; i++){
|
|
Ast_Module *module = pctx->modules[i];
|
|
if(module->state != MODULE_REGISTERED) continue;
|
|
|
|
for(S64 j = 0; j < module->all_loaded_files.len; j++){
|
|
Ast_File *file = module->all_loaded_files.data[j];
|
|
parse_file(file);
|
|
}
|
|
|
|
if(module != pctx->language_base_module)
|
|
module->implicit_imports.add(pctx->language_base_module);
|
|
|
|
module->state = MODULE_PARSED;
|
|
}
|
|
parsing_time_end = os_time();
|
|
}
|
|
|
|
function Ast_Module *
|
|
add_module(Token *pos, Intern_String filename, B32 command_line_module){
|
|
Scratch 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{
|
|
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_info("Returning registered module: %Q\n", absolute_file_path);
|
|
return it;
|
|
}
|
|
}
|
|
|
|
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);
|
|
// log_info("Adding module: %Q\n", filename);
|
|
result->module = result; // @warning: self referential
|
|
result->file = result; // @warning: self referential
|
|
result->all_loaded_files = {pctx->heap};
|
|
result->implicit_imports = {pctx->heap};
|
|
result->decls = {pctx->heap};
|
|
result->parent_scope = 0;
|
|
result->scope_id = pctx->scope_ids++;
|
|
|
|
register_ast_file(pos, result->absolute_file_path, result, GLOBAL_IMPLICIT_LOAD);
|
|
pctx->modules.add(result);
|
|
return result;
|
|
}
|
|
|
|
function void
|
|
resolve_everything_in_module(Ast_Module *module){
|
|
if(module->state == MODULE_RESOLVED) return;
|
|
resolving_time_begin = os_time();
|
|
for(S64 i = 0; i < module->all_loaded_files.len; i++){
|
|
Ast_File *file = module->all_loaded_files[i];
|
|
For(file->decls){
|
|
resolve_name(file, it->pos, it->name);
|
|
if(it->kind == AST_STRUCT){
|
|
type_complete(it->type_val);
|
|
}
|
|
}
|
|
}
|
|
module->state = MODULE_RESOLVED;
|
|
resolving_time_end = os_time();
|
|
}
|
|
|
|
function String
|
|
compile_file_to_string(String filename){
|
|
total_time = os_time();
|
|
begin_compilation();
|
|
{
|
|
Ast_Module *module = add_module(0, pctx->intern("language.kl"_s));
|
|
insert_builtin_types_into_scope(module);
|
|
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 = pctx->ordered_decls.len;
|
|
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);
|
|
|
|
|
|
arena_clear(&pctx->stage_arena);
|
|
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;
|
|
|
|
function void
|
|
compile_file(String filename, U32 compile_flags = COMPILE_NULL){
|
|
if(is_flag_set(compile_flags, COMPILE_AND_RUN)){
|
|
log_info("\n%Q - ", filename);
|
|
}
|
|
|
|
String result = compile_file_to_string(filename);
|
|
assert(os_write_file("program.c"_s, result));
|
|
|
|
Scratch scratch;
|
|
F64 begin = os_time();
|
|
String compiler_call = string_fmt(scratch, "clang.exe program.c -Wall -Wno-parentheses-equality -g -o a.exe -lgdi32 -luser32 -lwinmm");
|
|
system((const char *)compiler_call.str);
|
|
F64 end = os_time();
|
|
|
|
if(is_flag_set(compile_flags, COMPILE_PRINT_STATS)){
|
|
log_info("\ntotal = %f", os_time() - total_time);
|
|
log_info("\nclang = %f", end - begin);
|
|
log_info("\nparsing = %f", parsing_time_end - parsing_time_begin);
|
|
log_info("\nresolving = %f", resolving_time_end - resolving_time_begin);
|
|
log_info("\ngeneratin = %f", generating_time_end - generating_time_begin);
|
|
}
|
|
|
|
if(is_flag_set(compile_flags, COMPILE_PRINT_ALLOCATOR_STATS_BEFORE_DESTROY)){
|
|
Arena *p = (Arena *)pctx->perm;
|
|
Arena *stage = (Arena *)&pctx->stage_arena;
|
|
log_info("\nPernament arena len: %llu commit: %llu", p->len, p->memory.commit);
|
|
log_info("\nStage arena len: %llu commit: %llu", stage->len, stage->memory.commit);
|
|
log_info("\nScratch1 len: %llu commit: %llu", thread_ctx.scratch[0].len, thread_ctx.scratch[0].memory.commit);
|
|
log_info("\nScratch2 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;
|
|
String sys = string_fmt(scratch, "a.exe %Q", testing);
|
|
int result = system((char *)sys.str);
|
|
assert(result != -1);
|
|
if(result == 0){
|
|
log_info(PRINTF_GREEN "OK!" PRINTF_RESET);
|
|
}
|
|
if(result != 0){
|
|
log_info(PRINTF_RED "ERROR!" PRINTF_RESET);
|
|
}
|
|
}
|
|
|
|
destroy_compiler();
|
|
} |