From c53a7cd63158324c30441b78aa93551e3a6817f3 Mon Sep 17 00:00:00 2001 From: Toni Date: Wed, 2 Jul 2025 13:56:30 +0200 Subject: [PATCH] break and continue --- .gitignore | 3 ++- examples/euler12.zr | 2 +- examples/euler7.zr | 2 +- examples/guess_number.zr | 2 +- src/codegen_x86_64.rs | 45 +++++++++++++++++++++++++++++----------- src/parser.rs | 6 ++++++ src/tokenizer.rs | 4 ++++ 7 files changed, 48 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 39c4888..0db1aa4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target /out* /TODO -/musl-* \ No newline at end of file +/musl-* +/vscode \ No newline at end of file diff --git a/examples/euler12.zr b/examples/euler12.zr index 53617d7..a5bd93e 100644 --- a/examples/euler12.zr +++ b/examples/euler12.zr @@ -17,5 +17,5 @@ func main[] : I64 n = n + i if num_divisors(n) > 500 print_i64(n) - return 0 + break i = i + 1 \ No newline at end of file diff --git a/examples/euler7.zr b/examples/euler7.zr index ec9e5ec..b4a4961 100644 --- a/examples/euler7.zr +++ b/examples/euler7.zr @@ -7,5 +7,5 @@ func main[] : I64 found = found + 1 if found == 10001 print_i64(i) - return 0 + break i = i + 1 \ No newline at end of file diff --git a/examples/guess_number.zr b/examples/guess_number.zr index bb2c007..f33c24a 100644 --- a/examples/guess_number.zr +++ b/examples/guess_number.zr @@ -7,7 +7,7 @@ func main[] : I64 if guess == answer print("You win!") - return 0 + break else if guess < answer print("Too low!") else diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index 1f48174..116d6e7 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -13,6 +13,8 @@ pub struct Var { pub struct Env { scopes: Vec>, next_offset: usize, + loop_begin_label: String, + loop_end_label: String, } impl Env { @@ -20,6 +22,8 @@ impl Env { Env { scopes: vec![HashMap::new()], next_offset: 8, + loop_begin_label: String::new(), + loop_end_label: String::new(), } } @@ -310,16 +314,21 @@ _builtin_array_free: emit!(&mut self.output, "{}:", end_label); } Stmt::While { condition, body } => { - let begin_label = self.label(); - let end_label = self.label(); + let old_loop_begin_label = env.loop_begin_label.clone(); + let old_loop_end_label = env.loop_end_label.clone(); + env.loop_begin_label = self.label(); + env.loop_end_label = self.label(); - emit!(&mut self.output, "{}:", begin_label); + emit!(&mut self.output, "{}:", env.loop_begin_label); self.compile_expr(env, condition)?; emit!(&mut self.output, " test rax, rax"); - emit!(&mut self.output, " je {}", end_label); + emit!(&mut self.output, " je {}", env.loop_end_label); self.compile_stmt(env, *body.clone())?; - emit!(&mut self.output, " jmp {}", begin_label); - emit!(&mut self.output, "{}:", end_label); + emit!(&mut self.output, " jmp {}", env.loop_begin_label); + emit!(&mut self.output, "{}:", env.loop_end_label); + + env.loop_begin_label = old_loop_begin_label; + env.loop_end_label = old_loop_end_label; } Stmt::Function { name, @@ -371,28 +380,40 @@ _builtin_array_free: end, body, } => { - let begin_label = self.label(); - let end_label = self.label(); + let old_loop_begin_label = env.loop_begin_label.clone(); + let old_loop_end_label = env.loop_end_label.clone(); + env.loop_begin_label = self.label(); + env.loop_end_label = self.label(); env.push_scope(); let offset = env.define_var(var.lexeme, "I64".into()); self.compile_expr(env, start)?; emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset); - emit!(&mut self.output, "{}:", begin_label); + emit!(&mut self.output, "{}:", env.loop_begin_label); emit!(&mut self.output, " mov rax, QWORD [rbp-{}]", offset); emit!(&mut self.output, " push rax"); self.compile_expr(env, end)?; emit!(&mut self.output, " pop rcx"); emit!(&mut self.output, " cmp rcx, rax"); - emit!(&mut self.output, " jge {}", end_label); + emit!(&mut self.output, " jge {}", env.loop_end_label); self.compile_stmt(env, *body)?; emit!(&mut self.output, " mov rax, QWORD [rbp-{}]", offset); emit!(&mut self.output, " add rax, 1"); emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset); - emit!(&mut self.output, " jmp {}", begin_label); - emit!(&mut self.output, "{}:", end_label); + emit!(&mut self.output, " jmp {}", env.loop_begin_label); + emit!(&mut self.output, "{}:", env.loop_end_label); env.pop_scope(); + + env.loop_begin_label = old_loop_begin_label; + env.loop_end_label = old_loop_end_label; + } + Stmt::Break => { + emit!(&mut self.output, " jmp {}", env.loop_end_label); + } + Stmt::Continue => { + // TODO: skips incrementing when used in a for loop + emit!(&mut self.output, " jmp {}", env.loop_begin_label); } } Ok(()) diff --git a/src/parser.rs b/src/parser.rs index b400878..27359f6 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -37,6 +37,8 @@ pub enum Stmt { body: Box, }, Return(Expr), + Break, + Continue, } #[derive(Debug, Clone)] @@ -193,6 +195,10 @@ impl Parser { self.for_statement() } else if self.match_token(&[TokenType::KeywordReturn]) { Ok(Stmt::Return(self.expression()?)) + } else if self.match_token(&[TokenType::KeywordBreak]) { + Ok(Stmt::Break) + } else if self.match_token(&[TokenType::KeywordContinue]) { + Ok(Stmt::Continue) } else { Ok(Stmt::Expression(self.expression()?)) } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index b3dd527..7791385 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -43,6 +43,8 @@ pub enum TokenType { KeywordIn, KeywordFunc, KeywordReturn, + KeywordBreak, + KeywordContinue, Indent, Dedent, @@ -335,6 +337,8 @@ impl Tokenizer { "in" => TokenType::KeywordIn, "func" => TokenType::KeywordFunc, "return" => TokenType::KeywordReturn, + "break" => TokenType::KeywordBreak, + "continue" => TokenType::KeywordContinue, "true" => TokenType::True, "false" => TokenType::False, _ => TokenType::Identifier,