diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-25 10:33:34 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-25 10:33:34 +0000 |
| commit | 17cc0542def402f5b0788ff9e8fe0538f81d5ddf (patch) | |
| tree | 58ef40e2974cb34cae20c7da96b7de9341988c4c /src/parser | |
| parent | 7813a12ed59274989ce954c931bf02ebea2a0104 (diff) | |
Fix for #114
Diffstat (limited to 'src/parser')
| -rw-r--r-- | src/parser/parser_expr.c | 229 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 182 | ||||
| -rw-r--r-- | src/parser/parser_struct.c | 64 |
3 files changed, 375 insertions, 100 deletions
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 == '=') { |
