make parsing of toplevel tokens more robust
This commit is contained in:
parent
bb89a865d4
commit
a01ed981ce
@ -400,5 +400,12 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
|||||||
_ => values.push(toks.next().unwrap()),
|
_ => values.push(toks.next().unwrap()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if `values` is not empty, there was an unexpected toplevel token
|
||||||
|
// that should be part of a selector
|
||||||
|
if let Some(v) = values.pop() {
|
||||||
|
return Err(("expected \"{\".", v.pos).into());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
@ -151,23 +151,40 @@ struct StyleSheetParser<'a> {
|
|||||||
path: &'a Path,
|
path: &'a Path,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_selector_char(c: char) -> bool {
|
|
||||||
c.is_alphanumeric()
|
|
||||||
|| matches!(
|
|
||||||
c,
|
|
||||||
'_' | '-' | '[' | '#' | ':' | '*' | '%' | '.' | '>' | '\\' | '+'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> StyleSheetParser<'a> {
|
impl<'a> StyleSheetParser<'a> {
|
||||||
fn parse_toplevel(mut self) -> SassResult<(Vec<Spanned<Stmt>>, Scope)> {
|
fn parse_toplevel(mut self) -> SassResult<(Vec<Spanned<Stmt>>, Scope)> {
|
||||||
let mut rules: Vec<Spanned<Stmt>> = Vec::new();
|
let mut rules: Vec<Spanned<Stmt>> = Vec::new();
|
||||||
|
devour_whitespace(self.lexer);
|
||||||
while let Some(Token { kind, .. }) = self.lexer.peek() {
|
while let Some(Token { kind, .. }) = self.lexer.peek() {
|
||||||
match kind {
|
match kind {
|
||||||
_ if is_selector_char(*kind) => {
|
'a'..='z'
|
||||||
|
| 'A'..='Z'
|
||||||
|
| '0'..='9'
|
||||||
|
| '['
|
||||||
|
| '\\'
|
||||||
|
| ']'
|
||||||
|
| '^'
|
||||||
|
| '_'
|
||||||
|
| '-'
|
||||||
|
| '#'
|
||||||
|
| ':'
|
||||||
|
| '*'
|
||||||
|
| '%'
|
||||||
|
| '.'
|
||||||
|
| '>'
|
||||||
|
| '+'
|
||||||
|
| '='
|
||||||
|
| ','
|
||||||
|
| '('
|
||||||
|
| ')'
|
||||||
|
| '<'
|
||||||
|
| '?'
|
||||||
|
| '~'
|
||||||
|
| '|'
|
||||||
|
| '\u{7f}'..=std::char::MAX => {
|
||||||
rules.extend(self.eat_rules(&Selector::new(), &mut Scope::new())?)
|
rules.extend(self.eat_rules(&Selector::new(), &mut Scope::new())?)
|
||||||
}
|
}
|
||||||
'\t' | '\n' | ' ' => {
|
'\t' | '\n' | ' ' | ';' => {
|
||||||
self.lexer.next();
|
self.lexer.next();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -331,20 +348,16 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
c if c.is_control() => {
|
'\u{0}'..='\u{8}' | '\u{b}'..='\u{1f}' => {
|
||||||
return Err(("expected selector.", self.lexer.next().unwrap().pos).into());
|
return Err(("expected selector.", self.lexer.next().unwrap().pos).into());
|
||||||
}
|
}
|
||||||
',' | '!' | '(' | ')' => {
|
'{' | '!' => {
|
||||||
return Err(("expected \"{\".", self.lexer.next().unwrap().pos).into());
|
|
||||||
}
|
|
||||||
'{' => {
|
|
||||||
return Err(("expected \"}\".", self.lexer.next().unwrap().pos).into());
|
return Err(("expected \"}\".", self.lexer.next().unwrap().pos).into());
|
||||||
}
|
}
|
||||||
'`' | '\'' | '"' => {
|
'`' | '\'' | '"' => {
|
||||||
return Err(("expected selector.", self.lexer.next().unwrap().pos).into());
|
return Err(("expected selector.", self.lexer.next().unwrap().pos).into());
|
||||||
}
|
}
|
||||||
'}' => return Err(("unmatched \"}\".", self.lexer.next().unwrap().pos).into()),
|
'}' => return Err(("unmatched \"}\".", self.lexer.next().unwrap().pos).into()),
|
||||||
_ => todo!("unexpected toplevel token: {:?}", kind),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Ok((rules, GLOBAL_SCOPE.with(|s| s.borrow().clone())))
|
Ok((rules, GLOBAL_SCOPE.with(|s| s.borrow().clone())))
|
||||||
|
@ -83,20 +83,15 @@ error!(
|
|||||||
"a {$a", "Error: expected \":\"."
|
"a {$a", "Error: expected \":\"."
|
||||||
);
|
);
|
||||||
error!(toplevel_comma, "a {},", "Error: expected \"{\".");
|
error!(toplevel_comma, "a {},", "Error: expected \"{\".");
|
||||||
error!(toplevel_exclamation, "! {}", "Error: expected \"{\".");
|
error!(toplevel_exclamation_alone, "!", "Error: expected \"}\".");
|
||||||
|
error!(toplevel_exclamation, "! {}", "Error: expected \"}\".");
|
||||||
error!(toplevel_backtick, "` {}", "Error: expected selector.");
|
error!(toplevel_backtick, "` {}", "Error: expected selector.");
|
||||||
error!(
|
error!(
|
||||||
toplevel_open_curly_brace,
|
toplevel_open_curly_brace,
|
||||||
"{ {color: red;}", "Error: expected \"}\"."
|
"{ {color: red;}", "Error: expected \"}\"."
|
||||||
);
|
);
|
||||||
error!(
|
error!(toplevel_open_paren, "(", "Error: expected \"{\".");
|
||||||
toplevel_open_paren,
|
error!(toplevel_close_paren, "(", "Error: expected \"{\".");
|
||||||
"(", "Error: expected \"{\"."
|
|
||||||
);
|
|
||||||
error!(
|
|
||||||
toplevel_close_paren,
|
|
||||||
"(", "Error: expected \"{\"."
|
|
||||||
);
|
|
||||||
error!(
|
error!(
|
||||||
backtick_in_value,
|
backtick_in_value,
|
||||||
"a {color:`red;}", "Error: Expected expression."
|
"a {color:`red;}", "Error: Expected expression."
|
||||||
@ -184,3 +179,24 @@ error!(
|
|||||||
nothing_after_gt,
|
nothing_after_gt,
|
||||||
"a {color: 1 >", "Error: Expected expression."
|
"a {color: 1 >", "Error: Expected expression."
|
||||||
);
|
);
|
||||||
|
error!(toplevel_eq_alone, "=", "Error: expected \"{\".");
|
||||||
|
error!(toplevel_gt_alone, ">", "Error: expected \"{\".");
|
||||||
|
error!(toplevel_lt_alone, "<", "Error: expected \"{\".");
|
||||||
|
error!(toplevel_question_alone, "?", "Error: expected \"{\".");
|
||||||
|
error!(toplevel_caret_alone, "^", "Error: expected \"{\".");
|
||||||
|
test!(toplevel_gt_as_selector, "> {}", "");
|
||||||
|
test!(toplevel_tilde_as_selector, "~ {}", "");
|
||||||
|
error!(toplevel_lt_as_selector, "< {}", "Error: expected selector.");
|
||||||
|
error!(
|
||||||
|
toplevel_question_as_selector,
|
||||||
|
"? {}", "Error: expected selector."
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
toplevel_caret_as_selector,
|
||||||
|
"^ {}", "Error: expected selector."
|
||||||
|
);
|
||||||
|
error!(toplevel_eq, "= {}", "Error: expected selector.");
|
||||||
|
error!(value_after_style, "a {}a", "Error: expected \"{\".");
|
||||||
|
test!(whitespace_after_style, "a {}\t\n ", "");
|
||||||
|
test!(toplevel_semicolon, ";", "");
|
||||||
|
test!(toplevel_semicolon_after_style, "a {};", "");
|
||||||
|
@ -15,7 +15,7 @@ test!(
|
|||||||
test!(
|
test!(
|
||||||
same_function_equal,
|
same_function_equal,
|
||||||
"@function user-defined() {@return null}
|
"@function user-defined() {@return null}
|
||||||
a {b: get-function(user-defined) == get-function(user-defined)}s",
|
a {b: get-function(user-defined) == get-function(user-defined)}",
|
||||||
"a {\n b: true;\n}\n"
|
"a {\n b: true;\n}\n"
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
|
@ -530,3 +530,14 @@ test!(
|
|||||||
"+ {\n color: &;\n}\n",
|
"+ {\n color: &;\n}\n",
|
||||||
"+ {\n color: +;\n}\n"
|
"+ {\n color: +;\n}\n"
|
||||||
);
|
);
|
||||||
|
error!(
|
||||||
|
#[ignore = "namespaces are not yet parsed correctly"]
|
||||||
|
empty_namespace,
|
||||||
|
"| {}", "Error: Expected identifier."
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
#[ignore = "namespaces are not yet parsed correctly"]
|
||||||
|
simple_namespace,
|
||||||
|
"|f {\n color: &;\n}\n",
|
||||||
|
"|f {\n color: |f;\n}\n"
|
||||||
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user