Loading page…
Rust walkthroughs
Loading page…
nom::branch::alt and nom::branch::permutation for parsing alternatives?nom::branch::alt implements ordered choice: it tries each parser in sequence and returns the result of the first successful match, failing only when all alternatives fail. nom::branch::permutation applies all parsers exactly once in any order, succeeding when every parser matches and returning a tuple of all results. The fundamental difference is that alt is "one of these must succeed" while permutation is "all of these must succeed, in any order." Use alt for mutually exclusive alternatives like different literal tokens or keyword variations. Use permutation for unordered collections of required elements like command-line flags that can appear in any sequence.
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::IResult;
fn parse_color(input: &str) -> IResult<&str, &str> {
// Try each parser in order, return first success
alt((
tag("red"),
tag("green"),
tag("blue"),
))(input)
}
fn main() {
// Matches first successful alternative
assert_eq!(parse_color("red"), Ok(("", "red")));
assert_eq!(parse_color("green"), Ok(("", "green")));
assert_eq!(parse_color("blue"), Ok(("", "blue")));
// Order matters - "red" matches before "green" could
assert_eq!(parse_color("greenish"), Ok(("ish", "green")));
// Fails if no alternative matches
assert!(parse_color("yellow").is_err());
}alt returns the first successful result among alternatives.
use nom::branch::permutation;
use nom::bytes::complete::tag;
use nom::IResult;
fn parse_pair(input: &str) -> IResult<&str, (&str, &str)> {
// All parsers must match exactly once, in any order
permutation((
tag("a"),
tag("b"),
))(input)
}
fn main() {
// Order in input doesn't matter
assert_eq!(parse_pair("ab"), Ok(("", ("a", "b"))));
assert_eq!(parse_pair("ba"), Ok(("", ("b", "a"))));
// Result is always in parser definition order
let (remaining, (first, second)) = parse_pair("ba").unwrap();
assert_eq!(first, "b"); // Was tag("a") - matched "b" at position 0
assert_eq!(second, "a"); // Was tag("b") - matched "a" at position 1
// Fails if any parser doesn't match
assert!(parse_pair("ac").is_err()); // "b" not found
assert!(parse_pair("aa").is_err()); // Each parser must match exactly once
}permutation requires all parsers to match exactly once in any order.
use nom::branch::{alt, permutation};
use nom::bytes::complete::tag;
use nom::character::complete::digit1;
use nom::IResult;
fn alt_example(input: &str) -> IResult<&str, &str> {
// alt returns ONE result - the type of successful parser
alt((
tag("hello"),
tag("world"),
tag("!"),
))(input)
}
fn permutation_example(input: &str) -> IResult<&str, (&str, &str, &str)> {
// permutation returns TUPLE of all results
permutation((
tag("a"),
tag("b"),
tag("c"),
))(input)
}
fn main() {
// alt: one result
let result = alt_example("world!");
assert_eq!(result, Ok(("!", "world")));
// permutation: tuple of all results
let result = permutation_example("cba");
assert_eq!(result, Ok(("", ("c", "b", "a"))));
}alt returns a single value; permutation returns a tuple of all values.
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::IResult;
fn parse_keyword(input: &str) -> IResult<&str, &str> {
// ORDER MATTERS: try shorter matches first might consume wrong input
alt((
tag("let"),
tag("letter"),
))(input)
}
fn parse_keyword_fixed(input: &str) -> IResult<&str, &str> {
// ORDER MATTERS: try longer matches first
alt((
tag("letter"),
tag("let"),
))(input)
}
fn main() {
// Problem: "let" matches first, consuming "let" from "letter"
assert_eq!(parse_keyword("letter"), Ok(("ter", "let")));
// Fixed: try "letter" first
assert_eq!(parse_keyword_fixed("letter"), Ok(("", "letter")));
assert_eq!(parse_keyword_fixed("let"), Ok(("", "let")));
}alt is ordered choice; parser order affects behavior significantly.
use nom::branch::permutation;
use nom::bytes::complete::tag;
use nom::IResult;
fn parse_command_flags(input: &str) -> IResult<&str, (&str, &str, &str)> {
// Order doesn't matter - all must appear somewhere
permutation((
tag("-v"),
tag("-f"),
tag("-q"),
))(input)
}
fn main() {
// Any order works
assert_eq!(parse_command_flags("-v-f-q"), Ok(("", ("-v", "-f", "-q"))));
assert_eq!(parse_command_flags("-q-v-f"), Ok(("", ("-q", "-v", "-f"))));
assert_eq!(parse_command_flags("-f-q-v"), Ok(("", ("-f", "-q", "-v"))));
// All flags required
assert!(parse_command_flags("-v-f").is_err()); // Missing -q
}permutation matches elements in any order; all must be present.
use nom::branch::{alt, permutation};
use nom::bytes::complete::tag;
use nom::character::complete::{alpha1, digit1};
use nom::sequence::tuple;
use nom::IResult;
fn alt_with_complex_parsers(input: &str) -> IResult<&str, &str> {
// alt can combine any parser types
alt((
digit1,
alpha1,
tag("!"),
))(input)
}
fn permutation_with_complex_parsers(input: &str) -> IResult<&str, (&str, &str)> {
// permutation requires each parser to match once
permutation((
digit1,
alpha1,
))(input)
}
fn main() {
// alt: tries each in order
assert_eq!(alt_with_complex_parsers("123"), Ok(("", "123")));
assert_eq!(alt_with_complex_parsers("abc"), Ok(("", "abc")));
assert_eq!(alt_with_complex_parsers("!"), Ok(("", "!")));
// permutation: both must match
assert_eq!(permutation_with_complex_parsers("123abc"), Ok(("", ("123", "abc"))));
assert_eq!(permutation_with_complex_parsers("abc123"), Ok(("", ("abc", "123"))));
}Both combinators work with any parser type, not just tag.
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::{char, digit1};
use nom::sequence::delimited;
use nom::IResult;
#[derive(Debug)]
enum Value {
Number(i64),
String(String),
Boolean(bool),
}
fn parse_value(input: &str) -> IResult<&str, Value> {
alt((
// Try each alternative in order
|i| {
let (i, _) = tag("true")(i)?;
Ok((i, Value::Boolean(true)))
},
|i| {
let (i, _) = tag("false")(i)?;
Ok((i, Value::Boolean(false)))
},
|i| {
let (i, num) = digit1(i)?;
Ok((i, Value::Number(num.parse().unwrap())))
},
|i| {
let (i, s) = delimited(char('"'), |i| {
// Simplified string parsing
let (i, content) = nom::character::complete::alphanumeric1(i)?;
Ok((i, content.to_string()))
}, char('"'))(i)?;
Ok((i, Value::String(s)))
},
))(input)
}
fn main() {
assert_eq!(parse_value("true").unwrap().1, Value::Boolean(true));
assert_eq!(parse_value("42").unwrap().1, Value::Number(42));
assert_eq!(parse_value("\"hello\"").unwrap().1, Value::String("hello".to_string()));
}alt is ideal for mutually exclusive alternatives like different value types.
use nom::branch::permutation;
use nom::bytes::complete::tag;
use nom::character::complete::multispace0;
use nom::sequence::preceded;
use nom::IResult;
#[derive(Debug, Default)]
struct Args {
verbose: bool,
force: bool,
quiet: bool,
}
fn parse_flag(input: &str, flag: &str) -> impl Fn(&str) -> IResult<&str, bool> + '_ {
move |i: &str| {
let (i, _) = tag(flag)(i)?;
Ok((i, true))
}
}
fn parse_args(input: &str) -> IResult<&str, (bool, bool, bool)> {
permutation((
preceded(multispace0, parse_flag(input, "-v")),
preceded(multispace0, parse_flag(input, "-f")),
preceded(multispace0, parse_flag(input, "-q")),
))(input)
}
fn main() {
// Flags in any order
let (rem, (v, f, q)) = parse_args("-v -f -q").unwrap();
println!("verbose: {}, force: {}, quiet: {}", v, f, q);
let (rem, (v, f, q)) = parse_args("-q -v -f").unwrap();
println!("verbose: {}, force: {}, quiet: {}", v, f, q);
}permutation handles flags/arguments that can appear in any order.
use nom::branch::{alt, permutation};
use nom::bytes::complete::tag;
use nom::error::Error;
use nom::IResult;
fn main() {
// alt: succeeds if ANY alternative succeeds
let result: IResult<&str, &str, Error<&str>> = alt((
tag("a"),
tag("b"),
tag("c"),
))("d");
assert!(result.is_err());
// Error: all alternatives failed
// permutation: succeeds if ALL parsers match
let result: IResult<&str, (&str, &str), Error<&str>> = permutation((
tag("a"),
tag("b"),
))("ab");
assert_eq!(result, Ok(("", ("a", "b"))));
// permutation fails if any parser fails to match
let result: IResult<&str, (&str, &str), Error<&str>> = permutation((
tag("a"),
tag("b"),
))("ac");
assert!(result.is_err()); // "b" not found
}alt fails when all alternatives fail; permutation fails when any parser fails.
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::sequence::pair;
use nom::IResult;
fn main() {
// alt backtracks between alternatives
// If first parser partially matches but fails later, tries next
fn parser1(input: &str) -> IResult<&str, (&str, &str)> {
pair(tag("ab"), tag("c"))(input) // Must match "abc"
}
fn parser2(input: &str) -> IResult<&str, (&str, &str)> {
pair(tag("ab"), tag("d"))(input) // Must match "abd"
}
let result = alt((parser1, parser2))("abd");
// parser1 tries "abc", fails at "c" vs "d"
// alt backtracks, tries parser2, succeeds
assert_eq!(result, Ok(("", ("ab", "d"))));
// permutation also backtracks when trying different orders
use nom::branch::permutation;
let result = permutation((
tag("ab"),
tag("cd"),
))("abcd");
// Tries to match parsers in different orders until finding valid order
assert_eq!(result, Ok(("", ("ab", "cd"))));
}Both alt and permutation backtrack when trying alternatives.
use nom::branch::{alt, permutation};
use nom::bytes::complete::tag;
use nom::IResult;
fn main() {
// alt: O(n) where n = number of alternatives
// Each parser tried in order until success
// For many alternatives, put common ones first
fn parse_keyword(input: &str) -> IResult<&str, &str> {
alt((
tag("if"), // Common
tag("else"), // Common
tag("while"), // Less common
tag("for"), // Less common
// ... many more
))(input)
}
// permutation: tries all orderings
// O(n!) worst case, but practical for small n
// n = number of parsers
// For many elements, permutation can be slow
fn parse_many(input: &str) -> IResult<&str, (&str, &str, &str)> {
permutation((
tag("a"),
tag("b"),
tag("c"),
))(input)
}
// OK: 3 parsers = manageable
// Avoid: 10+ parsers = exponential complexity
}alt is linear in alternatives; permutation is factorial in worst case.
use nom::branch::{alt, permutation};
use nom::bytes::complete::tag;
use nom::IResult;
fn main() {
// alt inside alt: more alternatives
fn parse_animal(input: &str) -> IResult<&str, &str> {
alt((
alt((tag("cat"), tag("dog"))),
alt((tag("bird"), tag("fish"))),
))(input)
}
assert_eq!(parse_animal("cat"), Ok(("", "cat")));
assert_eq!(parse_animal("fish"), Ok(("", "fish")));
// permutation inside permutation: nested groups
fn parse_nested(input: &str) -> IResult<&str, ((&str, &str), (&str, &str))> {
permutation((
permutation((tag("a"), tag("b"))),
permutation((tag("c"), tag("d"))),
))(input)
}
// Each inner permutation must match in any order
// Outer permutation: groups can appear in any order
assert_eq!(parse_nested("abcd"), Ok(("", (("a", "b"), ("c", "d")))));
assert_eq!(parse_nested("cdab"), Ok(("", (("c", "d"), ("a", "b")))));
// alt inside permutation: choices for each position
fn parse_flags(input: &str) -> IResult<&str, (&str, &str)> {
permutation((
alt((tag("-v"), tag("--verbose"))),
alt((tag("-f"), tag("--force"))),
))(input)
}
assert_eq!(parse_flags("-v-f"), Ok(("", ("-v", "-f"))));
assert_eq!(parse_flags("--verbose--force"), Ok(("", ("--verbose", "--force"))));
assert_eq!(parse_flags("-f--verbose"), Ok(("", ("-f", "--verbose"))));
}Combinators can nest to create complex parsing patterns.
use nom::branch::{alt, permutation};
use nom::bytes::complete::tag;
use nom::character::complete::{alphanumeric1, multispace0, space0};
use nom::sequence::{preceded, terminated};
use nom::IResult;
#[derive(Debug, Default)]
struct Config {
host: Option<String>,
port: Option<u16>,
debug: bool,
}
fn parse_host(input: &str) -> IResult<&str, String> {
let (i, _) = tag("host=")(input)?;
let (i, host) = alphanumeric1(i)?;
Ok((i, host.to_string()))
}
fn parse_port(input: &str) -> IResult<&str, u16> {
let (i, _) = tag("port=")(input)?;
let (i, port) = nom::character::complete::u16(i)?;
Ok((i, port))
}
fn parse_debug(input: &str) -> IResult<&str, bool> {
let (i, _) = tag("debug")(input)?;
Ok((i, true))
}
fn parse_config_item(input: &str) -> IResult<&str, ConfigItem> {
// Each item is mutually exclusive - use alt
alt((
|i| parse_host(i).map(|(rem, h)| (rem, ConfigItem::Host(h))),
|i| parse_port(i).map(|(rem, p)| (rem, ConfigItem::Port(p))),
|i| parse_debug(i).map(|(rem, _)| (rem, ConfigItem::Debug)),
))(input)
}
enum ConfigItem {
Host(String),
Port(u16),
Debug(bool),
}
fn main() {
// alt for individual item types
let result = alt((
tag("host=localhost"),
tag("port=8080"),
tag("debug"),
))("port=8080");
// In practice, use permutation for ordered items:
// host=X port=Y debug - any order
// But alt for the value of each: host=(X|Y|Z)
}Combine alt and permutation for realistic parsing scenarios.
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::error::{Error, ErrorKind};
use nom::IResult;
fn main() {
// alt returns error from last alternative
let result: IResult<&str, &str, Error<&str>> = alt((
tag("a"),
tag("b"),
tag("c"),
))("xyz");
if let Err(nom::Err::Error(e)) = result {
// Error from last parser that was tried
println!("Error at input: {:?}", e.input);
println!("Error kind: {:?}", e.code);
}
// All alternatives tried, last error returned
// Consider nom::error::VerboseError for better diagnostics
}alt returns the error from the last alternative tried.
Core distinction:
alt: "one of these must succeed" (ordered choice, first success wins)permutation: "all of these must succeed in any order" (unordered collection)Return types:
alt((A, B, C)) returns A | B | C (one result)permutation((A, B, C)) returns (A, B, C) (tuple of all results)Order sensitivity:
alt: order matters (first match wins, like if/else if)permutation: order irrelevant (matches in any order)Failure conditions:
alt: fails when ALL alternatives failpermutation: fails when ANY parser failsUse alt for:
Use permutation for:
Performance:
alt: O(n) for n alternatives, linear scanpermutation: O(n!) worst case, but practical for small nKey insight: The choice between alt and permutation depends on your semantics. If alternatives are mutually exclusive (only one should match), use alt. If elements are complementary (all should match, order irrelevant), use permutation. For complex grammars, combine them: alt for choices within a position, permutation for ordering flexibility across positions. The ordered choice in alt requires careful ordering (longer matches before shorter to avoid premature matching), while permutation handles ordering transparently but requires all elements to be present.