#include "../ast/ast.h" #include "../parser/parser.h" #include "../zprep.h" #include "codegen.h" #include #include #include static void emit_freestanding_preamble(FILE *out) { fputs("#include \n#include \n#include " "\n#include \n", out); fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out); fputs("typedef size_t usize;\ntypedef char* string;\n", out); fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 " "int16_t\n#define U16 uint16_t\n", out); fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 " "int64_t\n#define U64 " "uint64_t\n", out); fputs("#define F32 float\n#define F64 double\n", out); fputs("#define _z_str(x) _Generic((x), _Bool: \"%d\", char: \"%c\", " "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", " "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", " "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", " "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", " "char*: \"%s\", void*: \"%p\")\n", out); fputs("typedef struct { void *func; void *ctx; } z_closure_T;\n", out); fputs("__attribute__((weak)) void* z_malloc(usize sz) { return NULL; }\n", out); fputs("__attribute__((weak)) void* z_realloc(void* ptr, usize sz) { return " "NULL; }\n", out); fputs("__attribute__((weak)) void z_free(void* ptr) { }\n", out); fputs("__attribute__((weak)) void z_print(const char* fmt, ...) { }\n", out); fputs("__attribute__((weak)) void z_panic(const char* msg) { while(1); }\n", out); } void emit_preamble(ParserContext *ctx, FILE *out) { if (g_config.is_freestanding) { emit_freestanding_preamble(out); } else { // Standard hosted preamble. fputs("#include \n#include \n#include " "\n#include \n", out); fputs("#include \n#include \n#include \n", out); fputs("#include \n#include \n", out); // POSIX functions // C++ compatibility if (g_config.use_cpp) { // For C++: define ZC_AUTO as auto, include compat.h macros inline fputs("#define ZC_AUTO auto\n", out); fputs("#define ZC_CAST(T, x) static_cast(x)\n", out); // C++ _z_str via overloads fputs("inline const char* _z_str(bool) { return \"%d\"; }\n", out); fputs("inline const char* _z_str(char) { return \"%c\"; }\n", out); fputs("inline const char* _z_str(int) { return \"%d\"; }\n", out); fputs("inline const char* _z_str(unsigned int) { return \"%u\"; }\n", out); fputs("inline const char* _z_str(long) { return \"%ld\"; }\n", out); fputs("inline const char* _z_str(unsigned long) { return \"%lu\"; }\n", out); fputs("inline const char* _z_str(long long) { return \"%lld\"; }\n", out); fputs("inline const char* _z_str(unsigned long long) { return \"%llu\"; }\n", out); fputs("inline const char* _z_str(float) { return \"%f\"; }\n", out); fputs("inline const char* _z_str(double) { return \"%f\"; }\n", out); fputs("inline const char* _z_str(char*) { return \"%s\"; }\n", out); fputs("inline const char* _z_str(const char*) { return \"%s\"; }\n", out); fputs("inline const char* _z_str(void*) { return \"%p\"; }\n", out); } else { // C mode fputs("#define ZC_AUTO __auto_type\n", out); fputs("#define ZC_CAST(T, x) ((T)(x))\n", out); fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out); fputs("#define _z_str(x) _Generic((x), _Bool: \"%d\", char: \"%c\", " "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", " "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", " "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", " "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", " "char*: \"%s\", void*: \"%p\")\n", out); } fputs("typedef size_t usize;\ntypedef char* string;\n", out); if (ctx->has_async) { fputs("#include \n", out); fputs("typedef struct { pthread_t thread; void *result; } Async;\n", out); } fputs("typedef struct { void *func; void *ctx; } z_closure_T;\n", out); fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 " "int16_t\n#define U16 uint16_t\n", out); fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 " "int64_t\n#define U64 " "uint64_t\n", out); fputs("#define F32 float\n#define F64 double\n", out); // Memory Mapping. if (g_config.use_cpp) { // C++ needs explicit casts for void* conversions fputs("#define z_malloc(sz) static_cast(malloc(sz))\n", out); fputs("#define z_realloc(p, sz) static_cast(realloc(p, sz))\n", out); } else { fputs("#define z_malloc malloc\n#define z_realloc realloc\n", out); } fputs("#define z_free free\n#define z_print printf\n", out); fputs("void z_panic(const char* msg) { fprintf(stderr, \"Panic: %s\\n\", " "msg); exit(1); }\n", out); fputs("void _z_autofree_impl(void *p) { void **pp = (void**)p; if(*pp) { " "z_free(*pp); *pp " "= NULL; } }\n", out); fputs("#define assert(cond, ...) if (!(cond)) { fprintf(stderr, " "\"Assertion failed: \" " "__VA_ARGS__); exit(1); }\n", out); // C++ compatible readln helper if (g_config.use_cpp) { fputs( "string _z_readln_raw() { " "size_t cap = 64; size_t len = 0; " "char *line = static_cast(malloc(cap)); " "if(!line) return NULL; " "int c; " "while((c = fgetc(stdin)) != EOF) { " "if(c == '\\n') break; " "if(len + 1 >= cap) { cap *= 2; char *n = static_cast(realloc(line, cap)); " "if(!n) { free(line); return NULL; } line = n; } " "line[len++] = c; } " "if(len == 0 && c == EOF) { free(line); return NULL; } " "line[len] = 0; return line; }\n", out); } else { fputs("string _z_readln_raw() { " "size_t cap = 64; size_t len = 0; " "char *line = z_malloc(cap); " "if(!line) return NULL; " "int c; " "while((c = fgetc(stdin)) != EOF) { " "if(c == '\\n') break; " "if(len + 1 >= cap) { cap *= 2; char *n = z_realloc(line, cap); " "if(!n) { z_free(line); return NULL; } line = n; } " "line[len++] = c; } " "if(len == 0 && c == EOF) { z_free(line); return NULL; } " "line[len] = 0; return line; }\n", out); } fputs("int _z_scan_helper(const char *fmt, ...) { char *l = " "_z_readln_raw(); if(!l) return " "0; va_list ap; va_start(ap, fmt); int r = vsscanf(l, fmt, ap); " "va_end(ap); " "z_free(l); return r; }\n", out); // REPL helpers: suppress/restore stdout. fputs("int _z_orig_stdout = -1;\n", out); fputs("void _z_suppress_stdout() {\n", out); fputs(" fflush(stdout);\n", out); fputs(" if (_z_orig_stdout == -1) _z_orig_stdout = dup(STDOUT_FILENO);\n", out); fputs(" int nullfd = open(\"/dev/null\", O_WRONLY);\n", out); fputs(" dup2(nullfd, STDOUT_FILENO);\n", out); fputs(" close(nullfd);\n", out); fputs("}\n", out); fputs("void _z_restore_stdout() {\n", out); fputs(" fflush(stdout);\n", out); fputs(" if (_z_orig_stdout != -1) {\n", out); fputs(" dup2(_z_orig_stdout, STDOUT_FILENO);\n", out); fputs(" close(_z_orig_stdout);\n", out); fputs(" _z_orig_stdout = -1;\n", out); fputs(" }\n", out); fputs("}\n", out); } } // Emit includes and type aliases. void emit_includes_and_aliases(ASTNode *node, FILE *out) { while (node) { if (node->type == NODE_INCLUDE) { if (node->include.is_system) { fprintf(out, "#include <%s>\n", node->include.path); } else { fprintf(out, "#include \"%s\"\n", node->include.path); } } node = node->next; } } // Emit type aliases (after struct defs so the aliased types exist) void emit_type_aliases(ASTNode *node, FILE *out) { while (node) { if (node->type == NODE_TYPE_ALIAS) { fprintf(out, "typedef %s %s;\n", node->type_alias.original_type, node->type_alias.alias); } node = node->next; } } void emit_global_aliases(ParserContext *ctx, FILE *out) { TypeAlias *ta = ctx->type_aliases; while (ta) { fprintf(out, "typedef %s %s;\n", ta->original_type, ta->alias); ta = ta->next; } } // Emit enum constructor prototypes void emit_enum_protos(ASTNode *node, FILE *out) { while (node) { if (node->type == NODE_ENUM && !node->enm.is_template) { ASTNode *v = node->enm.variants; while (v) { if (v->variant.payload) { char *tstr = codegen_type_to_string(v->variant.payload); fprintf(out, "%s %s_%s(%s v);\n", node->enm.name, node->enm.name, v->variant.name, tstr); free(tstr); } else { fprintf(out, "%s %s_%s();\n", node->enm.name, node->enm.name, v->variant.name); } v = v->next; } } node = node->next; } } // Emit lambda definitions. void emit_lambda_defs(ParserContext *ctx, FILE *out) { LambdaRef *cur = ctx->global_lambdas; while (cur) { ASTNode *node = cur->node; int saved_defer = defer_count; defer_count = 0; if (node->lambda.num_captures > 0) { fprintf(out, "struct Lambda_%d_Ctx {\n", node->lambda.lambda_id); for (int i = 0; i < node->lambda.num_captures; i++) { fprintf(out, " %s %s;\n", node->lambda.captured_types[i], node->lambda.captured_vars[i]); } fprintf(out, "};\n"); } fprintf(out, "%s _lambda_%d(void* _ctx", node->lambda.return_type, node->lambda.lambda_id); for (int i = 0; i < node->lambda.num_params; i++) { fprintf(out, ", %s %s", node->lambda.param_types[i], node->lambda.param_names[i]); } fprintf(out, ") {\n"); if (node->lambda.num_captures > 0) { fprintf(out, " struct Lambda_%d_Ctx* ctx = (struct Lambda_%d_Ctx*)_ctx;\n", node->lambda.lambda_id, node->lambda.lambda_id); } g_current_lambda = node; if (node->lambda.body && node->lambda.body->type == NODE_BLOCK) { codegen_walker(ctx, node->lambda.body->block.statements, out); } g_current_lambda = NULL; for (int i = defer_count - 1; i >= 0; i--) { codegen_node_single(ctx, defer_stack[i], out); } fprintf(out, "}\n\n"); defer_count = saved_defer; cur = cur->next; } } // Emit struct and enum definitions. void emit_struct_defs(ParserContext *ctx, ASTNode *node, FILE *out) { while (node) { if (node->type == NODE_STRUCT && node->strct.is_template) { node = node->next; continue; } if (node->type == NODE_ENUM && node->enm.is_template) { node = node->next; continue; } if (node->type == NODE_STRUCT) { if (node->strct.is_incomplete) { // Forward declaration - no body needed (typedef handles it) node = node->next; continue; } if (node->strct.is_union) { fprintf(out, "union %s {", node->strct.name); } else { fprintf(out, "struct %s {", node->strct.name); } fprintf(out, "\n"); if (node->strct.fields) { codegen_walker(ctx, node->strct.fields, out); } else { // C requires at least one member in a struct. fprintf(out, " char _placeholder;\n"); } fprintf(out, "}"); if (node->strct.is_packed && node->strct.align) { fprintf(out, " __attribute__((packed, aligned(%d)))", node->strct.align); } else if (node->strct.is_packed) { fprintf(out, " __attribute__((packed))"); } else if (node->strct.align) { fprintf(out, " __attribute__((aligned(%d)))", node->strct.align); } fprintf(out, ";\n\n"); } else if (node->type == NODE_ENUM) { fprintf(out, "typedef enum { "); ASTNode *v = node->enm.variants; while (v) { fprintf(out, "%s_%s_Tag, ", node->enm.name, v->variant.name); v = v->next; } fprintf(out, "} %s_Tag;\n", node->enm.name); fprintf(out, "struct %s { %s_Tag tag; union { ", node->enm.name, node->enm.name); v = node->enm.variants; while (v) { if (v->variant.payload) { char *tstr = codegen_type_to_string(v->variant.payload); fprintf(out, "%s %s; ", tstr, v->variant.name); free(tstr); } v = v->next; } fprintf(out, "} data; };\n\n"); v = node->enm.variants; while (v) { if (v->variant.payload) { char *tstr = codegen_type_to_string(v->variant.payload); fprintf(out, "%s %s_%s(%s v) { return (%s){.tag=%s_%s_Tag, " ".data.%s=v}; }\n", node->enm.name, node->enm.name, v->variant.name, tstr, node->enm.name, node->enm.name, v->variant.name, v->variant.name); free(tstr); } else { fprintf(out, "%s %s_%s() { return (%s){.tag=%s_%s_Tag}; }\n", node->enm.name, node->enm.name, v->variant.name, node->enm.name, node->enm.name, v->variant.name); } v = v->next; } } node = node->next; } } // Helper to substitute 'Self' with replacement string static char *substitute_proto_self(const char *type_str, const char *replacement) { if (!type_str) { return NULL; } if (strcmp(type_str, "Self") == 0) { return xstrdup(replacement); } // Handle pointers (Self* -> replacement*) if (strncmp(type_str, "Self", 4) == 0) { char *rest = (char *)type_str + 4; char *buf = xmalloc(strlen(replacement) + strlen(rest) + 1); sprintf(buf, "%s%s", replacement, rest); return buf; } return xstrdup(type_str); } // Emit trait definitions. void emit_trait_defs(ASTNode *node, FILE *out) { while (node) { if (node->type == NODE_TRAIT) { if (node->trait.generic_param_count > 0) { node = node->next; continue; } fprintf(out, "typedef struct %s_VTable {\n", node->trait.name); ASTNode *m = node->trait.methods; while (m) { char *ret_safe = substitute_proto_self(m->func.ret_type, "void*"); fprintf(out, " %s (*%s)(", ret_safe, parse_original_method_name(m->func.name)); free(ret_safe); int has_self = (m->func.args && strstr(m->func.args, "self")); if (!has_self) { fprintf(out, "void* self"); } if (m->func.args) { if (!has_self) { fprintf(out, ", "); } char *args_safe = xstrdup(m->func.args); // TODO: better replace, but for now this works. char *p = strstr(args_safe, "Self"); while (p) { // Check word boundary if ((p == args_safe || !isalnum(p[-1])) && !isalnum(p[4])) { int off = p - args_safe; char *new_s = xmalloc(strlen(args_safe) + 10); strncpy(new_s, args_safe, off); new_s[off] = 0; strcat(new_s, "void*"); strcat(new_s, p + 4); free(args_safe); args_safe = new_s; p = strstr(args_safe + off + 5, "Self"); } else { p = strstr(p + 1, "Self"); } } fprintf(out, "%s", args_safe); free(args_safe); } fprintf(out, ");\n"); m = m->next; } fprintf(out, "} %s_VTable;\n", node->trait.name); fprintf(out, "typedef struct %s { void *self; %s_VTable *vtable; } %s;\n", node->trait.name, node->trait.name, node->trait.name); m = node->trait.methods; while (m) { const char *orig = parse_original_method_name(m->func.name); char *ret_sub = substitute_proto_self(m->func.ret_type, node->trait.name); fprintf(out, "%s %s__%s(%s* self", ret_sub, node->trait.name, orig, node->trait.name); int has_self = (m->func.args && strstr(m->func.args, "self")); if (m->func.args) { if (has_self) { char *comma = strchr(m->func.args, ','); if (comma) { // Substitute Self -> TraitName in wrapper args char *args_sub = xstrdup(comma + 1); char *p = strstr(args_sub, "Self"); while (p) { int off = p - args_sub; char *new_s = xmalloc(strlen(args_sub) + strlen(node->trait.name) + 5); strncpy(new_s, args_sub, off); new_s[off] = 0; strcat(new_s, node->trait.name); strcat(new_s, p + 4); free(args_sub); args_sub = new_s; p = strstr(args_sub + off + strlen(node->trait.name), "Self"); } fprintf(out, ", %s", args_sub); free(args_sub); } } else { fprintf(out, ", %s", m->func.args); // TODO: recursive subst } } fprintf(out, ") {\n"); int ret_is_self = (strcmp(m->func.ret_type, "Self") == 0); if (ret_is_self) { // Special handling: return (Trait){.self = call(), .vtable = self->vtable} fprintf(out, " void* ret = self->vtable->%s(self->self", orig); } else { fprintf(out, " return self->vtable->%s(self->self", orig); } if (m->func.args) { char *call_args = extract_call_args(m->func.args); if (has_self) { char *comma = strchr(call_args, ','); if (comma) { fprintf(out, ", %s", comma + 1); } } else { if (strlen(call_args) > 0) { fprintf(out, ", %s", call_args); } } free(call_args); } fprintf(out, ");\n"); if (ret_is_self) { fprintf(out, " return (%s){.self = ret, .vtable = self->vtable};\n", node->trait.name); } fprintf(out, "}\n\n"); free(ret_sub); m = m->next; } fprintf(out, "\n"); } node = node->next; } } // Emit global variables void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out) { while (node) { if (node->type == NODE_VAR_DECL || node->type == NODE_CONST) { if (node->type == NODE_CONST) { fprintf(out, "const "); } if (node->var_decl.type_str) { emit_var_decl_type(ctx, out, node->var_decl.type_str, node->var_decl.name); } else { char *inferred = NULL; if (node->var_decl.init_expr) { inferred = infer_type(ctx, node->var_decl.init_expr); } if (inferred && strcmp(inferred, "__auto_type") != 0) { emit_var_decl_type(ctx, out, inferred, node->var_decl.name); } else { emit_auto_type(ctx, node->var_decl.init_expr, node->token, out); fprintf(out, " %s", node->var_decl.name); } } if (node->var_decl.init_expr) { fprintf(out, " = "); codegen_expression(ctx, node->var_decl.init_expr, out); } fprintf(out, ";\n"); } node = node->next; } } // Emit function prototypes void emit_protos(ASTNode *node, FILE *out) { ASTNode *f = node; while (f) { if (f->type == NODE_FUNCTION) { if (f->func.is_async) { fprintf(out, "Async %s(%s);\n", f->func.name, f->func.args); // Also emit _impl_ prototype if (f->func.ret_type) { fprintf(out, "%s _impl_%s(%s);\n", f->func.ret_type, f->func.name, f->func.args); } else { fprintf(out, "void _impl_%s(%s);\n", f->func.name, f->func.args); } } else { emit_func_signature(out, f, NULL); fprintf(out, ";\n"); } } else if (f->type == NODE_IMPL) { char *sname = f->impl.struct_name; if (!sname) { f = f->next; continue; } char *mangled = replace_string_type(sname); ASTNode *def = find_struct_def_codegen(g_parser_ctx, mangled); int skip = 0; if (def) { if (def->type == NODE_STRUCT && def->strct.is_template) { skip = 1; } else if (def->type == NODE_ENUM && def->enm.is_template) { skip = 1; } } else { char *buf = strip_template_suffix(sname); if (buf) { def = find_struct_def_codegen(g_parser_ctx, buf); if (def && def->strct.is_template) { skip = 1; } free(buf); } } if (mangled) { free(mangled); } if (skip) { f = f->next; continue; } ASTNode *m = f->impl.methods; while (m) { char *fname = m->func.name; char *proto = xmalloc(strlen(fname) + strlen(sname) + 2); int slen = strlen(sname); if (strncmp(fname, sname, slen) == 0 && fname[slen] == '_' && fname[slen + 1] == '_') { strcpy(proto, fname); } else { sprintf(proto, "%s__%s", sname, fname); } if (m->func.is_async) { fprintf(out, "Async %s(%s);\n", proto, m->func.args); } else { emit_func_signature(out, m, proto); fprintf(out, ";\n"); } free(proto); m = m->next; } } else if (f->type == NODE_IMPL_TRAIT) { char *sname = f->impl_trait.target_type; if (!sname) { f = f->next; continue; } char *mangled = replace_string_type(sname); ASTNode *def = find_struct_def_codegen(g_parser_ctx, mangled); int skip = 0; if (def) { if (def->strct.is_template) { skip = 1; } } else { char *buf = strip_template_suffix(sname); if (buf) { def = find_struct_def_codegen(g_parser_ctx, buf); if (def && def->strct.is_template) { skip = 1; } free(buf); } } if (mangled) { free(mangled); } if (skip) { f = f->next; continue; } if (strcmp(f->impl_trait.trait_name, "Drop") == 0) { fprintf(out, "void %s__Drop_glue(%s *self);\n", sname, sname); } ASTNode *m = f->impl_trait.methods; while (m) { if (m->func.is_async) { fprintf(out, "Async %s(%s);\n", m->func.name, m->func.args); } else { fprintf(out, "%s %s(%s);\n", m->func.ret_type, m->func.name, m->func.args); } m = m->next; } // RAII: Emit glue prototype if (strcmp(f->impl_trait.trait_name, "Drop") == 0) { char *tname = f->impl_trait.target_type; fprintf(out, "void %s_Drop_glue(%s *self);\n", tname, tname); } } f = f->next; } } // Emit VTable instances for trait implementations. void emit_impl_vtables(ParserContext *ctx, FILE *out) { StructRef *ref = ctx->parsed_impls_list; struct { char *trait; char *strct; } emitted[1024]; int count = 0; while (ref) { ASTNode *node = ref->node; if (node && node->type == NODE_IMPL_TRAIT) { char *trait = node->impl_trait.trait_name; // Filter generic traits (VTables for them are not emitted) int is_generic_trait = 0; StructRef *search = ctx->parsed_globals_list; while (search) { if (search->node && search->node->type == NODE_TRAIT && strcmp(search->node->trait.name, trait) == 0) { if (search->node->trait.generic_param_count > 0) { is_generic_trait = 1; } break; } search = search->next; } if (is_generic_trait) { ref = ref->next; continue; } char *strct = node->impl_trait.target_type; // Filter templates char *mangled = replace_string_type(strct); ASTNode *def = find_struct_def_codegen(ctx, mangled); int skip = 0; if (def) { if (def->type == NODE_STRUCT && def->strct.is_template) { skip = 1; } else if (def->type == NODE_ENUM && def->enm.is_template) { skip = 1; } } else { char *buf = strip_template_suffix(strct); if (buf) { def = find_struct_def_codegen(ctx, buf); if (def && def->strct.is_template) { skip = 1; } free(buf); } } if (mangled) { free(mangled); } if (skip) { ref = ref->next; continue; } // Check duplication int dup = 0; for (int i = 0; i < count; i++) { if (strcmp(emitted[i].trait, trait) == 0 && strcmp(emitted[i].strct, strct) == 0) { dup = 1; break; } } if (dup) { ref = ref->next; continue; } emitted[count].trait = trait; emitted[count].strct = strct; count++; if (0 == strcmp(trait, "Copy")) { // Marker trait, no runtime vtable needed ref = ref->next; continue; } fprintf(out, "%s_VTable %s_%s_VTable = {", trait, strct, trait); ASTNode *m = node->impl_trait.methods; while (m) { const char *orig = parse_original_method_name(m->func.name); fprintf(out, ".%s = (__typeof__(((%s_VTable*)0)->%s))%s__%s_%s", orig, trait, orig, strct, trait, orig); if (m->next) { fprintf(out, ", "); } m = m->next; } fprintf(out, "};\n"); } ref = ref->next; } } // Emit test functions and runner void emit_tests_and_runner(ParserContext *ctx, ASTNode *node, FILE *out) { ASTNode *cur = node; int test_count = 0; while (cur) { if (cur->type == NODE_TEST) { fprintf(out, "static void _z_test_%d() {\n", test_count); codegen_walker(ctx, cur->test_stmt.body, out); fprintf(out, "}\n"); test_count++; } cur = cur->next; } if (test_count > 0) { fprintf(out, "\nvoid _z_run_tests() {\n"); for (int i = 0; i < test_count; i++) { fprintf(out, " _z_test_%d();\n", i); } fprintf(out, "}\n\n"); } else { fprintf(out, "void _z_run_tests() {}\n"); } } // Emit type definitions- void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes) { if (!g_config.is_freestanding) { fprintf(out, "typedef char* string;\n"); } fprintf(out, "typedef struct { void **data; int len; int cap; } Vec;\n"); fprintf(out, "#define Vec_new() (Vec){.data=0, .len=0, .cap=0}\n"); if (g_config.use_cpp) { fprintf(out, "void _z_vec_push(Vec *v, void *item) { if(v->len >= v->cap) { " "v->cap = v->cap?v->cap*2:8; " "v->data = static_cast(realloc(v->data, v->cap * sizeof(void*))); } " "v->data[v->len++] = item; }\n"); fprintf(out, "static inline Vec _z_make_vec(int count, ...) { Vec v = {0}; v.cap = " "count > 8 ? " "count : 8; v.data = static_cast(malloc(v.cap * sizeof(void*))); " "v.len = 0; va_list " "args; " "va_start(args, count); for(int i=0; ilen >= v->cap) { " "v->cap = v->cap?v->cap*2:8; " "v->data = z_realloc(v->data, v->cap * sizeof(void*)); } " "v->data[v->len++] = item; }\n"); fprintf(out, "static inline Vec _z_make_vec(int count, ...) { Vec v = {0}; v.cap = " "count > 8 ? " "count : 8; v.data = z_malloc(v.cap * sizeof(void*)); v.len = 0; va_list " "args; " "va_start(args, count); for(int i=0; i= (limit)) { z_panic(\"index out of bounds\"); } _i; })\n"); } else { fprintf(out, "#define _z_check_bounds(index, limit) ({ ZC_AUTO _i = " "(index); if(_i < 0 " "|| _i >= (limit)) { fprintf(stderr, \"Index out of bounds: " "%%ld (limit " "%%d)\\n\", (long)_i, (int)(limit)); exit(1); } _i; })\n"); } SliceType *c = ctx->used_slices; while (c) { fprintf(out, "typedef struct Slice_%s Slice_%s;\nstruct Slice_%s { %s *data; " "int len; int cap; };\n", c->name, c->name, c->name, c->name); c = c->next; } TupleType *t = ctx->used_tuples; while (t) { fprintf(out, "typedef struct Tuple_%s Tuple_%s;\nstruct Tuple_%s { ", t->sig, t->sig, t->sig); char *s = xstrdup(t->sig); char *p = strtok(s, "_"); int i = 0; while (p) { fprintf(out, "%s v%d; ", p, i++); p = strtok(NULL, "_"); } free(s); fprintf(out, "};\n"); t = t->next; } fprintf(out, "\n"); // FIRST: Emit typedefs for ALL structs and enums in the current compilation // unit (local definitions) ASTNode *local = nodes; while (local) { if (local->type == NODE_STRUCT && !local->strct.is_template) { const char *keyword = local->strct.is_union ? "union" : "struct"; fprintf(out, "typedef %s %s %s;\n", keyword, local->strct.name, local->strct.name); } if (local->type == NODE_ENUM && !local->enm.is_template) { fprintf(out, "typedef struct %s %s;\n", local->enm.name, local->enm.name); } local = local->next; } }