ludtwig_parser/parser/
parse_error.rs

1use crate::lexer::Token;
2use std::fmt;
3use std::fmt::Formatter;
4
5use crate::syntax::untyped::{SyntaxKind, TextRange};
6
7#[derive(Debug, Clone, Eq, PartialEq)]
8pub struct ParseErrorBuilder {
9    pub(super) range: Option<TextRange>,
10    pub(super) found: Option<SyntaxKind>,
11    pub(super) expected: String,
12}
13
14impl ParseErrorBuilder {
15    /// expected should only describe what is missing.
16    /// It is later passed into "expected <expectedString> but found ..."
17    pub fn new<S>(expected: S) -> Self
18    where
19        S: Into<String>,
20    {
21        Self {
22            range: None,
23            found: None,
24            expected: expected.into(),
25        }
26    }
27
28    pub(crate) fn at_token(mut self, token: &Token) -> Self {
29        self.range = Some(token.range);
30        self.found = Some(token.kind);
31        self
32    }
33
34    pub(super) fn build(self) -> ParseError {
35        ParseError {
36            range: self.range.unwrap(),
37            found: self.found,
38            expected: self.expected,
39        }
40    }
41}
42
43#[derive(Debug, Clone, Eq, PartialEq)]
44pub struct ParseError {
45    pub range: TextRange,
46    pub found: Option<SyntaxKind>,
47    pub expected: String,
48}
49
50impl ParseError {
51    #[must_use]
52    pub fn expected_message(&self) -> String {
53        if let Some(found) = self.found {
54            format!("expected {} but found {}", self.expected, found)
55        } else {
56            format!("expected {} but reached end of file", self.expected)
57        }
58    }
59}
60
61impl fmt::Display for ParseError {
62    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
63        write!(
64            f,
65            "error at {}..{}: ",
66            u32::from(self.range.start()),
67            u32::from(self.range.end()),
68        )?;
69
70        write!(f, "{}", self.expected_message())
71    }
72}
73
74#[cfg(test)]
75mod test {
76    use super::*;
77    use crate::T;
78    use rowan::TextSize;
79
80    #[test]
81    fn parse_error_display() {
82        let range = TextRange::new(TextSize::from(3), TextSize::from(5));
83        let parse_error = ParseError {
84            range,
85            found: Some(T!["{%"]),
86            expected: "word".to_string(),
87        };
88
89        assert_eq!(
90            format!("{parse_error}"),
91            "error at 3..5: expected word but found {%"
92        );
93    }
94}