simplify lookahead for @use and @media

This commit is contained in:
Connor Skees 2021-07-19 20:50:12 -04:00
parent c2e84e3854
commit a34aa32128
6 changed files with 106 additions and 80 deletions

View File

@ -4,7 +4,7 @@ use codemap::Spanned;
use crate::{common::unvendor, error::SassError}; use crate::{common::unvendor, error::SassError};
#[derive(Debug)] #[derive(Debug, PartialEq, Eq)]
pub enum AtRuleKind { pub enum AtRuleKind {
// Sass specific @rules // Sass specific @rules
/// Loads mixins, functions, and variables from other Sass /// Loads mixins, functions, and variables from other Sass

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
error::SassResult, error::SassResult,
utils::{is_name_start, peek_ident_no_interpolation}, utils::is_name_start,
{Cow, Token}, {Cow, Token},
}; };
@ -10,10 +10,14 @@ impl<'a> Parser<'a> {
/// Peeks to see if the `ident` is at the current position. If it is, /// Peeks to see if the `ident` is at the current position. If it is,
/// consume the identifier /// consume the identifier
pub fn scan_identifier(&mut self, ident: &'static str, case_insensitive: bool) -> bool { pub fn scan_identifier(&mut self, ident: &'static str, case_insensitive: bool) -> bool {
let mut peeked_identifier = let start = self.toks.cursor();
match peek_ident_no_interpolation(self.toks, false, self.span_before) {
let mut peeked_identifier = match self.parse_identifier_no_interpolation(false) {
Ok(v) => v.node, Ok(v) => v.node,
Err(..) => return false, Err(..) => {
self.toks.set_cursor(start);
return false;
}
}; };
if case_insensitive { if case_insensitive {
@ -21,12 +25,10 @@ impl<'a> Parser<'a> {
} }
if peeked_identifier == ident { if peeked_identifier == ident {
self.toks.truncate_iterator_to_cursor();
self.toks.next();
return true; return true;
} }
self.toks.reset_cursor(); self.toks.set_cursor(start);
false false
} }

View File

@ -136,12 +136,14 @@ impl<'a> Parser<'a> {
pub fn expect_identifier(&mut self, ident: &'static str) -> SassResult<()> { pub fn expect_identifier(&mut self, ident: &'static str) -> SassResult<()> {
let this_ident = self.parse_identifier_no_interpolation(false)?; let this_ident = self.parse_identifier_no_interpolation(false)?;
self.span_before = this_ident.span; self.span_before = this_ident.span;
if this_ident.node == ident { if this_ident.node == ident {
return Ok(()); return Ok(());
} }
Err((format!("Expected \"{}\".", ident), self.span_before).into()) Err((format!("Expected \"{}\".", ident), this_ident.span).into())
} }
fn parse_stmt(&mut self) -> SassResult<Vec<Stmt>> { fn parse_stmt(&mut self) -> SassResult<Vec<Stmt>> {

View File

@ -13,15 +13,22 @@ use crate::{
lexer::Lexer, lexer::Lexer,
parse::{common::Comment, Parser, Stmt, VariableValue}, parse::{common::Comment, Parser, Stmt, VariableValue},
scope::Scope, scope::Scope,
utils::peek_ident_no_interpolation,
Token, Token,
}; };
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
fn parse_module_alias(&mut self) -> SassResult<Option<String>> { fn parse_module_alias(&mut self) -> SassResult<Option<String>> {
if let Some(Token { kind: 'a', .. }) | Some(Token { kind: 'A', .. }) = self.toks.peek() { if !matches!(
let mut ident = peek_ident_no_interpolation(self.toks, false, self.span_before)?; self.toks.peek(),
Some(Token { kind: 'a', .. }) | Some(Token { kind: 'A', .. })
) {
return Ok(None);
}
let mut ident = self.parse_identifier_no_interpolation(false)?;
ident.node.make_ascii_lowercase(); ident.node.make_ascii_lowercase();
if ident.node != "as" { if ident.node != "as" {
return Err(("expected \";\".", ident.span).into()); return Err(("expected \";\".", ident.span).into());
} }
@ -35,17 +42,21 @@ impl<'a> Parser<'a> {
let name = self.parse_identifier_no_interpolation(false)?; let name = self.parse_identifier_no_interpolation(false)?;
return Ok(Some(name.node)); Ok(Some(name.node))
}
Ok(None)
} }
fn parse_module_config(&mut self) -> SassResult<ModuleConfig> { fn parse_module_config(&mut self) -> SassResult<ModuleConfig> {
let mut config = ModuleConfig::default(); let mut config = ModuleConfig::default();
if let Some(Token { kind: 'w', .. }) | Some(Token { kind: 'W', .. }) = self.toks.peek() { if !matches!(
let mut ident = peek_ident_no_interpolation(self.toks, false, self.span_before)?; self.toks.peek(),
Some(Token { kind: 'w', .. }) | Some(Token { kind: 'W', .. })
) {
return Ok(config);
}
let mut ident = self.parse_identifier_no_interpolation(false)?;
ident.node.make_ascii_lowercase(); ident.node.make_ascii_lowercase();
if ident.node != "with" { if ident.node != "with" {
return Err(("expected \";\".", ident.span).into()); return Err(("expected \";\".", ident.span).into());
@ -88,7 +99,6 @@ impl<'a> Parser<'a> {
} }
} }
} }
}
Ok(config) Ok(config)
} }
@ -157,28 +167,26 @@ impl<'a> Parser<'a> {
loop { loop {
self.whitespace(); self.whitespace();
match self.toks.peek() { match self.toks.peek() {
Some(Token { kind: '@', .. }) => { Some(Token { kind: '@', .. }) => {
self.toks.advance_cursor(); let start = self.toks.cursor();
self.toks.next();
if let Some(Token { kind, .. }) = self.toks.peek() { if let Some(Token { kind, .. }) = self.toks.peek() {
if !matches!(kind, 'a'..='z' | 'A'..='Z' | '\\') { if !matches!(kind, 'u' | 'U' | '\\') {
self.toks.set_cursor(start);
break; break;
} }
} }
match AtRuleKind::try_from(&peek_ident_no_interpolation( let ident = self.parse_identifier_no_interpolation(false)?;
self.toks,
false, if AtRuleKind::try_from(&ident)? != AtRuleKind::Use {
self.span_before, self.toks.set_cursor(start);
)?)? {
AtRuleKind::Use => {
self.toks.truncate_iterator_to_cursor();
}
_ => {
break; break;
} }
}
self.whitespace_or_comment(); self.whitespace_or_comment();

View File

@ -231,3 +231,8 @@ error!(
invalid_toplevel_selector, invalid_toplevel_selector,
"@if true { & { } }", "Error: Top-level selectors may not contain the parent selector \"&\"." "@if true { & { } }", "Error: Top-level selectors may not contain the parent selector \"&\"."
); );
error!(
#[ignore = "unsure what the exact rule is here wrt denying interpolation (@media allows this)"]
denies_interpolated_at_rule,
"@#{if} true { a { color: red; } }", "Error: expected \"(\"."
);

View File

@ -161,6 +161,15 @@ test!(
}", }",
"@media foo {\n a {\n color: red;\n }\n}\n@media bar {\n a {\n color: green;\n }\n}\n" "@media foo {\n a {\n color: red;\n }\n}\n@media bar {\n a {\n color: green;\n }\n}\n"
); );
test!(
allows_interpolated_at_rule,
"@#{media} (true) {
a {
color: red;
}
}",
"@media (true) {\n a {\n color: red;\n }\n}\n"
);
error!( error!(
media_feature_missing_closing_paren, media_feature_missing_closing_paren,