include full parser context in predicate

This commit is contained in:
Connor Skees 2021-07-20 10:37:29 -04:00
parent a34aa32128
commit fd685ee36f
6 changed files with 46 additions and 44 deletions

View File

@ -183,15 +183,16 @@ impl<'a> Parser<'a> {
self.whitespace_or_comment(); self.whitespace_or_comment();
let value = self.parse_value(true, &|c| match c.peek() { let value = self.parse_value(true, &|parser| match parser.toks.peek() {
Some(Token { kind: ')', .. }) | Some(Token { kind: ',', .. }) => true, Some(Token { kind: ')', .. }) | Some(Token { kind: ',', .. }) => true,
Some(Token { kind: '.', .. }) => { Some(Token { kind: '.', .. }) => {
let next_is_dot = matches!(c.peek_n(1), Some(Token { kind: '.', .. })); let next_is_dot =
matches!(parser.toks.peek_n(1), Some(Token { kind: '.', .. }));
next_is_dot next_is_dot
} }
Some(Token { kind: '=', .. }) => { Some(Token { kind: '=', .. }) => {
let next_is_eq = matches!(c.peek_n(1), Some(Token { kind: '=', .. })); let next_is_eq = matches!(parser.toks.peek_n(1), Some(Token { kind: '=', .. }));
!next_is_eq !next_is_eq
} }
@ -283,10 +284,11 @@ impl<'a> Parser<'a> {
self.toks.next(); self.toks.next();
let left = value?; let left = value?;
let right = self.parse_value(true, &|c| match c.peek() { let right = self.parse_value(true, &|parser| match parser.toks.peek() {
Some(Token { kind: ')', .. }) | Some(Token { kind: ',', .. }) => true, Some(Token { kind: ')', .. }) | Some(Token { kind: ',', .. }) => true,
Some(Token { kind: '.', .. }) => { Some(Token { kind: '.', .. }) => {
let next_is_dot = matches!(c.peek_n(1), Some(Token { kind: '.', .. })); let next_is_dot =
matches!(parser.toks.peek_n(1), Some(Token { kind: '.', .. }));
next_is_dot next_is_dot
} }

View File

@ -176,18 +176,18 @@ impl<'a> Parser<'a> {
} }
self.whitespace_or_comment(); self.whitespace_or_comment();
let from_val = self.parse_value(false, &|toks| match toks.peek() { let from_val = self.parse_value(false, &|parser| match parser.toks.peek() {
Some(Token { kind: 't', pos }) Some(Token { kind: 't', pos })
| Some(Token { kind: 'T', pos }) | Some(Token { kind: 'T', pos })
| Some(Token { kind: '\\', pos }) => { | Some(Token { kind: '\\', pos }) => {
let span = pos; let span = pos;
let mut ident = match peek_ident_no_interpolation(toks, false, span) { let mut ident = match peek_ident_no_interpolation(parser.toks, false, span) {
Ok(s) => s, Ok(s) => s,
Err(..) => return false, Err(..) => return false,
}; };
ident.node.make_ascii_lowercase(); ident.node.make_ascii_lowercase();
let v = matches!(ident.node.to_ascii_lowercase().as_str(), "to" | "through"); let v = matches!(ident.node.to_ascii_lowercase().as_str(), "to" | "through");
toks.reset_cursor(); parser.toks.reset_cursor();
v v
} }
Some(..) | None => false, Some(..) | None => false,

View File

@ -34,14 +34,14 @@ impl<'a> Parser<'a> {
} }
pub fn expression_until_comparison(&mut self) -> SassResult<Cow<'static, str>> { pub fn expression_until_comparison(&mut self) -> SassResult<Cow<'static, str>> {
let value = self.parse_value(false, &|toks| match toks.peek() { let value = self.parse_value(false, &|parser| match parser.toks.peek() {
Some(Token { kind: '>', .. }) Some(Token { kind: '>', .. })
| Some(Token { kind: '<', .. }) | Some(Token { kind: '<', .. })
| Some(Token { kind: ':', .. }) | Some(Token { kind: ':', .. })
| Some(Token { kind: ')', .. }) => true, | Some(Token { kind: ')', .. }) => true,
Some(Token { kind: '=', .. }) => { Some(Token { kind: '=', .. }) => {
let is_double_eq = matches!(toks.peek_next(), Some(Token { kind: '=', .. })); let is_double_eq = matches!(parser.toks.peek_next(), Some(Token { kind: '=', .. }));
toks.reset_cursor(); parser.toks.reset_cursor();
// if it is a double eq, then parse as normal // if it is a double eq, then parse as normal
// //
// otherwise, it is a single eq and we should // otherwise, it is a single eq and we should
@ -88,8 +88,8 @@ impl<'a> Parser<'a> {
buf.push(':'); buf.push(':');
buf.push(' '); buf.push(' ');
let value = self.parse_value(false, &|toks| { let value = self.parse_value(false, &|parser| {
matches!(toks.peek(), Some(Token { kind: ')', .. })) matches!(parser.toks.peek(), Some(Token { kind: ')', .. }))
})?; })?;
self.expect_char(')')?; self.expect_char(')')?;

View File

@ -78,9 +78,9 @@ impl<'a> Parser<'a> {
self.expect_char(':')?; self.expect_char(':')?;
self.whitespace_or_comment(); self.whitespace_or_comment();
let value = self.parse_value(false, &|toks| { let value = self.parse_value(false, &|parser| {
matches!( matches!(
toks.peek(), parser.toks.peek(),
Some(Token { kind: ',', .. }) | Some(Token { kind: ')', .. }) Some(Token { kind: ',', .. }) | Some(Token { kind: ')', .. })
) )
})?; })?;

View File

@ -45,6 +45,9 @@ impl IsWhitespace for IntermediateValue {
} }
} }
/// We parse a value until the predicate returns true
type Predicate<'a> = &'a dyn Fn(&mut Parser<'_>) -> bool;
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
/// Parse a value from a stream of tokens /// Parse a value from a stream of tokens
/// ///
@ -52,7 +55,7 @@ impl<'a> Parser<'a> {
pub(crate) fn parse_value( pub(crate) fn parse_value(
&mut self, &mut self,
in_paren: bool, in_paren: bool,
predicate: &dyn Fn(&mut Lexer) -> bool, predicate: Predicate<'_>,
) -> SassResult<Spanned<Value>> { ) -> SassResult<Spanned<Value>> {
self.whitespace(); self.whitespace();
@ -64,7 +67,7 @@ impl<'a> Parser<'a> {
Some(Token { pos, .. }) => pos, Some(Token { pos, .. }) => pos,
}; };
if predicate(self.toks) { if predicate(self) {
return Err(("Expected expression.", span).into()); return Err(("Expected expression.", span).into());
} }
@ -290,7 +293,7 @@ impl<'a> Parser<'a> {
fn parse_ident_value( fn parse_ident_value(
&mut self, &mut self,
predicate: &dyn Fn(&mut Lexer) -> bool, predicate: Predicate<'_>,
) -> SassResult<Spanned<IntermediateValue>> { ) -> SassResult<Spanned<IntermediateValue>> {
let Spanned { node: mut s, span } = self.parse_identifier()?; let Spanned { node: mut s, span } = self.parse_identifier()?;
@ -320,7 +323,7 @@ impl<'a> Parser<'a> {
return self.parse_fn_call(s, lower); return self.parse_fn_call(s, lower);
} }
Some(Token { kind: '.', .. }) => { Some(Token { kind: '.', .. }) => {
if !predicate(self.toks) { if !predicate(self) {
self.toks.next(); self.toks.next();
return self.parse_module_item(&s, span); return self.parse_module_item(&s, span);
} }
@ -360,14 +363,11 @@ impl<'a> Parser<'a> {
} }
} }
fn parse_number( fn parse_number(&mut self, predicate: Predicate<'_>) -> SassResult<Spanned<ParsedNumber>> {
&mut self,
predicate: &dyn Fn(&mut Lexer) -> bool,
) -> SassResult<Spanned<ParsedNumber>> {
let mut span = self.toks.peek().unwrap().pos; let mut span = self.toks.peek().unwrap().pos;
let mut whole = eat_whole_number(self.toks); let mut whole = eat_whole_number(self.toks);
if self.toks.peek().is_none() || predicate(self.toks) { if self.toks.peek().is_none() || predicate(self) {
return Ok(Spanned { return Ok(Spanned {
node: ParsedNumber::new(whole, 0, String::new(), true), node: ParsedNumber::new(whole, 0, String::new(), true),
span, span,
@ -452,8 +452,8 @@ impl<'a> Parser<'a> {
.span(span) .span(span)
} else { } else {
// todo: we don't know if we're `in_paren` here // todo: we don't know if we're `in_paren` here
let inner = self.parse_value(false, &|toks| { let inner = self.parse_value(false, &|parser| {
matches!(toks.peek(), Some(Token { kind: ']', .. })) matches!(parser.toks.peek(), Some(Token { kind: ']', .. }))
})?; })?;
span = span.merge(inner.span); span = span.merge(inner.span);
@ -470,7 +470,7 @@ impl<'a> Parser<'a> {
fn parse_intermediate_value_dimension( fn parse_intermediate_value_dimension(
&mut self, &mut self,
predicate: &dyn Fn(&mut Lexer) -> bool, predicate: Predicate<'_>,
) -> SassResult<Spanned<IntermediateValue>> { ) -> SassResult<Spanned<IntermediateValue>> {
let Spanned { node, span } = self.parse_dimension(predicate)?; let Spanned { node, span } = self.parse_dimension(predicate)?;
@ -479,7 +479,7 @@ impl<'a> Parser<'a> {
pub(crate) fn parse_dimension( pub(crate) fn parse_dimension(
&mut self, &mut self,
predicate: &dyn Fn(&mut Lexer) -> bool, predicate: Predicate<'_>,
) -> SassResult<Spanned<Value>> { ) -> SassResult<Spanned<Value>> {
let Spanned { let Spanned {
node: val, node: val,
@ -568,9 +568,9 @@ impl<'a> Parser<'a> {
} }
let mut map = SassMap::new(); let mut map = SassMap::new();
let key = self.parse_value(true, &|c| { let key = self.parse_value(true, &|parser| {
matches!( matches!(
c.peek(), parser.toks.peek(),
Some(Token { kind: ':', .. }) | Some(Token { kind: ')', .. }) Some(Token { kind: ':', .. }) | Some(Token { kind: ')', .. })
) )
})?; })?;
@ -586,9 +586,9 @@ impl<'a> Parser<'a> {
Some(..) | None => return Err(("expected \")\".", key.span).into()), Some(..) | None => return Err(("expected \")\".", key.span).into()),
} }
let val = self.parse_value(true, &|c| { let val = self.parse_value(true, &|parser| {
matches!( matches!(
c.peek(), parser.toks.peek(),
Some(Token { kind: ',', .. }) | Some(Token { kind: ')', .. }) Some(Token { kind: ',', .. }) | Some(Token { kind: ')', .. })
) )
})?; })?;
@ -624,9 +624,9 @@ impl<'a> Parser<'a> {
} }
loop { loop {
let key = self.parse_value(true, &|c| { let key = self.parse_value(true, &|parser| {
matches!( matches!(
c.peek(), parser.toks.peek(),
Some(Token { kind: ':', .. }) | Some(Token { kind: ',', .. }) Some(Token { kind: ':', .. }) | Some(Token { kind: ',', .. })
) )
})?; })?;
@ -634,9 +634,9 @@ impl<'a> Parser<'a> {
self.expect_char(':')?; self.expect_char(':')?;
self.whitespace_or_comment(); self.whitespace_or_comment();
let val = self.parse_value(true, &|c| { let val = self.parse_value(true, &|parser| {
matches!( matches!(
c.peek(), parser.toks.peek(),
Some(Token { kind: ',', .. }) | Some(Token { kind: ')', .. }) Some(Token { kind: ',', .. }) | Some(Token { kind: ')', .. })
) )
})?; })?;
@ -763,9 +763,9 @@ impl<'a> Parser<'a> {
fn parse_intermediate_value( fn parse_intermediate_value(
&mut self, &mut self,
predicate: &dyn Fn(&mut Lexer) -> bool, predicate: Predicate<'_>,
) -> Option<SassResult<Spanned<IntermediateValue>>> { ) -> Option<SassResult<Spanned<IntermediateValue>>> {
if predicate(self.toks) { if predicate(self) {
return None; return None;
} }
let (kind, span) = match self.toks.peek() { let (kind, span) = match self.toks.peek() {
@ -1038,7 +1038,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(&mut Lexer) -> bool, predicate: Predicate<'a>,
} }
impl<'a, 'b: 'a> Iterator for IntermediateValueIterator<'a, 'b> { impl<'a, 'b: 'a> Iterator for IntermediateValueIterator<'a, 'b> {
@ -1053,7 +1053,7 @@ impl<'a, 'b: 'a> Iterator for IntermediateValueIterator<'a, 'b> {
} }
impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> { impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
pub fn new(parser: &'a mut Parser<'b>, predicate: &'a dyn Fn(&mut Lexer) -> bool) -> Self { pub fn new(parser: &'a mut Parser<'b>, predicate: Predicate<'a>) -> Self {
Self { Self {
parser, parser,
peek: None, peek: None,

View File

@ -111,15 +111,15 @@ impl<'a> Parser<'a> {
let mut default = false; let mut default = false;
let mut global = false; let mut global = false;
let value = self.parse_value(true, &|toks| { let value = self.parse_value(true, &|parser| {
if matches!(toks.peek(), Some(Token { kind: '!', .. })) { if matches!(parser.toks.peek(), Some(Token { kind: '!', .. })) {
let is_important = matches!( let is_important = matches!(
toks.peek_next(), parser.toks.peek_next(),
Some(Token { kind: 'i', .. }) Some(Token { kind: 'i', .. })
| Some(Token { kind: 'I', .. }) | Some(Token { kind: 'I', .. })
| Some(Token { kind: '=', .. }) | Some(Token { kind: '=', .. })
); );
toks.reset_cursor(); parser.toks.reset_cursor();
!is_important !is_important
} else { } else {
false false