Added eval support for let statements
This commit is contained in:
parent
73f84cc379
commit
697ca392b3
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -5,5 +5,5 @@ authors = ["ishanjain28 <ishanjain28@gmail.com>"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.10.5"
|
||||
lazy_static = "1.4.0"
|
||||
itertools = "0.8.2"
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
nightly-2020-05-31
|
|
@ -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<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)]
|
||||
|
@ -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);
|
||||
|
|
|
@ -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<Object> {
|
||||
fn eval(&self, node: Node, env: &mut Environment) -> Option<Object> {
|
||||
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<Object> {
|
||||
fn eval_program(&self, prg: Program, env: &mut Environment) -> Option<Object> {
|
||||
let mut out: Option<Object> = 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<Object> {
|
||||
fn eval_block_statement(&self, bs: BlockStatement, env: &mut Environment) -> Option<Object> {
|
||||
let mut out: Option<Object> = 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<Object> {
|
||||
env.get(&node.to_string()).or(Some(Object::Error(format!(
|
||||
"identifier not found: {}",
|
||||
node.to_string()
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Expression>,
|
||||
pub value: Option<Expression>,
|
||||
}
|
||||
|
||||
impl LetStatement {
|
||||
|
|
|
@ -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<R: BufRead, W: Write>(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<R: BufRead, W: Write>(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();
|
||||
|
|
Loading…
Reference in New Issue
Block a user