don't desugar for into while until I find that subtle bug

This commit is contained in:
Sebastian Hugentobler 2025-05-28 21:03:59 +02:00
parent e0ab0a6c67
commit 61a056157b
Signed by: shu
SSH key fingerprint: SHA256:ppcx6MlixdNZd5EUM1nkHOKoyQYoJwzuQKXM6J/t66M
5 changed files with 154 additions and 38 deletions

View file

@ -93,6 +93,41 @@ fn print_statement(statement: &Statement, indent: usize) -> String {
}
result.trim_end().to_string()
}
Statement::For {
initializer,
condition,
increment,
body,
} => {
let mut result = format!("{}For:\n", indent_str);
if let Some(init_stmt) = initializer {
result.push_str(&format!(
"{} Initializer:\n{}\n",
indent_str,
print_statement(init_stmt, indent + 2)
));
}
if let Some(cond_expr) = condition {
result.push_str(&format!(
"{} Condition: {}\n",
indent_str,
print_expression(cond_expr)
));
}
if let Some(inc_expr) = increment {
result.push_str(&format!(
"{} Increment: {}\n",
indent_str,
print_expression(inc_expr)
));
}
result.push_str(&format!(
"{} Body:\n{}",
indent_str,
print_statement(body, indent + 2)
));
result.trim_end().to_string()
}
}
}

View file

