Skip to main content

winnow/combinator/
branch.rs

1use crate::combinator::trace;
2use crate::error::ParserError;
3use crate::stream::Stream;
4use crate::{Parser, Result};
5
6#[doc(inline)]
7pub use crate::dispatch;
8
9/// Helper trait for the [`alt()`] combinator.
10///
11/// This trait is implemented for tuples of up to 21 elements
12pub trait Alt<I, O, E> {
13    /// Tests each parser in the tuple and returns the result of the first one that succeeds
14    fn choice(&mut self, input: &mut I) -> Result<O, E>;
15}
16
17/// Pick the first successful parser
18///
19/// To stop on an error, rather than trying further cases, see
20/// [`cut_err`][crate::combinator::cut_err] ([example][crate::_tutorial::chapter_7]).
21///
22/// For tight control over the error when no match is found, add a final case using [`fail`][crate::combinator::fail].
23/// Alternatively, with a [custom error type][crate::_topic::error], it is possible to track all
24/// errors or return the error of the parser that went the farthest in the input data.
25///
26/// When the alternative cases have unique prefixes, [`dispatch`] can offer better performance.
27///
28/// # Example
29///
30/// ```rust
31/// # #[cfg(feature = "ascii")] {
32/// # use winnow::{error::ErrMode, error::Needed};
33/// # use winnow::prelude::*;
34/// use winnow::ascii::{alpha1, digit1};
35/// use winnow::combinator::alt;
36/// fn parser<'i>(input: &mut &'i str) -> ModalResult<&'i str> {
37///   alt((alpha1, digit1)).parse_next(input)
38/// };
39///
40/// // the first parser, alpha1, takes the input
41/// assert_eq!(parser.parse_peek("abc"), Ok(("", "abc")));
42///
43/// // the first parser returns an error, so alt tries the second one
44/// assert_eq!(parser.parse_peek("123456"), Ok(("", "123456")));
45///
46/// // both parsers failed, and with the default error type, alt will return the last error
47/// assert!(parser.parse_peek(" ").is_err());
48/// # }
49/// ```
50#[doc(alias = "choice")]
51#[inline(always)]
52pub fn alt<Input: Stream, Output, Error, Alternatives>(
53    mut alternatives: Alternatives,
54) -> impl Parser<Input, Output, Error>
55where
56    Alternatives: Alt<Input, Output, Error>,
57    Error: ParserError<Input>,
58{
59    trace("alt", move |i: &mut Input| alternatives.choice(i))
60}
61
62impl<const N: usize, I: Stream, O, E: ParserError<I>, P: Parser<I, O, E>> Alt<I, O, E> for [P; N] {
63    fn choice(&mut self, input: &mut I) -> Result<O, E> {
64        let mut error: Option<E> = None;
65
66        let start = input.checkpoint();
67        for branch in self {
68            input.reset(&start);
69            match branch.parse_next(input) {
70                Err(e) if e.is_backtrack() => {
71                    error = match error {
72                        Some(error) => Some(error.or(e)),
73                        None => Some(e),
74                    };
75                }
76                res => return res,
77            }
78        }
79
80        match error {
81            Some(e) => Err(e.append(input, &start)),
82            None => Err(ParserError::assert(
83                input,
84                "`alt` needs at least one parser",
85            )),
86        }
87    }
88}
89
90impl<I: Stream, O, E: ParserError<I>, P: Parser<I, O, E>> Alt<I, O, E> for &mut [P] {
91    fn choice(&mut self, input: &mut I) -> Result<O, E> {
92        let mut error: Option<E> = None;
93
94        let start = input.checkpoint();
95        for branch in self.iter_mut() {
96            input.reset(&start);
97            match branch.parse_next(input) {
98                Err(e) if e.is_backtrack() => {
99                    error = match error {
100                        Some(error) => Some(error.or(e)),
101                        None => Some(e),
102                    };
103                }
104                res => return res,
105            }
106        }
107
108        match error {
109            Some(e) => Err(e.append(input, &start)),
110            None => Err(ParserError::assert(
111                input,
112                "`alt` needs at least one parser",
113            )),
114        }
115    }
116}
117
118macro_rules! alt_trait(
119  ($first:ident $second:ident $($id: ident)+) => (
120    alt_trait!(__impl $first $second; $($id)+);
121  );
122  (__impl $($current:ident)*; $head:ident $($id: ident)+) => (
123    alt_trait_impl!($($current)*);
124
125    alt_trait!(__impl $($current)* $head; $($id)+);
126  );
127  (__impl $($current:ident)*; $head:ident) => (
128    alt_trait_impl!($($current)*);
129    alt_trait_impl!($($current)* $head);
130  );
131);
132
133macro_rules! alt_trait_impl(
134  ($($id:ident)+) => (
135    impl<
136      I: Stream, Output, Error: ParserError<I>,
137      $($id: Parser<I, Output, Error>),+
138    > Alt<I, Output, Error> for ( $($id),+ ) {
139
140      fn choice(&mut self, input: &mut I) -> Result<Output, Error> {
141        let start = input.checkpoint();
142        match self.0.parse_next(input) {
143          Err(e) if e.is_backtrack() => alt_trait_inner!(1, self, input, start, e, $($id)+),
144          res => res,
145        }
146      }
147    }
148  );
149);
150
151macro_rules! succ (
152    (1, $submac:ident ! ($($rest:tt)*)) => ($submac!(2, $($rest)*));
153    (2, $submac:ident ! ($($rest:tt)*)) => ($submac!(3, $($rest)*));
154    (3, $submac:ident ! ($($rest:tt)*)) => ($submac!(4, $($rest)*));
155    (4, $submac:ident ! ($($rest:tt)*)) => ($submac!(5, $($rest)*));
156    (5, $submac:ident ! ($($rest:tt)*)) => ($submac!(6, $($rest)*));
157    (6, $submac:ident ! ($($rest:tt)*)) => ($submac!(7, $($rest)*));
158    (7, $submac:ident ! ($($rest:tt)*)) => ($submac!(8, $($rest)*));
159    (8, $submac:ident ! ($($rest:tt)*)) => ($submac!(9, $($rest)*));
160);
161
162macro_rules! alt_trait_inner(
163    ($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident $($id:ident)+) => ({
164        $input.reset(&$start);
165        match $self.$it.parse_next($input) {
166            Err(e) if e.is_backtrack() => {
167                let err = $err.or(e);
168                succ!($it, alt_trait_inner!($self, $input, $start, err, $($id)+))
169            }
170            res => res,
171        }
172    });
173    ($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident) => ({
174        Err($err.append($input, &$start))
175    });
176);
177
178alt_trait!(Alt2 Alt3 Alt4 Alt5 Alt6 Alt7 Alt8 Alt9 Alt10);
179
180// Manually implement Alt for (A,), the 1-tuple type
181impl<I: Stream, O, E: ParserError<I>, A: Parser<I, O, E>> Alt<I, O, E> for (A,) {
182    fn choice(&mut self, input: &mut I) -> Result<O, E> {
183        self.0.parse_next(input)
184    }
185}