• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 (see [migration
36 //!     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::stream::Stream as _;
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) -> PResult<&'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) -> PResult<usize> {
211 //!     trace("foo", |i: &mut _| {
212 //!         // ...
213 //! #       Ok(0)
214 //!     }).parse_next(i)
215 //! }
216 //! ```
217 
218 #![allow(unused_imports)]
219 use crate::_topic::partial;
220 use crate::_tutorial;
221 use crate::combinator::alt;
222 use crate::combinator::dispatch;
223 use crate::error::ContextError;
224 use crate::error::ErrMode;
225 use crate::error::ModalResult;
226 use crate::stream::Accumulate;
227 use crate::stream::Partial;
228 use crate::stream::Stateful;
229 use crate::stream::Stream;
230 use crate::token::take_while;
231