Repository: Geal/nom
Pull request #1052 (2019-10-26 15:27:24)

Add link to SWF parser
## Prerequisites

Please provide the following information with this pull request:

- related issue number: #14
- a test case reproducing the issue: N/A
- if adding a new combinator, please add code documentation and some unit tests
in the same file: N/A

## Description

This commit adds a link to Open Flash's SWF parser implemented using nom. The parser was recently updated to nom 5 add provides a good example for parsing a fairly complex binary format.


Patch:
diff --git a/README.md b/README.md
index a69e49d..6f28262 100644
--- a/README.md
+++ b/README.md
@@ -247,6 +247,7 @@ Here is a (non exhaustive) list of known projects using nom:
   * [libconfig-like configuration file format](https://github.com/filipegoncalves/rust-config)
   * [Web archive](https://github.com/sbeckeriv/warc_nom_parser)
   * [proto files](https://github.com/tafia/protobuf-parser)
+  * [Fountain screenplay markup](https://github.com/adamchalmers/fountain-rs)
 - Programming languages:
   * [PHP](https://github.com/tagua-vm/parser)
   * [Basic Calculator](https://github.com/balajisivaraman/basic_calculator_rs)
@@ -254,12 +255,14 @@ Here is a (non exhaustive) list of known projects using nom:
   * [Lua](https://github.com/doomrobo/nom-lua53)
   * [SQL](https://github.com/ms705/nom-sql)
   * [Elm](https://github.com/cout970/Elm-interpreter)
+  * [SystemVerilog](https://github.com/dalance/sv-parser)
 - Interface definition formats:
   * [Thrift](https://github.com/thehydroimpulse/thrust)
 - Audio, video and image formats:
   * [GIF](https://github.com/Geal/gif.rs)
   * [MagicaVoxel .vox](https://github.com/davidedmonds/dot_vox)
   * [midi](https://github.com/derekdreery/nom-midi-rs)
+  * [SWF](https://github.com/open-flash/swf-parser)
   * [WAVE](http://github.com/noise-Labs/wave)
 - Document formats:
   * [TAR](https://github.com/Keruspe/tar-parser.rs)
@@ -268,7 +271,7 @@ Here is a (non exhaustive) list of known projects using nom:
   * [Redis database files](https://github.com/badboy/rdb-rs)
 - Network protocol formats:
   * [Bencode](https://github.com/jbaum98/bencode.rs)
-  * [IMAP](https://github.com/djc/imap-proto)
+  * [IMAP](https://github.com/djc/tokio-imap)
   * [IRC](https://github.com/Detegr/RBot-parser)
   * [Pcap-NG](https://github.com/richo/pcapng-rs)
   * [Pcap](https://github.com/ithinuel/pcap-rs)
diff --git a/doc/archive/upgrading_to_nom_4.md b/doc/archive/upgrading_to_nom_4.md
index 3c8fae1..092a48d 100644
--- a/doc/archive/upgrading_to_nom_4.md
+++ b/doc/archive/upgrading_to_nom_4.md
@@ -18,7 +18,7 @@ pub enum IResult<I,O,E=u32> {
   Done(I,O),
   /// indicates the parser encountered an error. E is a custom error type you can redefine
   Error(Err<E>),
-  /// Incomplete contains a Needed, an enum than can represent a known quantity of input data, or unknown
+  /// Incomplete contains a Needed, an enum that can represent a known quantity of input data, or unknown
   Incomplete(Needed)
 }
 
diff --git a/doc/choosing_a_combinator.md b/doc/choosing_a_combinator.md
index cc7f670..507b4b7 100644
--- a/doc/choosing_a_combinator.md
+++ b/doc/choosing_a_combinator.md
@@ -10,51 +10,50 @@ Those are used to recognize the lowest level elements of your grammar, like, "he
 
 | combinator | usage | input | output | comment |
 |---|---|---|---|---|
-| [char](https://docs.rs/nom/4.0.0/nom/macro.char.html) | `char!('a')` |  `"abc"` | `Ok(("bc", 'a'))`| matches one character (works with non ASCII chars too) | 
-| [is_a](https://docs.rs/nom/4.0.0/nom/macro.is_a.html) | ` is_a!("ab")` |  `"ababc"` | `Ok(("c", "abab"))`|matches a sequence of any of the characters passed as arguments|
-| [is_not](https://docs.rs/nom/4.0.0/nom/macro.is_not.html) |`is_not!("cd")` |  `"ababc"` | `Ok(("c", "abab"))`|matches a sequence of none of the characters passed as arguments|
-| [one_of](https://docs.rs/nom/4.0.0/nom/macro.one_of.html) |`one_of!("abc")` |  `"abc"` | `Ok(("bc", 'a'))`|matches one of the provided characters (works with non ASCII characters too)|
-| [none_of](https://docs.rs/nom/4.0.0/nom/macro.none_of.html) |`none_of!("abc")` |  `"xyab"` | `Ok(("yab", 'x'))`|matches anything but the provided characters|
-| [tag](https://docs.rs/nom/4.0.0/nom/macro.tag.html) |`tag!("hello")` |  `"hello world"` | `Ok((" world", "hello"))`|recognizes a specific suite of characters or bytes|
-| [tag_no_case](https://docs.rs/nom/4.0.0/nom/macro.tag_no_case.html) |`tag_no_case!("hello")` |  `"HeLLo World"` | `Ok((" World", "HeLLo"))`|case insensitive comparison. Note that case insensitive comparison is not well defined for unicode, and that you might have bad surprises|
-| [take](https://docs.rs/nom/4.0.0/nom/macro.take.html) |`take!(4)` |  `"hello"` | `Ok(("o", "hell"))`|takes a specific number of bytes or characters|
-| [take_while](https://docs.rs/nom/4.0.0/nom/macro.take_while.html) |`take_while!(is_alphabetic)` |  `"abc123"` | `Ok(("123", "abc"))`| returns the longest list of bytes for which the provided function returns true. `take_while1` does the same, but must return at least one character|
-| [take_till](https://docs.rs/nom/4.0.0/nom/macro.take_till.html) |`take_till!(is_alphabetic)` |  `"123abc"` | `Ok(("abc", "123))`|returns the longest list of bytes or characters until the provided function returns true. `take_till1` does the same, but must return at least one character. This is the reverse behaviour from `take_while`: `take_till!(f)` is equivalent to `take_while!(|c| !f(c))`|
-| [take_until](https://docs.rs/nom/4.0.0/nom/macro.take_until.html) | `take_until!("world")` |  `"Hello world"` | `Ok(("world", "Hello "))`|returns the longest list of bytes or characters until the provided tag is found. `take_until1` does the same, but must return at least one character|
+| [char](https://docs.rs/nom/latest/nom/macro.char.html) | `char!('a')` |  `"abc"` | `Ok(("bc", 'a'))`| matches one character (works with non ASCII chars too) | 
+| [is_a](https://docs.rs/nom/latest/nom/macro.is_a.html) | ` is_a!("ab")` |  `"ababc"` | `Ok(("c", "abab"))`|matches a sequence of any of the characters passed as arguments|
+| [is_not](https://docs.rs/nom/latest/nom/macro.is_not.html) |`is_not!("cd")` |  `"ababc"` | `Ok(("c", "abab"))`|matches a sequence of none of the characters passed as arguments|
+| [one_of](https://docs.rs/nom/latest/nom/macro.one_of.html) |`one_of!("abc")` |  `"abc"` | `Ok(("bc", 'a'))`|matches one of the provided characters (works with non ASCII characters too)|
+| [none_of](https://docs.rs/nom/latest/nom/macro.none_of.html) |`none_of!("abc")` |  `"xyab"` | `Ok(("yab", 'x'))`|matches anything but the provided characters|
+| [tag](https://docs.rs/nom/latest/nom/macro.tag.html) |`tag!("hello")` |  `"hello world"` | `Ok((" world", "hello"))`|recognizes a specific suite of characters or bytes|
+| [tag_no_case](https://docs.rs/nom/latest/nom/macro.tag_no_case.html) |`tag_no_case!("hello")` |  `"HeLLo World"` | `Ok((" World", "HeLLo"))`|case insensitive comparison. Note that case insensitive comparison is not well defined for unicode, and that you might have bad surprises|
+| [take](https://docs.rs/nom/latest/nom/macro.take.html) |`take!(4)` |  `"hello"` | `Ok(("o", "hell"))`|takes a specific number of bytes or characters|
+| [take_while](https://docs.rs/nom/latest/nom/macro.take_while.html) |`take_while!(is_alphabetic)` |  `"abc123"` | `Ok(("123", "abc"))`| returns the longest list of bytes for which the provided function returns true. `take_while1` does the same, but must return at least one character|
+| [take_till](https://docs.rs/nom/latest/nom/macro.take_till.html) |`take_till!(is_alphabetic)` |  `"123abc"` | `Ok(("abc", "123))`|returns the longest list of bytes or characters until the provided function returns true. `take_till1` does the same, but must return at least one character. This is the reverse behaviour from `take_while`: `take_till!(f)` is equivalent to `take_while!(|c| !f(c))`|
+| [take_until](https://docs.rs/nom/latest/nom/macro.take_until.html) | `take_until!("world")` |  `"Hello world"` | `Ok(("world", "Hello "))`|returns the longest list of bytes or characters until the provided tag is found. `take_until1` does the same, but must return at least one character|
 
 
 ## Choice combinators
 
 | combinator | usage | input | output | comment |
 |---|---|---|---|---|
-| [alt](https://docs.rs/nom/4.0.0/nom/macro.alt.html) |`alt!(tag!("ab") \| tag!("cd"))` |  `"cdef"` | `Ok( ("ef", "cd") )`|try a list of parsers and return the result of the first successful one|
-| [switch](https://docs.rs/nom/4.0.0/nom/macro.switch.html) |`switch!(take!(2), "ab" => tag!("XYZ") \| "cd" => tag!("123"))` |  `"cd1234"` | `Ok(("4", "123"))`|choose the next parser depending on the result of the first one, if successful, and returns the result of the second parser|
-| [permutation](https://docs.rs/nom/4.0.0/nom/macro.permutation.html) | `permutation!(tag!("ab"), tag!("cd"), tag!("12"))` |  `"cd12abc"` | `Ok( ("c", ("ab", "cd", "12") )`|succeeds when all its child parser have succeeded, whatever the order|
+| [alt](https://docs.rs/nom/latest/nom/macro.alt.html) |`alt!(tag!("ab") \| tag!("cd"))` |  `"cdef"` | `Ok( ("ef", "cd") )`|try a list of parsers and return the result of the first successful one|
+| [switch](https://docs.rs/nom/latest/nom/macro.switch.html) |`switch!(take!(2), "ab" => tag!("XYZ") \| "cd" => tag!("123"))` |  `"cd1234"` | `Ok(("4", "123"))`|choose the next parser depending on the result of the first one, if successful, and returns the result of the second parser|
+| [permutation](https://docs.rs/nom/latest/nom/macro.permutation.html) | `permutation!(tag!("ab"), tag!("cd"), tag!("12"))` |  `"cd12abc"` | `Ok( ("c", ("ab", "cd", "12") )`|succeeds when all its child parser have succeeded, whatever the order|
 
 ## Sequence combinators
 | combinator | usage | input | output | comment |
 |---|---|---|---|---|
-| [delimited](https://docs.rs/nom/4.0.0/nom/macro.delimited.html) |`delimited!(char!('('), take!(2), char!(')'))` |  `"(ab)cd"` | `Ok( ("cd", "ab") )`||
-| [preceded](https://docs.rs/nom/4.0.0/nom/macro.preceded.html) |`preceded!(tag!("ab"), tag!("XY"))` |  `"abXYZ"` | `Ok( ("Z", "XY") )`||
-| [terminated](https://docs.rs/nom/4.0.0/nom/macro.terminated.html) |`terminated!(tag!("ab"), tag!("XY"))` |  `"abXYZ"` | `Ok( ("Z", "ab") )`||
-| [pair](https://docs.rs/nom/4.0.0/nom/macro.pair.html) |`pair!(tag!("ab"), tag!("XY"))` |  `"abXYZ"` | `Ok( ("Z", ("ab", "XY")) )`||
-| [separated_pair](https://docs.rs/nom/4.0.0/nom/macro.separated_pair.html) |`separated_pair!(tag!("hello"), char!(','), tag!("world"))` |  `"hello,world!"` | `Ok( ("!", ("hello", "world")) )`||
-| [tuple](https://docs.rs/nom/4.0.0/nom/macro.tuple.html) |`tuple!(tag!("ab"), tag!("XY"), take!(1))` |  `"abXYZ!"` | `Ok( ("!", ("ab", "XY", "Z")) )`|chains parsers and assemble the sub results in a tuple. You can use as many child parsers as you can put elements in a tuple|
-| [do_parse](https://docs.rs/nom/4.0.0/nom/macro.do_parse.html) |`do_parse!(tag: take!(2) >> length: be_u8 >> data: take!(length) >> (Buffer { tag: tag, data: data}) )` |  `&[0, 0, 3, 1, 2, 3][..]` | `Buffer { tag: &[0, 0][..], data: &[1, 2, 3][..] }`|`do_parse` applies sub parsers in a sequence. it can store intermediary results and make them available for later parsers|
+| [delimited](https://docs.rs/nom/latest/nom/macro.delimited.html) |`delimited!(char!('('), take!(2), char!(')'))` |  `"(ab)cd"` | `Ok( ("cd", "ab") )`||
+| [preceded](https://docs.rs/nom/latest/nom/macro.preceded.html) |`preceded!(tag!("ab"), tag!("XY"))` |  `"abXYZ"` | `Ok( ("Z", "XY") )`||
+| [terminated](https://docs.rs/nom/latest/nom/macro.terminated.html) |`terminated!(tag!("ab"), tag!("XY"))` |  `"abXYZ"` | `Ok( ("Z", "ab") )`||
+| [pair](https://docs.rs/nom/latest/nom/macro.pair.html) |`pair!(tag!("ab"), tag!("XY"))` |  `"abXYZ"` | `Ok( ("Z", ("ab", "XY")) )`||
+| [separated_pair](https://docs.rs/nom/latest/nom/macro.separated_pair.html) |`separated_pair!(tag!("hello"), char!(','), tag!("world"))` |  `"hello,world!"` | `Ok( ("!", ("hello", "world")) )`||
+| [tuple](https://docs.rs/nom/latest/nom/macro.tuple.html) |`tuple!(tag!("ab"), tag!("XY"), take!(1))` |  `"abXYZ!"` | `Ok( ("!", ("ab", "XY", "Z")) )`|chains parsers and assemble the sub results in a tuple. You can use as many child parsers as you can put elements in a tuple|
+| [do_parse](https://docs.rs/nom/latest/nom/macro.do_parse.html) |`do_parse!(tag: take!(2) >> length: be_u8 >> data: take!(length) >> (Buffer { tag: tag, data: data}) )` |  `&[0, 0, 3, 1, 2, 3][..]` | `Buffer { tag: &[0, 0][..], data: &[1, 2, 3][..] }`|`do_parse` applies sub parsers in a sequence. it can store intermediary results and make them available for later parsers|
 
 ## Applying a parser multiple times
 
 | combinator | usage | input | output | comment |
 |---|---|---|---|---|
-| [count](https://docs.rs/nom/4.0.0/nom/macro.count.html) |`count!(take!(2), 3)` |  `"abcdefgh"` | `Ok( ("gh", vec!("ab", "cd", "ef")) )`|applies the child parser a specified number of times|
-| [count_fixed](https://docs.rs/nom/4.0.0/nom/macro.count_fixed.html) |`count_fixed!(&[u8], take!(2), 3)` |  `"abcdefgh"` | `Ok( ("gh", ["ab", "cd", "ef"]))`|applies the child parser a fixed number of times and returns a fixed size array. The type must be specified and it must be `Copy`|
-| [many0](https://docs.rs/nom/4.0.0/nom/macro.many0.html) |`many0!(tag!("ab"))` |  `"abababc"` | `Ok( ("c", vec!("ab", "ab", "ab")) )`|Applies the parser 0 or more times and returns the list of results in a Vec. `many1` does the same operation but must return at least one element|
-| [many_m_n](https://docs.rs/nom/4.0.0/nom/macro.many_m_n.html) |`many_m_n!(1, 3, tag!("ab"))` |  `"ababc"` | `Ok( ("c", vec!("ab", "ab")) )`|applies the parser between m and n times (n included) and returns the list of results in a Vec|
-| [many_till](https://docs.rs/nom/4.0.0/nom/macro.many_till.html) |`many_till!( tag!( "ab" ), tag!( "ef" ) )` |  `"ababefg"` | `Ok( ("g", (vec!("ab", "ab"), "ef")) )`|Applies the first parser until the second applies. Returns a tuple containing the list of results from the first in a Vec and the result of the second.|
-| [separated_list](https://docs.rs/nom/4.0.0/nom/macro.separated_list.html) |`separated_list!(tag!(","), tag!("ab"))` |  `"ab,ab,ab."` | `Ok( (".", vec!("ab", "ab", "ab")) )`|`separated_nonempty_list` works like `separated_list` but must returns at least one element|
-| [fold_many0](https://docs.rs/nom/4.0.0/nom/macro.fold_many0.html) |`fold_many0!(be_u8, 0, \|acc, item\| acc + item)` |  `[1, 2, 3]` | `Ok( ([], 6) )`|applies the parser 0 or more times and folds the list of return values. The `fold_many1` version must apply the child parser at least one time|
-| [fold_many_m_n](https://docs.rs/nom/4.0.0/nom/macro.fold_many_m_n.html) |`fold_many_m_n!(1, 2, be_u8, 0, \|acc, item\| acc + item)` |  `[1, 2, 3]` | `Ok( ([3], 3))`|applies the parser between m and n times (n included) and folds the list of return value|
-| [length_count](https://docs.rs/nom/4.0.0/nom/macro.length_count.html) |`length_count!(number, tag!("ab"))` |  `"2ababab"` | `Ok( ("ab", vec!("ab", "ab")) )`|gets a number from the first parser, then applies the second parser that many times|
+| [count](https://docs.rs/nom/latest/nom/macro.count.html) |`count!(take!(2), 3)` |  `"abcdefgh"` | `Ok( ("gh", vec!("ab", "cd", "ef")) )`|applies the child parser a specified number of times|
+| [many0](https://docs.rs/nom/latest/nom/macro.many0.html) |`many0!(tag!("ab"))` |  `"abababc"` | `Ok( ("c", vec!("ab", "ab", "ab")) )`|Applies the parser 0 or more times and returns the list of results in a Vec. `many1` does the same operation but must return at least one element|
+| [many_m_n](https://docs.rs/nom/latest/nom/macro.many_m_n.html) |`many_m_n!(1, 3, tag!("ab"))` |  `"ababc"` | `Ok( ("c", vec!("ab", "ab")) )`|applies the parser between m and n times (n included) and returns the list of results in a Vec|
+| [many_till](https://docs.rs/nom/latest/nom/macro.many_till.html) |`many_till!( tag!( "ab" ), tag!( "ef" ) )` |  `"ababefg"` | `Ok( ("g", (vec!("ab", "ab"), "ef")) )`|Applies the first parser until the second applies. Returns a tuple containing the list of results from the first in a Vec and the result of the second.|
+| [separated_list](https://docs.rs/nom/latest/nom/macro.separated_list.html) |`separated_list!(tag!(","), tag!("ab"))` |  `"ab,ab,ab."` | `Ok( (".", vec!("ab", "ab", "ab")) )`|`separated_nonempty_list` works like `separated_list` but must returns at least one element|
+| [fold_many0](https://docs.rs/nom/latest/nom/macro.fold_many0.html) |`fold_many0!(be_u8, 0, \|acc, item\| acc + item)` |  `[1, 2, 3]` | `Ok( ([], 6) )`|applies the parser 0 or more times and folds the list of return values. The `fold_many1` version must apply the child parser at least one time|
+| [fold_many_m_n](https://docs.rs/nom/latest/nom/macro.fold_many_m_n.html) |`fold_many_m_n!(1, 2, be_u8, 0, \|acc, item\| acc + item)` |  `[1, 2, 3]` | `Ok( ([3], 3))`|applies the parser between m and n times (n included) and folds the list of return value|
+| [length_count](https://docs.rs/nom/latest/nom/macro.length_count.html) |`length_count!(number, tag!("ab"))` |  `"2ababab"` | `Ok( ("ab", vec!("ab", "ab")) )`|gets a number from the first parser, then applies the second parser that many times|
 
 ## Integers
 
@@ -107,7 +106,7 @@ Parsing integers from binary formats can be done in two ways: with parser functi
 
 ## Binary format parsing
 
-- `length_data!`: gets a number from the first parser, than takes a subslice of the input of that size, and returns that subslice
+- `length_data!`: gets a number from the first parser, then takes a subslice of the input of that size, and returns that subslice
 - `length_bytes!`: alias for length_data
 - `length_value!`: gets a number from the first parser, takes a subslice of the input of that size, then applies the second parser on that subslice. If the second parser returns `Incomplete`, `length_value!` will return an error
 
diff --git a/doc/error_management.md b/doc/error_management.md
index 171cc10..3151fb3 100644
--- a/doc/error_management.md
+++ b/doc/error_management.md
@@ -8,13 +8,13 @@ This is done through the third type parameter of `IResult`, nom's parser result
 type:
 
 ```rust
-pub type IResult<I, O, E=(I,ErrorKind)> = Result<(I, O), Err<E>>;
+pub type IResult<I, O, E=(I, ErrorKind)> = Result<(I, O), Err<E>>;
 
 pub enum Err<E> {
-  Incomplete(Needed),
-  Error(E),
-  Failure(E),
-  }
+    Incomplete(Needed),
+    Error(E),
+    Failure(E),
+}
 ```
 
 This error type is completely generic in nom's combinators, so you can choose
@@ -35,21 +35,21 @@ combinators, defined as follows:
 
 ```rust
 pub trait ParseError<I>: Sized {
-  fn from_error_kind(input: I, kind: ErrorKind) -> Self;
+    fn from_error_kind(input: I, kind: ErrorKind) -> Self;
 
-  fn append(input: I, kind: ErrorKind, other: Self) -> Self;
+    fn append(input: I, kind: ErrorKind, other: Self) -> Self;
 
-  fn from_char(input: I, _: char) -> Self {
-    Self::from_error_kind(input, ErrorKind::Char)
-  }
+    fn from_char(input: I, _: char) -> Self {
+        Self::from_error_kind(input, ErrorKind::Char)
+    }
 
-  fn or(self, other: Self) -> Self {
-  other
-  }
+    fn or(self, other: Self) -> Self {
+        other
+    }
 
-  fn add_context(_input: I, _ctx: &'static str, other: Self) -> Self {
-    other
-  }
+    fn add_context(_input: I, _ctx: &'static str, other: Self) -> Self {
+        other
+    }
 }
 ```
 
@@ -98,7 +98,7 @@ error. Here is what it could return:
 
 ```rust
 fn f(i: &[u8]) -> IResult<&[u8], &[u8]> {
-  dbg_dmp(tag("abcd"), "tag")(i)
+    dbg_dmp(tag("abcd"), "tag")(i)
 }
 
 let a = &b"efghijkl"[..];
@@ -119,28 +119,28 @@ You can go further with the [nom-trace crate](https://github.com/rust-bakery/nom
 declare_trace!();
 
 pub fn main() {
-  named!(parser<&str, Vec<&str>>,
-    //wrap a parser with tr!() to add a trace point
-    tr!(preceded!(
-      tr!(tag!("data: ")),
-      tr!(delimited!(
-        tag!("("),
-        separated_list!(
-          tr!(tag!(",")),
-          tr!(nom::digit)
-        ),
-        tr!(tag!(")"))
-      ))
-    ))
-  );
-
-  println!("parsed: {:?}", parser("data: (1,2,3)"));
-
-  // prints the last parser trace
-  print_trace!();
-
-  // the list of trace events can be cleared
-  reset_trace!();
+    named!(parser<&str, Vec<&str>>,
+        //wrap a parser with tr!() to add a trace point
+        tr!(preceded!(
+        tr!(tag!("data: ")),
+        tr!(delimited!(
+            tag!("("),
+            separated_list!(
+            tr!(tag!(",")),
+            tr!(nom::digit)
+            ),
+            tr!(tag!(")"))
+        ))
+        ))
+    );
+
+    println!("parsed: {:?}", parser("data: (1,2,3)"));
+
+    // prints the last parser trace
+    print_trace!();
+
+    // the list of trace events can be cleared
+    reset_trace!();
 }
 ```
 
diff --git a/examples/json.rs b/examples/json.rs
index f703ad4..d51b9c7 100644
--- a/examples/json.rs
+++ b/examples/json.rs
@@ -110,7 +110,7 @@ fn array<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Vec<JsonVal
 }
 
 fn key_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (&'a str, JsonValue), E> {
-separated_pair(preceded(sp, string), cut(preceded(sp, char(':'))), value)(i)
+  separated_pair(preceded(sp, string), cut(preceded(sp, char(':'))), value)(i)
 }
 
 fn hash<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, HashMap<String, JsonValue>, E> {
diff --git a/src/character/complete.rs b/src/character/complete.rs
index 1f3cd35..af4adb9 100644
--- a/src/character/complete.rs
+++ b/src/character/complete.rs
@@ -365,6 +365,16 @@ where
 }
 
 /// Recognizes zero or more ASCII numerical characters: 0-9
+///
+/// *complete version*: Will return an error if there's not enough input data,
+/// or the whole input if no terminating token is found (a non digit character).
+///
+/// # Example
+///
+/// ```
+/// # use nom::{Err, error::ErrorKind, IResult, Needed};
+/// # use nom::character::complete::digit0;
+/// # fn main() {
 /// fn parser(input: &str) -> IResult<&str, &str> {
 ///     digit0(input)
 /// }
diff --git a/src/error.rs b/src/error.rs
index 91baa9f..b02aa19 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -187,7 +187,11 @@ pub fn convert_error(input: &str, e: VerboseError<&str>) -> crate::lib::std::str
             result += &repeat(' ').take(column).collect::<crate::lib::std::string::String>();
           }
           result += "^\n";
-          result += &format!("expected '{}', found {}\n\n", c, substring.chars().next().unwrap());
+          if let Some(actual) = substring.chars().next() {
+            result += &format!("expected '{}', found {}\n\n", c, actual);
+          } else {
+            result += &format!("expected '{}', got end of input\n\n", c);
+          }
         }
         VerboseErrorKind::Context(s) => {
           result += &format!("{}: at line {}, in {}:\n", i, line, s);
@@ -535,7 +539,6 @@ macro_rules! flat_map(
   );
 );
 
-
 #[cfg(test)]
 #[cfg(feature = "alloc")]
 mod tests {
@@ -547,6 +550,13 @@ mod tests {
     let input = "";
 
     let result: IResult<_, _, VerboseError<&str>> = char('x')(input);
+    let err = match result.unwrap_err() {
+      Err::Error(e) => e,
+      _ => unreachable!(),
+    };
+
+    let msg = convert_error(&input, err);
+    assert_eq!(msg, "0: expected \'x\', got empty input\n\n");
   }
 }
 
diff --git a/src/internal.rs b/src/internal.rs
index e53e4c2..271d69f 100644
--- a/src/internal.rs
+++ b/src/internal.rs
@@ -84,7 +84,36 @@ impl<E> Err<E> {
   }
 }
 
-/*
+impl<T> Err<(T, ErrorKind)> {
+  /// maps `Err<(T, ErrorKind)>` to `Err<(U, ErrorKind)>` with the given F: T -> U
+  pub fn map_input<U, F>(self, f: F) -> Err<(U, ErrorKind)>
+    where F: FnOnce(T) -> U {
+    match self {
+      Err::Incomplete(n) => Err::Incomplete(n),
+      Err::Failure((input, k)) => Err::Failure((f(input), k)),
+      Err::Error((input, k)) => Err::Error((f(input), k)),
+    }
+  }
+}
+
+#[cfg(feature = "std")]
+impl Err<(&[u8], ErrorKind)> {
+  /// Obtaining ownership
+  pub fn to_owned(self) -> Err<(Vec<u8>, ErrorKind)> {
+    self.map_input(ToOwned::to_owned)
+  }
+}
+
+#[cfg(feature = "std")]
+impl Err<(&str, ErrorKind)> {
+  /// automatically converts between errors if the underlying type supports it
+  pub fn to_owned(self) -> Err<(String, ErrorKind)> {
+    self.map_input(ToOwned::to_owned)
+  }
+}
+
+impl<E: Eq> Eq for Err<E> {}
+
 #[cfg(feature = "std")]
 use std::fmt;
 
@@ -94,7 +123,12 @@ where
   E: fmt::Debug,
 {
   fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-    write!(f, "{:?}", self)
+    match self {
+      Err::Incomplete(Needed::Size(u)) => write!(f, "Parsing requires {} bytes/chars", u),
+      Err::Incomplete(Needed::Unknown) => write!(f, "Parsing requires more data"),
+      Err::Failure(c) => write!(f, "Parsing Failure: {:?}", c),
+      Err::Error(c) => write!(f, "Parsing Error: {:?}", c),
+    }
   }
 }
 
@@ -104,21 +138,12 @@ use std::error::Error;
 #[cfg(feature = "std")]
 impl<E> Error for Err<E>
 where
-  I: fmt::Debug,
   E: fmt::Debug,
 {
-  fn description(&self) -> &str {
-    match self {
-      &Err::Incomplete(..) => "there was not enough data",
-      &Err::Error(Context::Code(_, ref error_kind)) | &Err::Failure(Context::Code(_, ref error_kind)) => error_kind.description(),
-    }
-  }
-
-  fn cause(&self) -> Option<&Error> {
-    None
+  fn source(&self) -> Option<&(dyn Error + 'static)> {
+    None // no underlying error
   }
 }
-*/
 
 #[cfg(test)]
 mod tests {
diff --git a/src/multi/macros.rs b/src/multi/macros.rs
index 4d884b4..10b66b8 100644
--- a/src/multi/macros.rs
+++ b/src/multi/macros.rs
@@ -356,8 +356,8 @@ macro_rules! length_count(
 
 /// `length_data!(I -> IResult<I, nb>) => O`
 ///
-/// `length_data` gets a number from the first parser, than takes a subslice of the input
-/// of that size, and returns that subslice
+/// `length_data` gets a number from the first parser, then takes a subslice of the input
+/// of that size and returns that subslice
 ///
 /// ```rust
 /// # #[macro_use] extern crate nom;
diff --git a/src/multi/mod.rs b/src/multi/mod.rs
index 7e86112..5c04cca 100644
--- a/src/multi/mod.rs
+++ b/src/multi/mod.rs
@@ -893,11 +893,10 @@ where
   fold_many_m_n(m, n, f, init, g)(i)
 }
 
-/// Gets a number from the first parser,
-/// takes a subslice of the input of that size,
-/// then applies the second parser on that subslice.
-/// If the second parser returns Incomplete,
-/// length_value will return an error.
+/// Gets a number from the parser and returns a
+/// subslice of the input of that size.
+/// If the parser returns Incomplete,
+/// length_data will return an error.
 /// # Arguments
 /// * `f` The parser to apply.
 /// ```rust


Test patch:
diff --git a/tests/issues.rs b/tests/issues.rs
index 094b47c..7b7f032 100644
--- a/tests/issues.rs
+++ b/tests/issues.rs
@@ -298,3 +298,21 @@ fn issue_many_m_n_with_zeros() {
     let parser = many_m_n::<_, _, (), _>(0, 0, char('a'));
     assert_eq!(parser("aaa"), Ok(("aaa", vec!())));
 }
+
+#[test]
+fn issue_1027_convert_error_panic_nonempty() {
+  use nom::error::{VerboseError, convert_error};
+  use nom::sequence::pair;
+  use nom::character::complete::char;
+
+  let input = "a";
+
+  let result: IResult<_, _, VerboseError<&str>> = pair(char('a'), char('b'))(input);
+  let err = match result.unwrap_err() {
+    Err::Error(e) => e,
+    _ => unreachable!(),
+  };
+
+  let msg = convert_error(&input, err);
+  assert_eq!(msg, "0: at line 0:\na\n ^\nexpected \'b\', got end of input\n\n");
+}
\ No newline at end of file