handle negative values in @for

This commit is contained in:
ConnorSkees 2020-05-02 15:11:58 -04:00
parent 4321a383f2
commit 96e916e750
4 changed files with 51 additions and 19 deletions

View File

@ -1,4 +1,5 @@
use std::iter::Iterator; use std::iter::{Iterator, Rev, Skip};
use std::ops::Range;
use codemap::{Span, Spanned}; use codemap::{Span, Spanned};
@ -20,11 +21,27 @@ use crate::utils::{
use crate::value::{Number, Value}; use crate::value::{Number, Value};
use crate::{Stmt, Token}; use crate::{Stmt, Token};
pub(crate) enum ForIterator {
Forward(Range<isize>),
Backward(Rev<Skip<Range<isize>>>),
}
impl Iterator for ForIterator {
type Item = isize;
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::Forward(i) => i.next(),
Self::Backward(i) => i.next(),
}
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct For { pub(crate) struct For {
pub var: Spanned<String>, pub var: Spanned<String>,
// TODO: optimization: this could be a generic or &dyn Iterator maybe? from: isize,
pub iter: Vec<usize>, to: isize,
through: isize,
pub body: Vec<Token>, pub body: Vec<Token>,
} }
@ -36,7 +53,7 @@ impl For {
content: Option<&[Spanned<Stmt>]>, content: Option<&[Spanned<Stmt>]>,
) -> SassResult<Vec<Spanned<Stmt>>> { ) -> SassResult<Vec<Spanned<Stmt>>> {
let mut stmts = Vec::new(); let mut stmts = Vec::new();
for i in self.iter { for i in self.iter() {
scope.insert_var( scope.insert_var(
&self.var.node, &self.var.node,
Spanned { Spanned {
@ -45,7 +62,7 @@ impl For {
}, },
)?; )?;
ruleset_eval( ruleset_eval(
&mut self.body.clone().into_iter().peekmore(), &mut self.body.iter().cloned().peekmore(),
scope, scope,
super_selector, super_selector,
false, false,
@ -56,8 +73,12 @@ impl For {
Ok(stmts) Ok(stmts)
} }
pub fn iter(&self) -> std::slice::Iter<'_, usize> { pub fn iter(&self) -> ForIterator {
self.iter.iter() if self.from < self.to {
ForIterator::Forward(self.from..(self.to + self.through))
} else {
ForIterator::Backward(((self.to - self.through)..(self.from + 1)).skip(1).rev())
}
} }
} }
@ -110,8 +131,8 @@ pub(crate) fn parse_for<I: Iterator<Item = Token>>(
} }
devour_whitespace(toks); devour_whitespace(toks);
let from_val = Value::from_vec(from_toks, scope, super_selector)?; let from_val = Value::from_vec(from_toks, scope, super_selector)?;
let from = match from_val.node { let from = match from_val.node.eval(from_val.span)?.node {
Value::Dimension(n, _) => match n.to_integer().to_usize() { Value::Dimension(n, _) => match n.to_integer().to_isize() {
Some(v) => v, Some(v) => v,
None => return Err((format!("{} is not a int.", n), from_val.span).into()), None => return Err((format!("{} is not a int.", n), from_val.span).into()),
}, },
@ -127,8 +148,8 @@ pub(crate) fn parse_for<I: Iterator<Item = Token>>(
let to_toks = read_until_open_curly_brace(toks); let to_toks = read_until_open_curly_brace(toks);
toks.next(); toks.next();
let to_val = Value::from_vec(to_toks, scope, super_selector)?; let to_val = Value::from_vec(to_toks, scope, super_selector)?;
let to = match to_val.node { let to = match to_val.node.eval(to_val.span)?.node {
Value::Dimension(n, _) => match n.to_integer().to_usize() { Value::Dimension(n, _) => match n.to_integer().to_isize() {
Some(v) => v, Some(v) => v,
None => return Err((format!("{} is not a int.", n), to_val.span).into()), None => return Err((format!("{} is not a int.", n), to_val.span).into()),
}, },
@ -145,11 +166,11 @@ pub(crate) fn parse_for<I: Iterator<Item = Token>>(
devour_whitespace(toks); devour_whitespace(toks);
let iter = if from < to { Ok(AtRule::For(For {
(from..(to + through)).collect() from,
} else { to,
((to - through)..(from + 1)).skip(1).rev().collect() through,
}; body,
var,
Ok(AtRule::For(For { iter, body, var })) }))
} }

View File

@ -141,7 +141,7 @@ impl Function {
)); ));
} }
Stmt::AtRule(AtRule::For(f)) => { Stmt::AtRule(AtRule::For(f)) => {
for i in f.iter().cloned() { for i in f.iter() {
self.scope.insert_var( self.scope.insert_var(
&f.var.node, &f.var.node,
Spanned { Spanned {

View File

@ -157,6 +157,7 @@ impl From<f64> for Number {
from_integer!(u16); from_integer!(u16);
from_integer!(usize); from_integer!(usize);
from_integer!(isize);
from_integer!(i32); from_integer!(i32);
from_integer!(u32); from_integer!(u32);
from_integer!(u8); from_integer!(u8);

View File

@ -58,3 +58,13 @@ test!(
"a {\n @for $i from 1 to 3 {\n @if $i==2 {\n color: red;\n }\n }\n}\n", "a {\n @for $i from 1 to 3 {\n @if $i==2 {\n color: red;\n }\n }\n}\n",
"a {\n color: red;\n}\n" "a {\n color: red;\n}\n"
); );
test!(
from_negative_to_positive,
"@for $i from -1 to 3 {\n a {\n color: red;\n }\n}\n",
"a {\n color: red;\n}\n\na {\n color: red;\n}\n\na {\n color: red;\n}\n\na {\n color: red;\n}\n"
);
test!(
from_negative_to_negative,
"@for $i from -1 to -3 {\n a {\n color: red;\n }\n}\n",
"a {\n color: red;\n}\n\na {\n color: red;\n}\n"
);