1
0
Fork 0

Added Tree walker evaluator

1. Added a tree walker interpreter
2. Added tests to support evaluator
This commit is contained in:
Ishan Jain 2020-01-21 23:20:14 +05:30
parent b0cbe5d4f7
commit 22479487ca
6 changed files with 263 additions and 16 deletions

View File

@ -6,5 +6,4 @@ edition = "2018"
[dependencies]
lazy_static = "1.4.0"
itertools = "0.8.2"
itertools = "0.8.2"

135
src/evaluator/mod.rs Normal file
View File

@ -0,0 +1,135 @@
use crate::parser::ast::Node;
pub mod tree_walker;
pub trait Evaluator {
fn eval(&self, node: Node) -> Option<Object>;
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Object {
Integer(i64),
Boolean(bool),
Null,
}
const NULL: Object = Object::Null;
const TRUE: Object = Object::Boolean(true);
const FALSE: Object = Object::Boolean(false);
impl Object {
pub fn inspect(&self) -> String {
match self {
Object::Integer(v) => v.to_string(),
Object::Boolean(v) => v.to_string(),
Object::Null => "NULL".into(),
}
}
}
#[cfg(test)]
mod tests {
use crate::{
evaluator::{tree_walker::TreeWalker, Evaluator, Object},
lexer::Lexer,
parser::{ast::Node, Parser},
};
#[test]
fn eval_integer_expression() {
let test_cases = [
("5", Some(Object::Integer(5))),
("10", Some(Object::Integer(10))),
];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
assert!(program.is_some());
let program = program.unwrap();
let evaluator = TreeWalker::new();
let eval = evaluator.eval(Node::Program(program));
assert_eq!(eval, test.1);
}
}
#[test]
fn eval_boolean_expression() {
let test_cases = [
("true", Some(Object::Boolean(true))),
("false", Some(Object::Boolean(false))),
("3 < 4", Some(Object::Boolean(true))),
("3 > 4", Some(Object::Boolean(false))),
("3 == 4", Some(Object::Boolean(false))),
("3 != 4", Some(Object::Boolean(true))),
("(1 < 2 ) == true", Some(Object::Boolean(true))),
("(1 < 2 ) == false", Some(Object::Boolean(false))),
("(1 > 2 ) == true", Some(Object::Boolean(false))),
("(1 > 2 ) == false", Some(Object::Boolean(true))),
];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
assert!(program.is_some());
let program = program.unwrap();
let evaluator = TreeWalker::new();
let eval = evaluator.eval(Node::Program(program));
assert_eq!(eval, test.1);
}
}
#[test]
fn eval_bang_operator() {
let test_cases = [
("!5", Some(Object::Boolean(false))),
("!!true", Some(Object::Boolean(true))),
("!!false", Some(Object::Boolean(false))),
("!!5", Some(Object::Boolean(true))),
("!true", Some(Object::Boolean(false))),
("!false", Some(Object::Boolean(true))),
];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
assert!(program.is_some());
let program = program.unwrap();
let evaluator = TreeWalker::new();
let eval = evaluator.eval(Node::Program(program));
assert_eq!(eval, test.1);
}
}
#[test]
fn eval_integer_literal_expression() {
let test_cases = [
("5", Some(Object::Integer(5))),
("10", Some(Object::Integer(10))),
("-5", Some(Object::Integer(-5))),
("-10", Some(Object::Integer(-10))),
("5 + 5 + 5 + 5 - 10", Some(Object::Integer(10))),
("2 * 2 * 2 * 2 * 2", Some(Object::Integer(32))),
("-50 + 100 + -50", Some(Object::Integer(0))),
("5 * 2 + 10 ", Some(Object::Integer(20))),
("5 + 2 * 10", Some(Object::Integer(25))),
("20 + 2 * -10", Some(Object::Integer(0))),
("50 / 2 * 2 + 10", Some(Object::Integer(60))),
("2 * (5 + 10)", Some(Object::Integer(30))),
("3 * (3 * 3) + 10", Some(Object::Integer(37))),
("(5 + 10 * 2 + 15 / 3) * 2 + -10", Some(Object::Integer(50))),
];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
assert!(program.is_some());
let program = program.unwrap();
let evaluator = TreeWalker::new();
let eval = evaluator.eval(Node::Program(program));
assert_eq!(eval, test.1);
}
}
}

View File

