Macro nom::alt [] [src]

macro_rules! alt {
    (__impl $i:expr, $submac:ident!( $($args:tt)* ), $($rest:tt)* ) => { ... };
    (__impl $i:expr, $e:ident, $($rest:tt)* ) => { ... };
    (__impl $i:expr, $e:ident | $($rest:tt)*) => { ... };
    (__impl $i:expr, $subrule:ident!( $($args:tt)*) | $($rest:tt)*) => { ... };
    (__impl $i:expr, $subrule:ident!( $($args:tt)* ) => { $gen:expr } | $($rest:tt)*) => { ... };
    (__impl $i:expr, $e:ident => { $gen:expr } | $($rest:tt)*) => { ... };
    (__impl $i:expr, __end) => { ... };
    ($i:expr, $($rest:tt)*) => { ... };
}

Try a list of parsers and return the result of the first successful one

Be careful when using this code, it's not being tested!
alt!(I -> IResult<I,O> | I -> IResult<I,O> | ... | I -> IResult<I,O> ) => I -> IResult<I, O>

All the parsers must have the same return type.

If one of the parsers returns Incomplete, alt! will return Incomplete, to retry once you get more input. Note that it is better for performance to know the minimum size of data you need before you get into alt!.

The alt! combinator is used in the following way:

Be careful when using this code, it's not being tested!
alt!(parser_1 | parser_2 | ... | parser_n)

Basic example

 // Create a parser that will match either "dragon" or "beast"
 named!( dragon_or_beast, alt!( tag!( "dragon" ) | tag!( "beast" ) ) );

 // Given the input "dragon slayer", the parser will match "dragon"
 // and the rest will be " slayer"
 let (rest, result) = dragon_or_beast(b"dragon slayer").unwrap();
 assert_eq!(result, b"dragon");
 assert_eq!(rest, b" slayer");

 // Given the input "beast of Gevaudan", the parser will match "beast"
 // and the rest will be " of Gevaudan"
 let (rest, result) = dragon_or_beast(&b"beast of Gevaudan"[..]).unwrap();
 assert_eq!(result, b"beast");
 assert_eq!(rest, b" of Gevaudan");

Manipulate results

There exists another syntax for alt! that gives you the ability to manipulate the result from each parser:

// We create an enum to represent our creatures
#[derive(Debug,PartialEq,Eq)]
enum Creature {
    Dragon,
    Beast,
    Unknown(usize)
}

// Let's make a helper function that returns true when not a space
// we are required to do this because the `take_while!` macro is limited
// to idents, so we can't negate `ìs_space` at the call site
fn is_not_space(c: u8) -> bool { ! nom::is_space(c) }

// Our parser will return the `Dragon` variant when matching "dragon",
// the `Beast` variant when matching "beast" and otherwise it will consume
// the input until a space is found and return an `Unknown` creature with
// the size of it's name.
named!(creature<Creature>, alt!(
    tag!("dragon")            => { |_| Creature::Dragon } |
    tag!("beast")             => { |_| Creature::Beast }  |
    take_while!(is_not_space) => { |r: &[u8]| Creature::Unknown(r.len()) }
    // the closure takes the result as argument if the parser is successful
));

// Given the input "dragon slayer" the parser will return `Creature::Dragon`
// and the rest will be " slayer"
let (rest, result) = creature(b"dragon slayer").unwrap();
assert_eq!(result, Creature::Dragon);
assert_eq!(rest, b" slayer");

// Given the input "beast of Gevaudan" the parser will return `Creature::Beast`
// and the rest will be " of Gevaudan"
let (rest, result) = creature(b"beast of Gevaudan").unwrap();
assert_eq!(result, Creature::Beast);
assert_eq!(rest, b" of Gevaudan");

// Given the input "demon hunter" the parser will return `Creature::Unkown(5)`
// and the rest will be " hunter"
let (rest, result) = creature(b"demon hunter").unwrap();
assert_eq!(result, Creature::Unknown(5));
assert_eq!(rest, b" hunter");

Behaviour of alt!

BE CAREFUL there is a case where the behaviour of alt! can be confusing:

when the alternatives have different lengths, like this case:

Be careful when using this code, it's not being tested!
named!( test, alt!( tag!( "abcd" ) | tag!( "ef" ) | tag!( "ghi" ) | tag!( "kl" ) ) );

With this parser, if you pass "abcd" as input, the first alternative parses it correctly, but if you pass "efg", the first alternative will return Incomplete, since it needs an input of 4 bytes. This behaviour of alt! is expected: if you get a partial input that isn't matched by the first alternative, but would match if the input was complete, you want alt! to indicate that it cannot decide with limited information.

There are two ways to fix this behaviour. The first one consists in ordering the alternatives by size, like this:

Be careful when using this code, it's not being tested!
named!( test, alt!( tag!( "ef" ) | tag!( "kl") | tag!( "ghi" ) | tag!( "abcd" ) ) );

With this solution, the largest alternative will be tested last.

The other solution uses the complete! combinator, which transforms an Incomplete in an Error. If one of the alternatives returns Incomplete but is wrapped by complete!, alt! will try the next alternative. This is useful when you know that you will not get partial input:

Be careful when using this code, it's not being tested!
named!( test,
   alt!(
     complete!( tag!( "abcd" ) ) |
     complete!( tag!( "ef"   ) ) |
     complete!( tag!( "ghi"  ) ) |
     complete!( tag!( "kl"   ) )
   )
 );

If you want the complete! combinator to be applied to all rules then use the convenience alt_complete! macro (see below).

This behaviour of alt! can get especially confusing if multiple alternatives have different sizes but a common prefix, like this:

Be careful when using this code, it's not being tested!
named!( test, alt!( tag!( "abcd" ) | tag!( "ab" ) | tag!( "ef" ) ) );

in that case, if you order by size, passing "abcd" as input will always be matched by the smallest parser, so the solution using complete! is better suited.

You can also nest multiple alt!, like this:

Be careful when using this code, it's not being tested!
named!( test,
   alt!(
     preceded!(
       tag!("ab"),
       alt!(
         tag!( "cd" ) |
         eof!()
       )
     )
   | tag!( "ef" )
   )
 );

preceded! will first parse "ab" then, if successful, try the alternatives "cd", or empty input (End Of File). If none of them work, preceded! will fail and "ef" will be tested.