1use hemtt_error::{make_source, thiserror, PrettyError, Source};
2use hemtt_tokens::{Symbol, Token};
3
4use crate::{
5 defines::{Defines, DefinitionLibrary},
6 parse::Rule,
7};
8
9#[derive(thiserror::Error, Debug)]
10pub enum Error {
12 #[error("Expected `{expected:?}`, found `{token:?}`,")]
13 UnexpectedToken {
15 token: Box<Token>,
17 expected: Vec<Symbol>,
19 trace: Vec<Token>,
21 },
22 #[error("Unexpected EOF at `{token:?}`")]
23 UnexpectedEOF {
25 token: Box<Token>,
27 },
28 #[error("Expected `{{ident}}`, found `{token:?}`, ")]
29 ExpectedIdent {
31 token: Box<Token>,
33 trace: Vec<Token>,
35 },
36 #[error("Unknown directive `{directive:?}`, ")]
37 UnknownDirective {
39 directive: Box<Token>,
41 trace: Vec<Token>,
43 },
44 #[error("Function definition has multi-token arguments, `{token:?}`")]
45 DefineMultiTokenArgument {
51 token: Box<Token>,
53 trace: Vec<Token>,
55 },
56 #[error("Can not change built-in macros `{token:?}`")]
57 ChangeBuiltin {
59 token: Box<Token>,
61 trace: Vec<Token>,
63 },
64 #[error("Attempted to use `#if` on a unit or function macro, `{token:?}`")]
65 IfUnitOrFunction {
67 token: Box<Token>,
69 trace: Vec<Token>,
71 },
72 #[error("Attempted to use `#if` on an undefined macro, `{token:?}`")]
73 IfUndefined {
75 token: Box<Token>,
77 trace: Vec<Token>,
79 },
80 #[error("Function call with incorrect number of arguments, expected `{expected}` got `{got}`. `{token:?}`")]
81 FunctionCallArgumentCount {
83 token: Box<Token>,
85 expected: usize,
87 got: usize,
89 trace: Vec<Token>,
91 defines: Defines,
93 },
94 #[error("Expected Function or Value, found Unit, `{token:?}`")]
95 ExpectedFunctionOrValue {
97 token: Box<Token>,
99 trace: Vec<Token>,
101 skipped: Vec<Token>,
103 },
104 #[error("`#include` was encountered while using `NoResolver`")]
105 ResolveWithNoResolver {
107 token: Box<Token>,
109 trace: Vec<Token>,
111 },
112 #[error("`#include` target `{target:?}` was not found")]
113 IncludeNotFound {
115 target: Vec<Token>,
117 trace: Vec<Token>,
119 },
120 #[error("IO Error: {0}")]
121 Io(Box<std::io::Error>),
123 #[error("Pest Error: {0}")]
124 Pest(Box<pest::error::Error<Rule>>),
126}
127
128impl From<std::io::Error> for Error {
129 fn from(e: std::io::Error) -> Self {
130 Self::Io(Box::new(e))
131 }
132}
133
134impl From<pest::error::Error<Rule>> for Error {
135 fn from(e: pest::error::Error<Rule>) -> Self {
136 Self::Pest(Box::new(e))
137 }
138}
139
140impl PrettyError for Error {
141 fn brief(&self) -> String {
142 match self {
143 Self::UnexpectedToken {
144 token,
145 expected,
146 trace: _,
147 } => {
148 format!(
149 "Expected `{expected:?}`, found `{symbol:?}`,",
150 symbol = token.symbol(),
151 expected = expected
152 )
153 }
154 Self::UnexpectedEOF { token } => {
155 format!("Unexpected EOF near `{token:?}`,")
156 }
157 Self::ExpectedIdent { token, trace: _ } => {
158 format!(
159 "Expected `{{ident}}`, found `{symbol:?}`,",
160 symbol = token.symbol()
161 )
162 }
163 Self::UnknownDirective {
164 directive,
165 trace: _,
166 } => {
167 format!(
168 "Unknown directive `{directive:?}`,",
169 directive = directive.symbol()
170 )
171 }
172 Self::DefineMultiTokenArgument { .. } => {
173 "Function definition has multi-token arguments".to_string()
174 }
175 Self::ChangeBuiltin { token, trace: _ } => {
176 format!(
177 "Can not change built-in macros `{symbol:?}`",
178 symbol = token.symbol()
179 )
180 }
181 Self::IfUnitOrFunction { token, trace: _ } => {
182 format!(
183 "Attempted to use `#if` on a unit or function macro, `{symbol:?}`",
184 symbol = token.symbol()
185 )
186 }
187 Self::IfUndefined { token, trace: _ } => {
188 format!(
189 "Attempted to use `#if` on an undefined macro, `{symbol:?}`",
190 symbol = token.symbol()
191 )
192 }
193 Self::FunctionCallArgumentCount {
194 token,
195 expected,
196 got,
197 trace: _,
198 defines: _,
199 } => {
200 format!("Function call with incorrect number of arguments, expected `{expected}` got `{got}`. `{symbol:?}`", symbol = token.symbol())
201 }
202 Self::ExpectedFunctionOrValue { token, .. } => {
203 format!(
204 "Expected Function or Value, found Unit, `{symbol:?}`",
205 symbol = token.symbol()
206 )
207 }
208 Self::ResolveWithNoResolver { token: _, trace: _ } => {
209 "`#include` was encountered while using `NoResolver`".to_string()
210 }
211 Self::IncludeNotFound { target, trace: _ } => {
212 let target = target
213 .iter()
214 .map(|t| t.symbol().to_string())
215 .collect::<String>();
216 format!("`#include` target `{target:?}` was not found")
217 }
218 Self::Io(e) => {
219 format!("IO Error: {e}")
220 }
221 Self::Pest(e) => {
222 format!("Pest Error: {e}")
223 }
224 }
225 }
226
227 fn details(&self) -> Option<String> {
228 match self {
229 Self::ExpectedFunctionOrValue { skipped, .. } => {
230 let empty_comment = skipped.iter().all(|t| {
231 matches!(t.symbol(), Symbol::Comment(_))
232 || matches!(t.symbol(), Symbol::Whitespace(_))
233 });
234 if empty_comment {
235 Some(String::from("`#define` with only a comment is considered a Unit (flag). This differs from other preprocessors."))
236 } else {
237 None
238 }
239 }
240 _ => None,
241 }
242 }
243
244 fn help(&self) -> Option<String> {
245 match self {
246 Self::ExpectedFunctionOrValue { token, skipped, .. } => {
247 let empty_comment = skipped.iter().all(|t| {
248 matches!(t.symbol(), Symbol::Comment(_))
249 || matches!(t.symbol(), Symbol::Whitespace(_))
250 });
251 if empty_comment {
252 Some(format!(
253 "Try using `#define {} ; /* .. */`",
254 token.symbol().output()
255 ))
256 } else {
257 None
258 }
259 }
260 Self::FunctionCallArgumentCount {
261 token,
262 expected: _,
263 got,
264 trace: _,
265 defines,
266 } => {
267 let Symbol::Word(function) = token.symbol() else {
268 return None;
269 };
270 let did_you_mean = defines.similar_function(function, Some(*got));
271 Some(format!("Did you mean `{}`?", did_you_mean.join("`, `")))
272 }
273 _ => None,
274 }
275 }
276
277 fn source(&self) -> Option<Box<Source>> {
278 match self {
279 Self::UnexpectedToken {
280 token,
281 expected,
282 trace: _,
283 } => make_source(token, format!("expected one of: {expected:?}"))
284 .ok()
285 .map(Box::new),
286 Self::ExpectedIdent { token, trace: _ } => {
287 make_source(token, "expected an identifier".to_string())
288 .ok()
289 .map(Box::new)
290 }
291 Self::UnknownDirective {
292 directive,
293 trace: _,
294 } => make_source(directive, "unknown directive".to_string())
295 .ok()
296 .map(Box::new),
297 Self::DefineMultiTokenArgument { token, trace: _ } => {
298 make_source(token, "invalid arguments".to_string())
299 .ok()
300 .map(Box::new)
301 }
302 Self::ChangeBuiltin { token, trace: _ } => {
303 make_source(token, "build-in macro".to_string())
304 .ok()
305 .map(Box::new)
306 }
307 Self::IfUnitOrFunction { token, trace: _ } => {
308 make_source(token, "invalid macro type".to_string())
309 .ok()
310 .map(Box::new)
311 }
312 Self::IfUndefined { token, trace: _ } => {
313 make_source(token, "macro is undefined".to_string())
314 .ok()
315 .map(Box::new)
316 }
317 Self::FunctionCallArgumentCount {
318 token, expected, ..
319 } => make_source(token, format!("Expects {expected} arguments"))
320 .ok()
321 .map(Box::new),
322 Self::ExpectedFunctionOrValue { token, .. } => {
323 make_source(token, "expects function or value".to_string())
324 .ok()
325 .map(Box::new)
326 }
327 _ => None,
328 }
329 }
330
331 fn trace(&self) -> Vec<Source> {
332 let trace = match self {
333 Self::UnexpectedToken { trace, .. }
334 | Self::ExpectedIdent { trace, .. }
335 | Self::UnknownDirective { trace, .. }
336 | Self::DefineMultiTokenArgument { trace, .. }
337 | Self::ChangeBuiltin { trace, .. }
338 | Self::IfUnitOrFunction { trace, .. }
339 | Self::IfUndefined { trace, .. }
340 | Self::FunctionCallArgumentCount { trace, .. }
341 | Self::ExpectedFunctionOrValue { trace, .. }
342 | Self::ResolveWithNoResolver { trace, .. }
343 | Self::IncludeNotFound { trace, .. } => trace.clone(),
344 _ => vec![],
345 };
346 trace
347 .into_iter()
348 .map(|t| make_source(&t, String::new()).unwrap()) .collect()
350 }
351}