1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
//! Error types, traits and utilities.
//!
//! *“I like the cover," he said. "Don't Panic. It's the first helpful or intelligible thing anybody's said to me all
//! day.”*
//!
//! You can implement the [`Error`] trait to create your own parser errors, or you can use one provided by the crate
//! like [`Simple`] or [`Cheap`].
use super::*;
use std::{collections::HashSet, hash::Hash};
#[cfg(feature = "ahash")]
type RandomState = ahash::RandomState;
#[cfg(not(feature = "ahash"))]
type RandomState = std::collections::hash_map::RandomState;
/// A trait that describes parser error types.
///
/// If you have a custom error type in your compiler, or your needs are not sufficiently met by [`Simple`], you should
/// implement this trait. If your error type has 'extra' features that allow for more specific error messages, you can
/// use the [`Parser::map_err`] or [`Parser::try_map`] functions to take advantage of these inline within your parser.
///
/// # Examples
///
/// ```
/// # use chumsky::{prelude::*, error::Cheap};
/// type Span = std::ops::Range<usize>;
///
/// // A custom error type
/// #[derive(Debug, PartialEq)]
/// enum MyError {
/// ExpectedFound(Span, Vec<Option<char>>, Option<char>),
/// NotADigit(Span, char),
/// }
///
/// impl chumsky::Error<char> for MyError {
/// type Span = Span;
/// type Label = ();
///
/// fn expected_input_found<Iter: IntoIterator<Item = Option<char>>>(
/// span: Span,
/// expected: Iter,
/// found: Option<char>,
/// ) -> Self {
/// Self::ExpectedFound(span, expected.into_iter().collect(), found)
/// }
///
/// fn with_label(mut self, label: Self::Label) -> Self { self }
///
/// fn merge(mut self, mut other: Self) -> Self {
/// if let (Self::ExpectedFound(_, expected, _), Self::ExpectedFound(_, expected_other, _)) = (
/// &mut self,
/// &mut other,
/// ) {
/// expected.append(expected_other);
/// }
/// self
/// }
/// }
///
/// let numeral = filter_map(|span, c: char| match c.to_digit(10) {
/// Some(x) => Ok(x),
/// None => Err(MyError::NotADigit(span, c)),
/// });
///
/// assert_eq!(numeral.parse("3"), Ok(3));
/// assert_eq!(numeral.parse("7"), Ok(7));
/// assert_eq!(numeral.parse("f"), Err(vec![MyError::NotADigit(0..1, 'f')]));
/// ```
pub trait Error<I>: Sized {
/// The type of spans to be used in the error.
type Span: Span; // TODO: Default to = Range<usize>;
/// The label used to describe a syntatic structure currently being parsed.
///
/// This can be used to generate errors that tell the user what syntactic structure was currently being parsed when
/// the error occured.
type Label; // TODO: Default to = &'static str;
/// Create a new error describing a conflict between expected inputs and that which was actually found.
///
/// `found` having the value `None` indicates that the end of input was reached, but was not expected.
///
/// An expected input having the value `None` indicates that the end of input was expected.
fn expected_input_found<Iter: IntoIterator<Item = Option<I>>>(
span: Self::Span,
expected: Iter,
found: Option<I>,
) -> Self;
/// Create a new error describing a delimiter that was not correctly closed.
///
/// Provided to this function is the span of the unclosed delimiter, the delimiter itself, the span of the input
/// that was found in its place, the closing delimiter that was expected but not found, and the input that was
/// found in its place.
///
/// The default implementation of this function uses [`Error::expected_input_found`], but you'll probably want to
/// implement it yourself to take full advantage of the extra diagnostic information.
fn unclosed_delimiter(
unclosed_span: Self::Span,
unclosed: I,
span: Self::Span,
expected: I,
found: Option<I>,
) -> Self {
#![allow(unused_variables)]
Self::expected_input_found(span, Some(Some(expected)), found)
}
/// Indicate that the error occured while parsing a particular syntactic structure.
///
/// How the error handles this information is up to it. It can append it to a list of structures to get a sort of
/// 'parse backtrace', or it can just keep only the most recent label. If the latter, this method should have no
/// effect when the error already has a label.
fn with_label(self, label: Self::Label) -> Self;
/// Merge two errors that point to the same input together, combining their information.
fn merge(self, other: Self) -> Self;
}
// /// A simple default input pattern that allows describing inputs and input patterns in error messages.
// #[derive(Clone, Debug, PartialEq, Eq, Hash)]
// pub enum SimplePattern<I> {
// /// A pattern with the given name was expected.
// Labelled(&'static str),
// /// A specific input was expected.
// Token(I),
// }
// impl<I> From<&'static str> for SimplePattern<I> {
// fn from(s: &'static str) -> Self { Self::Labelled(s) }
// }
// impl<I: fmt::Display> fmt::Display for SimplePattern<I> {
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// match self {
// Self::Labelled(s) => write!(f, "{}", s),
// Self::Token(x) => write!(f, "'{}'", x),
// }
// }
// }
/// A type representing possible reasons for an error.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SimpleReason<I, S> {
/// An unexpected input was found.
Unexpected,
/// An unclosed delimiter was found.
Unclosed {
/// The span of the unclosed delimiter.
span: S,
/// The unclosed delimiter.
delimiter: I,
},
/// An error with a custom message occurred.
Custom(String),
}
/// A simple default error type that tracks error spans, expected inputs, and the actual input found at an error site.
///
/// Please note that it uses a [`HashSet`] to remember expected symbols. If you find this to be too slow, you can
/// implement [`Error`] for your own error type or use [`Cheap`] instead.
#[derive(Clone, Debug)]
pub struct Simple<I: Hash, S = Range<usize>> {
span: S,
reason: SimpleReason<I, S>,
expected: HashSet<Option<I>, RandomState>,
found: Option<I>,
label: Option<&'static str>,
}
impl<I: Hash + Eq, S: Clone> Simple<I, S> {
/// Create an error with a custom error message.
pub fn custom<M: ToString>(span: S, msg: M) -> Self {
Self {
span,
reason: SimpleReason::Custom(msg.to_string()),
expected: HashSet::default(),
found: None,
label: None,
}
}
/// Returns the span that the error occured at.
pub fn span(&self) -> S {
self.span.clone()
}
/// Returns an iterator over possible expected patterns.
pub fn expected(&self) -> impl ExactSizeIterator<Item = &Option<I>> + '_ {
self.expected.iter()
}
/// Returns the input, if any, that was found instead of an expected pattern.
pub fn found(&self) -> Option<&I> {
self.found.as_ref()
}
/// Returns the reason for the error.
pub fn reason(&self) -> &SimpleReason<I, S> {
&self.reason
}
/// Returns the error's label, if any.
pub fn label(&self) -> Option<&'static str> {
self.label
}
/// Map the error's inputs using the given function.
///
/// This can be used to unify the errors between parsing stages that operate upon two forms of input (for example,
/// the initial lexing stage and the parsing stage in most compilers).
pub fn map<U: Hash + Eq, F: FnMut(I) -> U>(self, mut f: F) -> Simple<U, S> {
Simple {
span: self.span,
reason: match self.reason {
SimpleReason::Unclosed { span, delimiter } => SimpleReason::Unclosed {
span,
delimiter: f(delimiter),
},
SimpleReason::Unexpected => SimpleReason::Unexpected,
SimpleReason::Custom(msg) => SimpleReason::Custom(msg),
},
expected: self.expected.into_iter().map(|e| e.map(&mut f)).collect(),
found: self.found.map(f),
label: self.label,
}
}
}
impl<I: Hash + Eq, S: Span + Clone + fmt::Debug> Error<I> for Simple<I, S> {
type Span = S;
type Label = &'static str;
fn expected_input_found<Iter: IntoIterator<Item = Option<I>>>(
span: Self::Span,
expected: Iter,
found: Option<I>,
) -> Self {
Self {
span,
reason: SimpleReason::Unexpected,
expected: expected.into_iter().collect(),
found,
label: None,
}
}
fn unclosed_delimiter(
unclosed_span: Self::Span,
delimiter: I,
span: Self::Span,
expected: I,
found: Option<I>,
) -> Self {
Self {
span,
reason: SimpleReason::Unclosed {
span: unclosed_span,
delimiter,
},
expected: std::iter::once(Some(expected)).collect(),
found,
label: None,
}
}
fn with_label(mut self, label: Self::Label) -> Self {
self.label.get_or_insert(label);
self
}
fn merge(mut self, other: Self) -> Self {
// TODO: Assert that `self.span == other.span` here?
self.reason = match (&self.reason, &other.reason) {
(SimpleReason::Unclosed { .. }, _) => self.reason,
(_, SimpleReason::Unclosed { .. }) => other.reason,
_ => self.reason,
};
for expected in other.expected {
self.expected.insert(expected);
}
self
}
}
impl<I: Hash + PartialEq, S: PartialEq> PartialEq for Simple<I, S> {
fn eq(&self, other: &Self) -> bool {
self.span == other.span
&& self.found == other.found
&& self.reason == other.reason
&& self.label == other.label
}
}
impl<I: fmt::Display + Hash, S: Span> fmt::Display for Simple<I, S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// TODO: Take `self.reason` into account
if let Some(found) = &self.found {
write!(f, "found '{}'", found)?;
} else {
write!(f, "found end of input")?;
}
match self.expected.len() {
0 => {} //write!(f, " but end of input was expected")?,
1 => write!(
f,
" but {} was expected",
match self.expected.iter().next().unwrap() {
Some(x) => format!("{}", x),
None => format!("end of input"),
},
)?,
_ => write!(
f,
" but one of {} was expected",
self.expected
.iter()
.map(|expected| match expected {
Some(x) => format!("{}", x),
None => format!("end of input"),
})
.collect::<Vec<_>>()
.join(", ")
)?,
}
Ok(())
}
}
impl<I: fmt::Debug + fmt::Display + Hash, S: Span + fmt::Display + fmt::Debug> std::error::Error
for Simple<I, S>
{
}
/// A minimal error type that tracks only the error span and label. This type is most useful when you want fast parsing
/// but do not particularly care about the quality of error messages.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Cheap<I, S = Range<usize>> {
span: S,
label: Option<&'static str>,
phantom: PhantomData<I>,
}
impl<I, S: Clone> Cheap<I, S> {
/// Returns the span that the error occured at.
pub fn span(&self) -> S {
self.span.clone()
}
/// Returns the error's label, if any.
pub fn label(&self) -> Option<&'static str> {
self.label
}
}
impl<I, S: Span + Clone + fmt::Debug> Error<I> for Cheap<I, S> {
type Span = S;
type Label = &'static str;
fn expected_input_found<Iter: IntoIterator<Item = Option<I>>>(
span: Self::Span,
_: Iter,
_: Option<I>,
) -> Self {
Self {
span,
label: None,
phantom: PhantomData,
}
}
fn with_label(mut self, label: Self::Label) -> Self {
self.label.get_or_insert(label);
self
}
fn merge(self, _: Self) -> Self {
self
}
}
/// An internal type used to facilitate error prioritisation. You shouldn't need to interact with this type during
/// normal use of the crate.
pub struct Located<I, E> {
pub(crate) at: usize,
pub(crate) error: E,
pub(crate) phantom: PhantomData<I>,
}
impl<I, E: Error<I>> Located<I, E> {
/// Create a new [`Located`] with the give input position and error.
pub fn at(at: usize, error: E) -> Self {
Self {
at,
error,
phantom: PhantomData,
}
}
/// Get the maximum of two located errors. If they hold the same position in the input, merge them.
pub fn max(self, other: impl Into<Option<Self>>) -> Self {
let other = match other.into() {
Some(other) => other,
None => return self,
};
match self.at.cmp(&other.at) {
Ordering::Greater => self,
Ordering::Less => other,
Ordering::Equal => Self {
error: self.error.merge(other.error),
..self
},
}
}
/// Map the error with the given function.
pub fn map<U, F: FnOnce(E) -> U>(self, f: F) -> Located<I, U> {
Located {
at: self.at,
error: f(self.error),
phantom: PhantomData,
}
}
}
// Merge two alternative errors
pub(crate) fn merge_alts<I, E: Error<I>, T: IntoIterator<Item = Located<I, E>>>(
mut error: Option<Located<I, E>>,
errors: T,
) -> Option<Located<I, E>> {
for other in errors {
match (error, other) {
(Some(a), b) => {
error = Some(b.max(a));
}
(None, b) => {
error = Some(b);
}
}
}
error
}