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::fmt::{self, Display};
|
||||||
use std::iter::Peekable;
|
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::error::SassResult;
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::mixin::Mixin;
|
use crate::mixin::Mixin;
|
||||||
use crate::selector::Selector;
|
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::{eat_expr, Expr, Stmt};
|
||||||
use crate::{RuleSet, Token, TokenKind};
|
use crate::{RuleSet, Token, TokenKind};
|
||||||
|
|
||||||
@ -20,6 +25,7 @@ pub(crate) enum AtRule {
|
|||||||
Return(Vec<Token>),
|
Return(Vec<Token>),
|
||||||
Charset,
|
Charset,
|
||||||
Unknown(UnknownAtRule),
|
Unknown(UnknownAtRule),
|
||||||
|
For(Vec<Stmt>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -102,7 +108,98 @@ impl AtRule {
|
|||||||
AtRuleKind::Extend => todo!("@extend not yet implemented"),
|
AtRuleKind::Extend => todo!("@extend not yet implemented"),
|
||||||
AtRuleKind::If => todo!("@if not yet implemented"),
|
AtRuleKind::If => todo!("@if not yet implemented"),
|
||||||
AtRuleKind::Else => todo!("@else 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::While => todo!("@while not yet implemented"),
|
||||||
AtRuleKind::Keyframes => todo!("@keyframes not yet implemented"),
|
AtRuleKind::Keyframes => todo!("@keyframes not yet implemented"),
|
||||||
AtRuleKind::Unknown(name) => {
|
AtRuleKind::Unknown(name) => {
|
||||||
|
@ -220,6 +220,9 @@ pub enum Keyword {
|
|||||||
False,
|
False,
|
||||||
Null,
|
Null,
|
||||||
Default,
|
Default,
|
||||||
|
From,
|
||||||
|
To,
|
||||||
|
Through,
|
||||||
// Infinity,
|
// Infinity,
|
||||||
// NaN,
|
// NaN,
|
||||||
// Auto,
|
// Auto,
|
||||||
@ -240,6 +243,10 @@ impl Display for Keyword {
|
|||||||
Self::False => write!(f, "false"),
|
Self::False => write!(f, "false"),
|
||||||
Self::Null => write!(f, "null"),
|
Self::Null => write!(f, "null"),
|
||||||
Self::Default => write!(f, "!default"),
|
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::Infinity => write!(f, "Infinity"),
|
||||||
// Self::NaN => write!(f, "NaN"),
|
// Self::NaN => write!(f, "NaN"),
|
||||||
// Self::Auto => write!(f, "auto"),
|
// Self::Auto => write!(f, "auto"),
|
||||||
@ -262,6 +269,9 @@ impl Into<&'static str> for Keyword {
|
|||||||
Self::False => "false",
|
Self::False => "false",
|
||||||
Self::Null => "null",
|
Self::Null => "null",
|
||||||
Self::Default => "!default",
|
Self::Default => "!default",
|
||||||
|
Self::From => "from",
|
||||||
|
Self::To => "to",
|
||||||
|
Self::Through => "through",
|
||||||
// Self::Infinity => "Infinity",
|
// Self::Infinity => "Infinity",
|
||||||
// Self::NaN => "NaN",
|
// Self::NaN => "NaN",
|
||||||
// Self::Auto => "auto",
|
// Self::Auto => "auto",
|
||||||
@ -280,13 +290,15 @@ impl TryFrom<&str> for Keyword {
|
|||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
|
||||||
fn try_from(kw: &str) -> Result<Self, Self::Error> {
|
fn try_from(kw: &str) -> Result<Self, Self::Error> {
|
||||||
// todo: case insensitive?
|
match kw.to_ascii_lowercase().as_str() {
|
||||||
match kw {
|
|
||||||
"important" => Ok(Self::Important),
|
"important" => Ok(Self::Important),
|
||||||
"true" => Ok(Self::True),
|
"true" => Ok(Self::True),
|
||||||
"false" => Ok(Self::False),
|
"false" => Ok(Self::False),
|
||||||
"null" => Ok(Self::Null),
|
"null" => Ok(Self::Null),
|
||||||
"default" => Ok(Self::Default),
|
"default" => Ok(Self::Default),
|
||||||
|
"from" => Ok(Self::From),
|
||||||
|
"to" => Ok(Self::To),
|
||||||
|
"through" => Ok(Self::Through),
|
||||||
// "infinity" => Ok(Self::Infinity),
|
// "infinity" => Ok(Self::Infinity),
|
||||||
// "nan" => Ok(Self::NaN),
|
// "nan" => Ok(Self::NaN),
|
||||||
// "auto" => Ok(Self::Auto),
|
// "auto" => Ok(Self::Auto),
|
||||||
|
@ -474,6 +474,7 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
AtRule::Return(_) => {
|
AtRule::Return(_) => {
|
||||||
return Err("This at-rule is not allowed here.".into())
|
return Err("This at-rule is not allowed here.".into())
|
||||||
}
|
}
|
||||||
|
AtRule::For(s) => rules.extend(s),
|
||||||
u @ AtRule::Unknown(..) => rules.push(Stmt::AtRule(u)),
|
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::Warn(a, b) => Ok(Some(Expr::Warn(a, b))),
|
||||||
AtRule::Error(pos, err) => Err(SassError::new(err, pos)),
|
AtRule::Error(pos, err) => Err(SassError::new(err, pos)),
|
||||||
AtRule::Return(_) => todo!("@return in unexpected location!"),
|
AtRule::Return(_) => todo!("@return in unexpected location!"),
|
||||||
|
f @ AtRule::For(..) => Ok(Some(Expr::AtRule(f))),
|
||||||
u @ AtRule::Unknown(..) => Ok(Some(Expr::AtRule(u))),
|
u @ AtRule::Unknown(..) => Ok(Some(Expr::AtRule(u))),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user