Skip to content

Commit

Permalink
feat(longest): introduce AsOrd
Browse files Browse the repository at this point in the history
  • Loading branch information
urso committed Jan 4, 2024
1 parent eb5f257 commit 89f94a8
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 29 deletions.
59 changes: 42 additions & 17 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,22 +752,47 @@ impl crate::lib::std::fmt::Display for StrContextValue {
}
}

/// Used by [`LongestMatch`] to compare checkpoint positions
pub trait AsOrd {
/// The type used to compare checkpoint positions
type Ord: Ord + Clone + crate::lib::std::fmt::Debug;

/// Get comparable value
fn as_ord(&self) -> Self::Ord;
}

impl<'a, T> AsOrd for &'a [T] {
type Ord = *const T;

fn as_ord(&self) -> Self::Ord {
self.as_ptr()
}
}

impl<'a> AsOrd for &'a str {
type Ord = *const u8;

fn as_ord(&self) -> Self::Ord {
self.as_ptr()
}
}

/// Collect context of the longest matching parser while backtracking on errors
#[derive(Clone, Debug)]
pub struct LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
<I as Stream>::Checkpoint: AsOrd,
E: MergeContext,
{
checkpoint: I::Checkpoint,
ord: <<I as Stream>::Checkpoint as AsOrd>::Ord,
inner: E,
}

impl<I, E> LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
<I as Stream>::Checkpoint: AsOrd,
E: MergeContext,
{
/// Extract the error for the longest matching parser
Expand All @@ -780,24 +805,24 @@ where
impl<I, E> ParserError<I> for LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
<I as Stream>::Checkpoint: AsOrd,
E: ParserError<I> + MergeContext,
{
#[inline]
fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
Self {
checkpoint: input.checkpoint(),
ord: input.checkpoint().as_ord(),
inner: E::from_error_kind(input, kind),
}
}

#[inline]
fn append(mut self, input: &I, kind: ErrorKind) -> Self {
let checkpoint = input.checkpoint();
match checkpoint.cmp(&self.checkpoint) {
let checkpoint = input.checkpoint().as_ord();
match checkpoint.cmp(&self.ord) {
core::cmp::Ordering::Less => self,
core::cmp::Ordering::Greater => {
self.checkpoint = checkpoint;
self.ord = checkpoint;
self.clear_context();
self.inner = self.inner.append(input, kind);
self
Expand All @@ -818,19 +843,19 @@ where
impl<I, E, C> AddContext<I, C> for LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
<I as Stream>::Checkpoint: AsOrd,
E: AddContext<I, C> + MergeContext,
{
#[inline]
fn add_context(mut self, input: &I, ctx: C) -> Self {
let checkpoint = input.checkpoint();
match checkpoint.cmp(&self.checkpoint) {
let ord = input.checkpoint().as_ord();
match ord.cmp(&self.ord) {
core::cmp::Ordering::Less => {}
core::cmp::Ordering::Equal => {
self.inner = self.inner.add_context(input, ctx);
}
core::cmp::Ordering::Greater => {
self.checkpoint = checkpoint;
self.ord = ord;
self.inner.clear_context();
self.inner = self.inner.add_context(input, ctx);
}
Expand All @@ -842,11 +867,11 @@ where
impl<I, E: MergeContext> MergeContext for LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
<I as Stream>::Checkpoint: AsOrd,
{
#[inline]
fn merge_context(mut self, other: Self) -> Self {
match other.checkpoint.cmp(&self.checkpoint) {
match other.ord.cmp(&self.ord) {
core::cmp::Ordering::Less => self,
core::cmp::Ordering::Greater => other,
core::cmp::Ordering::Equal => {
Expand All @@ -860,13 +885,13 @@ where
impl<I, E, EX> FromExternalError<I, EX> for LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
<I as Stream>::Checkpoint: AsOrd,
E: FromExternalError<I, EX> + MergeContext,
{
#[inline]
fn from_external_error(input: &I, kind: ErrorKind, e: EX) -> Self {
Self {
checkpoint: input.checkpoint(),
ord: input.checkpoint().as_ord(),
inner: E::from_external_error(input, kind, e),
}
}
Expand All @@ -875,7 +900,7 @@ where
impl<I, E> crate::lib::std::fmt::Display for LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: Ord,
<I as Stream>::Checkpoint: AsOrd,
E: crate::lib::std::fmt::Display + MergeContext,
{
fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
Expand Down
18 changes: 7 additions & 11 deletions src/error/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ use super::*;

mod longest_match {
use super::*;
use crate::{
combinator::{alt, eof, terminated},
Located,
};
use crate::combinator::{alt, eof, terminated};

type Input<'a> = Located<&'a str>;
type Input<'a> = &'a str;
type Error<'a> = LongestMatch<Input<'a>, ContextError<&'static str>>;
type PResult<'a, O> = crate::error::PResult<O, Error<'a>>;

Expand Down Expand Up @@ -37,7 +34,7 @@ mod longest_match {
fn parser<'a>(input: &mut Input<'a>) -> PResult<'a, &'a str> {
alt((syms("abcd"), syms("abc"), syms("ab"))).parse_next(input)
}
let ctx = expect_err(parser(&mut Input::new("abcde")));
let ctx = expect_err(parser(&mut "abcde"));
assert_eq!(ctx, vec!["abcd"]);
}

Expand All @@ -47,24 +44,23 @@ mod longest_match {
alt((syms("abcd"), syms("abc"), syms("def"), syms("defg"))).parse_next(input)
}

let ctx = expect_err(parser(&mut Input::new("abd")));
let ctx = expect_err(parser(&mut "abd"));
assert_eq!(ctx, vec!["abcd", "abc"]);

let ctx = expect_err(parser(&mut Input::new("deg")));
let ctx = expect_err(parser(&mut "deg"));
assert_eq!(ctx, vec!["def", "defg"]);
}

#[test]
#[ignore]
fn multi_longest_match_eof() {
fn parser<'a>(input: &mut Input<'a>) -> PResult<'a, &'a str> {
alt((syms("abcd"), syms("abc"), syms("def"), syms("defg"))).parse_next(input)
}

let ctx = expect_err(parser(&mut Input::new("ab")));
let ctx = expect_err(parser(&mut "ab"));
assert_eq!(ctx, vec!["abcd", "abc"]);

let ctx = expect_err(parser(&mut Input::new("de")));
let ctx = expect_err(parser(&mut "de"));
assert_eq!(ctx, vec!["def", "defg"]);
}
}
10 changes: 9 additions & 1 deletion src/stream/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use core::hash::BuildHasher;
use core::num::NonZeroUsize;

use crate::ascii::Caseless as AsciiCaseless;
use crate::error::Needed;
use crate::error::{AsOrd, Needed};
use crate::lib::std::iter::{Cloned, Enumerate};
use crate::lib::std::slice::Iter;
use crate::lib::std::str::from_utf8;
Expand Down Expand Up @@ -2200,6 +2200,14 @@ where
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Checkpoint<T>(T);

impl<T: AsOrd> AsOrd for Checkpoint<T> {
type Ord = T::Ord;

fn as_ord(&self) -> Self::Ord {
self.0.as_ord()
}
}

/// A range bounded inclusively for counting parses performed
#[derive(PartialEq, Eq)]
pub struct Range {
Expand Down

0 comments on commit 89f94a8

Please sign in to comment.