From f82919414e1fa524af914a1d9b4f71624a28c7fc Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Wed, 12 Feb 2025 13:10:07 +0100 Subject: [PATCH] chapter 9 in rust --- rust/rox/src/environment.rs | 13 ++-- rust/rox/src/expression.rs | 5 ++ rust/rox/src/interpreter.rs | 132 +++++++++++++++++++++++++------- rust/rox/src/parser.rs | 147 +++++++++++++++++++++++++++++++++++- rust/rox/src/statement.rs | 9 +++ 5 files changed, 271 insertions(+), 35 deletions(-) diff --git a/rust/rox/src/environment.rs b/rust/rox/src/environment.rs index 49ac733..5f5966b 100644 --- a/rust/rox/src/environment.rs +++ b/rust/rox/src/environment.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; use thiserror::Error; @@ -15,14 +15,15 @@ pub enum EnvironmentError { #[derive(Default, Debug, Clone)] pub struct Environment { values: HashMap, - enclosing: Option>, + enclosing: Option>>, } impl Environment { - pub fn with_enclosing(enclosing: Environment) -> Self { + /// Initialize a new environment with an enclosing one. + pub fn with_enclosing(enclosing: Rc>) -> Self { Self { values: HashMap::default(), - enclosing: Some(Box::new(enclosing)), + enclosing: Some(enclosing), } } @@ -40,7 +41,7 @@ impl Environment { self.values.insert(token.lexeme.clone(), value); Ok(()) } else if let Some(enclosing) = &mut self.enclosing { - enclosing.assign(token, value) + enclosing.borrow_mut().assign(token, value) } else { Err(EnvironmentError::UndefinedVariable( token.line, @@ -55,7 +56,7 @@ impl Environment { if let Some(v) = self.values.get(token.lexeme.as_str()) { Ok(v.clone()) } else if let Some(enclosing) = &self.enclosing { - enclosing.get(token) + enclosing.borrow().get(token) } else { Err(EnvironmentError::UndefinedVariable( token.line, diff --git a/rust/rox/src/expression.rs b/rust/rox/src/expression.rs index a625331..0600328 100644 --- a/rust/rox/src/expression.rs +++ b/rust/rox/src/expression.rs @@ -18,6 +18,11 @@ pub enum Expression { Literal { value: token::Literal, }, + Logical { + left: Box, + operator: Token, + right: Box, + }, Unary { operator: Token, right: Box, diff --git a/rust/rox/src/interpreter.rs b/rust/rox/src/interpreter.rs index 16d5f8a..1438d81 100644 --- a/rust/rox/src/interpreter.rs +++ b/rust/rox/src/interpreter.rs @@ -1,3 +1,5 @@ +use std::{cell::RefCell, rc::Rc}; + use thiserror::Error; use tracing::error; @@ -28,14 +30,14 @@ pub enum InterpreterError { /// Interpreter for the Lox language. #[derive(Default, Debug)] pub struct Interpreter { - environment: Environment, + environment: Rc>, } impl Interpreter { /// Try to evaluate an expression and return its result. pub fn run(&mut self, statements: Vec) -> Result<(), InterpreterError> { for stmt in statements { - match self.execute(stmt) { + match self.execute(&stmt) { Ok(_) => {} Err(e) => error!("{e}"), }; @@ -45,7 +47,7 @@ impl Interpreter { } ///Execute a statement. - fn execute(&mut self, statement: Statement) -> Result<(), InterpreterError> { + fn execute(&mut self, statement: &Statement) -> Result<(), InterpreterError> { match statement { Statement::Block(statements) => { let sub_env = Environment::with_enclosing(self.environment.clone()); @@ -55,7 +57,18 @@ impl Interpreter { Statement::Expression(expression) => { self.evaluate(expression)?; } - Statement::Var { name, initializer } => self.var_statement(name, *initializer)?, + Statement::Var { name, initializer } => { + self.var_statement(name, initializer.as_ref().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())? + } + Statement::While { condition, body } => self.while_statement(condition, body)?, }; Ok(()) @@ -65,11 +78,11 @@ impl Interpreter { /// enclosing one). fn block( &mut self, - statements: Vec, + statements: &Vec, environment: Environment, ) -> Result<(), InterpreterError> { - let prev_env = &self.environment.clone(); - self.environment = environment; + let prev_env = self.environment.clone(); + self.environment = Rc::new(RefCell::new(environment)); for stmt in statements { if let Err(e) = self.execute(stmt) { @@ -83,26 +96,47 @@ impl Interpreter { } /// Evaluate an expression and return its value. - fn evaluate(&mut self, expression: Expression) -> Result { + fn evaluate(&mut self, expression: &Expression) -> Result { match expression { - Expression::Literal { value } => self.literal(value), - Expression::Grouping { expression } => self.grouping(*expression), + Expression::Literal { value } => self.literal(value.clone()), + Expression::Grouping { expression } => self.grouping(expression), Expression::Unary { operator: op, right, - } => self.unary(op, *right), + } => self.unary(op, right), Expression::Binary { left, operator, right, - } => self.binary(*left, operator, *right), - Expression::Variable { name } => self.var_expression(&name), - Expression::Assign { name, value } => self.assign(&name, *value), + } => self.binary(left, operator, right), + Expression::Variable { name } => self.var_expression(name), + Expression::Assign { name, value } => self.assign(name, value), + Expression::Logical { + left, + operator, + right, + } => self.logical_expression(left, operator, right), + } + } + + /// If the condition evaluates to truthy, execute the then branch, otherwise the else branch. + fn if_statement( + &mut self, + condition: &Expression, + then_branch: &Statement, + else_branch: Option<&Statement>, + ) -> Result<(), InterpreterError> { + if self.evaluate(condition)?.is_truthy() { + self.execute(then_branch) + } else if let Some(else_branch) = else_branch { + self.execute(else_branch) + } else { + Ok(()) } } /// Evaluate an expression and print its value to stdout. - fn print_statement(&mut self, expression: Expression) -> Result<(), InterpreterError> { + fn print_statement(&mut self, expression: &Expression) -> Result<(), InterpreterError> { let value = self.evaluate(expression)?; println!("{value}"); @@ -112,8 +146,8 @@ impl Interpreter { /// Initialize a variable with an initializer expression or nil. fn var_statement( &mut self, - name: Token, - initializer: Option, + name: &Token, + initializer: Option<&Expression>, ) -> Result<(), InterpreterError> { let value = if let Some(initializer) = initializer { self.evaluate(initializer) @@ -121,16 +155,32 @@ impl Interpreter { Ok(Value::Nil) }?; - self.environment.define(name.lexeme, value); + self.environment + .borrow_mut() + .define(name.lexeme.clone(), value); + + Ok(()) + } + + /// Execute the body as long as the condition evaluates to true. + fn while_statement( + &mut self, + condition: &Expression, + body: &Statement, + ) -> Result<(), InterpreterError> { + while self.evaluate(condition)?.is_truthy() { + self.execute(body)?; + } Ok(()) } /// Assign the value of an expression to a variable. - fn assign(&mut self, name: &Token, value: Expression) -> Result { + fn assign(&mut self, name: &Token, value: &Expression) -> Result { let value = self.evaluate(value)?; self.environment + .borrow_mut() .assign(name, value.clone()) .map_err(InterpreterError::UndefinedVariable)?; Ok(value) @@ -141,13 +191,36 @@ impl Interpreter { Ok(literal.into()) } + /// 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. + fn logical_expression( + &mut self, + left: &Expression, + operator: &Token, + right: &Expression, + ) -> Result { + let left = self.evaluate(left)?; + + let truthy = if operator.token_type == TokenType::Or { + left.is_truthy() + } else { + !left.is_truthy() + }; + + if truthy { + Ok(left) + } else { + self.evaluate(right) + } + } + /// Evaluate the inner expression. - fn grouping(&mut self, inner: Expression) -> Result { + fn grouping(&mut self, inner: &Expression) -> Result { self.evaluate(inner) } /// Evaluate the expression on the right and use its result when evaluating the unary operator. - fn unary(&mut self, op: Token, right: Expression) -> Result { + fn unary(&mut self, op: &Token, right: &Expression) -> Result { let right = self.evaluate(right)?; match op.token_type { @@ -159,13 +232,17 @@ impl Interpreter { } } TokenType::Bang => Ok(Value::Boolean(!right.is_truthy())), - _ => Err(InterpreterError::UnaryOperatorUnknown(op.line, op.lexeme)), + _ => Err(InterpreterError::UnaryOperatorUnknown( + op.line, + op.lexeme.clone(), + )), } } /// Get the value of a variable. fn var_expression(&mut self, name: &Token) -> Result { self.environment + .borrow() .get(name) .map_err(InterpreterError::UndefinedVariable) } @@ -196,9 +273,9 @@ impl Interpreter { /// specified operator. fn binary( &mut self, - left: Expression, - op: Token, - right: Expression, + left: &Expression, + op: &Token, + right: &Expression, ) -> Result { let left = self.evaluate(left)?; let right = self.evaluate(right)?; @@ -227,7 +304,10 @@ impl Interpreter { } TokenType::BangEqual => Ok(Value::Boolean(left != right)), TokenType::EqualEqual => Ok(Value::Boolean(left == right)), - _ => Err(InterpreterError::BinaryOperatorUnknown(op.line, op.lexeme)), + _ => Err(InterpreterError::BinaryOperatorUnknown( + op.line, + op.lexeme.clone(), + )), } } } diff --git a/rust/rox/src/parser.rs b/rust/rox/src/parser.rs index 2f24df0..faab052 100644 --- a/rust/rox/src/parser.rs +++ b/rust/rox/src/parser.rs @@ -5,7 +5,7 @@ use crate::{ expression::Expression, statement::Statement, token::{ - self, Token, + self, Literal, Token, TokenType::{self, *}, }, }; @@ -32,6 +32,18 @@ pub enum ParserError { InvalidAssignmentTarget(usize), #[error("[line {0}] expected '}}' after block.")] RightBraceAfterBlockExpected(usize), + #[error("[line {0}] expected '(' after if.")] + LeftParenAfterIfExpected(usize), + #[error("[line {0}] expected ')' after condition.")] + RightParenAfterConditionExpected(usize), + #[error("[line {0}] expected '(' after while.")] + LeftParenAfterWhileExpected(usize), + #[error("[line {0}] expected '(' after for.")] + LeftParenAfterForExpected(usize), + #[error("[line {0}] expected ';' after loop condition.")] + SemicolonAfterLoopConditionExpected(usize), + #[error("[line {0}] expected ')' after for clauses.")] + RightParenAfterForClausesExpected(usize), } /// Parse the Lox language. @@ -157,8 +169,14 @@ impl Parser { } fn statement(&mut self) -> Result { - if self.matches(&[Print]) { + if self.matches(&[For]) { + self.for_statement() + } else if self.matches(&[If]) { + self.if_statement() + } else if self.matches(&[Print]) { self.print_statement() + } else if self.matches(&[While]) { + self.while_statement() } else if self.matches(&[LeftBrace]) { Ok(Statement::Block(self.block()?)) } else { @@ -166,6 +184,79 @@ impl Parser { } } + /// Build up a while statement from a for statement. + fn for_statement(&mut self) -> Result { + let line = self.current_token.line; + self.consume(&LeftParen) + .ok_or(ParserError::LeftParenAfterForExpected(line))?; + + let initializer = if self.matches(&[Semicolon]) { + None + } else if self.matches(&[Var]) { + Some(self.var_declaration()?) + } else { + Some(self.expression_statement()?) + }; + + let condition = if !self.matches(&[Semicolon]) { + self.expression()? + } else { + Expression::Literal { + value: Literal::Boolean(true), + } + }; + self.consume(&Semicolon) + .ok_or(ParserError::SemicolonAfterLoopConditionExpected(line))?; + + let increment = if !self.check(&RightParen) { + Some(self.expression()?) + } else { + None + }; + 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, + }; + + Ok(body) + } + + fn if_statement(&mut self) -> Result { + let line = self.current_token.line; + self.consume(&LeftParen) + .ok_or(ParserError::LeftParenAfterIfExpected(line))?; + + let condition = self.expression()?; + + self.consume(&RightParen) + .ok_or(ParserError::RightParenAfterConditionExpected(line))?; + + let then_branch = self.statement()?; + let else_branch = if self.matches(&[Else]) { + Some(Box::new(self.statement()?)) + } else { + None + }; + + Ok(Statement::If { + condition, + then_branch: Box::new(then_branch), + else_branch, + }) + } + fn print_statement(&mut self) -> Result { let value = self.expression()?; let line = self.current_token.line; @@ -197,6 +288,24 @@ impl Parser { }) } + fn while_statement(&mut self) -> Result { + let line = self.current_token.line; + self.consume(&LeftParen) + .ok_or(ParserError::LeftParenAfterWhileExpected(line))?; + + let condition = self.expression()?; + + self.consume(&RightParen) + .ok_or(ParserError::RightParenAfterConditionExpected(line))?; + + let body = self.statement()?; + + Ok(Statement::While { + condition, + body: Box::new(body), + }) + } + fn expression_statement(&mut self) -> Result { let expr = self.expression()?; let line = self.current_token.line; @@ -221,7 +330,7 @@ impl Parser { } fn assignment(&mut self) -> Result { - let expr = self.equality()?; + let expr = self.or()?; if self.matches(&[Equal]) { let equals = self.previous()?.clone(); @@ -240,6 +349,38 @@ impl Parser { } } + fn logical_operator( + &mut self, + operator: TokenType, + parse_fn: F, + ) -> Result + where + F: Fn(&mut Self) -> Result, + { + let mut expr = parse_fn(self)?; + + while self.matches(&[operator]) { + let operator = self.previous()?.clone(); + let right = parse_fn(self)?; + + expr = Expression::Logical { + left: Box::new(expr), + operator, + right: Box::new(right), + }; + } + + Ok(expr) + } + + fn or(&mut self) -> Result { + self.logical_operator(Or, Self::and) + } + + fn and(&mut self) -> Result { + self.logical_operator(And, Self::equality) + } + /// equality -> comparison ( ( "!=" | "==" ) comparison )* ; fn equality(&mut self) -> Result { self.binary_expr(Self::comparison, &[BangEqual, EqualEqual]) diff --git a/rust/rox/src/statement.rs b/rust/rox/src/statement.rs index 7624e73..1ade330 100644 --- a/rust/rox/src/statement.rs +++ b/rust/rox/src/statement.rs @@ -10,4 +10,13 @@ pub enum Statement { name: Token, initializer: Box>, }, + If { + condition: Expression, + then_branch: Box, + else_branch: Option>, + }, + While { + condition: Expression, + body: Box, + }, }