Skip to main content

scarf_python/
error.rs

1// =======================================================================
2// error.rs
3// =======================================================================
4//! Making Rust errors Python-compatible
5
6use crate::{Span, Token};
7use pyo3::prelude::*;
8
9// -----------------------------------------------------------------------
10// Verbose
11// -----------------------------------------------------------------------
12
13/// An expectation instead of what was found
14#[pyclass(eq, from_py_object, module = "scarf_python")]
15#[derive(Clone, PartialEq, Eq)]
16pub enum Expectation {
17    /// A specific token
18    Token { token: Token },
19    /// A verbose human-readable label
20    Label { label: String },
21    /// The end of a file
22    EOI(),
23}
24
25impl<'a> From<scarf_parser::Expectation<'a>> for Expectation {
26    fn from(value: scarf_parser::Expectation<'a>) -> Self {
27        match value {
28            scarf_parser::Expectation::Token(rust_token) => {
29                Expectation::Token {
30                    token: rust_token.into(),
31                }
32            }
33            scarf_parser::Expectation::Label(rust_str) => Expectation::Label {
34                label: rust_str.to_string(),
35            },
36            scarf_parser::Expectation::EOI => Expectation::EOI(),
37        }
38    }
39}
40
41impl<'a> std::fmt::Display for Expectation {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        match self {
44            Expectation::Token { token } => {
45                let rust_token: scarf_parser::Token = token.into();
46                rust_token.fmt(f)
47            }
48            Expectation::Label { label } => write!(f, "{}", label),
49            Expectation::EOI() => write!(f, "end of input"),
50        }
51    }
52}
53
54/// A wrapper around [`scarf_parser::VerboseError`]
55#[pyclass(eq, from_py_object, module = "scarf_python")]
56#[derive(Clone, PartialEq, Eq)]
57pub struct VerboseError {
58    /// The [`Span`] that the error occurred at
59    #[pyo3(get, set)]
60    pub span: Span,
61    /// What token was found - [`None`] if the end of the file was reached
62    #[pyo3(get, set)]
63    pub found: Option<Token>,
64    /// What was expected instead (listing all possibilities)
65    #[pyo3(get, set)]
66    pub expected: Vec<Expectation>,
67}
68
69impl<'a> From<scarf_parser::VerboseError<'a>> for VerboseError {
70    fn from(value: scarf_parser::VerboseError<'a>) -> Self {
71        Self {
72            span: value.span.into(),
73            found: match value.found {
74                Some(rust_token) => Some(rust_token.into()),
75                None => None,
76            },
77            expected: value
78                .expected
79                .into_iter()
80                .map(|rust_expectation| rust_expectation.into())
81                .collect(),
82        }
83    }
84}
85
86impl<'a> std::fmt::Display for VerboseError {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        write!(f, "found ")?;
89        match &self.found {
90            Some(tok) => tok.fmt(f)?,
91            None => write!(f, "end of input")?,
92        };
93        write!(f, ", expected ")?;
94        let mut dedup_expected: Vec<Expectation> = vec![];
95        for expected in self.expected.iter() {
96            if !dedup_expected.contains(expected) {
97                dedup_expected.push(expected.clone());
98            }
99        }
100        match &dedup_expected[..] {
101            [] => write!(f, "something else"),
102            [expected] => expected.fmt(f),
103            _ => {
104                for expected in &dedup_expected[..dedup_expected.len() - 1] {
105                    expected.fmt(f)?;
106                    write!(f, ", ")?;
107                }
108                write!(f, "or ")?;
109                dedup_expected.last().unwrap().fmt(f)
110            }
111        }
112    }
113}
114
115// -----------------------------------------------------------------------
116// Preprocessor
117// -----------------------------------------------------------------------
118
119/// A wrapper around [`scarf_parser::PreprocessorError`]
120///
121/// See its documentation for details on each variant
122#[pyclass(eq, from_py_object, module = "scarf_python")]
123#[derive(Clone, PartialEq, Eq)]
124pub enum PreprocessorError {
125    Endif {
126        /// The [`Span`] of the `` `endif ``
127        endif_span: Span,
128    },
129    NoEndif {
130        /// The conditional token (either [`Token::DirIfdef`], [`Token::DirIfndef`],
131        /// [`Token::DirElsif`], or [`Token::DirElse`]) with no matching `` `endif ``
132        cond_token: Token,
133        /// The [`Span`] of the conditional token
134        cond_token_span: Span,
135    },
136    Elsif {
137        /// The [`Span`] of the `` `elsif ``
138        elsif_span: Span,
139    },
140    Else {
141        /// The [`Span`] of the `` `else ``
142        else_span: Span,
143    },
144    EndKeywords {
145        /// The [`Span`] of the `` `end_keywords ``
146        end_keywords_span: Span,
147    },
148    NoEndKeywords {
149        /// The [`Span`] of the unterminated `` `begin_keywords ``
150        begin_keywords_span: Span,
151    },
152    InvalidDefineParameter {
153        /// The [`Token`] found instead of the `` `define `` parameter
154        other_token: Token,
155        /// The [`Span`] of the token found instead
156        other_span: Span,
157    },
158    InvalidDefineArgument {
159        /// The [`Token`] found instead of the valid `` `define `` argument
160        other_token: Token,
161        /// The [`Span`] of the token found instead
162        other_span: Span,
163    },
164    InvalidVersionSpecifier {
165        /// The [`Token`] provided instead of a valid version specifier
166        ///
167        /// If the token is a [`Token::StringLiteral`], the string isn't a version recognized
168        /// by 1800-2023
169        invalid_version: Token,
170        /// The [`Span`] of the invalid version specifier
171        invalid_version_span: Span,
172    },
173    IncompleteDirective {
174        /// The [`Span`] of the incomplete preprocessor directive
175        ///
176        /// This is usually the primary directive, but can be other more indicative
177        /// tokens as well, such as an unmatched opening parenthesis
178        directive_span: Span,
179    },
180    IncompleteDefine {
181        /// If known, the [`Token`] found instead of a valid function macro argument specification
182        ///
183        /// In the case that the token wasn't tracked, the opening [`Token::Paren`] is referenced
184        /// instead
185        other_token: Token,
186        /// The [`Span`] of the token found instead
187        other_span: Span,
188    },
189    UndefinedMacro {
190        /// The name of the undefined macro
191        undefined_name: String,
192        /// The [`Span`] of the undefined macro
193        undefined_span: Span,
194    },
195    DuplicateMacroParameter {
196        /// The name of the macro for which duplicate parameters were specified
197        define_name: String,
198        /// The name of the parameter that was specified multiple times
199        param_name: String,
200        /// The [`Span`] of the duplicate specification
201        dup_span: Span,
202        /// The [`Span`] of the previous/original specification
203        prev_span: Span,
204    },
205    NoDefaultAfterDefault {
206        /// The name of the previously-specified default parameter
207        default_param: String,
208        /// The [`Span`] of the previously-specified default parameter
209        default_param_span: Span,
210        /// The name of the non-default parameter
211        non_default_param: String,
212        /// The [`Span`] of the non-default parameter
213        non_default_param_span: Span,
214    },
215    NoMacroArguments {
216        /// The name of the macro
217        macro_name: String,
218        /// The [`Span`] of the macro definition (with arguments)
219        define_span: Span,
220        /// The [`Span`] where the macro was used with no arguments
221        use_span: Span,
222    },
223    TooManyMacroArguments {
224        /// The name of the macro
225        macro_name: String,
226        /// The [`Span`] of the macro definition
227        define_span: Span,
228        /// The [`Span`] where the macro was used with too many arguments
229        use_span: Span,
230        /// How many arguments were expected
231        expected: usize,
232        /// How many arguments were found
233        found: usize,
234    },
235    MissingMacroArgument {
236        /// The [`Span`] of the macro definition
237        define_span: Span,
238        /// The [`Span`] where the macro was used with a missing argument
239        use_span: Span,
240        /// The name of the missing parameter
241        param_name: String,
242    },
243    InvalidIdentifierFormation {
244        /// The name of the parameter used in a preprocessor identifier
245        param_name: String,
246        /// The [`Span`] of the invalid argument
247        arg_span: Span,
248    },
249    InvalidRelativeTimescales {
250        /// The [`Span`] of the `` `timescale `` directive
251        timescale_span: Span,
252    },
253    IncompleteMacroWithToken {
254        /// The error-causing [`Token`] (either [`Token::EParen`],
255        /// [`Token::EBracket`], or [`Token::EBrace`])
256        error_token: Token,
257        /// The error-causing [`Span`]
258        error_span: Span,
259    },
260    Include {
261        /// The path for the `` `include `` directive
262        include_path: String,
263        /// The [`Span`] of the include path
264        include_path_span: Span,
265        /// The [`std::io::Error`] raised when attempting to read the file
266        read_err: String,
267    },
268    IncludeDepth {
269        /// The [`Span`] of the `` `include `` directive that exceeded the limit
270        include_span: Span,
271    },
272    VerboseError {
273        /// The [`VerboseError`] for the preprocessor error
274        err: VerboseError,
275    },
276    NotPreviouslyDefinedMacro {
277        /// The name that wasn't previously defined
278        macro_name: String,
279        /// The [`Span`] where the not-previously-defined name was specified
280        macro_span: Span,
281    },
282    RedefinedMacro {
283        /// The name of the macro being redefined
284        macro_name: String,
285        /// The [`Span`] of the redefinition
286        redef_span: Span,
287        /// The [`Span`] where the macro was previously defined
288        prev_def_span: Span,
289    },
290}
291
292impl<'a> From<scarf_parser::PreprocessorError<'a>> for PreprocessorError {
293    fn from(value: scarf_parser::PreprocessorError<'a>) -> Self {
294        match value {
295            scarf_parser::PreprocessorError::Endif { endif_span } => {
296                PreprocessorError::Endif {
297                    endif_span: endif_span.into(),
298                }
299            }
300            scarf_parser::PreprocessorError::NoEndif {
301                cond_token,
302                cond_token_span,
303            } => PreprocessorError::NoEndif {
304                cond_token: cond_token.into(),
305                cond_token_span: cond_token_span.into(),
306            },
307            scarf_parser::PreprocessorError::Elsif { elsif_span } => {
308                PreprocessorError::Elsif {
309                    elsif_span: elsif_span.into(),
310                }
311            }
312            scarf_parser::PreprocessorError::Else { else_span } => {
313                PreprocessorError::Else {
314                    else_span: else_span.into(),
315                }
316            }
317            scarf_parser::PreprocessorError::EndKeywords {
318                end_keywords_span,
319            } => PreprocessorError::EndKeywords {
320                end_keywords_span: end_keywords_span.into(),
321            },
322            scarf_parser::PreprocessorError::NoEndKeywords {
323                begin_keywords_span,
324            } => PreprocessorError::NoEndKeywords {
325                begin_keywords_span: begin_keywords_span.into(),
326            },
327            scarf_parser::PreprocessorError::InvalidDefineParameter {
328                other_token,
329                other_span,
330            } => PreprocessorError::InvalidDefineParameter {
331                other_token: other_token.into(),
332                other_span: other_span.into(),
333            },
334            scarf_parser::PreprocessorError::InvalidDefineArgument {
335                other_token,
336                other_span,
337            } => PreprocessorError::InvalidDefineArgument {
338                other_token: other_token.into(),
339                other_span: other_span.into(),
340            },
341            scarf_parser::PreprocessorError::InvalidVersionSpecifier {
342                invalid_version,
343                invalid_version_span,
344            } => PreprocessorError::InvalidVersionSpecifier {
345                invalid_version: invalid_version.into(),
346                invalid_version_span: invalid_version_span.into(),
347            },
348            scarf_parser::PreprocessorError::IncompleteDirective {
349                directive_span,
350            } => PreprocessorError::IncompleteDirective {
351                directive_span: directive_span.into(),
352            },
353            scarf_parser::PreprocessorError::IncompleteDefine {
354                other_token,
355                other_span,
356            } => PreprocessorError::IncompleteDefine {
357                other_token: other_token.into(),
358                other_span: other_span.into(),
359            },
360            scarf_parser::PreprocessorError::UndefinedMacro {
361                undefined_name,
362                undefined_span,
363            } => PreprocessorError::UndefinedMacro {
364                undefined_name: undefined_name.to_string(),
365                undefined_span: undefined_span.into(),
366            },
367            scarf_parser::PreprocessorError::DuplicateMacroParameter {
368                define_name,
369                param_name,
370                dup_span,
371                prev_span,
372            } => PreprocessorError::DuplicateMacroParameter {
373                define_name: define_name.to_string(),
374                param_name: param_name.to_string(),
375                dup_span: dup_span.into(),
376                prev_span: prev_span.into(),
377            },
378            scarf_parser::PreprocessorError::NoDefaultAfterDefault {
379                default_param,
380                default_param_span,
381                non_default_param,
382                non_default_param_span,
383            } => PreprocessorError::NoDefaultAfterDefault {
384                default_param: default_param.to_string(),
385                default_param_span: default_param_span.into(),
386                non_default_param: non_default_param.to_string(),
387                non_default_param_span: non_default_param_span.into(),
388            },
389            scarf_parser::PreprocessorError::NoMacroArguments {
390                macro_name,
391                define_span,
392                use_span,
393            } => PreprocessorError::NoMacroArguments {
394                macro_name: macro_name.to_string(),
395                define_span: define_span.into(),
396                use_span: use_span.into(),
397            },
398            scarf_parser::PreprocessorError::TooManyMacroArguments {
399                macro_name,
400                define_span,
401                use_span,
402                expected,
403                found,
404            } => PreprocessorError::TooManyMacroArguments {
405                macro_name: macro_name.to_string(),
406                define_span: define_span.into(),
407                use_span: use_span.into(),
408                expected,
409                found,
410            },
411            scarf_parser::PreprocessorError::MissingMacroArgument {
412                define_span,
413                use_span,
414                param_name,
415            } => PreprocessorError::MissingMacroArgument {
416                define_span: define_span.into(),
417                use_span: use_span.into(),
418                param_name: param_name.to_string(),
419            },
420            scarf_parser::PreprocessorError::InvalidIdentifierFormation {
421                param_name,
422                arg_span,
423            } => PreprocessorError::InvalidIdentifierFormation {
424                param_name: param_name.to_string(),
425                arg_span: arg_span.into(),
426            },
427            scarf_parser::PreprocessorError::InvalidRelativeTimescales {
428                timescale_span,
429            } => PreprocessorError::InvalidRelativeTimescales {
430                timescale_span: timescale_span.into(),
431            },
432            scarf_parser::PreprocessorError::IncompleteMacroWithToken {
433                error_token,
434                error_span,
435            } => PreprocessorError::IncompleteMacroWithToken {
436                error_token: error_token.into(),
437                error_span: error_span.into(),
438            },
439            scarf_parser::PreprocessorError::Include {
440                include_path,
441                include_path_span,
442                read_err,
443            } => PreprocessorError::Include {
444                include_path: include_path.to_string(),
445                include_path_span: include_path_span.into(),
446                read_err: read_err.to_string(),
447            },
448            scarf_parser::PreprocessorError::IncludeDepth { include_span } => {
449                PreprocessorError::IncludeDepth {
450                    include_span: include_span.into(),
451                }
452            }
453            scarf_parser::PreprocessorError::VerboseError { err } => {
454                PreprocessorError::VerboseError { err: err.into() }
455            }
456            scarf_parser::PreprocessorError::RedefinedMacro {
457                macro_name,
458                redef_span,
459                prev_def_span,
460            } => PreprocessorError::RedefinedMacro {
461                macro_name: macro_name.to_string(),
462                redef_span: redef_span.into(),
463                prev_def_span: prev_def_span.into(),
464            },
465            scarf_parser::PreprocessorError::NotPreviouslyDefinedMacro {
466                macro_name,
467                macro_span,
468            } => PreprocessorError::NotPreviouslyDefinedMacro {
469                macro_name: macro_name.to_string(),
470                macro_span: macro_span.into(),
471            },
472            scarf_parser::PreprocessorError::EndOfFunctionArgument(_)
473            | scarf_parser::PreprocessorError::NewlineInDefine(_) => panic!(
474                "Unexpected internal error encountered during preprocessing"
475            ),
476        }
477    }
478}