1use crate::SourceFile;
12use crate::Span;
13
14use std::collections::HashSet;
15use std::fmt;
16use std::sync::Arc;
17
18use strum::EnumDiscriminants;
19
20#[cfg(feature = "ariadne")]
21mod ariadne;
22#[cfg(feature = "ariadne")]
23pub use self::ariadne::Report;
24
25#[derive(Debug, Clone, EnumDiscriminants)]
26#[strum_discriminants(repr(u16))]
27#[repr(u16)]
28pub(crate) enum ErrorKind {
29 Silent,
30 UnknownCharacter(Span),
31 UnterminatedGroup {
32 start: String,
33 span: Span,
34 },
35 UnterminatedChar(Span),
36 LongChar(Span),
37 UnterminatedString(Span),
38 UnexpectedToken {
39 expected: HashSet<String>,
40 span: Span,
41 },
42 EndOfFile(usize),
43 Custom {
44 message: String,
45 span: Span,
46 code: u16,
47 },
48}
49
50impl ErrorKind {
51 fn code(&self) -> u16 {
52 let discriminant = ErrorKindDiscriminants::from(self) as u16;
55 if let ErrorKind::Custom { code, .. } = self {
56 discriminant + code
57 } else {
58 discriminant
59 }
60 }
61
62 fn start(&self) -> usize {
63 match self {
64 ErrorKind::Silent => panic!("called `start` on `ErrorKind::Silent`"),
65 ErrorKind::Custom { span, .. }
66 | ErrorKind::UnknownCharacter(span)
67 | ErrorKind::UnterminatedGroup { span, .. }
68 | ErrorKind::UnterminatedChar(span)
69 | ErrorKind::LongChar(span)
70 | ErrorKind::UnterminatedString(span)
71 | ErrorKind::UnexpectedToken { span, .. } => span.start,
72 ErrorKind::EndOfFile(n) => *n,
73 }
74 }
75}
76
77fn unexpected_token_message(expected: &HashSet<String>) -> String {
78 if expected.len() == 1 {
79 format!("Expected {}", expected.iter().next().unwrap())
80 } else if expected.len() == 2 {
81 let mut iter = expected.iter();
82 format!(
83 "Expected {} or {}",
84 iter.next().unwrap(),
85 iter.next().unwrap()
86 )
87 } else {
88 let mut message = "Expected one of: ".to_string();
89 for (i, token) in expected.iter().enumerate() {
90 message.push_str(token);
91 if i + 1 < expected.len() {
92 message.push_str(", ");
93 }
94 }
95 message
96 }
97}
98
99#[derive(Debug, Clone)]
100pub(crate) struct SingleError {
101 source: Arc<SourceFile>,
102 kind: ErrorKind,
103}
104
105#[derive(Debug, Clone)]
113pub struct Error {
114 errors: Vec<SingleError>,
115}
116
117impl Error {
118 pub(crate) fn new(source: Arc<SourceFile>, kind: ErrorKind) -> Error {
119 Error {
120 errors: vec![SingleError { source, kind }],
121 }
122 }
123
124 pub(crate) const fn empty() -> Error {
125 Error { errors: vec![] }
126 }
127
128 pub(crate) fn is_empty(&self) -> bool {
129 self.errors.is_empty()
130 }
131
132 pub(crate) fn eof_to_group(&mut self, span: &Span, start: &str) {
133 for error in &mut self.errors {
134 if let ErrorKind::EndOfFile(_) = error.kind {
135 error.kind = ErrorKind::UnterminatedGroup {
136 start: start.to_string(),
137 span: span.clone(),
138 };
139 }
140 }
141 }
142
143 pub(crate) fn group_to_string(&mut self) {
144 for error in &mut self.errors {
145 if let ErrorKind::UnterminatedGroup { span, .. } = &error.kind {
146 let span = span.to_owned();
147 error.kind = ErrorKind::UnterminatedString(span);
148 }
149 }
150 }
151
152 pub(crate) fn string_to_char(&mut self) {
153 for error in &mut self.errors {
154 if let ErrorKind::UnterminatedString(span) = &error.kind {
155 let span = span.to_owned();
156 error.kind = ErrorKind::UnterminatedChar(span);
157 }
158 }
159 }
160
161 pub fn add(&mut self, mut other: Error) {
163 self.errors.append(&mut other.errors);
164 }
165
166 #[must_use]
169 pub fn with(mut self, other: Error) -> Self {
170 self.add(other);
171 self
172 }
173}
174
175impl fmt::Display for Error {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 for error in &self.errors {
178 match &error.kind {
179 ErrorKind::Silent => {}
180 ErrorKind::Custom { message, span, .. } => {
181 writeln!(f, "[E{:02}] Error: {}", error.kind.code(), message)?;
182 let (line, col) = span.start_location();
183 write!(f, "[{}:{}:{}]", error.source.id(), line, col)?;
184 }
185 ErrorKind::UnknownCharacter(span) => {
186 writeln!(
187 f,
188 "[E{:02}] Error: Unrecognised character",
189 error.kind.code()
190 )?;
191 let (line, col) = span.start_location();
192 write!(f, "[{}:{}:{}]", error.source.id(), line, col)?;
193 }
194 ErrorKind::UnterminatedGroup { start, span } => {
195 writeln!(
196 f,
197 "[E{:02}] Error: Unmatched '{}'",
198 error.kind.code(),
199 start
200 )?;
201 let (line, col) = span.start_location();
202 write!(f, "[{}:{}:{}]", error.source.id(), line, col)?;
203 }
204 ErrorKind::UnterminatedChar(span) => {
205 writeln!(
206 f,
207 "[E{:02}] Error: Unterminated character literal",
208 error.kind.code()
209 )?;
210 let (line, col) = span.start_location();
211 write!(f, "[{}:{}:{}]", error.source.id(), line, col)?;
212 }
213 ErrorKind::LongChar(span) => {
214 writeln!(
215 f,
216 "[E{:02}] Error: Character literals must be exactly one character long",
217 error.kind.code()
218 )?;
219 let (line, col) = span.start_location();
220 write!(f, "[{}:{}:{}]", error.source.id(), line, col)?;
221 }
222 ErrorKind::UnterminatedString(span) => {
223 writeln!(
224 f,
225 "[E{:02}] Error: Unterminated string literal",
226 error.kind.code()
227 )?;
228 let (line, col) = span.start_location();
229 write!(f, "[{}:{}:{}]", error.source.id(), line, col)?;
230 }
231 ErrorKind::UnexpectedToken { expected, span } => {
232 writeln!(f, "[E{:02}] Error: Unexpected token", error.kind.code())?;
233 let (line, col) = span.start_location();
234 writeln!(f, "[{}:{}:{}]", error.source.id(), line, col)?;
235 write!(f, "{}", unexpected_token_message(expected))?;
236 }
237 ErrorKind::EndOfFile(_) => write!(f, "Unexpected end of file while parsing")?,
238 }
239 }
240
241 Ok(())
242 }
243}