Skip to content

Commit

Permalink
testing
Browse files Browse the repository at this point in the history
  • Loading branch information
urso committed Jan 5, 2024
1 parent 89f94a8 commit 55b8390
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 70 deletions.
91 changes: 66 additions & 25 deletions src/error.rs → src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,14 +337,10 @@ pub trait AddContext<I, C = &'static str>: Sized {
/// Used by [`LongestMatch::or`] to merge context states while backtracking.
pub trait MergeContext: Sized {
/// Apply the context from `other` into `self`
#[inline]
fn merge_context(self, _other: Self) -> Self {
self
}
fn merge_context(self, _other: Self) -> Self;

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

/// Create a new error with an external error, from [`std::str::FromStr`]
Expand Down Expand Up @@ -755,7 +751,7 @@ 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;
type Ord: Ord + Clone + core::cmp::Ord + crate::lib::std::fmt::Debug;

/// Get comparable value
fn as_ord(&self) -> Self::Ord;
Expand Down Expand Up @@ -785,10 +781,22 @@ where
<I as Stream>::Checkpoint: AsOrd,
E: MergeContext,
{
ord: <<I as Stream>::Checkpoint as AsOrd>::Ord,
checkpoint: Option<<I as Stream>::Checkpoint>,
inner: E,
}

// For tests
impl<I, E> core::cmp::PartialEq for LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: AsOrd + core::cmp::PartialEq,
E: MergeContext + core::cmp::PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.checkpoint == other.checkpoint && self.inner == other.inner
}
}

impl<I, E> LongestMatch<I, E>
where
I: Stream,
Expand All @@ -800,6 +808,35 @@ where
pub fn into_inner(self) -> E {
self.inner
}

#[inline]
fn cmp_checkpoints(&self, other: &Self) -> core::cmp::Ordering {
let a = self.checkpoint.as_ref().map(|c| c.as_ord());
let b = other.checkpoint.as_ref().map(|c| c.as_ord());
a.cmp(&b)
}

#[inline]
fn cmp_with_checkpoint(&self, other: &<I as Stream>::Checkpoint) -> core::cmp::Ordering {
match self.checkpoint {
None => core::cmp::Ordering::Less,
Some(ref c) => c.as_ord().cmp(&other.as_ord()),
}
}
}

impl<I, E> Default for LongestMatch<I, E>
where
I: Stream,
<I as Stream>::Checkpoint: AsOrd,
E: MergeContext + Default,
{
fn default() -> Self {
Self {
checkpoint: None,
inner: Default::default(),
}
}
}

impl<I, E> ParserError<I> for LongestMatch<I, E>
Expand All @@ -811,18 +848,17 @@ where
#[inline]
fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
Self {
ord: input.checkpoint().as_ord(),
checkpoint: Some(input.checkpoint()),
inner: E::from_error_kind(input, kind),
}
}

