use super::*;
use alloc::borrow::Cow;
pub use label::LabelError;
pub trait Error<'a, I: Input<'a>>:
Sized + LabelError<'a, I, DefaultExpected<'a, I::Token>>
{
#[inline(always)]
fn merge(self, other: Self) -> Self {
#![allow(unused_variables)]
self
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Default)]
pub struct EmptyErr(());
impl<'a, I: Input<'a>> Error<'a, I> for EmptyErr {}
impl<'a, I: Input<'a>, L> LabelError<'a, I, L> for EmptyErr {
#[inline(always)]
fn expected_found<E: IntoIterator<Item = L>>(
_: E,
_: Option<MaybeRef<'a, I::Token>>,
_: I::Span,
) -> Self {
EmptyErr(())
}
}
impl fmt::Display for EmptyErr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "error")
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Cheap<S = SimpleSpan<usize>> {
span: S,
}
impl<S> Cheap<S> {
pub fn new(span: S) -> Self {
Self { span }
}
pub fn span(&self) -> &S {
&self.span
}
}
impl<'a, I: Input<'a>> Error<'a, I> for Cheap<I::Span> {}
impl<'a, I: Input<'a>, L> LabelError<'a, I, L> for Cheap<I::Span> {
#[inline]
fn expected_found<E: IntoIterator<Item = L>>(
_expected: E,
_found: Option<MaybeRef<'a, I::Token>>,
span: I::Span,
) -> Self {
Self { span }
}
}
impl<S> fmt::Debug for Cheap<S>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "at {:?}", self.span)?;
Ok(())
}
}
impl<S> fmt::Display for Cheap<S>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Simple<'a, T, S = SimpleSpan<usize>> {
span: S,
found: Option<MaybeRef<'a, T>>,
}
impl<T, S> Simple<'_, T, S> {
pub fn span(&self) -> &S {
&self.span
}
pub fn found(&self) -> Option<&T> {
self.found.as_deref()
}
}
impl<'a, T, S> Simple<'a, T, S> {
pub fn new(found: Option<MaybeRef<'a, T>>, span: S) -> Self {
Self { span, found }
}
pub fn map_token<U, F: FnOnce(T) -> U>(self, f: F) -> Simple<'a, U, S>
where
T: Clone,
{
Simple {
span: self.span,
found: self.found.map(|found| f(found.into_inner()).into()),
}
}
pub fn map_span<S2, F: FnOnce(S) -> S2>(self, f: F) -> Simple<'a, T, S2> {
Simple {
span: f(self.span),
found: self.found,
}
}
}
impl<'a, I: Input<'a>> Error<'a, I> for Simple<'a, I::Token, I::Span> {}
impl<'a, I: Input<'a>, L> LabelError<'a, I, L> for Simple<'a, I::Token, I::Span> {
#[inline]
fn expected_found<E: IntoIterator<Item = L>>(
_expected: E,
found: Option<MaybeRef<'a, I::Token>>,
span: I::Span,
) -> Self {
Self { span, found }
}
}
impl<T, S> fmt::Debug for Simple<'_, T, S>
where
T: fmt::Debug,
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "found ")?;
write_token(f, T::fmt, self.found.as_deref())?;
write!(f, " at {:?}", self.span)?;
Ok(())
}
}
impl<T, S> fmt::Display for Simple<'_, T, S>
where
T: fmt::Debug,
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum RichPattern<'a, T> {
Token(MaybeRef<'a, T>),
Label(Cow<'a, str>),
Identifier(String),
Any,
SomethingElse,
EndOfInput,
}
impl<'a, T> TryFrom<DefaultExpected<'a, T>> for RichPattern<'a, T> {
type Error = ();
fn try_from(expected: DefaultExpected<'a, T>) -> Result<Self, ()> {
Ok(match expected {
DefaultExpected::Token(tok) => RichPattern::Token(tok),
DefaultExpected::Any => RichPattern::Any,
DefaultExpected::SomethingElse => RichPattern::SomethingElse,
DefaultExpected::EndOfInput => RichPattern::EndOfInput,
DefaultExpected::NothingElse => return Err(()),
})
}
}
impl<'a, Slice: core::fmt::Debug, T> TryFrom<text::TextExpected<Slice>> for RichPattern<'a, T> {
type Error = ();
fn try_from(expected: text::TextExpected<Slice>) -> Result<Self, ()> {
Ok(match expected {
text::TextExpected::Whitespace => RichPattern::Label(Cow::Borrowed("whitespace")),
text::TextExpected::InlineWhitespace => {
RichPattern::Label(Cow::Borrowed("inline whitespace"))
}
text::TextExpected::Newline => RichPattern::Label(Cow::Borrowed("newline")),
text::TextExpected::Digit(start, _end) if start > 0 => {
RichPattern::Label(Cow::Borrowed("non-zero digit"))
}
text::TextExpected::Digit(_, _) => RichPattern::Label(Cow::Borrowed("digit")),
text::TextExpected::AnyIdentifier => RichPattern::Label(Cow::Borrowed("identifier")),
text::TextExpected::Identifier(i) => RichPattern::Identifier(alloc::format!("{i:?}")),
text::TextExpected::Int => RichPattern::Label(Cow::Borrowed("int")),
})
}
}
impl<'a, T> TryFrom<MaybeRef<'a, T>> for RichPattern<'a, T> {
type Error = ();
fn try_from(tok: MaybeRef<'a, T>) -> Result<Self, ()> {
Ok(RichPattern::Token(tok))
}
}
impl<T> TryFrom<&'static str> for RichPattern<'_, T> {
type Error = ();
fn try_from(label: &'static str) -> Result<Self, ()> {
Ok(RichPattern::Label(Cow::Borrowed(label)))
}
}
impl<T> TryFrom<String> for RichPattern<'_, T> {
type Error = ();
fn try_from(label: String) -> Result<Self, ()> {
Ok(RichPattern::Label(Cow::Owned(label)))
}
}
impl TryFrom<char> for RichPattern<'_, char> {
type Error = ();
fn try_from(c: char) -> Result<Self, ()> {
Ok(Self::Token(MaybeRef::Val(c)))
}
}
impl<'a, T> RichPattern<'a, T> {
pub fn map_token<U, F: FnMut(T) -> U>(self, mut f: F) -> RichPattern<'a, U>
where
T: Clone,
{
match self {
Self::Token(t) => RichPattern::Token(f(t.into_inner()).into()),
Self::Label(l) => RichPattern::Label(l),
Self::Identifier(i) => RichPattern::Identifier(i),
Self::Any => RichPattern::Any,
Self::SomethingElse => RichPattern::SomethingElse,
Self::EndOfInput => RichPattern::EndOfInput,
}
}
pub fn into_owned<'b>(self) -> RichPattern<'b, T>
where
T: Clone,
{
match self {
Self::Token(tok) => RichPattern::Token(tok.into_owned()),
Self::Label(l) => RichPattern::Label(Cow::Owned(l.into_owned())),
Self::Identifier(i) => RichPattern::Identifier(i),
Self::Any => RichPattern::Any,
Self::SomethingElse => RichPattern::SomethingElse,
Self::EndOfInput => RichPattern::EndOfInput,
}
}
fn write(
&self,
f: &mut fmt::Formatter,
mut fmt_token: impl FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result,
) -> fmt::Result {
match self {
Self::Token(tok) => {
write!(f, "'")?;
fmt_token(tok, f)?;
write!(f, "'")
}
Self::Label(l) => write!(f, "{l}"),
Self::Identifier(i) => write!(f, "'{i}'"),
Self::Any => write!(f, "any"),
Self::SomethingElse => write!(f, "something else"),
Self::EndOfInput => write!(f, "end of input"),
}
}
}
impl<T> fmt::Debug for RichPattern<'_, T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.write(f, |t, f| write!(f, "{t:?}"))
}
}
impl<T> fmt::Display for RichPattern<'_, T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write(f, |t, f| write!(f, "{t}"))
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum RichReason<'a, T, C = String> {
ExpectedFound {
expected: Vec<RichPattern<'a, T>>,
found: Option<MaybeRef<'a, T>>,
},
Custom(C),
}
impl<'a, T, C> RichReason<'a, T, C> {
pub fn found(&self) -> Option<&T> {
match self {
Self::ExpectedFound { found, .. } => found.as_deref(),
Self::Custom(_) => None,
}
}
pub fn into_owned<'b>(self) -> RichReason<'b, T, C>
where
T: Clone,
{
match self {
Self::ExpectedFound { found, expected } => RichReason::ExpectedFound {
expected: expected.into_iter().map(RichPattern::into_owned).collect(),
found: found.map(MaybeRef::into_owned),
},
Self::Custom(msg) => RichReason::Custom(msg),
}
}
fn take_found(&mut self) -> Option<MaybeRef<'a, T>> {
match self {
RichReason::ExpectedFound { found, .. } => found.take(),
RichReason::Custom(_) => None,
}
}
pub fn map_token<U, F: FnMut(T) -> U>(self, mut f: F) -> RichReason<'a, U, C>
where
T: Clone,
{
match self {
RichReason::ExpectedFound { expected, found } => RichReason::ExpectedFound {
expected: expected
.into_iter()
.map(|pat| pat.map_token(&mut f))
.collect(),
found: found.map(|found| f(found.into_inner()).into()),
},
RichReason::Custom(msg) => RichReason::Custom(msg),
}
}
}
impl<'a, T, C> RichReason<'a, T, C>
where
C: fmt::Display,
{
fn inner_fmt<S>(
&self,
f: &mut fmt::Formatter<'_>,
mut fmt_token: impl FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result,
mut fmt_span: impl FnMut(&S, &mut fmt::Formatter<'_>) -> fmt::Result,
span: Option<&S>,
context: &[(RichPattern<'a, T>, S)],
) -> fmt::Result {
match self {
RichReason::ExpectedFound { expected, found } => {
write!(f, "found ")?;
write_token(f, &mut fmt_token, found.as_deref())?;
if let Some(span) = span {
write!(f, " at ")?;
fmt_span(span, f)?;
}
write!(f, " expected ")?;
match &expected[..] {
[] => write!(f, "something else")?,
[expected] => expected.write(f, &mut fmt_token)?,
_ => {
for expected in &expected[..expected.len() - 1] {
expected.write(f, &mut fmt_token)?;
write!(f, ", ")?;
}
write!(f, "or ")?;
expected.last().unwrap().write(f, &mut fmt_token)?;
}
}
}
RichReason::Custom(msg) => {
write!(f, "{msg}")?;
if let Some(span) = span {
write!(f, " at ")?;
fmt_span(span, f)?;
}
}
}
for (l, s) in context {
write!(f, " in ")?;
l.write(f, &mut fmt_token)?;
write!(f, " at ")?;
fmt_span(s, f)?;
}
Ok(())
}
}
impl<T, C> RichReason<'_, T, C>
where
T: PartialEq,
{
#[inline]
fn flat_merge(self, other: Self) -> Self {
match (self, other) {
(a @ RichReason::Custom(_), _) => a,
(_, b @ RichReason::Custom(_)) => b,
(
RichReason::ExpectedFound {
expected: mut this_expected,
found,
},
RichReason::ExpectedFound {
expected: mut other_expected,
..
},
) => {
if other_expected.len() > this_expected.len() {
core::mem::swap(&mut this_expected, &mut other_expected);
}
for expected in other_expected {
if !this_expected[..].contains(&expected) {
this_expected.push(expected);
}
}
RichReason::ExpectedFound {
expected: this_expected,
found,
}
}
}
}
}
impl<T, C> fmt::Display for RichReason<'_, T, C>
where
T: fmt::Display,
C: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner_fmt(f, T::fmt, |_: &(), _| Ok(()), None, &[])
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Rich<'a, T, S = SimpleSpan<usize>, C = String> {
span: S,
reason: Box<RichReason<'a, T, C>>,
context: Vec<(RichPattern<'a, T>, S)>,
}
impl<T, S, C> Rich<'_, T, S, C>
where
C: fmt::Display,
{
fn inner_fmt(
&self,
f: &mut fmt::Formatter<'_>,
fmt_token: impl FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result,
fmt_span: impl FnMut(&S, &mut fmt::Formatter<'_>) -> fmt::Result,
with_spans: bool,
) -> fmt::Result {
self.reason.inner_fmt(
f,
fmt_token,
fmt_span,
if with_spans { Some(&self.span) } else { None },
&self.context,
)
}
}
impl<'a, T, S, C> Rich<'a, T, S, C> {
#[inline]
pub fn custom<M: Into<C>>(span: S, msg: M) -> Self {
Rich {
span,
reason: Box::new(RichReason::Custom(msg.into())),
context: Vec::new(),
}
}
pub fn span(&self) -> &S {
&self.span
}
pub fn reason(&self) -> &RichReason<'a, T, C> {
&self.reason
}
pub fn into_reason(self) -> RichReason<'a, T, C> {
*self.reason
}
pub fn found(&self) -> Option<&T> {
self.reason.found()
}
pub fn contexts(&self) -> impl Iterator<Item = (&RichPattern<'a, T>, &S)> {
self.context.iter().map(|(l, s)| (l, s))
}
pub fn into_owned<'b>(self) -> Rich<'b, T, S, C>
where
T: Clone,
{
Rich {
reason: Box::new(self.reason.into_owned()),
context: self
.context
.into_iter()
.map(|(p, s)| (p.into_owned(), s))
.collect(),
..self
}
}
pub fn expected(&self) -> impl ExactSizeIterator<Item = &RichPattern<'a, T>> {
match &*self.reason {
RichReason::ExpectedFound { expected, .. } => expected.iter(),
RichReason::Custom(_) => [].iter(),
}
}
pub fn map_token<U, F: FnMut(T) -> U>(self, mut f: F) -> Rich<'a, U, S, C>
where
T: Clone,
{
Rich {
span: self.span,
reason: Box::new(self.reason.map_token(&mut f)),
context: self
.context
.into_iter()
.map(|(p, s)| (p.map_token(&mut f), s))
.collect(),
}
}
pub fn map_span<S2, F: FnMut(S) -> S2>(self, mut f: F) -> Rich<'a, T, S2, C> {
Rich {
span: f(self.span),
reason: self.reason,
context: self.context.into_iter().map(|(p, s)| (p, f(s))).collect(),
}
}
}
impl<'a, I: Input<'a>, C> Error<'a, I> for Rich<'a, I::Token, I::Span, C>
where
I::Token: PartialEq,
{
#[inline]
fn merge(self, other: Self) -> Self {
let new_reason = self.reason.flat_merge(*other.reason);
Self {
span: self.span,
reason: Box::new(new_reason),
context: self.context, }
}
}
impl<'a, I: Input<'a>, L, C> LabelError<'a, I, L> for Rich<'a, I::Token, I::Span, C>
where
I::Token: PartialEq,
L: TryInto<RichPattern<'a, I::Token>>,
{
#[inline]
fn expected_found<E: IntoIterator<Item = L>>(
expected: E,
found: Option<MaybeRef<'a, I::Token>>,
span: I::Span,
) -> Self {
Self {
span,
reason: Box::new(RichReason::ExpectedFound {
expected: expected
.into_iter()
.filter_map(|tok| tok.try_into().ok())
.collect(),
found,
}),
context: Vec::new(),
}
}
#[inline]
fn merge_expected_found<E: IntoIterator<Item = L>>(
mut self,
new_expected: E,
new_found: Option<MaybeRef<'a, I::Token>>,
_span: I::Span,
) -> Self {
match &mut *self.reason {
RichReason::ExpectedFound { expected, found } => {
for new_expected in new_expected {
if let Ok(new_expected) = new_expected.try_into() {
if !expected[..].contains(&new_expected) {
expected.push(new_expected);
}
}
}
*found = found.take().or(new_found); }
RichReason::Custom(_) => {}
}
self
}
#[inline]
fn replace_expected_found<E: IntoIterator<Item = L>>(
mut self,
new_expected: E,
new_found: Option<MaybeRef<'a, I::Token>>,
span: I::Span,
) -> Self {
self.span = span;
match &mut *self.reason {
RichReason::ExpectedFound { expected, found } => {
expected.clear();
expected.extend(
new_expected
.into_iter()
.filter_map(|tok| tok.try_into().ok()),
);
*found = new_found;
}
_ => {
*self.reason = RichReason::ExpectedFound {
expected: new_expected
.into_iter()
.filter_map(|tok| tok.try_into().ok())
.collect(),
found: new_found,
};
}
}
self.context.clear();
self
}
#[inline]
fn label_with(&mut self, label: L) {
match &mut *self.reason {
RichReason::ExpectedFound { expected, found: _ } => {
expected.clear();
expected.extend(label.try_into().ok());
}
_ => {
*self.reason = RichReason::ExpectedFound {
expected: label.try_into().ok().into_iter().collect(),
found: self.reason.take_found(),
};
}
}
}
#[inline]
fn in_context(&mut self, label: L, span: I::Span) {
if let Ok(label) = label.try_into() {
if self.context.iter().all(|(l, _)| l != &label) {
self.context.push((label, span));
}
}
}
}
impl<T, S, C> fmt::Debug for Rich<'_, T, S, C>
where
T: fmt::Debug,
S: fmt::Debug,
C: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.inner_fmt(f, T::fmt, S::fmt, true)
}
}
impl<T, S, C> fmt::Display for Rich<'_, T, S, C>
where
T: fmt::Display,
S: fmt::Display,
C: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner_fmt(f, T::fmt, S::fmt, false)
}
}
fn write_token<T>(
f: &mut fmt::Formatter,
mut fmt_token: impl FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result,
tok: Option<&T>,
) -> fmt::Result {
match tok {
Some(tok) => {
write!(f, "'")?;
fmt_token(tok, f)?;
write!(f, "'")
}
None => write!(f, "end of input"),
}
}