diff --git a/src/evaluate/function.rs b/src/evaluate/function.rs index 73c9e61..ac95a3b 100644 --- a/src/evaluate/function.rs +++ b/src/evaluate/function.rs @@ -2,8 +2,30 @@ use crate::parser::Expression; use crate::parser::Function; use crate::parser::Operator; use crate::parser::LineLocation; +use crate::quantity::FreeUnit; +use crate::quantity::WholeUnit; +use crate::quantity::Quantity; +use crate::quantity::Scalar; use super::EvalError; + +// If unitless, do nothing +// If compatible with radians, convert to radians and return unitless +// Otherwise, error. +// +// Used for trig functions. +fn to_radians(q: Quantity) -> Result { + if q.unitless() { return Ok(q); } + + let mut r = Quantity::new_rational(1f64).unwrap(); + r.insert_unit(FreeUnit::from_whole(WholeUnit::Radian), Scalar::new_rational(1f64).unwrap()); + let Some(q) = q.convert_to(r) else { return Err(()) }; + + return Ok(q.without_unit()); +} + + + pub fn eval_function(g: &Expression) -> Result { let Expression::Operator(loc, Operator::Function(f), args) = g else {panic!()}; @@ -16,6 +38,56 @@ pub fn eval_function(g: &Expression) -> Result { return Ok(Expression::Quantity(*loc + *l, q.without_unit())); } Function::ToBase => { return Ok(Expression::Quantity(*loc + *l, q.convert_to_base())); } + + // Trigonometry + Function::Sin => { + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + return Ok(Expression::Quantity(*loc + *l, q.sin())); + }, + Function::Cos => { + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + return Ok(Expression::Quantity(*loc + *l, q.cos())); + }, + Function::Tan => { + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + return Ok(Expression::Quantity(*loc + *l, q.tan())); + }, + Function::Csc => { + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + return Ok(Expression::Quantity(*loc + *l, q.csc())); + }, + Function::Sec => { + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + return Ok(Expression::Quantity(*loc + *l, q.sec())); + }, + Function::Cot => { + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + return Ok(Expression::Quantity(*loc + *l, q.cot())); + }, + Function::Sinh => { + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + return Ok(Expression::Quantity(*loc + *l, q.sinh())); + }, + Function::Cosh => { + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + return Ok(Expression::Quantity(*loc + *l, q.cosh())); + }, + Function::Tanh => { + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + return Ok(Expression::Quantity(*loc + *l, q.tanh())); + }, + Function::Csch => { + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + return Ok(Expression::Quantity(*loc + *l, q.csch())); + }, + Function::Sech => { + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + return Ok(Expression::Quantity(*loc + *l, q.sech())); + }, + Function::Coth => { + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + return Ok(Expression::Quantity(*loc + *l, q.coth())); + }, _ => {} } @@ -32,30 +104,28 @@ pub fn eval_function(g: &Expression) -> Result { return Ok(Expression::Quantity(*loc + *l, q.ln())); }, Function::TenLog => { return Ok(Expression::Quantity(*loc + *l, q.log10())); }, - Function::Sin => { return Ok(Expression::Quantity(*loc + *l, q.sin())); }, - Function::Cos => { return Ok(Expression::Quantity(*loc + *l, q.cos())); }, - Function::Tan => { return Ok(Expression::Quantity(*loc + *l, q.tan())); }, Function::Asin => { return Ok(Expression::Quantity(*loc + *l, q.asin())); }, Function::Acos => { return Ok(Expression::Quantity(*loc + *l, q.acos())); }, Function::Atan => { return Ok(Expression::Quantity(*loc + *l, q.atan())); }, - Function::Csc => { return Ok(Expression::Quantity(*loc + *l, q.csc())); }, - Function::Sec => { return Ok(Expression::Quantity(*loc + *l, q.sec())); }, - Function::Cot => { return Ok(Expression::Quantity(*loc + *l, q.cot())); }, - - Function::Sinh => { return Ok(Expression::Quantity(*loc + *l, q.sinh())); }, - Function::Cosh => { return Ok(Expression::Quantity(*loc + *l, q.cosh())); }, - Function::Tanh => { return Ok(Expression::Quantity(*loc + *l, q.tanh())); }, Function::Asinh => { return Ok(Expression::Quantity(*loc + *l, q.asinh())); }, Function::Acosh => { return Ok(Expression::Quantity(*loc + *l, q.acosh())); }, Function::Atanh => { return Ok(Expression::Quantity(*loc + *l, q.atanh())); }, - Function::Csch => { return Ok(Expression::Quantity(*loc + *l, q.csch())); }, - Function::Sech => { return Ok(Expression::Quantity(*loc + *l, q.sech())); }, - Function::Coth => { return Ok(Expression::Quantity(*loc + *l, q.coth())); }, - Function::ToBase | Function::NoUnit + | Function::Sin + | Function::Cos + | Function::Tan + | Function::Csc + | Function::Sec + | Function::Cot + | Function::Sinh + | Function::Cosh + | Function::Tanh + | Function::Csch + | Function::Sech + | Function::Coth => unreachable!() } } \ No newline at end of file diff --git a/src/quantity/mod.rs b/src/quantity/mod.rs index 93607ec..4c9dc90 100644 --- a/src/quantity/mod.rs +++ b/src/quantity/mod.rs @@ -16,12 +16,13 @@ cross-compilation to other systems. RUG does not work on all systems. */ mod scalar; -pub(in crate::quantity) use crate::quantity::scalar::Scalar; +pub use crate::quantity::scalar::Scalar; mod unit; pub use crate::quantity::unit::Unit; pub use crate::quantity::unit::FreeUnit; +pub use crate::quantity::unit::WholeUnit; mod quantity; pub use crate::quantity::quantity::Quantity; diff --git a/src/tests.rs b/src/tests.rs index b17c8be..df72d28 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -220,6 +220,11 @@ fn complex_units() { #[test] fn functions() { + good_expr("1", "sin(90 deg)"); + good_expr("-1", "cos(180 deg)"); + good_expr("0.70711", "sin(pi/4)"); + good_expr("0.70711", "sin((pi/4) r)"); + good_expr("2", "nounit(2 mm)"); good_expr("2", "nounit(2 meter * second)"); //good_expr("5000 m²·g/(s²·A²)", "tobase(5H)");