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.
|
# 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",
|
||||||
]
|
]
|
||||||
|
|
|
@ -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"
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
nightly-2020-05-31
|
|
|
@ -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);
|
||||||
|
|
|
@ -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()
|
||||||
|
))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user