summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-20 12:06:28 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-20 12:06:28 +0000
commite5d8c4219cfe5629a3ce4dbff01406a1817a788f (patch)
treec600b4f6713877b748dbf677a405dc1fbafae8df
parentdb690b368f7e05b242f2e775f620f35ab0df5bc3 (diff)
Reference binding...
-rw-r--r--README.md20
-rw-r--r--src/ast/ast.h1
-rw-r--r--src/codegen/codegen.c84
-rw-r--r--src/parser/parser_core.c4
-rw-r--r--src/parser/parser_stmt.c20
-rw-r--r--tests/features/test_match_ref.zc17
6 files changed, 128 insertions, 18 deletions
diff --git a/README.md b/README.md
index cdba0f3..c09161d 100644
--- a/README.md
+++ b/README.md
@@ -273,6 +273,21 @@ match shape {
Rect(w, h) => print(f"Area: {w*h}"),
Point => print("Point")
}
+
+#### Reference Binding
+To inspect a value without taking ownership (moving it), use the `ref` keyword in the pattern. This is essential for types that implement Move Semantics (e.g. `Option`, `Result`, non-Copy structs).
+
+```zc
+var opt = Some(NonCopyVal{...});
+match opt {
+ Some(ref x) => {
+ // 'x' is a pointer to the value inside 'opt'
+ // 'opt' is NOT moved/consumed here
+ print(x.field);
+ },
+ None => {}
+}
+```
```
#### Loops
@@ -418,7 +433,10 @@ autofree var types = malloc(1024);
Zen C prevents double-free errors by enforcing **Move Semantics** for non-trivial types (structs) by default.
- **Move by Default**: Assigning a struct variable transfers ownership. The original variable becomes invalid.
-- **Copy Trait**: Implement the `Copy` trait to opt-out of move semantics for simple types (e.g. `Point`).
+- **Management Strategies**:
+ - **`Copy` Trait**: Opt-out of move semantics for simple types.
+ - **`ref` Binding**: Use `match val { Some(ref x) => ... }` to inspect without moving.
+ - **Smart Derives**: `@derive(Eq)` uses references for comparison to avoid moves.
```zc
struct Mover { val: int; }
diff --git a/src/ast/ast.h b/src/ast/ast.h
index 2233b09..508a247 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -303,6 +303,7 @@ struct ASTNode
char *pattern;
char *binding_name;
int is_destructuring;
+ int is_ref; // New: Supports 'ref' binding (Some(ref x))
ASTNode *guard;
ASTNode *body;
int is_default;
diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c
index b25c79c..32fbdcf 100644
--- a/src/codegen/codegen.c
+++ b/src/codegen/codegen.c
@@ -246,38 +246,89 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out,
{
if (strstr(g_config.cc, "tcc"))
{
- fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id,
- c->match_case.binding_name, id);
+ if (c->match_case.is_ref)
+ {
+ fprintf(out, "__typeof__(&_m_%d.val) %s = &_m_%d.val; ", id,
+ c->match_case.binding_name, id);
+ }
+ else
+ {
+ fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id,
+ c->match_case.binding_name, id);
+ }
}
else
{
- fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id);
+ if (c->match_case.is_ref)
+ {
+ fprintf(out, "ZC_AUTO %s = &_m_%d.val; ", c->match_case.binding_name, id);
+ }
+ else
+ {
+ fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id);
+ }
}
}
- if (is_result)
+ 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"))
{
- fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id,
- c->match_case.binding_name, id);
+ if (c->match_case.is_ref)
+ {
+ fprintf(out, "__typeof__(&_m_%d.val) %s = &_m_%d.val; ", id,
+ c->match_case.binding_name, id);
+ }
+ else
+ {
+ fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id,
+ c->match_case.binding_name, id);
+ }
}
else
{
- fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id);
+ if (c->match_case.is_ref)
+ {
+ fprintf(out, "ZC_AUTO %s = &_m_%d.val; ", c->match_case.binding_name,
+ id);
+ }
+ else
+ {
+ fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name,
+ id);
+ }
}
}
else
{
if (strstr(g_config.cc, "tcc"))
{
- fprintf(out, "__typeof__(_m_%d.err) %s = _m_%d.err; ", id,
- c->match_case.binding_name, id);
+ if (c->match_case.is_ref)
+ {
+ fprintf(out, "__typeof__(&_m_%d.err) %s = &_m_%d.err; ", id,
+ c->match_case.binding_name, id);
+ }
+ else
+ {
+ fprintf(out, "__typeof__(_m_%d.err) %s = _m_%d.err; ", id,
+ c->match_case.binding_name, id);
+ }
}
else
{
- fprintf(out, "ZC_AUTO %s = _m_%d.err; ", c->match_case.binding_name, id);
+ if (c->match_case.is_ref)
+ {
+ fprintf(out, "ZC_AUTO %s = &_m_%d.err; ", c->match_case.binding_name,
+ id);
+ }
+ else
+ {
+ fprintf(out, "ZC_AUTO %s = _m_%d.err; ", c->match_case.binding_name,
+ id);
+ }
}
}
}
@@ -292,7 +343,18 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out,
{
f = c->match_case.pattern;
}
- fprintf(out, "ZC_AUTO %s = _m_%d.data.%s; ", c->match_case.binding_name, id, f);
+ // Generic struct destructuring (e.g. MyStruct_Variant)
+ // Assuming data union or accessible field.
+ // Original: _m_%d.data.%s
+ if (c->match_case.is_ref)
+ {
+ fprintf(out, "ZC_AUTO %s = &_m_%d.data.%s; ", c->match_case.binding_name, id,
+ f);
+ }
+ else
+ {
+ fprintf(out, "ZC_AUTO %s = _m_%d.data.%s; ", c->match_case.binding_name, id, f);
+ }
}
}
diff --git a/src/parser/parser_core.c b/src/parser/parser_core.c
index acab268..3c2805c 100644
--- a/src/parser/parser_core.c
+++ b/src/parser/parser_core.c
@@ -591,8 +591,8 @@ static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, char *
else if (fdef && fdef->type == NODE_STRUCT)
{
// Struct field: use _eq function, pass addresses
- // self.field is L-value, other.field is L-value (auto-deref from pointer)
- // We need addresses of them: &self.field, &other.field
+ // self.field is L-value, other.field is L-value (auto-deref from
+ // pointer) We need addresses of them: &self.field, &other.field
sprintf(cmp, "%s__eq(&self.%s, &other.%s)", ft, fn, fn);
}
else
diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c
index fc34a21..7433525 100644
--- a/src/parser/parser_stmt.c
+++ b/src/parser/parser_stmt.c
@@ -217,11 +217,11 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l)
break;
}
- // --- 1. Parse Patterns (with OR and range support) ---
+ // Parse Patterns (with OR and range support)
// Patterns can be:
// - Single value: 1
- // - OR patterns: 1 || 2 or 1 or 2
- // - Range patterns: 1..5 or 1..=5
+ // - OR patterns: 1 || 2 or 1 or 2 or 1, 2
+ // - Range patterns: 1..5 or 1..=5 or 1..<5
char patterns_buf[1024];
patterns_buf[0] = 0;
int pattern_count = 0;
@@ -292,11 +292,22 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l)
char *binding = NULL;
int is_destructure = 0;
- // --- 2. Handle Destructuring: Ok(v) ---
+ int is_ref = 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;
+ }
+
Token b = lexer_next(l);
if (b.type != TOK_IDENT)
{
@@ -348,6 +359,7 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l)
c->match_case.pattern = pattern;
c->match_case.binding_name = binding;
c->match_case.is_destructuring = is_destructure;
+ c->match_case.is_ref = is_ref; // Store is_ref flag
c->match_case.guard = guard;
c->match_case.body = body;
c->match_case.is_default = is_default;
diff --git a/tests/features/test_match_ref.zc b/tests/features/test_match_ref.zc
new file mode 100644
index 0000000..9734ffe
--- /dev/null
+++ b/tests/features/test_match_ref.zc
@@ -0,0 +1,17 @@
+
+enum MyOption<T> {
+ Some(T),
+ None
+}
+
+test "match_ref_int" {
+ var r = MyOption<int>::Some(42);
+ match r {
+ MyOption::Some(ref i) => {
+ // i is int*
+ assert(*i == 42, "int ref check failed");
+ *i = 100;
+ },
+ MyOption::None => assert(false, "fail")
+ }
+}