Added eval support for let statements

This commit is contained in:
Ishan Jain 2024-05-17 16:50:02 +05:30
parent 73f84cc379
commit 697ca392b3
Signed by: ishan
GPG Key ID: 0506DB2A1CC75C27
7 changed files with 105 additions and 37 deletions

10
Cargo.lock generated
View File

@ -1,16 +1,18 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3
[[package]] [[package]]
name = "either" name = "either"
version = "1.5.3" version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.8.2" version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [ dependencies = [
"either", "either",
] ]

View File

@ -5,5 +5,5 @@ authors = ["ishanjain28 <ishanjain28@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
itertools = "0.10.5"
lazy_static = "1.4.0" lazy_static = "1.4.0"
itertools = "0.8.2"

View File

@ -1 +0,0 @@
nightly-2020-05-31

View File

@ -1,11 +1,35 @@
use { use {
crate::parser::ast::Node, 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 mod tree_walker;
pub trait Evaluator { pub trait Evaluator {
fn eval(&self, node: Node) -> Option<Object>; fn eval(&self, node: Node, env: &mut Environment) -> Option<Object>;
}
pub struct Environment {
store: HashMap<String, Object>,
}
impl Environment {
pub fn new() -> Self {
Self {
store: HashMap::new(),
}
}
pub fn get(&self, name: &str) -> Option<Object> {
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)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -48,7 +72,7 @@ impl Display for Object {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
evaluator::{tree_walker::TreeWalker, Evaluator, Object, FALSE, NULL, TRUE}, evaluator::{tree_walker::TreeWalker, Environment, Evaluator, Object, FALSE, NULL, TRUE},
lexer::Lexer, lexer::Lexer,
parser::{ast::Node, Parser}, parser::{ast::Node, Parser},
}; };
@ -61,7 +85,8 @@ mod tests {
assert!(program.is_some()); assert!(program.is_some());
let program = program.unwrap(); let program = program.unwrap();
let evaluator = TreeWalker::new(); 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); assert_eq!(eval, test.1);
} }
} }
@ -209,6 +234,25 @@ mod tests {
}", }",
Some(Object::Error("unknown operator: BOOLEAN + BOOLEAN".into())), 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); run_test_cases(&test_cases);

View File

