diff options
| -rw-r--r-- | README.md | 27 | ||||
| -rw-r--r-- | src/codegen/codegen.c | 20 | ||||
| -rw-r--r-- | src/parser/parser_expr.c | 125 | ||||
| -rw-r--r-- | tests/features/test_tuples.zc | 29 |
4 files changed, 190 insertions, 11 deletions
@@ -172,11 +172,32 @@ var zeros: [int; 5]; // Zero-initialized ``` #### Tuples -Group multiple values together. +Group multiple values together, access elements by index. ```zc var pair = (1, "Hello"); -var x = pair.0; -var s = pair.1; +var x = pair.0; // 1 +var s = pair.1; // "Hello" +``` + +**Multiple Return Values** + +Functions can return tuples to provide multiple results: +```zc +fn add_and_subtract(a: int, b: int) -> (int, int) { + return (a + b, a - b); +} + +var result = add_and_subtract(3, 2); +var sum = result.0; // 5 +var diff = result.1; // 1 +``` + +**Destructuring** + +Tuples can be destructured directly into variables: +```zc +var (sum, diff) = add_and_subtract(3, 2); +// sum = 5, diff = 1 ``` #### Structs diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 22cbc15..95e0ccd 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -603,7 +603,15 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) emit_auto_type(ctx, node->member.target, node->token, out); fprintf(out, " _t = ("); codegen_expression(ctx, node->member.target, out); - fprintf(out, "); _t ? _t->%s : 0; })", node->member.field); + char *field = node->member.field; + if (field && field[0] >= '0' && field[0] <= '9') + { + fprintf(out, "); _t ? _t->v%s : 0; })", field); + } + else + { + fprintf(out, "); _t ? _t->%s : 0; })", field); + } } else { @@ -619,7 +627,15 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { free(lt); } - fprintf(out, "%s%s", actually_ptr ? "->" : ".", node->member.field); + char *field = node->member.field; + if (field && field[0] >= '0' && field[0] <= '9') + { + fprintf(out, "%sv%s", actually_ptr ? "->" : ".", field); + } + else + { + fprintf(out, "%s%s", actually_ptr ? "->" : ".", field); + } } break; case NODE_EXPR_INDEX: diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index bfbdb28..990942d 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -20,6 +20,7 @@ int is_struct_type(ParserContext *ctx, const char *type_name) } Type *get_field_type(ParserContext *ctx, Type *struct_type, const char *field_name); +char *infer_type(ParserContext *ctx, ASTNode *node); // from codegen int is_type_copy(ParserContext *ctx, Type *t) { @@ -2387,11 +2388,123 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) l->pos = saved; // Reset if not a cast ASTNode *expr = parse_expression(ctx, l); - if (lexer_next(l).type != TOK_RPAREN) + + // Check for tuple literal: (expr, expr, ...) + if (lexer_peek(l).type == TOK_COMMA) + { + // This is a tuple literal - collect all elements and infer types + ASTNode **elements = xmalloc(sizeof(ASTNode *) * 16); + char **type_strs = xmalloc(sizeof(char *) * 16); + int count = 0; + + // First element + elements[count] = expr; + char *t1 = infer_type(ctx, expr); + type_strs[count] = t1 ? t1 : xstrdup("int"); + count++; + + // Parse remaining elements + while (lexer_peek(l).type == TOK_COMMA) + { + lexer_next(l); // eat comma + ASTNode *elem = parse_expression(ctx, l); + elements[count] = elem; + char *ti = infer_type(ctx, elem); + type_strs[count] = ti ? ti : xstrdup("int"); + count++; + } + + if (lexer_next(l).type != TOK_RPAREN) + { + zpanic_at(lexer_peek(l), "Expected ) after tuple"); + } + + // Build tuple signature + char sig[512]; + sig[0] = 0; + for (int i = 0; i < count; i++) + { + if (i > 0) + { + strcat(sig, "_"); + } + strcat(sig, type_strs[i]); + } + + register_tuple(ctx, sig); + + char tuple_name[256]; + sprintf(tuple_name, "Tuple_%s", sig); + + char *code = xmalloc(4096); + sprintf(code, "(%s){", tuple_name); + + for (int i = 0; i < count; i++) + { + if (i > 0) + { + strcat(code, ", "); + } + + if (elements[i]->type == NODE_EXPR_LITERAL) + { + char buf[256]; + if (elements[i]->literal.type_kind == 0) // int + { + sprintf(buf, "%lld", elements[i]->literal.int_val); + } + else if (elements[i]->literal.type_kind == 1) // float + { + sprintf(buf, "%f", elements[i]->literal.float_val); + } + else if (elements[i]->literal.type_kind == 2) // string + { + sprintf(buf, "\"%s\"", elements[i]->literal.string_val); + } + else + { + sprintf(buf, "0"); + } + strcat(code, buf); + } + else if (elements[i]->type == NODE_EXPR_VAR) + { + strcat(code, elements[i]->var_ref.name); + } + else + { + // For complex expressions, we need a different approach + // For now, just put a placeholder - this won't work for all cases + // So it's a TODO... + strcat(code, "/* complex expr */0"); + } + } + strcat(code, "}"); + + node = ast_create(NODE_RAW_STMT); + node->raw_stmt.content = code; + + // Set type info + Type *tuple_type = type_new(TYPE_STRUCT); + tuple_type->name = xstrdup(tuple_name); + node->type_info = tuple_type; + + // Cleanup + free(elements); + for (int i = 0; i < count; i++) + { + free(type_strs[i]); + } + free(type_strs); + } + else { - zpanic_at(lexer_peek(l), "Expected )"); + if (lexer_next(l).type != TOK_RPAREN) + { + zpanic_at(lexer_peek(l), "Expected )"); + } + node = expr; } - node = expr; } else if (t.type == TOK_LBRACKET) @@ -3488,7 +3601,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) { lexer_next(l); Token field = lexer_next(l); - if (field.type != TOK_IDENT) + if (field.type != TOK_IDENT && field.type != TOK_INT) { zpanic_at(field, "Expected field name after ->"); break; @@ -3517,7 +3630,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) { lexer_next(l); Token field = lexer_next(l); - if (field.type != TOK_IDENT) + if (field.type != TOK_IDENT && field.type != TOK_INT) { zpanic_at(field, "Expected field name after ?."); break; @@ -4080,7 +4193,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) if (op.type == TOK_OP && is_token(op, ".")) { Token field = lexer_next(l); - if (field.type != TOK_IDENT) + if (field.type != TOK_IDENT && field.type != TOK_INT) { zpanic_at(field, "Expected field name after ."); break; diff --git a/tests/features/test_tuples.zc b/tests/features/test_tuples.zc new file mode 100644 index 0000000..f8ccb1f --- /dev/null +++ b/tests/features/test_tuples.zc @@ -0,0 +1,29 @@ +fn get_pair() -> (int, int) { + return (10, 20); +} + +fn main() { + // Inferred type tuple literal + var p = (1, 2); + assert(p.0 == 1, "Tuple access 0"); + assert(p.1 == 2, "Tuple access 1"); + + // Function returning tuple + var p2 = get_pair(); + assert(p2.0 == 10, "Tuple return 0"); + assert(p2.1 == 20, "Tuple return 1"); + + // Explicit type tuple + var p3: (int, int) = (5, 6); + assert(p3.0 == 5, "Explicit tuple 0"); + assert(p3.1 == 6, "Explicit tuple 1"); + + // Different types + var mixed: (int, string) = (10, "Hello"); + assert(mixed.0 == 10, "Mixed 0"); + + // Tuple destructuring + var (a, b) = get_pair(); + assert(a == 10, "Destructured a"); + assert(b == 20, "Destructured b"); +} |
