From e67b0dc440ffdfe7300f993d7d961fd893a8025b Mon Sep 17 00:00:00 2001 From: Connor Skees Date: Sun, 26 Jul 2020 22:11:19 -0400 Subject: [PATCH] implement builtin math functions `sin`, `tan`, `acos`, `asin`, and `atan` --- src/builtin/modules/math.rs | 147 ++++++++++++++++++++++++++++++++++-- src/value/number/mod.rs | 71 ++++++++++++++++- 2 files changed, 209 insertions(+), 9 deletions(-) diff --git a/src/builtin/modules/math.rs b/src/builtin/modules/math.rs index db97a78..1c6dce0 100644 --- a/src/builtin/modules/math.rs +++ b/src/builtin/modules/math.rs @@ -172,27 +172,159 @@ fn cos(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { fn sin(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - todo!() + let number = args.get_err(0, "number")?; + + Ok(match number { + Value::Dimension(Some(n), Unit::None, ..) | Value::Dimension(Some(n), Unit::Rad, ..) => { + Value::Dimension(n.sin(), Unit::None, true) + } + Value::Dimension(Some(n), Unit::Deg, ..) => Value::Dimension(n.sin_deg(), Unit::None, true), + v @ Value::Dimension(Some(..), ..) => { + return Err(( + format!( + "$number: Expected {} to be an angle.", + v.inspect(args.span())? + ), + args.span(), + ) + .into()) + } + Value::Dimension(None, ..) => Value::Dimension(None, Unit::None, true), + v => { + return Err(( + format!("$number: {} is not a number.", v.inspect(args.span())?), + args.span(), + ) + .into()) + } + }) } fn tan(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - todo!() + let number = args.get_err(0, "number")?; + + Ok(match number { + Value::Dimension(Some(n), Unit::None, ..) | Value::Dimension(Some(n), Unit::Rad, ..) => { + Value::Dimension(n.tan(), Unit::None, true) + } + Value::Dimension(Some(n), Unit::Deg, ..) => Value::Dimension(n.tan_deg(), Unit::None, true), + v @ Value::Dimension(Some(..), ..) => { + return Err(( + format!( + "$number: Expected {} to be an angle.", + v.inspect(args.span())? + ), + args.span(), + ) + .into()) + } + Value::Dimension(None, ..) => Value::Dimension(None, Unit::None, true), + v => { + return Err(( + format!("$number: {} is not a number.", v.inspect(args.span())?), + args.span(), + ) + .into()) + } + }) } fn acos(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - todo!() + args.max_args(1)?; + let number = args.get_err(0, "number")?; + + Ok(match number { + Value::Dimension(Some(n), Unit::None, ..) | Value::Dimension(Some(n), Unit::Rad, ..) => { + Value::Dimension(n.acos(), Unit::None, true) + } + Value::Dimension(Some(n), Unit::Deg, ..) => { + Value::Dimension(n.acos_deg(), Unit::None, true) + } + v @ Value::Dimension(Some(..), ..) => { + return Err(( + format!( + "$number: Expected {} to be an angle.", + v.inspect(args.span())? + ), + args.span(), + ) + .into()) + } + Value::Dimension(None, ..) => Value::Dimension(None, Unit::None, true), + v => { + return Err(( + format!("$number: {} is not a number.", v.inspect(args.span())?), + args.span(), + ) + .into()) + } + }) } fn asin(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - todo!() + let number = args.get_err(0, "number")?; + + Ok(match number { + Value::Dimension(Some(n), Unit::None, ..) | Value::Dimension(Some(n), Unit::Rad, ..) => { + Value::Dimension(n.asin(), Unit::None, true) + } + Value::Dimension(Some(n), Unit::Deg, ..) => { + Value::Dimension(n.asin_deg(), Unit::None, true) + } + v @ Value::Dimension(Some(..), ..) => { + return Err(( + format!( + "$number: Expected {} to be an angle.", + v.inspect(args.span())? + ), + args.span(), + ) + .into()) + } + Value::Dimension(None, ..) => Value::Dimension(None, Unit::None, true), + v => { + return Err(( + format!("$number: {} is not a number.", v.inspect(args.span())?), + args.span(), + ) + .into()) + } + }) } fn atan(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - todo!() + let number = args.get_err(0, "number")?; + + Ok(match number { + Value::Dimension(Some(n), Unit::None, ..) | Value::Dimension(Some(n), Unit::Rad, ..) => { + Value::Dimension(n.atan(), Unit::None, true) + } + Value::Dimension(Some(n), Unit::Deg, ..) => { + Value::Dimension(n.atan_deg(), Unit::None, true) + } + v @ Value::Dimension(Some(..), ..) => { + return Err(( + format!( + "$number: Expected {} to be an angle.", + v.inspect(args.span())? + ), + args.span(), + ) + .into()) + } + Value::Dimension(None, ..) => Value::Dimension(None, Unit::None, true), + v => { + return Err(( + format!("$number: {} is not a number.", v.inspect(args.span())?), + args.span(), + ) + .into()) + } + }) } fn atan2(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { @@ -214,6 +346,11 @@ pub(crate) fn declare(f: &mut Module) { f.insert_builtin("clamp", clamp); f.insert_builtin("sqrt", sqrt); f.insert_builtin("cos", cos); + f.insert_builtin("sin", sin); + f.insert_builtin("tan", tan); + f.insert_builtin("acos", acos); + f.insert_builtin("asin", asin); + f.insert_builtin("atan", atan); #[cfg(feature = "random")] f.insert_builtin("random", random); diff --git a/src/value/number/mod.rs b/src/value/number/mod.rs index 3c99f68..8e9b9e7 100644 --- a/src/value/number/mod.rs +++ b/src/value/number/mod.rs @@ -117,21 +117,84 @@ impl Number { }) } + pub fn sqrt(self) -> Option { + Some(Number::Big(Box::new(BigRational::from_float( + self.as_float()?.sqrt(), + )?))) + } +} + +/// Trigonometry methods +impl Number { + pub fn cos(self) -> Option { + Some(Number::Big(Box::new(BigRational::from_float( + self.as_float()?.cos(), + )?))) + } + pub fn cos_deg(self) -> Option { Some(Number::Big(Box::new(BigRational::from_float( self.as_float()?.to_radians().cos(), )?))) } - pub fn sqrt(self) -> Option { + pub fn acos(self) -> Option { Some(Number::Big(Box::new(BigRational::from_float( - self.as_float()?.sqrt(), + self.as_float()?.acos(), )?))) } - pub fn cos(self) -> Option { + pub fn acos_deg(self) -> Option { Some(Number::Big(Box::new(BigRational::from_float( - self.as_float()?.cos(), + self.as_float()?.to_radians().acos(), + )?))) + } + + pub fn sin(self) -> Option { + Some(Number::Big(Box::new(BigRational::from_float( + self.as_float()?.sin(), + )?))) + } + + pub fn sin_deg(self) -> Option { + Some(Number::Big(Box::new(BigRational::from_float( + self.as_float()?.to_radians().sin(), + )?))) + } + + pub fn asin(self) -> Option { + Some(Number::Big(Box::new(BigRational::from_float( + self.as_float()?.asin(), + )?))) + } + + pub fn asin_deg(self) -> Option { + Some(Number::Big(Box::new(BigRational::from_float( + self.as_float()?.to_radians().asin(), + )?))) + } + + pub fn tan(self) -> Option { + Some(Number::Big(Box::new(BigRational::from_float( + self.as_float()?.tan(), + )?))) + } + + pub fn tan_deg(self) -> Option { + Some(Number::Big(Box::new(BigRational::from_float( + self.as_float()?.to_radians().tan(), + )?))) + } + + pub fn atan(self) -> Option { + Some(Number::Big(Box::new(BigRational::from_float( + self.as_float()?.atan(), + )?))) + } + + pub fn atan_deg(self) -> Option { + Some(Number::Big(Box::new(BigRational::from_float( + self.as_float()?.to_radians().atan(), )?))) } }