diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-11 15:11:00 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-11 15:11:00 +0000 |
| commit | 55247a3f12a9eee7ba3fd7ca6d8fcea7a82c20f3 (patch) | |
| tree | a2a71e2eb8ca0b2c483518c1902d89d18709c9ab /src/codegen/codegen_utils.c | |
| parent | 2e7abed7cfe84a2c0df371cde35f8f68cfdca16c (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.c | 499 |
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"); + } + } +} |
