Skip to content

Commit

Permalink
9.5 - Parse "for" loops
Browse files Browse the repository at this point in the history
  • Loading branch information
l0s committed Feb 10, 2025
1 parent 1fef63a commit b9c5da6
Show file tree
Hide file tree
Showing 9 changed files with 535 additions and 125 deletions.
7 changes: 7 additions & 0 deletions src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::grammar::EvaluationResult;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::rc::Rc;

/// The scope for variables
Expand All @@ -22,6 +23,12 @@ pub(crate) struct UndefinedError;
#[derive(Eq, PartialEq, Debug)]
pub(crate) struct ExistsError;

impl Display for ExistsError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Attempted to redefine a variable that already exists in the local scope. This is only permitted in the global scope.")
}
}

impl Environment {
/// Define a new variable or, if this is the global scope, redefine an existing variable.
///
Expand Down
11 changes: 11 additions & 0 deletions src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,17 @@ pub enum EvaluationError {
Undefined,
}

impl Display for EvaluationError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
TypeMismatch => write!(f, "TypeMismatch"),
NilValue => write!(f, "Attempted to use nil value"),
DivideByZero => write!(f, "Attempted to divide by zero"),
Undefined => write!(f, "Expression refers to an undefined variable"),
}
}
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub(crate) enum BinaryOperator {
Equal,
Expand Down
62 changes: 59 additions & 3 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ impl Default for Interpreter<StandardSideEffects> {
}
}

#[derive(Debug)]
pub(crate) enum InterpreterError {
/// Errors occurred while tokenizing the input
Lex(Vec<LexicalError>),
Expand Down Expand Up @@ -57,9 +58,14 @@ impl<S: SideEffects> Interpreter<S> {
let parser: Parser = tokens.into();
let statements: Vec<Statement> = parser.try_into().map_err(Parse)?;
for statement in statements {
statement
.execute(self.environment.clone(), self.side_effects.clone())
.map_err(Execution)?;
if let Err(execution_error) =
statement.execute(self.environment.clone(), self.side_effects.clone())
{
self.side_effects
.borrow_mut()
.eprintln(&format!("{}", execution_error));
return Err(Execution(execution_error));
}
}

Ok(())
Expand All @@ -72,3 +78,53 @@ impl<S: SideEffects> Interpreter<S> {
expression.evaluate(&mut self.environment.borrow_mut())
}
}

#[cfg(test)]
mod tests {
use super::Interpreter;
use crate::side_effects::SideEffects;
use std::cell::RefCell;
use std::rc::Rc;

#[test]
fn can_interpret_fib() {
// given
let source = "
var a = 0;
var temp;
for (var b = 1; a < 10000; b = temp + b) {
print a;
temp = a;
a = b;
}
";
let side_effects = Rc::new(RefCell::new(TestSideEffects::default()));
let mut interpreter = Interpreter::new(side_effects.clone());

// when
interpreter
.run(source)
.expect("Unable to execute source file");

// then
let side_effects = side_effects.borrow();
assert_eq!(side_effects.lines.len(), 21);
assert_eq!(side_effects.lines[20], "6.765e3");
}

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

impl SideEffects for TestSideEffects {
fn println(&mut self, text: &str) {
self.lines.push(text.to_string())
}

fn eprintln(&mut self, _text: &str) {
unimplemented!()
}
}
}
10 changes: 7 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ fn interpret_line<S: SideEffects>(
(0, 0)
}
Err(error) => {
eprintln!("Evaluation error: {:?}", error);
eprintln!("Evaluation error: {}", error);
(0, 1)
}
},
Expand All @@ -129,11 +129,11 @@ fn report(result: Result<(), InterpreterError>) -> (usize, usize) {
(error_count, 0)
}
InterpreterError::Parse(parse_error) => {
eprintln!("Parse error: {:?}", parse_error);
eprintln!("Parse error: {}", parse_error);
(1, 0)
}
InterpreterError::Execution(execution_error) => {
eprintln!("Execution error: {:?}", execution_error);
eprintln!("Execution error: {}", execution_error);
(0, 1)
}
},
Expand Down Expand Up @@ -211,5 +211,9 @@ mod tests {
fn println(&mut self, text: &str) {
self.lines.push(text.to_string());
}

fn eprintln(&mut self, text: &str) {
eprintln!("{}", text);
}
}
}
Loading

0 comments on commit b9c5da6

Please sign in to comment.