Skip to content

Commit

Permalink
Implement variable assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
l0s committed Jan 6, 2025
1 parent a4467c8 commit 2f00511
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 38 deletions.
4 changes: 4 additions & 0 deletions src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ impl Environment {
pub fn get(&self, name: &str) -> Option<&EvaluationResult> {
self.values.get(name)
}

pub fn exists(&self, name: &str) -> bool {
self.get(name).is_some()
}
}
105 changes: 88 additions & 17 deletions src/grammar.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use crate::environment::Environment;
use crate::grammar::EvaluationError::Undefined;
use crate::side_effects::SideEffects;
use bigdecimal::{BigDecimal, Zero};
use std::fmt::{Debug, Display, Formatter};
use std::ops::Neg;
use std::str::FromStr;
use EvaluationError::{DivideByZero, NilValue, TypeMismatch};
use EvaluationError::{DivideByZero, NilValue, TypeMismatch, Undefined};

#[derive(Clone, Debug)]
pub(crate) struct Program {
Expand Down Expand Up @@ -74,7 +73,7 @@ impl Statement {
}
}

/// An expression evaluates to a value and produces no side effects.
/// An expression evaluates to a value
#[derive(Clone, Eq, PartialEq)]
pub(crate) enum Expression {
Literal(Literal),
Expand All @@ -87,6 +86,8 @@ pub(crate) enum Expression {
Grouping(Box<Expression>),
/// A reference to a variable's value
Variable(String),
/// Assign a new value to an existing variable
Assignment(String, Box<Expression>),
}

#[derive(Eq, PartialEq, Copy, Clone, Debug)]
Expand All @@ -106,7 +107,10 @@ impl ResultType {
}

impl Expression {
pub fn evaluate(&self, environment: &Environment) -> Result<EvaluationResult, EvaluationError> {
pub fn evaluate(
&self,
environment: &mut Environment,
) -> Result<EvaluationResult, EvaluationError> {
match self.result_type(environment) {
ResultType::None => Ok(EvaluationResult::Nil),
ResultType::Some(data_type) => data_type.evaluate(self, environment),
Expand Down Expand Up @@ -140,10 +144,11 @@ impl Expression {
ResultType::Undefined
}
}
Self::Assignment(_, expression) => expression.result_type(environment),
}
}

pub fn evaluate_boolean(&self, environment: &Environment) -> Result<bool, EvaluationError> {
pub fn evaluate_boolean(&self, environment: &mut Environment) -> Result<bool, EvaluationError> {
match self {
Self::Literal(literal) => match literal {
Literal::False | Literal::Nil => Ok(false),
Expand Down Expand Up @@ -173,12 +178,20 @@ impl Expression {
Ok(false)
}
}
Self::Assignment(identifier, expression) => {
let result = expression.evaluate_boolean(environment)?;
if !environment.exists(identifier) {
return Err(Undefined);
}
environment.define(identifier.clone(), EvaluationResult::Boolean(result));
Ok(result)
}
}
}

pub fn evaluate_number(
&self,
environment: &Environment,
environment: &mut Environment,
) -> Result<BigDecimal, EvaluationError> {
match self {
Self::Literal(literal) => match literal {
Expand Down Expand Up @@ -210,10 +223,21 @@ impl Expression {
Err(NilValue)
}
}
Self::Assignment(identifier, expression) => {
let result = expression.evaluate_number(environment)?;
if !environment.exists(identifier) {
return Err(Undefined);
}
environment.define(identifier.clone(), EvaluationResult::Number(result.clone()));
Ok(result)
}
}
}

pub fn evaluate_string(&self, environment: &Environment) -> Result<String, EvaluationError> {
pub fn evaluate_string(
&self,
environment: &mut Environment,
) -> Result<String, EvaluationError> {
match self {
Self::Literal(literal) => match literal {
Literal::String(value) => Ok(value.clone()),
Expand All @@ -238,6 +262,14 @@ impl Expression {
Err(NilValue)
}
}
Self::Assignment(identifier, expression) => {
let result = expression.evaluate_string(environment)?;
if !environment.exists(identifier) {
return Err(Undefined);
}
environment.define(identifier.clone(), EvaluationResult::String(result.clone()));
Ok(result)
}
}
}
}
Expand Down Expand Up @@ -277,6 +309,9 @@ impl Debug for Expression {
}
Self::Grouping(expression) => write!(f, "(group {:?})", expression),
Self::Variable(name) => write!(f, "(var {})", name),
Self::Assignment(identifier, expression) => {
write!(f, "(set {} {:?})", identifier, expression)
}
}
}
}
Expand Down Expand Up @@ -307,7 +342,7 @@ pub(crate) enum BinaryOperator {
impl BinaryOperator {
pub fn evaluate_boolean(
&self,
environment: &Environment,
environment: &mut Environment,
left_value: &Expression,
right_value: &Expression,
) -> Result<bool, EvaluationError> {
Expand Down Expand Up @@ -420,7 +455,7 @@ impl BinaryOperator {

pub fn evaluate_number(
&self,
environment: &Environment,
environment: &mut Environment,
left_value: &Expression,
right_value: &Expression,
) -> Result<BigDecimal, EvaluationError> {
Expand All @@ -445,7 +480,7 @@ impl BinaryOperator {

pub fn evaluate_string(
&self,
environment: &Environment,
environment: &mut Environment,
left_value: &Expression,
right_value: &Expression,
) -> Result<String, EvaluationError> {
Expand All @@ -455,8 +490,8 @@ impl BinaryOperator {
if left_type == ResultType::Some(DataType::String)
|| right_type == ResultType::Some(DataType::String)
{
let convert_to_string = |expression: &Expression,
data_type: ResultType|
let mut convert_to_string = |expression: &Expression,
data_type: ResultType|
-> Result<String, EvaluationError> {
Ok(match data_type {
ResultType::None => "".to_string(),
Expand Down Expand Up @@ -595,7 +630,7 @@ impl DataType {
pub fn evaluate(
&self,
expression: &Expression,
environment: &Environment,
environment: &mut Environment,
) -> Result<EvaluationResult, EvaluationError> {
match self {
Self::Number => Ok(EvaluationResult::Number(
Expand Down Expand Up @@ -681,10 +716,10 @@ mod tests {

fn successful_evaluation_test(expression: &Expression, expected: &EvaluationResult) {
// given
let environment = Environment::default();
let mut environment = Environment::default();

// when
let result = expression.evaluate(&environment).unwrap();
let result = expression.evaluate(&mut environment).unwrap();

// then
assert_eq!(
Expand All @@ -696,10 +731,10 @@ mod tests {

fn unsuccessful_evaluation_test(expression: &Expression, expected: &EvaluationError) {
// given
let environment = Environment::default();
let mut environment = Environment::default();

// when
let result = expression.evaluate(&environment).unwrap_err();
let result = expression.evaluate(&mut environment).unwrap_err();

// then
assert_eq!(
Expand Down Expand Up @@ -872,6 +907,10 @@ mod tests {
Statement::Print(Expression::Variable("a".to_string())),
ExecutionError::Evaluation(EvaluationError::Undefined),
),
assignment_without_declaration: (
Statement::Expression(Expression::Assignment("a".to_string(), Box::new(Expression::Literal(BigDecimal::one().into())))),
ExecutionError::Evaluation(EvaluationError::Undefined),
),
}

#[test]
Expand Down Expand Up @@ -995,6 +1034,38 @@ mod tests {
assert_eq!(side_effects.lines[0], "3e0");
}

#[test]
fn assignment_returns_value() {
// given
let mut environment = Environment::default();
let mut side_effects = TestSideEffects::default();

let define_a = Statement::Variable {
identifier: "a".to_string(),
expression: Some(Expression::Literal(BigDecimal::one().into())),
};
let print_statement = Statement::Print(Expression::Assignment(
"a".to_string(),
Box::new(Expression::Literal(BigDecimal::from(2).into())),
));

// when
define_a
.execute(&mut environment, &mut side_effects)
.expect("Unable to define variable");
print_statement
.execute(&mut environment, &mut side_effects)
.expect("Unable to print expression");

// then
assert_eq!(side_effects.lines.len(), 1);
assert_eq!(side_effects.lines[0], "2e0");
assert_eq!(
environment.get("a"),
Some(&EvaluationResult::Number(BigDecimal::from(2).into()))
);
}

#[derive(Default)]
struct TestSideEffects {
lines: Vec<String>,
Expand Down
Loading

0 comments on commit 2f00511

Please sign in to comment.