[go: up one dir, main page]

select

Macro select 

Source
macro_rules! select {
    ($($p:pat $(= $extra:ident)? $(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 Parser::filter.

Internally, select! is very similar to a single-token Parser::filter and thinking of it as such might make it less confusing.

select! requires that tokens implement Clone and the input type implements ValueInput. If you’re trying to access tokens referentially (for the sake of nested parsing, or simply because you want to avoid cloning the token), see select_ref!.

§Examples

select! is syntactically similar to a match expression and has support for pattern guards:

#[derive(Clone)]
enum Token<'src> { Ident(&'src str) }

enum Expr<'src> { Local(&'src str), Null, True, False }

select! {
    Token::Ident(s) if s == "true" => Expr::True,
    Token::Ident(s) if s == "false" => Expr::False,
    Token::Ident(s) if s == "null" => Expr::Null,
    Token::Ident(s) => Expr::Local(s),
}

If you require access to the token’s span or other metadata, you may add an argument after a pattern to gain access to it (see the docs for Parser::map_with and MapExtra):

#[derive(Clone)]
enum Token<'src> { Num(f64), Str(&'src str) }

enum Expr<'src> { Num(f64), Str(&'src str) }

type Span = SimpleSpan<usize>;

impl<'src> Expr<'src> {
    fn spanned(self, span: Span) -> (Self, Span) { (self, span) }
}

select! {
    Token::Num(x) = e => Expr::Num(x).spanned(e.span()),
    Token::Str(s) = e => Expr::Str(s).spanned(e.span()),
}
// 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::<_, _, extra::Err<Simple<Token>>, _, _>(|ast| {
    let literal = select! {
        Token::Num(x) => Ast::Num(x),
        Token::Bool(x) => Ast::Bool(x),
    };

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

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