1use lalrpop_util::ParseError;
4use thiserror::Error;
5
6use crate::{
7 errors::diagnostics::{ExprDiagnosisSeverity, ExprDiagnostic, get_range},
8 lexer::Token,
9 span::{Span, Spanned},
10 types::Type,
11};
12
13pub type ExprResult<T> = std::result::Result<T, Vec<ExprErrorS>>;
14
15#[derive(Debug, Error, PartialEq)]
16pub enum ExprError {
17 #[error("There was an error lexing expression: {0}")]
18 LexError(#[from] LexicalError),
19 #[error("There was an error in the expression syntax: {0}")]
20 SyntaxError(#[from] SyntaxError),
21 #[error("There was a compliation error with the expression: {0}")]
22 CompileError(#[from] CompileError),
23 #[error("There was a runtime error with the expression: {0}")]
24 RuntimeError(#[from] RuntimeError),
25}
26
27impl diagnostics::AsDiagnostic for ExprError {
28 fn as_diagnostic(&self, source: &str, span: &Span) -> ExprDiagnostic {
29 match self {
30 ExprError::LexError(e) => e.as_diagnostic(source, span),
31 ExprError::CompileError(e) => e.as_diagnostic(source, span),
32 ExprError::SyntaxError(e) => e.as_diagnostic(source, span),
33 ExprError::RuntimeError(e) => e.as_diagnostic(source, span),
34 }
35 }
36}
37
38#[derive(Default, Debug, Clone, PartialEq, Error)]
39pub enum LexicalError {
40 #[default]
41 #[error("Invalid token")]
42 InvalidToken,
43}
44
45impl diagnostics::AsDiagnostic for LexicalError {
46 fn as_diagnostic(&self, source: &str, span: &Span) -> ExprDiagnostic {
47 let error_code = "lexical".to_string();
48 match self {
49 LexicalError::InvalidToken => ExprDiagnostic {
50 code: error_code,
51 range: get_range(source, span),
52 severity: Some(ExprDiagnosisSeverity::ERROR),
53 message: format!("{self}"),
54 },
55 }
56 }
57}
58
59#[derive(Debug, Clone, Error, PartialEq)]
60pub enum SyntaxError {
61 #[error("extraneous input: {token:?}")]
62 ExtraToken { token: String },
63 #[error("invalid input")]
64 InvalidToken,
65 #[error("unexpected input: {token:?}")]
66 UnexpectedInput { token: String },
67 #[error("unexpected end of file; expected: {expected:?}")]
68 UnrecognizedEOF { expected: Vec<String> },
69 #[error("unexpected {token:?}; expected: {expected:?}")]
70 UnrecognizedToken {
71 token: String,
72 expected: Vec<String>,
73 },
74 #[error("unterminated string")]
75 UnterminatedString,
76}
77
78impl SyntaxError {
79 pub fn from_parser_error(
80 err: ParseError<usize, Token, ExprErrorS>,
81 source: &str,
82 ) -> ExprErrorS {
83 match err {
84 ParseError::InvalidToken { location } => {
85 (SyntaxError::InvalidToken.into(), location..location)
86 }
87 ParseError::UnrecognizedEof { location, expected } => (
88 SyntaxError::UnrecognizedEOF { expected }.into(),
89 location..location,
90 ),
91 ParseError::UnrecognizedToken {
92 token: (start, _, end),
93 expected,
94 } => (
95 SyntaxError::UnrecognizedToken {
96 token: source[start..end].to_string(),
97 expected,
98 }
99 .into(),
100 start..end,
101 ),
102 ParseError::ExtraToken {
103 token: (start, _, end),
104 } => (
105 SyntaxError::ExtraToken {
106 token: source[start..end].to_string(),
107 }
108 .into(),
109 start..end,
110 ),
111 ParseError::User { error } => error,
112 }
113 }
114}
115
116impl diagnostics::AsDiagnostic for SyntaxError {
117 fn as_diagnostic(&self, source: &str, span: &Span) -> ExprDiagnostic {
118 let error_code = "syntax".to_string();
119 match self {
120 SyntaxError::ExtraToken { token: _ } => ExprDiagnostic {
121 code: error_code,
122 range: get_range(source, span),
123 severity: Some(ExprDiagnosisSeverity::ERROR),
124 message: format!("{self}"),
125 },
126 SyntaxError::InvalidToken => ExprDiagnostic {
127 code: error_code,
128 range: get_range(source, span),
129 severity: Some(ExprDiagnosisSeverity::ERROR),
130 message: format!("{self}"),
131 },
132 SyntaxError::UnexpectedInput { token: _ } => ExprDiagnostic {
133 code: error_code,
134 range: get_range(source, span),
135 severity: Some(ExprDiagnosisSeverity::ERROR),
136 message: format!("{self}"),
137 },
138 SyntaxError::UnrecognizedEOF { expected: _ } => ExprDiagnostic {
139 code: error_code,
140 range: get_range(source, span),
141 severity: Some(ExprDiagnosisSeverity::ERROR),
142 message: format!("{self}"),
143 },
144 SyntaxError::UnrecognizedToken {
145 token: _,
146 expected: _,
147 } => ExprDiagnostic {
148 code: error_code,
149 range: get_range(source, span),
150 severity: Some(ExprDiagnosisSeverity::ERROR),
151 message: format!("{self}"),
152 },
153 SyntaxError::UnterminatedString => ExprDiagnostic {
154 code: error_code,
155 range: get_range(source, span),
156 severity: Some(ExprDiagnosisSeverity::ERROR),
157 message: format!("{self}"),
158 },
159 }
160 }
161}
162
163#[derive(Debug, Clone, PartialEq, Error)]
164pub enum CompileError {
165 #[error("undefined: {0}")]
166 Undefined(String),
167 #[error("expects {expected} arguments but received {actual}")]
168 WrongNumberOfArgs { expected: usize, actual: usize },
169 #[error("call expression without a callee")]
170 NoCallee,
171 #[error("expected type {expected} but received {actual}")]
172 TypeMismatch { expected: Type, actual: Type },
173 #[error("invalid lookup type: {0}")]
174 InvalidLookupType(u8),
175}
176
177impl diagnostics::AsDiagnostic for CompileError {
178 fn as_diagnostic(&self, source: &str, span: &Span) -> ExprDiagnostic {
179 let error_code = "compiler".to_string();
180 match self {
181 CompileError::Undefined(_) => ExprDiagnostic {
182 code: error_code,
183 range: get_range(source, span),
184 severity: Some(ExprDiagnosisSeverity::ERROR),
185 message: format!("{self}"),
186 },
187 CompileError::WrongNumberOfArgs {
188 expected: _,
189 actual: _,
190 } => ExprDiagnostic {
191 code: error_code,
192 range: get_range(source, span),
193 severity: Some(ExprDiagnosisSeverity::ERROR),
194 message: format!("{self}"),
195 },
196 CompileError::NoCallee => ExprDiagnostic {
197 code: error_code,
198 range: get_range(source, span),
199 severity: Some(ExprDiagnosisSeverity::ERROR),
200 message: format!("{self}"),
201 },
202 CompileError::TypeMismatch {
203 expected: _,
204 actual: _,
205 } => ExprDiagnostic {
206 code: error_code,
207 range: get_range(source, span),
208 severity: Some(ExprDiagnosisSeverity::ERROR),
209 message: format!("{self}"),
210 },
211 CompileError::InvalidLookupType(_) => ExprDiagnostic {
212 code: error_code,
213 range: get_range(source, span),
214 severity: Some(ExprDiagnosisSeverity::ERROR),
215 message: format!("{self}"),
216 },
217 }
218 }
219}
220
221#[derive(Debug, Clone, PartialEq, Error)]
222pub enum RuntimeError {
223 #[error("attempting to pop from an empty stack")]
224 EmptyStack,
225 #[error("expected type {expected} but received {actual}")]
226 TypeMismatch { expected: Type, actual: Type },
227}
228
229impl diagnostics::AsDiagnostic for RuntimeError {
230 fn as_diagnostic(&self, source: &str, span: &Span) -> ExprDiagnostic {
231 let error_code = "runtime".to_string();
232 match self {
233 RuntimeError::EmptyStack => ExprDiagnostic {
234 code: error_code,
235 range: get_range(source, span),
236 severity: Some(ExprDiagnosisSeverity::ERROR),
237 message: format!("{self}"),
238 },
239 RuntimeError::TypeMismatch {
240 expected: _,
241 actual: _,
242 } => ExprDiagnostic {
243 code: error_code,
244 range: get_range(source, span),
245 severity: Some(ExprDiagnosisSeverity::ERROR),
246 message: format!("{self}"),
247 },
248 }
249 }
250}
251
252pub type ExprErrorS = Spanned<ExprError>;
253
254pub mod diagnostics {
255 use codespan_reporting::diagnostic::{Diagnostic, Label, Severity};
256 use line_col::LineColLookup;
257
258 use crate::{errors::ExprErrorS, span::Span};
259
260 pub fn get_diagnostics(errs: &[ExprErrorS], source: &str) -> Vec<Diagnostic<()>> {
261 errs.iter()
262 .map(|(err, span)| {
263 let a = err.as_diagnostic(source, span);
264 let b = a.to_diagnostic(span).with_message(a.message.clone());
265
266 b
267 })
268 .collect()
269 }
270
271 pub trait AsDiagnostic {
272 fn as_diagnostic(&self, source: &str, span: &Span) -> ExprDiagnostic;
273 }
274
275 #[derive(Debug, Eq, PartialEq, Clone, Default)]
276 pub struct ExprDiagnostic {
277 pub code: String,
278
279 pub range: ExprDiagnosticRange,
280
281 pub severity: Option<ExprDiagnosisSeverity>,
282
283 pub message: String,
284 }
285
286 impl ExprDiagnostic {
287 pub fn to_diagnostic(&self, span: &Span) -> codespan_reporting::diagnostic::Diagnostic<()> {
288 codespan_reporting::diagnostic::Diagnostic {
289 severity: ExprDiagnosisSeverity::ERROR.to_severity(),
290 code: Some(self.code.clone()),
291 message: self.message.clone(),
292 labels: vec![Label::primary((), span.clone())],
293 notes: vec![],
294 }
295 }
296 }
297
298 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
299 pub struct ExprDiagnosisSeverity(i32);
300 #[allow(dead_code)]
301 impl ExprDiagnosisSeverity {
302 pub const ERROR: ExprDiagnosisSeverity = ExprDiagnosisSeverity(1);
303 pub const WARNING: ExprDiagnosisSeverity = ExprDiagnosisSeverity(2);
304 pub const INFORMATION: ExprDiagnosisSeverity = ExprDiagnosisSeverity(3);
305 pub const HINT: ExprDiagnosisSeverity = ExprDiagnosisSeverity(4);
306 }
307
308 impl ExprDiagnosisSeverity {
309 fn to_severity(&self) -> Severity {
310 match *self {
311 ExprDiagnosisSeverity::HINT => Severity::Help,
312 ExprDiagnosisSeverity::INFORMATION => Severity::Note,
313 ExprDiagnosisSeverity::WARNING => Severity::Warning,
314 ExprDiagnosisSeverity::ERROR => Severity::Error,
315 _ => panic!("Invalid diagnosis severity: {}", self.0),
316 }
317 }
318 }
319
320 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Default)]
321 pub struct ExprDiagnosticPosition {
322 pub line: u32,
323 pub character: u32,
324 }
325
326 impl ExprDiagnosticPosition {
327 pub fn new(line: u32, character: u32) -> ExprDiagnosticPosition {
328 ExprDiagnosticPosition { line, character }
329 }
330 }
331
332 #[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
333 pub struct ExprDiagnosticRange {
334 pub start: ExprDiagnosticPosition,
336 pub end: ExprDiagnosticPosition,
338 }
339
340 impl ExprDiagnosticRange {
341 pub fn new(
342 start: ExprDiagnosticPosition,
343 end: ExprDiagnosticPosition,
344 ) -> ExprDiagnosticRange {
345 ExprDiagnosticRange { start, end }
346 }
347 }
348
349 pub fn get_range(source: &str, span: &Span) -> ExprDiagnosticRange {
350 ExprDiagnosticRange::new(
351 get_position(source, span.start),
352 get_position(source, span.end),
353 )
354 }
355
356 pub fn get_position(source: &str, idx: usize) -> ExprDiagnosticPosition {
357 let (line, character) = index_to_position(source, idx);
358
359 ExprDiagnosticPosition::new(line as u32, character as u32)
360 }
361
362 pub fn index_to_position(source: &str, index: usize) -> (usize, usize) {
366 let lookup = LineColLookup::new(source);
367
368 let (line, char) = lookup.get(index);
369
370 (line - 1, char - 1)
371 }
372
373 pub fn position_to_index(source: &str, position: (usize, usize)) -> usize {
377 let (line, character) = position;
378 let lines = source.split('\n');
379 let lines_before = lines.take(line);
380 let line_chars_before = lines_before.fold(0usize, |acc, e| acc + e.len() + 1);
381 let chars = character;
382
383 line_chars_before + chars
384 }
385
386 #[cfg(test)]
387 mod index_position_fn_tests {
388 use super::*;
389
390 #[test]
391 fn it_should_convert_index_to_position() {
392 let source = "let a = 123;\nlet b = 456;";
393
394 let index = 17usize;
395 let expected_position = (1, 4);
396
397 let index_to_position = index_to_position(source, index);
398 let actual_position = index_to_position;
399
400 assert_eq!(expected_position, actual_position);
401 }
402
403 #[test]
404 fn it_should_convert_position_to_index() {
405 let source = "let a = 123;\nlet b = 456;";
406 let position = (1, 4);
407 let expected_index = 17usize;
408 let actual_index = position_to_index(source, position);
409
410 assert_eq!(expected_index, actual_index);
411 }
412
413 #[test]
414 fn it_should_convert_position_to_index_and_back() {
415 let source = "let a = 123;\nlet b = 456;";
416 let position = (1, 4);
417 let actual_index = position_to_index(source, position);
418
419 assert_eq!(position, index_to_position(source, actual_index));
420 }
421
422 #[test]
423 fn it_should_convert_position_to_index_and_back_b() {
424 let source = "let a = 123;\n{\n let b = 456;\n}";
425 let position = (2, 12);
426 let actual_index = position_to_index(source, position);
427
428 assert_eq!(position, index_to_position(source, actual_index));
429 }
430
431 #[test]
432 fn it_should_convert_position_to_index_b() {
433 let source = "let a = 123;\n{\n let b = 456;\n}";
434 let position = (2, 12);
435 let actual_index = position_to_index(source, position);
436
437 assert_eq!(27, actual_index);
438 }
439
440 #[test]
441 fn it_should_convert_position_to_index_c() {
442 let source = "let a = 123;\nlet b = 456;\nlet c = 789;";
443 let position = (2, 8);
444 let actual_index = position_to_index(source, position);
445
446 assert_eq!(34, actual_index);
447 }
448
449 #[test]
450 fn it_should_convert_position_to_index_d() {
451 let source = "let a = 123;\nlet b = 456;\nlet c = 789;\nlet d = 000;";
452 let position = (3, 8);
453 let actual_index = position_to_index(source, position);
454
455 assert_eq!(47, actual_index);
456 }
457
458 #[test]
459 fn it_should_convert_position_to_index_e() {
460 let source = "let a = 123;\nlet b = 456;\nlet c = 789;\nlet d = 000;\nlet e = 999;";
461 let position = (4, 8);
462 let actual_index = position_to_index(source, position);
463
464 assert_eq!(60, actual_index);
465 }
466
467 #[test]
468 fn it_should_convert_position_to_index_f() {
469 let source = "let a = 123;\nlet b = 456;\nlet c = 789;\nlet d = 000;\nlet e = 999;\n";
470 let position = (4, 8);
471 let actual_index = position_to_index(source, position);
472
473 assert_eq!(60, actual_index);
474 }
475 }
476
477 #[cfg(test)]
478 mod error_to_diagnostics_tests {
479 use crate::errors::{CompileError, ExprError, LexicalError};
480
481 use super::*;
482 use std::ops::Range;
483
484 fn dummy_source() -> &'static str {
485 "fn test_function(x: i32) -> i32 { x + 1 }"
486 }
487
488 fn dummy_range() -> Span {
489 Range { start: 0, end: 5 }
490 }
491
492 #[test]
493 fn it_converts_lexerror_to_diagnostic() {
494 let source = dummy_source();
495 let range = dummy_range();
496 let error = ExprError::LexError(LexicalError::InvalidToken);
497 let diagnostics = get_diagnostics(&[(error, range.clone())], source);
498
499 assert_eq!(diagnostics.len(), 1);
500 let diagnostic = &diagnostics[0];
501 assert_eq!(diagnostic.code, Some("lexical".to_string()));
502 assert_eq!(diagnostic.message, "Invalid token".to_string());
503 assert_eq!(diagnostic.severity, Severity::Error);
504 assert_eq!(diagnostic.labels.len(), 1);
505 assert_eq!(diagnostic.labels[0], Label::primary((), range));
506 }
507
508 #[test]
509 fn it_converts_compileerror_undefined_to_diagnostic() {
510 let source = dummy_source();
511 let range = dummy_range();
512 let error = ExprError::CompileError(CompileError::Undefined("var".to_string()));
513 let diagnostics = get_diagnostics(&[(error, range.clone())], source);
514
515 assert_eq!(diagnostics.len(), 1);
516 let diagnostic = &diagnostics[0];
517 assert_eq!(diagnostic.code, Some("compiler".to_string()));
518 assert_eq!(diagnostic.message, "undefined: var".to_string());
519 assert_eq!(diagnostic.severity, Severity::Error);
520 assert_eq!(diagnostic.labels.len(), 1);
521 assert_eq!(diagnostic.labels[0], Label::primary((), range));
522 }
523
524 #[test]
525 fn it_converts_compileerror_wrong_number_of_args_to_diagnostic() {
526 let source = dummy_source();
527 let range = dummy_range();
528 let error = ExprError::CompileError(CompileError::WrongNumberOfArgs {
529 expected: 2,
530 actual: 3,
531 });
532 let diagnostics = get_diagnostics(&[(error, range.clone())], source);
533
534 assert_eq!(diagnostics.len(), 1);
535 let diagnostic = &diagnostics[0];
536 assert_eq!(diagnostic.code, Some("compiler".to_string()));
537 assert_eq!(
538 diagnostic.message,
539 "expects 2 arguments but received 3".to_string()
540 );
541 assert_eq!(diagnostic.severity, Severity::Error);
542 assert_eq!(diagnostic.labels.len(), 1);
543 assert_eq!(diagnostic.labels[0], Label::primary((), range));
544 }
545 }
546}