Skip to main content

shannon_brush_parser/
error.rs

1use crate::tokenizer;
2
3/// Represents an error that occurred while parsing tokens.
4#[derive(thiserror::Error, Debug)]
5pub enum ParseError {
6    /// A parsing error occurred near the given position.
7    #[error("syntax error at line {} col {}", .0.line, .0.column)]
8    ParsingNear(crate::SourcePosition),
9
10    /// A parsing error occurred at the end of the input.
11    #[error("syntax error at end of input")]
12    ParsingAtEndOfInput,
13
14    /// An error occurred while tokenizing the input stream.
15    #[error("{} (detected near {})", .inner, .position.as_ref().map_or_else(|| String::from("<unknown position>"), |p| std::format!("line {} col {}", p.line, p.column)))]
16    Tokenizing {
17        /// The inner error.
18        inner: tokenizer::TokenizerError,
19        /// Optionally provides the position of the error.
20        position: Option<crate::SourcePosition>,
21    },
22}
23
24#[cfg(feature = "diagnostics")]
25#[allow(clippy::cast_sign_loss)]
26#[allow(unused)] // Workaround unused warnings in nightly versions of the compiler
27pub mod miette {
28    use super::ParseError;
29    use miette::SourceOffset;
30
31    impl ParseError {
32        /// Convert the original error to one miette can pretty print
33        pub fn to_pretty_error(self, input: impl Into<String>) -> PrettyError {
34            let input = input.into();
35            let location = match self {
36                Self::ParsingNear(ref pos) => {
37                    Some(SourceOffset::from_location(&input, pos.line, pos.column))
38                }
39                Self::Tokenizing { ref position, .. } => position
40                    .as_ref()
41                    .map(|p| SourceOffset::from_location(&input, p.line, p.column)),
42                Self::ParsingAtEndOfInput => {
43                    Some(SourceOffset::from_location(&input, usize::MAX, usize::MAX))
44                }
45            };
46
47            PrettyError {
48                cause: self,
49                input,
50                location,
51            }
52        }
53    }
54
55    /// Represents an error that occurred while parsing tokens.
56    #[derive(thiserror::Error, Debug, miette::Diagnostic)]
57    #[error("Cannot parse the input script")]
58    pub struct PrettyError {
59        cause: ParseError,
60        #[source_code]
61        input: String,
62        #[label("{cause}")]
63        location: Option<SourceOffset>,
64    }
65}
66
67/// Represents a parsing error with its location information
68#[derive(Debug, thiserror::Error)]
69#[error(transparent)]
70pub struct ParseErrorLocation {
71    #[from]
72    inner: peg::error::ParseError<peg::str::LineCol>,
73}
74
75/// Represents an error that occurred while parsing a word.
76#[derive(Debug, thiserror::Error)]
77pub enum WordParseError {
78    /// An error occurred while parsing an arithmetic expression.
79    #[error("failed to parse arithmetic expression")]
80    ArithmeticExpression(ParseErrorLocation),
81
82    /// An error occurred while parsing a shell pattern.
83    #[error("failed to parse pattern")]
84    Pattern(ParseErrorLocation),
85
86    /// An error occurred while parsing a prompt string.
87    #[error("failed to parse prompt string")]
88    Prompt(ParseErrorLocation),
89
90    /// An error occurred while parsing a parameter.
91    #[error("failed to parse parameter '{0}'")]
92    Parameter(String, ParseErrorLocation),
93
94    /// An error occurred while parsing for brace expansion.
95    #[error("failed to parse for brace expansion: '{0}'")]
96    BraceExpansion(String, ParseErrorLocation),
97
98    /// An error occurred while parsing a word.
99    #[error("failed to parse word '{0}'")]
100    Word(String, ParseErrorLocation),
101}
102
103/// Represents an error that occurred while parsing a (non-extended) test command.
104#[derive(Debug, thiserror::Error)]
105#[error(transparent)]
106pub struct TestCommandParseError(#[from] peg::error::ParseError<usize>);
107
108/// Represents an error that occurred while parsing a key-binding specification.
109#[derive(Debug, thiserror::Error)]
110pub enum BindingParseError {
111    /// An unknown error occurred while parsing a key-binding specification.
112    #[error("unknown error while parsing key-binding: '{0}'")]
113    Unknown(String),
114
115    /// A key code was missing from the key-binding specification.
116    #[error("missing key code in key-binding")]
117    MissingKeyCode,
118}
119
120pub(crate) fn convert_peg_parse_error(
121    err: &peg::error::ParseError<usize>,
122    tokens: &[crate::Token],
123) -> ParseError {
124    let approx_token_index = err.location;
125
126    if approx_token_index < tokens.len() {
127        let token = &tokens[approx_token_index];
128        ParseError::ParsingNear((*token.location().start).clone())
129    } else {
130        ParseError::ParsingAtEndOfInput
131    }
132}