continue statements now working
This commit is contained in:
@@ -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(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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!() => {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user