esr/
error.rs

1use std::fmt::{self, Debug, Display};
2use crate::lexer::Token;
3
4/// Error type used by the tokenizer and the parser internally.
5#[derive(PartialEq, Clone)]
6pub struct Error {
7    pub token: Token,
8    pub raw: Box<str>,
9    pub start: usize,
10    pub end: usize,
11}
12
13impl Debug for Error {
14    #[inline]
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16        write!(f, "Unexpected {:?}({}) at {}:{}", &self.token, &*self.raw, self.start, self.end)
17    }
18}
19
20/// Error type returned by `parser::parse`. This error will include
21/// owned `String` of the source code where the error occurred, so
22/// that a meaningful error can be printed out.
23pub enum ParseError {
24    UnexpectedEndOfProgram,
25    UnexpectedToken {
26        source: String,
27        start: usize,
28        end: usize,
29    },
30}
31
32impl Debug for ParseError {
33    #[inline]
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        fmt::Display::fmt(self, f)
36    }
37}
38
39impl Display for ParseError {
40    #[inline]
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match *self {
43            ParseError::UnexpectedEndOfProgram => {
44                r#try!(write!(f, "Unexpected end of program"))
45            },
46
47            ParseError::UnexpectedToken {
48                ref source,
49                start,
50                end
51            } => {
52                let (lineno, line) = source[..start]
53                                       .lines()
54                                       .enumerate()
55                                       .last()
56                                       .unwrap_or((0, ""));
57
58                let colno = line.chars().count();
59                let token_len = source[start..end].chars().count();
60
61                r#try!(writeln!(f, "Unexpected token at {}:{}\n", lineno + 1, colno + 1));
62
63                let iter = source
64                            .lines()
65                            .enumerate()
66                            .skip_while(|&(index, _)| index < lineno.saturating_sub(2))
67                            .take_while(|&(index, _)| index < lineno + 3);
68
69                let width = log10(lineno + 3);
70
71                for (index, line) in iter {
72                    if index == lineno {
73                        r#try!(writeln!(f, "> {0:1$} | {2}", index+1, width, line));
74
75                        for _ in 0..width {
76                            r#try!(write!(f, " "));
77                        }
78
79                        r#try!(write!(f, "   | "));
80
81                        for _ in 0..colno {
82                            r#try!(write!(f, " "));
83                        }
84
85                        for _ in 0..token_len {
86                            r#try!(write!(f, "^"));
87                        }
88
89                        r#try!(write!(f, "\n"));
90                    } else {
91                        r#try!(writeln!(f, "{0:1$} | {2}", index+1, width+2, line));
92                    }
93                }
94
95            },
96        }
97
98        Ok(())
99    }
100}
101
102fn log10(mut num: usize) -> usize {
103    let mut log = 0;
104
105    while num > 0 {
106        log += 1;
107        num /= 10;
108    }
109
110    log
111}
112
113pub type Result<T> = ::std::result::Result<T, Error>;
114
115pub type ParseResult<T> = ::std::result::Result<T, ParseError>;
116
117#[cfg(test)]
118mod test {
119    use super::*;
120
121    #[test]
122    fn test_format_unexpected_token_error () {
123        let err = ParseError::UnexpectedToken {
124            source: "foo".to_string(),
125            start: 0,
126            end: 1
127        };
128
129        let expected = "Unexpected token at 1:1\n\n> 1 | foo\n    | ^\n";
130
131        assert_eq!(format!("{}", err), expected);
132    }
133
134}