initial implementation of @each
This commit is contained in:
parent
e5b7043480
commit
f7d9d4432a
@ -3,7 +3,7 @@ use std::iter::Peekable;
|
||||
|
||||
use num_traits::cast::ToPrimitive;
|
||||
|
||||
use crate::common::Pos;
|
||||
use crate::common::{Brackets, ListSeparator, Pos};
|
||||
use crate::error::SassResult;
|
||||
use crate::scope::Scope;
|
||||
use crate::selector::Selector;
|
||||
@ -39,6 +39,7 @@ pub(crate) enum AtRule {
|
||||
Content,
|
||||
Unknown(UnknownAtRule),
|
||||
For(Vec<Stmt>),
|
||||
Each(Vec<Stmt>),
|
||||
If(If),
|
||||
}
|
||||
|
||||
@ -103,7 +104,82 @@ impl AtRule {
|
||||
devour_whitespace(toks);
|
||||
AtRule::Charset
|
||||
}
|
||||
AtRuleKind::Each => todo!("@each not yet implemented"),
|
||||
AtRuleKind::Each => {
|
||||
let mut stmts = Vec::new();
|
||||
devour_whitespace(toks);
|
||||
let mut vars = Vec::new();
|
||||
loop {
|
||||
match toks.next().ok_or("expected \"$\".")?.kind {
|
||||
'$' => vars.push(eat_ident(toks, scope, super_selector)?),
|
||||
_ => return Err("expected \"$\".".into()),
|
||||
}
|
||||
devour_whitespace(toks);
|
||||
if toks.peek().ok_or("expected \"$\".")?.kind == ',' {
|
||||
toks.next();
|
||||
devour_whitespace(toks);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if toks.peek().is_none()
|
||||
|| eat_ident(toks, scope, super_selector)?.to_ascii_lowercase() != "in"
|
||||
{
|
||||
return Err("Expected \"in\".".into());
|
||||
}
|
||||
devour_whitespace(toks);
|
||||
let iterator = match Value::from_tokens(
|
||||
&mut read_until_open_curly_brace(toks).into_iter().peekable(),
|
||||
scope,
|
||||
super_selector,
|
||||
)? {
|
||||
Value::List(v, ..) => v,
|
||||
Value::Map(m) => m
|
||||
.into_iter()
|
||||
.map(|(k, v)| Value::List(vec![k, v], ListSeparator::Space, Brackets::None))
|
||||
.collect(),
|
||||
v => vec![v],
|
||||
};
|
||||
toks.next();
|
||||
devour_whitespace(toks);
|
||||
let mut body = read_until_closing_curly_brace(toks);
|
||||
body.push(toks.next().unwrap());
|
||||
devour_whitespace(toks);
|
||||
|
||||
for row in iterator {
|
||||
let this_iterator = match row {
|
||||
Value::List(v, ..) => v,
|
||||
Value::Map(m) => m
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
Value::List(vec![k, v], ListSeparator::Space, Brackets::None)
|
||||
})
|
||||
.collect(),
|
||||
v => vec![v],
|
||||
};
|
||||
|
||||
if vars.len() == 1 {
|
||||
scope.insert_var(
|
||||
&vars[0],
|
||||
Value::List(this_iterator, ListSeparator::Space, Brackets::None),
|
||||
)?;
|
||||
} else {
|
||||
for (var, val) in vars.clone().into_iter().zip(
|
||||
this_iterator
|
||||
.into_iter()
|
||||
.chain(std::iter::once(Value::Null).cycle()),
|
||||
) {
|
||||
scope.insert_var(&var, val)?;
|
||||
}
|
||||
}
|
||||
|
||||
stmts.extend(eat_stmts(
|
||||
&mut body.clone().into_iter().peekable(),
|
||||
scope,
|
||||
super_selector,
|
||||
)?);
|
||||
}
|
||||
AtRule::Each(stmts)
|
||||
}
|
||||
AtRuleKind::Extend => todo!("@extend not yet implemented"),
|
||||
AtRuleKind::If => AtRule::If(If::from_tokens(toks)?),
|
||||
AtRuleKind::Else => todo!("@else not yet implemented"),
|
||||
|
13
src/lib.rs
13
src/lib.rs
@ -382,7 +382,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
AtRule::Return(_) => {
|
||||
return Err("This at-rule is not allowed here.".into())
|
||||
}
|
||||
AtRule::For(s) => rules.extend(s),
|
||||
AtRule::Each(s) | AtRule::For(s) => rules.extend(s),
|
||||
AtRule::Content => return Err("@content is only allowed within mixin declarations.".into()),
|
||||
AtRule::If(i) => {
|
||||
rules.extend(i.eval(&mut Scope::new(), &Selector::new())?);
|
||||
@ -412,7 +412,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
match expr {
|
||||
Expr::Style(s) => stmts.push(Stmt::Style(s)),
|
||||
Expr::AtRule(a) => match a {
|
||||
AtRule::For(s) => stmts.extend(s),
|
||||
AtRule::Each(s) | AtRule::For(s) => stmts.extend(s),
|
||||
AtRule::If(i) => stmts.extend(i.eval(scope, super_selector)?),
|
||||
AtRule::Content => {
|
||||
return Err("@content is only allowed within mixin declarations.".into())
|
||||
@ -558,11 +558,9 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
continue;
|
||||
} else if values.is_empty() && peeked.kind == '*' {
|
||||
toks.next();
|
||||
return Ok(Some(Expr::MultilineComment(eat_comment(
|
||||
toks,
|
||||
scope,
|
||||
super_selector,
|
||||
)?)));
|
||||
let comment = eat_comment(toks, scope, super_selector)?;
|
||||
devour_whitespace(toks);
|
||||
return Ok(Some(Expr::MultilineComment(comment)));
|
||||
} else {
|
||||
values.push(tok);
|
||||
}
|
||||
@ -593,6 +591,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
c @ AtRule::Content => Ok(Some(Expr::AtRule(c))),
|
||||
f @ AtRule::If(..) => Ok(Some(Expr::AtRule(f))),
|
||||
f @ AtRule::For(..) => Ok(Some(Expr::AtRule(f))),
|
||||
f @ AtRule::Each(..) => Ok(Some(Expr::AtRule(f))),
|
||||
u @ AtRule::Unknown(..) => Ok(Some(Expr::AtRule(u))),
|
||||
};
|
||||
}
|
||||
|
36
tests/each.rs
Normal file
36
tests/each.rs
Normal file
@ -0,0 +1,36 @@
|
||||
#![cfg(test)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
test!(
|
||||
each_space_separated_inner,
|
||||
"a {\n @each $i in 1 2 3 {\n color: $i;\n }\n}\n",
|
||||
"a {\n color: 1;\n color: 2;\n color: 3;\n}\n"
|
||||
);
|
||||
test!(
|
||||
each_space_separated_outer,
|
||||
"@each $i in 1 2 3 {\n a {\n color: $i;\n }\n}\n",
|
||||
"a {\n color: 1;\n}\n\na {\n color: 2;\n}\n\na {\n color: 3;\n}\n"
|
||||
);
|
||||
test!(
|
||||
each_two_variables_one_null,
|
||||
"a {\n @each $i, $c in 1 2 3 {\n color: $i;\n }\n}\n",
|
||||
"a {\n color: 1;\n color: 2;\n color: 3;\n}\n"
|
||||
);
|
||||
test!(
|
||||
each_one_var_in_one_map,
|
||||
"a {\n @each $i in (a: b) {\n color: $i;\n }\n}\n",
|
||||
"a {\n color: a b;\n}\n"
|
||||
);
|
||||
test!(
|
||||
each_two_vars_in_one_map,
|
||||
"a {\n @each $i, $c in (a: b) {\n color: $i;\n }\n}\n",
|
||||
"a {\n color: a;\n}\n"
|
||||
);
|
||||
// blocked on better parsing of comma and space separated lists
|
||||
// test!(
|
||||
// each_two_vars_in_3_2_list,
|
||||
// "a {\n @each $i, $c in (1 2 3, 4 5) {\n color: $i, $c;\n }\n}\n",
|
||||
// "a {\n color: 1, 2;\n color: 4, 5;\n}\n"
|
||||
// );
|
Loading…
x
Reference in New Issue
Block a user