// @todo @cleanup // :PrinterCleanup // Instead of relying on global state, this probably should // use a String_Builder or some kind of Builder context + macros #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; CORE_Static void gen_ast(Ast *ast); CORE_Static bool gen_expr(Ast_Expr *ast); CORE_Static void gen_indent() { for (S32 i = 0; i < global_indent; i++) gen(" "); } global Intern_String last_filename; global int last_line; CORE_Static void gen_line(Ast *node) { if (pctx->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); } } } CORE_Static void gen_last_line() { if (pctx->emit_line_directives) { genln("#line %d", last_line); } } CORE_Static String string_scope_name(Ast_Scope *scope) { String string = {}; if (scope->parent_scope) string = string_scope_name(scope->parent_scope); assert_message(scope->scope_id != 0, "Scope id is equal to 0 which is invalid, scope didn't initialize id"); string = pctx->fmt("%QS%u_", string, scope->scope_id); return string; } CORE_Static void gen_scope_name(Ast_Scope *scope) { gen("%Q", string_scope_name(scope)); } CORE_Static Intern_String get_unique_name(Ast *ast) { String result = string_scope_name(ast->parent_scope); assert(result.len); return pctx->internf("%Q%d", result, ast->pos->line); } global String prefixed_string_type; CORE_Static 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 (char *)prefixed_string_type.str; 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"; case TYPE_INCOMPLETE: { // This happens in case where we have a procedure in an away module. // Meaning that it's not resolved fully on purpose (treeshaking). // And that procedure takes a pointer from a struct that is never // used elsewhere. In that case this seems fine. return "void"; } invalid_default_case; } return ""; } CORE_Static String string_simple_decl_prefix(Ast_Type *ast) { switch (ast->kind) { case TYPE_POINTER: { String string = string_simple_decl_prefix(ast->base); string = pctx->fmt("%Q*", string); return string; } break; case TYPE_LAMBDA: return {}; break; case TYPE_ENUM: case TYPE_ARRAY: { return string_simple_decl_prefix(ast->base); } break; case TYPE_SLICE: { String string = string_simple_decl_prefix(ast->base); string = pctx->fmt("Slice%llu ", ast->type_id); return string; } break; case TYPE_TUPLE: { String string = pctx->fmt("Tuple%llu ", ast->type_id); return string; } break; case TYPE_UNION: case TYPE_STRUCT: { auto constant = (Ast_Decl *)ast->ast; auto name = constant->unique_name; String string = pctx->fmt("%Q ", name); String sc = {}; return pctx->fmt("%Q%Q", sc, string); } break; default: { String string = pctx->fmt("%s ", get_ctype_name_for_type(ast)); return string; } } return {}; } CORE_Static String string_simple_decl_postfix(Ast_Type *ast) { switch (ast->kind) { case TYPE_POINTER: return string_simple_decl_postfix(ast->base); break; case TYPE_ARRAY: { String result = string_simple_decl_postfix(ast->arr.base); String string = pctx->fmt("[%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 {}; } CORE_Static String string_simple_decl(Ast_Type *ast, Intern_String name = {}) { if (ast->kind == TYPE_LAMBDA) { String prefix = string_simple_decl_prefix(ast->func.ret); String string = pctx->fmt("%Q(*%Q)(", prefix, name); For(ast->func.args) { String prefix_arg = string_simple_decl_prefix(it); string = pctx->fmt("%Q%Q", string, prefix_arg); if (&it != ast->func.args.end() - 1) string = pctx->fmt("%Q, ", string); } string = pctx->fmt("%Q)", string); return string; } else { String string = string_simple_decl_prefix(ast); if (name.len) { string = pctx->fmt("%Q%Q", string, name); } String postfix = string_simple_decl_postfix(ast); string = pctx->fmt("%Q%Q", string, postfix); return string; } } CORE_Static void gen_simple_decl(Ast_Type *ast, Intern_String name = {}) { String string = string_simple_decl(ast, name); gen("%Q", string); } CORE_Static 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; } CORE_Static 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 : { Scoped_Arena scratch(pctx->scratch); String postfix = get_type_postfix(type); const char *string = bigint_to_error_string(scratch.arena, &a.big_int_val, 10); gen("%s%Q", string, postfix); } break; case TYPE_POINTER: { if (a.type == pctx->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_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: { if (is_string(type)) { int length = 0; gen("(%QString){(uint8_t *)\"", pctx->symbol_prefix); 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); } else result = false; } } return result; } CORE_Static 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, }; CORE_Static void gen_pointer(Ast_Expr *expr) { gen("&"); if (is_flag_set(expr->flags, AST_IS_LVALUE)) { gen_expr(expr); } else { gen("("); gen_simple_decl(expr->resolved_type); gen(")"); gen("{"); gen_expr(expr); gen("}"); } } CORE_Static void gen_try_any_or_slice(Ast_Expr *expr, Ast_Type *decl_type) { // We want normal values to get boxed as Any pointers // but other then that we shouldn't box other Any values if (expr->kind != AST_COMPOUND && is_any(decl_type) && !is_any(expr->resolved_type)) { gen("(Any){"); gen_pointer(expr); gen(", %d}", expr->resolved_type->type_id); } else if (expr->kind == AST_IDENT && is_slice(decl_type)) { Ast_Atom *atom = (Ast_Atom *)expr; gen("("); gen_simple_decl(decl_type); gen(")"); gen("{%d, ", atom->resolved_type->arr.size); gen_expr(expr); gen("}"); } else gen_expr(expr); } CORE_Static 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_try_any_or_slice(decl->expr, decl->type); } else { // Default zero if (is_numeric(decl->type)) { gen(" = 0"); } else { gen(" = {}"); } } } CORE_Static 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) { 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(";"); } CORE_Static bool gen_expr(Ast_Expr *ast) { switch (ast->kind) { CASE(IDENT, Atom) { if (node->resolved_decl->kind == AST_NAMESPACE) return false; if (node->resolved_decl->kind == AST_LAMBDA) { gen("%Q", node->resolved_decl->unique_name); } else { gen("%Q", node->intern_val); } BREAK(); } CASE(VALUE, Atom) { 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); if (node->index_original_type == pctx->type_string || node->index_original_type == pctx->untyped_string) { gen(".data"); } 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)) { if (node->dot_access_step_resolution && node->dot_access_step_resolution->kind == TYPE_POINTER) gen("->"); else gen("."); } gen_expr(node->right); 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_try_any_or_slice(it->item, it->resolved_type); if (!node->exprs.is_last(&it)) gen(", "); } gen(")"); BREAK(); } CASE(COMPOUND, Call) { gen("("); gen_simple_decl(node->resolved_type); gen(")"); gen("{"); // We need to double wrap it because Any is a pointer // We need to pass it a compound array 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) || is_union(node->resolved_type)) gen(".%Q = ", it->resolved_name); else if (is_array(node->resolved_type)) gen("[%d] = ", (int)it->resolved_index); gen_try_any_or_slice(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; } /* - [ ] Assigning arrays to arrays doesn't work in C. Being able to do that seems like a good idea though */ CORE_Static void gen_ast(Ast *ast) { switch (ast->kind) { CASE(RUNTIME_ASSERT, Builtin) { if (node->assert_message.len == 0) gen("%QAssert", pctx->symbol_prefix); else gen("%QAssertMessage", pctx->symbol_prefix); 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)) { Scoped_Arena scratch(pctx->scratch); Intern_String tuple_name = get_unique_name(node); gen_simple_decl(node->resolved_type, tuple_name); gen(";"); int i = 0; For(node->expr) { // We cant assign to array in C so we need a special case if (is_array(it->resolved_type)) { genln("%QMemoryCopy(&%Q.m%d, ", pctx->symbol_prefix, tuple_name, i); gen_expr(it); gen(", sizeof(%Q.m%d));", tuple_name, i); } else { genln("%Q.m%d = ", tuple_name, i); gen_expr(it); gen(";"); } i += 1; } gen_line(node); genln("return %Q;", tuple_name); #if 0 gen("return ("); gen_simple_decl(node->resolved_type); gen("){"); int i = 0; global_indent++; For(node->expr){ gen_line(it); genln(""); gen_expr(it); if (!node->expr.is_last(&it)) { gen(","); } } global_indent--; genln(""); gen("};"); #endif 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(it); 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 { gen("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("%QBufferSize(", pctx->symbol_prefix); 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 AST_UNION: CASE(STRUCT, Decl) { gen("%s ", node->kind == AST_STRUCT ? "struct" : "union"); gen("%Q{", node->unique_name); global_indent++; is_inside_struct++; For(node->scope->decls) { genln(""); gen_ast(it); } is_inside_struct--; global_indent--; genln("};"); 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 AST_TYPE: 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); Scoped_Arena scratch(pctx->scratch); Intern_String var_name = get_unique_name(node); gen_simple_decl(node->resolved_type, var_name); gen(" = "); gen_expr(node->expr); gen(";"); int i = 0; For(node->vars) { gen("%QMemoryCopy((void *)&%Q, (void *)&%Q.m%d, sizeof(%Q));", pctx->symbol_prefix, it->name, var_name, i++, it->name); } BREAK(); } case AST_CONSTANT_ASSERT: case AST_NAMESPACE: break; default: { assert(is_flag_set(ast->flags, AST_EXPR)); gen_expr((Ast_Expr *)ast); gen(";"); } } } CORE_Static String compile_to_c_code() { pctx->time.code_generation = os_time(); #if 0 String core_stringify(Ast *); For(pctx->ordered_decls) { Ast *copy = ast_copy(it, 0); String r = core_stringify(it); printf("\n-----REAL!!!------\n"); printf("%s\n", r.str); r = core_stringify(copy); printf("\n-----COPY!!!------\n"); printf("%s\n", r.str); } pctx->gen.reset(); #endif #if 0 int di = 0; For(pctx->ordered_decls) { for (Ast_Iter iter = iterate_depth_first(pctx->heap, it); iter.ast; next(&iter)) { Ast_Decl *decl = (Ast_Decl *)iter.ast; Ast *ast = iter.ast; Ast_Atom *atom = (Ast_Atom *)iter.ast; switch (iter.kind) { case AST_STRUCT: { genln("%Q :: struct", decl->name); global_indent += 1; } break; case AST_STRUCT + AST_NODE_END: { global_indent -= 1; } break; case AST_LAMBDA: { Ast_Lambda *lambda = decl->lambda; genln("%Q :: (", decl->name); For(lambda->args) { gen("%Q: %Q, ", it->name, core_type_to_string(it->type)); } gen(")"); // @cleanup @refactor: return value shouldn't be a array of expressions. // It should be a single expression. So probably need a special type // for that. if (lambda->args.len) { gen(": "); For(lambda->args) { gen("%Q ", core_type_to_string(it->type)); } } iter.skip_children = true; iter.stack.add(decl); if (lambda->scope) iter.stack.add(lambda->scope); global_indent += 1; } break; case AST_LAMBDA + AST_NODE_END: { global_indent -= 1; } break; case AST_VAR: { genln("%Q: %Q", decl->name, core_type_to_string(decl->type)); } break; case AST_VAR + AST_NODE_END: { } break; } } String str = string_flatten(pctx->perm, &pctx->gen); printf("%s", str.str); pctx->gen.reset(); di += 1; } printf("\n"); #endif prefixed_string_type = string_fmt(pctx->perm, "%QString", pctx->symbol_prefix); if (pctx->single_header_library_mode) { gen(R"( /* Do this: #define %Q_IMPLEMENTATION before you include this file in *one* C or C++ file to create the implementation. // i.e. it should look like this: #include ... #include ... #include ... #define %Q_IMPLEMENTATION #include "%Q.h" You can #define %QAssert(x) to avoid using default assert You can #define %QAssertMessage(x) to get more comprehensive error info You can #define %QMemoryCopy(x) to avoid using default memory copy */ )", pctx->single_header_library_name, pctx->single_header_library_name, pctx->single_header_library_name, pctx->symbol_prefix, pctx->symbol_prefix, pctx->symbol_prefix); genln("#ifndef %Q_LIBRARY_HEADER ", pctx->single_header_library_name); genln("#define %Q_LIBRARY_HEADER ", pctx->single_header_library_name); } gen(R"( #include #include #include #ifndef Assert #define Assert(x) do{if(!(x))(*(volatile int *)0 = 0);}while(0) #endif #ifndef AssertMessage #define AssertMessage(x,...) Assert(x) #endif #ifndef MemoryCopy #define MemoryCopy MemoryCopy_ static void 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]; } } #endif #define BufferSize(x) (sizeof(x)/sizeof((x)[0])) )"); // Generate struct forward decls For(pctx->ordered_decls) { if (it->kind == AST_STRUCT) { genln("typedef struct %Q %Q;", it->unique_name, it->unique_name); } else if (it->kind == AST_UNION) { genln("typedef union %Q %Q;", it->unique_name, it->unique_name); } } // Generate slice and tuple types For_Named(pctx->all_types, type) { Scoped_Arena scratch(pctx->scratch); 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->internf("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 For(pctx->ordered_decls) { if (it->kind == AST_LAMBDA) { if (it->name == intern_main) { main = it; it->unique_name = it->name; } if (it->name == intern_win_main) { win_main = it; it->unique_name = it->name; } genln(""); gen_lambda(it->unique_name, it->lambda, false); } } if (!main && !win_main) { compiler_error(0, "Entry point is not defined! Try main or WinMain"); } if (pctx->emit_type_info) { // 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++; For_Named(pctx->all_types, t) { 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_arguments.len = %d, ", t->func.args.len); gen(".lambda_arguments.data = (Type_Info[%d]){", t->func.args.len); For_Named(t->func.args, arg) { gen("{.type = %d}, ", arg->type_id); } gen("}"); } break; case TYPE_UNION: case TYPE_STRUCT: { gen(".struct_members.len = %d, ", t->agg.members.len); gen(".struct_members.data = (Type_Info_Struct_Member[]){"); For_Named(t->agg.members, m) { gen("{.name = (%Q){(uint8_t *)\"%Q\", %d}, .type = %d, .offset = %d}, ", prefixed_string_type, m.name, m.name.len, m.type->type_id, m.offset); } gen("}"); } break; default: { } // invalid_default_case; } gen("},"); } global_indent--; gen("};"); } if (pctx->single_header_library_mode) { genln("#endif"); genln("#ifdef %Q_IMPLEMENTATION ", pctx->single_header_library_name); } // Generate actual code int index = 0; For(pctx->ordered_decls) { if (index >= pctx->base_language_ordered_decl_len) { genln(""); gen_ast(it); } index += 1; } if (pctx->single_header_library_mode) { genln("#endif"); } String string_result = string_flatten(pctx->perm, &pctx->gen); pctx->time.code_generation = os_time() - pctx->time.code_generation; return string_result; }