From 4b0b6442645da1f36085b979b49867d5dd5552dc Mon Sep 17 00:00:00 2001 From: Connor Skees Date: Wed, 8 Jul 2020 10:15:54 -0400 Subject: [PATCH] refactor control flow parsing into separate module --- src/parse/control_flow.rs | 527 ++++++++++++++++++++++++++++++++++++++ src/parse/mod.rs | 520 +------------------------------------ 2 files changed, 530 insertions(+), 517 deletions(-) create mode 100644 src/parse/control_flow.rs diff --git a/src/parse/control_flow.rs b/src/parse/control_flow.rs new file mode 100644 index 0000000..9730370 --- /dev/null +++ b/src/parse/control_flow.rs @@ -0,0 +1,527 @@ +use codemap::Spanned; +use num_traits::cast::ToPrimitive; +use peekmore::PeekMore; + +use crate::{ + common::Identifier, + error::SassResult, + parse::{ContextFlags, Parser, Stmt}, + unit::Unit, + utils::{ + peek_ident_no_interpolation, read_until_closing_curly_brace, read_until_closing_quote, + read_until_open_curly_brace, + }, + value::{Number, Value}, + Token, +}; + +impl<'a> Parser<'a> { + pub(super) fn parse_if(&mut self) -> SassResult> { + self.whitespace_or_comment(); + + let mut found_true = false; + let mut body = Vec::new(); + + let init_cond = self.parse_value()?.node; + + // consume the open curly brace + let span_before = match self.toks.next() { + Some(Token { kind: '{', pos }) => pos, + Some(..) | None => return Err(("expected \"{\".", self.span_before).into()), + }; + + if self.toks.peek().is_none() { + return Err(("expected \"}\".", span_before).into()); + } + + self.whitespace_or_comment(); + + if init_cond.is_true() { + found_true = true; + body = Parser { + toks: self.toks, + map: self.map, + path: self.path, + scopes: self.scopes, + global_scope: self.global_scope, + super_selectors: self.super_selectors, + span_before: self.span_before, + content: self.content, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, + at_root: self.at_root, + at_root_has_selector: self.at_root_has_selector, + extender: self.extender, + } + .parse_stmt()?; + } else { + self.throw_away_until_closing_curly_brace()?; + } + + loop { + self.whitespace_or_comment(); + if let Some(Token { kind: '@', pos }) = self.toks.peek().cloned() { + self.toks.peek_forward(1); + let ident = peek_ident_no_interpolation(self.toks, false, pos)?; + if ident.as_str() != "else" { + self.toks.reset_cursor(); + break; + } + self.toks.truncate_iterator_to_cursor(); + } else { + break; + } + self.whitespace_or_comment(); + if let Some(tok) = self.toks.peek().cloned() { + match tok.kind { + 'i' if matches!( + self.toks.peek_forward(1), + Some(Token { kind: 'f', .. }) | Some(Token { kind: 'F', .. }) + ) => + { + self.toks.next(); + self.toks.next(); + let cond = if found_true { + self.throw_away_until_open_curly_brace()?; + false + } else { + let v = self.parse_value()?.node.is_true(); + match self.toks.next() { + Some(Token { kind: '{', .. }) => {} + Some(..) | None => { + return Err(("expected \"{\".", self.span_before).into()) + } + } + v + }; + if cond { + found_true = true; + body = Parser { + toks: self.toks, + map: self.map, + path: self.path, + scopes: self.scopes, + global_scope: self.global_scope, + super_selectors: self.super_selectors, + span_before: self.span_before, + content: self.content, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, + at_root: self.at_root, + at_root_has_selector: self.at_root_has_selector, + extender: self.extender, + } + .parse_stmt()?; + } else { + self.throw_away_until_closing_curly_brace()?; + } + self.whitespace(); + } + '{' => { + self.toks.next(); + if found_true { + self.throw_away_until_closing_curly_brace()?; + break; + } else { + return Parser { + toks: self.toks, + map: self.map, + path: self.path, + scopes: self.scopes, + global_scope: self.global_scope, + super_selectors: self.super_selectors, + span_before: self.span_before, + content: self.content, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, + at_root: self.at_root, + at_root_has_selector: self.at_root_has_selector, + extender: self.extender, + } + .parse_stmt(); + } + } + _ => { + return Err(("expected \"{\".", tok.pos()).into()); + } + } + } else { + break; + } + } + self.whitespace(); + + Ok(body) + } + + pub(super) fn parse_for(&mut self) -> SassResult> { + self.whitespace(); + let next = self + .toks + .next() + .ok_or(("expected \"$\".", self.span_before))?; + let var: Spanned = match next.kind { + '$' => self + .parse_identifier_no_interpolation(false)? + .map_node(|i| i.into()), + _ => return Err(("expected \"$\".", self.span_before).into()), + }; + self.whitespace(); + self.span_before = match self.toks.peek() { + Some(tok) => tok.pos, + None => return Err(("Expected \"from\".", var.span).into()), + }; + if self.parse_identifier()?.node.to_ascii_lowercase() != "from" { + return Err(("Expected \"from\".", var.span).into()); + } + self.whitespace(); + let mut from_toks = Vec::new(); + let mut through = 0; + while let Some(tok) = self.toks.peek().cloned() { + match tok.kind { + 't' | 'T' | '\\' => { + let ident = peek_ident_no_interpolation(self.toks, false, tok.pos)?; + match ident.node.to_ascii_lowercase().as_str() { + "through" => { + through = 1; + self.toks.truncate_iterator_to_cursor(); + break; + } + "to" => { + self.toks.truncate_iterator_to_cursor(); + break; + } + _ => { + from_toks.push(tok); + self.toks.next(); + self.toks.reset_cursor(); + } + } + } + '$' => { + from_toks.push(tok); + self.toks.next(); + while let Some(tok) = self.toks.peek() { + if matches!(tok.kind, '0'..='9' | 'a'..='z' | 'A'..='Z' | '\\' | '-' | '_') + { + from_toks.push(self.toks.next().unwrap()); + } else { + break; + } + } + } + '{' => { + return Err(("Expected \"to\" or \"through\".", tok.pos()).into()); + } + '#' => { + from_toks.push(tok); + self.toks.next(); + match self.toks.peek() { + Some(Token { kind: '{', .. }) => { + from_toks.push(self.toks.next().unwrap()); + from_toks.append(&mut read_until_closing_curly_brace(self.toks)?); + } + Some(..) => {} + None => return Err(("expected \"{\".", self.span_before).into()), + } + } + q @ '\'' | q @ '"' => { + from_toks.push(tok); + self.toks.next(); + from_toks.append(&mut read_until_closing_quote(self.toks, q)?); + } + _ => { + from_toks.push(tok); + self.toks.next(); + } + } + } + self.whitespace(); + let from_val = self.parse_value_from_vec(from_toks)?; + let from = match from_val.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()), + }, + v => { + return Err(( + format!("{} is not an integer.", v.inspect(from_val.span)?), + from_val.span, + ) + .into()) + } + }; + + let to_val = self.parse_value()?; + let to = match to_val.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()), + }, + v => { + return Err(( + format!("{} is not an integer.", v.to_css_string(to_val.span)?), + to_val.span, + ) + .into()) + } + }; + + // consume the open curly brace + match self.toks.next() { + Some(Token { kind: '{', pos }) => pos, + Some(..) | None => return Err(("expected \"{\".", to_val.span).into()), + }; + + let body = read_until_closing_curly_brace(self.toks)?; + self.toks.next(); + + self.whitespace(); + + let (mut x, mut y); + // we can't use an inclusive range here + #[allow(clippy::range_plus_one)] + let iter: &mut dyn Iterator = if from < to { + x = from..(to + through); + &mut x + } else { + y = ((to - through)..(from + 1)).skip(1).rev(); + &mut y + }; + + let mut stmts = Vec::new(); + + self.scopes.push(self.scopes.last().clone()); + + for i in iter { + self.scopes.last_mut().insert_var( + var.node.clone(), + Spanned { + node: Value::Dimension(Number::from(i), Unit::None), + span: var.span, + }, + ); + if self.flags.in_function() { + let these_stmts = Parser { + toks: &mut body.clone().into_iter().peekmore(), + map: self.map, + path: self.path, + scopes: self.scopes, + global_scope: self.global_scope, + super_selectors: self.super_selectors, + span_before: self.span_before, + content: self.content, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, + at_root: self.at_root, + at_root_has_selector: self.at_root_has_selector, + extender: self.extender, + } + .parse()?; + if !these_stmts.is_empty() { + return Ok(these_stmts); + } + } else { + stmts.append( + &mut Parser { + toks: &mut body.clone().into_iter().peekmore(), + map: self.map, + path: self.path, + scopes: self.scopes, + global_scope: self.global_scope, + super_selectors: self.super_selectors, + span_before: self.span_before, + content: self.content, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, + at_root: self.at_root, + at_root_has_selector: self.at_root_has_selector, + extender: self.extender, + } + .parse()?, + ); + } + } + + self.scopes.pop(); + + Ok(stmts) + } + + pub(super) fn parse_while(&mut self) -> SassResult> { + self.whitespace(); + let cond = read_until_open_curly_brace(self.toks)?; + + if cond.is_empty() { + return Err(("Expected expression.", self.span_before).into()); + } + + self.toks.next(); + + let mut body = read_until_closing_curly_brace(self.toks)?; + + body.push(match self.toks.next() { + Some(tok) => tok, + None => return Err(("expected \"}\".", self.span_before).into()), + }); + + self.whitespace(); + + let mut stmts = Vec::new(); + let mut val = self.parse_value_from_vec(cond.clone())?; + self.scopes.push(self.scopes.last().clone()); + while val.node.is_true() { + if self.flags.in_function() { + let these_stmts = Parser { + toks: &mut body.clone().into_iter().peekmore(), + map: self.map, + path: self.path, + scopes: self.scopes, + global_scope: self.global_scope, + super_selectors: self.super_selectors, + span_before: self.span_before, + content: self.content, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, + at_root: self.at_root, + at_root_has_selector: self.at_root_has_selector, + extender: self.extender, + } + .parse()?; + if !these_stmts.is_empty() { + return Ok(these_stmts); + } + } else { + stmts.append( + &mut Parser { + toks: &mut body.clone().into_iter().peekmore(), + map: self.map, + path: self.path, + scopes: self.scopes, + global_scope: self.global_scope, + super_selectors: self.super_selectors, + span_before: self.span_before, + content: self.content, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, + at_root: self.at_root, + at_root_has_selector: self.at_root_has_selector, + extender: self.extender, + } + .parse()?, + ); + } + val = self.parse_value_from_vec(cond.clone())?; + } + self.scopes.pop(); + + Ok(stmts) + } + + pub(super) fn parse_each(&mut self) -> SassResult> { + self.whitespace(); + let mut vars: Vec> = Vec::new(); + + loop { + let next = self + .toks + .next() + .ok_or(("expected \"$\".", self.span_before))?; + + match next.kind { + '$' => vars.push(self.parse_identifier()?.map_node(|i| i.into())), + _ => return Err(("expected \"$\".", next.pos()).into()), + } + self.whitespace(); + if self + .toks + .peek() + .ok_or(("expected \"$\".", vars[vars.len() - 1].span))? + .kind + == ',' + { + self.toks.next(); + self.whitespace(); + } else { + break; + } + } + let i = self.parse_identifier()?; + if i.node.to_ascii_lowercase() != "in" { + return Err(("Expected \"in\".", i.span).into()); + } + self.whitespace(); + let iter_val_toks = read_until_open_curly_brace(self.toks)?; + let iter = self.parse_value_from_vec(iter_val_toks)?.node.as_list(); + self.toks.next(); + self.whitespace(); + let mut body = read_until_closing_curly_brace(self.toks)?; + body.push(match self.toks.next() { + Some(tok) => tok, + None => return Err(("expected \"}\".", self.span_before).into()), + }); + self.whitespace(); + + let mut stmts = Vec::new(); + + for row in iter { + if vars.len() == 1 { + self.scopes.last_mut().insert_var( + vars[0].node.clone(), + Spanned { + node: row, + span: vars[0].span, + }, + ); + } else { + for (var, val) in vars.iter().zip( + row.as_list() + .into_iter() + .chain(std::iter::once(Value::Null).cycle()), + ) { + self.scopes.last_mut().insert_var( + var.node.clone(), + Spanned { + node: val, + span: var.span, + }, + ); + } + } + + if self.flags.in_function() { + let these_stmts = Parser { + toks: &mut body.clone().into_iter().peekmore(), + map: self.map, + path: self.path, + scopes: self.scopes, + global_scope: self.global_scope, + super_selectors: self.super_selectors, + span_before: self.span_before, + content: self.content, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, + at_root: self.at_root, + at_root_has_selector: self.at_root_has_selector, + extender: self.extender, + } + .parse()?; + if !these_stmts.is_empty() { + return Ok(these_stmts); + } + } else { + stmts.append( + &mut Parser { + toks: &mut body.clone().into_iter().peekmore(), + map: self.map, + path: self.path, + scopes: self.scopes, + global_scope: self.global_scope, + super_selectors: self.super_selectors, + span_before: self.span_before, + content: self.content, + flags: self.flags | ContextFlags::IN_CONTROL_FLOW, + at_root: self.at_root, + at_root_has_selector: self.at_root_has_selector, + extender: self.extender, + } + .parse()?, + ); + } + } + + Ok(stmts) + } +} diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 0801b3e..bc7858e 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1,7 +1,6 @@ use std::{convert::TryFrom, path::Path, vec::IntoIter}; use codemap::{CodeMap, Span, Spanned}; -use num_traits::cast::ToPrimitive; use peekmore::{PeekMore, PeekMoreIterator}; use crate::{ @@ -10,19 +9,14 @@ use crate::{ media::MediaRule, AtRuleKind, Content, SupportsRule, UnknownAtRule, }, - common::Identifier, error::SassResult, scope::Scope, selector::{ ComplexSelectorComponent, ExtendRule, ExtendedSelector, Extender, Selector, SelectorParser, }, style::Style, - unit::Unit, - utils::{ - peek_ident_no_interpolation, read_until_closing_curly_brace, read_until_closing_quote, - read_until_open_curly_brace, read_until_semicolon_or_closing_curly_brace, - }, - value::{Number, Value}, + utils::{read_until_closing_curly_brace, read_until_semicolon_or_closing_curly_brace}, + value::Value, {Cow, Token}, }; @@ -32,6 +26,7 @@ pub(crate) use value::{HigherIntermediateValue, ValueVisitor}; mod args; pub mod common; +mod control_flow; mod function; mod ident; mod import; @@ -504,515 +499,6 @@ impl<'a> Parser<'a> { } impl<'a> Parser<'a> { - fn parse_if(&mut self) -> SassResult> { - self.whitespace_or_comment(); - - let mut found_true = false; - let mut body = Vec::new(); - - let init_cond = self.parse_value()?.node; - - // consume the open curly brace - let span_before = match self.toks.next() { - Some(Token { kind: '{', pos }) => pos, - Some(..) | None => return Err(("expected \"{\".", self.span_before).into()), - }; - - if self.toks.peek().is_none() { - return Err(("expected \"}\".", span_before).into()); - } - - self.whitespace_or_comment(); - - if init_cond.is_true() { - found_true = true; - body = Parser { - toks: self.toks, - map: self.map, - path: self.path, - scopes: self.scopes, - global_scope: self.global_scope, - super_selectors: self.super_selectors, - span_before: self.span_before, - content: self.content, - flags: self.flags | ContextFlags::IN_CONTROL_FLOW, - at_root: self.at_root, - at_root_has_selector: self.at_root_has_selector, - extender: self.extender, - } - .parse_stmt()?; - } else { - self.throw_away_until_closing_curly_brace()?; - } - - loop { - self.whitespace_or_comment(); - if let Some(Token { kind: '@', pos }) = self.toks.peek().cloned() { - self.toks.peek_forward(1); - let ident = peek_ident_no_interpolation(self.toks, false, pos)?; - if ident.as_str() != "else" { - self.toks.reset_cursor(); - break; - } - self.toks.truncate_iterator_to_cursor(); - } else { - break; - } - self.whitespace_or_comment(); - if let Some(tok) = self.toks.peek().cloned() { - match tok.kind { - 'i' if matches!( - self.toks.peek_forward(1), - Some(Token { kind: 'f', .. }) | Some(Token { kind: 'F', .. }) - ) => - { - self.toks.next(); - self.toks.next(); - let cond = if found_true { - self.throw_away_until_open_curly_brace()?; - false - } else { - let v = self.parse_value()?.node.is_true(); - match self.toks.next() { - Some(Token { kind: '{', .. }) => {} - Some(..) | None => { - return Err(("expected \"{\".", self.span_before).into()) - } - } - v - }; - if cond { - found_true = true; - body = Parser { - toks: self.toks, - map: self.map, - path: self.path, - scopes: self.scopes, - global_scope: self.global_scope, - super_selectors: self.super_selectors, - span_before: self.span_before, - content: self.content, - flags: self.flags | ContextFlags::IN_CONTROL_FLOW, - at_root: self.at_root, - at_root_has_selector: self.at_root_has_selector, - extender: self.extender, - } - .parse_stmt()?; - } else { - self.throw_away_until_closing_curly_brace()?; - } - self.whitespace(); - } - '{' => { - self.toks.next(); - if found_true { - self.throw_away_until_closing_curly_brace()?; - break; - } else { - return Parser { - toks: self.toks, - map: self.map, - path: self.path, - scopes: self.scopes, - global_scope: self.global_scope, - super_selectors: self.super_selectors, - span_before: self.span_before, - content: self.content, - flags: self.flags | ContextFlags::IN_CONTROL_FLOW, - at_root: self.at_root, - at_root_has_selector: self.at_root_has_selector, - extender: self.extender, - } - .parse_stmt(); - } - } - _ => { - return Err(("expected \"{\".", tok.pos()).into()); - } - } - } else { - break; - } - } - self.whitespace(); - - Ok(body) - } - - fn parse_for(&mut self) -> SassResult> { - self.whitespace(); - let next = self - .toks - .next() - .ok_or(("expected \"$\".", self.span_before))?; - let var: Spanned = match next.kind { - '$' => self - .parse_identifier_no_interpolation(false)? - .map_node(|i| i.into()), - _ => return Err(("expected \"$\".", self.span_before).into()), - }; - self.whitespace(); - self.span_before = match self.toks.peek() { - Some(tok) => tok.pos, - None => return Err(("Expected \"from\".", var.span).into()), - }; - if self.parse_identifier()?.node.to_ascii_lowercase() != "from" { - return Err(("Expected \"from\".", var.span).into()); - } - self.whitespace(); - let mut from_toks = Vec::new(); - let mut through = 0; - while let Some(tok) = self.toks.peek().cloned() { - match tok.kind { - 't' | 'T' | '\\' => { - let ident = peek_ident_no_interpolation(self.toks, false, tok.pos)?; - match ident.node.to_ascii_lowercase().as_str() { - "through" => { - through = 1; - self.toks.truncate_iterator_to_cursor(); - break; - } - "to" => { - self.toks.truncate_iterator_to_cursor(); - break; - } - _ => { - from_toks.push(tok); - self.toks.next(); - self.toks.reset_cursor(); - } - } - } - '$' => { - from_toks.push(tok); - self.toks.next(); - while let Some(tok) = self.toks.peek() { - if matches!(tok.kind, '0'..='9' | 'a'..='z' | 'A'..='Z' | '\\' | '-' | '_') - { - from_toks.push(self.toks.next().unwrap()); - } else { - break; - } - } - } - '{' => { - return Err(("Expected \"to\" or \"through\".", tok.pos()).into()); - } - '#' => { - from_toks.push(tok); - self.toks.next(); - match self.toks.peek() { - Some(Token { kind: '{', .. }) => { - from_toks.push(self.toks.next().unwrap()); - from_toks.append(&mut read_until_closing_curly_brace(self.toks)?); - } - Some(..) => {} - None => return Err(("expected \"{\".", self.span_before).into()), - } - } - q @ '\'' | q @ '"' => { - from_toks.push(tok); - self.toks.next(); - from_toks.append(&mut read_until_closing_quote(self.toks, q)?); - } - _ => { - from_toks.push(tok); - self.toks.next(); - } - } - } - self.whitespace(); - let from_val = self.parse_value_from_vec(from_toks)?; - let from = match from_val.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()), - }, - v => { - return Err(( - format!("{} is not an integer.", v.inspect(from_val.span)?), - from_val.span, - ) - .into()) - } - }; - - let to_val = self.parse_value()?; - let to = match to_val.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()), - }, - v => { - return Err(( - format!("{} is not an integer.", v.to_css_string(to_val.span)?), - to_val.span, - ) - .into()) - } - }; - - // consume the open curly brace - match self.toks.next() { - Some(Token { kind: '{', pos }) => pos, - Some(..) | None => return Err(("expected \"{\".", to_val.span).into()), - }; - - let body = read_until_closing_curly_brace(self.toks)?; - self.toks.next(); - - self.whitespace(); - - let (mut x, mut y); - // we can't use an inclusive range here - #[allow(clippy::range_plus_one)] - let iter: &mut dyn Iterator = if from < to { - x = from..(to + through); - &mut x - } else { - y = ((to - through)..(from + 1)).skip(1).rev(); - &mut y - }; - - let mut stmts = Vec::new(); - - self.scopes.push(self.scopes.last().clone()); - - for i in iter { - self.scopes.last_mut().insert_var( - var.node.clone(), - Spanned { - node: Value::Dimension(Number::from(i), Unit::None), - span: var.span, - }, - ); - if self.flags.in_function() { - let these_stmts = Parser { - toks: &mut body.clone().into_iter().peekmore(), - map: self.map, - path: self.path, - scopes: self.scopes, - global_scope: self.global_scope, - super_selectors: self.super_selectors, - span_before: self.span_before, - content: self.content, - flags: self.flags | ContextFlags::IN_CONTROL_FLOW, - at_root: self.at_root, - at_root_has_selector: self.at_root_has_selector, - extender: self.extender, - } - .parse()?; - if !these_stmts.is_empty() { - return Ok(these_stmts); - } - } else { - stmts.append( - &mut Parser { - toks: &mut body.clone().into_iter().peekmore(), - map: self.map, - path: self.path, - scopes: self.scopes, - global_scope: self.global_scope, - super_selectors: self.super_selectors, - span_before: self.span_before, - content: self.content, - flags: self.flags | ContextFlags::IN_CONTROL_FLOW, - at_root: self.at_root, - at_root_has_selector: self.at_root_has_selector, - extender: self.extender, - } - .parse()?, - ); - } - } - - self.scopes.pop(); - - Ok(stmts) - } - - fn parse_while(&mut self) -> SassResult> { - self.whitespace(); - let cond = read_until_open_curly_brace(self.toks)?; - - if cond.is_empty() { - return Err(("Expected expression.", self.span_before).into()); - } - - self.toks.next(); - - let mut body = read_until_closing_curly_brace(self.toks)?; - - body.push(match self.toks.next() { - Some(tok) => tok, - None => return Err(("expected \"}\".", self.span_before).into()), - }); - - self.whitespace(); - - let mut stmts = Vec::new(); - let mut val = self.parse_value_from_vec(cond.clone())?; - self.scopes.push(self.scopes.last().clone()); - while val.node.is_true() { - if self.flags.in_function() { - let these_stmts = Parser { - toks: &mut body.clone().into_iter().peekmore(), - map: self.map, - path: self.path, - scopes: self.scopes, - global_scope: self.global_scope, - super_selectors: self.super_selectors, - span_before: self.span_before, - content: self.content, - flags: self.flags | ContextFlags::IN_CONTROL_FLOW, - at_root: self.at_root, - at_root_has_selector: self.at_root_has_selector, - extender: self.extender, - } - .parse()?; - if !these_stmts.is_empty() { - return Ok(these_stmts); - } - } else { - stmts.append( - &mut Parser { - toks: &mut body.clone().into_iter().peekmore(), - map: self.map, - path: self.path, - scopes: self.scopes, - global_scope: self.global_scope, - super_selectors: self.super_selectors, - span_before: self.span_before, - content: self.content, - flags: self.flags | ContextFlags::IN_CONTROL_FLOW, - at_root: self.at_root, - at_root_has_selector: self.at_root_has_selector, - extender: self.extender, - } - .parse()?, - ); - } - val = self.parse_value_from_vec(cond.clone())?; - } - self.scopes.pop(); - - Ok(stmts) - } - - fn parse_each(&mut self) -> SassResult> { - self.whitespace(); - let mut vars: Vec> = Vec::new(); - - loop { - let next = self - .toks - .next() - .ok_or(("expected \"$\".", self.span_before))?; - - match next.kind { - '$' => vars.push(self.parse_identifier()?.map_node(|i| i.into())), - _ => return Err(("expected \"$\".", next.pos()).into()), - } - self.whitespace(); - if self - .toks - .peek() - .ok_or(("expected \"$\".", vars[vars.len() - 1].span))? - .kind - == ',' - { - self.toks.next(); - self.whitespace(); - } else { - break; - } - } - let i = self.parse_identifier()?; - if i.node.to_ascii_lowercase() != "in" { - return Err(("Expected \"in\".", i.span).into()); - } - self.whitespace(); - let iter_val_toks = read_until_open_curly_brace(self.toks)?; - let iter = self.parse_value_from_vec(iter_val_toks)?.node.as_list(); - self.toks.next(); - self.whitespace(); - let mut body = read_until_closing_curly_brace(self.toks)?; - body.push(match self.toks.next() { - Some(tok) => tok, - None => return Err(("expected \"}\".", self.span_before).into()), - }); - self.whitespace(); - - let mut stmts = Vec::new(); - - for row in iter { - if vars.len() == 1 { - self.scopes.last_mut().insert_var( - vars[0].node.clone(), - Spanned { - node: row, - span: vars[0].span, - }, - ); - } else { - for (var, val) in vars.iter().zip( - row.as_list() - .into_iter() - .chain(std::iter::once(Value::Null).cycle()), - ) { - self.scopes.last_mut().insert_var( - var.node.clone(), - Spanned { - node: val, - span: var.span, - }, - ); - } - } - - if self.flags.in_function() { - let these_stmts = Parser { - toks: &mut body.clone().into_iter().peekmore(), - map: self.map, - path: self.path, - scopes: self.scopes, - global_scope: self.global_scope, - super_selectors: self.super_selectors, - span_before: self.span_before, - content: self.content, - flags: self.flags | ContextFlags::IN_CONTROL_FLOW, - at_root: self.at_root, - at_root_has_selector: self.at_root_has_selector, - extender: self.extender, - } - .parse()?; - if !these_stmts.is_empty() { - return Ok(these_stmts); - } - } else { - stmts.append( - &mut Parser { - toks: &mut body.clone().into_iter().peekmore(), - map: self.map, - path: self.path, - scopes: self.scopes, - global_scope: self.global_scope, - super_selectors: self.super_selectors, - span_before: self.span_before, - content: self.content, - flags: self.flags | ContextFlags::IN_CONTROL_FLOW, - at_root: self.at_root, - at_root_has_selector: self.at_root_has_selector, - extender: self.extender, - } - .parse()?, - ); - } - } - - Ok(stmts) - } - fn parse_unknown_at_rule(&mut self, name: String) -> SassResult { let mut params = String::new(); self.whitespace();