diff --git a/mute-interpreter/src/env/core.rs b/mute-interpreter/src/env/core.rs index f0a0d66..91cd778 100644 --- a/mute-interpreter/src/env/core.rs +++ b/mute-interpreter/src/env/core.rs @@ -1,96 +1,16 @@ use std::borrow::Borrow; use std::collections::HashMap; -use crate::evaluator::{eval, eval_node}; -use crate::macros::arg_count; -use crate::{Error, Node}; +use super::{NativeFunc, Value}; +use crate::Node; -pub(super) fn core() -> HashMap { +pub(super) fn core() -> HashMap { [ - // Meta - ( - "eval", - Node::NativeFunc(|env, args| { - arg_count!(1, args.len()); - let ast = args.into_iter().next().unwrap(); - eval_node(env, ast) - }), - ), - ( - "apply", - Node::NativeFunc(|env, args| { - arg_count!(2, args.len()); - - let mut args = args.into_iter(); - let operator = args.next().unwrap(); - let args = match args.next().unwrap() { - Node::List(list) => list, - _ => Err(Error::ExpectedList)?, - }; - - let mut list = vec![operator]; - list.extend(args); - - eval_node(env, Node::List(list)) - }), - ), - ( - "quote", - Node::Special(|_env, args| { - arg_count!(1, args.len()); - let val = args.into_iter().next().unwrap(); - Ok(val) - }), - ), - ( - "quasiquote", - Node::Special(|env, args| { - arg_count!(1, args.len()); - - let node = args.into_iter().next().unwrap(); - - // If we have a list as our object, then we need to check for any unquote expressions. - // To do this, we iterate over each value in the list. If we detect that one - // of those values is a list, then we check if the first value in the list is an - // unquote symbol. If it is, then we evaluate the unqote list - match node { - Node::List(list) => { - let inner: Vec = list - .into_iter() - .map(|node| match node { - Node::List(ref list) => match list.first() { - Some(Node::Symbol(symbol)) if symbol == "unquote" => { - eval_node(env, node) - } - Some(_) => Ok(node), - None => Ok(Node::Nil), - }, - _ => Ok(node), - }) - .collect::, _>>()?; - - Ok(Node::List(inner)) - } - - node => Ok(node), - } - }), - ), - ( - "unquote", - Node::Special(|env, args| { - arg_count!(1, args.len()); - - let node = args.into_iter().next().unwrap(); - eval_node(env, node) - }), - ), // Arithmetic operations ( "+", - Node::NativeFunc(|_env, args| { - let res = args - .into_iter() + NativeFunc(|args| { + args.into_iter() .reduce(|lhs, rhs| match (lhs, rhs) { (Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs + rhs), (Node::Float(lhs), Node::Float(rhs)) => Node::Float(lhs + rhs), @@ -98,15 +18,13 @@ pub(super) fn core() -> HashMap { (Node::Float(lhs), Node::Int(rhs)) => Node::Float(lhs + rhs as f64), _ => todo!(), }) - .unwrap_or(Node::Int(0)); - Ok(res) + .unwrap_or(Node::Int(0)) }), ), ( "-", - Node::NativeFunc(|_env, args| { - let res = args - .into_iter() + NativeFunc(|args| { + args.into_iter() .reduce(|lhs, rhs| match (lhs, rhs) { (Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs - rhs), (Node::Float(lhs), Node::Float(rhs)) => Node::Float(lhs - rhs), @@ -114,16 +32,13 @@ pub(super) fn core() -> HashMap { (Node::Int(lhs), Node::Float(rhs)) => Node::Float(lhs as f64 - rhs), _ => todo!(), }) - .unwrap_or(Node::Int(0)); - - Ok(res) + .unwrap_or(Node::Int(0)) }), ), ( "*", - Node::NativeFunc(|_env, args| { - let res = args - .into_iter() + NativeFunc(|args| { + args.into_iter() .reduce(|lhs, rhs| match (lhs, rhs) { (Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs * rhs), (Node::Float(lhs), Node::Float(rhs)) => Node::Float(lhs * rhs), @@ -131,16 +46,13 @@ pub(super) fn core() -> HashMap { (Node::Int(lhs), Node::Float(rhs)) => Node::Float(lhs as f64 * rhs), _ => todo!(), }) - .unwrap_or(Node::Int(0)); - - Ok(res) + .unwrap_or(Node::Int(0)) }), ), ( "/", - Node::NativeFunc(|_env, args| { - let res = args - .into_iter() + NativeFunc(|args| { + args.into_iter() .reduce(|lhs, rhs| match (lhs, rhs) { (Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs / rhs), (Node::Float(lhs), Node::Float(rhs)) => Node::Float(lhs / rhs), @@ -148,45 +60,43 @@ pub(super) fn core() -> HashMap { (Node::Int(lhs), Node::Float(rhs)) => Node::Float(lhs as f64 / rhs), _ => todo!(), }) - .unwrap_or(Node::Int(0)); - - Ok(res) + .unwrap_or(Node::Int(0)) }), ), // Collections - ("list", Node::NativeFunc(|_env, args| Ok(Node::List(args)))), + ("list", NativeFunc(|args| Node::List(args))), ( "list?", - Node::NativeFunc(|_env, args| { - arg_count!(1, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // arg_count!(1, args.len()); if let Node::List(_list) = args[0].borrow() { - return Ok(Node::Boolean(true)); + return Node::Boolean(true); } - Ok(Node::Boolean(false)) + Node::Boolean(false) }), ), - ( - "vector", - Node::NativeFunc(|_env, args| Ok(Node::Vector(args))), - ), + ("vector", NativeFunc(|args| Node::Vector(args))), ( "vector?", - Node::NativeFunc(|_env, args| { - arg_count!(1, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // arg_count!(1, args.len()); if let Node::Vector(_vec) = args[0].borrow() { - return Ok(Node::Boolean(true)); + return Node::Boolean(true); } - Ok(Node::Boolean(false)) + Node::Boolean(false) }), ), ( "hashmap", - Node::NativeFunc(|_env, args| { - arg_count!(modulo: 2, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // arg_count!(modulo: 2, args.len()); let mut index = -1; let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().partition(|_| { @@ -202,82 +112,88 @@ pub(super) fn core() -> HashMap { .zip(values) .collect(); - Ok(Node::Map(res)) + Node::Map(res) }), ), ( "hashmap?", - Node::NativeFunc(|_env, args| { - arg_count!(1, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // arg_count!(1, args.len()); if let Node::Map(_map) = args[0].borrow() { - return Ok(Node::Boolean(true)); + return Node::Boolean(true); } - Ok(Node::Boolean(false)) + Node::Boolean(false) }), ), ( "empty?", - Node::NativeFunc(|_env, args| { - arg_count!(1, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // arg_count!(1, args.len()); match args[0].borrow() { - Node::List(list) => Ok(Node::Boolean(list.is_empty())), - Node::Vector(vec) => Ok(Node::Boolean(vec.is_empty())), - Node::Map(map) => Ok(Node::Boolean(map.is_empty())), - _ => Err(Error::ExpectedCollection)?, + Node::List(list) => Node::Boolean(list.is_empty()), + Node::Vector(vec) => Node::Boolean(vec.is_empty()), + Node::Map(map) => Node::Boolean(map.is_empty()), + _ => todo!(), } }), ), ( "count", - Node::NativeFunc(|_env, args| { - arg_count!(1, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // arg_count!(1, args.len()); match args[0].borrow() { - Node::List(list) => Ok(Node::Int(list.len() as i64)), - Node::Vector(vec) => Ok(Node::Int(vec.len() as i64)), - Node::Map(map) => Ok(Node::Int(map.len() as i64)), - _ => Err(Error::ExpectedCollection)?, + Node::List(list) => Node::Int(list.len() as i64), + Node::Vector(vec) => Node::Int(vec.len() as i64), + Node::Map(map) => Node::Int(map.len() as i64), + _ => todo!(), } }), ), // Ordering ( "eq?", - Node::NativeFunc(|_env, args| { - arg_count!(2, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // 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)?, + (Node::Int(lhs), Node::Int(rhs)) => Node::Boolean(lhs == rhs), + (Node::Boolean(lhs), Node::Boolean(rhs)) => Node::Boolean(lhs == rhs), + (Node::String(lhs), Node::String(rhs)) => Node::Boolean(lhs == rhs), + (Node::Nil, Node::Nil) => Node::Boolean(true), + _ => todo!(), } }), ), ( "not", - Node::NativeFunc(|_env, args| { - arg_count!(1, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // arg_count!(1, args.len()); let expr = args[0].borrow(); if let Node::Boolean(false) = expr { - return Ok(Node::Boolean(true)); + return Node::Boolean(true); } - Ok(Node::Boolean(false)) + Node::Boolean(false) }), ), ( "<", - Node::NativeFunc(|_env, args| { - arg_count!(2, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // arg_count!(2, args.len()); let mut args = args.into_iter(); let lhs = args.next().unwrap(); @@ -288,16 +204,17 @@ pub(super) fn core() -> HashMap { (Node::Float(lhs), Node::Float(rhs)) => lhs < rhs, (Node::Float(lhs), Node::Int(rhs)) => lhs < rhs as f64, (Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) < rhs, - _ => Err(Error::ExpectedNumber)?, + _ => todo!(), }; - Ok(Node::Boolean(less_than)) + Node::Boolean(less_than) }), ), ( ">", - Node::NativeFunc(|_env, args| { - arg_count!(2, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // arg_count!(2, args.len()); let mut args = args.into_iter(); let lhs = args.next().unwrap(); @@ -308,16 +225,17 @@ pub(super) fn core() -> HashMap { (Node::Float(lhs), Node::Float(rhs)) => lhs > rhs, (Node::Float(lhs), Node::Int(rhs)) => lhs > rhs as f64, (Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) > rhs, - _ => Err(Error::ExpectedNumber)?, + _ => todo!(), }; - Ok(Node::Boolean(greater_than)) + Node::Boolean(greater_than) }), ), ( "<=", - Node::NativeFunc(|_env, args| { - arg_count!(2, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // arg_count!(2, args.len()); let mut args = args.into_iter(); let lhs = args.next().unwrap(); @@ -328,16 +246,17 @@ pub(super) fn core() -> HashMap { (Node::Float(lhs), Node::Float(rhs)) => lhs <= rhs, (Node::Float(lhs), Node::Int(rhs)) => lhs <= rhs as f64, (Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) <= rhs, - _ => Err(Error::ExpectedNumber)?, + _ => todo!(), }; - Ok(Node::Boolean(less_than_equal)) + Node::Boolean(less_than_equal) }), ), ( ">=", - Node::NativeFunc(|_env, args| { - arg_count!(2, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // arg_count!(2, args.len()); let mut args = args.into_iter(); let lhs = args.next().unwrap(); @@ -348,170 +267,25 @@ pub(super) fn core() -> HashMap { (Node::Float(lhs), Node::Float(rhs)) => lhs >= rhs, (Node::Float(lhs), Node::Int(rhs)) => lhs >= rhs as f64, (Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) >= rhs, - _ => Err(Error::ExpectedNumber)?, + _ => todo!(), }; - Ok(Node::Boolean(greater_than_equal)) - }), - ), - // Branching - ( - "if", - Node::Special(|env, args| { - let mut args = args; - if args.len() == 2 { - args.push(Node::Nil) - } - - arg_count!(3, args.len()); - let mut args = args.into_iter(); - - let cond = args.next().unwrap(); - let consequence = args.next().unwrap(); - let alternative = args.next().unwrap(); - - let cond = eval_node(env, cond)?; - - // If the value is anything other than false, then it is truthy - if let Node::Boolean(false) = cond { - let res = eval_node(env, alternative)?; - return Ok(res); - }; - - let res = eval_node(env, consequence)?; - Ok(res) - }), - ), - // 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(Node::Void) - }), - ), - ( - "fn!", - Node::Special(|env, args| { - arg_count!(3, args.len()); - - let mut args = args.into_iter(); - - let name = match args.next().unwrap() { - Node::Symbol(name) => name, - _ => Err(Error::ExpectedSymbol)?, - }; - - let arg_list = args.next().unwrap(); - let body = args.next().unwrap(); - - 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), - }?; - - let func = Node::Function { - params: args, - env: env.clone(), - body: Box::new(body), - }; - - env.set(name.clone(), func.clone()); - Ok(Node::Void) - }), - ), - ( - "let*", - Node::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_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), - }) + Node::Boolean(greater_than_equal) }), ), // Strings ( "str", - Node::NativeFunc(|_env, args| { - arg_count!(1, args.len()); + NativeFunc(|args| { + // TODO: Reimplement this + // arg_count!(1, args.len()); let val = args[0].borrow(); - Ok(Node::String(val.to_string())) - }), - ), - ( - "do", - Node::Special(|env, args| { - let res = eval(env, args)?; - - if let Some(res) = res.last() { - return Ok(res.to_owned()); - } - - Ok(Node::Nil) + Node::String(val.to_string()) }), ), ] .into_iter() - .map(|(k, v)| (k.to_string(), v)) + .map(|(k, v)| (k.to_string(), Value::NativeFunc(v))) .collect() } diff --git a/mute-interpreter/src/env/io.rs b/mute-interpreter/src/env/io.rs index 5768df7..f668637 100644 --- a/mute-interpreter/src/env/io.rs +++ b/mute-interpreter/src/env/io.rs @@ -1,50 +1,61 @@ use std::borrow::Borrow; use std::collections::HashMap; -use crate::macros::arg_count; -use crate::{Error, Node}; +use super::{NativeFunc, Value}; +use crate::Node; -pub(super) fn io() -> HashMap { +pub(super) fn io() -> HashMap { [ // Stdout ( "print", - Node::NativeFunc(|_env, args| { - arg_count!(1, args.len()); + NativeFunc(|args| { + print!( + "{}", + args.into_iter() + .map(|node| node.to_string()) + .reduce(|lhs, rhs| format!("{lhs} {rhs}")) + .unwrap_or_default() + ); - print!("{}", args[0]); - - Ok(Node::Void) + Node::Void }), ), ( "println", - Node::NativeFunc(|_env, args| { - arg_count!(1, args.len()); + NativeFunc(|args| { + println!( + "{}", + args.into_iter() + .map(|node| node.to_string()) + .reduce(|lhs, rhs| format!("{lhs} {rhs}")) + .unwrap_or_default() + ); - println!("{}", args[0]); - - Ok(Node::Void) + Node::Void }), ), // File IO ( "read-file", - Node::NativeFunc(|_env, args| { - arg_count!(1, args.len()); + NativeFunc(|args| { + // TODO: implement this + // arg_count!(1, args.len()); let val = args[0].borrow(); if let Node::String(path) = val { - let contents = std::fs::read_to_string(path) - .map_err(|err| Error::SystemError(err.to_string()))?; - return Ok(Node::String(contents)); + let contents = std::fs::read_to_string(path).unwrap(); + // FIXME Lmao, we need error handling + // .map_err(|err| Error::SystemError(err.to_string()))?; + return Node::String(contents); } - Err(Error::ExpectedString)? + // FIXME What if they don't pass a string??? + Node::Void }), ), ] .into_iter() - .map(|(k, v)| (k.to_string(), v)) + .map(|(k, v)| (k.to_string(), Value::NativeFunc(v))) .collect() } diff --git a/mute-interpreter/src/env/mod.rs b/mute-interpreter/src/env/mod.rs index 6c27822..231796f 100644 --- a/mute-interpreter/src/env/mod.rs +++ b/mute-interpreter/src/env/mod.rs @@ -7,17 +7,32 @@ use crate::Node; mod core; mod io; +#[derive(Debug, Clone)] +pub struct NativeFunc(fn(args: Vec) -> Node); + +impl NativeFunc { + pub fn call(&self, args: Vec) -> Node { + (self.0)(args) + } +} + +#[derive(Debug, Clone)] +pub enum Value { + Node(Node), + NativeFunc(NativeFunc), +} + #[derive(Debug, Clone)] pub struct Environment(Rc); #[derive(Debug, Clone)] pub struct RawEnvironment { - current: RefCell>, + current: RefCell>, outer: Option, } impl Environment { - pub fn get(&self, ident: &str) -> Option { + pub fn get(&self, ident: &str) -> Option { if let Some(val) = self.0.current.borrow().get(ident) { return Some(val.clone()); } @@ -27,15 +42,20 @@ impl Environment { pub fn wrap(&self, records: Vec<(String, Node)>) -> Environment { let env = RawEnvironment { - current: RefCell::new(records.into_iter().collect()), + current: RefCell::new( + records + .into_iter() + .map(|(k, v)| (k, Value::Node(v))) + .collect(), + ), outer: Some(self.clone()), }; Environment(Rc::new(env)) } - fn set(&self, ident: String, val: Node) { - self.0.current.borrow_mut().insert(ident, val); + pub fn set(&self, ident: String, val: Node) { + self.0.current.borrow_mut().insert(ident, Value::Node(val)); } } diff --git a/mute-interpreter/src/error.rs b/mute-interpreter/src/error.rs index 31d47f7..a3bf81f 100644 --- a/mute-interpreter/src/error.rs +++ b/mute-interpreter/src/error.rs @@ -18,6 +18,9 @@ pub enum Error { ExpectedNumber, #[error("expected string")] ExpectedString, + #[error("expected boolean")] + ExpectedBoolean, + #[error("expected {0} arguments, got {1}")] MismatchedArgCount(usize, usize), #[error("system error {0}")] diff --git a/mute-interpreter/src/evaluator.rs b/mute-interpreter/src/evaluator.rs index ada604a..e4f9ecf 100644 --- a/mute-interpreter/src/evaluator.rs +++ b/mute-interpreter/src/evaluator.rs @@ -1,11 +1,13 @@ use std::borrow::Borrow; -use crate::{Environment, Error, Node, Result}; +use crate::{Environment, Error, Result}; +use mute_parser::Node; pub fn eval(env: &Environment, ast: Vec) -> Result> { let mut exprs = Vec::new(); for node in ast { + dbg!(&node); let res = eval_node(env, node)?; if let Node::Void = res { continue; @@ -30,45 +32,130 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result { 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, - } => { + Node::Function { parameters, body } => { let args = list; - if args.len() != params.len() { - Err(Error::MismatchedArgCount(params.len(), args.len()))? + if args.len() != parameters.len() { + Err(Error::MismatchedArgCount(parameters.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); + let records = parameters.iter().map(|k| k.to_owned()).zip(args).collect(); + let env = env.wrap(records); - eval_node(&env, *(*body).clone())? + let res = eval(&env, (*body).clone())?; + res.last() + .expect("all functions return something") + .to_owned() } + + // HACK: This feels sooooooo wrong + Node::Symbol(sym) => { + let operator = env.get(&sym).ok_or_else(|| Error::NotInEnv(sym.clone()))?; + match operator { + crate::env::Value::NativeFunc(func) => { + let args = list + .map(|node| eval_node(env, node)) + .collect::>>()?; + + func.call(args) + } + crate::env::Value::Node(node) => node, + } + } + + // TODO: Native functions??? _ => Err(Error::InvalidOperator)?, } } - Node::Symbol(sym) => env.get(&sym).ok_or(Error::NotInEnv(sym))?.to_owned(), + Node::Define { args } => { + args.into_iter() + .map(|(k, v)| Ok((k.to_owned(), eval_node(env, v)?))) + .collect::>>()? + .into_iter() + .for_each(|(k, v)| env.set(k, v)); - val => val, + Node::Void + } + Node::Let { args, body } => { + let args = args + .into_iter() + .map(|(k, v)| Ok((k.to_owned(), eval_node(env, v)?))) + .collect::>>()?; + + let env = env.wrap(args); + eval(&env, body)?.last().unwrap().to_owned() + } + + Node::If { + cond, + consequence, + alternative, + } => { + let cond = eval_node(env, *cond)?; + + match cond { + Node::Boolean(true) => eval_node(env, *consequence)?, + Node::Boolean(false) => eval_node(env, *alternative)?, + _ => Err(Error::ExpectedBoolean)?, + } + } + + Node::Quote(node) => *node, + Node::Quasiquote(node) => { + fn unquote(env: &Environment, node: Node) -> Result { + match node { + Node::List(list) => { + let list = list + .into_iter() + .map(|node| unquote(env, node)) + .collect::>>()?; + Ok(Node::List(list)) + } + Node::Unquote(_) => eval_node(env, node), + node => Ok(node), + } + } + + unquote(env, *node)? + } + Node::Unquote(node) => eval_node(env, *node)?, + + Node::Do { body } => eval(env, body)?.last().unwrap().to_owned(), + Node::Eval { body } => eval_node(env, eval_node(env, *body)?)?, + Node::Apply { func, args } => { + let mut list = vec![*func]; + match eval_node(env, *args)? { + Node::List(args) => list.extend(args), + _ => Err(Error::ExpectedList)?, + } + + eval_node(env, Node::List(list))? + } + + Node::Symbol(sym) => match env + .get(&sym) + .ok_or_else(|| Error::NotInEnv(sym.clone()))? + .to_owned() + { + crate::env::Value::Node(node) => node, + // HACK: This feels so wrong + crate::env::Value::NativeFunc(_) => Node::Symbol(sym), + }, + + Node::Keyword(_) + | Node::Int(_) + | Node::Float(_) + | Node::String(_) + | Node::Boolean(_) + | Node::Nil + | Node::Void + | Node::Vector(_) + | Node::Map(_) + | Node::Function { .. } => ast_node, }; Ok(expr) @@ -148,23 +235,20 @@ mod test { #[case("(count {})", "0")] #[case("(count {:a 1})", "1")] // Environment manipulation - #[case("(define! asdf (+ 2 2)) (+ asdf 2)", "6")] - #[case("(fn! add (a b) (+ a b)) (add 1 2)", "3")] - #[case("(let* (a 2) (+ a 2))", "4")] + #[case("(define (asdf (+ 2 2))) (+ asdf 2)", "6")] + #[case("(define (add (fn* (a b) (+ a b)))) (add 1 2)", "3")] + #[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")] #[case("(if true 3)", "3")] #[case("(if false 3)", "()")] // Functions #[case( - "(fn! factorial (n) (if + "(define (factorial (fn* (n) (if (<= n 1) 1 - (* n (factorial (- n 1))))) + (* n (factorial (- n 1))))))) (factorial 5)", "120" )] @@ -210,16 +294,4 @@ mod test { assert_eq!(res, expected); } - - #[rstest] - #[case("{:a}")] - #[case("(not-a-func :uwu)")] - #[case("{:a}")] - fn test_evaluator_fail(#[case] input: &str) { - let env = Environment::default(); - let ast = parse_str(input).unwrap(); - let res = crate::eval(&env, ast); - - assert!(res.is_err()) - } } diff --git a/mute-interpreter/src/lib.rs b/mute-interpreter/src/lib.rs index c82ee0e..caa0a23 100644 --- a/mute-interpreter/src/lib.rs +++ b/mute-interpreter/src/lib.rs @@ -3,13 +3,14 @@ mod env; mod error; mod evaluator; mod macros; -mod node; pub use env::Environment; pub use error::{Error, Result}; -pub use node::Node; -pub fn eval(env: &Environment, ast: Vec) -> Result> { +// Crate Imports +use mute_parser::Node; + +pub fn eval(env: &Environment, ast: Vec) -> Result> { let ast = ast.into_iter().map(Into::into).collect(); evaluator::eval(env, ast) } diff --git a/mute-interpreter/src/node.rs b/mute-interpreter/src/node.rs deleted file mode 100644 index 8cc34b6..0000000 --- a/mute-interpreter/src/node.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::collections::HashMap; - -use crate::{Environment, Result}; - -#[derive(Debug, Clone)] -pub enum Node { - List(Vec), - - Symbol(String), - Keyword(String), - Int(i64), - Float(f64), - String(String), - Boolean(bool), - Nil, - - Vector(Vec), - Map(HashMap), - Function { - params: Vec, - env: Environment, - body: Box, - }, - NativeFunc(fn(env: &Environment, args: Vec) -> Result), - Special(fn(env: &Environment, args: Vec) -> Result), - - Void, -} - -impl From for Node { - fn from(value: mute_parser::Node) -> Self { - match value { - mute_parser::Node::List(list) => Node::List(list.into_iter().map(Into::into).collect()), - mute_parser::Node::Symbol(symbol) => Node::Symbol(symbol), - mute_parser::Node::Keyword(keyword) => Node::Keyword(keyword), - mute_parser::Node::Int(int) => Node::Int(int), - mute_parser::Node::Float(float) => Node::Float(float), - mute_parser::Node::String(string) => Node::String(string), - mute_parser::Node::Boolean(boolean) => Node::Boolean(boolean), - mute_parser::Node::Nil => Node::Nil, - mute_parser::Node::Vector(vector) => { - Node::Vector(vector.into_iter().map(Into::into).collect()) - } - mute_parser::Node::Map(map) => { - Node::Map(map.into_iter().map(|(k, v)| (k, v.into())).collect()) - } - } - } -} - -impl std::fmt::Display for Node { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Node::List(list) => { - let s = list - .iter() - .map(|elem| elem.to_string()) - .reduce(|lhs, rhs| format!("{lhs} {rhs}")) - .unwrap_or_default(); - - write!(f, "({s})") - } - - Node::Int(val) => write!(f, "{}", val), - Node::Float(val) => write!(f, "{}", val), - Node::Boolean(true) => write!(f, "true"), - Node::Boolean(false) => write!(f, "false"), - Node::Symbol(val) => write!(f, "{}", val), - Node::Keyword(val) => write!(f, ":{}", val), - Node::String(val) => write!(f, "{}", val), - Node::Nil => write!(f, "()"), - - Node::Vector(vec) => { - let s = vec - .iter() - .map(|elem| elem.to_string()) - .reduce(|lhs, rhs| format!("{lhs} {rhs}")) - .unwrap_or_default(); - - write!(f, "[{s}]") - } - Node::Map(map) => { - let res = map - .iter() - .map(|(k, v)| format!("{k}: {v}")) - .reduce(|lhs, rhs| format!("{lhs}, {rhs}")) - .unwrap_or_default(); - - write!(f, "{{{res}}}") - } - - Node::Function { - params: _, - env: _, - body: _, - } => { - write!(f, "#") - } - - Node::NativeFunc(func) => write!(f, "{func:?}"), - Node::Special(func) => write!(f, "{func:?}"), - - Node::Void => write!(f, ""), - } - } -} diff --git a/mute-parser/src/error.rs b/mute-parser/src/error.rs index 450e0d1..f79b01a 100644 --- a/mute-parser/src/error.rs +++ b/mute-parser/src/error.rs @@ -19,10 +19,43 @@ pub enum Error { #[error("unexpected character '{0}'")] UnexpectedChar(char), + #[error("unexpected end of file")] + UnexpectedEof, #[error("string is never closed")] UnterminatedString, #[error("the quote does not have a valid follower node")] NoFollowerNode, + #[error("specials must be the first node in a list")] + SpecialNotOperator, + + #[error("expected list, got {0}")] + ExpectedList(String), + #[error("expected symbol, got {0}")] + ExpectedSymbol(String), + #[error("expected {0} values, got {1}")] + MismatchedValueCount(usize, usize), + + // TODO: Consolidate these errors + #[error("cannot have empty define statements")] + EmptyDefine, + #[error("missing function args")] + EmptyFunctionArgs, + #[error("missing function body")] + EmptyFunctionBody, + #[error("missing let args")] + EmptyLetArgs, + #[error("missing let body")] + EmptyLetBody, + #[error("missing apply operator")] + EmptyApplyOperator, + #[error("missing apply body")] + EmptyApplyBody, + #[error("missing if condition")] + EmptyIfCondition, + #[error("missing if consequene")] + EmptyIfConsequence, + #[error("missing do body")] + EmptyDo, #[error("please file a bug report")] Unreachable, diff --git a/mute-parser/src/lexer.rs b/mute-parser/src/lexer.rs index 5de445f..023087c 100644 --- a/mute-parser/src/lexer.rs +++ b/mute-parser/src/lexer.rs @@ -13,13 +13,6 @@ pub enum Token { LeftBrace, RightBrace, - WeirdSign, - Apostrophe, - Grave, - Tilde, - Carot, - AtSign, - // Math Operators Plus, Minus, @@ -40,6 +33,19 @@ pub enum Token { True, False, Nil, + + // Specials + Function, + Define, + Let, + Eval, + Apply, + If, + Do, + + Quote, + Quasiquote, + Unquote, } pub fn read(input: &str) -> Result, Error> { @@ -60,13 +66,6 @@ fn next_token(input: &mut Peekable) -> Result, Error> { }; let tok = match tok { - // Weird sign - '~' if input.peek().is_some_and(|c| c == &'@') => { - // Munch the @ - input.next(); - Token::WeirdSign - } - // Negative numbers '-' if input.peek().is_some_and(|c| c.is_ascii_digit()) => read_int(input, '-'), @@ -92,11 +91,9 @@ fn next_token(input: &mut Peekable) -> Result, Error> { '{' => Token::LeftBrace, '}' => Token::RightBrace, - '\'' => Token::Apostrophe, - '`' => Token::Grave, - '~' => Token::Tilde, - '^' => Token::Carot, - '@' => Token::AtSign, + '\'' => Token::Quote, + '`' => Token::Quasiquote, + ',' => Token::Unquote, '+' => Token::Plus, '-' => Token::Minus, @@ -211,77 +208,16 @@ fn read_ident(input: &mut Peekable, first: char) -> Token { "true" => Token::True, "false" => Token::False, "nil" => Token::Nil, + "fn*" => Token::Function, + "define" => Token::Define, + "let*" => Token::Let, + "eval" => Token::Eval, + "apply" => Token::Apply, + "quote" => Token::Quote, + "quasiquote" => Token::Quasiquote, + "unquote" => Token::Unquote, + "if" => Token::If, + "do" => Token::Do, ident => Token::Ident(ident.to_owned()), } } - -#[cfg(test)] -mod test { - use super::*; - use rstest::rstest; - - #[rstest] - #[case("()[]{}", vec![Token::LeftParen, Token::RightParen, Token::LeftBracket, Token::RightBracket, Token::LeftBrace, Token::RightBrace])] - #[case(" ' ` ^ ~@ ~ @", vec![Token::Apostrophe, Token::Grave, Token::Carot, Token::WeirdSign, Token::Tilde, Token::AtSign])] - #[case("(+ 1 2)", vec![Token::LeftParen, Token::Plus, Token::Int(1), Token::Int(2), Token::RightParen])] - #[case("(- 1 2)", vec![Token::LeftParen, Token::Minus, Token::Int(1), Token::Int(2), Token::RightParen])] - #[case("(* 1 2)", vec![Token::LeftParen, Token::Asterisk, Token::Int(1), Token::Int(2), Token::RightParen])] - #[case("(/ 1 2)", vec![Token::LeftParen, Token::Slash, Token::Int(1), Token::Int(2), Token::RightParen])] - #[case("(- -2 1)", vec![Token::LeftParen, Token::Minus, Token::Int(-2), Token::Int(1), Token::RightParen])] - #[case("(\"string and stuff\")", vec![Token::LeftParen, Token::String("string and stuff".into()), Token::RightParen])] - #[case("define!", vec![Token::Ident("define!".into())])] - #[case("let*", vec![Token::Ident("let*".into())])] - #[case("is-int?", vec![Token::Ident("is-int?".into())])] - #[case( - "(func a b)", - vec![ - Token::LeftParen, - Token::Ident("func".into()), - Token::Ident("a".into()), - Token::Ident("b".into()), - Token::RightParen - ] - )] - #[case( - "(+ 1 (- 2 1))", - vec![ - Token::LeftParen, - Token::Plus, - Token::Int(1), - Token::LeftParen, - Token::Minus, - Token::Int(2), - Token::Int(1), - Token::RightParen, - Token::RightParen - ] - )] - #[case( - "(fn a ;; This comment is useless - (+ 1 2))", - vec![ - Token::LeftParen, - Token::Ident("fn".into()), - Token::Ident("a".into()), - Token::LeftParen, - Token::Plus, - Token::Int(1), - Token::Int(2), - Token::RightParen, - Token::RightParen - - ] - )] - fn test_lexer(#[case] input: &str, #[case] expected: Vec) { - let res = read(input).unwrap(); - assert_eq!(res, expected); - } - - #[rstest] - // Unbalanced string - #[case("(\"asdf)")] - fn test_lexer_errors(#[case] input: &str) { - let res = read(input); - assert!(res.is_err()); - } -} diff --git a/mute-parser/src/lib.rs b/mute-parser/src/lib.rs index 8f411fa..ab5a5f2 100644 --- a/mute-parser/src/lib.rs +++ b/mute-parser/src/lib.rs @@ -31,7 +31,35 @@ fn next_statement(tokens: &mut TokenIter) -> Result> { }; let node = match tok { - Token::LeftParen => read_list(tokens, Token::RightParen)?, + Token::LeftParen => match tokens.peek().ok_or(Error::UnclosedParenthesis)? { + Token::Function => read_function(tokens)?, + Token::Define => read_define(tokens)?, + Token::Let => read_let(tokens)?, + Token::Eval => read_eval(tokens)?, + Token::Apply => read_apply(tokens)?, + Token::If => read_if(tokens)?, + Token::Do => read_do(tokens)?, + Token::Quote => { + tokens.next(); // Munch ( + let quote = read_quote(tokens, "quote")?; + tokens.next(); // Munch ) + quote + } + Token::Quasiquote => { + tokens.next(); // Munch ( + let quote = read_quote(tokens, "quasiquote")?; + tokens.next(); // Munch ) + quote + } + Token::Unquote => { + tokens.next(); // Munch ( + let quote = read_quote(tokens, "unquote")?; + tokens.next(); // Munch ) + quote + } + _ => read_list(tokens, Token::RightParen)?, + }, + Token::RightParen => Err(Error::UnopenedParenthesis)?, Token::LeftBracket => read_list(tokens, Token::RightBracket)?, @@ -40,16 +68,6 @@ fn next_statement(tokens: &mut TokenIter) -> Result> { Token::LeftBrace => read_list(tokens, Token::RightBrace)?, Token::RightBrace => Err(Error::UnopenedBrace)?, - Token::WeirdSign => read_quote(tokens, "splice-unquote")?, - Token::Apostrophe => read_quote(tokens, "quote")?, - Token::Grave => read_quote(tokens, "quasiquote")?, - Token::Tilde => read_quote(tokens, "unquote")?, - - // TODO: meta - Token::Carot => todo!(), - // TODO: deref - Token::AtSign => todo!(), - Token::Plus => Node::Symbol("+".into()), Token::Minus => Node::Symbol("-".into()), Token::Asterisk => Node::Symbol("*".into()), @@ -68,6 +86,18 @@ fn next_statement(tokens: &mut TokenIter) -> Result> { Token::True => Node::Boolean(true), Token::False => Node::Boolean(false), Token::Nil => Node::Nil, + + Token::Quote => read_quote(tokens, "quote")?, + Token::Quasiquote => read_quote(tokens, "quasiquote")?, + Token::Unquote => read_quote(tokens, "unquote")?, + + Token::Function + | Token::Define + | Token::Let + | Token::Eval + | Token::Apply + | Token::If + | Token::Do => Err(Error::SpecialNotOperator)?, }; Ok(Some(node)) @@ -108,12 +138,241 @@ fn read_list(tokens: &mut TokenIter, closer: Token) -> Result { fn read_quote(tokens: &mut TokenIter, quote_type: &str) -> Result { let follower_node = match next_statement(tokens)? { - Some(node) => node, + Some(node) => Box::new(node), None => Err(Error::NoFollowerNode)?, }; - Ok(Node::List(vec![ - Node::Symbol(quote_type.into()), - follower_node, - ])) + let node = match quote_type { + "quote" => Node::Quote(follower_node), + "quasiquote" => Node::Quasiquote(follower_node), + "unquote" => Node::Unquote(follower_node), + _ => Err(Error::Unreachable)?, + }; + + Ok(node) +} + +fn read_define(tokens: &mut TokenIter) -> Result { + tokens.next(); // Munch special + + let mut args = Vec::new(); + + if tokens.peek() == Some(&Token::RightParen) { + return Err(Error::EmptyDefine); + } + + while tokens.peek() != Some(&Token::RightParen) { + let values = match next_statement(tokens)? { + Some(Node::List(list)) => list, + Some(node) => Err(Error::ExpectedList(node.get_type()))?, + None => Err(Error::UnexpectedEof)?, + }; + + if values.len() != 2 { + return Err(Error::MismatchedValueCount(2, values.len())); + } + + let mut values = values.into_iter(); + let name = match values.next().unwrap() { + Node::Symbol(name) => name, + val => Err(Error::ExpectedSymbol(val.get_type()))?, + }; + + let value = values.next().unwrap(); + + args.push((name.to_owned(), value.to_owned())) + } + + tokens.next(); // Munch closer + + Ok(Node::Define { args }) +} + +fn read_let(tokens: &mut TokenIter) -> Result { + tokens.next(); // Munch special + + let raw_args = match next_statement(tokens)? { + Some(Node::List(list)) => list, + Some(node) => Err(Error::ExpectedList(node.get_type()))?, + None => Err(Error::UnexpectedEof)?, + }; + + let mut args = Vec::new(); + + for node in raw_args.iter() { + match node { + Node::List(list) => { + if list.len() != 2 { + return Err(Error::MismatchedValueCount(2, list.len())); + } + + let mut list = list.into_iter(); + let name = match list.next().unwrap() { + Node::Symbol(name) => name, + val => Err(Error::ExpectedSymbol(val.get_type()))?, + }; + + let value = list.next().unwrap(); + + args.push((name.to_owned(), value.to_owned())) + } + + _ => return Err(Error::ExpectedList(node.get_type()))?, + } + } + + let body = read_body(tokens)?; + + tokens.next(); // Munch closer + + Ok(Node::Let { args, body }) +} + +fn read_function(tokens: &mut TokenIter) -> Result { + tokens.next(); // Munch special + + let parameters = match next_statement(tokens)? { + Some(Node::List(args)) => args, + Some(Node::Nil) => vec![], + Some(node) => Err(Error::ExpectedList(node.get_type()))?, + None => Err(Error::EmptyFunctionArgs)?, + }; + + // Ensure all parameters are symbols then extract their names + let parameters = parameters + .into_iter() + .map(|node| match node { + Node::Symbol(name) => Ok(name), + _ => Err(Error::ExpectedSymbol(node.get_type()))?, + }) + .collect::>>()?; + + if tokens.peek() == Some(&Token::RightParen) { + return Err(Error::EmptyFunctionBody); + } + + let body = read_body(tokens)?; + + tokens.next(); // Munch closer + + Ok(Node::Function { parameters, body }) +} + +fn read_eval(tokens: &mut TokenIter) -> Result { + tokens.next(); // Munch special + + let node = match next_statement(tokens)? { + Some(node) => node, + None => Err(Error::UnexpectedEof)?, + }; + + if tokens.peek() != Some(&Token::RightParen) { + // FIXME: This should not say expected is 0 + return Err(Error::MismatchedValueCount(1, 0)); + } + + tokens.next(); // Munch closer + + Ok(Node::Eval { + body: Box::new(node), + }) +} + +fn read_apply(tokens: &mut TokenIter) -> Result { + tokens.next(); // Munch special + + if tokens.peek() == Some(&Token::RightParen) { + return Err(Error::EmptyApplyOperator); + } + let func = match next_statement(tokens)? { + Some(node) => node, + None => Err(Error::UnexpectedEof)?, + }; + + if tokens.peek() == Some(&Token::RightParen) { + return Err(Error::EmptyApplyBody); + } + + let args = match next_statement(tokens)? { + Some(node) => node, + None => Err(Error::UnexpectedEof)?, + }; + + tokens.next(); // Munch closer + + Ok(Node::Apply { + func: Box::new(func), + args: Box::new(args), + }) +} + +fn read_if(tokens: &mut TokenIter) -> Result { + tokens.next(); // Munch special + + if tokens.peek() == Some(&Token::RightParen) { + return Err(Error::EmptyIfCondition); + } + + let cond = match next_statement(tokens)? { + Some(node) => node, + None => Err(Error::UnexpectedEof)?, + }; + + if tokens.peek() == Some(&Token::RightParen) { + return Err(Error::EmptyIfConsequence); + } + + let consequence = match next_statement(tokens)? { + Some(node) => node, + None => Err(Error::UnexpectedEof)?, + }; + + let alternative = match tokens.peek() { + Some(&Token::RightParen) => Node::Nil, + Some(_) => match next_statement(tokens)? { + Some(node) => node, + None => Err(Error::UnexpectedEof)?, + }, + None => Err(Error::UnexpectedEof)?, + }; + + tokens.next(); // Munch closer + + Ok(Node::If { + cond: Box::new(cond), + consequence: Box::new(consequence), + alternative: Box::new(alternative), + }) +} + +fn read_do(tokens: &mut TokenIter) -> Result { + tokens.next(); // Munch special + + if tokens.peek() == Some(&Token::RightParen) { + return Err(Error::EmptyDo); + } + + let body = read_body(tokens)?; + + tokens.next(); // Munch closer + + Ok(Node::Do { body }) +} + +fn read_body(tokens: &mut TokenIter) -> Result> { + let mut body = Vec::new(); + + while tokens.peek() != Some(&Token::RightParen) { + if let Some(node) = next_statement(tokens)? { + body.push(node); + continue; + } + + match next_statement(tokens)? { + Some(node) => body.push(node), + None => Err(Error::UnclosedParenthesis)?, + } + } + + Ok(body) } diff --git a/mute-parser/src/node.rs b/mute-parser/src/node.rs index 87615c7..1853694 100644 --- a/mute-parser/src/node.rs +++ b/mute-parser/src/node.rs @@ -11,9 +11,71 @@ pub enum Node { String(String), Boolean(bool), Nil, + Void, // XXX: Not sure if this should be a node Vector(Vec), Map(HashMap), + + // Specials + Define { + args: Vec<(String, Node)>, + }, + Let { + args: Vec<(String, Node)>, + body: Vec, + }, + Function { + parameters: Vec, + body: Vec, + }, + If { + cond: Box, + consequence: Box, + alternative: Box, + }, + + Quote(Box), + Quasiquote(Box), + Unquote(Box), + + Do { + body: Vec, + }, + Eval { + body: Box, + }, + Apply { + func: Box, + args: Box, + }, +} + +impl Node { + pub fn get_type(&self) -> String { + match self { + Node::List(_) => "list".to_string(), + Node::Symbol(_) => "symbol".to_string(), + Node::Keyword(_) => "keyword".to_string(), + Node::Int(_) => "int".to_string(), + Node::Float(_) => "float".to_string(), + Node::String(_) => "string".to_string(), + Node::Boolean(_) => "boolean".to_string(), + Node::Nil => "nil".to_string(), + Node::Void => "void".to_string(), + Node::Vector(_) => "vector".to_string(), + Node::Map(_) => "map".to_string(), + Node::Define { .. } => "define".to_string(), + Node::Let { .. } => "let".to_string(), + Node::Function { .. } => "function".to_string(), + Node::If { .. } => "if".to_string(), + Node::Quote(_) => "quote".to_string(), + Node::Quasiquote(_) => "quasiquote".to_string(), + Node::Unquote(_) => "unquote".to_string(), + Node::Do { .. } => "do".to_string(), + Node::Eval { .. } => "eval".to_string(), + Node::Apply { .. } => "apply".to_string(), + } + } } impl std::fmt::Display for Node { @@ -37,6 +99,7 @@ impl std::fmt::Display for Node { Node::Keyword(val) => write!(f, ":{}", val), Node::String(val) => write!(f, "{}", val), Node::Nil => write!(f, "()"), + Node::Void => write!(f, ""), Node::Vector(vec) => { let s = vec @@ -56,6 +119,70 @@ impl std::fmt::Display for Node { write!(f, "{{{res}}}") } + + Node::Define { args } => { + let res = args + .iter() + .map(|(k, v)| format!("({k} {v})")) + .reduce(|lhs, rhs| format!("{lhs} {rhs}")) + .expect("parser does not allow empty define statements"); + + write!(f, "(define {res})") + } + + Node::Let { args, body } => { + let args = args + .iter() + .map(|(k, v)| format!("({k} {v})")) + .reduce(|lhs, rhs| format!("{lhs} {rhs}")) + .expect("parser does not allow empty let statements"); + let body = body + .iter() + .map(|node| node.to_string()) + .reduce(|lhs, rhs| format!("{lhs} {rhs}")) + .expect("parser does not allow empty let bodies"); + + write!(f, "(let ({args}) {body})") + } + Node::Function { + parameters: args, + body, + } => { + let args = args + .iter() + .map(|node| node.to_string()) + .reduce(|lhs, rhs| format!("{lhs} {rhs}")) + .unwrap_or_default(); + + let body = body + .iter() + .map(|node| node.to_string()) + .reduce(|lhs, rhs| format!("{lhs} {rhs}")) + .expect("parser does not allow empty function bodies"); + + write!(f, "(fn ({args}) {body})") + } + Node::If { + cond, + consequence, + alternative, + } => write!(f, "(if {cond} {consequence} {alternative})"), + + Node::Quote(node) => write!(f, "'{node}"), + Node::Quasiquote(node) => write!(f, "`{node}"), + Node::Unquote(node) => write!(f, ",{node}"), + + Node::Do { body } => { + let body = body + .iter() + .map(|node| node.to_string()) + .reduce(|lhs, rhs| format!("{lhs} {rhs}")) + .expect("parser does not allow empty do statements"); + + write!(f, "(do {body})") + } + Node::Eval { body } => write!(f, "(eval {body})"), + Node::Apply { func, args } => write!(f, "(apply {func} {args})"), } } }