495 lines
18 KiB
Rust
495 lines
18 KiB
Rust
use crate::ast::*;
|
|
use crate::object::Object;
|
|
use crate::environment::Environment;
|
|
use crate::inbuilt::{get_inbuilt_function, eval_inbuilt_function};
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
use std::collections::HashMap;
|
|
|
|
pub fn eval_identifier(identifier: &Expression, env: &Rc<RefCell<Environment>>) -> Object
|
|
{
|
|
match identifier {
|
|
Expression::Identifier(i) => {
|
|
let id = env.borrow_mut().get(i.as_str());
|
|
if id.is_some() {
|
|
return id.unwrap();
|
|
}
|
|
|
|
let inbuilt_func = get_inbuilt_function(i.as_str());
|
|
if inbuilt_func.is_some() {
|
|
return inbuilt_func.unwrap();
|
|
}
|
|
}
|
|
_ => panic!("Expected identifier")
|
|
}
|
|
|
|
Object::Nil
|
|
}
|
|
|
|
pub fn eval_prefix_expression(prefix: &Prefix, expression: &Expression, env: &mut Rc<RefCell<Environment>>) -> Object {
|
|
let expr_val = eval_expression(expression, env);
|
|
match prefix {
|
|
Prefix::Minus => {
|
|
match expr_val {
|
|
Object::Integer(i) => Object::Integer(-1 * i),
|
|
_ => panic!("Invalid expression {} in prefix expression, expected integer", expr_val)
|
|
}
|
|
}
|
|
Prefix::Bang => {
|
|
match expr_val {
|
|
Object::Boolean(b) => Object::Boolean(!b),
|
|
_ => panic!("Invalid expression {} in prefix expression, expected boolean", expr_val)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn eval_infix_expression(infix: &Infix, left: &Expression, right: &Expression, env: &mut Rc<RefCell<Environment>>) -> Object {
|
|
let left_obj = eval_expression(left, env);
|
|
let right_obj = eval_expression(right, env);
|
|
|
|
match left_obj {
|
|
Object::Integer(i) => {
|
|
let left_int = i;
|
|
let right_int;
|
|
match right_obj {
|
|
Object::Integer(i) => {
|
|
right_int = i;
|
|
match infix {
|
|
Infix::Plus => Object::Integer(left_int + right_int),
|
|
Infix::Minus => Object::Integer(left_int - right_int),
|
|
Infix::Asterisk => Object::Integer(left_int * right_int),
|
|
Infix::Slash => Object::Integer(left_int / right_int),
|
|
Infix::NotEq => Object::Boolean(left_int != right_int),
|
|
Infix::Eq => Object::Boolean(left_int == right_int),
|
|
Infix::Gt => Object::Boolean(left_int > right_int),
|
|
Infix::Lt => Object::Boolean(left_int < right_int),
|
|
_ => panic!("Inintid op {}", infix)
|
|
}
|
|
}
|
|
_ => panic!("Expected integer {}", right_obj),
|
|
}
|
|
}
|
|
Object::String(s) => {
|
|
let left_str = s;
|
|
let right_str;
|
|
match right_obj {
|
|
Object::String(s) => {
|
|
right_str = s;
|
|
match infix {
|
|
Infix::Plus => Object::String(left_str + &*right_str),
|
|
_ => panic!("Invalid op {} for strings", infix)
|
|
}
|
|
}
|
|
_ => panic!("Expected String{}", right_obj),
|
|
}
|
|
}
|
|
_ => panic!("Invalid value in expression {}, expected int", left_obj),
|
|
}
|
|
}
|
|
|
|
pub fn eval_block_statement(block: &BlockStatement, env: &mut Rc<RefCell<Environment>>) -> Object {
|
|
let mut val = Object::Nil;
|
|
for stmt in &block.stmts {
|
|
val = match stmt {
|
|
Statement::Let(x, expr) => eval_let_statement(x.to_string(), &*expr, env),
|
|
Statement::Return(Some(x)) => {
|
|
return eval_return_statement(&*x, env);
|
|
}
|
|
Statement::Return(None) => {
|
|
return Object::Nil;
|
|
}
|
|
Statement::Expression(expr) => eval_expression(&*expr, env),
|
|
}
|
|
}
|
|
val
|
|
}
|
|
|
|
pub fn eval_if_expression(expr: &Expression, true_block: &BlockStatement,
|
|
false_block: &Option<Box<BlockStatement>>, env: &mut Rc<RefCell<Environment>>) -> Object {
|
|
let expr_obj = eval_expression(expr, env);
|
|
let expr_val = match expr_obj {
|
|
Object::Boolean(v) => v,
|
|
_ => panic!("Expected boolean expression in if statement, found {}", expr_obj)
|
|
};
|
|
|
|
if expr_val {
|
|
eval_block_statement(true_block, env)
|
|
} else if false_block.is_some() {
|
|
eval_block_statement(false_block.as_ref().unwrap().as_ref(), env)
|
|
} else {
|
|
Object::Nil
|
|
}
|
|
}
|
|
|
|
pub fn eval_function_parameters(params: &Vec<Expression>, env: &mut Rc<RefCell<Environment>>) -> Vec<Object> {
|
|
let mut param_objs = vec![];
|
|
for param in params.iter() {
|
|
let param_obj = eval_expression(param, env);
|
|
if param_obj == Object::Nil {
|
|
panic!("Unable to evaluate parameter {}", param);
|
|
}
|
|
param_objs.push(eval_expression(param, env))
|
|
}
|
|
param_objs
|
|
}
|
|
|
|
pub fn eval_user_defined_function_call(func_params: &Vec<String>, param_objs: &Vec<Object>,
|
|
func_block: &BlockStatement,
|
|
env: &mut Rc<RefCell<Environment>>) -> Object {
|
|
let mut func_new_env = Rc::new(RefCell::new(Environment::extend(env.clone())));
|
|
|
|
let mut idx = 0;
|
|
while idx < param_objs.len() {
|
|
func_new_env.borrow_mut().set(&*func_params[idx], param_objs[idx].clone());
|
|
idx += 1;
|
|
}
|
|
|
|
eval_block_statement(&func_block, &mut func_new_env)
|
|
}
|
|
|
|
pub fn eval_dict_literal(dict_expr: &Vec<(Expression, Expression)>,
|
|
env: &mut Rc<RefCell<Environment>>) -> Object {
|
|
let mut dict = HashMap::new();
|
|
for (key_expr, val_expr) in dict_expr {
|
|
let key = eval_expression(key_expr, env);
|
|
let val = eval_expression(val_expr, env);
|
|
dict.insert(key, val);
|
|
}
|
|
|
|
Object::Dict(dict)
|
|
}
|
|
|
|
pub fn eval_array_literal(member_expr: &Vec<Expression>, env: &mut Rc<RefCell<Environment>>) -> Object {
|
|
let mut members = vec![];
|
|
for mem in member_expr.iter() {
|
|
members.push(eval_expression(mem, env));
|
|
}
|
|
Object::Array(members)
|
|
}
|
|
|
|
pub fn eval_arr_idx(arr: &Vec<Object>, idx: &Object) -> Object {
|
|
match idx {
|
|
Object::Integer(index) => {
|
|
if *index as usize > arr.len() - 1 {
|
|
panic!("Array index {} greater that array size {}", idx, arr.len());
|
|
}
|
|
|
|
let obj = arr.get(*index as usize).unwrap().clone();
|
|
obj
|
|
}
|
|
_ => panic!("Invalid array index {}, expected a positive integer", idx),
|
|
}
|
|
}
|
|
|
|
pub fn eval_dict_idx(dict: &HashMap<Object, Object>, idx: &Object) -> Object {
|
|
let mut ret_val = Object::Nil;
|
|
if dict.contains_key(idx) {
|
|
ret_val = dict.get(idx).unwrap().clone();
|
|
}
|
|
ret_val
|
|
}
|
|
|
|
pub fn eval_index(container_expr: &Box<Expression>, idx_expr: &Box<Expression>,
|
|
env: &mut Rc<RefCell<Environment>>) -> Object {
|
|
let container = eval_expression(container_expr, env);
|
|
let idx = eval_expression(idx_expr, env);
|
|
|
|
match container {
|
|
Object::Array(arr) => eval_arr_idx(&arr, &idx),
|
|
Object::Dict(dict) => eval_dict_idx(&dict, &idx),
|
|
_ => panic!("Expected array or dictionary got {}", container.to_string()),
|
|
}
|
|
}
|
|
|
|
pub fn eval_function_call(func_expr: &Box<Expression>, parameters: &Vec<Expression>,
|
|
env: &mut Rc<RefCell<Environment>>) -> Object {
|
|
let func_obj = eval_expression(func_expr, env);
|
|
let param_objs = eval_function_parameters(parameters, env);
|
|
|
|
match func_obj {
|
|
Object::FunctionLiteral(params, block, mut func_env) =>
|
|
{
|
|
if param_objs.len() != params.len() {
|
|
panic!("Did not find the expected number of arguments for the function");
|
|
}
|
|
eval_user_defined_function_call(¶ms, ¶m_objs, &block, &mut func_env)
|
|
}
|
|
Object::FunctionInBuilt(_) => eval_inbuilt_function(&func_obj, ¶m_objs),
|
|
_ => panic!("Invalid object type {}, expected function object", func_obj)
|
|
}
|
|
}
|
|
|
|
pub fn eval_expression(expr: &Expression, env: &mut Rc<RefCell<Environment>>) -> Object {
|
|
match expr {
|
|
Expression::IntegerLiteral(i) => Object::Integer(*i),
|
|
Expression::Identifier(_s) => eval_identifier(expr, env),
|
|
Expression::String(s) => Object::String(s.to_string()),
|
|
Expression::Boolean(b) => Object::Boolean(*b),
|
|
Expression::Prefix(prefix, expr) =>
|
|
eval_prefix_expression(prefix, expr, env),
|
|
Expression::Infix(infix, left, right) =>
|
|
eval_infix_expression(infix, left, right, env),
|
|
Expression::If(expr, true_block, false_block) =>
|
|
eval_if_expression(expr, true_block, false_block, env),
|
|
Expression::ArrayLiteral(arr) => eval_array_literal(arr, env),
|
|
Expression::DictionaryLiteral(dict) => eval_dict_literal(dict, env),
|
|
Expression::Index(arr, idx) => eval_index(arr, idx, env),
|
|
Expression::FunctionLiteral(params, block) =>
|
|
Object::FunctionLiteral(params.clone(), *block.clone(), env.clone()),
|
|
Expression::Call(func, params) => eval_function_call(func, params, env),
|
|
}
|
|
}
|
|
|
|
pub fn eval_let_statement(identifier: String, expr: &Expression, env: &mut Rc<RefCell<Environment>>) -> Object {
|
|
let expr_val = eval_expression(expr, env);
|
|
env.borrow_mut().set(identifier.as_str(), expr_val);
|
|
Object::Nil
|
|
}
|
|
|
|
pub fn eval_return_statement(expr: &Expression, env: &mut Rc<RefCell<Environment>>) -> Object {
|
|
eval_expression(expr, env)
|
|
}
|
|
|
|
pub fn eval_program(program: &Program, env: &mut Rc<RefCell<Environment>>) -> Object
|
|
{
|
|
let mut val = Object::Nil;
|
|
for stmt in &program.stmts {
|
|
val = match stmt {
|
|
Statement::Let(id, expr) => eval_let_statement(id.to_string(), &*expr, env),
|
|
Statement::Return(Some(expr)) => { return eval_return_statement(&*expr, env); }
|
|
Statement::Return(None) => { return Object::Nil; }
|
|
Statement::Expression(expr) => eval_expression(&*expr, env),
|
|
};
|
|
}
|
|
return val;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::lexer::Lexer;
|
|
use crate::parser::Parser;
|
|
use crate::object::Object;
|
|
use crate::environment::Environment;
|
|
use crate::evaluator::*;
|
|
|
|
fn test_eval_program(input: &str) -> Object {
|
|
let mut env = Rc::new(RefCell::new(Environment::new()));
|
|
let lexer = Lexer::new(input);
|
|
let mut parser = Parser::new(lexer);
|
|
let program = parser.parse_program().unwrap();
|
|
eval_program(program.as_ref(), &mut env)
|
|
}
|
|
|
|
struct TestCase<'a> {
|
|
test_str: &'a str,
|
|
val: Object,
|
|
}
|
|
|
|
fn check_test_cases(test_cases: Vec<TestCase>) {
|
|
for test_case in test_cases {
|
|
assert_eq!(test_eval_program(test_case.test_str), test_case.val);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_eval_integer() {
|
|
let test_cases = vec![TestCase { test_str: "10", val: Object::Integer(10) }];
|
|
check_test_cases(test_cases);
|
|
}
|
|
|
|
#[test]
|
|
fn test_eval_boolean() {
|
|
let test_cases = vec![
|
|
TestCase { test_str: "true", val: Object::Boolean(true) },
|
|
TestCase { test_str: "false", val: Object::Boolean(false) },
|
|
];
|
|
check_test_cases(test_cases);
|
|
}
|
|
|
|
#[test]
|
|
fn test_eval_return() {
|
|
let test_cases = vec![
|
|
TestCase { test_str: "return 0;", val: Object::Integer(0) },
|
|
TestCase { test_str: "return;", val: Object::Nil },
|
|
];
|
|
|
|
check_test_cases(test_cases);
|
|
}
|
|
|
|
#[test]
|
|
fn test_eval_prefix() {
|
|
let test_cases = vec![
|
|
TestCase { test_str: "!true", val: Object::Boolean(false) },
|
|
TestCase { test_str: "!false", val: Object::Boolean(true) },
|
|
TestCase { test_str: "-1", val: Object::Integer(-1) }
|
|
];
|
|
|
|
check_test_cases(test_cases);
|
|
}
|
|
|
|
#[test]
|
|
fn test_eval_infix() {
|
|
let test_cases = vec![
|
|
TestCase { test_str: "10 > 20", val: Object::Boolean(false) },
|
|
TestCase { test_str: "-1 + 0", val: Object::Integer(-1) },
|
|
TestCase { test_str: "5 > 3", val: Object::Boolean(true) },
|
|
TestCase { test_str: "4 < 2", val: Object::Boolean(false) },
|
|
TestCase { test_str: "5 == 3", val: Object::Boolean(false) },
|
|
TestCase { test_str: "4 != 2", val: Object::Boolean(true) },
|
|
TestCase { test_str: "(2*2 + 1) == 5", val: Object::Boolean(true) },
|
|
TestCase { test_str: "(2 + 3)*2 == 10", val: Object::Boolean(true) },
|
|
TestCase { test_str: "\"ab\" + \"cd\"", val: Object::String(String::from("abcd")) },
|
|
TestCase {
|
|
test_str: "\"ab\" + \"cd\" + \"ef\"",
|
|
val: Object::String(String::from("abcdef")),
|
|
},
|
|
];
|
|
|
|
check_test_cases(test_cases);
|
|
}
|
|
|
|
#[test]
|
|
fn test_eval_if_expr() {
|
|
let test_cases = vec![
|
|
TestCase { test_str: "if (10 > 20) {20} else {10}", val: Object::Integer(10) },
|
|
TestCase { test_str: "if (21 > 20) {20} else {10}", val: Object::Integer(20) },
|
|
TestCase { test_str: "if (21 > 20) {20} ", val: Object::Integer(20) },
|
|
TestCase { test_str: "if (21 < 20) {20} ", val: Object::Nil },
|
|
TestCase { test_str: "if (21 > 20) {let x = 30; 20} ", val: Object::Integer(20) },
|
|
];
|
|
|
|
check_test_cases(test_cases);
|
|
}
|
|
|
|
#[test]
|
|
fn test_eval_let_statements() {
|
|
let test_cases = vec![
|
|
TestCase { test_str: "let x = 10; if (x > 20) {20} else {10}", val: Object::Integer(10) },
|
|
TestCase { test_str: "let y = 20; if (21 > y) {20} else {10}", val: Object::Integer(20) },
|
|
TestCase { test_str: "let z = 30; z*z; ", val: Object::Integer(900) },
|
|
TestCase { test_str: "let a = 30; let b = 40; a + b", val: Object::Integer(70) },
|
|
];
|
|
|
|
check_test_cases(test_cases);
|
|
}
|
|
|
|
#[test]
|
|
fn test_eval_functions() {
|
|
let test_cases = vec![
|
|
TestCase {
|
|
test_str: "let sum = fn(x, y){ x + y;}; \
|
|
sum(10, 20);",
|
|
val: Object::Integer(30),
|
|
},
|
|
TestCase {
|
|
test_str: "let square = fn(x){x*x}; \
|
|
square(10)",
|
|
val: Object::Integer(100),
|
|
},
|
|
TestCase {
|
|
test_str: "let gt = fn(x, y){ \
|
|
if (!(x > y)) {\
|
|
x*x\
|
|
} else {\
|
|
y\
|
|
};\
|
|
}; \
|
|
gt(3, 2)",
|
|
val: Object::Integer(2),
|
|
},
|
|
TestCase {
|
|
test_str: "let gt = fn(x, y){ \
|
|
if (x > y) \
|
|
{x*x} \
|
|
else {\
|
|
y\
|
|
};\
|
|
}; \
|
|
gt(3, 2)",
|
|
val: Object::Integer(9),
|
|
},
|
|
TestCase {
|
|
test_str: "let fact = fn(x) {\
|
|
if (x > 1) {\
|
|
x*fact(x - 1);\
|
|
} else {\
|
|
x\
|
|
};\
|
|
}; \
|
|
fact(8);",
|
|
val: Object::Integer(40320),
|
|
},
|
|
TestCase {
|
|
test_str: "let sum = fn(x,y){x + y;};\
|
|
let sqr = fn(x){let z = sum(x, x); z*z;};\
|
|
let z = sum(2, 3) + sqr(2);\
|
|
z;",
|
|
val: Object::Integer(21),
|
|
},
|
|
TestCase { test_str: "fn(x, y, z){x + y + z}(1, 2, 3);", val: Object::Integer(6) },
|
|
TestCase {
|
|
test_str: "let a = \"wx\";\
|
|
let b = \"yz\";\
|
|
let z = a + b;\
|
|
z;",
|
|
val: Object::String(String::from("wxyz")),
|
|
},
|
|
];
|
|
|
|
check_test_cases(test_cases);
|
|
}
|
|
|
|
#[test]
|
|
fn test_closures() {
|
|
let test_cases = vec![
|
|
TestCase {
|
|
test_str: "let adder = fn(x){fn(x,y) { x + y; };};\
|
|
let a2 = adder(2);
|
|
a2(10);",
|
|
val: Object::Integer(12),
|
|
},
|
|
];
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn test_eval_inbuilt_functions() {
|
|
let test_cases = vec![
|
|
TestCase { test_str: "let x = \"cartman\"; len(x)", val: Object::Integer(7) },
|
|
TestCase { test_str: "len(\"cartman\");", val: Object::Integer(7) },
|
|
];
|
|
|
|
check_test_cases(test_cases);
|
|
}
|
|
|
|
#[test]
|
|
fn test_eval_arrays() {
|
|
let test_cases = vec![
|
|
TestCase { test_str: "let x = [1, 2, 3]; x[2]", val: Object::Integer(3) },
|
|
TestCase { test_str: "let x = [1, 2, [1, 3]]; x[2][0]", val: Object::Integer(1) },
|
|
TestCase {
|
|
test_str: "let x = [1, \"abc\", 32, 43]; x[1]",
|
|
val: Object::String(String::from("abc")),
|
|
},
|
|
];
|
|
|
|
check_test_cases(test_cases);
|
|
}
|
|
|
|
#[test]
|
|
fn test_eval_dict() {
|
|
let test_cases = vec![
|
|
TestCase { test_str: "let x = {1: 2, 2: 3, 3: 4}; x[2]", val: Object::Integer(3)},
|
|
TestCase { test_str: "{1: 2, 2: 3, 3: 4}[2]", val: Object::Integer(3)},
|
|
TestCase { test_str: "let x = {1: 2, \"test\": 3, 3: 4}; x[\"test\"]",
|
|
val: Object::Integer(3)},
|
|
TestCase { test_str: "let s = 3; let x = {1: 2, \"test\": 3, 3: (2*10)}; x[s]",
|
|
val: Object::Integer(20)},
|
|
TestCase { test_str: "let s = \"test\"; let x = {1: 2, \"test\": \"res\", 3: (2*10)}; x[s]",
|
|
val: Object::String(String::from("res"))},
|
|
];
|
|
|
|
check_test_cases(test_cases);
|
|
}
|
|
} |