Skip to content

Commit

Permalink
Support blocks and nesting
Browse files Browse the repository at this point in the history
  • Loading branch information
l0s committed Jan 7, 2025
1 parent 110bae8 commit e469c9b
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 34 deletions.
67 changes: 41 additions & 26 deletions src/grammar.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use crate::environment::{Environment, ExistsError};
use crate::side_effects::SideEffects;
use bigdecimal::{BigDecimal, Zero};
use std::cell::RefCell;
use std::fmt::{Debug, Display, Formatter};
use std::ops::Neg;
use std::rc::Rc;
use std::str::FromStr;
use EvaluationError::{DivideByZero, NilValue, TypeMismatch, Undefined};

Expand All @@ -26,6 +28,8 @@ pub(crate) enum Statement {
/// If None, this is a variable declaration without assignment
expression: Option<Expression>,
},

Block(Vec<Statement>),
}

#[derive(Eq, PartialEq, Debug)]
Expand All @@ -37,19 +41,19 @@ pub(crate) enum ExecutionError {
impl Statement {
pub fn execute<S: SideEffects>(
&self,
environment: &mut Environment,
environment: Rc<RefCell<Environment>>,
side_effects: &mut S,
) -> Result<(), ExecutionError> {
match self {
Self::Expression(expression) => {
expression
.evaluate(environment)
.evaluate(&mut environment.borrow_mut())
.map_err(ExecutionError::Evaluation)?;
Ok(())
}
Self::Print(value) => {
let result = value
.evaluate(environment)
.evaluate(&mut environment.borrow_mut())
.map_err(ExecutionError::Evaluation)?;

side_effects.println(&format!("{}", result));
Expand All @@ -62,16 +66,26 @@ impl Statement {
} => {
let result = expression
.clone() // TODO can we avoid cloning?
.map(|e| e.evaluate(environment))
.map(|e| e.evaluate(&mut environment.borrow_mut()))
.unwrap_or(Ok(EvaluationResult::Nil))
.map_err(ExecutionError::Evaluation)?;

environment
.borrow_mut()
.define(identifier.clone(), result)
.map_err(ExecutionError::CannotRedefineVariable)?;

Ok(())
}
Self::Block(statements) => {
let child = Rc::new(RefCell::new(Environment::new_nested_scope(
environment.clone(),
)));
for statement in statements {
statement.execute(child.clone(), side_effects)?;
}
Ok(())
}
}
}
}
Expand Down Expand Up @@ -676,7 +690,6 @@ impl Display for EvaluationResult {

#[cfg(test)]
mod tests {

use super::BinaryOperator::{Add, Divide, Equal, LessThan, Multiply};
use super::EvaluationError::{DivideByZero, NilValue, TypeMismatch};
use super::Literal::Nil;
Expand All @@ -687,6 +700,8 @@ mod tests {
use crate::environment::Environment;
use crate::side_effects::{SideEffects, StandardSideEffects};
use bigdecimal::{BigDecimal, One, Zero};
use std::cell::RefCell;
use std::rc::Rc;
use std::str::FromStr;

#[test]
Expand Down Expand Up @@ -744,11 +759,11 @@ mod tests {

fn unsuccessful_execution_test(statement: &Statement, expected: &ExecutionError) {
// given
let mut environment = Environment::default();
let environment = Rc::new(RefCell::new(Environment::default()));
let mut side_effects = StandardSideEffects::default();

// when
let result = statement.execute(&mut environment, &mut side_effects);
let result = statement.execute(environment, &mut side_effects);

// then
assert!(result.is_err());
Expand Down Expand Up @@ -914,7 +929,7 @@ mod tests {
#[test]
fn print_variable() {
// given
let mut environment = Environment::default();
let environment = Rc::new(RefCell::new(Environment::default()));
let mut side_effects = TestSideEffects::default();
let variable_definition = Statement::VariableDeclaration {
identifier: "beverage".to_string(),
Expand All @@ -925,10 +940,10 @@ mod tests {

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

// then
Expand All @@ -939,7 +954,7 @@ mod tests {
#[test]
fn redefine_variable() {
// given
let mut environment = Environment::default();
let environment = Rc::new(RefCell::new(Environment::default()));
let mut side_effects = TestSideEffects::default();

let initial_definition = Statement::VariableDeclaration {
Expand All @@ -954,16 +969,16 @@ mod tests {

// when
initial_definition
.execute(&mut environment, &mut side_effects)
.execute(environment.clone(), &mut side_effects)
.expect("Unable to define variable");
print_statement
.execute(&mut environment, &mut side_effects)
.execute(environment.clone(), &mut side_effects)
.expect("Unable to print initial variable value");
subsequent_definition
.execute(&mut environment, &mut side_effects)
.execute(environment.clone(), &mut side_effects)
.expect("Unable to redefine variable");
print_statement
.execute(&mut environment, &mut side_effects)
.execute(environment, &mut side_effects)
.expect("Unable to print subsequent variable value");

// then
Expand All @@ -975,7 +990,7 @@ mod tests {
#[test]
fn uninitialized_variables_are_nil() {
// given
let mut environment = Environment::default();
let environment = Rc::new(RefCell::new(Environment::default()));
let mut side_effects = TestSideEffects::default();

let declaration = Statement::VariableDeclaration {
Expand All @@ -986,10 +1001,10 @@ mod tests {

// when
declaration
.execute(&mut environment, &mut side_effects)
.execute(environment.clone(), &mut side_effects)
.expect("Unable to declare variable");
print_statement
.execute(&mut environment, &mut side_effects)
.execute(environment, &mut side_effects)
.expect("Unable to print initial variable value");

// then
Expand All @@ -1000,7 +1015,7 @@ mod tests {
#[test]
fn math_on_variables() {
// given
let mut environment = Environment::default();
let environment = Rc::new(RefCell::new(Environment::default()));
let mut side_effects = TestSideEffects::default();

let define_a = Statement::VariableDeclaration {
Expand All @@ -1019,13 +1034,13 @@ mod tests {

// when
define_a
.execute(&mut environment, &mut side_effects)
.execute(environment.clone(), &mut side_effects)
.expect("Unable to define a");
define_b
.execute(&mut environment, &mut side_effects)
.execute(environment.clone(), &mut side_effects)
.expect("Unable to define b");
print_statement
.execute(&mut environment, &mut side_effects)
.execute(environment, &mut side_effects)
.expect("Unable to print evaluation result");

// then
Expand All @@ -1036,7 +1051,7 @@ mod tests {
#[test]
fn assignment_returns_value() {
// given
let mut environment = Environment::default();
let environment = Rc::new(RefCell::new(Environment::default()));
let mut side_effects = TestSideEffects::default();

let define_a = Statement::VariableDeclaration {
Expand All @@ -1050,17 +1065,17 @@ mod tests {

// when
define_a
.execute(&mut environment, &mut side_effects)
.execute(environment.clone(), &mut side_effects)
.expect("Unable to define variable");
print_statement
.execute(&mut environment, &mut side_effects)
.execute(environment.clone(), &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"),
environment.borrow().get("a"),
Ok(EvaluationResult::Number(BigDecimal::from(2).into()))
);
}
Expand Down
6 changes: 4 additions & 2 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ use crate::parser::{ParseError, Parser};
use crate::scanner::Scanner;
use crate::side_effects::{SideEffects, StandardSideEffects};
use crate::token::Token;
use std::cell::RefCell;
use std::rc::Rc;
use InterpreterError::{Lex, Parse};

/// An interpreter takes source code and executes it
pub(crate) struct Interpreter<S: SideEffects> {
environment: Environment,
environment: Rc<RefCell<Environment>>,
side_effects: S,
}

Expand Down Expand Up @@ -51,7 +53,7 @@ impl<S: SideEffects> Interpreter<S> {
let statements: Vec<Statement> = parser.try_into().map_err(Parse)?;
for statement in statements {
statement
.execute(&mut self.environment, &mut self.side_effects)
.execute(self.environment.clone(), &mut self.side_effects)
.map_err(Execution)?;
}

Expand Down
30 changes: 24 additions & 6 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::grammar::{BinaryOperator, Expression, Literal, Statement, UnaryOperator};
use crate::parser::ParseError::{
InvalidAssignmentTarget, InvalidBinaryOperator, InvalidLiteral, InvalidPrimaryToken,
InvalidUnaryOperator, UnclosedGrouping, UnterminatedStatement, VariableNameExpected,
InvalidUnaryOperator, UnclosedGrouping, UnterminatedBlock, UnterminatedStatement,
VariableNameExpected,
};
use crate::token::TokenType::Semicolon;
use crate::token::{Token, TokenType};

#[derive(Debug)]
Expand All @@ -30,6 +30,8 @@ pub(crate) enum ParseError {
UnclosedGrouping,
/// Statement is missing a semicolon
UnterminatedStatement,
/// Block is missing the closing brace
UnterminatedBlock,
VariableNameExpected,
InvalidAssignmentTarget(Token),
}
Expand Down Expand Up @@ -122,7 +124,7 @@ impl Parser {
} else {
None
};
self.consume(&Semicolon, UnterminatedStatement)?; // TODO distinguish from unterminated print statement
self.consume(&TokenType::Semicolon, UnterminatedStatement)?; // TODO distinguish from unterminated print statement
Ok(Statement::VariableDeclaration {
identifier,
expression,
Expand All @@ -132,20 +134,36 @@ impl Parser {
fn statement(&mut self) -> Result<Statement, ParseError> {
if self.token_match(&[TokenType::Print]) {
self.print_statement()
} else if self.token_match(&[TokenType::OpenBrace]) {
self.block()
} else {
self.expression_statement()
}
}

fn block(&mut self) -> Result<Statement, ParseError> {
let mut statements = vec![];

while !self.check(&TokenType::CloseBrace) && !self.at_end() {
if let Some(declaration) = self.declaration()? {
statements.push(declaration);
}
}

self.consume(&TokenType::CloseBrace, UnterminatedBlock)?;

Ok(Statement::Block(statements))
}

fn print_statement(&mut self) -> Result<Statement, ParseError> {
let value = self.expression()?;
self.consume(&Semicolon, UnterminatedStatement)?; // TODO distinguish from unterminated expression
self.consume(&TokenType::Semicolon, UnterminatedStatement)?; // TODO distinguish from unterminated expression
Ok(Statement::Print(value))
}

fn expression_statement(&mut self) -> Result<Statement, ParseError> {
let expression = self.expression()?;
self.consume(&Semicolon, UnterminatedStatement)?; // TODO distinguish from unterminated print statement
self.consume(&TokenType::Semicolon, UnterminatedStatement)?; // TODO distinguish from unterminated print statement
Ok(Statement::Expression(expression))
}

Expand Down Expand Up @@ -320,7 +338,7 @@ impl Parser {
fn synchronize(&mut self) {
self.advance();
while !self.at_end() {
if self.previous().token_type == Semicolon {
if self.previous().token_type == TokenType::Semicolon {
return;
}

Expand Down

0 comments on commit e469c9b

Please sign in to comment.