diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ast/ast.c | 59 | ||||
| -rw-r--r-- | src/ast/ast.h | 60 | ||||
| -rw-r--r-- | src/codegen/codegen.c | 381 | ||||
| -rw-r--r-- | src/codegen/codegen.h | 4 | ||||
| -rw-r--r-- | src/codegen/codegen_decl.c | 26 | ||||
| -rw-r--r-- | src/codegen/codegen_main.c | 35 | ||||
| -rw-r--r-- | src/codegen/codegen_stmt.c | 2 | ||||
| -rw-r--r-- | src/codegen/codegen_utils.c | 108 | ||||
| -rw-r--r-- | src/parser/parser.h | 3 | ||||
| -rw-r--r-- | src/parser/parser_decl.c | 6 | ||||
| -rw-r--r-- | src/parser/parser_expr.c | 67 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 303 | ||||
| -rw-r--r-- | src/parser/parser_struct.c | 133 | ||||
| -rw-r--r-- | src/parser/parser_type.c | 62 | ||||
| -rw-r--r-- | src/parser/parser_utils.c | 75 |
15 files changed, 1110 insertions, 214 deletions
diff --git a/src/ast/ast.c b/src/ast/ast.c index 439a9f5..28fe678 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -101,6 +101,9 @@ int is_integer_type(Type *t) t->kind == TYPE_ISIZE || t->kind == TYPE_BYTE || t->kind == TYPE_RUNE || t->kind == TYPE_UINT || t->kind == TYPE_I128 || t->kind == TYPE_U128 || t->kind == TYPE_BITINT || t->kind == TYPE_UBITINT || + t->kind == TYPE_C_INT || t->kind == TYPE_C_UINT || t->kind == TYPE_C_LONG || + t->kind == TYPE_C_ULONG || t->kind == TYPE_C_SHORT || t->kind == TYPE_C_USHORT || + t->kind == TYPE_C_CHAR || t->kind == TYPE_C_UCHAR || (t->kind == TYPE_STRUCT && t->name && (0 == strcmp(t->name, "int8_t") || 0 == strcmp(t->name, "uint8_t") || 0 == strcmp(t->name, "int16_t") || 0 == strcmp(t->name, "uint16_t") || @@ -259,6 +262,25 @@ static char *type_to_string_impl(Type *t) return xstrdup("int32_t"); case TYPE_UINT: return xstrdup("unsigned int"); + + // Portable C Types + case TYPE_C_INT: + return xstrdup("c_int"); + case TYPE_C_UINT: + return xstrdup("c_uint"); + case TYPE_C_LONG: + return xstrdup("c_long"); + case TYPE_C_ULONG: + return xstrdup("c_ulong"); + case TYPE_C_SHORT: + return xstrdup("c_short"); + case TYPE_C_USHORT: + return xstrdup("c_ushort"); + case TYPE_C_CHAR: + return xstrdup("c_char"); + case TYPE_C_UCHAR: + return xstrdup("c_uchar"); + case TYPE_INT: return xstrdup("int"); case TYPE_FLOAT: @@ -461,8 +483,29 @@ static char *type_to_c_string_impl(Type *t) return xstrdup("int32_t"); case TYPE_UINT: return xstrdup("unsigned int"); - case TYPE_INT: + + // Portable C Types (Map directly to C types) + case TYPE_C_INT: return xstrdup("int"); + case TYPE_C_UINT: + return xstrdup("unsigned int"); + case TYPE_C_LONG: + return xstrdup("long"); + case TYPE_C_ULONG: + return xstrdup("unsigned long"); + case TYPE_C_SHORT: + return xstrdup("short"); + case TYPE_C_USHORT: + return xstrdup("unsigned short"); + case TYPE_C_CHAR: + return xstrdup("char"); + case TYPE_C_UCHAR: + return xstrdup("unsigned char"); + + case TYPE_INT: + // 'int' in Zen C maps to 'i32' now for portability. + // FFI should use c_int. + return xstrdup("int32_t"); case TYPE_FLOAT: return xstrdup("float"); case TYPE_BITINT: @@ -519,8 +562,11 @@ static char *type_to_c_string_impl(Type *t) return res; } - char *res = xmalloc(strlen(inner) + 7); - sprintf(res, "Slice_%s", inner); + char *inner_zens = type_to_string(t->inner); + char *res = xmalloc(strlen(inner_zens) + 7); + sprintf(res, "Slice_%s", inner_zens); + free(inner_zens); + free(inner); return res; } @@ -561,7 +607,12 @@ static char *type_to_c_string_impl(Type *t) return xstrdup("z_closure_T"); case TYPE_GENERIC: - return xstrdup(t->name); + // Use type_to_string to get the mangled name (e.g. Option_int) instead of raw C string + // composition This ensures consistency with struct definitions. + { + char *s = type_to_string(t); + return s; + } case TYPE_ALIAS: return type_to_c_string(t->inner); diff --git a/src/ast/ast.h b/src/ast/ast.h index 71d9943..fa67043 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -28,30 +28,40 @@ typedef enum */ typedef enum { - TYPE_VOID, ///< `void` type. - TYPE_BOOL, ///< `bool` type. - TYPE_CHAR, ///< `char` type. - TYPE_STRING, ///< `string` type. - TYPE_U0, ///< `u0` type. - TYPE_I8, ///< `i8` type. - TYPE_U8, ///< `u8` type. - TYPE_I16, ///< `i16` type. - TYPE_U16, ///< `u16` type. - TYPE_I32, ///< `i32` type. - TYPE_U32, ///< `u32` type. - TYPE_I64, ///< `i64` type. - TYPE_U64, ///< `u64` type. - TYPE_I128, ///< `i128` type. - TYPE_U128, ///< `u128` type. - TYPE_F32, ///< `f32` type. - TYPE_F64, ///< `f64` type. - TYPE_INT, ///< `int` (alias, usually i32). - TYPE_FLOAT, ///< `float` (alias). - TYPE_USIZE, ///< `usize` (pointer size unsigned). - TYPE_ISIZE, ///< `isize` (pointer size signed). - TYPE_BYTE, ///< `byte`. - TYPE_RUNE, ///< `rune`. - TYPE_UINT, ///< `uint` (alias). + TYPE_VOID, ///< `void` type. + TYPE_BOOL, ///< `bool` type. + TYPE_CHAR, ///< `char` type. + TYPE_STRING, ///< `string` type. + TYPE_U0, ///< `u0` type. + TYPE_I8, ///< `i8` type. + TYPE_U8, ///< `u8` type. + TYPE_I16, ///< `i16` type. + TYPE_U16, ///< `u16` type. + TYPE_I32, ///< `i32` type. + TYPE_U32, ///< `u32` type. + TYPE_I64, ///< `i64` type. + TYPE_U64, ///< `u64` type. + TYPE_I128, ///< `i128` type. + TYPE_U128, ///< `u128` type. + TYPE_F32, ///< `f32` type. + TYPE_F64, ///< `f64` type. + TYPE_INT, ///< `int` (alias, usually i32). + TYPE_FLOAT, ///< `float` (alias). + TYPE_USIZE, ///< `usize` (pointer size unsigned). + TYPE_ISIZE, ///< `isize` (pointer size signed). + TYPE_BYTE, ///< `byte`. + TYPE_RUNE, ///< `rune`. + TYPE_UINT, ///< `uint` (alias). + // Portable C Types (FFI) + TYPE_C_INT, ///< `c_int` (int). + TYPE_C_UINT, ///< `c_uint` (unsigned int). + TYPE_C_LONG, ///< `c_long` (long). + TYPE_C_ULONG, ///< `c_ulong` (unsigned long). + TYPE_C_SHORT, ///< `c_short` (short). + TYPE_C_USHORT, ///< `c_ushort` (unsigned short). + TYPE_C_CHAR, ///< `c_char` (char). + TYPE_C_UCHAR, ///< `c_uchar` (unsigned char). + TYPE_STRUCT, ///< Struct type. TYPE_ENUM, ///< Enum type. TYPE_POINTER, ///< Pointer type (*). @@ -229,6 +239,8 @@ struct ASTNode int cuda_device; // @device -> __device__ int cuda_host; // @host -> __host__ + char **c_type_overrides; // @ctype("...") per parameter + Attribute *attributes; // Custom attributes } func; diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index a66f179..384820b 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -1,4 +1,3 @@ - #include "codegen.h" #include "zprep.h" #include "../constants.h" @@ -59,12 +58,58 @@ static void codegen_var_expr(ParserContext *ctx, ASTNode *node, FILE *out) if (node->var_ref.suggestion && !ctx->silent_warnings) { char msg[256]; - sprintf(msg, "Undefined variable '%s'", node->var_ref.name); char help[256]; - sprintf(help, "Did you mean '%s'?", node->var_ref.suggestion); + snprintf(msg, sizeof(msg), "Undefined variable '%s'", node->var_ref.name); + snprintf(help, sizeof(help), "Did you mean '%s'?", node->var_ref.suggestion); zwarn_at(node->token, "%s\n = help: %s", msg, help); } } + + // Check for static method call pattern: Type::method or Slice<T>::method + char *double_colon = strstr(node->var_ref.name, "::"); + if (double_colon) + { + // Extract type name and method name + int type_len = double_colon - node->var_ref.name; + char *type_name = xmalloc(type_len + 1); + strncpy(type_name, node->var_ref.name, type_len); + type_name[type_len] = 0; + + char *method_name = double_colon + 2; // Skip :: + + // Handle generic types: Slice<int> -> Slice_int + char mangled_type[256]; + if (strchr(type_name, '<')) + { + // Generic type - need to mangle it + char *lt = strchr(type_name, '<'); + char *gt = strchr(type_name, '>'); + + if (lt && gt) + { + // Extract base type and type argument + *lt = 0; + char *type_arg = lt + 1; + *gt = 0; + + sprintf(mangled_type, "%s_%s", type_name, type_arg); + } + else + { + strcpy(mangled_type, type_name); + } + } + else + { + strcpy(mangled_type, type_name); + } + + // Output as Type__method + fprintf(out, "%s__%s", mangled_type, method_name); + free(type_name); + return; + } + fprintf(out, "%s", node->var_ref.name); } @@ -146,7 +191,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) else if ((strcmp(node->binary.op, "==") == 0 || strcmp(node->binary.op, "!=") == 0)) { char *t1 = infer_type(ctx, node->binary.left); - int is_ptr = 0; if (t1) { @@ -161,19 +205,16 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } int resolved = 0; ASTNode *alias = global_user_structs; - if (alias) + while (alias) { - while (alias) + if (alias->type == NODE_TYPE_ALIAS && + strcmp(check, alias->type_alias.alias) == 0) { - if (alias->type == NODE_TYPE_ALIAS && - strcmp(check, alias->type_alias.alias) == 0) - { - check = alias->type_alias.original_type; - resolved = 1; - break; - } - alias = alias->next; + check = alias->type_alias.original_type; + resolved = 1; + break; } + alias = alias->next; } if (!resolved) { @@ -183,8 +224,8 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } int is_basic = IS_BASIC_TYPE(t1); - ASTNode *def = t1 ? find_struct_def(ctx, t1) : NULL; + if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic && !is_ptr) { @@ -239,8 +280,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) else if (t1 && (strcmp(t1, "string") == 0 || strcmp(t1, "char*") == 0 || strcmp(t1, "const char*") == 0)) { - // Check if comparing to NULL - don't use strcmp for NULL comparisons - char *t2 = infer_type(ctx, node->binary.right); int is_null_compare = 0; if (node->binary.right->type == NODE_EXPR_VAR && strcmp(node->binary.right->var_ref.name, "NULL") == 0) @@ -252,9 +291,28 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { is_null_compare = 1; } + else if (node->binary.right->type == NODE_EXPR_LITERAL && + node->binary.right->literal.type_kind == LITERAL_INT && + node->binary.right->literal.int_val == 0) + { + is_null_compare = 1; + } + else if (node->binary.left->type == NODE_EXPR_LITERAL && + node->binary.left->literal.type_kind == LITERAL_INT && + node->binary.left->literal.int_val == 0) + { + is_null_compare = 1; + } - if (!is_null_compare && strcmp(t1, "string") == 0 && t2 && - strcmp(t2, "string") == 0) + if (is_null_compare) + { + fprintf(out, "("); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, " %s ", node->binary.op); + codegen_expression(ctx, node->binary.right, out); + fprintf(out, ")"); + } + else { fprintf(out, "(strcmp("); codegen_expression(ctx, node->binary.left, out); @@ -269,19 +327,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, ") != 0)"); } } - else - { - // Direct pointer comparison - fprintf(out, "("); - codegen_expression(ctx, node->binary.left, out); - fprintf(out, " %s ", node->binary.op); - codegen_expression(ctx, node->binary.right, out); - fprintf(out, ")"); - } - if (t2) - { - free(t2); - } } else { @@ -291,6 +336,10 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) codegen_expression(ctx, node->binary.right, out); fprintf(out, ")"); } + if (t1) + { + free(t1); + } } else { @@ -354,7 +403,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) if (def && def->type == NODE_ENUM) { char mangled[256]; - sprintf(mangled, "%s_%s", target->var_ref.name, method); + snprintf(mangled, sizeof(mangled), "%s_%s", target->var_ref.name, method); FuncSig *sig = find_func(ctx, mangled); if (sig) { @@ -363,7 +412,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) int arg_idx = 0; while (arg) { - if (arg_idx > 0 && arg) + if (arg_idx > 0) { fprintf(out, ", "); } @@ -371,7 +420,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) Type *param_t = (arg_idx < sig->total_args) ? sig->arg_types[arg_idx] : NULL; - // Tuple Packing Logic if (param_t && param_t->kind == TYPE_STRUCT && strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 && node->call.arg_count > 1) @@ -389,7 +437,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) arg = arg->next; } fprintf(out, "}"); - break; // All args consumed + break; } codegen_expression(ctx, arg, out); @@ -409,7 +457,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) char *ptr = strchr(clean, '*'); if (ptr) { - *ptr = 0; + *ptr = '\0'; } char *base = clean; @@ -418,11 +466,43 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) base += 7; } + char *mangled_base = base; + char base_buf[256]; + + // Mangle generic types: Slice<int> -> Slice_int, Vec<Point> -> Vec_Point + char *lt = strchr(base, '<'); + if (lt) + { + char *gt = strchr(lt, '>'); + if (gt) + { + int prefix_len = lt - base; + int arg_len = gt - lt - 1; + snprintf(base_buf, 255, "%.*s_%.*s", prefix_len, base, arg_len, lt + 1); + mangled_base = base_buf; + } + } + if (!strchr(type, '*') && target->type == NODE_EXPR_CALL) { - fprintf(out, "({ %s _t = ", type); + char *type_mangled = type; + char type_buf[256]; + char *t_lt = strchr(type, '<'); + if (t_lt) + { + char *t_gt = strchr(t_lt, '>'); + if (t_gt) + { + int p_len = t_lt - type; + int a_len = t_gt - t_lt - 1; + snprintf(type_buf, 255, "%.*s_%.*s", p_len, type, a_len, t_lt + 1); + type_mangled = type_buf; + } + } + + fprintf(out, "({ %s _t = ", type_mangled); codegen_expression(ctx, target, out); - fprintf(out, "; %s__%s(&_t", base, method); + fprintf(out, "; %s__%s(&_t", mangled_base, method); ASTNode *arg = node->call.args; while (arg) { @@ -435,36 +515,34 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) else { // Mixin Lookup Logic - char *call_base = base; + char *call_base = mangled_base; + int need_cast = 0; char mixin_func_name[128]; - sprintf(mixin_func_name, "%s__%s", base, method); + snprintf(mixin_func_name, sizeof(mixin_func_name), "%s__%s", call_base, method); char *resolved_method_suffix = NULL; if (!find_func(ctx, mixin_func_name)) { - // Try resolving as a trait method: Struct__Trait_Method StructRef *ref = ctx->parsed_impls_list; while (ref) { - if (ref->node && ref->node->type == NODE_IMPL_TRAIT) + if (ref->node && ref->node->type == NODE_IMPL_TRAIT && + strcmp(ref->node->impl_trait.target_type, base) == 0) { - if (strcmp(ref->node->impl_trait.target_type, base) == 0) + char trait_mangled[256]; + snprintf(trait_mangled, sizeof(trait_mangled), "%s__%s_%s", base, + ref->node->impl_trait.trait_name, method); + if (find_func(ctx, trait_mangled)) { - char trait_mangled[256]; - sprintf(trait_mangled, "%s__%s_%s", base, - ref->node->impl_trait.trait_name, method); - if (find_func(ctx, trait_mangled)) - { - char *suffix = - xmalloc(strlen(ref->node->impl_trait.trait_name) + - strlen(method) + 2); - sprintf(suffix, "%s_%s", ref->node->impl_trait.trait_name, - method); - resolved_method_suffix = suffix; - break; - } + size_t suffix_len = strlen(ref->node->impl_trait.trait_name) + + strlen(method) + 2; + char *suffix = xmalloc(suffix_len); + snprintf(suffix, suffix_len, "%s_%s", + ref->node->impl_trait.trait_name, method); + resolved_method_suffix = suffix; + break; } } ref = ref->next; @@ -479,15 +557,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) if (it->impl_node && it->impl_node->type == NODE_IMPL_TRAIT) { tname = it->impl_node->impl_trait.trait_name; - } - if (tname) - { char trait_mangled[512]; - sprintf(trait_mangled, "%s__%s_%s", base, tname, method); + snprintf(trait_mangled, sizeof(trait_mangled), "%s__%s_%s", + base, tname, method); if (find_func(ctx, trait_mangled)) { - char *suffix = xmalloc(strlen(tname) + strlen(method) + 2); - sprintf(suffix, "%s_%s", tname, method); + size_t suffix_len = strlen(tname) + strlen(method) + 2; + char *suffix = xmalloc(suffix_len); + snprintf(suffix, suffix_len, "%s_%s", tname, method); resolved_method_suffix = suffix; break; } @@ -502,15 +579,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } else { - // Method not found on primary struct, check mixins ASTNode *def = find_struct_def(ctx, base); if (def && def->type == NODE_STRUCT && def->strct.used_structs) { for (int k = 0; k < def->strct.used_struct_count; k++) { char mixin_check[128]; - sprintf(mixin_check, "%s__%s", def->strct.used_structs[k], - method); + snprintf(mixin_check, sizeof(mixin_check), "%s__%s", + def->strct.used_structs[k], method); if (find_func(ctx, mixin_check)) { call_base = def->strct.used_structs[k]; @@ -540,11 +616,22 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) arg = arg->next; } fprintf(out, ")"); + + if (resolved_method_suffix) + { + free(resolved_method_suffix); + } } free(clean); + free(type); return; } + if (type) + { + free(type); + } } + if (node->call.callee->type == NODE_EXPR_VAR) { ASTNode *def = find_struct_def(ctx, node->call.callee->var_ref.name); @@ -600,26 +687,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) if (node->call.arg_names && node->call.callee->type == NODE_EXPR_VAR) { - char *fn_name = node->call.callee->var_ref.name; - FuncSig *sig = find_func(ctx, fn_name); - - if (sig && sig->arg_types) - { - for (int p = 0; p < sig->total_args; p++) - { - ASTNode *arg = node->call.args; - - for (int i = 0; i < node->call.arg_count && arg; i++, arg = arg->next) - { - if (node->call.arg_names[i] && p < node->call.arg_count) - { - - // For now, emit in order provided... - } - } - } - } - ASTNode *arg = node->call.args; int first = 1; while (arg) @@ -666,11 +733,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 && node->call.arg_count > 1) { - // Implicit Tuple Packing: - // Function expects 1 Tuple argument, but call has multiple args -> Pack - // them fprintf(out, "(%s){", param_t->name); - ASTNode *curr = arg; int first_field = 1; while (curr) @@ -685,8 +748,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } fprintf(out, "}"); handled = 1; - - // Advance main loop iterator to end arg = NULL; } } @@ -695,7 +756,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { if (arg == NULL) { - break; // Tuple packed all args + break; } } else @@ -720,16 +781,12 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) case NODE_EXPR_MEMBER: if (strcmp(node->member.field, "len") == 0) { - if (node->member.target->type_info) + if (node->member.target->type_info && + node->member.target->type_info->kind == TYPE_ARRAY && + node->member.target->type_info->array_size > 0) { - if (node->member.target->type_info->kind == TYPE_ARRAY) - { - if (node->member.target->type_info->array_size > 0) - { - fprintf(out, "%d", node->member.target->type_info->array_size); - break; - } - } + fprintf(out, "%d", node->member.target->type_info->array_size); + break; } } @@ -751,16 +808,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } else { - if (node->member.target->type == NODE_EXPR_CAST) - { - fprintf(out, "("); - } codegen_expression(ctx, node->member.target, out); - if (node->member.target->type == NODE_EXPR_CAST) - { - fprintf(out, ")"); - } - // Verify actual type instead of trusting is_pointer_access flag char *lt = infer_type(ctx, node->member.target); int actually_ptr = 0; if (lt && (lt[strlen(lt) - 1] == '*' || strstr(lt, "*"))) @@ -771,6 +819,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { free(lt); } + char *field = node->member.field; if (field && field[0] >= '0' && field[0] <= '9') { @@ -793,7 +842,8 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) is_slice_struct = 1; } } - if (node->index.array->resolved_type) + + if (!is_slice_struct && node->index.array->resolved_type) { if (strncmp(node->index.array->resolved_type, "Slice_", 6) == 0) { @@ -910,7 +960,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } else { - fprintf(out, "/* UNSAFE: Full Slice on unknown size */ 0; "); + fprintf(out, "0; "); } } @@ -926,6 +976,10 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, "(Slice_%s){ .data = _arr + _start, .len = _len, .cap = _len }; })", tname); } + if (tname && strcmp(tname, "unknown") != 0) + { + free(tname); + } break; } case NODE_BLOCK: @@ -1016,9 +1070,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) break; case NODE_PLUGIN: { - // Plugin registry - declare external plugins ZPlugin *found = zptr_find_plugin(node->plugin_stmt.plugin_name); - if (found) { ZApi api = {.filename = g_current_filename ? g_current_filename : "input.zc", @@ -1098,14 +1150,102 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) break; } case NODE_EXPR_CAST: - fprintf(out, "(%s)(", node->cast.target_type); + { + const char *t = node->cast.target_type; + const char *mapped = t; + if (strcmp(t, "c_int") == 0) + { + mapped = "int"; + } + else if (strcmp(t, "c_uint") == 0) + { + mapped = "unsigned int"; + } + else if (strcmp(t, "c_long") == 0) + { + mapped = "long"; + } + else if (strcmp(t, "c_ulong") == 0) + { + mapped = "unsigned long"; + } + else if (strcmp(t, "c_short") == 0) + { + mapped = "short"; + } + else if (strcmp(t, "c_ushort") == 0) + { + mapped = "unsigned short"; + } + else if (strcmp(t, "c_char") == 0) + { + mapped = "char"; + } + else if (strcmp(t, "c_uchar") == 0) + { + mapped = "unsigned char"; + } + else if (strcmp(t, "int") == 0) + { + mapped = "int32_t"; + } + else if (strcmp(t, "uint") == 0) + { + mapped = "unsigned int"; + } + + fprintf(out, "((%s)(", mapped); codegen_expression(ctx, node->cast.expr, out); - fprintf(out, ")"); + fprintf(out, "))"); break; + } case NODE_EXPR_SIZEOF: if (node->size_of.target_type) { - fprintf(out, "sizeof(%s)", node->size_of.target_type); + const char *t = node->size_of.target_type; + const char *mapped = t; + if (strcmp(t, "c_int") == 0) + { + mapped = "int"; + } + else if (strcmp(t, "c_uint") == 0) + { + mapped = "unsigned int"; + } + else if (strcmp(t, "c_long") == 0) + { + mapped = "long"; + } + else if (strcmp(t, "c_ulong") == 0) + { + mapped = "unsigned long"; + } + else if (strcmp(t, "c_short") == 0) + { + mapped = "short"; + } + else if (strcmp(t, "c_ushort") == 0) + { + mapped = "unsigned short"; + } + else if (strcmp(t, "c_char") == 0) + { + mapped = "char"; + } + else if (strcmp(t, "c_uchar") == 0) + { + mapped = "unsigned char"; + } + else if (strcmp(t, "int") == 0) + { + mapped = "int32_t"; // Strict mapping + } + else if (strcmp(t, "uint") == 0) + { + mapped = "unsigned int"; // uint alias + } + + fprintf(out, "sizeof(%s)", mapped); } else { @@ -1131,20 +1271,19 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { Type *t = node->reflection.target_type; if (node->reflection.kind == 0) - { // @type_name + { char *s = codegen_type_to_string(t); fprintf(out, "\"%s\"", s); free(s); } else - { // @fields + { if (t->kind != TYPE_STRUCT || !t->name) { fprintf(out, "((void*)0)"); break; } char *sname = t->name; - // Find definition ASTNode *def = find_struct_def(ctx, sname); if (!def) { diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h index b3e971d..89614f7 100644 --- a/src/codegen/codegen.h +++ b/src/codegen/codegen.h @@ -48,7 +48,7 @@ char *replace_string_type(const char *args); const char *parse_original_method_name(const char *mangled); void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out); char *codegen_type_to_string(Type *t); -void emit_func_signature(FILE *out, ASTNode *func, const char *name_override); +void emit_func_signature(ParserContext *ctx, FILE *out, ASTNode *func, const char *name_override); char *strip_template_suffix(const char *name); int emit_move_invalidation(ParserContext *ctx, ASTNode *node, FILE *out); void codegen_expression_with_move(ParserContext *ctx, ASTNode *node, FILE *out); @@ -66,7 +66,7 @@ void emit_trait_defs(ASTNode *node, FILE *out); void emit_enum_protos(ASTNode *node, FILE *out); void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out); void emit_lambda_defs(ParserContext *ctx, FILE *out); -void emit_protos(ASTNode *node, FILE *out); +void emit_protos(ParserContext *ctx, ASTNode *node, FILE *out); void emit_impl_vtables(ParserContext *ctx, FILE *out); /** diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index 0b78676..1623ffc 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -50,6 +50,7 @@ void emit_preamble(ParserContext *ctx, FILE *out) else { // Standard hosted preamble. + fputs("#define _GNU_SOURCE\n", out); fputs("#include <stdio.h>\n#include <stdlib.h>\n#include " "<stddef.h>\n#include <string.h>\n", out); @@ -698,7 +699,7 @@ void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out) } // Emit function prototypes -void emit_protos(ASTNode *node, FILE *out) +void emit_protos(ParserContext *ctx, ASTNode *node, FILE *out) { ASTNode *f = node; while (f) @@ -721,7 +722,7 @@ void emit_protos(ASTNode *node, FILE *out) } else { - emit_func_signature(out, f, NULL); + emit_func_signature(ctx, out, f, NULL); fprintf(out, ";\n"); } } @@ -799,7 +800,7 @@ void emit_protos(ASTNode *node, FILE *out) } else { - emit_func_signature(out, m, proto); + emit_func_signature(ctx, out, m, proto); fprintf(out, ";\n"); } @@ -1129,12 +1130,23 @@ void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes) 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, "_"); + char *current = s; + char *next_sep = strstr(current, "__"); int i = 0; - while (p) + while (current) { - fprintf(out, "%s v%d; ", p, i++); - p = strtok(NULL, "_"); + if (next_sep) + { + *next_sep = 0; + fprintf(out, "%s v%d; ", current, i++); + current = next_sep + 2; + next_sep = strstr(current, "__"); + } + else + { + fprintf(out, "%s v%d; ", current, i++); + break; + } } free(s); fprintf(out, "};\n"); diff --git a/src/codegen/codegen_main.c b/src/codegen/codegen_main.c index a140070..82fc3ce 100644 --- a/src/codegen/codegen_main.c +++ b/src/codegen/codegen_main.c @@ -448,6 +448,39 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out) emit_type_aliases(kids, out); // Emit local aliases (redundant but safe) emit_trait_defs(kids, out); + // Also emit traits from parsed_globals_list (from auto-imported files like std/mem.zc) + // but only if they weren't already emitted from kids + StructRef *trait_ref = ctx->parsed_globals_list; + while (trait_ref) + { + if (trait_ref->node && trait_ref->node->type == NODE_TRAIT) + { + // Check if this trait was already in kids (explicitly imported) + int already_in_kids = 0; + ASTNode *k = kids; + while (k) + { + if (k->type == NODE_TRAIT && k->trait.name && trait_ref->node->trait.name && + strcmp(k->trait.name, trait_ref->node->trait.name) == 0) + { + already_in_kids = 1; + break; + } + k = k->next; + } + + if (!already_in_kids) + { + // Create a temporary single-node list for emit_trait_defs + ASTNode *saved_next = trait_ref->node->next; + trait_ref->node->next = NULL; + emit_trait_defs(trait_ref->node, out); + trait_ref->node->next = saved_next; + } + } + trait_ref = trait_ref->next; + } + // Track emitted raw statements to prevent duplicates EmittedContent *emitted_raw = NULL; @@ -616,7 +649,7 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out) } } - emit_protos(merged_funcs, out); + emit_protos(ctx, merged_funcs, out); emit_impl_vtables(ctx, out); diff --git a/src/codegen/codegen_stmt.c b/src/codegen/codegen_stmt.c index 2f9a2ba..7828ecf 100644 --- a/src/codegen/codegen_stmt.c +++ b/src/codegen/codegen_stmt.c @@ -750,7 +750,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) { fprintf(out, "inline "); } - emit_func_signature(out, node, NULL); + emit_func_signature(ctx, out, node, NULL); fprintf(out, "\n"); fprintf(out, "{\n"); char *prev_ret = g_current_func_ret_type; diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index 391ebd3..92c5395 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -41,7 +41,7 @@ char *strip_template_suffix(const char *name) } // Helper to emit C declaration (handle arrays, function pointers correctly) -void emit_c_decl(FILE *out, const char *type_str, const char *name) +void emit_c_decl(ParserContext *ctx, FILE *out, const char *type_str, const char *name) { char *bracket = strchr(type_str, '['); char *generic = strchr(type_str, '<'); @@ -64,9 +64,34 @@ void emit_c_decl(FILE *out, const char *type_str, const char *name) } else if (generic && (!bracket || generic < bracket)) { - // Strip generic part for C output - int base_len = generic - type_str; - fprintf(out, "%.*s %s", base_len, type_str, name); + char mangled_candidate[256]; + char *gt = strchr(generic, '>'); + int success = 0; + + if (gt) + { + int base_len = generic - type_str; + int arg_len = gt - generic - 1; + + // Limit check + if (base_len + arg_len + 2 < 256) + { + snprintf(mangled_candidate, 256, "%.*s_%.*s", base_len, type_str, arg_len, + generic + 1); + + if (find_struct_def_codegen(ctx, mangled_candidate)) + { + fprintf(out, "%s %s", mangled_candidate, name); + success = 1; + } + } + } + + if (!success) + { + int base_len = generic - type_str; + fprintf(out, "%.*s %s", base_len, type_str, name); + } if (bracket) { @@ -87,8 +112,7 @@ void emit_c_decl(FILE *out, const char *type_str, const char *name) // 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; - emit_c_decl(out, type_str, var_name); + emit_c_decl(ctx, out, type_str, var_name); } // Find struct definition @@ -169,6 +193,7 @@ char *infer_type(ParserContext *ctx, ASTNode *node) { return NULL; } + if (node->resolved_type && strcmp(node->resolved_type, "unknown") != 0 && strcmp(node->resolved_type, "void*") != 0) { @@ -302,6 +327,65 @@ char *infer_type(ParserContext *ctx, ASTNode *node) return extracted; } } + + // Find the struct/enum definition and look for "Ok" or "val" + char *search_name = inner_type; + if (strncmp(search_name, "struct ", 7) == 0) + { + search_name += 7; + } + + ASTNode *def = find_struct_def_codegen(ctx, search_name); + if (!def) + { + // check enums list explicitly if not found in instantiated list + StructRef *er = ctx->parsed_enums_list; + while (er) + { + if (er->node && er->node->type == NODE_ENUM && + strcmp(er->node->enm.name, search_name) == 0) + { + def = er->node; + break; + } + er = er->next; + } + } + + if (def) + { + if (def->type == NODE_ENUM) + { + // Look for "Ok" variant + ASTNode *var = def->enm.variants; + while (var) + { + if (var->variant.name && strcmp(var->variant.name, "Ok") == 0) + { + if (var->variant.payload) + { + return codegen_type_to_string(var->variant.payload); + } + // Ok with no payload? Then it's void/u0. + return "void"; + } + var = var->next; + } + } + else if (def->type == NODE_STRUCT) + { + // Look for "val" field + ASTNode *field = def->strct.fields; + while (field) + { + if (field->field.name && strcmp(field->field.name, "val") == 0) + { + return xstrdup(field->field.type); + } + field = field->next; + } + } + } } } @@ -644,7 +728,7 @@ char *codegen_type_to_string(Type *t) } // Emit function signature using Type info for correct C codegen -void emit_func_signature(FILE *out, ASTNode *func, const char *name_override) +void emit_func_signature(ParserContext *ctx, FILE *out, ASTNode *func, const char *name_override) { if (!func || func->type != NODE_FUNCTION) { @@ -714,7 +798,12 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override) } char *type_str = NULL; - if (func->func.arg_types && func->func.arg_types[i]) + // Check for @ctype override first + if (func->func.c_type_overrides && func->func.c_type_overrides[i]) + { + type_str = xstrdup(func->func.c_type_overrides[i]); + } + else if (func->func.arg_types && func->func.arg_types[i]) { type_str = codegen_type_to_string(func->func.arg_types[i]); } @@ -724,13 +813,14 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override) } const char *name = ""; + if (func->func.param_names && func->func.param_names[i]) { name = func->func.param_names[i]; } // check if array type - emit_c_decl(out, type_str, name); + emit_c_decl(ctx, out, type_str, name); free(type_str); } if (func->func.is_varargs) diff --git a/src/parser/parser.h b/src/parser/parser.h index 262c359..23c2920 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -561,7 +561,8 @@ ASTNode *parse_arrow_lambda_multi(ParserContext *ctx, Lexer *l, char **param_nam * @brief Parses and converts arguments. */ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, int *count_out, - Type ***types_out, char ***names_out, int *is_varargs_out); + Type ***types_out, char ***names_out, int *is_varargs_out, + char ***ctype_overrides_out); /** * @brief Checks if a file has been imported. diff --git a/src/parser/parser_decl.c b/src/parser/parser_decl.c index c96ca36..93a124d 100644 --- a/src/parser/parser_decl.c +++ b/src/parser/parser_decl.c @@ -99,10 +99,11 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) int count; Type **arg_types; char **param_names; + char **ctype_overrides; int is_varargs = 0; - char *args = - parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, ¶m_names, &is_varargs); + char *args = parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, ¶m_names, + &is_varargs, &ctype_overrides); char *ret = "void"; Type *ret_type_obj = type_new(TYPE_VOID); @@ -191,6 +192,7 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) node->func.defaults = defaults; node->func.ret_type_info = ret_type_obj; node->func.is_varargs = is_varargs; + node->func.c_type_overrides = ctype_overrides; if (gen_param) { diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index 6156cc0..f27e2c3 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -370,8 +370,7 @@ static void check_format_string(ASTNode *call, Token t) if (spec == 'd' || spec == 'i' || spec == 'u' || spec == 'x' || spec == 'X' || spec == 'o') { - if (vt && vt->kind != TYPE_INT && vt->kind != TYPE_I64 && !type_is_unsigned(vt) && - vt->kind != TYPE_CHAR) + if (vt && !is_integer_type(vt)) { warn_format_string(t, arg_num, "integer", got_type); } @@ -1908,7 +1907,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) { Type *formal_type = parse_type_formal(ctx, l); concrete_types[arg_count] = type_to_string(formal_type); - unmangled_types[arg_count] = type_to_c_string(formal_type); + unmangled_types[arg_count] = type_to_string(formal_type); arg_count++; if (lexer_peek(l).type == TOK_COMMA) @@ -2944,7 +2943,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) { if (i > 0) { - strcat(sig, "_"); + strcat(sig, "__"); } strcat(sig, type_strs[i]); } @@ -4116,6 +4115,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) break; } ASTNode *node = ast_create(NODE_EXPR_MEMBER); + node->token = field; node->member.target = lhs; node->member.field = token_strdup(field); node->member.is_pointer_access = 1; @@ -4169,6 +4169,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) break; } ASTNode *node = ast_create(NODE_EXPR_MEMBER); + node->token = field; node->member.target = lhs; node->member.field = token_strdup(field); node->member.is_pointer_access = 2; @@ -4350,6 +4351,52 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) if (sig) { + // Check if this is a static method being called with dot operator + // Static methods don't have 'self' as first parameter + int is_static_method = 0; + if (sig->total_args == 0) + { + // No arguments at all - definitely static + is_static_method = 1; + } + else if (sig->arg_types[0]) + { + // Check if first parameter is a pointer to the struct type + // Instance methods have: fn method(self) where self is StructType* + // Static methods have: fn method(x: int, y: int) etc. + Type *first_param = sig->arg_types[0]; + + // If first param is not a pointer, it's likely static + // OR if it's a pointer but not to this struct type + if (first_param->kind != TYPE_POINTER) + { + is_static_method = 1; + } + else if (first_param->inner) + { + // Check if the inner type matches the struct + char *inner_name = NULL; + if (first_param->inner->kind == TYPE_STRUCT) + { + inner_name = first_param->inner->name; + } + + if (!inner_name || strcmp(inner_name, struct_name) != 0) + { + is_static_method = 1; + } + } + } + + if (is_static_method) + { + zpanic_at(lhs->token, + "Cannot call static method '%s' with dot operator\n" + " = help: Use '%s::%s(...)' instead of instance.%s(...)", + lhs->member.field, struct_name, lhs->member.field, + lhs->member.field); + } + resolved_name = xstrdup(mangled); resolved_sig = sig; @@ -4772,6 +4819,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) break; } ASTNode *node = ast_create(NODE_EXPR_MEMBER); + node->token = field; node->member.target = lhs; node->member.field = token_strdup(field); node->member.is_pointer_access = 0; @@ -5421,6 +5469,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) // This gives a warning as "unused" but it's needed for the rewrite. char *r_name = resolve_struct_name_from_type(ctx, rhs->type_info, &is_rhs_ptr, &r_alloc); + (void)r_name; if (r_alloc) { free(r_alloc); @@ -5643,7 +5692,17 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) char *t1 = type_to_string(lhs->type_info); char *t2 = type_to_string(rhs->type_info); // Skip type check if either operand is void* (escape hatch type) + // or if either operand is a generic type parameter (T, K, V, etc.) int skip_check = (strcmp(t1, "void*") == 0 || strcmp(t2, "void*") == 0); + if (lhs->type_info->kind == TYPE_GENERIC || rhs->type_info->kind == TYPE_GENERIC) + { + skip_check = 1; + } + // Also check if type name is a single uppercase letter (common generic param) + if ((strlen(t1) == 1 && isupper(t1[0])) || (strlen(t2) == 1 && isupper(t2[0]))) + { + skip_check = 1; + } // Allow comparing pointers/strings with integer literal 0 (NULL) if (!skip_check) diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index a471fe6..ae16243 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -14,6 +14,118 @@ char *curr_func_ret = NULL; char *run_comptime_block(ParserContext *ctx, Lexer *l); +extern char *g_current_filename; + +/** + * @brief Auto-imports std/slice.zc if not already imported. + * + * This is called when array iteration is detected in for-in loops, + * to ensure the Slice<T>, SliceIter<T>, and Option<T> templates are available. + */ +static void auto_import_std_slice(ParserContext *ctx) +{ + // Check if already imported via templates + GenericTemplate *t = ctx->templates; + while (t) + { + if (strcmp(t->name, "Slice") == 0) + { + return; // Already have the Slice template + } + t = t->next; + } + + // Try to find and import std/slice.zc + static const char *std_paths[] = {"std/slice.zc", "./std/slice.zc", NULL}; + static const char *system_paths[] = {"/usr/local/share/zenc", "/usr/share/zenc", NULL}; + + char resolved_path[1024]; + int found = 0; + + // First, try relative to current file + if (g_current_filename) + { + char *current_dir = xstrdup(g_current_filename); + char *last_slash = strrchr(current_dir, '/'); + if (last_slash) + { + *last_slash = 0; + snprintf(resolved_path, sizeof(resolved_path), "%s/std/slice.zc", current_dir); + if (access(resolved_path, R_OK) == 0) + { + found = 1; + } + } + free(current_dir); + } + + // Try relative paths + if (!found) + { + for (int i = 0; std_paths[i] && !found; i++) + { + if (access(std_paths[i], R_OK) == 0) + { + strncpy(resolved_path, std_paths[i], sizeof(resolved_path) - 1); + resolved_path[sizeof(resolved_path) - 1] = '\0'; + found = 1; + } + } + } + + // Try system paths + if (!found) + { + for (int i = 0; system_paths[i] && !found; i++) + { + snprintf(resolved_path, sizeof(resolved_path), "%s/std/slice.zc", system_paths[i]); + if (access(resolved_path, R_OK) == 0) + { + found = 1; + } + } + } + + if (!found) + { + return; // Could not find std/slice.zc, instantiate_generic will error + } + + // Canonicalize path + char *real_fn = realpath(resolved_path, NULL); + if (real_fn) + { + strncpy(resolved_path, real_fn, sizeof(resolved_path) - 1); + resolved_path[sizeof(resolved_path) - 1] = '\0'; + free(real_fn); + } + + // Check if already imported + if (is_file_imported(ctx, resolved_path)) + { + return; + } + mark_file_imported(ctx, resolved_path); + + // Load and parse the file + char *src = load_file(resolved_path); + if (!src) + { + return; // Could not load file + } + + Lexer i; + lexer_init(&i, src); + + // Save and restore filename context + char *saved_fn = g_current_filename; + g_current_filename = resolved_path; + + // Parse the slice module contents + parse_program_nodes(ctx, &i); + + g_current_filename = saved_fn; +} static void check_assignment_condition(ASTNode *cond) { @@ -730,7 +842,7 @@ ASTNode *parse_asm(ParserContext *ctx, Lexer *l) } } - // Parse clobbers (: "eax", "memory") + // Parse clobbers (: "eax", "memory" OR : clobber("eax"), clobber("memory")) char **clobbers = NULL; int num_clobbers = 0; @@ -753,17 +865,36 @@ ASTNode *parse_asm(ParserContext *ctx, Lexer *l) continue; } - if (t.type == TOK_STRING) + // check for clobber("...") + if (t.type == TOK_IDENT && strncmp(t.start, "clobber", 7) == 0) { - lexer_next(l); - // Extract string content - char *clob = xmalloc(t.len); - strncpy(clob, t.start + 1, t.len - 2); - clob[t.len - 2] = 0; - clobbers[num_clobbers++] = clob; + lexer_next(l); // eat clobber + if (lexer_peek(l).type != TOK_LPAREN) + { + zpanic_at(lexer_peek(l), "Expected ( after clobber"); + } + lexer_next(l); // eat ( + + Token clob = lexer_next(l); + if (clob.type != TOK_STRING) + { + zpanic_at(clob, "Expected string literal for clobber"); + } + + if (lexer_peek(l).type != TOK_RPAREN) + { + zpanic_at(lexer_peek(l), "Expected ) after clobber string"); + } + lexer_next(l); // eat ) + + char *c = xmalloc(clob.len); + strncpy(c, clob.start + 1, clob.len - 2); + c[clob.len - 2] = 0; + clobbers[num_clobbers++] = c; } else { + zpanic_at(t, "Expected 'clobber(\"...\")' in clobber list"); break; } } @@ -1133,6 +1264,7 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) ASTNode *obj_expr = start_expr; char *iter_method = "iterator"; + ASTNode *slice_decl = NULL; // Track if we need to add a slice declaration // Check for reference iteration: for x in &vec if (obj_expr->type == NODE_EXPR_UNARY && obj_expr->unary.op && @@ -1142,6 +1274,80 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) iter_method = "iter_ref"; } + // Check for array iteration: wrap with Slice<T>::from_array + if (obj_expr->type_info && obj_expr->type_info->kind == TYPE_ARRAY && + obj_expr->type_info->array_size > 0) + { + // Create a var decl for the slice + slice_decl = ast_create(NODE_VAR_DECL); + slice_decl->var_decl.name = xstrdup("__zc_arr_slice"); + + // Build type string: Slice<elem_type> + char *elem_type_str = type_to_string(obj_expr->type_info->inner); + char slice_type[256]; + sprintf(slice_type, "Slice<%s>", elem_type_str); + slice_decl->var_decl.type_str = xstrdup(slice_type); + + ASTNode *from_array_call = ast_create(NODE_EXPR_CALL); + ASTNode *static_method = ast_create(NODE_EXPR_VAR); + + // The function name for static methods is Type::method format + char func_name[512]; + snprintf(func_name, 511, "%s::from_array", slice_type); + static_method->var_ref.name = xstrdup(func_name); + + from_array_call->call.callee = static_method; + + // Create arguments + ASTNode *arr_addr = ast_create(NODE_EXPR_UNARY); + arr_addr->unary.op = xstrdup("&"); + arr_addr->unary.operand = obj_expr; + + ASTNode *arr_cast = ast_create(NODE_EXPR_CAST); + char cast_type[256]; + sprintf(cast_type, "%s*", elem_type_str); + arr_cast->cast.target_type = xstrdup(cast_type); + arr_cast->cast.expr = arr_addr; + + ASTNode *size_arg = ast_create(NODE_EXPR_LITERAL); + size_arg->literal.type_kind = LITERAL_INT; + size_arg->literal.int_val = obj_expr->type_info->array_size; + char size_buf[32]; + sprintf(size_buf, "%d", obj_expr->type_info->array_size); + size_arg->literal.string_val = xstrdup(size_buf); + + arr_cast->next = size_arg; + from_array_call->call.args = arr_cast; + from_array_call->call.arg_count = 2; + + slice_decl->var_decl.init_expr = from_array_call; + + // Manually trigger generic instantiation for Slice<T> + // This ensures that Slice_int, Slice_float, etc. structures are generated + // First, ensure std/slice.zc is imported (auto-import if needed) + auto_import_std_slice(ctx); + Token dummy_tok = {0}; + instantiate_generic(ctx, "Slice", elem_type_str, elem_type_str, dummy_tok); + + // Instantiate SliceIter and Option too for the loop logic + char iter_type[256]; + sprintf(iter_type, "SliceIter<%s>", elem_type_str); + instantiate_generic(ctx, "SliceIter", elem_type_str, elem_type_str, dummy_tok); + + char option_type[256]; + sprintf(option_type, "Option<%s>", elem_type_str); + instantiate_generic(ctx, "Option", elem_type_str, elem_type_str, dummy_tok); + + // Replace obj_expr with a reference to the slice variable + ASTNode *slice_ref = ast_create(NODE_EXPR_VAR); + slice_ref->var_ref.name = xstrdup("__zc_arr_slice"); + slice_ref->resolved_type = + xstrdup(slice_type); // Explicitly set type for codegen + obj_expr = slice_ref; + + free(elem_type_str); + } + // var __it = obj.iterator(); ASTNode *it_decl = ast_create(NODE_VAR_DECL); it_decl->var_decl.name = xstrdup("__it"); @@ -1182,6 +1388,34 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) stmts_tail = node; \ } + char *iter_type_ptr = NULL; + char *option_type_ptr = NULL; + + if (slice_decl) + { + char *slice_t = slice_decl->var_decl.type_str; + char *start = strchr(slice_t, '<'); + if (start) + { + char *end = strrchr(slice_t, '>'); + if (end) + { + int len = end - start - 1; + char *elem = xmalloc(len + 1); + strncpy(elem, start + 1, len); + elem[len] = 0; + + iter_type_ptr = xmalloc(256); + sprintf(iter_type_ptr, "SliceIter<%s>", elem); + + option_type_ptr = xmalloc(256); + sprintf(option_type_ptr, "Option<%s>", elem); + + free(elem); + } + } + } + // var __opt = __it.next(); ASTNode *opt_decl = ast_create(NODE_VAR_DECL); opt_decl->var_decl.name = xstrdup("__opt"); @@ -1192,6 +1426,10 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) ASTNode *memb_next = ast_create(NODE_EXPR_MEMBER); ASTNode *it_ref = ast_create(NODE_EXPR_VAR); it_ref->var_ref.name = xstrdup("__it"); + if (iter_type_ptr) + { + it_ref->resolved_type = xstrdup(iter_type_ptr); + } memb_next->member.target = it_ref; memb_next->member.field = xstrdup("next"); call_next->call.callee = memb_next; @@ -1204,15 +1442,22 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) ASTNode *memb_is_none = ast_create(NODE_EXPR_MEMBER); ASTNode *opt_ref1 = ast_create(NODE_EXPR_VAR); opt_ref1->var_ref.name = xstrdup("__opt"); + if (option_type_ptr) + { + opt_ref1->resolved_type = xstrdup(option_type_ptr); + } memb_is_none->member.target = opt_ref1; memb_is_none->member.field = xstrdup("is_none"); call_is_none->call.callee = memb_is_none; + call_is_none->call.args = NULL; + call_is_none->call.arg_count = 0; - ASTNode *break_stmt = ast_create(NODE_BREAK); - + // if (__opt.is_none()) break; ASTNode *if_break = ast_create(NODE_IF); if_break->if_stmt.condition = call_is_none; + ASTNode *break_stmt = ast_create(NODE_BREAK); if_break->if_stmt.then_body = break_stmt; + if_break->if_stmt.else_body = NULL; APPEND_STMT(if_break); // var <user_var> = __opt.unwrap(); @@ -1225,25 +1470,28 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) ASTNode *memb_unwrap = ast_create(NODE_EXPR_MEMBER); ASTNode *opt_ref2 = ast_create(NODE_EXPR_VAR); opt_ref2->var_ref.name = xstrdup("__opt"); + if (option_type_ptr) + { + opt_ref2->resolved_type = xstrdup(option_type_ptr); + } memb_unwrap->member.target = opt_ref2; memb_unwrap->member.field = xstrdup("unwrap"); call_unwrap->call.callee = memb_unwrap; + call_unwrap->call.args = NULL; + call_unwrap->call.arg_count = 0; user_var_decl->var_decl.init_expr = call_unwrap; APPEND_STMT(user_var_decl); - // User Body + // User body statements enter_scope(ctx); add_symbol(ctx, var_name, NULL, NULL); - ASTNode *user_body_node; - if (lexer_peek(l).type == TOK_LBRACE) + // Body block + ASTNode *stmt = parse_statement(ctx, l); + ASTNode *user_body_node = stmt; + if (stmt && stmt->type != NODE_BLOCK) { - user_body_node = parse_block(ctx, l); - } - else - { - ASTNode *stmt = parse_statement(ctx, l); ASTNode *blk = ast_create(NODE_BLOCK); blk->block.statements = stmt; user_body_node = blk; @@ -1256,10 +1504,21 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) loop_body->block.statements = stmts_head; while_loop->while_stmt.body = loop_body; - // Wrap entire thing in a block to scope _it + // Wrap entire thing in a block to scope __it (and __zc_arr_slice if present) ASTNode *outer_block = ast_create(NODE_BLOCK); - it_decl->next = while_loop; - outer_block->block.statements = it_decl; + if (slice_decl) + { + // Chain: slice_decl -> it_decl -> while_loop + slice_decl->next = it_decl; + it_decl->next = while_loop; + outer_block->block.statements = slice_decl; + } + else + { + // Chain: it_decl -> while_loop + it_decl->next = while_loop; + outer_block->block.statements = it_decl; + } return outer_block; } @@ -3179,7 +3438,7 @@ char *run_comptime_block(ParserContext *ctx, Lexer *l) ASTNode *fn = ref->node; if (fn && fn->type == NODE_FUNCTION && fn->func.is_comptime) { - emit_func_signature(f, fn, NULL); + emit_func_signature(ctx, f, fn, NULL); fprintf(f, ";\n"); codegen_node_single(ctx, fn, f); } diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c index 82dd346..0c984a6 100644 --- a/src/parser/parser_struct.c +++ b/src/parser/parser_struct.c @@ -12,6 +12,114 @@ #include "zprep_plugin.h" #include "../codegen/codegen.h" +extern char *g_current_filename; + +/** + * @brief Auto-imports std/mem.zc if not already imported. + * + * This is called when the Drop trait is used (impl Drop for X). + */ +static void auto_import_std_mem(ParserContext *ctx) +{ + // Check if Drop trait is already registered (means mem.zc was imported) + if (check_impl(ctx, "Drop", "__trait_marker__")) + { + // Check_impl returns 0 if not found, but we need a different check + // Let's check if we can find any indicator that mem.zc was loaded + } + + // Try to find and import std/mem.zc + static const char *std_paths[] = {"std/mem.zc", "./std/mem.zc", NULL}; + static const char *system_paths[] = {"/usr/local/share/zenc", "/usr/share/zenc", NULL}; + + char resolved_path[1024]; + int found = 0; + + // First, try relative to current file + if (g_current_filename) + { + char *current_dir = xstrdup(g_current_filename); + char *last_slash = strrchr(current_dir, '/'); + if (last_slash) + { + *last_slash = 0; + snprintf(resolved_path, sizeof(resolved_path), "%s/std/mem.zc", current_dir); + if (access(resolved_path, R_OK) == 0) + { + found = 1; + } + } + free(current_dir); + } + + // Try relative paths + if (!found) + { + for (int i = 0; std_paths[i] && !found; i++) + { + if (access(std_paths[i], R_OK) == 0) + { + strncpy(resolved_path, std_paths[i], sizeof(resolved_path) - 1); + resolved_path[sizeof(resolved_path) - 1] = '\0'; + found = 1; + } + } + } + + // Try system paths + if (!found) + { + for (int i = 0; system_paths[i] && !found; i++) + { + snprintf(resolved_path, sizeof(resolved_path), "%s/std/mem.zc", system_paths[i]); + if (access(resolved_path, R_OK) == 0) + { + found = 1; + } + } + } + + if (!found) + { + return; // Could not find std/mem.zc + } + + // Canonicalize path + char *real_fn = realpath(resolved_path, NULL); + if (real_fn) + { + strncpy(resolved_path, real_fn, sizeof(resolved_path) - 1); + resolved_path[sizeof(resolved_path) - 1] = '\0'; + free(real_fn); + } + + // Check if already imported + if (is_file_imported(ctx, resolved_path)) + { + return; + } + mark_file_imported(ctx, resolved_path); + + // Load and parse the file + char *src = load_file(resolved_path); + if (!src) + { + return; // Could not load file + } + + Lexer i; + lexer_init(&i, src); + + // Save and restore filename context + char *saved_fn = g_current_filename; + g_current_filename = resolved_path; + + // Parse the mem module contents + parse_program_nodes(ctx, &i); + + g_current_filename = saved_fn; +} + // Trait Parsing ASTNode *parse_trait(ParserContext *ctx, Lexer *l) { @@ -100,7 +208,7 @@ ASTNode *parse_trait(ParserContext *ctx, Lexer *l) char **param_names = NULL; int is_varargs = 0; char *args = parse_and_convert_args(ctx, l, &defaults, &arg_count, &arg_types, ¶m_names, - &is_varargs); + &is_varargs, NULL); char *ret = xstrdup("void"); if (lexer_peek(l).type == TOK_ARROW) @@ -149,6 +257,7 @@ ASTNode *parse_trait(ParserContext *ctx, Lexer *l) } register_trait(name); + add_to_global_list(ctx, n_node); // Track for codegen (VTable emission) return n_node; } @@ -206,6 +315,22 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l) register_generic(ctx, target_gen_param); } + // Check for common error: swapped Struct and Trait + // impl MyStruct for MyTrait (Wrong) vs impl MyTrait for MyStruct (Correct) + if (!is_trait(name1) && is_trait(name2)) + { + zpanic_at(t1, + "Incorrect usage of impl. Did you mean 'impl %s for %s'? Syntax is 'impl " + "<Trait> for <Struct>'", + name2, name1); + } + + // Auto-import std/mem.zc if implementing Drop, Copy, or Clone traits + if (strcmp(name1, "Drop") == 0 || strcmp(name1, "Copy") == 0 || strcmp(name1, "Clone") == 0) + { + auto_import_std_mem(ctx); + } + register_impl(ctx, name1, name2); // RAII: Check for "Drop" trait implementation @@ -748,7 +873,7 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union, int is_opaque) Token field_name = lexer_next(l); lexer_next(l); // eat : Type *ft = parse_type_formal(ctx, l); - char *field_type_str = type_to_string(ft); + char *field_type_str = type_to_c_string(ft); expect(l, TOK_SEMICOLON, "Expected ;"); ASTNode *nf = ast_create(NODE_FIELD); @@ -832,7 +957,7 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union, int is_opaque) Token f_name = lexer_next(l); expect(l, TOK_COLON, "Expected :"); Type *ft = parse_type_formal(ctx, l); - char *f_type = type_to_string(ft); + char *f_type = type_to_c_string(ft); ASTNode *f = ast_create(NODE_FIELD); f->field.name = token_strdup(f_name); @@ -1005,7 +1130,7 @@ ASTNode *parse_enum(ParserContext *ctx, Lexer *l) while (lexer_peek(l).type == TOK_COMMA) { lexer_next(l); // eat , - strcat(sig, "_"); + strcat(sig, "__"); Type *next_t = parse_type_obj(ctx, l); char *ns = type_to_string(next_t); if (strlen(sig) + strlen(ns) + 2 > 510) diff --git a/src/parser/parser_type.c b/src/parser/parser_type.c index 49e961c..fcbe12d 100644 --- a/src/parser/parser_type.c +++ b/src/parser/parser_type.c @@ -427,13 +427,13 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) if (strcmp(name, "uint") == 0) { free(name); - return type_new(TYPE_UINT); + return type_new(TYPE_U32); // Strict uint32_t } if (strcmp(name, "int") == 0) { free(name); - return type_new(TYPE_INT); + return type_new(TYPE_I32); // Strict int32_t } if (strcmp(name, "float") == 0) { @@ -467,23 +467,31 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) } if (strcmp(name, "long") == 0) { + zwarn_at(t, "'long' is treated as portable 'int64_t' in Zen C. Use 'c_long' for " + "platform-dependent C long."); free(name); return type_new(TYPE_I64); } if (strcmp(name, "short") == 0) { + zwarn_at(t, "'short' is treated as portable 'int16_t' in Zen C. Use 'c_short' for " + "platform-dependent C short."); free(name); return type_new(TYPE_I16); } if (strcmp(name, "unsigned") == 0) { + zwarn_at(t, "'unsigned' is treated as portable 'uint32_t' in Zen C. Use 'c_uint' for " + "platform-dependent C unsigned int."); free(name); - return type_new(TYPE_UINT); + return type_new(TYPE_U32); } if (strcmp(name, "signed") == 0) { + zwarn_at(t, "'signed' is treated as portable 'int32_t' in Zen C. Use 'c_int' for " + "platform-dependent C int."); free(name); - return type_new(TYPE_INT); + return type_new(TYPE_I32); } if (strcmp(name, "int8_t") == 0) { @@ -536,6 +544,48 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) return type_new(TYPE_ISIZE); } + // Portable C Types + if (strcmp(name, "c_int") == 0) + { + free(name); + return type_new(TYPE_C_INT); + } + if (strcmp(name, "c_uint") == 0) + { + free(name); + return type_new(TYPE_C_UINT); + } + if (strcmp(name, "c_long") == 0) + { + free(name); + return type_new(TYPE_C_LONG); + } + if (strcmp(name, "c_ulong") == 0) + { + free(name); + return type_new(TYPE_C_ULONG); + } + if (strcmp(name, "c_short") == 0) + { + free(name); + return type_new(TYPE_C_SHORT); + } + if (strcmp(name, "c_ushort") == 0) + { + free(name); + return type_new(TYPE_C_USHORT); + } + if (strcmp(name, "c_char") == 0) + { + free(name); + return type_new(TYPE_C_CHAR); + } + if (strcmp(name, "c_uchar") == 0) + { + free(name); + return type_new(TYPE_C_UCHAR); + } + // Relaxed Type Check: If explicit 'struct Name', trust the user. if (explicit_struct) { @@ -677,7 +727,7 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) zpanic_at(t, "Expected > after generic"); } - char *unmangled_arg = type_to_c_string(first_arg); + char *unmangled_arg = type_to_string(first_arg); int is_single_dep = 0; for (int k = 0; k < ctx->known_generics_count; ++k) @@ -791,7 +841,7 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) if (lexer_peek(l).type == TOK_COMMA) { lexer_next(l); - strcat(sig, "_"); + strcat(sig, "__"); } else { diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c index 48418b6..8ea2934 100644 --- a/src/parser/parser_utils.c +++ b/src/parser/parser_utils.c @@ -691,16 +691,22 @@ void register_tuple(ParserContext *ctx, const char *sig) s_def->strct.name = xstrdup(struct_name); char *s_sig = xstrdup(sig); - char *tok = strtok(s_sig, "_"); + char *current = s_sig; + char *next_sep = strstr(current, "__"); ASTNode *head = NULL, *tail = NULL; int i = 0; - while (tok) + while (current) { + if (next_sep) + { + *next_sep = 0; + } + ASTNode *f = ast_create(NODE_FIELD); char fname[32]; sprintf(fname, "v%d", i++); f->field.name = xstrdup(fname); - f->field.type = xstrdup(tok); + f->field.type = xstrdup(current); if (!head) { @@ -712,7 +718,15 @@ void register_tuple(ParserContext *ctx, const char *sig) } tail = f; - tok = strtok(NULL, "_"); + if (next_sep) + { + current = next_sep + 2; + next_sep = strstr(current, "__"); + } + else + { + break; + } } free(s_sig); s_def->strct.fields = head; @@ -3392,7 +3406,8 @@ char *consume_and_rewrite(ParserContext *ctx, Lexer *l) } char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, int *count_out, - Type ***types_out, char ***names_out, int *is_varargs_out) + Type ***types_out, char ***names_out, int *is_varargs_out, + char ***ctype_overrides_out) { Token t = lexer_next(l); if (t.type != TOK_LPAREN) @@ -3406,18 +3421,52 @@ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, char **defaults = xmalloc(sizeof(char *) * 16); Type **types = xmalloc(sizeof(Type *) * 16); char **names = xmalloc(sizeof(char *) * 16); + char **ctype_overrides = xmalloc(sizeof(char *) * 16); for (int i = 0; i < 16; i++) { defaults[i] = NULL; types[i] = NULL; names[i] = NULL; + ctype_overrides[i] = NULL; } if (lexer_peek(l).type != TOK_RPAREN) { while (1) { + // Check for @ctype("...") before parameter + char *ctype_override = NULL; + if (lexer_peek(l).type == TOK_AT) + { + lexer_next(l); // eat @ + Token attr = lexer_next(l); + if (attr.type == TOK_IDENT && attr.len == 5 && strncmp(attr.start, "ctype", 5) == 0) + { + if (lexer_next(l).type != TOK_LPAREN) + { + zpanic_at(lexer_peek(l), "Expected ( after @ctype"); + } + Token ctype_tok = lexer_next(l); + if (ctype_tok.type != TOK_STRING) + { + zpanic_at(ctype_tok, "@ctype requires a string argument"); + } + // Extract string content (strip quotes) + ctype_override = xmalloc(ctype_tok.len - 1); + strncpy(ctype_override, ctype_tok.start + 1, ctype_tok.len - 2); + ctype_override[ctype_tok.len - 2] = 0; + if (lexer_next(l).type != TOK_RPAREN) + { + zpanic_at(lexer_peek(l), "Expected ) after @ctype string"); + } + } + else + { + zpanic_at(attr, "Unknown parameter attribute @%.*s", attr.len, attr.start); + } + } + Token t = lexer_next(l); // Handle 'self' if (t.type == TOK_IDENT && strncmp(t.start, "self", 4) == 0 && t.len == 4) @@ -3470,6 +3519,7 @@ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, types[count] = type_new_ptr(type_new(TYPE_VOID)); add_symbol(ctx, "self", "void*", types[count]); } + ctype_overrides[count] = ctype_override; count++; } else @@ -3514,11 +3564,20 @@ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, } else { - strcat(buf, type_str); + // Use @ctype override if present + if (ctype_override) + { + strcat(buf, ctype_override); + } + else + { + strcat(buf, type_str); + } strcat(buf, " "); strcat(buf, name); } + ctype_overrides[count] = ctype_override; count++; if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) @@ -3593,6 +3652,10 @@ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, *count_out = count; *types_out = types; *names_out = names; + if (ctype_overrides_out) + { + *ctype_overrides_out = ctype_overrides; + } return buf; } |
