[−][src]Macro nom::alt
Try a list of parsers and return the result of the first successful one
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:
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:
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:
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:
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:
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:
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.