aboutsummaryrefslogtreecommitdiffstats
path: root/src/cexpr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/cexpr.rs')
-rw-r--r--src/cexpr.rs179
1 files changed, 129 insertions, 50 deletions
diff --git a/src/cexpr.rs b/src/cexpr.rs
index 5014f74..39c708e 100644
--- a/src/cexpr.rs
+++ b/src/cexpr.rs
@@ -5,20 +5,22 @@
*
*/
+use std::fmt;
use crate::sexpr::SExpr;
+/*
pub enum NumericConstant {
Pi, // 3.141592...
E, // 2.718281...
- Infinity,
}
+*/
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, PartialOrd)]
pub enum CNumber {
+ // order here is important for sorting, etc
Integer(i64),
- Rational(i64, u64),
- // Float
- // Constant
+ Rational(i64, i64),
+ Float(f64),
}
impl CNumber {
@@ -28,29 +30,46 @@ impl CNumber {
CNumber::Rational(a, b) => Ok(SExpr::SList(vec![
SExpr::SIdentifier("/".to_string()),
SExpr::SInteger(*a),
- SExpr::SInteger(*b as i64),
+ SExpr::SInteger(*b),
])),
+ CNumber::Float(v) => Ok(SExpr::SFloat(*v)),
}
}
}
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, PartialOrd)]
pub enum CExpr {
- Symbol(String),
+ // order here is important for sorting, etc
Number(CNumber),
+ Symbol(String),
Sum(Option<CNumber>, Vec<CExpr>),
Product(Option<CNumber>, Vec<CExpr>),
- Power(Box<CExpr>, Box<CExpr>),
+ Exponent(Box<CExpr>, Box<CExpr>),
Factorial(Box<CExpr>),
UnaryFunction(String, Box<CExpr>),
-
- // TODO: Infinity?
- // TODO: Limit?
- // TODO: Vector?
}
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
@@ -58,61 +77,106 @@ impl CExpr {
SExpr::SNull => Err("null not handled".to_string()),
SExpr::SBoolean(_) => Err("booleans not handled".to_string()),
SExpr::SInteger(v) => Ok(CExpr::Number(CNumber::Integer(*v))),
- SExpr::SFloat(v) => Err("floats not handled".to_string()),
+ 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::SList(_) => Err("null not handled".to_string()),
- //SExpr::SList(l) => CExpr::from_sexpr_list(l),
+ SExpr::SList(l) => CExpr::from_sexpr_list(l),
}
}
-/*
pub fn from_sexpr_list(list: &Vec<SExpr>) -> Result<CExpr, String> {
- if list.is_empty() {
- unimplemented!()
- }
- match list[0] {
- SExpr::SIdentifier("+") => {
- Ok(CExpr::Sum(
- // XXX
- None,
- list[1..].iter().map(|v| CExpr::from_sexpr(v)).collect::<Result<Vec<SExpr>, String>>()?,
- ))
- },
- SExpr::SIdentifier("*") => {
- },
- SExpr::SIdentifier("^") => {
- },
- _ => {
- unimplemented!()
+ // 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])?))),
+ ("^", 2) => {
+ let base = CExpr::from_sexpr(&rest[0])?;
+ let power = CExpr::from_sexpr(&rest[1])?;
+ use CExpr::*;
+ use CNumber::*;
+ match (base, power) {
+ (Number(Integer(0)), _) => Ok(Number(Integer(0))),
+ (Number(Integer(1)), _) => Ok(Number(Integer(1))),
+ (_, Number(Integer(0))) => Ok(Number(Integer(1))),
+ (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))),
+ }
+ } 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)),
+ _ => Err(format!("procedure not handled: {}", ident)),
}
+ } else {
+ Err(format!("S-Expr pattern not handled: {:?}", list))
}
}
-*/
- pub fn new_sum(list: &Vec<SExpr>) -> Result<CExpr, String> {
- unimplemented!()
+ pub fn new_sum(mut list: Vec<CExpr>) -> Result<CExpr, String> {
+ use CExpr::*;
+ use CNumber::*;
+ 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))
+ }
}
- pub fn new_product(list: &Vec<SExpr>) -> Result<CExpr, String> {
- unimplemented!()
+ pub fn new_product(mut list: Vec<CExpr>) -> Result<CExpr, String> {
+ use CExpr::*;
+ use CNumber::*;
+ 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))
+ }
}
pub fn to_sexpr(&self) -> Result<SExpr, String> {
match self {
CExpr::Symbol(s) => Ok(SExpr::SIdentifier(s.to_string())),
CExpr::Number(n) => n.to_sexpr(),
- CExpr::Sum(n, l) => Ok(SExpr::SList(vec![
- SExpr::SIdentifier("+".to_string()),
- SExpr::SList(l.iter().map(|v| v.to_sexpr()).collect::<Result<Vec<SExpr>, String>>()?),
- // XXX: n
- ])),
- CExpr::Product(n, l) => Ok(SExpr::SList(vec![
- SExpr::SIdentifier("*".to_string()),
- SExpr::SList(l.iter().map(|v| v.to_sexpr()).collect::<Result<Vec<SExpr>, String>>()?),
- // XXX: n
- ])),
- CExpr::Power(a, b) => Ok(SExpr::SList(vec![
+ CExpr::Sum(n, l) => {
+ 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>>()?;
+ 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()?,
b.to_sexpr()?,
@@ -127,4 +191,19 @@ impl CExpr {
])),
}
}
+
+ pub fn from_str(raw: &str) -> Result<CExpr, String> {
+ let ast = SExpr::from_str(raw)?;
+ CExpr::from_sexpr(&ast)
+ }
}
+
+impl fmt::Display for CExpr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.to_sexpr() {
+ Ok(ast) => ast.fmt(f),
+ Err(_) => Err(std::fmt::Error),
+ }
+ }
+}
+