From b49112647d9e193dd6510bdc738a260e4bd558a3 Mon Sep 17 00:00:00 2001 From: Ivan Gromov Date: Wed, 4 Oct 2023 22:16:55 +0100 Subject: [PATCH] Implement assignemnts --- src/ast/expr.rs | 2 ++ src/ast/parser.rs | 61 +++++++++++++++++++++++++++++++++++++++++++- src/ast/printer.rs | 1 + src/ast/statement.rs | 6 +++++ src/interpreter.rs | 25 +++++++++++++++++- 5 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 5232495..a6d6b54 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -95,6 +95,7 @@ pub enum ExprType { Binary(Box, BinaryOp, Box), Grouping(Box), Variable { name: String }, + Assignment { name: String, value: Box } } impl Display for ExprType { @@ -108,6 +109,7 @@ impl Display for ExprType { ExprType::Binary(left, op, right) => write!(f, "{}{}{}", left, op, right), ExprType::Grouping(expr) => write!(f, "({})", expr), ExprType::Variable { name } => write!(f, "{}", name), + ExprType::Assignment { name, value } => write!(f, "{} = {}", name, value), } } } diff --git a/src/ast/parser.rs b/src/ast/parser.rs index ae58a0f..3bb7971 100644 --- a/src/ast/parser.rs +++ b/src/ast/parser.rs @@ -125,7 +125,30 @@ impl Parser<'_> { } fn expression(&mut self) -> Result { - self.equality() + self.assignment() + } + + fn assignment(&mut self) -> Result { + let expr = self.equality()?; + + if let Some(_equals) = self.consume_if_matches(&[TokenType::Equal]) { + let value = self.assignment()?; + match expr.expr_type { + ExprType::Variable { name } => { + Ok(Expr { + expr_type: ExprType::Assignment { name, value: value.boxed() }, + position: PositionRange::from_bounds(&expr.position, &value.position) + }) + } + _ => Err(LoxError { + kind: LoxErrorKind::Syntax, + position: PositionRange::from_bounds(&expr.position, &value.position), + message: format!("Invalid assignment target: {}", expr.expr_type), + }) + } + } else { + Ok(expr) + } } fn equality(&mut self) -> Result { @@ -670,5 +693,41 @@ mod parser_tests { }], ) ), + + assignment: ( + vec![ + Token::new(TokenType::Identifier("test".to_owned()), range(1, 1, 4)), + Token::new(TokenType::Equal, range(1, 5, 5)), + Token::new(TokenType::Number(123.0), range(1, 6, 8)), + Token::new(TokenType::Semicolon, range(1, 9, 9)), + ], + Ok(vec![ + Statement::Expression { + expr: Expr { + expr_type: ExprType::Assignment { + name: "test".to_owned(), + value: Expr { + expr_type: ExprType::NumberLiteral(123.0), + position: range(1, 6, 8), + }.boxed(), + }, + position: range(1, 1, 8), + }, + position: range(1, 1, 9) + } + ]), + ), + assignment_throws_error_if_no_expression: ( + vec![ + Token::new(TokenType::Identifier("test".to_owned()), range(1, 1, 4)), + Token::new(TokenType::Equal, range(1, 5, 5)), + Token::new(TokenType::Semicolon, range(1, 9, 9)), + ], + Err(vec![LoxError { + kind: LoxErrorKind::Syntax, + message: "Expected expression, got ;.".to_owned(), + position: range(1, 5, 5), + }]), + ), ); } diff --git a/src/ast/printer.rs b/src/ast/printer.rs index 9f0f8ed..dd8e20e 100644 --- a/src/ast/printer.rs +++ b/src/ast/printer.rs @@ -25,6 +25,7 @@ impl Visitor for AstPrinter { ), ExprType::Grouping(e) => format!("(group {})", self.visit_expr(e)), ExprType::Variable { name } => name.clone(), + ExprType::Assignment { name, value } => format!("{} = {}", name, value), } } } diff --git a/src/ast/statement.rs b/src/ast/statement.rs index 0e02424..2843276 100644 --- a/src/ast/statement.rs +++ b/src/ast/statement.rs @@ -4,6 +4,11 @@ use super::expr::Expr; #[derive(Debug, Clone, PartialEq)] pub enum Statement { + // Assignment { + // name: String, + // expr: Expr, + // position: PositionRange, + // }, Expression { expr: Expr, position: PositionRange, @@ -22,6 +27,7 @@ pub enum Statement { impl Statement { fn position(&self) -> &PositionRange { match &self { + // Self::Assignment { name: _, expr: _, position } => position, Self::Expression { expr: _, position } => position, Self::Print { expr: _, position } => position, Self::Var { diff --git a/src/interpreter.rs b/src/interpreter.rs index 926aaf7..9c3daed 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -11,7 +11,6 @@ use crate::ast::expr::ExprType; use crate::ast::expr::UnaryOp; use crate::ast::visitor::StatementVisitor; use crate::ast::{Expr, Visitor}; -use crate::error; use crate::error::LoxError; use crate::error::LoxErrorKind; use crate::position::PositionRange; @@ -65,6 +64,10 @@ impl Environment { fn set(&mut self, name: String, value: Value) { self.map.insert(name, value); } + + fn contains_key(&self, name: &str) -> bool { + self.map.contains_key(name) + } } struct Interpreter<'a> { @@ -197,6 +200,19 @@ impl Visitor for Interpreter<'_> { // TODO: can we avoid cloning here? Some(value) => Ok((*value).clone()), }, + ExprType::Assignment { name, value: value_expression } => { + if self.environment.contains_key(name) { + let value = self.visit_expr(value_expression)?; + self.environment.set(name.clone(), value); + Ok(Value::Nil) + } else { + Err(LoxError { + kind: LoxErrorKind::Runtime, + message: format!("Undefined variable '{}'", name), + position: expr.position.clone(), + }) + } + } } } } @@ -369,5 +385,12 @@ mod run_tests { Ok(()), b"6\n".to_vec(), ), + assignment: ( + "var a = 1; + a = 2; + print a;", + Ok(()), + b"2\n".to_vec(), + ), ); }