Macro chumsky::select

source ·
macro_rules! select {
    (|$span:ident| $($p:pat $(if $guard:expr)? => $out:expr),+ $(,)?) => { ... };
    ($($p:pat $(if $guard:expr)? => $out:expr),+ $(,)?) => { ... };
}
Expand description

Create a parser that selects one or more input patterns and map them to an output value.

This is most useful when turning the tokens of a previous compilation pass (such as lexing) into data that can be used for parsing, although it can also generally be used to select inputs and map them to outputs. Any unmapped input patterns will become syntax errors, just as with filter.

The macro is semantically similar to a match expression and so supports pattern guards too.

select! {
    Token::Bool(x) if x => Expr::True,
    Token::Bool(x) if !x => Expr::False,
}

If you require access to the input’s span, you may add an argument before the patterns to gain access to it.

select! { |span|
    Token::Num(x) => Expr::Num(x).spanned(span),
    Token::Str(s) => Expr::Str(s).spanned(span),
}

Internally, select! is a loose wrapper around filter_map and thinking of it as such might make it less confusing.

Examples

// The type of our parser's input (tokens like this might be emitted by your compiler's lexer)
#[derive(Clone, Debug, PartialEq)]
enum Token {
    Num(u64),
    Bool(bool),
    LParen,
    RParen,
}

// The type of our parser's output, a syntax tree
#[derive(Debug, PartialEq)]
enum Ast {
    Num(u64),
    Bool(bool),
    List(Vec<Ast>),
}

// Our parser converts a stream of input tokens into an AST
// `select!` is used to deconstruct some of the tokens and turn them into AST nodes
let ast = recursive::<_, _, _, _, Cheap<Token>>(|ast| {
    let literal = select! {
        Token::Num(x) => Ast::Num(x),
        Token::Bool(x) => Ast::Bool(x),
    };

    literal.or(ast
        .repeated()
        .delimited_by(just(Token::LParen), just(Token::RParen))
        .map(Ast::List))
});

use Token::*;
assert_eq!(
    ast.parse(vec![LParen, Num(5), LParen, Bool(false), Num(42), RParen, RParen]),
    Ok(Ast::List(vec![
        Ast::Num(5),
        Ast::List(vec![
            Ast::Bool(false),
            Ast::Num(42),
        ]),
    ])),
);