diff --git a/Cargo.lock b/Cargo.lock index f37bbb5..f701fad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,16 +1,18 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "either" -version = "1.5.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "itertools" -version = "0.8.2" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] diff --git a/Cargo.toml b/Cargo.toml index ea8846c..ebac586 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,5 +5,5 @@ authors = ["ishanjain28 "] edition = "2018" [dependencies] +itertools = "0.10.5" lazy_static = "1.4.0" -itertools = "0.8.2" diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 29cb0a8..0000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly-2020-05-31 diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index 2920add..6755d6e 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -1,11 +1,35 @@ use { crate::parser::ast::Node, - std::fmt::{Display, Formatter, Result as FmtResult}, + std::{ + collections::HashMap, + fmt::{Display, Formatter, Result as FmtResult}, + }, }; pub mod tree_walker; pub trait Evaluator { - fn eval(&self, node: Node) -> Option; + fn eval(&self, node: Node, env: &mut Environment) -> Option; +} + +pub struct Environment { + store: HashMap, +} + +impl Environment { + pub fn new() -> Self { + Self { + store: HashMap::new(), + } + } + pub fn get(&self, name: &str) -> Option { + match self.store.get(name) { + Some(v) => Some(v.clone()), + None => None, + } + } + pub fn set(&mut self, name: String, val: Object) { + self.store.insert(name, val); + } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -48,7 +72,7 @@ impl Display for Object { #[cfg(test)] mod tests { use crate::{ - evaluator::{tree_walker::TreeWalker, Evaluator, Object, FALSE, NULL, TRUE}, + evaluator::{tree_walker::TreeWalker, Environment, Evaluator, Object, FALSE, NULL, TRUE}, lexer::Lexer, parser::{ast::Node, Parser}, }; @@ -61,7 +85,8 @@ mod tests { assert!(program.is_some()); let program = program.unwrap(); let evaluator = TreeWalker::new(); - let eval = evaluator.eval(Node::Program(program)); + let mut env = Environment::new(); + let eval = evaluator.eval(Node::Program(program), &mut env); assert_eq!(eval, test.1); } } @@ -209,6 +234,25 @@ mod tests { }", Some(Object::Error("unknown operator: BOOLEAN + BOOLEAN".into())), ), + ( + "foobar", + Some(Object::Error("identifier not found: foobar".into())), + ), + ]; + + run_test_cases(&test_cases); + } + + #[test] + fn test_let_statements() { + let test_cases = [ + ("let a = 5; a;", Some(Object::Integer(5))), + ("let a = 5*5; a;", Some(Object::Integer(25))), + ("let a = 5; let b = a; b;", Some(Object::Integer(5))), + ( + "let a = 5; let b = a; let c = a + b +5; c;", + Some(Object::Integer(15)), + ), ]; run_test_cases(&test_cases); diff --git a/src/evaluator/tree_walker/mod.rs b/src/evaluator/tree_walker/mod.rs index a4b2e9b..788d594 100644 --- a/src/evaluator/tree_walker/mod.rs +++ b/src/evaluator/tree_walker/mod.rs @@ -4,9 +4,14 @@ use crate::{ evaluator::{Evaluator, Object, FALSE, NULL, TRUE}, lexer::TokenType, - parser::ast::{BlockStatement, Expression, ExpressionStatement, Node, Program, Statement}, + parser::ast::{ + BlockStatement, Expression, ExpressionStatement, Identifier, LetStatement, Node, Program, + Statement, + }, }; +use super::Environment; + pub struct TreeWalker; impl TreeWalker { @@ -16,39 +21,49 @@ impl TreeWalker { } impl Evaluator for TreeWalker { - fn eval(&self, node: Node) -> Option { + fn eval(&self, node: Node, env: &mut Environment) -> Option { match node { - Node::Program(p) => self.eval_program(p), + Node::Program(p) => self.eval_program(p, env), Node::Statement(stmt) => match stmt { Statement::ExpressionStatement(ExpressionStatement { expression, .. }) => { - self.eval(Node::Expression(expression)) + self.eval(Node::Expression(expression), env) } - Statement::BlockStatement(bs) => self.eval_block_statement(bs), + Statement::BlockStatement(bs) => self.eval_block_statement(bs, env), Statement::Return(ret) => { - let ret_val = self.eval(Node::Expression(ret.value?))?; + let ret_val = self.eval(Node::Expression(ret.value?), env)?; Some(Object::ReturnValue(Box::new(ret_val))) } + Statement::Let(LetStatement { name, value }) => { + let value = self.eval(Node::Expression(value.unwrap()), env)?; + env.set(name.to_string(), value.clone()); + Some(value) + } + _ => None, }, Node::Expression(expr) => match expr { + Expression::Identifier(v) => self.eval_identifier(v, env), Expression::IntegerLiteral(il) => Some(Object::Integer(il.value)), Expression::BooleanExpression(b) => Some(Object::Boolean(b.value)), Expression::PrefixExpression(p) => { - let expr = self.eval(Node::Expression(*p.right))?; + let expr = self.eval(Node::Expression(*p.right), env)?; self.eval_prefix_expression(p.operator, expr) } Expression::InfixExpression(ie) => { - let left = self.eval(Node::Expression(*ie.left))?; - let right = self.eval(Node::Expression(*ie.right))?; + let left = self.eval(Node::Expression(*ie.left), env)?; + let right = self.eval(Node::Expression(*ie.right), env)?; self.eval_infix_expression(left, ie.operator, right) } Expression::IfExpression(ie) => { - let condition = self.eval(Node::Expression(*ie.condition))?; + let condition = self.eval(Node::Expression(*ie.condition), env)?; if self.is_truthy(&condition) { - self.eval(Node::Statement(Statement::BlockStatement(ie.consequence))) + self.eval( + Node::Statement(Statement::BlockStatement(ie.consequence)), + env, + ) } else if let Some(alternative) = ie.alternative { - self.eval(Node::Statement(Statement::BlockStatement(alternative))) + self.eval(Node::Statement(Statement::BlockStatement(alternative)), env) } else { Some(NULL) } @@ -60,16 +75,16 @@ impl Evaluator for TreeWalker { } impl TreeWalker { - fn eval_program(&self, prg: Program) -> Option { + fn eval_program(&self, prg: Program, env: &mut Environment) -> Option { let mut out: Option = None; for stmt in prg.statements { - out = self.eval(Node::Statement(stmt)); + out = self.eval(Node::Statement(stmt), env); // No need to evaluate any more statements from a statements vector once we // get a return keyword. nothing after in the block matters. - if let Some(out) = out.clone() { + if let Some(out) = out.as_ref() { match out { - Object::ReturnValue(v) => return Some(*v), - Object::Error(_) => return Some(out), + Object::ReturnValue(v) => return Some(*v.clone()), + Object::Error(_) => return Some(out.clone()), _ => {} } } @@ -77,11 +92,11 @@ impl TreeWalker { out } - fn eval_block_statement(&self, bs: BlockStatement) -> Option { + fn eval_block_statement(&self, bs: BlockStatement, env: &mut Environment) -> Option { let mut out: Option = None; for stmt in bs.statements { - out = self.eval(Node::Statement(stmt)); + out = self.eval(Node::Statement(stmt), env); // TODO: Find a nicer way to do this. :( // The objective here is, @@ -99,12 +114,12 @@ impl TreeWalker { // But in reality that shouldn't happen. It should be returning 10 // So, We don't unwrap the ReturnValue node when we encounter 10. Just return it as is and it is later eval-ed // and the correct value is returned - if let Some(out) = out.clone() { + if let Some(out) = out.as_ref() { match out { - Object::ReturnValue(v) => { - return Some(Object::ReturnValue(v)); + Object::ReturnValue(_) => { + return Some(out.clone()); } - Object::Error(_) => return Some(out), + Object::Error(_) => return Some(out.clone()), _ => {} } } @@ -177,4 +192,11 @@ impl TreeWalker { _ => true, } } + + fn eval_identifier(&self, node: Identifier, env: &mut Environment) -> Option { + env.get(&node.to_string()).or(Some(Object::Error(format!( + "identifier not found: {}", + node.to_string() + )))) + } } diff --git a/src/parser/ast/mod.rs b/src/parser/ast/mod.rs index 1e35288..331e7ea 100644 --- a/src/parser/ast/mod.rs +++ b/src/parser/ast/mod.rs @@ -68,9 +68,9 @@ impl Display for Statement { #[derive(Debug, PartialEq)] pub struct LetStatement { // name field is to store the identifier of the binding - name: Identifier, + pub name: Identifier, // value is to store the expression that'll produce value - value: Option, + pub value: Option, } impl LetStatement { diff --git a/src/repl.rs b/src/repl.rs index 60f86c8..4bcdc3a 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,6 +1,6 @@ use { crate::{ - evaluator::{tree_walker::TreeWalker, Evaluator}, + evaluator::{tree_walker::TreeWalker, Environment, Evaluator}, lexer::Lexer, parser::{ast::Node, Error as ParserError, Parser}, }, @@ -19,6 +19,7 @@ pub fn init() { } fn start(mut ip: R, mut out: W) { + let mut environment = Environment::new(); loop { out.write_all(PROMPT).unwrap(); out.flush().unwrap(); @@ -34,7 +35,7 @@ fn start(mut ip: R, mut out: W) { } let program = program.unwrap(); let evaluator = TreeWalker::new(); - let obj = evaluator.eval(Node::Program(program)); + let obj = evaluator.eval(Node::Program(program), &mut environment); if let Some(node) = obj { out.write_fmt(format_args!("{}\n", node.inspect())).unwrap(); out.flush().unwrap();