@ -107,16 +107,17 @@ impl Interpreter {
Statement::Print(expression) => self.print_statement(expression),
Statement::Expression(expression) => Ok(Some(self.evaluate(expression)?)),
Statement::Var { name, initializer } => {
self.var_statement(name, initializer.as_ref().as_ref())
self.var_statement(name, (**initializer).as_ref())
}
Statement::If {
condition,
then_branch,
else_branch,
} => {
let else_branch = else_branch.as_ref().map(|x| *x.clone());
self.if_statement(condition, then_branch, else_branch.as_ref())
}
} => self.if_statement(
condition,
then_branch,
else_branch.as_ref().map(|b| b.as_ref()),
),
Statement::While { condition, body } => self.while_statement(condition, body),
Statement::Function { name, params, body } => {
self.function_statement(name, params, body)
@ -127,6 +128,12 @@ impl Interpreter {
superclass,
methods,
} => self.class(name, superclass, methods),
Statement::For {
initializer,
condition,
increment,
body,
} => self.for_statement(initializer, condition, increment, body),
}
}
@ -449,6 +456,47 @@ impl Interpreter {
Ok(None)
}
/// Execute a for loop.
fn for_statement(
&mut self,
initializer: &Option<Box<Statement>>,
condition: &Option<Expression>,
increment: &Option<Expression>,
body: &Statement,
) -> Result<Option<Value>, InterpreterError> {
let previous_environment = self.environment.clone();
self.environment = Rc::new(RefCell::new(Environment::with_enclosing(
previous_environment.clone(),
)));
if let Some(init_stmt) = initializer {
self.execute(init_stmt)?;
}
loop {
let mut loop_condition_met = true;
if let Some(cond_expr) = condition {
loop_condition_met = self.evaluate(cond_expr)?.is_truthy();
}
if !loop_condition_met {
break;
}
if let Some(Value::Return(val)) = self.execute(body)? {
self.environment = previous_environment;
return Ok(Some(Value::Return(val)));
}
if let Some(inc_expr) = increment {
self.evaluate(inc_expr)?;
}
}
self.environment = previous_environment;
Ok(None)
}
/// Assign the value of an expression to a variable.
fn assign(&mut self, assign_expr: &Expression) -> Result<Value, InterpreterError> {
if let Expression::Assign { name, value } = assign_expr {
@ -468,7 +516,6 @@ impl Interpreter {
}
Ok(evaluated_value)
} else {
// This should ideally not be reached if called correctly from `evaluate`
unreachable!("Interpreter::assign called with a non-Assign expression");
}
}
@ -479,7 +526,7 @@ impl Interpreter {
}
/// Evaluate left and if the operator is Or and left is truthy or the operator is not Or and
/// not leftis truthy short circuit and return left. Otherwise evaluate and return right.
/// not left is truthy short circuit and return left. Otherwise evaluate and return right.
fn logical_expression(
&mut self,
left: &Expression,

View file

@ -2,7 +2,7 @@ use crate::{
expression::Expression,
statement::Statement,
token::{
self, Literal, Token,
self, Token,
TokenType::{self, *},
},
};
@ -221,7 +221,7 @@ impl Parser {
}
}
/// Parse a for statement by desugaring it into a while loop.
/// Parse a for statement.
fn for_statement(&mut self) -> Result<Statement, ParserError> {
let line = self.current_token.line;
self.consume(&LeftParen)
@ -230,20 +230,19 @@ impl Parser {
let initializer = if self.matches(&[Semicolon]) {
None
} else if self.matches(&[Var]) {
Some(self.var_declaration()?)
Some(Box::new(self.var_declaration()?))
} else {
Some(self.expression_statement()?)
Some(Box::new(self.expression_statement()?))
};
let condition = if !self.matches(&[Semicolon]) {
self.expression()?
let condition = if self.matches(&[Semicolon]) {
None // No condition means it defaults to true later
} else {
Expression::Literal {
value: Literal::Boolean(true),
}
let cond_expr = self.expression()?;
self.consume(&Semicolon)
.ok_or(ParserError::SemicolonAfterLoopConditionExpected(line))?;
Some(cond_expr)
};
self.consume(&Semicolon)
.ok_or(ParserError::SemicolonAfterLoopConditionExpected(line))?;
let increment = if !self.check(&RightParen) {
Some(self.expression()?)
@ -253,21 +252,14 @@ impl Parser {
self.consume(&RightParen)
.ok_or(ParserError::RightParenAfterForClausesExpected(line))?;
let body = self.statement()?;
let body = match increment {
Some(inc) => Statement::Block(vec![body, Statement::Expression(inc)]),
None => body,
};
let body = Statement::While {
condition,
body: Box::new(body),
};
let body = match initializer {
Some(initializer) => Statement::Block(vec![initializer, body]),
None => body,
};
let body = Box::new(self.statement()?);
Ok(body)
Ok(Statement::For {
initializer,
condition,
increment,
body,
})
}
/// Parse an if statement with a condition, then branch, and optional else branch.
@ -676,7 +668,6 @@ impl Parser {
}
/// Synchronize the parser after an error by advancing to the next statement boundary.
/// This allows parsing to continue after encountering a syntax error.
fn synchronize(&mut self) {
let _ = self.advance();
while !self.is_at_end()

View file

@ -71,7 +71,9 @@ impl<'a> Resolver<'a> {
Statement::Block(statements) => self.resolve_block(statements),
Statement::Print(expression) => self.resolve_expression(expression),
Statement::Expression(expression) => self.resolve_expression(expression),
Statement::Var { name, initializer } => self.resolve_var(name, initializer.as_ref().as_ref()),
Statement::Var { name, initializer } => {
self.resolve_var(name, (**initializer).as_ref())
}
Statement::If {
condition,
then_branch,
@ -79,7 +81,7 @@ impl<'a> Resolver<'a> {
} => self.resolve_if_stmt(
condition,
then_branch,
else_branch.as_ref().map(|boxed| *boxed.clone()),
else_branch.as_ref().map(|b| b.as_ref()),
),
Statement::While { condition, body } => self.resolve_while_stmt(condition, body),
Statement::Function { name, params, body } => {
@ -91,6 +93,12 @@ impl<'a> Resolver<'a> {
superclass,
methods,
} => self.resolve_class_stmt(name, superclass, methods),
Statement::For {
initializer,
condition,
increment,
body,
} => self.resolve_for_statement(initializer, condition, increment, body),
}
}
@ -374,13 +382,13 @@ impl<'a> Resolver<'a> {
&mut self,
condition: &Expression,
then_branch: &Statement,
else_branch: Option<Statement>,
else_branch: Option<&Statement>,
) -> Result<(), ResolverError> {
self.resolve_expression(condition)?;
self.resolve_statement(then_branch)?;
if let Some(stmt) = else_branch {
self.resolve_statement(&stmt)?;
if let Some(stmt_ref) = else_branch {
self.resolve_statement(stmt_ref)?;
}
Ok(())
@ -398,6 +406,35 @@ impl<'a> Resolver<'a> {
Ok(())
}
/// Resolve a for statement.
/// This creates a new scope for the entire for construct.
fn resolve_for_statement(
&mut self,
initializer: &Option<Box<Statement>>,
condition: &Option<Expression>,
increment: &Option<Expression>,
body: &Statement,
) -> Result<(), ResolverError> {
self.begin_scope();
if let Some(init_stmt) = initializer {
self.resolve_statement(init_stmt)?;
}
if let Some(cond_expr) = condition {
self.resolve_expression(cond_expr)?;
}
self.resolve_statement(body)?;
if let Some(inc_expr) = increment {
self.resolve_expression(inc_expr)?;
}
self.end_scope();
Ok(())
}
/// Resolve a function definition with parameters and body.
/// Create a new scope for the function body and declare all parameters.
fn resolve_function(

View file

@ -33,4 +33,10 @@ pub enum Statement {
superclass: Option<Expression>,
methods: Vec<Statement>,
},
For {
initializer: Option<Box<Statement>>,
condition: Option<Expression>,
increment: Option<Expression>,
body: Box<Statement>,
},
}