use crate::sexp::{SExp, SExp::*, SLeaf::*, util::*}; impl SExp { /// Evaluates the s-expression one step. /// /// **Integer operations** /// /// Addition, subtraction, multiplication and division /// are supported. /// ```rust /// use melisp::sexp::{SExp::*, SLeaf::*, util::*}; /// /// assert_eq!( /// scons(Add, scons(1, scons(1, Nil))).step(), /// Ok(Atom(Int(2))) /// ); /// /// assert_eq!( /// scons(Sub, scons(1, scons(2, Nil))).step(), /// Ok(Atom(Int(-1))) /// ); /// /// assert_eq!( /// scons(Mul, scons(2, scons(3, Nil))).step(), /// Ok(Atom(Int(6))) /// ); /// /// assert_eq!( /// scons(Div, scons(6, scons(2, Nil))).step(), /// Ok(Atom(Int(3))) /// ); /// ``` /// /// Division truncates the decimals. /// ```rust /// use melisp::sexp::{SExp::*, SLeaf::*, util::*}; /// /// assert_eq!( /// scons(Div, scons(5, scons(2, Nil))).step(), /// Ok(Atom(Int(2))) /// ); /// ``` /// /// Also, addition and multiplication can take more than two arguments: /// ```rust /// use melisp::sexp::{SExp::*, SLeaf::*, util::*}; /// /// assert_eq!( /// scons(Add, scons(1, scons(2, scons(3, Nil)))).step(), /// Ok(Atom(Int(6))) /// ); /// /// assert_eq!( /// scons(Mul, scons(1, scons(2, scons(3, Nil)))).step(), /// Ok(Atom(Int(6))) /// ); /// ``` /// /// Here's an example of a bit more complicated expression /// from wikipedias article on s-expressions. /// ```rust /// use melisp::sexp::{SExp::*, SLeaf::*, util::*}; /// /// fn main() { /// let exp = scons( /// Mul, /// scons( /// 2, /// scons( /// scons(Add, scons(3, scons(4, Nil))), /// Nil /// ) /// ) /// ); /// /// let exp = exp.step().unwrap(); /// assert_eq!(exp, scons(Mul, scons(2, scons(7, Nil)))); /// /// let exp = exp.step().unwrap(); /// assert_eq!(exp, Atom(Int(14))); /// } /// /// ``` /// /// **Quotes** /// If an s-expression should not be evaluated /// as a function, but it is instead to be treated /// as a list, `quote` can be used. /// With it as the operator, the rest of the list /// is evaluated to values, but they are not passed /// to the operator after that. /// ```rust /// use melisp::sexp::{SExp::*, SLeaf::*, util::*}; /// assert_eq!( /// scons(Quote, scons(1, scons(2, Nil))).step(), /// Ok(scons(Quote, scons(1, scons(2, Nil)))) /// ); /// assert_eq!( /// scons(Quote, scons( /// scons(Sub, scons(2, scons(1, Nil))), /// scons(2, Nil))).step(), /// Ok(scons(Quote, scons(1, scons(2, Nil)))) /// ); /// ``` pub fn step(self) -> Result { match self { // List processing // op not a value // ----------------------- // (op x) -> (op.step() x) SCons(op, l) if !(*op).is_value() => Ok(scons(op.step()?, l)), // op value and a1 .. an values, and b0, b1 .. bn not values // --------------------------------------------------------- // (op a1 ... an b) -> (op a1 ... an b0.step() b1 .. bn) SCons(op, l) if !(*l).consists_of_values() => { fn inner(s: SExp) -> Result { match s { SCons(a, b) if (*a).is_value() => Ok(scons(a, inner(*b)?)), SCons(a, b) => Ok(scons(a.step()?, b)), x => x.step() } } Ok(scons(op, inner(*l)?)) }, // Arithmetic // t1, ..., tn integers // ------------------------------ // (+ t1 ... tn) -> t1 + ... + tn SCons(op, l) if *op == Atom(Add) => l.into_vec()? .iter() .fold( Ok(0), |acc, el| { match el { Int(x) => acc.map(|v| x+v), _ => Err( "'+' should only be given integers".to_string() ) } } ).map(|x| Atom(Int(x))), // t1, ..., tn integers // ------------------------------ // (* t1 ... tn) -> t1 * ... * tn SCons(op, l) if *op == Atom(Mul) => l.into_vec()? .iter() .fold( Ok(1), |acc, el| { match el { Int(x) => acc.map(|v| x*v), _ => Err( format!("'*' should only be given integers, found {:?}", el) ) } } ).map(|x| Atom(Int(x))), // t1,t2 integers // -------------------- // (- t1 t2) -> t1 - t2 SCons(op, l) if *op == Atom(Sub) => { let ls = l.into_vec()?; if ls.len() == 2 { match (ls[0].clone(), ls[1].clone()) { (Int(a), Int(b)) => Ok(Atom(Int(a - b))), _ => Err("'-' should be only given integers".to_string()) } } else { Err(format!("'-' should be given 2 arguments, found {}", ls.len())) } }, // t1,t2 integers // -------------------- // (/ t1 t2) -> t1 / t2 SCons(op, l) if *op == Atom(Div) => { let ls = l.into_vec()?; if ls.len() == 2 { match (ls[0].clone(), ls[1].clone()) { (_, Int(0)) => Err("division by zero".to_string()), (Int(a), Int(b)) => Ok(Atom(Int(a / b))), _ => Err("'/' should be only given integers".to_string()) } } else { Err(format!("'/' should be given 2 arguments, found {}", ls.len())) } }, // t is value // ---------- // t -> t t if t.is_value() => Ok(t), t => Err(format!("unimplemented: {:?}.step()", t)), } } }