|
56 | 56 | //!
|
57 | 57 | //! ## Differences
|
58 | 58 | //!
|
| 59 | +//! These are key differences to help Nom users adapt to writing parsers with Winnow. |
| 60 | +//! |
59 | 61 | //! ### Renamed APIs
|
60 | 62 | //!
|
61 | 63 | //! Names have changed for consistency or clarity.
|
|
68 | 70 | //!
|
69 | 71 | //! `nom` v8 back-propagates how you will use a parser to parser functions using a language feature
|
70 | 72 | //! called GATs.
|
71 |
| -//! Winnow avoids this. |
72 |
| -//! |
73 |
| -//! Benefits for avoiding GATs: |
74 |
| -//! - Predictable performance as writing; idiomatic `fn(&mut I) -> Result<O>` parser sever the |
75 |
| -//! back-propagation from GATs. |
76 |
| -//! - No "eek out X% perf improvement" pressure to contort a parser to be written declaratively |
77 |
| -//! that is better written imperatively |
78 |
| -//! - Built-in parsers serve are simple examples of idiomatic parsers |
79 |
| -//! - Faster build times and smaller binary size as parsers only need to be generated for one mode, not upto 6 |
80 |
| -//! |
81 |
| -//! Downsides |
82 |
| -//! - Performance |
| 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 |
83 | 95 | //!
|
84 | 96 | //! #### Partial/streaming parsers
|
85 | 97 | //!
|
86 | 98 | //! `nom` v8 back-propagates whether `Parser::parse_complete` was used to select `complete`
|
87 | 99 | //! parsers.
|
88 | 100 | //! Previously, users had ensure consistently using a parser from the `streaming` or `complete` module.
|
89 |
| -//! Instead, we tag the input type (`I`) by wrapping it in [`Partial<I>`] and parsers will adjust |
| 101 | +//! |
| 102 | +//! Instead, you tag the input type (`I`) by wrapping it in [`Partial<I>`] and parsers will adjust |
90 | 103 | //! their behavior accordingly.
|
91 | 104 | //! See [partial] special topic.
|
92 | 105 | //!
|
|
95 | 108 | //! `nom` v8 back-propagates whether an Output will be used and skips its creation.
|
96 | 109 | //! For example, `value(Null, many0(_))` will avoid creating and pushing to a `Vec`.
|
97 | 110 | //! Previously, users had to select `count_many0` over `many0` to avoid creating a `Vec`.
|
| 111 | +//! |
98 | 112 | //! Instead, `repeat` returns an `impl Accumulate<T>` which could be a `Vec`, a `usize` for `count`
|
99 | 113 | //! variants, or `()` to do no extra work.
|
100 | 114 | //!
|
|
103 | 117 | //! Under the hood, [`alt`] is an `if-not-error-else` ladder, see [`_tutorial::chapter_3`].
|
104 | 118 | //! nom v8 back-propagates whether the error will be discarded and avoids any expensive work done
|
105 | 119 | //! for rich error messages.
|
| 120 | +//! |
106 | 121 | //! Instead, [`ContextError`] and other changes have made it so errors have very little overhead.
|
107 | 122 | //! [`dispatch!`] can also be used in some situations to avoid `alt`s overhead.
|
108 | 123 | //!
|
|
111 | 126 | //! In `nom`, parsers like [`take_while`] parse a [`Stream`] and return a [`Stream`].
|
112 | 127 | //! When wrapping the input, like with [`Stateful`],
|
113 | 128 | //! you have to unwrap the input to integrate it in your application,
|
114 |
| -//! requires [`Stream`] to be `Clone` (which requires `RefCell` for mutable external state), |
115 |
| -//! and is then expensive to `clone()`. |
| 129 | +//! and it requires [`Stream`] to be `Clone` |
| 130 | +//! (which requires `RefCell` for mutable external state and can be expensive). |
| 131 | +//! |
116 | 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`]. |
117 | 135 | //!
|
118 | 136 | //! ### `&mut I`
|
119 | 137 | //!
|
120 | 138 | //! `winnow` switched from pure-function parser (`Fn(I) -> (I, O)` to `Fn(&mut I) -> O`).
|
121 | 139 | //! On error, `i` is left pointing at where the error happened.
|
122 | 140 | //!
|
123 |
| -//! Benefits: |
| 141 | +//! Benefits of `Fn(&mut I) -> O`: |
124 | 142 | //! - Cleaner code: Removes need to pass `i` everywhere and makes changes to `i` more explicit
|
125 | 143 | //! - Correctness: No forgetting to chain `i` through a parser
|
126 | 144 | //! - Flexibility: `I` does not need to be `Copy` or even `Clone`. For example, [`Stateful`] can use `&mut S` instead of `RefCell<S>`.
|
|
130 | 148 | //! to the error.
|
131 | 149 | //! See also [#72](https://github.com/winnow-rs/winnow/issues/72).
|
132 | 150 | //!
|
133 |
| -//! Downsides: |
134 |
| -//! - When returning a slice, you have to add a lifetime (`fn foo<'i>(i: &mut &i str) -> ModalResult<&i str>`) |
135 |
| -//! - When writing a closure, you need to annotate the type (`|i: &mut _|`, at least the full type isn't needed) |
| 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)* |
136 | 185 | //!
|
137 | 186 | //! To save and restore from intermediate states, [`Stream::checkpoint`] and [`Stream::reset`] can help:
|
138 | 187 | //! ```rust
|
|
0 commit comments