1use std::{
2 borrow::Cow,
3 fmt::{Debug, Display},
4};
5
6use itertools::Itertools;
7use thiserror::Error;
8
9use crate::{lexer::Token, span::Span};
10
11#[derive(Error, Clone, Debug, PartialEq)]
13pub enum ErrorKind {
14 #[error("invalid token")]
15 InvalidToken,
16 #[error("unexpected token `{token}`, expected `{}`", .expected.iter().format(", "))]
17 UnexpectedToken {
18 token: String,
19 expected: Vec<String>,
20 },
21 #[error("unexpected end of file, expected `{}`", .expected.iter().format(", "))]
22 UnexpectedEof { expected: Vec<String> },
23 #[error("extra token `{0}` at the end of the file")]
24 ExtraToken(String),
25 #[error("invalid diagnostic severity")]
26 DiagnosticSeverity,
27 #[error("invalid `{0}` attribute, {1}")]
28 Attribute(&'static str, &'static str),
29 #[error("invalid `var` template arguments, {0}")]
30 VarTemplate(&'static str),
31}
32
33#[derive(Default, Clone, Debug, PartialEq)]
34pub(crate) enum CustomLalrError {
35 #[default]
36 LexerError,
37 DiagnosticSeverity,
38 Attribute(&'static str, &'static str),
39 VarTemplate(&'static str),
40}
41
42type LalrError = lalrpop_util::ParseError<usize, Token, (usize, CustomLalrError, usize)>;
43
44#[derive(Error, Clone, Debug, PartialEq)]
48pub struct Error {
49 pub error: ErrorKind,
50 pub span: Span,
51}
52
53impl Error {
54 pub fn with_source(self, source: Cow<'_, str>) -> ErrorWithSource<'_> {
57 ErrorWithSource::new(self, source)
58 }
59}
60
61impl Display for Error {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 write!(f, "chars {:?}: {}", self.span.range(), self.error)
64 }
65}
66
67impl From<LalrError> for Error {
68 fn from(err: LalrError) -> Self {
69 match err {
70 LalrError::InvalidToken { location } => {
71 let span = Span::new(location..location + 1);
72 let error = ErrorKind::InvalidToken;
73 Self { span, error }
74 }
75 LalrError::UnrecognizedEof { location, expected } => {
76 let span = Span::new(location..location + 1);
77 let error = ErrorKind::UnexpectedEof { expected };
78 Self { span, error }
79 }
80 LalrError::UnrecognizedToken {
81 token: (l, token, r),
82 expected,
83 } => {
84 let span = Span::new(l..r);
85 let error = ErrorKind::UnexpectedToken {
86 token: token.to_string(),
87 expected,
88 };
89 Self { span, error }
90 }
91 LalrError::ExtraToken {
92 token: (l, token, r),
93 } => {
94 let span = Span::new(l..r);
95 let error = ErrorKind::ExtraToken(token.to_string());
96 Self { span, error }
97 }
98 LalrError::User {
99 error: (l, error, r),
100 } => {
101 let span = Span::new(l..r);
102 let error = match error {
103 CustomLalrError::DiagnosticSeverity => ErrorKind::DiagnosticSeverity,
104 CustomLalrError::LexerError => ErrorKind::InvalidToken,
105 CustomLalrError::Attribute(attr, expected) => {
106 ErrorKind::Attribute(attr, expected)
107 }
108 CustomLalrError::VarTemplate(reason) => ErrorKind::VarTemplate(reason),
109 };
110 Self { span, error }
111 }
112 }
113 }
114}
115
116#[derive(Clone, Debug, PartialEq)]
118pub struct ErrorWithSource<'s> {
119 pub error: Error,
120 pub source: Cow<'s, str>,
121}
122
123impl std::error::Error for ErrorWithSource<'_> {}
124
125impl<'s> ErrorWithSource<'s> {
126 pub fn new(error: Error, source: Cow<'s, str>) -> Self {
127 Self { error, source }
128 }
129}
130
131impl Display for ErrorWithSource<'_> {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 use annotate_snippets::*;
134 let text = format!("{}", self.error.error);
135
136 let annot = Level::Info.span(self.error.span.range());
137 let snip = Snippet::source(&self.source).fold(true).annotation(annot);
138 let msg = Level::Error.title(&text).snippet(snip);
139
140 let renderer = Renderer::styled();
141 let rendered = renderer.render(msg);
142 write!(f, "{rendered}")
143 }
144}