mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Tweak parser error messages 🔮
This commit is contained in:
parent
6bbedeaa2c
commit
2336aeb4c3
@ -85,8 +85,9 @@ fn node(p: &mut Parser, at_start: bool) -> Option<Spanned<SynNode>> {
|
||||
}
|
||||
|
||||
// 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<Ident>) -> 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<LitDictEntry> {
|
||||
expr,
|
||||
})
|
||||
} else {
|
||||
p.diag_expected("value");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -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<Token>) {
|
||||
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.
|
||||
|
@ -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"));
|
||||
}
|
||||
|
@ -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"\"));
|
||||
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user