aboutsummaryrefslogtreecommitdiffstats
path: root/src/cexpr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/cexpr.rs')
-rw-r--r--src/cexpr.rs159
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 {
}
}
}
-