diff --git a/src/env/core.rs b/src/env/core.rs new file mode 100644 index 0000000..3b00535 --- /dev/null +++ b/src/env/core.rs @@ -0,0 +1,306 @@ +use std::borrow::Borrow; +use std::collections::HashMap; + +use crate::error::Error; +use crate::evaluator::eval_node; +use crate::macros::arg_count; +use crate::node::Node; + +pub(super) fn core() -> HashMap { + [ + // Arithmetic operations + ( + "+", + Node::NativeFunc(|_env, args| { + let res = args + .into_iter() + .reduce(|lhs, rhs| match (lhs, rhs) { + (Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs + rhs), + _ => todo!(), + }) + .unwrap_or(Node::Int(0)); + + Ok(res) + }), + ), + ( + "-", + Node::NativeFunc(|_env, args| { + let res = args + .into_iter() + .reduce(|lhs, rhs| match (lhs, rhs) { + (Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs - rhs), + _ => todo!(), + }) + .unwrap_or(Node::Int(0)); + + Ok(res) + }), + ), + ( + "*", + Node::NativeFunc(|_env, args| { + let res = args + .into_iter() + .reduce(|lhs, rhs| match (lhs, rhs) { + (Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs * rhs), + _ => todo!(), + }) + .unwrap_or(Node::Int(0)); + + Ok(res) + }), + ), + ( + "/", + Node::NativeFunc(|_env, args| { + let res = args + .into_iter() + .reduce(|lhs, rhs| match (lhs, rhs) { + (Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs / rhs), + _ => todo!(), + }) + .unwrap_or(Node::Int(0)); + + Ok(res) + }), + ), + // Collections + ( + "vector", + Node::NativeFunc(|_env, args| Ok(Node::Vector(args))), + ), + ( + "vector?", + Node::NativeFunc(|_env, args| { + arg_count!(1, args.len()); + + if let Node::Vector(_vec) = args[0].borrow() { + return Ok(Node::Boolean(true)); + } + + Ok(Node::Boolean(false)) + }), + ), + ( + "hashmap", + Node::NativeFunc(|_env, args| { + arg_count!(modulo: 2, args.len()); + + let mut index = -1; + let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().partition(|_| { + index += 1; + index % 2 == 0 + }); + + let res = keys + .into_iter() + // We turn the keys into strings because they're hashable + // This feels so hacky, but ¯\_(ツ)_/¯ + .map(|key| key.to_string()) + .zip(values) + .collect(); + + Ok(Node::Map(res)) + }), + ), + ( + "hashmap?", + Node::NativeFunc(|_env, args| { + arg_count!(1, args.len()); + + if let Node::Map(_map) = args[0].borrow() { + return Ok(Node::Boolean(true)); + } + + Ok(Node::Boolean(false)) + }), + ), + // Ordering + ( + "eq?", + Node::NativeFunc(|_env, args| { + arg_count!(2, args.len()); + + let lhs = args[0].borrow(); + let rhs = args[1].borrow(); + + match (lhs, rhs) { + (Node::Int(lhs), Node::Int(rhs)) => Ok(Node::Boolean(lhs == rhs)), + (Node::Boolean(lhs), Node::Boolean(rhs)) => Ok(Node::Boolean(lhs == rhs)), + (Node::String(lhs), Node::String(rhs)) => Ok(Node::Boolean(lhs == rhs)), + (Node::Nil, Node::Nil) => Ok(Node::Boolean(true)), + _ => Err(Error::ExpectedNumber)?, + } + }), + ), + ( + "not", + Node::NativeFunc(|_env, args| { + arg_count!(1, args.len()); + + let expr = args[0].borrow(); + if let Node::Boolean(false) = expr { + return Ok(Node::Boolean(true)); + } + + Ok(Node::Boolean(false)) + }), + ), + ( + "<", + Node::NativeFunc(|_env, args| { + arg_count!(2, args.len()); + + let less_than = match (args[0].borrow(), args[1].borrow()) { + (Node::Int(lhs), Node::Int(rhs)) => lhs < rhs, + _ => Err(Error::ExpectedNumber)?, + }; + + Ok(Node::Boolean(less_than)) + }), + ), + ( + ">", + Node::NativeFunc(|_env, args| { + arg_count!(2, args.len()); + + let greater_than = match (args[0].borrow(), args[1].borrow()) { + (Node::Int(lhs), Node::Int(rhs)) => lhs > rhs, + _ => Err(Error::ExpectedNumber)?, + }; + + Ok(Node::Boolean(greater_than)) + }), + ), + ( + "<=", + Node::NativeFunc(|_env, args| { + arg_count!(2, args.len()); + + let less_than_equal = match (args[0].borrow(), args[1].borrow()) { + (Node::Int(lhs), Node::Int(rhs)) => lhs <= rhs, + _ => Err(Error::ExpectedNumber)?, + }; + + Ok(Node::Boolean(less_than_equal)) + }), + ), + ( + ">=", + Node::NativeFunc(|_env, args| { + arg_count!(2, args.len()); + + let greater_than_equal = match (args[0].borrow(), args[1].borrow()) { + (Node::Int(lhs), Node::Int(rhs)) => lhs >= rhs, + _ => Err(Error::ExpectedNumber)?, + }; + + Ok(Node::Boolean(greater_than_equal)) + }), + ), + // Branching + ( + "if", + Node::NativeFunc(|_env, args| { + arg_count!(3, args.len()); + + let (cond, consequence, alternative) = + (args[0].clone(), args[1].clone(), args[2].clone()); + + // If the value is anything other than false, then it is truthy + if let Node::Boolean(false) = cond { + return Ok(alternative); + }; + + Ok(consequence) + }), + ), + // Environment Manipulation + ( + "define!", + Node::Special(|env, args| { + let mut args = args.into_iter(); + arg_count!(2, args.len()); + + let key = match args.next().unwrap() { + Node::Symbol(name) => name, + _ => Err(Error::ExpectedSymbol)?, + }; + + let val = eval_node(env, args.next().unwrap())?; + + env.set(key, val.clone()); + + Ok(val) + }), + ), + ( + "let*", + Node::Special(|env, args| { + arg_count!(2, args.len()); + dbg!("Hi mom"); + + let mut args = args.into_iter(); + let new_env = match args.next().unwrap() { + Node::List(list) if list.len() == 2 => { + let mut list = list; + let sym = match list.remove(0) { + Node::Symbol(name) => name, + _ => Err(Error::ExpectedSymbol)?, + }; + + let val = eval_node(env, list.remove(0))?; + env.wrap(vec![(sym, val)]) + } + Node::List(list) => Err(Error::MismatchedArgCount(2, list.len()))?, + _ => Err(Error::ExpectedList)?, + }; + + eval_node(&new_env, args.next().unwrap()) + }), + ), + ( + "fn*", + Node::Special(|env, args| { + let mut args = args; + arg_count!(2, args.len()); + + let arg_list = args.remove(0); + let body = args.remove(0); + + let args: Vec = match arg_list { + Node::List(args) => args + .into_iter() + .map(|v| match v { + Node::Symbol(sym) => Ok(sym), + _ => Err(Error::ExpectedSymbol), + }) + .collect(), + _ => Err(Error::ExpectedList), + }?; + + Ok(Node::Function { + params: args, + env: env.clone(), + body: Box::new(body), + }) + }), + ), + // IO + ( + "display", + Node::NativeFunc(|_env, args| { + arg_count!(1, args.len()); + + let val = args[0].borrow(); + print!("{}", val); + + Ok(val.clone()) + }), + ), + ] + .into_iter() + .map(|(k, v)| (k.to_string(), v)) + .collect() +} diff --git a/src/env/mod.rs b/src/env/mod.rs new file mode 100644 index 0000000..f39c3c5 --- /dev/null +++ b/src/env/mod.rs @@ -0,0 +1,44 @@ +use std::cell::RefCell; +use std::collections::HashMap; + +use crate::node::Node; + +mod core; + +type RawEnvironment = HashMap; + +#[derive(Debug, Clone)] +pub struct Environment { + current: RefCell, + outer: Option>, +} + +impl Environment { + pub fn new() -> Environment { + let current = RefCell::new(core::core()); + + Environment { + current, + outer: None, + } + } + + pub fn get(&self, ident: &str) -> Option { + if let Some(val) = self.current.borrow().get(ident) { + return Some(val.clone()); + } + + self.outer.as_ref()?.get(ident) + } + + pub fn wrap(&self, records: Vec<(String, Node)>) -> Environment { + Environment { + current: RefCell::new(records.into_iter().collect()), + outer: Some(Box::new(self.clone())), + } + } + + fn set(&self, ident: String, val: Node) { + self.current.borrow_mut().insert(ident, val); + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..0e6ba91 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,17 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum Error { + #[error("could not find symbol '{0}' in environment")] + NotInEnv(String), + #[error("expression does not have a valid operator")] + InvalidOperator, + #[error("expected symbol")] + ExpectedSymbol, + #[error("expected list")] + ExpectedList, + #[error("expected number")] + ExpectedNumber, + #[error("expected {0} arguments, got {1}")] + MismatchedArgCount(usize, usize), +} diff --git a/src/evaluator.rs b/src/evaluator.rs new file mode 100644 index 0000000..65bd195 --- /dev/null +++ b/src/evaluator.rs @@ -0,0 +1,166 @@ +use std::borrow::Borrow; + +use anyhow::Result; + +use crate::env::Environment; +use crate::error::Error; +use crate::node::Node; + +pub fn eval(env: &Environment, ast: Vec) -> Result> { + let mut res = Vec::new(); + + for node in ast { + res.push(eval_node(env, node)?); + } + + Ok(res) +} + +pub fn eval_node(env: &Environment, ast_node: Node) -> Result { + let expr = match ast_node { + Node::List(list) => { + // Empty lists are nil in scheme + if list.is_empty() { + return Ok(Node::Nil); + } + + // We always assume lists evaluate + let mut list = list.into_iter(); + let operator = eval_node(env, list.next().ok_or(Error::InvalidOperator)?)?; + + match operator.borrow() { + Node::NativeFunc(func) => { + let mut args = Vec::new(); + for node in list { + args.push(eval_node(env, node)?); + } + + func(env, args)? + } + + Node::Special(func) => { + let args = list.collect(); + func(&env, args)? + } + + Node::Function { + params, + env: inner_env, + body, + } => { + let args = list; + if args.len() != params.len() { + Err(Error::MismatchedArgCount(params.len(), args.len()))? + } + + let args = args + .map(|node| eval_node(env, node)) + .collect::>>()?; + + let records = params.iter().map(|k| k.to_owned()).zip(args).collect(); + + let env = inner_env.wrap(records); + + eval_node(&env, *(*body).clone())? + } + _ => Err(Error::InvalidOperator)?, + } + } + Node::Symbol(sym) => env.get(&sym).ok_or(Error::NotInEnv(sym))?.to_owned(), + + val => val, + }; + + Ok(expr) +} + +#[cfg(test)] +mod test { + use crate::parser; + + use super::*; + use rstest::rstest; + + #[rstest] + // Raw values + #[case("1", "1")] + #[case("\"uwu\"", "uwu")] + #[case(":owo", ":owo")] + #[case("()", "()")] + #[case("nil", "()")] + // Arithmetic + #[case("(+ 1 2)", "3")] + #[case("(- 5 1)", "4")] + #[case("(* 8 9)", "72")] + #[case("(/ 86 2)", "43")] + // Native functions + #[case("(+ 1 2 (- 3 4))", "2")] + #[case("(vector 1 2 3)", "[1 2 3]")] + // Native functions defaults + #[case("(+)", "0")] + #[case("(-)", "0")] + #[case("(*)", "0")] + #[case("(/)", "0")] + #[case("(vector)", "[]")] + // Collections + #[case("[]", "[]")] + #[case("[1 2]", "[1 2]")] + #[case("[1 (+ 1 2)]", "[1 3]")] + #[case("{}", "{}")] + #[case("{:a \"uwu\"}", "{:a: uwu}")] + // Environment manipulation + #[case("(define! asdf (+ 2 2)) (+ asdf 2)", "4\n6")] + #[case("(let* (a 2) (+ a 2))", "4")] + // Branching + #[case("(if true true false)", "true")] + #[case("(if nil true false)", "true")] + #[case("(if false true false)", "false")] + #[case("(if 4 true false)", "true")] + #[case("(if \"blue\" true false)", "true")] + // Functions + #[case("((fn* (a) a) 3)", "3")] + #[case("((fn* (a) (+ a 2)) 1)", "3")] + #[case("((fn* (a b) (+ a b)) 1 2)", "3")] + // Boolean operations + #[case("(eq? 1 1)", "true")] + #[case("(eq? 1 2)", "false")] + #[case("(not false)", "true")] + #[case("(not true)", "false")] + #[case("(not nil)", "false")] + #[case("(not 1)", "false")] + // Ordering + #[case("(< 1 2)", "true")] + #[case("(< 2 1)", "false")] + #[case("(> 1 2)", "false")] + #[case("(> 2 1)", "true")] + #[case("(<= 1 2)", "true")] + #[case("(<= 1 1)", "true")] + #[case("(<= 2 1)", "false")] + #[case("(>= 2 1)", "true")] + #[case("(>= 1 1)", "true")] + #[case("(>= 1 2)", "false")] + fn test_evaluator(#[case] input: &str, #[case] expected: &str) { + let env = Environment::new(); + let ast = parser::parse_str(input).unwrap(); + let res = eval(&env, ast) + .unwrap() + .into_iter() + .map(|elem| elem.to_string()) + .reduce(|lhs, rhs| format!("{lhs}\n{rhs}")) + .unwrap(); + + assert_eq!(res, expected); + } + + #[rstest] + #[case("{:a}")] + #[case("(not-a-func :uwu)")] + #[case("{:a}")] + fn test_evaluator_fail(#[case] input: &str) { + let env = Environment::new(); + let ast = parser::parse_str(input).unwrap(); + let res = eval(&env, ast); + + assert!(res.is_err()) + } +} diff --git a/src/evaluator/env.rs b/src/evaluator/env.rs deleted file mode 100644 index 58d5a9d..0000000 --- a/src/evaluator/env.rs +++ /dev/null @@ -1,352 +0,0 @@ -use std::{borrow::Borrow, cell::RefCell, collections::HashMap, rc::Rc}; - -use super::macros::arg_count; -use super::{eval_ast_node, Error, Expression}; -use crate::node::Node; - -pub type RawEnvironment = HashMap>; -#[derive(Debug, Clone)] -pub struct Environment { - current: RefCell, - outer: Option>, -} - -impl Environment { - pub fn get(&self, ident: &str) -> Option> { - if let Some(val) = self.current.borrow().get(ident) { - return Some(val.clone()); - } - - self.outer.clone()?.get(ident) - } - - pub fn wrap(&self, records: Vec<(String, Rc)>) -> Environment { - Environment { - current: RefCell::new(records.into_iter().collect()), - outer: Some(Rc::new(self.clone())), - } - } - - fn set(&self, ident: String, val: Rc) { - self.current.borrow_mut().insert(ident, val); - } -} - -pub fn core_environment() -> Rc { - let env = [ - // Arithmetic operations - ( - "+", - Expression::NativeFunc(|args| { - let res = args - .into_iter() - .reduce(|lhs, rhs| match (lhs.borrow(), rhs.borrow()) { - (Expression::Int(lhs), Expression::Int(rhs)) => { - Expression::Int(lhs + rhs).into() - } - _ => todo!(), - }) - .unwrap_or(Rc::new(Expression::Int(0))); - - Ok(res) - }), - ), - ( - "-", - Expression::NativeFunc(|args| { - let res = args - .into_iter() - .reduce(|lhs, rhs| match (lhs.borrow(), rhs.borrow()) { - (Expression::Int(lhs), Expression::Int(rhs)) => { - Expression::Int(lhs - rhs).into() - } - _ => todo!(), - }) - .unwrap_or(Rc::new(Expression::Int(0))); - - Ok(res) - }), - ), - ( - "*", - Expression::NativeFunc(|args| { - let res = args - .into_iter() - .reduce(|lhs, rhs| match (lhs.borrow(), rhs.borrow()) { - (Expression::Int(lhs), Expression::Int(rhs)) => { - Expression::Int(lhs * rhs).into() - } - _ => todo!(), - }) - .unwrap_or(Rc::new(Expression::Int(0))); - - Ok(res) - }), - ), - ( - "/", - Expression::NativeFunc(|args| { - let res = args - .into_iter() - .reduce(|lhs, rhs| match (lhs.borrow(), rhs.borrow()) { - (Expression::Int(lhs), Expression::Int(rhs)) => { - Expression::Int(lhs / rhs).into() - } - _ => todo!(), - }) - .unwrap_or(Rc::new(Expression::Int(0))); - - Ok(res) - }), - ), - // Collections - ( - "vector", - Expression::NativeFunc(|args| Ok(Rc::new(Expression::Vector(args)))), - ), - ( - "vector?", - Expression::NativeFunc(|args| { - arg_count!(1, args.len()); - - if let Expression::Vector(_vec) = args[0].borrow() { - return Ok(Expression::Boolean(true).into()); - } - - Ok(Expression::Boolean(false).into()) - }), - ), - ( - "hashmap", - Expression::NativeFunc(|args| { - arg_count!(modulo: 2, args.len()); - - let mut index = -1; - let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().partition(|_| { - index += 1; - index % 2 == 0 - }); - - let res = keys - .into_iter() - // We turn the keys into strings because they're hashable - // This feels so hacky, but ¯\_(ツ)_/¯ - .map(|key| key.to_string()) - .zip(values) - .collect(); - - Ok(Rc::new(Expression::HashMap(res))) - }), - ), - ( - "hashmap?", - Expression::NativeFunc(|args| { - arg_count!(1, args.len()); - - if let Expression::HashMap(_map) = args[0].borrow() { - return Ok(Expression::Boolean(true).into()); - } - - Ok(Expression::Boolean(false).into()) - }), - ), - // Ordering - ( - "eq?", - Expression::NativeFunc(|args| { - arg_count!(2, args.len()); - - let lhs: &Expression = args[0].borrow(); - let rhs: &Expression = args[1].borrow(); - - match (lhs, rhs) { - (Expression::Int(lhs), Expression::Int(rhs)) => { - Ok(Expression::Boolean(lhs == rhs).into()) - } - (Expression::Boolean(lhs), Expression::Boolean(rhs)) => { - Ok(Expression::Boolean(lhs == rhs).into()) - } - (Expression::String(lhs), Expression::String(rhs)) => { - Ok(Expression::Boolean(lhs == rhs).into()) - } - (Expression::Nil, Expression::Nil) => Ok(Expression::Boolean(true).into()), - _ => Err(Error::ExpectedNumber)?, - } - }), - ), - ( - "not", - Expression::NativeFunc(|args| { - arg_count!(1, args.len()); - - let expr: &Expression = args[0].borrow(); - if let Expression::Boolean(false) = expr { - return Ok(Expression::Boolean(true).into()); - } - - Ok(Expression::Boolean(false).into()) - }), - ), - ( - "<", - Expression::NativeFunc(|args| { - arg_count!(2, args.len()); - - let less_than = match (args[0].borrow(), args[1].borrow()) { - (Expression::Int(lhs), Expression::Int(rhs)) => lhs < rhs, - _ => Err(Error::ExpectedNumber)?, - }; - - Ok(Expression::Boolean(less_than).into()) - }), - ), - ( - ">", - Expression::NativeFunc(|args| { - arg_count!(2, args.len()); - - let greater_than = match (args[0].borrow(), args[1].borrow()) { - (Expression::Int(lhs), Expression::Int(rhs)) => lhs > rhs, - _ => Err(Error::ExpectedNumber)?, - }; - - Ok(Expression::Boolean(greater_than).into()) - }), - ), - ( - "<=", - Expression::NativeFunc(|args| { - arg_count!(2, args.len()); - - let less_than_equal = match (args[0].borrow(), args[1].borrow()) { - (Expression::Int(lhs), Expression::Int(rhs)) => lhs <= rhs, - _ => Err(Error::ExpectedNumber)?, - }; - - Ok(Expression::Boolean(less_than_equal).into()) - }), - ), - ( - ">=", - Expression::NativeFunc(|args| { - arg_count!(2, args.len()); - - let greater_than_equal = match (args[0].borrow(), args[1].borrow()) { - (Expression::Int(lhs), Expression::Int(rhs)) => lhs >= rhs, - _ => Err(Error::ExpectedNumber)?, - }; - - Ok(Expression::Boolean(greater_than_equal).into()) - }), - ), - // Branching - ( - "if", - Expression::NativeFunc(|args| { - arg_count!(3, args.len()); - - let (cond, consequence, alternative) = - (args[0].clone(), args[1].clone(), args[2].clone()); - - // If the value is anything other than false, then it is truthy - if let Expression::Boolean(false) = cond.borrow() { - return Ok(alternative); - }; - - Ok(consequence) - }), - ), - // Environment Manipulation - ( - "define!", - Expression::Special(|env, args| { - let mut args = args.into_iter(); - arg_count!(2, args.len()); - - let key = match args.next().unwrap() { - Node::Symbol(name) => name, - _ => Err(Error::ExpectedSymbol)?, - }; - - let val = eval_ast_node(env.clone(), args.next().unwrap())?; - - env.set(key, val.clone()); - - Ok(val) - }), - ), - ( - "let*", - Expression::Special(|env, args| { - arg_count!(2, args.len()); - - let mut args = args.into_iter(); - let new_env = match args.next().unwrap() { - Node::List(list) if list.len() == 2 => { - let mut list = list; - let sym = match list.remove(0) { - Node::Symbol(name) => name, - _ => Err(Error::ExpectedSymbol)?, - }; - - let val = eval_ast_node(env.clone(), list.remove(0))?; - Environment { - current: RefCell::new([(sym.clone(), val)].into()), - outer: Some(env.clone()), - } - } - Node::List(list) => Err(Error::MismatchedArgCount(2, list.len()))?, - _ => Err(Error::ExpectedList)?, - }; - - eval_ast_node(Rc::new(new_env), args.next().unwrap()) - }), - ), - ( - "fn*", - Expression::Special(|env, args| { - let mut args = args; - arg_count!(2, args.len()); - - let arg_list = args.remove(0); - let body = args.remove(0); - - let args: Vec = match arg_list { - Node::List(args) => args - .into_iter() - .map(|v| match v { - Node::Symbol(sym) => Ok(sym), - _ => Err(Error::ExpectedSymbol), - }) - .collect(), - _ => Err(Error::ExpectedList), - }?; - - Ok(Rc::new(Expression::Function { - params: args, - env: (*env).clone(), - body, - })) - }), - ), - // IO - ( - "display", - Expression::NativeFunc(|args| { - arg_count!(1, args.len()); - - print!("{}", args[0]); - - Ok(Expression::Void.into()) - }), - ), - ] - .into_iter() - .map(|(k, v)| (k.to_string(), Rc::new(v))); - - Environment { - current: RefCell::new(HashMap::from_iter(env)), - outer: None, - } - .into() -} diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs deleted file mode 100644 index 6744bda..0000000 --- a/src/evaluator/mod.rs +++ /dev/null @@ -1,268 +0,0 @@ -use std::{borrow::Borrow, collections::HashMap, rc::Rc}; - -use anyhow::Result; -use thiserror::Error; - -use crate::node::Node; - -mod env; -mod macros; - -pub use env::{core_environment, Environment}; - -#[derive(Debug)] -pub enum Expression { - // Values - Int(i64), - Boolean(bool), - Keyword(String), - String(String), - Nil, - Void, - - // Collections - Vector(Vec>), - HashMap(HashMap>), - - Function { - params: Vec, - env: Environment, - body: Node, - }, - NativeFunc(fn(args: Vec>) -> Result>), - Special(fn(env: Rc, args: Vec) -> Result>), -} - -impl std::fmt::Display for Expression { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Expression::Int(val) => write!(f, "{}", val), - Expression::Boolean(true) => write!(f, "true"), - Expression::Boolean(false) => write!(f, "false"), - Expression::Keyword(val) => write!(f, ":{}", val), - Expression::String(val) => write!(f, "{}", val), - Expression::Nil => write!(f, "()"), - Expression::Void => write!(f, ""), - - Expression::Vector(vec) => { - let s = vec - .iter() - .map(|elem| elem.to_string()) - .reduce(|lhs, rhs| format!("{lhs} {rhs}")) - .unwrap_or_default(); - - write!(f, "[{s}]") - } - Expression::HashMap(map) => { - let res = map - .iter() - .map(|(k, v)| format!("{k}: {v}")) - .reduce(|lhs, rhs| format!("{lhs}, {rhs}")) - .unwrap_or_default(); - - write!(f, "{{{res}}}") - } - - Expression::Function { - params: _, - env: _, - body: _, - } => { - write!(f, "#") - } - - Expression::NativeFunc(func) => write!(f, "{func:?}"), - Expression::Special(func) => write!(f, "{func:?}"), - } - } -} - -#[derive(Debug, Error)] -pub enum Error { - #[error("could not find symbol '{0}' in environment")] - NotInEnv(String), - #[error("expression does not have a valid operator")] - InvalidOperator, - #[error("expected symbol")] - ExpectedSymbol, - #[error("expected list")] - ExpectedList, - #[error("expected number")] - ExpectedNumber, - #[error("expected {0} arguments, got {1}")] - MismatchedArgCount(usize, usize), -} - -pub fn eval(env: Rc, ast: Vec) -> Result>> { - let mut res = Vec::new(); - - for node in ast { - res.push(eval_ast_node(env.clone(), node)?); - } - - Ok(res) -} - -fn eval_ast_node(env: Rc, ast_node: Node) -> Result> { - let expr = match ast_node { - Node::List(list) => { - // Empty lists are nil in scheme - if list.is_empty() { - return Ok(Expression::Nil.into()); - } - - // We always assume lists evaluate - let mut list = list.into_iter(); - let operator = eval_ast_node(env.clone(), list.next().ok_or(Error::InvalidOperator)?)?; - - match operator.borrow() { - Expression::NativeFunc(func) => { - let mut args = Vec::new(); - for node in list { - args.push(eval_ast_node(env.clone(), node)?); - } - - func(args)? - } - - Expression::Special(func) => { - let args = list.collect(); - func(env, args)? - } - - Expression::Function { - params, - env: inner_env, - body, - } => { - let args = list; - if args.len() != params.len() { - Err(Error::MismatchedArgCount(params.len(), args.len()))? - } - - let args = args - .map(|node| eval_ast_node(env.clone(), node)) - .collect::>>>()?; - - let records = params - .into_iter() - .map(|k| k.to_owned()) - .zip(args.into_iter()) - .collect(); - - let env = inner_env.wrap(records).into(); - - eval_ast_node(env, (*body).clone())? - } - _ => Err(Error::InvalidOperator)?, - } - } - Node::Symbol(sym) => env.get(&sym).ok_or(Error::NotInEnv(sym))?.to_owned(), - - Node::Int(i) => Expression::Int(i).into(), - Node::Keyword(val) => Expression::Keyword(val).into(), - Node::String(s) => Expression::String(s).into(), - - Node::Boolean(true) => Expression::Boolean(true).into(), - Node::Boolean(false) => Expression::Boolean(false).into(), - Node::Nil => Expression::Nil.into(), - - Node::Vector(vec) => todo!(), - Node::Map(map) => todo!(), - Node::Function { params, env, body } => todo!(), - Node::NativeFunc(_) => todo!(), - }; - - Ok(expr) -} - -#[cfg(test)] -mod test { - use crate::parser; - - use super::*; - use rstest::rstest; - - #[rstest] - // Raw values - #[case("1", "1")] - #[case("\"uwu\"", "uwu")] - #[case(":owo", ":owo")] - #[case("()", "()")] - #[case("nil", "()")] - // Arithmetic - #[case("(+ 1 2)", "3")] - #[case("(- 5 1)", "4")] - #[case("(* 8 9)", "72")] - #[case("(/ 86 2)", "43")] - // Native functions - #[case("(+ 1 2 (- 3 4))", "2")] - #[case("(vector 1 2 3)", "[1 2 3]")] - // Native functions defaults - #[case("(+)", "0")] - #[case("(-)", "0")] - #[case("(*)", "0")] - #[case("(/)", "0")] - #[case("(vector)", "[]")] - // Collections - #[case("[]", "[]")] - #[case("[1 2]", "[1 2]")] - #[case("[1 (+ 1 2)]", "[1 3]")] - #[case("{}", "{}")] - #[case("{:a \"uwu\"}", "{:a: uwu}")] - // Environment manipulation - #[case("(define! asdf (+ 2 2)) (+ asdf 2)", "4\n6")] - #[case("(let* (a 2) (+ a 2))", "4")] - // Branching - #[case("(if true true false)", "true")] - #[case("(if nil true false)", "true")] - #[case("(if false true false)", "false")] - #[case("(if 4 true false)", "true")] - #[case("(if \"blue\" true false)", "true")] - // Functions - #[case("((fn* (a) a) 3)", "3")] - #[case("((fn* (a) (+ a 2)) 1)", "3")] - #[case("((fn* (a b) (+ a b)) 1 2)", "3")] - // Boolean operations - #[case("(eq? 1 1)", "true")] - #[case("(eq? 1 2)", "false")] - #[case("(not false)", "true")] - #[case("(not true)", "false")] - #[case("(not nil)", "false")] - #[case("(not 1)", "false")] - // Ordering - #[case("(< 1 2)", "true")] - #[case("(< 2 1)", "false")] - #[case("(> 1 2)", "false")] - #[case("(> 2 1)", "true")] - #[case("(<= 1 2)", "true")] - #[case("(<= 1 1)", "true")] - #[case("(<= 2 1)", "false")] - #[case("(>= 2 1)", "true")] - #[case("(>= 1 1)", "true")] - #[case("(>= 1 2)", "false")] - fn test_evaluator(#[case] input: &str, #[case] expected: &str) { - let env = core_environment(); - let ast = parser::parse_str(input).unwrap(); - let res = eval(env, ast) - .unwrap() - .into_iter() - .map(|elem| elem.to_string()) - .reduce(|lhs, rhs| format!("{lhs}\n{rhs}")) - .unwrap(); - - assert_eq!(res, expected); - } - - #[rstest] - #[case("{:a}")] - #[case("(not-a-func :uwu)")] - #[case("{:a}")] - fn test_evaluator_fail(#[case] input: &str) { - let env = core_environment(); - let ast = parser::parse_str(input).unwrap(); - let res = eval(env, ast); - - assert!(res.is_err()) - } -} diff --git a/src/evaluator/macros.rs b/src/macros.rs similarity index 100% rename from src/evaluator/macros.rs rename to src/macros.rs diff --git a/src/main.rs b/src/main.rs index 8d71a83..c503571 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ use std::io::{self, Write}; +mod env; +mod error; mod evaluator; +mod macros; mod node; mod parser; fn main() { - let env = evaluator::core_environment(); + let env = env::Environment::new(); let mut input = String::new(); println!("Mute -- REPL"); @@ -23,11 +26,11 @@ fn main() { } let ast = parser::parse_str(&input).unwrap(); - let res = evaluator::eval(env.clone(), ast); + let res = evaluator::eval(&env, ast); match res { Ok(expressions) => expressions.into_iter().for_each(|expr| println!("{expr}")), - Err(err) => println!("{}", err.to_string()), + Err(err) => println!("{}", err), } input.clear(); diff --git a/src/node.rs b/src/node.rs index f79dbdc..7cfd97c 100644 --- a/src/node.rs +++ b/src/node.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use anyhow::Result; -use crate::evaluator::Environment; +use crate::env::Environment; #[derive(Debug, Clone)] pub enum Node { @@ -22,7 +22,8 @@ pub enum Node { env: Environment, body: Box, }, - NativeFunc(fn(env: Environment, args: Vec) -> Result), + NativeFunc(fn(env: &Environment, args: Vec) -> Result), + Special(fn(env: &Environment, args: Vec) -> Result), } impl std::fmt::Display for Node { @@ -74,6 +75,7 @@ impl std::fmt::Display for Node { } Node::NativeFunc(func) => write!(f, "{func:?}"), + Node::Special(func) => write!(f, "{func:?}"), } } }