refactor parsing of media queries to use predicate parsing

This commit is contained in:
Connor Skees 2020-08-07 12:04:37 -04:00
parent d5b2410a8c
commit be032b66f0
3 changed files with 55 additions and 59 deletions

View File

@ -1,9 +1,6 @@
use crate::{ use crate::{
error::SassResult, error::SassResult,
utils::{ utils::{is_name_start, peek_ident_no_interpolation},
is_name_start, peek_ident_no_interpolation, read_until_closing_paren,
read_until_closing_quote,
},
{Cow, Token}, {Cow, Token},
}; };
@ -35,38 +32,25 @@ impl<'a> Parser<'a> {
false false
} }
// todo: replace with predicate
pub fn expression_until_comparison(&mut self) -> SassResult<Cow<'static, str>> { pub fn expression_until_comparison(&mut self) -> SassResult<Cow<'static, str>> {
let mut toks = Vec::new(); let value = self.parse_value(false, &|toks| match toks.peek() {
while let Some(tok) = self.toks.peek().cloned() { Some(Token { kind: '>', .. })
match tok.kind { | Some(Token { kind: '<', .. })
'=' => { | Some(Token { kind: ':', .. })
self.toks.advance_cursor(); | Some(Token { kind: ')', .. }) => true,
if matches!(self.toks.peek(), Some(Token { kind: '=', .. })) { Some(Token { kind: '=', .. }) => {
self.toks.reset_cursor(); if matches!(toks.peek_next(), Some(Token { kind: '=', .. })) {
break; toks.reset_cursor();
} true
self.toks.reset_cursor(); } else {
toks.push(tok); toks.reset_cursor();
toks.push(tok); false
self.toks.next();
self.toks.next();
}
'>' | '<' | ':' | ')' => {
break;
}
'\'' | '"' => {
toks.push(tok);
self.toks.next();
toks.append(&mut read_until_closing_quote(self.toks, tok.kind)?);
}
_ => {
toks.push(tok);
self.toks.next();
} }
} }
} _ => false,
self.parse_value_as_string_from_vec(toks, false) })?;
value.node.unquote().to_css_string(value.span)
} }
pub(super) fn parse_media_query_list(&mut self) -> SassResult<String> { pub(super) fn parse_media_query_list(&mut self) -> SassResult<String> {
@ -85,13 +69,10 @@ impl<'a> Parser<'a> {
fn parse_media_feature(&mut self) -> SassResult<String> { fn parse_media_feature(&mut self) -> SassResult<String> {
if let Some(Token { kind: '#', .. }) = self.toks.peek() { if let Some(Token { kind: '#', .. }) = self.toks.peek() {
if let Some(Token { kind: '{', .. }) = self.toks.peek_forward(1) {
self.toks.next();
self.toks.next(); self.toks.next();
self.expect_char('{')?;
return Ok(self.parse_interpolation_as_string()?.into_owned()); return Ok(self.parse_interpolation_as_string()?.into_owned());
} }
todo!()
}
let mut buf = String::with_capacity(2); let mut buf = String::with_capacity(2);
self.expect_char('(')?; self.expect_char('(')?;
buf.push('('); buf.push('(');
@ -105,13 +86,14 @@ impl<'a> Parser<'a> {
buf.push(':'); buf.push(':');
buf.push(' '); buf.push(' ');
let mut toks = read_until_closing_paren(self.toks)?;
if let Some(tok) = toks.pop() { let value = self.parse_value(false, &|toks| match toks.peek() {
if tok.kind != ')' { Some(Token { kind: ')', .. }) => true,
todo!() _ => false,
} })?;
} self.expect_char(')')?;
buf.push_str(&self.parse_value_as_string_from_vec(toks, true)?);
buf.push_str(&value.node.to_css_string(value.span)?);
self.whitespace_or_comment(); self.whitespace_or_comment();
buf.push(')'); buf.push(')');

View File

@ -531,19 +531,7 @@ impl<'a> Parser<'a> {
}) })
} }
pub fn parse_value_as_string_from_vec( // todo: this should also consume silent comments
&mut self,
toks: Vec<Token>,
quoted: bool,
) -> SassResult<Cow<'static, str>> {
let value = self.parse_value_from_vec(toks, false)?;
if quoted {
value.node.to_css_string(value.span)
} else {
value.node.unquote().to_css_string(value.span)
}
}
pub fn whitespace(&mut self) -> bool { pub fn whitespace(&mut self) -> bool {
let mut found_whitespace = false; let mut found_whitespace = false;
while let Some(tok) = self.toks.peek() { while let Some(tok) = self.toks.peek() {

View File

@ -72,3 +72,29 @@ test!(
}", }",
"@media foo and (bar) {\n a {\n color: red;\n }\n}\n" "@media foo and (bar) {\n a {\n color: red;\n }\n}\n"
); );
test!(
comparison_in_query,
"@media (100px < 400px) {
a {
interpolation: in-parens
}
}",
"@media (100px < 400px) {\n a {\n interpolation: in-parens;\n }\n}\n"
);
test!(
interpolated_comparison_in_query,
"@media (#{100px < 400px}) {
a {
interpolation: in-parens
}
}",
"@media (true) {\n a {\n interpolation: in-parens;\n }\n}\n"
);
error!(
media_feature_missing_closing_paren,
"@media foo and (bar:a", "Error: expected \")\"."
);
error!(
media_feature_missing_curly_brace_after_hash,
"@media foo and # {}", "Error: expected \"{\"."
);