Initial implementation of @for
This commit is contained in:
parent
e2fcfeec47
commit
1b79127cc5
103
src/atrule.rs
103
src/atrule.rs
@ -1,12 +1,17 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{self, Display};
|
||||
use std::iter::Peekable;
|
||||
|
||||
use crate::common::{Pos, Scope, Symbol};
|
||||
use num_traits::cast::ToPrimitive;
|
||||
|
||||
use crate::common::{Keyword, Pos, Scope, Symbol};
|
||||
use crate::error::SassResult;
|
||||
use crate::function::Function;
|
||||
use crate::mixin::Mixin;
|
||||
use crate::selector::Selector;
|
||||
use crate::utils::{devour_whitespace, parse_interpolation};
|
||||
use crate::units::Unit;
|
||||
use crate::utils::{devour_whitespace, devour_whitespace_or_comment, parse_interpolation};
|
||||
use crate::value::{Number, Value};
|
||||
use crate::{eat_expr, Expr, Stmt};
|
||||
use crate::{RuleSet, Token, TokenKind};
|
||||
|
||||
@ -20,6 +25,7 @@ pub(crate) enum AtRule {
|
||||
Return(Vec<Token>),
|
||||
Charset,
|
||||
Unknown(UnknownAtRule),
|
||||
For(Vec<Stmt>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -102,7 +108,98 @@ impl AtRule {
|
||||
AtRuleKind::Extend => todo!("@extend not yet implemented"),
|
||||
AtRuleKind::If => todo!("@if not yet implemented"),
|
||||
AtRuleKind::Else => todo!("@else not yet implemented"),
|
||||
AtRuleKind::For => todo!("@for not yet implemented"),
|
||||
AtRuleKind::For => {
|
||||
let mut stmts = Vec::new();
|
||||
devour_whitespace_or_comment(toks);
|
||||
let var = if let Some(tok) = toks.next() {
|
||||
match tok.kind {
|
||||
TokenKind::Variable(s) => s,
|
||||
_ => return Err("expected \"$\".".into()),
|
||||
}
|
||||
} else {
|
||||
return Err("expected \"$\".".into());
|
||||
};
|
||||
devour_whitespace_or_comment(toks);
|
||||
if let Some(tok) = toks.next() {
|
||||
match tok.kind {
|
||||
TokenKind::Keyword(Keyword::From) => {}
|
||||
_ => return Err("Expected \"from\".".into()),
|
||||
}
|
||||
} else {
|
||||
return Err("Expected \"from\".".into());
|
||||
};
|
||||
devour_whitespace_or_comment(toks);
|
||||
let mut from_toks = Vec::new();
|
||||
let mut through = 0;
|
||||
while let Some(tok) = toks.next() {
|
||||
match tok.kind {
|
||||
TokenKind::Keyword(Keyword::Through) => {
|
||||
through = 1;
|
||||
break;
|
||||
}
|
||||
TokenKind::Keyword(Keyword::To) => break,
|
||||
_ => from_toks.push(tok),
|
||||
}
|
||||
}
|
||||
let from = match Value::from_tokens(&mut from_toks.into_iter().peekable(), scope)? {
|
||||
Value::Dimension(n, _) => match n.to_integer().to_usize() {
|
||||
Some(v) => v,
|
||||
None => todo!(),
|
||||
},
|
||||
v => return Err(format!("{} is not a number.", v).into()),
|
||||
};
|
||||
devour_whitespace_or_comment(toks);
|
||||
let mut to_toks = Vec::new();
|
||||
while let Some(tok) = toks.next() {
|
||||
match tok.kind {
|
||||
TokenKind::Symbol(Symbol::OpenCurlyBrace) => break,
|
||||
_ => to_toks.push(tok),
|
||||
}
|
||||
}
|
||||
let to = match Value::from_tokens(&mut to_toks.into_iter().peekable(), scope)? {
|
||||
Value::Dimension(n, _) => match n.to_integer().to_usize() {
|
||||
Some(v) => v,
|
||||
None => todo!(),
|
||||
},
|
||||
v => return Err(format!("{} is not a number.", v).into()),
|
||||
};
|
||||
let mut body = Vec::new();
|
||||
let mut n = 1;
|
||||
for tok in toks {
|
||||
match tok.kind {
|
||||
TokenKind::Symbol(Symbol::OpenCurlyBrace) => n += 1,
|
||||
TokenKind::Symbol(Symbol::CloseCurlyBrace) => n -= 1,
|
||||
TokenKind::Interpolation => n += 1,
|
||||
_ => {}
|
||||
}
|
||||
body.push(tok);
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut scope = scope.clone();
|
||||
if from < to {
|
||||
for i in from..(to + through) {
|
||||
scope.insert_var(&var, Value::Dimension(Number::from(i), Unit::None));
|
||||
stmts.extend(eat_unknown_atrule_body(
|
||||
&mut body.clone().into_iter().peekable(),
|
||||
&scope,
|
||||
super_selector,
|
||||
)?);
|
||||
}
|
||||
} else if from > to {
|
||||
for i in ((to - through)..(from + 1)).skip(1).rev() {
|
||||
scope.insert_var(&var, Value::Dimension(Number::from(i), Unit::None));
|
||||
stmts.extend(eat_unknown_atrule_body(
|
||||
&mut body.clone().into_iter().peekable(),
|
||||
&scope,
|
||||
super_selector,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
AtRule::For(stmts)
|
||||
}
|
||||
AtRuleKind::While => todo!("@while not yet implemented"),
|
||||
AtRuleKind::Keyframes => todo!("@keyframes not yet implemented"),
|
||||
AtRuleKind::Unknown(name) => {
|
||||
|
@ -220,6 +220,9 @@ pub enum Keyword {
|
||||
False,
|
||||
Null,
|
||||
Default,
|
||||
From,
|
||||
To,
|
||||
Through,
|
||||
// Infinity,
|
||||
// NaN,
|
||||
// Auto,
|
||||
@ -240,6 +243,10 @@ impl Display for Keyword {
|
||||
Self::False => write!(f, "false"),
|
||||
Self::Null => write!(f, "null"),
|
||||
Self::Default => write!(f, "!default"),
|
||||
// todo!(maintain casing for keywords)
|
||||
Self::From => write!(f, "from"),
|
||||
Self::To => write!(f, "to"),
|
||||
Self::Through => write!(f, "through"),
|
||||
// Self::Infinity => write!(f, "Infinity"),
|
||||
// Self::NaN => write!(f, "NaN"),
|
||||
// Self::Auto => write!(f, "auto"),
|
||||
@ -262,6 +269,9 @@ impl Into<&'static str> for Keyword {
|
||||
Self::False => "false",
|
||||
Self::Null => "null",
|
||||
Self::Default => "!default",
|
||||
Self::From => "from",
|
||||
Self::To => "to",
|
||||
Self::Through => "through",
|
||||
// Self::Infinity => "Infinity",
|
||||
// Self::NaN => "NaN",
|
||||
// Self::Auto => "auto",
|
||||
@ -280,13 +290,15 @@ impl TryFrom<&str> for Keyword {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(kw: &str) -> Result<Self, Self::Error> {
|
||||
// todo: case insensitive?
|
||||
match kw {
|
||||
match kw.to_ascii_lowercase().as_str() {
|
||||
"important" => Ok(Self::Important),
|
||||
"true" => Ok(Self::True),
|
||||
"false" => Ok(Self::False),
|
||||
"null" => Ok(Self::Null),
|
||||
"default" => Ok(Self::Default),
|
||||
"from" => Ok(Self::From),
|
||||
"to" => Ok(Self::To),
|
||||
"through" => Ok(Self::Through),
|
||||
// "infinity" => Ok(Self::Infinity),
|
||||
// "nan" => Ok(Self::NaN),
|
||||
// "auto" => Ok(Self::Auto),
|
||||
|
@ -474,6 +474,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
AtRule::Return(_) => {
|
||||
return Err("This at-rule is not allowed here.".into())
|
||||
}
|
||||
AtRule::For(s) => rules.extend(s),
|
||||
u @ AtRule::Unknown(..) => rules.push(Stmt::AtRule(u)),
|
||||
}
|
||||
}
|
||||
@ -652,6 +653,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
AtRule::Warn(a, b) => Ok(Some(Expr::Warn(a, b))),
|
||||
AtRule::Error(pos, err) => Err(SassError::new(err, pos)),
|
||||
AtRule::Return(_) => todo!("@return in unexpected location!"),
|
||||
f @ AtRule::For(..) => Ok(Some(Expr::AtRule(f))),
|
||||
u @ AtRule::Unknown(..) => Ok(Some(Expr::AtRule(u))),
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user