@ -0,0 +1,102 @@
use crate::{
evaluator::{Evaluator, Object, FALSE, NULL, TRUE},
lexer::TokenType,
parser::ast::{Expression, ExpressionStatement, Node, Statement},
};
pub struct TreeWalker;
impl TreeWalker {
pub fn new() -> impl Evaluator {
Self
}
}
impl Evaluator for TreeWalker {
fn eval(&self, node: Node) -> Option<Object> {
match node {
Node::Program(p) => self.eval_statements(p.statements),
Node::Statement(stmt) => match stmt {
Statement::ExpressionStatement(ExpressionStatement { expression, .. }) => {
self.eval(Node::Expression(expression))
}
_ => None,
},
Node::Expression(expr) => match expr {
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))?;
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))?;
self.eval_infix_expression(left, ie.operator, right)
}
_ => None,
},
}
}
}
impl TreeWalker {
fn eval_statements(&self, stmts: Vec<Statement>) -> Option<Object> {
let mut out: Option<Object> = None;
for stmt in stmts {
out = self.eval(Node::Statement(stmt))
}
out
}
fn eval_prefix_expression(&self, operator: TokenType, expr: Object) -> Option<Object> {
match operator {
TokenType::Bang => Some(self.eval_bang_operator_expression(expr)),
TokenType::Minus => Some(self.eval_minus_prefix_operator_expression(expr)),
_ => None,
}
}
fn eval_infix_expression(
&self,
left: Object,
operator: TokenType,
right: Object,
) -> Option<Object> {
Some(match (left, right) {
(Object::Integer(l), Object::Integer(r)) => match operator {
TokenType::Plus => Object::Integer(l + r),
TokenType::Minus => Object::Integer(l - r),
TokenType::Asterisk => Object::Integer(l * r),
TokenType::Slash => Object::Integer(l / r),
TokenType::Equals => Object::Boolean(l == r),
TokenType::NotEquals => Object::Boolean(l != r),
TokenType::GreaterThan => Object::Boolean(l > r),
TokenType::LessThan => Object::Boolean(l < r),
_ => NULL,
},
_ => match operator {
TokenType::Equals => Object::Boolean(left == right),
TokenType::NotEquals => Object::Boolean(left != right),
_ => NULL,
},
})
}
fn eval_bang_operator_expression(&self, expr: Object) -> Object {
match expr {
TRUE => FALSE,
FALSE => TRUE,
NULL => TRUE,
_ => FALSE,
}
}
fn eval_minus_prefix_operator_expression(&self, expr: Object) -> Object {
match expr {
Object::Integer(v) => Object::Integer(-v),
_ => NULL,
}
}
}

View File

@ -1,6 +1,7 @@
#[macro_use]
extern crate lazy_static;
mod evaluator;
mod lexer;
mod parser;
mod repl;

View File

@ -16,6 +16,13 @@ pub struct Program {
pub statements: Vec<Statement>,
}
#[derive(Debug)]
pub enum Node {
Program(Program),
Statement(Statement),
Expression(Expression),
}
impl Display for Program {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
let mut out = String::new();
@ -143,7 +150,7 @@ impl Display for ReturnStatement {
#[derive(Debug, PartialEq)]
pub struct ExpressionStatement {
token: Token,
expression: Expression,
pub expression: Expression,
}
impl ExpressionStatement {
@ -287,7 +294,7 @@ impl Display for Identifier {
#[derive(Debug, PartialEq)]
pub struct IntegerLiteral {
value: i64,
pub value: i64,
}
impl IntegerLiteral {
@ -310,8 +317,8 @@ impl IntegerLiteral {
#[derive(Debug, PartialEq)]
pub struct PrefixExpression {
operator: TokenType,
right: Box<Expression>,
pub operator: TokenType,
pub right: Box<Expression>,
}
impl PrefixExpression {
@ -344,9 +351,9 @@ impl Display for PrefixExpression {
#[derive(Debug, PartialEq)]
pub struct InfixExpression {
left: Box<Expression>,
operator: TokenType,
right: Box<Expression>,
pub left: Box<Expression>,
pub operator: TokenType,
pub right: Box<Expression>,
}
impl InfixExpression {
@ -381,7 +388,7 @@ impl Display for InfixExpression {
#[derive(Debug, PartialEq)]
pub struct BooleanExpression {
token: TokenType,
value: bool,
pub value: bool,
}
impl BooleanExpression {

View File

@ -1,6 +1,7 @@
use crate::{
evaluator::{tree_walker::TreeWalker, Evaluator},
lexer::Lexer,
parser::{Error as ParserError, Parser},
parser::{ast::Node, Error as ParserError, Parser},
};
use std::io::{self, BufRead, Result as IoResult, Write};
@ -29,11 +30,13 @@ fn start<R: BufRead, W: Write>(mut ip: R, mut out: W) {
print_parser_errors(&mut out, &parser.errors).unwrap();
continue;
}
if let Some(program) = program {
for stmt in &program.statements {
out.write_fmt(format_args!("{}\n", stmt)).unwrap();
}
};
let program = program.unwrap();
let evaluator = TreeWalker::new();
let obj = evaluator.eval(Node::Program(program));
if let Some(node) = obj {
out.write_fmt(format_args!("{}\n", node.inspect())).unwrap();
out.flush().unwrap();
}
}
}