From 0b16cc451302c9811f5538a670964660ccae9a0c Mon Sep 17 00:00:00 2001 From: Toni Date: Thu, 28 May 2026 16:55:16 +0200 Subject: [PATCH] destructuring multiple returns --- src/codegen_x86_64.rs | 25 +++++++++++++++++++++++++ src/parser.rs | 23 ++++++++++++++++++++++- src/std/std.zr | 3 +++ src/tokenizer.rs | 2 ++ src/typechecker.rs | 22 +++++++++++++++++++++- 5 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index 0e6d2ad..425bcb5 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -275,6 +275,31 @@ _builtin_environ: _ => return error!(&op.loc, "invalid assignment target"), }; } + Stmt::Destructure { targets, op, value } => { + self.compile_expr(env, value)?; + + for (i, target) in targets.iter().enumerate() { + let reg = match i { + 0 => "rax", + 1 => "rdx", + _ => { + return error!( + &op.loc, + "destructuring more than 2 values not implemented yet" + ); + } + }; + + let offset = match env.get_var(&target.lexeme) { + Some(var) => var.stack_offset, + None => { + let types: Vec<&str> = self.expr_types[&value.id].split(',').collect(); + env.define_var(target.lexeme.clone(), types[i].to_string()) + } + }; + emit!(&mut self.output, " mov QWORD [rbp-{}], {}", offset, reg); + } + } Stmt::Const { name: _, value: _ } => { // handled in SymbolTable } diff --git a/src/parser.rs b/src/parser.rs index d6c09d1..9acefa5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,6 +1,10 @@ use std::sync::atomic::{AtomicUsize, Ordering}; -use crate::tokenizer::{Token, TokenType, ZernError, error}; +use crate::tokenizer::{ + Token, + TokenType::{self, Identifier}, + ZernError, error, +}; #[derive(Debug, Clone)] pub struct Param { @@ -27,6 +31,11 @@ pub enum Stmt { op: Token, value: Expr, }, + Destructure { + targets: Vec, + op: Token, + value: Expr, + }, Const { name: Token, value: Token, @@ -322,6 +331,18 @@ impl Parser { Ok(Stmt::Break) } else if self.match_token(&[TokenType::KeywordContinue]) { Ok(Stmt::Continue) + } else if self.match_token(&[TokenType::Tilde]) { + let mut targets = vec![]; + loop { + targets.push(self.consume(Identifier, "expected an identifier")?); + if !self.match_token(&[TokenType::Comma]) { + break; + } + } + let op = self.consume(TokenType::Colon, "expected ':'")?; + self.consume(TokenType::Equal, "expected '=' after ':'")?; + let value = self.expression()?; + Ok(Stmt::Destructure { targets, op, value }) } else { let expr = self.expression()?; if self.match_token(&[TokenType::Equal]) { diff --git a/src/std/std.zr b/src/std/std.zr index 35c7b59..331f408 100644 --- a/src/std/std.zr +++ b/src/std/std.zr @@ -123,6 +123,9 @@ func mem.free[x: any] : void return 0 blk := (x as ptr - MEM_BLOCK_SIZE) as mem.Block + if blk->free + return 0 + blk->free = true next := blk->next diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 5e74ce5..680fc8f 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -24,6 +24,7 @@ pub enum TokenType { ShiftLeft, ShiftRight, Arrow, + Tilde, Equal, DoubleEqual, @@ -159,6 +160,7 @@ impl Tokenizer { '%' => self.add_token(TokenType::Mod)?, '^' => self.add_token(TokenType::Xor)?, ':' => self.add_token(TokenType::Colon)?, + '~' => self.add_token(TokenType::Tilde)?, '-' => { if self.match_char('>') { self.add_token(TokenType::Arrow)? diff --git a/src/typechecker.rs b/src/typechecker.rs index 272c3b0..8559cf4 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -176,6 +176,26 @@ impl<'a> TypeChecker<'a> { _ => return error!(&op.loc, "invalid assignment target"), } } + Stmt::Destructure { targets, op, value } => { + let value_type = self.typecheck_expr(env, value)?; + let types: Vec<&str> = value_type.split(',').collect(); + if types.len() != targets.len() { + return error!( + &op.loc, + "destructure target count does not match return count" + ); + } + for (target, ty) in targets.iter().zip(types.iter()) { + match env.get_var_type(&target.lexeme) { + Some(existing) => { + expect_type!(ty.to_string(), *existing, target.loc); + } + None => { + env.define_var(target.lexeme.clone(), ty.to_string()); + } + } + } + } Stmt::Const { name: _, value: _ } => { // handled in SymbolTable } @@ -257,7 +277,7 @@ impl<'a> TypeChecker<'a> { if params[0].var_type.lexeme != "i64" { return error!( &name.loc, - "first parameter of the main function must be a i64" + "first parameter of the main function must be an i64" ); } if params[1].var_type.lexeme != "ptr" {