1use std::{
2 borrow::Cow,
3 fmt::{Debug, Display},
4};
5
6use itertools::Itertools;
7use thiserror::Error;
8
9use crate::{
10 lexer::Token,
11 span::Span,
12 syntax::{BinaryOperator, DeclarationKind},
13};
14
15#[derive(Error, Clone, Debug, PartialEq)]
17pub enum ErrorKind {
18 #[error("invalid token")]
19 InvalidToken,
20 #[error("use of a reserved word `{0}`")]
21 ReservedWord(String),
22 #[error("unexpected token `{token}`, expected `{}`", .expected.iter().format(", "))]
23 UnexpectedToken {
24 token: String,
25 expected: Vec<String>,
26 },
27 #[error("unexpected end of file, expected `{}`", .expected.iter().format(", "))]
28 UnexpectedEof { expected: Vec<String> },
29 #[error("extra token `{0}` at the end of the file")]
30 ExtraToken(String),
31 #[error("invalid diagnostic severity")]
32 DiagnosticSeverity,
33 #[error("invalid `{0}` attribute, {1}")]
34 Attribute(&'static str, &'static str),
35 #[error("invalid `var` template arguments, {0}")]
36 VarTemplate(&'static str),
37 #[error("cannot mix `{0}` and `{1}` operators without parentheses")]
38 MixedBinaryOperators(BinaryOperator, BinaryOperator),
39 #[error("this syntax is only supported with feature \"{0}\" enabled")]
40 UnsupportedExtension(&'static str),
41 #[error("invalid {0} declaration: {1}")]
42 InvalidDeclaration(DeclarationKind, &'static str),
43}
44
45#[derive(Default, Clone, Debug, PartialEq)]
46pub enum ParseError {
47 #[default]
48 LexerError,
49 ReservedWord(String),
50 DiagnosticSeverity,
51 Attribute(&'static str, &'static str),
52 VarTemplate(&'static str),
53 MixedBinaryOperators(BinaryOperator, BinaryOperator),
54 UnsupportedExtension(&'static str),
55 InvalidDeclaration(DeclarationKind, &'static str),
56}
57
58type LalrpopError = lalrpop_util::ParseError<usize, Token, (usize, ParseError, usize)>;
59
60#[derive(Error, Clone, Debug, PartialEq)]
64pub struct Error {
65 pub error: ErrorKind,
66 pub span: Span,
67}
68
69impl Error {
70 pub fn with_source(self, source: Cow<'_, str>) -> ErrorWithSource<'_> {
73 ErrorWithSource::new(self, source)
74 }
75}
76
77impl Display for Error {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 write!(f, "chars {:?}: {}", self.span.range(), self.error)
80 }
81}
82
83impl From<LalrpopError> for Error {
84 fn from(err: LalrpopError) -> Self {
85 match err {
86 LalrpopError::InvalidToken { location } => {
87 let span = Span::new(location..location + 1);
88 let error = ErrorKind::InvalidToken;
89 Self { span, error }
90 }
91 LalrpopError::UnrecognizedEof { location, expected } => {
92 let span = Span::new(location..location + 1);
93 let error = ErrorKind::UnexpectedEof { expected };
94 Self { span, error }
95 }
96 LalrpopError::UnrecognizedToken {
97 token: (l, token, r),
98 expected,
99 } => {
100 let span = Span::new(l..r);
101 let error = ErrorKind::UnexpectedToken {
102 token: token.to_string(),
103 expected,
104 };
105 Self { span, error }
106 }
107 LalrpopError::ExtraToken {
108 token: (l, token, r),
109 } => {
110 let span = Span::new(l..r);
111 let error = ErrorKind::ExtraToken(token.to_string());
112 Self { span, error }
113 }
114 LalrpopError::User {
115 error: (l, error, r),
116 } => {
117 let span = Span::new(l..r);
118 let error = match error {
119 ParseError::LexerError => ErrorKind::InvalidToken,
120 ParseError::ReservedWord(word) => ErrorKind::ReservedWord(word),
121 ParseError::DiagnosticSeverity => ErrorKind::DiagnosticSeverity,
122 ParseError::Attribute(attr, expected) => ErrorKind::Attribute(attr, expected),
123 ParseError::VarTemplate(reason) => ErrorKind::VarTemplate(reason),
124 ParseError::MixedBinaryOperators(a, b) => ErrorKind::MixedBinaryOperators(a, b),
125 ParseError::UnsupportedExtension(ext) => ErrorKind::UnsupportedExtension(ext),
126 ParseError::InvalidDeclaration(kind, msg) => {
127 ErrorKind::InvalidDeclaration(kind, msg)
128 }
129 };
130 Self { span, error }
131 }
132 }
133 }
134}
135
136#[derive(Clone, Debug, PartialEq)]
138pub struct ErrorWithSource<'s> {
139 pub error: Error,
140 pub source: Cow<'s, str>,
141}
142
143impl std::error::Error for ErrorWithSource<'_> {}
144
145impl<'s> ErrorWithSource<'s> {
146 pub fn new(error: Error, source: Cow<'s, str>) -> Self {
147 Self { error, source }
148 }
149}
150
151impl Display for ErrorWithSource<'_> {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 use annotate_snippets::*;
154 let text = format!("{}", self.error.error);
155
156 let annot = AnnotationKind::Primary.span(self.error.span.range());
157 let snip = Snippet::source(self.source.clone())
158 .fold(true)
159 .annotation(annot);
160 let title = Level::ERROR.primary_title(&text);
161 let group = title.element(snip);
162
163 let renderer = Renderer::styled();
164 let rendered = renderer.render(&[group]);
165 write!(f, "{rendered}")
166 }
167}