don't desugar for into while until I find that subtle bug
This commit is contained in:
parent
e0ab0a6c67
commit
61a056157b
5 changed files with 154 additions and 38 deletions
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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>,
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue