Repository: rust-bakery/nom (language: rust)

Issue:
Ergonomics of `alt()` taking tuple versus slice
Is there a reason `alt()` cannot take a slice of child parsers?

Instead, it takes a `List`, which is any of the 1- to 21-tuple implementations.

This is fine when the format for which you are trying to parse is known at compile-time/statically, but unusable when the format is only known at runtime.

For example, I'm writing a software-version bumper right now, and the user provides both their arbitrary format (e.g. `yyyy.patch`) and their current version (e.g. `2024.5`) as inputs to my package/program. First, I have a parser for the format string (which is static-defined), but then I need to create a new parser from its result for the actual version string, which is runtime-defined. Therefore, this second parser cannot be made from a tuple.

I have been able to work around this by continually building a parser up in a "reduce" fashion, something like

```rust
parsers.reduce(|acc, p| acc.or(p))
```

(I believe that applying pairwise `or()` calls has the same effect as `alt()`, right?) Nonetheless, it's cumbersome, especially with the type annotations, for a newer Rust user like me: plenty of `Box`, `dyn`, lifetimes, `Sized`, etc.

So, I wonder why there isn't a version of alt that could take in a dynamically sized slice of child parsers.

## Prerequisites

- Rust version: `rustc 1.77.0-nightly (e51e98dde 2023-12-31)`
- nom version : `7.1.3`
- nom compilation features used: default ones





Discussion / hints:
`alt` for arrays was added in #1556 but that was after the start of 8.0.  I've not seen any indication of how much remaining work Geal intends for the 8.0 release.
Hmm, that is still just for a fixed size array and it sounds like you don't know the size of your array until runtime, right?

Fix patch:
diff --git a/src/branch/mod.rs b/src/branch/mod.rs
index 80c3d1f84..20205f595 100644
--- a/src/branch/mod.rs
+++ b/src/branch/mod.rs
@@ -212,6 +212,44 @@ impl<
   }
 }
 
+impl<
+    Input: Clone,
+    Output,
+    Error: ParseError<Input>,
+    A: Parser<Input, Output = Output, Error = Error>,
+  > Parser<Input> for Choice<&mut [A]>
+{
+  type Output = Output;
+  type Error = Error;
+
+  #[inline]
+  fn process<OM: crate::OutputMode>(
+    &mut self,
+    input: Input,
+  ) -> crate::PResult<OM, Input, Self::Output, Self::Error> {
+    let mut error = None;
+
+    for branch in self.parser.iter_mut() {
+      match branch.process::<OM>(input.clone()) {
+        Err(Err::Error(e)) => match error {
+          None => error = Some(e),
+          Some(err) => error = Some(OM::Error::combine(err, e, |e1, e2| e1.or(e2))),
+        },
+        res => return res,
+      }
+    }
+
+    match error {
+      Some(e) => Err(Err::Error(OM::Error::map(e, |err| {
+        Error::append(input, ErrorKind::Alt, err)
+      }))),
+      None => Err(Err::Error(OM::Error::bind(|| {
+        Error::from_error_kind(input, ErrorKind::Alt)
+      }))),
+    }
+  }
+}
+
 macro_rules! permutation_trait(
   (
     $name1:ident $ty1:ident $item1:ident


Test patch:
diff --git a/src/branch/tests.rs b/src/branch/tests.rs
index b409d0b3f..0e374a590 100644
--- a/src/branch/tests.rs
+++ b/src/branch/tests.rs
@@ -131,6 +131,22 @@ fn alt_array() {
   assert_eq!(alt1(defg), Ok((&b"g"[..], (&b"def"[..]))));
 }
 
+#[test]
+fn alt_dynamic_array() {
+  fn alt1(i: &[u8]) -> IResult<&[u8], &[u8]> {
+    alt(&mut [tag("a"), tag("bc"), tag("def")][..]).parse(i)
+  }
+
+  let a = &b"a"[..];
+  assert_eq!(alt1(a), Ok((&b""[..], (&b"a"[..]))));
+
+  let bc = &b"bc"[..];
+  assert_eq!(alt1(bc), Ok((&b""[..], (&b"bc"[..]))));
+
+  let defg = &b"defg"[..];
+  assert_eq!(alt1(defg), Ok((&b"g"[..], (&b"def"[..]))));
+}
+
 #[test]
 fn permutation_test() {
   #[allow(clippy::type_complexity)]