feat: add display UI with splash screen, dashboard, and RSSI sparkline

Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:54 -06:00
1 parent 7064110293
commit 3102fc747a
17624 files changed
+4564928 -38

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
//! # Arithmetic
//!
//! This parses arithmetic expressions and directly evaluates them.
//!
//! See also [C-style Expressions][crate::_topic::language#c-style-expressions],
//! for a more complex example.
//!
//! ```rust
#![doc = include_str!("../../examples/arithmetic/parser.rs")]
//! ```
+44
View File
@@ -0,0 +1,44 @@
//! # Custom Errors
//!
//! A lot can be accomplished with the built-in error tools, like:
//! - [`ContextError`]
//! - [`Parser::context`]
//! - [`cut_err`]
//!
//! *(see [tutorial][chapter_7])*
//!
//! Most other needs can likely be met by using a custom context type with [`ContextError`] instead
//! of [`StrContext`].
//! This will require implementing a custom renderer.
//!
//! ## `ParserError` Trait
//!
//! When needed, you can also create your own type that implements [`ParserError`].
//!
//! Optional traits include:
//! - [`AddContext`]
//! - [`FromExternalError`]
//! - [`ErrorConvert`]
//!
//! There are multiple strategies for implementing support for [`AddContext`] and [`FromExternalError`]:
//! - Make your error type generic over the context or external error
//! - Require a trait for the context or external error and `Box` it
//! - Make the context an enum like [`StrContext`]
//! - Implement the trait multiple times, one for each concrete context or external error type,
//! allowing custom behavior per type
//!
//! Example:
//!```rust
#![doc = include_str!("../../examples/custom_error.rs")]
//!```
#![allow(unused_imports)]
use crate::combinator::cut_err;
use crate::error::ContextError;
use crate::error::ErrorConvert;
use crate::error::StrContext;
use crate::Parser;
use crate::_tutorial::chapter_7;
use crate::error::AddContext;
use crate::error::FromExternalError;
use crate::error::ParserError;
+8
View File
@@ -0,0 +1,8 @@
//! # Implementing `FromStr`
//!
//! The [`FromStr` trait][std::str::FromStr] provides
//! a common interface to parse from a string.
//!
//! ```rust
#![doc = include_str!("../../examples/css/parser.rs")]
//! ```
+5
View File
@@ -0,0 +1,5 @@
//! # HTTP
//!
//! ```rust
#![doc = include_str!("../../examples/http/parser.rs")]
//! ```
+5
View File
@@ -0,0 +1,5 @@
//! # INI
//!
//! ```rust
#![doc = include_str!("../../examples/ini/parser.rs")]
//! ```
+5
View File
@@ -0,0 +1,5 @@
//! # json
//!
//! ```rust,ignore
#![doc = include_str!("../../examples/json/parser_dispatch.rs")]
//! ```
+341
View File
@@ -0,0 +1,341 @@
//! # Elements of Programming Languages
//!
//! These are short recipes for accomplishing common tasks.
//!
//! * [Whitespace](#whitespace)
//! + [Wrapper combinators that eat whitespace before and after a parser](#wrapper-combinators-that-eat-whitespace-before-and-after-a-parser)
//! * [Comments](#comments)
//! + [`// C++/EOL-style comments`](#-ceol-style-comments)
//! + [`/* C-style comments */`](#-c-style-comments-)
//! * [Identifiers](#identifiers)
//! + [`Rust-Style Identifiers`](#rust-style-identifiers)
//! * [Literal Values](#literal-values)
//! + [Escaped Strings](#escaped-strings)
//! + [Integers](#integers)
//! - [Hexadecimal](#hexadecimal)
//! - [Octal](#octal)
//! - [Binary](#binary)
//! - [Decimal](#decimal)
//! + [Floating Point Numbers](#floating-point-numbers)
//! * [C-style Expressions](#c-style-expressions)
//!
//! ## Whitespace
//!
//!
//!
//! ### Wrapper combinators that eat whitespace before and after a parser
//!
//! ```rust
//! use winnow::prelude::*;
//! use winnow::{
//! error::ParserError,
//! combinator::delimited,
//! ascii::multispace0,
//! };
//!
//! /// A combinator that takes a parser `inner` and produces a parser that also consumes both leading and
//! /// trailing whitespace, returning the output of `inner`.
//! fn ws<'a, F, O, E: ParserError<&'a str>>(inner: F) -> impl Parser<&'a str, O, E>
//! where
//! F: Parser<&'a str, O, E>,
//! {
//! delimited(
//! multispace0,
//! inner,
//! multispace0
//! )
//! }
//! ```
//!
//! To eat only trailing whitespace, replace `delimited(...)` with `terminated(&inner, multispace0)`.
//! Likewise, the eat only leading whitespace, replace `delimited(...)` with `preceded(multispace0,
//! &inner)`. You can use your own parser instead of `multispace0` if you want to skip a different set
//! of lexemes.
//!
//! ## Comments
//!
//! ### `// C++/EOL-style comments`
//!
//! This version uses `%` to start a comment, does not consume the newline character, and returns an
//! output of `()`.
//!
//! ```rust
//! use winnow::prelude::*;
//! use winnow::{
//! error::ParserError,
//! token::take_till,
//! };
//!
//! pub fn peol_comment<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> ModalResult<(), E>
//! {
//! ('%', take_till(1.., ['\n', '\r']))
//! .void() // Output is thrown away.
//! .parse_next(i)
//! }
//! ```
//!
//! ### `/* C-style comments */`
//!
//! Inline comments surrounded with sentinel literals `(*` and `*)`. This version returns an output of `()`
//! and does not handle nested comments.
//!
//! ```rust
//! use winnow::prelude::*;
//! use winnow::{
//! error::ParserError,
//! token::take_until,
//! };
//!
//! pub fn pinline_comment<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> ModalResult<(), E> {
//! (
//! "(*",
//! take_until(0.., "*)"),
//! "*)"
//! )
//! .void() // Output is thrown away.
//! .parse_next(i)
//! }
//! ```
//!
//! ## Identifiers
//!
//! ### `Rust-Style Identifiers`
//!
//! Parsing identifiers that may start with a letter (or underscore) and may contain underscores,
//! letters and numbers may be parsed like this:
//!
//! ```rust
//! use winnow::prelude::*;
//! use winnow::{
//! stream::AsChar,
//! token::take_while,
//! token::one_of,
//! };
//!
//! pub fn identifier<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! (
//! one_of(|c: char| c.is_alpha() || c == '_'),
//! take_while(0.., |c: char| c.is_alphanum() || c == '_')
//! )
//! .take()
//! .parse_next(input)
//! }
//! ```
//!
//! Let's say we apply this to the identifier `hello_world123abc`. The first element of the tuple
//! would uses [`one_of`][crate::token::one_of] which would take `h`. The tuple ensures that
//! `ello_world123abc` will be piped to the next [`take_while`][crate::token::take_while] parser,
//! which takes every remaining character. However, the tuple returns a tuple of the results
//! of its sub-parsers. The [`take`][crate::Parser::take] parser produces a `&str` of the
//! input text that was parsed, which in this case is the entire `&str` `hello_world123abc`.
//!
//! ## Literal Values
//!
//! ### Escaped Strings
//!
//! ```rust
#![doc = include_str!("../../examples/string/parser.rs")]
//! ```
//!
//! See also [`take_escaped`] and [`escaped`].
//!
//! ### Integers
//!
//! The following recipes all return string slices rather than integer values. How to obtain an
//! integer value instead is demonstrated for hexadecimal integers. The others are similar.
//!
//! The parsers allow the grouping character `_`, which allows one to group the digits by byte, for
//! example: `0xA4_3F_11_28`. If you prefer to exclude the `_` character, the lambda to convert from a
//! string slice to an integer value is slightly simpler. You can also strip the `_` from the string
//! slice that is returned, which is demonstrated in the second hexadecimal number parser.
//!
//! #### Hexadecimal
//!
//! The parser outputs the string slice of the digits without the leading `0x`/`0X`.
//!
//! ```rust
//! use winnow::prelude::*;
//! use winnow::{
//! combinator::alt,
//! combinator::repeat,
//! combinator::{preceded, terminated},
//! token::one_of,
//! };
//!
//! fn hexadecimal<'s>(input: &mut &'s str) -> ModalResult<&'s str> { // <'a, E: ParserError<&'a str>>
//! preceded(
//! alt(("0x", "0X")),
//! repeat(1..,
//! terminated(one_of(('0'..='9', 'a'..='f', 'A'..='F')), repeat(0.., '_').map(|()| ()))
//! ).map(|()| ()).take()
//! ).parse_next(input)
//! }
//! ```
//!
//! If you want it to return the integer value instead, use map:
//!
//! ```rust
//! use winnow::prelude::*;
//! use winnow::{
//! combinator::alt,
//! combinator::repeat,
//! combinator::{preceded, terminated},
//! token::one_of,
//! };
//!
//! fn hexadecimal_value(input: &mut &str) -> ModalResult<i64> {
//! preceded(
//! alt(("0x", "0X")),
//! repeat(1..,
//! terminated(one_of(('0'..='9', 'a'..='f', 'A'..='F')), repeat(0.., '_').map(|()| ()))
//! ).map(|()| ()).take()
//! ).try_map(
//! |out: &str| i64::from_str_radix(&str::replace(&out, "_", ""), 16)
//! ).parse_next(input)
//! }
//! ```
//!
//! See also [`hex_uint`]
//!
//! #### Octal
//!
//! ```rust
//! use winnow::prelude::*;
//! use winnow::{
//! combinator::alt,
//! combinator::repeat,
//! combinator::{preceded, terminated},
//! token::one_of,
//! };
//!
//! fn octal<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! preceded(
//! alt(("0o", "0O")),
//! repeat(1..,
//! terminated(one_of('0'..='7'), repeat(0.., '_').map(|()| ()))
//! ).map(|()| ()).take()
//! ).parse_next(input)
//! }
//! ```
//!
//! #### Binary
//!
//! ```rust
//! use winnow::prelude::*;
//! use winnow::{
//! combinator::alt,
//! combinator::repeat,
//! combinator::{preceded, terminated},
//! token::one_of,
//! };
//!
//! fn binary<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! preceded(
//! alt(("0b", "0B")),
//! repeat(1..,
//! terminated(one_of('0'..='1'), repeat(0.., '_').map(|()| ()))
//! ).map(|()| ()).take()
//! ).parse_next(input)
//! }
//! ```
//!
//! #### Decimal
//!
//! ```rust
//! use winnow::prelude::*;
//! use winnow::{
//! combinator::repeat,
//! combinator::terminated,
//! token::one_of,
//! };
//!
//! fn decimal<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! repeat(1..,
//! terminated(one_of('0'..='9'), repeat(0.., '_').map(|()| ()))
//! ).map(|()| ())
//! .take()
//! .parse_next(input)
//! }
//! ```
//!
//! See also [`dec_uint`] and [`dec_int`]
//!
//! ### Floating Point Numbers
//!
//! The following is adapted from [the Python parser by Valentin Lorentz](https://github.com/ProgVal/rust-python-parser/blob/master/src/numbers.rs).
//!
//! ```rust
//! use winnow::prelude::*;
//! use winnow::{
//! combinator::alt,
//! combinator::repeat,
//! combinator::opt,
//! combinator::{preceded, terminated},
//! token::one_of,
//! };
//!
//! fn float<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! alt((
//! // Case one: .42
//! (
//! '.',
//! decimal,
//! opt((
//! one_of(['e', 'E']),
//! opt(one_of(['+', '-'])),
//! decimal
//! ))
//! ).take()
//! , // Case two: 42e42 and 42.42e42
//! (
//! decimal,
//! opt(preceded(
//! '.',
//! decimal,
//! )),
//! one_of(['e', 'E']),
//! opt(one_of(['+', '-'])),
//! decimal
//! ).take()
//! , // Case three: 42. and 42.42
//! (
//! decimal,
//! '.',
//! opt(decimal)
//! ).take()
//! )).parse_next(input)
//! }
//!
//! fn decimal<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! repeat(1..,
//! terminated(one_of('0'..='9'), repeat(0.., '_').map(|()| ()))
//! ).
//! map(|()| ())
//! .take()
//! .parse_next(input)
//! }
//! ```
//!
//! See also [`float`]
//!
//! ## C-style Expressions
//!
//! An example using the [`expression()`] parser to build an abstract syntax tree
//! for C-style expressions.
//!
//! The operator precedence level is based on the [C language][c-precedence].
//!
//! [c-precedence]: https://en.cppreference.com/w/c/language/operator_precedence.html
//!
//! ```rust
#![doc = include_str!("../../examples/c_expression/parser.rs")]
//! ```
#![allow(unused_imports)]
use crate::ascii::dec_int;
use crate::ascii::dec_uint;
use crate::ascii::escaped;
use crate::ascii::float;
use crate::ascii::hex_uint;
use crate::ascii::take_escaped;
use crate::combinator::expression;
+23
View File
@@ -0,0 +1,23 @@
//! # Lexing and Parsing
//!
//! ## Parse to AST
//!
//! The simplest way to write a parser is to parse directly to the AST.
//!
//! Example:
//! ```rust
#![doc = include_str!("../../examples/arithmetic/parser_ast.rs")]
//! ```
//!
//! ## Lexing
//!
//! However, there are times when you may want to separate lexing from parsing.
//! Winnow provides [`TokenSlice`] to simplify this.
//!
//! Example:
//! ```rust
#![doc = include_str!("../../examples/arithmetic/parser_lexer.rs")]
//! ```
#![allow(unused_imports)]
use crate::stream::TokenSlice;
+43
View File
@@ -0,0 +1,43 @@
//! # Special Topics
//!
//! These are short recipes for accomplishing common tasks.
//!
//! - [Why `winnow`?][why]
//! - [For `nom` users][nom]
//! - Formats:
//! - [Elements of Programming Languages][language]
//! - [Arithmetic][arithmetic]
//! - [s-expression][s_expression]
//! - [json]
//! - [INI][ini]
//! - [HTTP][http]
//! - Special Topics:
//! - [Implementing `FromStr`][fromstr]
//! - [Performance][performance]
//! - [Parsing Partial Input][partial]
//! - [Lexing and Parsing][lexing]
//! - [Custom stream or token][stream]
//! - [Custom errors][error]
//! - [Debugging][crate::_tutorial::chapter_8]
//!
//! See also parsers written with `winnow`:
//!
//! - [`toml_edit`](https://crates.io/crates/toml_edit)
//! - [`hcl-edit`](https://crates.io/crates/hcl-edit)
#![allow(clippy::std_instead_of_core)]
#![allow(clippy::test_attr_in_doctest)]
pub mod arithmetic;
pub mod error;
pub mod fromstr;
pub mod http;
pub mod ini;
pub mod json;
pub mod language;
pub mod lexing;
pub mod nom;
pub mod partial;
pub mod performance;
pub mod s_expression;
pub mod stream;
pub mod why;
+240
View File
@@ -0,0 +1,240 @@
//! # For `nom` users
//!
//! ## Migrating from `nom`
//!
//! For comparisons with `nom`, see
//! - [Why `winnow`][super::why]
//! - [parse-rosetta-rs](https://github.com/rosetta-rs/parse-rosetta-rs/)
//!
//! What approach you take depends on the size and complexity of your parser.
//! For small, simple parsers, its likely easiest to directly port from `nom`.
//! When trying to look for the equivalent of a `nom` combinator, search in the docs for the name
//! of the `nom` combinator. It is expected that, where names diverge, a doc alias exists.
//! See also the [List of combinators][crate::combinator].
//!
//! ### Complex migrations
//!
//! For larger parsers, it is likely best to take smaller steps
//! - Easier to debug when something goes wrong
//! - Deprecation messages will help assist through the process
//!
//! The workflow goes something like:
//! 1. Run `cargo rm nom && cargo add winnow@0.3`
//! 1. Ensure everything compiles and tests pass, ignoring deprecation messages (see [migration
//! notes](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md#nom-migration-guide))
//! 1. Commit
//! 1. Switch any `impl FnMut(I) -> IResult<I, O, E>` to `impl Parser<I, O, E>`
//! 1. Resolve deprecation messages
//! 1. Commit
//! 1. Run `cargo add winnow@0.4`
//! 1. Ensure everything compiles and tests pass, ignoring deprecation messages (see [changelog](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md#040---2023-03-18) for more details)
//! 1. Commit
//! 1. Resolve deprecation messages
//! 1. Commit
//! 1. Run `cargo add winnow@0.5`
//! 1. Ensure everything compiles and tests pass, ignoring deprecation messages
//! (see [migration notes](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md#050---2023-07-13))
//! 1. Commit
//! 1. Resolve deprecation messages
//! 1. Commit
//!
//! ### Examples
//!
//! For example migrations, see
//! - [git-config-env](https://github.com/gitext-rs/git-config-env/pull/11) (nom to winnow 0.3)
//! - [git-conventional](https://github.com/crate-ci/git-conventional/pull/37) (nom to winnow 0.3,
//! adds explicit tracing for easier debugging)
//! - [typos](https://github.com/crate-ci/typos/pull/664) (nom to winnow 0.3)
//! - [cargo-smart-release](https://github.com/Byron/gitoxide/pull/948) (gradual migration from nom
//! to winnow 0.5)
//! - [gix-config](https://github.com/Byron/gitoxide/pull/951) (gradual migration from nom
//! to winnow 0.5)
//! - [gix-protocol](https://github.com/Byron/gitoxide/pull/1009) (gradual migration from nom
//! to winnow 0.5)
//! - [gitoxide](https://github.com/Byron/gitoxide/pull/956) (gradual migration from nom
//! to winnow 0.5)
//!
//! ## Differences
//!
//! These are key differences to help Nom users adapt to writing parsers with Winnow.
//!
//! ### Renamed APIs
//!
//! Names have changed for consistency or clarity.
//!
//! To find a parser you are looking for,
//! - Search the docs for the `nom` parser
//! - See the [List of combinators][crate::combinator]
//!
//! ### GATs
//!
//! `nom` v8 back-propagates how you will use a parser to parser functions using a language feature
//! called GATs.
//! Winnow has made the conscious choice not to use this feature, finding alternative ways of
//! getting most of the benefits.
//!
//! Benefits of GATs:
//! - Performance as the compiler is able to instantiate copies of a parser that are
//! better tailored to how it will be used, like discarding unused allocations for output or
//! errors.
//!
//! Benefits of not using GATs:
//! - Predictable performance:
//! With GATs, seemingly innocuous changes like choosing to hand write a parser using idiomatic function parsers
//! (`fn(&mut I) -> Result<O>`) can cause surprising slow downs because these functions sever the back-propagation from GATs.
//! The causes of these slowdowns could be hard to identify by inspection or profiling.
//! - No "eek out X% perf improvement" pressure to contort a parser
//! that is more easily written imperatively
//! to be written declaratively
//! so it can preserve the back-propagation from GATs.
//! - Built-in parsers serve are can serve as examples to users of idiomatic function parsers
//! (`fn(&mut I) -> Result<O>`).
//! With GATs, built-in parsers tend to be complex implementations of traits.
//! - Faster build times and smaller binary size as parsers only need to be generated for one mode,
//! not upto 8
//!
//! #### Partial/streaming parsers
//!
//! `nom` v8 back-propagates whether `Parser::parse_complete` was used to select `complete`
//! parsers.
//! Previously, users had ensure consistently using a parser from the `streaming` or `complete` module.
//!
//! Instead, you tag the input type (`I`) by wrapping it in [`Partial<I>`] and parsers will adjust
//! their behavior accordingly.
//! See [partial] special topic.
//!
//! #### Eliding Output
//!
//! `nom` v8 back-propagates whether an Output will be used and skips its creation.
//! For example, `value(Null, many0(_))` will avoid creating and pushing to a `Vec`.
//! Previously, users had to select `count_many0` over `many0` to avoid creating a `Vec`.
//!
//! Instead, `repeat` returns an `impl Accumulate<T>` which could be a `Vec`, a `usize` for `count`
//! variants, or `()` to do no extra work.
//!
//! #### Eliding Backtracked Errors
//!
//! Under the hood, [`alt`] is an `if-not-error-else` ladder, see [`_tutorial::chapter_3`].
//! nom v8 back-propagates whether the error will be discarded and avoids any expensive work done
//! for rich error messages.
//!
//! Instead, [`ContextError`] and other changes have made it so errors have very little overhead.
//! [`dispatch!`] can also be used in some situations to avoid `alt`s overhead.
//!
//! ### Parsers return [`Stream::Slice`], rather than [`Stream`]
//!
//! In `nom`, parsers like [`take_while`] parse a [`Stream`] and return a [`Stream`].
//! When wrapping the input, like with [`Stateful`],
//! you have to unwrap the input to integrate it in your application,
//! and it requires [`Stream`] to be `Clone`
//! (which requires `RefCell` for mutable external state and can be expensive).
//!
//! Instead, [`Stream::Slice`] was added to track the intended type for parsers to return.
//! If you want to then parse the slice, you then need to take it and turn it back into a
//! [`Stream`].
//!
//! ### `&mut I`
//!
//! `winnow` switched from pure-function parser (`Fn(I) -> (I, O)` to `Fn(&mut I) -> O`).
//! On error, `i` is left pointing at where the error happened.
//!
//! Benefits of `Fn(&mut I) -> O`:
//! - Cleaner code: Removes need to pass `i` everywhere and makes changes to `i` more explicit
//! - Correctness: No forgetting to chain `i` through a parser
//! - Flexibility: `I` does not need to be `Copy` or even `Clone`. For example, [`Stateful`] can use `&mut S` instead of `RefCell<S>`.
//! - Performance: `Result::Ok` is smaller without `i`, reducing the risk that the output will be
//! returned on the stack, rather than the much faster CPU registers.
//! `Result::Err` can also be smaller because the error type does not need to carry `i` to point
//! to the error.
//! See also [#72](https://github.com/winnow-rs/winnow/issues/72).
//!
//! Benefits of `Fn(I) -> (I, O)`:
//! - Pure functions can be easier to reason about
//! - Less boilerplate in some situations (see below)
//! - Less syntactic noise in some situations (see below)
//!
//! When returning a slice from the input, you have to add a lifetime:
//! ```rust
//! # use winnow::prelude::*;
//! fn foo<'i>(i: &mut &'i str) -> ModalResult<&'i str> {
//! # Ok("")
//! // ...
//! }
//! ```
//!
//! When writing a closure, you need to annotate the type
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::combinator::alt;
//! # use winnow::error::ContextError;
//! # let mut input = "";
//! # fn foo<'i>() -> impl ModalParser<&'i str, &'i str, ContextError> {
//! alt((
//! |i: &mut _| {
//! # Ok("")
//! // ...
//! },
//! |i: &mut _| {
//! # Ok("")
//! // ...
//! },
//! ))
//! # }
//! ```
//! *(at least the full type isn't needed)*
//!
//! To save and restore from intermediate states, [`Stream::checkpoint`] and [`Stream::reset`] can help:
//! ```rust
//! use winnow::prelude::*;
//! # let mut i = "";
//! # let i = &mut i;
//!
//! let start = i.checkpoint();
//! // ...
//! i.reset(&start);
//! ```
//!
//! When the Output of a parser is a slice, you have to add a lifetime:
//! ```rust
//! # use winnow::prelude::*;
//! fn foo<'i>(i: &mut &'i str) -> ModalResult<&'i str> {
//! // ...
//! # winnow::token::rest.parse_next(i)
//! }
//! ```
//!
//! When writing a closure, you need to annotate the type:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::combinator::trace;
//! fn foo(i: &mut &str) -> ModalResult<usize> {
//! trace("foo", |i: &mut _| {
//! // ...
//! # Ok(0)
//! }).parse_next(i)
//! }
//! ```
//!
//! ### Optional [`ErrMode`]
//!
//! Called `Err` in `nom`, [`ErrMode`] is responsible for
//! - Deciding whether to backtrack and try another branch in cases like `alt` or report back to
//! the error back to users
//! - Tracking incomplete input on partial parsing
//!
//! As this isn't needed in every parser, it was made optional. [`ModalResult`] is a convenience
//! type for using [`ErrMode`].
#![allow(unused_imports)]
use crate::_topic::partial;
use crate::_tutorial;
use crate::combinator::alt;
use crate::combinator::dispatch;
use crate::error::ContextError;
use crate::error::ErrMode;
use crate::error::ModalResult;
use crate::stream::Accumulate;
use crate::stream::Partial;
use crate::stream::Stateful;
use crate::stream::Stream;
use crate::token::take_while;
+46
View File
@@ -0,0 +1,46 @@
//! # Parsing Partial Input
//!
//! Typically, the input being parsed is all in-memory, or is complete. Some data sources are too
//! large to fit into memory, only allowing parsing an incomplete or [`Partial`] subset of the
//! data, requiring incrementally parsing.
//!
//! By wrapping a stream, like `&[u8]`, with [`Partial`], parsers will report when the data is
//! [`Incomplete`] and more input is [`Needed`], allowing the caller to stream-in additional data
//! to be parsed. The data is then parsed a chunk at a time.
//!
//! Chunks are typically defined by either:
//! - A header reporting the number of bytes, like with [`length_and_then`]
//! - [`Partial`] can explicitly be changed to being complete once the specified bytes are
//! acquired via [`StreamIsPartial::complete`].
//! - A delimiter, like with [ndjson](https://github.com/ndjson/ndjson-spec/)
//! - You can parse up-to the delimiter or do a `take_until(0.., delim).and_then(parser)`
//!
//! If the chunks are not homogeneous, a state machine will be needed to track what the expected
//! parser is for the next chunk.
//!
//! Caveats:
//! - `winnow` takes the approach of re-parsing from scratch. Chunks should be relatively small to
//! prevent the re-parsing overhead from dominating.
//! - Parsers like [`repeat`] do not know when an `eof` is from insufficient data or the end of the
//! stream, causing them to always report [`Incomplete`].
//!
//! # Example
//!
//! `main.rs`:
//! ```rust,ignore
#![doc = include_str!("../../examples/ndjson/main.rs")]
//! ```
//!
//! `parser.rs`:
//! ```rust,ignore
#![doc = include_str!("../../examples/ndjson/parser.rs")]
//! ```
#![allow(unused_imports)] // Used for intra-doc links
use crate::binary::length_and_then;
use crate::combinator::repeat;
use crate::error::ErrMode::Incomplete;
use crate::error::Needed;
use crate::stream::Partial;
use crate::stream::StreamIsPartial;
+55
View File
@@ -0,0 +1,55 @@
//! # Performance
//!
//! ## Runtime Performance
//!
//! See also the general Rust [Performance Book](https://nnethercote.github.io/perf-book/)
//!
//! Tips
//! - Try `cargo add winnow -F simd`. For some it offers significant performance improvements
//! - When enough cases of an [`alt`] have unique prefixes, prefer [`dispatch`]
//! - When parsing text, try to parse as bytes (`u8`) rather than `char`s ([`BStr`] can make
//! debugging easier)
//! - Find simplified subsets of the grammar to parse, falling back to the full grammar when it
//! doesn't work. For example, when parsing json strings, parse them without support for escapes,
//! falling back to escape support if it fails.
//! - Watch for large return types. A surprising place these can show up is when chaining parsers
//! with a tuple.
//!
//! ## Build-time Performance
//!
//! Returning complex types as `impl Trait` can negatively impact build times. This can hit in
//! surprising cases like:
//! ```rust
//! # use winnow::prelude::*;
//! fn foo<I, O, E>() -> impl Parser<I, O, E>
//! # where
//! # I: winnow::stream::Stream<Token=O>,
//! # I: winnow::stream::StreamIsPartial,
//! # E: winnow::error::ParserError<I>,
//! {
//! // ...some chained combinators...
//! # winnow::token::any
//! }
//! ```
//!
//! Instead, wrap the combinators in a closure to simplify the type:
//! ```rust
//! # use winnow::prelude::*;
//! fn foo<I, O, E>() -> impl Parser<I, O, E>
//! # where
//! # I: winnow::stream::Stream<Token=O>,
//! # I: winnow::stream::StreamIsPartial,
//! # E: winnow::error::ParserError<I>,
//! {
//! move |input: &mut I| {
//! // ...some chained combinators...
//! # winnow::token::any
//! .parse_next(input)
//! }
//! }
//! ```
#![allow(unused_imports)]
use crate::combinator::alt;
use crate::combinator::dispatch;
use crate::stream::BStr;
+5
View File
@@ -0,0 +1,5 @@
//! # s-expression
//!
//! ```rust
#![doc = include_str!("../../examples/s_expression/parser.rs")]
//! ```
+63
View File
@@ -0,0 +1,63 @@
//! # Custom [`Stream`]
//!
//! `winnow` is batteries included with support for
//! - Basic inputs like `&str`, newtypes with
//! - Improved debug output like [`Bytes`]
//! - [`Stateful`] for passing state through your parser, like tracking recursion
//! depth
//! - [`LocatingSlice`] for looking up the absolute position of a token
//!
//! ## Implementing a custom token
//!
//! The first level of customization is parsing [`&[MyItem]`][Stream#impl-Stream-for-%26%5BT%5D]
//! or [`TokenSlice<MyItem>`].
//!
//! The basic traits you may want for a custom token type are:
//!
//! | trait | usage |
//! |---|---|
//! | [`AsChar`] |Transforms common types to a char for basic token parsing|
//! | [`ContainsToken`] |Look for the token in the given set|
//!
//! See also [`TokenSlice<MyItem>`], [lexing].
//!
//! ## Implementing a custom stream
//!
//! Let's assume we have an input type we'll call `MyStream`.
//! `MyStream` is a sequence of `MyItem` tokens.
//!
//! The goal is to define parsers with this signature: `&mut MyStream -> ModalResult<Output>`.
//! ```rust
//! # use winnow::prelude::*;
//! # type MyStream<'i> = &'i str;
//! # type Output<'i> = &'i str;
//! fn parser<'s>(i: &mut MyStream<'s>) -> ModalResult<Output<'s>> {
//! "test".parse_next(i)
//! }
//! ```
//!
//! Like above, you'll need to implement the related token traits for `MyItem`.
//!
//! The traits you may want to implement for `MyStream` include:
//!
//! | trait | usage |
//! |---|---|
//! | [`Stream`] |Core trait for driving parsing|
//! | [`StreamIsPartial`] | Marks the input as being the complete buffer or a partial buffer for streaming input |
//! | [`AsBytes`] |Casts the input type to a byte slice|
//! | [`AsBStr`] |Casts the input type to a slice of ASCII / UTF-8-like bytes|
//! | [`Compare`] |Character comparison operations|
//! | [`FindSlice`] |Look for a substring in self|
//! | [`Location`] |Calculate location within initial input|
//! | [`Offset`] |Calculate the offset between slices|
//!
//! And for `&[MyItem]` (slices returned by [`Stream`]):
//!
//! | trait | usage |
//! |---|---|
//! | [`SliceLen`] |Calculate the input length|
//! | [`ParseSlice`] |Used to integrate `&str`'s `parse()` method|
#![allow(unused_imports)] // Here for intra-dock links
use super::lexing;
use crate::stream::*;
+110
View File
@@ -0,0 +1,110 @@
//! # Why `winnow`?
//!
//! To answer this question, it will be useful to contrast this with other approaches to parsing.
//!
//! <div class="warning">
//!
//! **Note:** This will focus on principles and priorities. For a deeper and wider
//! comparison with other Rust parser libraries, see
//! [parse-rosetta-rs](https://github.com/rosetta-rs/parse-rosetta-rs).
//!
//! </div>
//!
//! ## Hand-written parsers
//!
//! Typically, a hand-written parser gives you the flexibility to get
//! - Fast parse performance
//! - Fast compile-time
//! - Small binary sizes
//! - High quality error message
//! - Fewer dependencies to audit
//!
//! However, this comes at the cost of doing it all yourself, including
//! - Optimizing for each of the above characteristics you care about
//! - Ensuring the safety of any `unsafe` code (buffer overflows being a common bug with parsers)
//! - Being aware of, familiar with, and correctly implement the relevant algorithms.
//! matklad, who has written two rust compile frontends, commented
//! ["I’ve implemented a production-grade Pratt parser once, but I no longer immediately understand that code :-)"](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html)
//!
//! This approach works well if:
//! - Your format is small and is unlikely to change
//! - Your format is large but you have people who can focus solely on parsing, like with large
//! programming languages
//!
//! ## `winnow`
//!
//! Unlike traditional programming language parsers that use
//! [lex](https://en.wikipedia.org/wiki/Lex_(software)) or
//! [yacc](https://en.wikipedia.org/wiki/Yacc), you can think of `winnow` as a general version of
//! the helpers you would create along the way to writing a hand-written parser.
//!
//! `winnow` includes support for:
//! - Zero-copy parsing
//! - [Parse traces][trace] for easier debugging
//! - [Streaming parsing][Partial] for network communication or large file
//! - [Stateful] parsers
//!
//! For binary formats, `winnow` includes:
//! - [A hexadecimal view][crate::Bytes] in [trace]
//! - [TLV](https://en.wikipedia.org/wiki/Type-length-value) (e.g. [`length_take`])
//! - Some common parsers to help get started, like numbers
//!
//! For text formats, `winnow` includes:
//! - [Tracking of spans][crate::LocatingSlice]
//! - [A textual view when parsing as bytes][crate::BStr] in [trace]
//! - Ability to evaluate directly, parse to an AST, or lex and parse the format
//!
//! This works well for:
//! - Prototyping for what will be a hand-written parser
//! - When you want to minimize the work to evolve your format
//! - When you don't have contributors focused solely on parsing and your grammar is large enough
//! to be unwieldy to hand write.
//!
//! ## `nom`
//!
//! `winnow` is a fork of the venerable [`nom`](https://crates.io/crates/nom). The difference
//! between them is largely in priorities. `nom` prioritizes:
//! - Lower churn for existing users while `winnow` is trying to find ways to make things better
//! for the parsers yet to be written.
//! - Having a small core, relying on external crates like
//! [`nom-locate`](https://crates.io/crates/nom_locate) and
//! [`nom-supreme`](https://crates.io/crates/nom-supreme), encouraging flexibility among users
//! and to not block users on new features being merged while `winnow` aims to include all the
//! fundamentals for parsing to ensure the experience is cohesive and high quality.
//!
//! For more details, see the [design differences][super::nom#api-differences].
//!
//! See also our [nom migration guide][super::nom#migrating-from-nom].
//!
//! ## `chumsky`
//!
//! [`chumsky`](https://crates.io/crates/chumsky) is an up and coming parser-combinator library
//! that includes advanced features like error recovery.
//!
//! Probably the biggest diverging philosophy is `chumsky`s stance:
//!
//! > "If you need to implement either `Parser` or `Strategy` by hand, that's a problem that needs fixing".
//!
//! This is under "batteries included" but it also ties into the feeling that `chumsky` acts more like
//! a framework. Instead of composing together helpers, you are expected to do everything through
//! their system to the point that it is non-trivial to implement their `Parser` trait and are
//! encouraged to use the
//! [`custom`](https://docs.rs/chumsky/0.9.0/chumsky/primitive/fn.custom.html) helper. This
//! requires re-framing everything to fit within their model and makes the code harder to understand
//! and debug as you are working with abstract operations that will eventually be applied
//! rather than directly with the parsers.
//!
//! In contrast, `winnow` is an introspectable toolbox that can easily be customized at any level.
//! Probably the biggest thing that `winnow` loses out on is optimizations from ["parse modes" via
//! GATs](https://github.com/zesterer/chumsky/pull/82) which allows downstream parsers to tell
//! upstream parsers when information will be discarded, allowing bypassing expensive operations,
//! like allocations. This requires a lot more complex interaction with parsers that isn't as
//! trivial to do with bare functions which would lose out on any of that side-band information.
//! Instead, we work around this with things like the [`Accumulate`] trait.
#![allow(unused_imports)]
use crate::binary::length_take;
use crate::combinator::trace;
use crate::stream::Accumulate;
use crate::stream::Partial;
use crate::stream::Stateful;
+38
View File
@@ -0,0 +1,38 @@
//! # Chapter 0: Introduction
//!
//! This tutorial assumes that you are:
//! - Already familiar with Rust
//! - Using `winnow` for the first time
//!
//! The focus will be on parsing in-memory strings (`&str`). Once done, you might want to check the
//! [Special Topics][_topic] for more specialized topics or examples.
//!
//! ## About
//!
//! `winnow` is a parser-combinator library. In other words, it gives you tools to define:
//! - "parsers", or functions that take an input and give back an output
//! - "combinators", or functions that take parsers and _combine_ them together!
//!
//! While "combinator" might be an unfamiliar word, you are likely using them in your rust code
//! today, like with the [`Iterator`] trait:
//! ```rust
//! let data = vec![1, 2, 3, 4, 5];
//! let even_count = data.iter()
//! .copied() // combinator
//! .filter(|d| d % 2 == 0) // combinator
//! .count(); // combinator
//! ```
//!
//! Parser combinators are great because:
//!
//! - 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;
use std::iter::Iterator;
pub use super::chapter_1 as next;
pub use crate::_tutorial as table_of_contents;
+88
View File
@@ -0,0 +1,88 @@
//! # 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 compose more complex parsers from more simple
//! ones (using "combinators").
//!
//! 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 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.
//! We'll explore this further in [`chapter_7`].
//!
//! ```text
//! ┌─► Ok(what matched the parser)
//! ┌────────┐ │
//! my input───►│a parser├──►either──┤
//! └────────┘ └─► Err(...)
//! ```
//!
//!
//! To represent this model of the world, winnow uses the [`Result<O>`] type.
//! The `Ok` variant has `output: O`;
//! whereas the `Err` variant stores an error.
//!
//! You can import that from:
//!
//! ```rust
//! use winnow::Result;
//! ```
//!
//! To combine parsers, we need a common way to refer to them which is where the [`Parser<I, O, E>`]
//! trait comes in with [`Parser::parse_next`] being the primary way to drive
//! parsing forward.
//! In [`chapter_6`], we'll cover how to integrate these into your application, particularly with
//! [`Parser::parse`].
//!
//! 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][stream]; 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.
//!
//! To make it easier to implement a [`Parser`], the trait is implemented for
//! functions of the form `Fn(&mut I) -> Result<O>`.
//!
//! 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 input will be left where it started.
//! - Since it doesn't parse anything, it also should just return an empty string.
//!
//! ```rust
//! use winnow::Result;
//! use winnow::Parser;
//!
//! pub fn do_nothing_parser<'s>(input: &mut &'s str) -> Result<&'s str> {
//! Ok("")
//! }
//!
//! fn main() {
//! let mut input = "0x1a2b Hello";
//!
//! let output = do_nothing_parser.parse_next(&mut input).unwrap();
//! // Same as:
//! // let output = do_nothing_parser(&mut input).unwrap();
//!
//! assert_eq!(input, "0x1a2b Hello");
//! assert_eq!(output, "");
//! }
//! ```
#![allow(unused_imports)]
use super::chapter_6;
use super::chapter_7;
use crate::Parser;
use crate::_topic::stream;
pub use super::chapter_0 as previous;
pub use super::chapter_2 as next;
pub use crate::_tutorial as table_of_contents;
+301
View File
@@ -0,0 +1,301 @@
//! # Chapter 2: Tokens and Tags
//!
//! The simplest *useful* parser you can write is one which matches tokens.
//! In our case, tokens are `char`.
//!
//! ## Tokens
//!
//! [`Stream`] provides some core operations to help with parsing. For example, to process a
//! single token, you can do:
//! ```rust
//! # use winnow::Parser;
//! # use winnow::Result;
//! use winnow::stream::Stream;
//! use winnow::error::ParserError;
//!
//! fn parse_prefix(input: &mut &str) -> Result<char> {
//! let c = input.next_token().ok_or_else(|| {
//! ParserError::from_input(input)
//! })?;
//! if c != '0' {
//! return Err(ParserError::from_input(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());
//! }
//! ```
//!
//! This extraction of a token is encapsulated in the [`any`] parser:
//! ```rust
//! # use winnow::Result;
//! # use winnow::error::ParserError;
//! use winnow::Parser;
//! use winnow::token::any;
//!
//! fn parse_prefix(input: &mut &str) -> Result<char> {
//! let c = any
//! .parse_next(input)?;
//! if c != '0' {
//! return Err(ParserError::from_input(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());
//! # }
//! ```
//!
//! 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::Result;
//! use winnow::Parser;
//! use winnow::token::any;
//!
//! fn parse_prefix(input: &mut &str) -> Result<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::Result;
//! use winnow::Parser;
//!
//! fn parse_prefix(input: &mut &str) -> Result<char> {
//! let 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());
//! # }
//! ```
//!
//! ## Tags
//!
//! [`Stream`] also supports processing slices of tokens:
//! ```rust
//! # use winnow::Parser;
//! # use winnow::Result;
//! use winnow::stream::Stream;
//! use winnow::error::ParserError;
//!
//! fn parse_prefix<'s>(input: &mut &'s str) -> Result<&'s str> {
//! let expected = "0x";
//! if input.len() < expected.len() {
//! return Err(ParserError::from_input(input));
//! }
//! let actual = input.next_slice(expected.len());
//! if actual != expected {
//! return Err(ParserError::from_input(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());
//! }
//! ```
//!
//! Matching the input position against a string literal is encapsulated in the [`literal`] parser:
//! ```rust
//! # use winnow::Result;
//! # use winnow::Parser;
//! use winnow::token::literal;
//!
//! fn parse_prefix<'s>(input: &mut &'s str) -> Result<&'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::Result;
//! use winnow::Parser;
//!
//! fn parse_prefix<'s>(input: &mut &'s str) -> Result<&'s str> {
//! let actual = "0x".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());
//! # }
//! ```
//!
//! See [`token`] for additional individual and token-slice parsers.
//!
//! ## Character Classes
//!
//! Selecting a single `char` or a [`literal`] is fairly limited. Sometimes, you will want to select one of several
//! `chars` of a specific class, like digits. For this, we use the [`one_of`] parser:
//!
//! ```rust
//! # use winnow::Parser;
//! # use winnow::Result;
//! use winnow::token::one_of;
//!
//! fn parse_digits(input: &mut &str) -> Result<char> {
//! one_of(('0'..='9', 'a'..='f', 'A'..='F')).parse_next(input)
//! }
//!
//! fn main() {
//! let mut input = "1a2b Hello";
//!
//! let output = parse_digits.parse_next(&mut input).unwrap();
//! assert_eq!(input, "a2b Hello");
//! assert_eq!(output, '1');
//!
//! assert!(parse_digits.parse_next(&mut "Z").is_err());
//! }
//! ```
//!
//! > **Aside:** [`one_of`] might look straightforward, a function returning a value that implements `Parser`.
//! > Let's look at it more closely as its used above (resolving all generic parameters):
//! > ```rust
//! > # use winnow::prelude::*;
//! > # use winnow::error::ContextError;
//! > pub fn one_of<'i>(
//! > list: &'static [char]
//! > ) -> impl Parser<&'i str, char, ContextError> {
//! > // ...
//! > # winnow::token::one_of(list)
//! > }
//! > ```
//! > If you have not programmed in a language where functions are values, the type signature of the
//! > [`one_of`] function might be a surprise.
//! > The function [`one_of`] *returns a function*. The function it returns is a
//! > [`Parser`], taking a `&str` and returning an [`Result`]. This is a common pattern in winnow for
//! > configurable or stateful parsers.
//!
//! Some of character classes are common enough that a named parser is provided, like with:
//! - [`line_ending`][crate::ascii::line_ending]: Recognizes an end of line (both `\n` and `\r\n`)
//! - [`newline`][crate::ascii::newline]: Matches a newline character `\n`
//! - [`tab`][crate::ascii::tab]: Matches a tab character `\t`
//!
//! You can then capture sequences of these characters with parsers like [`take_while`].
//! ```rust
//! # use winnow::Parser;
//! # use winnow::Result;
//! use winnow::token::take_while;
//!
//! fn parse_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! take_while(1.., ('0'..='9', 'a'..='f', 'A'..='F')).parse_next(input)
//! }
//!
//! fn main() {
//! let mut input = "1a2b Hello";
//!
//! let output = parse_digits.parse_next(&mut input).unwrap();
//! assert_eq!(input, " Hello");
//! assert_eq!(output, "1a2b");
//!
//! assert!(parse_digits.parse_next(&mut "Z").is_err());
//! }
//! ```
//!
//! We could simplify this further by using one of the built-in character classes, [`hex_digit1`]:
//! ```rust
//! # use winnow::Parser;
//! # use winnow::Result;
//! use winnow::ascii::hex_digit1;
//!
//! fn parse_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! hex_digit1.parse_next(input)
//! }
//!
//! fn main() {
//! let mut input = "1a2b Hello";
//!
//! let output = parse_digits.parse_next(&mut input).unwrap();
//! assert_eq!(input, " Hello");
//! assert_eq!(output, "1a2b");
//!
//! assert!(parse_digits.parse_next(&mut "Z").is_err());
//! }
//! ```
//!
//! See [`ascii`] for more text-based parsers.
#![allow(unused_imports)]
use crate::ascii;
use crate::ascii::hex_digit1;
use crate::stream::ContainsToken;
use crate::stream::Stream;
use crate::token;
use crate::token::any;
use crate::token::literal;
use crate::token::one_of;
use crate::token::take_while;
use crate::Parser;
use crate::Result;
use std::ops::RangeInclusive;
pub use super::chapter_1 as previous;
pub use super::chapter_3 as next;
pub use crate::_tutorial as table_of_contents;
+394
View File
@@ -0,0 +1,394 @@
//! # Chapter 3: Sequencing and Alternatives
//!
//! In the last chapter, we saw how to create parsers using prebuilt parsers.
//!
//! In this chapter, we explore two other widely used features:
//! sequencing and alternatives.
//!
//! ## Sequencing
//!
//! Now that we can create more interesting parsers, we can sequence them together, like:
//!
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! #
//! fn parse_prefix<'s>(input: &mut &'s str) -> Result<&'s str> {
//! "0x".parse_next(input)
//! }
//!
//! fn parse_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! take_while(1.., (
//! ('0'..='9'),
//! ('A'..='F'),
//! ('a'..='f'),
//! )).parse_next(input)
//! }
//!
//! fn main() {
//! let mut input = "0x1a2b Hello";
//!
//! let prefix = parse_prefix.parse_next(&mut input).unwrap();
//! let digits = parse_digits.parse_next(&mut input).unwrap();
//!
//! assert_eq!(prefix, "0x");
//! assert_eq!(digits, "1a2b");
//! assert_eq!(input, " Hello");
//! }
//! ```
//!
//! To sequence these together, you can just put them in a tuple:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! #
//! # fn parse_prefix<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # "0x".parse_next(input)
//! # }
//! #
//! # fn parse_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//! #
//! //...
//!
//! fn main() {
//! let mut input = "0x1a2b Hello";
//!
//! let (prefix, digits) = (
//! parse_prefix,
//! parse_digits
//! ).parse_next(&mut input).unwrap();
//!
//! assert_eq!(prefix, "0x");
//! assert_eq!(digits, "1a2b");
//! assert_eq!(input, " Hello");
//! }
//! ```
//!
//! Frequently, you won't care about the literal and you can instead use one of the provided combinators,
//! like [`preceded`]:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! use winnow::combinator::preceded;
//!
//! # fn parse_prefix<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # "0x".parse_next(input)
//! # }
//! #
//! # fn parse_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//! #
//! //...
//!
//! fn main() {
//! let mut input = "0x1a2b Hello";
//!
//! let digits = preceded(
//! parse_prefix,
//! parse_digits
//! ).parse_next(&mut input).unwrap();
//!
//! assert_eq!(digits, "1a2b");
//! assert_eq!(input, " Hello");
//! }
//! ```
//!
//! See [`combinator`] for more sequencing parsers.
//!
//! ## Alternatives
//!
//! Sometimes, we might want to choose between two parsers; and we're happy with
//! either being used.
//!
//! 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::Result;
//! # use winnow::token::take_while;
//! use winnow::stream::Stream;
//!
//! fn parse_digits<'s>(input: &mut &'s str) -> Result<(&'s str, &'s str)> {
//! let start = input.checkpoint();
//! if let Ok(output) = ("0b", parse_bin_digits).parse_next(input) {
//! return Ok(output);
//! }
//!
//! input.reset(&start);
//! if let Ok(output) = ("0o", parse_oct_digits).parse_next(input) {
//! return Ok(output);
//! }
//!
//! input.reset(&start);
//! if let Ok(output) = ("0d", parse_dec_digits).parse_next(input) {
//! return Ok(output);
//! }
//!
//! input.reset(&start);
//! ("0x", parse_hex_digits).parse_next(input)
//! }
//!
//! // ...
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//!
//! fn main() {
//! let mut input = "0x1a2b Hello";
//!
//! let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap();
//!
//! assert_eq!(input, " Hello");
//! assert_eq!(prefix, "0x");
//! assert_eq!(digits, "1a2b");
//!
//! assert!(parse_digits(&mut "ghiWorld").is_err());
//! }
//! ```
//!
//! <div class="warning">
//!
//! **Warning:** the above example is for illustrative purposes and relying on `Result::Ok` or
//! `Result::Err` can lead to incorrect behavior. This will be clarified in later when covering
//! [error handling][`chapter_7`#error-cuts]
//!
//! </div>
//!
//! [`opt`] is a parser that encapsulates this pattern of "retry on failure":
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! use winnow::combinator::opt;
//!
//! fn parse_digits<'s>(input: &mut &'s str) -> Result<(&'s str, &'s str)> {
//! if let Some(output) = opt(("0b", parse_bin_digits)).parse_next(input)? {
//! Ok(output)
//! } else if let Some(output) = opt(("0o", parse_oct_digits)).parse_next(input)? {
//! Ok(output)
//! } else if let Some(output) = opt(("0d", parse_dec_digits)).parse_next(input)? {
//! Ok(output)
//! } else {
//! ("0x", parse_hex_digits).parse_next(input)
//! }
//! }
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn main() {
//! # let mut input = "0x1a2b Hello";
//! #
//! # let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap();
//! #
//! # assert_eq!(input, " Hello");
//! # assert_eq!(prefix, "0x");
//! # assert_eq!(digits, "1a2b");
//! #
//! # assert!(parse_digits(&mut "ghiWorld").is_err());
//! # }
//! ```
//!
//! [`alt`] encapsulates this if/else-if ladder pattern, with the last case being the "else":
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! use winnow::combinator::alt;
//!
//! fn parse_digits<'s>(input: &mut &'s str) -> Result<(&'s str, &'s str)> {
//! alt((
//! ("0b", parse_bin_digits),
//! ("0o", parse_oct_digits),
//! ("0d", parse_dec_digits),
//! ("0x", parse_hex_digits),
//! )).parse_next(input)
//! }
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn main() {
//! # let mut input = "0x1a2b Hello";
//! #
//! # let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap();
//! #
//! # assert_eq!(input, " Hello");
//! # assert_eq!(prefix, "0x");
//! # assert_eq!(digits, "1a2b");
//! #
//! # assert!(parse_digits(&mut "ghiWorld").is_err());
//! # }
//! ```
//!
//! <div class="warning">
//!
//! **Note:** [`empty`] and [`fail`] are parsers that might be useful in the "else" case.
//!
//! </div>
//!
//! 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
//! [`dispatch`] macro:
//!
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! use winnow::combinator::dispatch;
//! use winnow::token::take;
//! use winnow::combinator::fail;
//!
//! fn parse_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! dispatch!(take(2usize);
//! "0b" => parse_bin_digits,
//! "0o" => parse_oct_digits,
//! "0d" => parse_dec_digits,
//! "0x" => parse_hex_digits,
//! _ => fail,
//! ).parse_next(input)
//! }
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('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());
//! # }
//! ```
//!
//! <div class="warning">
//!
//! **Note:** [`peek`] may be useful when [`dispatch`]ing from hints from each case's parser.
//!
//! </div>
//!
//! See [`combinator`] for more alternative parsers.
#![allow(unused_imports)]
use super::chapter_7;
use crate::combinator;
use crate::combinator::alt;
use crate::combinator::dispatch;
use crate::combinator::empty;
use crate::combinator::fail;
use crate::combinator::opt;
use crate::combinator::peek;
use crate::combinator::preceded;
use crate::stream::Stream;
pub use super::chapter_2 as previous;
pub use super::chapter_4 as next;
pub use crate::_tutorial as table_of_contents;
+109
View File
@@ -0,0 +1,109 @@
//! # Chapter 4: Parsers With Custom Return Types
//!
//! So far, we have seen mostly functions that take an `&str`, and return a
//! [`Result<&str>`]. Splitting strings into smaller strings and characters is certainly
//! useful, but it's not the only thing winnow is capable of!
//!
//! A useful operation when parsing is to convert between types; for example
//! parsing from `&str` to another primitive, like [`usize`].
//!
//! All we need to do for our parser to return a different type is to change
//! the type parameter of [`Result`] to the desired return type.
//! For example, to return a `usize`, return a `Result<usize>`.
//!
//! One winnow-native way of doing a type conversion is to use the
//! [`Parser::parse_to`] combinator
//! to convert from a successful parse to a particular type using [`FromStr`].
//!
//! The following code converts from a string containing a number to `usize`:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::ascii::digit1;
//! #
//! fn parse_digits(input: &mut &str) -> Result<usize> {
//! digit1
//! .parse_to()
//! .parse_next(input)
//! }
//!
//! fn main() {
//! let mut input = "1024 Hello";
//!
//! let output = parse_digits.parse_next(&mut input).unwrap();
//! assert_eq!(input, " Hello");
//! assert_eq!(output, 1024);
//!
//! assert!(parse_digits(&mut "Z").is_err());
//! }
//! ```
//!
//! `Parser::parse_to` is just a convenient form of [`Parser::try_map`] which we can use to handle
//! all radices of numbers:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! use winnow::combinator::dispatch;
//! use winnow::token::take;
//! use winnow::combinator::fail;
//!
//! fn parse_digits(input: &mut &str) -> Result<usize> {
//! dispatch!(take(2usize);
//! "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
//! "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
//! "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
//! "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
//! _ => fail,
//! ).parse_next(input)
//! }
//!
//! // ...
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('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, 0x1a2b);
//!
//! assert!(parse_digits(&mut "ghiWorld").is_err());
//! }
//! ```
//!
//! See also [`Parser`] for more output-modifying parsers.
#![allow(unused_imports)]
use crate::Parser;
use crate::Result;
use std::str::FromStr;
pub use super::chapter_3 as previous;
pub use super::chapter_5 as next;
pub use crate::_tutorial as table_of_contents;
+300
View File
@@ -0,0 +1,300 @@
//! # Chapter 5: Repetition
//!
//! In [`chapter_3`], we covered how to sequence different parsers into a tuple but sometimes you need to run a
//! single parser multiple times, collecting the result into a container, like `Vec`.
//!
//! Let's collect the result of `parse_digits`:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! # use winnow::combinator::dispatch;
//! # use winnow::token::take;
//! # use winnow::combinator::fail;
//! use winnow::combinator::opt;
//! use winnow::combinator::repeat;
//! use winnow::combinator::terminated;
//!
//! fn parse_list(input: &mut &str) -> Result<Vec<usize>> {
//! let mut list = Vec::new();
//! while let Some(output) = opt(terminated(parse_digits, opt(','))).parse_next(input)? {
//! list.push(output);
//! }
//! Ok(list)
//! }
//!
//! // ...
//! # fn parse_digits(input: &mut &str) -> Result<usize> {
//! # dispatch!(take(2usize);
//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
//! # _ => fail,
//! # ).parse_next(input)
//! # }
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//!
//! fn main() {
//! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
//!
//! let digits = parse_list.parse_next(&mut input).unwrap();
//!
//! assert_eq!(input, " Hello");
//! assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]);
//!
//! assert!(parse_digits(&mut "ghiWorld").is_err());
//! }
//! ```
//!
//! We can implement this declaratively with [`repeat`]:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! # use winnow::combinator::dispatch;
//! # use winnow::token::take;
//! # use winnow::combinator::fail;
//! use winnow::combinator::opt;
//! use winnow::combinator::repeat;
//! use winnow::combinator::terminated;
//!
//! fn parse_list(input: &mut &str) -> Result<Vec<usize>> {
//! repeat(0..,
//! terminated(parse_digits, opt(','))
//! ).parse_next(input)
//! }
//! #
//! # fn parse_digits(input: &mut &str) -> Result<usize> {
//! # dispatch!(take(2usize);
//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
//! # _ => fail,
//! # ).parse_next(input)
//! # }
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn main() {
//! # let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
//! #
//! # let digits = parse_list.parse_next(&mut input).unwrap();
//! #
//! # assert_eq!(input, " Hello");
//! # assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]);
//! #
//! # assert!(parse_digits(&mut "ghiWorld").is_err());
//! # }
//! ```
//!
//! You'll notice that the above allows trailing `,`. However, if that's not desired, it
//! can easily be fixed by using [`separated`] instead of [`repeat`]:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! # use winnow::combinator::dispatch;
//! # use winnow::token::take;
//! # use winnow::combinator::fail;
//! use winnow::combinator::separated;
//!
//! fn parse_list(input: &mut &str) -> Result<Vec<usize>> {
//! separated(0.., parse_digits, ",").parse_next(input)
//! }
//!
//! // ...
//! # fn parse_digits(input: &mut &str) -> Result<usize> {
//! # dispatch!(take(2usize);
//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
//! # _ => fail,
//! # ).parse_next(input)
//! # }
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//!
//! fn main() {
//! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
//!
//! let digits = parse_list.parse_next(&mut input).unwrap();
//!
//! assert_eq!(input, " Hello");
//! assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]);
//!
//! assert!(parse_digits(&mut "ghiWorld").is_err());
//! }
//! ```
//!
//! If you look closely at [`separated`] and [`repeat`], they aren't limited to collecting
//! the result into a `Vec`, but rather anything that implements the [`Accumulate`] trait.
//! [`Accumulate`] is for instance also implemented for [`HashSet`], [`String`] and `()`.
//!
//! This lets us build more complex parsers than we did in
//! [`chapter_2`] by accumulating the results into a `()` and [`take`][Parser::take]-ing
//! the consumed input.
//!
//! `take` works by
//! 1. Creating a [`checkpoint`][Stream::checkpoint]
//! 2. Running the inner parser, in our case the `parse_list` parser, which will advance the input
//! 3. Returning the slice from the first checkpoint to the current position.
//!
//! Since the result of `parse_list` gets thrown away, we
//! accumulates into a `()` to not waste work creating an unused `Vec`.
//!
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! # use winnow::combinator::dispatch;
//! # use winnow::token::take;
//! # use winnow::combinator::fail;
//! # use winnow::combinator::separated;
//! #
//! fn take_list<'s>(input: &mut &'s str) -> Result<&'s str> {
//! parse_list.take().parse_next(input)
//! }
//!
//! fn parse_list(input: &mut &str) -> Result<()> {
//! separated(0.., parse_digits, ",").parse_next(input)
//! }
//!
//! # fn parse_digits(input: &mut &str) -> Result<usize> {
//! # dispatch!(take(2usize);
//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
//! # _ => fail,
//! # ).parse_next(input)
//! # }
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//!
//! fn main() {
//! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
//!
//! let digits = take_list.parse_next(&mut input).unwrap();
//!
//! assert_eq!(input, " Hello");
//! assert_eq!(digits, "0x1a2b,0x3c4d,0x5e6f");
//!
//! assert!(parse_digits(&mut "ghiWorld").is_err());
//! }
//! ```
//! See [`combinator`] for more repetition parsers.
#![allow(unused_imports)]
use super::chapter_2;
use super::chapter_3;
use crate::combinator;
use crate::combinator::repeat;
use crate::combinator::separated;
use crate::stream::Accumulate;
use crate::stream::Stream;
use crate::Parser;
use std::collections::HashSet;
pub use super::chapter_4 as previous;
pub use super::chapter_6 as next;
pub use crate::_tutorial as table_of_contents;
+120
View File
@@ -0,0 +1,120 @@
//! # Chapter 6: Integrating the Parser
//!
//! So far, we've highlighted how to incrementally parse, but how do we bring this all together
//! into our application?
//!
//! Parsers we've been working with look like:
//! ```rust
//! # use winnow::error::ContextError;
//! # use winnow::Parser;
//! use winnow::Result;
//!
//! pub fn parser<'s>(input: &mut &'s str) -> Result<&'s str> {
//! // ...
//! # Ok("")
//! }
//! ```
//! 1. We have to decide what to do about the "remainder" of the `input`.
//! 2. The [`Result`] may not be compatible with the rest of the Rust ecosystem.
//! Normally, Rust applications want errors that are `std::error::Error + Send + Sync + 'static`
//! meaning:
//! - They implement the [`std::error::Error`] trait
//! - They can be sent across threads
//! - They are safe to be referenced across threads
//! - They do not borrow
//!
//! winnow provides [`Parser::parse`] to help with this:
//! - Ensures we hit [`eof`]
//! - Wraps the error in [`ParseError`]
//! - For simple cases, [`ParseError`] provides a [`std::fmt::Display`] impl to render the error.
//! - For more involved cases, [`ParseError`] provides the original [`input`][ParseError::input] and the
//! [`offset`][ParseError::offset] of where it failed so you can capture this information in
//! your error, [rendering it as you wish][chapter_7#error-adaptation-and-rendering].
//! - Converts from [`ModalResult`] to [`Result`] (if used, more on this in [`chapter_7`])
//!
//! However, [`ParseError`] will still need some level of adaptation to integrate with your
//! application's error type (like with `?`).
//!
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! # use winnow::combinator::dispatch;
//! # use winnow::token::take;
//! # use winnow::combinator::fail;
//! use winnow::Parser;
//!
//! #[derive(Debug, PartialEq, Eq)]
//! pub struct Hex(usize);
//!
//! impl std::str::FromStr for Hex {
//! type Err = anyhow::Error;
//!
//! fn from_str(input: &str) -> Result<Self, Self::Err> {
//! parse_digits
//! .map(Hex)
//! .parse(input)
//! .map_err(|e| anyhow::format_err!("{e}"))
//! }
//! }
//!
//! // ...
//! # fn parse_digits<'s>(input: &mut &'s str) -> Result<usize> {
//! # dispatch!(take(2usize);
//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
//! # _ => fail,
//! # ).parse_next(input)
//! # }
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//!
//! fn main() {
//! let input = "0x1a2b";
//! assert_eq!(input.parse::<Hex>().unwrap(), Hex(0x1a2b));
//!
//! let input = "0x1a2b Hello";
//! assert!(input.parse::<Hex>().is_err());
//! let input = "ghiHello";
//! assert!(input.parse::<Hex>().is_err());
//! }
//! ```
#![allow(unused_imports)]
use super::chapter_1;
use super::chapter_7;
use crate::combinator::eof;
use crate::error::ErrMode;
use crate::error::ParseError;
use crate::ModalResult;
use crate::Parser;
pub use super::chapter_5 as previous;
pub use super::chapter_7 as next;
pub use crate::_tutorial as table_of_contents;
+736
View File
@@ -0,0 +1,736 @@
//! # Chapter 7: Error Reporting
//!
//! ## Context
//!
//! With [`Parser::parse`] we get errors that point to the failure but don't explain the reason for
//! the failure:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! # use winnow::combinator::alt;
//! # use winnow::token::take;
//! # use winnow::combinator::fail;
//! # use winnow::Parser;
//! #
//! # #[derive(Debug, PartialEq, Eq)]
//! # pub struct Hex(usize);
//! #
//! # impl std::str::FromStr for Hex {
//! # type Err = String;
//! #
//! # fn from_str(input: &str) -> Result<Self, Self::Err> {
//! # parse_digits
//! # .try_map(|(t, v)| match t {
//! # "0b" => usize::from_str_radix(v, 2),
//! # "0o" => usize::from_str_radix(v, 8),
//! # "0d" => usize::from_str_radix(v, 10),
//! # "0x" => usize::from_str_radix(v, 16),
//! # _ => unreachable!("`parse_digits` doesn't return `{t}`"),
//! # })
//! # .map(Hex)
//! # .parse(input)
//! # .map_err(|e| e.to_string())
//! # }
//! # }
//! #
//! // ...
//!
//! # fn parse_digits<'s>(input: &mut &'s str) -> Result<(&'s str, &'s str)> {
//! # alt((
//! # ("0b", parse_bin_digits),
//! # ("0o", parse_oct_digits),
//! # ("0d", parse_dec_digits),
//! # ("0x", parse_hex_digits),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//! fn main() {
//! let input = "0xZZ";
//! let error = "\
//! 0xZZ
//! ^
//! ";
//! assert_eq!(input.parse::<Hex>().unwrap_err(), error);
//! }
//! ```
//!
//! Back in [`chapter_1`], we glossed over the `Err` variant of [`Result`]. `Result<O>` is
//! actually short for `Result<O, E=ContextError>` where [`ContextError`] is a relatively cheap
//! way of building up reasonable errors for humans.
//!
//! You can use [`Parser::context`] to annotate the error with custom types
//! while unwinding to further clarify the error:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! # use winnow::combinator::alt;
//! # use winnow::token::take;
//! # use winnow::combinator::fail;
//! # use winnow::Parser;
//! use winnow::error::StrContext;
//! use winnow::error::StrContextValue;
//!
//! #
//! # #[derive(Debug, PartialEq, Eq)]
//! # pub struct Hex(usize);
//! #
//! # impl std::str::FromStr for Hex {
//! # type Err = String;
//! #
//! # fn from_str(input: &str) -> Result<Self, Self::Err> {
//! # parse_digits
//! # .try_map(|(t, v)| match t {
//! # "0b" => usize::from_str_radix(v, 2),
//! # "0o" => usize::from_str_radix(v, 8),
//! # "0d" => usize::from_str_radix(v, 10),
//! # "0x" => usize::from_str_radix(v, 16),
//! # _ => unreachable!("`parse_digits` doesn't return `{t}`"),
//! # })
//! # .map(Hex)
//! # .parse(input)
//! # .map_err(|e| e.to_string())
//! # }
//! # }
//! #
//! fn parse_digits<'s>(input: &mut &'s str) -> Result<(&'s str, &'s str)> {
//! alt((
//! ("0b", parse_bin_digits)
//! .context(StrContext::Label("digit"))
//! .context(StrContext::Expected(StrContextValue::Description("binary"))),
//! ("0o", parse_oct_digits)
//! .context(StrContext::Label("digit"))
//! .context(StrContext::Expected(StrContextValue::Description("octal"))),
//! ("0d", parse_dec_digits)
//! .context(StrContext::Label("digit"))
//! .context(StrContext::Expected(StrContextValue::Description("decimal"))),
//! ("0x", parse_hex_digits)
//! .context(StrContext::Label("digit"))
//! .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))),
//! )).parse_next(input)
//! }
//!
//! // ...
//!
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//! fn main() {
//! let input = "0xZZ";
//! let error = "\
//! 0xZZ
//! ^
//! invalid digit
//! expected hexadecimal";
//! assert_eq!(input.parse::<Hex>().unwrap_err(), error);
//! }
//! ```
//!
//! If you remember back to [`chapter_3`], [`alt`] will only report the last error.
//! So if the parsers fail for any reason, like a bad radix, it will be reported as an invalid
//! hexadecimal value:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! # use winnow::combinator::alt;
//! # use winnow::token::take;
//! # use winnow::combinator::fail;
//! # use winnow::Parser;
//! # use winnow::error::StrContext;
//! # use winnow::error::StrContextValue;
//! #
//! #
//! # #[derive(Debug, PartialEq, Eq)]
//! # pub struct Hex(usize);
//! #
//! # impl std::str::FromStr for Hex {
//! # type Err = String;
//! #
//! # fn from_str(input: &str) -> Result<Self, Self::Err> {
//! # parse_digits
//! # .try_map(|(t, v)| match t {
//! # "0b" => usize::from_str_radix(v, 2),
//! # "0o" => usize::from_str_radix(v, 8),
//! # "0d" => usize::from_str_radix(v, 10),
//! # "0x" => usize::from_str_radix(v, 16),
//! # _ => unreachable!("`parse_digits` doesn't return `{t}`"),
//! # })
//! # .map(Hex)
//! # .parse(input)
//! # .map_err(|e| e.to_string())
//! # }
//! # }
//! #
//! # fn parse_digits<'s>(input: &mut &'s str) -> Result<(&'s str, &'s str)> {
//! # alt((
//! # ("0b", parse_bin_digits)
//! # .context(StrContext::Label("digit"))
//! # .context(StrContext::Expected(StrContextValue::Description("binary"))),
//! # ("0o", parse_oct_digits)
//! # .context(StrContext::Label("digit"))
//! # .context(StrContext::Expected(StrContextValue::Description("octal"))),
//! # ("0d", parse_dec_digits)
//! # .context(StrContext::Label("digit"))
//! # .context(StrContext::Expected(StrContextValue::Description("decimal"))),
//! # ("0x", parse_hex_digits)
//! # .context(StrContext::Label("digit"))
//! # .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//! fn main() {
//! let input = "100";
//! let error = "\
//! 100
//! ^
//! invalid digit
//! expected hexadecimal";
//! assert_eq!(input.parse::<Hex>().unwrap_err(), error);
//! }
//! ```
//! We can improve this with [`fail`]:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! # use winnow::combinator::alt;
//! # use winnow::token::take;
//! # use winnow::combinator::fail;
//! # use winnow::Parser;
//! use winnow::error::StrContext;
//! use winnow::error::StrContextValue;
//!
//! #
//! # #[derive(Debug, PartialEq, Eq)]
//! # pub struct Hex(usize);
//! #
//! # impl std::str::FromStr for Hex {
//! # type Err = String;
//! #
//! # fn from_str(input: &str) -> Result<Self, Self::Err> {
//! # parse_digits
//! # .try_map(|(t, v)| match t {
//! # "0b" => usize::from_str_radix(v, 2),
//! # "0o" => usize::from_str_radix(v, 8),
//! # "0d" => usize::from_str_radix(v, 10),
//! # "0x" => usize::from_str_radix(v, 16),
//! # _ => unreachable!("`parse_digits` doesn't return `{t}`"),
//! # })
//! # .map(Hex)
//! # .parse(input)
//! # .map_err(|e| e.to_string())
//! # }
//! # }
//! #
//! fn parse_digits<'s>(input: &mut &'s str) -> Result<(&'s str, &'s str)> {
//! alt((
//! ("0b", parse_bin_digits)
//! .context(StrContext::Label("digit"))
//! .context(StrContext::Expected(StrContextValue::Description("binary"))),
//! ("0o", parse_oct_digits)
//! .context(StrContext::Label("digit"))
//! .context(StrContext::Expected(StrContextValue::Description("octal"))),
//! ("0d", parse_dec_digits)
//! .context(StrContext::Label("digit"))
//! .context(StrContext::Expected(StrContextValue::Description("decimal"))),
//! ("0x", parse_hex_digits)
//! .context(StrContext::Label("digit"))
//! .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))),
//! fail
//! .context(StrContext::Label("radix prefix"))
//! .context(StrContext::Expected(StrContextValue::StringLiteral("0b")))
//! .context(StrContext::Expected(StrContextValue::StringLiteral("0o")))
//! .context(StrContext::Expected(StrContextValue::StringLiteral("0d")))
//! .context(StrContext::Expected(StrContextValue::StringLiteral("0x"))),
//! )).parse_next(input)
//! }
//!
//! // ...
//!
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//! fn main() {
//! let input = "100";
//! let error = "\
//! 100
//! ^
//! invalid radix prefix
//! expected `0b`, `0o`, `0d`, `0x`";
//! assert_eq!(input.parse::<Hex>().unwrap_err(), error);
//! }
//! ```
//!
//! ## Error Cuts
//!
//! We still have the issue that we are falling-through when the radix is valid but the digits
//! don't match it:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::Result;
//! # use winnow::token::take_while;
//! # use winnow::combinator::alt;
//! # use winnow::token::take;
//! # use winnow::combinator::fail;
//! # use winnow::Parser;
//! # use winnow::error::StrContext;
//! # use winnow::error::StrContextValue;
//! #
//! #
//! # #[derive(Debug, PartialEq, Eq)]
//! # pub struct Hex(usize);
//! #
//! # impl std::str::FromStr for Hex {
//! # type Err = String;
//! #
//! # fn from_str(input: &str) -> Result<Self, Self::Err> {
//! # parse_digits
//! # .try_map(|(t, v)| match t {
//! # "0b" => usize::from_str_radix(v, 2),
//! # "0o" => usize::from_str_radix(v, 8),
//! # "0d" => usize::from_str_radix(v, 10),
//! # "0x" => usize::from_str_radix(v, 16),
//! # _ => unreachable!("`parse_digits` doesn't return `{t}`"),
//! # })
//! # .map(Hex)
//! # .parse(input)
//! # .map_err(|e| e.to_string())
//! # }
//! # }
//! #
//! # fn parse_digits<'s>(input: &mut &'s str) -> Result<(&'s str, &'s str)> {
//! # alt((
//! # ("0b", parse_bin_digits)
//! # .context(StrContext::Label("digit"))
//! # .context(StrContext::Expected(StrContextValue::Description("binary"))),
//! # ("0o", parse_oct_digits)
//! # .context(StrContext::Label("digit"))
//! # .context(StrContext::Expected(StrContextValue::Description("octal"))),
//! # ("0d", parse_dec_digits)
//! # .context(StrContext::Label("digit"))
//! # .context(StrContext::Expected(StrContextValue::Description("decimal"))),
//! # ("0x", parse_hex_digits)
//! # .context(StrContext::Label("digit"))
//! # .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))),
//! # fail
//! # .context(StrContext::Label("radix prefix"))
//! # .context(StrContext::Expected(StrContextValue::StringLiteral("0b")))
//! # .context(StrContext::Expected(StrContextValue::StringLiteral("0o")))
//! # .context(StrContext::Expected(StrContextValue::StringLiteral("0d")))
//! # .context(StrContext::Expected(StrContextValue::StringLiteral("0x"))),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//! fn main() {
//! let input = "0b5";
//! let error = "\
//! 0b5
//! ^
//! invalid radix prefix
//! expected `0b`, `0o`, `0d`, `0x`";
//! assert_eq!(input.parse::<Hex>().unwrap_err(), error);
//! }
//! ```
//!
//! Winnow provides an error wrapper, [`ErrMode<ContextError>`], so different failure modes can affect parsing.
//! [`ErrMode`] is an enum with [`Backtrack`] and [`Cut`] variants (ignore [`Incomplete`] as its only
//! relevant for [streaming][_topic::stream]). By default, errors are [`Backtrack`], meaning that
//! other parsing branches will be attempted on failure, like the next case of an [`alt`]. [`Cut`]
//! shortcircuits all other branches, immediately reporting the error.
//!
//! To make [`ErrMode`] more convenient, Winnow provides [`ModalResult`]:
//! ```rust
//! # use winnow::error::ContextError;
//! # use winnow::error::ErrMode;
//! pub type ModalResult<O, E = ContextError> = Result<O, ErrMode<E>>;
//! ```
//!
//! So we can get the correct `context` by changing to [`ModalResult`] and adding [`cut_err`]:
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::token::take_while;
//! # use winnow::combinator::alt;
//! # use winnow::token::take;
//! # use winnow::combinator::fail;
//! # use winnow::Parser;
//! # use winnow::error::StrContext;
//! # use winnow::error::StrContextValue;
//! use winnow::combinator::cut_err;
//!
//! #
//! # #[derive(Debug, PartialEq, Eq)]
//! # pub struct Hex(usize);
//! #
//! # impl std::str::FromStr for Hex {
//! # type Err = String;
//! #
//! # fn from_str(input: &str) -> Result<Self, Self::Err> {
//! # parse_digits
//! # .try_map(|(t, v)| match t {
//! # "0b" => usize::from_str_radix(v, 2),
//! # "0o" => usize::from_str_radix(v, 8),
//! # "0d" => usize::from_str_radix(v, 10),
//! # "0x" => usize::from_str_radix(v, 16),
//! # _ => unreachable!("`parse_digits` doesn't return `{t}`"),
//! # })
//! # .map(Hex)
//! # .parse(input)
//! # .map_err(|e| e.to_string())
//! # }
//! # }
//! #
//! fn parse_digits<'s>(input: &mut &'s str) -> ModalResult<(&'s str, &'s str)> {
//! alt((
//! ("0b", cut_err(parse_bin_digits))
//! .context(StrContext::Label("digit"))
//! .context(StrContext::Expected(StrContextValue::Description("binary"))),
//! ("0o", cut_err(parse_oct_digits))
//! .context(StrContext::Label("digit"))
//! .context(StrContext::Expected(StrContextValue::Description("octal"))),
//! ("0d", cut_err(parse_dec_digits))
//! .context(StrContext::Label("digit"))
//! .context(StrContext::Expected(StrContextValue::Description("decimal"))),
//! ("0x", cut_err(parse_hex_digits))
//! .context(StrContext::Label("digit"))
//! .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))),
//! fail
//! .context(StrContext::Label("radix prefix"))
//! .context(StrContext::Expected(StrContextValue::StringLiteral("0b")))
//! .context(StrContext::Expected(StrContextValue::StringLiteral("0o")))
//! .context(StrContext::Expected(StrContextValue::StringLiteral("0d")))
//! .context(StrContext::Expected(StrContextValue::StringLiteral("0x"))),
//! )).parse_next(input)
//! }
//!
//! // ...
//!
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//! fn main() {
//! let input = "0b5";
//! let error = "\
//! 0b5
//! ^
//! invalid digit
//! expected binary";
//! assert_eq!(input.parse::<Hex>().unwrap_err(), error);
//! }
//! ```
//!
//! ## Error Adaptation and Rendering
//!
//! While Winnow can provide basic rendering of errors, your application can have various demands
//! beyond the basics provided like
//! - Correctly reporting columns with unicode
//! - Conforming to a specific layout
//!
//! For example, to get rustc-like errors with [`annotate-snippets`](https://crates.io/crates/annotate-snippets):
//! ```rust
//! # use winnow::prelude::*;
//! # use winnow::token::take_while;
//! # use winnow::combinator::alt;
//! # use winnow::token::take;
//! # use winnow::combinator::fail;
//! # use winnow::Parser;
//! # use winnow::error::ParseError;
//! # use winnow::error::ContextError;
//! # use winnow::error::StrContext;
//! # use winnow::error::StrContextValue;
//! # use winnow::combinator::cut_err;
//! #
//! #
//! #[derive(Debug, PartialEq, Eq)]
//! pub struct Hex(usize);
//!
//! impl std::str::FromStr for Hex {
//! type Err = HexError;
//!
//! fn from_str(input: &str) -> Result<Self, Self::Err> {
//! // ...
//! # parse_digits
//! # .try_map(|(t, v)| match t {
//! # "0b" => usize::from_str_radix(v, 2),
//! # "0o" => usize::from_str_radix(v, 8),
//! # "0d" => usize::from_str_radix(v, 10),
//! # "0x" => usize::from_str_radix(v, 16),
//! # _ => unreachable!("`parse_digits` doesn't return `{t}`"),
//! # })
//! # .map(Hex)
//! .parse(input)
//! .map_err(|e| HexError::from_parse(e))
//! }
//! }
//!
//! #[derive(Debug)]
//! pub struct HexError {
//! message: String,
//! // Byte spans are tracked, rather than line and column.
//! // This makes it easier to operate on programmatically
//! // and doesn't limit us to one definition for column count
//! // which can depend on the output medium and application.
//! span: std::ops::Range<usize>,
//! input: String,
//! }
//!
//! impl HexError {
//! // Avoiding `From` so `winnow` types don't become part of our public API
//! fn from_parse(error: ParseError<&str, ContextError>) -> Self {
//! // The default renderer for `ContextError` is still used but that can be
//! // customized as well to better fit your needs.
//! let message = error.inner().to_string();
//! let input = (*error.input()).to_owned();
//! // Assume the error span is only for the first `char`.
//! let span = error.char_span();
//! Self {
//! message,
//! span,
//! input,
//! }
//! }
//! }
//!
//! impl std::fmt::Display for HexError {
//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//! let message = annotate_snippets::Level::Error.title(&self.message)
//! .snippet(annotate_snippets::Snippet::source(&self.input)
//! .fold(true)
//! .annotation(annotate_snippets::Level::Error.span(self.span.clone()))
//! );
//! let renderer = annotate_snippets::Renderer::plain();
//! let rendered = renderer.render(message);
//! rendered.fmt(f)
//! }
//! }
//!
//! impl std::error::Error for HexError {}
//!
//! # fn parse_digits<'s>(input: &mut &'s str) -> ModalResult<(&'s str, &'s str)> {
//! # alt((
//! # ("0b", cut_err(parse_bin_digits))
//! # .context(StrContext::Label("digit"))
//! # .context(StrContext::Expected(StrContextValue::Description("binary"))),
//! # ("0o", cut_err(parse_oct_digits))
//! # .context(StrContext::Label("digit"))
//! # .context(StrContext::Expected(StrContextValue::Description("octal"))),
//! # ("0d", cut_err(parse_dec_digits))
//! # .context(StrContext::Label("digit"))
//! # .context(StrContext::Expected(StrContextValue::Description("decimal"))),
//! # ("0x", cut_err(parse_hex_digits))
//! # .context(StrContext::Label("digit"))
//! # .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))),
//! # fail
//! # .context(StrContext::Label("radix prefix"))
//! # .context(StrContext::Expected(StrContextValue::StringLiteral("0b")))
//! # .context(StrContext::Expected(StrContextValue::StringLiteral("0o")))
//! # .context(StrContext::Expected(StrContextValue::StringLiteral("0d")))
//! # .context(StrContext::Expected(StrContextValue::StringLiteral("0x"))),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! # take_while(1.., (
//! # ('0'..='1'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! # take_while(1.., (
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! # take_while(1.., (
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//! fn main() {
//! let input = "0b5";
//! let error = "\
//! error: invalid digit
//! expected binary
//! |
//! 1 | 0b5
//! | ^
//! |";
//! assert_eq!(input.parse::<Hex>().unwrap_err().to_string(), error);
//! }
//! ```
//!
//! To add spans to your parsed data for inclusion in semantic errors, see [`Parser::with_span`].
//!
//! For richer syntactic errors with spans,
//! consider separating lexing and parsing and annotating your tokens with [`Parser::with_span`].
#![allow(unused_imports)]
use super::chapter_1;
use super::chapter_3;
use crate::combinator::alt;
use crate::combinator::cut_err;
use crate::combinator::fail;
use crate::error::ContextError;
use crate::error::ErrMode;
use crate::error::ErrMode::*;
use crate::ModalResult;
use crate::Parser;
use crate::Result;
use crate::_topic;
pub use super::chapter_6 as previous;
pub use super::chapter_8 as next;
pub use crate::_tutorial as table_of_contents;
+37
View File
@@ -0,0 +1,37 @@
//! # Chapter 8: Debugging
//!
//! When things inevitably go wrong, you can introspect the parsing state by running your test case
//! with `--features winnow/debug`.
//!
//! For example, the trace output of an [escaped string parser][crate::_topic::language#escaped-strings]:
//! ![Trace output from string example](https://raw.githubusercontent.com/winnow-rs/winnow/main/assets/trace.svg "Example output")
//!
//! You can extend your own parsers to show up by wrapping their body with
//! [`trace`][crate::combinator::trace]. Going back to [`do_nothing_parser`][super::chapter_1].
//! ```rust
//! # use winnow::ModalResult;
//! # use winnow::Parser;
//! use winnow::combinator::trace;
//!
//! pub fn do_nothing_parser<'s>(input: &mut &'s str) -> ModalResult<&'s str> {
//! trace(
//! "do_nothing_parser",
//! |i: &mut _| Ok("")
//! ).parse_next(input)
//! }
//! #
//! # fn main() {
//! # let mut input = "0x1a2b Hello";
//! #
//! # let output = do_nothing_parser.parse_next(&mut input).unwrap();
//! # // Same as:
//! # // let output = do_nothing_parser(&mut input).unwrap();
//! #
//! # assert_eq!(input, "0x1a2b Hello");
//! # assert_eq!(output, "");
//! # }
//! ```
pub use super::chapter_7 as previous;
pub use crate::_topic as next;
pub use crate::_tutorial as table_of_contents;
+14
View File
@@ -0,0 +1,14 @@
//! # Tutorial
//!
//! Table of Contents
#![allow(clippy::std_instead_of_core)]
pub mod chapter_0;
pub mod chapter_1;
pub mod chapter_2;
pub mod chapter_3;
pub mod chapter_4;
pub mod chapter_5;
pub mod chapter_6;
pub mod chapter_7;
pub mod chapter_8;
+207
View File
@@ -0,0 +1,207 @@
use crate::error::ParserError;
use crate::stream::{Compare, CompareResult};
use crate::stream::{SliceLen, Stream, StreamIsPartial};
use crate::Parser;
use crate::Result;
/// Mark a value as case-insensitive for ASCII characters
///
/// # Example
/// ```rust
/// # use winnow::prelude::*;
/// # use winnow::ascii::Caseless;
///
/// fn parser<'s>(s: &mut &'s str) -> ModalResult<&'s str> {
/// Caseless("hello").parse_next(s)
/// }
///
/// assert_eq!(parser.parse_peek("Hello, World!"), Ok((", World!", "Hello")));
/// assert_eq!(parser.parse_peek("hello, World!"), Ok((", World!", "hello")));
/// assert_eq!(parser.parse_peek("HeLlo, World!"), Ok((", World!", "HeLlo")));
/// assert!(parser.parse_peek("Some").is_err());
/// assert!(parser.parse_peek("").is_err());
/// ```
#[derive(Copy, Clone, Debug)]
pub struct Caseless<T>(pub T);
impl Caseless<&str> {
/// Get the byte-representation of this case-insensitive value
#[inline(always)]
pub fn as_bytes(&self) -> Caseless<&[u8]> {
Caseless(self.0.as_bytes())
}
}
impl<S: SliceLen> SliceLen for Caseless<S> {
#[inline(always)]
fn slice_len(&self) -> usize {
self.0.slice_len()
}
}
impl<'b> Compare<Caseless<&'b [u8]>> for &[u8] {
#[inline]
fn compare(&self, t: Caseless<&'b [u8]>) -> CompareResult {
if t.0
.iter()
.zip(*self)
.any(|(a, b)| !a.eq_ignore_ascii_case(b))
{
CompareResult::Error
} else if self.len() < t.slice_len() {
CompareResult::Incomplete
} else {
CompareResult::Ok(t.slice_len())
}
}
}
impl<const LEN: usize> Compare<Caseless<[u8; LEN]>> for &[u8] {
#[inline(always)]
fn compare(&self, t: Caseless<[u8; LEN]>) -> CompareResult {
self.compare(Caseless(&t.0[..]))
}
}
impl<'b, const LEN: usize> Compare<Caseless<&'b [u8; LEN]>> for &[u8] {
#[inline(always)]
fn compare(&self, t: Caseless<&'b [u8; LEN]>) -> CompareResult {
self.compare(Caseless(&t.0[..]))
}
}
impl<'b> Compare<Caseless<&'b str>> for &[u8] {
#[inline(always)]
fn compare(&self, t: Caseless<&'b str>) -> CompareResult {
self.compare(Caseless(t.0.as_bytes()))
}
}
impl Compare<Caseless<u8>> for &[u8] {
#[inline]
fn compare(&self, t: Caseless<u8>) -> CompareResult {
match self.first() {
Some(c) if t.0.eq_ignore_ascii_case(c) => CompareResult::Ok(t.slice_len()),
Some(_) => CompareResult::Error,
None => CompareResult::Incomplete,
}
}
}
impl Compare<Caseless<char>> for &[u8] {
#[inline(always)]
fn compare(&self, t: Caseless<char>) -> CompareResult {
self.compare(Caseless(t.0.encode_utf8(&mut [0; 4]).as_bytes()))
}
}
impl<'b> Compare<Caseless<&'b str>> for &str {
#[inline(always)]
fn compare(&self, t: Caseless<&'b str>) -> CompareResult {
self.as_bytes().compare(t.as_bytes())
}
}
impl Compare<Caseless<char>> for &str {
#[inline(always)]
fn compare(&self, t: Caseless<char>) -> CompareResult {
self.as_bytes().compare(t)
}
}
/// This is a shortcut for [`literal`][crate::token::literal].
///
/// # Example
/// ```rust
/// # use winnow::prelude::*;
/// # use winnow::{error::ErrMode, error::ContextError, error::Needed};
/// # use winnow::combinator::alt;
/// # use winnow::token::take;
/// use winnow::ascii::Caseless;
///
/// fn parser<'s>(s: &mut &'s [u8]) -> ModalResult<&'s [u8]> {
/// alt((Caseless(&"hello"[..]), take(5usize))).parse_next(s)
/// }
///
/// assert_eq!(parser.parse_peek(&b"Hello, World!"[..]), Ok((&b", World!"[..], &b"Hello"[..])));
/// assert_eq!(parser.parse_peek(&b"hello, World!"[..]), Ok((&b", World!"[..], &b"hello"[..])));
/// assert_eq!(parser.parse_peek(&b"HeLlo, World!"[..]), Ok((&b", World!"[..], &b"HeLlo"[..])));
/// assert_eq!(parser.parse_peek(&b"Something"[..]), Ok((&b"hing"[..], &b"Somet"[..])));
/// assert!(parser.parse_peek(&b"Some"[..]).is_err());
/// assert!(parser.parse_peek(&b""[..]).is_err());
/// ```
impl<'s, I, E: ParserError<I>> Parser<I, <I as Stream>::Slice, E> for Caseless<&'s [u8]>
where
I: Compare<Caseless<&'s [u8]>> + StreamIsPartial,
I: Stream,
{
#[inline(always)]
fn parse_next(&mut self, i: &mut I) -> Result<<I as Stream>::Slice, E> {
crate::token::literal(*self).parse_next(i)
}
}
/// This is a shortcut for [`literal`][crate::token::literal].
///
/// # Example
/// ```rust
/// # use winnow::prelude::*;
/// # use winnow::{error::ErrMode, error::ContextError, error::Needed};
/// # use winnow::combinator::alt;
/// # use winnow::token::take;
/// use winnow::ascii::Caseless;
///
/// fn parser<'s>(s: &mut &'s [u8]) -> ModalResult<&'s [u8]> {
/// alt((Caseless(b"hello"), take(5usize))).parse_next(s)
/// }
///
/// assert_eq!(parser.parse_peek(&b"Hello, World!"[..]), Ok((&b", World!"[..], &b"Hello"[..])));
/// assert_eq!(parser.parse_peek(&b"hello, World!"[..]), Ok((&b", World!"[..], &b"hello"[..])));
/// assert_eq!(parser.parse_peek(&b"HeLlo, World!"[..]), Ok((&b", World!"[..], &b"HeLlo"[..])));
/// assert_eq!(parser.parse_peek(&b"Something"[..]), Ok((&b"hing"[..], &b"Somet"[..])));
/// assert!(parser.parse_peek(&b"Some"[..]).is_err());
/// assert!(parser.parse_peek(&b""[..]).is_err());
/// ```
impl<'s, I, E: ParserError<I>, const N: usize> Parser<I, <I as Stream>::Slice, E>
for Caseless<&'s [u8; N]>
where
I: Compare<Caseless<&'s [u8; N]>> + StreamIsPartial,
I: Stream,
{
#[inline(always)]
fn parse_next(&mut self, i: &mut I) -> Result<<I as Stream>::Slice, E> {
crate::token::literal(*self).parse_next(i)
}
}
/// This is a shortcut for [`literal`][crate::token::literal].
///
/// # Example
/// ```rust
/// # use winnow::prelude::*;
/// # use winnow::{error::ErrMode, error::ContextError};
/// # use winnow::combinator::alt;
/// # use winnow::token::take;
/// # use winnow::ascii::Caseless;
///
/// fn parser<'s>(s: &mut &'s str) -> ModalResult<&'s str> {
/// alt((Caseless("hello"), take(5usize))).parse_next(s)
/// }
///
/// assert_eq!(parser.parse_peek("Hello, World!"), Ok((", World!", "Hello")));
/// assert_eq!(parser.parse_peek("hello, World!"), Ok((", World!", "hello")));
/// assert_eq!(parser.parse_peek("HeLlo, World!"), Ok((", World!", "HeLlo")));
/// assert_eq!(parser.parse_peek("Something"), Ok(("hing", "Somet")));
/// assert!(parser.parse_peek("Some").is_err());
/// assert!(parser.parse_peek("").is_err());
/// ```
impl<'s, I, E: ParserError<I>> Parser<I, <I as Stream>::Slice, E> for Caseless<&'s str>
where
I: Compare<Caseless<&'s str>> + StreamIsPartial,
I: Stream,
{
#[inline(always)]
fn parse_next(&mut self, i: &mut I) -> Result<<I as Stream>::Slice, E> {
crate::token::literal(*self).parse_next(i)
}
}
+1844
View File
File diff suppressed because it is too large. Load diff
+4956
View File
File diff suppressed because it is too large. Load diff
+405
View File
@@ -0,0 +1,405 @@
//! Bit level parsers
//!
mod stream;
#[cfg(test)]
mod tests;
pub use self::stream::BitOffsets;
pub use self::stream::Bits;
use crate::combinator::trace;
use crate::error::{ErrorConvert, Needed, ParserError};
use crate::stream::{Stream, StreamIsPartial, ToUsize};
use crate::{Parser, Result};
use core::ops::{AddAssign, Div, Shl, Shr};
/// Number of bits in a byte
const BYTE: usize = u8::BITS as usize;
/// Converts a byte-level input to a bit-level input
///
/// See [`bytes`] to convert it back.
///
/// # Example
/// ```rust
/// # use winnow::prelude::*;
/// # use winnow::Bytes;
/// # use winnow::binary::bits::{bits, take};
/// # use winnow::error::ContextError;
/// # use winnow::error::ErrMode;
/// type Stream<'i> = &'i Bytes;
///
/// fn stream(b: &[u8]) -> Stream<'_> {
/// Bytes::new(b)
/// }
///
/// fn parse(input: &mut Stream<'_>) -> ModalResult<(u8, u8)> {
/// bits::<_, _, ErrMode<ContextError>, _, _>((take(4usize), take(8usize))).parse_next(input)
/// }
///
/// let input = stream(&[0x12, 0x34, 0xff, 0xff]);
///
/// let output = parse.parse_peek(input).expect("We take 1.5 bytes and the input is longer than 2 bytes");
///
/// // The first byte is consumed, the second byte is partially consumed and dropped.
/// let remaining = output.0;
/// assert_eq!(remaining, stream(&[0xff, 0xff]));
///
/// let parsed = output.1;
/// assert_eq!(parsed.0, 0x01);
/// assert_eq!(parsed.1, 0x23);
/// ```
pub fn bits<Input, Output, BitError, ByteError, ParseNext>(
mut parser: ParseNext,
) -> impl Parser<Input, Output, ByteError>
where
BitError: ParserError<Bits<Input>> + ErrorConvert<ByteError>,
ByteError: ParserError<Input>,
Bits<Input>: Stream,
Input: Stream + Clone,
ParseNext: Parser<Bits<Input>, Output, BitError>,
{
trace("bits", move |input: &mut Input| {
let mut bit_input = Bits(input.clone(), 0);
match parser.parse_next(&mut bit_input) {
Ok(result) => {
let Bits(mut rest, offset) = bit_input;
// If the next byte has been partially read, it will be sliced away as well.
// The parser functions might already slice away all fully read bytes.
// That's why `offset / BYTE` isn't necessarily needed at all times.
let remaining_bytes_index = offset / BYTE + if offset % BYTE == 0 { 0 } else { 1 };
let _ = rest.next_slice(remaining_bytes_index);
*input = rest;
Ok(result)
}
Err(e) => match e.needed() {
Some(n) => Err(ParserError::incomplete(
input,
n.map(|u| u.get() / BYTE + 1),
)),
None => Err(ErrorConvert::convert(e)),
},
}
})
}
/// Convert a [`bits`] stream back into a byte stream
///
/// <div class="warning">
///
/// **Warning:** A partial byte remaining in the input will be ignored and the given parser will
/// start parsing at the next full byte.
///
/// </div>
///
/// # Examples
///
/// ```
/// # use winnow::prelude::*;
/// # use winnow::Bytes;
/// # use winnow::token::rest;
/// # use winnow::error::ContextError;
/// # use winnow::error::ErrMode;
/// use winnow::binary::bits::{bits, bytes, take};
///
/// type Stream<'i> = &'i Bytes;
///
/// fn stream(b: &[u8]) -> Stream<'_> {
/// Bytes::new(b)
/// }
///
/// fn parse<'i>(input: &mut Stream<'i>) -> ModalResult<(u8, u8, &'i [u8])> {
/// bits::<_, _, ErrMode<ContextError>, _, _>((
/// take(4usize),
/// take(8usize),
/// bytes::<_, _, ErrMode<ContextError>, _, _>(rest)
/// )).parse_next(input)
/// }
///
/// let input = stream(&[0x12, 0x34, 0xff, 0xff]);
///
/// assert_eq!(parse.parse_peek(input), Ok(( stream(&[]), (0x01, 0x23, &[0xff, 0xff][..]) )));
/// ```
pub fn bytes<Input, Output, ByteError, BitError, ParseNext>(
mut parser: ParseNext,
) -> impl Parser<Bits<Input>, Output, BitError>
where
ByteError: ParserError<Input> + ErrorConvert<BitError>,
BitError: ParserError<Bits<Input>>,
Input: Stream<Token = u8> + Clone,
ParseNext: Parser<Input, Output, ByteError>,
{
trace("bytes", move |bit_input: &mut Bits<Input>| {
let Bits(mut input, offset) = bit_input.clone();
let _ = if offset % BYTE != 0 {
input.next_slice(1 + offset / BYTE)
} else {
input.next_slice(offset / BYTE)
};
match parser.parse_next(&mut input) {
Ok(res) => {
*bit_input = Bits(input, 0);
Ok(res)
}
Err(e) => match e.needed() {
Some(Needed::Unknown) => Err(ParserError::incomplete(bit_input, Needed::Unknown)),
Some(Needed::Size(sz)) => Err(match sz.get().checked_mul(BYTE) {
Some(v) => ParserError::incomplete(bit_input, Needed::new(v)),
None => ParserError::assert(
bit_input,
"overflow in turning needed bytes into needed bits",
),
}),
None => Err(ErrorConvert::convert(e)),
},
}
})
}
/// Parse taking `count` bits
///
/// # Effective Signature
///
/// Assuming you are parsing a [`Bits<&[u8]>`][Bits] bit [Stream]:
/// ```rust
/// # use winnow::prelude::*;;
/// # use winnow::error::ContextError;
/// # use winnow::binary::bits::Bits;
/// pub fn take<'i>(count: usize) -> impl Parser<Bits<&'i [u8]>, u8, ContextError>
/// # {
/// # winnow::binary::bits::take(count)
/// # }
/// ```
///
/// # Example
/// ```rust
/// # use winnow::prelude::*;
/// # use winnow::Bytes;
/// # use winnow::error::ContextError;
/// use winnow::binary::bits::{Bits, take};
///
/// type Stream<'i> = &'i Bytes;
///
/// fn stream(b: &[u8]) -> Stream<'_> {
/// Bytes::new(b)
/// }
///
/// // Consumes 0 bits, returns 0
/// assert_eq!(take::<_, usize, _, ContextError>(0usize).parse_peek(Bits(stream(&[0b00010010]), 0)), Ok((Bits(stream(&[0b00010010]), 0), 0)));
///
/// // Consumes 4 bits, returns their values and increase offset to 4
/// assert_eq!(take::<_, usize, _, ContextError>(4usize).parse_peek(Bits(stream(&[0b00010010]), 0)), Ok((Bits(stream(&[0b00010010]), 4), 0b00000001)));
///
/// // Consumes 4 bits, offset is 4, returns their values and increase offset to 0 of next byte
/// assert_eq!(take::<_, usize, _, ContextError>(4usize).parse_peek(Bits(stream(&[0b00010010]), 4)), Ok((Bits(stream(&[]), 0), 0b00000010)));
///
/// // Tries to consume 12 bits but only 8 are available
/// assert!(take::<_, usize, _, ContextError>(12usize).parse_peek(Bits(stream(&[0b00010010]), 0)).is_err());
/// ```
#[inline(always)]
pub fn take<Input, Output, Count, Error>(count: Count) -> impl Parser<Bits<Input>, Output, Error>
where
Input: Stream<Token = u8> + StreamIsPartial + Clone,
Output: From<u8> + AddAssign + Shl<usize, Output = Output> + Shr<usize, Output = Output>,
Count: ToUsize,
Error: ParserError<Bits<Input>>,
{
let count = count.to_usize();
trace("take", move |input: &mut Bits<Input>| {
if <Input as StreamIsPartial>::is_partial_supported() {
take_::<_, _, _, true>(input, count)
} else {
take_::<_, _, _, false>(input, count)
}
})
}
fn take_<I, O, E: ParserError<Bits<I>>, const PARTIAL: bool>(
bit_input: &mut Bits<I>,
count: usize,
) -> Result<O, E>
where
I: StreamIsPartial,
I: Stream<Token = u8> + Clone,
O: From<u8> + AddAssign + Shl<usize, Output = O> + Shr<usize, Output = O>,
{
if count == 0 {
Ok(0u8.into())
} else {
let Bits(mut input, bit_offset) = bit_input.clone();
if input.eof_offset() * BYTE < count + bit_offset {
if PARTIAL && input.is_partial() {
Err(ParserError::incomplete(bit_input, Needed::new(count)))
} else {
Err(ParserError::from_input(&Bits(input, bit_offset)))
}
} else {
let cnt = (count + bit_offset).div(BYTE);
let mut acc: O = 0_u8.into();
let mut offset: usize = bit_offset;
let mut remaining: usize = count;
let mut end_offset: usize = 0;
for (_, byte) in input.iter_offsets().take(cnt + 1) {
if remaining == 0 {
break;
}
let val: O = if offset == 0 {
byte.into()
} else {
(byte << offset >> offset).into()
};
if remaining < BYTE - offset {
acc += val >> (BYTE - offset - remaining);
end_offset = remaining + offset;
break;
} else {
acc += val << (remaining - (BYTE - offset));
remaining -= BYTE - offset;
offset = 0;
}
}
let _ = input.next_slice(cnt);
*bit_input = Bits(input, end_offset);
Ok(acc)
}
}
}
/// Parse taking `count` bits and comparing them to `pattern`
///
/// # Effective Signature
///
/// Assuming you are parsing a [`Bits<&[u8]>`][Bits] bit [Stream]:
/// ```rust
/// # use winnow::prelude::*;;
/// # use winnow::error::ContextError;
/// # use winnow::binary::bits::Bits;
/// pub fn pattern<'i>(pattern: u8, count: usize) -> impl Parser<Bits<&'i [u8]>, u8, ContextError>
/// # {
/// # winnow::binary::bits::pattern(pattern, count)
/// # }
/// ```
///
/// # Example
///
/// ```rust
/// # use winnow::prelude::*;
/// # use winnow::Bytes;
/// # use winnow::error::ContextError;
/// use winnow::binary::bits::pattern;
///
/// type Stream<'i> = &'i Bytes;
///
/// fn stream(b: &[u8]) -> Stream<'_> {
/// Bytes::new(b)
/// }
///
/// /// Compare the lowest `count` bits of `input` against the lowest `count` bits of `pattern`.
/// /// Return Ok and the matching section of `input` if there's a match.
/// /// Return Err if there's no match.
/// # use winnow::binary::bits::Bits;
/// fn parser(bits: u8, count: u8, input: &mut Bits<Stream<'_>>) -> ModalResult<u8> {
/// pattern(bits, count).parse_next(input)
/// }
///
/// // The lowest 4 bits of 0b00001111 match the lowest 4 bits of 0b11111111.
/// assert_eq!(
/// pattern::<_, usize, _, ContextError>(0b0000_1111, 4usize).parse_peek(Bits(stream(&[0b1111_1111]), 0)),
/// Ok((Bits(stream(&[0b1111_1111]), 4), 0b0000_1111))
/// );
///
/// // The lowest bit of 0b00001111 matches the lowest bit of 0b11111111 (both are 1).
/// assert_eq!(
/// pattern::<_, usize, _, ContextError>(0b00000001, 1usize).parse_peek(Bits(stream(&[0b11111111]), 0)),
/// Ok((Bits(stream(&[0b11111111]), 1), 0b00000001))
/// );
///
/// // The lowest 2 bits of 0b11111111 and 0b00000001 are different.
/// assert!(pattern::<_, usize, _, ContextError>(0b000000_01, 2usize).parse_peek(Bits(stream(&[0b111111_11]), 0)).is_err());
///
/// // The lowest 8 bits of 0b11111111 and 0b11111110 are different.
/// assert!(pattern::<_, usize, _, ContextError>(0b11111110, 8usize).parse_peek(Bits(stream(&[0b11111111]), 0)).is_err());
/// ```
#[inline(always)]
#[doc(alias = "literal")]
#[doc(alias = "just")]
#[doc(alias = "tag")]
pub fn pattern<Input, Output, Count, Error: ParserError<Bits<Input>>>(
pattern: Output,
count: Count,
) -> impl Parser<Bits<Input>, Output, Error>
where
Input: Stream<Token = u8> + StreamIsPartial + Clone,
Count: ToUsize,
Output: From<u8>
+ AddAssign
+ Shl<usize, Output = Output>
+ Shr<usize, Output = Output>
+ PartialEq,
{
let count = count.to_usize();
trace("pattern", move |input: &mut Bits<Input>| {
let start = input.checkpoint();
take(count).parse_next(input).and_then(|o| {
if pattern == o {
Ok(o)
} else {
input.reset(&start);
Err(ParserError::from_input(input))
}
})
})
}
/// Parses one specific bit as a bool.
///
/// # Effective Signature
///
/// Assuming you are parsing a [`Bits<&[u8]>`][Bits] bit [Stream]:
/// ```rust
/// # use winnow::prelude::*;;
/// # use winnow::error::ContextError;
/// # use winnow::binary::bits::Bits;
/// pub fn bool(input: &mut Bits<&[u8]>) -> ModalResult<bool>
/// # {
/// # winnow::binary::bits::bool.parse_next(input)
/// # }
/// ```
///
/// # Example
///
/// ```rust
/// # use winnow::prelude::*;
/// # use winnow::Bytes;
/// # use winnow::error::InputError;
/// use winnow::binary::bits::bool;
///
/// type Stream<'i> = &'i Bytes;
///
/// fn stream(b: &[u8]) -> Stream<'_> {
/// Bytes::new(b)
/// }
///
/// # use winnow::binary::bits::Bits;
/// fn parse(input: &mut Bits<Stream<'_>>) -> ModalResult<bool> {
/// bool.parse_next(input)
/// }
///
/// assert_eq!(parse.parse_peek(Bits(stream(&[0b10000000]), 0)), Ok((Bits(stream(&[0b10000000]), 1), true)));
/// assert_eq!(parse.parse_peek(Bits(stream(&[0b10000000]), 1)), Ok((Bits(stream(&[0b10000000]), 2), false)));
/// ```
#[doc(alias = "any")]
pub fn bool<Input, Error: ParserError<Bits<Input>>>(input: &mut Bits<Input>) -> Result<bool, Error>
where
Input: Stream<Token = u8> + StreamIsPartial + Clone,
{
trace("bool", |input: &mut Bits<Input>| {
let bit: u32 = take(1usize).parse_next(input)?;
Ok(bit != 0)
})
.parse_next(input)
}
+250
View File
@@ -0,0 +1,250 @@
use core::num::NonZeroUsize;
use crate::error::Needed;
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
use crate::stream::Recover;
use crate::stream::{Checkpoint, Offset, Stream, StreamIsPartial};
/// Bit-level stream state over a byte stream.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Bits<I>(pub I, pub usize);
impl<I> Stream for Bits<I>
where
I: Stream<Token = u8> + Clone,
{
type Token = bool;
type Slice = (I::Slice, usize, usize);
type IterOffsets = BitOffsets<I>;
type Checkpoint = Checkpoint<Bits<I::Checkpoint>, Self>;
#[inline(always)]
fn iter_offsets(&self) -> Self::IterOffsets {
BitOffsets {
i: self.clone(),
o: 0,
}
}
#[inline(always)]
fn eof_offset(&self) -> usize {
let offset = self.0.eof_offset() * 8;
if offset == 0 {
0
} else {
offset - self.1
}
}
#[inline(always)]
fn next_token(&mut self) -> Option<Self::Token> {
next_bit(self)
}
#[inline(always)]
fn peek_token(&self) -> Option<Self::Token> {
peek_bit(self)
}
#[inline(always)]
fn offset_for<P>(&self, predicate: P) -> Option<usize>
where
P: Fn(Self::Token) -> bool,
{
self.iter_offsets()
.find_map(|(o, b)| predicate(b).then_some(o))
}
#[inline(always)]
fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
if let Some(needed) = tokens
.checked_sub(self.eof_offset())
.and_then(NonZeroUsize::new)
{
Err(Needed::Size(needed))
} else {
Ok(tokens)
}
}
#[inline(always)]
fn next_slice(&mut self, offset: usize) -> Self::Slice {
let byte_offset = (offset + self.1) / 8;
let end_offset = (offset + self.1) % 8;
let s = self.0.next_slice(byte_offset);
let start_offset = self.1;
self.1 = end_offset;
(s, start_offset, end_offset)
}
#[inline(always)]
fn peek_slice(&self, offset: usize) -> Self::Slice {
let byte_offset = (offset + self.1) / 8;
let end_offset = (offset + self.1) % 8;
let s = self.0.peek_slice(byte_offset);
let start_offset = self.1;
(s, start_offset, end_offset)
}
#[inline(always)]
fn checkpoint(&self) -> Self::Checkpoint {
Checkpoint::<_, Self>::new(Bits(self.0.checkpoint(), self.1))
}
#[inline(always)]
fn reset(&mut self, checkpoint: &Self::Checkpoint) {
self.0.reset(&checkpoint.inner.0);
self.1 = checkpoint.inner.1;
}
fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{self:#?}")
}
}
/// Iterator for [bit][crate::binary::bits] stream ([`Bits`])
pub struct BitOffsets<I> {
i: Bits<I>,
o: usize,
}
impl<I> Iterator for BitOffsets<I>
where
I: Stream<Token = u8> + Clone,
{
type Item = (usize, bool);
fn next(&mut self) -> Option<Self::Item> {
let b = next_bit(&mut self.i)?;
let o = self.o;
self.o += 1;
Some((o, b))
}
}
fn next_bit<I>(i: &mut Bits<I>) -> Option<bool>
where
I: Stream<Token = u8> + Clone,
{
if i.eof_offset() == 0 {
return None;
}
let offset = i.1;
let mut next_i = i.0.clone();
let byte = next_i.next_token()?;
let bit = (byte >> offset) & 0x1 == 0x1;
let next_offset = offset + 1;
if next_offset == 8 {
i.0 = next_i;
i.1 = 0;
} else {
i.1 = next_offset;
}
Some(bit)
}
fn peek_bit<I>(i: &Bits<I>) -> Option<bool>
where
I: Stream<Token = u8> + Clone,
{
if i.eof_offset() == 0 {
return None;
}
let offset = i.1;
let mut next_i = i.0.clone();
let byte = next_i.next_token()?;
Some((byte >> offset) & 0x1 == 0x1)
}
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
impl<I, E> Recover<E> for Bits<I>
where
I: Recover<E>,
I: Stream<Token = u8> + Clone,
{
#[inline(always)]
fn record_err(
&mut self,
_token_start: &Self::Checkpoint,
_err_start: &Self::Checkpoint,
err: E,
) -> Result<(), E> {
Err(err)
}
#[inline(always)]
fn is_recovery_supported() -> bool {
false
}
}
impl<I> StreamIsPartial for Bits<I>
where
I: StreamIsPartial,
{
type PartialState = I::PartialState;
#[inline]
fn complete(&mut self) -> Self::PartialState {
self.0.complete()
}
#[inline]
fn restore_partial(&mut self, state: Self::PartialState) {
self.0.restore_partial(state);
}
#[inline(always)]
fn is_partial_supported() -> bool {
I::is_partial_supported()
}
#[inline(always)]
fn is_partial(&self) -> bool {
self.0.is_partial()
}
}
impl<I> Offset for Bits<I>
where
I: Offset,
{
#[inline(always)]
fn offset_from(&self, start: &Self) -> usize {
self.0.offset_from(&start.0) * 8 + self.1 - start.1
}
}
impl<I> Offset<<Bits<I> as Stream>::Checkpoint> for Bits<I>
where
I: Stream<Token = u8> + Clone,
{
#[inline(always)]
fn offset_from(&self, other: &<Bits<I> as Stream>::Checkpoint) -> usize {
self.checkpoint().offset_from(other)
}
}
impl<I: Clone> crate::error::ErrorConvert<crate::error::InputError<Bits<I>>>
for crate::error::InputError<I>
{
#[inline]
fn convert(self) -> crate::error::InputError<Bits<I>> {
self.map_input(|i| Bits(i, 0))
}
}
impl<I: Clone> crate::error::ErrorConvert<crate::error::InputError<I>>
for crate::error::InputError<Bits<I>>
{
#[inline]
fn convert(self) -> crate::error::InputError<I> {
self.map_input(|Bits(i, _o)| i)
}
}
+282
View File
@@ -0,0 +1,282 @@
use super::*;
use crate::error::ErrMode;
use crate::error::InputError;
use crate::prelude::*;
use crate::stream::{Offset, Stream};
use crate::Partial;
#[cfg(feature = "std")]
use proptest::prelude::*;
#[test]
/// Take the `bits` function and assert that remaining bytes are correctly returned, if the
/// previous bytes are fully consumed
fn test_complete_byte_consumption_bits() {
let input = &[0x12, 0x34, 0x56, 0x78][..];
// Take 3 bit slices with sizes [4, 8, 4].
#[allow(clippy::type_complexity)]
let result: ModalResult<(&[u8], (u8, u8, u8)), InputError<_>> =
bits::<_, _, ErrMode<InputError<Bits<&[u8]>>>, _, _>((
take(4usize),
take(8usize),
take(4usize),
))
.parse_peek(input);
let output = result.expect("We take 2 bytes and the input is longer than 2 bytes");
let remaining = output.0;
assert_eq!(remaining, [0x56, 0x78]);
let parsed = output.1;
assert_eq!(parsed.0, 0x01);
assert_eq!(parsed.1, 0x23);
assert_eq!(parsed.2, 0x04);
}
#[test]
/// Take the `bits` function and assert that remaining bytes are correctly returned, if the
/// previous bytes are NOT fully consumed. Partially consumed bytes are supposed to be dropped.
/// I.e. if we consume 1.5 bytes of 4 bytes, 2 bytes will be returned, bits 13-16 will be
/// dropped.
fn test_partial_byte_consumption_bits() {
let input = &[0x12, 0x34, 0x56, 0x78][..];
// Take bit slices with sizes [4, 8].
let result: ModalResult<(&[u8], (u8, u8)), InputError<_>> =
bits::<_, _, ErrMode<InputError<Bits<&[u8]>>>, _, _>((take(4usize), take(8usize)))
.parse_peek(input);
let output = result.expect("We take 1.5 bytes and the input is longer than 2 bytes");
let remaining = output.0;
assert_eq!(remaining, [0x56, 0x78]);
let parsed = output.1;
assert_eq!(parsed.0, 0x01);
assert_eq!(parsed.1, 0x23);
}
#[test]
#[cfg(feature = "std")]
/// Ensure that in Incomplete error is thrown, if too few bytes are passed for a given parser.
fn test_incomplete_bits() {
let input = Partial::new(&[0x12][..]);
// Take bit slices with sizes [4, 8].
let result: ModalResult<(_, (u8, u8)), InputError<_>> =
bits::<_, _, ErrMode<InputError<Bits<_>>>, _, _>((take(4usize), take(8usize)))
.parse_peek(input);
assert!(result.is_err());
let error = result.err().unwrap();
assert_eq!("Parsing requires 2 more data", error.to_string());
}
#[test]
fn test_take_complete_0() {
let input = &[0b00010010][..];
let count = 0usize;
assert_eq!(count, 0usize);
let offset = 0usize;
let result: ModalResult<(Bits<&[u8]>, usize), InputError<_>> =
take(count).parse_peek(Bits(input, offset));
assert_eq!(result, Ok((Bits(input, offset), 0)));
}
#[test]
fn test_take_complete_eof() {
let input = &[0b00010010][..];
let result: ModalResult<(Bits<&[u8]>, usize), InputError<_>> =
take(1usize).parse_peek(Bits(input, 8));
assert_eq!(
result,
Err(crate::error::ErrMode::Backtrack(InputError::at(Bits(
input, 8
),)))
);
}
#[test]
fn test_take_complete_span_over_multiple_bytes() {
let input = &[0b00010010, 0b00110100, 0b11111111, 0b11111111][..];
let result: ModalResult<(Bits<&[u8]>, usize), InputError<_>> =
take(24usize).parse_peek(Bits(input, 4));
assert_eq!(
result,
Ok((Bits([0b11111111].as_ref(), 4), 0b1000110100111111111111))
);
}
#[test]
fn test_take_partial_0() {
let input = Partial::new(&[][..]);
let count = 0usize;
assert_eq!(count, 0usize);
let offset = 0usize;
let result: ModalResult<(Bits<_>, usize), InputError<_>> =
take(count).parse_peek(Bits(input, offset));
assert_eq!(result, Ok((Bits(input, offset), 0)));
}
#[test]
fn test_pattern_partial_ok() {
let input = Partial::new(&[0b00011111][..]);
let offset = 0usize;
let bits_to_take = 4usize;
let value_to_pattern = 0b0001;
let result: ModalResult<(Bits<_>, usize), InputError<_>> =
pattern(value_to_pattern, bits_to_take).parse_peek(Bits(input, offset));
assert_eq!(result, Ok((Bits(input, bits_to_take), value_to_pattern)));
}
#[test]
fn test_pattern_partial_err() {
let input = Partial::new(&[0b00011111][..]);
let offset = 0usize;
let bits_to_take = 4usize;
let value_to_pattern = 0b1111;
let result: ModalResult<(Bits<_>, usize), InputError<_>> =
pattern(value_to_pattern, bits_to_take).parse_peek(Bits(input, offset));
assert_eq!(
result,
Err(crate::error::ErrMode::Backtrack(InputError::at(Bits(
input, offset
),)))
);
}
#[test]
fn test_bool_0_complete() {
let input = [0b10000000].as_ref();
let result: ModalResult<(Bits<&[u8]>, bool), InputError<_>> = bool.parse_peek(Bits(input, 0));
assert_eq!(result, Ok((Bits(input, 1), true)));
}
#[test]
fn test_bool_eof_complete() {
let input = [0b10000000].as_ref();
let result: ModalResult<(Bits<&[u8]>, bool), InputError<_>> = bool.parse_peek(Bits(input, 8));
assert_eq!(
result,
Err(crate::error::ErrMode::Backtrack(InputError::at(Bits(
input, 8
),)))
);
}
#[test]
fn test_bool_0_partial() {
let input = Partial::new([0b10000000].as_ref());
#[allow(clippy::type_complexity)]
let result: ModalResult<(Bits<Partial<&[u8]>>, bool), InputError<_>> =
bool.parse_peek(Bits(input, 0));
assert_eq!(result, Ok((Bits(input, 1), true)));
}
#[test]
fn test_bool_eof_partial() {
let input = Partial::new([0b10000000].as_ref());
#[allow(clippy::type_complexity)]
let result: ModalResult<(Bits<Partial<&[u8]>>, bool), InputError<_>> =
bool.parse_peek(Bits(input, 8));
assert_eq!(
result,
Err(crate::error::ErrMode::Incomplete(Needed::new(1)))
);
}
#[test]
#[cfg(feature = "alloc")]
fn test_bit_stream_empty() {
let i = Bits(&b""[..], 0);
let actual = i.iter_offsets().collect::<alloc::vec::Vec<_>>();
assert_eq!(actual, vec![]);
let actual = i.eof_offset();
assert_eq!(actual, 0);
let actual = i.peek_token();
assert_eq!(actual, None);
let actual = i.offset_for(|b| b);
assert_eq!(actual, None);
let actual = i.offset_at(1);
assert_eq!(actual, Err(Needed::new(1)));
let actual_slice = i.peek_slice(0);
assert_eq!(actual_slice, (&b""[..], 0, 0));
}
#[test]
#[cfg(feature = "alloc")]
fn test_bit_offset_empty() {
let i = Bits(&b""[..], 0);
let actual = i.offset_from(&i);
assert_eq!(actual, 0);
}
#[cfg(feature = "std")]
proptest! {
#[test]
#[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253
fn bit_stream(byte_len in 0..20usize, start in 0..160usize) {
bit_stream_inner(byte_len, start);
}
}
#[cfg(feature = "std")]
fn bit_stream_inner(byte_len: usize, start: usize) {
let start = start.min(byte_len * 8);
let start_byte = start / 8;
let start_bit = start % 8;
let bytes = vec![0b1010_1010; byte_len];
let i = Bits(&bytes[start_byte..], start_bit);
let mut curr_i = i;
let mut curr_offset = 0;
while let Some(_token) = curr_i.peek_token() {
let to_offset = curr_i.offset_from(&i);
assert_eq!(curr_offset, to_offset);
let actual_slice = i.peek_slice(curr_offset);
let expected_slice = i.peek_slice(curr_offset);
assert_eq!(actual_slice, expected_slice);
let at_offset = i.offset_at(curr_offset).unwrap();
assert_eq!(curr_offset, at_offset);
let eof_offset = curr_i.eof_offset();
let eof_slice = curr_i.peek_slice(eof_offset);
let eof_slice_i = Bits(eof_slice.0, eof_slice.1);
assert_eq!(eof_slice_i, curr_i);
curr_offset += 1;
let _ = curr_i.next_token();
}
assert_eq!(i.eof_offset(), curr_offset);
}
+2533
View File
File diff suppressed because it is too large. Load diff
+3721
View File
File diff suppressed because it is too large. Load diff
+185
View File
@@ -0,0 +1,185 @@
use crate::combinator::trace;
use crate::error::ParserError;
use crate::stream::Stream;
use crate::{Parser, Result};
#[doc(inline)]
pub use crate::dispatch;
/// Helper trait for the [`alt()`] combinator.
///
/// This trait is implemented for tuples of up to 21 elements
pub trait Alt<I, O, E> {
/// Tests each parser in the tuple and returns the result of the first one that succeeds
fn choice(&mut self, input: &mut I) -> Result<O, E>;
}
/// Pick the first successful parser
///
/// To stop on an error, rather than trying further cases, see
/// [`cut_err`][crate::combinator::cut_err] ([example][crate::_tutorial::chapter_7]).
///
/// For tight control over the error when no match is found, add a final case using [`fail`][crate::combinator::fail].
/// Alternatively, with a [custom error type][crate::_topic::error], it is possible to track all
/// errors or return the error of the parser that went the farthest in the input data.
///
/// When the alternative cases have unique prefixes, [`dispatch`] can offer better performance.
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "ascii")] {
/// # use winnow::{error::ErrMode, error::Needed};
/// # use winnow::prelude::*;
/// use winnow::ascii::{alpha1, digit1};
/// use winnow::combinator::alt;
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
/// alt((alpha1, digit1)).parse_next(input)
/// };
///
/// // the first parser, alpha1, takes the input
/// assert_eq!(parser.parse_peek("abc"), Ok(("", "abc")));
///
/// // the first parser returns an error, so alt tries the second one
/// assert_eq!(parser.parse_peek("123456"), Ok(("", "123456")));
///
/// // both parsers failed, and with the default error type, alt will return the last error
/// assert!(parser.parse_peek(" ").is_err());
/// # }
/// ```
#[doc(alias = "choice")]
#[inline(always)]
pub fn alt<Input: Stream, Output, Error, Alternatives>(
mut alternatives: Alternatives,
) -> impl Parser<Input, Output, Error>
where
Alternatives: Alt<Input, Output, Error>,
Error: ParserError<Input>,
{
trace("alt", move |i: &mut Input| alternatives.choice(i))
}
impl<const N: usize, I: Stream, O, E: ParserError<I>, P: Parser<I, O, E>> Alt<I, O, E> for [P; N] {
fn choice(&mut self, input: &mut I) -> Result<O, E> {
let mut error: Option<E> = None;
let start = input.checkpoint();
for branch in self {
input.reset(&start);
match branch.parse_next(input) {
Err(e) if e.is_backtrack() => {
error = match error {
Some(error) => Some(error.or(e)),
None => Some(e),
};
}
res => return res,
}
}
match error {
Some(e) => Err(e.append(input, &start)),
None => Err(ParserError::assert(
input,
"`alt` needs at least one parser",
)),
}
}
}
impl<I: Stream, O, E: ParserError<I>, P: Parser<I, O, E>> Alt<I, O, E> for &mut [P] {
fn choice(&mut self, input: &mut I) -> Result<O, E> {
let mut error: Option<E> = None;
let start = input.checkpoint();
for branch in self.iter_mut() {
input.reset(&start);
match branch.parse_next(input) {
Err(e) if e.is_backtrack() => {
error = match error {
Some(error) => Some(error.or(e)),
None => Some(e),
};
}
res => return res,
}
}
match error {
Some(e) => Err(e.append(input, &start)),
None => Err(ParserError::assert(
input,
"`alt` needs at least one parser",
)),
}
}
}
macro_rules! alt_trait(
($first:ident $second:ident $($id: ident)+) => (
alt_trait!(__impl $first $second; $($id)+);
);
(__impl $($current:ident)*; $head:ident $($id: ident)+) => (
alt_trait_impl!($($current)*);
alt_trait!(__impl $($current)* $head; $($id)+);
);
(__impl $($current:ident)*; $head:ident) => (
alt_trait_impl!($($current)*);
alt_trait_impl!($($current)* $head);
);
);
macro_rules! alt_trait_impl(
($($id:ident)+) => (
impl<
I: Stream, Output, Error: ParserError<I>,
$($id: Parser<I, Output, Error>),+
> Alt<I, Output, Error> for ( $($id),+ ) {
fn choice(&mut self, input: &mut I) -> Result<Output, Error> {
let start = input.checkpoint();
match self.0.parse_next(input) {
Err(e) if e.is_backtrack() => alt_trait_inner!(1, self, input, start, e, $($id)+),
res => res,
}
}
}
);
);
macro_rules! succ (
(1, $submac:ident ! ($($rest:tt)*)) => ($submac!(2, $($rest)*));
(2, $submac:ident ! ($($rest:tt)*)) => ($submac!(3, $($rest)*));
(3, $submac:ident ! ($($rest:tt)*)) => ($submac!(4, $($rest)*));
(4, $submac:ident ! ($($rest:tt)*)) => ($submac!(5, $($rest)*));
(5, $submac:ident ! ($($rest:tt)*)) => ($submac!(6, $($rest)*));
(6, $submac:ident ! ($($rest:tt)*)) => ($submac!(7, $($rest)*));
(7, $submac:ident ! ($($rest:tt)*)) => ($submac!(8, $($rest)*));
(8, $submac:ident ! ($($rest:tt)*)) => ($submac!(9, $($rest)*));
);
macro_rules! alt_trait_inner(
($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident $($id:ident)+) => ({
$input.reset(&$start);
match $self.$it.parse_next($input) {
Err(e) if e.is_backtrack() => {
let err = $err.or(e);
succ!($it, alt_trait_inner!($self, $input, $start, err, $($id)+))
}
res => res,
}
});
($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident) => ({
Err($err.append($input, &$start))
});
);
alt_trait!(Alt2 Alt3 Alt4 Alt5 Alt6 Alt7 Alt8 Alt9 Alt10);
// Manually implement Alt for (A,), the 1-tuple type
impl<I: Stream, O, E: ParserError<I>, A: Parser<I, O, E>> Alt<I, O, E> for (A,) {
fn choice(&mut self, input: &mut I) -> Result<O, E> {
self.0.parse_next(input)
}
}
+400
View File
@@ -0,0 +1,400 @@
use crate::combinator::trace;
use crate::error::{ModalError, ParserError};
use crate::stream::Stream;
use crate::{Parser, Result};
/// Apply a [`Parser`], producing `None` on [`ErrMode::Backtrack`][crate::error::ErrMode::Backtrack].
///
/// To chain an error up, see [`cut_err`].
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "ascii")] {
/// # use winnow::prelude::*;
/// use winnow::combinator::opt;
/// use winnow::ascii::alpha1;
///
/// fn parser<'i>(i: &mut &'i str) -> ModalResult<Option<&'i str>> {
/// opt(alpha1).parse_next(i)
/// }
///
/// assert_eq!(parser.parse_peek("abcd;"), Ok((";", Some("abcd"))));
/// assert_eq!(parser.parse_peek("123;"), Ok(("123;", None)));
/// # }
/// ```
pub fn opt<Input: Stream, Output, Error, ParseNext>(
mut parser: ParseNext,
) -> impl Parser<Input, Option<Output>, Error>
where
ParseNext: Parser<Input, Output, Error>,
Error: ParserError<Input>,
{
trace("opt", move |input: &mut Input| {
let start = input.checkpoint();
match parser.parse_next(input) {
Ok(o) => Ok(Some(o)),
Err(e) if e.is_backtrack() => {
input.reset(&start);
Ok(None)
}
Err(e) => Err(e),
}
})
}
/// Calls the parser if the condition is met.
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "ascii")] {
/// # use winnow::prelude::*;
/// # use winnow::combinator::opt;
/// use winnow::combinator::cond;
/// use winnow::ascii::alpha1;
///
/// fn parser<'i>(i: &mut &'i str) -> ModalResult<Option<&'i str>> {
/// let prefix = opt("-").parse_next(i)?;
/// let condition = prefix.is_some();
/// cond(condition, alpha1).parse_next(i)
/// }
///
/// assert_eq!(parser.parse_peek("-abcd;"), Ok((";", Some("abcd"))));
/// assert_eq!(parser.parse_peek("abcd;"), Ok(("abcd;", None)));
/// assert!(parser.parse_peek("-123;").is_err());
/// assert_eq!(parser.parse_peek("123;"), Ok(("123;", None)));
/// # }
/// ```
pub fn cond<Input, Output, Error, ParseNext>(
cond: bool,
mut parser: ParseNext,
) -> impl Parser<Input, Option<Output>, Error>
where
Input: Stream,
ParseNext: Parser<Input, Output, Error>,
Error: ParserError<Input>,
{
trace("cond", move |input: &mut Input| {
if cond {
parser.parse_next(input).map(Some)
} else {
Ok(None)
}
})
}
/// Apply the parser without advancing the input.
///
/// To lookahead and only advance on success, see [`opt`].
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "ascii")] {
/// # use winnow::prelude::*;
/// use winnow::combinator::peek;
/// use winnow::ascii::alpha1;
///
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
/// peek(alpha1).parse_next(input)
/// }
///
/// assert_eq!(parser.parse_peek("abcd;"), Ok(("abcd;", "abcd")));
/// assert!(parser.parse_peek("123;").is_err());
/// # }
/// ```
#[doc(alias = "look_ahead")]
#[doc(alias = "rewind")]
pub fn peek<Input, Output, Error, ParseNext>(
mut parser: ParseNext,
) -> impl Parser<Input, Output, Error>
where
Input: Stream,
Error: ParserError<Input>,
ParseNext: Parser<Input, Output, Error>,
{
trace("peek", move |input: &mut Input| {
let start = input.checkpoint();
let res = parser.parse_next(input);
input.reset(&start);
res
})
}
/// Match the end of the [`Stream`]
///
/// Otherwise, it will error.
///
/// # Effective Signature
///
/// Assuming you are parsing a `&str` [Stream]:
/// ```rust
/// # use winnow::prelude::*;;
/// pub fn eof<'i>(input: &mut &'i str) -> ModalResult<&'i str>
/// # {
/// # winnow::combinator::eof.parse_next(input)
/// # }
/// ```
///
/// # Example
///
/// ```rust
/// # use std::str;
/// # use winnow::combinator::eof;
/// # use winnow::prelude::*;
///
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
/// eof.parse_next(input)
/// }
/// assert!(parser.parse_peek("abc").is_err());
/// assert_eq!(parser.parse_peek(""), Ok(("", "")));
/// ```
#[doc(alias = "end")]
#[doc(alias = "eoi")]
pub fn eof<Input, Error>(input: &mut Input) -> Result<<Input as Stream>::Slice, Error>
where
Input: Stream,
Error: ParserError<Input>,
{
trace("eof", move |input: &mut Input| {
if input.eof_offset() == 0 {
Ok(input.next_slice(0))
} else {
Err(ParserError::from_input(input))
}
})
.parse_next(input)
}
/// Succeeds if the child parser returns an error.
///
/// <div class="warning">
///
/// **Note:** This does not advance the [`Stream`]
///
/// </div>
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "ascii")] {
/// # use winnow::prelude::*;
/// use winnow::combinator::not;
/// use winnow::ascii::alpha1;
///
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<()> {
/// not(alpha1).parse_next(input)
/// }
///
/// assert_eq!(parser.parse_peek("123"), Ok(("123", ())));
/// assert!(parser.parse_peek("abcd").is_err());
/// # }
/// ```
pub fn not<Input, Output, Error, ParseNext>(mut parser: ParseNext) -> impl Parser<Input, (), Error>
where
Input: Stream,
Error: ParserError<Input>,
ParseNext: Parser<Input, Output, Error>,
{
trace("not", move |input: &mut Input| {
let start = input.checkpoint();
let res = parser.parse_next(input);
input.reset(&start);
match res {
Ok(_) => Err(ParserError::from_input(input)),
Err(e) if e.is_backtrack() => Ok(()),
Err(e) => Err(e),
}
})
}
/// Transforms an [`ErrMode::Backtrack`][crate::error::ErrMode::Backtrack] (recoverable) to [`ErrMode::Cut`][crate::error::ErrMode::Cut] (unrecoverable)
///
/// This commits the parse result, preventing alternative branch paths like with
/// [`winnow::combinator::alt`][crate::combinator::alt].
///
/// See the [tutorial][crate::_tutorial::chapter_7] for more details.
///
/// # Example
///
/// Without `cut_err`:
/// ```rust
/// # #[cfg(feature = "ascii")] {
/// # use winnow::token::one_of;
/// # use winnow::token::rest;
/// # use winnow::ascii::digit1;
/// # use winnow::combinator::alt;
/// # use winnow::combinator::preceded;
/// # use winnow::prelude::*;
///
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
/// alt((
/// preceded(one_of(['+', '-']), digit1),
/// rest
/// )).parse_next(input)
/// }
///
/// assert_eq!(parser.parse_peek("+10 ab"), Ok((" ab", "10")));
/// assert_eq!(parser.parse_peek("ab"), Ok(("", "ab")));
/// assert_eq!(parser.parse_peek("+"), Ok(("", "+")));
/// # }
/// ```
///
/// With `cut_err`:
/// ```rust
/// # #[cfg(feature = "ascii")] {
/// # use winnow::{error::ErrMode, error::ContextError};
/// # use winnow::prelude::*;
/// # use winnow::token::one_of;
/// # use winnow::token::rest;
/// # use winnow::ascii::digit1;
/// # use winnow::combinator::alt;
/// # use winnow::combinator::preceded;
/// use winnow::combinator::cut_err;
///
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
/// alt((
/// preceded(one_of(['+', '-']), cut_err(digit1)),
/// rest
/// )).parse_next(input)
/// }
///
/// assert_eq!(parser.parse_peek("+10 ab"), Ok((" ab", "10")));
/// assert_eq!(parser.parse_peek("ab"), Ok(("", "ab")));
/// assert_eq!(parser.parse_peek("+"), Err(ErrMode::Cut(ContextError::new())));
/// # }
/// ```
pub fn cut_err<Input, Output, Error, ParseNext>(
mut parser: ParseNext,
) -> impl Parser<Input, Output, Error>
where
Input: Stream,
Error: ParserError<Input> + ModalError,
ParseNext: Parser<Input, Output, Error>,
{
trace("cut_err", move |input: &mut Input| {
parser.parse_next(input).map_err(|e| e.cut())
})
}
/// Transforms an [`ErrMode::Cut`][crate::error::ErrMode::Cut] (unrecoverable) to [`ErrMode::Backtrack`][crate::error::ErrMode::Backtrack] (recoverable)
///
/// This attempts the parse, allowing other parsers to be tried on failure, like with
/// [`winnow::combinator::alt`][crate::combinator::alt].
pub fn backtrack_err<Input, Output, Error, ParseNext>(
mut parser: ParseNext,
) -> impl Parser<Input, Output, Error>
where
Input: Stream,
Error: ParserError<Input> + ModalError,
ParseNext: Parser<Input, Output, Error>,
{
trace("backtrack_err", move |input: &mut Input| {
parser.parse_next(input).map_err(|e| e.backtrack())
})
}
/// A placeholder for a not-yet-implemented [`Parser`]
///
/// This is analogous to the [`todo!`] macro and helps with prototyping.
///
/// # Panic
///
/// This will panic when parsing
///
/// # Example
///
/// ```rust
/// # use winnow::prelude::*;
/// # use winnow::combinator::todo;
///
/// fn parser(input: &mut &str) -> ModalResult<u64> {
/// todo(input)
/// }
/// ```
#[track_caller]
pub fn todo<Input, Output, Error>(input: &mut Input) -> Result<Output, Error>
where
Input: Stream,
Error: ParserError<Input>,
{
#![allow(clippy::todo)]
trace("todo", move |_input: &mut Input| {
todo!("unimplemented parse")
})
.parse_next(input)
}
/// Succeed, consuming no input
///
/// For example, it can be used as the last alternative in `alt` to
/// specify the default case.
///
/// Useful with:
/// - [`Parser::value`]
/// - [`Parser::default_value`]
/// - [`Parser::map`]
///
/// <div class="warning">
///
/// **Note:** This never advances the [`Stream`]
///
/// </div>
///
/// # Example
///
/// ```rust
/// # use winnow::prelude::*;
/// use winnow::combinator::alt;
/// use winnow::combinator::empty;
///
/// fn sign(input: &mut &str) -> ModalResult<isize> {
/// alt((
/// '-'.value(-1),
/// '+'.value(1),
/// empty.value(1)
/// )).parse_next(input)
/// }
/// assert_eq!(sign.parse_peek("+10"), Ok(("10", 1)));
/// assert_eq!(sign.parse_peek("-10"), Ok(("10", -1)));
/// assert_eq!(sign.parse_peek("10"), Ok(("10", 1)));
/// ```
#[doc(alias = "value")]
#[doc(alias = "success")]
#[inline]
pub fn empty<Input, Error>(_input: &mut Input) -> Result<(), Error>
where
Input: Stream,
Error: ParserError<Input>,
{
Ok(())
}
/// A parser which always fails.
///
/// For example, it can be used as the last alternative in `alt` to
/// control the error message given.
///
/// # Example
///
/// ```rust
/// # use winnow::{error::ErrMode, error::InputError};
/// # use winnow::prelude::*;
/// use winnow::combinator::fail;
///
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<(), InputError<&'i str>> {
/// fail.parse_next(input)
/// }
///
/// assert_eq!(parser.parse_peek("string"), Err(ErrMode::Backtrack(InputError::at("string"))));
/// ```
#[doc(alias = "unexpected")]
#[inline]
pub fn fail<Input, Output, Error>(i: &mut Input) -> Result<Output, Error>
where
Input: Stream,
Error: ParserError<Input>,
{
trace("fail", |i: &mut Input| Err(ParserError::from_input(i))).parse_next(i)
}
+298
View File
@@ -0,0 +1,298 @@
#![cfg(feature = "std")]
use std::io::Write;
use crate::error::ParserError;
use crate::stream::Stream;
use crate::{Parser, Result};
pub(crate) struct Trace<P, D, I, O, E>
where
P: Parser<I, O, E>,
I: Stream,
D: std::fmt::Display,
E: ParserError<I>,
{
parser: P,
name: D,
call_count: usize,
marker: core::marker::PhantomData<(I, O, E)>,
}
impl<P, D, I, O, E> Trace<P, D, I, O, E>
where
P: Parser<I, O, E>,
I: Stream,
D: std::fmt::Display,
E: ParserError<I>,
{
#[inline(always)]
pub(crate) fn new(parser: P, name: D) -> Self {
Self {
parser,
name,
call_count: 0,
marker: Default::default(),
}
}
}
impl<P, D, I, O, E> Parser<I, O, E> for Trace<P, D, I, O, E>
where
P: Parser<I, O, E>,
I: Stream,
D: std::fmt::Display,
E: ParserError<I>,
{
#[inline]
fn parse_next(&mut self, i: &mut I) -> Result<O, E> {
let depth = Depth::new();
let original = i.checkpoint();
start(*depth, &self.name, self.call_count, i);
let res = self.parser.parse_next(i);
let consumed = i.offset_from(&original);
let severity = Severity::with_result(&res);
end(*depth, &self.name, self.call_count, consumed, severity);
self.call_count += 1;
res
}
}
pub(crate) struct Depth {
depth: usize,
inc: bool,
}
impl Depth {
pub(crate) fn new() -> Self {
let depth = DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
let inc = true;
Self { depth, inc }
}
pub(crate) fn existing() -> Self {
let depth = DEPTH.load(std::sync::atomic::Ordering::SeqCst);
let inc = false;
Self { depth, inc }
}
}
impl Drop for Depth {
fn drop(&mut self) {
if self.inc {
let _ = DEPTH.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
}
}
}
impl AsRef<usize> for Depth {
#[inline(always)]
fn as_ref(&self) -> &usize {
&self.depth
}
}
impl core::ops::Deref for Depth {
type Target = usize;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.depth
}
}
static DEPTH: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
pub(crate) enum Severity {
Success,
Backtrack,
Cut,
Incomplete,
}
impl Severity {
pub(crate) fn with_result<T, I: Stream, E: ParserError<I>>(result: &Result<T, E>) -> Self {
match result {
Ok(_) => Self::Success,
Err(e) if e.is_backtrack() => Self::Backtrack,
Err(e) if e.is_incomplete() => Self::Incomplete,
_ => Self::Cut,
}
}
}
pub(crate) fn start<I: Stream>(
depth: usize,
name: &dyn core::fmt::Display,
count: usize,
input: &I,
) {
let gutter_style = anstyle::Style::new().bold();
let input_style = anstyle::Style::new().underline();
let eof_style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Cyan.into()));
let (call_width, input_width) = column_widths();
let count = if 0 < count {
format!(":{count}")
} else {
"".to_owned()
};
let call_column = format!("{:depth$}> {name}{count}", "");
// The debug version of `slice` might be wider, either due to rendering one byte as two nibbles or
// escaping in strings.
let mut debug_slice = format!("{:?}", crate::util::from_fn(|f| input.trace(f)));
let (debug_slice, eof) = if let Some(debug_offset) = debug_slice
.char_indices()
.enumerate()
.find_map(|(pos, (offset, _))| (input_width <= pos).then_some(offset))
{
debug_slice.truncate(debug_offset);
let eof = "";
(debug_slice, eof)
} else {
let eof = if debug_slice.chars().count() < input_width {
"∅"
} else {
""
};
(debug_slice, eof)
};
let writer = anstream::stderr();
let mut writer = writer.lock();
let _ = writeln!(
writer,
"{call_column:call_width$} {gutter_style}|{gutter_reset} {input_style}{debug_slice}{input_reset}{eof_style}{eof}{eof_reset}",
gutter_style=gutter_style.render(),
gutter_reset=gutter_style.render_reset(),
input_style=input_style.render(),
input_reset=input_style.render_reset(),
eof_style=eof_style.render(),
eof_reset=eof_style.render_reset(),
);
}
pub(crate) fn end(
depth: usize,
name: &dyn core::fmt::Display,
count: usize,
consumed: usize,
severity: Severity,
) {
let gutter_style = anstyle::Style::new().bold();
let (call_width, _) = column_widths();
let count = if 0 < count {
format!(":{count}")
} else {
"".to_owned()
};
let call_column = format!("{:depth$}< {name}{count}", "");
let (status_style, status) = match severity {
Severity::Success => {
let style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into()));
let status = format!("+{consumed}");
(style, status)
}
Severity::Backtrack => (
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Yellow.into())),
"backtrack".to_owned(),
),
Severity::Cut => (
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
"cut".to_owned(),
),
Severity::Incomplete => (
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
"incomplete".to_owned(),
),
};
let writer = anstream::stderr();
let mut writer = writer.lock();
let _ = writeln!(
writer,
"{status_style}{call_column:call_width$}{status_reset} {gutter_style}|{gutter_reset} {status_style}{status}{status_reset}",
gutter_style=gutter_style.render(),
gutter_reset=gutter_style.render_reset(),
status_style=status_style.render(),
status_reset=status_style.render_reset(),
);
}
pub(crate) fn result(depth: usize, name: &dyn core::fmt::Display, severity: Severity) {
let gutter_style = anstyle::Style::new().bold();
let (call_width, _) = column_widths();
let call_column = format!("{:depth$}| {name}", "");
let (status_style, status) = match severity {
Severity::Success => (
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into())),
"",
),
Severity::Backtrack => (
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Yellow.into())),
"backtrack",
),
Severity::Cut => (
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
"cut",
),
Severity::Incomplete => (
anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
"incomplete",
),
};
let writer = anstream::stderr();
let mut writer = writer.lock();
let _ = writeln!(
writer,
"{status_style}{call_column:call_width$}{status_reset} {gutter_style}|{gutter_reset} {status_style}{status}{status_reset}",
gutter_style=gutter_style.render(),
gutter_reset=gutter_style.render_reset(),
status_style=status_style.render(),
status_reset=status_style.render_reset(),
);
}
fn column_widths() -> (usize, usize) {
let term_width = term_width();
let min_call_width = 40;
let min_input_width = 20;
let decor_width = 3;
let extra_width = term_width.saturating_sub(min_call_width + min_input_width + decor_width);
let call_width = min_call_width + 2 * extra_width / 3;
let input_width = min_input_width + extra_width / 3;
(call_width, input_width)
}
fn term_width() -> usize {
columns_env().or_else(query_width).unwrap_or(80)
}
fn query_width() -> Option<usize> {
use is_terminal_polyfill::IsTerminal;
if std::io::stderr().is_terminal() {
terminal_size::terminal_size().map(|(w, _h)| w.0.into())
} else {
None
}
}
fn columns_env() -> Option<usize> {
std::env::var("COLUMNS")
.ok()
.and_then(|c| c.parse::<usize>().ok())
}
+96
View File
@@ -0,0 +1,96 @@
#![cfg_attr(feature = "debug", allow(clippy::std_instead_of_core))]
#[cfg(feature = "debug")]
mod internals;
use crate::error::ParserError;
use crate::stream::Stream;
use crate::Parser;
/// Trace the execution of the parser
///
/// Note that [`Parser::context`] also provides high level trace information.
///
/// See [tutorial][crate::_tutorial::chapter_8] for more details.
///
/// # Example
///
/// ```rust
/// # use winnow::{error::ErrMode, error::Needed};
/// # use winnow::token::take_while;
/// # use winnow::stream::AsChar;
/// # use winnow::prelude::*;
/// use winnow::combinator::trace;
///
/// fn short_alpha<'s>(s: &mut &'s [u8]) -> ModalResult<&'s [u8]> {
/// trace("short_alpha",
/// take_while(3..=6, AsChar::is_alpha)
/// ).parse_next(s)
/// }
///
/// assert_eq!(short_alpha.parse_peek(b"latin123"), Ok((&b"123"[..], &b"latin"[..])));
/// assert_eq!(short_alpha.parse_peek(b"lengthy"), Ok((&b"y"[..], &b"length"[..])));
/// assert_eq!(short_alpha.parse_peek(b"latin"), Ok((&b""[..], &b"latin"[..])));
/// assert!(short_alpha.parse_peek(b"ed").is_err());
/// assert!(short_alpha.parse_peek(b"12345").is_err());
/// ```
#[cfg_attr(not(feature = "debug"), allow(unused_variables))]
#[cfg_attr(not(feature = "debug"), allow(unused_mut))]
#[cfg_attr(not(feature = "debug"), inline(always))]
pub fn trace<I: Stream, O, E: ParserError<I>>(
name: impl core::fmt::Display,
parser: impl Parser<I, O, E>,
) -> impl Parser<I, O, E> {
#[cfg(feature = "debug")]
{
internals::Trace::new(parser, name)
}
#[cfg(not(feature = "debug"))]
{
parser
}
}
#[cfg_attr(not(feature = "debug"), allow(unused_variables))]
pub(crate) fn trace_result<T, I: Stream, E: ParserError<I>>(
name: impl core::fmt::Display,
res: &Result<T, E>,
) {
#[cfg(feature = "debug")]
{
let depth = internals::Depth::existing();
let severity = internals::Severity::with_result(res);
internals::result(*depth, &name, severity);
}
}
pub(crate) struct DisplayDebug<D>(pub(crate) D);
impl<D: core::fmt::Debug> core::fmt::Display for DisplayDebug<D> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:?}", self.0)
}
}
#[test]
#[cfg(feature = "std")]
#[cfg_attr(miri, ignore)]
#[cfg(unix)]
#[cfg(feature = "debug")]
fn example() {
use term_transcript::{test::TestConfig, ShellOptions};
let path = snapbox::cmd::compile_example("string", ["--features=debug"]).unwrap();
let current_dir = path.parent().unwrap();
let cmd = path.file_name().unwrap();
// HACK: term_transcript doesn't allow non-UTF8 paths
let cmd = format!("./{}", cmd.to_string_lossy());
TestConfig::new(
ShellOptions::default()
.with_current_dir(current_dir)
.with_env("CLICOLOR_FORCE", "1"),
)
.test("assets/trace.svg", [format!(r#"{cmd} '"abc"'"#).as_str()]);
}
+561
View File
@@ -0,0 +1,561 @@
use crate::combinator::empty;
use crate::combinator::fail;
use crate::combinator::opt;
use crate::combinator::trace;
use crate::error::ParserError;
use crate::stream::Stream;
use crate::stream::StreamIsPartial;
use crate::Parser;
use crate::Result;
/// Parses an expression based on operator precedence.
///
/// It uses a Pratt parsing algorithm, where operators are
/// associated with a binding power. The higher the power,
/// the more tightly an operator will bind to its operands.
///
/// This method returns an [`Expression`], which configures
/// the Pratt parser.
///
/// Each operator type is configured with [`Prefix`], [`Postfix`],
/// and [`Infix`]. These describe the operator's binding power,
/// a function that applies the operator to its operand, and the
/// operator's associativity (infix only).
///
/// For a more full-featured example, look at the [C-style Expressions][crate::_topic::language#c-style-expressions]
/// topic.
///
/// # Example
///
/// Parsing a simple arithmetic expression without parenthesis.
///
/// ```rust
/// # #[cfg(feature = "ascii")] {
/// # use winnow::prelude::*;
/// # use winnow::error::ContextError;
/// # use winnow::ascii::digit1;
/// # use winnow::combinator::{dispatch, fail};
/// # use winnow::token::any;
/// use winnow::combinator::expression;
/// use winnow::combinator::{Prefix, Postfix, Infix};
///
/// fn parser<'i>() -> impl Parser<&'i str, i32, ContextError> {
/// move |i: &mut &str| {
/// use Infix::*;
/// expression(digit1.parse_to::<i32>()) // operands are 32-bit integers
/// .prefix(dispatch! {any;
/// '-' => Prefix(12, |_, a: i32| Ok(-a)),
/// _ => fail,
/// })
/// .infix(dispatch! {any;
/// '+' => Left(5, |_, a, b| Ok(a + b)),
/// '-' => Left(5, |_, a, b| Ok(a - b)),
/// '*' => Left(7, |_, a, b| Ok(a * b)),
/// '/' => Left(7, |_, a: i32, b| Ok(a.checked_div(b).unwrap_or_default())),
/// _ => fail,
/// })
/// .postfix(dispatch! {any;
/// '!' => Postfix(15, |_, a| if a < 1 { Ok(1) } else { Ok((1..=a).fold(1, |acc, a| acc*a)) }),
/// _ => fail,
/// })
/// .parse_next(i)
/// }
/// }
///
/// assert_eq!(parser().parse("1+1"), Ok(2));
/// assert_eq!(parser().parse("0!"), Ok(1));
/// assert_eq!(parser().parse("-1*5*2*10+30/3!"), Ok(-95));
/// # }
/// ```
#[doc(alias = "pratt")]
#[doc(alias = "separated")]
#[doc(alias = "shunting_yard")]
#[doc(alias = "precedence_climbing")]
#[inline(always)]
#[allow(clippy::type_complexity)]
pub fn expression<I, ParseOperand, O, E>(
parse_operand: ParseOperand,
) -> Expression<
I,
O,
ParseOperand,
impl Parser<I, Prefix<I, O, E>, E>,
impl Parser<I, Postfix<I, O, E>, E>,
impl Parser<I, Infix<I, O, E>, E>,
E,
>
where
I: Stream + StreamIsPartial,
ParseOperand: Parser<I, O, E>,
E: ParserError<I>,
{
Expression {
precedence_level: 0,
parse_operand,
parse_prefix: fail,
parse_postfix: fail,
parse_infix: fail,
marker: Default::default(),
}
}
/// A helper struct for [`expression()`].
///
/// Holds the configuration for the Pratt parser, including
/// the operator and operand parsers. A precedence level can
/// also be set, which is useful to disambiguate parse trees
/// based on the parent operator's precedence.
///
/// Implements [`Parser`]. When parsing an input, it applies
/// the Pratt parser.
pub struct Expression<I, O, ParseOperand, Pre, Post, Pix, E>
where
I: Stream + StreamIsPartial,
ParseOperand: Parser<I, O, E>,
E: ParserError<I>,
{
precedence_level: i64,
parse_operand: ParseOperand,
parse_prefix: Pre,
parse_postfix: Post,
parse_infix: Pix,
marker: core::marker::PhantomData<(I, O, E)>,
}
impl<I, O, ParseOperand, Pre, Post, Pix, E> Expression<I, O, ParseOperand, Pre, Post, Pix, E>
where
ParseOperand: Parser<I, O, E>,
I: Stream + StreamIsPartial,
E: ParserError<I>,
{
/// Sets the prefix operator parser.
///
/// The parser should parse the input to a [`Prefix`],
/// which contains the operator's binding power and
/// a fold function which applies the operator to its
/// operands.
#[inline(always)]
pub fn prefix<NewParsePrefix>(
self,
parser: NewParsePrefix,
) -> Expression<I, O, ParseOperand, NewParsePrefix, Post, Pix, E>
where
NewParsePrefix: Parser<I, Prefix<I, O, E>, E>,
{
Expression {
precedence_level: self.precedence_level,
parse_operand: self.parse_operand,
parse_prefix: parser,
parse_postfix: self.parse_postfix,
parse_infix: self.parse_infix,
marker: Default::default(),
}
}
/// Sets the postfix operator parser.
///
/// The parser should parse the input to a [`Postfix`],
/// which contains the operator's binding power and
/// a fold function which applies the operator to its
/// operands.
#[inline(always)]
pub fn postfix<NewParsePostfix>(
self,
parser: NewParsePostfix,
) -> Expression<I, O, ParseOperand, Pre, NewParsePostfix, Pix, E>
where
NewParsePostfix: Parser<I, Postfix<I, O, E>, E>,
{
Expression {
precedence_level: self.precedence_level,
parse_operand: self.parse_operand,
parse_prefix: self.parse_prefix,
parse_postfix: parser,
parse_infix: self.parse_infix,
marker: Default::default(),
}
}
/// Sets the infix operator parser.
///
/// The parser should parse the input to a [`Infix`],
/// which contains the operator's binding power and
/// a fold function which applies the operator to its
/// operands.
#[inline(always)]
pub fn infix<NewParseInfix>(
self,
parser: NewParseInfix,
) -> Expression<I, O, ParseOperand, Pre, Post, NewParseInfix, E>
where
NewParseInfix: Parser<I, Infix<I, O, E>, E>,
{
Expression {
precedence_level: self.precedence_level,
parse_operand: self.parse_operand,
parse_prefix: self.parse_prefix,
parse_postfix: self.parse_postfix,
parse_infix: parser,
marker: Default::default(),
}
}
/// Sets the precedence level for the current instance of the parser.
///
/// It defaults to 0, which is traditionally treated as the "lowest"
/// possible precedence when parsing an expression.
///
/// This is useful to disambiguate grammars based on the parent operator's
/// precedence. This comes up primarily when parsing recursive expressions.
///
/// The parsing machinery underpinning [`Expression`] assumes that a "more
/// tightly binding" operator is numerically large, while a "more loosely
/// binding" operator is numerically small. For example, `13` is a higher
/// precedence level than `1` because `13 > 1`.
///
/// Other ways of describing this relationship:
/// - `13` has a higher precedence compared to `1`
/// - `13` has a higher binding power compared to `1`
///
/// Note: Binding power and precedence both refer to the same concept and
/// may be used interchangeably.
///
/// # Motivation
///
/// If you don't understand why this is useful to have, this section tries
/// to explain in more detail.
///
/// The [C-style Expressions][crate::_topic::arithmetic#c-style-expression]
/// example has source code for parsing the expression described below, and
/// can provide a clearer usage example.
///
/// Consider the following expression in the C language:
///
/// ```c
/// int x = (1 == 1 ? 0 : 1, -123); // <-- let's parse this
/// printf("%d\n", x); // -123
/// ```
///
/// Let's look at the right-hand side of the expression on the first line,
/// and replace some of the sub-expressions with symbols:
///
/// ```text
/// (1 == 1 ? 0 : 1, -123) // rhs
/// (a ? b : c, d ) // symbolic
/// (a ? b : c, d) // remove whitespace
/// (, (? a b c) d) // prefix notation
/// ```
///
/// Written symbolically:
/// - `a` is the condition, like `1 == 1`
/// - `b` is the value when the condition is true
/// - `c` is the value when the condition is false
/// - `d` is a secondary expression unrelated to the ternary
///
/// In prefix notation, it's easier to see the specific operators and what
/// they bind to:
/// - COMMA (`,`) binds to `(? a b c)` and `d`
/// - TERNARY (`?`) binds to `a`, `b`, and `c`
///
/// ## Parsing `c` and `d`
///
/// Let's focus on parsing the sub-expressions `c` and `d`, as that
/// motivates why a parser precedence level is necessary.
///
/// To parse `c`, we would really like to re-use the parser produced by
/// [`expression()`], because `c` is really *any* valid expression that
/// can be parsed by `expression()` already.
///
/// However, we can't re-use the parser naively. When parsing `c`, we need
/// to "escape" from the inner parser when encountering the comma separating
/// `c` from `d`.
///
/// The reason we have to "escape" is because of how operator precedence is
/// defined in the C language: the comma operator has the lowest precedence
/// among all the operators. When we're parsing `c`, we're in the context of
/// the ternary operator. We don't want to parse any valid expression! Just
/// what the ternary operator captures.
///
/// That's where the precedence level comes in: you specify the minimum
/// precedence this parser is willing to accept. If you come across an
/// expression in the top-level with a lower binding power than the starting
/// precedence, you know to stop parsing.
///
/// The parsing machinery inside of [`Expression`] handles most of this for
/// you, but it can't determine what the precedence level should be for a
/// given expression. That's a language-specific detail, and it depends on
/// what you want to parse.
#[inline(always)]
pub fn current_precedence_level(
mut self,
level: i64,
) -> Expression<I, O, ParseOperand, Pre, Post, Pix, E> {
self.precedence_level = level;
self
}
}
impl<I, O, Pop, Pre, Post, Pix, E> Parser<I, O, E> for Expression<I, O, Pop, Pre, Post, Pix, E>
where
I: Stream + StreamIsPartial,
Pop: Parser<I, O, E>,
Pix: Parser<I, Infix<I, O, E>, E>,
Pre: Parser<I, Prefix<I, O, E>, E>,
Post: Parser<I, Postfix<I, O, E>, E>,
E: ParserError<I>,
{
#[inline(always)]
fn parse_next(&mut self, input: &mut I) -> Result<O, E> {
trace("expression", move |i: &mut I| {
expression_impl(
i,
&mut self.parse_operand,
&mut self.parse_prefix,
&mut self.parse_postfix,
&mut self.parse_infix,
self.precedence_level,
)
})
.parse_next(input)
}
}
/// Opaque implementation of the Pratt parser.
fn expression_impl<I, O, Pop, Pre, Post, Pix, E>(
i: &mut I,
parse_operand: &mut Pop,
prefix: &mut Pre,
postfix: &mut Post,
infix: &mut Pix,
min_power: i64,
) -> Result<O, E>
where
I: Stream + StreamIsPartial,
Pop: Parser<I, O, E>,
Pix: Parser<I, Infix<I, O, E>, E>,
Pre: Parser<I, Prefix<I, O, E>, E>,
Post: Parser<I, Postfix<I, O, E>, E>,
E: ParserError<I>,
{
let operand = opt(trace("operand", parse_operand.by_ref())).parse_next(i)?;
let mut operand = if let Some(operand) = operand {
operand
} else {
// Prefix unary operators
let len = i.eof_offset();
let Prefix(power, fold_prefix) = trace("prefix", prefix.by_ref()).parse_next(i)?;
// infinite loop check: the parser must always consume
if i.eof_offset() == len {
return Err(E::assert(i, "`prefix` parsers must always consume"));
}
let operand = expression_impl(i, parse_operand, prefix, postfix, infix, power)?;
fold_prefix(i, operand)?
};
// A variable to stop the `'parse` loop when `Assoc::Neither` with the same
// precedence is encountered e.g. `a == b == c`. `Assoc::Neither` has similar
// associativity rules as `Assoc::Left`, but we stop parsing when the next operator
// is the same as the current one.
let mut prev_op_is_neither = None;
'parse: while i.eof_offset() > 0 {
// Postfix unary operators
let start = i.checkpoint();
if let Some(Postfix(power, fold_postfix)) =
opt(trace("postfix", postfix.by_ref())).parse_next(i)?
{
// control precedence over the prefix e.g.:
// `--(i++)` or `(--i)++`
if power < min_power {
i.reset(&start);
break 'parse;
}
operand = fold_postfix(i, operand)?;
continue 'parse;
}
// Infix binary operators
let start = i.checkpoint();
let parse_result = opt(trace("infix", infix.by_ref())).parse_next(i)?;
if let Some(infix_op) = parse_result {
let mut is_neither = None;
let (lpower, rpower, fold_infix) = match infix_op {
Infix::Right(p, f) => (p, p - 1, f),
Infix::Left(p, f) => (p, p + 1, f),
Infix::Neither(p, f) => {
is_neither = Some(p);
(p, p + 1, f)
}
};
if lpower < min_power
// MSRV: `is_some_and`
|| match prev_op_is_neither {
None => false,
Some(p) => lpower == p,
}
{
i.reset(&start);
break 'parse;
}
prev_op_is_neither = is_neither;
let rhs = expression_impl(i, parse_operand, prefix, postfix, infix, rpower)?;
operand = fold_infix(i, operand, rhs)?;
continue 'parse;
}
break 'parse;
}
Ok(operand)
}
/// Define an [`expression()`]'s prefix operator
///
/// It requires an operator binding power, as well as a
/// fold function which applies the operator.
pub struct Prefix<I, O, E>(
/// Binding power
pub i64,
/// Unary operator
pub fn(&mut I, O) -> Result<O, E>,
);
impl<I, O, E> Clone for Prefix<I, O, E> {
#[inline(always)]
fn clone(&self) -> Self {
Prefix(self.0, self.1)
}
}
impl<I: Stream, O, E: ParserError<I>> Parser<I, Prefix<I, O, E>, E> for Prefix<I, O, E> {
#[inline(always)]
fn parse_next(&mut self, input: &mut I) -> Result<Prefix<I, O, E>, E> {
empty.value(self.clone()).parse_next(input)
}
}
/// Define an [`expression()`]'s postfix operator
///
/// It requires an operator binding power, as well as a
/// fold function which applies the operator.
pub struct Postfix<I, O, E>(
/// Binding power
pub i64,
/// Unary operator
pub fn(&mut I, O) -> Result<O, E>,
);
impl<I, O, E> Clone for Postfix<I, O, E> {
#[inline(always)]
fn clone(&self) -> Self {
Postfix(self.0, self.1)
}
}
impl<I: Stream, O, E: ParserError<I>> Parser<I, Postfix<I, O, E>, E> for Postfix<I, O, E> {
#[inline(always)]
fn parse_next(&mut self, input: &mut I) -> Result<Postfix<I, O, E>, E> {
empty.value(self.clone()).parse_next(input)
}
}
/// Define an [`expression()`]'s infix operator
///
/// It requires an operator binding power, as well as a
/// fold function which applies the operator.
pub enum Infix<I, O, E> {
/// Left-associative operator
///
/// The operators will bind more tightly to their rightmost operands.
///
/// e.g `A op B op C` -> `(A op B) op C`
Left(
/// Binding power
i64,
/// Binary operator
fn(&mut I, O, O) -> Result<O, E>,
),
/// Right-associative operator
///
/// The operators will bind more tightly to their leftmost operands.
///
/// e.g `A op B op C` -> `A op (B op C)`
Right(
/// Binding power
i64,
/// Binary operator
fn(&mut I, O, O) -> Result<O, E>,
),
/// Neither left or right associative
///
/// `Infix::Neither` has similar associativity rules as `Assoc::Left`, but we stop
/// parsing when the next operator is the same as the current one.
///
/// e.g. `a == b == c` -> `(a == b)`, fail: `(== c)`
Neither(
/// Binding power
i64,
/// Binary operator
fn(&mut I, O, O) -> Result<O, E>,
),
}
impl<I, O, E> Clone for Infix<I, O, E> {
#[inline(always)]
fn clone(&self) -> Self {
match self {
Infix::Left(p, f) => Infix::Left(*p, *f),
Infix::Right(p, f) => Infix::Right(*p, *f),
Infix::Neither(p, f) => Infix::Neither(*p, *f),
}
}
}
impl<I: Stream, O, E: ParserError<I>> Parser<I, Infix<I, O, E>, E> for Infix<I, O, E> {
#[inline(always)]
fn parse_next(&mut self, input: &mut I) -> Result<Infix<I, O, E>, E> {
empty.value(self.clone()).parse_next(input)
}
}
#[cfg(all(test, feature = "ascii"))]
mod tests {
use crate::ascii::digit1;
use crate::combinator::fail;
use crate::dispatch;
use crate::error::ContextError;
use crate::token::any;
use super::*;
fn parser<'i>() -> impl Parser<&'i str, i32, ContextError> {
move |i: &mut &str| {
use Infix::*;
expression(digit1.parse_to::<i32>())
.current_precedence_level(0)
.prefix(dispatch! {any;
'+' => Prefix(12, |_, a| Ok(a)),
'-' => Prefix(12, |_, a: i32| Ok(-a)),
_ => fail
})
.infix(dispatch! {any;
'+' => Left(5, |_, a, b| Ok(a + b)),
'-' => Left(5, |_, a, b| Ok(a - b)),
'*' => Left(7, |_, a, b| Ok(a * b)),
'/' => Left(7, |_, a, b| Ok(a / b)),
'%' => Left(7, |_, a, b| Ok(a % b)),
'^' => Left(9, |_, a, b| Ok(a ^ b)),
_ => fail
})
.parse_next(i)
}
}
#[test]
fn test_expression() {
assert_eq!(parser().parse("-3+-3*4"), Ok(-15));
assert_eq!(parser().parse("+2+3*4"), Ok(14));
assert_eq!(parser().parse("2*3+4"), Ok(10));
}
}
+794
View File
@@ -0,0 +1,794 @@
//! Opaque implementations of [`Parser`]
use crate::combinator::trace;
use crate::combinator::trace_result;
use crate::combinator::DisplayDebug;
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
use crate::error::FromRecoverableError;
use crate::error::ParseError;
use crate::error::{AddContext, FromExternalError, ParserError};
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
use crate::stream::Recover;
use crate::stream::StreamIsPartial;
use crate::stream::{Location, Stream};
use crate::{Parser, Result};
use core::borrow::Borrow;
use core::ops::Range;
/// Iterator implementation for [`Parser::parse_iter`]
pub struct ParseIter<'p, P, I, O, E>
where
I: Stream,
{
pub(crate) parser: &'p mut P,
pub(crate) input: Option<I>,
pub(crate) start: Option<I::Checkpoint>,
pub(crate) marker: core::marker::PhantomData<(O, E)>,
}
impl<'p, I, O, E, P> Iterator for ParseIter<'p, P, I, O, E>
where
P: Parser<I, O, E>,
I: Stream,
// Force users to deal with `Incomplete` when `StreamIsPartial<true>`
I: StreamIsPartial,
E: ParserError<I>,
<E as ParserError<I>>::Inner: ParserError<I>,
{
type Item = Result<O, ParseError<I, <E as ParserError<I>>::Inner>>;
fn next(&mut self) -> Option<Self::Item> {
let input = self.input.as_mut()?;
let len = input.eof_offset();
if len == 0 {
self.input = None;
return None;
}
let mut output = self.parser.parse_next(input);
// infinite loop check: the parser must always consume
if output.is_ok() && input.eof_offset() == len {
let err = <E as ParserError<I>>::assert(
input,
"`Parser::parse_iter` parsers must always consume",
);
output = Err(err);
}
match output {
Ok(output) => Some(Ok(output)),
Err(err) => {
let err = err.into_inner().unwrap_or_else(|_err| {
panic!("complete parsers should not report `ErrMode::Incomplete(_)`")
});
let input = self.input.take()?;
let start = self.start.take()?;
Some(Err(ParseError::new(input, start, err)))
}
}
}
}
/// [`Parser`] implementation for [`Parser::by_ref`]
pub struct ByRef<'p, P, I, O, E> {
pub(crate) p: &'p mut P,
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
}
impl<I, O, E, P> Parser<I, O, E> for ByRef<'_, P, I, O, E>
where
P: Parser<I, O, E>,
{
#[inline(always)]
fn parse_next(&mut self, i: &mut I) -> Result<O, E> {
self.p.parse_next(i)
}
}
/// [`Parser`] implementation for [`Parser::map`]
pub struct Map<F, G, I, O, O2, E>
where
F: Parser<I, O, E>,
G: FnMut(O) -> O2,
{
pub(crate) parser: F,
pub(crate) map: G,
pub(crate) marker: core::marker::PhantomData<(I, O, E, O2)>,
}
impl<F, G, I, O, O2, E> Parser<I, O2, E> for Map<F, G, I, O, O2, E>
where
F: Parser<I, O, E>,
G: FnMut(O) -> O2,
{
#[inline]
fn parse_next(&mut self, i: &mut I) -> Result<O2, E> {
match self.parser.parse_next(i) {
Err(e) => Err(e),
Ok(o) => Ok((self.map)(o)),
}
}
}
/// [`Parser`] implementation for [`Parser::try_map`]
pub struct TryMap<F, G, I, O, O2, E, E2>
where
F: Parser<I, O, E>,
G: FnMut(O) -> Result<O2, E2>,
I: Stream,
E: FromExternalError<I, E2>,
E: ParserError<I>,
{
pub(crate) parser: F,
pub(crate) map: G,
pub(crate) marker: core::marker::PhantomData<(I, O, O2, E, E2)>,
}
impl<F, G, I, O, O2, E, E2> Parser<I, O2, E> for TryMap<F, G, I, O, O2, E, E2>
where
F: Parser<I, O, E>,
G: FnMut(O) -> Result<O2, E2>,
I: Stream,
E: FromExternalError<I, E2>,
E: ParserError<I>,
{
#[inline]
fn parse_next(&mut self, input: &mut I) -> Result<O2, E> {
let start = input.checkpoint();
let o = self.parser.parse_next(input)?;
let res = (self.map)(o).map_err(|err| {
input.reset(&start);
E::from_external_error(input, err)
});
trace_result("verify", &res);
res
}
}
/// [`Parser`] implementation for [`Parser::verify_map`]
pub struct VerifyMap<F, G, I, O, O2, E>
where
F: Parser<I, O, E>,
G: FnMut(O) -> Option<O2>,
I: Stream,
E: ParserError<I>,
{
pub(crate) parser: F,
pub(crate) map: G,
pub(crate) marker: core::marker::PhantomData<(I, O, E, O2)>,
}
impl<F, G, I, O, O2, E> Parser<I, O2, E> for VerifyMap<F, G, I, O, O2, E>
where
F: Parser<I, O, E>,
G: FnMut(O) -> Option<O2>,
I: Stream,
E: ParserError<I>,
{
#[inline]
fn parse_next(&mut self, input: &mut I) -> Result<O2, E> {
let start = input.checkpoint();
let o = self.parser.parse_next(input)?;
let res = (self.map)(o).ok_or_else(|| {
input.reset(&start);
ParserError::from_input(input)
});
trace_result("verify", &res);
res
}
}
/// [`Parser`] implementation for [`Parser::and_then`]
pub struct AndThen<F, G, I, O, O2, E>
where
F: Parser<I, O, E>,
G: Parser<O, O2, E>,
O: StreamIsPartial,
I: Stream,
{
pub(crate) outer: F,
pub(crate) inner: G,
pub(crate) marker: core::marker::PhantomData<(I, O, O2, E)>,
}
impl<F, G, I, O, O2, E> Parser<I, O2, E> for AndThen<F, G, I, O, O2, E>
where
F: Parser<I, O, E>,
G: Parser<O, O2, E>,
O: StreamIsPartial,
I: Stream,
{
#[inline(always)]
fn parse_next(&mut self, i: &mut I) -> Result<O2, E> {
let start = i.checkpoint();
let mut o = self.outer.parse_next(i)?;
let _ = o.complete();
let o2 = self.inner.parse_next(&mut o).map_err(|err| {
i.reset(&start);
err
})?;
Ok(o2)
}
}
/// [`Parser`] implementation for [`Parser::parse_to`]
pub struct ParseTo<P, I, O, O2, E>
where
P: Parser<I, O, E>,
I: Stream,
O: crate::stream::ParseSlice<O2>,
E: ParserError<I>,
{
pub(crate) p: P,
pub(crate) marker: core::marker::PhantomData<(I, O, O2, E)>,
}
impl<P, I, O, O2, E> Parser<I, O2, E> for ParseTo<P, I, O, O2, E>
where
P: Parser<I, O, E>,
I: Stream,
O: crate::stream::ParseSlice<O2>,
E: ParserError<I>,
{
#[inline]
fn parse_next(&mut self, i: &mut I) -> Result<O2, E> {
let start = i.checkpoint();
let o = self.p.parse_next(i)?;
let res = o.parse_slice().ok_or_else(|| {
i.reset(&start);
ParserError::from_input(i)
});
trace_result("verify", &res);
res
}
}
/// [`Parser`] implementation for [`Parser::flat_map`]
pub struct FlatMap<F, G, H, I, O, O2, E>
where
F: Parser<I, O, E>,
G: FnMut(O) -> H,
H: Parser<I, O2, E>,
{
pub(crate) f: F,
pub(crate) g: G,
pub(crate) marker: core::marker::PhantomData<(H, I, O, O2, E)>,
}
impl<F, G, H, I, O, O2, E> Parser<I, O2, E> for FlatMap<F, G, H, I, O, O2, E>
where
F: Parser<I, O, E>,
G: FnMut(O) -> H,
H: Parser<I, O2, E>,
{
#[inline(always)]
fn parse_next(&mut self, i: &mut I) -> Result<O2, E> {
let o = self.f.parse_next(i)?;
(self.g)(o).parse_next(i)
}
}
/// [`Parser`] implementation for [`Parser::complete_err`]
pub struct CompleteErr<P, I, O, E> {
pub(crate) p: P,
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
}
impl<P, I, O, E> Parser<I, O, E> for CompleteErr<P, I, O, E>
where
P: Parser<I, O, E>,
I: Stream,
E: ParserError<I>,
{
#[inline]
fn parse_next(&mut self, input: &mut I) -> Result<O, E> {
trace("complete_err", |input: &mut I| {
match (self.p).parse_next(input) {
Err(err) => match err.needed() {
Some(_) => Err(ParserError::from_input(input)),
None => Err(err),
},
rest => rest,
}
})
.parse_next(input)
}
}
/// [`Parser`] implementation for [`Parser::verify`]
pub struct Verify<F, G, I, O, O2, E>
where
F: Parser<I, O, E>,
G: FnMut(&O2) -> bool,
I: Stream,
O: Borrow<O2>,
O2: ?Sized,
E: ParserError<I>,
{
pub(crate) parser: F,
pub(crate) filter: G,
pub(crate) marker: core::marker::PhantomData<(I, O, E, O2)>,
}
impl<F, G, I, O, O2, E> Parser<I, O, E> for Verify<F, G, I, O, O2, E>
where
F: Parser<I, O, E>,
G: FnMut(&O2) -> bool,
I: Stream,
O: Borrow<O2>,
O2: ?Sized,
E: ParserError<I>,
{
#[inline]
fn parse_next(&mut self, input: &mut I) -> Result<O, E> {
let start = input.checkpoint();
let o = self.parser.parse_next(input)?;
let res = (self.filter)(o.borrow()).then_some(o).ok_or_else(|| {
input.reset(&start);
ParserError::from_input(input)
});
trace_result("verify", &res);
res
}
}
/// [`Parser`] implementation for [`Parser::value`]
pub struct Value<F, I, O, O2, E>
where
F: Parser<I, O, E>,
O2: Clone,
{
pub(crate) parser: F,
pub(crate) val: O2,
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
}
impl<F, I, O, O2, E> Parser<I, O2, E> for Value<F, I, O, O2, E>
where
F: Parser<I, O, E>,
O2: Clone,
{
#[inline]
fn parse_next(&mut self, input: &mut I) -> Result<O2, E> {
(self.parser).parse_next(input).map(|_| self.val.clone())
}
}
/// [`Parser`] implementation for [`Parser::default_value`]
pub struct DefaultValue<F, I, O, O2, E>
where
F: Parser<I, O, E>,
O2: core::default::Default,
{
pub(crate) parser: F,
pub(crate) marker: core::marker::PhantomData<(O2, I, O, E)>,
}
impl<F, I, O, O2, E> Parser<I, O2, E> for DefaultValue<F, I, O, O2, E>
where
F: Parser<I, O, E>,
O2: core::default::Default,
{
#[inline]
fn parse_next(&mut self, input: &mut I) -> Result<O2, E> {
(self.parser).parse_next(input).map(|_| O2::default())
}
}
/// [`Parser`] implementation for [`Parser::void`]
pub struct Void<F, I, O, E>
where
F: Parser<I, O, E>,
{
pub(crate) parser: F,
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
}
impl<F, I, O, E> Parser<I, (), E> for Void<F, I, O, E>
where
F: Parser<I, O, E>,
{
#[inline(always)]
fn parse_next(&mut self, input: &mut I) -> Result<(), E> {
(self.parser).parse_next(input).map(|_| ())
}
}
/// [`Parser`] implementation for [`Parser::take`]
pub struct Take<F, I, O, E>
where
F: Parser<I, O, E>,
I: Stream,
{
pub(crate) parser: F,
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
}
impl<I, O, E, F> Parser<I, <I as Stream>::Slice, E> for Take<F, I, O, E>
where
F: Parser<I, O, E>,
I: Stream,
{
#[inline]
fn parse_next(&mut self, input: &mut I) -> Result<<I as Stream>::Slice, E> {
let checkpoint = input.checkpoint();
match (self.parser).parse_next(input) {
Ok(_) => {
let offset = input.offset_from(&checkpoint);
input.reset(&checkpoint);
let taken = input.next_slice(offset);
Ok(taken)
}
Err(e) => Err(e),
}
}
}
/// [`Parser`] implementation for [`Parser::with_taken`]
pub struct WithTaken<F, I, O, E>
where
F: Parser<I, O, E>,
I: Stream,
{
pub(crate) parser: F,
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
}
impl<F, I, O, E> Parser<I, (O, <I as Stream>::Slice), E> for WithTaken<F, I, O, E>
where
F: Parser<I, O, E>,
I: Stream,
{
#[inline]
fn parse_next(&mut self, input: &mut I) -> Result<(O, <I as Stream>::Slice), E> {
let checkpoint = input.checkpoint();
match (self.parser).parse_next(input) {
Ok(result) => {
let offset = input.offset_from(&checkpoint);
input.reset(&checkpoint);
let taken = input.next_slice(offset);
Ok((result, taken))
}
Err(e) => Err(e),
}
}
}
/// [`Parser`] implementation for [`Parser::span`]
pub struct Span<F, I, O, E>
where
F: Parser<I, O, E>,
I: Stream + Location,
{
pub(crate) parser: F,
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
}
impl<I, O, E, F> Parser<I, Range<usize>, E> for Span<F, I, O, E>
where
F: Parser<I, O, E>,
I: Stream + Location,
{
#[inline]
fn parse_next(&mut self, input: &mut I) -> Result<Range<usize>, E> {
let start = input.current_token_start();
self.parser.parse_next(input).map(move |_| {
let end = input.previous_token_end();
start..end
})
}
}
/// [`Parser`] implementation for [`Parser::with_span`]
pub struct WithSpan<F, I, O, E>
where
F: Parser<I, O, E>,
I: Stream + Location,
{
pub(crate) parser: F,
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
}
impl<F, I, O, E> Parser<I, (O, Range<usize>), E> for WithSpan<F, I, O, E>
where
F: Parser<I, O, E>,
I: Stream + Location,
{
#[inline]
fn parse_next(&mut self, input: &mut I) -> Result<(O, Range<usize>), E> {
let start = input.current_token_start();
self.parser.parse_next(input).map(move |output| {
let end = input.previous_token_end();
(output, (start..end))
})
}
}
/// [`Parser`] implementation for [`Parser::output_into`]
pub struct OutputInto<F, I, O, O2, E>
where
F: Parser<I, O, E>,
O: Into<O2>,
{
pub(crate) parser: F,
pub(crate) marker: core::marker::PhantomData<(I, O, O2, E)>,
}
impl<F, I, O, O2, E> Parser<I, O2, E> for OutputInto<F, I, O, O2, E>
where
F: Parser<I, O, E>,
O: Into<O2>,
{
#[inline]
fn parse_next(&mut self, i: &mut I) -> Result<O2, E> {
self.parser.parse_next(i).map(|o| o.into())
}
}
/// [`Parser`] implementation for [`Parser::err_into`]
pub struct ErrInto<F, I, O, E, E2>
where
F: Parser<I, O, E>,
E: Into<E2>,
{
pub(crate) parser: F,
pub(crate) marker: core::marker::PhantomData<(I, O, E, E2)>,
}
impl<F, I, O, E, E2> Parser<I, O, E2> for ErrInto<F, I, O, E, E2>
where
F: Parser<I, O, E>,
E: Into<E2>,
{
#[inline]
fn parse_next(&mut self, i: &mut I) -> Result<O, E2> {
self.parser.parse_next(i).map_err(|err| err.into())
}
}
/// [`Parser`] implementation for [`Parser::context`]
pub struct Context<F, I, O, E, C>
where
F: Parser<I, O, E>,
I: Stream,
E: AddContext<I, C>,
E: ParserError<I>,
C: Clone + core::fmt::Debug,
{
pub(crate) parser: F,
pub(crate) context: C,
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
}
impl<F, I, O, E, C> Parser<I, O, E> for Context<F, I, O, E, C>
where
F: Parser<I, O, E>,
I: Stream,
E: AddContext<I, C>,
E: ParserError<I>,
C: Clone + core::fmt::Debug,
{
#[inline]
fn parse_next(&mut self, i: &mut I) -> Result<O, E> {
let context = self.context.clone();
trace(DisplayDebug(self.context.clone()), move |i: &mut I| {
let start = i.checkpoint();
(self.parser)
.parse_next(i)
.map_err(|err| err.add_context(i, &start, context.clone()))
})
.parse_next(i)
}
}
/// [`Parser`] implementation for [`Parser::context`]
pub struct ContextWith<P, I, O, E, F, C, FI>
where
P: Parser<I, O, E>,
I: Stream,
E: AddContext<I, C>,
E: ParserError<I>,
F: Fn() -> FI + Clone,
C: core::fmt::Debug,
FI: Iterator<Item = C>,
{
pub(crate) parser: P,
pub(crate) context: F,
pub(crate) marker: core::marker::PhantomData<(I, O, E, C, FI)>,
}
impl<P, I, O, E, F, C, FI> Parser<I, O, E> for ContextWith<P, I, O, E, F, C, FI>
where
P: Parser<I, O, E>,
I: Stream,
E: AddContext<I, C>,
E: ParserError<I>,
F: Fn() -> FI + Clone,
C: core::fmt::Debug,
FI: Iterator<Item = C>,
{
#[inline]
fn parse_next(&mut self, i: &mut I) -> Result<O, E> {
let context = self.context.clone();
let start = i.checkpoint();
(self.parser).parse_next(i).map_err(|mut err| {
for context in context() {
err = err.add_context(i, &start, context);
}
err
})
}
}
/// [`Parser`] implementation for [`Parser::map_err`]
pub struct MapErr<F, G, I, O, E, E2>
where
F: Parser<I, O, E>,
G: FnMut(E) -> E2,
{
pub(crate) parser: F,
pub(crate) map: G,
pub(crate) marker: core::marker::PhantomData<(I, O, E, E2)>,
}
impl<F, G, I, O, E, E2> Parser<I, O, E2> for MapErr<F, G, I, O, E, E2>
where
F: Parser<I, O, E>,
G: FnMut(E) -> E2,
{
#[inline]
fn parse_next(&mut self, i: &mut I) -> Result<O, E2> {
match self.parser.parse_next(i) {
Err(e) => Err((self.map)(e)),
Ok(o) => Ok(o),
}
}
}
/// [`Parser`] implementation for [`Parser::retry_after`]
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
pub struct RetryAfter<P, R, I, O, E>
where
P: Parser<I, O, E>,
R: Parser<I, (), E>,
I: Stream,
I: Recover<E>,
E: ParserError<I> + FromRecoverableError<I, E>,
{
pub(crate) parser: P,
pub(crate) recover: R,
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
}
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
impl<P, R, I, O, E> Parser<I, O, E> for RetryAfter<P, R, I, O, E>
where
P: Parser<I, O, E>,
R: Parser<I, (), E>,
I: Stream,
I: Recover<E>,
E: ParserError<I> + FromRecoverableError<I, E>,
{
#[inline(always)]
fn parse_next(&mut self, i: &mut I) -> Result<O, E> {
if I::is_recovery_supported() {
retry_after_inner(&mut self.parser, &mut self.recover, i)
} else {
self.parser.parse_next(i)
}
}
}
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
fn retry_after_inner<P, R, I, O, E>(parser: &mut P, recover: &mut R, i: &mut I) -> Result<O, E>
where
P: Parser<I, O, E>,
R: Parser<I, (), E>,
I: Stream,
I: Recover<E>,
E: ParserError<I> + FromRecoverableError<I, E>,
{
loop {
let token_start = i.checkpoint();
let mut err = match parser.parse_next(i) {
Ok(o) => {
return Ok(o);
}
Err(e) if e.is_incomplete() => return Err(e),
Err(err) => err,
};
let err_start = i.checkpoint();
let err_start_eof_offset = i.eof_offset();
if recover.parse_next(i).is_ok() {
let i_eof_offset = i.eof_offset();
if err_start_eof_offset == i_eof_offset {
// Didn't advance so bubble the error up
} else if let Err(err_) = i.record_err(&token_start, &err_start, err) {
err = err_;
} else {
continue;
}
}
i.reset(&err_start);
err = E::from_recoverable_error(&token_start, &err_start, i, err);
return Err(err);
}
}
/// [`Parser`] implementation for [`Parser::resume_after`]
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
pub struct ResumeAfter<P, R, I, O, E>
where
P: Parser<I, O, E>,
R: Parser<I, (), E>,
I: Stream,
I: Recover<E>,
E: ParserError<I> + FromRecoverableError<I, E>,
{
pub(crate) parser: P,
pub(crate) recover: R,
pub(crate) marker: core::marker::PhantomData<(I, O, E)>,
}
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
impl<P, R, I, O, E> Parser<I, Option<O>, E> for ResumeAfter<P, R, I, O, E>
where
P: Parser<I, O, E>,
R: Parser<I, (), E>,
I: Stream,
I: Recover<E>,
E: ParserError<I> + FromRecoverableError<I, E>,
{
#[inline(always)]
fn parse_next(&mut self, i: &mut I) -> Result<Option<O>, E> {
if I::is_recovery_supported() {
resume_after_inner(&mut self.parser, &mut self.recover, i)
} else {
self.parser.parse_next(i).map(Some)
}
}
}
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
fn resume_after_inner<P, R, I, O, E>(
parser: &mut P,
recover: &mut R,
i: &mut I,
) -> Result<Option<O>, E>
where
P: Parser<I, O, E>,
R: Parser<I, (), E>,
I: Stream,
I: Recover<E>,
E: ParserError<I> + FromRecoverableError<I, E>,
{
let token_start = i.checkpoint();
let mut err = match parser.parse_next(i) {
Ok(o) => {
return Ok(Some(o));
}
Err(e) if e.is_incomplete() => return Err(e),
Err(err) => err,
};
let err_start = i.checkpoint();
if recover.parse_next(i).is_ok() {
if let Err(err_) = i.record_err(&token_start, &err_start, err) {
err = err_;
} else {
return Ok(None);
}
}
i.reset(&err_start);
err = FromRecoverableError::from_recoverable_error(&token_start, &err_start, i, err);
Err(err)
}
+189
View File
@@ -0,0 +1,189 @@
//! # List of parsers and combinators
//!
//! <div class="warning">
//!
//! **Note**: this list is meant to provide a nicer way to find a parser than reading through the documentation on docs.rs. Function combinators are organized in module so they are a bit easier to find.
//!
//! </div>
//!
//! ## Basic elements
//!
//! Those are used to take a series of tokens for the lowest level elements of your grammar, like, "here is a dot", or "here is an big endian integer".
//!
//! | combinator | usage | input | new input | output | comment |
//! |---|---|---|---|---|---|
//! | [`one_of`][crate::token::one_of] | `one_of(['a', 'b', 'c'])` | `"abc"` | `"bc"` | `Ok('a')` |Matches one of the provided [set of tokens][crate::stream::ContainsToken] (works with non ASCII characters too)|
//! | [`none_of`][crate::token::none_of] | `none_of(['a', 'b', 'c'])` | `"xyab"` | `"yab"` | `Ok('x')` |Matches anything but one of the provided [set of tokens][crate::stream::ContainsToken]|
//! | [`literal`][crate::token::literal] | `"hello"` | `"hello world"` | `" world"` | `Ok("hello")` |Recognizes a specific suite of characters or bytes (see also [`Caseless`][crate::ascii::Caseless])|
//! | [`take`][crate::token::take] | `take(4)` | `"hello"` | `"o"` | `Ok("hell")` |Takes a specific number of bytes or characters|
//! | [`take_while`][crate::token::take_while] | `take_while(0.., is_alphabetic)` | `"abc123"` | `"123"` | `Ok("abc")` |Returns the longest slice of bytes or characters for which the provided [set of tokens][crate::stream::ContainsToken] matches.|
//! | [`take_till`][crate::token::take_till] | `take_till(0.., is_alphabetic)` | `"123abc"` | `"abc"` | `Ok("123")` |Returns a slice of bytes or characters until the provided [set of tokens][crate::stream::ContainsToken] matches. This is the reverse behaviour from `take_while`: `take_till(f)` is equivalent to `take_while(0.., \|c\| !f(c))`|
//! | [`take_until`][crate::token::take_until] | `take_until(0.., "world")` | `"Hello world"` | `"world"` | `Ok("Hello ")` |Returns a slice of bytes or characters until the provided [literal][crate::token::literal] is found.|
//!
//! ## Choice combinators
//!
//! | combinator | usage | input | new input | output | comment |
//! |---|---|---|---|---|---|
//! | [`alt`] | `alt(("ab", "cd"))` | `"cdef"` | `"ef"` | `Ok("cd")` |Try a list of parsers and return the result of the first successful one|
//! | [`dispatch`] | \- | \- | \- | \- | `match` for parsers |
//!
//! ## Sequence combinators
//!
//! | combinator | usage | input | new input | output | comment |
//! |---|---|---|---|---|---|
//! | [`(...)` (tuples)][crate::Parser] | `("ab", "XY", take(1))` | `"abXYZ!"` | `"!"` | `Ok(("ab", "XY", "Z"))` |Parse a series of values|
//! | [`seq!`] | `seq!(_: '(', take(2), _: ')')` | `"(ab)cd"` | `"cd"` | `Ok("ab")` |Parse a series of values, discarding those you specify|
//! | [`delimited`] | `delimited('(', take(2), ')')` | `"(ab)cd"` | `"cd"` | `Ok("ab")` |Parse three values, discarding the first and third value|
//! | [`preceded`] | `preceded("ab", "XY")` | `"abXYZ"` | `"Z"` | `Ok("XY")` |Parse two values, discarding the first value|
//! | [`terminated`] | `terminated("ab", "XY")` | `"abXYZ"` | `"Z"` | `Ok("ab")` |Parse two values, discarding the second value|
//! | [`separated_pair`] | `separated_pair("hello", ',', "world")` | `"hello,world!"` | `"!"` | `Ok(("hello", "world"))` | Parse three values, discarding the middle value|
//!
//! ## Applying a parser multiple times
//!
//! | combinator | usage | input | new input | output | comment |
//! |---|---|---|---|---|---|
//! | [`repeat`] | `repeat(1..=3, "ab")` | `"ababc"` | `"c"` | `Ok(vec!["ab", "ab"])` |Applies the parser between m and n times (n included) and returns the list of results in a Vec|
//! | [`repeat_till`] | `repeat_till(0.., "ab", "ef")` | `"ababefg"` | `"g"` | `Ok((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`] | `separated(1..=3, "ab", ",")` | `"ab,ab,ab."` | `"."` | `Ok(vec!["ab", "ab", "ab"])` |Applies the parser and separator between m and n times (n included) and returns the list of results in a Vec|
//! | [`Repeat::fold`] | <code>repeat(1..=2, `be_u8`).fold(\|\| 0, \|acc, item\| acc + item)</code> | `[1, 2, 3]` | `[3]` | `Ok(3)` |Applies the parser between m and n times (n included) and folds the list of return value|
//!
//! ## Partial related
//!
//! - [`eof`]: Returns its input if it is at the end of input data
//! - [`Parser::complete_err`]: Replaces an `Incomplete` returned by the child parser with an `Backtrack`
//!
//! ## Modifiers
//!
//! - [`cond`]: Conditional combinator. Wraps another parser and calls it if the condition is met
//! - [`Parser::flat_map`]: method to map a new parser from the output of the first parser, then apply that parser over the rest of the input
//! - [`Parser::value`]: method to replace the result of a parser
//! - [`Parser::default_value`]: method to replace the result of a parser
//! - [`Parser::void`]: method to discard the result of a parser
//! - [`Parser::map`]: method to map a function on the result of a parser
//! - [`Parser::and_then`]: Applies a second parser over the output of the first one
//! - [`Parser::verify_map`]: Maps a function returning an `Option` on the output of a parser
//! - [`Parser::try_map`]: Maps a function returning a `Result` on the output of a parser
//! - [`Parser::parse_to`]: Apply [`std::str::FromStr`] to the output of the parser
//! - [`not`]: Returns a result only if the embedded parser returns `Backtrack` or `Incomplete`. Does not consume the input
//! - [`opt`]: Make the underlying parser optional
//! - [`peek`]: Returns a result without consuming the input
//! - [`Parser::take`]: If the child parser was successful, return the consumed input as the produced value
//! - [`Parser::with_taken`]: If the child parser was successful, return a tuple of the consumed input and the produced output.
//! - [`Parser::span`]: If the child parser was successful, return the location of the consumed input as the produced value
//! - [`Parser::with_span`]: If the child parser was successful, return a tuple of the location of the consumed input and the produced output.
//! - [`Parser::verify`]: Returns the result of the child parser if it satisfies a verification function
//!
//! ## Error management and debugging
//!
//! - [`cut_err`]: Commit the parse result, disallowing alternative parsers from being attempted
//! - [`backtrack_err`]: Attempts a parse, allowing alternative parsers to be attempted despite
//! use of `cut_err`
//! - [`Parser::context`]: Add context to the error if the parser fails
//! - [`trace`]: Print the parse state with the `debug` feature flag
//! - [`todo()`]: Placeholder parser
//!
//! ## Remaining combinators
//!
//! - [`empty`]: Succeed, consuming no input
//! - [`fail`]: Inversion of [`empty`]. Always fails.
//! - [`Parser::by_ref`]: Allow moving `&mut impl Parser` into other parsers
//!
//! ## Text parsing
//!
//! - [`any`][crate::token::any]: Matches one token
//! - [`tab`][crate::ascii::tab]: Matches a tab character `\t`
//! - [`crlf`][crate::ascii::crlf]: Recognizes the string `\r\n`
//! - [`line_ending`][crate::ascii::line_ending]: Recognizes an end of line (both `\n` and `\r\n`)
//! - [`newline`][crate::ascii::newline]: Matches a newline character `\n`
//! - [`till_line_ending`][crate::ascii::till_line_ending]: Recognizes a string of any char except `\r` or `\n`
//! - [`rest`][crate::token::rest]: Return the remaining input
//!
//! - [`alpha0`][crate::ascii::alpha0]: Recognizes zero or more lowercase and uppercase alphabetic characters: `[a-zA-Z]`. [`alpha1`][crate::ascii::alpha1] does the same but returns at least one character
//! - [`alphanumeric0`][crate::ascii::alphanumeric0]: Recognizes zero or more numerical and alphabetic characters: `[0-9a-zA-Z]`. [`alphanumeric1`][crate::ascii::alphanumeric1] does the same but returns at least one character
//! - [`space0`][crate::ascii::space0]: Recognizes zero or more spaces and tabs. [`space1`][crate::ascii::space1] does the same but returns at least one character
//! - [`multispace0`][crate::ascii::multispace0]: Recognizes zero or more spaces, tabs, carriage returns and line feeds. [`multispace1`][crate::ascii::multispace1] does the same but returns at least one character
//! - [`digit0`][crate::ascii::digit0]: Recognizes zero or more numerical characters: `[0-9]`. [`digit1`][crate::ascii::digit1] does the same but returns at least one character
//! - [`hex_digit0`][crate::ascii::hex_digit0]: Recognizes zero or more hexadecimal numerical characters: `[0-9A-Fa-f]`. [`hex_digit1`][crate::ascii::hex_digit1] does the same but returns at least one character
//! - [`oct_digit0`][crate::ascii::oct_digit0]: Recognizes zero or more octal characters: `[0-7]`. [`oct_digit1`][crate::ascii::oct_digit1] does the same but returns at least one character
//!
//! - [`float`][crate::ascii::float]: Parse a floating point number in a byte string
//! - [`dec_int`][crate::ascii::dec_int]: Decode a variable-width, decimal signed integer
//! - [`dec_uint`][crate::ascii::dec_uint]: Decode a variable-width, decimal unsigned integer
//! - [`hex_uint`][crate::ascii::hex_uint]: Decode a variable-width, hexadecimal integer
//!
//! - [`take_escaped`][crate::ascii::take_escaped]: Recognize the input slice with escaped characters
//! - [`escaped`][crate::ascii::escaped]: Parse escaped characters, unescaping them
//!
//! - [`expression()`]: Parse an operator precedence expression with Pratt parsing
//!
//! ### Character test functions
//!
//! Use these functions with a combinator like `take_while`:
//!
//! - [`AsChar::is_alpha`][crate::stream::AsChar::is_alpha]: Tests if byte is ASCII alphabetic: `[A-Za-z]`
//! - [`AsChar::is_alphanum`][crate::stream::AsChar::is_alphanum]: Tests if byte is ASCII alphanumeric: `[A-Za-z0-9]`
//! - [`AsChar::is_dec_digit`][crate::stream::AsChar::is_dec_digit]: Tests if byte is ASCII digit: `[0-9]`
//! - [`AsChar::is_hex_digit`][crate::stream::AsChar::is_hex_digit]: Tests if byte is ASCII hex digit: `[0-9A-Fa-f]`
//! - [`AsChar::is_oct_digit`][crate::stream::AsChar::is_oct_digit]: Tests if byte is ASCII octal digit: `[0-7]`
//! - [`AsChar::is_space`][crate::stream::AsChar::is_space]: Tests if byte is ASCII space or tab: `[ \t]`
//! - [`AsChar::is_newline`][crate::stream::AsChar::is_newline]: Tests if byte is ASCII newline: `[\n]`
//!
//! ## Binary format parsing
//!
//! - [`length_repeat`][crate::binary::length_repeat] Gets a number from the first parser, then applies the second parser that many times
//! - [`length_take`][crate::binary::length_take]: Gets a number from the first parser, then takes a subslice of the input of that size, and returns that subslice
//! - [`length_and_then`][crate::binary::length_and_then]: 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
//!
//! ### Integers
//!
//! Parsing integers from binary formats can be done in two ways: With parser functions, or combinators with configurable endianness.
//!
//! - **configurable endianness:** [`i16`][crate::binary::i16], [`i32`][crate::binary::i32],
//! [`i64`][crate::binary::i64], [`u16`][crate::binary::u16], [`u32`][crate::binary::u32],
//! [`u64`][crate::binary::u64] are combinators that take as argument a
//! [`winnow::binary::Endianness`][crate::binary::Endianness], like this: `i16(endianness)`. If the
//! parameter is `winnow::binary::Endianness::Big`, parse a big endian `i16` integer, otherwise a
//! little endian `i16` integer.
//! - **fixed endianness**: The functions are prefixed by `be_` for big endian numbers, and by `le_` for little endian numbers, and the suffix is the type they parse to. As an example, `be_u32` parses a big endian unsigned integer stored in 32 bits.
//! - [`be_f32`][crate::binary::be_f32], [`be_f64`][crate::binary::be_f64]: Big endian floating point numbers
//! - [`le_f32`][crate::binary::le_f32], [`le_f64`][crate::binary::le_f64]: Little endian floating point numbers
//! - [`be_i8`][crate::binary::be_i8], [`be_i16`][crate::binary::be_i16], [`be_i24`][crate::binary::be_i24], [`be_i32`][crate::binary::be_i32], [`be_i64`][crate::binary::be_i64], [`be_i128`][crate::binary::be_i128]: Big endian signed integers
//! - [`be_u8`][crate::binary::be_u8], [`be_u16`][crate::binary::be_u16], [`be_u24`][crate::binary::be_u24], [`be_u32`][crate::binary::be_u32], [`be_u64`][crate::binary::be_u64], [`be_u128`][crate::binary::be_u128]: Big endian unsigned integers
//! - [`le_i8`][crate::binary::le_i8], [`le_i16`][crate::binary::le_i16], [`le_i24`][crate::binary::le_i24], [`le_i32`][crate::binary::le_i32], [`le_i64`][crate::binary::le_i64], [`le_i128`][crate::binary::le_i128]: Little endian signed integers
//! - [`le_u8`][crate::binary::le_u8], [`le_u16`][crate::binary::le_u16], [`le_u24`][crate::binary::le_u24], [`le_u32`][crate::binary::le_u32], [`le_u64`][crate::binary::le_u64], [`le_u128`][crate::binary::le_u128]: Little endian unsigned integers
//!
//! ### Bit stream parsing
//!
//! - [`bits`][crate::binary::bits::bits]: Transforms the current input type (byte slice `&[u8]`) to a bit stream on which bit specific parsers and more general combinators can be applied
//! - [`bytes`][crate::binary::bits::bytes]: Transforms its bits stream input back into a byte slice for the underlying parser
//! - [`take`][crate::binary::bits::take]: Take a set number of bits
//! - [`pattern`][crate::binary::bits::pattern]: Check if a set number of bits matches a pattern
//! - [`bool`][crate::binary::bits::bool]: Match any one bit
mod branch;
mod core;
mod debug;
mod expression;
mod multi;
mod sequence;
#[cfg(all(test, feature = "ascii", feature = "binary"))]
mod tests;
pub mod impls;
pub use self::branch::{alt, dispatch, Alt};
pub use self::core::{backtrack_err, cond, cut_err, empty, eof, fail, not, opt, peek, todo};
pub use self::debug::trace;
pub use self::expression::{expression, Expression, Infix, Postfix, Prefix};
#[cfg(feature = "alloc")]
pub use self::multi::separated_foldr1;
pub use self::multi::{
fill, iterator, repeat, repeat_till, separated, separated_foldl1, ParserIterator, Repeat,
};
pub use self::sequence::{delimited, preceded, separated_pair, seq, terminated, unordered_seq};
pub(crate) use self::debug::{trace_result, DisplayDebug};
#[allow(unused_imports)]
use crate::Parser;
File diff suppressed because it is too large. Load diff
+176
View File
@@ -0,0 +1,176 @@
use crate::combinator::trace;
use crate::error::ParserError;
use crate::stream::Stream;
use crate::Parser;
#[doc(inline)]
pub use crate::seq;
#[doc(inline)]
pub use crate::unordered_seq;
/// Sequence two parsers, only returning the output from the second.
///
/// See also [`seq`] to generalize this across any number of fields.
///
/// # Example
///
/// ```rust
/// # use winnow::{error::ErrMode, error::Needed};
/// # use winnow::prelude::*;
/// # use winnow::error::Needed::Size;
/// use winnow::combinator::preceded;
///
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
/// preceded("abc", "efg").parse_next(input)
/// }
///
/// assert_eq!(parser.parse_peek("abcefg"), Ok(("", "efg")));
/// assert_eq!(parser.parse_peek("abcefghij"), Ok(("hij", "efg")));
/// assert!(parser.parse_peek("").is_err());
/// assert!(parser.parse_peek("123").is_err());
/// ```
#[doc(alias = "ignore_then")]
pub fn preceded<Input, Ignored, Output, Error, IgnoredParser, ParseNext>(
mut ignored: IgnoredParser,
mut parser: ParseNext,
) -> impl Parser<Input, Output, Error>
where
Input: Stream,
Error: ParserError<Input>,
IgnoredParser: Parser<Input, Ignored, Error>,
ParseNext: Parser<Input, Output, Error>,
{
trace("preceded", move |input: &mut Input| {
let _ = ignored.parse_next(input)?;
parser.parse_next(input)
})
}
/// Sequence two parsers, only returning the output of the first.
///
/// See also [`seq`] to generalize this across any number of fields.
///
/// # Example
///
/// ```rust
/// # use winnow::{error::ErrMode, error::Needed};
/// # use winnow::prelude::*;
/// # use winnow::error::Needed::Size;
/// use winnow::combinator::terminated;
///
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
/// terminated("abc", "efg").parse_next(input)
/// }
///
/// assert_eq!(parser.parse_peek("abcefg"), Ok(("", "abc")));
/// assert_eq!(parser.parse_peek("abcefghij"), Ok(("hij", "abc")));
/// assert!(parser.parse_peek("").is_err());
/// assert!(parser.parse_peek("123").is_err());
/// ```
#[doc(alias = "then_ignore")]
pub fn terminated<Input, Output, Ignored, Error, ParseNext, IgnoredParser>(
mut parser: ParseNext,
mut ignored: IgnoredParser,
) -> impl Parser<Input, Output, Error>
where
Input: Stream,
Error: ParserError<Input>,
ParseNext: Parser<Input, Output, Error>,
IgnoredParser: Parser<Input, Ignored, Error>,
{
trace("terminated", move |input: &mut Input| {
let o = parser.parse_next(input)?;
ignored.parse_next(input).map(|_| o)
})
}
/// Sequence three parsers, only returning the values of the first and third.
///
/// See also [`seq`] to generalize this across any number of fields.
///
/// # Example
///
/// ```rust
/// # use winnow::{error::ErrMode, error::Needed};
/// # use winnow::error::Needed::Size;
/// # use winnow::prelude::*;
/// use winnow::combinator::separated_pair;
///
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<(&'i str, &'i str)> {
/// separated_pair("abc", "|", "efg").parse_next(input)
/// }
///
/// assert_eq!(parser.parse_peek("abc|efg"), Ok(("", ("abc", "efg"))));
/// assert_eq!(parser.parse_peek("abc|efghij"), Ok(("hij", ("abc", "efg"))));
/// assert!(parser.parse_peek("").is_err());
/// assert!(parser.parse_peek("123").is_err());
/// ```
pub fn separated_pair<Input, O1, Sep, O2, Error, P1, SepParser, P2>(
mut first: P1,
mut sep: SepParser,
mut second: P2,
) -> impl Parser<Input, (O1, O2), Error>
where
Input: Stream,
Error: ParserError<Input>,
P1: Parser<Input, O1, Error>,
SepParser: Parser<Input, Sep, Error>,
P2: Parser<Input, O2, Error>,
{
trace("separated_pair", move |input: &mut Input| {
let o1 = first.parse_next(input)?;
let _ = sep.parse_next(input)?;
second.parse_next(input).map(|o2| (o1, o2))
})
}
/// Sequence three parsers, only returning the output of the second.
///
/// See also [`seq`] to generalize this across any number of fields.
///
/// # Example
///
/// ```rust
/// # use winnow::{error::ErrMode, error::Needed};
/// # use winnow::error::Needed::Size;
/// # use winnow::prelude::*;
/// use winnow::combinator::delimited;
///
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
/// delimited("(", "abc", ")").parse_next(input)
/// }
///
/// assert_eq!(parser.parse_peek("(abc)"), Ok(("", "abc")));
/// assert_eq!(parser.parse_peek("(abc)def"), Ok(("def", "abc")));
/// assert!(parser.parse_peek("").is_err());
/// assert!(parser.parse_peek("123").is_err());
/// ```
#[doc(alias = "between")]
#[doc(alias = "padded")]
pub fn delimited<
Input,
Ignored1,
Output,
Ignored2,
Error,
IgnoredParser1,
ParseNext,
IgnoredParser2,
>(
mut ignored1: IgnoredParser1,
mut parser: ParseNext,
mut ignored2: IgnoredParser2,
) -> impl Parser<Input, Output, Error>
where
Input: Stream,
Error: ParserError<Input>,
IgnoredParser1: Parser<Input, Ignored1, Error>,
ParseNext: Parser<Input, Output, Error>,
IgnoredParser2: Parser<Input, Ignored2, Error>,
{
trace("delimited", move |input: &mut Input| {
let _ = ignored1.parse_next(input)?;
let o2 = parser.parse_next(input)?;
ignored2.parse_next(input).map(|_| o2)
})
}
File diff suppressed because it is too large. Load diff
+1527
View File
File diff suppressed because it is too large. Load diff
+189
View File
@@ -0,0 +1,189 @@
//! > winnow, making parsing a breeze
//!
//! `winnow` is a parser combinator library
//!
//! Quick links:
//! - [List of combinators][crate::combinator]
//! - [Tutorial][_tutorial::chapter_0]
//! - [Special Topics][_topic]
//! - [Discussions](https://github.com/winnow-rs/winnow/discussions)
//! - [CHANGELOG](https://github.com/winnow-rs/winnow/blob/v1.0.0/CHANGELOG.md) (includes major version migration
//! guides)
//!
//! ## Aspirations
//!
//! `winnow` aims to be your "do everything" parser, much like people treat regular expressions.
//!
//! In roughly priority order:
//! 1. Support writing parser declaratively while not getting in the way of imperative-style
//! parsing when needed, working as an open-ended toolbox rather than a close-ended framework.
//! 2. Flexible enough to be used for any application, including parsing strings, binary data,
//! or separate [lexing and parsing phases][_topic::lexing]
//! 3. Zero-cost abstractions, making it easy to write high performance parsers
//! 4. Easy to use, making it trivial for one-off uses
//!
//! In addition:
//! - Resilient maintainership, including
//! - Willing to break compatibility rather than batching up breaking changes in large releases
//! - Leverage feature flags to keep one active branch
//! - We will support the last 6 months of rust releases (MSRV)
//!
//! See also [Special Topic: Why winnow?][crate::_topic::why]
//!
//! ## Example
//!
//! Run
//! ```console
//! $ cargo add winnow
//! ```
//!
//! Then use it to parse:
//! ```rust
//! # #[cfg(all(feature = "alloc", feature = "parser"))] {
#![doc = include_str!("../examples/css/parser.rs")]
//! # }
//! ```
//!
//! See also the [Tutorial][_tutorial::chapter_0] and [Special Topics][_topic]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(extended_key_value_attributes))]
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#![warn(missing_docs)]
#![warn(clippy::std_instead_of_core)]
#![warn(clippy::std_instead_of_alloc)]
#![warn(clippy::print_stderr)]
#![warn(clippy::print_stdout)]
#[cfg(feature = "alloc")]
#[cfg_attr(test, macro_use)]
#[allow(unused_extern_crates)]
extern crate alloc;
#[doc = include_str!("../README.md")]
#[cfg(doctest)]
pub struct ReadmeDoctests;
pub(crate) mod util {
#[allow(dead_code)]
pub(crate) fn from_fn<F: Fn(&mut core::fmt::Formatter<'_>) -> core::fmt::Result>(
f: F,
) -> FromFn<F> {
FromFn(f)
}
pub(crate) struct FromFn<F>(F)
where
F: Fn(&mut core::fmt::Formatter<'_>) -> core::fmt::Result;
impl<F> core::fmt::Debug for FromFn<F>
where
F: Fn(&mut core::fmt::Formatter<'_>) -> core::fmt::Result,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
(self.0)(f)
}
}
impl<F> core::fmt::Display for FromFn<F>
where
F: Fn(&mut core::fmt::Formatter<'_>) -> core::fmt::Result,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
(self.0)(f)
}
}
}
#[macro_use]
mod macros;
#[macro_use]
#[cfg(feature = "parser")]
pub mod error;
#[cfg(feature = "parser")]
mod parser;
pub mod stream;
#[cfg(feature = "ascii")]
pub mod ascii;
#[cfg(feature = "binary")]
pub mod binary;
#[cfg(feature = "parser")]
pub mod combinator;
#[cfg(feature = "parser")]
pub mod token;
#[cfg(feature = "unstable-doc")]
pub mod _topic;
#[cfg(feature = "unstable-doc")]
pub mod _tutorial;
/// Core concepts available for glob import
///
/// Including
/// - [`StreamIsPartial`][crate::stream::StreamIsPartial]
/// - [`Parser`]
///
/// ## Example
///
/// ```rust
/// # #[cfg(feature = "ascii")] {
/// use winnow::prelude::*;
///
/// fn parse_data(input: &mut &str) -> ModalResult<u64> {
/// // ...
/// # winnow::ascii::dec_uint(input)
/// }
///
/// fn main() {
/// let result = parse_data.parse("100");
/// assert_eq!(result, Ok(100));
/// }
/// # }
/// ```
pub mod prelude {
#[cfg(feature = "parser")]
pub use crate::error::ModalError as _;
#[cfg(feature = "parser")]
pub use crate::error::ParserError as _;
pub use crate::stream::AsChar as _;
pub use crate::stream::ContainsToken as _;
pub use crate::stream::Stream as _;
pub use crate::stream::StreamIsPartial as _;
#[cfg(feature = "parser")]
pub use crate::ModalParser;
#[cfg(feature = "parser")]
pub use crate::ModalResult;
#[cfg(feature = "parser")]
pub use crate::Parser;
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
#[cfg(feature = "parser")]
pub use crate::RecoverableParser as _;
#[cfg(all(test, feature = "parser"))]
pub(crate) use crate::TestResult;
}
#[cfg(feature = "parser")]
pub use error::ModalResult;
#[cfg(feature = "parser")]
pub use error::Result;
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
#[cfg(feature = "parser")]
pub use parser::RecoverableParser;
#[cfg(feature = "parser")]
pub use parser::{ModalParser, Parser};
pub use stream::BStr;
pub use stream::Bytes;
pub use stream::LocatingSlice;
pub use stream::Partial;
pub use stream::Stateful;
pub use stream::Str;
#[cfg(all(test, feature = "parser"))]
pub(crate) use error::TestResult;
+96
View File
@@ -0,0 +1,96 @@
/// `match` for parsers
///
/// While `match` works by accepting a value and returning values:
/// ```rust,ignore
/// let result_value = match scrutinee_value {
/// ArmPattern => arm_value,
/// };
/// ```
/// `dispatch!` composes parsers:
/// ```rust,ignore
/// let result_parser = dispatch!{scrutinee_parser;
/// ArmPattern => arm_parser,
/// };
/// ```
///
/// This is useful when parsers have unique prefixes to test for.
/// This offers better performance over
/// [`alt`][crate::combinator::alt] though it might be at the cost of duplicating parts of your grammar
/// if you needed to [`peek(input_parser)`][crate::combinator::peek] the scrutinee.
///
/// For tight control over the error in a catch-all case, use [`fail`][crate::combinator::fail].
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "parser")] {
/// use winnow::prelude::*;
/// use winnow::combinator::dispatch;
/// # use winnow::token::take;
/// # use winnow::token::take_while;
/// # use winnow::combinator::fail;
///
/// fn integer(input: &mut &str) -> ModalResult<u64> {
/// dispatch! {take(2usize);
/// "0b" => take_while(1.., '0'..='1').try_map(|s| u64::from_str_radix(s, 2)),
/// "0o" => take_while(1.., '0'..='7').try_map(|s| u64::from_str_radix(s, 8)),
/// "0d" => take_while(1.., '0'..='9').try_map(|s| u64::from_str_radix(s, 10)),
/// "0x" => take_while(1.., ('0'..='9', 'a'..='f', 'A'..='F')).try_map(|s| u64::from_str_radix(s, 16)),
/// _ => fail::<_, u64, _>,
/// }
/// .parse_next(input)
/// }
///
/// assert_eq!(integer.parse_peek("0x100 Hello"), Ok((" Hello", 0x100)));
/// # }
/// ```
///
/// ```rust
/// # #[cfg(feature = "parser")] {
/// use winnow::prelude::*;
/// use winnow::combinator::dispatch;
/// # use winnow::token::any;
/// # use winnow::combinator::preceded;
/// # use winnow::combinator::empty;
/// # use winnow::combinator::fail;
///
/// fn escaped(input: &mut &str) -> ModalResult<char> {
/// preceded('\\', escape_seq_char).parse_next(input)
/// }
///
/// fn escape_seq_char(input: &mut &str) -> ModalResult<char> {
/// dispatch! {any;
/// 'b' => empty.value('\u{8}'),
/// 'f' => empty.value('\u{c}'),
/// 'n' => empty.value('\n'),
/// 'r' => empty.value('\r'),
/// 't' => empty.value('\t'),
/// '\\' => empty.value('\\'),
/// '"' => empty.value('"'),
/// _ => fail::<_, char, _>,
/// }
/// .parse_next(input)
/// }
///
/// assert_eq!(escaped.parse_peek("\\nHello"), Ok(("Hello", '\n')));
/// # }
/// ```
#[macro_export]
#[doc(hidden)] // forced to be visible in intended location
macro_rules! dispatch {
(
$scrutinee_parser:expr;
$( $arm_pat:pat $(if $arm_pred:expr)? => $arm_parser: expr ),+ $(,)?
) => {
$crate::combinator::trace("dispatch", move |i: &mut _|
{
use $crate::Parser;
let initial = $scrutinee_parser.parse_next(i)?;
match initial {
$(
$arm_pat $(if $arm_pred)? => $arm_parser.parse_next(i),
)*
}
})
}
}
+58
View File
@@ -0,0 +1,58 @@
mod dispatch;
mod seq;
mod unordered_seq;
#[cfg(test)]
macro_rules! assert_parse(
($left: expr, $right: expr) => {
let res: $crate::error::ModalResult<_, $crate::error::InputError<_>> = $left;
snapbox::assert_data_eq!(snapbox::data::ToDebug::to_debug(&res), $right);
};
);
macro_rules! impl_partial_eq {
($lhs:ty, $rhs:ty) => {
#[allow(unused_lifetimes)]
impl<'a> PartialEq<$rhs> for $lhs {
#[inline]
fn eq(&self, other: &$rhs) -> bool {
let l = self;
let r: &Self = other.as_ref();
PartialEq::eq(l, r)
}
}
#[allow(unused_lifetimes)]
impl<'a> PartialEq<$lhs> for $rhs {
#[inline]
fn eq(&self, other: &$lhs) -> bool {
PartialEq::eq(other, self)
}
}
};
}
macro_rules! impl_partial_ord {
($lhs:ty, $rhs:ty) => {
#[allow(unused_lifetimes)]
impl<'a> PartialOrd<$rhs> for $lhs {
#[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
let l = self;
let r: &Self = other.as_ref();
PartialOrd::partial_cmp(l, r)
}
}
#[allow(unused_lifetimes)]
impl<'a> PartialOrd<$lhs> for $rhs {
#[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
PartialOrd::partial_cmp(other, self)
}
}
};
}
#[cfg(all(test, feature = "ascii"))]
mod tests;
+336
View File
@@ -0,0 +1,336 @@
/// Initialize a struct or tuple out of a sequences of parsers
///
/// Unlike normal struct initialization syntax:
/// - `_` fields can exist to run a parser but ignore the result
/// - Parse results for a field can later be referenced using the field name
///
/// Unlike normal tuple initialization syntax:
/// - Struct-style initialization (`{ 0: _, 1: _}`) is not supported
/// - `_: <parser>` fields can exist to run a parser but ignore the result
///
///# Example
///
/// ```
/// # #[cfg(feature = "ascii")] {
/// # use winnow::prelude::*;
/// # use winnow::ascii::{alphanumeric1, dec_uint, space0};
/// # use winnow::combinator::delimited;
/// # use winnow::combinator::empty;
/// # use winnow::error::ContextError;
/// # use winnow::error::ErrMode;
/// use winnow::combinator::seq;
///
/// #[derive(Default, Debug, PartialEq)]
/// struct Field {
/// namespace: u32,
/// name: Vec<u8>,
/// value: Vec<u8>,
/// point: (u32, u32),
/// metadata: Vec<u8>,
/// }
///
/// // Parse into structs / tuple-structs
/// fn field(input: &mut &[u8]) -> ModalResult<Field> {
/// seq!{Field {
/// namespace: empty.value(5),
/// name: alphanumeric1.map(|s: &[u8]| s.to_owned()),
/// // `_` fields are ignored when building the struct
/// _: (space0, b':', space0),
/// value: alphanumeric1.map(|s: &[u8]| s.to_owned()),
/// _: (space0, b':', space0),
/// point: point,
/// // default initialization also works
/// ..Default::default()
/// }}.parse_next(input)
/// }
///
/// // Or parse into tuples
/// fn point(input: &mut &[u8]) -> ModalResult<(u32, u32)> {
/// let mut num = dec_uint::<_, u32, ErrMode<ContextError>>;
/// seq!(num, _: (space0, b',', space0), num).parse_next(input)
/// }
///
/// assert_eq!(
/// field.parse_peek(&b"test: data: 123 , 4"[..]),
/// Ok((
/// &b""[..],
/// Field {
/// namespace: 5,
/// name: b"test"[..].to_owned(),
/// value: b"data"[..].to_owned(),
/// point: (123, 4),
/// metadata: Default::default(),
/// },
/// )),
/// );
/// # }
/// ```
#[macro_export]
#[doc(alias = "tuple")]
#[doc(alias = "preceded")]
#[doc(alias = "terminated")]
#[doc(alias = "delimited")]
#[doc(alias = "pair")]
#[doc(alias = "separated_pair")]
#[doc(alias = "struct_parser")]
#[doc(hidden)] // forced to be visible in intended location
macro_rules! seq {
($($name: ident)::* { $($fields: tt)* }) => {
$crate::combinator::trace(stringify!($($name)::*), move |input: &mut _| {
$crate::seq_parse_struct_fields!(
( $($fields)* );
( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20 );
input ;
);
Ok($crate::seq_init_struct_fields!(
( $($fields)* );
$($name)::* ;
))
})
};
($($name: ident)::* ( $($fields: tt)* )) => {
$crate::combinator::trace(stringify!($($name)::*), move |input: &mut _| {
$crate::seq_parse_tuple_fields!(
( $($fields)* );
( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20 );
input;
);
Ok($crate::seq_init_tuple_fields!(
( $($fields)* );
( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20 );
$($name)::*;
))
})
};
(( $($fields: tt)* )) => {
$crate::combinator::trace("tuple", move |input: &mut _| {
$crate::seq_parse_tuple_fields!(
( $($fields)* );
( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20 );
input;
);
Ok($crate::seq_init_tuple_fields!(
( $($fields)* );
( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20 );
;
))
})
};
($($fields: tt)*) => {
$crate::seq!((
$($fields)*
))
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! seq_parse_struct_fields {
(
( _ : $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
$input: ident ;
) => {
let $unnamed1 = $crate::Parser::parse_next(&mut $head_parser, $input)?;
$crate::seq_parse_struct_fields!(
( $($fields)* );
( $($unnamed),* );
$input ;
)
};
(
( _ : $head_parser: expr );
( $unnamed1: ident, $($unnamed: ident),* );
$input: ident ;
) => {
let $unnamed1 = $crate::Parser::parse_next(&mut $head_parser, $input)?;
};
(
( $head_field: ident : $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
$input: ident ;
) => {
let $head_field = $crate::Parser::parse_next(&mut $head_parser, $input)?;
$crate::seq_parse_struct_fields!(
( $($fields)* );
( $($unnamed),* );
$input ;
)
};
(
( $head_field: ident : $head_parser: expr );
( $unnamed1: ident, $($unnamed: ident),* );
$input: ident ;
) => {
let $head_field = $crate::Parser::parse_next(&mut $head_parser, $input)?;
};
(
( .. $update: expr );
( $($unnamed: ident),* );
$input: expr ;
) => {};
(
( $(,)? );
( $($unnamed: ident),* );
$input: expr ;
) => {};
}
#[macro_export]
#[doc(hidden)]
macro_rules! seq_parse_tuple_fields {
(
( $(_ :)? $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
$input: ident;
) => {
let $unnamed1 = $crate::Parser::parse_next(&mut $head_parser, $input)?;
$crate::seq_parse_tuple_fields!(
( $($fields)* );
( $($unnamed),* );
$input ;
)
};
(
( $(_ :)? $head_parser: expr );
( $unnamed1: ident, $($unnamed: ident),* );
$input: ident;
) => {
let $unnamed1 = $crate::Parser::parse_next(&mut $head_parser, $input)?;
};
(
( $(,)? );
( $($unnamed: ident),* );
$input: expr;
) => {};
}
#[macro_export]
#[doc(hidden)]
macro_rules! seq_init_struct_fields {
(
( _ : $head_parser: expr, $($fields: tt)* );
$($name: ident)::* ;
$($inits: tt)*
) => {
$crate::seq_init_struct_fields!(
( $($fields)* );
$($name)::* ;
$($inits)*
)
};
(
( _ : $head_parser: expr );
$($name: ident)::* ;
$($inits: tt)*
) => {
$crate::seq_init_struct_fields!(
();
$($name)::* ;
$($inits)*
)
};
(
( $head_field: ident : $head_parser: expr, $($fields: tt)* );
$($name: ident)::* ;
$($inits: tt)*
) =>
{
$crate::seq_init_struct_fields!(
( $($fields)* );
$($name)::* ;
$($inits)* $head_field,
)
};
(
( $head_field: ident : $head_parser: expr );
$($name: ident)::* ;
$($inits: tt)*
) => {
$crate::seq_init_struct_fields!(
();
$($name)::* ;
$($inits)* $head_field,
)
};
(
( .. $update: expr );
$($name: ident)::* ;
$($inits: tt)*
) => {
$($name)::* { $($inits)* ..$update }
};
(
( $(,)? );
$($name: ident)::* ;
$($inits: tt)*
) => {
$($name)::* { $($inits)* }
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! seq_init_tuple_fields {
(
( _ : $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
$($name: ident)::*;
$($inits: tt)*
) => {
$crate::seq_init_tuple_fields!(
( $($fields)* );
( $($unnamed),* );
$($name)::* ;
$($inits)*
)
};
(
( _ : $head_parser: expr );
( $unnamed1: ident, $($unnamed: ident),* );
$($name: ident)::*;
$($inits: tt)*
) => {
$crate::seq_init_tuple_fields!(
();
( $($unnamed),* );
$($name)::* ;
$($inits)*
)
};
(
( $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
$($name: ident)::*;
$($inits: tt)*
) =>
{
$crate::seq_init_tuple_fields!(
( $($fields)* );
( $($unnamed),* );
$($name)::* ;
$($inits)* $unnamed1,
)
};
(
( $head_parser: expr );
( $unnamed1: ident, $($unnamed: ident),* );
$($name: ident)::*;
$($inits: tt)*
) => {
$crate::seq_init_tuple_fields!(
();
( $($unnamed),* );
$($name)::* ;
$($inits)* $unnamed1,
)
};
(
( $(,)? );
( $unnamed1: ident, $($unnamed: ident),* );
$($name: ident)::*;
$($inits: tt)*
) => {
$($name)::* ( $($inits)* )
};
}
+1330
View File
File diff suppressed because it is too large. Load diff
+594
View File
@@ -0,0 +1,594 @@
/// Initialize a struct or tuple out of an unordered sequences of parsers
///
/// Unlike normal struct initialization syntax:
/// - `_` fields can exist to run a parser but ignore the result
/// - Parse results for a field can later be referenced using the field name
///
/// Unlike normal tuple initialization syntax:
/// - Struct-style initialization (`{ 0: _, 1: _}`) is not supported
/// - `_: <parser>` fields can exist to run a parser but ignore the result
///
/// To stop on an error, rather than trying further permutations, see
/// [`cut_err`][crate::combinator::cut_err] ([example][crate::_tutorial::chapter_7]).
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "ascii")] {
/// # use winnow::{error::ErrMode, error::Needed};
/// # use winnow::prelude::*;
/// use winnow::ascii::{alpha1, digit1};
/// use winnow::combinator::unordered_seq;
/// fn parser<'i>(input: &mut &'i str) -> ModalResult<(&'i str, &'i str)> {
/// unordered_seq!((alpha1, digit1)).parse_next(input)
/// }
///
/// // permutation takes alphabetic characters then digit
/// assert_eq!(parser.parse_peek("abc123"), Ok(("", ("abc", "123"))));
///
/// // but also in inverse order
/// assert_eq!(parser.parse_peek("123abc"), Ok(("", ("abc", "123"))));
///
/// // it will fail if one of the parsers failed
/// assert!(parser.parse_peek("abc;").is_err());
/// # }
/// ```
///
/// The parsers are applied greedily: if there are multiple unapplied parsers
/// that could parse the next slice of input, the first one is used.
/// ```rust
/// # #[cfg(feature = "parser")] {
/// # use winnow::error::ErrMode;
/// # use winnow::prelude::*;
/// use winnow::combinator::unordered_seq;
/// use winnow::token::any;
///
/// fn parser(input: &mut &str) -> ModalResult<(char, char)> {
/// unordered_seq!((any, 'a')).parse_next(input)
/// }
///
/// // any parses 'b', then char('a') parses 'a'
/// assert_eq!(parser.parse_peek("ba"), Ok(("", ('b', 'a'))));
///
/// // any parses 'a', then char('a') fails on 'b',
/// // even though char('a') followed by any would succeed
/// assert!(parser.parse_peek("ab").is_err());
/// # }
/// ```
#[macro_export]
#[doc(alias = "permutation")]
#[doc(hidden)] // forced to be visible in intended location
macro_rules! unordered_seq {
($($name: ident)::* { $($fields: tt)* }) => {
$crate::combinator::trace(stringify!($($name)::*), move |input: &mut _| {
$crate::unordered_seq_parse_struct_body!(
( $($name)::* );
( $($fields)* );
( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20 );
( input );
)
})
};
(( $($fields: tt)* )) => {
$crate::combinator::trace("tuple", move |input: &mut _| {
$crate::unordered_seq_parse_tuple_body!(
( );
( $($fields)* );
( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20 );
( input );
)
})
};
($($name: ident)::* ( $($fields: tt)* )) => {
$crate::combinator::trace(stringify!($($name)::*), move |input: &mut _| {
$crate::unordered_seq_parse_tuple_body!(
( $($name)::* );
( $($fields)* );
( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20 );
( input );
)
})
};
($($fields: tt)*) => {
$crate::unordered_seq!((
$($fields)*
))
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! unordered_seq_parse_struct_body {
(
( $($name: ident)::* );
( $($fields: tt)* );
( $($unnamed: ident),* );
( $input: expr );
) => {{
// Workaround a type inference issue by hinting to the compiler `I` within `ParserError` calls
fn recover_err<I, E>(_input: &I, curr: E, acc: &mut Option<E>) -> Option<E>
where
I: $crate::stream::Stream,
E: $crate::error::ParserError<I>,
{
if $crate::error::ParserError::is_backtrack(&curr) {
*acc = Some(match acc.take() {
Some(err) => $crate::error::ParserError::or(err, curr),
None => curr,
});
None
} else {
Some(curr)
}
}
$crate::unordered_seq_parse_struct_init!(
( $($fields)* );
( $($unnamed),* );
);
loop {
let mut err = ::core::option::Option::None;
let start = $crate::stream::Stream::checkpoint($input);
$crate::unordered_seq_parse_struct_each!(
( $($fields)* );
( $($unnamed),* );
( $input );
( start );
( err );
);
// If we reach here, every iterator has either been applied before,
// or errored on the remaining input
if let ::core::option::Option::Some(err) = err {
// There are remaining parsers, and all errored on the remaining input
break Err($crate::error::ParserError::append(err, $input, &start));
}
// All parsers were applied
break Ok(
$crate::unordered_seq_parse_struct_collect!(
( $($fields)* );
( $($unnamed),* );
( $($name)::* );
)
);
}
}}
}
#[macro_export]
#[doc(hidden)]
macro_rules! unordered_seq_parse_struct_init {
(
( _ : $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
) => {
let mut $unnamed1 = ::core::option::Option::None;
$crate::unordered_seq_parse_struct_init!(
( $($fields)* );
( $($unnamed),* );
);
};
(
( $named: ident : $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
) => {
let mut $named = ::core::option::Option::None;
$crate::unordered_seq_parse_struct_init!(
( $($fields)* );
( $($unnamed),* );
);
};
(
( _ : $head_parser: expr );
( $unnamed1: ident, $($unnamed: ident),* );
) => {
let mut $unnamed1 = ::core::option::Option::None;
};
(
( $named: ident : $head_parser: expr );
( $unnamed1: ident, $($unnamed: ident),* );
) => {
let mut $named = ::core::option::Option::None;
};
(
( .. $update: expr $(,)? );
( $($unnamed: ident),* );
) => {
};
(
( $(,)? );
( $($unnamed: ident),* );
) => {
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! unordered_seq_parse_struct_collect {
(
( $(,)? );
( $($unnamed: ident),* );
( $($name: ident)::* );
$($inits: tt)*
) => {
$($name)::* { $($inits)* }
};
(
( .. $update: expr $(,)? );
( $unnamed1: ident, $($unnamed: ident),* );
( $($name: ident)::* );
$($inits: tt)*
) => {
$crate::unordered_seq_parse_struct_collect!(
( , );
( $($unnamed),* );
( $($name)::* );
$($inits)* .. $update
)
};
(
( _ : $head_parser: expr );
( $unnamed1: ident, $($unnamed: ident),* );
( $($name: ident)::* );
$($inits: tt)*
) => {
$crate::unordered_seq_parse_struct_collect!(
( , );
( $($unnamed),* );
( $($name)::* );
$($inits)*
)
};
(
( _ : $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
( $($name: ident)::* );
$($inits: tt)*
) => {
$crate::unordered_seq_parse_struct_collect!(
( $($fields)* );
( $($unnamed),* );
( $($name)::* );
$($inits)*
)
};
(
( $named: ident : $head_parser: expr );
( $unnamed1: ident, $($unnamed: ident),* );
( $($name: ident)::* );
$($inits: tt)*
) => {
$crate::unordered_seq_parse_struct_collect!(
( , );
( $($unnamed),* );
( $($name)::* );
$($inits)* $named: $named.unwrap(),
)
};
(
( $named: ident : $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
( $($name: ident)::* );
$($inits: tt)*
) => {
$crate::unordered_seq_parse_struct_collect!(
( $($fields)* );
( $($unnamed),* );
( $($name)::* );
$($inits)* $named: $named.unwrap(),
)
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! unordered_seq_parse_struct_each(
(
( _ : $head_parser: expr $(,)? );
( $unnamed1: ident, $($unnamed: ident),* );
( $input: expr );
( $start: expr );
( $err: expr );
) => (
$crate::unordered_seq_parse_next!(
( $head_parser );
( $unnamed1 );
( $input );
( $start );
( $err );
);
);
(
( $named: ident : $head_parser: expr $(,)? );
( $unnamed1: ident, $($unnamed: ident),* );
( $input: expr );
( $start: expr );
( $err: expr );
) => (
$crate::unordered_seq_parse_next!(
( $head_parser );
( $named );
( $input );
( $start );
( $err );
);
);
(
( _ : $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
( $input: expr );
( $start: expr );
( $err: expr );
) => (
$crate::unordered_seq_parse_next!(
( $head_parser );
( $unnamed1 );
( $input );
( $start );
( $err );
);
$crate::unordered_seq_parse_struct_each!(
( $($fields)* );
( $($unnamed),* );
( $input );
( $start );
( $err );
);
);
(
( $named: ident : $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
( $input: expr );
( $start: expr );
( $err: expr );
) => (
$crate::unordered_seq_parse_next!(
( $head_parser );
( $named );
( $input );
( $start );
( $err );
);
$crate::unordered_seq_parse_struct_each!(
( $($fields)* );
( $($unnamed),* );
( $input );
( $start );
( $err );
);
);
(
( .. $update: expr $(,)? );
( $unnamed1: ident, $($unnamed: ident),* );
( $input: expr );
( $start: expr );
( $err: expr );
) => (
);
);
#[macro_export]
#[doc(hidden)]
macro_rules! unordered_seq_parse_tuple_body {
(
( $($name: ident)::* );
( $($fields: tt)* );
( $($unnamed: ident),* );
( $input: expr );
) => {{
// Workaround a type inference issue by hinting to the compiler `I` within `ParserError` calls
fn recover_err<I, E>(_input: &I, curr: E, acc: &mut Option<E>) -> Option<E>
where
I: $crate::stream::Stream,
E: $crate::error::ParserError<I>,
{
if $crate::error::ParserError::is_backtrack(&curr) {
*acc = Some(match acc.take() {
Some(err) => $crate::error::ParserError::or(err, curr),
None => curr,
});
None
} else {
Some(curr)
}
}
$crate::unordered_seq_parse_tuple_init!(
( $($fields)* );
( $($unnamed),* );
);
loop {
let mut err = ::core::option::Option::None;
let start = $crate::stream::Stream::checkpoint($input);
$crate::unordered_seq_parse_tuple_each!(
( $($fields)* );
( $($unnamed),* );
( $input );
( start );
( err );
);
// If we reach here, every iterator has either been applied before,
// or errored on the remaining input
if let ::core::option::Option::Some(err) = err {
// There are remaining parsers, and all errored on the remaining input
break Err($crate::error::ParserError::append(err, $input, &start));
}
// All parsers were applied
break Ok(
$crate::unordered_seq_parse_tuple_collect!(
( $($fields)* );
( $($unnamed),* );
( $($name)::* );
)
);
}
}}
}
#[macro_export]
#[doc(hidden)]
macro_rules! unordered_seq_parse_tuple_init {
(
( $(_ :)? $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
) => {
let mut $unnamed1 = ::core::option::Option::None;
$crate::unordered_seq_parse_tuple_init!(
( $($fields)* );
( $($unnamed),* );
);
};
(
( $(_ :)? $head_parser: expr );
( $unnamed1: ident, $($unnamed: ident),* );
) => {
let mut $unnamed1 = ::core::option::Option::None;
};
(
( $(,)? );
( $($unnamed: ident),* );
) => {
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! unordered_seq_parse_tuple_collect {
(
( $(,)? );
( $($unnamed: ident),* );
( $($name: ident)::* );
$($inits: tt)*
) => {
$($name)::* ( $($inits)* )
};
(
( _ : $head_parser: expr );
( $unnamed1: ident, $($unnamed: ident),* );
( $($name: ident)::* );
$($inits: tt)*
) => {
$crate::unordered_seq_parse_tuple_collect!(
( , );
( $($unnamed),* );
( $($name)::* );
$($inits)*
)
};
(
( _ : $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
( $($name: ident)::* );
$($inits: tt)*
) => {
$crate::unordered_seq_parse_tuple_collect!(
( $($fields)* );
( $($unnamed),* );
( $($name)::* );
$($inits)*
)
};
(
( $head_parser: expr );
( $unnamed1: ident, $($unnamed: ident),* );
( $($name: ident)::* );
$($inits: tt)*
) => {
$crate::unordered_seq_parse_tuple_collect!(
( , );
( $($unnamed),* );
( $($name)::* );
$($inits)* $unnamed1.unwrap(),
)
};
(
( $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
( $($name: ident)::* );
$($inits: tt)*
) => {
$crate::unordered_seq_parse_tuple_collect!(
( $($fields)* );
( $($unnamed),* );
( $($name)::* );
$($inits)* $unnamed1.unwrap(),
)
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! unordered_seq_parse_tuple_each(
(
( $(_ :)? $head_parser: expr $(,)? );
( $unnamed1: ident, $($unnamed: ident),* );
( $input: expr );
( $start: expr );
( $err: expr );
) => (
$crate::unordered_seq_parse_next!(
( $head_parser );
( $unnamed1 );
( $input );
( $start );
( $err );
);
);
(
( $(_ :)? $head_parser: expr, $($fields: tt)* );
( $unnamed1: ident, $($unnamed: ident),* );
( $input: expr );
( $start: expr );
( $err: expr );
) => (
$crate::unordered_seq_parse_next!(
( $head_parser );
( $unnamed1 );
( $input );
( $start );
( $err );
);
$crate::unordered_seq_parse_tuple_each!(
( $($fields)* );
( $($unnamed),* );
( $input );
( $start );
( $err );
);
);
);
#[macro_export]
#[doc(hidden)]
macro_rules! unordered_seq_parse_next(
(
( $field: tt );
( $unnamed: ident );
( $input: expr );
( $start: expr );
( $err: expr );
) => (
if $unnamed.is_none() {
$crate::stream::Stream::reset($input, &$start);
match $crate::Parser::parse_next(&mut $field, $input) {
Ok(o) => {
$unnamed = Some(o);
continue;
}
Err(e) => {
if let Some(cut) = recover_err($input, e, &mut $err) {
return Err(cut);
}
}
};
}
);
);
+1487
View File
File diff suppressed because it is too large. Load diff
+540
View File
@@ -0,0 +1,540 @@
use core::num::NonZeroUsize;
use crate::stream::AsBStr;
use crate::stream::Checkpoint;
use crate::stream::Compare;
use crate::stream::CompareResult;
use crate::stream::FindSlice;
use crate::stream::Needed;
use crate::stream::Offset;
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
use crate::stream::Recover;
use crate::stream::SliceLen;
use crate::stream::Stream;
use crate::stream::StreamIsPartial;
use crate::stream::UpdateSlice;
use core::iter::{Cloned, Enumerate};
use core::slice::Iter;
use core::{cmp::Ordering, fmt, ops};
/// Improved `Debug` experience for `&[u8]` UTF-8-ish streams
#[allow(clippy::derived_hash_with_manual_eq)]
#[derive(Hash)]
#[repr(transparent)]
pub struct BStr([u8]);
impl BStr {
/// Make a stream out of a byte slice-like.
#[inline]
pub fn new<B: ?Sized + AsRef<[u8]>>(bytes: &B) -> &Self {
Self::from_bytes(bytes.as_ref())
}
#[inline]
fn from_bytes(slice: &[u8]) -> &Self {
unsafe { core::mem::transmute(slice) }
}
#[inline]
fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl SliceLen for &BStr {
#[inline(always)]
fn slice_len(&self) -> usize {
self.len()
}
}
impl<'i> Stream for &'i BStr {
type Token = u8;
type Slice = &'i [u8];
type IterOffsets = Enumerate<Cloned<Iter<'i, u8>>>;
type Checkpoint = Checkpoint<Self, Self>;
#[inline(always)]
fn iter_offsets(&self) -> Self::IterOffsets {
self.iter().cloned().enumerate()
}
#[inline(always)]
fn eof_offset(&self) -> usize {
self.len()
}
#[inline(always)]
fn next_token(&mut self) -> Option<Self::Token> {
if self.is_empty() {
None
} else {
let token = self[0];
*self = &self[1..];
Some(token)
}
}
#[inline(always)]
fn peek_token(&self) -> Option<Self::Token> {
if self.is_empty() {
None
} else {
Some(self[0])
}
}
#[inline(always)]
fn offset_for<P>(&self, predicate: P) -> Option<usize>
where
P: Fn(Self::Token) -> bool,
{
self.iter().position(|b| predicate(*b))
}
#[inline(always)]
fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
if let Some(needed) = tokens.checked_sub(self.len()).and_then(NonZeroUsize::new) {
Err(Needed::Size(needed))
} else {
Ok(tokens)
}
}
#[inline(always)]
fn next_slice(&mut self, offset: usize) -> Self::Slice {
let (slice, next) = self.0.split_at(offset);
*self = BStr::from_bytes(next);
slice
}
#[inline(always)]
unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice {
#[cfg(debug_assertions)]
self.peek_slice(offset);
// SAFETY: `Stream::next_slice_unchecked` requires `offset` to be in bounds
let slice = unsafe { self.0.get_unchecked(..offset) };
// SAFETY: `Stream::next_slice_unchecked` requires `offset` to be in bounds
let next = unsafe { self.0.get_unchecked(offset..) };
*self = BStr::from_bytes(next);
slice
}
#[inline(always)]
fn peek_slice(&self, offset: usize) -> Self::Slice {
&self[..offset]
}
#[inline(always)]
unsafe fn peek_slice_unchecked(&self, offset: usize) -> Self::Slice {
#[cfg(debug_assertions)]
self.peek_slice(offset);
// SAFETY: `Stream::next_slice_unchecked` requires `offset` to be in bounds
let slice = unsafe { self.0.get_unchecked(..offset) };
slice
}
#[inline(always)]
fn checkpoint(&self) -> Self::Checkpoint {
Checkpoint::<_, Self>::new(*self)
}
#[inline(always)]
fn reset(&mut self, checkpoint: &Self::Checkpoint) {
*self = checkpoint.inner;
}
fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{self:#?}")
}
}
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
impl<E> Recover<E> for &BStr {
#[inline(always)]
fn record_err(
&mut self,
_token_start: &Self::Checkpoint,
_err_start: &Self::Checkpoint,
err: E,
) -> Result<(), E> {
Err(err)
}
/// Report whether the [`Stream`] can save off errors for recovery
#[inline(always)]
fn is_recovery_supported() -> bool {
false
}
}
impl StreamIsPartial for &BStr {
type PartialState = ();
#[inline]
fn complete(&mut self) -> Self::PartialState {
// Already complete
}
#[inline]
fn restore_partial(&mut self, _state: Self::PartialState) {}
#[inline(always)]
fn is_partial_supported() -> bool {
false
}
}
impl Offset for &BStr {
#[inline(always)]
fn offset_from(&self, start: &Self) -> usize {
self.as_bytes().offset_from(&start.as_bytes())
}
}
impl<'a> Offset<<&'a BStr as Stream>::Checkpoint> for &'a BStr {
#[inline(always)]
fn offset_from(&self, other: &<&'a BStr as Stream>::Checkpoint) -> usize {
self.checkpoint().offset_from(other)
}
}
impl AsBStr for &BStr {
#[inline(always)]
fn as_bstr(&self) -> &[u8] {
(*self).as_bytes()
}
}
impl<'a, T> Compare<T> for &'a BStr
where
&'a [u8]: Compare<T>,
{
#[inline(always)]
fn compare(&self, t: T) -> CompareResult {
let bytes = (*self).as_bytes();
bytes.compare(t)
}
}
impl<'i, S> FindSlice<S> for &'i BStr
where
&'i [u8]: FindSlice<S>,
{
#[inline(always)]
fn find_slice(&self, substr: S) -> Option<core::ops::Range<usize>> {
let bytes = (*self).as_bytes();
let offset = bytes.find_slice(substr);
offset
}
}
impl UpdateSlice for &BStr {
#[inline(always)]
fn update_slice(self, inner: Self::Slice) -> Self {
BStr::new(inner)
}
}
#[cfg(feature = "alloc")]
impl fmt::Display for BStr {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
alloc::string::String::from_utf8_lossy(self.as_bytes()).fmt(f)
}
}
impl fmt::Debug for BStr {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !f.alternate() {
write!(f, "\"")?;
}
for byte in self.as_bytes() {
let c = *byte as char;
write!(f, "{}", c.escape_debug())?;
}
if !f.alternate() {
write!(f, "\"")?;
}
Ok(())
}
}
impl ops::Deref for BStr {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
self.as_bytes()
}
}
impl ops::Index<usize> for BStr {
type Output = u8;
#[inline]
fn index(&self, idx: usize) -> &u8 {
&self.as_bytes()[idx]
}
}
impl ops::Index<ops::RangeFull> for BStr {
type Output = BStr;
#[inline]
fn index(&self, _: ops::RangeFull) -> &BStr {
self
}
}
impl ops::Index<ops::Range<usize>> for BStr {
type Output = BStr;
#[inline]
fn index(&self, r: ops::Range<usize>) -> &BStr {
BStr::new(&self.as_bytes()[r.start..r.end])
}
}
impl ops::Index<ops::RangeInclusive<usize>> for BStr {
type Output = BStr;
#[inline]
fn index(&self, r: ops::RangeInclusive<usize>) -> &BStr {
BStr::new(&self.as_bytes()[*r.start()..=*r.end()])
}
}
impl ops::Index<ops::RangeFrom<usize>> for BStr {
type Output = BStr;
#[inline]
fn index(&self, r: ops::RangeFrom<usize>) -> &BStr {
BStr::new(&self.as_bytes()[r.start..])
}
}
impl ops::Index<ops::RangeTo<usize>> for BStr {
type Output = BStr;
#[inline]
fn index(&self, r: ops::RangeTo<usize>) -> &BStr {
BStr::new(&self.as_bytes()[..r.end])
}
}
impl ops::Index<ops::RangeToInclusive<usize>> for BStr {
type Output = BStr;
#[inline]
fn index(&self, r: ops::RangeToInclusive<usize>) -> &BStr {
BStr::new(&self.as_bytes()[..=r.end])
}
}
impl AsRef<[u8]> for BStr {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsRef<BStr> for [u8] {
#[inline]
fn as_ref(&self) -> &BStr {
BStr::new(self)
}
}
impl AsRef<BStr> for str {
#[inline]
fn as_ref(&self) -> &BStr {
BStr::new(self)
}
}
#[cfg(feature = "alloc")]
impl alloc::borrow::ToOwned for BStr {
type Owned = alloc::vec::Vec<u8>;
#[inline]
fn to_owned(&self) -> Self::Owned {
alloc::vec::Vec::from(self.as_bytes())
}
}
#[cfg(feature = "alloc")]
impl core::borrow::Borrow<BStr> for alloc::vec::Vec<u8> {
#[inline]
fn borrow(&self) -> &BStr {
BStr::from_bytes(self.as_slice())
}
}
impl<'a> Default for &'a BStr {
fn default() -> &'a BStr {
BStr::new(b"")
}
}
impl<'a> From<&'a [u8]> for &'a BStr {
#[inline]
fn from(s: &'a [u8]) -> &'a BStr {
BStr::new(s)
}
}
impl<'a> From<&'a BStr> for &'a [u8] {
#[inline]
fn from(s: &'a BStr) -> &'a [u8] {
BStr::as_bytes(s)
}
}
impl<'a> From<&'a str> for &'a BStr {
#[inline]
fn from(s: &'a str) -> &'a BStr {
BStr::new(s.as_bytes())
}
}
impl Eq for BStr {}
impl PartialEq<BStr> for BStr {
#[inline]
fn eq(&self, other: &BStr) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl_partial_eq!(BStr, [u8]);
impl_partial_eq!(BStr, &'a [u8]);
impl_partial_eq!(BStr, str);
impl_partial_eq!(BStr, &'a str);
impl PartialOrd for BStr {
#[inline]
fn partial_cmp(&self, other: &BStr) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for BStr {
#[inline]
fn cmp(&self, other: &BStr) -> Ordering {
Ord::cmp(self.as_bytes(), other.as_bytes())
}
}
impl_partial_ord!(BStr, [u8]);
impl_partial_ord!(BStr, &'a [u8]);
impl_partial_ord!(BStr, str);
impl_partial_ord!(BStr, &'a str);
#[cfg(test)]
mod test {
use crate::stream::BStr;
#[test]
fn partial_eq_bstr_byte_slice() {
let input = b"foo".as_slice();
let actual = BStr::new(input);
assert!(actual == input);
}
#[test]
fn partial_eq_byte_slice_bstr() {
let input = b"foo".as_slice();
let actual = BStr::new(input);
assert!(input == actual);
}
#[test]
fn partial_eq_bstr_str() {
let input = "foo";
let actual = BStr::new(input);
assert!(actual == input);
}
#[test]
fn partial_eq_str_bstr() {
let input = "foo";
let actual = BStr::new(input);
assert!(input == actual);
}
#[test]
fn partial_ord_bstr_byte_slice() {
let input = b"foo".as_slice();
let actual = BStr::new(input);
assert!(actual.partial_cmp(input) == Some(core::cmp::Ordering::Equal));
}
#[test]
fn partial_ord_byte_slice_bstr() {
let input = b"foo".as_slice();
let actual = BStr::new(input);
assert!(input.partial_cmp(actual) == Some(core::cmp::Ordering::Equal));
}
#[test]
fn partial_ord_bstr_str() {
let input = "foo";
let actual = BStr::new(input);
assert!(actual.partial_cmp(input) == Some(core::cmp::Ordering::Equal));
}
#[test]
fn partial_ord_str_bstr() {
let input = "foo";
let actual = BStr::new(input);
assert!(input.partial_cmp(actual) == Some(core::cmp::Ordering::Equal));
}
}
#[cfg(all(test, feature = "std"))]
mod display {
use crate::stream::BStr;
#[test]
fn clean() {
assert_eq!(&format!("{}", BStr::new(b"abc")), "abc");
assert_eq!(&format!("{}", BStr::new(b"\xf0\x28\x8c\xbc")), "�(��");
}
}
#[cfg(all(test, feature = "std"))]
mod debug {
use crate::stream::BStr;
use crate::stream::Stream as _;
use snapbox::assert_data_eq;
use snapbox::str;
#[test]
fn test_debug() {
let input = BStr::new(b"abc");
let expected = str![[r#""abc""#]];
assert_data_eq!(&format!("{input:?}"), expected);
let input = BStr::new(b"\0\0\0 ftypisom\0\0\x02\0isomiso2avc1mp");
let expected = str![[r#""/0/0/0 ftypisom/0/0/u{2}/0isomiso2avc1mp""#]];
assert_data_eq!(&format!("{input:?}"), expected);
}
#[test]
fn test_pretty_debug() {
let input = BStr::new(b"abc");
let expected = str!["abc"];
assert_data_eq!(&format!("{input:#?}"), expected);
}
#[test]
fn test_trace() {
let input = BStr::new(b"abc");
let expected = str!["abc"];
assert_data_eq!(
crate::util::from_fn(|f| input.trace(f)).to_string(),
expected
);
}
}
+562
View File
@@ -0,0 +1,562 @@
use core::num::NonZeroUsize;
use crate::stream::AsBytes;
use crate::stream::Checkpoint;
use crate::stream::Compare;
use crate::stream::CompareResult;
use crate::stream::FindSlice;
use crate::stream::Needed;
use crate::stream::Offset;
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
use crate::stream::Recover;
use crate::stream::SliceLen;
use crate::stream::Stream;
use crate::stream::StreamIsPartial;
use crate::stream::UpdateSlice;
use core::iter::{Cloned, Enumerate};
use core::slice::Iter;
use core::{cmp::Ordering, fmt, ops};
/// Improved `Debug` experience for `&[u8]` byte streams
#[allow(clippy::derived_hash_with_manual_eq)]
#[derive(Hash)]
#[repr(transparent)]
pub struct Bytes([u8]);
impl Bytes {
/// Make a stream out of a byte slice-like.
#[inline]
pub fn new<B: ?Sized + AsRef<[u8]>>(bytes: &B) -> &Self {
Self::from_bytes(bytes.as_ref())
}
#[inline]
fn from_bytes(slice: &[u8]) -> &Self {
unsafe { core::mem::transmute(slice) }
}
#[inline]
fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl SliceLen for &Bytes {
#[inline(always)]
fn slice_len(&self) -> usize {
self.len()
}
}
impl<'i> Stream for &'i Bytes {
type Token = u8;
type Slice = &'i [u8];
type IterOffsets = Enumerate<Cloned<Iter<'i, u8>>>;
type Checkpoint = Checkpoint<Self, Self>;
#[inline(always)]
fn iter_offsets(&self) -> Self::IterOffsets {
self.iter().cloned().enumerate()
}
#[inline(always)]
fn eof_offset(&self) -> usize {
self.len()
}
#[inline(always)]
fn next_token(&mut self) -> Option<Self::Token> {
if self.is_empty() {
None
} else {
let token = self[0];
*self = &self[1..];
Some(token)
}
}
#[inline(always)]
fn peek_token(&self) -> Option<Self::Token> {
if self.is_empty() {
None
} else {
Some(self[0])
}
}
#[inline(always)]
fn offset_for<P>(&self, predicate: P) -> Option<usize>
where
P: Fn(Self::Token) -> bool,
{
self.iter().position(|b| predicate(*b))
}
#[inline(always)]
fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
if let Some(needed) = tokens.checked_sub(self.len()).and_then(NonZeroUsize::new) {
Err(Needed::Size(needed))
} else {
Ok(tokens)
}
}
#[inline(always)]
fn next_slice(&mut self, offset: usize) -> Self::Slice {
let (slice, next) = self.0.split_at(offset);
*self = Bytes::from_bytes(next);
slice
}
#[inline(always)]
unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice {
#[cfg(debug_assertions)]
self.peek_slice(offset);
// SAFETY: `Stream::next_slice_unchecked` requires `offset` to be in bounds
let slice = unsafe { self.0.get_unchecked(..offset) };
// SAFETY: `Stream::next_slice_unchecked` requires `offset` to be in bounds
let next = unsafe { self.0.get_unchecked(offset..) };
*self = Bytes::from_bytes(next);
slice
}
#[inline(always)]
fn peek_slice(&self, offset: usize) -> Self::Slice {
&self[..offset]
}
#[inline(always)]
unsafe fn peek_slice_unchecked(&self, offset: usize) -> Self::Slice {
#[cfg(debug_assertions)]
self.peek_slice(offset);
// SAFETY: `Stream::next_slice_unchecked` requires `offset` to be in bounds
let slice = unsafe { self.0.get_unchecked(..offset) };
slice
}
#[inline(always)]
fn checkpoint(&self) -> Self::Checkpoint {
Checkpoint::<_, Self>::new(*self)
}
#[inline(always)]
fn reset(&mut self, checkpoint: &Self::Checkpoint) {
*self = checkpoint.inner;
}
fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{self:#?}")
}
}
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
impl<E> Recover<E> for &Bytes {
#[inline(always)]
fn record_err(
&mut self,
_token_start: &Self::Checkpoint,
_err_start: &Self::Checkpoint,
err: E,
) -> Result<(), E> {
Err(err)
}
/// Report whether the [`Stream`] can save off errors for recovery
#[inline(always)]
fn is_recovery_supported() -> bool {
false
}
}
impl StreamIsPartial for &Bytes {
type PartialState = ();
#[inline]
fn complete(&mut self) -> Self::PartialState {
// Already complete
}
#[inline]
fn restore_partial(&mut self, _state: Self::PartialState) {}
#[inline(always)]
fn is_partial_supported() -> bool {
false
}
}
impl Offset for &Bytes {
#[inline(always)]
fn offset_from(&self, start: &Self) -> usize {
self.as_bytes().offset_from(&start.as_bytes())
}
}
impl<'a> Offset<<&'a Bytes as Stream>::Checkpoint> for &'a Bytes {
#[inline(always)]
fn offset_from(&self, other: &<&'a Bytes as Stream>::Checkpoint) -> usize {
self.checkpoint().offset_from(other)
}
}
impl AsBytes for &Bytes {
#[inline(always)]
fn as_bytes(&self) -> &[u8] {
(*self).as_bytes()
}
}
impl<'a, T> Compare<T> for &'a Bytes
where
&'a [u8]: Compare<T>,
{
#[inline(always)]
fn compare(&self, t: T) -> CompareResult {
let bytes = (*self).as_bytes();
bytes.compare(t)
}
}
impl<'i, S> FindSlice<S> for &'i Bytes
where
&'i [u8]: FindSlice<S>,
{
#[inline(always)]
fn find_slice(&self, substr: S) -> Option<core::ops::Range<usize>> {
let bytes = (*self).as_bytes();
let offset = bytes.find_slice(substr);
offset
}
}
impl UpdateSlice for &Bytes {
#[inline(always)]
fn update_slice(self, inner: Self::Slice) -> Self {
Bytes::new(inner)
}
}
impl fmt::Display for Bytes {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<Self as fmt::UpperHex>::fmt(self, f)
}
}
impl fmt::Debug for Bytes {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<Self as fmt::UpperHex>::fmt(self, f)
}
}
impl fmt::LowerHex for Bytes {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.as_bytes() {
write!(f, "{byte:0>2x}")?;
}
Ok(())
}
}
impl fmt::UpperHex for Bytes {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, byte) in self.as_bytes().iter().enumerate() {
if 0 < i {
let absolute = (self.as_bytes().as_ptr() as usize) + i;
if f.alternate() && absolute != 0 && absolute % 4 == 0 {
write!(f, "_")?;
}
}
write!(f, "{byte:0>2X}")?;
}
Ok(())
}
}
impl ops::Deref for Bytes {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
self.as_bytes()
}
}
impl ops::Index<usize> for Bytes {
type Output = u8;
#[inline]
fn index(&self, idx: usize) -> &u8 {
&self.as_bytes()[idx]
}
}
impl ops::Index<ops::RangeFull> for Bytes {
type Output = Bytes;
#[inline]
fn index(&self, _: ops::RangeFull) -> &Bytes {
self
}
}
impl ops::Index<ops::Range<usize>> for Bytes {
type Output = Bytes;
#[inline]
fn index(&self, r: ops::Range<usize>) -> &Bytes {
Bytes::new(&self.as_bytes()[r.start..r.end])
}
}
impl ops::Index<ops::RangeInclusive<usize>> for Bytes {
type Output = Bytes;
#[inline]
fn index(&self, r: ops::RangeInclusive<usize>) -> &Bytes {
Bytes::new(&self.as_bytes()[*r.start()..=*r.end()])
}
}
impl ops::Index<ops::RangeFrom<usize>> for Bytes {
type Output = Bytes;
#[inline]
fn index(&self, r: ops::RangeFrom<usize>) -> &Bytes {
Bytes::new(&self.as_bytes()[r.start..])
}
}
impl ops::Index<ops::RangeTo<usize>> for Bytes {
type Output = Bytes;
#[inline]
fn index(&self, r: ops::RangeTo<usize>) -> &Bytes {
Bytes::new(&self.as_bytes()[..r.end])
}
}
impl ops::Index<ops::RangeToInclusive<usize>> for Bytes {
type Output = Bytes;
#[inline]
fn index(&self, r: ops::RangeToInclusive<usize>) -> &Bytes {
Bytes::new(&self.as_bytes()[..=r.end])
}
}
impl AsRef<[u8]> for Bytes {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsRef<Bytes> for [u8] {
#[inline]
fn as_ref(&self) -> &Bytes {
Bytes::new(self)
}
}
impl AsRef<Bytes> for str {
#[inline]
fn as_ref(&self) -> &Bytes {
Bytes::new(self)
}
}
#[cfg(feature = "alloc")]
impl alloc::borrow::ToOwned for Bytes {
type Owned = alloc::vec::Vec<u8>;
#[inline]
fn to_owned(&self) -> Self::Owned {
alloc::vec::Vec::from(self.as_bytes())
}
}
#[cfg(feature = "alloc")]
impl core::borrow::Borrow<Bytes> for alloc::vec::Vec<u8> {
#[inline]
fn borrow(&self) -> &Bytes {
Bytes::from_bytes(self.as_slice())
}
}
impl<'a> Default for &'a Bytes {
fn default() -> &'a Bytes {
Bytes::new(b"")
}
}
impl<'a> From<&'a [u8]> for &'a Bytes {
#[inline]
fn from(s: &'a [u8]) -> &'a Bytes {
Bytes::new(s)
}
}
impl<'a> From<&'a Bytes> for &'a [u8] {
#[inline]
fn from(s: &'a Bytes) -> &'a [u8] {
Bytes::as_bytes(s)
}
}
impl<'a> From<&'a str> for &'a Bytes {
#[inline]
fn from(s: &'a str) -> &'a Bytes {
Bytes::new(s.as_bytes())
}
}
impl Eq for Bytes {}
impl PartialEq<Bytes> for Bytes {
#[inline]
fn eq(&self, other: &Bytes) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl_partial_eq!(Bytes, [u8]);
impl_partial_eq!(Bytes, &'a [u8]);
impl_partial_eq!(Bytes, str);
impl_partial_eq!(Bytes, &'a str);
impl PartialOrd for Bytes {
#[inline]
fn partial_cmp(&self, other: &Bytes) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Bytes {
#[inline]
fn cmp(&self, other: &Bytes) -> Ordering {
Ord::cmp(self.as_bytes(), other.as_bytes())
}
}
impl_partial_ord!(Bytes, [u8]);
impl_partial_ord!(Bytes, &'a [u8]);
impl_partial_ord!(Bytes, str);
impl_partial_ord!(Bytes, &'a str);
#[cfg(test)]
mod test {
use crate::stream::Bytes;
#[test]
fn partial_eq_bytes_byte_slice() {
let input = b"foo".as_slice();
let actual = Bytes::new(input);
assert!(actual == input);
}
#[test]
fn partial_eq_byte_slice_bytes() {
let input = b"foo".as_slice();
let actual = Bytes::new(input);
assert!(input == actual);
}
#[test]
fn partial_eq_bytes_str() {
let input = "foo";
let actual = Bytes::new(input);
assert!(actual == input);
}
#[test]
fn partial_eq_str_bytes() {
let input = "foo";
let actual = Bytes::new(input);
assert!(input == actual);
}
#[test]
fn partial_ord_bytes_byte_slice() {
let input = b"foo".as_slice();
let actual = Bytes::new(input);
assert!(actual.partial_cmp(input) == Some(core::cmp::Ordering::Equal));
}
#[test]
fn partial_ord_byte_slice_bytes() {
let input = b"foo".as_slice();
let actual = Bytes::new(input);
assert!(input.partial_cmp(actual) == Some(core::cmp::Ordering::Equal));
}
#[test]
fn partial_ord_bytes_str() {
let input = "foo";
let actual = Bytes::new(input);
assert!(actual.partial_cmp(input) == Some(core::cmp::Ordering::Equal));
}
#[test]
fn partial_ord_str_bytes() {
let input = "foo";
let actual = Bytes::new(input);
assert!(input.partial_cmp(actual) == Some(core::cmp::Ordering::Equal));
}
}
#[cfg(all(test, feature = "std"))]
mod display {
use crate::stream::Bytes;
#[test]
fn clean() {
assert_eq!(&format!("{}", Bytes::new(b"abc")), "616263");
assert_eq!(&format!("{}", Bytes::new(b"\xf0\x28\x8c\xbc")), "F0288CBC");
}
}
#[cfg(all(test, feature = "std"))]
mod debug {
use crate::stream::Bytes;
use crate::stream::Stream as _;
use snapbox::assert_data_eq;
use snapbox::str;
#[test]
fn test_debug() {
let input = Bytes::new(b"\0\0\0 ftypisom\0\0\x02\0isomiso2avc1mp");
let expected = str!["000000206674797069736F6D0000020069736F6D69736F32617663316D70"];
assert_data_eq!(&format!("{input:?}"), expected);
}
#[test]
fn test_pretty_debug() {
let input = Bytes::new(b"\0\0\0 ftypisom\0\0\x02\0isomiso2avc1mp");
let expected = str!["000000206674797069736F6D0000020069736F6D69736F32617663316D70"];
assert_data_eq!(&format!("{input:#?}").replace('_', ""), expected);
}
#[test]
fn test_trace() {
let input = Bytes::new(b"\0\0\0 ftypisom\0\0\x02\0isomiso2avc1mp");
let expected = str!["000000206674797069736F6D0000020069736F6D69736F32617663316D70"];
assert_data_eq!(
crate::util::from_fn(|f| input.trace(f))
.to_string()
.replace('_', ""),
expected
);
}
#[test]
fn test_sliced() {
// Output can change from run-to-run
let total = Bytes::new(b"12345678901234567890");
let _ = format!("{total:#?}");
let _ = format!("{:#?}", &total[1..]);
let _ = format!("{:#?}", &total[10..]);
}
}
+320
View File
@@ -0,0 +1,320 @@
use crate::stream::AsBStr;
use crate::stream::AsBytes;
use crate::stream::Checkpoint;
use crate::stream::Compare;
use crate::stream::CompareResult;
use crate::stream::FindSlice;
use crate::stream::Location;
use crate::stream::Needed;
use crate::stream::Offset;
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
use crate::stream::Recover;
use crate::stream::SliceLen;
use crate::stream::Stream;
use crate::stream::StreamIsPartial;
use crate::stream::UpdateSlice;
/// Allow collecting the span of a parsed token within a slice
///
/// Converting byte offsets to line or column numbers is left up to the user, as computing column
/// numbers requires domain knowledge (are columns byte-based, codepoint-based, or grapheme-based?)
/// and O(n) iteration over the input to determine codepoint and line boundaries.
///
/// [The `line-span` crate](https://docs.rs/line-span/latest/line_span/) can help with converting
/// byte offsets to line numbers.
///
/// See [`Parser::span`][crate::Parser::span] and [`Parser::with_span`][crate::Parser::with_span] for more details
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
#[doc(alias = "LocatingSliceSpan")]
#[doc(alias = "Located")]
pub struct LocatingSlice<I> {
initial: I,
input: I,
}
impl<I> LocatingSlice<I>
where
I: Clone + Offset,
{
/// Wrap another Stream with span tracking
pub fn new(input: I) -> Self {
let initial = input.clone();
Self { initial, input }
}
#[inline]
fn previous_token_end(&self) -> usize {
// Assumptions:
// - Index offsets is sufficient
// - Tokens are continuous
self.input.offset_from(&self.initial)
}
#[inline]
fn current_token_start(&self) -> usize {
// Assumptions:
// - Index offsets is sufficient
self.input.offset_from(&self.initial)
}
}
impl<I> LocatingSlice<I>
where
I: Clone + Stream + Offset,
{
/// Reset the stream to the start
///
/// This is useful for formats that encode a graph with addresses relative to the start of the
/// input.
#[doc(alias = "fseek")]
#[inline]
pub fn reset_to_start(&mut self) {
let start = self.initial.checkpoint();
self.input.reset(&start);
}
}
impl<I> AsRef<I> for LocatingSlice<I> {
#[inline(always)]
fn as_ref(&self) -> &I {
&self.input
}
}
impl<I: core::fmt::Debug> core::fmt::Debug for LocatingSlice<I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.input.fmt(f)
}
}
impl<I> core::ops::Deref for LocatingSlice<I> {
type Target = I;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.input
}
}
impl<I: core::fmt::Display> core::fmt::Display for LocatingSlice<I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.input.fmt(f)
}
}
impl<I> SliceLen for LocatingSlice<I>
where
I: SliceLen,
{
#[inline(always)]
fn slice_len(&self) -> usize {
self.input.slice_len()
}
}
impl<I: Stream> Stream for LocatingSlice<I> {
type Token = <I as Stream>::Token;
type Slice = <I as Stream>::Slice;
type IterOffsets = <I as Stream>::IterOffsets;
type Checkpoint = Checkpoint<I::Checkpoint, Self>;
#[inline(always)]
fn iter_offsets(&self) -> Self::IterOffsets {
self.input.iter_offsets()
}
#[inline(always)]
fn eof_offset(&self) -> usize {
self.input.eof_offset()
}
#[inline(always)]
fn next_token(&mut self) -> Option<Self::Token> {
self.input.next_token()
}
#[inline(always)]
fn peek_token(&self) -> Option<Self::Token> {
self.input.peek_token()
}
#[inline(always)]
fn offset_for<P>(&self, predicate: P) -> Option<usize>
where
P: Fn(Self::Token) -> bool,
{
self.input.offset_for(predicate)
}
#[inline(always)]
fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
self.input.offset_at(tokens)
}
#[inline(always)]
fn next_slice(&mut self, offset: usize) -> Self::Slice {
self.input.next_slice(offset)
}
#[inline(always)]
unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice {
// SAFETY: Passing up invariants
unsafe { self.input.next_slice_unchecked(offset) }
}
#[inline(always)]
fn peek_slice(&self, offset: usize) -> Self::Slice {
self.input.peek_slice(offset)
}
#[inline(always)]
unsafe fn peek_slice_unchecked(&self, offset: usize) -> Self::Slice {
// SAFETY: Passing up invariants
unsafe { self.input.peek_slice_unchecked(offset) }
}
#[inline(always)]
fn checkpoint(&self) -> Self::Checkpoint {
Checkpoint::<_, Self>::new(self.input.checkpoint())
}
#[inline(always)]
fn reset(&mut self, checkpoint: &Self::Checkpoint) {
self.input.reset(&checkpoint.inner);
}
fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.input.trace(f)
}
}
impl<I> Location for LocatingSlice<I>
where
I: Clone + Offset,
{
#[inline(always)]
fn previous_token_end(&self) -> usize {
self.previous_token_end()
}
#[inline(always)]
fn current_token_start(&self) -> usize {
self.current_token_start()
}
}
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
impl<I, E> Recover<E> for LocatingSlice<I>
where
I: Recover<E>,
I: Stream,
{
#[inline(always)]
fn record_err(
&mut self,
_token_start: &Self::Checkpoint,
_err_start: &Self::Checkpoint,
err: E,
) -> Result<(), E> {
Err(err)
}
/// Report whether the [`Stream`] can save off errors for recovery
#[inline(always)]
fn is_recovery_supported() -> bool {
false
}
}
impl<I> StreamIsPartial for LocatingSlice<I>
where
I: StreamIsPartial,
{
type PartialState = I::PartialState;
#[inline]
fn complete(&mut self) -> Self::PartialState {
self.input.complete()
}
#[inline]
fn restore_partial(&mut self, state: Self::PartialState) {
self.input.restore_partial(state);
}
#[inline(always)]
fn is_partial_supported() -> bool {
I::is_partial_supported()
}
#[inline(always)]
fn is_partial(&self) -> bool {
self.input.is_partial()
}
}
impl<I> Offset for LocatingSlice<I>
where
I: Stream,
{
#[inline(always)]
fn offset_from(&self, other: &Self) -> usize {
self.offset_from(&other.checkpoint())
}
}
impl<I> Offset<<LocatingSlice<I> as Stream>::Checkpoint> for LocatingSlice<I>
where
I: Stream,
{
#[inline(always)]
fn offset_from(&self, other: &<LocatingSlice<I> as Stream>::Checkpoint) -> usize {
self.checkpoint().offset_from(other)
}
}
impl<I> AsBytes for LocatingSlice<I>
where
I: AsBytes,
{
#[inline(always)]
fn as_bytes(&self) -> &[u8] {
self.input.as_bytes()
}
}
impl<I> AsBStr for LocatingSlice<I>
where
I: AsBStr,
{
#[inline(always)]
fn as_bstr(&self) -> &[u8] {
self.input.as_bstr()
}
}
impl<I, U> Compare<U> for LocatingSlice<I>
where
I: Compare<U>,
{
#[inline(always)]
fn compare(&self, other: U) -> CompareResult {
self.input.compare(other)
}
}
impl<I, T> FindSlice<T> for LocatingSlice<I>
where
I: FindSlice<T>,
{
#[inline(always)]
fn find_slice(&self, substr: T) -> Option<core::ops::Range<usize>> {
self.input.find_slice(substr)
}
}
impl<I> UpdateSlice for LocatingSlice<I>
where
I: UpdateSlice,
{
#[inline(always)]
fn update_slice(mut self, inner: Self::Slice) -> Self {
self.input = I::update_slice(self.input, inner);
self
}
}
+2011
View File
File diff suppressed because it is too large. Load diff
+355
View File
@@ -0,0 +1,355 @@
use crate::stream::AsBStr;
use crate::stream::AsBytes;
use crate::stream::Checkpoint;
use crate::stream::Compare;
use crate::stream::CompareResult;
use crate::stream::FindSlice;
use crate::stream::Location;
use crate::stream::Needed;
use crate::stream::Offset;
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
use crate::stream::Recover;
use crate::stream::SliceLen;
use crate::stream::Stream;
use crate::stream::StreamIsPartial;
use crate::stream::UpdateSlice;
/// Mark the input as a partial buffer for streaming input.
///
/// Complete input means that we already have all of the data. This will be the common case with
/// small files that can be read entirely to memory.
///
/// In contrast, streaming input assumes that we might not have all of the data.
/// This can happen with some network protocol or large file parsers, where the
/// input buffer can be full and need to be resized or refilled.
/// - [`ErrMode::Incomplete`][crate::error::ErrMode::Incomplete] will report how much more data is needed.
/// - [`Parser::complete_err`][crate::Parser::complete_err] transform
/// [`ErrMode::Incomplete`][crate::error::ErrMode::Incomplete] to
/// [`ErrMode::Backtrack`][crate::error::ErrMode::Backtrack]
///
/// See also [`StreamIsPartial`] to tell whether the input supports complete or partial parsing.
///
/// See also [Special Topics: Parsing Partial Input][crate::_topic::partial].
///
/// # Example
///
/// Here is how it works in practice:
///
/// ```rust
/// # #[cfg(feature = "ascii")] {
/// # use winnow::{Result, error::ErrMode, error::Needed, error::ContextError, token, ascii, stream::Partial};
/// # use winnow::prelude::*;
///
/// fn take_partial<'s>(i: &mut Partial<&'s [u8]>) -> ModalResult<&'s [u8], ContextError> {
/// token::take(4u8).parse_next(i)
/// }
///
/// fn take_complete<'s>(i: &mut &'s [u8]) -> ModalResult<&'s [u8], ContextError> {
/// token::take(4u8).parse_next(i)
/// }
///
/// // both parsers will take 4 bytes as expected
/// assert_eq!(take_partial.parse_peek(Partial::new(&b"abcde"[..])), Ok((Partial::new(&b"e"[..]), &b"abcd"[..])));
/// assert_eq!(take_complete.parse_peek(&b"abcde"[..]), Ok((&b"e"[..], &b"abcd"[..])));
///
/// // if the input is smaller than 4 bytes, the partial parser
/// // will return `Incomplete` to indicate that we need more data
/// assert_eq!(take_partial.parse_peek(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
///
/// // but the complete parser will return an error
/// assert!(take_complete.parse_peek(&b"abc"[..]).is_err());
///
/// // the alpha0 function takes 0 or more alphabetic characters
/// fn alpha0_partial<'s>(i: &mut Partial<&'s str>) -> ModalResult<&'s str, ContextError> {
/// ascii::alpha0.parse_next(i)
/// }
///
/// fn alpha0_complete<'s>(i: &mut &'s str) -> ModalResult<&'s str, ContextError> {
/// ascii::alpha0.parse_next(i)
/// }
///
/// // if there's a clear limit to the taken characters, both parsers work the same way
/// assert_eq!(alpha0_partial.parse_peek(Partial::new("abcd;")), Ok((Partial::new(";"), "abcd")));
/// assert_eq!(alpha0_complete.parse_peek("abcd;"), Ok((";", "abcd")));
///
/// // but when there's no limit, the partial version returns `Incomplete`, because it cannot
/// // know if more input data should be taken. The whole input could be "abcd;", or
/// // "abcde;"
/// assert_eq!(alpha0_partial.parse_peek(Partial::new("abcd")), Err(ErrMode::Incomplete(Needed::new(1))));
///
/// // while the complete version knows that all of the data is there
/// assert_eq!(alpha0_complete.parse_peek("abcd"), Ok(("", "abcd")));
/// # }
/// ```
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Partial<I> {
input: I,
partial: bool,
}
impl<I> Partial<I>
where
I: StreamIsPartial,
{
/// Create a partial input
#[inline]
pub fn new(input: I) -> Self {
debug_assert!(
!I::is_partial_supported(),
"`Partial` can only wrap complete sources"
);
let partial = true;
Self { input, partial }
}
/// Extract the original [`Stream`]
#[inline(always)]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> Default for Partial<I>
where
I: Default + StreamIsPartial,
{
#[inline]
fn default() -> Self {
Self::new(I::default())
}
}
impl<I> core::ops::Deref for Partial<I> {
type Target = I;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.input
}
}
impl<I: core::fmt::Display> core::fmt::Display for Partial<I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.input.fmt(f)
}
}
impl<I> SliceLen for Partial<I>
where
I: SliceLen,
{
#[inline(always)]
fn slice_len(&self) -> usize {
self.input.slice_len()
}
}
impl<I: Stream> Stream for Partial<I> {
type Token = <I as Stream>::Token;
type Slice = <I as Stream>::Slice;
type IterOffsets = <I as Stream>::IterOffsets;
type Checkpoint = Checkpoint<I::Checkpoint, Self>;
#[inline(always)]
fn iter_offsets(&self) -> Self::IterOffsets {
self.input.iter_offsets()
}
#[inline(always)]
fn eof_offset(&self) -> usize {
self.input.eof_offset()
}
#[inline(always)]
fn next_token(&mut self) -> Option<Self::Token> {
self.input.next_token()
}
#[inline(always)]
fn peek_token(&self) -> Option<Self::Token> {
self.input.peek_token()
}
#[inline(always)]
fn offset_for<P>(&self, predicate: P) -> Option<usize>
where
P: Fn(Self::Token) -> bool,
{
self.input.offset_for(predicate)
}
#[inline(always)]
fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
self.input.offset_at(tokens)
}
#[inline(always)]
fn next_slice(&mut self, offset: usize) -> Self::Slice {
self.input.next_slice(offset)
}
#[inline(always)]
unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice {
// SAFETY: Passing up invariants
unsafe { self.input.next_slice_unchecked(offset) }
}
#[inline(always)]
fn peek_slice(&self, offset: usize) -> Self::Slice {
self.input.peek_slice(offset)
}
#[inline(always)]
unsafe fn peek_slice_unchecked(&self, offset: usize) -> Self::Slice {
// SAFETY: Passing up invariants
unsafe { self.input.peek_slice_unchecked(offset) }
}
#[inline(always)]
fn checkpoint(&self) -> Self::Checkpoint {
Checkpoint::<_, Self>::new(self.input.checkpoint())
}
#[inline(always)]
fn reset(&mut self, checkpoint: &Self::Checkpoint) {
self.input.reset(&checkpoint.inner);
}
fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.input.trace(f)
}
}
impl<I> Location for Partial<I>
where
I: Location,
{
#[inline(always)]
fn previous_token_end(&self) -> usize {
self.input.previous_token_end()
}
#[inline(always)]
fn current_token_start(&self) -> usize {
self.input.current_token_start()
}
}
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
impl<I, E> Recover<E> for Partial<I>
where
I: Recover<E>,
I: Stream,
{
#[inline(always)]
fn record_err(
&mut self,
_token_start: &Self::Checkpoint,
_err_start: &Self::Checkpoint,
err: E,
) -> Result<(), E> {
Err(err)
}
/// Report whether the [`Stream`] can save off errors for recovery
#[inline(always)]
fn is_recovery_supported() -> bool {
false
}
}
impl<I> StreamIsPartial for Partial<I>
where
I: StreamIsPartial,
{
type PartialState = bool;
#[inline]
fn complete(&mut self) -> Self::PartialState {
core::mem::replace(&mut self.partial, false)
}
#[inline]
fn restore_partial(&mut self, state: Self::PartialState) {
self.partial = state;
}
#[inline(always)]
fn is_partial_supported() -> bool {
true
}
#[inline(always)]
fn is_partial(&self) -> bool {
self.partial
}
}
impl<I> Offset for Partial<I>
where
I: Stream,
{
#[inline(always)]
fn offset_from(&self, start: &Self) -> usize {
self.offset_from(&start.checkpoint())
}
}
impl<I> Offset<<Partial<I> as Stream>::Checkpoint> for Partial<I>
where
I: Stream,
{
#[inline(always)]
fn offset_from(&self, other: &<Partial<I> as Stream>::Checkpoint) -> usize {
self.checkpoint().offset_from(other)
}
}
impl<I> AsBytes for Partial<I>
where
I: AsBytes,
{
#[inline(always)]
fn as_bytes(&self) -> &[u8] {
self.input.as_bytes()
}
}
impl<I> AsBStr for Partial<I>
where
I: AsBStr,
{
#[inline(always)]
fn as_bstr(&self) -> &[u8] {
self.input.as_bstr()
}
}
impl<I, T> Compare<T> for Partial<I>
where
I: Compare<T>,
{
#[inline(always)]
fn compare(&self, t: T) -> CompareResult {
self.input.compare(t)
}
}
impl<I, T> FindSlice<T> for Partial<I>
where
I: FindSlice<T>,
{
#[inline(always)]
fn find_slice(&self, substr: T) -> Option<core::ops::Range<usize>> {
self.input.find_slice(substr)
}
}
impl<I> UpdateSlice for Partial<I>
where
I: UpdateSlice,
{
#[inline(always)]
fn update_slice(self, inner: Self::Slice) -> Self {
Partial {
input: I::update_slice(self.input, inner),
partial: self.partial,
}
}
}
+143
View File
@@ -0,0 +1,143 @@
/// A range bounded inclusively for counting parses performed
///
/// This is flexible in what can be converted to a [Range]:
/// ```rust
/// # #[cfg(all(feature = "std", feature = "parser"))] {
/// # use winnow::prelude::*;
/// # use winnow::token::any;
/// # use winnow::combinator::repeat;
/// # fn inner(input: &mut &str) -> ModalResult<char> {
/// # any.parse_next(input)
/// # }
/// # let mut input = "0123456789012345678901234567890123456789";
/// # let input = &mut input;
/// let parser: Vec<_> = repeat(5, inner).parse_next(input).unwrap();
/// # let mut input = "0123456789012345678901234567890123456789";
/// # let input = &mut input;
/// let parser: Vec<_> = repeat(.., inner).parse_next(input).unwrap();
/// # let mut input = "0123456789012345678901234567890123456789";
/// # let input = &mut input;
/// let parser: Vec<_> = repeat(1.., inner).parse_next(input).unwrap();
/// # let mut input = "0123456789012345678901234567890123456789";
/// # let input = &mut input;
/// let parser: Vec<_> = repeat(5..8, inner).parse_next(input).unwrap();
/// # let mut input = "0123456789012345678901234567890123456789";
/// # let input = &mut input;
/// let parser: Vec<_> = repeat(5..=8, inner).parse_next(input).unwrap();
/// # }
/// ```
#[derive(PartialEq, Eq, Copy, Clone)]
pub struct Range {
pub(crate) start_inclusive: usize,
pub(crate) end_inclusive: Option<usize>,
}
impl Range {
#[inline(always)]
fn raw(start_inclusive: usize, end_inclusive: Option<usize>) -> Self {
Self {
start_inclusive,
end_inclusive,
}
}
}
impl core::ops::RangeBounds<usize> for Range {
#[inline(always)]
fn start_bound(&self) -> core::ops::Bound<&usize> {
core::ops::Bound::Included(&self.start_inclusive)
}
#[inline(always)]
fn end_bound(&self) -> core::ops::Bound<&usize> {
if let Some(end_inclusive) = &self.end_inclusive {
core::ops::Bound::Included(end_inclusive)
} else {
core::ops::Bound::Unbounded
}
}
}
impl From<usize> for Range {
#[inline(always)]
fn from(fixed: usize) -> Self {
(fixed..=fixed).into()
}
}
impl From<core::ops::Range<usize>> for Range {
#[inline(always)]
fn from(range: core::ops::Range<usize>) -> Self {
let start_inclusive = range.start;
let end_inclusive = Some(range.end.saturating_sub(1));
Self::raw(start_inclusive, end_inclusive)
}
}
impl From<core::ops::RangeFull> for Range {
#[inline(always)]
fn from(_: core::ops::RangeFull) -> Self {
let start_inclusive = 0;
let end_inclusive = None;
Self::raw(start_inclusive, end_inclusive)
}
}
impl From<core::ops::RangeFrom<usize>> for Range {
#[inline(always)]
fn from(range: core::ops::RangeFrom<usize>) -> Self {
let start_inclusive = range.start;
let end_inclusive = None;
Self::raw(start_inclusive, end_inclusive)
}
}
impl From<core::ops::RangeTo<usize>> for Range {
#[inline(always)]
fn from(range: core::ops::RangeTo<usize>) -> Self {
let start_inclusive = 0;
let end_inclusive = Some(range.end.saturating_sub(1));
Self::raw(start_inclusive, end_inclusive)
}
}
impl From<core::ops::RangeInclusive<usize>> for Range {
#[inline(always)]
fn from(range: core::ops::RangeInclusive<usize>) -> Self {
let start_inclusive = *range.start();
let end_inclusive = Some(*range.end());
Self::raw(start_inclusive, end_inclusive)
}
}
impl From<core::ops::RangeToInclusive<usize>> for Range {
#[inline(always)]
fn from(range: core::ops::RangeToInclusive<usize>) -> Self {
let start_inclusive = 0;
let end_inclusive = Some(range.end);
Self::raw(start_inclusive, end_inclusive)
}
}
impl core::fmt::Display for Range {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.start_inclusive.fmt(f)?;
match self.end_inclusive {
Some(e) if e == self.start_inclusive => {}
Some(e) => {
"..=".fmt(f)?;
e.fmt(f)?;
}
None => {
"..".fmt(f)?;
}
}
Ok(())
}
}
impl core::fmt::Debug for Range {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{self}")
}
}
+343
View File
@@ -0,0 +1,343 @@
use crate::error::FromRecoverableError;
use crate::error::Needed;
use crate::stream::AsBStr;
use crate::stream::AsBytes;
use crate::stream::Checkpoint;
use crate::stream::Compare;
use crate::stream::CompareResult;
use crate::stream::FindSlice;
use crate::stream::Location;
use crate::stream::Offset;
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
use crate::stream::Recover;
use crate::stream::SliceLen;
use crate::stream::Stream;
use crate::stream::StreamIsPartial;
use crate::stream::UpdateSlice;
/// Allow recovering from parse errors, capturing them as the parser continues
///
/// Generally, this will be used indirectly via
/// [`RecoverableParser::recoverable_parse`][crate::RecoverableParser::recoverable_parse].
#[derive(Clone, Debug)]
pub struct Recoverable<I, E>
where
I: Stream,
{
input: I,
errors: Vec<E>,
is_recoverable: bool,
}
impl<I, E> Default for Recoverable<I, E>
where
I: Default + Stream,
{
#[inline]
fn default() -> Self {
Self::new(I::default())
}
}
impl<I, E> Recoverable<I, E>
where
I: Stream,
{
/// Track recoverable errors with the stream
#[inline]
pub fn new(input: I) -> Self {
Self {
input,
errors: Default::default(),
is_recoverable: true,
}
}
/// Act as a normal stream
#[inline]
pub fn unrecoverable(input: I) -> Self {
Self {
input,
errors: Default::default(),
is_recoverable: false,
}
}
/// Access the current input and errors
#[inline]
pub fn into_parts(self) -> (I, Vec<E>) {
(self.input, self.errors)
}
}
impl<I, E> AsRef<I> for Recoverable<I, E>
where
I: Stream,
{
#[inline(always)]
fn as_ref(&self) -> &I {
&self.input
}
}
impl<I, E> core::ops::Deref for Recoverable<I, E>
where
I: Stream,
{
type Target = I;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.input
}
}
impl<I: core::fmt::Display, E> core::fmt::Display for Recoverable<I, E>
where
I: Stream,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(&self.input, f)
}
}
impl<I, E> SliceLen for Recoverable<I, E>
where
I: SliceLen,
I: Stream,
{
#[inline(always)]
fn slice_len(&self) -> usize {
self.input.slice_len()
}
}
impl<I, E: core::fmt::Debug> Stream for Recoverable<I, E>
where
I: Stream,
{
type Token = <I as Stream>::Token;
type Slice = <I as Stream>::Slice;
type IterOffsets = <I as Stream>::IterOffsets;
type Checkpoint = Checkpoint<I::Checkpoint, Self>;
#[inline(always)]
fn iter_offsets(&self) -> Self::IterOffsets {
self.input.iter_offsets()
}
#[inline(always)]
fn eof_offset(&self) -> usize {
self.input.eof_offset()
}
#[inline(always)]
fn next_token(&mut self) -> Option<Self::Token> {
self.input.next_token()
}
#[inline(always)]
fn peek_token(&self) -> Option<Self::Token> {
self.input.peek_token()
}
#[inline(always)]
fn offset_for<P>(&self, predicate: P) -> Option<usize>
where
P: Fn(Self::Token) -> bool,
{
self.input.offset_for(predicate)
}
#[inline(always)]
fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
self.input.offset_at(tokens)
}
#[inline(always)]
fn next_slice(&mut self, offset: usize) -> Self::Slice {
self.input.next_slice(offset)
}
#[inline(always)]
unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice {
// SAFETY: Passing up invariants
unsafe { self.input.next_slice_unchecked(offset) }
}
#[inline(always)]
fn peek_slice(&self, offset: usize) -> Self::Slice {
self.input.peek_slice(offset)
}
#[inline(always)]
unsafe fn peek_slice_unchecked(&self, offset: usize) -> Self::Slice {
// SAFETY: Passing up invariants
unsafe { self.input.peek_slice_unchecked(offset) }
}
#[inline(always)]
fn checkpoint(&self) -> Self::Checkpoint {
Checkpoint::<_, Self>::new(self.input.checkpoint())
}
#[inline(always)]
fn reset(&mut self, checkpoint: &Self::Checkpoint) {
self.input.reset(&checkpoint.inner);
}
fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.input.trace(f)
}
}
impl<I, E> Location for Recoverable<I, E>
where
I: Location,
I: Stream,
{
#[inline(always)]
fn previous_token_end(&self) -> usize {
self.input.previous_token_end()
}
#[inline(always)]
fn current_token_start(&self) -> usize {
self.input.current_token_start()
}
}
impl<I, E, R> Recover<E> for Recoverable<I, R>
where
I: Stream,
R: FromRecoverableError<Self, E>,
R: core::fmt::Debug,
E: crate::error::ParserError<Self>,
{
fn record_err(
&mut self,
token_start: &Self::Checkpoint,
err_start: &Self::Checkpoint,
err: E,
) -> Result<(), E> {
if self.is_recoverable {
if err.is_incomplete() {
Err(err)
} else {
self.errors
.push(R::from_recoverable_error(token_start, err_start, self, err));
Ok(())
}
} else {
Err(err)
}
}
/// Report whether the [`Stream`] can save off errors for recovery
#[inline(always)]
fn is_recovery_supported() -> bool {
true
}
}
impl<I, E> StreamIsPartial for Recoverable<I, E>
where
I: StreamIsPartial,
I: Stream,
{
type PartialState = I::PartialState;
#[inline]
fn complete(&mut self) -> Self::PartialState {
self.input.complete()
}
#[inline]
fn restore_partial(&mut self, state: Self::PartialState) {
self.input.restore_partial(state);
}
#[inline(always)]
fn is_partial_supported() -> bool {
I::is_partial_supported()
}
#[inline(always)]
fn is_partial(&self) -> bool {
self.input.is_partial()
}
}
impl<I, E> Offset for Recoverable<I, E>
where
I: Stream,
E: core::fmt::Debug,
{
#[inline(always)]
fn offset_from(&self, other: &Self) -> usize {
self.offset_from(&other.checkpoint())
}
}
impl<I, E> Offset<<Recoverable<I, E> as Stream>::Checkpoint> for Recoverable<I, E>
where
I: Stream,
E: core::fmt::Debug,
{
#[inline(always)]
fn offset_from(&self, other: &<Recoverable<I, E> as Stream>::Checkpoint) -> usize {
self.checkpoint().offset_from(other)
}
}
impl<I, E> AsBytes for Recoverable<I, E>
where
I: Stream,
I: AsBytes,
{
#[inline(always)]
fn as_bytes(&self) -> &[u8] {
self.input.as_bytes()
}
}
impl<I, E> AsBStr for Recoverable<I, E>
where
I: Stream,
I: AsBStr,
{
#[inline(always)]
fn as_bstr(&self) -> &[u8] {
self.input.as_bstr()
}
}
impl<I, E, U> Compare<U> for Recoverable<I, E>
where
I: Stream,
I: Compare<U>,
{
#[inline(always)]
fn compare(&self, other: U) -> CompareResult {
self.input.compare(other)
}
}
impl<I, E, T> FindSlice<T> for Recoverable<I, E>
where
I: Stream,
I: FindSlice<T>,
{
#[inline(always)]
fn find_slice(&self, substr: T) -> Option<core::ops::Range<usize>> {
self.input.find_slice(substr)
}
}
impl<I, E> UpdateSlice for Recoverable<I, E>
where
I: Stream,
I: UpdateSlice,
E: core::fmt::Debug,
{
#[inline(always)]
fn update_slice(mut self, inner: Self::Slice) -> Self {
self.input = I::update_slice(self.input, inner);
self
}
}
+308
View File
@@ -0,0 +1,308 @@
use crate::stream::AsBStr;
use crate::stream::AsBytes;
use crate::stream::Checkpoint;
use crate::stream::Compare;
use crate::stream::CompareResult;
use crate::stream::FindSlice;
use crate::stream::Location;
use crate::stream::Needed;
use crate::stream::Offset;
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
use crate::stream::Recover;
use crate::stream::SliceLen;
use crate::stream::Stream;
use crate::stream::StreamIsPartial;
use crate::stream::UpdateSlice;
/// Thread global state through your parsers
///
/// Use cases
/// - Recursion checks
/// - Error recovery
/// - Debugging
///
/// # Example
///
/// ```
/// # #[cfg(feature = "ascii")] {
/// # use std::cell::Cell;
/// # use winnow::prelude::*;
/// # use winnow::stream::Stateful;
/// # use winnow::ascii::alpha1;
/// # type Error = ();
///
/// #[derive(Debug)]
/// struct State<'s>(&'s mut u32);
///
/// impl<'s> State<'s> {
/// fn count(&mut self) {
/// *self.0 += 1;
/// }
/// }
///
/// type Stream<'is> = Stateful<&'is str, State<'is>>;
///
/// fn word<'s>(i: &mut Stream<'s>) -> ModalResult<&'s str> {
/// i.state.count();
/// alpha1.parse_next(i)
/// }
///
/// let data = "Hello";
/// let mut state = 0;
/// let input = Stream { input: data, state: State(&mut state) };
/// let output = word.parse(input).unwrap();
/// assert_eq!(state, 1);
/// # }
/// ```
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[doc(alias = "LocatingSliceSpan")]
pub struct Stateful<I, S> {
/// Inner input being wrapped in state
pub input: I,
/// User-provided state
pub state: S,
}
impl<I, S> AsRef<I> for Stateful<I, S> {
#[inline(always)]
fn as_ref(&self) -> &I {
&self.input
}
}
impl<I, S> core::ops::Deref for Stateful<I, S> {
type Target = I;
#[inline(always)]
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<I: core::fmt::Display, S> core::fmt::Display for Stateful<I, S> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.input.fmt(f)
}
}
impl<I, S> SliceLen for Stateful<I, S>
where
I: SliceLen,
{
#[inline(always)]
fn slice_len(&self) -> usize {
self.input.slice_len()
}
}
impl<I: Stream, S: core::fmt::Debug> Stream for Stateful<I, S> {
type Token = <I as Stream>::Token;
type Slice = <I as Stream>::Slice;
type IterOffsets = <I as Stream>::IterOffsets;
type Checkpoint = Checkpoint<I::Checkpoint, Self>;
#[inline(always)]
fn iter_offsets(&self) -> Self::IterOffsets {
self.input.iter_offsets()
}
#[inline(always)]
fn eof_offset(&self) -> usize {
self.input.eof_offset()
}
#[inline(always)]
fn next_token(&mut self) -> Option<Self::Token> {
self.input.next_token()
}
#[inline(always)]
fn peek_token(&self) -> Option<Self::Token> {
self.input.peek_token()
}
#[inline(always)]
fn offset_for<P>(&self, predicate: P) -> Option<usize>
where
P: Fn(Self::Token) -> bool,
{
self.input.offset_for(predicate)
}
#[inline(always)]
fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
self.input.offset_at(tokens)
}
#[inline(always)]
fn next_slice(&mut self, offset: usize) -> Self::Slice {
self.input.next_slice(offset)
}
#[inline(always)]
unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice {
// SAFETY: Passing up invariants
unsafe { self.input.next_slice_unchecked(offset) }
}
#[inline(always)]
fn peek_slice(&self, offset: usize) -> Self::Slice {
self.input.peek_slice(offset)
}
#[inline(always)]
unsafe fn peek_slice_unchecked(&self, offset: usize) -> Self::Slice {
// SAFETY: Passing up invariants
unsafe { self.input.peek_slice_unchecked(offset) }
}
#[inline(always)]
fn checkpoint(&self) -> Self::Checkpoint {
Checkpoint::<_, Self>::new(self.input.checkpoint())
}
#[inline(always)]
fn reset(&mut self, checkpoint: &Self::Checkpoint) {
self.input.reset(&checkpoint.inner);
}
fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.input.trace(f)
}
}
impl<I, S> Location for Stateful<I, S>
where
I: Location,
{
#[inline(always)]
fn previous_token_end(&self) -> usize {
self.input.previous_token_end()
}
#[inline(always)]
fn current_token_start(&self) -> usize {
self.input.current_token_start()
}
}
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
impl<I, E, S> Recover<E> for Stateful<I, S>
where
I: Recover<E>,
I: Stream,
S: Clone + core::fmt::Debug,
{
#[inline(always)]
fn record_err(
&mut self,
_token_start: &Self::Checkpoint,
_err_start: &Self::Checkpoint,
err: E,
) -> Result<(), E> {
Err(err)
}
/// Report whether the [`Stream`] can save off errors for recovery
#[inline(always)]
fn is_recovery_supported() -> bool {
false
}
}
impl<I, S> StreamIsPartial for Stateful<I, S>
where
I: StreamIsPartial,
{
type PartialState = I::PartialState;
#[inline]
fn complete(&mut self) -> Self::PartialState {
self.input.complete()
}
#[inline]
fn restore_partial(&mut self, state: Self::PartialState) {
self.input.restore_partial(state);
}
#[inline(always)]
fn is_partial_supported() -> bool {
I::is_partial_supported()
}
#[inline(always)]
fn is_partial(&self) -> bool {
self.input.is_partial()
}
}
impl<I, S> Offset for Stateful<I, S>
where
I: Stream,
S: Clone + core::fmt::Debug,
{
#[inline(always)]
fn offset_from(&self, start: &Self) -> usize {
self.offset_from(&start.checkpoint())
}
}
impl<I, S> Offset<<Stateful<I, S> as Stream>::Checkpoint> for Stateful<I, S>
where
I: Stream,
S: core::fmt::Debug,
{
#[inline(always)]
fn offset_from(&self, other: &<Stateful<I, S> as Stream>::Checkpoint) -> usize {
self.checkpoint().offset_from(other)
}
}
impl<I, S> AsBytes for Stateful<I, S>
where
I: AsBytes,
{
#[inline(always)]
fn as_bytes(&self) -> &[u8] {
self.input.as_bytes()
}
}
impl<I, S> AsBStr for Stateful<I, S>
where
I: AsBStr,
{
#[inline(always)]
fn as_bstr(&self) -> &[u8] {
self.input.as_bstr()
}
}
impl<I, S, U> Compare<U> for Stateful<I, S>
where
I: Compare<U>,
{
#[inline(always)]
fn compare(&self, other: U) -> CompareResult {
self.input.compare(other)
}
}
impl<I, S, T> FindSlice<T> for Stateful<I, S>
where
I: FindSlice<T>,
{
#[inline(always)]
fn find_slice(&self, substr: T) -> Option<core::ops::Range<usize>> {
self.input.find_slice(substr)
}
}
impl<I, S> UpdateSlice for Stateful<I, S>
where
I: UpdateSlice,
S: Clone + core::fmt::Debug,
{
#[inline(always)]
fn update_slice(mut self, inner: Self::Slice) -> Self {
self.input = I::update_slice(self.input, inner);
self
}
}
+171
View File
@@ -0,0 +1,171 @@
#![cfg(feature = "parser")]
use crate::error::ErrMode;
use crate::error::ErrMode::Backtrack;
use crate::error::InputError;
use crate::token::literal;
use crate::{
combinator::{separated, separated_pair},
ModalResult, Parser,
};
use super::*;
#[cfg(feature = "std")]
#[test]
fn test_fxhashmap_compiles() {
let input = "a=b";
fn pair(i: &mut &str) -> ModalResult<(char, char)> {
let out = separated_pair('a', '=', 'b').parse_next(i)?;
Ok(out)
}
let _: rustc_hash::FxHashMap<char, char> = separated(0.., pair, ',').parse(input).unwrap();
}
#[test]
fn test_offset_u8() {
let s = b"abcd123";
let a = &s[..];
let b = &a[2..];
let c = &a[..4];
let d = &a[3..5];
assert_eq!(b.offset_from(&a), 2);
assert_eq!(c.offset_from(&a), 0);
assert_eq!(d.offset_from(&a), 3);
}
#[test]
fn test_offset_str() {
let a = "abcřèÂßÇd123";
let b = &a[7..];
let c = &a[..5];
let d = &a[5..9];
assert_eq!(b.offset_from(&a), 7);
assert_eq!(c.offset_from(&a), 0);
assert_eq!(d.offset_from(&a), 5);
}
#[test]
fn test_partial_complete() {
let mut i = Partial::new(&b""[..]);
assert!(Partial::<&[u8]>::is_partial_supported());
assert!(i.is_partial(), "incomplete by default");
let incomplete_state = i.complete();
assert!(!i.is_partial(), "the stream should be marked as complete");
i.restore_partial(incomplete_state);
assert!(i.is_partial(), "incomplete stream state should be restored");
}
#[test]
fn test_custom_slice() {
type Token = usize;
type TokenSlice<'i> = &'i [Token];
let mut tokens: TokenSlice<'_> = &[1, 2, 3, 4];
let input = &mut tokens;
let start = input.checkpoint();
let _ = input.next_token();
let _ = input.next_token();
let offset = input.offset_from(&start);
assert_eq!(offset, 2);
}
#[test]
fn test_literal_support_char() {
assert_eq!(
literal::<_, _, ErrMode<InputError<_>>>('π').parse_peek("π"),
Ok(("", "π"))
);
assert_eq!(
literal::<_, _, ErrMode<InputError<_>>>('π').parse_peek("π3.14"),
Ok(("3.14", "π"))
);
assert_eq!(
literal::<_, _, ErrMode<InputError<_>>>("π").parse_peek("π3.14"),
Ok(("3.14", "π"))
);
assert_eq!(
literal::<_, _, ErrMode<InputError<_>>>('-').parse_peek("π"),
Err(Backtrack(InputError::at("π")))
);
assert_eq!(
literal::<_, Partial<&[u8]>, ErrMode<InputError<_>>>('π')
.parse_peek(Partial::new(b"\xCF\x80")),
Ok((Partial::new(Default::default()), "π".as_bytes()))
);
assert_eq!(
literal::<_, &[u8], ErrMode<InputError<_>>>('π').parse_peek(b"\xCF\x80"),
Ok((Default::default(), "π".as_bytes()))
);
assert_eq!(
literal::<_, Partial<&[u8]>, ErrMode<InputError<_>>>('π')
.parse_peek(Partial::new(b"\xCF\x803.14")),
Ok((Partial::new(&b"3.14"[..]), "π".as_bytes()))
);
assert_eq!(
literal::<_, &[u8], ErrMode<InputError<_>>>('π').parse_peek(b"\xCF\x80"),
Ok((Default::default(), "π".as_bytes()))
);
assert_eq!(
literal::<_, &[u8], ErrMode<InputError<_>>>('π').parse_peek(b"\xCF\x803.14"),
Ok((&b"3.14"[..], "π".as_bytes()))
);
assert_eq!(
literal::<_, &[u8], ErrMode<InputError<_>>>('-').parse_peek(b"\xCF\x80"),
Err(Backtrack(InputError::at(&b"\xCF\x80"[..],)))
);
}
#[test]
fn tokenslice_location() {
#[derive(Clone, Debug)]
struct Token {
span: core::ops::Range<usize>,
}
impl Location for Token {
#[inline(always)]
fn previous_token_end(&self) -> usize {
self.span.end
}
#[inline(always)]
fn current_token_start(&self) -> usize {
self.span.start
}
}
let input = [
Token { span: 1..9 },
Token { span: 11..19 },
Token { span: 21..29 },
];
let mut input = TokenSlice::new(&input);
assert_eq!(input.previous_token_end(), 1);
// Parse operation
assert_eq!(input.current_token_start(), 1);
let _ = input.next_token();
assert_eq!(input.previous_token_end(), 9);
// Parse operation
assert_eq!(input.current_token_start(), 11);
let _ = input.next_token();
assert_eq!(input.previous_token_end(), 19);
// Parse operation
assert_eq!(input.current_token_start(), 21);
let _ = input.next_token();
assert_eq!(input.previous_token_end(), 29);
assert_eq!(input.current_token_start(), 29);
}
+308
View File
@@ -0,0 +1,308 @@
use crate::stream::Checkpoint;
use crate::stream::Compare;
use crate::stream::CompareResult;
use crate::stream::Location;
use crate::stream::Needed;
use crate::stream::Offset;
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
use crate::stream::Recover;
use crate::stream::SliceLen;
use crate::stream::Stream;
use crate::stream::StreamIsPartial;
use crate::stream::UpdateSlice;
use core::iter::Enumerate;
use core::slice::Iter;
/// Specialized input for parsing lexed tokens
///
/// Helpful impls
/// - Any `PartialEq` type (e.g. a `TokenKind` or `&str`) can be used with
/// [`literal`][crate::token::literal]
/// - A `PartialEq` for `&str` allows for using `&str` as a parser for tokens
/// - [`ContainsToken`][crate::stream::ContainsToken] for `T` to for parsing with token sets
/// - [`Location`] for `T` to extract spans from tokens
///
/// See also [Lexing and Parsing][crate::_topic::lexing].
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct TokenSlice<'t, T> {
initial: &'t [T],
input: &'t [T],
}
impl<'t, T> TokenSlice<'t, T>
where
T: core::fmt::Debug + Clone,
{
/// Make a stream to parse tokens
#[inline]
pub fn new(input: &'t [T]) -> Self {
Self {
initial: input,
input,
}
}
/// Reset the stream to the start
///
/// This is useful for formats that encode a graph with addresses relative to the start of the
/// input.
#[doc(alias = "fseek")]
#[inline]
pub fn reset_to_start(&mut self) {
let start = self.initial.checkpoint();
self.input.reset(&start);
}
/// Iterate over consumed tokens starting with the last emitted
///
/// This is intended to help build up appropriate context when reporting errors.
#[inline]
pub fn previous_tokens(&self) -> impl Iterator<Item = &'t T> {
let offset = self.input.offset_from(&self.initial);
self.initial[0..offset].iter().rev()
}
}
/// Track locations by implementing [`Location`] on the Token.
impl<T> TokenSlice<'_, T>
where
T: Location,
{
#[inline(always)]
fn previous_token_end(&self) -> Option<usize> {
let index = self.input.offset_from(&self.initial);
index
.checked_sub(1)
.map(|i| self.initial[i].previous_token_end())
}
#[inline(always)]
fn current_token_start(&self) -> Option<usize> {
self.input.first().map(|t| t.current_token_start())
}
}
impl<T> Default for TokenSlice<'_, T>
where
T: core::fmt::Debug + Clone,
{
fn default() -> Self {
Self::new(&[])
}
}
impl<T> core::ops::Deref for TokenSlice<'_, T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.input
}
}
impl<T: core::fmt::Debug> core::fmt::Debug for TokenSlice<'_, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.input.fmt(f)
}
}
impl<T> SliceLen for TokenSlice<'_, T> {
#[inline(always)]
fn slice_len(&self) -> usize {
self.input.slice_len()
}
}
impl<'t, T> Stream for TokenSlice<'t, T>
where
T: core::fmt::Debug + Clone,
{
type Token = &'t T;
type Slice = &'t [T];
type IterOffsets = Enumerate<Iter<'t, T>>;
type Checkpoint = Checkpoint<&'t [T], Self>;
#[inline(always)]
fn iter_offsets(&self) -> Self::IterOffsets {
self.input.iter().enumerate()
}
#[inline(always)]
fn eof_offset(&self) -> usize {
self.input.eof_offset()
}
#[inline(always)]
fn next_token(&mut self) -> Option<Self::Token> {
let (token, next) = self.input.split_first()?;
self.input = next;
Some(token)
}
#[inline(always)]
fn peek_token(&self) -> Option<Self::Token> {
self.input.first()
}
#[inline(always)]
fn offset_for<P>(&self, predicate: P) -> Option<usize>
where
P: Fn(Self::Token) -> bool,
{
self.input.iter().position(predicate)
}
#[inline(always)]
fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
self.input.offset_at(tokens)
}
#[inline(always)]
fn next_slice(&mut self, offset: usize) -> Self::Slice {
self.input.next_slice(offset)
}
#[inline(always)]
unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice {
// SAFETY: Passing up invariants
unsafe { self.input.next_slice_unchecked(offset) }
}
#[inline(always)]
fn peek_slice(&self, offset: usize) -> Self::Slice {
self.input.peek_slice(offset)
}
#[inline(always)]
unsafe fn peek_slice_unchecked(&self, offset: usize) -> Self::Slice {
// SAFETY: Passing up invariants
unsafe { self.input.peek_slice_unchecked(offset) }
}
#[inline(always)]
fn checkpoint(&self) -> Self::Checkpoint {
Checkpoint::<_, Self>::new(self.input)
}
#[inline(always)]
fn reset(&mut self, checkpoint: &Self::Checkpoint) {
self.input = checkpoint.inner;
}
fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.input.trace(f)
}
}
impl<T> Location for TokenSlice<'_, T>
where
T: Location,
{
#[inline(always)]
fn previous_token_end(&self) -> usize {
self.previous_token_end()
.or_else(|| self.current_token_start())
.unwrap_or(0)
}
#[inline(always)]
fn current_token_start(&self) -> usize {
self.current_token_start()
.or_else(|| self.previous_token_end())
.unwrap_or(0)
}
}
#[cfg(feature = "unstable-recover")]
#[cfg(feature = "std")]
impl<T, E> Recover<E> for TokenSlice<'_, T>
where
T: core::fmt::Debug + Clone,
{
#[inline(always)]
fn record_err(
&mut self,
_token_start: &Self::Checkpoint,
_err_start: &Self::Checkpoint,
err: E,
) -> Result<(), E> {
Err(err)
}
/// Report whether the [`Stream`] can save off errors for recovery
#[inline(always)]
fn is_recovery_supported() -> bool {
false
}
}
impl<'t, T> StreamIsPartial for TokenSlice<'t, T>
where
T: core::fmt::Debug + Clone,
{
type PartialState = <&'t [T] as StreamIsPartial>::PartialState;
#[inline]
fn complete(&mut self) -> Self::PartialState {
#![allow(clippy::semicolon_if_nothing_returned)]
self.input.complete()
}
#[inline]
fn restore_partial(&mut self, state: Self::PartialState) {
self.input.restore_partial(state);
}
#[inline(always)]
fn is_partial_supported() -> bool {
<&[T] as StreamIsPartial>::is_partial_supported()
}
#[inline(always)]
fn is_partial(&self) -> bool {
self.input.is_partial()
}
}
impl<T> Offset for TokenSlice<'_, T>
where
T: core::fmt::Debug + Clone,
{
#[inline(always)]
fn offset_from(&self, other: &Self) -> usize {
self.offset_from(&other.checkpoint())
}
}
impl<T> Offset<<TokenSlice<'_, T> as Stream>::Checkpoint> for TokenSlice<'_, T>
where
T: core::fmt::Debug + Clone,
{
#[inline(always)]
fn offset_from(&self, other: &<TokenSlice<'_, T> as Stream>::Checkpoint) -> usize {
self.checkpoint().offset_from(other)
}
}
impl<T, O> Compare<O> for TokenSlice<'_, T>
where
T: PartialEq<O> + Eq,
{
#[inline]
fn compare(&self, t: O) -> CompareResult {
if let Some(token) = self.first() {
if *token == t {
CompareResult::Ok(1)
} else {
CompareResult::Error
}
} else {
CompareResult::Incomplete
}
}
}
impl<T> UpdateSlice for TokenSlice<'_, T>
where
T: core::fmt::Debug + Clone,
{
#[inline(always)]
fn update_slice(mut self, inner: Self::Slice) -> Self {
self.input = <&[T] as UpdateSlice>::update_slice(self.input, inner);
self
}
}
+1096
View File
File diff suppressed because it is too large. Load diff
+2895
View File
File diff suppressed because it is too large. Load diff