1use std::fmt;
4use crate::ast::Span;
5
6pub type Result<T> = std::result::Result<T, Error>;
7
8#[derive(Debug, Clone, PartialEq)]
10pub struct Error {
11 pub kind: ErrorKind,
12 pub span: Span,
13 pub source: Option<String>,
14}
15
16#[derive(Debug, Clone, PartialEq)]
17#[non_exhaustive]
18pub enum ErrorKind {
19 UnexpectedChar(char),
21 UnterminatedString,
22 InvalidNumber(String),
23
24 UnexpectedToken {
26 expected: String,
27 found: String,
28 },
29 UnexpectedEof,
30 InvalidExpression(String),
31 InvalidTypeInst(String),
32
33 UnsupportedFeature {
35 feature: String,
36 phase: String,
37 workaround: Option<String>,
38 },
39 TypeError {
40 expected: String,
41 found: String,
42 },
43 DuplicateDeclaration(String),
44 UndefinedVariable(String),
45
46 ArraySizeMismatch {
48 declared: usize,
49 provided: usize,
50 },
51 Array2DSizeMismatch {
52 declared_rows: usize,
53 declared_cols: usize,
54 provided_rows: usize,
55 provided_cols: usize,
56 },
57 Array3DSizeMismatch {
58 declared_d1: usize,
59 declared_d2: usize,
60 declared_d3: usize,
61 provided_d1: usize,
62 provided_d2: usize,
63 provided_d3: usize,
64 },
65 Array2DValueCountMismatch {
66 expected: usize,
67 provided: usize,
68 },
69 Array3DValueCountMismatch {
70 expected: usize,
71 provided: usize,
72 },
73 Array2DInvalidContext,
74 Array3DInvalidContext,
75 Array2DValuesMustBeLiteral,
76 Array3DValuesMustBeLiteral,
77
78 Message(String),
80}
81
82impl Error {
83 pub fn new(kind: ErrorKind, span: Span) -> Self {
84 Self {
85 kind,
86 span,
87 source: None,
88 }
89 }
90
91 pub fn with_source(mut self, source: String) -> Self {
92 self.source = Some(source);
93 self
94 }
95
96 pub fn unexpected_token(expected: &str, found: &str, span: Span) -> Self {
97 Self::new(
98 ErrorKind::UnexpectedToken {
99 expected: expected.to_string(),
100 found: found.to_string(),
101 },
102 span,
103 )
104 }
105
106 pub fn unexpected_eof(span: Span) -> Self {
107 Self::new(ErrorKind::UnexpectedEof, span)
108 }
109
110 pub fn unsupported_feature(feature: &str, phase: &str, span: Span) -> Self {
111 Self::new(
112 ErrorKind::UnsupportedFeature {
113 feature: feature.to_string(),
114 phase: phase.to_string(),
115 workaround: None,
116 },
117 span,
118 )
119 }
120
121 pub fn type_error(expected: &str, found: &str, span: Span) -> Self {
122 Self::new(
123 ErrorKind::TypeError {
124 expected: expected.to_string(),
125 found: found.to_string(),
126 },
127 span,
128 )
129 }
130
131 pub fn array_size_mismatch(declared: usize, provided: usize, span: Span) -> Self {
132 Self::new(
133 ErrorKind::ArraySizeMismatch { declared, provided },
134 span,
135 )
136 }
137
138 pub fn array2d_size_mismatch(declared_rows: usize, declared_cols: usize,
139 provided_rows: usize, provided_cols: usize, span: Span) -> Self {
140 Self::new(
141 ErrorKind::Array2DSizeMismatch {
142 declared_rows, declared_cols, provided_rows, provided_cols
143 },
144 span,
145 )
146 }
147
148 pub fn array3d_size_mismatch(declared_d1: usize, declared_d2: usize, declared_d3: usize,
149 provided_d1: usize, provided_d2: usize, provided_d3: usize, span: Span) -> Self {
150 Self::new(
151 ErrorKind::Array3DSizeMismatch {
152 declared_d1, declared_d2, declared_d3, provided_d1, provided_d2, provided_d3
153 },
154 span,
155 )
156 }
157
158 pub fn array2d_value_count_mismatch(expected: usize, provided: usize, span: Span) -> Self {
159 Self::new(
160 ErrorKind::Array2DValueCountMismatch { expected, provided },
161 span,
162 )
163 }
164
165 pub fn array3d_value_count_mismatch(expected: usize, provided: usize, span: Span) -> Self {
166 Self::new(
167 ErrorKind::Array3DValueCountMismatch { expected, provided },
168 span,
169 )
170 }
171
172 pub fn array2d_invalid_context(span: Span) -> Self {
173 Self::new(ErrorKind::Array2DInvalidContext, span)
174 }
175
176 pub fn array3d_invalid_context(span: Span) -> Self {
177 Self::new(ErrorKind::Array3DInvalidContext, span)
178 }
179
180 pub fn array2d_values_must_be_literal(span: Span) -> Self {
181 Self::new(ErrorKind::Array2DValuesMustBeLiteral, span)
182 }
183
184 pub fn array3d_values_must_be_literal(span: Span) -> Self {
185 Self::new(ErrorKind::Array3DValuesMustBeLiteral, span)
186 }
187
188 pub fn message(msg: &str, span: Span) -> Self {
189 Self::new(ErrorKind::Message(msg.to_string()), span)
190 }
191
192 pub fn with_workaround(mut self, workaround: &str) -> Self {
193 if let ErrorKind::UnsupportedFeature { workaround: w, .. } = &mut self.kind {
194 *w = Some(workaround.to_string());
195 }
196 self
197 }
198
199 pub fn location(&self) -> (usize, usize) {
201 if let Some(source) = &self.source {
202 let mut line = 1;
203 let mut col = 1;
204 let pos = if self.span.start >= source.len() {
205 source.len().saturating_sub(1)
207 } else {
208 self.span.start
209 };
210
211 for (i, c) in source.chars().enumerate() {
212 if i >= pos {
213 break;
214 }
215 if c == '\n' {
216 line += 1;
217 col = 1;
218 } else {
219 col += 1;
220 }
221 }
222 (line, col)
223 } else {
224 (0, 0)
225 }
226 }
227
228 pub fn source_line(&self) -> Option<(String, usize)> {
230 self.source.as_ref().map(|source| {
231 let lines: Vec<&str> = source.lines().collect();
232 let (line_num, col) = self.location();
233 let line = if line_num > 0 && line_num <= lines.len() {
234 lines[line_num - 1].to_string()
235 } else {
236 String::new()
237 };
238
239 let adjusted_col = if col > line.len() {
241 line.len()
242 } else {
243 col
244 };
245
246 (line, adjusted_col)
247 })
248 }
249}
250
251impl fmt::Display for Error {
252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253 let (line, col) = self.location();
254
255 write!(f, "Error")?;
256 if line > 0 {
257 write!(f, " at line {}, column {}", line, col)?;
258 }
259 write!(f, ": ")?;
260
261 match &self.kind {
262 ErrorKind::UnexpectedChar(c) => {
263 write!(f, "Unexpected character '{}'", c)
264 }
265 ErrorKind::UnterminatedString => {
266 write!(f, "Unterminated string literal")
267 }
268 ErrorKind::InvalidNumber(s) => {
269 write!(f, "Invalid number: {}", s)
270 }
271 ErrorKind::UnexpectedToken { expected, found } => {
272 write!(f, "Expected {}, found {}", expected, found)
273 }
274 ErrorKind::UnexpectedEof => {
275 write!(f, "Unexpected end of file")
276 }
277 ErrorKind::InvalidExpression(msg) => {
278 write!(f, "Invalid expression: {}", msg)
279 }
280 ErrorKind::InvalidTypeInst(msg) => {
281 write!(f, "Invalid type-inst: {}", msg)
282 }
283 ErrorKind::UnsupportedFeature { feature, phase, workaround } => {
284 write!(f, "Unsupported feature: {}", feature)?;
285 write!(f, " (will be supported in {})", phase)?;
286 if let Some(w) = workaround {
287 write!(f, "\nWorkaround: {}", w)?;
288 }
289 Ok(())
290 }
291 ErrorKind::TypeError { expected, found } => {
292 write!(f, "Type error: expected {}, found {}", expected, found)
293 }
294 ErrorKind::DuplicateDeclaration(name) => {
295 write!(f, "Duplicate declaration of '{}'", name)
296 }
297 ErrorKind::UndefinedVariable(name) => {
298 write!(f, "Undefined variable '{}'", name)
299 }
300 ErrorKind::ArraySizeMismatch { declared, provided } => {
301 write!(f, "Array size mismatch: declared {}, but got {}", declared, provided)
302 }
303 ErrorKind::Array2DSizeMismatch { declared_rows, declared_cols, provided_rows, provided_cols } => {
304 write!(f, "array2d size mismatch: declared {}x{}, but provided {}x{}",
305 declared_rows, declared_cols, provided_rows, provided_cols)
306 }
307 ErrorKind::Array3DSizeMismatch { declared_d1, declared_d2, declared_d3, provided_d1, provided_d2, provided_d3 } => {
308 write!(f, "array3d size mismatch: declared {}x{}x{}, but provided {}x{}x{}",
309 declared_d1, declared_d2, declared_d3, provided_d1, provided_d2, provided_d3)
310 }
311 ErrorKind::Array2DValueCountMismatch { expected, provided } => {
312 write!(f, "array2d value count mismatch: expected {}, got {}", expected, provided)
313 }
314 ErrorKind::Array3DValueCountMismatch { expected, provided } => {
315 write!(f, "array3d value count mismatch: expected {}, got {}", expected, provided)
316 }
317 ErrorKind::Array2DInvalidContext => {
318 write!(f, "array2d() can only be used for 2D arrays")
319 }
320 ErrorKind::Array3DInvalidContext => {
321 write!(f, "array3d() can only be used for 3D arrays")
322 }
323 ErrorKind::Array2DValuesMustBeLiteral => {
324 write!(f, "array2d() values must be an array literal [...]")
325 }
326 ErrorKind::Array3DValuesMustBeLiteral => {
327 write!(f, "array3d() values must be an array literal [...]")
328 }
329 ErrorKind::Message(msg) => {
330 write!(f, "{}", msg)
331 }
332 }?;
333
334 if let Some((source_line, col)) = self.source_line() {
335 write!(f, "\n {}", source_line)?;
336 if col > 0 {
337 write!(f, "\n {}{}", " ".repeat(col - 1), "^")?;
338 } else if source_line.is_empty() && matches!(self.kind, ErrorKind::UnexpectedEof) {
339 write!(f, "\n ^")?;
341 }
342 }
343
344 Ok(())
345 }
346}
347
348impl std::error::Error for Error {}
349
350impl From<String> for Error {
351 fn from(msg: String) -> Self {
352 Self::new(ErrorKind::Message(msg), Span::dummy())
353 }
354}
355
356impl From<&str> for Error {
357 fn from(msg: &str) -> Self {
358 Self::new(ErrorKind::Message(msg.to_string()), Span::dummy())
359 }
360}