Files
corelang/core_codegen_c_language.cpp

983 lines
24 KiB
C++

#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(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(emit_line_directives){
genln("#line %d", last_line);
}
}
CORE_Static String
string_scope_name(Arena *a, Ast_Scope *scope){
String string = {};
if(scope->parent_scope) string = string_scope_name(a, scope->parent_scope);
assert_message(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;
}
CORE_Static void
gen_scope_name(Ast_Scope *scope){
Scratch scratch;
String string = string_scope_name(scratch, scope);
gen("%.*s", (int)string.len, string.str);
}
CORE_Static String
unique_name(Arena *allocator, Ast *ast){
Scratch scratch(allocator);
String result = string_scope_name(scratch, ast->parent_scope);
assert(result.len);
result = string_fmt(allocator, "%Q%d", result, ast->pos->line);
return result;
}
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: return "(Internal compiler error: [Type_Incomplete])";
invalid_default_case;
}
return "<unknown_type>";
}
CORE_Static String
string_simple_decl_prefix(Arena *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->unique_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 {};
}
CORE_Static String
string_simple_decl_postfix(Arena *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 {};
}
CORE_Static String
string_simple_decl(Arena *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;
}
}
CORE_Static 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);
}
CORE_Static String
gen_string_simple_decl(Arena *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;
}
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: {
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("(%QString){(uint8_t *)\"", 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);
}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;
}
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("{%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 == type_string || node->index_original_type == untyped_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)){
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))
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;
}
CORE_Static void
gen_ast(Ast *ast){
switch(ast->kind){
CASE(RUNTIME_ASSERT, Builtin){
if(node->assert_message.len == 0) gen("%QAssert", symbol_prefix);
else gen("%QAssertMessage", 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)) {
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("%QMemoryCopy(&%Q.m%d, ", symbol_prefix, 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(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(", 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(STRUCT, Decl){
gen("struct ");
gen("%Q{", node->unique_name);
global_indent++;
is_inside_struct++;
Iter(&node->scope->decls){
genln("");
gen_ast(it.item[0]);
}
is_inside_struct--;
global_indent--;
genln("};");
BREAK();
}
CASE(ENUM, Decl){
gen("/*enum %Q{", node->name);
// @todo add typespec
global_indent++;
Iter(&node->scope->decls){
genln("%Q", it.item[0]->name);
gen(" = ");
gen_value(it.item[0]->pos, it.item[0]->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);
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("%QMemoryCopy((void *)&%Q, (void *)&%Q.m%d, sizeof(%Q));", 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->generating_time_begin = os_time();
prefixed_string_type = string_fmt(pctx->perm, "%QString", symbol_prefix);
if(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
*/
)", single_header_library_name, single_header_library_name, single_header_library_name,
symbol_prefix, symbol_prefix, symbol_prefix);
genln("#ifndef %Q_LIBRARY_HEADER ", single_header_library_name);
genln("#define %Q_LIBRARY_HEADER ", single_header_library_name);
}
gen(R"(
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#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]))
typedef struct String{
uint8_t *str;
int64_t len;
}String;
)");
// Generate struct forward decls
Iter(&pctx->ordered_decls){
auto i = it.item[0];
if(i->kind == AST_STRUCT){
genln("typedef struct %Q %Q;", i->unique_name, i->unique_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");
}
if(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++;
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 = (%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(single_header_library_mode){
genln("#endif");
genln("#ifdef %Q_IMPLEMENTATION ", single_header_library_name);
}
// Generate actual code
Iter(&pctx->ordered_decls){
if(it.index >= pctx->base_language_ordered_decl_len){
genln("");
gen_ast(it.item[0]);
}
}
if(single_header_library_mode){
genln("#endif");
}
String string_result = string_flatten(pctx->perm, &pctx->gen);
pctx->generating_time_end = os_time();
return string_result;
}