From a01ed981ceba0b05d68038c6c7a9d535ca115a67 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Sun, 24 May 2020 07:43:54 -0400 Subject: [PATCH] make parsing of toplevel tokens more robust --- src/lib.rs | 7 +++++++ src/stylesheet.rs | 45 ++++++++++++++++++++++++++++--------------- tests/error.rs | 34 +++++++++++++++++++++++--------- tests/get-function.rs | 2 +- tests/selectors.rs | 11 +++++++++++ 5 files changed, 73 insertions(+), 26 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d34c7f5..dfc500a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -400,5 +400,12 @@ pub(crate) fn eat_expr>( _ => 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) } diff --git a/src/stylesheet.rs b/src/stylesheet.rs index 550a19b..c5df0a8 100644 --- a/src/stylesheet.rs +++ b/src/stylesheet.rs @@ -151,23 +151,40 @@ struct StyleSheetParser<'a> { path: &'a Path, } -fn is_selector_char(c: char) -> bool { - c.is_alphanumeric() - || matches!( - c, - '_' | '-' | '[' | '#' | ':' | '*' | '%' | '.' | '>' | '\\' | '+' - ) -} - impl<'a> StyleSheetParser<'a> { fn parse_toplevel(mut self) -> SassResult<(Vec>, Scope)> { let mut rules: Vec> = Vec::new(); + devour_whitespace(self.lexer); while let Some(Token { kind, .. }) = self.lexer.peek() { 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())?) } - '\t' | '\n' | ' ' => { + '\t' | '\n' | ' ' | ';' => { self.lexer.next(); continue; } @@ -331,20 +348,16 @@ impl<'a> StyleSheetParser<'a> { ) .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 \"{\".", 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(("unmatched \"}\".", self.lexer.next().unwrap().pos).into()), - _ => todo!("unexpected toplevel token: {:?}", kind), }; } Ok((rules, GLOBAL_SCOPE.with(|s| s.borrow().clone()))) diff --git a/tests/error.rs b/tests/error.rs index d4ea7a6..04dec37 100644 --- a/tests/error.rs +++ b/tests/error.rs @@ -83,20 +83,15 @@ error!( "a {$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_open_curly_brace, "{ {color: red;}", "Error: expected \"}\"." ); -error!( - toplevel_open_paren, - "(", "Error: expected \"{\"." -); -error!( - toplevel_close_paren, - "(", "Error: expected \"{\"." -); +error!(toplevel_open_paren, "(", "Error: expected \"{\"."); +error!(toplevel_close_paren, "(", "Error: expected \"{\"."); error!( backtick_in_value, "a {color:`red;}", "Error: Expected expression." @@ -184,3 +179,24 @@ error!( nothing_after_gt, "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 {};", ""); diff --git a/tests/get-function.rs b/tests/get-function.rs index b752665..91da744 100644 --- a/tests/get-function.rs +++ b/tests/get-function.rs @@ -15,7 +15,7 @@ test!( test!( same_function_equal, "@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" ); test!( diff --git a/tests/selectors.rs b/tests/selectors.rs index 70b70ed..606827c 100644 --- a/tests/selectors.rs +++ b/tests/selectors.rs @@ -530,3 +530,14 @@ test!( "+ {\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" +);