summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-25 10:33:34 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-25 10:33:34 +0000
commit17cc0542def402f5b0788ff9e8fe0538f81d5ddf (patch)
tree58ef40e2974cb34cae20c7da96b7de9341988c4c /src
parent7813a12ed59274989ce954c931bf02ebea2a0104 (diff)
Fix for #114
Diffstat (limited to 'src')
-rw-r--r--src/ast/ast.c5
-rw-r--r--src/ast/ast.h6
-rw-r--r--src/codegen/codegen.c98
-rw-r--r--src/codegen/codegen_stmt.c157
-rw-r--r--src/parser/parser_expr.c229
-rw-r--r--src/parser/parser_stmt.c182
-rw-r--r--src/parser/parser_struct.c64
7 files changed, 544 insertions, 197 deletions
diff --git a/src/ast/ast.c b/src/ast/ast.c
index 78d7efb..b20d9c2 100644
--- a/src/ast/ast.c
+++ b/src/ast/ast.c
@@ -129,6 +129,11 @@ int type_eq(Type *a, Type *b)
return 1;
}
+ if (a->kind == TYPE_UNKNOWN || b->kind == TYPE_UNKNOWN)
+ {
+ return 1;
+ }
+
// Lax integer matching (bool == int, char == i8, etc.).
if (is_integer_type(a) && is_integer_type(b))
{
diff --git a/src/ast/ast.h b/src/ast/ast.h
index 5bcc4d5..21dbdbf 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -315,9 +315,11 @@ struct ASTNode
struct
{
char *pattern;
- char *binding_name;
+ char **binding_names; // Multiple bindings
+ int binding_count; // Count
+ int *binding_refs; // Ref flags per binding
int is_destructuring;
- int is_ref; // New: Supports 'ref' binding (Some(ref x))
+ int is_ref; // Legacy single ref, I will remove it next.
ASTNode *guard;
ASTNode *body;
int is_default;
diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c
index f239bb6..b5aecfa 100644
--- a/src/codegen/codegen.c
+++ b/src/codegen/codegen.c
@@ -328,6 +328,61 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
}
+ // Check for Static Enum Variant Call: Enum.Variant(...)
+ if (target->type == NODE_EXPR_VAR)
+ {
+ ASTNode *def = find_struct_def(ctx, target->var_ref.name);
+ if (def && def->type == NODE_ENUM)
+ {
+ char mangled[256];
+ sprintf(mangled, "%s_%s", target->var_ref.name, method);
+ FuncSig *sig = find_func(ctx, mangled);
+ if (sig)
+ {
+ fprintf(out, "%s(", mangled);
+ ASTNode *arg = node->call.args;
+ int arg_idx = 0;
+ while (arg)
+ {
+ if (arg_idx > 0 && arg)
+ {
+ fprintf(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)
+ {
+ fprintf(out, "(%s){", param_t->name);
+ int first = 1;
+ while (arg)
+ {
+ if (!first)
+ {
+ fprintf(out, ", ");
+ }
+ first = 0;
+ codegen_expression(ctx, arg, out);
+ arg = arg->next;
+ }
+ fprintf(out, "}");
+ break; // All args consumed
+ }
+
+ codegen_expression(ctx, arg, out);
+ arg = arg->next;
+ arg_idx++;
+ }
+ fprintf(out, ")");
+ return;
+ }
+ }
+ }
+
char *type = infer_type(ctx, target);
if (type)
{
@@ -561,18 +616,55 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
free(inner);
handled = 1;
}
+ else if (param_t && param_t->kind == TYPE_STRUCT &&
+ 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)
+ {
+ if (!first_field)
+ {
+ fprintf(out, ", ");
+ }
+ first_field = 0;
+ codegen_expression(ctx, curr, out);
+ curr = curr->next;
+ }
+ fprintf(out, "}");
+ handled = 1;
+
+ // Advance main loop iterator to end
+ arg = NULL;
+ }
}
- if (!handled)
+ if (handled)
+ {
+ if (arg == NULL)
+ {
+ break; // Tuple packed all args
+ }
+ }
+ else
{
codegen_expression(ctx, arg, out);
}
- if (arg->next)
+ if (arg && arg->next)
{
fprintf(out, ", ");
}
- arg = arg->next;
+ if (arg)
+ {
+ arg = arg->next;
+ }
arg_idx++;
}
}
diff --git a/src/codegen/codegen_stmt.c b/src/codegen/codegen_stmt.c
index 406a6e5..55a4be2 100644
--- a/src/codegen/codegen_stmt.c
+++ b/src/codegen/codegen_stmt.c
@@ -325,152 +325,125 @@ void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, int us
emit_pattern_condition(ctx, c->match_case.pattern, id, has_ref_binding, out);
}
fprintf(out, ") { ");
- if (c->match_case.binding_name)
+ if (c->match_case.binding_count > 0)
{
- if (is_option)
+ for (int i = 0; i < c->match_case.binding_count; i++)
{
- if (strstr(g_config.cc, "tcc"))
+ char *bname = c->match_case.binding_names[i];
+ int is_r = c->match_case.binding_refs ? c->match_case.binding_refs[i] : 0;
+
+ if (is_option)
{
- if (c->match_case.is_ref)
+ if (strstr(g_config.cc, "tcc"))
{
- fprintf(out, "__typeof__(&_m_%d.val) %s = &_m_%d.val; ", id,
- c->match_case.binding_name, id);
+ if (is_r)
+ {
+ fprintf(out, "__typeof__(&_m_%d.val) %s = &_m_%d.val; ", id, bname, id);
+ }
+ else
+ {
+ fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id, bname, id);
+ }
}
else
{
- fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id,
- c->match_case.binding_name, id);
+ if (is_r)
+ {
+ fprintf(out, "ZC_AUTO %s = &_m_%d->val; ", bname, id);
+ }
+ else if (has_ref_binding)
+ {
+ fprintf(out, "ZC_AUTO %s = _m_%d->val; ", bname, id);
+ }
+ else
+ {
+ fprintf(out, "ZC_AUTO %s = _m_%d.val; ", bname, id);
+ }
}
}
- else
+ else if (is_result)
{
- if (c->match_case.is_ref)
+ char *field = "val";
+ if (strcmp(c->match_case.pattern, "Err") == 0)
{
- // _m is pointer when has_ref_binding, use ->
- fprintf(out, "ZC_AUTO %s = &_m_%d->val; ", c->match_case.binding_name, id);
+ field = "err";
}
- else if (has_ref_binding)
- {
- // _m is pointer, use -> but don't take address
- fprintf(out, "ZC_AUTO %s = _m_%d->val; ", c->match_case.binding_name, id);
- }
- else
- {
- // _m is value, use .
- fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id);
- }
- }
- }
- else if (is_result) // FIX: Changed 'if' to 'else if' to match original logic structure
- // if needed, but original code had implicit fallthrough checks? No,
- // checks match pattern.
- {
- if (strcmp(c->match_case.pattern, "Ok") == 0)
- {
+
if (strstr(g_config.cc, "tcc"))
{
- if (c->match_case.is_ref)
+ if (is_r)
{
- fprintf(out, "__typeof__(&_m_%d->val) %s = &_m_%d->val; ", id,
- c->match_case.binding_name, id);
+ fprintf(out, "__typeof__(&_m_%d->%s) %s = &_m_%d->%s; ", id, field,
+ bname, id, field);
}
else
{
- fprintf(out, "__typeof__(_m_%d->val) %s = _m_%d->val; ", id,
- c->match_case.binding_name, id);
+ fprintf(out, "__typeof__(_m_%d->%s) %s = _m_%d->%s; ", id, field, bname,
+ id, field);
}
}
else
{
- if (c->match_case.is_ref)
+ if (is_r)
{
- // _m is pointer when has_ref_binding, use ->
- fprintf(out, "ZC_AUTO %s = &_m_%d->val; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = &_m_%d->%s; ", bname, id, field);
}
else if (has_ref_binding)
{
- // _m is pointer, use -> but don't take address
- fprintf(out, "ZC_AUTO %s = _m_%d->val; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = _m_%d->%s; ", bname, id, field);
}
else
{
- // _m is value, use .
- fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = _m_%d.%s; ", bname, id, field);
}
}
}
else
{
- if (strstr(g_config.cc, "tcc"))
+ char *v = strrchr(c->match_case.pattern, '_');
+ if (v)
+ {
+ v++;
+ }
+ else
+ {
+ v = c->match_case.pattern;
+ }
+
+ if (c->match_case.binding_count > 1)
{
- if (c->match_case.is_ref)
+ // Tuple destructuring: data.Variant.vI
+ if (is_r)
{
- fprintf(out, "__typeof__(&_m_%d->err) %s = &_m_%d->err; ", id,
- c->match_case.binding_name, id);
+ fprintf(out, "ZC_AUTO %s = &_m_%d->data.%s.v%d; ", bname, id, v, i);
+ }
+ else if (has_ref_binding)
+ {
+ fprintf(out, "ZC_AUTO %s = _m_%d->data.%s.v%d; ", bname, id, v, i);
}
else
{
- fprintf(out, "__typeof__(_m_%d->err) %s = _m_%d->err; ", id,
- c->match_case.binding_name, id);
+ fprintf(out, "ZC_AUTO %s = _m_%d.data.%s.v%d; ", bname, id, v, i);
}
}
else
{
- if (c->match_case.is_ref)
+ // Single destructuring: data.Variant
+ if (is_r)
{
- // _m is pointer when has_ref_binding, use ->
- fprintf(out, "ZC_AUTO %s = &_m_%d->err; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = &_m_%d->data.%s; ", bname, id, v);
}
else if (has_ref_binding)
{
- // _m is pointer, use -> but don't take address
- fprintf(out, "ZC_AUTO %s = _m_%d->err; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = _m_%d->data.%s; ", bname, id, v);
}
else
{
- // _m is value, use .
- fprintf(out, "ZC_AUTO %s = _m_%d.err; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = _m_%d.data.%s; ", bname, id, v);
}
}
}
}
- else
- {
- char *f = strrchr(c->match_case.pattern, '_');
- if (f)
- {
- f++;
- }
- else
- {
- f = c->match_case.pattern;
- }
- // Generic struct destructuring (for example, MyStruct_Variant)
- // Assuming data union or accessible field.
- if (c->match_case.is_ref)
- {
- // _m is pointer when has_ref_binding, use ->
- fprintf(out, "ZC_AUTO %s = &_m_%d->data.%s; ", c->match_case.binding_name, id,
- f);
- }
- else if (has_ref_binding)
- {
- // _m is pointer, use -> but don't take address
- fprintf(out, "ZC_AUTO %s = _m_%d->data.%s; ", c->match_case.binding_name, id,
- f);
- }
- else
- {
- // _m is value, use .
- fprintf(out, "ZC_AUTO %s = _m_%d.data.%s; ", c->match_case.binding_name, id, f);
- }
- }
}
// Check if body is a string literal (should auto-print).
diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c
index 28c19ad..3d563e0 100644
--- a/src/parser/parser_expr.c
+++ b/src/parser/parser_expr.c
@@ -605,11 +605,17 @@ static void find_declared_vars(ASTNode *node, char ***decls, int *count)
(*count)++;
}
- if (node->type == NODE_MATCH_CASE && node->match_case.binding_name)
+ if (node->type == NODE_MATCH_CASE)
{
- *decls = xrealloc(*decls, sizeof(char *) * (*count + 1));
- (*decls)[*count] = xstrdup(node->match_case.binding_name);
- (*count)++;
+ if (node->match_case.binding_names)
+ {
+ for (int i = 0; i < node->match_case.binding_count; i++)
+ {
+ *decls = xrealloc(*decls, sizeof(char *) * (*count + 1));
+ (*decls)[*count] = xstrdup(node->match_case.binding_names[i]);
+ (*count)++;
+ }
+ }
}
switch (node->type)
@@ -1310,18 +1316,47 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l)
char *pattern = token_strdup(p);
int is_default = (strcmp(pattern, "_") == 0);
- char *binding = NULL;
- int is_destructure = 0;
- skip_comments(l);
+ // Handle Destructuring: Ok(v) or Rect(w, h)
+ char **bindings = NULL;
+ int *binding_refs = NULL;
+ int binding_count = 0;
+ int is_destructure = 0; // Initialize here
+
+ // Assuming pattern_count is 1 for now, or needs to be determined
+ // For single identifier patterns, pattern_count would be 1.
+ // This logic needs to be adjusted if `pattern_count` is not available or needs to be
+ // calculated. For now, assuming `pattern_count == 1` is implicitly true for single
+ // token patterns.
if (!is_default && lexer_peek(l).type == TOK_LPAREN)
{
- lexer_next(l);
- Token b = lexer_next(l);
- if (b.type != TOK_IDENT)
+ lexer_next(l); // eat (
+ bindings = xmalloc(sizeof(char *) * 8); // Initial capacity
+ binding_refs = xmalloc(sizeof(int) * 8); // unused but consistent
+
+ while (1)
{
- zpanic_at(b, "Expected binding name");
+ int is_r = 0;
+ if (lexer_peek(l).type == TOK_IDENT && lexer_peek(l).len == 3 &&
+ strncmp(lexer_peek(l).start, "ref", 3) == 0)
+ {
+ lexer_next(l); // eat ref
+ is_r = 1;
+ }
+ Token b = lexer_next(l);
+ if (b.type != TOK_IDENT)
+ {
+ zpanic_at(b, "Expected binding");
+ }
+ bindings[binding_count] = token_strdup(b);
+ binding_refs[binding_count] = is_r;
+ binding_count++;
+ if (lexer_peek(l).type == TOK_COMMA)
+ {
+ lexer_next(l);
+ continue;
+ }
+ break;
}
- binding = token_strdup(b);
if (lexer_next(l).type != TOK_RPAREN)
{
zpanic_at(lexer_peek(l), "Expected )");
@@ -1343,6 +1378,17 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l)
zpanic_at(lexer_peek(l), "Expected '=>'");
}
+ // Create scope for the case to hold the binding
+ enter_scope(ctx);
+ if (binding_count > 0)
+ {
+ for (int i = 0; i < binding_count; i++)
+ {
+ add_symbol(ctx, bindings[i], NULL,
+ NULL); // Let inference handle it or default to void*?
+ }
+ }
+
ASTNode *body;
skip_comments(l);
Token pk = lexer_peek(l);
@@ -1364,10 +1410,28 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l)
body = parse_expression(ctx, l);
}
+ exit_scope(ctx);
+
+ int any_ref = 0;
+ if (binding_refs)
+ {
+ for (int i = 0; i < binding_count; i++)
+ {
+ if (binding_refs[i])
+ {
+ any_ref = 1;
+ break;
+ }
+ }
+ }
+
ASTNode *c = ast_create(NODE_MATCH_CASE);
c->match_case.pattern = pattern;
- c->match_case.binding_name = binding;
+ c->match_case.binding_names = bindings; // New multi-binding field
+ c->match_case.binding_count = binding_count; // New binding count field
+ c->match_case.binding_refs = binding_refs;
c->match_case.is_destructuring = is_destructure;
+ c->match_case.is_ref = any_ref;
c->match_case.guard = guard;
c->match_case.body = body;
c->match_case.is_default = is_default;
@@ -4817,6 +4881,8 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
bin->binary.op = token_strdup(op);
}
+ fprintf(stderr, "DEBUG: Binary Loop Op: %s\n", bin->binary.op);
+
if (strcmp(bin->binary.op, "/") == 0 || strcmp(bin->binary.op, "%") == 0)
{
if (rhs->type == NODE_EXPR_LITERAL && rhs->literal.type_kind == LITERAL_INT &&
@@ -5216,9 +5282,68 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
}
}
+ fprintf(stderr, "DEBUG: Past Method Check Op=%s\n", bin->binary.op);
+
// Standard Type Checking (if no overload found)
if (lhs->type_info && rhs->type_info)
{
+ // Ensure type_info is set for variables (critical for inference)
+ if (lhs->type == NODE_EXPR_VAR && !lhs->type_info)
+ {
+ Symbol *s = find_symbol_entry(ctx, lhs->var_ref.name);
+ if (s)
+ {
+ lhs->type_info = s->type_info;
+ }
+ }
+ if (rhs->type == NODE_EXPR_VAR && !rhs->type_info)
+ {
+ Symbol *s = find_symbol_entry(ctx, rhs->var_ref.name);
+ if (s)
+ {
+ rhs->type_info = s->type_info;
+ }
+ }
+
+ // Backward Inference for Lambda Params
+ // LHS is Unknown Var, RHS is Known
+ if (lhs->type == NODE_EXPR_VAR && lhs->type_info &&
+ lhs->type_info->kind == TYPE_UNKNOWN && rhs->type_info &&
+ rhs->type_info->kind != TYPE_UNKNOWN)
+ {
+ // Infer LHS type from RHS
+ Symbol *sym = find_symbol_entry(ctx, lhs->var_ref.name);
+ if (sym)
+ {
+ // Update Symbol
+ sym->type_info = rhs->type_info;
+ sym->type_name = type_to_string(rhs->type_info);
+
+ // Update AST Node
+ lhs->type_info = rhs->type_info;
+ lhs->resolved_type = xstrdup(sym->type_name);
+ }
+ }
+
+ // RHS is Unknown Var, LHS is Known
+ if (rhs->type == NODE_EXPR_VAR && rhs->type_info &&
+ rhs->type_info->kind == TYPE_UNKNOWN && lhs->type_info &&
+ lhs->type_info->kind != TYPE_UNKNOWN)
+ {
+ // Infer RHS type from LHS
+ Symbol *sym = find_symbol_entry(ctx, rhs->var_ref.name);
+ if (sym)
+ {
+ // Update Symbol
+ sym->type_info = lhs->type_info;
+ sym->type_name = type_to_string(lhs->type_info);
+
+ // Update AST Node
+ rhs->type_info = lhs->type_info;
+ rhs->resolved_type = xstrdup(sym->type_name);
+ }
+ }
+
if (is_comparison_op(bin->binary.op))
{
bin->type_info = type_new(TYPE_INT); // bool
@@ -5250,52 +5375,6 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
if (!skip_check && !type_eq(lhs->type_info, rhs->type_info) &&
!(is_integer_type(lhs->type_info) && is_integer_type(rhs->type_info)))
{
- // Backward Inference for Lambda Params
- // LHS is Unknown Var, RHS is Known
- if (lhs->type == NODE_EXPR_VAR && lhs->type_info &&
- lhs->type_info->kind == TYPE_UNKNOWN && rhs->type_info &&
- rhs->type_info->kind != TYPE_UNKNOWN)
- {
- // Infer LHS type from RHS
- Symbol *sym = find_symbol_entry(ctx, lhs->var_ref.name);
- if (sym)
- {
- // Update Symbol
- sym->type_info = rhs->type_info;
- sym->type_name = type_to_string(rhs->type_info);
-
- // Update AST Node
- lhs->type_info = rhs->type_info;
- lhs->resolved_type = xstrdup(sym->type_name);
-
- // Re-check validity (optional, but good)
- bin->type_info = rhs->type_info;
- goto inference_success;
- }
- }
-
- // RHS is Unknown Var, LHS is Known
- if (rhs->type == NODE_EXPR_VAR && rhs->type_info &&
- rhs->type_info->kind == TYPE_UNKNOWN && lhs->type_info &&
- lhs->type_info->kind != TYPE_UNKNOWN)
- {
- // Infer RHS type from LHS
- Symbol *sym = find_symbol_entry(ctx, rhs->var_ref.name);
- if (sym)
- {
- // Update Symbol
- sym->type_info = lhs->type_info;
- sym->type_name = type_to_string(lhs->type_info);
-
- // Update AST Node
- rhs->type_info = lhs->type_info;
- rhs->resolved_type = xstrdup(sym->type_name);
-
- bin->type_info = lhs->type_info;
- goto inference_success;
- }
- }
-
char msg[256];
sprintf(msg, "Type mismatch in comparison: cannot compare '%s' and '%s'", t1,
t2);
@@ -5304,8 +5383,6 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
sprintf(suggestion, "Both operands must have compatible types for comparison");
zpanic_with_suggestion(op, msg, suggestion);
-
- inference_success:;
}
}
else
@@ -5457,11 +5534,11 @@ ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l, char *param_nam
// Default param type: unknown (to be inferred)
lambda->lambda.param_types = xmalloc(sizeof(char *));
- lambda->lambda.param_types[0] = xstrdup("unknown");
+ lambda->lambda.param_types[0] = NULL;
// Create Type Info: unknown -> unknown
Type *t = type_new(TYPE_FUNCTION);
- t->inner = type_new(TYPE_UNKNOWN); // Return
+ t->inner = type_new(TYPE_INT); // Return (default to int)
t->args = xmalloc(sizeof(Type *));
t->args[0] = type_new(TYPE_UNKNOWN); // Arg
t->arg_count = 1;
@@ -5469,7 +5546,7 @@ ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l, char *param_nam
// Register parameter in scope for body parsing
enter_scope(ctx);
- add_symbol(ctx, param_name, "unknown", t->args[0]);
+ add_symbol(ctx, param_name, NULL, t->args[0]);
// Body parsing...
ASTNode *body_block = NULL;
@@ -5495,6 +5572,10 @@ ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l, char *param_nam
ASTNode *ret_val = lambda->lambda.body->block.statements->ret.value;
if (ret_val->type_info && ret_val->type_info->kind != TYPE_UNKNOWN)
{
+ if (param_name[0] == 'x')
+ {
+ fprintf(stderr, "DEBUG: Updating return type to %d\n", ret_val->type_info->kind);
+ }
// Update return type
if (t->inner)
{
@@ -5502,6 +5583,14 @@ ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l, char *param_nam
}
t->inner = ret_val->type_info;
}
+ else
+ {
+ if (param_name[0] == 'x')
+ {
+ fprintf(stderr, "DEBUG: Return type unknown/null! ret=%p kind=%d\n",
+ ret_val->type_info, ret_val->type_info ? ret_val->type_info->kind : -1);
+ }
+ }
}
// Update parameter types from symbol table (in case inference happened)
@@ -5514,9 +5603,19 @@ ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l, char *param_nam
}
else
{
+ if (param_name[0] == 'x')
+ {
+ fprintf(stderr, "DEBUG: Fallback! sym=%p kind=%d\n", sym,
+ sym && sym->type_info ? sym->type_info->kind : -1);
+ }
// Fallback to int if still unknown
- free(lambda->lambda.param_types[0]);
+ if (lambda->lambda.param_types[0])
+ {
+ free(lambda->lambda.param_types[0]);
+ }
lambda->lambda.param_types[0] = xstrdup("int");
+ t->args[0] = type_new(TYPE_INT); // FIX: Update AST type info too!
+
// Update symbol to match fallback
if (sym)
{
diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c
index 2e3d6e2..3bead26 100644
--- a/src/parser/parser_stmt.c
+++ b/src/parser/parser_stmt.c
@@ -133,32 +133,48 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l)
char *pattern = xstrdup(patterns_buf);
int is_default = (strcmp(pattern, "_") == 0);
-
- char *binding = NULL;
int is_destructure = 0;
- int is_ref = 0;
+ // Handle Destructuring: Ok(v) or Rect(w, h)
+ char **bindings = NULL;
+ int *binding_refs = NULL;
+ int binding_count = 0;
- // Handle Destructuring: Ok(v)
- // (Only allowed if we matched a single pattern, e.g. "Result::Ok(val)")
if (!is_default && pattern_count == 1 && lexer_peek(l).type == TOK_LPAREN)
{
lexer_next(l); // eat (
- // Check for 'ref' keyword
- if (lexer_peek(l).type == TOK_IDENT && lexer_peek(l).len == 3 &&
- strncmp(lexer_peek(l).start, "ref", 3) == 0)
- {
- lexer_next(l); // eat 'ref'
- is_ref = 1;
- }
+ bindings = xmalloc(sizeof(char *) * 8); // hardcap at 8 for now or realloc
+ binding_refs = xmalloc(sizeof(int) * 8);
- Token b = lexer_next(l);
- if (b.type != TOK_IDENT)
+ while (1)
{
- zpanic_at(b, "Expected variable name in pattern");
+ int is_r = 0;
+ // Check for 'ref' keyword
+ if (lexer_peek(l).type == TOK_IDENT && lexer_peek(l).len == 3 &&
+ strncmp(lexer_peek(l).start, "ref", 3) == 0)
+ {
+ lexer_next(l); // eat 'ref'
+ is_r = 1;
+ }
+
+ Token b = lexer_next(l);
+ if (b.type != TOK_IDENT)
+ {
+ zpanic_at(b, "Expected variable name in pattern");
+ }
+ bindings[binding_count] = token_strdup(b);
+ binding_refs[binding_count] = is_r;
+ binding_count++;
+
+ if (lexer_peek(l).type == TOK_COMMA)
+ {
+ lexer_next(l);
+ continue;
+ }
+ break;
}
- binding = token_strdup(b);
+
if (lexer_next(l).type != TOK_RPAREN)
{
zpanic_at(lexer_peek(l), "Expected )");
@@ -166,7 +182,7 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l)
is_destructure = 1;
}
- // --- 3. Parse Guard (if condition) ---
+ // Parse Guard (if condition)
ASTNode *guard = NULL;
if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "if", 2) == 0)
{
@@ -182,18 +198,21 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l)
// Create scope for the case to hold the binding
enter_scope(ctx);
- if (binding)
+ if (binding_count > 0)
{
// Try to infer binding type from enum variant payload
- char *binding_type = is_ref ? "void*" : "unknown";
- Type *binding_type_info = NULL;
-
// Look up the enum variant to get its payload type
EnumVariantReg *vreg = find_enum_variant(ctx, pattern);
+
+ ASTNode *payload_node_field = NULL;
+ int is_tuple_payload = 0;
+ Type *payload_type = NULL;
+ ASTNode *enum_def = NULL;
+
if (vreg)
{
// Find the enum definition
- ASTNode *enum_def = find_struct_def(ctx, vreg->enum_name);
+ enum_def = find_struct_def(ctx, vreg->enum_name);
if (enum_def && enum_def->type == NODE_ENUM)
{
// Find the specific variant
@@ -207,25 +226,107 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l)
if (strcmp(v_full, pattern) == 0 && v->variant.payload)
{
// Found the variant, extract payload type
- binding_type_info = v->variant.payload;
- binding_type = type_to_string(v->variant.payload);
- if (is_ref)
+ payload_type = v->variant.payload;
+ if (payload_type && payload_type->kind == TYPE_STRUCT &&
+ strncmp(payload_type->name, "Tuple_", 6) == 0)
{
- // For ref bindings, make it a pointer to the payload type
- char *ptr_type = xmalloc(strlen(binding_type) + 2);
- sprintf(ptr_type, "%s*", binding_type);
- binding_type = ptr_type;
+ is_tuple_payload = 1;
+ ASTNode *tuple_def = find_struct_def(ctx, payload_type->name);
+ if (tuple_def)
+ {
+ payload_node_field = tuple_def->strct.fields;
+ }
}
free(v_full);
break;
}
- free(v_full);
v = v->next;
}
}
}
- add_symbol(ctx, binding, binding_type, binding_type_info);
+ for (int i = 0; i < binding_count; i++)
+ {
+ char *binding = bindings[i];
+ int is_ref = binding_refs[i];
+ char *binding_type = is_ref ? "void*" : "unknown";
+ Type *binding_type_info = NULL; // Default unknown
+
+ if (payload_type)
+ {
+ if (binding_count == 1 && !is_tuple_payload)
+ {
+ binding_type = type_to_string(payload_type);
+ binding_type_info = payload_type;
+ }
+ else if (binding_count == 1 && is_tuple_payload)
+ {
+ binding_type = type_to_string(payload_type);
+ binding_type_info = payload_type;
+ }
+ else if (binding_count > 1 && is_tuple_payload)
+ {
+ if (payload_node_field)
+ {
+ Lexer tmp;
+ lexer_init(&tmp, payload_node_field->field.type);
+ binding_type_info = parse_type_formal(ctx, &tmp);
+ binding_type = type_to_string(binding_type_info);
+ payload_node_field = payload_node_field->next;
+ }
+ }
+ }
+
+ if (is_ref && binding_type_info)
+ {
+ Type *ptr = type_new(TYPE_POINTER);
+ ptr->inner = binding_type_info;
+ binding_type_info = ptr;
+
+ char *ptr_s = xmalloc(strlen(binding_type) + 2);
+ sprintf(ptr_s, "%s*", binding_type);
+ binding_type = ptr_s;
+ }
+
+ int is_generic_unresolved = 0;
+
+ if (enum_def)
+ {
+ if (enum_def->enm.generic_param)
+ {
+ char *param = enum_def->enm.generic_param;
+ if (strstr(binding_type, param))
+ {
+ is_generic_unresolved = 1;
+ }
+ }
+ }
+
+ if (!is_generic_unresolved &&
+ (strcmp(binding_type, "T") == 0 || strcmp(binding_type, "T*") == 0))
+ {
+ is_generic_unresolved = 1;
+ }
+
+ if (is_generic_unresolved)
+ {
+ if (is_ref)
+ {
+ binding_type = "unknown*";
+ Type *u = type_new(TYPE_UNKNOWN);
+ Type *p = type_new(TYPE_POINTER);
+ p->inner = u;
+ binding_type_info = p;
+ }
+ else
+ {
+ binding_type = "unknown";
+ binding_type_info = type_new(TYPE_UNKNOWN);
+ }
+ }
+
+ add_symbol(ctx, binding, binding_type, binding_type_info);
+ }
}
ASTNode *body;
@@ -250,11 +351,26 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l)
exit_scope(ctx);
+ int any_ref = 0;
+ if (binding_refs)
+ {
+ for (int i = 0; i < binding_count; i++)
+ {
+ if (binding_refs[i])
+ {
+ any_ref = 1;
+ break;
+ }
+ }
+ }
+
ASTNode *c = ast_create(NODE_MATCH_CASE);
c->match_case.pattern = pattern;
- c->match_case.binding_name = binding;
+ c->match_case.binding_names = bindings;
+ c->match_case.binding_count = binding_count;
+ c->match_case.binding_refs = binding_refs;
c->match_case.is_destructuring = is_destructure;
- c->match_case.is_ref = is_ref; // Store is_ref flag
+ c->match_case.is_ref = any_ref;
c->match_case.guard = guard;
c->match_case.body = body;
c->match_case.is_default = is_default;
diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c
index 19bfd47..3e5c73d 100644
--- a/src/parser/parser_struct.c
+++ b/src/parser/parser_struct.c
@@ -925,8 +925,50 @@ ASTNode *parse_enum(ParserContext *ctx, Lexer *l)
Type *payload = NULL;
if (lexer_peek(l).type == TOK_LPAREN)
{
- lexer_next(l);
- payload = parse_type_obj(ctx, l);
+ lexer_next(l); // eat (
+ Type *first_t = parse_type_obj(ctx, l);
+
+ if (lexer_peek(l).type == TOK_COMMA)
+ {
+ // Multi-arg variant -> Tuple
+ char sig[512];
+ sig[0] = 0;
+
+ char *s = type_to_string(first_t);
+ if (strlen(s) > 250)
+ { // Safety check
+ zpanic_at(lexer_peek(l), "Type name too long for tuple generation");
+ }
+ strcpy(sig, s);
+ free(s);
+
+ while (lexer_peek(l).type == TOK_COMMA)
+ {
+ lexer_next(l); // eat ,
+ strcat(sig, "_");
+ Type *next_t = parse_type_obj(ctx, l);
+ char *ns = type_to_string(next_t);
+ if (strlen(sig) + strlen(ns) + 2 > 510)
+ {
+ zpanic_at(lexer_peek(l), "Tuple signature too long");
+ }
+ strcat(sig, ns);
+ free(ns);
+ }
+
+ register_tuple(ctx, sig);
+
+ char *tuple_name = xmalloc(strlen(sig) + 7);
+ sprintf(tuple_name, "Tuple_%s", sig);
+
+ payload = type_new(TYPE_STRUCT);
+ payload->name = tuple_name;
+ }
+ else
+ {
+ payload = first_t;
+ }
+
if (lexer_next(l).type != TOK_RPAREN)
{
zpanic_at(lexer_peek(l), "Expected )");
@@ -943,6 +985,24 @@ ASTNode *parse_enum(ParserContext *ctx, Lexer *l)
sprintf(mangled, "%s_%s", ename, vname);
register_enum_variant(ctx, ename, mangled, va->variant.tag_id);
+ // Register Constructor Function Signature
+ if (payload && !gp) // Only for non-generic enums for now
+ {
+ Type **at = xmalloc(sizeof(Type *));
+ at[0] = payload;
+ Type *ret_t = type_new(TYPE_ENUM);
+ ret_t->name = xstrdup(ename);
+
+ register_func(ctx, mangled, 1, NULL, at, ret_t, 0, 0, vt);
+ }
+ else if (!gp)
+ {
+ // No payload: fn Name() -> Enum
+ Type *ret_t = type_new(TYPE_ENUM);
+ ret_t->name = xstrdup(ename);
+ register_func(ctx, mangled, 0, NULL, NULL, ret_t, 0, 0, vt);
+ }
+
// Handle explicit assignment: Ok = 5
if (lexer_peek(l).type == TOK_OP && *lexer_peek(l).start == '=')
{