From 61a056157b5baa257705029752ac50d530c94a55 Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Wed, 28 May 2025 21:03:59 +0200 Subject: [PATCH] don't desugar for into while until I find that subtle bug --- src/ast_printer.rs | 35 ++++++++++++++++++++++++++ src/interpreter.rs | 61 ++++++++++++++++++++++++++++++++++++++++------ src/parser.rs | 43 +++++++++++++------------------- src/resolver.rs | 47 +++++++++++++++++++++++++++++++---- src/statement.rs | 6 +++++ 5 files changed, 154 insertions(+), 38 deletions(-) diff --git a/src/ast_printer.rs b/src/ast_printer.rs index d48728c..e66acfc 100644 --- a/src/ast_printer.rs +++ b/src/ast_printer.rs @@ -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() + } } } diff --git a/src/interpreter.rs b/src/interpreter.rs index 7adfef3..70b1670 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -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>, + condition: &Option, + increment: &Option, + body: &Statement, + ) -> Result, 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 { 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, diff --git a/src/parser.rs b/src/parser.rs index 4e60e49..a7dc9a0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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 { 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() diff --git a/src/resolver.rs b/src/resolver.rs index 5c39cc9..e5d1fd5 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -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, + 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>, + condition: &Option, + increment: &Option, + 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( diff --git a/src/statement.rs b/src/statement.rs index 5871e98..f58ddbd 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -33,4 +33,10 @@ pub enum Statement { superclass: Option, methods: Vec, }, + For { + initializer: Option>, + condition: Option, + increment: Option, + body: Box, + }, }