#[inline]
fn append(mut self, input: &I, kind: ErrorKind) -> Self {
let checkpoint = input.checkpoint().as_ord();
match checkpoint.cmp(&self.ord) {
core::cmp::Ordering::Less => self,
core::cmp::Ordering::Greater => {
self.ord = checkpoint;
let checkpoint = input.checkpoint();
match self.cmp_with_checkpoint(&checkpoint) {
core::cmp::Ordering::Less => {
self.checkpoint = Some(checkpoint);
self.clear_context();
self.inner = self.inner.append(input, kind);
self
Expand All @@ -831,6 +867,7 @@ where
self.inner = self.inner.append(input, kind);
self
}
core::cmp::Ordering::Greater => self,
}
}

Expand All @@ -848,17 +885,17 @@ where
{
#[inline]
fn add_context(mut self, input: &I, ctx: C) -> Self {
let ord = input.checkpoint().as_ord();
match ord.cmp(&self.ord) {
core::cmp::Ordering::Less => {}
core::cmp::Ordering::Equal => {
let checkpoint = input.checkpoint();
match self.cmp_with_checkpoint(&checkpoint) {
core::cmp::Ordering::Less => {
self.checkpoint = Some(checkpoint);
self.inner.clear_context();
self.inner = self.inner.add_context(input, ctx);
}
core::cmp::Ordering::Greater => {
self.ord = ord;
self.inner.clear_context();
core::cmp::Ordering::Equal => {
self.inner = self.inner.add_context(input, ctx);
}
core::cmp::Ordering::Greater => {}
}
self
}
Expand All @@ -871,15 +908,19 @@ where
{
#[inline]
fn merge_context(mut self, other: Self) -> Self {
match other.ord.cmp(&self.ord) {
core::cmp::Ordering::Less => self,
core::cmp::Ordering::Greater => other,
match self.cmp_checkpoints(&other) {
core::cmp::Ordering::Less => other,
core::cmp::Ordering::Greater => self,
core::cmp::Ordering::Equal => {
self.inner = self.inner.merge_context(other.inner);
self
}
}
}

fn clear_context(&mut self) {
self.inner.clear_context();
}
}

impl<I, E, EX> FromExternalError<I, EX> for LongestMatch<I, E>
Expand All @@ -891,7 +932,7 @@ where
#[inline]
fn from_external_error(input: &I, kind: ErrorKind, e: EX) -> Self {
Self {
ord: input.checkpoint().as_ord(),
checkpoint: Some(input.checkpoint()),
inner: E::from_external_error(input, kind, e),
}
}
Expand Down
99 changes: 54 additions & 45 deletions src/error/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,74 @@ use super::*;

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

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

fn tag<'a>(t: &'static str) -> impl Parser<Input<'a>, &'a str, Error<'a>> {
move |input: &mut Input<'a>| {
let mut i = input.clone();
for mut c in t.chars() {
c.parse_next(input)?;
}
Ok(i.next_slice(t.len()))
}
}

fn syms<'a>(t: &'static str) -> impl Parser<Input<'a>, &'a str, Error<'a>> {
terminated(tag(t), eof).context(t)
}

fn expect_err<T: core::fmt::Debug>(res: PResult<'_, T>) -> Vec<&'static str> {
let err = res.unwrap_err();
let err = err.into_inner().unwrap();
let err = err.into_inner();
err.context().copied().collect::<Vec<_>>()
fn pattern<'a, O>(
p: impl Parser<&'a str, O, Error<'a>>,
label: &'static str,
) -> impl Parser<&'a str, &'a str, Error<'a>> {
(p, eof).recognize().context(label)
}

#[test]
fn single_longest_match_first_in_alt() {
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 "abcde"));
assert_eq!(ctx, vec!["abcd"]);
let mut parser = alt((
pattern(('a', 'b', 'c', 'd'), "wanted"),
pattern(('a', 'b', 'c'), "don't want 1"),
pattern(('a', 'b'), "don't want 2"),
));

let mut input = "abcde";
let checkpoint = &&input[4..]; // 4 characters consumed by longest match
assert_eq!(
parser.parse_next(&mut input),
Err(ErrMode::Backtrack(
LongestMatch::default().add_context(checkpoint, "wanted"),
))
);
}

#[test]
fn multi_longest_match() {
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 "abd"));
assert_eq!(ctx, vec!["abcd", "abc"]);
let mut parser = alt((
pattern(('d', 'e', 'f'), "don't want"),
pattern(('a', 'b', 'c', 'd'), "wanted 1"),
pattern(('a', 'b', 'c'), "wanted 2"),
pattern(('d', 'e', 'f', 'g'), "don't want"),
));

let ctx = expect_err(parser(&mut "deg"));
assert_eq!(ctx, vec!["def", "defg"]);
let mut input = "abd";
let checkpoint = &&input[2..]; // 2 characters consumed by longest match
assert_eq!(
parser.parse_next(&mut input),
Err(ErrMode::Backtrack(
LongestMatch::default()
.add_context(checkpoint, "wanted 1")
.add_context(checkpoint, "wanted 2"),
))
);
}

#[test]
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 "ab"));
assert_eq!(ctx, vec!["abcd", "abc"]);
fn multi_longest_match_input_short() {
let mut parser = alt((
pattern(('d', 'e', 'f'), "don't want"),
pattern(('a', 'b', 'c', 'd'), "wanted 1"),
pattern(('a', 'b', 'c'), "wanted 2"),
pattern(('d', 'e', 'f', 'g'), "don't want"),
));

let ctx = expect_err(parser(&mut "de"));
assert_eq!(ctx, vec!["def", "defg"]);
let mut input = "ab";
let checkpoint = &&input[2..]; // 2 characters consumed by longest match
assert_eq!(
parser.parse_next(&mut input),
Err(ErrMode::Backtrack(
LongestMatch::default()
.add_context(checkpoint, "wanted 1")
.add_context(checkpoint, "wanted 2"),
))
);
}
}

0 comments on commit 55b8390

Please sign in to comment.