summaryrefslogtreecommitdiff
path: root/src/codegen/codegen_utils.c
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-11 15:11:00 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-11 15:11:00 +0000
commit55247a3f12a9eee7ba3fd7ca6d8fcea7a82c20f3 (patch)
treea2a71e2eb8ca0b2c483518c1902d89d18709c9ab /src/codegen/codegen_utils.c
parent2e7abed7cfe84a2c0df371cde35f8f68cfdca16c (diff)
Added src/ folder. Now I will add the rest.
Diffstat (limited to 'src/codegen/codegen_utils.c')
-rw-r--r--src/codegen/codegen_utils.c499
1 files changed, 499 insertions, 0 deletions
diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c
new file mode 100644
index 0000000..fac6c6d
--- /dev/null
+++ b/src/codegen/codegen_utils.c
@@ -0,0 +1,499 @@
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "codegen.h"
+#include "../zprep.h"
+#include "../ast/ast.h"
+#include "../parser/parser.h"
+
+// Global state
+ASTNode *global_user_structs = NULL;
+char *g_current_impl_type = NULL;
+int tmp_counter = 0;
+ASTNode *defer_stack[MAX_DEFER];
+int defer_count = 0;
+ASTNode *g_current_lambda = NULL;
+
+// Helper to emit variable declarations with array types.
+void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, const char *var_name)
+{
+ (void)ctx;
+
+ char *bracket = strchr(type_str, '[');
+
+ if (bracket)
+ {
+ int base_len = bracket - type_str;
+ fprintf(out, "%.*s %s%s", base_len, type_str, var_name, bracket);
+ }
+ else
+ {
+ fprintf(out, "%s %s", type_str, var_name);
+ }
+}
+
+// Find struct definition
+ASTNode *find_struct_def_codegen(ParserContext *ctx, const char *name)
+{
+ if (!name)
+ {
+ return NULL;
+ }
+ ASTNode *s = global_user_structs;
+ while (s)
+ {
+ if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0)
+ {
+ return s;
+ }
+ s = s->next;
+ }
+
+ // Check parsed structs list (imports)-
+ StructRef *sr = ctx->parsed_structs_list;
+ while (sr)
+ {
+ if (sr->node && sr->node->type == NODE_STRUCT && strcmp(sr->node->strct.name, name) == 0)
+ {
+ return sr->node;
+ }
+ sr = sr->next;
+ }
+ s = ctx->instantiated_structs;
+ while (s)
+ {
+ if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0)
+ {
+ return s;
+ }
+ s = s->next;
+ }
+ return NULL;
+}
+
+// Get field type from struct.
+char *get_field_type_str(ParserContext *ctx, const char *struct_name, const char *field_name)
+{
+ char clean_name[256];
+ strncpy(clean_name, struct_name, sizeof(clean_name) - 1);
+ clean_name[sizeof(clean_name) - 1] = 0;
+
+ char *ptr = strchr(clean_name, '<');
+ if (ptr)
+ {
+ *ptr = 0;
+ }
+
+ ASTNode *def = find_struct_def_codegen(ctx, clean_name);
+ if (!def)
+ {
+ return NULL;
+ }
+
+ ASTNode *f = def->strct.fields;
+ while (f)
+ {
+ if (strcmp(f->field.name, field_name) == 0)
+ {
+ return f->field.type;
+ }
+ f = f->next;
+ }
+ return NULL;
+}
+
+// Type inference.
+char *infer_type(ParserContext *ctx, ASTNode *node)
+{
+ if (!node)
+ {
+ return NULL;
+ }
+ if (node->resolved_type && strcmp(node->resolved_type, "unknown") != 0)
+ {
+ return node->resolved_type;
+ }
+
+ if (node->type == NODE_EXPR_VAR)
+ {
+ Symbol *sym = find_symbol_entry(ctx, node->var_ref.name);
+ if (sym)
+ {
+ if (sym->type_name)
+ {
+ return sym->type_name;
+ }
+ if (sym->type_info)
+ {
+ return type_to_string(sym->type_info);
+ }
+ }
+ }
+
+ if (node->type == NODE_EXPR_CALL)
+ {
+ if (node->call.callee->type == NODE_EXPR_VAR)
+ {
+ FuncSig *sig = find_func(ctx, node->call.callee->var_ref.name);
+ if (sig)
+ {
+ if (sig->is_async)
+ {
+ return "Async";
+ }
+ if (sig->ret_type)
+ {
+ return type_to_string(sig->ret_type);
+ }
+ }
+
+ // Fallback for known stdlib memory functions.
+ if (strcmp(node->call.callee->var_ref.name, "malloc") == 0 ||
+ strcmp(node->call.callee->var_ref.name, "calloc") == 0 ||
+ strcmp(node->call.callee->var_ref.name, "realloc") == 0)
+ {
+ return "void*";
+ }
+ ASTNode *sdef = find_struct_def_codegen(ctx, node->call.callee->var_ref.name);
+ if (sdef)
+ {
+ return node->call.callee->var_ref.name;
+ }
+ }
+ // Method call: target.method() - look up Type_method signature.
+ if (node->call.callee->type == NODE_EXPR_MEMBER)
+ {
+ char *target_type = infer_type(ctx, node->call.callee->member.target);
+ if (target_type)
+ {
+ char clean_type[256];
+ strcpy(clean_type, target_type);
+ char *ptr = strchr(clean_type, '*');
+ if (ptr)
+ {
+ *ptr = 0;
+ }
+
+ char *base = clean_type;
+ if (strncmp(base, "struct ", 7) == 0)
+ {
+ base += 7;
+ }
+
+ char func_name[512];
+ sprintf(func_name, "%s_%s", base, node->call.callee->member.field);
+
+ FuncSig *sig = find_func(ctx, func_name);
+ if (sig && sig->ret_type)
+ {
+ return type_to_string(sig->ret_type);
+ }
+ }
+ }
+
+ // Check if it's a function pointer or built-in registered as a symbol.
+ if (node->call.callee->type == NODE_EXPR_VAR)
+ {
+ Symbol *sym = find_symbol_entry(ctx, node->call.callee->var_ref.name);
+ if (sym && sym->type_info)
+ {
+ // If it's a function pointer type, return return type.
+ if (sym->type_info->kind == TYPE_FUNCTION && sym->type_info->inner)
+ {
+ return type_to_string(sym->type_info->inner);
+ }
+ if (sym->type_info->kind == TYPE_POINTER && sym->type_info->inner &&
+ sym->type_info->inner->kind == TYPE_FUNCTION && sym->type_info->inner->inner)
+ {
+ return type_to_string(sym->type_info->inner->inner);
+ }
+ }
+ }
+
+ return NULL;
+ }
+
+ if (node->type == NODE_EXPR_MEMBER)
+ {
+ char *parent_type = infer_type(ctx, node->member.target);
+ if (!parent_type)
+ {
+ return NULL;
+ }
+
+ char clean_name[256];
+ strcpy(clean_name, parent_type);
+ char *ptr = strchr(clean_name, '*');
+ if (ptr)
+ {
+ *ptr = 0;
+ }
+
+ return get_field_type_str(ctx, clean_name, node->member.field);
+ }
+
+ if (node->type == NODE_EXPR_BINARY)
+ {
+ if (strcmp(node->binary.op, "??") == 0)
+ {
+ return infer_type(ctx, node->binary.left);
+ }
+
+ const char *op = node->binary.op;
+ char *left_type = infer_type(ctx, node->binary.left);
+ char *right_type = infer_type(ctx, node->binary.right);
+
+ int is_logical = (strcmp(op, "&&") == 0 || strcmp(op, "||") == 0 || strcmp(op, "==") == 0 ||
+ strcmp(op, "!=") == 0 || strcmp(op, "<") == 0 || strcmp(op, ">") == 0 ||
+ strcmp(op, "<=") == 0 || strcmp(op, ">=") == 0);
+
+ if (is_logical)
+ {
+ return xstrdup("int");
+ }
+
+ if (left_type && strcmp(left_type, "usize") == 0)
+ {
+ return "usize";
+ }
+ if (right_type && strcmp(right_type, "usize") == 0)
+ {
+ return "usize";
+ }
+ if (left_type && strcmp(left_type, "double") == 0)
+ {
+ return "double";
+ }
+
+ return left_type ? left_type : right_type;
+ }
+
+ if (node->type == NODE_MATCH)
+ {
+ ASTNode *case_node = node->match_stmt.cases;
+ while (case_node)
+ {
+ char *type = infer_type(ctx, case_node->match_case.body);
+ if (type && strcmp(type, "void") != 0 && strcmp(type, "unknown") != 0)
+ {
+ return type;
+ }
+ case_node = case_node->next;
+ }
+ return NULL;
+ }
+
+ if (node->type == NODE_EXPR_INDEX)
+ {
+ char *array_type = infer_type(ctx, node->index.array);
+ if (array_type)
+ {
+ // If T*, returns T. If T[], returns T.
+ char *ptr = strrchr(array_type, '*');
+ if (ptr)
+ {
+ int len = ptr - array_type;
+ char *buf = xmalloc(len + 1);
+ strncpy(buf, array_type, len);
+ buf[len] = 0;
+ return buf;
+ }
+ }
+ return "int";
+ }
+
+ if (node->type == NODE_EXPR_UNARY)
+ {
+ if (strcmp(node->unary.op, "&") == 0)
+ {
+ char *inner = infer_type(ctx, node->unary.operand);
+ if (inner)
+ {
+ char *buf = xmalloc(strlen(inner) + 2);
+ sprintf(buf, "%s*", inner);
+ return buf;
+ }
+ }
+ if (strcmp(node->unary.op, "*") == 0)
+ {
+ char *inner = infer_type(ctx, node->unary.operand);
+ if (inner)
+ {
+ char *ptr = strchr(inner, '*');
+ if (ptr)
+ {
+ // Return base type (naive)
+ int len = ptr - inner;
+ char *dup = xmalloc(len + 1);
+ strncpy(dup, inner, len);
+ dup[len] = 0;
+ return dup;
+ }
+ }
+ }
+ return infer_type(ctx, node->unary.operand);
+ }
+
+ if (node->type == NODE_AWAIT)
+ {
+ // Infer underlying type T from await Async<T>
+ // If it's a direct call await foo(), we know T from foo's signature.
+ if (node->unary.operand->type == NODE_EXPR_CALL &&
+ node->unary.operand->call.callee->type == NODE_EXPR_VAR)
+ {
+ FuncSig *sig = find_func(ctx, node->unary.operand->call.callee->var_ref.name);
+ if (sig && sig->ret_type)
+ {
+ return type_to_string(sig->ret_type);
+ }
+ }
+
+ return "void*";
+ }
+
+ if (node->type == NODE_EXPR_CAST)
+ {
+ return node->cast.target_type;
+ }
+
+ if (node->type == NODE_EXPR_STRUCT_INIT)
+ {
+ return node->struct_init.struct_name;
+ }
+
+ if (node->type == NODE_EXPR_LITERAL)
+ {
+ if (node->literal.type_kind == TOK_STRING)
+ {
+ return "string";
+ }
+ if (node->literal.type_kind == TOK_CHAR)
+ {
+ return "char";
+ }
+ if (node->literal.type_kind == 1)
+ {
+ return "double";
+ }
+ return "int";
+ }
+
+ return NULL;
+}
+
+// Extract variable names from argument string.
+char *extract_call_args(const char *args)
+{
+ if (!args || strlen(args) == 0)
+ {
+ return xstrdup("");
+ }
+ char *out = xmalloc(strlen(args) + 1);
+ out[0] = 0;
+
+ char *dup = xstrdup(args);
+ char *p = strtok(dup, ",");
+ while (p)
+ {
+ while (*p == ' ')
+ {
+ p++;
+ }
+ char *last_space = strrchr(p, ' ');
+ char *ptr_star = strrchr(p, '*');
+
+ char *name = p;
+ if (last_space)
+ {
+ name = last_space + 1;
+ }
+ if (ptr_star && ptr_star > last_space)
+ {
+ name = ptr_star + 1;
+ }
+
+ if (strlen(out) > 0)
+ {
+ strcat(out, ", ");
+ }
+ strcat(out, name);
+
+ p = strtok(NULL, ",");
+ }
+ free(dup);
+ return out;
+}
+
+// Parse original method name from mangled name.
+const char *parse_original_method_name(const char *mangled)
+{
+ const char *last = strrchr(mangled, '_');
+ return last ? last + 1 : mangled;
+}
+
+// Replace string type in arguments.
+char *replace_string_type(const char *args)
+{
+ if (!args)
+ {
+ return NULL;
+ }
+ char *res = xmalloc(strlen(args) * 2 + 1);
+ res[0] = 0;
+ const char *p = args;
+ while (*p)
+ {
+ const char *match = strstr(p, "string ");
+ if (match)
+ {
+ if (match > args && (isalnum(*(match - 1)) || *(match - 1) == '_'))
+ {
+ strncat(res, p, match - p + 6);
+ p = match + 6;
+ }
+ else
+ {
+ strncat(res, p, match - p);
+ strcat(res, "const char* ");
+ p = match + 7;
+ }
+ }
+ else
+ {
+ strcat(res, p);
+ break;
+ }
+ }
+ return res;
+}
+
+// Helper to emit auto type or fallback.
+void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out)
+{
+ char *inferred = NULL;
+ if (init_expr)
+ {
+ inferred = infer_type(ctx, init_expr);
+ }
+
+ if (inferred && strcmp(inferred, "__auto_type") != 0 && strcmp(inferred, "unknown") != 0)
+ {
+ fprintf(out, "%s", inferred);
+ }
+ else
+ {
+ if (strstr(g_config.cc, "tcc"))
+ {
+ zpanic_with_suggestion(t,
+ "Type inference failed for variable initialization and TCC does "
+ "not support __auto_type",
+ "Please specify the type explicitly: 'var x: Type = ...'");
+ }
+ else
+ {
+ fprintf(out, "__auto_type");
+ }
+ }
+}