diff --git a/.gitignore b/.gitignore index 088ba6b..3a68cc6 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +.idea/ +notes.txt diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6893480 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "monkey" +version = "0.1.0" +authors = ["Paul Dix "] +description = "A rust implementation of Thorsten Ball's Monkey programming language." +license = "MIT" + +[dependencies] diff --git a/src/ast.rs b/src/ast.rs new file mode 100644 index 0000000..0bb88b9 --- /dev/null +++ b/src/ast.rs @@ -0,0 +1,291 @@ +use token; +use std::fmt; +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; + +#[derive(Debug)] +pub enum Node { + Program(Box), + Statement(Box), + Expression(Box), +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub enum Statement { + Let(Box), + Return(Box), + Expression(Box), +} + +impl fmt::Display for Statement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + Statement::Let(stmt) => format!("{}", stmt), + Statement::Return(ret) => format!("{}", ret), + Statement::Expression(exp) => format!("{}", exp), + }; + write!(f, "{}", s) + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub enum Expression { + Identifier(String), + Integer(i64), + Prefix(Box), + Infix(Box), + Boolean(bool), + String(String), + If(Box), + Function(Box), + Call(Box), + Array(Box), + Index(Box), + Hash(Box), +} + +impl fmt::Display for Expression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + Expression::Identifier(s) => s.clone(), + Expression::Integer(value) => format!("{}", value), + Expression::Prefix(pref) => pref.to_string(), + Expression::Infix(infix) => infix.to_string(), + Expression::Boolean(b) => b.to_string(), + Expression::String(s) => s.clone(), + Expression::If(exp) => exp.to_string(), + Expression::Function(f) => f.to_string(), + Expression::Call(c) => c.to_string(), + Expression::Array(a) => a.to_string(), + Expression::Index(i) => i.to_string(), + Expression::Hash(h) => h.to_string(), + }; + write!(f, "{}", s) + } +} + +#[derive(Eq, PartialEq, Clone, Debug)] +pub struct HashLiteral { + pub pairs: HashMap, +} + +// Had to implement Hash for this because HashMap doesn't. Doesn't matter what this is because +// a HashLiteral isn't a valid expression as a key in a monkey hash. +impl Hash for HashLiteral { + fn hash(&self, _state: &mut H) { + panic!("hash not implemented for HashLiteral"); + } +} + +impl fmt::Display for HashLiteral { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let pairs: Vec = (&self.pairs).into_iter().map(|(k, v)| format!("{}:{}", k.to_string(), v.to_string())).collect(); + write!(f, "{{{}}}", pairs.join(", ")) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct IfExpression { + pub condition: Expression, + pub consequence: BlockStatement, + pub alternative: Option, +} + +impl fmt::Display for IfExpression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "if {} {}", self.condition, self.consequence)?; + + if let Some(ref stmt) = self.alternative { + write!(f, "else {}", stmt)?; + } + Ok(()) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct ArrayLiteral { + pub elements: Vec, +} + +impl fmt::Display for ArrayLiteral { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let elements: Vec = (&self.elements).into_iter().map(|e| e.to_string()).collect(); + write!(f, "[{}]", elements.join(", ")) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct IndexExpression { + pub left: Expression, + pub index: Expression, +} + +impl fmt::Display for IndexExpression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}[{}])", self.left.to_string(), self.index.to_string()) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct BlockStatement { + pub statements: Vec, +} + +impl fmt::Display for BlockStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for stmt in &self.statements { + write!(f, "{}", stmt)?; + } + Ok(()) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct FunctionLiteral { + pub parameters: Vec, + pub body: BlockStatement, +} + +impl fmt::Display for FunctionLiteral { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let param_list: Vec = (&self.parameters).into_iter().map(|p| p.to_string()).collect(); + write!(f, "({}) {}", param_list.join(", "), self.body) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct CallExpression { + pub function: Expression, + pub arguments: Vec, +} + +impl fmt::Display for CallExpression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let arg_list: Vec = (&self.arguments).into_iter().map(|exp| exp.to_string()).collect(); + write!(f, "{}({})", self.function.to_string(), arg_list.join(", ")) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct IdentifierExpression { + pub name: String, +} + +impl fmt::Display for IdentifierExpression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.name) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct PrefixExpression { + pub operator: token::Token, + pub right: Expression, +} + +impl fmt::Display for PrefixExpression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}{})", self.operator, self.right) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct InfixExpression { + pub operator: token::Token, + pub left: Expression, + pub right: Expression, +} + +impl fmt::Display for InfixExpression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({} {} {})", self.left, self.operator, self.right) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct IntegerLiteral { + pub value: i64, +} + +impl fmt::Display for IntegerLiteral { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.value) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct LetStatement { + pub name: String, + pub value: Expression, +} + +impl fmt::Display for LetStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "let {} = {};", self.name, self.value) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct ReturnStatement { + pub value: Expression, +} + +impl fmt::Display for ReturnStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "return {};", self.value) + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct ExpressionStatement { + pub expression: Expression +} + +impl fmt::Display for ExpressionStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.expression) + } +} + +#[derive(Debug)] +pub struct Program { + pub statements: Vec, +} + +impl Program { + pub fn new() -> Program { + Program { + statements: Vec::new(), + } + } +} + +impl fmt::Display for Program { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let statements: Vec = (&self.statements).into_iter().map(|stmt| stmt.to_string()).collect(); + write!(f, "{}", statements.join("")) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn display() { + + let p = Program{ + statements: vec![ + Statement::Let(Box::new( + LetStatement{ + name: "asdf".to_string(), + value: Expression::Identifier("bar".to_string())}))], + }; + + let expected = "let asdf = bar;"; + + if p.to_string() != expected { + panic!("expected {} but got {}", "foo", expected) + } + } +} diff --git a/src/evaluator.rs b/src/evaluator.rs new file mode 100644 index 0000000..dff247d --- /dev/null +++ b/src/evaluator.rs @@ -0,0 +1,802 @@ +use std; +use std::fmt; +use std::cell::RefCell; +use std::rc::Rc; +use std::collections::HashMap; +use ast::*; +use object; +use object::{Object, Environment, Function, Builtin, Array, MonkeyHash}; +use token::Token; +use parser; + +pub type EvalResult = Result, EvalError>; + +#[derive(Debug)] +pub struct EvalError { + pub message: String, +} + +impl fmt::Display for EvalError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self.message) + } +} + +impl std::error::Error for EvalError { + fn description(&self) -> &str { + &self.message + } +} + +pub fn eval(node: &Node, env: Rc>) -> EvalResult { + match node { + Node::Program(prog) => eval_program(&prog, env), + Node::Statement(stmt) => eval_statement(&stmt, env), + Node::Expression(exp) => eval_expression(&exp, env), + } +} + +fn eval_expression(exp: &Expression, env: Rc>) -> EvalResult { + match exp { + Expression::Integer(int) => Ok(Rc::new(Object::Int(*int))), + Expression::Boolean(b) => Ok(Rc::new(Object::Bool(*b))), + Expression::String(s) => Ok(Rc::new(Object::String(s.clone()))), + Expression::Prefix(pre) => { + let right = eval_expression(&pre.right, env)?; + eval_prefix_expression(&pre.operator, right) + }, + Expression::Infix(infix) => { + let left = eval_expression(&infix.left, Rc::clone(&env))?; + let right = eval_expression(&infix.right, env)?; + eval_infix_expression(&infix.operator, left, right) + }, + Expression::If(ifexp) => { + let evaluated = eval_expression(&ifexp.condition, Rc::clone(&env))?; + + match is_truthy(&evaluated) { + true => eval_block(&ifexp.consequence, env), + false => { + match &ifexp.alternative { + Some(alt) => eval_block(&alt, env), + None => Ok(Rc::new(Object::Null)), + } + } + } + }, + Expression::Identifier(ident) => { + eval_identifier(ident, env) + }, + Expression::Function(f) => { + let func = Function{parameters: f.parameters.clone(), body: f.body.clone(), env: Rc::clone(&env)}; + Ok(Rc::new(Object::Function(Rc::new(func)))) + }, + Expression::Call(exp) => { + let function = eval_expression(&exp.function, Rc::clone(&env))?; + let args = eval_expressions(&exp.arguments, env)?; + apply_function(&function, &args) + }, + Expression::Array(a) => { + let elements = eval_expressions(&a.elements, Rc::clone(&env))?; + Ok(Rc::new(Object::Array(Rc::new(Array{elements})))) + }, + Expression::Index(i) => { + let left = eval_expression(&i.left, Rc::clone(&env))?; + let index = eval_expression(&i.index, env)?; + eval_index_expression(left, index) + }, + Expression::Hash(h) => { + eval_hash_literal(&h, Rc::clone(&env)) + }, + } +} + +fn eval_hash_literal(h: &HashLiteral, env: Rc>) -> EvalResult { + let mut pairs = HashMap::new(); + + for (key_exp, val_exp) in &h.pairs { + let key = eval_expression(key_exp, Rc::clone(&env))?; + let value = eval_expression(val_exp, Rc::clone(&env))?; + pairs.insert(key, value); + } + + Ok(Rc::new(Object::Hash(Rc::new(MonkeyHash{pairs})))) +} + +fn eval_index_expression(left: Rc, index: Rc) -> EvalResult { + match (&*left, &*index) { + (Object::Array(a), Object::Int(i)) => { + match a.elements.get(*i as usize) { + Some(el) => Ok(Rc::clone(el)), + None => Ok(Rc::new(Object::Null)) + } + }, + (Object::Hash(h), _object) => { + match &*index { + Object::String(_) | Object::Int(_) | Object::Bool(_) => { + match h.pairs.get(&*index) { + Some(obj) => Ok(Rc::clone(obj)), + None => Ok(Rc::new(Object::Null)) + } + }, + _ => Err(EvalError{message: format!("unusable as hash key: {}", index)}) + } + } + _ => Err(EvalError{message: format!("index operator not supported {}", index)}) + } +} + +fn eval_identifier(ident: &str, env: Rc>) -> EvalResult { + match env.borrow().get(ident) { + Some(obj) => { + Ok(obj.clone()) + }, + None => { + match Builtin::lookup(ident) { + Some(obj) => Ok(Rc::new(obj)), + None => Err(EvalError{message: format!("identifier not found: {}", ident)}), + } + } + } +} + +fn apply_function(func: &Object, args: &Vec>) -> EvalResult { + match func { + Object::Function(f) => { + let mut extended_env = extend_function_env(f, args); + let evaluated = eval_block(&f.body, extended_env)?; + Ok(unwrap_return_value(evaluated)) + }, + Object::Builtin(b) => { + match b.apply(args) { + Ok(obj) => Ok(obj), + Err(err) => Err(EvalError{message: err}), + } + }, + f => Err(EvalError{message: format!("{:?} is not a function", f)}) + } +} + +fn extend_function_env(func: &Function, args: &Vec>) -> Rc> { + let env = Rc::new(RefCell::new(Environment::new_enclosed(Rc::clone(&func.env)))); + + let mut args_iter = args.into_iter(); + + for param in &func.parameters { + let arg = args_iter.next().unwrap(); + + env.borrow_mut().set(param.name.clone(), Rc::clone(arg)) + } + + env +} + +fn unwrap_return_value(obj: Rc) -> Rc { + if let Object::Return(ret) = &*obj { + return Rc::clone(&ret.value) + } + obj +} + +fn eval_expressions(exps: &Vec, env: Rc>) -> Result>, EvalError> { + let mut objs = Vec::with_capacity(exps.len()); + + for e in exps { + let res = eval_expression(&e, Rc::clone(&env))?; + objs.push(res); + } + + Ok(objs) +} + +fn is_truthy(obj: &Object) -> bool { + match obj { + Object::Null => false, + Object::Bool(false) => false, + _ => true, + } +} + +fn eval_infix_expression(operator: &Token, left: Rc, right: Rc) -> EvalResult { + match (&*left, &*right) { + (Object::Int(l), Object::Int(r)) => eval_integer_infix_expression(operator, *l, *r), + (Object::Bool(l), Object::Bool(r)) => eval_bool_infix_expression(operator, *l, *r), + (Object::String(l), Object::String(r)) => eval_string_infix_expression(operator, l.clone(), &*r), + _ => Err(EvalError{message: format!("type mismatch: {:?} {} {:?}", left, operator, right)}), + } +} + +fn eval_string_infix_expression(operator: &Token, left: String, right: &str) -> EvalResult { + match operator { + Token::Plus => Ok(Rc::new(Object::String(left + right))), + _ => Err(EvalError{message: format!("unknown operator {} {} {}", left, operator, right)}), + } +} + +fn eval_bool_infix_expression(operator: &Token, left: bool, right: bool) -> EvalResult { + match operator { + Token::Eq => Ok(Rc::new(Object::Bool(left == right))), + Token::Neq => Ok(Rc::new(Object::Bool(left != right))), + _ => Err(EvalError{message: format!("unknown operator: {} {} {}", left, operator, right)}) + } +} + +fn eval_integer_infix_expression(operator: &Token, left: i64, right: i64) -> EvalResult { + match operator { + Token::Plus => Ok(Rc::new(Object::Int(left + right))), + Token::Minus => Ok(Rc::new(Object::Int(left - right))), + Token::Asterisk => Ok(Rc::new(Object::Int(left * right))), + Token::Slash => Ok(Rc::new(Object::Int(left / right))), + Token::Lt => Ok(Rc::new(Object::Bool(left < right))), + Token::Gt => Ok(Rc::new(Object::Bool(left > right))), + Token::Eq => Ok(Rc::new(Object::Bool(left == right))), + Token::Neq => Ok(Rc::new(Object::Bool(left != right))), + _ => Err(EvalError{message: format!("unknown operator {}", operator)}), + } +} + +fn eval_block(block: &BlockStatement, env: Rc>) -> EvalResult { + let mut result = Rc::new(Object::Null); + + for stmt in &block.statements { + let res = eval_statement(stmt, Rc::clone(&env))?; + + match *res { + Object::Return(_) => return Ok(res), + _ => result = res, + } + } + + Ok(result) +} + +fn eval_statement(stmt: &Statement, env: Rc>) -> EvalResult { + match stmt { + Statement::Expression(exp) => eval_expression(&exp.expression, env), + Statement::Return(ret) => { + let value = eval_expression(&ret.value, env)?; + Ok(Rc::new(Object::Return(Rc::new(object::Return{value})))) + }, + Statement::Let(stmt) => { + let exp = eval_expression(&stmt.value, Rc::clone(&env))?; + let obj = Rc::clone(&exp); + env.borrow_mut().set(stmt.name.clone(), obj); + Ok(exp) + } + } +} + +fn eval_program(prog: &Program, env: Rc>) -> EvalResult { + let mut result = Rc::new(Object::Null); + + for stmt in &prog.statements { + let res = eval_statement(stmt, Rc::clone(&env))?; + let v = Rc::clone(&res); + + match &*v { + Object::Return(r) => return Ok(Rc::clone(&r.value)), + _ => result = res, + } + } + + Ok(result) +} + +fn eval_prefix_expression(operator: &Token, right: Rc) -> EvalResult { + match *operator { + Token::Bang => eval_bang_operator_expression(right), + Token::Minus => eval_minus_prefix_operator_expression(right), + _ => Err(EvalError{message:format!("unknown prefix operator {}", operator)}), + } +} + +fn eval_bang_operator_expression(right: Rc) -> EvalResult { + Ok(Rc::new(match *right { + Object::Bool(true) => Object::Bool(false), + Object::Bool(false) => Object::Bool(true), + Object::Null => Object::Bool(true), + _ => Object::Bool(false), + })) +} + +fn eval_minus_prefix_operator_expression(right: Rc) -> EvalResult { + match *right { + Object::Int(val) => { + Ok(Rc::new(Object::Int(-val))) + }, + _ => Err(EvalError{message: format!("unknown operator: -{:?}", right)}), + } +} + +#[cfg(test)] +mod test { + use super::*; + use object::Object; + + #[test] + fn eval_integer_expression() { + struct Test<'a> { + input: &'a str, + expected: i64, + } + let tests = vec![ + Test{input: "5", expected: 5}, + Test{input: "10", expected: 10}, + Test{input: "-5", expected: -5}, + Test{input: "-10", expected: -10}, + Test{input: "5 + 5 + 5 + 5 - 10", expected: 10}, + Test{input: "2 * 2 * 2 * 2 * 2", expected: 32}, + Test{input: "-50 + 100 + -50", expected: 0}, + Test{input: "5 * 2 + 10", expected: 20}, + Test{input: "5 + 2 * 10", expected: 25}, + Test{input: "20 + 2 * -10", expected: 0}, + Test{input: "50 / 2 * 2 + 10", expected: 60}, + Test{input: "2 * (5 + 10)", expected: 30}, + Test{input: "3 * 3 * 3 + 10", expected: 37}, + Test{input: "3 * (3 * 3) + 10", expected: 37}, + Test{input: "(5 + 10 * 2 + 15 / 3) * 2 + -10", expected: 50}, + ]; + + for t in tests { + let evaluated = test_eval(t.input); + test_integer_object(&evaluated, t.expected); + } + } + + #[test] + fn eval_boolean_expression() { + struct Test<'a> { + input: &'a str, + expected: bool, + } + let tests = vec![ + Test{input: "true", expected: true}, + Test{input: "false", expected: false}, + Test{input: "1 < 2", expected: true}, + Test{input: "1 > 2", expected: false}, + Test{input: "1 < 1", expected: false}, + Test{input: "1 > 1", expected: false}, + Test{input: "1 == 1", expected: true}, + Test{input: "1 != 1", expected: false}, + Test{input: "1 == 2", expected: false}, + Test{input: "1 != 2", expected: true}, + Test{input: "true == true", expected: true}, + Test{input: "false == false", expected: true}, + Test{input: "true == false", expected: false}, + Test{input: "true != false", expected: true}, + Test{input: "false != true", expected: true}, + Test{input: "(1 < 2) == true", expected: true}, + Test{input: "(1 < 2) == false", expected: false}, + Test{input: "(1 > 2) == true", expected: false}, + Test{input: "(1 > 2) == false", expected: true}, + ]; + + for t in tests { + let evaluated = test_eval(t.input); + + test_boolean_object(&evaluated, t.expected); + } + } + + #[test] + fn bang_operator() { + struct Test<'a> { + input: &'a str, + expected: bool, + } + let tests = vec![ + Test{input: "!true", expected: false}, + Test{input: "!false", expected: true}, + Test{input: "!5", expected: false}, + Test{input: "!!true", expected: true}, + Test{input: "!!false", expected: false}, + Test{input: "!!5", expected: true}, + ]; + + for t in tests { + let evaluated = test_eval(t.input); + test_boolean_object(&evaluated, t.expected); + } + } + + #[test] + fn if_else_expressions() { + struct Test<'a> { + input: &'a str, + expected: Object, + } + let tests = vec![ + Test{input: "if (true) { 10 }", expected: Object::Int(10)}, + Test{input: "if (false) { 10 }", expected: Object::Null}, + Test{input: "if (1) { 10 }", expected: Object::Int(10)}, + Test{input: "if (1 < 2) { 10 }", expected: Object::Int(10)}, + Test{input: "if (1 > 2) { 10 }", expected: Object::Null}, + Test{input: "if (1 > 2) { 10 } else { 20 }", expected: Object::Int(20)}, + Test{input: "if (1 < 2) { 10 } else { 20 }", expected: Object::Int(10)}, + ]; + + for t in tests { + let evaluated = &*test_eval(t.input); + + match t.expected { + Object::Int(i) => test_integer_object(&evaluated, i), + _ => test_null_object(&evaluated), + } + } + } + + #[test] + fn return_statements() { + struct Test<'a> { + input: &'a str, + expected: i64, + } + let tests = vec![ + Test{input: "return 10;", expected: 10}, + Test{input: "return 10; 9;", expected: 10}, + Test{input: "return 2 * 5; 9;", expected: 10}, + Test{input: "9; return 2 * 5; 9;", expected: 10}, + Test{input: "if (10 > 1) { + if (10 > 1) { + return 10; + } + return 1; + }", expected: 10}, + ]; + + for t in tests { + let evaluated = test_eval(t.input); + test_integer_object(&evaluated, t.expected) + } + } + + #[test] + fn error_handling() { + struct Test<'a> { + input: &'a str, + expected: &'a str, + } + let tests = vec![ + Test{input: "5 + true;", expected: "type mismatch: Int(5) + Bool(true)"}, + Test{input: "5 + true; 5;", expected: "type mismatch: Int(5) + Bool(true)"}, + Test{input: "-true", expected: "unknown operator: -Bool(true)"}, + Test{input: "true + false", expected: "unknown operator: true + false"}, + Test{input: "5; true + false; 5", expected: "unknown operator: true + false"}, + Test{input: "if (10 > 1) { true + false; }", expected: "unknown operator: true + false"}, + Test{input: "if (10 > 1) { + if (10 > 1) { + return true + false; + } + + return 1; + }", expected: "unknown operator: true + false"}, + Test{input: "foobar", expected: "identifier not found: foobar"}, + Test{input: r#" {"name": "Monkey"}[fn(x) { x }]; "#, expected: "unusable as hash key: fn(x) {\nx\n}"}, + ]; + + for t in tests { + let mut env = Rc::new(RefCell::new(Environment::new())); + match parser::parse(t.input) { + Ok(node) => { + match eval(&node, env) { + Err(e) => assert_eq!(e.message, t.expected), + n => panic!("expected error {} but got {:?}", t.expected, n) + } + }, + Err(e) => panic!("error {:?} on input {}", e, t.input), + } + } + } + + #[test] + fn let_statements() { + struct Test<'a> { + input: &'a str, + expected: i64, + } + let tests = vec![ + Test{input: "let a = 5; a;", expected: 5}, + Test{input: "let a = 5 * 5; a;", expected: 25}, + Test{input: "let a = 5; let b = a; b;", expected: 5}, + Test{input: "let a = 5; let b = a; let c = a + b + 5; c;", expected: 15}, + ]; + + for t in tests { + let evaluated = test_eval(t.input); + test_integer_object(&evaluated, t.expected) + } + } + + #[test] + fn function_object() { + let input = "fn(x) { x + 2; };"; + let evaluated = &*test_eval(input); + + match evaluated { + Object::Function(f) => { + assert_eq!(f.parameters.len(), 1); + assert_eq!(f.parameters.first().unwrap().name, "x"); + assert_eq!(f.body.to_string(), "(x + 2)"); + }, + _ => panic!("expected function object but got {:?}", evaluated) + } + } + #[test] + fn function_application() { + struct Test<'a> { + input: &'a str, + expected: i64, + } + let tests = vec![ + Test{input: "let identity = fn(x) { x; }; identity(5);", expected: 5}, + Test{input: "let identity = fn(x) { return x; }; identity(5);", expected: 5}, + Test{input: "let double = fn(x) { x * 2; }; double(5);", expected: 10}, + Test{input: "let add = fn(x, y) { x + y; }; add(5, 5);", expected: 10}, + Test{input: "let add = fn(x, y) { x + y; }; add(5 + 5, add(5, 5));", expected: 20}, + Test{input: "fn(x) { x; }(5)", expected: 5}, + ]; + + for t in tests { + test_integer_object(&test_eval(t.input), t.expected) + } + } + + #[test] + fn closures() { + let input = "let newAdder = fn(x) { + fn(y) { x + y }; +}; + +let addTwo = newAdder(2); +addTwo(2);"; + test_integer_object(&test_eval(input), 4) + } + + #[test] + fn string_literal() { + let input = r#""Hello World!"#; + + match &*test_eval(input) { + Object::String(s) => assert_eq!(s, "Hello World!"), + obj => panic!(format!("expected string but got {:?}", obj)) + } + + } + + #[test] + fn string_concatenation() { + let input = r#""Hello" + " " + "World!""#; + + match &*test_eval(input) { + Object::String(s) => assert_eq!(s, "Hello World!"), + obj => panic!(format!("expected string but got {:?}", obj)) + } + } + + #[test] + fn builtin_functions() { + struct Test<'a> { + input: &'a str, + expected: Object, + } + let tests = vec![ + Test{input: r#"len("")"#, expected: Object::Int(0)}, + Test{input: r#"len("four")"#, expected: Object::Int(4)}, + Test{input: r#"len("hello world")"#, expected: Object::Int(11)}, + Test{input: "len([1, 2, 3])", expected: Object::Int(3)}, + Test{input: "len([])", expected: Object::Int(0)}, + Test{input: "first([1, 2, 3])", expected: Object::Int(1)}, + Test{input: "first([])", expected: Object::Null}, + Test{input: "last([1, 2, 3])", expected: Object::Int(3)}, + Test{input: "last([])", expected: Object::Null}, + Test{input: "rest([1, 2, 3])", expected: Object::Array(Rc::new(Array{elements: vec![Rc::new(Object::Int(2)), Rc::new(Object::Int(3))]}))}, + Test{input: "rest([])", expected: Object::Array(Rc::new(Array{elements: vec![]}))}, + Test{input: "push([], 1)", expected: Object::Array(Rc::new(Array{elements: vec![Rc::new(Object::Int(1))]}))}, + ]; + + for t in tests { + let obj = test_eval(t.input); + + match (&t.expected, &*obj) { + (Object::Int(exp), Object::Int(got)) => assert_eq!(*exp, *got, "on input {} expected {} but got {}", t.input, exp, got), + (Object::Null, Object::Null) => {}, + (Object::Array(ex), Object::Array(got)) => { + assert_eq!(ex.elements.len(), got.elements.len()); + let mut got_iter = (&got.elements).into_iter(); + for obj in &ex.elements { + let got_obj = Rc::clone(got_iter.next().unwrap()); + match (&*Rc::clone(obj), &*got_obj) { + (Object::Int(exi), Object::Int(goti)) => assert_eq!(*exi, *goti), + _ => panic!("{:?} not same type as {:?}", got_obj, obj) + } + } + } + _ => panic!("on input {} expected {:?} but got {:?}", t.input, t.expected, obj) + } + } + } + + #[test] + fn builtin_errors() { + struct Test<'a> { + input: &'a str, + expected: &'a str, + } + let tests = vec![ + Test{input: r#"len(1)"#, expected: "object Int(1) not supported as an argument for len"}, + Test{input: r#"len("one", "two")"#, expected: "len takes only 1 array or string argument"}, + Test{input: r#"first(1)"#, expected: "object Int(1) not supported as an argument for first"}, + ]; + + for t in tests { + let env = Rc::new(RefCell::new(Environment::new())); + match parser::parse(t.input) { + Ok(node) => { + match eval(&node, env) { + Ok(obj) => panic!("expected error on input {} but got {:?}", t.input, obj), + Err(err) => assert_eq!(t.expected, err.message, "on input {} expected error {} but got {}", t.input, t.expected, err.message) + } + }, + Err(e) => panic!("error {:?} on input {}", e, t.input), + } + } + } + + #[test] + fn array_literals() { + let input = "[1, 2 * 2, 3 + 3]"; + let obj = test_eval(input); + match &*obj { + Object::Array(a) => { + test_integer_object(a.elements.get(0).unwrap(), 1); + test_integer_object(a.elements.get(1).unwrap(), 4); + test_integer_object(a.elements.get(2).unwrap(), 6); + }, + _ => panic!("expected array but got {:?}", obj) + } + } + + #[test] + fn array_index_expressions() { + struct Test<'a> { + input: &'a str, + expected: i64, + } + let tests = vec![ + Test{input: "[1, 2, 3][0]", expected: 1}, + Test{input: "[1, 2, 3][1]", expected: 2}, + Test{input: "[1, 2, 3][2]", expected: 3}, + Test{input: "let i = 0; [1][i];", expected: 1}, + Test{input: "[1, 2, 3][1 + 1];", expected: 3}, + Test{input: "let myArray = [1, 2, 3]; myArray[2];", expected: 3}, + Test{input: "let myArray = [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];", expected: 6}, + Test{input: "let myArray = [1, 2, 3]; let i = myArray[0]; myArray[i]", expected: 2}, + ]; + + for t in tests { + let obj = test_eval(t.input); + match &*obj { + Object::Int(i) => assert_eq!(*i, t.expected), + _ => panic!("expected int obj but got {:?}", obj) + } + } + } + + #[test] + fn invalid_array_index() { + let inputs = vec![ + "[1, 2, 3][3]", + "[1, 2, 3][-1]", + ]; + + for input in inputs { + let obj = test_eval(input); + match &*obj { + Object::Null => {}, + _ => panic!("expected null object, but got {:?}", obj) + } + } + } + + #[test] + fn hash_literal() { + let input = r#"let two = "two"; + { + "one": 10 - 9, + two: 1 + 1, + "thr" + "ee": 6 / 2, + 4: 4, + true: 5, + false: 6 + } + "#; + + let obj = test_eval(input); + match &*obj { + Object::Hash(h) => { + assert_eq!(h.pairs.len(), 6); + + for (key, value) in &h.pairs { + match (&*Rc::clone(key), &*Rc::clone(value)) { + (Object::String(k), Object::Int(val)) => { + match k.as_str() { + "one" => assert_eq!(*val, 1), + "two" => assert_eq!(*val, 2), + "three" => assert_eq!(*val, 3), + _ => panic!("unexpected string key {}", k) + } + }, + (Object::Bool(b), Object::Int(val)) => { + if *b { + assert_eq!(*val, 5) + } else { + assert_eq!(*val, 6) + } + }, + (Object::Int(k), Object::Int(val)) => assert_eq!(k, val), + _ => panic!("unexpected key value pair {:?} {:?}", key, value) + } + } + }, + _ => panic!("expected hash object, but got {:?}", obj) + } + } + + #[test] + fn hash_index_expressions() { + struct Test<'a> { + input: &'a str, + expected: Object, + } + let tests = vec![ + Test{input: r#" {"foo":5}["foo"] "#, expected: Object::Int(5)}, + Test{input: r#" {"foo":5}["bar"] "#, expected: Object::Null}, + Test{input: r#" let key = "foo"; {"foo":5}[key] "#, expected: Object::Int(5)}, + Test{input: r#" {}["foo"] "#, expected: Object::Null}, + Test{input: r#" {5: 5}[5] "#, expected: Object::Int(5)}, + Test{input: r#" {true: 5}[true] "#, expected: Object::Int(5)}, + Test{input: r#" {false: 5}[false] "#, expected: Object::Int(5)}, + ]; + + for t in tests { + let obj = test_eval(t.input); + + match (&t.expected, &*obj) { + (Object::Int(exp), Object::Int(got)) => assert_eq!(*exp, *got, "on input {} expected {} but got {}", t.input, exp, got), + (Object::Null, Object::Null) => {}, + _ => panic!("on input {} expected {:?} but got {:?}", t.input, t.expected, obj) + } + } + } + + fn test_eval(input: &str) -> Rc { + let env = Rc::new(RefCell::new(Environment::new())); + match parser::parse(input) { + Ok(node) => { + eval(&node, env).expect(input) + + }, + Err(e) => panic!("error {:?} on input {}", e, input), + } + } + + fn test_integer_object(obj: &Object, expected: i64) { + match obj { + Object::Int(i) => assert_eq!(i, &expected), + _ => panic!("expected integer object, but got {:?}", obj), + } + } + + fn test_boolean_object(obj: &Object, expected: bool) { + match obj { + Object::Bool(b) => assert_eq!(b, &expected), + _ => panic!("expected boolean object, but got {:?}", obj), + } + } + + fn test_null_object(obj: &Object) { + match obj { + Object::Null => {}, + _ => panic!("expected null but got {:?}", obj), + } + } +} \ No newline at end of file diff --git a/src/lexer.rs b/src/lexer.rs new file mode 100644 index 0000000..6896498 --- /dev/null +++ b/src/lexer.rs @@ -0,0 +1,272 @@ +use token; +use token::Token; +use std::iter::Peekable; +use std::str::Chars; + +pub struct Lexer<'a> { + input: Peekable>, +} + +impl<'a> Lexer<'a> { + pub fn new(input: &'a str) -> Lexer { + Lexer{ + input: input.chars().peekable(), + } + } + + pub fn next_token(&mut self) -> Token { + self.eat_whitespace(); + match self.read_char() { + Some('=') => { + if let Some('=') = self.peek_char() { + self.read_char(); + Token::Eq + } else { + Token::Assign + } + }, + Some('+') => Token::Plus, + Some('(') => Token::Lparen, + Some(')') => Token::Rparen, + Some('{') => Token::Lbrace, + Some('}') => Token::Rbrace, + Some('[') => Token::Lbracket, + Some(']') => Token::Rbracket, + Some(',') => Token::Comma, + Some(';') => Token::Semicolon, + Some(':') => Token::Colon, + Some('-') => Token::Minus, + Some('!') => { + if let Some('=') = self.peek_char() { + self.read_char(); + Token::Neq + } else { + Token::Bang + } + }, + Some('*') => Token::Asterisk, + Some('/') => Token::Slash, + Some('<') => Token::Lt, + Some('>') => Token::Gt, + Some(ch) => { + if is_letter(ch) { + let ident = self.read_identifier(ch); + let tok = token::lookup_ident(ident); + tok + } else if ch.is_digit(10) { + Token::Int(self.read_int(ch)) + } else if ch == '"' { + Token::String(self.read_string()) + } else { + Token::Illegal + } + }, + None => Token::EOF, + } + } + + fn read_string(&mut self) -> String { + let mut str = String::new(); + while let Some(ch) = self.read_char() { + if ch == '"' { + return str; + } + str.push(ch); + } + str + } + + fn eat_whitespace(&mut self) { + while let Some(&ch) = self.input.peek() { + if ch.is_whitespace() { + self.read_char(); + } else { + break; + } + } + } + + fn read_char(&mut self) -> Option { + self.input.next() + } + + fn peek_char(&mut self) -> Option<&char> { + self.input.peek() + } + + fn read_int(&mut self, ch: char) -> i64 { + let mut s = String::new(); + s.push(ch); + + while let Some(&ch) = self.peek_char() { + if ch.is_digit(10) { + s.push(self.read_char().unwrap()); + } else { + break; + } + } + s.parse().unwrap() + } + + fn read_identifier(&mut self, ch: char) -> String { + let mut ident = String::new(); + ident.push(ch); + + while let Some(&ch) = self.peek_char() { + if is_letter(ch) { + ident.push(self.read_char().unwrap()); + } else { + break; + } + } + ident + } +} + +impl<'a> Iterator for Lexer<'a> { + type Item = Token; + + fn next(&mut self) -> Option { + let tok = self.next_token(); + if tok == Token::EOF { + None + } else { + Some(tok) + } + } +} + +fn is_letter(ch: char) -> bool { + ch.is_alphabetic() || ch == '_' +} + +#[cfg(test)] +mod test { + use token::Token; + use super::*; + + #[test] + fn next_token() { + let input = r#"let five = 5; +let ten = 10; + +let add = fn(x, y) { + x + y; +}; + +let result = add(five, ten); +!-/*5; +5 < 10 > 5; + +if (5 < 10) { + return true; +} else { + return false; +} + +10 == 10; +10 != 9; +"foobar" +"foo bar" +[1, 2]; +{"foo": "bar"}"#; + + let tests = vec![ + Token::Let, + Token::Ident("five".to_string()), + Token::Assign, + Token::Int(5), + Token::Semicolon, + Token::Let, + Token::Ident("ten".to_string()), + Token::Assign, + Token::Int(10), + Token::Semicolon, + Token::Let, + Token::Ident("add".to_string()), + Token::Assign, + Token::Function, + Token::Lparen, + Token::Ident("x".to_string()), + Token::Comma, + Token::Ident("y".to_string()), + Token::Rparen, + Token::Lbrace, + Token::Ident("x".to_string()), + Token::Plus, + Token::Ident("y".to_string()), + Token::Semicolon, + Token::Rbrace, + Token::Semicolon, + Token::Let, + Token::Ident("result".to_string()), + Token::Assign, + Token::Ident("add".to_string()), + Token::Lparen, + Token::Ident("five".to_string()), + Token::Comma, + Token::Ident("ten".to_string()), + Token::Rparen, + Token::Semicolon, + Token::Bang, + Token::Minus, + Token::Slash, + Token::Asterisk, + Token::Int(5), + Token::Semicolon, + Token::Int(5), + Token::Lt, + Token::Int(10), + Token::Gt, + Token::Int(5), + Token::Semicolon, + Token::If, + Token::Lparen, + Token::Int(5), + Token::Lt, + Token::Int(10), + Token::Rparen, + Token::Lbrace, + Token::Return, + Token::True, + Token::Semicolon, + Token::Rbrace, + Token::Else, + Token::Lbrace, + Token::Return, + Token::False, + Token::Semicolon, + Token::Rbrace, + Token::Int(10), + Token::Eq, + Token::Int(10), + Token::Semicolon, + Token::Int(10), + Token::Neq, + Token::Int(9), + Token::Semicolon, + Token::String("foobar".to_string()), + Token::String("foo bar".to_string()), + Token::Lbracket, + Token::Int(1), + Token::Comma, + Token::Int(2), + Token::Rbracket, + Token::Semicolon, + Token::Lbrace, + Token::String("foo".to_string()), + Token::Colon, + Token::String("bar".to_string()), + Token::Rbrace, + Token::EOF, + ]; + + let mut l = Lexer::new(input); + + for t in tests.iter() { + let tok = l.next_token(); + + assert_eq!(*t, tok, "expected {} token but got {}", t, tok) + } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..56b33d5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,42 @@ +//! # Monkey Rust +//! +//! This is an implementation of Thorsten Ball's Monkey programming language from his excellent book +//! [Writing An Interpreter in Go](https://interpreterbook.com/). I ([pauldix](https://twitter.com/pauldix)) +//! built it as a fun way to learn the basics of Rust. An interpreter is great for this because you +//! only use the standard library and don't yet have to worry about threads, network programming +//! or a bunch of other complicated stuff. I attempted to keep the structure as close to +//! Thorsten's Go implementation as possible, which means it might not be the best way to +//! structure things in Rust (I'm still learning). Although I did pull in a few Rust idioms like +//! results and error handling. I've written up more details on my learning process, some open +//! questions, and other stuff at [github.com/pauldix/monkey-rust](https://github.com/pauldix/monkey-rust). +//! +//! This is split out into a library and an executable that provides a REPL for the language. +//! Here's a short example for parsing and executing a monkey program using the library: +//! +//! ```rust +//! use monkey::parser; +//! use monkey::evaluator; +//! use monkey::object::Environment; +//! use std::cell::RefCell; +//! use std::rc::Rc; +//! +//! let input = r#" let hi = "hello world"; puts(hi); "#; +//! +//! let mut env = Rc::new(RefCell::new(Environment::new())); +//! +//! match parser::parse(input) { +//! Ok(node) => { +//! evaluator::eval(&node, env); // maybe do something on error +//! (()) +//! } +//! Err(_parse_errors) => (()) // maybe actually do something here +//! } +//! ``` + +mod token; +mod ast; +pub mod object; +pub mod lexer; +pub mod repl; +pub mod parser; +pub mod evaluator; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e160607 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,12 @@ +extern crate monkey; + +use monkey::repl; +use std::io; + +fn main() -> io::Result<()> { + println!("Welcome to the Monkey REPL!"); + let input = io::stdin(); + let output = io::stdout(); + let result = repl::start(input.lock(), output.lock()); + result +} diff --git a/src/object.rs b/src/object.rs new file mode 100644 index 0000000..30a5c2b --- /dev/null +++ b/src/object.rs @@ -0,0 +1,321 @@ +use std::fmt; +use std::collections::HashMap; +use std::hash::{Hash,Hasher}; +use std::cell::RefCell; +use std::rc::Rc; +use ast; + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub enum Object { + Int(i64), + Bool(bool), + String(String), + Return(Rc), + Function(Rc), + Builtin(Builtin), + Array(Rc), + Hash(Rc), + Null, +} + +impl Object { + pub fn inspect(&self) -> String { + match self { + Object::Int(i) => i.to_string(), + Object::Bool(b) => b.to_string(), + Object::String(s) => s.clone(), + Object::Return(r) => r.value.inspect(), + Object::Function(f) => f.inspect(), + Object::Builtin(b) => b.inspect(), + Object::Array(a) => a.inspect(), + Object::Hash(h) => h.inspect(), + Object::Null => String::from("null"), + } + } +} +impl fmt::Display for Object { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self.inspect()) + } +} + +#[derive(Eq, PartialEq, Clone, Debug)] +pub struct MonkeyHash { + pub pairs: HashMap,Rc>, +} +impl MonkeyHash { + fn inspect(&self) -> String { + let pairs: Vec = (&self.pairs).into_iter().map(|(key, value)| format!("{}: {}", key.inspect(), value.inspect())).collect(); + format!("{{{}}}", pairs.join(", ")) + } +} +impl Hash for MonkeyHash { + fn hash(&self, _state: &mut H) { + // should never happen + panic!("hash not implmented for monkey hash"); + } +} + +#[derive(Clone, Debug)] +pub struct Array { + pub elements: Vec>, +} + +impl Array { + fn inspect(&self) -> String { + let elements: Vec = (&self.elements).into_iter().map(|e| e.to_string()).collect(); + format!("[{}]", elements.join(", ")) + } +} +impl PartialEq for Array { + fn eq(&self, _other: &Array) -> bool { + // TODO: implment this later, but this shouldn't get used right now + panic!("eq not implemented for array"); + } +} +impl Eq for Array {} +impl Hash for Array { + fn hash(&self, _state: &mut H) { + // we should never hash an array so should be fine + panic!("hash for array not supported"); + } +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub enum Builtin { + Len, + First, + Last, + Rest, + Push, + Puts, +} + +impl Builtin { + pub fn lookup(name: &str) -> Option { + match name { + "len" => Some(Object::Builtin(Builtin::Len)), + "first" => Some(Object::Builtin(Builtin::First)), + "last" => Some(Object::Builtin(Builtin::Last)), + "rest" => Some(Object::Builtin(Builtin::Rest)), + "push" => Some(Object::Builtin(Builtin::Push)), + "puts" => Some(Object::Builtin(Builtin::Puts)), + _ => None, + } + } + + pub fn apply(&self, args: &Vec>) -> Result, String> { + match self { + Builtin::Len => { + if args.len() != 1 { + return Err("len takes only 1 array or string argument".to_string()) + } + + let arg = &*Rc::clone(args.first().unwrap()); + match arg { + Object::String(s) => Ok(Rc::new(Object::Int(s.len() as i64))), + Object::Array(a) => Ok(Rc::new(Object::Int(a.elements.len() as i64))), + obj => Err(format!("object {:?} not supported as an argument for len", obj)) + } + }, + Builtin::First => { + if args.len() != 1 { + return Err("first takes only 1 array argument".to_string()) + } + + let arg = &*Rc::clone( args.first().unwrap()); + match arg { + Object::Array(a) => { + match a.elements.first() { + Some(el) => Ok(Rc::clone(el)), + None => Ok(Rc::new(Object::Null)), + } + }, + obj => Err(format!("object {:?} not supported as an argument for first", obj)) + } + }, + Builtin::Last => { + if args.len() != 1 { + return Err("last takes only 1 array argument".to_string()) + } + + let arg = &*Rc::clone(args.first().unwrap()); + match arg { + Object::Array(a) => { + match a.elements.last() { + Some(el) => Ok(Rc::clone(el)), + None => Ok(Rc::new(Object::Null)), + } + }, + obj => Err(format!("object {:?} not supported as an argument for last", obj)) + } + }, + Builtin::Rest => { + if args.len() != 1 { + return Err("rest takes only 1 array argument".to_string()) + } + + let arg = &*Rc::clone(args.first().unwrap()); + match arg { + Object::Array(a) => { + if a.elements.len() <= 1 { + Ok(Rc::new(Object::Array(Rc::new(Array{elements: vec![]})))) + } else { + let mut elements = a.elements.clone(); + elements.remove(0); + Ok(Rc::new(Object::Array(Rc::new(Array{elements})))) + } + }, + obj => Err(format!("object {:?} is not supported as an argument for rest", obj)) + } + }, + Builtin::Push => { + if args.len() != 2 { + return Err("push takes an array and an object".to_string()) + } + + let array = &*Rc::clone(args.first().unwrap()); + let obj = Rc::clone(args.last().unwrap()); + + // TODO: handle pushing objects like an array onto an array + match array { + Object::Array(a) => { + let mut elements = a.elements.clone(); + elements.push(obj); + Ok(Rc::new(Object::Array(Rc::new(Array{elements})))) + }, + _ => Err("first argument to push must be an array".to_string()) + } + }, + Builtin::Puts => { + for arg in args { + println!("{}", arg.inspect()) + } + Ok(Rc::new(Object::Null)) + } + } + } + + fn inspect(&self) -> String { + match self { + Builtin::Len => "len".to_string(), + Builtin::First => "first".to_string(), + Builtin::Last => "last".to_string(), + Builtin::Rest => "rest".to_string(), + Builtin::Push => "push".to_string(), + Builtin::Puts => "puts".to_string(), + } + } +} + +#[derive(Clone, Debug)] +pub struct Function { + pub parameters: Vec, + pub body: ast::BlockStatement, + pub env: Rc>, +} + +impl Function { + fn inspect(&self) -> String { + let params: Vec = (&self.parameters).into_iter().map(|p| p.to_string()).collect(); + format!("fn({}) {{\n{}\n}}", params.join(", "), self.body.to_string()) + } +} +impl PartialEq for Function { + fn eq(&self, _other: &Function) -> bool { + // TODO: implement this, but it should never get used + panic!("partial eq not implemented for function"); + } +} +impl Eq for Function {} +impl Hash for Function { + fn hash(&self, _state: &mut H) { + // we should never hash an array so should be fine + panic!("hash for function not supported"); + } +} + +#[derive(Clone, Debug)] +pub struct Return { + pub value: Rc, +} +impl PartialEq for Return { + fn eq(&self, _other: &Return) -> bool { + // TODO: implement this, but it should never get used + panic!("partial eq not implemented for Return"); + } +} +impl Eq for Return {} +impl Hash for Return { + fn hash(&self, _state: &mut H) { + // we should never hash an array so should be fine + panic!("hash for return not supported"); + } +} + +#[derive(Clone, Debug)] +pub struct Environment { + pub store: HashMap>, + pub outer: Option>>, +} + +impl Environment { + pub fn new() -> Environment { + Environment{store: HashMap::new(), outer: None} + } + + pub fn new_enclosed(env: Rc>) -> Environment { + Environment{store: HashMap::new(), outer: Some(Rc::clone(&env))} + } + + pub fn get(&self, name: &str) -> Option> { + match self.store.get(name) { + Some(obj) => { + Some(Rc::clone(obj)) + }, + None => { + match &self.outer { + Some(o) => o.borrow().get(name), + _ => None, + } + }, + } + } + + pub fn set(&mut self, name: String, obj: Rc) { + self.store.insert(name, obj); + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::collections::hash_map::DefaultHasher; + + #[test] + // this test is unnecessary, but here for completeness with the Monkey book. + fn string_hash_key() { + let hello1 = Object::String(String::from("Hello World")); + let hello2 = Object::String(String::from("Hello World")); + let diff1 = Object::String(String::from("my name is johnny")); + let diff2 = Object::String(String::from("my name is johnny")); + + let mut hasher1 = DefaultHasher::new(); + hello1.hash(&mut hasher1); + let mut hasher2 = DefaultHasher::new(); + hello2.hash(&mut hasher2); + assert_eq!(hasher1.finish(), hasher2.finish()); + + let mut hasher1 = DefaultHasher::new(); + diff1.hash(&mut hasher1); + let mut hasher2 = DefaultHasher::new(); + diff2.hash(&mut hasher2); + assert_eq!(hasher1.finish(), hasher2.finish()); + + let mut hasher1 = DefaultHasher::new(); + hello1.hash(&mut hasher1); + let mut hasher2 = DefaultHasher::new(); + diff1.hash(&mut hasher2); + assert_ne!(hasher1.finish(), hasher2.finish()); + } +} \ No newline at end of file diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..b617d7c --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,1120 @@ +use ast::*; +use std::collections::HashMap; +use token::Token; +use lexer::Lexer; + +type ParseError = String; +type ParseErrors = Vec; +pub type ParseResult = Result; +type PrefixFn = fn(parser: &mut Parser) -> ParseResult; +type InfixFn = fn(parser: &mut Parser, left: Expression) -> ParseResult; + +#[derive(PartialEq, PartialOrd)] +enum Precedence { + Lowest, + Equals, + LessGreater, + Sum, + Product, + Prefix, + Call, + Index, +} + +impl Precedence { + fn token_precedence(tok: &Token) -> Precedence { + match tok { + Token::Eq => Precedence::Equals, + Token::Neq => Precedence::Equals, + Token::Lt => Precedence::LessGreater, + Token::Gt => Precedence::LessGreater, + Token::Plus => Precedence::Sum, + Token::Minus => Precedence::Sum, + Token::Slash => Precedence::Product, + Token::Asterisk => Precedence::Product, + Token::Lparen => Precedence::Call, + Token::Lbracket => Precedence::Index, + _ => Precedence::Lowest, + } + } +} + +pub struct Parser<'a> { + l: Lexer<'a>, + + cur_token: Token, + peek_token: Token, +} + +pub fn parse(input: &str) -> Result { + let l = Lexer::new(input); + let mut p = Parser::new(l); + let prog = p.parse_program()?; + + Ok(Node::Program(Box::new(prog))) +} + +impl<'a> Parser<'a> { + pub fn new(l: Lexer) -> Parser { + let mut l = l; + let cur = l.next_token(); + let next = l.next_token(); + Parser { + l, + cur_token: cur, + peek_token: next, + } + } + + pub fn parse_program(&mut self) -> Result { + let mut prog = Program::new(); + let mut errors = ParseErrors::new(); + let mut tok = self.cur_token.clone(); + + while tok != Token::EOF { + match self.parse_statement() { + Ok(stmt) => prog.statements.push(stmt), + Err(err) => errors.push(err), + } + self.next_token(); + tok = self.cur_token.clone(); + } + + if errors.len() > 0 { + return Err(errors); + } + + Ok(prog) + } + + fn parse_statement(&mut self) -> ParseResult { + match &self.cur_token.clone() { + Token::Let => self.parse_let_statement(), + Token::Return => self.parse_return_statement(), + _ => self.parse_expression_statement(), + } + } + + fn parse_expression_statement(&mut self) -> ParseResult { + let expression = self.parse_expression(Precedence::Lowest)?; + + if self.peek_token_is(&Token::Semicolon) { + self.next_token(); + } + + Ok(Statement::Expression(Box::new(ExpressionStatement { expression }))) + } + + fn parse_expression(&mut self, precedence: Precedence) -> ParseResult { + let mut left_exp; + + if let Some(f) = self.prefix_fn() { + left_exp = f(self)?; + } else { + return Err(format!("no prefix parse function for {} found", self.cur_token)); + } + + while !self.peek_token_is(&Token::Semicolon) && precedence < self.peek_precedence() { + match self.infix_fn() { + Some(f) => { + self.next_token(); + left_exp = f(self, left_exp)?; + } + None => return Ok(left_exp), + } + } + + Ok(left_exp) + } + + fn prefix_fn(&mut self) -> Option { + match self.cur_token { + Token::Ident(_) => Some(Parser::parse_identifier), + Token::Int(_) => Some(Parser::parse_integer_literal), + Token::String(_) => Some(Parser::parse_string_literal), + Token::Bang | Token::Minus => Some(Parser::parse_prefix_expression), + Token::True | Token::False => Some(Parser::parse_boolean), + Token::Lparen => Some(Parser::parse_grouped_expression), + Token::If => Some(Parser::parse_if_expression), + Token::Function => Some(Parser::parse_function_literal), + Token::Lbracket => Some(Parser::parse_array_literal), + Token::Lbrace => Some(Parser::parse_hash_literal), + _ => None, + } + } + + fn parse_hash_literal(parser: &mut Parser) -> ParseResult { + let mut pairs: HashMap = HashMap::new(); + + while !parser.peek_token_is(&Token::Rbrace) { + parser.next_token(); + let key = parser.parse_expression(Precedence::Lowest)?; + + parser.expect_peek(Token::Colon)?; + parser.next_token(); + let value = parser.parse_expression(Precedence::Lowest)?; + + pairs.insert(key, value); + + if !parser.peek_token_is(&Token::Rbrace) { + parser.expect_peek(Token::Comma)?; + } + } + + parser.expect_peek(Token::Rbrace)?; + + Ok(Expression::Hash(Box::new(HashLiteral{pairs}))) + } + + fn parse_array_literal(parser: &mut Parser) -> ParseResult { + let elements = parser.parse_expression_list(Token::Rbracket)?; + Ok(Expression::Array(Box::new(ArrayLiteral{elements}))) + } + + fn parse_expression_list(&mut self, end: Token) -> ParseResult> { + let mut list: Vec = Vec::new(); + + if self.peek_token_is(&end) { + self.next_token(); + return Ok(list) + } + + self.next_token(); + list.push(self.parse_expression(Precedence::Lowest)?); + + while self.peek_token_is(&Token::Comma) { + self.next_token(); + self.next_token(); + list.push(self.parse_expression(Precedence::Lowest)?); + } + + self.expect_peek(end)?; + + Ok(list) + } + + fn parse_prefix_expression(parser: &mut Parser) -> ParseResult { + let operator = parser.cur_token.clone(); + + parser.next_token(); + + let right = parser.parse_expression(Precedence::Prefix)?; + + Ok(Expression::Prefix(Box::new(PrefixExpression { operator, right }))) + } + + fn parse_if_expression(parser: &mut Parser) -> ParseResult { + parser.expect_peek(Token::Lparen)?; + parser.next_token(); + + let condition = parser.parse_expression(Precedence::Lowest)?; + + parser.expect_peek(Token::Rparen)?; + parser.expect_peek(Token::Lbrace)?; + + let consequence = parser.parse_block_statement()?; + + let alternative = if parser.peek_token_is(&Token::Else) { + parser.next_token(); + + parser.expect_peek(Token::Lbrace)?; + + let alt_block = parser.parse_block_statement()?; + Some(alt_block) + } else { + None + }; + + Ok(Expression::If(Box::new(IfExpression { condition, consequence, alternative }))) + } + + fn parse_block_statement(&mut self) -> ParseResult { + let mut statements = Vec::new(); + + self.next_token(); + + while !self.cur_token_is(Token::Rbrace) && !self.cur_token_is(Token::EOF) { + if let Ok(stmt) = self.parse_statement() { + statements.push(stmt); + } + self.next_token(); + } + + Ok(BlockStatement { statements }) + } + + fn parse_function_literal(parser: &mut Parser) -> ParseResult { + parser.expect_peek(Token::Lparen)?; + + let parameters = parser.parse_function_parameters()?; + + parser.expect_peek(Token::Lbrace)?; + + let body = parser.parse_block_statement()?; + + Ok(Expression::Function(Box::new(FunctionLiteral{parameters,body}))) + } + + fn parse_function_parameters(&mut self) -> Result, ParseError> { + let mut identifiers: Vec = Vec::new(); + + if self.peek_token_is(&Token::Rparen) { + self.next_token(); + return Ok(identifiers) + } + + self.next_token(); + + identifiers.push(self.parse_identifier_into_identifier_expression()?); + + while self.peek_token_is(&Token::Comma) { + self.next_token(); + self.next_token(); + identifiers.push(self.parse_identifier_into_identifier_expression()?); + } + + self.expect_peek(Token::Rparen)?; + + Ok(identifiers) + } + + fn parse_grouped_expression(parser: &mut Parser) -> ParseResult { + parser.next_token(); + + let exp = parser.parse_expression(Precedence::Lowest); + + parser.expect_peek(Token::Rparen)?; + + exp + } + + fn infix_fn(&mut self) -> Option { + match self.peek_token { + Token::Plus | Token::Minus | Token::Slash | Token::Asterisk | Token::Eq | Token::Neq | Token::Lt | Token::Gt => Some(Parser::parse_infix_expression), + Token::Lparen => Some(Parser::parse_call_expression), + Token::Lbracket => Some(Parser::parse_index_expression), + _ => None, + } + } + + fn parse_index_expression(parser: &mut Parser, left: Expression) -> ParseResult { + parser.next_token(); + + let exp = IndexExpression{left, index: parser.parse_expression(Precedence::Lowest)?}; + + parser.expect_peek(Token::Rbracket)?; + + Ok(Expression::Index(Box::new(exp))) + } + + fn parse_call_expression(parser: &mut Parser, function: Expression) -> ParseResult { + let arguments = parser.parse_expression_list(Token::Rparen)?; + Ok(Expression::Call(Box::new(CallExpression{function, arguments}))) + } + + fn parse_infix_expression(parser: &mut Parser, left: Expression) -> ParseResult { + let operator = parser.cur_token.clone(); + let precedence = parser.cur_precedence(); + + parser.next_token(); + + let right = parser.parse_expression(precedence)?; + + Ok(Expression::Infix(Box::new(InfixExpression { operator, left, right }))) + } + + fn parse_boolean(parser: &mut Parser) -> ParseResult { + match parser.cur_token { + Token::True => Ok(Expression::Boolean(true)), + Token::False => Ok(Expression::Boolean(false)), + // we should never hit this since this function is only handed out for tokens matched as boolean + _ => panic!("couldn't parse {:?} to boolean", parser.cur_token) + } + } + + fn parse_identifier_into_identifier_expression(&mut self) -> ParseResult { + if let Token::Ident(ref name) = self.cur_token { + return Ok(IdentifierExpression { name: name.to_string() }); + } + + Err(format!("unexpected error on identifier parse with {}", self.cur_token)) + } + + fn parse_identifier(parser: &mut Parser) -> ParseResult { + if let Token::Ident(ref name) = parser.cur_token { + return Ok(Expression::Identifier(name.to_string())); + } + + Err(format!("unexpected error on identifier parse with {}", parser.cur_token)) + } + + fn parse_string_literal(parser: &mut Parser) -> ParseResult { + if let Token::String(ref s) = parser.cur_token { + return Ok(Expression::String(s.to_string())); + } + + Err(format!("unexpected error on string parse with {}", parser.cur_token)) + } + + fn parse_integer_literal(parser: &mut Parser) -> ParseResult { + if let Token::Int(value) = parser.cur_token { + return Ok(Expression::Integer(value)); + } + + Err(format!("error parsing integer literal {}", parser.cur_token)) + } + + fn parse_return_statement(&mut self) -> ParseResult { + self.next_token(); + + let value = self.parse_expression(Precedence::Lowest)?; + + if self.peek_token_is(&Token::Semicolon) { + self.next_token(); + } + + Ok(Statement::Return(Box::new(ReturnStatement { value }))) + } + + fn parse_let_statement(&mut self) -> ParseResult { + let name = self.expect_ident()?; + + self.expect_peek(Token::Assign)?; + + self.next_token(); + + let value = self.parse_expression(Precedence::Lowest)?; + + if self.peek_token_is(&Token::Semicolon) { + self.next_token(); + } + + Ok(Statement::Let(Box::new(LetStatement { name, value }))) + } + + fn peek_precedence(&self) -> Precedence { + Precedence::token_precedence(&self.peek_token) + } + + fn cur_precedence(&self) -> Precedence { + Precedence::token_precedence(&self.cur_token) + } + + fn cur_token_is(&self, tok: Token) -> bool { + match (&tok, &self.cur_token) { + (Token::Ident(_), Token::Ident(_)) => true, + (Token::Int(_), Token::Int(_)) => true, + _ => tok == self.cur_token, + } + } + + fn peek_token_is(&self, tok: &Token) -> bool { + match (&tok, &self.peek_token) { + (Token::Ident(_), Token::Ident(_)) => true, + (Token::Int(_), Token::Int(_)) => true, + _ => tok == &self.peek_token, + } + } + + fn expect_peek(&mut self, tok: Token) -> Result<(), ParseError> { + match self.peek_token_is(&tok) { + true => { + self.next_token(); + Ok(()) + } + false => Err(format!("expected next token to be {} got {} instead", tok, self.peek_token)) + } + } + + fn expect_ident(&mut self) -> Result { + let name = match &self.peek_token { + Token::Ident(name) => name.to_string(), + _ => return Err(format!("invalid identifier {}", self.peek_token)), + }; + + self.next_token(); + Ok(name) + } + + fn next_token(&mut self) { + self.cur_token = self.peek_token.clone(); + self.peek_token = self.l.next_token(); + } +} + +#[cfg(test)] +mod test { + use super::*; + use lexer::Lexer; + + #[test] + fn let_statement() { + let input = "\ +let x = 5; +let y = 10; +let foobar = 838383;"; + + let prog = setup(input, 3); + + let tests = vec![ + "x", + "y", + "foobar", + ]; + + let mut itr = prog.statements.iter(); + + for t in tests { + match itr.next().unwrap() { + Statement::Let(ref l) => { + assert_eq!(l.name, t); + + }, + _ => panic!("unknown node") + } + } + } + + #[test] + fn let_statement_bool() { + let prog = setup("let y = true;", 1); + let exp = let_statement_parse_and_verify(&prog, "y"); + match exp { + Expression::Boolean(b) => assert_eq!(b, &true), + _ => panic!("expected boolean expression") + } + } + + #[test] + fn let_statement_ident() { + let prog = setup("let foobar = y;", 1); + let exp = let_statement_parse_and_verify(&prog, "foobar"); + match exp { + Expression::Identifier(_) => test_identifier(&exp, "y"), + _ => panic!("expected identifier expression") + } + } + + fn let_statement_parse_and_verify<'a>(prog: &'a Program, expected_ident: &str) -> &'a Expression { + let stmt = prog.statements.first().unwrap(); + match stmt { + Statement::Let(stmt) => { + assert_eq!(stmt.name.as_str(), expected_ident); + return &stmt.value + }, + stmt => panic!("expected let statement but got {:?}", stmt) + } + } + + #[test] + fn let_statement_error() { + let input = "\ +let x 5; +let = 10; +let y = 23; +let 23432"; + + let l = Lexer::new(input); + let mut p = Parser::new(l); + + match p.parse_program() { + Ok(_) => panic!("should have retured a parse failure"), + Err(errors) => { + if errors.len() != 4 { + panic!("got {} errors instead of 3\n{:?}", errors.len(), errors) + } + + let expected_errors = vec![ + "expected next token to be = got 5 instead", + "invalid identifier =", + "no prefix parse function for = found", + "invalid identifier 23432" + ]; + + let mut itr = errors.iter(); + + for err in expected_errors { + let message = itr.next().unwrap(); + assert_eq!(message, err, "expected error '{}' but got '{}'", err, message) + } + } + } + } + + #[test] + fn return_statement() { + let input = "\ +return 5;\ +return 10;\ +return 993322;"; + + let prog = setup(input, 3); + + for stmt in prog.statements { + match stmt { + Statement::Return(_) => {} + _ => panic!("statement {:?} isn't a return statement", stmt), + } + } + } + + #[test] + fn return_statement_bool() { + let prog = setup("return true;", 1); + let exp = return_statement_parse_and_verify(&prog); + match exp { + Expression::Boolean(b) => assert_eq!(b, &true), + _ => panic!("expected boolean expression") + } + } + + #[test] + fn return_statement_ident() { + let prog = setup("return foobar;", 1); + let exp = return_statement_parse_and_verify(&prog); + match exp { + Expression::Identifier(_) => test_identifier(&exp, "foobar"), + _ => panic!("expected identifier expression") + } + } + + fn return_statement_parse_and_verify<'a>(prog: &'a Program) -> &'a Expression { + let stmt = prog.statements.first().unwrap(); + match stmt { + Statement::Return(stmt) => { + return &stmt.value + }, + stmt => panic!("expected return statement but got {:?}", stmt) + } + } + + #[test] + fn identifier_expression() { + let input = "foobar;"; + + let prog = setup(input, 1); + let exp = unwrap_expression(&prog); + + test_identifier(exp, "foobar"); + } + + #[test] + fn integer_literal() { + let input = "5;"; + + let prog = setup(input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Integer(int) => assert_eq!(*int, 5, "expected value to be 5 but got {}", int), + exp => panic!("expected integer literal expression but got {:?}", exp) + } + } + + #[test] + fn prefix_expressions() { + struct Test<'a> { + input: &'a str, + operator: Token, + value: i64, + } + + // TODO: add tests for boolean prefix expressions like !true; and !false; + let tests = vec![ + Test { input: "!5;", operator: Token::Bang, value: 5 }, + Test { input: "-15;", operator: Token::Minus, value: 15 }, + ]; + + for t in tests { + let prog = setup(t.input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Prefix(prefix) => { + assert_eq!(t.operator, prefix.operator, "expected {} operator but got {}", t.operator, prefix.operator); + test_integer_literal(&prefix.right, t.value); + } + exp => panic!("expected prefix expression but got {:?}", exp) + } + } + } + + #[test] + fn infix_expressions() { + struct Test<'a> { + input: &'a str, + left_value: i64, + operator: Token, + right_value: i64, + } + + let tests = vec![ + Test { input: "5 + 5;", left_value: 5, operator: Token::Plus, right_value: 5 }, + Test { input: "5 - 5;", left_value: 5, operator: Token::Minus, right_value: 5 }, + Test { input: "5 * 5;", left_value: 5, operator: Token::Asterisk, right_value: 5 }, + Test { input: "5 / 5;", left_value: 5, operator: Token::Slash, right_value: 5 }, + Test { input: "5 > 5;", left_value: 5, operator: Token::Gt, right_value: 5 }, + Test { input: "5 < 5;", left_value: 5, operator: Token::Lt, right_value: 5 }, + Test { input: "5 == 5;", left_value: 5, operator: Token::Eq, right_value: 5 }, + Test { input: "5 != 5;", left_value: 5, operator: Token::Neq, right_value: 5 }, + ]; + + for t in tests { + let prog = setup(t.input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Infix(infix) => { + assert_eq!(t.operator, infix.operator, "expected {} operator but got {}", t.operator, infix.operator); + test_integer_literal(&infix.left, t.left_value); + test_integer_literal(&infix.right, t.right_value); + } + exp => panic!("expected prefix expression but got {:?}", exp) + } + } + } + + #[test] + fn infix_boolean_literal_expressions() { + struct Test<'a> { + input: &'a str, + left_value: bool, + operator: Token, + right_value: bool, + } + + let tests = vec![ + Test { input: "true == true", left_value: true, operator: Token::Eq, right_value: true }, + Test { input: "true != false", left_value: true, operator: Token::Neq, right_value: false }, + Test { input: "false == false", left_value: false, operator: Token::Eq, right_value: false }, + ]; + + for t in tests { + let prog = setup(t.input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Infix(infix) => { + assert_eq!(t.operator, infix.operator, "expected {} operator but got {}", t.operator, infix.operator); + test_boolean_literal(&infix.left, t.left_value); + test_boolean_literal(&infix.right, t.right_value); + } + exp => panic!("expected infix expression but got {:?}", exp) + } + } + } + + #[test] + fn operator_precedence() { + struct Test<'a> { + input: &'a str, + expected: &'a str, + } + + let tests = vec![ + Test { input: "-a * b", expected: "((-a) * b)" }, + Test { input: "!-a", expected: "(!(-a))" }, + Test { input: "a + b + c", expected: "((a + b) + c)" }, + Test { input: "a + b - c", expected: "((a + b) - c)" }, + Test { input: "a * b * c", expected: "((a * b) * c)" }, + Test { input: "a * b / c", expected: "((a * b) / c)" }, + Test { input: "a + b / c", expected: "(a + (b / c))" }, + Test { input: "a + b * c + d / e - f", expected: "(((a + (b * c)) + (d / e)) - f)" }, + Test { input: "3 + 4; -5 * 5", expected: "(3 + 4)((-5) * 5)" }, + Test { input: "5 > 4 == 3 < 4", expected: "((5 > 4) == (3 < 4))" }, + Test { input: "5 < 4 != 3 > 4", expected: "((5 < 4) != (3 > 4))" }, + Test { input: "3 + 4 * 5 == 3 * 1 + 4 * 5", expected: "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))" }, + Test { input: "true", expected: "true" }, + Test { input: "false", expected: "false" }, + Test { input: "3 > 5 == false", expected: "((3 > 5) == false)" }, + Test { input: "3 < 5 == true", expected: "((3 < 5) == true)" }, + Test { input: "1 + (2 + 3) + 4", expected: "((1 + (2 + 3)) + 4)" }, + Test { input: "(5 + 5) * 2", expected: "((5 + 5) * 2)" }, + Test { input: "2 / (5 + 5)", expected: "(2 / (5 + 5))" }, + Test { input: "-(5 + 5)", expected: "(-(5 + 5))" }, + Test { input: "!(true == true)", expected: "(!(true == true))" }, + Test { input: "a + add(b * c) + d", expected: "((a + add((b * c))) + d)" }, + Test { input: "add(a, b, 1, 2 * 3, 4 + 5, add(6, 7 * 8))", expected: "add(a, b, 1, (2 * 3), (4 + 5), add(6, (7 * 8)))" }, + Test { input: "add(a + b + c * d / f + g)", expected: "add((((a + b) + ((c * d) / f)) + g))" }, + Test { input: "a * [1, 2, 3, 4][b * c] * d", expected: "((a * ([1, 2, 3, 4][(b * c)])) * d)" }, + Test { input: "add(a * b[2], b[1], 2 * [1, 2][1])", expected: "add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))" }, + ]; + + for t in tests { + let prog = setup(t.input, 0).to_string(); + + assert_eq!(t.expected, prog, "expected '{}' but got '{}'", t.expected, prog) + } + } + + #[test] + fn boolean_expression() { + struct Test<'a> { + input: &'a str, + expected: bool, + } + + let tests = vec![ + Test { input: "true;", expected: true }, + Test { input: "false;", expected: false }, + ]; + + for t in tests { + let prog = setup(t.input, 1); + let exp = unwrap_expression(&prog); + + test_boolean_literal(&exp, t.expected); + } + } + + #[test] + fn if_expression() { + let input = "if (x < y) { x }"; + + let prog = setup(input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::If(ifexpr) => { + test_if_condition(&ifexpr.condition, Token::Lt, "x", "y"); + + assert_eq!(ifexpr.consequence.statements.len(), 1, "expected only 1 statement"); + match ifexpr.consequence.statements.first().unwrap() { + Statement::Expression(stmt) => test_identifier(&stmt.expression, "x"), + stmt => panic!("expected expression statement but got {:?}", stmt) + } + if let Some(stmt) = &ifexpr.alternative { + panic!("expected alternative to be None but got {:?}", stmt) + } + } + _ => panic!("expected if expression but got {:?}", exp) + } + } + + #[test] + fn if_else_expression() { + let input = "if (x < y) { x } else { y }"; + + let prog = setup(input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::If(ifexpr) => { + test_if_condition(&ifexpr.condition, Token::Lt, "x", "y"); + + assert_eq!(ifexpr.consequence.statements.len(), 1); + match &ifexpr.consequence.statements.first().unwrap() { + Statement::Expression(stmt) => test_identifier(&stmt.expression, "x"), + stmt => panic!("expected expression statement but got {:?}", stmt) + } + + if let Some(stmt) = &ifexpr.alternative { + assert_eq!(stmt.statements.len(), 1); + match stmt.statements.first().unwrap() { + Statement::Expression(stmt) => test_identifier(&stmt.expression, "y"), + stmt => panic!("expected expression statement but got {:?}", stmt) + } + } else { + panic!("expected alternative block") + } + } + _ => panic!("expected if expression but got {:?}", exp) + } + } + + #[test] + fn function_literal() { + let input = "fn(x, y) { x + y; }"; + let prog = setup(input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Function(func) => { + assert_eq!(2, func.parameters.len(), "expected 2 parameters but got {:?}", func.parameters); + assert_eq!(func.parameters.first().unwrap().name, "x"); + assert_eq!(func.parameters.last().unwrap().name, "y"); + assert_eq!(1, func.body.statements.len(), "expecte 1 body statement but got {:?}", func.body.statements); + + match func.body.statements.first().unwrap() { + Statement::Expression(stmt) => match &stmt.expression { + Expression::Infix(infix) => { + assert_eq!(infix.operator, Token::Plus, "expected + but got {}", infix.operator); + test_identifier(&infix.left, "x"); + test_identifier(&infix.right, "y"); + }, + _ => panic!("expected infix expression but got {:?}", stmt.expression) + }, + stmt => panic!("expected expression statement but got {:?}", stmt) + } + }, + _ => panic!("{} is not a function literal", exp) + } + } + + #[test] + fn function_parameters() { + struct Test<'a> { + input: &'a str, + expected_params: Vec<&'a str>, + } + + let tests = vec![ + Test{input: "fn() {};", expected_params: vec![]}, + Test{input: "fn(x) {};", expected_params: vec!["x"]}, + Test{input: "fn(x, y, z) {};", expected_params: vec!["x", "y", "z"]}, + ]; + + for t in tests { + let prog = setup(t.input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Function(func) => { + assert_eq!(func.parameters.len(), t.expected_params.len()); + let mut params = t.expected_params.into_iter(); + for param in &func.parameters { + let expected_param = params.next().unwrap(); + assert_eq!(expected_param, param.name.as_str()); + } + }, + _ => panic!("{:?} not a function literal", exp) + } + } + } + + #[test] + fn call_expression() { + let input = "add(1, 2 * 3, 4 + 5);"; + let prog = setup(input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Call(call) => { + test_identifier(&call.function, "add"); + assert_eq!(call.arguments.len(), 3); + let mut args = (&call.arguments).into_iter(); + test_integer_literal(&args.next().unwrap(), 1); + test_infix(&args.next().unwrap(), 2, Token::Asterisk, 3); + test_infix(&args.next().unwrap(), 4, Token::Plus, 5) + }, + _ => panic!("{} is not a call expression", exp) + } + } + + #[test] + fn call_expression_parameter_parsing() { + struct Test<'a> { + input: &'a str, + expected_ident: &'a str, + expected_args: Vec<&'a str>, + } + + let tests = vec![ + Test{input: "add();", expected_ident: "add", expected_args: vec![]}, + Test{input: "add(1);", expected_ident: "add", expected_args: vec!["1"]}, + Test{input: "add(1, 2 * 3, 4 + 5);", expected_ident: "add", expected_args: vec!["1", "(2 * 3)", "(4 + 5)"]}, + ]; + + for t in tests { + let prog = setup(t.input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Call(call) => { + test_identifier(&call.function, t.expected_ident); + assert_eq!(call.arguments.len(), t.expected_args.len()); + let mut args = (&call.arguments).into_iter(); + for a in t.expected_args { + assert_eq!(a.to_string(), args.next().unwrap().to_string()); + } + }, + _ => panic!("{:?} is not a call expression", exp) + } + } + } + + #[test] + fn string_literal_expression() { + let input = r#""hello world""#; + let prog = setup(input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::String(s) => assert_eq!(s, "hello world"), + _ => panic!("expected string literal but got {:?}", exp) + } + } + + #[test] + fn array_literals() { + let input = "[1, 2 * 2, 3 + 3]"; + let prog = setup(input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Array(a) => { + test_integer_literal(a.elements.first().unwrap(), 1); + test_infix(a.elements.get(1).unwrap(), 2, Token::Asterisk, 2); + test_infix(a.elements.last().unwrap(), 3, Token::Plus, 3); + }, + _ => panic!("expected array literal but got {:?}", exp) + } + } + + #[test] + fn index_expressions() { + let input = "myArray[1 + 1]"; + let prog = setup(input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Index(i) => { + test_identifier(&i.left, "myArray"); + test_infix(&i.index, 1, Token::Plus, 1); + }, + _ => panic!("expected an index expression but got {:?}", exp) + } + } + + #[test] + fn hash_literals() { + let input = r#"{"one": 1, "two": 2, "three": 3, 4: 4, true: true}"#; + let prog = setup(input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Hash(h) => { + assert_eq!(h.pairs.len(), 5); + + for (k, v) in &h.pairs { + match (&k, &v) { + (Expression::String(key), Expression::Integer(int)) => { + match key.as_str() { + "one" => assert_eq!(1, *int), + "two" => assert_eq!(2, *int), + "three" => assert_eq!(3, *int), + _ => panic!("unexpected key {}", k) + } + }, + (Expression::Integer(key), Expression::Integer(int)) => { + assert_eq!(*key, *int); + assert_eq!(*int, 4); + }, + (Expression::Boolean(key), Expression::Boolean(val)) => { + assert_eq!(key, val) + }, + _ => panic!("expected key to be a string and value to be an int but got {:?} and {:?}", k, v) + } + } + }, + _ => panic!("expected a hash literal but got {:?}", exp) + } + } + + #[test] + fn hash_literal_with_expressions() { + let input = r#"{"one": 0 + 1, "two": 10 - 8, "three": 15 / 5}"#; + let prog = setup(input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Hash(h) => { + assert_eq!(h.pairs.len(), 3); + + for (k, v) in &h.pairs { + match (&k, &v) { + (Expression::String(key), Expression::Infix(_)) => { + match key.as_str() { + "one" => test_infix(v, 0, Token::Plus, 1), + "two" => test_infix(v, 10, Token::Minus, 8), + "three" => test_infix(v, 15, Token::Slash, 5), + _ => panic!("unexpected key {}", key) + } + }, + _ => panic!("expected key to be a string and value to be an infix expression but got {:?} and {:?}", k, v) + } + } + }, + _ => panic!("expected a hash literal but got {:?}", exp) + } + } + + #[test] + fn empty_hash_literal() { + let input = "{}"; + let prog = setup(input, 1); + let exp = unwrap_expression(&prog); + + match exp { + Expression::Hash(h) => { + assert_eq!(h.pairs.len(), 0) + }, + _ => panic!("expected a hash literal but got {:?}", exp) + } + } + + fn test_infix(exp: &Expression, left: i64, op: Token, right: i64) { + match exp { + Expression::Infix(infix) => { + assert_eq!(op, infix.operator, "expected {} operator but got {}", op, infix.operator); + test_integer_literal(&infix.left, left); + test_integer_literal(&infix.right, right); + } + exp => panic!("expected prefix expression but got {:?}", exp) + } + } + + fn test_if_condition(exp: &Expression, operator: Token, left: &str, right: &str) { + match exp { + Expression::Infix(infix) => { + test_identifier(&infix.left, left); + test_identifier(&infix.right, right); + if operator != infix.operator { + panic!("expected {} operator but got {}", operator, infix.operator) + } + } + _ => panic!("expected infix expression but got {:?}", exp) + } + } + + fn setup(input: &str, stmt_count: usize) -> Program { + let l = Lexer::new(input); + let mut p = Parser::new(l); + let prog = p.parse_program().unwrap(); + + if stmt_count != 0 && prog.statements.len() != stmt_count { + panic!("expected 1 statement for '{}' but got {:?}", input, prog.statements) + } + + prog + } + + fn unwrap_expression(prog: &Program) -> &Expression { + match prog.statements.first().unwrap() { + Statement::Expression(stmt) => &stmt.expression, + stmt => panic!("{:?} isn't an expression statement", stmt) + } + } + + fn test_integer_literal(exp: &Expression, value: i64) { + match exp { + Expression::Integer(int) => assert_eq!(value, *int, "expected {} but got {}", value, int), + _ => panic!("expected integer literal {} but got {:?}", value, exp) + } + } + + fn test_boolean_literal(exp: &Expression, value: bool) { + match exp { + Expression::Boolean(val) => assert_eq!(value, *val, "expected {} but got {}", value, val), + _ => panic!("expected boolean literal {} but got {:?}", value, exp) + } + } + + fn test_identifier(exp: &Expression, value: &str) { + match exp { + Expression::Identifier(ident) => assert_eq!(value, ident, "expected {} but got {}", value, ident), + _ => panic!("expected identifier expression but got {:?}", exp) + } + } +} + diff --git a/src/repl.rs b/src/repl.rs new file mode 100644 index 0000000..36c271d --- /dev/null +++ b/src/repl.rs @@ -0,0 +1,32 @@ +use std::io; +use std::cell::RefCell; +use std::rc::Rc; +use parser; +use evaluator; +use object::Environment; + +pub fn start(mut reader: R, mut writer: W) -> io::Result<()> { + #![allow(warnings)] + let mut env = Rc::new(RefCell::new(Environment::new())); + loop { + writer.write(b"> "); + writer.flush(); + let mut line = String::new(); + reader.read_line(&mut line)?; + + match parser::parse(&line) { + Ok(node) => { + match evaluator::eval(&node, Rc::clone(&env)) { + Ok(n) => write!(writer, "{}\n", n.inspect()), + Err(e) => write!(writer, "error: {}\n", e.message), + }; + }, + Err(errors) => { + for err in errors { + write!(writer, "parse errors:\n{}\n", err.to_string()); + }; + }, + } + } + Ok(()) +} diff --git a/src/token.rs b/src/token.rs new file mode 100644 index 0000000..750608a --- /dev/null +++ b/src/token.rs @@ -0,0 +1,105 @@ +use std::fmt; + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub enum Token { + Illegal, + EOF, + + // Identifiers + literals + Ident(String), + Int(i64), + String(String), + + // Operators + Assign, + Plus, + Minus, + Bang, + Asterisk, + Slash, + + Lt, + Gt, + Eq, + Neq, + + // Delimiters + Comma, + Semicolon, + Colon, + + Lparen, + Rparen, + Lbrace, + Rbrace, + Lbracket, + Rbracket, + + // Keywords + Function, + Let, + True, + False, + If, + Else, + Return, +} + +impl fmt::Display for Token { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Token::Ident(name) => write!(f, "{}", name), + Token::Int(val) => write!(f, "{}", val), + Token::Minus => write!(f, "-"), + Token::Plus => write!(f, "+"), + Token::Bang => write!(f, "!"), + Token::Asterisk => write!(f, "*"), + Token::Slash => write!(f, "/"), + Token::Gt => write!(f, ">"), + Token::Lt => write!(f, "<"), + Token::Eq => write!(f, "=="), + Token::Neq => write!(f, "!="), + Token::Semicolon => write!(f, ";"), + Token::Colon => write!(f, ":"), + Token::Assign => write!(f, "="), + Token::Function => write!(f, "fn"), + Token::Lparen => write!(f, "("), + Token::Rparen => write!(f, ")"), + Token::Lbrace => write!(f, "{{"), + Token::Rbrace => write!(f, "}}"), + Token::Comma => write!(f, ","), + Token::Lbracket => write!(f, "["), + Token::Rbracket => write!(f, "]"), + tok => write!(f, "{:?}", tok), + } + } +} + +pub fn lookup_ident(ident: String) -> Token { + match ident.as_str() { + "let" => Token::Let, + "fn" => Token::Function, + "true" => Token::True, + "false" => Token::False, + "if" => Token::If, + "else" => Token::Else, + "return" => Token::Return, + _ => Token::Ident(ident), + } +} + +impl Token { + pub fn is_ident(&self) -> bool { + match self { + Token::Ident(_) => true, + _ => false, + } + } + + pub fn is_int(&self) -> bool { + match self { + Token::Int(_) => true, + _ => false, + } + } +} \ No newline at end of file