Skip to content

Commit

Permalink
Implement assignemnts
Browse files Browse the repository at this point in the history
  • Loading branch information
lgyanf committed Oct 4, 2023
1 parent 03643ee commit b491126
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub enum ExprType {
Binary(Box<Expr>, BinaryOp, Box<Expr>),
Grouping(Box<Expr>),
Variable { name: String },
Assignment { name: String, value: Box<Expr> }
}

impl Display for ExprType {
Expand All @@ -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),
}
}
}
Expand Down
61 changes: 60 additions & 1 deletion src/ast/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,30 @@ impl Parser<'_> {
}

fn expression(&mut self) -> Result<Expr, LoxError> {
self.equality()
self.assignment()
}

fn assignment(&mut self) -> Result<Expr, LoxError> {
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<Expr, LoxError> {
Expand Down Expand Up @@ -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),
}]),
),
);
}
1 change: 1 addition & 0 deletions src/ast/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/ast/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand Down
25 changes: 24 additions & 1 deletion src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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(),
})
}
}
}
}
}
Expand Down Expand Up @@ -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(),
),
);
}

0 comments on commit b491126

Please sign in to comment.