From 53cf2816e0ec61c7f5c45544783725274d46a570 Mon Sep 17 00:00:00 2001 From: Connor Skees Date: Sun, 26 Jul 2020 19:38:41 -0400 Subject: [PATCH] use `None` to represent `NaN` --- src/builtin/functions/color/hsl.rs | 58 ++++++++------ src/builtin/functions/color/opacity.rs | 20 +++-- src/builtin/functions/color/other.rs | 15 ++-- src/builtin/functions/color/rgb.rs | 59 ++++++++------ src/builtin/functions/list.rs | 10 ++- src/builtin/functions/math.rs | 44 ++++++----- src/builtin/functions/string.rs | 31 ++++---- src/builtin/modules/math.rs | 4 +- src/color/mod.rs | 4 +- src/parse/control_flow.rs | 8 +- src/parse/value/eval.rs | 105 +++++++++++++++---------- src/parse/value/parse.rs | 8 +- src/value/mod.rs | 24 +++--- tests/division.rs | 5 ++ tests/meta.rs | 5 ++ 15 files changed, 248 insertions(+), 152 deletions(-) diff --git a/src/builtin/functions/color/hsl.rs b/src/builtin/functions/color/hsl.rs index 72821bb..b97ef24 100644 --- a/src/builtin/functions/color/hsl.rs +++ b/src/builtin/functions/color/hsl.rs @@ -35,7 +35,8 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> } let lightness = match channels.pop() { - Some(Value::Dimension(n, ..)) => n / Number::from(100), + Some(Value::Dimension(Some(n), ..)) => n / Number::from(100), + Some(Value::Dimension(None, ..)) => todo!(), Some(v) => { return Err(( format!("$lightness: {} is not a number.", v.inspect(args.span())?), @@ -47,7 +48,8 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> }; let saturation = match channels.pop() { - Some(Value::Dimension(n, ..)) => n / Number::from(100), + Some(Value::Dimension(Some(n), ..)) => n / Number::from(100), + Some(Value::Dimension(None, ..)) => todo!(), Some(v) => { return Err(( format!("$saturation: {} is not a number.", v.inspect(args.span())?), @@ -59,7 +61,8 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> }; let hue = match channels.pop() { - Some(Value::Dimension(n, ..)) => n, + Some(Value::Dimension(Some(n), ..)) => n, + Some(Value::Dimension(None, ..)) => todo!(), Some(v) => { return Err(( format!("$hue: {} is not a number.", v.inspect(args.span())?), @@ -78,7 +81,8 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> )))) } else { let hue = match args.get_err(0, "hue")? { - Value::Dimension(n, ..) => n, + Value::Dimension(Some(n), ..) => n, + Value::Dimension(None, ..) => todo!(), v if v.is_special_function() => { let saturation = args.get_err(1, "saturation")?; let lightness = args.get_err(2, "lightness")?; @@ -105,7 +109,8 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> } }; let saturation = match args.get_err(1, "saturation")? { - Value::Dimension(n, ..) => n / Number::from(100), + Value::Dimension(Some(n), ..) => n / Number::from(100), + Value::Dimension(None, ..) => todo!(), v if v.is_special_function() => { let lightness = args.get_err(2, "lightness")?; let mut string = format!( @@ -134,7 +139,8 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> } }; let lightness = match args.get_err(2, "lightness")? { - Value::Dimension(n, ..) => n / Number::from(100), + Value::Dimension(Some(n), ..) => n / Number::from(100), + Value::Dimension(None, ..) => todo!(), v if v.is_special_function() => { let mut string = format!( "{}({}, {}, {}", @@ -164,10 +170,11 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> let alpha = match args.default_arg( 3, "alpha", - Value::Dimension(Number::one(), Unit::None, true), + Value::Dimension(Some(Number::one()), Unit::None, true), )? { - Value::Dimension(n, Unit::None, _) => n, - Value::Dimension(n, Unit::Percent, _) => n / Number::from(100), + Value::Dimension(Some(n), Unit::None, _) => n, + Value::Dimension(Some(n), Unit::Percent, _) => n / Number::from(100), + Value::Dimension(None, ..) => todo!(), v @ Value::Dimension(..) => { return Err(( format!( @@ -216,7 +223,7 @@ pub(crate) fn hsla(args: CallArgs, parser: &mut Parser<'_>) -> SassResult pub(crate) fn hue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; match args.get_err(0, "color")? { - Value::Color(c) => Ok(Value::Dimension(c.hue(), Unit::Deg, true)), + Value::Color(c) => Ok(Value::Dimension(Some(c.hue()), Unit::Deg, true)), v => Err(( format!("$color: {} is not a color.", v.inspect(args.span())?), args.span(), @@ -228,7 +235,7 @@ pub(crate) fn hue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult) -> SassResult { args.max_args(1)?; match args.get_err(0, "color")? { - Value::Color(c) => Ok(Value::Dimension(c.saturation(), Unit::Percent, true)), + Value::Color(c) => Ok(Value::Dimension(Some(c.saturation()), Unit::Percent, true)), v => Err(( format!("$color: {} is not a color.", v.inspect(args.span())?), args.span(), @@ -240,7 +247,7 @@ pub(crate) fn saturation(mut args: CallArgs, parser: &mut Parser<'_>) -> SassRes pub(crate) fn lightness(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; match args.get_err(0, "color")? { - Value::Color(c) => Ok(Value::Dimension(c.lightness(), Unit::Percent, true)), + Value::Color(c) => Ok(Value::Dimension(Some(c.lightness()), Unit::Percent, true)), v => Err(( format!("$color: {} is not a color.", v.inspect(args.span())?), args.span(), @@ -262,7 +269,8 @@ pub(crate) fn adjust_hue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassRes } }; let degrees = match args.get_err(1, "degrees")? { - Value::Dimension(n, ..) => n, + Value::Dimension(Some(n), ..) => n, + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!( @@ -290,7 +298,8 @@ fn lighten(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { } }; let amount = match args.get_err(1, "amount")? { - Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 100) / Number::from(100), + Value::Dimension(Some(n), u, _) => bound!(args, "amount", n, u, 0, 100) / Number::from(100), + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!( @@ -318,7 +327,8 @@ fn darken(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { } }; let amount = match args.get_err(1, "amount")? { - Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 100) / Number::from(100), + Value::Dimension(Some(n), u, _) => bound!(args, "amount", n, u, 0, 100) / Number::from(100), + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!( @@ -346,7 +356,8 @@ fn saturate(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { } let amount = match args.get_err(1, "amount")? { - Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 100) / Number::from(100), + Value::Dimension(Some(n), u, _) => bound!(args, "amount", n, u, 0, 100) / Number::from(100), + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!( @@ -360,7 +371,7 @@ fn saturate(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { }; let color = match args.get_err(0, "color")? { Value::Color(c) => c, - Value::Dimension(n, u, _) => { + Value::Dimension(Some(n), u, _) => { return Ok(Value::String( format!("saturate({}{})", n, u), QuoteKind::None, @@ -390,7 +401,8 @@ fn desaturate(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult } }; let amount = match args.get_err(1, "amount")? { - Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 100) / Number::from(100), + Value::Dimension(Some(n), u, _) => bound!(args, "amount", n, u, 0, 100) / Number::from(100), + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!( @@ -409,7 +421,7 @@ pub(crate) fn grayscale(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResu args.max_args(1)?; let color = match args.get_err(0, "color")? { Value::Color(c) => c, - Value::Dimension(n, u, _) => { + Value::Dimension(Some(n), u, _) => { return Ok(Value::String( format!("grayscale({}{})", n, u), QuoteKind::None, @@ -446,9 +458,10 @@ pub(crate) fn invert(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult< let weight = match args.default_arg( 1, "weight", - Value::Dimension(Number::from(100), Unit::Percent, true), + Value::Dimension(Some(Number::from(100)), Unit::Percent, true), )? { - Value::Dimension(n, u, _) => bound!(args, "weight", n, u, 0, 100) / Number::from(100), + Value::Dimension(Some(n), u, _) => bound!(args, "weight", n, u, 0, 100) / Number::from(100), + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!( @@ -462,9 +475,10 @@ pub(crate) fn invert(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult< }; match args.get_err(0, "color")? { Value::Color(c) => Ok(Value::Color(Box::new(c.invert(weight)))), - Value::Dimension(n, Unit::Percent, _) => { + Value::Dimension(Some(n), Unit::Percent, _) => { Ok(Value::String(format!("invert({}%)", n), QuoteKind::None)) } + Value::Dimension(None, ..) => todo!(), Value::Dimension(..) => Err(( "Only one argument may be passed to the plain-CSS invert() function.", args.span(), diff --git a/src/builtin/functions/color/opacity.rs b/src/builtin/functions/color/opacity.rs index 553740e..b6f82eb 100644 --- a/src/builtin/functions/color/opacity.rs +++ b/src/builtin/functions/color/opacity.rs @@ -8,7 +8,8 @@ use crate::{ pub(crate) fn alpha(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; match args.get_err(0, "color")? { - Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None, true)), + Value::Color(c) => Ok(Value::Dimension(Some(c.alpha()), Unit::None, true)), + Value::Dimension(None, ..) => todo!(), v => Err(( format!("$color: {} is not a color.", v.inspect(args.span())?), args.span(), @@ -20,11 +21,12 @@ pub(crate) fn alpha(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult) -> SassResult { args.max_args(1)?; match args.get_err(0, "color")? { - Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None, true)), - Value::Dimension(num, unit, _) => Ok(Value::String( + Value::Color(c) => Ok(Value::Dimension(Some(c.alpha()), Unit::None, true)), + Value::Dimension(Some(num), unit, _) => Ok(Value::String( format!("opacity({}{})", num, unit), QuoteKind::None, )), + Value::Dimension(None, ..) => todo!(), v => Err(( format!("$color: {} is not a color.", v.inspect(args.span())?), args.span(), @@ -47,7 +49,8 @@ fn opacify(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { } }; let amount = match args.get_err(1, "amount")? { - Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 1), + Value::Dimension(Some(n), u, _) => bound!(args, "amount", n, u, 0, 1), + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!("$amount: {} is not a number.", v.inspect(args.span())?), @@ -72,7 +75,8 @@ fn fade_in(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { } }; let amount = match args.get_err(1, "amount")? { - Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 1), + Value::Dimension(Some(n), u, _) => bound!(args, "amount", n, u, 0, 1), + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!("$amount: {} is not a number.", v.inspect(args.span())?), @@ -98,7 +102,8 @@ fn transparentize(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult bound!(args, "amount", n, u, 0, 1), + Value::Dimension(Some(n), u, _) => bound!(args, "amount", n, u, 0, 1), + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!("$amount: {} is not a number.", v.inspect(args.span())?), @@ -123,7 +128,8 @@ fn fade_out(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { } }; let amount = match args.get_err(1, "amount")? { - Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 1), + Value::Dimension(Some(n), u, _) => bound!(args, "amount", n, u, 0, 1), + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!("$amount: {} is not a number.", v.inspect(args.span())?), diff --git a/src/builtin/functions/color/other.rs b/src/builtin/functions/color/other.rs index dc4d8ae..5b0b66e 100644 --- a/src/builtin/functions/color/other.rs +++ b/src/builtin/functions/color/other.rs @@ -15,7 +15,8 @@ use crate::{ macro_rules! opt_rgba { ($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal) => { let $name = match $args.default_named_arg($arg, Value::Null)? { - Value::Dimension(n, u, _) => Some(bound!($args, $arg, n, u, $low, $high)), + Value::Dimension(Some(n), u, _) => Some(bound!($args, $arg, n, u, $low, $high)), + Value::Dimension(None, ..) => todo!(), Value::Null => None, v => { return Err(( @@ -31,9 +32,10 @@ macro_rules! opt_rgba { macro_rules! opt_hsl { ($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal) => { let $name = match $args.default_named_arg($arg, Value::Null)? { - Value::Dimension(n, u, _) => { + Value::Dimension(Some(n), u, _) => { Some(bound!($args, $arg, n, u, $low, $high) / Number::from(100)) } + Value::Dimension(None, ..) => todo!(), Value::Null => None, v => { return Err(( @@ -81,7 +83,8 @@ pub(crate) fn change_color(mut args: CallArgs, parser: &mut Parser<'_>) -> SassR } let hue = match args.default_named_arg("hue", Value::Null)? { - Value::Dimension(n, ..) => Some(n), + Value::Dimension(Some(n), ..) => Some(n), + Value::Dimension(None, ..) => todo!(), Value::Null => None, v => { return Err(( @@ -140,7 +143,8 @@ pub(crate) fn adjust_color(mut args: CallArgs, parser: &mut Parser<'_>) -> SassR } let hue = match args.default_named_arg("hue", Value::Null)? { - Value::Dimension(n, ..) => Some(n), + Value::Dimension(Some(n), ..) => Some(n), + Value::Dimension(None, ..) => todo!(), Value::Null => None, v => { return Err(( @@ -198,9 +202,10 @@ pub(crate) fn scale_color(mut args: CallArgs, parser: &mut Parser<'_>) -> SassRe macro_rules! opt_scale_arg { ($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal) => { let $name = match $args.default_named_arg($arg, Value::Null)? { - Value::Dimension(n, Unit::Percent, _) => { + Value::Dimension(Some(n), Unit::Percent, _) => { Some(bound!($args, $arg, n, Unit::Percent, $low, $high) / Number::from(100)) } + Value::Dimension(None, ..) => todo!(), v @ Value::Dimension(..) => { return Err(( format!( diff --git a/src/builtin/functions/color/rgb.rs b/src/builtin/functions/color/rgb.rs index a744c14..9bf36ab 100644 --- a/src/builtin/functions/color/rgb.rs +++ b/src/builtin/functions/color/rgb.rs @@ -38,10 +38,11 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> } let blue = match channels.pop() { - Some(Value::Dimension(n, Unit::None, _)) => n, - Some(Value::Dimension(n, Unit::Percent, _)) => { + Some(Value::Dimension(Some(n), Unit::None, _)) => n, + Some(Value::Dimension(Some(n), Unit::Percent, _)) => { (n / Number::from(100)) * Number::from(255) } + Some(Value::Dimension(None, ..)) => todo!(), Some(v) if v.is_special_function() => { let green = channels.pop().unwrap(); let red = channels.pop().unwrap(); @@ -67,10 +68,11 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> }; let green = match channels.pop() { - Some(Value::Dimension(n, Unit::None, _)) => n, - Some(Value::Dimension(n, Unit::Percent, _)) => { + Some(Value::Dimension(Some(n), Unit::None, _)) => n, + Some(Value::Dimension(Some(n), Unit::Percent, _)) => { (n / Number::from(100)) * Number::from(255) } + Some(Value::Dimension(None, ..)) => todo!(), Some(v) if v.is_special_function() => { let string = match channels.pop() { Some(red) => format!( @@ -95,10 +97,11 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> }; let red = match channels.pop() { - Some(Value::Dimension(n, Unit::None, _)) => n, - Some(Value::Dimension(n, Unit::Percent, _)) => { + Some(Value::Dimension(Some(n), Unit::None, _)) => n, + Some(Value::Dimension(Some(n), Unit::Percent, _)) => { (n / Number::from(100)) * Number::from(255) } + Some(Value::Dimension(None, ..)) => todo!(), Some(v) if v.is_special_function() => { return Ok(Value::String( format!( @@ -148,8 +151,9 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> } }; let alpha = match args.get_err(1, "alpha")? { - Value::Dimension(n, Unit::None, _) => n, - Value::Dimension(n, Unit::Percent, _) => n / Number::from(100), + Value::Dimension(Some(n), Unit::None, _) => n, + Value::Dimension(Some(n), Unit::Percent, _) => n / Number::from(100), + Value::Dimension(None, ..) => todo!(), v @ Value::Dimension(..) => { return Err(( format!( @@ -184,8 +188,11 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> Ok(Value::Color(Box::new(color.with_alpha(alpha)))) } else { let red = match args.get_err(0, "red")? { - Value::Dimension(n, Unit::None, _) => n, - Value::Dimension(n, Unit::Percent, _) => (n / Number::from(100)) * Number::from(255), + Value::Dimension(Some(n), Unit::None, _) => n, + Value::Dimension(Some(n), Unit::Percent, _) => { + (n / Number::from(100)) * Number::from(255) + } + Value::Dimension(None, ..) => todo!(), v @ Value::Dimension(..) => { return Err(( format!( @@ -222,8 +229,11 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> } }; let green = match args.get_err(1, "green")? { - Value::Dimension(n, Unit::None, _) => n, - Value::Dimension(n, Unit::Percent, _) => (n / Number::from(100)) * Number::from(255), + Value::Dimension(Some(n), Unit::None, _) => n, + Value::Dimension(Some(n), Unit::Percent, _) => { + (n / Number::from(100)) * Number::from(255) + } + Value::Dimension(None, ..) => todo!(), v @ Value::Dimension(..) => { return Err(( format!( @@ -259,8 +269,11 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> } }; let blue = match args.get_err(2, "blue")? { - Value::Dimension(n, Unit::None, _) => n, - Value::Dimension(n, Unit::Percent, _) => (n / Number::from(100)) * Number::from(255), + Value::Dimension(Some(n), Unit::None, _) => n, + Value::Dimension(Some(n), Unit::Percent, _) => { + (n / Number::from(100)) * Number::from(255) + } + Value::Dimension(None, ..) => todo!(), v @ Value::Dimension(..) => { return Err(( format!( @@ -297,10 +310,11 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> let alpha = match args.default_arg( 3, "alpha", - Value::Dimension(Number::one(), Unit::None, true), + Value::Dimension(Some(Number::one()), Unit::None, true), )? { - Value::Dimension(n, Unit::None, _) => n, - Value::Dimension(n, Unit::Percent, _) => n / Number::from(100), + Value::Dimension(Some(n), Unit::None, _) => n, + Value::Dimension(Some(n), Unit::Percent, _) => n / Number::from(100), + Value::Dimension(None, ..) => todo!(), v @ Value::Dimension(..) => { return Err(( format!( @@ -347,7 +361,7 @@ pub(crate) fn rgba(args: CallArgs, parser: &mut Parser<'_>) -> SassResult pub(crate) fn red(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; match args.get_err(0, "color")? { - Value::Color(c) => Ok(Value::Dimension(c.red(), Unit::None, true)), + Value::Color(c) => Ok(Value::Dimension(Some(c.red()), Unit::None, true)), v => Err(( format!("$color: {} is not a color.", v.inspect(args.span())?), args.span(), @@ -359,7 +373,7 @@ pub(crate) fn red(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult) -> SassResult { args.max_args(1)?; match args.get_err(0, "color")? { - Value::Color(c) => Ok(Value::Dimension(c.green(), Unit::None, true)), + Value::Color(c) => Ok(Value::Dimension(Some(c.green()), Unit::None, true)), v => Err(( format!("$color: {} is not a color.", v.inspect(args.span())?), args.span(), @@ -371,7 +385,7 @@ pub(crate) fn green(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult) -> SassResult { args.max_args(1)?; match args.get_err(0, "color")? { - Value::Color(c) => Ok(Value::Dimension(c.blue(), Unit::None, true)), + Value::Color(c) => Ok(Value::Dimension(Some(c.blue()), Unit::None, true)), v => Err(( format!("$color: {} is not a color.", v.inspect(args.span())?), args.span(), @@ -407,9 +421,10 @@ pub(crate) fn mix(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult bound!(args, "weight", n, u, 0, 100) / Number::from(100), + Value::Dimension(Some(n), u, _) => bound!(args, "weight", n, u, 0, 100) / Number::from(100), + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!( diff --git a/src/builtin/functions/list.rs b/src/builtin/functions/list.rs index c0b8550..56ac391 100644 --- a/src/builtin/functions/list.rs +++ b/src/builtin/functions/list.rs @@ -14,7 +14,7 @@ use crate::{ pub(crate) fn length(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; Ok(Value::Dimension( - Number::from(args.get_err(0, "list")?.as_list().len()), + Some(Number::from(args.get_err(0, "list")?.as_list().len())), Unit::None, true, )) @@ -24,7 +24,8 @@ pub(crate) fn nth(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult num, + Value::Dimension(Some(num), ..) => num, + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!("$n: {} is not a number.", v.inspect(args.span())?), @@ -81,7 +82,8 @@ pub(crate) fn set_nth(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult v => (vec![v], ListSeparator::Space, Brackets::None), }; let n = match args.get_err(1, "n")? { - Value::Dimension(num, ..) => num, + Value::Dimension(Some(num), ..) => num, + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!("$n: {} is not a number.", v.inspect(args.span())?), @@ -244,7 +246,7 @@ pub(crate) fn index(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult Number::from(v + 1), None => return Ok(Value::Null), }; - Ok(Value::Dimension(index, Unit::None, true)) + Ok(Value::Dimension(Some(index), Unit::None, true)) } pub(crate) fn zip(args: CallArgs, parser: &mut Parser<'_>) -> SassResult { diff --git a/src/builtin/functions/math.rs b/src/builtin/functions/math.rs index 42cce7d..470937b 100644 --- a/src/builtin/functions/math.rs +++ b/src/builtin/functions/math.rs @@ -16,7 +16,8 @@ use crate::{ pub(crate) fn percentage(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; let num = match args.get_err(0, "number")? { - Value::Dimension(n, Unit::None, _) => n * Number::from(100), + Value::Dimension(Some(n), Unit::None, _) => n * Number::from(100), + Value::Dimension(None, ..) => todo!(), v @ Value::Dimension(..) => { return Err(( format!( @@ -35,13 +36,14 @@ pub(crate) fn percentage(mut args: CallArgs, parser: &mut Parser<'_>) -> SassRes .into()) } }; - Ok(Value::Dimension(num, Unit::Percent, true)) + Ok(Value::Dimension(Some(num), Unit::Percent, true)) } pub(crate) fn round(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; match args.get_err(0, "number")? { - Value::Dimension(n, u, _) => Ok(Value::Dimension(n.round(), u, true)), + Value::Dimension(Some(n), u, _) => Ok(Value::Dimension(Some(n.round()), u, true)), + Value::Dimension(None, ..) => todo!(), v => Err(( format!("$number: {} is not a number.", v.inspect(args.span())?), args.span(), @@ -53,7 +55,8 @@ pub(crate) fn round(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult) -> SassResult { args.max_args(1)?; match args.get_err(0, "number")? { - Value::Dimension(n, u, _) => Ok(Value::Dimension(n.ceil(), u, true)), + Value::Dimension(Some(n), u, _) => Ok(Value::Dimension(Some(n.ceil()), u, true)), + Value::Dimension(None, ..) => todo!(), v => Err(( format!("$number: {} is not a number.", v.inspect(args.span())?), args.span(), @@ -65,7 +68,8 @@ pub(crate) fn ceil(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult) -> SassResult { args.max_args(1)?; match args.get_err(0, "number")? { - Value::Dimension(n, u, _) => Ok(Value::Dimension(n.floor(), u, true)), + Value::Dimension(Some(n), u, _) => Ok(Value::Dimension(Some(n.floor()), u, true)), + Value::Dimension(None, ..) => todo!(), v => Err(( format!("$number: {} is not a number.", v.inspect(args.span())?), args.span(), @@ -77,7 +81,8 @@ pub(crate) fn floor(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult) -> SassResult { args.max_args(1)?; match args.get_err(0, "number")? { - Value::Dimension(n, u, _) => Ok(Value::Dimension(n.abs(), u, true)), + Value::Dimension(Some(n), u, _) => Ok(Value::Dimension(Some(n.abs()), u, true)), + Value::Dimension(None, ..) => todo!(), v => Err(( format!("$number: {} is not a number.", v.inspect(args.span())?), args.span(), @@ -117,11 +122,12 @@ pub(crate) fn comparable(mut args: CallArgs, parser: &mut Parser<'_>) -> SassRes pub(crate) fn random(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; let limit = match args.default_arg(0, "limit", Value::Null)? { - Value::Dimension(n, ..) => n, + Value::Dimension(Some(n), ..) => n, + Value::Dimension(None, ..) => todo!(), Value::Null => { let mut rng = rand::thread_rng(); return Ok(Value::Dimension( - Number::from(rng.gen_range(0.0, 1.0)), + Some(Number::from(rng.gen_range(0.0, 1.0))), Unit::None, true, )); @@ -136,7 +142,7 @@ pub(crate) fn random(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult< }; if limit.is_one() { - return Ok(Value::Dimension(Number::one(), Unit::None, true)); + return Ok(Value::Dimension(Some(Number::one()), Unit::None, true)); } if limit.is_decimal() { @@ -164,7 +170,7 @@ pub(crate) fn random(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult< let mut rng = rand::thread_rng(); Ok(Value::Dimension( - Number::from(rng.gen_range(0, limit) + 1), + Some(Number::from(rng.gen_range(0, limit) + 1)), Unit::None, true, )) @@ -177,7 +183,8 @@ pub(crate) fn min(args: CallArgs, parser: &mut Parser<'_>) -> SassResult .get_variadic()? .into_iter() .map(|val| match val.node { - Value::Dimension(number, unit, _) => Ok((number, unit)), + Value::Dimension(Some(number), unit, _) => Ok((number, unit)), + Value::Dimension(None, ..) => todo!(), v => Err((format!("{} is not a number.", v.inspect(span)?), span).into()), }) .collect::>>()? @@ -190,12 +197,12 @@ pub(crate) fn min(args: CallArgs, parser: &mut Parser<'_>) -> SassResult if ValueVisitor::new(parser, span) .less_than( HigherIntermediateValue::Literal(Value::Dimension( - num.0.clone(), + Some(num.0.clone()), num.1.clone(), true, )), HigherIntermediateValue::Literal(Value::Dimension( - min.0.clone(), + Some(min.0.clone()), min.1.clone(), true, )), @@ -205,7 +212,7 @@ pub(crate) fn min(args: CallArgs, parser: &mut Parser<'_>) -> SassResult min = num; } } - Ok(Value::Dimension(min.0, min.1, true)) + Ok(Value::Dimension(Some(min.0), min.1, true)) } pub(crate) fn max(args: CallArgs, parser: &mut Parser<'_>) -> SassResult { @@ -215,7 +222,8 @@ pub(crate) fn max(args: CallArgs, parser: &mut Parser<'_>) -> SassResult .get_variadic()? .into_iter() .map(|val| match val.node { - Value::Dimension(number, unit, _) => Ok((number, unit)), + Value::Dimension(Some(number), unit, _) => Ok((number, unit)), + Value::Dimension(None, ..) => todo!(), v => Err((format!("{} is not a number.", v.inspect(span)?), span).into()), }) .collect::>>()? @@ -228,12 +236,12 @@ pub(crate) fn max(args: CallArgs, parser: &mut Parser<'_>) -> SassResult if ValueVisitor::new(parser, span) .greater_than( HigherIntermediateValue::Literal(Value::Dimension( - num.0.clone(), + Some(num.0.clone()), num.1.clone(), true, )), HigherIntermediateValue::Literal(Value::Dimension( - max.0.clone(), + Some(max.0.clone()), max.1.clone(), true, )), @@ -243,7 +251,7 @@ pub(crate) fn max(args: CallArgs, parser: &mut Parser<'_>) -> SassResult max = num; } } - Ok(Value::Dimension(max.0, max.1, true)) + Ok(Value::Dimension(Some(max.0), max.1, true)) } pub(crate) fn declare(f: &mut GlobalFunctionMap) { diff --git a/src/builtin/functions/string.rs b/src/builtin/functions/string.rs index 8e95059..edcbbd7 100644 --- a/src/builtin/functions/string.rs +++ b/src/builtin/functions/string.rs @@ -49,7 +49,7 @@ pub(crate) fn str_length(mut args: CallArgs, parser: &mut Parser<'_>) -> SassRes args.max_args(1)?; match args.get_err(0, "string")? { Value::String(i, _) => Ok(Value::Dimension( - Number::from(i.chars().count()), + Some(Number::from(i.chars().count())), Unit::None, true, )), @@ -99,17 +99,18 @@ pub(crate) fn str_slice(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResu }; let str_len = string.chars().count(); let start = match args.get_err(1, "start-at")? { - Value::Dimension(n, Unit::None, _) if n.is_decimal() => { + Value::Dimension(Some(n), Unit::None, _) if n.is_decimal() => { return Err((format!("{} is not an int.", n), args.span()).into()) } - Value::Dimension(n, Unit::None, _) if n.is_positive() => { + Value::Dimension(Some(n), Unit::None, _) if n.is_positive() => { n.to_integer().to_usize().unwrap_or(str_len + 1) } - Value::Dimension(n, Unit::None, _) if n.is_zero() => 1_usize, - Value::Dimension(n, Unit::None, _) if n < -Number::from(str_len) => 1_usize, - Value::Dimension(n, Unit::None, _) => (n.to_integer() + BigInt::from(str_len + 1)) + Value::Dimension(Some(n), Unit::None, _) if n.is_zero() => 1_usize, + Value::Dimension(Some(n), Unit::None, _) if n < -Number::from(str_len) => 1_usize, + Value::Dimension(Some(n), Unit::None, _) => (n.to_integer() + BigInt::from(str_len + 1)) .to_usize() .unwrap(), + Value::Dimension(None, ..) => todo!(), v @ Value::Dimension(..) => { return Err(( format!( @@ -129,17 +130,18 @@ pub(crate) fn str_slice(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResu } }; let mut end = match args.default_arg(2, "end-at", Value::Null)? { - Value::Dimension(n, Unit::None, _) if n.is_decimal() => { + Value::Dimension(Some(n), Unit::None, _) if n.is_decimal() => { return Err((format!("{} is not an int.", n), args.span()).into()) } - Value::Dimension(n, Unit::None, _) if n.is_positive() => { + Value::Dimension(Some(n), Unit::None, _) if n.is_positive() => { n.to_integer().to_usize().unwrap_or(str_len + 1) } - Value::Dimension(n, Unit::None, _) if n.is_zero() => 0_usize, - Value::Dimension(n, Unit::None, _) if n < -Number::from(str_len) => 0_usize, - Value::Dimension(n, Unit::None, _) => (n.to_integer() + BigInt::from(str_len + 1)) + Value::Dimension(Some(n), Unit::None, _) if n.is_zero() => 0_usize, + Value::Dimension(Some(n), Unit::None, _) if n < -Number::from(str_len) => 0_usize, + Value::Dimension(Some(n), Unit::None, _) => (n.to_integer() + BigInt::from(str_len + 1)) .to_usize() .unwrap_or(str_len + 1), + Value::Dimension(None, ..) => todo!(), v @ Value::Dimension(..) => { return Err(( format!( @@ -203,7 +205,7 @@ pub(crate) fn str_index(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResu }; Ok(match s1.find(&substr) { - Some(v) => Value::Dimension(Number::from(v + 1), Unit::None, true), + Some(v) => Value::Dimension(Some(Number::from(v + 1)), Unit::None, true), None => Value::Null, }) } @@ -233,10 +235,11 @@ pub(crate) fn str_insert(mut args: CallArgs, parser: &mut Parser<'_>) -> SassRes }; let index = match args.get_err(2, "index")? { - Value::Dimension(n, Unit::None, _) if n.is_decimal() => { + Value::Dimension(Some(n), Unit::None, _) if n.is_decimal() => { return Err((format!("$index: {} is not an int.", n), args.span()).into()) } - Value::Dimension(n, Unit::None, _) => n, + Value::Dimension(Some(n), Unit::None, _) => n, + Value::Dimension(None, ..) => todo!(), v @ Value::Dimension(..) => { return Err(( format!( diff --git a/src/builtin/modules/math.rs b/src/builtin/modules/math.rs index 533f6e9..fd55a8a 100644 --- a/src/builtin/modules/math.rs +++ b/src/builtin/modules/math.rs @@ -170,10 +170,10 @@ pub(crate) fn declare(f: &mut Module) { f.insert_builtin_var( "pi", - Value::Dimension(Number::from(std::f64::consts::PI), Unit::None, true), + Value::Dimension(Some(Number::from(std::f64::consts::PI)), Unit::None, true), ); f.insert_builtin_var( "e", - Value::Dimension(Number::from(std::f64::consts::E), Unit::None, true), + Value::Dimension(Some(Number::from(std::f64::consts::E)), Unit::None, true), ); } diff --git a/src/color/mod.rs b/src/color/mod.rs index 8c3da1a..af8d261 100644 --- a/src/color/mod.rs +++ b/src/color/mod.rs @@ -264,7 +264,7 @@ impl Color { return h.saturation() * Number::from(100); } - let red = self.red() / Number::from(255); + let red: Number = self.red() / Number::from(255); let green = self.green() / Number::from(255); let blue = self.blue() / Number::from(255); @@ -291,7 +291,7 @@ impl Color { return h.luminance() * Number::from(100); } - let red = self.red() / Number::from(255); + let red: Number = self.red() / Number::from(255); let green = self.green() / Number::from(255); let blue = self.blue() / Number::from(255); let min = min(&red, min(&green, &blue)).clone(); diff --git a/src/parse/control_flow.rs b/src/parse/control_flow.rs index 47d69c4..913ad2a 100644 --- a/src/parse/control_flow.rs +++ b/src/parse/control_flow.rs @@ -245,10 +245,11 @@ impl<'a> Parser<'a> { self.whitespace(); let from_val = self.parse_value_from_vec(from_toks, true)?; let from = match from_val.node { - Value::Dimension(n, ..) => match n.to_integer().to_isize() { + Value::Dimension(Some(n), ..) => match n.to_integer().to_isize() { Some(v) => v, None => return Err((format!("{} is not a int.", n), from_val.span).into()), }, + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!("{} is not an integer.", v.inspect(from_val.span)?), @@ -260,10 +261,11 @@ impl<'a> Parser<'a> { let to_val = self.parse_value(true)?; let to = match to_val.node { - Value::Dimension(n, ..) => match n.to_integer().to_isize() { + Value::Dimension(Some(n), ..) => match n.to_integer().to_isize() { Some(v) => v, None => return Err((format!("{} is not a int.", n), to_val.span).into()), }, + Value::Dimension(None, ..) => todo!(), v => { return Err(( format!("{} is not an integer.", v.to_css_string(to_val.span)?), @@ -303,7 +305,7 @@ impl<'a> Parser<'a> { self.scopes.insert_var_last( var.node, Spanned { - node: Value::Dimension(Number::from(i), Unit::None, true), + node: Value::Dimension(Some(Number::from(i)), Unit::None, true), span: var.span, }, ); diff --git a/src/parse/value/eval.rs b/src/parse/value/eval.rs index 736eed9..20ccfbd 100644 --- a/src/parse/value/eval.rs +++ b/src/parse/value/eval.rs @@ -3,6 +3,7 @@ use std::cmp::Ordering; use codemap::{Span, Spanned}; +use num_traits::Zero; use crate::{ args::CallArgs, @@ -119,7 +120,11 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> { fn unary_minus(&self, val: Value) -> SassResult { Ok(match val { - Value::Dimension(n, u, should_divide) => Value::Dimension(-n, u, should_divide), + Value::Dimension(Some(n), u, should_divide) => { + Value::Dimension(Some(-n), u, should_divide) + } + // todo: NaN test + Value::Dimension(None, u, should_divide) => Value::Dimension(None, u, should_divide), v => Value::String(format!("-{}", v.to_css_string(self.span)?), QuoteKind::None), }) } @@ -205,8 +210,10 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> { QuoteKind::None, ), }, - Value::Dimension(num, unit, _) => match right { - Value::Dimension(num2, unit2, _) => { + v @ Value::Dimension(None, ..) => v, + Value::Dimension(Some(num), unit, _) => match right { + v @ Value::Dimension(None, ..) => v, + Value::Dimension(Some(num2), unit2, _) => { if !unit.comparable(&unit2) { return Err(( format!("Incompatible units {} and {}.", unit2, unit), @@ -215,17 +222,19 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> { .into()); } if unit == unit2 { - Value::Dimension(num + num2, unit, true) + Value::Dimension(Some(num + num2), unit, true) } else if unit == Unit::None { - Value::Dimension(num + num2, unit2, true) + Value::Dimension(Some(num + num2), unit2, true) } else if unit2 == Unit::None { - Value::Dimension(num + num2, unit, true) + Value::Dimension(Some(num + num2), unit, true) } else { Value::Dimension( - num + num2 - * UNIT_CONVERSION_TABLE[unit.to_string().as_str()] - [unit2.to_string().as_str()] - .clone(), + Some( + num + num2 + * UNIT_CONVERSION_TABLE[unit.to_string().as_str()] + [unit2.to_string().as_str()] + .clone(), + ), unit, true, ) @@ -314,8 +323,10 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> { format!("-{}", right.to_css_string(self.span)?), QuoteKind::None, ), - Value::Dimension(num, unit, _) => match right { - Value::Dimension(num2, unit2, _) => { + Value::Dimension(None, ..) => todo!(), + Value::Dimension(Some(num), unit, _) => match right { + Value::Dimension(None, ..) => todo!(), + Value::Dimension(Some(num2), unit2, _) => { if !unit.comparable(&unit2) { return Err(( format!("Incompatible units {} and {}.", unit2, unit), @@ -324,17 +335,19 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> { .into()); } if unit == unit2 { - Value::Dimension(num - num2, unit, true) + Value::Dimension(Some(num - num2), unit, true) } else if unit == Unit::None { - Value::Dimension(num - num2, unit2, true) + Value::Dimension(Some(num - num2), unit2, true) } else if unit2 == Unit::None { - Value::Dimension(num - num2, unit, true) + Value::Dimension(Some(num - num2), unit, true) } else { Value::Dimension( - num - num2 - * UNIT_CONVERSION_TABLE[unit.to_string().as_str()] - [unit2.to_string().as_str()] - .clone(), + Some( + num - num2 + * UNIT_CONVERSION_TABLE[unit.to_string().as_str()] + [unit2.to_string().as_str()] + .clone(), + ), unit, true, ) @@ -434,14 +447,16 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> { v => panic!("{:?}", v), }; Ok(match left { - Value::Dimension(num, unit, _) => match right { - Value::Dimension(num2, unit2, _) => { + Value::Dimension(None, ..) => todo!(), + Value::Dimension(Some(num), unit, _) => match right { + Value::Dimension(None, ..) => todo!(), + Value::Dimension(Some(num2), unit2, _) => { if unit == Unit::None { - Value::Dimension(num * num2, unit2, true) + Value::Dimension(Some(num * num2), unit2, true) } else if unit2 == Unit::None { - Value::Dimension(num * num2, unit, true) + Value::Dimension(Some(num * num2), unit, true) } else { - Value::Dimension(num * num2, unit * unit2, true) + Value::Dimension(Some(num * num2), unit * unit2, true) } } _ => { @@ -490,28 +505,36 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> { format!("/{}", right.to_css_string(self.span)?), QuoteKind::None, ), - Value::Dimension(num, unit, should_divide1) => match right { - Value::Dimension(num2, unit2, should_divide2) => { + Value::Dimension(None, ..) => todo!(), + Value::Dimension(Some(num), unit, should_divide1) => match right { + Value::Dimension(None, ..) => todo!(), + Value::Dimension(Some(num2), unit2, should_divide2) => { if should_divide1 || should_divide2 || in_parens { + if num.is_zero() && num2.is_zero() { + return Ok(Value::Dimension(None, Unit::None, true)); + } + // `unit(1em / 1em)` => `""` if unit == unit2 { - Value::Dimension(num / num2, Unit::None, true) + Value::Dimension(Some(num / num2), Unit::None, true) // `unit(1 / 1em)` => `"em^-1"` } else if unit == Unit::None { - Value::Dimension(num / num2, Unit::None / unit2, true) + Value::Dimension(Some(num / num2), Unit::None / unit2, true) // `unit(1em / 1)` => `"em"` } else if unit2 == Unit::None { - Value::Dimension(num / num2, unit, true) + Value::Dimension(Some(num / num2), unit, true) // `unit(1in / 1px)` => `""` } else if unit.comparable(&unit2) { Value::Dimension( - num / (num2 - * UNIT_CONVERSION_TABLE[unit.to_string().as_str()] - [unit2.to_string().as_str()] - .clone()), + Some( + num / (num2 + * UNIT_CONVERSION_TABLE[unit.to_string().as_str()] + [unit2.to_string().as_str()] + .clone()), + ), Unit::None, true, ) @@ -630,28 +653,30 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> { v => panic!("{:?}", v), }; Ok(match left { - Value::Dimension(n, u, _) => match right { - Value::Dimension(n2, u2, _) => { + Value::Dimension(None, ..) => todo!(), + Value::Dimension(Some(n), u, _) => match right { + Value::Dimension(None, ..) => todo!(), + Value::Dimension(Some(n2), u2, _) => { if !u.comparable(&u2) { return Err( (format!("Incompatible units {} and {}.", u2, u), self.span).into() ); } if u == u2 { - Value::Dimension(n % n2, u, true) + Value::Dimension(Some(n % n2), u, true) } else if u == Unit::None { - Value::Dimension(n % n2, u2, true) + Value::Dimension(Some(n % n2), u2, true) } else if u2 == Unit::None { - Value::Dimension(n % n2, u, true) + Value::Dimension(Some(n % n2), u, true) } else { - Value::Dimension(n, u, true) + Value::Dimension(Some(n), u, true) } } _ => { return Err(( format!( "Undefined operation \"{} % {}\".", - Value::Dimension(n, u, true).inspect(self.span)?, + Value::Dimension(Some(n), u, true).inspect(self.span)?, right.inspect(self.span)? ), self.span, diff --git a/src/parse/value/parse.rs b/src/parse/value/parse.rs index be1ce79..795261c 100644 --- a/src/parse/value/parse.rs +++ b/src/parse/value/parse.rs @@ -459,7 +459,7 @@ impl<'a> Parser<'a> { let n = Rational64::new_raw(parse_i64(&val.num), 1); return Some(Ok(IntermediateValue::Value( HigherIntermediateValue::Literal(Value::Dimension( - Number::new_small(n), + Some(Number::new_small(n)), unit, false, )), @@ -472,7 +472,7 @@ impl<'a> Parser<'a> { let n = Rational64::new(parse_i64(&val.num), pow(10, val.dec_len)); return Some(Ok(IntermediateValue::Value( HigherIntermediateValue::Literal(Value::Dimension( - Number::new_small(n), + Some(Number::new_small(n)), unit, false, )), @@ -485,7 +485,7 @@ impl<'a> Parser<'a> { if val.times_ten.is_empty() { return Some(Ok(IntermediateValue::Value( HigherIntermediateValue::Literal(Value::Dimension( - Number::new_big(n), + Some(Number::new_big(n)), unit, false, )), @@ -514,7 +514,7 @@ impl<'a> Parser<'a> { }; IntermediateValue::Value(HigherIntermediateValue::Literal(Value::Dimension( - Number::new_big(n * times_ten), + Some(Number::new_big(n * times_ten)), unit, false, ))) diff --git a/src/value/mod.rs b/src/value/mod.rs index 7766b3f..4e50ac0 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -31,7 +31,8 @@ pub(crate) enum Value { True, False, Null, - Dimension(Number, Unit, bool), + /// A `None` value for `Number` indicates a `NaN` value + Dimension(Option, Unit, bool), List(Vec, ListSeparator, Brackets), Color(Box), String(String, QuoteKind), @@ -48,8 +49,8 @@ impl PartialEq for Value { Value::String(s2, ..) => s1 == s2, _ => false, }, - Value::Dimension(n, unit, _) => match other { - Value::Dimension(n2, unit2, _) => { + Value::Dimension(Some(n), unit, _) => match other { + Value::Dimension(Some(n2), unit2, _) => { if !unit.comparable(unit2) { false } else if unit == unit2 { @@ -65,6 +66,7 @@ impl PartialEq for Value { } _ => false, }, + Value::Dimension(None, ..) => false, Value::List(list1, sep1, brackets1) => match other { Value::List(list2, sep2, brackets2) => { if sep1 != sep2 || brackets1 != brackets2 || list1.len() != list2.len() { @@ -200,12 +202,13 @@ impl Value { pub fn to_css_string(&self, span: Span) -> SassResult> { Ok(match self { Value::Important => Cow::const_str("!important"), - Value::Dimension(num, unit, _) => match unit { + Value::Dimension(Some(num), unit, _) => match unit { Unit::Mul(..) | Unit::Div(..) => { return Err((format!("{}{} isn't a valid CSS value.", num, unit), span).into()); } _ => Cow::owned(format!("{}{}", num, unit)), }, + Value::Dimension(None, ..) => Cow::const_str("NaN"), Value::Map(..) | Value::FunctionRef(..) => { return Err(( format!("{} isn't a valid CSS value.", self.inspect(span)?), @@ -326,8 +329,10 @@ impl Value { pub fn cmp(&self, other: &Self, span: Span, op: Op) -> SassResult { Ok(match self { - Value::Dimension(num, unit, _) => match &other { - Value::Dimension(num2, unit2, _) => { + Value::Dimension(None, ..) => todo!(), + Value::Dimension(Some(num), unit, _) => match &other { + Value::Dimension(None, ..) => todo!(), + Value::Dimension(Some(num2), unit2, _) => { if !unit.comparable(unit2) { return Err( (format!("Incompatible units {} and {}.", unit2, unit), span).into(), @@ -387,8 +392,8 @@ impl Value { Value::String(s2, ..) => s1 != s2, _ => true, }, - Value::Dimension(n, unit, _) => match other { - Value::Dimension(n2, unit2, _) => { + Value::Dimension(Some(n), unit, _) => match other { + Value::Dimension(Some(n2), unit2, _) => { if !unit.comparable(unit2) { true } else if unit == unit2 { @@ -464,7 +469,8 @@ impl Value { .collect::>>()? .join(", ") )), - Value::Dimension(num, unit, _) => Cow::owned(format!("{}{}", num, unit)), + Value::Dimension(Some(num), unit, _) => Cow::owned(format!("{}{}", num, unit)), + Value::Dimension(None, ..) => Cow::const_str("NaN"), Value::ArgList(args) if args.is_empty() => Cow::const_str("()"), Value::ArgList(args) if args.len() == 1 => Cow::owned(format!( "({},)", diff --git a/tests/division.rs b/tests/division.rs index 1c02dce..ad7bcfb 100644 --- a/tests/division.rs +++ b/tests/division.rs @@ -169,3 +169,8 @@ test!( "a {\n color: 1 + 3 / 4;\n}\n", "a {\n color: 1.75;\n}\n" ); +test!( + zero_div_zero_is_nan, + "a {\n color: (0 / 0);\n}\n", + "a {\n color: NaN;\n}\n" +); diff --git a/tests/meta.rs b/tests/meta.rs index 6690635..56f4d4b 100644 --- a/tests/meta.rs +++ b/tests/meta.rs @@ -195,6 +195,11 @@ test!( "a {\n color: type-of(- 2)\n}\n", "a {\n color: number;\n}\n" ); +test!( + type_of_nan, + "a {\n color: type-of((0 / 0))\n}\n", + "a {\n color: number;\n}\n" +); test!( type_of_arglist, "@mixin foo($a...) {color: type-of($a);}\na {@include foo(1, 2, 3, 4, 5);}",