@ -4,9 +4,14 @@
use crate::{ use crate::{
evaluator::{Evaluator, Object, FALSE, NULL, TRUE}, evaluator::{Evaluator, Object, FALSE, NULL, TRUE},
lexer::TokenType, 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; pub struct TreeWalker;
impl TreeWalker { impl TreeWalker {
@ -16,39 +21,49 @@ impl TreeWalker {
} }
impl Evaluator for TreeWalker { impl Evaluator for TreeWalker {
fn eval(&self, node: Node) -> Option<Object> { fn eval(&self, node: Node, env: &mut Environment) -> Option<Object> {
match node { match node {
Node::Program(p) => self.eval_program(p), Node::Program(p) => self.eval_program(p, env),
Node::Statement(stmt) => match stmt { Node::Statement(stmt) => match stmt {
Statement::ExpressionStatement(ExpressionStatement { expression, .. }) => { 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) => { 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))) 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, _ => None,
}, },
Node::Expression(expr) => match expr { Node::Expression(expr) => match expr {
Expression::Identifier(v) => self.eval_identifier(v, env),
Expression::IntegerLiteral(il) => Some(Object::Integer(il.value)), Expression::IntegerLiteral(il) => Some(Object::Integer(il.value)),
Expression::BooleanExpression(b) => Some(Object::Boolean(b.value)), Expression::BooleanExpression(b) => Some(Object::Boolean(b.value)),
Expression::PrefixExpression(p) => { 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) self.eval_prefix_expression(p.operator, expr)
} }
Expression::InfixExpression(ie) => { Expression::InfixExpression(ie) => {
let left = self.eval(Node::Expression(*ie.left))?; let left = self.eval(Node::Expression(*ie.left), env)?;
let right = self.eval(Node::Expression(*ie.right))?; let right = self.eval(Node::Expression(*ie.right), env)?;
self.eval_infix_expression(left, ie.operator, right) self.eval_infix_expression(left, ie.operator, right)
} }
Expression::IfExpression(ie) => { 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) { 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 { } else if let Some(alternative) = ie.alternative {
self.eval(Node::Statement(Statement::BlockStatement(alternative))) self.eval(Node::Statement(Statement::BlockStatement(alternative)), env)
} else { } else {
Some(NULL) Some(NULL)
} }
@ -60,16 +75,16 @@ impl Evaluator for TreeWalker {
} }
impl TreeWalker { impl TreeWalker {
fn eval_program(&self, prg: Program) -> Option<Object> { fn eval_program(&self, prg: Program, env: &mut Environment) -> Option<Object> {
let mut out: Option<Object> = None; let mut out: Option<Object> = None;
for stmt in prg.statements { 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 // No need to evaluate any more statements from a statements vector once we
// get a return keyword. nothing after in the block matters. // 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 { match out {
Object::ReturnValue(v) => return Some(*v), Object::ReturnValue(v) => return Some(*v.clone()),
Object::Error(_) => return Some(out), Object::Error(_) => return Some(out.clone()),
_ => {} _ => {}
} }
} }
@ -77,11 +92,11 @@ impl TreeWalker {
out out
} }
fn eval_block_statement(&self, bs: BlockStatement) -> Option<Object> { fn eval_block_statement(&self, bs: BlockStatement, env: &mut Environment) -> Option<Object> {
let mut out: Option<Object> = None; let mut out: Option<Object> = None;
for stmt in bs.statements { 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. :( // TODO: Find a nicer way to do this. :(
// The objective here is, // The objective here is,
@ -99,12 +114,12 @@ impl TreeWalker {
// But in reality that shouldn't happen. It should be returning 10 // 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 // 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 // and the correct value is returned
if let Some(out) = out.clone() { if let Some(out) = out.as_ref() {
match out { match out {
Object::ReturnValue(v) => { Object::ReturnValue(_) => {
return Some(Object::ReturnValue(v)); return Some(out.clone());
} }
Object::Error(_) => return Some(out), Object::Error(_) => return Some(out.clone()),
_ => {} _ => {}
} }
} }
@ -177,4 +192,11 @@ impl TreeWalker {
_ => true, _ => true,
} }
} }
fn eval_identifier(&self, node: Identifier, env: &mut Environment) -> Option<Object> {
env.get(&node.to_string()).or(Some(Object::Error(format!(
"identifier not found: {}",
node.to_string()
))))
}
} }

View File

@ -68,9 +68,9 @@ impl Display for Statement {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct LetStatement { pub struct LetStatement {
// name field is to store the identifier of the binding // 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 is to store the expression that'll produce value
value: Option<Expression>, pub value: Option<Expression>,
} }
impl LetStatement { impl LetStatement {

View File

@ -1,6 +1,6 @@
use { use {
crate::{ crate::{
evaluator::{tree_walker::TreeWalker, Evaluator}, evaluator::{tree_walker::TreeWalker, Environment, Evaluator},
lexer::Lexer, lexer::Lexer,
parser::{ast::Node, Error as ParserError, Parser}, parser::{ast::Node, Error as ParserError, Parser},
}, },
@ -19,6 +19,7 @@ pub fn init() {
} }
fn start<R: BufRead, W: Write>(mut ip: R, mut out: W) { fn start<R: BufRead, W: Write>(mut ip: R, mut out: W) {
let mut environment = Environment::new();
loop { loop {
out.write_all(PROMPT).unwrap(); out.write_all(PROMPT).unwrap();
out.flush().unwrap(); out.flush().unwrap();
@ -34,7 +35,7 @@ fn start<R: BufRead, W: Write>(mut ip: R, mut out: W) {
} }
let program = program.unwrap(); let program = program.unwrap();
let evaluator = TreeWalker::new(); 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 { if let Some(node) = obj {
out.write_fmt(format_args!("{}\n", node.inspect())).unwrap(); out.write_fmt(format_args!("{}\n", node.inspect())).unwrap();
out.flush().unwrap(); out.flush().unwrap();