diff --git a/libs/compiler/src/test/loops.rs b/libs/compiler/src/test/loops.rs index 0e8a838..c421a99 100644 --- a/libs/compiler/src/test/loops.rs +++ b/libs/compiler/src/test/loops.rs @@ -106,3 +106,44 @@ fn test_while_loop() -> anyhow::Result<()> { Ok(()) } + +#[test] +fn test_loop_continue() -> anyhow::Result<()> { + let compiled = compile! { + debug + " + let a = 0; + loop { + a = a + 1; + if (a < 5) { + continue; + } + break; + } + " + }; + + // Labels: L1 (start), L2 (end), L3 (if end) + assert_eq!( + compiled, + indoc! { + " + j main + main: + move r8 0 #a + L1: + add r1 r8 1 + move r8 r1 #a + slt r2 r8 5 + beq r2 0 L3 + j L1 + L3: + j L2 + j L1 + L2: + " + } + ); + + Ok(()) +} diff --git a/libs/compiler/src/v1.rs b/libs/compiler/src/v1.rs index e36da2c..7329091 100644 --- a/libs/compiler/src/v1.rs +++ b/libs/compiler/src/v1.rs @@ -77,7 +77,7 @@ pub struct Compiler<'a, W: std::io::Write> { config: CompilerConfig, temp_counter: usize, label_counter: usize, - loop_stack: Vec, // Stores the 'end' label of the current loops + loop_stack: Vec<(String, String)>, // Stores (start_label, end_label) } impl<'a, W: std::io::Write> Compiler<'a, W> { @@ -160,6 +160,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { self.expression_break()?; Ok(None) } + Expression::Continue => { + self.expression_continue()?; + Ok(None) + } Expression::DeviceDeclaration(expr_dev) => { self.expression_device(expr_dev)?; Ok(None) @@ -592,8 +596,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { let start_label = self.next_label_name(); let end_label = self.next_label_name(); - // Push end label to stack for 'break' - self.loop_stack.push(end_label.clone()); + // Push labels to stack for 'break' and 'continue' + self.loop_stack + .push((start_label.clone(), end_label.clone())); self.write_output(format!("{start_label}:"))?; @@ -617,8 +622,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { let start_label = self.next_label_name(); let end_label = self.next_label_name(); - // Push end label to stack for 'break' - self.loop_stack.push(end_label.clone()); + // Push labels to stack for 'break' and 'continue' + self.loop_stack + .push((start_label.clone(), end_label.clone())); self.write_output(format!("{start_label}:"))?; @@ -645,8 +651,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { } fn expression_break(&mut self) -> Result<(), Error> { - if let Some(label) = self.loop_stack.last() { - self.write_output(format!("j {label}"))?; + if let Some((_, end_label)) = self.loop_stack.last() { + self.write_output(format!("j {end_label}"))?; Ok(()) } else { // This is a semantic error, but for now we can return a generic error @@ -655,6 +661,15 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { } } + fn expression_continue(&mut self) -> Result<(), Error> { + if let Some((start_label, _)) = self.loop_stack.last() { + self.write_output(format!("j {start_label}"))?; + Ok(()) + } else { + Err(Error::Unknown("Continue statement outside of loop".into())) + } + } + /// Helper to resolve a location to a register string (e.g., "r0"). /// Note: This does not handle Stack locations automatically, as they require /// instruction emission to load. Use `compile_operand` for general handling. diff --git a/libs/parser/src/lib.rs b/libs/parser/src/lib.rs index 92c5a12..308a4d6 100644 --- a/libs/parser/src/lib.rs +++ b/libs/parser/src/lib.rs @@ -235,6 +235,16 @@ impl Parser { Expression::Break } + // match continue statements + TokenType::Keyword(Keyword::Continue) => { + // make sure the next token is a semi-colon + let next = token_from_option!(self.get_next()?); + if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) { + return Err(Error::UnexpectedToken(next.clone())); + } + Expression::Continue + } + // match syscalls with a `syscall` keyword TokenType::Identifier(ref id) if SysCall::is_syscall(id) => { Expression::Syscall(self.syscall()?) @@ -1192,3 +1202,4 @@ impl Parser { } } } + diff --git a/libs/parser/src/tree_node.rs b/libs/parser/src/tree_node.rs index b634beb..3e49e09 100644 --- a/libs/parser/src/tree_node.rs +++ b/libs/parser/src/tree_node.rs @@ -214,6 +214,7 @@ pub enum Expression { Binary(BinaryExpression), Block(BlockExpression), Break, + Continue, Declaration(String, Box), DeviceDeclaration(DeviceDeclarationExpression), Function(FunctionExpression), @@ -237,6 +238,7 @@ impl std::fmt::Display for Expression { Expression::Binary(e) => write!(f, "{}", e), Expression::Block(e) => write!(f, "{}", e), Expression::Break => write!(f, "break"), + Expression::Continue => write!(f, "continue"), Expression::Declaration(id, e) => write!(f, "(let {} = {})", id, e), Expression::DeviceDeclaration(e) => write!(f, "{}", e), Expression::Function(e) => write!(f, "{}", e), @@ -254,4 +256,3 @@ impl std::fmt::Display for Expression { } } } - diff --git a/libs/tokenizer/src/lib.rs b/libs/tokenizer/src/lib.rs index 4afb2e2..9e0bfb8 100644 --- a/libs/tokenizer/src/lib.rs +++ b/libs/tokenizer/src/lib.rs @@ -410,6 +410,7 @@ impl Tokenizer { "loop" if next_ws!() => keyword!(Loop), "break" if next_ws!() => keyword!(Break), "while" if next_ws!() => keyword!(While), + "continue" if next_ws!() => keyword!(Continue), // boolean literals "true" if next_ws!() => { diff --git a/libs/tokenizer/src/token.rs b/libs/tokenizer/src/token.rs index 5e7d597..bea72dc 100644 --- a/libs/tokenizer/src/token.rs +++ b/libs/tokenizer/src/token.rs @@ -210,6 +210,8 @@ impl Symbol { #[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)] pub enum Keyword { + /// Represents the `continue` keyword + Continue, /// Represents the `let` keyword Let, /// Represents the `fn` keyword