continue statements now working

This commit is contained in:
2025-11-25 01:47:49 -07:00
parent 05b16d59a4
commit 81f412453b
6 changed files with 79 additions and 8 deletions

View File

@@ -106,3 +106,44 @@ fn test_while_loop() -> anyhow::Result<()> {
Ok(()) 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(())
}

View File

@@ -77,7 +77,7 @@ pub struct Compiler<'a, W: std::io::Write> {
config: CompilerConfig, config: CompilerConfig,
temp_counter: usize, temp_counter: usize,
label_counter: usize, label_counter: usize,
loop_stack: Vec<String>, // 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> { 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()?; self.expression_break()?;
Ok(None) Ok(None)
} }
Expression::Continue => {
self.expression_continue()?;
Ok(None)
}
Expression::DeviceDeclaration(expr_dev) => { Expression::DeviceDeclaration(expr_dev) => {
self.expression_device(expr_dev)?; self.expression_device(expr_dev)?;
Ok(None) Ok(None)
@@ -592,8 +596,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
let start_label = self.next_label_name(); let start_label = self.next_label_name();
let end_label = self.next_label_name(); let end_label = self.next_label_name();
// Push end label to stack for 'break' // Push labels to stack for 'break' and 'continue'
self.loop_stack.push(end_label.clone()); self.loop_stack
.push((start_label.clone(), end_label.clone()));
self.write_output(format!("{start_label}:"))?; 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 start_label = self.next_label_name();
let end_label = self.next_label_name(); let end_label = self.next_label_name();
// Push end label to stack for 'break' // Push labels to stack for 'break' and 'continue'
self.loop_stack.push(end_label.clone()); self.loop_stack
.push((start_label.clone(), end_label.clone()));
self.write_output(format!("{start_label}:"))?; 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> { fn expression_break(&mut self) -> Result<(), Error> {
if let Some(label) = self.loop_stack.last() { if let Some((_, end_label)) = self.loop_stack.last() {
self.write_output(format!("j {label}"))?; self.write_output(format!("j {end_label}"))?;
Ok(()) Ok(())
} else { } else {
// This is a semantic error, but for now we can return a generic error // 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"). /// Helper to resolve a location to a register string (e.g., "r0").
/// Note: This does not handle Stack locations automatically, as they require /// Note: This does not handle Stack locations automatically, as they require
/// instruction emission to load. Use `compile_operand` for general handling. /// instruction emission to load. Use `compile_operand` for general handling.

View File

@@ -235,6 +235,16 @@ impl Parser {
Expression::Break 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 // match syscalls with a `syscall` keyword
TokenType::Identifier(ref id) if SysCall::is_syscall(id) => { TokenType::Identifier(ref id) if SysCall::is_syscall(id) => {
Expression::Syscall(self.syscall()?) Expression::Syscall(self.syscall()?)
@@ -1192,3 +1202,4 @@ impl Parser {
} }
} }
} }

View File

@@ -214,6 +214,7 @@ pub enum Expression {
Binary(BinaryExpression), Binary(BinaryExpression),
Block(BlockExpression), Block(BlockExpression),
Break, Break,
Continue,
Declaration(String, Box<Expression>), Declaration(String, Box<Expression>),
DeviceDeclaration(DeviceDeclarationExpression), DeviceDeclaration(DeviceDeclarationExpression),
Function(FunctionExpression), Function(FunctionExpression),
@@ -237,6 +238,7 @@ impl std::fmt::Display for Expression {
Expression::Binary(e) => write!(f, "{}", e), Expression::Binary(e) => write!(f, "{}", e),
Expression::Block(e) => write!(f, "{}", e), Expression::Block(e) => write!(f, "{}", e),
Expression::Break => write!(f, "break"), Expression::Break => write!(f, "break"),
Expression::Continue => write!(f, "continue"),
Expression::Declaration(id, e) => write!(f, "(let {} = {})", id, e), Expression::Declaration(id, e) => write!(f, "(let {} = {})", id, e),
Expression::DeviceDeclaration(e) => write!(f, "{}", e), Expression::DeviceDeclaration(e) => write!(f, "{}", e),
Expression::Function(e) => write!(f, "{}", e), Expression::Function(e) => write!(f, "{}", e),
@@ -254,4 +256,3 @@ impl std::fmt::Display for Expression {
} }
} }
} }

View File

@@ -410,6 +410,7 @@ impl Tokenizer {
"loop" if next_ws!() => keyword!(Loop), "loop" if next_ws!() => keyword!(Loop),
"break" if next_ws!() => keyword!(Break), "break" if next_ws!() => keyword!(Break),
"while" if next_ws!() => keyword!(While), "while" if next_ws!() => keyword!(While),
"continue" if next_ws!() => keyword!(Continue),
// boolean literals // boolean literals
"true" if next_ws!() => { "true" if next_ws!() => {

View File

@@ -210,6 +210,8 @@ impl Symbol {
#[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)]
pub enum Keyword { pub enum Keyword {
/// Represents the `continue` keyword
Continue,
/// Represents the `let` keyword /// Represents the `let` keyword
Let, Let,
/// Represents the `fn` keyword /// Represents the `fn` keyword