summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-23 15:55:02 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-23 15:55:02 +0000
commitc735d4b45f2db336e2aff8d94da7a08bb1fad68f (patch)
tree16f079ddb7e400ddce4a83be47bed5cc6d46c884
parent8d0ea93a7220730ccce754429549fd63e4eeaa7c (diff)
Update for tuples.
-rw-r--r--README.md27
-rw-r--r--src/codegen/codegen.c20
-rw-r--r--src/parser/parser_expr.c125
-rw-r--r--tests/features/test_tuples.zc29
4 files changed, 190 insertions, 11 deletions
diff --git a/README.md b/README.md
index 2995ad4..e91e63c 100644
--- a/README.md
+++ b/README.md
@@ -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");
+}