From 6c75c083df4ff4249d5dfb1b540440b2fe2efd5a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 2 Jan 2024 21:27:44 -0600 Subject: [PATCH 1/4] feat(stream): Allow comparing Checkpoints --- src/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 39a1e0d48..2b219f20e 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -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); /// A range bounded inclusively for counting parses performed From ff36774e6583a6e4a192f3095aebc4f1d81f7435 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 2 Jan 2024 21:38:34 -0600 Subject: [PATCH 2/4] feat(error): Add AddContext::merge_error --- src/error.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/error.rs b/src/error.rs index fd7d94695..89f6573a2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -225,6 +225,14 @@ impl> AddContext for ErrMode { 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, + } + } } impl ErrMode> { @@ -311,6 +319,12 @@ pub trait AddContext: 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 + } } /// Create a new error with an external error, from [`std::str::FromStr`] @@ -534,6 +548,13 @@ impl AddContext for ContextError { self.context.push(ctx); self } + + #[inline] + fn merge_context(mut self, other: Self) -> Self { + #[cfg(feature = "alloc")] + self.context.extend(other.context); + self + } } #[cfg(feature = "std")] From dbc92c4c1ac3c377c728e74bb2197c4b14d79e58 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 2 Jan 2024 21:52:30 -0600 Subject: [PATCH 3/4] feat(error): Add AddContext::clear_error --- src/error.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/error.rs b/src/error.rs index 89f6573a2..20b9ff910 100644 --- a/src/error.rs +++ b/src/error.rs @@ -233,6 +233,14 @@ impl> AddContext for ErrMode { None => self, } } + + #[inline] + fn clear_context(&mut self) { + match self { + ErrMode::Incomplete(_) => {} + ErrMode::Cut(t) | ErrMode::Backtrack(t) => t.clear_context(), + } + } } impl ErrMode> { @@ -325,6 +333,10 @@ pub trait AddContext: Sized { 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`] @@ -555,6 +567,12 @@ impl AddContext for ContextError { self.context.extend(other.context); self } + + #[inline] + fn clear_context(&mut self) { + #[cfg(feature = "alloc")] + self.context.clear(); + } } #[cfg(feature = "std")] From 8500d6619e11d223557895affe50f21cfe6adfba Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 2 Jan 2024 22:02:27 -0600 Subject: [PATCH 4/4] WIP: feat(error): Add LongestMatch error decorator --- src/error.rs | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/src/error.rs b/src/error.rs index 20b9ff910..7eb18a3c1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -734,6 +734,160 @@ impl crate::lib::std::fmt::Display for StrContextValue { } } +#[derive(Clone, Debug)] +pub struct LongestMatch +where + I: Stream, + ::Checkpoint: Ord, +{ + checkpoint: I::Checkpoint, + inner: E, +} + +impl LongestMatch +where + I: Stream, + ::Checkpoint: Ord, +{ + #[inline] + pub fn into_inner(self) -> E { + self.inner + } +} + +impl ParserError for LongestMatch +where + I: Stream, + ::Checkpoint: Ord, + E: ParserError, +{ + #[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 AddContext for LongestMatch +where + I: Stream, + ::Checkpoint: Ord, + E: AddContext, +{ + #[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 FromExternalError for LongestMatch +where + I: Stream, + ::Checkpoint: Ord, + E: FromExternalError, +{ + #[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 crate::lib::std::fmt::Display for LongestMatch> +where + I: 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::>(); + + 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")]