boreal_parser/
error.rs

1//! Parsing error types.
2use std::num::ParseIntError;
3use std::ops::Range;
4
5use codespan_reporting::diagnostic::{Diagnostic, Label};
6use nom::error::{ErrorKind as NomErrorKind, ParseError};
7
8use super::types::Input;
9
10/// Parsing error.
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct Error {
13    /// Span of the error in the input.
14    ///
15    /// This is a range of offset, in chars, from the beginning
16    /// of the input given to [`crate::parse`].
17    span: Range<usize>,
18
19    /// Kind of the error.
20    kind: ErrorKind,
21}
22
23impl Error {
24    #[must_use]
25    pub(crate) fn new(span: Range<usize>, kind: ErrorKind) -> Self {
26        Self { span, kind }
27    }
28
29    /// Convert to a [`Diagnostic`].
30    ///
31    /// This can be used to display the error in a user-friendly manner.
32    #[must_use]
33    pub fn to_diagnostic(&self) -> Diagnostic<()> {
34        match &self.kind {
35            ErrorKind::Base64AlphabetInvalidLength { length } => Diagnostic::error()
36                .with_message("base64 modifier alphabet must contain exactly 64 characters")
37                .with_labels(vec![Label::primary((), self.span.clone())
38                    .with_message(format!("this contains {length} characters"))]),
39
40            ErrorKind::Base64AlphabetIncompatible => Diagnostic::error()
41                .with_message("alphabets used for base64 and base64wide must be identical")
42                .with_labels(vec![Label::primary((), self.span.clone())]),
43
44            ErrorKind::CannotNegateMaskAll => Diagnostic::error()
45                .with_message("negating an unknown byte is not allowed")
46                .with_labels(vec![Label::primary((), self.span.clone())]),
47
48            ErrorKind::ExprTooDeep => Diagnostic::error()
49                .with_message("too many imbricated expressions")
50                .with_labels(vec![Label::primary((), self.span.clone())]),
51
52            ErrorKind::RegexClassRangeInvalid => Diagnostic::error()
53                .with_message("invalid regex class range, start must be <= to end")
54                .with_labels(vec![Label::primary((), self.span.clone())]),
55
56            ErrorKind::RegexNonAsciiByte => Diagnostic::error()
57                .with_message("regex should only contain ascii bytes")
58                .with_labels(vec![Label::primary((), self.span.clone())]),
59
60            ErrorKind::RegexRangeInvalid => Diagnostic::error()
61                .with_message("invalid regex range, start must be <= to end")
62                .with_labels(vec![Label::primary((), self.span.clone())]),
63
64            ErrorKind::RegexTooDeep => Diagnostic::error()
65                .with_message("too many imbricated groups in the regex")
66                .with_labels(vec![Label::primary((), self.span.clone())]),
67
68            ErrorKind::HexStringTooDeep => Diagnostic::error()
69                .with_message("too many imbricated groups in the hex string")
70                .with_labels(vec![Label::primary((), self.span.clone())]),
71
72            ErrorKind::JumpAtBound => Diagnostic::error()
73                .with_message("a list of tokens cannot start or end with a jump")
74                .with_labels(vec![Label::primary((), self.span.clone())]),
75
76            ErrorKind::JumpEmpty => Diagnostic::error()
77                .with_message("jump cannot have a length of 0")
78                .with_labels(vec![Label::primary((), self.span.clone())]),
79
80            ErrorKind::JumpRangeInvalid { from, to } => Diagnostic::error()
81                .with_message(format!("invalid range for the jump: {from} > {to}"))
82                .with_labels(vec![Label::primary((), self.span.clone())]),
83
84            ErrorKind::JumpTooBigInAlternation { limit } => Diagnostic::error()
85                .with_message(format!(
86                    "jumps over {limit} not allowed inside alternations (|)",
87                ))
88                .with_labels(vec![Label::primary((), self.span.clone())]),
89
90            ErrorKind::JumpUnboundedInAlternation => Diagnostic::error()
91                .with_message("unbounded jumps not allowed inside alternations (|)")
92                .with_labels(vec![Label::primary((), self.span.clone())]),
93
94            ErrorKind::ModifiersDuplicated { modifier_name } => Diagnostic::error()
95                .with_message(format!(
96                    "string modifier {modifier_name} appears multiple times",
97                ))
98                .with_labels(vec![Label::primary((), self.span.clone())]),
99
100            ErrorKind::ModifiersIncompatible {
101                first_modifier_name,
102                second_modifier_name,
103            } => Diagnostic::error()
104                .with_message(format!(
105                    "string modifiers {first_modifier_name} and {second_modifier_name} are incompatible",
106                ))
107                .with_labels(vec![Label::primary((), self.span.clone())]),
108
109            ErrorKind::MulOverflow { left, right } => Diagnostic::error()
110                .with_message(format!("multiplication {left} * {right} overflows"))
111                .with_labels(vec![Label::primary((), self.span.clone())]),
112
113            ErrorKind::NomError(_) => Diagnostic::error()
114                // TODO: improve nom error reporting.
115                // At least, on tag and char errors, it would be great to indicate
116                // which char and tag was expected.
117                .with_message("syntax error")
118                .with_labels(vec![Label::primary((), self.span.clone())]),
119
120            ErrorKind::StrToIntError(err) => Diagnostic::error()
121                .with_message(format!("error converting to integer: {err}"))
122                .with_labels(vec![Label::primary((), self.span.clone())]),
123
124            ErrorKind::StrToHexIntError(err) => Diagnostic::error()
125                .with_message(format!(
126                    "error converting hexadecimal notation to integer: {err}"
127                ))
128                .with_labels(vec![Label::primary((), self.span.clone())]),
129
130            ErrorKind::StrToOctIntError(err) => Diagnostic::error()
131                .with_message(format!(
132                    "error converting octal notation to integer: {err}"
133                ))
134                .with_labels(vec![Label::primary((), self.span.clone())]),
135
136            ErrorKind::XorRangeInvalidValue { value } => Diagnostic::error()
137                .with_message(format!(
138                    "xor range value {value} invalid, must be in [0-255]"
139                ))
140                .with_labels(vec![Label::primary((), self.span.clone())]),
141
142            ErrorKind::XorRangeInvalid { from, to } => Diagnostic::error()
143                .with_message(format!("xor range invalid: {from} > {to}"))
144                .with_labels(vec![Label::primary((), self.span.clone())]),
145        }
146    }
147
148    fn from_nom_error_kind(position: usize, kind: NomErrorKind) -> Self {
149        Self {
150            span: Range {
151                start: position,
152                end: position + 1,
153            },
154            kind: ErrorKind::NomError(kind),
155        }
156    }
157}
158
159impl ParseError<Input<'_>> for Error {
160    fn from_error_kind(input: Input, kind: NomErrorKind) -> Self {
161        Self::from_nom_error_kind(input.get_position_offset(), kind)
162    }
163
164    fn append(_: Input, _: NomErrorKind, other: Self) -> Self {
165        other
166    }
167}
168
169#[derive(Clone, Debug, PartialEq, Eq)]
170pub(crate) enum ErrorKind {
171    /// A base64 modifier alphabet has an invalid length.
172    ///
173    /// The length must be 64.
174    Base64AlphabetInvalidLength { length: usize },
175
176    /// Alphabets used for base64 and base64wide for the same string are not identical.
177    Base64AlphabetIncompatible,
178
179    /// The '~??' syntax cannot be used in a hex string
180    CannotNegateMaskAll,
181
182    /// An expression contains too many imbricated expressions.
183    ExprTooDeep,
184
185    /// A hex string contains too many imbricated groups.
186    HexStringTooDeep,
187
188    /// A jump is not allowed at the beginning or end of hex tokens
189    JumpAtBound,
190
191    /// Jump of an empty size (i.e. `[0]`).
192    JumpEmpty,
193
194    /// Jump with a invalid range, ie `from` > `to`:
195    JumpRangeInvalid { from: u32, to: u32 },
196
197    /// Jump over a certain size used inside an alternation (`|`).
198    JumpTooBigInAlternation {
199        /// Maximum size of jumps (included).
200        limit: u32,
201    },
202
203    /// Unbounded jump (`[-]`) used inside an alternation (`|`) in an hex string.
204    JumpUnboundedInAlternation,
205
206    /// Duplicated string modifiers
207    ModifiersDuplicated {
208        /// First modifier name
209        modifier_name: String,
210    },
211
212    /// Incompatible string modifiers.
213    ModifiersIncompatible {
214        /// First modifier name
215        first_modifier_name: String,
216        /// Second modifier name
217        second_modifier_name: String,
218    },
219
220    /// Overflow on a multiplication
221    MulOverflow { left: i64, right: i64 },
222
223    /// Generic error on nom parsing utilities
224    NomError(NomErrorKind),
225
226    /// Range used in regex class is invalid, from > to.
227    RegexClassRangeInvalid,
228
229    /// Regex contains a non ascii byte, this is not allowed.
230    RegexNonAsciiByte,
231
232    /// Invalid range in a regex, from > to.
233    RegexRangeInvalid,
234
235    /// Regex has too much depth, meaning there are too many imbricated groups.
236    RegexTooDeep,
237
238    /// Error converting a string to an integer
239    StrToIntError(ParseIntError),
240
241    /// Error converting a string to an integer in base 16
242    StrToHexIntError(ParseIntError),
243
244    /// Error converting a string to an integer in base 16
245    StrToOctIntError(ParseIntError),
246
247    /// A value used in a xor modifier range is outside the [0-255] range.
248    XorRangeInvalidValue { value: i64 },
249
250    /// Xor modifier with a invalid range, ie `from` > `to`:
251    XorRangeInvalid { from: u8, to: u8 },
252}
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257    use crate::test_helpers::test_public_type;
258
259    #[test]
260    fn test_public_types() {
261        test_public_type(Error::new(0..3, ErrorKind::JumpEmpty));
262    }
263}