use a predicate to simplify and improve parsing of maps
This commit is contained in:
parent
f69b863e33
commit
ace9757897
@ -22,7 +22,7 @@ impl<'a> Parser<'a> {
|
|||||||
let mut found_true = false;
|
let mut found_true = false;
|
||||||
let mut body = Vec::new();
|
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
|
// consume the open curly brace
|
||||||
let span_before = match self.toks.next() {
|
let span_before = match self.toks.next() {
|
||||||
@ -86,7 +86,7 @@ impl<'a> Parser<'a> {
|
|||||||
self.throw_away_until_open_curly_brace()?;
|
self.throw_away_until_open_curly_brace()?;
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
let v = self.parse_value(true)?.node.is_true();
|
let v = self.parse_value(true, &|_| true)?.node.is_true();
|
||||||
match self.toks.next() {
|
match self.toks.next() {
|
||||||
Some(Token { kind: '{', .. }) => {}
|
Some(Token { kind: '{', .. }) => {}
|
||||||
Some(..) | None => {
|
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 {
|
let to = match to_val.node {
|
||||||
Value::Dimension(n, ..) => match n.to_integer().to_isize() {
|
Value::Dimension(n, ..) => match n.to_integer().to_isize() {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
|
@ -123,7 +123,7 @@ impl<'a> Parser<'a> {
|
|||||||
let Spanned {
|
let Spanned {
|
||||||
node: file_name_as_value,
|
node: file_name_as_value,
|
||||||
span,
|
span,
|
||||||
} = self.parse_value(true)?;
|
} = self.parse_value(true, &|_| true)?;
|
||||||
|
|
||||||
match file_name_as_value {
|
match file_name_as_value {
|
||||||
Value::String(s, QuoteKind::Quoted) => {
|
Value::String(s, QuoteKind::Quoted) => {
|
||||||
|
@ -143,7 +143,7 @@ impl<'a> Parser<'a> {
|
|||||||
let Spanned {
|
let Spanned {
|
||||||
node: message,
|
node: message,
|
||||||
span,
|
span,
|
||||||
} = self.parse_value(false)?;
|
} = self.parse_value(false, &|_| true)?;
|
||||||
|
|
||||||
return Err((
|
return Err((
|
||||||
message.inspect(span)?.to_string(),
|
message.inspect(span)?.to_string(),
|
||||||
@ -155,7 +155,7 @@ impl<'a> Parser<'a> {
|
|||||||
let Spanned {
|
let Spanned {
|
||||||
node: message,
|
node: message,
|
||||||
span,
|
span,
|
||||||
} = self.parse_value(false)?;
|
} = self.parse_value(false, &|_| true)?;
|
||||||
span.merge(kind_string.span);
|
span.merge(kind_string.span);
|
||||||
if let Some(Token { kind: ';', pos }) = self.toks.peek() {
|
if let Some(Token { kind: ';', pos }) = self.toks.peek() {
|
||||||
kind_string.span.merge(*pos);
|
kind_string.span.merge(*pos);
|
||||||
@ -170,7 +170,7 @@ impl<'a> Parser<'a> {
|
|||||||
let Spanned {
|
let Spanned {
|
||||||
node: message,
|
node: message,
|
||||||
span,
|
span,
|
||||||
} = self.parse_value(false)?;
|
} = self.parse_value(false, &|_| true)?;
|
||||||
span.merge(kind_string.span);
|
span.merge(kind_string.span);
|
||||||
if let Some(Token { kind: ';', pos }) = self.toks.peek() {
|
if let Some(Token { kind: ';', pos }) = self.toks.peek() {
|
||||||
kind_string.span.merge(*pos);
|
kind_string.span.merge(*pos);
|
||||||
@ -434,7 +434,7 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_interpolation(&mut self) -> SassResult<Spanned<Value>> {
|
pub fn parse_interpolation(&mut self) -> SassResult<Spanned<Value>> {
|
||||||
let val = self.parse_value(true)?;
|
let val = self.parse_value(true, &|_| true)?;
|
||||||
match self.toks.next() {
|
match self.toks.next() {
|
||||||
Some(Token { kind: '}', .. }) => {}
|
Some(Token { kind: '}', .. }) => {}
|
||||||
Some(..) | None => return Err(("expected \"}\".", val.span).into()),
|
Some(..) | None => return Err(("expected \"}\".", val.span).into()),
|
||||||
|
@ -173,7 +173,7 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_style_value(&mut self) -> SassResult<Spanned<Value>> {
|
fn parse_style_value(&mut self) -> SassResult<Spanned<Value>> {
|
||||||
self.parse_value(false)
|
self.parse_value(false, &|_| true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn parse_style_group(
|
pub(super) fn parse_style_group(
|
||||||
|
@ -15,8 +15,8 @@ use crate::{
|
|||||||
error::SassResult,
|
error::SassResult,
|
||||||
unit::Unit,
|
unit::Unit,
|
||||||
utils::{
|
utils::{
|
||||||
devour_whitespace, eat_number, read_until_char, read_until_closing_paren,
|
devour_whitespace, eat_number, read_until_closing_paren, read_until_closing_square_brace,
|
||||||
read_until_closing_square_brace, IsWhitespace,
|
IsWhitespace,
|
||||||
},
|
},
|
||||||
value::{Number, SassFunction, SassMap, Value},
|
value::{Number, SassFunction, SassMap, Value},
|
||||||
Token,
|
Token,
|
||||||
@ -52,7 +52,17 @@ impl IsWhitespace for IntermediateValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
pub(crate) fn parse_value(&mut self, in_paren: bool) -> SassResult<Spanned<Value>> {
|
/// 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<Spanned<Value>> {
|
||||||
self.whitespace();
|
self.whitespace();
|
||||||
let span = match self.toks.peek() {
|
let span = match self.toks.peek() {
|
||||||
Some(Token { kind: '}', .. })
|
Some(Token { kind: '}', .. })
|
||||||
@ -64,7 +74,7 @@ impl<'a> Parser<'a> {
|
|||||||
let mut last_was_whitespace = false;
|
let mut last_was_whitespace = false;
|
||||||
let mut space_separated = Vec::new();
|
let mut space_separated = Vec::new();
|
||||||
let mut comma_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() {
|
while let Some(val) = iter.next() {
|
||||||
let val = val?;
|
let val = val?;
|
||||||
match val.node {
|
match val.node {
|
||||||
@ -182,6 +192,31 @@ impl<'a> Parser<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_value_with_body(
|
||||||
|
&mut self,
|
||||||
|
toks: &mut peekmore::PeekMoreIterator<std::vec::IntoIter<Token>>,
|
||||||
|
in_paren: bool,
|
||||||
|
predicate: &dyn Fn(char) -> bool,
|
||||||
|
) -> SassResult<Spanned<Value>> {
|
||||||
|
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(
|
pub(crate) fn parse_value_from_vec(
|
||||||
&mut self,
|
&mut self,
|
||||||
toks: Vec<Token>,
|
toks: Vec<Token>,
|
||||||
@ -203,7 +238,7 @@ impl<'a> Parser<'a> {
|
|||||||
content_scopes: self.content_scopes,
|
content_scopes: self.content_scopes,
|
||||||
options: self.options,
|
options: self.options,
|
||||||
}
|
}
|
||||||
.parse_value(in_paren)
|
.parse_value(in_paren, &|_| true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ident_value(&mut self) -> SassResult<Spanned<IntermediateValue>> {
|
fn parse_ident_value(&mut self) -> SassResult<Spanned<IntermediateValue>> {
|
||||||
@ -340,9 +375,17 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_intermediate_value(&mut self) -> Option<SassResult<Spanned<IntermediateValue>>> {
|
fn parse_intermediate_value(
|
||||||
|
&mut self,
|
||||||
|
predicate: &dyn Fn(char) -> bool,
|
||||||
|
) -> Option<SassResult<Spanned<IntermediateValue>>> {
|
||||||
let (kind, span) = match self.toks.peek() {
|
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,
|
None => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -720,6 +763,7 @@ impl<'a> Parser<'a> {
|
|||||||
struct IntermediateValueIterator<'a, 'b: 'a> {
|
struct IntermediateValueIterator<'a, 'b: 'a> {
|
||||||
parser: &'a mut Parser<'b>,
|
parser: &'a mut Parser<'b>,
|
||||||
peek: Option<SassResult<Spanned<IntermediateValue>>>,
|
peek: Option<SassResult<Spanned<IntermediateValue>>>,
|
||||||
|
predicate: &'a dyn Fn(char) -> bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b: 'a> Iterator for IntermediateValueIterator<'a, 'b> {
|
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() {
|
if self.peek.is_some() {
|
||||||
self.peek.take()
|
self.peek.take()
|
||||||
} else {
|
} else {
|
||||||
self.parser.parse_intermediate_value()
|
self.parser.parse_intermediate_value(self.predicate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||||
pub fn new(parser: &'a mut Parser<'b>) -> Self {
|
pub fn new(parser: &'a mut Parser<'b>, predicate: &'a dyn Fn(char) -> bool) -> Self {
|
||||||
Self { parser, peek: None }
|
Self {
|
||||||
|
parser,
|
||||||
|
peek: None,
|
||||||
|
predicate,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&mut self) -> &Option<SassResult<Spanned<IntermediateValue>>> {
|
fn peek(&mut self) -> &Option<SassResult<Spanned<IntermediateValue>>> {
|
||||||
@ -1062,16 +1110,13 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
|||||||
let paren_toks = &mut t.node.into_iter().peekmore();
|
let paren_toks = &mut t.node.into_iter().peekmore();
|
||||||
|
|
||||||
let mut map = SassMap::new();
|
let mut map = SassMap::new();
|
||||||
let key_toks = read_until_char(paren_toks, ':')?;
|
let key = self
|
||||||
if key_toks.iter().all(|t| t.is_whitespace()) {
|
.parser
|
||||||
return Ok(Spanned {
|
.parse_value_with_body(paren_toks, true, &|c| c != ':')?;
|
||||||
node: HigherIntermediateValue::Paren(Box::new(HigherIntermediateValue::Literal(
|
|
||||||
Value::Map(map),
|
if let Some(Token { kind: ':', .. }) = paren_toks.peek() {
|
||||||
))),
|
paren_toks.next();
|
||||||
span: t.span,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
let key = self.parser.parse_value_from_vec(key_toks, true)?;
|
|
||||||
|
|
||||||
if paren_toks.peek().is_none() {
|
if paren_toks.peek().is_none() {
|
||||||
return Ok(Spanned {
|
return Ok(Spanned {
|
||||||
@ -1084,7 +1129,11 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
|||||||
|
|
||||||
let val = self
|
let val = self
|
||||||
.parser
|
.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);
|
map.insert(key.node, val.node);
|
||||||
|
|
||||||
@ -1102,12 +1151,20 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
|||||||
loop {
|
loop {
|
||||||
let key = self
|
let key = self
|
||||||
.parser
|
.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);
|
devour_whitespace(paren_toks);
|
||||||
let val = self
|
let val = self
|
||||||
.parser
|
.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);
|
span = span.merge(val.span);
|
||||||
devour_whitespace(paren_toks);
|
devour_whitespace(paren_toks);
|
||||||
if map.insert(key.node.clone(), val.node) {
|
if map.insert(key.node.clone(), val.node) {
|
||||||
|
@ -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<IntoIter<Token>>,
|
|
||||||
c: char,
|
|
||||||
) -> SassResult<Vec<Token>> {
|
|
||||||
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 {
|
pub(crate) fn hex_char_for(number: u32) -> char {
|
||||||
debug_assert!(number < 0x10);
|
debug_assert!(number < 0x10);
|
||||||
std::char::from_u32(if number < 0xA {
|
std::char::from_u32(if number < 0xA {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user