diff --git a/src/parse/control_flow.rs b/src/parse/control_flow.rs index 5ce5333..05a4d5f 100644 --- a/src/parse/control_flow.rs +++ b/src/parse/control_flow.rs @@ -22,7 +22,7 @@ impl<'a> Parser<'a> { let mut found_true = false; let mut body = Vec::new(); - let init_cond = self.parse_value(true)?.node; + let init_cond = self.parse_value(true, &|_| true)?.node; // consume the open curly brace let span_before = match self.toks.next() { @@ -86,7 +86,7 @@ impl<'a> Parser<'a> { self.throw_away_until_open_curly_brace()?; false } else { - let v = self.parse_value(true)?.node.is_true(); + let v = self.parse_value(true, &|_| true)?.node.is_true(); match self.toks.next() { Some(Token { kind: '{', .. }) => {} Some(..) | None => { @@ -255,7 +255,7 @@ impl<'a> Parser<'a> { } }; - let to_val = self.parse_value(true)?; + let to_val = self.parse_value(true, &|_| true)?; let to = match to_val.node { Value::Dimension(n, ..) => match n.to_integer().to_isize() { Some(v) => v, diff --git a/src/parse/import.rs b/src/parse/import.rs index debad53..f4cbf63 100644 --- a/src/parse/import.rs +++ b/src/parse/import.rs @@ -123,7 +123,7 @@ impl<'a> Parser<'a> { let Spanned { node: file_name_as_value, span, - } = self.parse_value(true)?; + } = self.parse_value(true, &|_| true)?; match file_name_as_value { Value::String(s, QuoteKind::Quoted) => { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 040f568..d5a3420 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -143,7 +143,7 @@ impl<'a> Parser<'a> { let Spanned { node: message, span, - } = self.parse_value(false)?; + } = self.parse_value(false, &|_| true)?; return Err(( message.inspect(span)?.to_string(), @@ -155,7 +155,7 @@ impl<'a> Parser<'a> { let Spanned { node: message, span, - } = self.parse_value(false)?; + } = self.parse_value(false, &|_| true)?; span.merge(kind_string.span); if let Some(Token { kind: ';', pos }) = self.toks.peek() { kind_string.span.merge(*pos); @@ -170,7 +170,7 @@ impl<'a> Parser<'a> { let Spanned { node: message, span, - } = self.parse_value(false)?; + } = self.parse_value(false, &|_| true)?; span.merge(kind_string.span); if let Some(Token { kind: ';', pos }) = self.toks.peek() { kind_string.span.merge(*pos); @@ -434,7 +434,7 @@ impl<'a> Parser<'a> { } pub fn parse_interpolation(&mut self) -> SassResult> { - let val = self.parse_value(true)?; + let val = self.parse_value(true, &|_| true)?; match self.toks.next() { Some(Token { kind: '}', .. }) => {} Some(..) | None => return Err(("expected \"}\".", val.span).into()), diff --git a/src/parse/style.rs b/src/parse/style.rs index d4052b1..8c1b56a 100644 --- a/src/parse/style.rs +++ b/src/parse/style.rs @@ -173,7 +173,7 @@ impl<'a> Parser<'a> { } fn parse_style_value(&mut self) -> SassResult> { - self.parse_value(false) + self.parse_value(false, &|_| true) } pub(super) fn parse_style_group( diff --git a/src/parse/value/parse.rs b/src/parse/value/parse.rs index 497a303..d70f89d 100644 --- a/src/parse/value/parse.rs +++ b/src/parse/value/parse.rs @@ -15,8 +15,8 @@ use crate::{ error::SassResult, unit::Unit, utils::{ - devour_whitespace, eat_number, read_until_char, read_until_closing_paren, - read_until_closing_square_brace, IsWhitespace, + devour_whitespace, eat_number, read_until_closing_paren, read_until_closing_square_brace, + IsWhitespace, }, value::{Number, SassFunction, SassMap, Value}, Token, @@ -52,7 +52,17 @@ impl IsWhitespace for IntermediateValue { } impl<'a> Parser<'a> { - pub(crate) fn parse_value(&mut self, in_paren: bool) -> SassResult> { + /// Parse a value from a stream of tokens + /// + /// This function will cease parsing if the predicate returns false. + /// + /// E.g. A predicate of `|c| c != 'a'` will cease parsing as soon as an + /// identifier beginning with `'a'` is encountered. + pub(crate) fn parse_value( + &mut self, + in_paren: bool, + predicate: &dyn Fn(char) -> bool, + ) -> SassResult> { self.whitespace(); let span = match self.toks.peek() { Some(Token { kind: '}', .. }) @@ -64,7 +74,7 @@ impl<'a> Parser<'a> { let mut last_was_whitespace = false; let mut space_separated = Vec::new(); let mut comma_separated = Vec::new(); - let mut iter = IntermediateValueIterator::new(self); + let mut iter = IntermediateValueIterator::new(self, &predicate); while let Some(val) = iter.next() { let val = val?; match val.node { @@ -182,6 +192,31 @@ impl<'a> Parser<'a> { }) } + fn parse_value_with_body( + &mut self, + toks: &mut peekmore::PeekMoreIterator>, + in_paren: bool, + predicate: &dyn Fn(char) -> bool, + ) -> SassResult> { + Parser { + 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, + at_root: self.at_root, + at_root_has_selector: self.at_root_has_selector, + extender: self.extender, + content_scopes: self.content_scopes, + options: self.options, + } + .parse_value(in_paren, predicate) + } + pub(crate) fn parse_value_from_vec( &mut self, toks: Vec, @@ -203,7 +238,7 @@ impl<'a> Parser<'a> { content_scopes: self.content_scopes, options: self.options, } - .parse_value(in_paren) + .parse_value(in_paren, &|_| true) } fn parse_ident_value(&mut self) -> SassResult> { @@ -340,9 +375,17 @@ impl<'a> Parser<'a> { } } - fn parse_intermediate_value(&mut self) -> Option>> { + fn parse_intermediate_value( + &mut self, + predicate: &dyn Fn(char) -> bool, + ) -> Option>> { let (kind, span) = match self.toks.peek() { - Some(v) => (v.kind, v.pos()), + Some(v) => { + if !predicate(v.kind) { + return None; + } + (v.kind, v.pos()) + } None => return None, }; @@ -720,6 +763,7 @@ impl<'a> Parser<'a> { struct IntermediateValueIterator<'a, 'b: 'a> { parser: &'a mut Parser<'b>, peek: Option>>, + predicate: &'a dyn Fn(char) -> bool, } impl<'a, 'b: 'a> Iterator for IntermediateValueIterator<'a, 'b> { @@ -728,14 +772,18 @@ impl<'a, 'b: 'a> Iterator for IntermediateValueIterator<'a, 'b> { if self.peek.is_some() { self.peek.take() } else { - self.parser.parse_intermediate_value() + self.parser.parse_intermediate_value(self.predicate) } } } impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> { - pub fn new(parser: &'a mut Parser<'b>) -> Self { - Self { parser, peek: None } + pub fn new(parser: &'a mut Parser<'b>, predicate: &'a dyn Fn(char) -> bool) -> Self { + Self { + parser, + peek: None, + predicate, + } } fn peek(&mut self) -> &Option>> { @@ -1062,16 +1110,13 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> { let paren_toks = &mut t.node.into_iter().peekmore(); let mut map = SassMap::new(); - let key_toks = read_until_char(paren_toks, ':')?; - if key_toks.iter().all(|t| t.is_whitespace()) { - return Ok(Spanned { - node: HigherIntermediateValue::Paren(Box::new(HigherIntermediateValue::Literal( - Value::Map(map), - ))), - span: t.span, - }); + let key = self + .parser + .parse_value_with_body(paren_toks, true, &|c| c != ':')?; + + if let Some(Token { kind: ':', .. }) = paren_toks.peek() { + paren_toks.next(); } - let key = self.parser.parse_value_from_vec(key_toks, true)?; if paren_toks.peek().is_none() { return Ok(Spanned { @@ -1084,7 +1129,11 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> { let val = self .parser - .parse_value_from_vec(read_until_char(paren_toks, ',')?, true)?; + .parse_value_with_body(paren_toks, true, &|c| c != ',')?; + + if let Some(Token { kind: ',', .. }) = paren_toks.peek() { + paren_toks.next(); + } map.insert(key.node, val.node); @@ -1102,12 +1151,20 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> { loop { let key = self .parser - .parse_value_from_vec(read_until_char(paren_toks, ':')?, true)?; + .parse_value_with_body(paren_toks, true, &|c| c != ':')?; + + if let Some(Token { kind: ':', .. }) = paren_toks.peek() { + paren_toks.next(); + } devour_whitespace(paren_toks); let val = self .parser - .parse_value_from_vec(read_until_char(paren_toks, ',')?, true)?; + .parse_value_with_body(paren_toks, true, &|c| c != ',')?; + + if let Some(Token { kind: ',', .. }) = paren_toks.peek() { + paren_toks.next(); + } span = span.merge(val.span); devour_whitespace(paren_toks); if map.insert(key.node.clone(), val.node) { diff --git a/src/utils/chars.rs b/src/utils/chars.rs index cb39bfa..d8af874 100644 --- a/src/utils/chars.rs +++ b/src/utils/chars.rs @@ -1,44 +1,3 @@ -use std::vec::IntoIter; - -use peekmore::PeekMoreIterator; - -use crate::{error::SassResult, Token}; - -use super::{read_until_closing_paren, read_until_closing_quote, read_until_newline}; -/// Reads until the char is found, consuming the char, -/// or until the end of the iterator is hit -pub(crate) fn read_until_char( - toks: &mut PeekMoreIterator>, - c: char, -) -> SassResult> { - let mut v = Vec::new(); - while let Some(tok) = toks.next() { - match tok.kind { - '"' | '\'' => { - v.push(tok); - v.extend(read_until_closing_quote(toks, tok.kind)?); - continue; - } - '(' => { - v.push(tok); - v.extend(read_until_closing_paren(toks)?); - continue; - } - '/' => { - match toks.peek() { - Some(Token { kind: '/', .. }) => read_until_newline(toks), - _ => v.push(tok), - }; - continue; - } - t if t == c => break, - _ => {} - } - v.push(tok) - } - Ok(v) -} - pub(crate) fn hex_char_for(number: u32) -> char { debug_assert!(number < 0x10); std::char::from_u32(if number < 0xA {