only evaluate division in certain cases
This commit is contained in:
parent
0639a6ba2b
commit
830d56bd77
@ -35,7 +35,7 @@ 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(n, ..)) => n / Number::from(100),
|
||||
Some(v) => {
|
||||
return Err((
|
||||
format!("$lightness: {} is not a number.", v.inspect(args.span())?),
|
||||
@ -47,7 +47,7 @@ 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(n, ..)) => n / Number::from(100),
|
||||
Some(v) => {
|
||||
return Err((
|
||||
format!("$saturation: {} is not a number.", v.inspect(args.span())?),
|
||||
@ -59,7 +59,7 @@ 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(n, ..)) => n,
|
||||
Some(v) => {
|
||||
return Err((
|
||||
format!("$hue: {} is not a number.", v.inspect(args.span())?),
|
||||
@ -78,7 +78,7 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) ->
|
||||
))))
|
||||
} else {
|
||||
let hue = match parser.arg(&mut args, 0, "hue")? {
|
||||
Value::Dimension(n, _) => n,
|
||||
Value::Dimension(n, ..) => n,
|
||||
v if v.is_special_function() => {
|
||||
let saturation = parser.arg(&mut args, 1, "saturation")?;
|
||||
let lightness = parser.arg(&mut args, 2, "lightness")?;
|
||||
@ -109,7 +109,7 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) ->
|
||||
}
|
||||
};
|
||||
let saturation = match parser.arg(&mut args, 1, "saturation")? {
|
||||
Value::Dimension(n, _) => n / Number::from(100),
|
||||
Value::Dimension(n, ..) => n / Number::from(100),
|
||||
v if v.is_special_function() => {
|
||||
let lightness = parser.arg(&mut args, 2, "lightness")?;
|
||||
let mut string = format!(
|
||||
@ -142,7 +142,7 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) ->
|
||||
}
|
||||
};
|
||||
let lightness = match parser.arg(&mut args, 2, "lightness")? {
|
||||
Value::Dimension(n, _) => n / Number::from(100),
|
||||
Value::Dimension(n, ..) => n / Number::from(100),
|
||||
v if v.is_special_function() => {
|
||||
let mut string = format!(
|
||||
"{}({}, {}, {}",
|
||||
@ -177,10 +177,10 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) ->
|
||||
&mut args,
|
||||
3,
|
||||
"alpha",
|
||||
Value::Dimension(Number::one(), Unit::None),
|
||||
Value::Dimension(Number::one(), Unit::None, true),
|
||||
)? {
|
||||
Value::Dimension(n, Unit::None) => n,
|
||||
Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
||||
Value::Dimension(n, Unit::None, _) => n,
|
||||
Value::Dimension(n, Unit::Percent, _) => n / Number::from(100),
|
||||
v @ Value::Dimension(..) => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -229,7 +229,7 @@ fn hsla(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
fn hue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match parser.arg(&mut args, 0, "color")? {
|
||||
Value::Color(c) => Ok(Value::Dimension(c.hue(), Unit::Deg)),
|
||||
Value::Color(c) => Ok(Value::Dimension(c.hue(), Unit::Deg, true)),
|
||||
v => Err((
|
||||
format!("$color: {} is not a color.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
@ -241,7 +241,7 @@ fn hue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
fn saturation(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match parser.arg(&mut args, 0, "color")? {
|
||||
Value::Color(c) => Ok(Value::Dimension(c.saturation(), Unit::Percent)),
|
||||
Value::Color(c) => Ok(Value::Dimension(c.saturation(), Unit::Percent, true)),
|
||||
v => Err((
|
||||
format!("$color: {} is not a color.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
@ -253,7 +253,7 @@ fn saturation(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
|
||||
fn lightness(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match parser.arg(&mut args, 0, "color")? {
|
||||
Value::Color(c) => Ok(Value::Dimension(c.lightness(), Unit::Percent)),
|
||||
Value::Color(c) => Ok(Value::Dimension(c.lightness(), Unit::Percent, true)),
|
||||
v => Err((
|
||||
format!("$color: {} is not a color.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
@ -275,7 +275,7 @@ fn adjust_hue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
|
||||
}
|
||||
};
|
||||
let degrees = match parser.arg(&mut args, 1, "degrees")? {
|
||||
Value::Dimension(n, _) => n,
|
||||
Value::Dimension(n, ..) => n,
|
||||
v => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -303,7 +303,7 @@ fn lighten(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
}
|
||||
};
|
||||
let amount = match parser.arg(&mut args, 1, "amount")? {
|
||||
Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 100) / Number::from(100),
|
||||
Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 100) / Number::from(100),
|
||||
v => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -331,7 +331,7 @@ fn darken(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
}
|
||||
};
|
||||
let amount = match parser.arg(&mut args, 1, "amount")? {
|
||||
Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 100) / Number::from(100),
|
||||
Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 100) / Number::from(100),
|
||||
v => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -361,7 +361,7 @@ fn saturate(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
}
|
||||
|
||||
let amount = match parser.arg(&mut args, 1, "amount")? {
|
||||
Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 100) / Number::from(100),
|
||||
Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 100) / Number::from(100),
|
||||
v => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -375,7 +375,7 @@ fn saturate(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
};
|
||||
let color = match parser.arg(&mut args, 0, "color")? {
|
||||
Value::Color(c) => c,
|
||||
Value::Dimension(n, u) => {
|
||||
Value::Dimension(n, u, _) => {
|
||||
return Ok(Value::String(
|
||||
format!("saturate({}{})", n, u),
|
||||
QuoteKind::None,
|
||||
@ -405,7 +405,7 @@ fn desaturate(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
|
||||
}
|
||||
};
|
||||
let amount = match parser.arg(&mut args, 1, "amount")? {
|
||||
Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 100) / Number::from(100),
|
||||
Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 100) / Number::from(100),
|
||||
v => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -424,7 +424,7 @@ fn grayscale(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
let color = match parser.arg(&mut args, 0, "color")? {
|
||||
Value::Color(c) => c,
|
||||
Value::Dimension(n, u) => {
|
||||
Value::Dimension(n, u, _) => {
|
||||
return Ok(Value::String(
|
||||
format!("grayscale({}{})", n, u),
|
||||
QuoteKind::None,
|
||||
@ -462,9 +462,9 @@ fn invert(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
&mut args,
|
||||
1,
|
||||
"weight",
|
||||
Value::Dimension(Number::from(100), Unit::Percent),
|
||||
Value::Dimension(Number::from(100), Unit::Percent, true),
|
||||
)? {
|
||||
Value::Dimension(n, u) => bound!(args, "weight", n, u, 0, 100) / Number::from(100),
|
||||
Value::Dimension(n, u, _) => bound!(args, "weight", n, u, 0, 100) / Number::from(100),
|
||||
v => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -478,7 +478,7 @@ fn invert(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
};
|
||||
match parser.arg(&mut args, 0, "color")? {
|
||||
Value::Color(c) => Ok(Value::Color(Box::new(c.invert(weight)))),
|
||||
Value::Dimension(n, Unit::Percent) => {
|
||||
Value::Dimension(n, Unit::Percent, _) => {
|
||||
Ok(Value::String(format!("invert({}%)", n), QuoteKind::None))
|
||||
}
|
||||
Value::Dimension(..) => Err((
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
fn alpha(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match parser.arg(&mut args, 0, "color")? {
|
||||
Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None)),
|
||||
Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None, true)),
|
||||
v => Err((
|
||||
format!("$color: {} is not a color.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
@ -20,8 +20,8 @@ fn alpha(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
fn opacity(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match parser.arg(&mut args, 0, "color")? {
|
||||
Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None)),
|
||||
Value::Dimension(num, unit) => Ok(Value::String(
|
||||
Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None, true)),
|
||||
Value::Dimension(num, unit, _) => Ok(Value::String(
|
||||
format!("opacity({}{})", num, unit),
|
||||
QuoteKind::None,
|
||||
)),
|
||||
@ -46,7 +46,7 @@ fn opacify(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
}
|
||||
};
|
||||
let amount = match parser.arg(&mut args, 1, "amount")? {
|
||||
Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 1),
|
||||
Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 1),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$amount: {} is not a number.", v.inspect(args.span())?),
|
||||
@ -71,7 +71,7 @@ fn fade_in(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
}
|
||||
};
|
||||
let amount = match parser.arg(&mut args, 1, "amount")? {
|
||||
Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 1),
|
||||
Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 1),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$amount: {} is not a number.", v.inspect(args.span())?),
|
||||
@ -96,7 +96,7 @@ fn transparentize(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Val
|
||||
}
|
||||
};
|
||||
let amount = match parser.arg(&mut args, 1, "amount")? {
|
||||
Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 1),
|
||||
Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 1),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$amount: {} is not a number.", v.inspect(args.span())?),
|
||||
@ -121,7 +121,7 @@ fn fade_out(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
}
|
||||
};
|
||||
let amount = match parser.arg(&mut args, 1, "amount")? {
|
||||
Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 1),
|
||||
Value::Dimension(n, u, _) => bound!(args, "amount", n, u, 0, 1),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$amount: {} is not a number.", v.inspect(args.span())?),
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
||||
macro_rules! opt_rgba {
|
||||
($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal, $parser:ident) => {
|
||||
let $name = match $parser.default_named_arg(&mut $args, $arg, Value::Null)? {
|
||||
Value::Dimension(n, u) => Some(bound!($args, $arg, n, u, $low, $high)),
|
||||
Value::Dimension(n, u, _) => Some(bound!($args, $arg, n, u, $low, $high)),
|
||||
Value::Null => None,
|
||||
v => {
|
||||
return Err((
|
||||
@ -31,7 +31,7 @@ macro_rules! opt_rgba {
|
||||
macro_rules! opt_hsl {
|
||||
($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal, $parser:ident) => {
|
||||
let $name = match $parser.default_named_arg(&mut $args, $arg, Value::Null)? {
|
||||
Value::Dimension(n, u) => {
|
||||
Value::Dimension(n, u, _) => {
|
||||
Some(bound!($args, $arg, n, u, $low, $high) / Number::from(100))
|
||||
}
|
||||
Value::Null => None,
|
||||
@ -81,7 +81,7 @@ fn change_color(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value
|
||||
}
|
||||
|
||||
let hue = match parser.default_named_arg(&mut args, "hue", Value::Null)? {
|
||||
Value::Dimension(n, _) => Some(n),
|
||||
Value::Dimension(n, ..) => Some(n),
|
||||
Value::Null => None,
|
||||
v => {
|
||||
return Err((
|
||||
@ -140,7 +140,7 @@ fn adjust_color(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value
|
||||
}
|
||||
|
||||
let hue = match parser.default_named_arg(&mut args, "hue", Value::Null)? {
|
||||
Value::Dimension(n, _) => Some(n),
|
||||
Value::Dimension(n, ..) => Some(n),
|
||||
Value::Null => None,
|
||||
v => {
|
||||
return Err((
|
||||
@ -198,7 +198,7 @@ fn scale_color(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
|
||||
macro_rules! opt_scale_arg {
|
||||
($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal, $parser:ident) => {
|
||||
let $name = match $parser.default_named_arg(&mut $args, $arg, Value::Null)? {
|
||||
Value::Dimension(n, Unit::Percent) => {
|
||||
Value::Dimension(n, Unit::Percent, _) => {
|
||||
Some(bound!($args, $arg, n, Unit::Percent, $low, $high) / Number::from(100))
|
||||
}
|
||||
v @ Value::Dimension(..) => {
|
||||
|
@ -38,8 +38,10 @@ 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)) => (n / Number::from(100)) * Number::from(255),
|
||||
Some(Value::Dimension(n, Unit::None, _)) => n,
|
||||
Some(Value::Dimension(n, Unit::Percent, _)) => {
|
||||
(n / Number::from(100)) * Number::from(255)
|
||||
}
|
||||
Some(v) if v.is_special_function() => {
|
||||
let green = channels.pop().unwrap();
|
||||
let red = channels.pop().unwrap();
|
||||
@ -65,8 +67,10 @@ 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)) => (n / Number::from(100)) * Number::from(255),
|
||||
Some(Value::Dimension(n, Unit::None, _)) => n,
|
||||
Some(Value::Dimension(n, Unit::Percent, _)) => {
|
||||
(n / Number::from(100)) * Number::from(255)
|
||||
}
|
||||
Some(v) if v.is_special_function() => {
|
||||
let string = match channels.pop() {
|
||||
Some(red) => format!(
|
||||
@ -91,8 +95,10 @@ 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)) => (n / Number::from(100)) * Number::from(255),
|
||||
Some(Value::Dimension(n, Unit::None, _)) => n,
|
||||
Some(Value::Dimension(n, Unit::Percent, _)) => {
|
||||
(n / Number::from(100)) * Number::from(255)
|
||||
}
|
||||
Some(v) if v.is_special_function() => {
|
||||
return Ok(Value::String(
|
||||
format!(
|
||||
@ -142,8 +148,8 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) ->
|
||||
}
|
||||
};
|
||||
let alpha = match parser.arg(&mut args, 1, "alpha")? {
|
||||
Value::Dimension(n, Unit::None) => n,
|
||||
Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
||||
Value::Dimension(n, Unit::None, _) => n,
|
||||
Value::Dimension(n, Unit::Percent, _) => n / Number::from(100),
|
||||
v @ Value::Dimension(..) => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -178,8 +184,8 @@ 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 parser.arg(&mut args, 0, "red")? {
|
||||
Value::Dimension(n, Unit::None) => n,
|
||||
Value::Dimension(n, Unit::Percent) => (n / Number::from(100)) * Number::from(255),
|
||||
Value::Dimension(n, Unit::None, _) => n,
|
||||
Value::Dimension(n, Unit::Percent, _) => (n / Number::from(100)) * Number::from(255),
|
||||
v @ Value::Dimension(..) => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -220,8 +226,8 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) ->
|
||||
}
|
||||
};
|
||||
let green = match parser.arg(&mut args, 1, "green")? {
|
||||
Value::Dimension(n, Unit::None) => n,
|
||||
Value::Dimension(n, Unit::Percent) => (n / Number::from(100)) * Number::from(255),
|
||||
Value::Dimension(n, Unit::None, _) => n,
|
||||
Value::Dimension(n, Unit::Percent, _) => (n / Number::from(100)) * Number::from(255),
|
||||
v @ Value::Dimension(..) => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -261,8 +267,8 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) ->
|
||||
}
|
||||
};
|
||||
let blue = match parser.arg(&mut args, 2, "blue")? {
|
||||
Value::Dimension(n, Unit::None) => n,
|
||||
Value::Dimension(n, Unit::Percent) => (n / Number::from(100)) * Number::from(255),
|
||||
Value::Dimension(n, Unit::None, _) => n,
|
||||
Value::Dimension(n, Unit::Percent, _) => (n / Number::from(100)) * Number::from(255),
|
||||
v @ Value::Dimension(..) => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -304,10 +310,10 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) ->
|
||||
&mut args,
|
||||
3,
|
||||
"alpha",
|
||||
Value::Dimension(Number::one(), Unit::None),
|
||||
Value::Dimension(Number::one(), Unit::None, true),
|
||||
)? {
|
||||
Value::Dimension(n, Unit::None) => n,
|
||||
Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
||||
Value::Dimension(n, Unit::None, _) => n,
|
||||
Value::Dimension(n, Unit::Percent, _) => n / Number::from(100),
|
||||
v @ Value::Dimension(..) => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -354,7 +360,7 @@ fn rgba(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
fn red(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match parser.arg(&mut args, 0, "color")? {
|
||||
Value::Color(c) => Ok(Value::Dimension(c.red(), Unit::None)),
|
||||
Value::Color(c) => Ok(Value::Dimension(c.red(), Unit::None, true)),
|
||||
v => Err((
|
||||
format!("$color: {} is not a color.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
@ -366,7 +372,7 @@ fn red(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
fn green(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match parser.arg(&mut args, 0, "color")? {
|
||||
Value::Color(c) => Ok(Value::Dimension(c.green(), Unit::None)),
|
||||
Value::Color(c) => Ok(Value::Dimension(c.green(), Unit::None, true)),
|
||||
v => Err((
|
||||
format!("$color: {} is not a color.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
@ -378,7 +384,7 @@ fn green(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
fn blue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match parser.arg(&mut args, 0, "color")? {
|
||||
Value::Color(c) => Ok(Value::Dimension(c.blue(), Unit::None)),
|
||||
Value::Color(c) => Ok(Value::Dimension(c.blue(), Unit::None, true)),
|
||||
v => Err((
|
||||
format!("$color: {} is not a color.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
@ -415,9 +421,9 @@ fn mix(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
&mut args,
|
||||
2,
|
||||
"weight",
|
||||
Value::Dimension(Number::from(50), Unit::None),
|
||||
Value::Dimension(Number::from(50), Unit::None, true),
|
||||
)? {
|
||||
Value::Dimension(n, u) => bound!(args, "weight", n, u, 0, 100) / Number::from(100),
|
||||
Value::Dimension(n, u, _) => bound!(args, "weight", n, u, 0, 100) / Number::from(100),
|
||||
v => {
|
||||
return Err((
|
||||
format!(
|
||||
|
@ -16,6 +16,7 @@ fn length(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
Ok(Value::Dimension(
|
||||
Number::from(parser.arg(&mut args, 0, "list")?.as_list().len()),
|
||||
Unit::None,
|
||||
true,
|
||||
))
|
||||
}
|
||||
|
||||
@ -23,7 +24,7 @@ fn nth(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(2)?;
|
||||
let mut list = parser.arg(&mut args, 0, "list")?.as_list();
|
||||
let n = match parser.arg(&mut args, 1, "n")? {
|
||||
Value::Dimension(num, _) => num,
|
||||
Value::Dimension(num, ..) => num,
|
||||
v => {
|
||||
return Err((
|
||||
format!("$n: {} is not a number.", v.inspect(args.span())?),
|
||||
@ -80,7 +81,7 @@ fn set_nth(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
v => (vec![v], ListSeparator::Space, Brackets::None),
|
||||
};
|
||||
let n = match parser.arg(&mut args, 1, "n")? {
|
||||
Value::Dimension(num, _) => num,
|
||||
Value::Dimension(num, ..) => num,
|
||||
v => {
|
||||
return Err((
|
||||
format!("$n: {} is not a number.", v.inspect(args.span())?),
|
||||
@ -248,7 +249,7 @@ fn index(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
Some(v) => Number::from(v + 1),
|
||||
None => return Ok(Value::Null),
|
||||
};
|
||||
Ok(Value::Dimension(index, Unit::None))
|
||||
Ok(Value::Dimension(index, Unit::None, true))
|
||||
}
|
||||
|
||||
fn zip(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
|
@ -16,7 +16,7 @@ use crate::{
|
||||
fn percentage(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
let num = match parser.arg(&mut args, 0, "number")? {
|
||||
Value::Dimension(n, Unit::None) => n * Number::from(100),
|
||||
Value::Dimension(n, Unit::None, _) => n * Number::from(100),
|
||||
v @ Value::Dimension(..) => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -35,13 +35,13 @@ fn percentage(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
|
||||
.into())
|
||||
}
|
||||
};
|
||||
Ok(Value::Dimension(num, Unit::Percent))
|
||||
Ok(Value::Dimension(num, Unit::Percent, true))
|
||||
}
|
||||
|
||||
fn round(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match parser.arg(&mut args, 0, "number")? {
|
||||
Value::Dimension(n, u) => Ok(Value::Dimension(n.round(), u)),
|
||||
Value::Dimension(n, u, _) => Ok(Value::Dimension(n.round(), u, true)),
|
||||
v => Err((
|
||||
format!("$number: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
@ -53,7 +53,7 @@ fn round(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
fn ceil(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match parser.arg(&mut args, 0, "number")? {
|
||||
Value::Dimension(n, u) => Ok(Value::Dimension(n.ceil(), u)),
|
||||
Value::Dimension(n, u, _) => Ok(Value::Dimension(n.ceil(), u, true)),
|
||||
v => Err((
|
||||
format!("$number: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
@ -65,7 +65,7 @@ fn ceil(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
fn floor(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match parser.arg(&mut args, 0, "number")? {
|
||||
Value::Dimension(n, u) => Ok(Value::Dimension(n.floor(), u)),
|
||||
Value::Dimension(n, u, _) => Ok(Value::Dimension(n.floor(), u, true)),
|
||||
v => Err((
|
||||
format!("$number: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
@ -77,7 +77,7 @@ fn floor(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
fn abs(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match parser.arg(&mut args, 0, "number")? {
|
||||
Value::Dimension(n, u) => Ok(Value::Dimension(n.abs(), u)),
|
||||
Value::Dimension(n, u, _) => Ok(Value::Dimension(n.abs(), u, true)),
|
||||
v => Err((
|
||||
format!("$number: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
@ -89,7 +89,7 @@ fn abs(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
fn comparable(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(2)?;
|
||||
let unit1 = match parser.arg(&mut args, 0, "number1")? {
|
||||
Value::Dimension(_, u) => u,
|
||||
Value::Dimension(_, u, _) => u,
|
||||
v => {
|
||||
return Err((
|
||||
format!("$number1: {} is not a number.", v.inspect(args.span())?),
|
||||
@ -99,7 +99,7 @@ fn comparable(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
|
||||
}
|
||||
};
|
||||
let unit2 = match parser.arg(&mut args, 1, "number2")? {
|
||||
Value::Dimension(_, u) => u,
|
||||
Value::Dimension(_, u, _) => u,
|
||||
v => {
|
||||
return Err((
|
||||
format!("$number2: {} is not a number.", v.inspect(args.span())?),
|
||||
@ -117,12 +117,13 @@ fn comparable(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
|
||||
fn random(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
let limit = match parser.default_arg(&mut args, 0, "limit", Value::Null)? {
|
||||
Value::Dimension(n, _) => n,
|
||||
Value::Dimension(n, ..) => n,
|
||||
Value::Null => {
|
||||
let mut rng = rand::thread_rng();
|
||||
return Ok(Value::Dimension(
|
||||
Number::from(rng.gen_range(0.0, 1.0)),
|
||||
Unit::None,
|
||||
true,
|
||||
));
|
||||
}
|
||||
v => {
|
||||
@ -135,7 +136,7 @@ fn random(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
};
|
||||
|
||||
if limit.is_one() {
|
||||
return Ok(Value::Dimension(Number::one(), Unit::None));
|
||||
return Ok(Value::Dimension(Number::one(), Unit::None, true));
|
||||
}
|
||||
|
||||
if limit.is_decimal() {
|
||||
@ -165,6 +166,7 @@ fn random(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
Ok(Value::Dimension(
|
||||
Number::from(rng.gen_range(0, limit) + 1),
|
||||
Unit::None,
|
||||
true,
|
||||
))
|
||||
}
|
||||
|
||||
@ -175,7 +177,7 @@ fn min(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
.variadic_args(args)?
|
||||
.into_iter()
|
||||
.map(|val| match val.node {
|
||||
Value::Dimension(number, unit) => Ok((number, unit)),
|
||||
Value::Dimension(number, unit, _) => Ok((number, unit)),
|
||||
v => Err((format!("{} is not a number.", v.inspect(span)?), span).into()),
|
||||
})
|
||||
.collect::<SassResult<Vec<(Number, Unit)>>>()?
|
||||
@ -187,15 +189,23 @@ fn min(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
for num in nums {
|
||||
if ValueVisitor::new(parser, span)
|
||||
.less_than(
|
||||
HigherIntermediateValue::Literal(Value::Dimension(num.0.clone(), num.1.clone())),
|
||||
HigherIntermediateValue::Literal(Value::Dimension(min.0.clone(), min.1.clone())),
|
||||
HigherIntermediateValue::Literal(Value::Dimension(
|
||||
num.0.clone(),
|
||||
num.1.clone(),
|
||||
true,
|
||||
)),
|
||||
HigherIntermediateValue::Literal(Value::Dimension(
|
||||
min.0.clone(),
|
||||
min.1.clone(),
|
||||
true,
|
||||
)),
|
||||
)?
|
||||
.is_true()
|
||||
{
|
||||
min = num;
|
||||
}
|
||||
}
|
||||
Ok(Value::Dimension(min.0, min.1))
|
||||
Ok(Value::Dimension(min.0, min.1, true))
|
||||
}
|
||||
|
||||
fn max(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
@ -205,7 +215,7 @@ fn max(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
.variadic_args(args)?
|
||||
.into_iter()
|
||||
.map(|val| match val.node {
|
||||
Value::Dimension(number, unit) => Ok((number, unit)),
|
||||
Value::Dimension(number, unit, _) => Ok((number, unit)),
|
||||
v => Err((format!("{} is not a number.", v.inspect(span)?), span).into()),
|
||||
})
|
||||
.collect::<SassResult<Vec<(Number, Unit)>>>()?
|
||||
@ -217,15 +227,23 @@ fn max(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
for num in nums {
|
||||
if ValueVisitor::new(parser, span)
|
||||
.greater_than(
|
||||
HigherIntermediateValue::Literal(Value::Dimension(num.0.clone(), num.1.clone())),
|
||||
HigherIntermediateValue::Literal(Value::Dimension(max.0.clone(), max.1.clone())),
|
||||
HigherIntermediateValue::Literal(Value::Dimension(
|
||||
num.0.clone(),
|
||||
num.1.clone(),
|
||||
true,
|
||||
)),
|
||||
HigherIntermediateValue::Literal(Value::Dimension(
|
||||
max.0.clone(),
|
||||
max.1.clone(),
|
||||
true,
|
||||
)),
|
||||
)?
|
||||
.is_true()
|
||||
{
|
||||
max = num;
|
||||
}
|
||||
}
|
||||
Ok(Value::Dimension(max.0, max.1))
|
||||
Ok(Value::Dimension(max.0, max.1, true))
|
||||
}
|
||||
|
||||
pub(crate) fn declare(f: &mut GlobalFunctionMap) {
|
||||
|
@ -53,7 +53,7 @@ fn feature_exists(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Val
|
||||
fn unit(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
let unit = match parser.arg(&mut args, 0, "number")? {
|
||||
Value::Dimension(_, u) => u.to_string(),
|
||||
Value::Dimension(_, u, _) => u.to_string(),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$number: {} is not a number.", v.inspect(args.span())?),
|
||||
@ -75,8 +75,8 @@ fn unitless(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
#[allow(clippy::match_same_arms)]
|
||||
Ok(match parser.arg(&mut args, 0, "number")? {
|
||||
Value::Dimension(_, Unit::None) => Value::True,
|
||||
Value::Dimension(_, _) => Value::False,
|
||||
Value::Dimension(_, Unit::None, _) => Value::True,
|
||||
Value::Dimension(..) => Value::False,
|
||||
_ => Value::True,
|
||||
})
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ fn str_length(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
|
||||
Value::String(i, _) => Ok(Value::Dimension(
|
||||
Number::from(i.chars().count()),
|
||||
Unit::None,
|
||||
true,
|
||||
)),
|
||||
v => Err((
|
||||
format!("$string: {} is not a string.", v.inspect(args.span())?),
|
||||
@ -98,15 +99,15 @@ fn str_slice(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
};
|
||||
let str_len = string.chars().count();
|
||||
let start = match parser.arg(&mut args, 1, "start-at")? {
|
||||
Value::Dimension(n, Unit::None) if n.is_decimal() => {
|
||||
Value::Dimension(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(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(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))
|
||||
.to_usize()
|
||||
.unwrap(),
|
||||
v @ Value::Dimension(..) => {
|
||||
@ -128,15 +129,15 @@ fn str_slice(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
}
|
||||
};
|
||||
let mut end = match parser.default_arg(&mut args, 2, "end-at", Value::Null)? {
|
||||
Value::Dimension(n, Unit::None) if n.is_decimal() => {
|
||||
Value::Dimension(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(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(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))
|
||||
.to_usize()
|
||||
.unwrap_or(str_len + 1),
|
||||
v @ Value::Dimension(..) => {
|
||||
@ -202,7 +203,7 @@ fn str_index(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
};
|
||||
|
||||
Ok(match s1.find(&substr) {
|
||||
Some(v) => Value::Dimension(Number::from(v + 1), Unit::None),
|
||||
Some(v) => Value::Dimension(Number::from(v + 1), Unit::None, true),
|
||||
None => Value::Null,
|
||||
})
|
||||
}
|
||||
@ -232,10 +233,10 @@ fn str_insert(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
|
||||
};
|
||||
|
||||
let index = match parser.arg(&mut args, 2, "index")? {
|
||||
Value::Dimension(n, Unit::None) if n.is_decimal() => {
|
||||
Value::Dimension(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(n, Unit::None, _) => n,
|
||||
v @ Value::Dimension(..) => {
|
||||
return Err((
|
||||
format!(
|
||||
|
@ -178,7 +178,7 @@ impl<'a> Parser<'a> {
|
||||
} else {
|
||||
CallArg::Named(mem::take(&mut name).into())
|
||||
},
|
||||
self.parse_value_from_vec(val),
|
||||
self.parse_value_from_vec(val, true),
|
||||
);
|
||||
span = span.merge(tok.pos());
|
||||
return Ok(CallArgs(args, span));
|
||||
@ -218,7 +218,7 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
if is_splat {
|
||||
let val = self.parse_value_from_vec(mem::take(&mut val))?;
|
||||
let val = self.parse_value_from_vec(mem::take(&mut val), true)?;
|
||||
match val.node {
|
||||
Value::ArgList(v) => {
|
||||
for arg in v {
|
||||
@ -263,7 +263,7 @@ impl<'a> Parser<'a> {
|
||||
} else {
|
||||
CallArg::Named(mem::take(&mut name).into())
|
||||
},
|
||||
self.parse_value_from_vec(mem::take(&mut val)),
|
||||
self.parse_value_from_vec(mem::take(&mut val), true),
|
||||
);
|
||||
}
|
||||
|
||||
@ -374,7 +374,7 @@ impl<'a> Parser<'a> {
|
||||
let val = match args.get(idx, arg.name) {
|
||||
Some(v) => v,
|
||||
None => match arg.default.as_mut() {
|
||||
Some(v) => self.parse_value_from_vec(mem::take(v)),
|
||||
Some(v) => self.parse_value_from_vec(mem::take(v), true),
|
||||
None => {
|
||||
return Err(
|
||||
(format!("Missing argument ${}.", &arg.name), args.span()).into()
|
||||
|
@ -22,7 +22,7 @@ impl<'a> Parser<'a> {
|
||||
let mut found_true = false;
|
||||
let mut body = Vec::new();
|
||||
|
||||
let init_cond = self.parse_value()?.node;
|
||||
let init_cond = self.parse_value(true)?.node;
|
||||
|
||||
// consume the open curly brace
|
||||
let span_before = match self.toks.next() {
|
||||
@ -85,7 +85,7 @@ impl<'a> Parser<'a> {
|
||||
self.throw_away_until_open_curly_brace()?;
|
||||
false
|
||||
} else {
|
||||
let v = self.parse_value()?.node.is_true();
|
||||
let v = self.parse_value(true)?.node.is_true();
|
||||
match self.toks.next() {
|
||||
Some(Token { kind: '{', .. }) => {}
|
||||
Some(..) | None => {
|
||||
@ -237,9 +237,9 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
self.whitespace();
|
||||
let from_val = self.parse_value_from_vec(from_toks)?;
|
||||
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(n, ..) => match n.to_integer().to_isize() {
|
||||
Some(v) => v,
|
||||
None => return Err((format!("{} is not a int.", n), from_val.span).into()),
|
||||
},
|
||||
@ -252,9 +252,9 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
let to_val = self.parse_value()?;
|
||||
let to_val = self.parse_value(true)?;
|
||||
let to = match to_val.node {
|
||||
Value::Dimension(n, _) => match n.to_integer().to_isize() {
|
||||
Value::Dimension(n, ..) => match n.to_integer().to_isize() {
|
||||
Some(v) => v,
|
||||
None => return Err((format!("{} is not a int.", n), to_val.span).into()),
|
||||
},
|
||||
@ -297,7 +297,7 @@ impl<'a> Parser<'a> {
|
||||
self.scopes.insert_var_last(
|
||||
var.node,
|
||||
Spanned {
|
||||
node: Value::Dimension(Number::from(i), Unit::None),
|
||||
node: Value::Dimension(Number::from(i), Unit::None, true),
|
||||
span: var.span,
|
||||
},
|
||||
);
|
||||
@ -368,7 +368,7 @@ impl<'a> Parser<'a> {
|
||||
self.whitespace();
|
||||
|
||||
let mut stmts = Vec::new();
|
||||
let mut val = self.parse_value_from_vec(cond.clone())?;
|
||||
let mut val = self.parse_value_from_vec(cond.clone(), true)?;
|
||||
self.scopes.enter_new_scope();
|
||||
while val.node.is_true() {
|
||||
if self.flags.in_function() {
|
||||
@ -411,7 +411,7 @@ impl<'a> Parser<'a> {
|
||||
.parse()?,
|
||||
);
|
||||
}
|
||||
val = self.parse_value_from_vec(cond.clone())?;
|
||||
val = self.parse_value_from_vec(cond.clone(), true)?;
|
||||
}
|
||||
self.scopes.exit_scope();
|
||||
|
||||
@ -452,7 +452,10 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
self.whitespace();
|
||||
let iter_val_toks = read_until_open_curly_brace(self.toks)?;
|
||||
let iter = self.parse_value_from_vec(iter_val_toks)?.node.as_list();
|
||||
let iter = self
|
||||
.parse_value_from_vec(iter_val_toks, true)?
|
||||
.node
|
||||
.as_list();
|
||||
self.toks.next();
|
||||
self.whitespace();
|
||||
let mut body = read_until_closing_curly_brace(self.toks)?;
|
||||
|
@ -63,7 +63,7 @@ impl<'a> Parser<'a> {
|
||||
|
||||
pub(super) fn parse_return(&mut self) -> SassResult<Box<Value>> {
|
||||
let toks = read_until_semicolon_or_closing_curly_brace(self.toks)?;
|
||||
let v = self.parse_value_from_vec(toks)?;
|
||||
let v = self.parse_value_from_vec(toks, true)?;
|
||||
if let Some(Token { kind: ';', .. }) = self.toks.peek() {
|
||||
self.toks.next();
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ impl<'a> Parser<'a> {
|
||||
let Spanned {
|
||||
node: file_name_as_value,
|
||||
span,
|
||||
} = self.parse_value()?;
|
||||
} = self.parse_value(true)?;
|
||||
let file_name = match file_name_as_value {
|
||||
Value::String(s, QuoteKind::Quoted) => {
|
||||
if s.ends_with(".css") || s.starts_with("http://") || s.starts_with("https://") {
|
||||
|
@ -139,7 +139,7 @@ impl<'a> Parser<'a> {
|
||||
let Spanned {
|
||||
node: message,
|
||||
span,
|
||||
} = self.parse_value()?;
|
||||
} = self.parse_value(false)?;
|
||||
|
||||
return Err((
|
||||
message.inspect(span)?.to_string(),
|
||||
@ -151,7 +151,7 @@ impl<'a> Parser<'a> {
|
||||
let Spanned {
|
||||
node: message,
|
||||
span,
|
||||
} = self.parse_value()?;
|
||||
} = self.parse_value(false)?;
|
||||
span.merge(kind_string.span);
|
||||
if let Some(Token { kind: ';', pos }) = self.toks.peek() {
|
||||
kind_string.span.merge(*pos);
|
||||
@ -166,7 +166,7 @@ impl<'a> Parser<'a> {
|
||||
let Spanned {
|
||||
node: message,
|
||||
span,
|
||||
} = self.parse_value()?;
|
||||
} = self.parse_value(false)?;
|
||||
span.merge(kind_string.span);
|
||||
if let Some(Token { kind: ';', pos }) = self.toks.peek() {
|
||||
kind_string.span.merge(*pos);
|
||||
@ -416,7 +416,7 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
pub fn parse_interpolation(&mut self) -> SassResult<Spanned<Value>> {
|
||||
let val = self.parse_value()?;
|
||||
let val = self.parse_value(true)?;
|
||||
match self.toks.next() {
|
||||
Some(Token { kind: '}', .. }) => {}
|
||||
Some(..) | None => return Err(("expected \"}\".", val.span).into()),
|
||||
@ -437,7 +437,7 @@ impl<'a> Parser<'a> {
|
||||
toks: Vec<Token>,
|
||||
quoted: bool,
|
||||
) -> SassResult<Cow<'static, str>> {
|
||||
let value = self.parse_value_from_vec(toks)?;
|
||||
let value = self.parse_value_from_vec(toks, false)?;
|
||||
if quoted {
|
||||
value.node.to_css_string(value.span)
|
||||
} else {
|
||||
|
@ -125,7 +125,7 @@ impl<'a> Parser<'a> {
|
||||
c if is_name(*c) => {
|
||||
if let Some(toks) = self.parse_style_value_when_no_space_after_semicolon() {
|
||||
let len = toks.len();
|
||||
if let Ok(val) = self.parse_value_from_vec(toks) {
|
||||
if let Ok(val) = self.parse_value_from_vec(toks, false) {
|
||||
self.toks.take(len).for_each(drop);
|
||||
return Ok(SelectorOrStyle::Style(
|
||||
InternedString::get_or_intern(property),
|
||||
@ -173,7 +173,7 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
fn parse_style_value(&mut self) -> SassResult<Spanned<Value>> {
|
||||
self.parse_value()
|
||||
self.parse_value(false)
|
||||
}
|
||||
|
||||
pub(super) fn parse_style_group(
|
||||
|
@ -339,7 +339,7 @@ impl<'a> Parser<'a> {
|
||||
fn peek_interpolation(&mut self) -> SassResult<Spanned<Value>> {
|
||||
let vec = peek_until_closing_curly_brace(self.toks)?;
|
||||
self.toks.advance_cursor();
|
||||
let val = self.parse_value_from_vec(vec)?;
|
||||
let val = self.parse_value_from_vec(vec, false)?;
|
||||
Ok(Spanned {
|
||||
node: val.node.unquote(),
|
||||
span: val.span,
|
||||
|
@ -46,12 +46,15 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
Self { parser, span }
|
||||
}
|
||||
|
||||
pub fn eval(&mut self, value: HigherIntermediateValue) -> SassResult<Value> {
|
||||
pub fn eval(&mut self, value: HigherIntermediateValue, in_parens: bool) -> SassResult<Value> {
|
||||
match value {
|
||||
HigherIntermediateValue::Literal(Value::Dimension(n, u, _)) if in_parens => {
|
||||
Ok(Value::Dimension(n, u, true))
|
||||
}
|
||||
HigherIntermediateValue::Literal(v) => Ok(v),
|
||||
HigherIntermediateValue::BinaryOp(v1, op, v2) => self.bin_op(*v1, op, *v2),
|
||||
HigherIntermediateValue::UnaryOp(op, val) => self.unary_op(op, *val),
|
||||
HigherIntermediateValue::Paren(val) => self.eval(*val),
|
||||
HigherIntermediateValue::BinaryOp(v1, op, v2) => self.bin_op(*v1, op, *v2, in_parens),
|
||||
HigherIntermediateValue::UnaryOp(op, val) => self.unary_op(op, *val, in_parens),
|
||||
HigherIntermediateValue::Paren(val) => self.eval(*val, true),
|
||||
HigherIntermediateValue::Function(function, args) => {
|
||||
self.parser.call_function(function, args)
|
||||
}
|
||||
@ -63,16 +66,21 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
val1: HigherIntermediateValue,
|
||||
op: Op,
|
||||
val2: HigherIntermediateValue,
|
||||
in_parens: bool,
|
||||
) -> SassResult<Value> {
|
||||
let mut val1 = self.paren_or_unary(val1)?;
|
||||
let val2 = self.paren_or_unary(val2)?;
|
||||
let mut val1 = self.paren_or_unary(val1, in_parens)?;
|
||||
let val2 = self.paren_or_unary(val2, in_parens)?;
|
||||
|
||||
if let HigherIntermediateValue::BinaryOp(val1_1, op2, val1_2) = val1 {
|
||||
let in_parens = op != Op::Div || op2 != Op::Div;
|
||||
if op2.precedence() >= op.precedence() {
|
||||
val1 = HigherIntermediateValue::Literal(self.bin_op(*val1_1, op2, *val1_2)?);
|
||||
val1 = HigherIntermediateValue::Literal(
|
||||
self.bin_op(*val1_1, op2, *val1_2, in_parens)?,
|
||||
);
|
||||
} else {
|
||||
let val2 = HigherIntermediateValue::Literal(self.bin_op(*val1_2, op, val2)?);
|
||||
return self.bin_op(*val1_1, op2, val2);
|
||||
let val2 =
|
||||
HigherIntermediateValue::Literal(self.bin_op(*val1_2, op, val2, in_parens)?);
|
||||
return self.bin_op(*val1_1, op2, val2, in_parens);
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +88,7 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
Op::Plus => self.add(val1, val2)?,
|
||||
Op::Minus => self.sub(val1, val2)?,
|
||||
Op::Mul => self.mul(val1, val2)?,
|
||||
Op::Div => self.div(val1, val2)?,
|
||||
Op::Div => self.div(val1, val2, in_parens)?,
|
||||
Op::Rem => self.rem(val1, val2)?,
|
||||
Op::And => Self::and(val1, val2)?,
|
||||
Op::Or => Self::or(val1, val2)?,
|
||||
@ -94,8 +102,13 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
})
|
||||
}
|
||||
|
||||
fn unary_op(&mut self, op: Op, val: HigherIntermediateValue) -> SassResult<Value> {
|
||||
let val = self.eval(val)?;
|
||||
fn unary_op(
|
||||
&mut self,
|
||||
op: Op,
|
||||
val: HigherIntermediateValue,
|
||||
in_parens: bool,
|
||||
) -> SassResult<Value> {
|
||||
let val = self.eval(val, in_parens)?;
|
||||
match op {
|
||||
Op::Minus => self.unary_minus(val),
|
||||
Op::Not => Self::unary_not(&val),
|
||||
@ -106,7 +119,7 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
|
||||
fn unary_minus(&self, val: Value) -> SassResult<Value> {
|
||||
Ok(match val {
|
||||
Value::Dimension(n, u) => Value::Dimension(-n, u),
|
||||
Value::Dimension(n, u, should_divide) => Value::Dimension(-n, u, should_divide),
|
||||
v => Value::String(format!("-{}", v.to_css_string(self.span)?), QuoteKind::None),
|
||||
})
|
||||
}
|
||||
@ -124,7 +137,7 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
|
||||
fn paren(&mut self, val: HigherIntermediateValue) -> SassResult<HigherIntermediateValue> {
|
||||
Ok(if let HigherIntermediateValue::Paren(v) = val {
|
||||
HigherIntermediateValue::Literal(self.eval(*v)?)
|
||||
HigherIntermediateValue::Literal(self.eval(*v, true)?)
|
||||
} else {
|
||||
val
|
||||
})
|
||||
@ -133,11 +146,12 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
fn paren_or_unary(
|
||||
&mut self,
|
||||
val: HigherIntermediateValue,
|
||||
in_parens: bool,
|
||||
) -> SassResult<HigherIntermediateValue> {
|
||||
let val = self.paren(val)?;
|
||||
Ok(match val {
|
||||
HigherIntermediateValue::UnaryOp(op, val) => {
|
||||
HigherIntermediateValue::Literal(self.unary_op(op, *val)?)
|
||||
HigherIntermediateValue::Literal(self.unary_op(op, *val, in_parens)?)
|
||||
}
|
||||
HigherIntermediateValue::Function(function, args) => {
|
||||
HigherIntermediateValue::Literal(self.parser.call_function(function, args)?)
|
||||
@ -191,8 +205,8 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
QuoteKind::None,
|
||||
),
|
||||
},
|
||||
Value::Dimension(num, unit) => match right {
|
||||
Value::Dimension(num2, unit2) => {
|
||||
Value::Dimension(num, unit, _) => match right {
|
||||
Value::Dimension(num2, unit2, _) => {
|
||||
if !unit.comparable(&unit2) {
|
||||
return Err((
|
||||
format!("Incompatible units {} and {}.", unit2, unit),
|
||||
@ -201,11 +215,11 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
.into());
|
||||
}
|
||||
if unit == unit2 {
|
||||
Value::Dimension(num + num2, unit)
|
||||
Value::Dimension(num + num2, unit, true)
|
||||
} else if unit == Unit::None {
|
||||
Value::Dimension(num + num2, unit2)
|
||||
Value::Dimension(num + num2, unit2, true)
|
||||
} else if unit2 == Unit::None {
|
||||
Value::Dimension(num + num2, unit)
|
||||
Value::Dimension(num + num2, unit, true)
|
||||
} else {
|
||||
Value::Dimension(
|
||||
num + num2
|
||||
@ -213,6 +227,7 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
[unit2.to_string().as_str()]
|
||||
.clone(),
|
||||
unit,
|
||||
true,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -299,8 +314,8 @@ 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(num, unit, _) => match right {
|
||||
Value::Dimension(num2, unit2, _) => {
|
||||
if !unit.comparable(&unit2) {
|
||||
return Err((
|
||||
format!("Incompatible units {} and {}.", unit2, unit),
|
||||
@ -309,11 +324,11 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
.into());
|
||||
}
|
||||
if unit == unit2 {
|
||||
Value::Dimension(num - num2, unit)
|
||||
Value::Dimension(num - num2, unit, true)
|
||||
} else if unit == Unit::None {
|
||||
Value::Dimension(num - num2, unit2)
|
||||
Value::Dimension(num - num2, unit2, true)
|
||||
} else if unit2 == Unit::None {
|
||||
Value::Dimension(num - num2, unit)
|
||||
Value::Dimension(num - num2, unit, true)
|
||||
} else {
|
||||
Value::Dimension(
|
||||
num - num2
|
||||
@ -321,6 +336,7 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
[unit2.to_string().as_str()]
|
||||
.clone(),
|
||||
unit,
|
||||
true,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -418,14 +434,14 @@ 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(num, unit, _) => match right {
|
||||
Value::Dimension(num2, unit2, _) => {
|
||||
if unit == Unit::None {
|
||||
Value::Dimension(num * num2, unit2)
|
||||
Value::Dimension(num * num2, unit2, true)
|
||||
} else if unit2 == Unit::None {
|
||||
Value::Dimension(num * num2, unit)
|
||||
Value::Dimension(num * num2, unit, true)
|
||||
} else {
|
||||
Value::Dimension(num * num2, unit * unit2)
|
||||
Value::Dimension(num * num2, unit * unit2, true)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@ -459,6 +475,7 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
&self,
|
||||
left: HigherIntermediateValue,
|
||||
right: HigherIntermediateValue,
|
||||
in_parens: bool,
|
||||
) -> SassResult<Value> {
|
||||
let left = match left {
|
||||
HigherIntermediateValue::Literal(v) => v,
|
||||
@ -473,35 +490,43 @@ 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) => {
|
||||
// `unit(1em / 1em)` => `""`
|
||||
if unit == unit2 {
|
||||
Value::Dimension(num / num2, Unit::None)
|
||||
Value::Dimension(num, unit, should_divide1) => match right {
|
||||
Value::Dimension(num2, unit2, should_divide2) => {
|
||||
if should_divide1 || should_divide2 || in_parens {
|
||||
// `unit(1em / 1em)` => `""`
|
||||
if unit == unit2 {
|
||||
Value::Dimension(num / num2, Unit::None, true)
|
||||
|
||||
// `unit(1 / 1em)` => `"em^-1"`
|
||||
} else if unit == Unit::None {
|
||||
Value::Dimension(num / num2, Unit::None / unit2)
|
||||
// `unit(1 / 1em)` => `"em^-1"`
|
||||
} else if unit == Unit::None {
|
||||
Value::Dimension(num / num2, Unit::None / unit2, true)
|
||||
|
||||
// `unit(1em / 1)` => `"em"`
|
||||
} else if unit2 == Unit::None {
|
||||
Value::Dimension(num / num2, unit)
|
||||
// `unit(1em / 1)` => `"em"`
|
||||
} else if unit2 == Unit::None {
|
||||
Value::Dimension(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()),
|
||||
Unit::None,
|
||||
)
|
||||
// `unit(1em / 1px)` => `"em/px"`
|
||||
// todo: this should probably be its own variant
|
||||
// within the `Value` enum
|
||||
// `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()),
|
||||
Unit::None,
|
||||
true,
|
||||
)
|
||||
// `unit(1em / 1px)` => `"em/px"`
|
||||
// todo: this should probably be its own variant
|
||||
// within the `Value` enum
|
||||
} else {
|
||||
// todo: remember to account for `Mul` and `Div`
|
||||
todo!("non-comparable inverse units")
|
||||
}
|
||||
} else {
|
||||
// todo: remember to account for `Mul` and `Div`
|
||||
todo!("non-comparable inverse units")
|
||||
Value::String(
|
||||
format!("{}{}/{}{}", num, unit, num2, unit2),
|
||||
QuoteKind::None,
|
||||
)
|
||||
}
|
||||
}
|
||||
Value::String(s, q) => {
|
||||
@ -605,28 +630,28 @@ 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(n, u, _) => match right {
|
||||
Value::Dimension(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)
|
||||
Value::Dimension(n % n2, u, true)
|
||||
} else if u == Unit::None {
|
||||
Value::Dimension(n % n2, u2)
|
||||
Value::Dimension(n % n2, u2, true)
|
||||
} else if u2 == Unit::None {
|
||||
Value::Dimension(n % n2, u)
|
||||
Value::Dimension(n % n2, u, true)
|
||||
} else {
|
||||
Value::Dimension(n, u)
|
||||
Value::Dimension(n, u, true)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err((
|
||||
format!(
|
||||
"Undefined operation \"{} % {}\".",
|
||||
Value::Dimension(n, u).inspect(self.span)?,
|
||||
Value::Dimension(n, u, true).inspect(self.span)?,
|
||||
right.inspect(self.span)?
|
||||
),
|
||||
self.span,
|
||||
@ -711,8 +736,8 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
||||
v => panic!("{:?}", v),
|
||||
};
|
||||
let ordering = match left {
|
||||
Value::Dimension(num, unit) => match &right {
|
||||
Value::Dimension(num2, unit2) => {
|
||||
Value::Dimension(num, unit, _) => match &right {
|
||||
Value::Dimension(num2, unit2, _) => {
|
||||
if !unit.comparable(unit2) {
|
||||
return Err((
|
||||
format!("Incompatible units {} and {}.", unit2, unit),
|
||||
|
@ -52,7 +52,7 @@ impl IsWhitespace for IntermediateValue {
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
pub(crate) fn parse_value(&mut self) -> SassResult<Spanned<Value>> {
|
||||
pub(crate) fn parse_value(&mut self, in_paren: bool) -> SassResult<Spanned<Value>> {
|
||||
self.whitespace();
|
||||
let span = match self.toks.peek() {
|
||||
Some(Token { kind: '}', .. })
|
||||
@ -80,6 +80,7 @@ impl<'a> Parser<'a> {
|
||||
},
|
||||
&mut space_separated,
|
||||
last_was_whitespace,
|
||||
in_paren,
|
||||
)?;
|
||||
}
|
||||
IntermediateValue::Whitespace => {
|
||||
@ -104,7 +105,7 @@ impl<'a> Parser<'a> {
|
||||
span = span.merge(a.span);
|
||||
a.node
|
||||
})
|
||||
.map(|a| ValueVisitor::new(iter.parser, span).eval(a))
|
||||
.map(|a| ValueVisitor::new(iter.parser, span).eval(a, in_paren))
|
||||
.collect::<SassResult<Vec<Value>>>()?,
|
||||
ListSeparator::Space,
|
||||
Brackets::None,
|
||||
@ -117,7 +118,7 @@ impl<'a> Parser<'a> {
|
||||
last_was_whitespace = false;
|
||||
space_separated.push(
|
||||
HigherIntermediateValue::Literal(
|
||||
match iter.parser.parse_value_from_vec(t)?.node {
|
||||
match iter.parser.parse_value_from_vec(t, in_paren)?.node {
|
||||
Value::List(v, sep, Brackets::None) => {
|
||||
Value::List(v, sep, Brackets::Bracketed)
|
||||
}
|
||||
@ -147,7 +148,7 @@ impl<'a> Parser<'a> {
|
||||
HigherIntermediateValue::Literal(Value::List(
|
||||
space_separated
|
||||
.into_iter()
|
||||
.map(|a| ValueVisitor::new(self, span).eval(a.node))
|
||||
.map(|a| ValueVisitor::new(self, span).eval(a.node, in_paren))
|
||||
.collect::<SassResult<Vec<Value>>>()?,
|
||||
ListSeparator::Space,
|
||||
Brackets::None,
|
||||
@ -158,7 +159,7 @@ impl<'a> Parser<'a> {
|
||||
Value::List(
|
||||
comma_separated
|
||||
.into_iter()
|
||||
.map(|a| ValueVisitor::new(self, span).eval(a.node))
|
||||
.map(|a| ValueVisitor::new(self, span).eval(a.node, in_paren))
|
||||
.collect::<SassResult<Vec<Value>>>()?,
|
||||
ListSeparator::Comma,
|
||||
Brackets::None,
|
||||
@ -166,13 +167,13 @@ impl<'a> Parser<'a> {
|
||||
.span(span)
|
||||
} else if space_separated.len() == 1 {
|
||||
ValueVisitor::new(self, span)
|
||||
.eval(space_separated.pop().unwrap().node)?
|
||||
.eval(space_separated.pop().unwrap().node, in_paren)?
|
||||
.span(span)
|
||||
} else {
|
||||
Value::List(
|
||||
space_separated
|
||||
.into_iter()
|
||||
.map(|a| ValueVisitor::new(self, span).eval(a.node))
|
||||
.map(|a| ValueVisitor::new(self, span).eval(a.node, in_paren))
|
||||
.collect::<SassResult<Vec<Value>>>()?,
|
||||
ListSeparator::Space,
|
||||
Brackets::None,
|
||||
@ -181,7 +182,11 @@ impl<'a> Parser<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn parse_value_from_vec(&mut self, toks: Vec<Token>) -> SassResult<Spanned<Value>> {
|
||||
pub(crate) fn parse_value_from_vec(
|
||||
&mut self,
|
||||
toks: Vec<Token>,
|
||||
in_paren: bool,
|
||||
) -> SassResult<Spanned<Value>> {
|
||||
Parser {
|
||||
toks: &mut toks.into_iter().peekmore(),
|
||||
map: self.map,
|
||||
@ -197,7 +202,7 @@ impl<'a> Parser<'a> {
|
||||
extender: self.extender,
|
||||
content_scopes: self.content_scopes,
|
||||
}
|
||||
.parse_value()
|
||||
.parse_value(in_paren)
|
||||
}
|
||||
|
||||
fn parse_ident_value(&mut self) -> SassResult<Spanned<IntermediateValue>> {
|
||||
@ -391,6 +396,7 @@ impl<'a> Parser<'a> {
|
||||
HigherIntermediateValue::Literal(Value::Dimension(
|
||||
Number::new_small(n),
|
||||
unit,
|
||||
false,
|
||||
)),
|
||||
)
|
||||
.span(span)));
|
||||
@ -403,6 +409,7 @@ impl<'a> Parser<'a> {
|
||||
HigherIntermediateValue::Literal(Value::Dimension(
|
||||
Number::new_small(n),
|
||||
unit,
|
||||
false,
|
||||
)),
|
||||
)
|
||||
.span(span)));
|
||||
@ -415,6 +422,7 @@ impl<'a> Parser<'a> {
|
||||
HigherIntermediateValue::Literal(Value::Dimension(
|
||||
Number::new_big(n),
|
||||
unit,
|
||||
false,
|
||||
)),
|
||||
)
|
||||
.span(span)));
|
||||
@ -443,6 +451,7 @@ impl<'a> Parser<'a> {
|
||||
IntermediateValue::Value(HigherIntermediateValue::Literal(Value::Dimension(
|
||||
Number::new_big(n * times_ten),
|
||||
unit,
|
||||
false,
|
||||
)))
|
||||
.span(span)
|
||||
}
|
||||
@ -747,11 +756,12 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
op: Spanned<Op>,
|
||||
space_separated: &mut Vec<Spanned<HigherIntermediateValue>>,
|
||||
last_was_whitespace: bool,
|
||||
in_paren: bool,
|
||||
) -> SassResult<()> {
|
||||
match op.node {
|
||||
Op::Not => {
|
||||
self.whitespace();
|
||||
let right = self.single_value()?;
|
||||
let right = self.single_value(in_paren)?;
|
||||
space_separated.push(Spanned {
|
||||
node: HigherIntermediateValue::UnaryOp(op.node, Box::new(right.node)),
|
||||
span: right.span,
|
||||
@ -759,7 +769,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
}
|
||||
Op::Div => {
|
||||
self.whitespace();
|
||||
let right = self.single_value()?;
|
||||
let right = self.single_value(in_paren)?;
|
||||
if let Some(left) = space_separated.pop() {
|
||||
space_separated.push(Spanned {
|
||||
node: HigherIntermediateValue::BinaryOp(
|
||||
@ -776,7 +786,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
format!(
|
||||
"/{}",
|
||||
ValueVisitor::new(self.parser, right.span)
|
||||
.eval(right.node)?
|
||||
.eval(right.node, false)?
|
||||
.to_css_string(right.span)?
|
||||
),
|
||||
QuoteKind::None,
|
||||
@ -788,7 +798,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
Op::Plus => {
|
||||
if let Some(left) = space_separated.pop() {
|
||||
self.whitespace();
|
||||
let right = self.single_value()?;
|
||||
let right = self.single_value(in_paren)?;
|
||||
space_separated.push(Spanned {
|
||||
node: HigherIntermediateValue::BinaryOp(
|
||||
Box::new(left.node),
|
||||
@ -799,7 +809,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
});
|
||||
} else {
|
||||
self.whitespace();
|
||||
let right = self.single_value()?;
|
||||
let right = self.single_value(in_paren)?;
|
||||
space_separated.push(Spanned {
|
||||
node: HigherIntermediateValue::UnaryOp(op.node, Box::new(right.node)),
|
||||
span: right.span,
|
||||
@ -808,7 +818,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
}
|
||||
Op::Minus => {
|
||||
if self.whitespace() || !last_was_whitespace {
|
||||
let right = self.single_value()?;
|
||||
let right = self.single_value(in_paren)?;
|
||||
if let Some(left) = space_separated.pop() {
|
||||
space_separated.push(Spanned {
|
||||
node: HigherIntermediateValue::BinaryOp(
|
||||
@ -826,7 +836,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let right = self.single_value()?;
|
||||
let right = self.single_value(in_paren)?;
|
||||
space_separated.push(
|
||||
right.map_node(|n| HigherIntermediateValue::UnaryOp(op.node, Box::new(n))),
|
||||
);
|
||||
@ -846,10 +856,10 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
} else if let Some(left) = space_separated.pop() {
|
||||
self.whitespace();
|
||||
if ValueVisitor::new(self.parser, left.span)
|
||||
.eval(left.node.clone())?
|
||||
.eval(left.node.clone(), false)?
|
||||
.is_true()
|
||||
{
|
||||
let right = self.single_value()?;
|
||||
let right = self.single_value(in_paren)?;
|
||||
space_separated.push(
|
||||
HigherIntermediateValue::BinaryOp(
|
||||
Box::new(left.node),
|
||||
@ -890,7 +900,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
} else if let Some(left) = space_separated.pop() {
|
||||
self.whitespace();
|
||||
if ValueVisitor::new(self.parser, left.span)
|
||||
.eval(left.node.clone())?
|
||||
.eval(left.node.clone(), false)?
|
||||
.is_true()
|
||||
{
|
||||
// we explicitly ignore errors here as a workaround for short circuiting
|
||||
@ -912,7 +922,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
}
|
||||
space_separated.push(left);
|
||||
} else {
|
||||
let right = self.single_value()?;
|
||||
let right = self.single_value(in_paren)?;
|
||||
space_separated.push(
|
||||
HigherIntermediateValue::BinaryOp(
|
||||
Box::new(left.node),
|
||||
@ -929,7 +939,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
_ => {
|
||||
if let Some(left) = space_separated.pop() {
|
||||
self.whitespace();
|
||||
let right = self.single_value()?;
|
||||
let right = self.single_value(in_paren)?;
|
||||
space_separated.push(
|
||||
HigherIntermediateValue::BinaryOp(
|
||||
Box::new(left.node),
|
||||
@ -946,7 +956,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn single_value(&mut self) -> SassResult<Spanned<HigherIntermediateValue>> {
|
||||
fn single_value(&mut self, in_paren: bool) -> SassResult<Spanned<HigherIntermediateValue>> {
|
||||
let next = self
|
||||
.next()
|
||||
.ok_or(("Expected expression.", self.parser.span_before))??;
|
||||
@ -955,7 +965,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
IntermediateValue::Op(op) => match op {
|
||||
Op::Minus => {
|
||||
self.whitespace();
|
||||
let val = self.single_value()?;
|
||||
let val = self.single_value(in_paren)?;
|
||||
Spanned {
|
||||
node: HigherIntermediateValue::UnaryOp(Op::Minus, Box::new(val.node)),
|
||||
span: next.span.merge(val.span),
|
||||
@ -963,7 +973,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
}
|
||||
Op::Not => {
|
||||
self.whitespace();
|
||||
let val = self.single_value()?;
|
||||
let val = self.single_value(in_paren)?;
|
||||
Spanned {
|
||||
node: HigherIntermediateValue::UnaryOp(Op::Not, Box::new(val.node)),
|
||||
span: next.span.merge(val.span),
|
||||
@ -971,17 +981,17 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
}
|
||||
Op::Plus => {
|
||||
self.whitespace();
|
||||
self.single_value()?
|
||||
self.single_value(in_paren)?
|
||||
}
|
||||
Op::Div => {
|
||||
self.whitespace();
|
||||
let val = self.single_value()?;
|
||||
let val = self.single_value(in_paren)?;
|
||||
Spanned {
|
||||
node: HigherIntermediateValue::Literal(Value::String(
|
||||
format!(
|
||||
"/{}",
|
||||
ValueVisitor::new(self.parser, val.span)
|
||||
.eval(val.node)?
|
||||
.eval(val.node, false)?
|
||||
.to_css_string(val.span)?
|
||||
),
|
||||
QuoteKind::None,
|
||||
@ -1012,7 +1022,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
return Err(("Expected expression.", self.parser.span_before).into())
|
||||
}
|
||||
IntermediateValue::Bracketed(t) => {
|
||||
let v = self.parser.parse_value_from_vec(t)?;
|
||||
let v = self.parser.parse_value_from_vec(t, in_paren)?;
|
||||
HigherIntermediateValue::Literal(match v.node {
|
||||
Value::List(v, sep, Brackets::None) => Value::List(v, sep, Brackets::Bracketed),
|
||||
v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed),
|
||||
@ -1050,7 +1060,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
let mut map = SassMap::new();
|
||||
let key = self
|
||||
.parser
|
||||
.parse_value_from_vec(read_until_char(paren_toks, ':')?)?;
|
||||
.parse_value_from_vec(read_until_char(paren_toks, ':')?, true)?;
|
||||
|
||||
if paren_toks.peek().is_none() {
|
||||
return Ok(Spanned {
|
||||
@ -1063,7 +1073,7 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
|
||||
let val = self
|
||||
.parser
|
||||
.parse_value_from_vec(read_until_char(paren_toks, ',')?)?;
|
||||
.parse_value_from_vec(read_until_char(paren_toks, ',')?, true)?;
|
||||
|
||||
map.insert(key.node, val.node);
|
||||
|
||||
@ -1081,11 +1091,11 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
loop {
|
||||
let key = self
|
||||
.parser
|
||||
.parse_value_from_vec(read_until_char(paren_toks, ':')?)?;
|
||||
.parse_value_from_vec(read_until_char(paren_toks, ':')?, true)?;
|
||||
devour_whitespace(paren_toks);
|
||||
let val = self
|
||||
.parser
|
||||
.parse_value_from_vec(read_until_char(paren_toks, ',')?)?;
|
||||
.parse_value_from_vec(read_until_char(paren_toks, ',')?, true)?;
|
||||
span = span.merge(val.span);
|
||||
devour_whitespace(paren_toks);
|
||||
if map.insert(key.node, val.node) {
|
||||
|
@ -169,7 +169,7 @@ impl<'a> Parser<'a> {
|
||||
_ => val_toks.push(self.toks.next().unwrap()),
|
||||
}
|
||||
}
|
||||
let val = self.parse_value_from_vec(val_toks)?;
|
||||
let val = self.parse_value_from_vec(val_toks, true)?;
|
||||
Ok(VariableValue::new(val, global, default))
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ pub(crate) enum Value {
|
||||
True,
|
||||
False,
|
||||
Null,
|
||||
Dimension(Number, Unit),
|
||||
Dimension(Number, Unit, bool),
|
||||
List(Vec<Value>, ListSeparator, Brackets),
|
||||
Color(Box<Color>),
|
||||
String(String, QuoteKind),
|
||||
@ -122,7 +122,7 @@ impl Value {
|
||||
pub fn to_css_string(&self, span: Span) -> SassResult<Cow<'static, str>> {
|
||||
Ok(match self {
|
||||
Value::Important => Cow::const_str("!important"),
|
||||
Value::Dimension(num, unit) => match unit {
|
||||
Value::Dimension(num, unit, _) => match unit {
|
||||
Unit::Mul(..) | Unit::Div(..) => {
|
||||
return Err((format!("{}{} isn't a valid CSS value.", num, unit), span).into());
|
||||
}
|
||||
@ -252,8 +252,8 @@ impl Value {
|
||||
Value::String(s2, ..) => s1 == s2,
|
||||
_ => false,
|
||||
},
|
||||
Value::Dimension(n, unit) => match other {
|
||||
Value::Dimension(n2, unit2) => {
|
||||
Value::Dimension(n, unit, _) => match other {
|
||||
Value::Dimension(n2, unit2, _) => {
|
||||
if !unit.comparable(unit2) {
|
||||
false
|
||||
} else if unit == unit2 {
|
||||
@ -294,8 +294,8 @@ impl Value {
|
||||
Value::String(s2, ..) => s1 != s2,
|
||||
_ => true,
|
||||
},
|
||||
Value::Dimension(n, unit) => match other {
|
||||
Value::Dimension(n2, unit2) => {
|
||||
Value::Dimension(n, unit, _) => match other {
|
||||
Value::Dimension(n2, unit2, _) => {
|
||||
if !unit.comparable(unit2) {
|
||||
true
|
||||
} else if unit == unit2 {
|
||||
@ -371,7 +371,7 @@ impl Value {
|
||||
.collect::<SassResult<Vec<String>>>()?
|
||||
.join(", ")
|
||||
)),
|
||||
Value::Dimension(num, unit) => Cow::owned(format!("{}{}", num, unit)),
|
||||
Value::Dimension(num, unit, _) => Cow::owned(format!("{}{}", num, unit)),
|
||||
Value::ArgList(args) if args.is_empty() => Cow::const_str("()"),
|
||||
Value::ArgList(args) if args.len() == 1 => Cow::owned(format!(
|
||||
"({},)",
|
||||
|
@ -118,3 +118,54 @@ error!(
|
||||
"a {\n color: 1 / get-function(lighten);\n}\n",
|
||||
"Error: get-function(\"lighten\") isn't a valid CSS value."
|
||||
);
|
||||
test!(
|
||||
does_not_eval_plain,
|
||||
"a {\n color: 1 / 2;\n}\n",
|
||||
"a {\n color: 1/2;\n}\n"
|
||||
);
|
||||
test!(
|
||||
does_eval_inside_parens,
|
||||
"a {\n color: (1/2);\n}\n",
|
||||
"a {\n color: 0.5;\n}\n"
|
||||
);
|
||||
test!(
|
||||
does_eval_when_one_is_calculated,
|
||||
"a {\n color: (1*1) / 2;\n}\n",
|
||||
"a {\n color: 0.5;\n}\n"
|
||||
);
|
||||
test!(
|
||||
does_not_eval_from_unary_minus,
|
||||
"a {\n color: -1 / 2;\n}\n",
|
||||
"a {\n color: -1/2;\n}\n"
|
||||
);
|
||||
test!(
|
||||
does_eval_from_variable,
|
||||
"$a: 1;\na {\n color: $a / 2;\n}\n",
|
||||
"a {\n color: 0.5;\n}\n"
|
||||
);
|
||||
test!(
|
||||
does_eval_single_number_in_parens,
|
||||
"a {\n color: (1) / 2;\n}\n",
|
||||
"a {\n color: 0.5;\n}\n"
|
||||
);
|
||||
test!(
|
||||
does_eval_function_call,
|
||||
"@function foo() {
|
||||
@return 1;
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo() / 2;
|
||||
}",
|
||||
"a {\n color: 0.5;\n}\n"
|
||||
);
|
||||
test!(
|
||||
does_not_eval_chained_binop_division,
|
||||
"a {\n color: 1 / 3 / 4;\n}\n",
|
||||
"a {\n color: 1/3/4;\n}\n"
|
||||
);
|
||||
test!(
|
||||
does_not_eval_chained_binop_one_not_division,
|
||||
"a {\n color: 1 + 3 / 4;\n}\n",
|
||||
"a {\n color: 1.75;\n}\n"
|
||||
);
|
||||
|
@ -50,11 +50,6 @@ test!(
|
||||
);
|
||||
test!(positive_float_leading_zero, "a {\n color: 0.1;\n}\n");
|
||||
test!(negative_float_leading_zero, "a {\n color: -0.1;\n}\n");
|
||||
test!(
|
||||
num_plus_div,
|
||||
"a {\n color: 1 + 3/4;\n}\n",
|
||||
"a {\n color: 1.75;\n}\n"
|
||||
);
|
||||
test!(
|
||||
negative_near_zero_no_sign,
|
||||
"a {\n color: -0.000000000001;\n}\n",
|
||||
|
Loading…
x
Reference in New Issue
Block a user