diff --git a/src/atrule/for_rule.rs b/src/atrule/for_rule.rs index 0d19b8b..b1e3a8b 100644 --- a/src/atrule/for_rule.rs +++ b/src/atrule/for_rule.rs @@ -1,4 +1,5 @@ -use std::iter::Iterator; +use std::iter::{Iterator, Rev, Skip}; +use std::ops::Range; use codemap::{Span, Spanned}; @@ -20,11 +21,27 @@ use crate::utils::{ use crate::value::{Number, Value}; use crate::{Stmt, Token}; +pub(crate) enum ForIterator { + Forward(Range), + Backward(Rev>>), +} + +impl Iterator for ForIterator { + type Item = isize; + fn next(&mut self) -> Option { + match self { + Self::Forward(i) => i.next(), + Self::Backward(i) => i.next(), + } + } +} + #[derive(Debug, Clone)] pub(crate) struct For { pub var: Spanned, - // TODO: optimization: this could be a generic or &dyn Iterator maybe? - pub iter: Vec, + from: isize, + to: isize, + through: isize, pub body: Vec, } @@ -36,7 +53,7 @@ impl For { content: Option<&[Spanned]>, ) -> SassResult>> { let mut stmts = Vec::new(); - for i in self.iter { + for i in self.iter() { scope.insert_var( &self.var.node, Spanned { @@ -45,7 +62,7 @@ impl For { }, )?; ruleset_eval( - &mut self.body.clone().into_iter().peekmore(), + &mut self.body.iter().cloned().peekmore(), scope, super_selector, false, @@ -56,8 +73,12 @@ impl For { Ok(stmts) } - pub fn iter(&self) -> std::slice::Iter<'_, usize> { - self.iter.iter() + pub fn iter(&self) -> ForIterator { + 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>( } devour_whitespace(toks); let from_val = Value::from_vec(from_toks, scope, super_selector)?; - let from = match from_val.node { - Value::Dimension(n, _) => match n.to_integer().to_usize() { + let from = match from_val.node.eval(from_val.span)?.node { + Value::Dimension(n, _) => match n.to_integer().to_isize() { Some(v) => v, None => return Err((format!("{} is not a int.", n), from_val.span).into()), }, @@ -127,8 +148,8 @@ pub(crate) fn parse_for>( let to_toks = read_until_open_curly_brace(toks); toks.next(); let to_val = Value::from_vec(to_toks, scope, super_selector)?; - let to = match to_val.node { - Value::Dimension(n, _) => match n.to_integer().to_usize() { + let to = match to_val.node.eval(to_val.span)?.node { + Value::Dimension(n, _) => match n.to_integer().to_isize() { Some(v) => v, None => return Err((format!("{} is not a int.", n), to_val.span).into()), }, @@ -145,11 +166,11 @@ pub(crate) fn parse_for>( devour_whitespace(toks); - let iter = if from < to { - (from..(to + through)).collect() - } else { - ((to - through)..(from + 1)).skip(1).rev().collect() - }; - - Ok(AtRule::For(For { iter, body, var })) + Ok(AtRule::For(For { + from, + to, + through, + body, + var, + })) } diff --git a/src/atrule/function.rs b/src/atrule/function.rs index 87cb5a5..5c81975 100644 --- a/src/atrule/function.rs +++ b/src/atrule/function.rs @@ -141,7 +141,7 @@ impl Function { )); } Stmt::AtRule(AtRule::For(f)) => { - for i in f.iter().cloned() { + for i in f.iter() { self.scope.insert_var( &f.var.node, Spanned { diff --git a/src/value/number.rs b/src/value/number.rs index 791cdef..7034199 100644 --- a/src/value/number.rs +++ b/src/value/number.rs @@ -157,6 +157,7 @@ impl From for Number { from_integer!(u16); from_integer!(usize); +from_integer!(isize); from_integer!(i32); from_integer!(u32); from_integer!(u8); diff --git a/tests/for.rs b/tests/for.rs index 57ea36e..8efa921 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -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 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" +);