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