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()
|
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::Print(expression) => self.print_statement(expression),
|
||||||
Statement::Expression(expression) => Ok(Some(self.evaluate(expression)?)),
|
Statement::Expression(expression) => Ok(Some(self.evaluate(expression)?)),
|
||||||
Statement::Var { name, initializer } => {
|
Statement::Var { name, initializer } => {
|
||||||
self.var_statement(name, initializer.as_ref().as_ref())
|
self.var_statement(name, (**initializer).as_ref())
|
||||||
}
|
}
|
||||||
Statement::If {
|
Statement::If {
|
||||||
condition,
|
condition,
|
||||||
then_branch,
|
then_branch,
|
||||||
else_branch,
|
else_branch,
|
||||||
} => {
|
} => self.if_statement(
|
||||||
let else_branch = else_branch.as_ref().map(|x| *x.clone());
|
condition,
|
||||||
self.if_statement(condition, then_branch, else_branch.as_ref())
|
then_branch,
|
||||||
}
|
else_branch.as_ref().map(|b| b.as_ref()),
|
||||||
|
),
|
||||||
Statement::While { condition, body } => self.while_statement(condition, body),
|
Statement::While { condition, body } => self.while_statement(condition, body),
|
||||||
Statement::Function { name, params, body } => {
|
Statement::Function { name, params, body } => {
|
||||||
self.function_statement(name, params, body)
|
self.function_statement(name, params, body)
|
||||||
|
@ -127,6 +128,12 @@ impl Interpreter {
|
||||||
superclass,
|
superclass,
|
||||||
methods,
|
methods,
|
||||||
} => self.class(name, 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)
|
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.
|
/// Assign the value of an expression to a variable.
|
||||||
fn assign(&mut self, assign_expr: &Expression) -> Result<Value, InterpreterError> {
|
fn assign(&mut self, assign_expr: &Expression) -> Result<Value, InterpreterError> {
|
||||||
if let Expression::Assign { name, value } = assign_expr {
|
if let Expression::Assign { name, value } = assign_expr {
|
||||||
|
@ -468,7 +516,6 @@ impl Interpreter {
|
||||||
}
|
}
|
||||||
Ok(evaluated_value)
|
Ok(evaluated_value)
|
||||||
} else {
|
} else {
|
||||||
// This should ideally not be reached if called correctly from `evaluate`
|
|
||||||
unreachable!("Interpreter::assign called with a non-Assign expression");
|
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
|
/// 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(
|
fn logical_expression(
|
||||||
&mut self,
|
&mut self,
|
||||||
left: &Expression,
|
left: &Expression,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
expression::Expression,
|
expression::Expression,
|
||||||
statement::Statement,
|
statement::Statement,
|
||||||
token::{
|
token::{
|
||||||
self, Literal, Token,
|
self, Token,
|
||||||
TokenType::{self, *},
|
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> {
|
fn for_statement(&mut self) -> Result<Statement, ParserError> {
|
||||||
let line = self.current_token.line;
|
let line = self.current_token.line;
|
||||||
self.consume(&LeftParen)
|
self.consume(&LeftParen)
|
||||||
|
@ -230,20 +230,19 @@ impl Parser {
|
||||||
let initializer = if self.matches(&[Semicolon]) {
|
let initializer = if self.matches(&[Semicolon]) {
|
||||||
None
|
None
|
||||||
} else if self.matches(&[Var]) {
|
} else if self.matches(&[Var]) {
|
||||||
Some(self.var_declaration()?)
|
Some(Box::new(self.var_declaration()?))
|
||||||
} else {
|
} else {
|
||||||
Some(self.expression_statement()?)
|
Some(Box::new(self.expression_statement()?))
|
||||||
};
|
};
|
||||||
|
|
||||||
let condition = if !self.matches(&[Semicolon]) {
|
let condition = if self.matches(&[Semicolon]) {
|
||||||
self.expression()?
|
None // No condition means it defaults to true later
|
||||||
} else {
|
} else {
|
||||||
Expression::Literal {
|
let cond_expr = self.expression()?;
|
||||||
value: Literal::Boolean(true),
|
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) {
|
let increment = if !self.check(&RightParen) {
|
||||||
Some(self.expression()?)
|
Some(self.expression()?)
|
||||||
|
@ -253,21 +252,14 @@ impl Parser {
|
||||||
self.consume(&RightParen)
|
self.consume(&RightParen)
|
||||||
.ok_or(ParserError::RightParenAfterForClausesExpected(line))?;
|
.ok_or(ParserError::RightParenAfterForClausesExpected(line))?;
|
||||||
|
|
||||||
let body = self.statement()?;
|
let body = Box::new(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,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(body)
|
Ok(Statement::For {
|
||||||
|
initializer,
|
||||||
|
condition,
|
||||||
|
increment,
|
||||||
|
body,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an if statement with a condition, then branch, and optional else branch.
|
/// 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.
|
/// 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) {
|
fn synchronize(&mut self) {
|
||||||
let _ = self.advance();
|
let _ = self.advance();
|
||||||
while !self.is_at_end()
|
while !self.is_at_end()
|
||||||
|
|
|
@ -71,7 +71,9 @@ impl<'a> Resolver<'a> {
|
||||||
Statement::Block(statements) => self.resolve_block(statements),
|
Statement::Block(statements) => self.resolve_block(statements),
|
||||||
Statement::Print(expression) => self.resolve_expression(expression),
|
Statement::Print(expression) => self.resolve_expression(expression),
|
||||||
Statement::Expression(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 {
|
Statement::If {
|
||||||
condition,
|
condition,
|
||||||
then_branch,
|
then_branch,
|
||||||
|
@ -79,7 +81,7 @@ impl<'a> Resolver<'a> {
|
||||||
} => self.resolve_if_stmt(
|
} => self.resolve_if_stmt(
|
||||||
condition,
|
condition,
|
||||||
then_branch,
|
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::While { condition, body } => self.resolve_while_stmt(condition, body),
|
||||||
Statement::Function { name, params, body } => {
|
Statement::Function { name, params, body } => {
|
||||||
|
@ -91,6 +93,12 @@ impl<'a> Resolver<'a> {
|
||||||
superclass,
|
superclass,
|
||||||
methods,
|
methods,
|
||||||
} => self.resolve_class_stmt(name, 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,
|
&mut self,
|
||||||
condition: &Expression,
|
condition: &Expression,
|
||||||
then_branch: &Statement,
|
then_branch: &Statement,
|
||||||
else_branch: Option<Statement>,
|
else_branch: Option<&Statement>,
|
||||||
) -> Result<(), ResolverError> {
|
) -> Result<(), ResolverError> {
|
||||||
self.resolve_expression(condition)?;
|
self.resolve_expression(condition)?;
|
||||||
self.resolve_statement(then_branch)?;
|
self.resolve_statement(then_branch)?;
|
||||||
|
|
||||||
if let Some(stmt) = else_branch {
|
if let Some(stmt_ref) = else_branch {
|
||||||
self.resolve_statement(&stmt)?;
|
self.resolve_statement(stmt_ref)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -398,6 +406,35 @@ impl<'a> Resolver<'a> {
|
||||||
Ok(())
|
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.
|
/// Resolve a function definition with parameters and body.
|
||||||
/// Create a new scope for the function body and declare all parameters.
|
/// Create a new scope for the function body and declare all parameters.
|
||||||
fn resolve_function(
|
fn resolve_function(
|
||||||
|
|
|
@ -33,4 +33,10 @@ pub enum Statement {
|
||||||
superclass: Option<Expression>,
|
superclass: Option<Expression>,
|
||||||
methods: Vec<Statement>,
|
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