Skip to main content

winnow/_topic/
nom.rs

1//! # For `nom` users
2//!
3//! ## Migrating from `nom`
4//!
5//! For comparisons with `nom`, see
6//! - [Why `winnow`][super::why]
7//! - [parse-rosetta-rs](https://github.com/rosetta-rs/parse-rosetta-rs/)
8//!
9//! What approach you take depends on the size and complexity of your parser.
10//! For small, simple parsers, its likely easiest to directly port from `nom`.
11//! When trying to look for the equivalent of a `nom` combinator, search in the docs for the name
12//! of the `nom` combinator.  It is expected that, where names diverge, a doc alias exists.
13//! See also the [List of combinators][crate::combinator].
14//!
15//! ### Complex migrations
16//!
17//! For larger parsers, it is likely best to take smaller steps
18//! - Easier to debug when something goes wrong
19//! - Deprecation messages will help assist through the process
20//!
21//! The workflow goes something like:
22//! 1. Run `cargo rm nom && cargo add winnow@0.3`
23//! 1. Ensure everything compiles and tests pass, ignoring deprecation messages (see [migration
24//!    notes](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md#nom-migration-guide))
25//! 1. Commit
26//! 1. Switch any `impl FnMut(I) -> IResult<I, O, E>` to `impl Parser<I, O, E>`
27//! 1. Resolve deprecation messages
28//! 1. Commit
29//! 1. Run `cargo add winnow@0.4`
30//! 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)
31//! 1. Commit
32//! 1. Resolve deprecation messages
33//! 1. Commit
34//! 1. Run `cargo add winnow@0.5`
35//! 1. Ensure everything compiles and tests pass, ignoring deprecation messages
36//!    (see [migration notes](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md#050---2023-07-13))
37//! 1. Commit
38//! 1. Resolve deprecation messages
39//! 1. Commit
40//!
41//! ### Examples
42//!
43//! For example migrations, see
44//! - [git-config-env](https://github.com/gitext-rs/git-config-env/pull/11) (nom to winnow 0.3)
45//! - [git-conventional](https://github.com/crate-ci/git-conventional/pull/37) (nom to winnow 0.3,
46//!   adds explicit tracing for easier debugging)
47//! - [typos](https://github.com/crate-ci/typos/pull/664) (nom to winnow 0.3)
48//! - [cargo-smart-release](https://github.com/Byron/gitoxide/pull/948) (gradual migration from nom
49//!   to winnow 0.5)
50//! - [gix-config](https://github.com/Byron/gitoxide/pull/951) (gradual migration from nom
51//!   to winnow 0.5)
52//! - [gix-protocol](https://github.com/Byron/gitoxide/pull/1009) (gradual migration from nom
53//!   to winnow 0.5)
54//! - [gitoxide](https://github.com/Byron/gitoxide/pull/956) (gradual migration from nom
55//!   to winnow 0.5)
56//!
57//! ## Differences
58//!
59//! These are key differences to help Nom users adapt to writing parsers with Winnow.
60//!
61//! ### Renamed APIs
62//!
63//! Names have changed for consistency or clarity.
64//!
65//! To find a parser you are looking for,
66//! - Search the docs for the `nom` parser
67//! - See the [List of combinators][crate::combinator]
68//!
69//! ### GATs
70//!
71//! `nom` v8 back-propagates how you will use a parser to parser functions using a language feature
72//! called GATs.
73//! Winnow has made the conscious choice not to use this feature, finding alternative ways of
74//! getting most of the benefits.
75//!
76//! Benefits of GATs:
77//! - Performance as the compiler is able to instantiate copies of a parser that are
78//!   better tailored to how it will be used, like discarding unused allocations for output or
79//!   errors.
80//!
81//! Benefits of not using GATs:
82//! - Predictable performance:
83//!   With GATs, seemingly innocuous changes like choosing to hand write a parser using idiomatic function parsers
84//!   (`fn(&mut I) -> Result<O>`) can cause surprising slow downs because these functions sever the back-propagation from GATs.
85//!   The causes of these slowdowns could be hard to identify by inspection or profiling.
86//! - No "eek out X% perf improvement" pressure to contort a parser
87//!   that is more easily written imperatively
88//!   to be written declaratively
89//!   so it can preserve the back-propagation from GATs.
90//! - Built-in parsers serve are can serve as examples to users of idiomatic function parsers
91//!   (`fn(&mut I) -> Result<O>`).
92//!   With GATs, built-in parsers tend to be complex implementations of traits.
93//! - Faster build times and smaller binary size as parsers only need to be generated for one mode,
94//!   not upto 8
95//!
96//! #### Partial/streaming parsers
97//!
98//! `nom` v8 back-propagates whether `Parser::parse_complete` was used to select `complete`
99//! parsers.
100//! Previously, users had ensure consistently using a parser from the `streaming` or `complete` module.
101//!
102//! Instead, you tag the input type (`I`) by wrapping it in [`Partial<I>`] and parsers will adjust
103//! their behavior accordingly.
104//! See [partial] special topic.
105//!
106//! #### Eliding Output
107//!
108//! `nom` v8 back-propagates whether an Output will be used and skips its creation.
109//! For example, `value(Null, many0(_))` will avoid creating and pushing to a `Vec`.
110//! Previously, users had to select `count_many0` over `many0` to avoid creating a `Vec`.
111//!
112//! Instead, `repeat` returns an `impl Accumulate<T>` which could be a `Vec`, a `usize` for `count`
113//! variants, or `()` to do no extra work.
114//!
115//! #### Eliding Backtracked Errors
116//!
117//! Under the hood, [`alt`] is an `if-not-error-else` ladder, see [`_tutorial::chapter_3`].
118//! nom v8 back-propagates whether the error will be discarded and avoids any expensive work done
119//! for rich error messages.
120//!
121//! Instead, [`ContextError`] and other changes have made it so errors have very little overhead.
122//! [`dispatch!`] can also be used in some situations to avoid `alt`s overhead.
123//!
124//! ### Parsers return [`Stream::Slice`], rather than [`Stream`]
125//!
126//! In `nom`, parsers like [`take_while`] parse a [`Stream`] and return a [`Stream`].
127//! When wrapping the input, like with [`Stateful`],
128//! you have to unwrap the input to integrate it in your application,
129//! and it requires [`Stream`] to be `Clone`
130//! (which requires `RefCell` for mutable external state and can be expensive).
131//!
132//! Instead, [`Stream::Slice`] was added to track the intended type for parsers to return.
133//! If you want to then parse the slice, you then need to take it and turn it back into a
134//! [`Stream`].
135//!
136//! ### `&mut I`
137//!
138//! `winnow` switched from pure-function parser (`Fn(I) -> (I, O)` to `Fn(&mut I) -> O`).
139//! On error, `i` is left pointing at where the error happened.
140//!
141//! Benefits of `Fn(&mut I) -> O`:
142//! - Cleaner code: Removes need to pass `i` everywhere and makes changes to `i` more explicit
143//! - Correctness: No forgetting to chain `i` through a parser
144//! - Flexibility: `I` does not need to be `Copy` or even `Clone`. For example, [`Stateful`] can use `&mut S` instead of `RefCell<S>`.
145//! - Performance: `Result::Ok` is smaller without `i`, reducing the risk that the output will be
146//!   returned on the stack, rather than the much faster CPU registers.
147//!   `Result::Err` can also be smaller because the error type does not need to carry `i` to point
148//!   to the error.
149//!   See also [#72](https://github.com/winnow-rs/winnow/issues/72).
150//!
151//! Benefits of `Fn(I) -> (I, O)`:
152//! - Pure functions can be easier to reason about
153//! - Less boilerplate in some situations (see below)
154//! - Less syntactic noise in some situations (see below)
155//!
156//! When returning a slice from the input, you have to add a lifetime:
157//! ```rust
158//! # use winnow::prelude::*;
159//! fn foo<'i>(i: &mut &'i str) -> ModalResult<&'i str> {
160//! #   Ok("")
161//!     // ...
162//! }
163//! ```
164//!
165//! When writing a closure, you need to annotate the type
166//! ```rust
167//! # use winnow::prelude::*;
168//! # use winnow::combinator::alt;
169//! # use winnow::error::ContextError;
170//! # let mut input = "";
171//! # fn foo<'i>() -> impl ModalParser<&'i str, &'i str, ContextError> {
172//! alt((
173//!     |i: &mut _| {
174//! #       Ok("")
175//!         // ...
176//!     },
177//!     |i: &mut _| {
178//! #       Ok("")
179//!         // ...
180//!     },
181//! ))
182//! # }
183//! ```
184//! *(at least the full type isn't needed)*
185//!
186//! To save and restore from intermediate states, [`Stream::checkpoint`] and [`Stream::reset`] can help:
187//! ```rust
188//! use winnow::prelude::*;
189//! # let mut i = "";
190//! # let i = &mut i;
191//!
192//! let start = i.checkpoint();
193//! // ...
194//! i.reset(&start);
195//! ```
196//!
197//! When the Output of a parser is a slice, you have to add a lifetime:
198//! ```rust
199//! # use winnow::prelude::*;
200//! fn foo<'i>(i: &mut &'i str) -> ModalResult<&'i str> {
201//!     // ...
202//! #   winnow::token::rest.parse_next(i)
203//! }
204//! ```
205//!
206//! When writing a closure, you need to annotate the type:
207//! ```rust
208//! # use winnow::prelude::*;
209//! # use winnow::combinator::trace;
210//! fn foo(i: &mut &str) -> ModalResult<usize> {
211//!     trace("foo", |i: &mut _| {
212//!         // ...
213//! #       Ok(0)
214//!     }).parse_next(i)
215//! }
216//! ```
217//!
218//! ### Optional [`ErrMode`]
219//!
220//! Called `Err` in `nom`, [`ErrMode`] is responsible for
221//! - Deciding whether to backtrack and try another branch in cases like `alt` or report back to
222//!   the error back to users
223//! - Tracking incomplete input on partial parsing
224//!
225//! As this isn't needed in every parser, it was made optional.  [`ModalResult`] is a convenience
226//! type for using [`ErrMode`].
227
228#![allow(unused_imports)]
229use crate::_topic::partial;
230use crate::_tutorial;
231use crate::combinator::alt;
232use crate::combinator::dispatch;
233use crate::error::ContextError;
234use crate::error::ErrMode;
235use crate::error::ModalResult;
236use crate::stream::Accumulate;
237use crate::stream::Partial;
238use crate::stream::Stateful;
239use crate::stream::Stream;
240use crate::token::take_while;