#define gen(...) pctx->gen.addf(__VA_ARGS__) #define genln(...) do{gen("\n"); gen_indent(); gen(__VA_ARGS__); }while(0) global S32 global_indent; global S32 is_inside_struct; function void gen_ast(Ast *ast); function bool gen_expr(Ast_Expr *ast, Ast_Type *type_of_var = 0, bool is_vargs = false); function void gen_indent(){ for(S32 i = 0; i < global_indent; i++) gen(" "); } global Intern_String last_filename; global int last_line; function void gen_line(Ast *node){ if(emit_line_directives){ last_line = node->pos->line+1; genln("#line %d", last_line); if(node->pos->file != last_filename){ last_filename = node->pos->file; gen(" \"%Q\"", last_filename); } } } function void gen_last_line(){ if(emit_line_directives){ genln("#line %d", last_line); } } function String string_scope_name(Allocator *a, Ast_Scope *scope){ String string = {}; if(scope->parent_scope) string = string_scope_name(a, scope->parent_scope); assert_msg(scope->scope_id != 0, "Scope id is equal to 0 which is invalid, scope didn't initialize id"); string = string_fmt(a, "%QS%u_", string, scope->scope_id); return string; } function void gen_scope_name(Ast_Scope *scope){ Scratch scratch; String string = string_scope_name(scratch, scope); gen("%.*s", (int)string.len, string.str); } function String unique_name(Allocator *allocator, Ast *ast){ Scratch scratch; String result = string_scope_name(scratch, ast->parent_scope); assert(result.len); result = string_fmt(allocator, "%Q%d", result, ast->pos->line); return result; } function const char * get_ctype_name_for_type(Ast_Type *type){ switch(type->kind){ case TYPE_VOID: return "void"; case TYPE_BOOL: return "bool"; case TYPE_STRING: return "String"; case TYPE_CHAR: return "char"; case TYPE_F32: return "float"; case TYPE_F64: return "double"; case TYPE_INT: return "int"; case TYPE_S8: return "int8_t"; case TYPE_S16: return "int16_t"; case TYPE_S32: return "int32_t"; case TYPE_S64: return "int64_t"; case TYPE_U8: return "uint8_t"; case TYPE_U16: return "uint16_t"; case TYPE_U32: return "uint32_t"; case TYPE_U64: return "uint64_t"; case TYPE_TUPLE: return "Tuple"; case TYPE_TYPE: return "int64_t"; invalid_default_case; } return ""; } function String string_simple_decl_prefix(Allocator *a, Ast_Type *ast){ switch(ast->kind){ case TYPE_POINTER:{ String string = string_simple_decl_prefix(a, ast->base); string = string_fmt(a, "%Q*", string); return string; }break; case TYPE_LAMBDA: return {}; break; case TYPE_ENUM: case TYPE_ARRAY: { return string_simple_decl_prefix(a, ast->base); }break; case TYPE_SLICE:{ String string = string_simple_decl_prefix(a, ast->base); string = string_fmt(a, "Slice%llu ", ast->type_id); return string; }break; case TYPE_TUPLE:{ String string = string_fmt(a, "Tuple%llu ", ast->type_id); return string; }break; case TYPE_STRUCT: { auto constant = (Ast_Decl *)ast->ast; auto name = constant->name; String string = string_fmt(a, "%Q ", name); String sc = {}; return string_fmt(a, "%Q%Q", sc, string); }break; default: { String string = string_fmt(a, "%s ", get_ctype_name_for_type(ast)); return string; } } return {}; } function String string_simple_decl_postfix(Allocator *a, Ast_Type *ast){ switch(ast->kind){ case TYPE_POINTER: return string_simple_decl_postfix(a, ast->base); break; case TYPE_ARRAY:{ String result = string_simple_decl_postfix(a, ast->arr.base); String string = string_fmt(a, "[%d]%Q", ast->arr.size, result); return string; }break; case TYPE_LAMBDA:break; case TYPE_SLICE: case TYPE_ENUM: case TYPE_STRUCT:break; default:break; // default: return string_from_cstring((char *)name(ast)); } return {}; } function String string_simple_decl(Allocator *a, Ast_Type *ast, Intern_String name = {}){ if(ast->kind == TYPE_LAMBDA) { String prefix = string_simple_decl_prefix(a, ast->func.ret); String string = string_fmt(a, "%Q(*%Q)(", prefix, name); For(ast->func.args){ String prefix_arg = string_simple_decl_prefix(a, it); string = string_fmt(a, "%Q%Q", string, prefix_arg); if(&it != ast->func.args.end() - 1) string = string_fmt(a, "%Q, ", string); } string = string_fmt(a, "%Q)", string); return string; } else{ String string = string_simple_decl_prefix(a, ast); if(name.len) { string = string_fmt(a, "%Q%Q", string, name); } String postfix = string_simple_decl_postfix(a, ast); string = string_fmt(a, "%Q%Q", string, postfix); return string; } } function void gen_simple_decl(Ast_Type *ast, Intern_String name = {}){ Scratch scratch; String string = string_simple_decl(scratch, ast, name); gen("%.*s", (int)string.len, string.str); } function String gen_string_simple_decl(Allocator *a, Ast_Type *ast, String name){ Scratch scratch; String string = string_simple_decl(scratch, ast, pctx->intern(name)); String result = string_copy(a, string); return result; } function String get_type_postfix(Ast_Type *type){ switch(type->kind) { case TYPE_F32: return "f"_s; break; case TYPE_U64: return "ULL"_s; break; case TYPE_S64: return "LL"_s; break; case TYPE_F64:case TYPE_S8:case TYPE_S16: case TYPE_S32:case TYPE_U8:case TYPE_U16: case TYPE_INT:case TYPE_U32:case TYPE_CHAR: return ""_s; break; invalid_default_case; } assert(!"Unhandled case or error"); return ""_s; } function B32 gen_value(Token *pos, Value a){ if(is_untyped(a.type)) compiler_error(pos, "Internal compiler error: Untyped got propagated to the codegen stage"); B32 result = true; Ast_Type *type = a.type; if(is_enum(a.type)){ type = a.type->base; } switch(type->kind){ CASE_INT: { Scratch scratch; String postfix = get_type_postfix(type); const char *string = bigint_to_error_string(scratch, &a.big_int_val, 10); gen("%s%Q", string, postfix); }break; case TYPE_POINTER:{ if(a.type == type_pointer_to_char){ gen("\"%Q\"", a.intern_val); } else{ U64 pointer_value = bigint_as_unsigned(&a.big_int_val); gen("0x%llx", pointer_value); } }break; case TYPE_STRING:{ int length = 0; gen("(String){(uint8_t *)\""); for(int i = 0; i < a.intern_val.len; i++){ if(a.intern_val.str[i] == '\n'){length += 2; gen("\\n");} else if(a.intern_val.str[i] == '\r'){length += 2; gen("\\r");} else{length += 1; gen("%c", a.intern_val.str[i]);} } gen("\", %d}", length); }break; CASE_BOOL: { a.bool_val ? gen("true"):gen("false"); }break; CASE_FLOAT: { String postfix = get_type_postfix(type); gen("%f%Q", a.f64_val, postfix); }break; case TYPE_TYPE: { gen("%d", a.type_val->type_id); }break; default: result = false; } return result; } function void gen_stmt_scope(Ast_Scope *scope, B32 switch_case_gen_break = 0){ gen("{"); global_indent++; For(scope->stmts) { gen_line(it); genln(""); gen_ast(it); } if(switch_case_gen_break == 1) genln("break;"); global_indent--; gen_last_line(); genln("}"); } enum { ALWAYS_EMIT_VALUE = 0, DONT_EMIT_VALUE = 1, }; function void gen_var(Ast_Decl *decl, B32 emit_value, B32 scope_names){ if(is_flag_set(decl->flags, AST_FOREIGN)) gen("extern "); gen_simple_decl(decl->type, decl->name); if(is_flag_set(decl->flags, AST_FOREIGN)) return; if(emit_value == DONT_EMIT_VALUE){ return; } if(decl->expr){ gen(" = "); gen_expr(decl->expr, decl->type); } else { // Default zero if(is_numeric(decl->type)){ gen(" = 0"); } else { gen(" = {}"); } } } function void gen_lambda(Intern_String name, Ast_Lambda *lambda, B32 generate_block = true){ gen_simple_decl(lambda->resolved_type->func.ret, name); gen("("); For(lambda->args){ if(is_flag_set(it->flags, AST_ANY_VARGS)){ gen("..."); } else{ gen_var(it, DONT_EMIT_VALUE, true); if(&it != (lambda->args.end() - 1)) gen(", "); } } gen(")"); if(generate_block && lambda->scope){ gen_stmt_scope(lambda->scope); } else gen(";"); } function bool gen_expr(Ast_Expr *ast, Ast_Type *type_of_var, bool is_vargs){ switch(ast->kind){ CASE(IDENT, Atom){ if(node->resolved_decl->kind == AST_MODULE_NAMESPACE || node->resolved_decl->kind == AST_FILE_NAMESPACE) return false; if(type_of_var && is_slice(type_of_var) && is_array(node->resolved_decl->type)){ assert(!is_vargs); gen("{%d, ", (int)node->resolved_decl->type->arr.size); gen("%Q}", node->intern_val); } else if(type_of_var && !is_any(ast->resolved_type) && is_any(type_of_var)){ if(is_vargs){ gen_expr(ast, 0, is_vargs); } else { gen("(Any){&"); gen_expr(ast); gen(", %d}", ast->resolved_type->type_id); } } else if(node->resolved_decl->kind == AST_LAMBDA){ gen("%Q", node->resolved_decl->unique_name); } else { gen("%Q", node->intern_val); } BREAK(); } CASE(VALUE, Atom){ if(is_any(type_of_var)){ if(is_vargs){ gen("(Any){&"); gen("("); gen_simple_decl(node->type); gen("){"); gen_value(node->pos, node->value); gen("}"); gen(", %d}", node->type->type_id); } else{ gen_value(node->pos, node->value); } return true; } B32 written = gen_value(node->pos, node->value); if(!written) { gen("%Q", node->value.intern_val); } BREAK(); } CASE(ARRAY, Array){ gen("%d", node->resolved_type->type_id); BREAK(); } CASE(INDEX, Index){ gen("("); gen_expr(node->expr, 0, is_vargs); if(node->index_original_type == type_string){ gen(".str"); } else if(is_slice(node->index_original_type)){ gen(".data"); } gen("["); gen_expr(node->index); gen("]"); gen(")"); BREAK(); } CASE(LAMBDA_EXPR, Lambda){ gen_lambda({}, node); BREAK(); } CASE(BINARY, Binary){ if(node->op == TK_Dot){ if(gen_expr(node->left, 0, 0)){ if(node->dot_access_step_resolution && node->dot_access_step_resolution->kind == TYPE_POINTER) gen("->"); else gen("."); } gen_expr(node->right, 0, 0); return true; } else if(node->op == TK_Arrow){ gen("("); gen("("); gen_simple_decl(node->resolved_type); gen(")"); gen_expr(node->left); gen(")"); return true; } else if(node->resolved_operator_overload){ gen("%Q(", node->resolved_operator_overload->unique_name); gen_expr(node->left); gen(", "); gen_expr(node->right); gen(")"); } else { if(!token_is_assign(node->op)) gen("("); gen_expr(node->left); gen("%s", name(node->op)); gen_expr(node->right); if(!token_is_assign(node->op)) gen(")"); } BREAK(); } CASE(UNARY, Unary){ if(node->resolved_operator_overload){ gen("%Q(", node->resolved_operator_overload->unique_name); gen_expr(node->expr); gen(")"); } else { gen("("); if(node->op != TK_PostIncrement && node->op != TK_PostDecrement) gen("%s", name(node->op)); gen_expr(node->expr); if(node->op == TK_PostIncrement || node->op == TK_PostDecrement) gen("%s", name(node->op)); gen(")"); } BREAK(); } CASE(VAR, Decl){ gen_ast(node); BREAK(); } CASE(LENGTH_OF, Call){ Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); gen_expr(expr); if(is_pointer(expr->resolved_type)) gen("->len"); else gen(".len"); BREAK(); } CASE(CALL, Call){ gen("%Q(", node->resolved_decl->unique_name); For(node->exprs){ gen_expr(it->item, it->resolved_type, is_flag_set(it->flags, AST_ANY_VARGS)); if(!node->exprs.is_last(&it)) gen(", "); } gen(")"); BREAK(); } CASE(COMPOUND, Call){ if(is_vargs){ For(node->exprs){ gen_expr(it->item, it->resolved_type, true); if(!node->exprs.is_last(&it)) gen(", "); } return true; } gen("("); gen_simple_decl(node->resolved_type); gen(")"); gen("{"); if(is_slice(node->resolved_type)) { gen(".len = %d, ", node->exprs.len); gen(".data = ("); gen_simple_decl(node->resolved_type->base); gen("[]"); gen(")"); gen("{"); } For(node->exprs){ if(is_struct(node->resolved_type)) gen(".%Q = ", it->resolved_name); else if(is_array(node->resolved_type)) gen("[%d] = ", (int)it->resolved_index); gen_expr(it->item, it->resolved_type); if(!node->exprs.is_last(&it)) gen(", "); } if(is_slice(node->resolved_type)) gen("}"); gen("}"); BREAK(); } invalid_default_case; } return true; } function void gen_ast(Ast *ast){ switch(ast->kind){ CASE(RUNTIME_ASSERT, Builtin){ if(node->assert_message.len == 0) gen("CORE_Assert"); else gen("CORE_AssertMessage"); gen("("); gen_expr(node->expr); if(node->assert_message.len){ gen(", \"%Q\"", node->assert_message); } gen(");"); BREAK(); } CASE(RETURN, Return){ if(is_tuple(node->resolved_type)) { Scratch scratch; Intern_String var_name = pctx->intern(unique_name(scratch, node)); gen_simple_decl(node->resolved_type, var_name); gen(";"); int i = 0; For(node->expr){ genln("CORE_MemoryCopy(&%Q.m%d, ", var_name, i); if(!is_array(it->resolved_type)) gen("&"); gen("("); gen_expr(it); gen(")"); gen(", sizeof(%Q.m%d));", var_name, i++); } genln("return %Q;", var_name); return; } assert(node->expr.len <= 1); gen("return "); For(node->expr) gen_expr(it); gen(";"); BREAK(); } CASE(VAR, Decl){ gen_var(node, is_inside_struct ? DONT_EMIT_VALUE : ALWAYS_EMIT_VALUE, true); if(!is_flag_set(ast->flags, AST_EXPR)) gen(";"); BREAK(); } CASE(IF, If){ For(node->ifs){ gen_line(node); genln(""); if(it->init) { gen_expr(it->init); gen(";"); genln(""); } if(node->ifs.is_first(&it)){ gen("if("); gen_expr(it->expr); gen(")"); gen_stmt_scope(it->scope); } else{ genln("else"); if(it->expr){ gen(" if("); gen_expr(it->expr); gen(")"); } gen_stmt_scope(it->scope); } } BREAK(); } CASE(BREAK, Break){ unused(node); gen("break;"); BREAK(); } CASE(PASS, Pass){ unused(node); gen("//pass"); BREAK(); } CASE(BINARY,Binary){ gen_expr(node); gen(";"); BREAK(); } CASE(FOR, For){ // Array iter if(node->is_array_traversal){ gen("for(int64_t _i%d = 0; _i%d < ", node->pos->line, node->pos->line); if(is_array(node->cond->resolved_type)){ gen("CORE_BufferSize("); gen_expr(node->cond); gen(")"); } else{ assert(is_slice(node->cond->resolved_type)); gen_expr(node->cond); gen(".len"); } gen("; _i%d+=1)", node->pos->line); gen("{"); global_indent++; genln(""); gen_simple_decl(node->array_traversal_var->type, node->array_traversal_var->name); gen(" = "); gen_expr(node->cond); if(node->is_also_slice_traversal) gen(".data"); gen(" + _i%d;", node->pos->line); For(node->scope->stmts) { gen_line(it); genln(""); gen_ast(it); } global_indent--; gen_last_line(); genln("}"); } // Normal for loop else{ gen("for("); if(node->init) gen_expr(node->init); gen(";"); if(node->cond) gen_expr(node->cond); gen(";"); if(node->iter) gen_expr(node->iter); gen(")"); gen_stmt_scope(node->scope); } BREAK(); } CASE(LAMBDA, Decl){ gen_line(node); genln(""); if(is_flag_set(node->expr->flags, AST_FOREIGN)){ gen("/*foreign*/"); } gen_lambda(node->unique_name, node->lambda); BREAK(); } CASE(STRUCT, Decl){ gen("struct "); gen("%Q{", node->name); global_indent++; is_inside_struct++; For(node->scope->decls){ genln(""); gen_ast(it); } is_inside_struct--; global_indent--; genln("};"); BREAK(); } CASE(TYPE, Decl){ gen("// Type %Q = ", node->name); gen_simple_decl(node->type_val); BREAK(); } CASE(ENUM, Decl){ gen("/*enum %Q{", node->name); // @todo add typespec global_indent++; For(node->scope->decls){ genln("%Q", it->name); gen(" = "); gen_value(it->pos, it->value); gen(","); } global_indent--; genln("};*/"); BREAK(); } CASE(CONST, Decl){unused(node);BREAK();} CASE(SWITCH, Switch){ gen("switch("); gen_expr(node->value); gen("){"); global_indent++; For(node->cases){ For_Named(it->labels, label){ gen_line(it); genln(""); gen("case "); gen_expr(label); gen(":"); } gen_stmt_scope(it->scope, it->fallthrough ? false : true); } if(node->default_scope){ genln("default: "); gen_stmt_scope(node->default_scope); } global_indent--; gen_last_line(); genln("}"); BREAK(); } CASE(VAR_UNPACK, Var_Unpack){ For(node->vars) gen_ast(it); Scratch scratch; Intern_String var_name = pctx->intern(unique_name(scratch, node)); gen_simple_decl(node->resolved_type, var_name); gen(" = "); gen_expr(node->expr); gen(";"); int i = 0; For(node->vars){ gen("CORE_MemoryCopy((void *)&%Q, (void *)&%Q.m%d, sizeof(%Q));", it->name, var_name, i++, it->name); } BREAK(); } case AST_CONSTANT_ASSERT: case AST_MODULE_NAMESPACE: CASE(FILE_NAMESPACE, File_Namespace){unused(node); BREAK();} default: { assert(is_flag_set(ast->flags, AST_EXPR)); gen_expr((Ast_Expr *)ast); gen(";"); } } } function String compile_to_c_code(){ generating_time_begin = os_time(); gen(R"==( #include #include #define CORE_Assert(x) do{if(!(x))__debugbreak();}while(0) #define CORE_AssertMessage(x,...) CORE_Assert(x) #define CORE_BufferSize(x) (sizeof(x)/sizeof((x)[0])) typedef struct String{ uint8_t *str; int64_t len; }String; static void CORE_MemoryCopy(void *dst, void *src, size_t size){ uint8_t *d = (uint8_t*)dst; uint8_t *s = (uint8_t*)src; for(size_t i = 0; i < size; i++){ d[i] = s[i]; } } )=="); // Generate struct forward decls Iter(&pctx->ordered_decls){ auto i = it.item[0]; if(i->kind == AST_STRUCT){ genln("typedef struct %Q %Q;", i->name, i->name); } } // Generate slice and tuple types Iter(&pctx->all_types){ Scratch scratch; Ast_Type *type = it.item[0]; if(type->kind == TYPE_SLICE){ genln("typedef struct Slice%llu{", type->type_id); global_indent++; genln("int64_t len;"); genln(""); gen_simple_decl(type_pointer(type->base), pctx->intern("data"_s)); gen(";"); global_indent--; genln("} Slice%llu;", type->type_id); } else if(type->kind == TYPE_TUPLE){ genln("typedef struct Tuple%llu{", type->type_id); global_indent++; For(type->agg.members){ genln(""); // @todo remove intern from gen Intern_String name = pctx->intern(string_fmt(scratch, "m%llu", type->agg.members.get_index(&it))); gen_simple_decl(it.type, name); gen(";"); } global_indent--; genln("} Tuple%llu;", type->type_id); } } Intern_String intern_main = pctx->intern("main"_s); Intern_String intern_win_main = pctx->intern("WinMain"_s); Ast_Decl *main = 0; Ast_Decl *win_main = 0; // Generate lambda forward decls Iter(&pctx->ordered_decls){ if(it.item[0]->kind == AST_LAMBDA){ if(it.item[0]->name == intern_main){ main = it.item[0]; it.item[0]->unique_name = it.item[0]->name; } if(it.item[0]->name == intern_win_main){ win_main = it.item[0]; it.item[0]->unique_name = it.item[0]->name; } genln(""); gen_lambda(it.item[0]->unique_name, it.item[0]->lambda, false); } } if(!main && !win_main){ compiler_error(0, "Entry point is not defined! Try main or WinMain"); } // Generate language.core for(S32 i = 0; i < pctx->base_language_ordered_decl_len; i++){ Ast_Decl *it = get(&pctx->ordered_decls, i); genln(""); gen_ast(it); } // Generate type info genln("int64_t type_infos_len = %d;", length(&pctx->all_types)); genln("Type_Info *type_infos = (Type_Info[]){"); global_indent++; Iter(&pctx->all_types){ Ast_Type *t = it.item[0]; genln("{/*%Q*/.kind = %d, .size = %d, .align = %d, .is_unsigned = %s, .type = %d, ", typestring(t), (S32)t->kind, (S32)t->size, (S32)t->align, t->is_unsigned ? "true" : "false", t->type_id); switch(t->kind){ case TYPE_POINTER: case TYPE_SLICE: { gen(".base_type = %d", t->base->type_id); } break; case TYPE_ARRAY: { gen(".base_type = %d, ", t->base->type_id); gen(".array_size = %d", t->arr.size); }break; case TYPE_LAMBDA: { gen(".lambda_return = %d, ", t->func.ret->type_id); gen(".lambda_argument_count = %d, ", t->func.args.len); gen(".lambda_arguments = (Type_Info[%d]){", t->func.args.len); For_Named(t->func.args, arg){ gen("{.type = %d}, ", arg->type_id); } gen("}"); } break; case TYPE_STRUCT:{ gen(".struct_member_count = %d, ", t->agg.members.len); gen(".struct_members = (Type_Info_Struct_Member[]){"); For_Named(t->agg.members, m){ gen("{.name = (String){(uint8_t *)\"%Q\", %d}, .type = %d, .offset = %d}, ", m.name, m.name.len, m.type->type_id, m.offset); } gen("}"); }break; default: {} // invalid_default_case; } gen("},"); } global_indent--; gen("};"); // Generate actual code Iter(&pctx->ordered_decls){ if(it.index >= pctx->base_language_ordered_decl_len){ genln(""); gen_ast(it.item[0]); } } String string_result = string_flatten(pctx->perm, &pctx->gen); generating_time_end = os_time(); return string_result; }