diff options
Diffstat (limited to 'src/cexpr.rs')
-rw-r--r-- | src/cexpr.rs | 159 |
1 files changed, 102 insertions, 57 deletions
diff --git a/src/cexpr.rs b/src/cexpr.rs index 39c708e..1971feb 100644 --- a/src/cexpr.rs +++ b/src/cexpr.rs @@ -5,15 +5,33 @@ * */ -use std::fmt; use crate::sexpr::SExpr; +use std::fmt; -/* +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum NumericConstant { Pi, // 3.141592... E, // 2.718281... } -*/ + +impl NumericConstant { + pub fn as_number(&self) -> CNumber { + use CNumber::*; + use NumericConstant::*; + match self { + Pi => Float(3.141592f64), + E => Float(2.718281f64), + } + } + + pub fn as_string(&self) -> String { + use NumericConstant::*; + match self { + Pi => "pi".to_string(), + E => "e".to_string(), + } + } +} #[derive(Clone, PartialEq, PartialOrd)] pub enum CNumber { @@ -41,6 +59,7 @@ impl CNumber { pub enum CExpr { // order here is important for sorting, etc Number(CNumber), + Constant(NumericConstant), Symbol(String), Sum(Option<CNumber>, Vec<CExpr>), Product(Option<CNumber>, Vec<CExpr>), @@ -50,28 +69,7 @@ pub enum CExpr { } impl CExpr { - -/* - fn sort_type_val(&self) -> u8 { - use CExpr::*; - match self { - Number(Integer(_)) => 1, - Number(Rational(_)) => 2, - Number(Float(_)) => 3, - Symbol(_) => 3, - Sum(Some(_), _) => 4, - Sum(None, _) => 4, - Product(Some(_), _) => 4, - Product(None, _) => 4, - Exponent(_, _) => 4, - Factorial(_) => 4, - UnaryFunction(_, _) => 4, - } - } -*/ - pub fn from_sexpr(sexpr: &SExpr) -> Result<CExpr, String> { - // not all cases are handled; some atoms are covered trivialy match sexpr { SExpr::SNull => Err("null not handled".to_string()), @@ -79,7 +77,11 @@ impl CExpr { SExpr::SInteger(v) => Ok(CExpr::Number(CNumber::Integer(*v))), SExpr::SFloat(v) => Ok(CExpr::Number(CNumber::Float(*v))), SExpr::SString(_) => Err("null not handled".to_string()), - SExpr::SIdentifier(v) => Ok(CExpr::Symbol(v.to_string())), + SExpr::SIdentifier(v) => match v.as_str() { + "pi" => Ok(CExpr::Constant(NumericConstant::Pi)), + "e" => Ok(CExpr::Constant(NumericConstant::E)), + _ => Ok(CExpr::Symbol(v.to_string())), + }, SExpr::SList(l) => CExpr::from_sexpr_list(l), } } @@ -88,10 +90,11 @@ impl CExpr { // https://adventures.michaelfbryan.com/posts/daily/slice-patterns/ if let [SExpr::SIdentifier(ident), rest @ ..] = list.as_slice() { match (ident.as_str(), rest.len()) { - ("factorial", 1) => - Ok(CExpr::Factorial(Box::new(CExpr::from_sexpr(&rest[0])?))), - ("cos" | "sin" | "tan", 1) => - Ok(CExpr::UnaryFunction(ident.to_string(), Box::new(CExpr::from_sexpr(&rest[0])?))), + ("factorial", 1) => Ok(CExpr::Factorial(Box::new(CExpr::from_sexpr(&rest[0])?))), + ("cos" | "sin" | "tan", 1) => Ok(CExpr::UnaryFunction( + ident.to_string(), + Box::new(CExpr::from_sexpr(&rest[0])?), + )), ("^", 2) => { let base = CExpr::from_sexpr(&rest[0])?; let power = CExpr::from_sexpr(&rest[1])?; @@ -104,27 +107,63 @@ impl CExpr { (base, Number(Integer(1))) => Ok(base), (base, power) => Ok(Exponent(Box::new(base), Box::new(power))), } - }, + } ("/", 2) => { - if let (SExpr::SInteger(numer), SExpr::SInteger(denom)) = (&rest[0], &rest[1]) { - match (numer, denom) { - (_, 0) => Err("division by zero".to_string()), - (0, _) => Ok(CExpr::Number(CNumber::Integer(0))), - (a, 1) => Ok(CExpr::Number(CNumber::Integer(*a))), - (a, b) if a == b => Ok(CExpr::Number(CNumber::Integer(1))), - (a, b) if a % b == 0 => Ok(CExpr::Number(CNumber::Integer(a/b))), - _ => Ok(CExpr::Number(CNumber::Rational(*numer, *denom))), + use CExpr::*; + use CNumber::*; + use SExpr::*; + match (&rest[0], &rest[1]) { + (_, SInteger(0)) => Err("division by zero".to_string()), + (SInteger(0), _) => Ok(Number(Integer(0))), + (se, SInteger(1)) => CExpr::from_sexpr(se), + (SInteger(a), SInteger(b)) if a == b => Ok(Number(Integer(1))), + (SInteger(a), SInteger(b)) if a % b == 0 => Ok(Number(Integer(a / b))), + (SInteger(a), SInteger(b)) => Ok(Number(Rational(*a, *b))), + (SInteger(1), b) => { + let denom = CExpr::from_sexpr(b)?; + Ok(Exponent(Box::new(denom), Box::new(Number(Integer(-1))))) + } + (a, b) => { + let (numer, denom) = (CExpr::from_sexpr(a)?, CExpr::from_sexpr(b)?); + CExpr::new_product(vec![ + numer, + Exponent(Box::new(denom), Box::new(Number(Integer(-1)))), + ]) } - } else { - Err("only integers in fractions supported".to_string()) } - }, + } // TODO: how to make range unbounded? or less bounded? - ("+", 2..=5000) => CExpr::new_sum(rest.iter().map(|v| CExpr::from_sexpr(v)).collect::<Result<Vec<CExpr>, String>>()?), - ("*", 2..=5000) => CExpr::new_product(rest.iter().map(|v| CExpr::from_sexpr(v)).collect::<Result<Vec<CExpr>, String>>()?), - // TODO: subtraction, division - ("factorial" | "^" | "cos" | "sin" | "tan", count) => - Err(format!("wrong number of arguments to {}: {}", ident, count)), + ("+", 2..=5000) => { + CExpr::new_sum(rest.iter().map(|v| CExpr::from_sexpr(v)).collect::<Result< + Vec<CExpr>, + String, + >>( + )?) + } + ("*", 2..=5000) => { + CExpr::new_product(rest.iter().map(|v| CExpr::from_sexpr(v)).collect::<Result< + Vec<CExpr>, + String, + >>( + )?) + } + ("-", 2) => { + let a = CExpr::from_sexpr(&rest[0])?; + let b = CExpr::from_sexpr(&rest[1])?; + use CExpr::*; + use CNumber::*; + match (a, b) { + (expr, Number(Integer(0))) => Ok(expr), + (Number(Integer(a)), Number(Integer(b))) => Ok(Number(Integer(a - b))), + (a, b) => CExpr::new_sum(vec![ + a, + CExpr::new_product(vec![Number(Integer(-1)), b])?, + ]), + } + } + ("factorial" | "^" | "cos" | "sin" | "tan", count) => { + Err(format!("wrong number of arguments to {}: {}", ident, count)) + } _ => Err(format!("procedure not handled: {}", ident)), } } else { @@ -138,9 +177,9 @@ impl CExpr { list.sort_by(|a, b| a.partial_cmp(b).unwrap()); match list.as_slice() { [Number(Integer(0)), e] => Ok(e.clone()), // XXX: remove clone() - [Number(Integer(a)), Number(Integer(b))] => Ok(Number(Integer(a+b))), - [Number(Integer(a)), Number(Rational(n, d))] => Ok(Number(Rational(n+a*d, *d))), - _ => Ok(Sum(None, list)) + [Number(Integer(a)), Number(Integer(b))] => Ok(Number(Integer(a + b))), + [Number(Integer(a)), Number(Rational(n, d))] => Ok(Number(Rational(n + a * d, *d))), + _ => Ok(Sum(None, list)), } } @@ -150,9 +189,9 @@ impl CExpr { list.sort_by(|a, b| a.partial_cmp(b).unwrap()); match list.as_slice() { [Number(Integer(1)), e] => Ok(e.clone()), // XXX: remove clone() - [Number(Integer(a)), Number(Integer(b))] => Ok(Number(Integer(a*b))), - [Number(Integer(a)), Number(Rational(n, d))] => Ok(Number(Rational(a*n, *d))), - _ => Ok(Product(None, list)) + [Number(Integer(a)), Number(Integer(b))] => Ok(Number(Integer(a * b))), + [Number(Integer(a)), Number(Rational(n, d))] => Ok(Number(Rational(a * n, *d))), + _ => Ok(Product(None, list)), } } @@ -160,22 +199,29 @@ impl CExpr { match self { CExpr::Symbol(s) => Ok(SExpr::SIdentifier(s.to_string())), CExpr::Number(n) => n.to_sexpr(), + CExpr::Constant(n) => Ok(SExpr::SIdentifier(n.as_string())), CExpr::Sum(n, l) => { - let mut list = l.iter().map(|v| v.to_sexpr()).collect::<Result<Vec<SExpr>, String>>()?; + let mut list = l + .iter() + .map(|v| v.to_sexpr()) + .collect::<Result<Vec<SExpr>, String>>()?; list.insert(0, SExpr::SIdentifier("+".to_string())); if let Some(num) = n { list.insert(1, num.to_sexpr()?); } Ok(SExpr::SList(list)) - }, + } CExpr::Product(n, l) => { - let mut list = l.iter().map(|v| v.to_sexpr()).collect::<Result<Vec<SExpr>, String>>()?; + let mut list = l + .iter() + .map(|v| v.to_sexpr()) + .collect::<Result<Vec<SExpr>, String>>()?; list.insert(0, SExpr::SIdentifier("*".to_string())); if let Some(num) = n { list.insert(1, num.to_sexpr()?); } Ok(SExpr::SList(list)) - }, + } CExpr::Exponent(a, b) => Ok(SExpr::SList(vec![ SExpr::SIdentifier("^".to_string()), a.to_sexpr()?, @@ -206,4 +252,3 @@ impl fmt::Display for CExpr { } } } - |