Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 193 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,22 @@ impl<I, C, E: AddContext<I, C>> AddContext<I, C> for ErrMode<E> {
fn add_context(self, input: &I, ctx: C) -> Self {
self.map(|err| err.add_context(input, ctx))
}

#[inline(always)]
fn merge_context(self, other: Self) -> Self {
match other.into_inner() {
Some(other) => self.map(|err| err.merge_context(other)),
None => self,
}
}

#[inline]
fn clear_context(&mut self) {
match self {
ErrMode::Incomplete(_) => {}
ErrMode::Cut(t) | ErrMode::Backtrack(t) => t.clear_context(),
}
}
}

impl<T: Clone> ErrMode<InputError<T>> {
Expand Down Expand Up @@ -311,6 +327,16 @@ pub trait AddContext<I, C = &'static str>: Sized {
fn add_context(self, _input: &I, _ctx: C) -> Self {
self
}

/// Apply the context from `other` into `self`
#[inline]
fn merge_context(self, _other: Self) -> Self {
self
}

/// Remove all context
#[inline]
fn clear_context(&mut self) {}
}

/// Create a new error with an external error, from [`std::str::FromStr`]
Expand Down Expand Up @@ -534,6 +560,19 @@ impl<C, I> AddContext<I, C> for ContextError<C> {
self.context.push(ctx);
self
}

#[inline]
fn merge_context(mut self, other: Self) -> Self {
#[cfg(feature = "alloc")]
self.context.extend(other.context);
self
}

#[inline]
fn clear_context(&mut self) {
#[cfg(feature = "alloc")]
self.context.clear();
}
}

#[cfg(feature = "std")]
Expand Down Expand Up @@ -695,6 +734,160 @@ impl crate::lib::std::fmt::Display for StrContextValue {
}
}

#[derive(Clone, Debug)]
pub struct LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
{
checkpoint: I::Checkpoint,
inner: E,
}

impl<I, E> LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
{
#[inline]
pub fn into_inner(self) -> E {
self.inner
}
}

impl<I, E> ParserError<I> for LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
E: ParserError<I>,
{
#[inline]
fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
Self {
checkpoint: input.checkpoint(),
inner: E::from_error_kind(input, kind),
}
}

#[inline]
fn append(mut self, input: &I, kind: ErrorKind) -> Self {
self.inner.append(input, kind)
}

#[inline]
fn or(self, other: Self) -> Self {
other
}
}

impl<I, E, C> AddContext<I, C> for LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
E: AddContext<I, C>,
{
#[inline]
fn add_context(mut self, input: &I, ctx: C) -> Self {
let checkpoint = input.checkpoint();
match checkpoint.cmp(self.checkpoint) {
core::cmp::Ordering::Less => {}
core::cmp::Ordering::Greater => {
self.checkpoint = checkpoint;
self.inner.clear_context();
self.inner = self.inner.add_context(input, ctx);
}
core::cmp::Ordering::Equal => {
self.inner = self.inner.add_context(input, ctx);
}
}
}

#[inline]
fn merge_context(mut self, other: Self) -> Self {
match other.checkpoint.cmp(self.checkpoint) {
core::cmp::Ordering::Less => self,
core::cmp::Ordering::Greater => other,
core::cmp::Ordering::Equal => {
self.inner.clear_context();
self.inner = self.inner.merge_context(other.inner);
}
}
}
}

impl<I, E, EX> FromExternalError<I, EX> for LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
E: FromExternalError<I, EX>,
{
#[inline]
fn from_external_error(input: &I, kind: ErrorKind, e: EX) -> Self {
Self {
checkpoint: input.checkpoint(),
inner: E::from_external_error(input, kind, e),
}
}
}

impl<I> crate::lib::std::fmt::Display for LongestMatch<I, ContextError<StrContext>>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
{
fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
#[cfg(feature = "alloc")]
{
let expression = self.inner.context().find_map(|c| match c {
StrContext::Label(c) => Some(c),
_ => None,
});
let expected = self
.inner
.context()
.filter_map(|c| match c {
StrContext::Expected(c) => Some(c),
_ => None,
})
.collect::<crate::lib::std::vec::Vec<_>>();

let mut newline = false;

if let Some(expression) = expression {
newline = true;

write!(f, "invalid {}", expression)?;
}

if !expected.is_empty() {
if newline {
writeln!(f)?;
}
newline = true;

write!(f, "expected ")?;
for (i, expected) in expected.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "{}", expected)?;
}
}
#[cfg(feature = "std")]
{
if let Some(cause) = self.inner.cause() {
if newline {
writeln!(f)?;
}
write!(f, "{}", cause)?;
}
}
}

Ok(())
}
}

/// Trace all error paths, particularly for tests
#[derive(Debug)]
#[cfg(feature = "std")]
Expand Down
2 changes: 1 addition & 1 deletion src/stream/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2197,7 +2197,7 @@ where
}

/// Ensure checkpoint details are kept private
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Checkpoint<T>(T);

/// A range bounded inclusively for counting parses performed
Expand Down