1
0
Fork 0

Added Return statement evaluator and basic error system

1. Added Return statement parser with tests
2. Added a very basic error reporting system in the evaluator
3. Removed duplicate code
This commit is contained in:
Ishan Jain 2020-01-23 20:53:06 +05:30
parent 62fd586acb
commit 9e04c61310
4 changed files with 197 additions and 168 deletions

View File

@ -1,14 +1,19 @@
use crate::parser::ast::Node;
use {
crate::parser::ast::Node,
std::fmt::{Display, Formatter, Result as FmtResult},
};
pub mod tree_walker;
pub trait Evaluator {
fn eval(&self, node: Node) -> Option<Object>;
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Object {
Integer(i64),
Boolean(bool),
ReturnValue(Box<Object>),
Error(String),
Null,
}
@ -21,11 +26,25 @@ impl Object {
match self {
Object::Integer(v) => v.to_string(),
Object::Boolean(v) => v.to_string(),
Object::ReturnValue(ret) => ret.inspect(),
Object::Error(s) => s.to_string(),
Object::Null => "NULL".into(),
}
}
}
impl Display for Object {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_str(match self {
Object::Integer(_) => "INTEGER",
Object::Boolean(_) => "BOOLEAN",
Object::ReturnValue(_) => "RETURN_VALUE",
Object::Error(_) => "ERROR",
Object::Null => "NULL",
})
}
}
#[cfg(test)]
mod tests {
use crate::{
@ -34,13 +53,7 @@ mod tests {
parser::{ast::Node, Parser},
};
#[test]
fn eval_integer_expression() {
let test_cases = [
("5", Some(Object::Integer(5))),
("10", Some(Object::Integer(10))),
];
fn run_test_cases(test_cases: &[(&str, Option<Object>)]) {
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
@ -53,6 +66,16 @@ mod tests {
}
}
#[test]
fn eval_integer_expression() {
let test_cases = [
("5", Some(Object::Integer(5))),
("10", Some(Object::Integer(10))),
];
run_test_cases(&test_cases);
}
#[test]
fn eval_boolean_expression() {
let test_cases = [
@ -67,17 +90,7 @@ mod tests {
("(1 > 2 ) == true", Some(FALSE)),
("(1 > 2 ) == false", Some(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);
}
run_test_cases(&test_cases);
}
#[test]
@ -90,16 +103,7 @@ mod tests {
("!true", Some(FALSE)),
("!false", Some(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);
}
run_test_cases(&test_cases);
}
#[test]
@ -121,16 +125,7 @@ mod tests {
("(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);
}
run_test_cases(&test_cases);
}
#[test]
@ -154,15 +149,68 @@ mod tests {
("if (1 < 2) {10} else {20}", 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);
}
run_test_cases(&test_cases);
}
#[test]
fn eval_return_statements() {
let test_cases = [
("return 10; ", Some(Object::Integer(10))),
("return 10; 9;", Some(Object::Integer(10))),
("return 2 * 5; 9;", Some(Object::Integer(10))),
("9; return 2 * 5; 9;", Some(Object::Integer(10))),
(
"if(10 > 1) {
if(10 > 2) {
return 10;
}
return 1;
}",
Some(Object::Integer(10)),
),
];
run_test_cases(&test_cases);
}
#[test]
fn error_handling() {
let test_cases = [
(
"-true",
Some(Object::Error("unknown operator: -BOOLEAN".into())),
),
(
"true + false;",
Some(Object::Error("unknown operator: BOOLEAN + BOOLEAN".into())),
),
(
"5 + true;",
Some(Object::Error("type mismatch: INTEGER + BOOLEAN".into())),
),
(
"5 + true; 5",
Some(Object::Error("type mismatch: INTEGER + BOOLEAN".into())),
),
(
"5; true + false; 5",
Some(Object::Error("unknown operator: BOOLEAN + BOOLEAN".into())),
),
(
"if (10>1){true + false;}",
Some(Object::Error("unknown operator: BOOLEAN + BOOLEAN".into())),
),
(
"if(10 > 1) {
if(10 > 2) {
return true + false;
}
return 1;
}",
Some(Object::Error("unknown operator: BOOLEAN + BOOLEAN".into())),
),
];
run_test_cases(&test_cases);
}
}

View File

@ -1,7 +1,10 @@
// TODO: This is all a mess. Almost certainly because right now, I don't know any better way to do this.
// It's just constantly unwrapping enums from one place and rewrapping it to some other enum(or even the same enum) and returning it
// The error handling story is pretty bad too
use crate::{
evaluator::{Evaluator, Object, FALSE, NULL, TRUE},
lexer::TokenType,
parser::ast::{Expression, ExpressionStatement, Node, Statement},
parser::ast::{BlockStatement, Expression, ExpressionStatement, Node, Program, Statement},
};
pub struct TreeWalker;
@ -15,12 +18,16 @@ impl TreeWalker {
impl Evaluator for TreeWalker {
fn eval(&self, node: Node) -> Option<Object> {
match node {
Node::Program(p) => self.eval_statements(p.statements),
Node::Program(p) => self.eval_program(p),
Node::Statement(stmt) => match stmt {
Statement::ExpressionStatement(ExpressionStatement { expression, .. }) => {
self.eval(Node::Expression(expression))
}
Statement::BlockStatement(bs) => self.eval_statements(bs.statements),
Statement::BlockStatement(bs) => self.eval_block_statement(bs),
Statement::Return(ret) => {
let ret_val = self.eval(Node::Expression(ret.value?))?;
Some(Object::ReturnValue(Box::new(ret_val)))
}
_ => None,
},
Node::Expression(expr) => match expr {
@ -53,10 +60,54 @@ impl Evaluator for TreeWalker {
}
impl TreeWalker {
fn eval_statements(&self, stmts: Vec<Statement>) -> Option<Object> {
fn eval_program(&self, prg: Program) -> Option<Object> {
let mut out: Option<Object> = None;
for stmt in stmts {
out = self.eval(Node::Statement(stmt))
for stmt in prg.statements {
out = self.eval(Node::Statement(stmt));
// 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() {
match out {
Object::ReturnValue(v) => return Some(*v),
Object::Error(_) => return Some(out),
_ => {}
}
}
}
out
}
fn eval_block_statement(&self, bs: BlockStatement) -> Option<Object> {
let mut out: Option<Object> = None;
for stmt in bs.statements {
out = self.eval(Node::Statement(stmt));
// TODO: Find a nicer way to do this. :(
// The objective here is,
// If we encounter a node of type ReturnValue, Don't unwrap it. Return it as is
// So, It can evaluated again by the eval function.
// This is helpful when we have a nested structure with multiple return statments.
// something like,
// if (true) {
// if (true) {
// return 10;
// }
// return 1;
// }
// This will return 1 if we unwrap return right here and return the value within.
// 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() {
match out {
Object::ReturnValue(v) => {
return Some(Object::ReturnValue(v));
}
Object::Error(_) => return Some(out),
_ => {}
}
}
}
out
}
@ -65,7 +116,10 @@ impl TreeWalker {
match operator {
TokenType::Bang => Some(self.eval_bang_operator_expression(expr)),
TokenType::Minus => Some(self.eval_minus_prefix_operator_expression(expr)),
_ => None,
_ => Some(Object::Error(format!(
"unknown operator: {}{}",
operator, expr
))),
}
}
@ -75,7 +129,7 @@ impl TreeWalker {
operator: TokenType,
right: Object,
) -> Option<Object> {
Some(match (left, right) {
Some(match (left.clone(), right.clone()) {
(Object::Integer(l), Object::Integer(r)) => match operator {
TokenType::Plus => Object::Integer(l + r),
TokenType::Minus => Object::Integer(l - r),
@ -85,12 +139,16 @@ impl TreeWalker {
TokenType::NotEquals => Object::Boolean(l != r),
TokenType::GreaterThan => Object::Boolean(l > r),
TokenType::LessThan => Object::Boolean(l < r),
_ => NULL,
_ => Object::Error(format!("unknown operator: {} {} {}", l, operator, r)),
},
(o1 @ _, o2 @ _) if o1.to_string() != o2.to_string() => {
Object::Error(format!("type mismatch: {} {} {}", o1, operator, o2))
}
_ => match operator {
TokenType::Equals => Object::Boolean(left == right),
TokenType::NotEquals => Object::Boolean(left != right),
_ => NULL,
_ => Object::Error(format!("unknown operator: {} {} {}", left, operator, right)),
},
})
}
@ -107,7 +165,7 @@ impl TreeWalker {
fn eval_minus_prefix_operator_expression(&self, expr: Object) -> Object {
match expr {
Object::Integer(v) => Object::Integer(-v),
_ => NULL,
v @ _ => Object::Error(format!("unknown operator: -{}", v)),
}
}

View File

@ -115,21 +115,19 @@ impl Display for LetStatement {
#[derive(Debug, PartialEq)]
pub struct ReturnStatement {
return_value: Option<Expression>,
pub value: Option<Expression>,
}
impl ReturnStatement {
pub fn new(expr: Expression) -> Self {
ReturnStatement {
return_value: Some(expr),
}
ReturnStatement { value: Some(expr) }
}
fn parse(parser: &mut Parser) -> Option<Self> {
let token = parser.lexer.next()?;
let expr = Expression::parse(parser, token, ExpressionPriority::Lowest);
parser.expect_peek(TokenType::Semicolon)?;
Some(ReturnStatement { return_value: expr })
Some(ReturnStatement { value: expr })
}
}
@ -137,7 +135,7 @@ impl Display for ReturnStatement {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
let mut out: String = TokenType::Return.to_string();
if let Some(v) = &self.return_value {
if let Some(v) = &self.value {
out.push(' ');
let a: String = v.into();
out.push_str(&a);
@ -660,12 +658,9 @@ mod tests {
))),
}),
Statement::Return(ReturnStatement {
return_value: Some(Expression::Identifier(Identifier::new(
TokenType::Int,
"5",
))),
value: Some(Expression::Identifier(Identifier::new(TokenType::Int, "5"))),
}),
Statement::Return(ReturnStatement { return_value: None }),
Statement::Return(ReturnStatement { value: None }),
],
};
assert_eq!(

View File

@ -181,6 +181,18 @@ mod tests {
}
}
fn check_test_cases(test_cases: &[(&str, Vec<Statement>)]) {
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some());
assert_eq!(program.unwrap().statements, test.1);
}
}
#[test]
fn let_statements() {
let test_cases = [(
@ -201,15 +213,7 @@ mod tests {
],
)];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some());
assert_eq!(program.unwrap().statements, test.1);
}
check_test_cases(&test_cases);
let fail_case = "let x 5; let 10; let 83838383;";
@ -241,30 +245,12 @@ mod tests {
],
)];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some());
assert_eq!(program.unwrap().statements, test.1);
}
let lexer = Lexer::new("return 5; return 10; return add(10);");
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some());
let program = program.unwrap();
assert_eq!(program.statements.len(), 3);
assert_eq!(parser.errors.len(), 0);
check_test_cases(&test_cases);
}
#[test]
fn identifier_expression() {
// TODO: Add more tests for this
let lexer = Lexer::new("foobar;");
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
@ -303,7 +289,7 @@ mod tests {
#[test]
fn prefix_expressions() {
let prefix_tests = [
let test_cases = [
(
"!5",
vec![Statement::ExpressionStatement(ExpressionStatement::new(
@ -372,20 +358,12 @@ mod tests {
),
];
for test in prefix_tests.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some());
assert_eq!(program.unwrap().statements, test.1);
}
check_test_cases(&test_cases);
}
#[test]
fn parsing_infix_expressions() {
let infix_tests = [
let test_cases = [
(
"5 + 10;",
vec![Statement::ExpressionStatement(ExpressionStatement::new(
@ -490,15 +468,7 @@ mod tests {
))],
),
];
for test in infix_tests.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some());
assert_eq!(program.unwrap().statements, test.1);
}
check_test_cases(&test_cases);
}
#[test]
@ -570,16 +540,7 @@ mod tests {
))],
),
];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some());
assert_eq!(program.unwrap().statements, test.1);
}
check_test_cases(&test_cases);
}
#[test]
fn if_expression() {
@ -604,15 +565,7 @@ mod tests {
))],
)];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some());
assert_eq!(program.unwrap().statements, test.1);
}
check_test_cases(&test_cases);
}
#[test]
fn if_else_expression() {
@ -641,16 +594,7 @@ mod tests {
)),
))],
)];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some());
assert_eq!(program.unwrap().statements, test.1);
}
check_test_cases(&test_cases);
}
#[test]
@ -762,15 +706,7 @@ mod tests {
),
];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some());
assert_eq!(program.unwrap().statements, test.1);
}
check_test_cases(&test_cases);
}
#[test]
fn call_expression_parsing() {
@ -910,15 +846,7 @@ mod tests {
),
];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some());
assert_eq!(program.unwrap().statements, test.1);
}
check_test_cases(&test_cases);
}
#[test]
fn call_expression_parsing_string() {