diff options
| -rw-r--r-- | src/ast/ast.h | 8 | ||||
| -rw-r--r-- | src/codegen/codegen.c | 34 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 12 | ||||
| -rw-r--r-- | src/parser/parser_utils.c | 114 | ||||
| -rw-r--r-- | tests/features/test_mixin_methods.zc | 33 |
5 files changed, 194 insertions, 7 deletions
diff --git a/src/ast/ast.h b/src/ast/ast.h index 3269e55..a528129 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -380,9 +380,11 @@ struct ASTNode int generic_param_count; // Number of generic parameters char *parent; int is_union; - int is_packed; // @packed attribute. - int align; // @align(N) attribute, 0 = default. - int is_incomplete; // Forward declaration (prototype) + int is_packed; // @packed attribute. + int align; // @align(N) attribute, 0 = default. + int is_incomplete; // Forward declaration (prototype) + char **used_structs; // Names of structs used/mixed-in + int used_struct_count; } strct; struct diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index f019a4b..25ae5b3 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -560,8 +560,38 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } else { - fprintf(out, "%s__%s(", base, method); - if (!strchr(type, '*')) + // Mixin Lookup Logic + char *call_base = base; + int need_cast = 0; + char mixin_func_name[128]; + sprintf(mixin_func_name, "%s__%s", base, method); + + if (!find_func(ctx, mixin_func_name)) + { + // 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); + if (find_func(ctx, mixin_check)) + { + call_base = def->strct.used_structs[k]; + need_cast = 1; + break; + } + } + } + } + + fprintf(out, "%s__%s(", call_base, method); + if (need_cast) + { + fprintf(out, "(%s*)%s", call_base, strchr(type, '*') ? "" : "&"); + } + else if (!strchr(type, '*')) { fprintf(out, "&"); } diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index c9b144d..ca8cd8f 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -3268,6 +3268,10 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) lexer_next(l); // eat { ASTNode *h = 0, *tl = 0; + // Temp storage for used structs + char **temp_used_structs = NULL; + int temp_used_count = 0; + while (1) { skip_comments(l); @@ -3308,6 +3312,12 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) if (def && def->type == NODE_STRUCT) { + if (!temp_used_structs) + { + temp_used_structs = xmalloc(sizeof(char *) * 8); + } + temp_used_structs[temp_used_count++] = xstrdup(use_name); + ASTNode *f = def->strct.fields; while (f) { @@ -3410,6 +3420,8 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) node->strct.generic_params = gps; node->strct.generic_param_count = gp_count; node->strct.is_union = is_union; + node->strct.used_structs = temp_used_structs; + node->strct.used_struct_count = temp_used_count; if (gp_count > 0) { diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c index d2214eb..87cf24e 100644 --- a/src/parser/parser_utils.c +++ b/src/parser/parser_utils.c @@ -2298,7 +2298,63 @@ char *rewrite_expr_methods(ParserContext *ctx, char *raw) } } - dest += sprintf(dest, "%s__%s(%s%s", ptr_check, method, is_ptr ? "" : "&", acc); + // Mixin Lookup Logic + char target_func[128]; + sprintf(target_func, "%s__%s", ptr_check, method); + + char *final_cast = NULL; + char *final_method = xstrdup(method); + char *final_struct = xstrdup(ptr_check); + + // Check if method exists on primary struct + if (!find_func(ctx, target_func)) + { + // Not found, check mixins + ASTNode *def = find_struct_def(ctx, ptr_check); + if (def && def->type == NODE_STRUCT && def->strct.used_structs) + { + for (int k = 0; k < def->strct.used_struct_count; k++) + { + char mixin_func[128]; + sprintf(mixin_func, "%s__%s", def->strct.used_structs[k], method); + if (find_func(ctx, mixin_func)) + { + // Found in mixin! + free(final_struct); + final_struct = xstrdup(def->strct.used_structs[k]); + + // Create cast string: (Mixin*) or (Mixin*)& + char cast_buf[128]; + if (is_ptr) + { + sprintf(cast_buf, "(%s*)", final_struct); + } + else + { + sprintf(cast_buf, "(%s*)&", final_struct); + } + final_cast = xstrdup(cast_buf); + break; + } + } + } + } + + if (final_cast) + { + // Mixin call: Foo__method((Foo*)&obj + dest += + sprintf(dest, "%s__%s(%s%s", final_struct, final_method, final_cast, acc); + free(final_cast); + } + else + { + // Standard call + dest += sprintf(dest, "%s__%s(%s%s", final_struct, final_method, + is_ptr ? "" : "&", acc); + } + free(final_struct); + free(final_method); int has_args = 0; while (*src && paren_depth > 0) @@ -2348,7 +2404,61 @@ char *rewrite_expr_methods(ParserContext *ctx, char *raw) *p = 0; } } - dest += sprintf(dest, "%s__%s(%s%s)", ptr_check, method, is_ptr ? "" : "&", acc); + // Mixin Lookup Logic (No Parens) + char target_func[128]; + sprintf(target_func, "%s__%s", ptr_check, method); + + char *final_cast = NULL; + char *final_method = xstrdup(method); + char *final_struct = xstrdup(ptr_check); + + // Check if method exists on primary struct + if (!find_func(ctx, target_func)) + { + // Not found, check mixins + ASTNode *def = find_struct_def(ctx, ptr_check); + if (def && def->type == NODE_STRUCT && def->strct.used_structs) + { + for (int k = 0; k < def->strct.used_struct_count; k++) + { + char mixin_func[128]; + sprintf(mixin_func, "%s__%s", def->strct.used_structs[k], method); + if (find_func(ctx, mixin_func)) + { + // Found in mixin! + free(final_struct); + final_struct = xstrdup(def->strct.used_structs[k]); + + // Create cast string: (Mixin*) or (Mixin*)& + char cast_buf[128]; + if (is_ptr) + { + sprintf(cast_buf, "(%s*)", final_struct); + } + else + { + sprintf(cast_buf, "(%s*)&", final_struct); + } + final_cast = xstrdup(cast_buf); + break; + } + } + } + } + + if (final_cast) + { + dest += + sprintf(dest, "%s__%s(%s%s)", final_struct, final_method, final_cast, acc); + free(final_cast); + } + else + { + dest += sprintf(dest, "%s__%s(%s%s)", final_struct, final_method, + is_ptr ? "" : "&", acc); + } + free(final_struct); + free(final_method); continue; } } diff --git a/tests/features/test_mixin_methods.zc b/tests/features/test_mixin_methods.zc new file mode 100644 index 0000000..0932429 --- /dev/null +++ b/tests/features/test_mixin_methods.zc @@ -0,0 +1,33 @@ + +struct Foo { + i: i32; +} + +impl Foo { + fn get_i(self) -> i32 { + return self.i; + } + + fn set_i(self, val: i32) { + self.i = val; + } +} + +struct Bar { + use Foo; + f: f32; +} + +test "test_mixin_methods" { + var b: Bar; + b.i = 42; + b.f = 3.14; + + var val = b.get_i(); + assert(val == 42, "Mixin method get_i() should return 42"); + + b.set_i(100); + assert(b.i == 100, "Mixin method set_i() should update i to 100"); + + "Mixin method dispatch OK"; +} |
