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