1use std::fmt::{self, Debug, Display};
2use crate::lexer::Token;
3
4#[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
20pub 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}