Repository: Marwes/combine (language: rust)

Issue:
not_followed_by behaves inconsistently on empty errors
See https://github.com/haskell/parsec/issues/8


Discussion / hints:

Fix patch:
diff --git a/src/parser/combinator.rs b/src/parser/combinator.rs
index ab88e72e..ff7d6fe2 100644
--- a/src/parser/combinator.rs
+++ b/src/parser/combinator.rs
@@ -6,8 +6,6 @@ use lib::mem;
 use lib::str;
 
 use error::{ConsumedResult, Info, ParseError, StreamError, Tracked};
-use parser::error::unexpected;
-use parser::item::value;
 use parser::ParseMode;
 use stream::{input_at_eof, Positioned, Resetable, Stream, StreamErrorFor, StreamOnce};
 use Parser;
@@ -16,9 +14,49 @@ use either::Either;
 
 use error::FastResult::*;
 
-parser! {
 #[derive(Copy, Clone)]
-pub struct NotFollowedBy;
+pub struct NotFollowedBy<P>(P);
+impl<I, O, P> Parser for NotFollowedBy<P>
+where
+    I: Stream,
+    P: Parser<Input = I, Output = O>,
+{
+    type Input = I;
+    type Output = ();
+    type PartialState = P::PartialState;
+
+    parse_mode!();
+    #[inline]
+    fn parse_mode_impl<M>(
+        &mut self,
+        mode: M,
+        input: &mut Self::Input,
+        state: &mut Self::PartialState,
+    ) -> ConsumedResult<Self::Output, Self::Input>
+    where
+        M: ParseMode,
+    {
+        let checkpoint = input.checkpoint();
+        let result = self.0.parse_mode(mode, input, state);
+        input.reset(checkpoint);
+        match result {
+            ConsumedOk(_) | EmptyOk(_) => EmptyErr(I::Error::empty(input.position()).into()),
+            ConsumedErr(_) | EmptyErr(_) => EmptyOk(()),
+        }
+    }
+
+    #[inline]
+    fn add_error(&mut self, _errors: &mut Tracked<<Self::Input as StreamOnce>::Error>) {}
+
+    fn add_consumed_expected_error(
+        &mut self,
+        _error: &mut Tracked<<Self::Input as StreamOnce>::Error>,
+    ) {
+    }
+
+    forward_parser!(parser_count, 0);
+}
+
 /// Succeeds only if `parser` fails.
 /// Never consumes any input.
 ///
@@ -32,18 +70,16 @@ pub struct NotFollowedBy;
 ///     .parse("letx")
 ///     .map(|x| x.0);
 /// assert!(result.is_err());
+///
 /// # }
 /// ```
 #[inline(always)]
-pub fn not_followed_by[P](parser: P)(P::Input) -> ()
-where [
+pub fn not_followed_by<P>(parser: P) -> NotFollowedBy<P>
+where
     P: Parser,
     P::Output: Into<Info<<P::Input as StreamOnce>::Item, <P::Input as StreamOnce>::Range>>,
-]
 {
-    attempt(attempt(parser).then(unexpected)
-        .or(value(())))
-}
+    NotFollowedBy(parser)
 }
 
 /*


Test patch:
diff --git a/tests/parser.rs b/tests/parser.rs
index 7c319741..78a6f8bd 100644
--- a/tests/parser.rs
+++ b/tests/parser.rs
@@ -6,7 +6,9 @@ use combine::parser::combinator::{attempt, no_partial, not_followed_by};
 use combine::parser::error::unexpected;
 use combine::parser::item::{any, eof, position, token, value, Token};
 use combine::parser::range::{self, range};
-use combine::parser::repeat::{count, count_min_max, many, sep_by, sep_end_by1, skip_until, take_until};
+use combine::parser::repeat::{
+    count, count_min_max, many, sep_by, sep_end_by1, skip_until, take_until,
+};
 use combine::Parser;
 
 #[test]
@@ -43,8 +45,8 @@ fn not_followed_by_does_not_consume_any_input() {
 #[cfg(feature = "std")]
 mod tests_std {
     use super::*;
-    use combine::parser::byte::{alpha_num, bytes};
     use combine::parser::byte::num::be_u32;
+    use combine::parser::byte::{alpha_num, bytes};
     use combine::parser::char::{char, digit, letter};
     use combine::stream::easy::{self, Error, Errors};
     use combine::stream::state::{SourcePosition, State};
@@ -574,10 +576,10 @@ mod tests_std {
     #[test]
     fn test_nested_count_overflow() {
         let key = || count::<Vec<_>, _>(64, alpha_num());
-        let value_bytes = || be_u32()
-            .then_partial(|&mut size| count::<Vec<_>, _>(size as usize, any()));
-        let value_messages = (be_u32(), be_u32())
-            .then_partial(|&mut (_body_size, message_count)| {
+        let value_bytes =
+            || be_u32().then_partial(|&mut size| count::<Vec<_>, _>(size as usize, any()));
+        let value_messages =
+            (be_u32(), be_u32()).then_partial(|&mut (_body_size, message_count)| {
                 count::<Vec<_>, _>(message_count as usize, value_bytes())
             });
         let put = (bytes(b"PUT"), key())
@@ -590,4 +592,13 @@ mod tests_std {
         let result = parser().parse(command).unwrap();
         assert_eq!(2, result.0.len());
     }
+
+    #[test]
+    fn not_followed_by_empty_error_issue_220() {
+        let mut parser = string("let").skip(not_followed_by(eof().map(|_| "EOF")));
+        assert_eq!(
+            parser.easy_parse("let").map_err(|err| err.errors),
+            Err(vec![]),
+        );
+    }
 }