From 424e3972f3ec7c5780dee5cec9479268b43572cb Mon Sep 17 00:00:00 2001 From: Tom Kunc Date: Tue, 3 May 2022 00:05:00 +1000 Subject: [PATCH 1/3] Initial commit for feedback. --- doc/nom-guide/.gitignore | 1 + doc/nom-guide/book.toml | 6 ++ doc/nom-guide/scripts/build.sh | 6 ++ doc/nom-guide/src/SUMMARY.md | 17 ++++ doc/nom-guide/src/chapter_1.md | 82 ++++++++++++++++++++ doc/nom-guide/src/chapter_2.md | 105 +++++++++++++++++++++++++ doc/nom-guide/src/chapter_3.md | 124 ++++++++++++++++++++++++++++++ doc/nom-guide/src/chapter_4.md | 1 + doc/nom-guide/src/introduction.md | 31 ++++++++ doc/nom-guide/src/todo.md | 1 + 10 files changed, 374 insertions(+) create mode 100644 doc/nom-guide/.gitignore create mode 100644 doc/nom-guide/book.toml create mode 100755 doc/nom-guide/scripts/build.sh create mode 100644 doc/nom-guide/src/SUMMARY.md create mode 100644 doc/nom-guide/src/chapter_1.md create mode 100644 doc/nom-guide/src/chapter_2.md create mode 100644 doc/nom-guide/src/chapter_3.md create mode 100644 doc/nom-guide/src/chapter_4.md create mode 100644 doc/nom-guide/src/introduction.md create mode 100644 doc/nom-guide/src/todo.md diff --git a/doc/nom-guide/.gitignore b/doc/nom-guide/.gitignore new file mode 100644 index 000000000..7585238ef --- /dev/null +++ b/doc/nom-guide/.gitignore @@ -0,0 +1 @@ +book diff --git a/doc/nom-guide/book.toml b/doc/nom-guide/book.toml new file mode 100644 index 000000000..500119ad7 --- /dev/null +++ b/doc/nom-guide/book.toml @@ -0,0 +1,6 @@ +[book] +authors = ["Tom Kunc"] +language = "en" +multilingual = false +src = "src" +title = "The Nom Guide (Nominomicon)" diff --git a/doc/nom-guide/scripts/build.sh b/doc/nom-guide/scripts/build.sh new file mode 100755 index 000000000..1a0764a22 --- /dev/null +++ b/doc/nom-guide/scripts/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash +BOOK_ROOT_PATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )/.." +cd $BOOK_ROOT_PATH + +[[ ! -e $BOOK_ROOT_PATH/../../target ]] && (cd ../../ && cargo build) +mdbook test -L $(cd ../../ && pwd)/target/debug/deps/ diff --git a/doc/nom-guide/src/SUMMARY.md b/doc/nom-guide/src/SUMMARY.md new file mode 100644 index 000000000..65983d6c9 --- /dev/null +++ b/doc/nom-guide/src/SUMMARY.md @@ -0,0 +1,17 @@ +## Summary + +[Introduction](./introduction.md) + +- [Chapter 1: The Nom Way](./chapter_1.md) +- [Chapter 2: Tags and Character Classes](./chapter_2.md) +- [Chapter 3: Alternatives and Composition](./chapter_3.md) +- [Chapter 4: Custom Outputs from Functions](./chapter_4.md) +- [Chapter 5: Parsing Functions](./todo.md) +- [Chapter 6: Repeated Inputs](./todo.md) +- [Chapter 7: Simple Exercises](./todo.md) +- [Chapter 8: Custom Errors in Functions](./todo.md) +- [Chapter 9: Modifiers](./todo.md) +- [Chapter 10: Characters vs. Bytes](./todo.md) +- [Chapter 11: Streaming vs. Complete](./todo.md) +- [Chapter 12: Complex Exercises](./todo.md) + diff --git a/doc/nom-guide/src/chapter_1.md b/doc/nom-guide/src/chapter_1.md new file mode 100644 index 000000000..bb9a00042 --- /dev/null +++ b/doc/nom-guide/src/chapter_1.md @@ -0,0 +1,82 @@ +# Chapter 1: The Nom Way + +First of all, we need to understand the way that regexes and nom think about +parsing. + +A regex, in a sense, controls its whole input. Given a single input, +it decides that either some text **did** match the regex, or it **didn't**. + +```text + ┌────────┐ ┌─► Some text that matched the regex + my input───►│my regex├──►either──┤ + └────────┘ └─► None +``` + +As we mentioned above, Nom parsers are designed to be combined. +This makes the assumption that a regex controls its entire input +more difficult to maintain. So, there are three important changes +required to our mental model of a regex. + +1. Rather than just returning the text that matched + the regex, Nom tells you *both* what it parsed, and what is left + to parse. + +2. Additionally, to help with combining parsers, Nom also gives you + error information about your parser. We'll talk about this more later, + for now let's assume it's "basically" the same as the `None` we have above. + + Points 1 and 2 are illustrated in the diagram below: + +```text + ┌─► Ok( + │ text that the parser didn't touch, + │ text that matched the regex + │ ) + ┌─────────┐ │ + my input───►│my parser├──►either──┤ + └─────────┘ └─► Err(...) +``` + +3. Lastly, Nom parsers are normally anchored to the beginning of their input. + In other words, if you converted a Nom parser to regex, it would generally + begin with `/^/`. This is sensible, because it means that nom parsers must + (conceptually) be sequential -- your parser isn't going to jump + ahead and start parsing the middle of the line. + + +To represent this model of the world, nom uses the `IResult<(I, O)>` type. +The `Ok` variant has a tuple of `(remaining_input: I, output: O)`; +The `Err` variant stores an error. You can import that from: + +```rust +# extern crate nom; +use nom::IResult; +``` + +The simplest parser we can write is one which successfully does nothing. +In other words, the regex `/^/`. + +This parser should take in an `&str`. + - Since it is supposed to succeed, we know it will return the Ok Variant. + - Since it does nothing to our input, the remaining input is the same as the input. + - Since it doesn't do anything, it also should just return the unit type. + + +In other words, this code should be equivalent to the regex `/^/`. + +```rust +# extern crate nom; +# use nom::IResult; + +pub fn do_nothing_parser(input: &str) -> IResult<&str, ()> { + Ok((input, ())) +} + +match do_nothing_parser("my_input") { + Ok((remaining_input, output)) => { + assert_eq!(remaining_input, "my_input"); + assert_eq!(output, ()); + }, + Err(_) => unreachable!() +} +``` diff --git a/doc/nom-guide/src/chapter_2.md b/doc/nom-guide/src/chapter_2.md new file mode 100644 index 000000000..2d66c64bc --- /dev/null +++ b/doc/nom-guide/src/chapter_2.md @@ -0,0 +1,105 @@ +# Chapter 2: Tags and Character Classes + +The simplest _useful_ regex you can write is one which +has no special characters, it just matches a string. + +Imagine, for example, the regex `/abc/`. It simply matches when the string +`"abc"` occurs. + +In `nom`, we call a simple collection of bytes a tag. Because +these are so common, there already exists a function called `tag()`. +This function returns a parser for a given string. + +
+ **Warning**: `nom` has multiple different definitions of `tag`, make sure you use this one for the
+ moment!
+
+ +```rust +# extern crate nom; +pub use nom::bytes::complete::tag; +``` + +For example, the regex `/abc/` (really, the regex `/^abc/`) +could be represented as `tag("abc")`. + +Note, that the function `tag` will return +another function, namely, a parser for the tag you requested. + +Below, we see a function using this: + +```rust +# extern crate nom; +# pub use nom::bytes::complete::tag; +# pub use nom::IResult; + +fn parse_input(input: &str) -> IResult<&str, &str> { + // note that this is really creating a function, the parser for abc + // vvvvv + // which is then called here, returning an IResult<&str, &str> + // vvvvv + tag("abc")(input) +} + + let ok_input = "abcWorld"; + + match parse_input(ok_input) { + Ok((leftover_input, output)) => { + assert_eq!(leftover_input, "World"); + assert_eq!(output, "abc"); + }, + Err(_) => unreachable!() + } + + let err_input = "defWorld"; + match parse_input(err_input) { + Ok((leftover_input, output)) => unreachable!(), + Err(_) => assert!(true), + } +``` + +If you'd like to, you can also check case insensitive `/tag/i` +with the `tag_case_insensitive`. + +## Character Classes + +Tags are incredibly useful, but they are also incredibly restrictive. +The other end of Nom's functionality is pre-written parsers that allow us to accept any of a group of characters, +rather than just accepting characters in a defined sequence. + +Here is a selection of them: + +- [`alpha0`](https://docs.rs/nom/latest/nom/character/complete/fn.alpha0.html): Recognizes zero or more lowercase and uppercase alphabetic characters: `/[a-zA-Z]/`. [`alpha1`](https://docs.rs/nom/latest/nom/character/complete/fn.alpha1.html) does the same but returns at least one character +- [`alphanumeric0`](https://docs.rs/nom/latest/nom/character/complete/fn.alphanumeric0.html): Recognizes zero or more numerical and alphabetic characters: `/[0-9a-zA-Z]/`. [`alphanumeric1`](https://docs.rs/nom/latest/nom/character/complete/fn.alphanumeric1.html) does the same but returns at least one character +- [`digit0`](https://docs.rs/nom/latest/nom/character/complete/fn.digit0.html): Recognizes zero or more numerical characters: `/[0-9]/`. [`digit1`](https://docs.rs/nom/latest/nom/character/complete/fn.digit1.html) does the same but returns at least one character +- [`multispace0`](https://docs.rs/nom/latest/nom/character/complete/fn.multispace0.html): Recognizes zero or more spaces, tabs, carriage returns and line feeds. [`multispace1`](https://docs.rs/nom/latest/nom/character/complete/fn.multispace1.html) does the same but returns at least one character +- [`space0`](https://docs.rs/nom/latest/nom/character/complete/fn.space0.html): Recognizes zero or more spaces and tabs. [`space1`](https://docs.rs/nom/latest/nom/character/complete/fn.space1.html) does the same but returns at least one character +- [`line_ending`](https://docs.rs/nom/latest/nom/character/complete/fn.line_ending.html): Recognizes an end of line (both `\n` and `\r\n`) +- [`newline`](https://docs.rs/nom/latest/nom/character/complete/fn.newline.html): Matches a newline character `\n` +- [`tab`](https://docs.rs/nom/latest/nom/character/complete/fn.tab.html): Matches a tab character `\t` + + +We can use these in +```rust +# extern crate nom; +# pub use nom::IResult; +pub use nom::character::complete::alpha0; +fn parser(input: &str) -> IResult<&str, &str> { + alpha0(input) +} + + let ok_input = "abc123"; + match parser(ok_input) { + Ok((remaining, letters)) => { + assert_eq!(remaining, "123"); + assert_eq!(letters, "abc"); + }, + Err(_) => unreachable!() + } + +``` + +One important note is that, due to the type signature of these functions, +it is generally best to use them within a function that returns an `IResult`. + +*TODO* : Better explaination of why. diff --git a/doc/nom-guide/src/chapter_3.md b/doc/nom-guide/src/chapter_3.md new file mode 100644 index 000000000..03cfcfd10 --- /dev/null +++ b/doc/nom-guide/src/chapter_3.md @@ -0,0 +1,124 @@ +# Chapter 3: Alternatives and Composition + +In the last chapter, we saw how to convert a simple regex into a nom parser. +In this chapter, we explore features two other very important features of Nom, +alternatives, and composition. + +## Alternatives + +In regex, we can write `/(^abc|^def)/`, which means "match either `/^abc/` or `/^def/`". +Nom gives us a similar ability through the `alt()` combinator. + +```rust +# extern crate nom; +use nom::branch::alt; +``` + +The `alt()` combinator will execute each parser in a tuple until it finds one +that does not error. If all error, then by default you are given the error from +the last error. +We can see a basic example of `alt()` below. + +```rust +# extern crate nom; +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::IResult; + +fn parse_abc_or_def(input: &str) -> IResult<&str, &str> { + alt(( + tag("abc"), + tag("def") + ))(input) +} + + match parse_abc_or_def("abcWorld") { + Ok((leftover_input, output)) => { + assert_eq!(leftover_input, "World"); + assert_eq!(output, "abc"); + }, + Err(_) => unreachable!() + } + + match parse_abc_or_def("ghiWorld") { + Ok((leftover_input, output)) => unreachable!(), + Err(_) => assert!(true), + } +``` + +## Composition + +Now that we can create more interesting regexes, we can compose them together. +The simplest way to do this is just to evaluate them in sequence: + +```rust +# extern crate nom; +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::IResult; + +fn parse_abc(input: &str) -> IResult<&str, &str> { + tag("abc")(input) +} +fn parse_def_or_ghi(input: &str) -> IResult<&str, &str> { + alt(( + tag("def"), + tag("ghi") + ))(input) +} + + let input = "abcghi"; + if let Ok((remainder, abc)) = parse_abc(input) { + if let Ok((remainder, def_or_ghi)) = parse_def_or_ghi(remainder) { + println!("first parsed: {abc}; then parsed: {def_or_ghi};"); + } + } + +``` + +Composing tags is such a common requirement that, in fact, Nom has a few built in +combinators to do it. The simplest of these is `tuple()`. The `tuple()` combinator takes a tuple of parsers, +and either returns `Ok` with a tuple of all of their successful parses, or it +returns the `Err` of the first failed parser. + +```rust +# extern crate nom; +use nom::branch::alt; +use nom::bytes::complete::{tag}; +use nom::character::complete::{digit1}; +use nom::IResult; + +fn parse_numbers_or_abc(input: &str) -> IResult<&str, &str> { + alt(( + tag("abc"), + digit1 + ))(input) +} + + + let input = "abc"; + let parsed_input = parse_numbers_or_abc(input); + match parsed_input { + Ok((_, matched_str)) => assert_eq!(matched_str, "abc"), + Err(_) => unreachable!() + } + + + let input = "def"; + let parsed_input = parse_numbers_or_abc(input); + match parsed_input { + Ok(_) => unreachable!(), + Err(_) => assert!(true) + } +``` + + +## Extra Nom Tools + +After using `alt()` and `tuple()`, you might also be interested in the `permutation()` parser, which +requires all of the parsers it contains to succeed, but in any order. + +```rust +# extern crate nom; +use nom::branch::permutation; +``` diff --git a/doc/nom-guide/src/chapter_4.md b/doc/nom-guide/src/chapter_4.md new file mode 100644 index 000000000..989179ccc --- /dev/null +++ b/doc/nom-guide/src/chapter_4.md @@ -0,0 +1 @@ +# Chapter 4: Custom Outputs from Functions diff --git a/doc/nom-guide/src/introduction.md b/doc/nom-guide/src/introduction.md new file mode 100644 index 000000000..19733e4c2 --- /dev/null +++ b/doc/nom-guide/src/introduction.md @@ -0,0 +1,31 @@ +# The Nom Guide + +Welcome to The Nom Guide (or, the nominomicon); a guide to using the Nom parser for great good. +This guide is written to take you from an understanding of Regular Expressions, to an understanding +of Nom. + +This guide assumes that you are: + - Wanting to learn Nom, + - Already familiar with regular expressions (at least, somewhat), and + - Already familiar with Rust. + +Nom is a parser-combinator library. In other words, it gives you tools to define: + - "parsers" (a function that takes an input, and gives back an output), and + - "combinators" (functions that take parsers, and _combine_ them together!). + +By combining parsers with combinators, you can build complex parsers up from +simpler ones. These complex parsers are enough to understand HTML, mkv or Python! + +Before we set off, it's important to list some caveats: + - This guide is for Nom7. Nom has undergone significant changes, so if + you are searching for documentation or StackOverflow answers, you may + find older documentation. Some common indicators that it is an old version are: + - Documentation older than 21st August, 2021 + - Use of the `named!` macro + - Use of `CompleteStr` or `CompleteByteArray`. + - Nom can parse (almost) anything; but this guide will focus entirely on parsing + complete `&str` into things. + +And finally, some nomenclature: + - In this guide, regexes will be denoted inside slashes (for example `/abc/`) + to distinguish them from regular strings. diff --git a/doc/nom-guide/src/todo.md b/doc/nom-guide/src/todo.md new file mode 100644 index 000000000..9bdc15ed7 --- /dev/null +++ b/doc/nom-guide/src/todo.md @@ -0,0 +1 @@ +# To Be Completed From fd4a9d3261259be19ce4e5e8a9378538458da98e Mon Sep 17 00:00:00 2001 From: Tom Kunc Date: Fri, 1 Jul 2022 21:07:09 +1000 Subject: [PATCH 2/3] Add chapters 4 thru 7 --- doc/nom-guide/scripts/build.sh | 5 + doc/nom-guide/src/SUMMARY.md | 14 ++- doc/nom-guide/src/chapter_1.md | 91 ++++++++--------- doc/nom-guide/src/chapter_2.md | 84 +++++++++------- doc/nom-guide/src/chapter_3.md | 104 +++++++++++-------- doc/nom-guide/src/chapter_4.md | 162 +++++++++++++++++++++++++++++- doc/nom-guide/src/chapter_5.md | 64 ++++++++++++ doc/nom-guide/src/chapter_6.md | 39 +++++++ doc/nom-guide/src/chapter_7.md | 10 ++ doc/nom-guide/src/introduction.md | 17 ++-- 10 files changed, 439 insertions(+), 151 deletions(-) create mode 100644 doc/nom-guide/src/chapter_5.md create mode 100644 doc/nom-guide/src/chapter_6.md create mode 100644 doc/nom-guide/src/chapter_7.md diff --git a/doc/nom-guide/scripts/build.sh b/doc/nom-guide/scripts/build.sh index 1a0764a22..f08c34e28 100755 --- a/doc/nom-guide/scripts/build.sh +++ b/doc/nom-guide/scripts/build.sh @@ -1,6 +1,11 @@ #!/bin/bash +command="build" + +[[ "$1" == "serve" ]] && command="serve" + BOOK_ROOT_PATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )/.." cd $BOOK_ROOT_PATH [[ ! -e $BOOK_ROOT_PATH/../../target ]] && (cd ../../ && cargo build) mdbook test -L $(cd ../../ && pwd)/target/debug/deps/ +mdbook $command diff --git a/doc/nom-guide/src/SUMMARY.md b/doc/nom-guide/src/SUMMARY.md index 65983d6c9..e38d56aea 100644 --- a/doc/nom-guide/src/SUMMARY.md +++ b/doc/nom-guide/src/SUMMARY.md @@ -6,12 +6,10 @@ - [Chapter 2: Tags and Character Classes](./chapter_2.md) - [Chapter 3: Alternatives and Composition](./chapter_3.md) - [Chapter 4: Custom Outputs from Functions](./chapter_4.md) -- [Chapter 5: Parsing Functions](./todo.md) -- [Chapter 6: Repeated Inputs](./todo.md) -- [Chapter 7: Simple Exercises](./todo.md) -- [Chapter 8: Custom Errors in Functions](./todo.md) -- [Chapter 9: Modifiers](./todo.md) -- [Chapter 10: Characters vs. Bytes](./todo.md) -- [Chapter 11: Streaming vs. Complete](./todo.md) -- [Chapter 12: Complex Exercises](./todo.md) +- [Chapter 5: Repeating with Predicates](./chapter_5.md) +- [Chapter 6: Repeating Parsers](./chapter_6.md) +- [Chapter 7: Using Errors from Outside Nom](./chapter_7.md) +- [Chapter 8: Streaming vs. Complete](./todo.md) +- [Chapter 9: Characters vs. Bytes](./todo.md) +- [Chapter 10: Exercises and Further Reading](./todo.md) diff --git a/doc/nom-guide/src/chapter_1.md b/doc/nom-guide/src/chapter_1.md index bb9a00042..d4813923e 100644 --- a/doc/nom-guide/src/chapter_1.md +++ b/doc/nom-guide/src/chapter_1.md @@ -1,82 +1,75 @@ # Chapter 1: The Nom Way -First of all, we need to understand the way that regexes and nom think about -parsing. - -A regex, in a sense, controls its whole input. Given a single input, -it decides that either some text **did** match the regex, or it **didn't**. - -```text - ┌────────┐ ┌─► Some text that matched the regex - my input───►│my regex├──►either──┤ - └────────┘ └─► None -``` - -As we mentioned above, Nom parsers are designed to be combined. -This makes the assumption that a regex controls its entire input -more difficult to maintain. So, there are three important changes -required to our mental model of a regex. - -1. Rather than just returning the text that matched - the regex, Nom tells you *both* what it parsed, and what is left - to parse. - -2. Additionally, to help with combining parsers, Nom also gives you - error information about your parser. We'll talk about this more later, - for now let's assume it's "basically" the same as the `None` we have above. - - Points 1 and 2 are illustrated in the diagram below: +First of all, we need to understand the way that nom thinks about parsing. +As discussed in the introduction, nom lets us build simple parsers, and +then combine them (using "combinators"). + +Let's discuss what a "parser" actually does. A parser takes an input and returns +a result, where: + - `Ok` indicates the parser successfully found what it was looking for; or + - `Err` indicates the parser could not find what it was looking for. + +Parsers do more than just return a binary "success"/"failure" code. If +the parser was successful, then it will return a tuple. The first field of the +tuple will contain everything the parser did not process. The second will contain +everything the parser processed. The idea is that a parser can happily parse the first +*part* of an input, without being able to parse the whole thing. + +If the parser failed, then there are multiple errors that could be returned. +For simplicity, however, in the next chapters we will leave these unexplored. ```text ┌─► Ok( - │ text that the parser didn't touch, - │ text that matched the regex + │ what the parser didn't touch, + │ what matched the regex │ ) ┌─────────┐ │ my input───►│my parser├──►either──┤ └─────────┘ └─► Err(...) ``` -3. Lastly, Nom parsers are normally anchored to the beginning of their input. - In other words, if you converted a Nom parser to regex, it would generally - begin with `/^/`. This is sensible, because it means that nom parsers must - (conceptually) be sequential -- your parser isn't going to jump - ahead and start parsing the middle of the line. - To represent this model of the world, nom uses the `IResult<(I, O)>` type. The `Ok` variant has a tuple of `(remaining_input: I, output: O)`; -The `Err` variant stores an error. You can import that from: +whereas the `Err` variant stores an error. + +You can import that from: ```rust # extern crate nom; use nom::IResult; ``` +You'll note that `I` and `O` are parameterized -- while most of the examples in this book +will be with `&str` (i.e. parsing a string); they do not have to be strings; nor do they +have to be the same type (consider the simple example where `I = &str`, and `O = u64` -- this +parses a string into an unsigned integer.) + +Let's write our first parser! The simplest parser we can write is one which successfully does nothing. -In other words, the regex `/^/`. -This parser should take in an `&str`. - - Since it is supposed to succeed, we know it will return the Ok Variant. - - Since it does nothing to our input, the remaining input is the same as the input. - - Since it doesn't do anything, it also should just return the unit type. +This parser should take in an `&str`: + - Since it is supposed to succeed, we know it will return the Ok Variant. + - Since it does nothing to our input, the remaining input is the same as the input. + - Since it doesn't parse anything, it also should just return an empty string. -In other words, this code should be equivalent to the regex `/^/`. ```rust # extern crate nom; # use nom::IResult; +# use std::error::Error; -pub fn do_nothing_parser(input: &str) -> IResult<&str, ()> { - Ok((input, ())) +pub fn do_nothing_parser(input: &str) -> IResult<&str, &str> { + Ok((input, "")) } -match do_nothing_parser("my_input") { - Ok((remaining_input, output)) => { - assert_eq!(remaining_input, "my_input"); - assert_eq!(output, ()); - }, - Err(_) => unreachable!() +fn main() -> Result<(), Box> { + let (remaining_input, output) = do_nothing_parser("my_input")?; + assert_eq!(remaining_input, "my_input"); + assert_eq!(output, ""); +# Ok(()) } ``` + +It's that easy! diff --git a/doc/nom-guide/src/chapter_2.md b/doc/nom-guide/src/chapter_2.md index 2d66c64bc..043a37bbd 100644 --- a/doc/nom-guide/src/chapter_2.md +++ b/doc/nom-guide/src/chapter_2.md @@ -1,37 +1,50 @@ # Chapter 2: Tags and Character Classes -The simplest _useful_ regex you can write is one which +The simplest _useful_ parser you can write is one which has no special characters, it just matches a string. -Imagine, for example, the regex `/abc/`. It simply matches when the string -`"abc"` occurs. - In `nom`, we call a simple collection of bytes a tag. Because these are so common, there already exists a function called `tag()`. This function returns a parser for a given string. -
  **Warning**: `nom` has multiple different definitions of `tag`, make sure you use this one for the
  moment!
-
-```rust +```rust,ignore # extern crate nom; pub use nom::bytes::complete::tag; ``` -For example, the regex `/abc/` (really, the regex `/^abc/`) -could be represented as `tag("abc")`. +For example, code to parse the string `"abc"` could be represented as `tag("abc")`. + +If you have not programmed in a language where functions are values, the type signature of them +tag function might be a surprise: + +```rust,ignore +pub fn tag>( + tag: T +) -> impl Fn(Input) -> IResult where + Input: InputTake + Compare, + T: InputLength + Clone, +``` + +Or, for the case where `Input` and `T` are both `&str`, and simplifying slightly: -Note, that the function `tag` will return -another function, namely, a parser for the tag you requested. +```rust,ignore +fn tag(tag: &str) -> (impl Fn(&str) -> IResult<&str, Error>) +``` + +In other words, this function `tag` *returns a function*. The function it returns is a +parser, taking a `&str` and returning an `IResult`. Functions creating parsers and +returning them is a common pattern in Nom, so it is useful to call out. -Below, we see a function using this: +Below, we have implemented a function that uses `tag`. ```rust # extern crate nom; # pub use nom::bytes::complete::tag; # pub use nom::IResult; +# use std::error::Error; fn parse_input(input: &str) -> IResult<&str, &str> { // note that this is really creating a function, the parser for abc @@ -41,25 +54,18 @@ fn parse_input(input: &str) -> IResult<&str, &str> { tag("abc")(input) } - let ok_input = "abcWorld"; - - match parse_input(ok_input) { - Ok((leftover_input, output)) => { - assert_eq!(leftover_input, "World"); - assert_eq!(output, "abc"); - }, - Err(_) => unreachable!() - } - - let err_input = "defWorld"; - match parse_input(err_input) { - Ok((leftover_input, output)) => unreachable!(), - Err(_) => assert!(true), - } +fn main() -> Result<(), Box> { + let (leftover_input, output) = parse_input("abcWorld")?; + assert_eq!(leftover_input, "World"); + assert_eq!(output, "abc"); + + assert!(parse_input("defWorld").is_err()); +# Ok(()) +} ``` -If you'd like to, you can also check case insensitive `/tag/i` -with the `tag_case_insensitive`. +If you'd like to, you can also check tags without case-sensitivity +with the [`tag_no_case`](https://docs.rs/nom/latest/nom/bytes/complete/fn.tag_no_case.html) function. ## Character Classes @@ -83,23 +89,23 @@ We can use these in ```rust # extern crate nom; # pub use nom::IResult; +# use std::error::Error; pub use nom::character::complete::alpha0; fn parser(input: &str) -> IResult<&str, &str> { alpha0(input) } - let ok_input = "abc123"; - match parser(ok_input) { - Ok((remaining, letters)) => { - assert_eq!(remaining, "123"); - assert_eq!(letters, "abc"); - }, - Err(_) => unreachable!() - } - +fn main() -> Result<(), Box> { + let (remaining, letters) = parser("abc123")?; + assert_eq!(remaining, "123"); + assert_eq!(letters, "abc"); + +# Ok(()) +} ``` One important note is that, due to the type signature of these functions, it is generally best to use them within a function that returns an `IResult`. -*TODO* : Better explaination of why. +If you don't, some of the information around the type of the `tag` function must be +manually specified, which can lead to verbose code or confusing errors. diff --git a/doc/nom-guide/src/chapter_3.md b/doc/nom-guide/src/chapter_3.md index 03cfcfd10..f42a92338 100644 --- a/doc/nom-guide/src/chapter_3.md +++ b/doc/nom-guide/src/chapter_3.md @@ -1,12 +1,16 @@ # Chapter 3: Alternatives and Composition -In the last chapter, we saw how to convert a simple regex into a nom parser. -In this chapter, we explore features two other very important features of Nom, -alternatives, and composition. +In the last chapter, we saw how to create simple parsers using the `tag` function; +and some of Nom's prebuilt parsers. + +In this chapter, we explore two other widely used features of Nom: +alternatives and composition. ## Alternatives -In regex, we can write `/(^abc|^def)/`, which means "match either `/^abc/` or `/^def/`". +Sometimes, we might want to choose between two parsers; and we're happy with +either being used. + Nom gives us a similar ability through the `alt()` combinator. ```rust @@ -17,6 +21,7 @@ use nom::branch::alt; The `alt()` combinator will execute each parser in a tuple until it finds one that does not error. If all error, then by default you are given the error from the last error. + We can see a basic example of `alt()` below. ```rust @@ -24,6 +29,7 @@ We can see a basic example of `alt()` below. use nom::branch::alt; use nom::bytes::complete::tag; use nom::IResult; +# use std::error::Error; fn parse_abc_or_def(input: &str) -> IResult<&str, &str> { alt(( @@ -32,18 +38,14 @@ fn parse_abc_or_def(input: &str) -> IResult<&str, &str> { ))(input) } - match parse_abc_or_def("abcWorld") { - Ok((leftover_input, output)) => { - assert_eq!(leftover_input, "World"); - assert_eq!(output, "abc"); - }, - Err(_) => unreachable!() - } - - match parse_abc_or_def("ghiWorld") { - Ok((leftover_input, output)) => unreachable!(), - Err(_) => assert!(true), - } +fn main() -> Result<(), Box> { + let (leftover_input, output) = parse_abc_or_def("abcWorld")?; + assert_eq!(leftover_input, "World"); + assert_eq!(output, "abc"); + + assert!(parse_abc_or_def("ghiWorld").is_err()); +# Ok(()) +} ``` ## Composition @@ -56,6 +58,7 @@ The simplest way to do this is just to evaluate them in sequence: use nom::branch::alt; use nom::bytes::complete::tag; use nom::IResult; +# use std::error::Error; fn parse_abc(input: &str) -> IResult<&str, &str> { tag("abc")(input) @@ -67,13 +70,14 @@ fn parse_def_or_ghi(input: &str) -> IResult<&str, &str> { ))(input) } +fn main() -> Result<(), Box> { let input = "abcghi"; - if let Ok((remainder, abc)) = parse_abc(input) { - if let Ok((remainder, def_or_ghi)) = parse_def_or_ghi(remainder) { - println!("first parsed: {abc}; then parsed: {def_or_ghi};"); - } - } + let (remainder, abc) = parse_abc(input)?; + let (remainder, def_or_ghi) = parse_def_or_ghi(remainder)?; + println!("first parsed: {abc}; then parsed: {def_or_ghi};"); +# Ok(()) +} ``` Composing tags is such a common requirement that, in fact, Nom has a few built in @@ -81,44 +85,58 @@ combinators to do it. The simplest of these is `tuple()`. The `tuple()` combinat and either returns `Ok` with a tuple of all of their successful parses, or it returns the `Err` of the first failed parser. +```rust +# extern crate nom; +use nom::sequence::tuple; +``` + + ```rust # extern crate nom; use nom::branch::alt; -use nom::bytes::complete::{tag}; +use nom::sequence::tuple; +use nom::bytes::complete::tag_no_case; use nom::character::complete::{digit1}; use nom::IResult; +# use std::error::Error; -fn parse_numbers_or_abc(input: &str) -> IResult<&str, &str> { +fn parse_base(input: &str) -> IResult<&str, &str> { alt(( - tag("abc"), - digit1 + tag_no_case("a"), + tag_no_case("t"), + tag_no_case("c"), + tag_no_case("g") ))(input) } +fn parse_pair(input: &str) -> IResult<&str, (&str, &str)> { + // the many_m_n combinator might also be appropriate here. + tuple(( + parse_base, + parse_base, + ))(input) +} - let input = "abc"; - let parsed_input = parse_numbers_or_abc(input); - match parsed_input { - Ok((_, matched_str)) => assert_eq!(matched_str, "abc"), - Err(_) => unreachable!() - } +fn main() -> Result<(), Box> { + let (remaining, parsed) = parse_pair("aTcG")?; + assert_eq!(parsed, ("a", "T")); + assert_eq!(remaining, "cG"); + assert!(parse_pair("Dct").is_err()); - let input = "def"; - let parsed_input = parse_numbers_or_abc(input); - match parsed_input { - Ok(_) => unreachable!(), - Err(_) => assert!(true) - } +# Ok(()) +} ``` ## Extra Nom Tools -After using `alt()` and `tuple()`, you might also be interested in the `permutation()` parser, which -requires all of the parsers it contains to succeed, but in any order. +After using `alt()` and `tuple()`, you might also be interested in a few other parsers that do similar things: -```rust -# extern crate nom; -use nom::branch::permutation; -``` +| combinator | usage | input | output | comment | +|---|---|---|---|---| +| [delimited](https://docs.rs/nom/latest/nom/sequence/fn.delimited.html) | `delimited(char('('), take(2), char(')'))` | `"(ab)cd"` | `Ok(("cd", "ab"))` || +| [preceded](https://docs.rs/nom/latest/nom/sequence/fn.preceded.html) | `preceded(tag("ab"), tag("XY"))` | `"abXYZ"` | `Ok(("Z", "XY"))` || +| [terminated](https://docs.rs/nom/latest/nom/sequence/fn.terminated.html) | `terminated(tag("ab"), tag("XY"))` | `"abXYZ"` | `Ok(("Z", "ab"))` || +| [pair](https://docs.rs/nom/latest/nom/sequence/fn.pair.html) | `pair(tag("ab"), tag("XY"))` | `"abXYZ"` | `Ok(("Z", ("ab", "XY")))` || +| [separated_pair](https://docs.rs/nom/latest/nom/sequence/fn.separated_pair.html) | `separated_pair(tag("hello"), char(','), tag("world"))` | `"hello,world!"` | `Ok(("!", ("hello", "world")))` || diff --git a/doc/nom-guide/src/chapter_4.md b/doc/nom-guide/src/chapter_4.md index 989179ccc..20bde8bde 100644 --- a/doc/nom-guide/src/chapter_4.md +++ b/doc/nom-guide/src/chapter_4.md @@ -1 +1,161 @@ -# Chapter 4: Custom Outputs from Functions +# Chapter 4: Parsers With Custom Return Types + +So far, we have seen mostly functions that take an `&str`, and return a +`IResult<&str, &str>`. Splitting strings into smaller strings is certainly useful, +but it's not the only thing Nom is capable of! + +A useful operation when parsing is to convert between types; for example +parsing from `&str` to another primitive, like `bool`. + +All we need to do for our parser to return a different type is to change +the second type parameter of `IResult` to the desired return type. +For example, to return a bool, return a `IResult<&str, bool>`. + +Recall that the first type parameter of the `IResult` is the input +type, so even if you're returning something different, if your input +is a `&str`, the first type argument of `IResult` should be also. + +Until you have read the chapter on Errors, we strongly suggest avoiding +the use of parsers built into Rust (like `str.parse`); as they require +special handling to work well with Nom. + +That said, one Nom-native way of doing a type conversion is to use the +[`value`](https://docs.rs/nom/latest/nom/combinator/fn.value.html) combinator +to convert from a successful parse to a particular value. + +The following code converts from a string containing `"true"` or `"false"`, +to the corresponding `bool`. + +```rust +# extern crate nom; +# use std::error::Error; +use nom::IResult; +use nom::bytes::complete::tag; +use nom::combinator::value; +use nom::branch::alt; + +fn parse_bool(input: &str) -> IResult<&str, bool> { + // either, parse `"true"` -> `true`; `"false"` -> `false`, or error. + alt(( + value(true, tag("true")), + value(false, tag("false")), + ))(input) +} + +fn main() -> Result<(), Box> { + // Parses the `"true"` out. + let (remaining, parsed) = parse_bool("true|false")?; + assert_eq!(parsed, true); + assert_eq!(remaining, "|false"); + + // If we forget about the "|", we get an error. + let parsing_error = parse_bool(remaining); + assert!(parsing_error.is_err()); + + // Skipping the first byte gives us `false`! + let (remaining, parsed) = parse_bool(&remaining[1..])?; + assert_eq!(parsed, false); + assert_eq!(remaining, ""); + + + +# Ok(()) +} +``` + +## Nom's in-built parser functions + +Nom has a wide array of parsers built in. Here is a list of +[parsers which recognize specific characters](https://docs.rs/nom/latest/nom/character/complete/index.html). + +Some of them we have seen before in Chapter 2, but now we also can try out the parsers that return different +types, like `i32`. An example of this parser is shown in the next section. + +## Building a More Complex Example + +A more complex example of parsing custom types might be parsing a 2D coordinate. + +Let us try to figure out how to design this. + + - We know that we want to take a string, like `"(3, -2)"`, and convert into + a `Coordinate` struct. + - We can split this into three parts: + +```ignore +(vvvvvvvvvvvvv) # The outer brackets. + vvvv , vvvv # The comma, separating values. + 3 -2 # The actual integers. +``` + + - So, we will need three parsers, to deal with this: + 1. A parser for integers, which will deal with the raw numbers. + 2. A parser for comma seperated pair, which will split it up into integers. + 3. A parser for the outer brackets. + + - We can see below how we achieve this: + +```rust +# extern crate nom; +# use std::error::Error; +use nom::IResult; +use nom::bytes::complete::tag; +use nom::sequence::{separated_pair, delimited}; + +// This is the type we will parse into. +#[derive(Debug,PartialEq)] +pub struct Coordinate { + pub x: i32, + pub y: i32, +} + +// 1. Nom has an in-built i32 parser. +use nom::character::complete::i32; + +// 2. Use the `separated_pair` parser to combine two parsers (in this case, +// both `i32`), ignoring something in-between. +fn parse_integer_pair(input: &str) -> IResult<&str, (i32, i32)> { + separated_pair( + i32, + tag(", "), + i32 + )(input) +} + +// 3. Use the `delimited` parser to apply a parser, ignoring the results +// of two surrounding parsers. +fn parse_coordinate(input: &str) -> IResult<&str, Coordinate> { + let (remaining, (x, y)) = delimited( + tag("("), + parse_integer_pair, + tag(")") + )(input)?; + + // Note: we could construct this by implementing `From` on `Coordinate`, + // We don't, just so it's obvious what's happening. + Ok((remaining, Coordinate {x, y})) + +} + +fn main() -> Result<(), Box> { + let (_, parsed) = parse_coordinate("(3, 5)")?; + assert_eq!(parsed, Coordinate {x: 3, y: 5}); + + let (_, parsed) = parse_coordinate("(2, -4)")?; + assert_eq!(parsed, Coordinate {x: 2, y: -4}); + + let parsing_error = parse_coordinate("(3,)"); + assert!(parsing_error.is_err()); + + let parsing_error = parse_coordinate("(,3)"); + assert!(parsing_error.is_err()); + + let parsing_error = parse_coordinate("Ferris"); + assert!(parsing_error.is_err()); + + +# Ok(()) +} +``` + +As an exercise, you might want to explore how to make this parser deal gracefully with +whitespace in the input. diff --git a/doc/nom-guide/src/chapter_5.md b/doc/nom-guide/src/chapter_5.md new file mode 100644 index 000000000..b23dd589e --- /dev/null +++ b/doc/nom-guide/src/chapter_5.md @@ -0,0 +1,64 @@ +# Chapter 5: Repeating with Predicates + +Just as, when programming, the humble while loop unlocks many useful +features; in Nom, repeating a parser multiple times can be incredibly useful + +There are, however, two ways of including repeating functionality into Nom -- +parsers which are governed by a predicate; and combinators which repeat +a parser. + +## Parsers which use a predicate + +A `predicate` is a function which returns a boolean value (i.e. given some input, +it returns `true` or `false`). These are incredibly common when parsing -- for instance, +a predicate `is_vowel` might decide whether a character is an english vowel (a, e, i, o or u). + +These can be used to make parsers that Nom hasn't built in. For instance, the below +parser will take as many vowels as possible. + +There are a few different categories of predicate parsers that are worth mentioning: + + - For bytes, there are three different categories of parser: `take_till`, `take_until`, and `take_while`. + `take_till` will continue consuming input until its input meets the predicate. + `take_while` will continue consuming input until its input *does not* meet the predicate. + `take_until` looks a lot like a predicate parser, but simply consumes until the first + occurence of the pattern of bytes. + - Some parsers have a "twin" with a `1` at the end of their name -- for example, `take_while` + has `take_while1`. The difference between them is that `take_while` could return an empty + slice if the first byte does not satisfy a predicate. `take_while1` returns an error if + the predicate is not met. + - As a special case, `take_while_m_n` is like `take_while`, but guarantees that it will consume + at least `m` bytes, and no more than `n` bytes. + + +```rust +# extern crate nom; +# use std::error::Error; +use nom::IResult; +use nom::bytes::complete::{tag, take_until, take_while}; +use nom::character::{is_space}; +use nom::sequence::{terminated}; + +fn parse_sentence(input: &str) -> IResult<&str, &str> { + terminated(take_until("."), take_while(|c| c == '.' || c == ' '))(input) +} + +fn main() -> Result<(), Box> { + let (remaining, parsed) = parse_sentence("I am Tom. I write Rust.")?; + assert_eq!(parsed, "I am Tom"); + assert_eq!(remaining, "I write Rust."); + + let parsing_error = parse_sentence("Not a sentence (no period at the end)"); + assert!(parsing_error.is_err()); + + +# Ok(()) +} +``` + For detailed examples, see their documentation, shown below: + +| combinator | usage | input | output | comment | +|---|---|---|---|---| + | [take_while](https://docs.rs/nom/latest/nom/bytes/complete/fn.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_while_m_n` does the same, but must return between `m` and `n` characters.| +| [take_till](https://docs.rs/nom/latest/nom/bytes/complete/fn.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/bytes/complete/fn.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| diff --git a/doc/nom-guide/src/chapter_6.md b/doc/nom-guide/src/chapter_6.md new file mode 100644 index 000000000..4f807ab3b --- /dev/null +++ b/doc/nom-guide/src/chapter_6.md @@ -0,0 +1,39 @@ +# Chapter 6: Repeating Parsers + +A single parser which repeats a predicate is useful, but more useful still is a combinator that +repeats a parser. Nom has multiple combinators which operate on this principle; the most obvious of +which is `many0`, which applies a parser as many times as possible; and returns a vector of +the results of those parses. Here is an example: + +```rust +# extern crate nom; +# use std::error::Error; +use nom::IResult; +use nom::multi::many0; +use nom::bytes::complete::tag; + +fn parser(s: &str) -> IResult<&str, Vec<&str>> { + many0(tag("abc"))(s) +} + +fn main() { + assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); + assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); + assert_eq!(parser("123123"), Ok(("123123", vec![]))); + assert_eq!(parser(""), Ok(("", vec![]))); +} +``` + +There are many different parsers to choose from: + +| combinator | usage | input | output | comment | +|---|---|---|---|---| +| [count](https://docs.rs/nom/latest/nom/multi/fn.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/multi/fn.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/multi/fn.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/multi/fn.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_list0](https://docs.rs/nom/latest/nom/multi/fn.separated_list0.html) | `separated_list0(tag(","), tag("ab"))` | `"ab,ab,ab."` | `Ok((".", vec!["ab", "ab", "ab"]))` |`separated_list1` works like `separated_list0` but must returns at least one element| +| [fold_many0](https://docs.rs/nom/latest/nom/multi/fn.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/multi/fn.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/multi/fn.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| + diff --git a/doc/nom-guide/src/chapter_7.md b/doc/nom-guide/src/chapter_7.md new file mode 100644 index 000000000..0645753e0 --- /dev/null +++ b/doc/nom-guide/src/chapter_7.md @@ -0,0 +1,10 @@ +# Chapter 7: Using Errors from Outside Nom + +[Nom has other documentation about errors, so in place of this chapter, read this page.](https://github.com/Geal/nom/blob/main/doc/error_management.md) + +## Particular Notes + + - It's particularly useful to use the `map_res` function. It allows you to + convert an external error to a Nom error. For an example, + see [the Nom example on the front page](https://github.com/Geal/nom#example). + diff --git a/doc/nom-guide/src/introduction.md b/doc/nom-guide/src/introduction.md index 19733e4c2..7152ac6d0 100644 --- a/doc/nom-guide/src/introduction.md +++ b/doc/nom-guide/src/introduction.md @@ -1,12 +1,11 @@ -# The Nom Guide +# The Nominomicon -Welcome to The Nom Guide (or, the nominomicon); a guide to using the Nom parser for great good. -This guide is written to take you from an understanding of Regular Expressions, to an understanding -of Nom. +Welcome to Nominomicon; a guide to using the Nom parser for great good. +This guide will give you an introduction to the theory and practice of +using Nom. -This guide assumes that you are: +This guide assumes only that you are: - Wanting to learn Nom, - - Already familiar with regular expressions (at least, somewhat), and - Already familiar with Rust. Nom is a parser-combinator library. In other words, it gives you tools to define: @@ -23,9 +22,5 @@ Before we set off, it's important to list some caveats: - Documentation older than 21st August, 2021 - Use of the `named!` macro - Use of `CompleteStr` or `CompleteByteArray`. - - Nom can parse (almost) anything; but this guide will focus entirely on parsing + - Nom can parse (almost) anything; but this guide will focus almost entirely on parsing complete `&str` into things. - -And finally, some nomenclature: - - In this guide, regexes will be denoted inside slashes (for example `/abc/`) - to distinguish them from regular strings. From 94252a492e0075d9cabc5b1cdc78f285d18afc97 Mon Sep 17 00:00:00 2001 From: Tom Kunc Date: Fri, 1 Jul 2022 21:11:57 +1000 Subject: [PATCH 3/3] Update Xiretza's tuple issue --- doc/nom-guide/src/chapter_1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/nom-guide/src/chapter_1.md b/doc/nom-guide/src/chapter_1.md index d4813923e..6b0d1f7f6 100644 --- a/doc/nom-guide/src/chapter_1.md +++ b/doc/nom-guide/src/chapter_1.md @@ -29,7 +29,7 @@ For simplicity, however, in the next chapters we will leave these unexplored. ``` -To represent this model of the world, nom uses the `IResult<(I, O)>` type. +To represent this model of the world, nom uses the `IResult` type. The `Ok` variant has a tuple of `(remaining_input: I, output: O)`; whereas the `Err` variant stores an error.