diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 33de5c24a..61c90f6c5 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -85,8 +85,9 @@ fn node(p: &mut Parser, at_start: bool) -> Option> { } // Bad tokens. - token => { - p.diag_unexpected(token.span_with(start .. p.pos())); + _ => { + p.jump(start); + p.diag_unexpected(); return None; } }; @@ -233,6 +234,7 @@ fn paren_call(p: &mut Parser, name: Spanned) -> ExprCall { fn dict_contents(p: &mut Parser) -> (LitDict, bool) { let mut dict = LitDict::new(); let mut comma_and_keyless = true; + let mut expected_comma = None; loop { p.skip_white(); @@ -243,10 +245,15 @@ fn dict_contents(p: &mut Parser) -> (LitDict, bool) { let entry = if let Some(entry) = dict_entry(p) { entry } else { - p.diag_expected("value"); + expected_comma = None; + p.diag_unexpected(); continue; }; + if let Some(pos) = expected_comma.take() { + p.diag_expected_at("comma", pos); + } + if let Some(key) = &entry.key { comma_and_keyless = false; p.deco(Deco::DictKey.span_with(key.span)); @@ -261,7 +268,7 @@ fn dict_contents(p: &mut Parser) -> (LitDict, bool) { } if !p.eat_if(Token::Comma) { - p.diag_expected_at("comma", behind); + expected_comma = Some(behind); } comma_and_keyless = false; @@ -286,6 +293,7 @@ fn dict_entry(p: &mut Parser) -> Option { expr, }) } else { + p.diag_expected("value"); None } } diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 047f6e4cd..a8e5883a3 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -38,7 +38,7 @@ impl<'s> Parser<'s> { self.f.diags.push(diag); } - /// Eat the next token and add a diagnostic that it was not the expected + /// Eat the next token and add a diagnostic that it is not the expected /// `thing`. pub fn diag_expected(&mut self, thing: &str) { let before = self.pos(); @@ -60,9 +60,16 @@ impl<'s> Parser<'s> { self.diag(error!(pos, "expected {}", thing)); } - /// Add a diagnostic that the given `token` was unexpected. - pub fn diag_unexpected(&mut self, token: Spanned) { - self.diag(error!(token.span, "unexpected {}", token.v.name())); + /// Eat the next token and add a diagnostic that it is unexpected. + pub fn diag_unexpected(&mut self) { + let before = self.pos(); + if let Some(found) = self.eat() { + let after = self.pos(); + self.diag(match found { + Token::Invalid(_) => error!(before .. after, "invalid token"), + _ => error!(before .. after, "unexpected {}", found.name()), + }); + } } /// Add a decoration to the feedback. diff --git a/src/parse/tests.rs b/src/parse/tests.rs index 172b1d15a..2fd6d0b11 100644 --- a/src/parse/tests.rs +++ b/src/parse/tests.rs @@ -415,6 +415,7 @@ fn test_parse_values() { // Simple. v!("_" => Id("_")); v!("name" => Id("name")); + v!("ke-bab" => Id("ke-bab")); v!("α" => Id("α")); v!("\"hi\"" => Str("hi")); v!("true" => Bool(true)); @@ -457,6 +458,7 @@ fn test_parse_expressions() { // Operations. v!("-1" => Unary(Neg, Int(1))); v!("-- 1" => Unary(Neg, Unary(Neg, Int(1)))); + v!("--css" => Unary(Neg, Unary(Neg, Id("css")))); v!("3.2in + 6pt" => Binary(Add, Length(3.2, In), Length(6.0, Pt))); v!("5 - 0.01" => Binary(Sub, Int(5), Float(0.01))); v!("(3mm * 2)" => Binary(Mul, Length(3.0, Mm), Int(2))); @@ -547,7 +549,7 @@ fn test_parse_dicts_compute_func_calls() { // Invalid name. v!("👠(\"abc\", 13e-5)" => Dict!(Str("abc"), Float(13.0e-5))); - e!("[val: 👠(\"abc\", 13e-5)]" => s(6, 10, "expected value, found invalid token")); + e!("[val: 👠(\"abc\", 13e-5)]" => s(6, 10, "invalid token")); } #[test] @@ -567,12 +569,12 @@ fn test_parse_dicts_nested() { #[test] fn test_parse_dicts_errors() { // Expected value. - e!("[val: (=)]" => s(7, 8, "expected value, found equals sign")); - e!("[val: (,)]" => s(7, 8, "expected value, found comma")); + e!("[val: (=)]" => s(7, 8, "unexpected equals sign")); + e!("[val: (,)]" => s(7, 8, "unexpected comma")); v!("(\x07 abc,)" => Dict![Id("abc")]); - e!("[val: (\x07 abc,)]" => s(7, 8, "expected value, found invalid token")); + e!("[val: (\x07 abc,)]" => s(7, 8, "invalid token")); e!("[val: (key=,)]" => s(11, 12, "expected value, found comma")); - e!("[val: hi,)]" => s(9, 10, "expected value, found closing paren")); + e!("[val: hi,)]" => s(9, 10, "unexpected closing paren")); // Expected comma. v!("(true false)" => Dict![Bool(true), Bool(false)]); @@ -586,13 +588,9 @@ fn test_parse_dicts_errors() { // Bad key. v!("true=you" => Bool(true), Id("you")); - e!("[val: true=you]" => - s(10, 10, "expected comma"), - s(10, 11, "expected value, found equals sign")); + e!("[val: true=you]" => s(10, 11, "unexpected equals sign")); // Unexpected equals sign. v!("z=y=4" => "z" => Id("y"), Int(4)); - e!("[val: z=y=4]" => - s(9, 9, "expected comma"), - s(9, 10, "expected value, found equals sign")); + e!("[val: z=y=4]" => s(9, 10, "unexpected equals sign")); } diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index 91477eb9b..3dcc64604 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -75,7 +75,7 @@ impl<'s> Iterator for Tokens<'s> { // Comments. '/' if self.s.eat_if('/') => self.line_comment(), '/' if self.s.eat_if('*') => self.block_comment(), - '*' if self.s.eat_if('/') => Token::Invalid("*/"), + '*' if self.s.eat_if('/') => Token::StarSlash, // Functions and blocks. '[' => Token::LeftBracket, @@ -779,8 +779,8 @@ mod tests { #[test] fn test_tokenize_invalid() { // Test invalidly closed block comments. - t!(Both: "*/" => Invalid("*/")); - t!(Both: "/**/*/" => BC(""), Invalid("*/")); + t!(Both: "*/" => StarSlash); + t!(Both: "/**/*/" => BC(""), StarSlash); // Test invalid expressions. t!(Header: r"\" => Invalid(r"\")); diff --git a/src/syntax/token.rs b/src/syntax/token.rs index 21a560043..e105ad2fb 100644 --- a/src/syntax/token.rs +++ b/src/syntax/token.rs @@ -19,6 +19,8 @@ pub enum Token<'s> { /// /// The comment can contain nested block comments. BlockComment(&'s str), + /// An end of a block comment that was not started. + StarSlash, /// A star: `*`. Star, @@ -83,7 +85,7 @@ pub enum Token<'s> { /// A quoted string: `"..."`. Str(TokenStr<'s>), - /// Things that are not valid in the context they appeared in. + /// Things that are not valid tokens. Invalid(&'s str), } @@ -129,6 +131,7 @@ impl<'s> Token<'s> { Self::LineComment(_) => "line comment", Self::BlockComment(_) => "block comment", + Self::StarSlash => "end of block comment", Self::Star => "star", Self::Underscore => "underscore", @@ -162,7 +165,6 @@ impl<'s> Token<'s> { Self::Hex(_) => "hex value", Self::Str { .. } => "string", - Self::Invalid("*/") => "end of block comment", Self::Invalid(_) => "invalid token", } }