add support for splats
This commit is contained in:
parent
94d94abf7a
commit
f437649103
@ -76,7 +76,7 @@ fn set_nth(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(3)?;
|
||||
let (mut list, sep, brackets) = match parser.arg(&mut args, 0, "list")? {
|
||||
Value::List(v, sep, b) => (v, sep, b),
|
||||
Value::Map(m) => (m.entries(), ListSeparator::Comma, Brackets::None),
|
||||
Value::Map(m) => (m.as_list(), ListSeparator::Comma, Brackets::None),
|
||||
v => (vec![v], ListSeparator::Space, Brackets::None),
|
||||
};
|
||||
let n = match parser.arg(&mut args, 1, "n")? {
|
||||
@ -165,12 +165,12 @@ fn join(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(4)?;
|
||||
let (mut list1, sep1, brackets) = match parser.arg(&mut args, 0, "list1")? {
|
||||
Value::List(v, sep, brackets) => (v, sep, brackets),
|
||||
Value::Map(m) => (m.entries(), ListSeparator::Comma, Brackets::None),
|
||||
Value::Map(m) => (m.as_list(), ListSeparator::Comma, Brackets::None),
|
||||
v => (vec![v], ListSeparator::Space, Brackets::None),
|
||||
};
|
||||
let (list2, sep2) = match parser.arg(&mut args, 1, "list2")? {
|
||||
Value::List(v, sep, ..) => (v, sep),
|
||||
Value::Map(m) => (m.entries(), ListSeparator::Comma),
|
||||
Value::Map(m) => (m.as_list(), ListSeparator::Comma),
|
||||
v => (vec![v], ListSeparator::Space),
|
||||
};
|
||||
let sep = match parser.default_arg(
|
||||
|
@ -167,6 +167,8 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
self.whitespace_or_comment();
|
||||
|
||||
let mut is_splat = false;
|
||||
|
||||
while let Some(tok) = self.toks.next() {
|
||||
match tok.kind {
|
||||
')' => {
|
||||
@ -197,21 +199,67 @@ impl<'a> Parser<'a> {
|
||||
val.push(tok);
|
||||
val.extend(read_until_closing_quote(self.toks, tok.kind)?);
|
||||
}
|
||||
'.' => {
|
||||
if let Some(Token { kind: '.', pos }) = self.toks.peek().cloned() {
|
||||
if !name.is_empty() {
|
||||
return Err(("expected \")\".", pos).into());
|
||||
}
|
||||
self.toks.next();
|
||||
if let Some(Token { kind: '.', .. }) = self.toks.peek() {
|
||||
self.toks.next();
|
||||
is_splat = true;
|
||||
break;
|
||||
} else {
|
||||
return Err(("expected \".\".", pos).into());
|
||||
}
|
||||
} else {
|
||||
val.push(tok);
|
||||
}
|
||||
}
|
||||
_ => val.push(tok),
|
||||
}
|
||||
}
|
||||
|
||||
args.insert(
|
||||
if name.is_empty() {
|
||||
CallArg::Positional(args.len())
|
||||
} else {
|
||||
CallArg::Named(name.as_str().into())
|
||||
},
|
||||
{
|
||||
if is_splat {
|
||||
let val = {
|
||||
let val = self.parse_value_from_vec(mem::take(&mut val))?;
|
||||
val.node.eval(val.span)?
|
||||
},
|
||||
);
|
||||
};
|
||||
match val.node {
|
||||
Value::ArgList(v) => {
|
||||
for arg in v {
|
||||
args.insert(CallArg::Positional(args.len()), arg);
|
||||
}
|
||||
}
|
||||
Value::List(v, ..) => {
|
||||
for arg in v {
|
||||
args.insert(CallArg::Positional(args.len()), arg.eval(val.span)?);
|
||||
}
|
||||
}
|
||||
Value::Map(v) => {
|
||||
for (name, arg) in v.entries() {
|
||||
let name = name.to_css_string(val.span)?.to_string();
|
||||
args.insert(CallArg::Named(name.into()), arg.eval(val.span)?);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
args.insert(CallArg::Positional(args.len()), val);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
args.insert(
|
||||
if name.is_empty() {
|
||||
CallArg::Positional(args.len())
|
||||
} else {
|
||||
CallArg::Named(name.as_str().into())
|
||||
},
|
||||
{
|
||||
let val = self.parse_value_from_vec(mem::take(&mut val))?;
|
||||
val.node.eval(val.span)?
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
self.whitespace();
|
||||
|
||||
if self.toks.peek().is_none() {
|
||||
@ -222,8 +270,9 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn arg(
|
||||
&mut self,
|
||||
&self,
|
||||
args: &mut CallArgs,
|
||||
position: usize,
|
||||
name: &'static str,
|
||||
@ -231,8 +280,9 @@ impl<'a> Parser<'a> {
|
||||
Ok(args.get_err(position, name)?.node)
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn default_arg(
|
||||
&mut self,
|
||||
&self,
|
||||
args: &mut CallArgs,
|
||||
position: usize,
|
||||
name: &'static str,
|
||||
@ -244,21 +294,19 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn positional_arg(
|
||||
&mut self,
|
||||
args: &mut CallArgs,
|
||||
position: usize,
|
||||
) -> Option<Spanned<Value>> {
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn positional_arg(&self, args: &mut CallArgs, position: usize) -> Option<Spanned<Value>> {
|
||||
args.get_positional(position)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn named_arg(&mut self, args: &mut CallArgs, name: &'static str) -> Option<Spanned<Value>> {
|
||||
#[allow(dead_code, clippy::unused_self)]
|
||||
fn named_arg(&self, args: &mut CallArgs, name: &'static str) -> Option<Spanned<Value>> {
|
||||
args.get_named(name)
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn default_named_arg(
|
||||
&mut self,
|
||||
&self,
|
||||
args: &mut CallArgs,
|
||||
name: &'static str,
|
||||
default: Value,
|
||||
@ -269,7 +317,8 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn variadic_args(&mut self, args: CallArgs) -> SassResult<Vec<Spanned<Value>>> {
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn variadic_args(&self, args: CallArgs) -> SassResult<Vec<Spanned<Value>>> {
|
||||
let mut vals = Vec::new();
|
||||
let mut args = match args
|
||||
.0
|
||||
|
@ -48,13 +48,18 @@ impl SassMap {
|
||||
self.0.into_iter().map(|(.., v)| v).collect()
|
||||
}
|
||||
|
||||
pub fn entries(self) -> Vec<Value> {
|
||||
pub fn as_list(self) -> Vec<Value> {
|
||||
self.0
|
||||
.into_iter()
|
||||
.map(|(k, v)| Value::List(vec![k, v], ListSeparator::Space, Brackets::None))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_const_for_fn)]
|
||||
pub fn entries(self) -> Vec<(Value, Value)> {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns true if the key already exists
|
||||
pub fn insert(&mut self, key: Value, value: Value) -> bool {
|
||||
for (ref k, ref mut v) in &mut self.0 {
|
||||
|
@ -316,7 +316,7 @@ impl Value {
|
||||
pub fn as_list(self) -> Vec<Value> {
|
||||
match self {
|
||||
Value::List(v, ..) => v,
|
||||
Value::Map(m) => m.entries(),
|
||||
Value::Map(m) => m.as_list(),
|
||||
Value::ArgList(v) => v.into_iter().map(|val| val.node).collect(),
|
||||
v => vec![v],
|
||||
}
|
||||
|
55
tests/splat.rs
Normal file
55
tests/splat.rs
Normal file
@ -0,0 +1,55 @@
|
||||
#![cfg(test)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
test!(
|
||||
splat_list_two_elements,
|
||||
"@function foo($a, $b) {
|
||||
@return $a+$b;
|
||||
}
|
||||
a {
|
||||
color: foo([1, 2]...);
|
||||
}",
|
||||
"a {\n color: 3;\n}\n"
|
||||
);
|
||||
test!(
|
||||
splat_map_single_key,
|
||||
"@function foo($a) {
|
||||
@return $a;
|
||||
}
|
||||
a {
|
||||
color: foo((a: b)...);
|
||||
}",
|
||||
"a {\n color: b;\n}\n"
|
||||
);
|
||||
test!(
|
||||
splat_single_value,
|
||||
"@function foo($a) {
|
||||
@return $a;
|
||||
}
|
||||
a {
|
||||
color: foo(1...);
|
||||
}",
|
||||
"a {\n color: 1;\n}\n"
|
||||
);
|
||||
error!(
|
||||
splat_missing_last_period,
|
||||
"@function foo($a) {
|
||||
@return $a;
|
||||
}
|
||||
a {
|
||||
color: foo(1..);
|
||||
}",
|
||||
"Error: expected \".\"."
|
||||
);
|
||||
error!(
|
||||
splat_with_named_arg,
|
||||
"@function foo($a) {
|
||||
@return $a;
|
||||
}
|
||||
a {
|
||||
color: foo($a: 1...);
|
||||
}",
|
||||
"Error: expected \")\"."
|
||||
);
|
Loading…
x
Reference in New Issue
Block a user