use None to represent NaN

This commit is contained in:
Connor Skees 2020-07-26 19:38:41 -04:00
parent dbfa691505
commit 53cf2816e0
15 changed files with 248 additions and 152 deletions

View File

@ -35,7 +35,8 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) ->
} }
let lightness = match channels.pop() { 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) => { Some(v) => {
return Err(( return Err((
format!("$lightness: {} is not a number.", v.inspect(args.span())?), 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() { 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) => { Some(v) => {
return Err(( return Err((
format!("$saturation: {} is not a number.", v.inspect(args.span())?), 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() { let hue = match channels.pop() {
Some(Value::Dimension(n, ..)) => n, Some(Value::Dimension(Some(n), ..)) => n,
Some(Value::Dimension(None, ..)) => todo!(),
Some(v) => { Some(v) => {
return Err(( return Err((
format!("$hue: {} is not a number.", v.inspect(args.span())?), 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 { } else {
let hue = match args.get_err(0, "hue")? { 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() => { v if v.is_special_function() => {
let saturation = args.get_err(1, "saturation")?; let saturation = args.get_err(1, "saturation")?;
let lightness = args.get_err(2, "lightness")?; 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")? { 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() => { v if v.is_special_function() => {
let lightness = args.get_err(2, "lightness")?; let lightness = args.get_err(2, "lightness")?;
let mut string = format!( 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")? { 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() => { v if v.is_special_function() => {
let mut string = format!( 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( let alpha = match args.default_arg(
3, 3,
"alpha", "alpha",
Value::Dimension(Number::one(), Unit::None, true), Value::Dimension(Some(Number::one()), Unit::None, true),
)? { )? {
Value::Dimension(n, Unit::None, _) => n, Value::Dimension(Some(n), Unit::None, _) => n,
Value::Dimension(n, Unit::Percent, _) => n / Number::from(100), Value::Dimension(Some(n), Unit::Percent, _) => n / Number::from(100),
Value::Dimension(None, ..) => todo!(),
v @ Value::Dimension(..) => { v @ Value::Dimension(..) => {
return Err(( return Err((
format!( format!(
@ -216,7 +223,7 @@ pub(crate) fn hsla(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
pub(crate) fn hue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { pub(crate) fn hue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "color")? { 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(( v => Err((
format!("$color: {} is not a color.", v.inspect(args.span())?), format!("$color: {} is not a color.", v.inspect(args.span())?),
args.span(), args.span(),
@ -228,7 +235,7 @@ pub(crate) fn hue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Val
pub(crate) fn saturation(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { pub(crate) fn saturation(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "color")? { 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(( v => Err((
format!("$color: {} is not a color.", v.inspect(args.span())?), format!("$color: {} is not a color.", v.inspect(args.span())?),
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<Value> { pub(crate) fn lightness(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "color")? { 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(( v => Err((
format!("$color: {} is not a color.", v.inspect(args.span())?), format!("$color: {} is not a color.", v.inspect(args.span())?),
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")? { let degrees = match args.get_err(1, "degrees")? {
Value::Dimension(n, ..) => n, Value::Dimension(Some(n), ..) => n,
Value::Dimension(None, ..) => todo!(),
v => { v => {
return Err(( return Err((
format!( format!(
@ -290,7 +298,8 @@ fn lighten(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
} }
}; };
let amount = match args.get_err(1, "amount")? { 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 => { v => {
return Err(( return Err((
format!( format!(
@ -318,7 +327,8 @@ fn darken(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
} }
}; };
let amount = match args.get_err(1, "amount")? { 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 => { v => {
return Err(( return Err((
format!( format!(
@ -346,7 +356,8 @@ fn saturate(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
} }
let amount = match args.get_err(1, "amount")? { 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 => { v => {
return Err(( return Err((
format!( format!(
@ -360,7 +371,7 @@ fn saturate(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
}; };
let color = match args.get_err(0, "color")? { let color = match args.get_err(0, "color")? {
Value::Color(c) => c, Value::Color(c) => c,
Value::Dimension(n, u, _) => { Value::Dimension(Some(n), u, _) => {
return Ok(Value::String( return Ok(Value::String(
format!("saturate({}{})", n, u), format!("saturate({}{})", n, u),
QuoteKind::None, QuoteKind::None,
@ -390,7 +401,8 @@ fn desaturate(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
} }
}; };
let amount = match args.get_err(1, "amount")? { 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 => { v => {
return Err(( return Err((
format!( format!(
@ -409,7 +421,7 @@ pub(crate) fn grayscale(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResu
args.max_args(1)?; args.max_args(1)?;
let color = match args.get_err(0, "color")? { let color = match args.get_err(0, "color")? {
Value::Color(c) => c, Value::Color(c) => c,
Value::Dimension(n, u, _) => { Value::Dimension(Some(n), u, _) => {
return Ok(Value::String( return Ok(Value::String(
format!("grayscale({}{})", n, u), format!("grayscale({}{})", n, u),
QuoteKind::None, QuoteKind::None,
@ -446,9 +458,10 @@ pub(crate) fn invert(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<
let weight = match args.default_arg( let weight = match args.default_arg(
1, 1,
"weight", "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 => { v => {
return Err(( return Err((
format!( format!(
@ -462,9 +475,10 @@ pub(crate) fn invert(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<
}; };
match args.get_err(0, "color")? { match args.get_err(0, "color")? {
Value::Color(c) => Ok(Value::Color(Box::new(c.invert(weight)))), 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)) Ok(Value::String(format!("invert({}%)", n), QuoteKind::None))
} }
Value::Dimension(None, ..) => todo!(),
Value::Dimension(..) => Err(( Value::Dimension(..) => Err((
"Only one argument may be passed to the plain-CSS invert() function.", "Only one argument may be passed to the plain-CSS invert() function.",
args.span(), args.span(),

View File

@ -8,7 +8,8 @@ use crate::{
pub(crate) fn alpha(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { pub(crate) fn alpha(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "color")? { 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(( v => Err((
format!("$color: {} is not a color.", v.inspect(args.span())?), format!("$color: {} is not a color.", v.inspect(args.span())?),
args.span(), args.span(),
@ -20,11 +21,12 @@ pub(crate) fn alpha(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<V
pub(crate) fn opacity(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { pub(crate) fn opacity(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "color")? { 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(num, unit, _) => Ok(Value::String( Value::Dimension(Some(num), unit, _) => Ok(Value::String(
format!("opacity({}{})", num, unit), format!("opacity({}{})", num, unit),
QuoteKind::None, QuoteKind::None,
)), )),
Value::Dimension(None, ..) => todo!(),
v => Err(( v => Err((
format!("$color: {} is not a color.", v.inspect(args.span())?), format!("$color: {} is not a color.", v.inspect(args.span())?),
args.span(), args.span(),
@ -47,7 +49,8 @@ fn opacify(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
} }
}; };
let amount = match args.get_err(1, "amount")? { 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 => { v => {
return Err(( return Err((
format!("$amount: {} is not a number.", v.inspect(args.span())?), format!("$amount: {} is not a number.", v.inspect(args.span())?),
@ -72,7 +75,8 @@ fn fade_in(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
} }
}; };
let amount = match args.get_err(1, "amount")? { 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 => { v => {
return Err(( return Err((
format!("$amount: {} is not a number.", v.inspect(args.span())?), format!("$amount: {} is not a number.", v.inspect(args.span())?),
@ -98,7 +102,8 @@ fn transparentize(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Val
} }
}; };
let amount = match args.get_err(1, "amount")? { 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 => { v => {
return Err(( return Err((
format!("$amount: {} is not a number.", v.inspect(args.span())?), format!("$amount: {} is not a number.", v.inspect(args.span())?),
@ -123,7 +128,8 @@ fn fade_out(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
} }
}; };
let amount = match args.get_err(1, "amount")? { 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 => { v => {
return Err(( return Err((
format!("$amount: {} is not a number.", v.inspect(args.span())?), format!("$amount: {} is not a number.", v.inspect(args.span())?),

View File

@ -15,7 +15,8 @@ use crate::{
macro_rules! opt_rgba { macro_rules! opt_rgba {
($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal) => { ($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal) => {
let $name = match $args.default_named_arg($arg, Value::Null)? { 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, Value::Null => None,
v => { v => {
return Err(( return Err((
@ -31,9 +32,10 @@ macro_rules! opt_rgba {
macro_rules! opt_hsl { macro_rules! opt_hsl {
($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal) => { ($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal) => {
let $name = match $args.default_named_arg($arg, Value::Null)? { 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)) Some(bound!($args, $arg, n, u, $low, $high) / Number::from(100))
} }
Value::Dimension(None, ..) => todo!(),
Value::Null => None, Value::Null => None,
v => { v => {
return Err(( 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)? { 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, Value::Null => None,
v => { v => {
return Err(( 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)? { 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, Value::Null => None,
v => { v => {
return Err(( return Err((
@ -198,9 +202,10 @@ pub(crate) fn scale_color(mut args: CallArgs, parser: &mut Parser<'_>) -> SassRe
macro_rules! opt_scale_arg { macro_rules! opt_scale_arg {
($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal) => { ($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal) => {
let $name = match $args.default_named_arg($arg, Value::Null)? { 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)) Some(bound!($args, $arg, n, Unit::Percent, $low, $high) / Number::from(100))
} }
Value::Dimension(None, ..) => todo!(),
v @ Value::Dimension(..) => { v @ Value::Dimension(..) => {
return Err(( return Err((
format!( format!(

View File

@ -38,10 +38,11 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) ->
} }
let blue = match channels.pop() { let blue = match channels.pop() {
Some(Value::Dimension(n, Unit::None, _)) => n, Some(Value::Dimension(Some(n), Unit::None, _)) => n,
Some(Value::Dimension(n, Unit::Percent, _)) => { Some(Value::Dimension(Some(n), Unit::Percent, _)) => {
(n / Number::from(100)) * Number::from(255) (n / Number::from(100)) * Number::from(255)
} }
Some(Value::Dimension(None, ..)) => todo!(),
Some(v) if v.is_special_function() => { Some(v) if v.is_special_function() => {
let green = channels.pop().unwrap(); let green = channels.pop().unwrap();
let red = 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() { let green = match channels.pop() {
Some(Value::Dimension(n, Unit::None, _)) => n, Some(Value::Dimension(Some(n), Unit::None, _)) => n,
Some(Value::Dimension(n, Unit::Percent, _)) => { Some(Value::Dimension(Some(n), Unit::Percent, _)) => {
(n / Number::from(100)) * Number::from(255) (n / Number::from(100)) * Number::from(255)
} }
Some(Value::Dimension(None, ..)) => todo!(),
Some(v) if v.is_special_function() => { Some(v) if v.is_special_function() => {
let string = match channels.pop() { let string = match channels.pop() {
Some(red) => format!( Some(red) => format!(
@ -95,10 +97,11 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) ->
}; };
let red = match channels.pop() { let red = match channels.pop() {
Some(Value::Dimension(n, Unit::None, _)) => n, Some(Value::Dimension(Some(n), Unit::None, _)) => n,
Some(Value::Dimension(n, Unit::Percent, _)) => { Some(Value::Dimension(Some(n), Unit::Percent, _)) => {
(n / Number::from(100)) * Number::from(255) (n / Number::from(100)) * Number::from(255)
} }
Some(Value::Dimension(None, ..)) => todo!(),
Some(v) if v.is_special_function() => { Some(v) if v.is_special_function() => {
return Ok(Value::String( return Ok(Value::String(
format!( 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")? { let alpha = match args.get_err(1, "alpha")? {
Value::Dimension(n, Unit::None, _) => n, Value::Dimension(Some(n), Unit::None, _) => n,
Value::Dimension(n, Unit::Percent, _) => n / Number::from(100), Value::Dimension(Some(n), Unit::Percent, _) => n / Number::from(100),
Value::Dimension(None, ..) => todo!(),
v @ Value::Dimension(..) => { v @ Value::Dimension(..) => {
return Err(( return Err((
format!( 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)))) Ok(Value::Color(Box::new(color.with_alpha(alpha))))
} else { } else {
let red = match args.get_err(0, "red")? { let red = match args.get_err(0, "red")? {
Value::Dimension(n, Unit::None, _) => n, Value::Dimension(Some(n), Unit::None, _) => n,
Value::Dimension(n, Unit::Percent, _) => (n / Number::from(100)) * Number::from(255), Value::Dimension(Some(n), Unit::Percent, _) => {
(n / Number::from(100)) * Number::from(255)
}
Value::Dimension(None, ..) => todo!(),
v @ Value::Dimension(..) => { v @ Value::Dimension(..) => {
return Err(( return Err((
format!( 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")? { let green = match args.get_err(1, "green")? {
Value::Dimension(n, Unit::None, _) => n, Value::Dimension(Some(n), Unit::None, _) => n,
Value::Dimension(n, Unit::Percent, _) => (n / Number::from(100)) * Number::from(255), Value::Dimension(Some(n), Unit::Percent, _) => {
(n / Number::from(100)) * Number::from(255)
}
Value::Dimension(None, ..) => todo!(),
v @ Value::Dimension(..) => { v @ Value::Dimension(..) => {
return Err(( return Err((
format!( 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")? { let blue = match args.get_err(2, "blue")? {
Value::Dimension(n, Unit::None, _) => n, Value::Dimension(Some(n), Unit::None, _) => n,
Value::Dimension(n, Unit::Percent, _) => (n / Number::from(100)) * Number::from(255), Value::Dimension(Some(n), Unit::Percent, _) => {
(n / Number::from(100)) * Number::from(255)
}
Value::Dimension(None, ..) => todo!(),
v @ Value::Dimension(..) => { v @ Value::Dimension(..) => {
return Err(( return Err((
format!( format!(
@ -297,10 +310,11 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) ->
let alpha = match args.default_arg( let alpha = match args.default_arg(
3, 3,
"alpha", "alpha",
Value::Dimension(Number::one(), Unit::None, true), Value::Dimension(Some(Number::one()), Unit::None, true),
)? { )? {
Value::Dimension(n, Unit::None, _) => n, Value::Dimension(Some(n), Unit::None, _) => n,
Value::Dimension(n, Unit::Percent, _) => n / Number::from(100), Value::Dimension(Some(n), Unit::Percent, _) => n / Number::from(100),
Value::Dimension(None, ..) => todo!(),
v @ Value::Dimension(..) => { v @ Value::Dimension(..) => {
return Err(( return Err((
format!( format!(
@ -347,7 +361,7 @@ pub(crate) fn rgba(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
pub(crate) fn red(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { pub(crate) fn red(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "color")? { 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(( v => Err((
format!("$color: {} is not a color.", v.inspect(args.span())?), format!("$color: {} is not a color.", v.inspect(args.span())?),
args.span(), args.span(),
@ -359,7 +373,7 @@ pub(crate) fn red(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Val
pub(crate) fn green(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { pub(crate) fn green(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "color")? { 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(( v => Err((
format!("$color: {} is not a color.", v.inspect(args.span())?), format!("$color: {} is not a color.", v.inspect(args.span())?),
args.span(), args.span(),
@ -371,7 +385,7 @@ pub(crate) fn green(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<V
pub(crate) fn blue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { pub(crate) fn blue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "color")? { 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(( v => Err((
format!("$color: {} is not a color.", v.inspect(args.span())?), format!("$color: {} is not a color.", v.inspect(args.span())?),
args.span(), args.span(),
@ -407,9 +421,10 @@ pub(crate) fn mix(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Val
let weight = match args.default_arg( let weight = match args.default_arg(
2, 2,
"weight", "weight",
Value::Dimension(Number::from(50), Unit::None, true), Value::Dimension(Some(Number::from(50)), Unit::None, 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 => { v => {
return Err(( return Err((
format!( format!(

View File

@ -14,7 +14,7 @@ use crate::{
pub(crate) fn length(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { pub(crate) fn length(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
Ok(Value::Dimension( 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, Unit::None,
true, true,
)) ))
@ -24,7 +24,8 @@ pub(crate) fn nth(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Val
args.max_args(2)?; args.max_args(2)?;
let mut list = args.get_err(0, "list")?.as_list(); let mut list = args.get_err(0, "list")?.as_list();
let n = match args.get_err(1, "n")? { let n = match args.get_err(1, "n")? {
Value::Dimension(num, ..) => num, Value::Dimension(Some(num), ..) => num,
Value::Dimension(None, ..) => todo!(),
v => { v => {
return Err(( return Err((
format!("$n: {} is not a number.", v.inspect(args.span())?), 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), v => (vec![v], ListSeparator::Space, Brackets::None),
}; };
let n = match args.get_err(1, "n")? { let n = match args.get_err(1, "n")? {
Value::Dimension(num, ..) => num, Value::Dimension(Some(num), ..) => num,
Value::Dimension(None, ..) => todo!(),
v => { v => {
return Err(( return Err((
format!("$n: {} is not a number.", v.inspect(args.span())?), 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<V
Some(v) => Number::from(v + 1), Some(v) => Number::from(v + 1),
None => return Ok(Value::Null), 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<Value> { pub(crate) fn zip(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {

View File

@ -16,7 +16,8 @@ use crate::{
pub(crate) fn percentage(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { pub(crate) fn percentage(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
let num = match args.get_err(0, "number")? { 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(..) => { v @ Value::Dimension(..) => {
return Err(( return Err((
format!( format!(
@ -35,13 +36,14 @@ pub(crate) fn percentage(mut args: CallArgs, parser: &mut Parser<'_>) -> SassRes
.into()) .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<Value> { pub(crate) fn round(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "number")? { 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(( v => Err((
format!("$number: {} is not a number.", v.inspect(args.span())?), format!("$number: {} is not a number.", v.inspect(args.span())?),
args.span(), args.span(),
@ -53,7 +55,8 @@ pub(crate) fn round(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<V
pub(crate) fn ceil(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { pub(crate) fn ceil(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "number")? { 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(( v => Err((
format!("$number: {} is not a number.", v.inspect(args.span())?), format!("$number: {} is not a number.", v.inspect(args.span())?),
args.span(), args.span(),
@ -65,7 +68,8 @@ pub(crate) fn ceil(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Va
pub(crate) fn floor(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { pub(crate) fn floor(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "number")? { 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(( v => Err((
format!("$number: {} is not a number.", v.inspect(args.span())?), format!("$number: {} is not a number.", v.inspect(args.span())?),
args.span(), args.span(),
@ -77,7 +81,8 @@ pub(crate) fn floor(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<V
pub(crate) fn abs(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> { pub(crate) fn abs(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "number")? { 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(( v => Err((
format!("$number: {} is not a number.", v.inspect(args.span())?), format!("$number: {} is not a number.", v.inspect(args.span())?),
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<Value> { pub(crate) fn random(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
let limit = match args.default_arg(0, "limit", Value::Null)? { 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 => { Value::Null => {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
return Ok(Value::Dimension( 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, Unit::None,
true, true,
)); ));
@ -136,7 +142,7 @@ pub(crate) fn random(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<
}; };
if limit.is_one() { 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() { 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(); let mut rng = rand::thread_rng();
Ok(Value::Dimension( Ok(Value::Dimension(
Number::from(rng.gen_range(0, limit) + 1), Some(Number::from(rng.gen_range(0, limit) + 1)),
Unit::None, Unit::None,
true, true,
)) ))
@ -177,7 +183,8 @@ pub(crate) fn min(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
.get_variadic()? .get_variadic()?
.into_iter() .into_iter()
.map(|val| match val.node { .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()), v => Err((format!("{} is not a number.", v.inspect(span)?), span).into()),
}) })
.collect::<SassResult<Vec<(Number, Unit)>>>()? .collect::<SassResult<Vec<(Number, Unit)>>>()?
@ -190,12 +197,12 @@ pub(crate) fn min(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
if ValueVisitor::new(parser, span) if ValueVisitor::new(parser, span)
.less_than( .less_than(
HigherIntermediateValue::Literal(Value::Dimension( HigherIntermediateValue::Literal(Value::Dimension(
num.0.clone(), Some(num.0.clone()),
num.1.clone(), num.1.clone(),
true, true,
)), )),
HigherIntermediateValue::Literal(Value::Dimension( HigherIntermediateValue::Literal(Value::Dimension(
min.0.clone(), Some(min.0.clone()),
min.1.clone(), min.1.clone(),
true, true,
)), )),
@ -205,7 +212,7 @@ pub(crate) fn min(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
min = num; 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<Value> { pub(crate) fn max(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
@ -215,7 +222,8 @@ pub(crate) fn max(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
.get_variadic()? .get_variadic()?
.into_iter() .into_iter()
.map(|val| match val.node { .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()), v => Err((format!("{} is not a number.", v.inspect(span)?), span).into()),
}) })
.collect::<SassResult<Vec<(Number, Unit)>>>()? .collect::<SassResult<Vec<(Number, Unit)>>>()?
@ -228,12 +236,12 @@ pub(crate) fn max(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
if ValueVisitor::new(parser, span) if ValueVisitor::new(parser, span)
.greater_than( .greater_than(
HigherIntermediateValue::Literal(Value::Dimension( HigherIntermediateValue::Literal(Value::Dimension(
num.0.clone(), Some(num.0.clone()),
num.1.clone(), num.1.clone(),
true, true,
)), )),
HigherIntermediateValue::Literal(Value::Dimension( HigherIntermediateValue::Literal(Value::Dimension(
max.0.clone(), Some(max.0.clone()),
max.1.clone(), max.1.clone(),
true, true,
)), )),
@ -243,7 +251,7 @@ pub(crate) fn max(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
max = num; 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) { pub(crate) fn declare(f: &mut GlobalFunctionMap) {

View File

@ -49,7 +49,7 @@ pub(crate) fn str_length(mut args: CallArgs, parser: &mut Parser<'_>) -> SassRes
args.max_args(1)?; args.max_args(1)?;
match args.get_err(0, "string")? { match args.get_err(0, "string")? {
Value::String(i, _) => Ok(Value::Dimension( Value::String(i, _) => Ok(Value::Dimension(
Number::from(i.chars().count()), Some(Number::from(i.chars().count())),
Unit::None, Unit::None,
true, true,
)), )),
@ -99,17 +99,18 @@ pub(crate) fn str_slice(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResu
}; };
let str_len = string.chars().count(); let str_len = string.chars().count();
let start = match args.get_err(1, "start-at")? { 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()) 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) n.to_integer().to_usize().unwrap_or(str_len + 1)
} }
Value::Dimension(n, Unit::None, _) if n.is_zero() => 1_usize, Value::Dimension(Some(n), Unit::None, _) if n.is_zero() => 1_usize,
Value::Dimension(n, Unit::None, _) if n < -Number::from(str_len) => 1_usize, Value::Dimension(Some(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, _) => (n.to_integer() + BigInt::from(str_len + 1))
.to_usize() .to_usize()
.unwrap(), .unwrap(),
Value::Dimension(None, ..) => todo!(),
v @ Value::Dimension(..) => { v @ Value::Dimension(..) => {
return Err(( return Err((
format!( 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)? { 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()) 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) n.to_integer().to_usize().unwrap_or(str_len + 1)
} }
Value::Dimension(n, Unit::None, _) if n.is_zero() => 0_usize, Value::Dimension(Some(n), Unit::None, _) if n.is_zero() => 0_usize,
Value::Dimension(n, Unit::None, _) if n < -Number::from(str_len) => 0_usize, Value::Dimension(Some(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, _) => (n.to_integer() + BigInt::from(str_len + 1))
.to_usize() .to_usize()
.unwrap_or(str_len + 1), .unwrap_or(str_len + 1),
Value::Dimension(None, ..) => todo!(),
v @ Value::Dimension(..) => { v @ Value::Dimension(..) => {
return Err(( return Err((
format!( format!(
@ -203,7 +205,7 @@ pub(crate) fn str_index(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResu
}; };
Ok(match s1.find(&substr) { 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, 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")? { 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()) 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(..) => { v @ Value::Dimension(..) => {
return Err(( return Err((
format!( format!(

View File

@ -170,10 +170,10 @@ pub(crate) fn declare(f: &mut Module) {
f.insert_builtin_var( f.insert_builtin_var(
"pi", "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( f.insert_builtin_var(
"e", "e",
Value::Dimension(Number::from(std::f64::consts::E), Unit::None, true), Value::Dimension(Some(Number::from(std::f64::consts::E)), Unit::None, true),
); );
} }

View File

@ -264,7 +264,7 @@ impl Color {
return h.saturation() * Number::from(100); 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 green = self.green() / Number::from(255);
let blue = self.blue() / Number::from(255); let blue = self.blue() / Number::from(255);
@ -291,7 +291,7 @@ impl Color {
return h.luminance() * Number::from(100); 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 green = self.green() / Number::from(255);
let blue = self.blue() / Number::from(255); let blue = self.blue() / Number::from(255);
let min = min(&red, min(&green, &blue)).clone(); let min = min(&red, min(&green, &blue)).clone();

View File

@ -245,10 +245,11 @@ impl<'a> Parser<'a> {
self.whitespace(); self.whitespace();
let from_val = self.parse_value_from_vec(from_toks, true)?; let from_val = self.parse_value_from_vec(from_toks, true)?;
let from = match from_val.node { 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, Some(v) => v,
None => return Err((format!("{} is not a int.", n), from_val.span).into()), None => return Err((format!("{} is not a int.", n), from_val.span).into()),
}, },
Value::Dimension(None, ..) => todo!(),
v => { v => {
return Err(( return Err((
format!("{} is not an integer.", v.inspect(from_val.span)?), 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_val = self.parse_value(true)?;
let to = match to_val.node { 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, Some(v) => v,
None => return Err((format!("{} is not a int.", n), to_val.span).into()), None => return Err((format!("{} is not a int.", n), to_val.span).into()),
}, },
Value::Dimension(None, ..) => todo!(),
v => { v => {
return Err(( return Err((
format!("{} is not an integer.", v.to_css_string(to_val.span)?), 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( self.scopes.insert_var_last(
var.node, var.node,
Spanned { Spanned {
node: Value::Dimension(Number::from(i), Unit::None, true), node: Value::Dimension(Some(Number::from(i)), Unit::None, true),
span: var.span, span: var.span,
}, },
); );

View File

@ -3,6 +3,7 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use codemap::{Span, Spanned}; use codemap::{Span, Spanned};
use num_traits::Zero;
use crate::{ use crate::{
args::CallArgs, args::CallArgs,
@ -119,7 +120,11 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
fn unary_minus(&self, val: Value) -> SassResult<Value> { fn unary_minus(&self, val: Value) -> SassResult<Value> {
Ok(match val { 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), 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, QuoteKind::None,
), ),
}, },
Value::Dimension(num, unit, _) => match right { v @ Value::Dimension(None, ..) => v,
Value::Dimension(num2, unit2, _) => { Value::Dimension(Some(num), unit, _) => match right {
v @ Value::Dimension(None, ..) => v,
Value::Dimension(Some(num2), unit2, _) => {
if !unit.comparable(&unit2) { if !unit.comparable(&unit2) {
return Err(( return Err((
format!("Incompatible units {} and {}.", unit2, unit), format!("Incompatible units {} and {}.", unit2, unit),
@ -215,17 +222,19 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
.into()); .into());
} }
if unit == unit2 { if unit == unit2 {
Value::Dimension(num + num2, unit, true) Value::Dimension(Some(num + num2), unit, true)
} else if unit == Unit::None { } else if unit == Unit::None {
Value::Dimension(num + num2, unit2, true) Value::Dimension(Some(num + num2), unit2, true)
} else if unit2 == Unit::None { } else if unit2 == Unit::None {
Value::Dimension(num + num2, unit, true) Value::Dimension(Some(num + num2), unit, true)
} else { } else {
Value::Dimension( Value::Dimension(
num + num2 Some(
* UNIT_CONVERSION_TABLE[unit.to_string().as_str()] num + num2
[unit2.to_string().as_str()] * UNIT_CONVERSION_TABLE[unit.to_string().as_str()]
.clone(), [unit2.to_string().as_str()]
.clone(),
),
unit, unit,
true, true,
) )
@ -314,8 +323,10 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
format!("-{}", right.to_css_string(self.span)?), format!("-{}", right.to_css_string(self.span)?),
QuoteKind::None, QuoteKind::None,
), ),
Value::Dimension(num, unit, _) => match right { Value::Dimension(None, ..) => todo!(),
Value::Dimension(num2, unit2, _) => { Value::Dimension(Some(num), unit, _) => match right {
Value::Dimension(None, ..) => todo!(),
Value::Dimension(Some(num2), unit2, _) => {
if !unit.comparable(&unit2) { if !unit.comparable(&unit2) {
return Err(( return Err((
format!("Incompatible units {} and {}.", unit2, unit), format!("Incompatible units {} and {}.", unit2, unit),
@ -324,17 +335,19 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
.into()); .into());
} }
if unit == unit2 { if unit == unit2 {
Value::Dimension(num - num2, unit, true) Value::Dimension(Some(num - num2), unit, true)
} else if unit == Unit::None { } else if unit == Unit::None {
Value::Dimension(num - num2, unit2, true) Value::Dimension(Some(num - num2), unit2, true)
} else if unit2 == Unit::None { } else if unit2 == Unit::None {
Value::Dimension(num - num2, unit, true) Value::Dimension(Some(num - num2), unit, true)
} else { } else {
Value::Dimension( Value::Dimension(
num - num2 Some(
* UNIT_CONVERSION_TABLE[unit.to_string().as_str()] num - num2
[unit2.to_string().as_str()] * UNIT_CONVERSION_TABLE[unit.to_string().as_str()]
.clone(), [unit2.to_string().as_str()]
.clone(),
),
unit, unit,
true, true,
) )
@ -434,14 +447,16 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
v => panic!("{:?}", v), v => panic!("{:?}", v),
}; };
Ok(match left { Ok(match left {
Value::Dimension(num, unit, _) => match right { Value::Dimension(None, ..) => todo!(),
Value::Dimension(num2, unit2, _) => { Value::Dimension(Some(num), unit, _) => match right {
Value::Dimension(None, ..) => todo!(),
Value::Dimension(Some(num2), unit2, _) => {
if unit == Unit::None { if unit == Unit::None {
Value::Dimension(num * num2, unit2, true) Value::Dimension(Some(num * num2), unit2, true)
} else if unit2 == Unit::None { } else if unit2 == Unit::None {
Value::Dimension(num * num2, unit, true) Value::Dimension(Some(num * num2), unit, true)
} else { } 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)?), format!("/{}", right.to_css_string(self.span)?),
QuoteKind::None, QuoteKind::None,
), ),
Value::Dimension(num, unit, should_divide1) => match right { Value::Dimension(None, ..) => todo!(),
Value::Dimension(num2, unit2, should_divide2) => { 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 should_divide1 || should_divide2 || in_parens {
if num.is_zero() && num2.is_zero() {
return Ok(Value::Dimension(None, Unit::None, true));
}
// `unit(1em / 1em)` => `""` // `unit(1em / 1em)` => `""`
if unit == unit2 { if unit == unit2 {
Value::Dimension(num / num2, Unit::None, true) Value::Dimension(Some(num / num2), Unit::None, true)
// `unit(1 / 1em)` => `"em^-1"` // `unit(1 / 1em)` => `"em^-1"`
} else if unit == Unit::None { } 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"` // `unit(1em / 1)` => `"em"`
} else if unit2 == Unit::None { } else if unit2 == Unit::None {
Value::Dimension(num / num2, unit, true) Value::Dimension(Some(num / num2), unit, true)
// `unit(1in / 1px)` => `""` // `unit(1in / 1px)` => `""`
} else if unit.comparable(&unit2) { } else if unit.comparable(&unit2) {
Value::Dimension( Value::Dimension(
num / (num2 Some(
* UNIT_CONVERSION_TABLE[unit.to_string().as_str()] num / (num2
[unit2.to_string().as_str()] * UNIT_CONVERSION_TABLE[unit.to_string().as_str()]
.clone()), [unit2.to_string().as_str()]
.clone()),
),
Unit::None, Unit::None,
true, true,
) )
@ -630,28 +653,30 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
v => panic!("{:?}", v), v => panic!("{:?}", v),
}; };
Ok(match left { Ok(match left {
Value::Dimension(n, u, _) => match right { Value::Dimension(None, ..) => todo!(),
Value::Dimension(n2, u2, _) => { Value::Dimension(Some(n), u, _) => match right {
Value::Dimension(None, ..) => todo!(),
Value::Dimension(Some(n2), u2, _) => {
if !u.comparable(&u2) { if !u.comparable(&u2) {
return Err( return Err(
(format!("Incompatible units {} and {}.", u2, u), self.span).into() (format!("Incompatible units {} and {}.", u2, u), self.span).into()
); );
} }
if u == u2 { if u == u2 {
Value::Dimension(n % n2, u, true) Value::Dimension(Some(n % n2), u, true)
} else if u == Unit::None { } else if u == Unit::None {
Value::Dimension(n % n2, u2, true) Value::Dimension(Some(n % n2), u2, true)
} else if u2 == Unit::None { } else if u2 == Unit::None {
Value::Dimension(n % n2, u, true) Value::Dimension(Some(n % n2), u, true)
} else { } else {
Value::Dimension(n, u, true) Value::Dimension(Some(n), u, true)
} }
} }
_ => { _ => {
return Err(( return Err((
format!( format!(
"Undefined operation \"{} % {}\".", "Undefined operation \"{} % {}\".",
Value::Dimension(n, u, true).inspect(self.span)?, Value::Dimension(Some(n), u, true).inspect(self.span)?,
right.inspect(self.span)? right.inspect(self.span)?
), ),
self.span, self.span,

View File

@ -459,7 +459,7 @@ impl<'a> Parser<'a> {
let n = Rational64::new_raw(parse_i64(&val.num), 1); let n = Rational64::new_raw(parse_i64(&val.num), 1);
return Some(Ok(IntermediateValue::Value( return Some(Ok(IntermediateValue::Value(
HigherIntermediateValue::Literal(Value::Dimension( HigherIntermediateValue::Literal(Value::Dimension(
Number::new_small(n), Some(Number::new_small(n)),
unit, unit,
false, false,
)), )),
@ -472,7 +472,7 @@ impl<'a> Parser<'a> {
let n = Rational64::new(parse_i64(&val.num), pow(10, val.dec_len)); let n = Rational64::new(parse_i64(&val.num), pow(10, val.dec_len));
return Some(Ok(IntermediateValue::Value( return Some(Ok(IntermediateValue::Value(
HigherIntermediateValue::Literal(Value::Dimension( HigherIntermediateValue::Literal(Value::Dimension(
Number::new_small(n), Some(Number::new_small(n)),
unit, unit,
false, false,
)), )),
@ -485,7 +485,7 @@ impl<'a> Parser<'a> {
if val.times_ten.is_empty() { if val.times_ten.is_empty() {
return Some(Ok(IntermediateValue::Value( return Some(Ok(IntermediateValue::Value(
HigherIntermediateValue::Literal(Value::Dimension( HigherIntermediateValue::Literal(Value::Dimension(
Number::new_big(n), Some(Number::new_big(n)),
unit, unit,
false, false,
)), )),
@ -514,7 +514,7 @@ impl<'a> Parser<'a> {
}; };
IntermediateValue::Value(HigherIntermediateValue::Literal(Value::Dimension( IntermediateValue::Value(HigherIntermediateValue::Literal(Value::Dimension(
Number::new_big(n * times_ten), Some(Number::new_big(n * times_ten)),
unit, unit,
false, false,
))) )))

View File

@ -31,7 +31,8 @@ pub(crate) enum Value {
True, True,
False, False,
Null, Null,
Dimension(Number, Unit, bool), /// A `None` value for `Number` indicates a `NaN` value
Dimension(Option<Number>, Unit, bool),
List(Vec<Value>, ListSeparator, Brackets), List(Vec<Value>, ListSeparator, Brackets),
Color(Box<Color>), Color(Box<Color>),
String(String, QuoteKind), String(String, QuoteKind),
@ -48,8 +49,8 @@ impl PartialEq for Value {
Value::String(s2, ..) => s1 == s2, Value::String(s2, ..) => s1 == s2,
_ => false, _ => false,
}, },
Value::Dimension(n, unit, _) => match other { Value::Dimension(Some(n), unit, _) => match other {
Value::Dimension(n2, unit2, _) => { Value::Dimension(Some(n2), unit2, _) => {
if !unit.comparable(unit2) { if !unit.comparable(unit2) {
false false
} else if unit == unit2 { } else if unit == unit2 {
@ -65,6 +66,7 @@ impl PartialEq for Value {
} }
_ => false, _ => false,
}, },
Value::Dimension(None, ..) => false,
Value::List(list1, sep1, brackets1) => match other { Value::List(list1, sep1, brackets1) => match other {
Value::List(list2, sep2, brackets2) => { Value::List(list2, sep2, brackets2) => {
if sep1 != sep2 || brackets1 != brackets2 || list1.len() != list2.len() { if sep1 != sep2 || brackets1 != brackets2 || list1.len() != list2.len() {
@ -200,12 +202,13 @@ impl Value {
pub fn to_css_string(&self, span: Span) -> SassResult<Cow<'static, str>> { pub fn to_css_string(&self, span: Span) -> SassResult<Cow<'static, str>> {
Ok(match self { Ok(match self {
Value::Important => Cow::const_str("!important"), Value::Important => Cow::const_str("!important"),
Value::Dimension(num, unit, _) => match unit { Value::Dimension(Some(num), unit, _) => match unit {
Unit::Mul(..) | Unit::Div(..) => { Unit::Mul(..) | Unit::Div(..) => {
return Err((format!("{}{} isn't a valid CSS value.", num, unit), span).into()); return Err((format!("{}{} isn't a valid CSS value.", num, unit), span).into());
} }
_ => Cow::owned(format!("{}{}", num, unit)), _ => Cow::owned(format!("{}{}", num, unit)),
}, },
Value::Dimension(None, ..) => Cow::const_str("NaN"),
Value::Map(..) | Value::FunctionRef(..) => { Value::Map(..) | Value::FunctionRef(..) => {
return Err(( return Err((
format!("{} isn't a valid CSS value.", self.inspect(span)?), 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<Ordering> { pub fn cmp(&self, other: &Self, span: Span, op: Op) -> SassResult<Ordering> {
Ok(match self { Ok(match self {
Value::Dimension(num, unit, _) => match &other { Value::Dimension(None, ..) => todo!(),
Value::Dimension(num2, unit2, _) => { Value::Dimension(Some(num), unit, _) => match &other {
Value::Dimension(None, ..) => todo!(),
Value::Dimension(Some(num2), unit2, _) => {
if !unit.comparable(unit2) { if !unit.comparable(unit2) {
return Err( return Err(
(format!("Incompatible units {} and {}.", unit2, unit), span).into(), (format!("Incompatible units {} and {}.", unit2, unit), span).into(),
@ -387,8 +392,8 @@ impl Value {
Value::String(s2, ..) => s1 != s2, Value::String(s2, ..) => s1 != s2,
_ => true, _ => true,
}, },
Value::Dimension(n, unit, _) => match other { Value::Dimension(Some(n), unit, _) => match other {
Value::Dimension(n2, unit2, _) => { Value::Dimension(Some(n2), unit2, _) => {
if !unit.comparable(unit2) { if !unit.comparable(unit2) {
true true
} else if unit == unit2 { } else if unit == unit2 {
@ -464,7 +469,8 @@ impl Value {
.collect::<SassResult<Vec<String>>>()? .collect::<SassResult<Vec<String>>>()?
.join(", ") .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.is_empty() => Cow::const_str("()"),
Value::ArgList(args) if args.len() == 1 => Cow::owned(format!( Value::ArgList(args) if args.len() == 1 => Cow::owned(format!(
"({},)", "({},)",

View File

@ -169,3 +169,8 @@ test!(
"a {\n color: 1 + 3 / 4;\n}\n", "a {\n color: 1 + 3 / 4;\n}\n",
"a {\n color: 1.75;\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"
);

View File

@ -195,6 +195,11 @@ test!(
"a {\n color: type-of(- 2)\n}\n", "a {\n color: type-of(- 2)\n}\n",
"a {\n color: number;\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!( test!(
type_of_arglist, type_of_arglist,
"@mixin foo($a...) {color: type-of($a);}\na {@include foo(1, 2, 3, 4, 5);}", "@mixin foo($a...) {color: type-of($a);}\na {@include foo(1, 2, 3, 4, 5);}",