Skip to content

Commit

Permalink
Merge pull request #485 from epage/docs
Browse files Browse the repository at this point in the history
docs: Try to clear up some confusion
  • Loading branch information
epage authored Feb 28, 2024
2 parents 18a319a + 55700b4 commit c3e3e4d
Show file tree
Hide file tree
Showing 14 changed files with 879 additions and 774 deletions.
9 changes: 4 additions & 5 deletions src/_tutorial/chapter_0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@
//!
//! Parser combinators are great because:
//!
//! - The parsers are small and easy to write
//! - The parsers components are easy to reuse (if they're general enough, please add them to winnow!)
//! - The parsers components are easy to test separately (unit tests and property-based tests)
//! - The parser combination code looks close to the grammar you would have written
//! - You can build partial parsers, specific to the data you need at the moment, and ignore the rest
//! - Individual parser functions are small, focused on one thing, ignoring the rest
//! - You can write tests focused on individual parsers (unit tests and property-based tests)
//! in addition to testing the top-level parser as a whole.
//! - Top-level parsing code looks close to the grammar you would have written

#![allow(unused_imports)]
use crate::_topic;
Expand Down
13 changes: 6 additions & 7 deletions src/_tutorial/chapter_1.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
//! # Chapter 1: The Winnow Way
//!
//! First of all, we need to understand the way that winnow thinks about parsing.
//! As discussed in the introduction, winnow lets us build simple parsers, and
//! then combine them (using "combinators").
//! As discussed in the introduction, winnow lets us compose more complex parsers from more simple
//! ones (using "combinators").
//!
//! Let's discuss what a "parser" actually does. A parser takes an input and returns
//! Let's discuss what a "parser" actually does. A parser takes an input and advances it until it 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.
//! On success, the parser will return the processed data. The input will be left pointing to
//! data that still needs processing
//! On success, the parser will return the processed data. The input will be advanced to the end of
//! what was processed, pointing to what will be parsed next.
//!
//! 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.
Expand Down Expand Up @@ -43,7 +43,6 @@
//! 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.
Expand All @@ -54,7 +53,7 @@
//! This parser function should take in a `&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 does nothing to our input, the input will be left where it started.
//! - Since it doesn't parse anything, it also should just return an empty string.
//!
//! ```rust
Expand Down
75 changes: 66 additions & 9 deletions src/_tutorial/chapter_2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,22 @@
//! }
//! ```
//!
//! [`any`] and [`Parser::verify`] are [`Parser`] building blocks on top of [`Stream`]:
//! This extraction of a token is encapsulated in the [`any`] parser:
//! ```rust
//! # use winnow::PResult;
//! # use winnow::error::ParserError;
//! # use winnow::error::ErrorKind;
//! # use winnow::error::ErrMode;
//! use winnow::Parser;
//! use winnow::token::any;
//!
//! fn parse_prefix(input: &mut &str) -> PResult<char> {
//! any.verify(|c| *c == '0').parse_next(input)
//! let c = any
//! .parse_next(input)?;
//! if c != '0' {
//! return Err(ErrMode::from_error_kind(input, ErrorKind::Verify));
//! }
//! Ok(c)
//! }
//! #
//! # fn main() {
Expand All @@ -58,15 +66,41 @@
//! # }
//! ```
//!
//! Matching a single token literal is common enough that [`Parser`] is implemented for
//! `char`.
//! Using the higher level [`any`] parser opens `parse_prefix` to the helpers on the [`Parser`] trait,
//! like [`Parser::verify`] which fails a parse if a condition isn't met, like our check above:
//! ```rust
//! # use winnow::PResult;
//! use winnow::Parser;
//! use winnow::token::any;
//!
//! fn parse_prefix(input: &mut &str) -> PResult<char> {
//! let c = any
//! .verify(|c| *c == '0')
//! .parse_next(input)?;
//! Ok(c)
//! }
//! #
//! # fn main() {
//! # let mut input = "0x1a2b Hello";
//! #
//! # let output = parse_prefix.parse_next(&mut input).unwrap();
//! #
//! # assert_eq!(input, "x1a2b Hello");
//! # assert_eq!(output, '0');
//! #
//! # assert!(parse_prefix.parse_next(&mut "d").is_err());
//! # }
//! ```
//!
//! Matching a single token literal is common enough that [`Parser`] is implemented for
//! the `char` type, encapsulating both [`any`] and [`Parser::verify`]:
//! ```rust
//! # use winnow::PResult;
//! use winnow::Parser;
//!
//! fn parse_prefix(input: &mut &str) -> PResult<char> {
//! '0'.parse_next(input)
//! let c = '0'.parse_next(input)?;
//! Ok(c)
//! }
//! #
//! # fn main() {
Expand Down Expand Up @@ -115,13 +149,37 @@
//! }
//! ```
//!
//! Again, matching a literal is common enough that [`Parser`] is implemented for `&str`:
//! Matching the input position against a string literal is encapsulated in the [`literal`] parser:
//! ```rust
//! # use winnow::PResult;
//! # use winnow::Parser;
//! use winnow::token::literal;
//!
//! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> {
//! let expected = "0x";
//! let actual = literal(expected).parse_next(input)?;
//! Ok(actual)
//! }
//! #
//! # fn main() {
//! # let mut input = "0x1a2b Hello";
//! #
//! # let output = parse_prefix.parse_next(&mut input).unwrap();
//! # assert_eq!(input, "1a2b Hello");
//! # assert_eq!(output, "0x");
//! #
//! # assert!(parse_prefix.parse_next(&mut "0o123").is_err());
//! # }
//! ```
//!
//! Like for a single token, matching a string literal is common enough that [`Parser`] is implemented for the `&str` type:
//! ```rust
//! # use winnow::PResult;
//! use winnow::Parser;
//!
//! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> {
//! "0x".parse_next(input)
//! let actual = "0x".parse_next(input)?;
//! Ok(actual)
//! }
//! #
//! # fn main() {
Expand All @@ -135,8 +193,7 @@
//! # }
//! ```
//!
//! In `winnow`, we call this type of parser a [`literal`]. See [`token`] for additional individual
//! and token-slice parsers.
//! See [`token`] for additional individual and token-slice parsers.
//!
//! ## Character Classes
//!
Expand Down
39 changes: 19 additions & 20 deletions src/_tutorial/chapter_3.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! # Chapter 3: Sequencing and Alternatives
//!
//! In the last chapter, we saw how to create simple parsers using prebuilt parsers.
//! In the last chapter, we saw how to create parsers using prebuilt parsers.
//!
//! In this chapter, we explore two other widely used features:
//! alternatives and composition.
//! sequencing and alternatives.
//!
//! ## Sequencing
//!
Expand Down Expand Up @@ -111,15 +111,15 @@
//! Sometimes, we might want to choose between two parsers; and we're happy with
//! either being used.
//!
//! [`Stream::checkpoint`] helps us to retry parsing:
//! To retry a parse result, we can save off a [`Stream::checkpoint`] and later restore the parser
//! back to that position with [`Stream::reset`]:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::token::take_while;
//! use winnow::stream::Stream;
//!
//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
//! let start = input.checkpoint();
//!
//! if let Ok(output) = ("0b", parse_bin_digits).parse_next(input) {
//! return Ok(output);
//! }
Expand Down Expand Up @@ -182,7 +182,7 @@
//! > `Result::Err` can lead to incorrect behavior. This will be clarified in later when covering
//! > [error handling][`chapter_6`#errmode]
//!
//! [`opt`] is a basic building block for correctly handling retrying parsing:
//! [`opt`] is a parser that encapsulates this pattern of "retry on failure":
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::token::take_while;
Expand Down Expand Up @@ -239,7 +239,7 @@
//! # }
//! ```
//!
//! [`alt`] encapsulates this if/else-if ladder pattern, with the last case being the `else`:
//! [`alt`] encapsulates this if/else-if ladder pattern, with the last case being the "else":
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::token::take_while;
Expand Down Expand Up @@ -293,7 +293,7 @@
//! # }
//! ```
//!
//! > **Note:** [`empty`] and [`fail`] are parsers that might be useful in the `else` case.
//! > **Note:** [`empty`] and [`fail`] are parsers that might be useful in the "else" case.
//!
//! Sometimes a giant if/else-if ladder can be slow and you'd rather have a `match` statement for
//! branches of your parser that have unique prefixes. In this case, you can use the
Expand All @@ -315,8 +315,7 @@
//! _ => fail,
//! ).parse_next(input)
//! }
//!
//! // ...
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
Expand All @@ -342,17 +341,17 @@
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//!
//! fn main() {
//! let mut input = "0x1a2b Hello";
//!
//! let digits = parse_digits.parse_next(&mut input).unwrap();
//!
//! assert_eq!(input, " Hello");
//! assert_eq!(digits, "1a2b");
//!
//! assert!(parse_digits(&mut "ghiWorld").is_err());
//! }
//! #
//! # fn main() {
//! # let mut input = "0x1a2b Hello";
//! #
//! # let digits = parse_digits.parse_next(&mut input).unwrap();
//! #
//! # assert_eq!(input, " Hello");
//! # assert_eq!(digits, "1a2b");
//! #
//! # assert!(parse_digits(&mut "ghiWorld").is_err());
//! # }
//! ```
//!
//! > **Note:** [`peek`] may be useful when [`dispatch`]ing from hints from each case's parser.
Expand Down
Loading

0 comments on commit c3e3e4d

Please sign in to comment.