lalrpop 0.12.2

convenient LR(1) parser generator
Documentation
use intern::InternedString;
use grammar::parse_tree::{ActionKind, Alternative, ExprSymbol, Symbol, SymbolKind};

#[derive(Debug)]
pub enum AlternativeAction<'a> {
    User(&'a ActionKind),
    Default(Symbols<'a>),
}

#[derive(Debug)]
pub enum Symbols<'a> {
    Named(Vec<(usize, InternedString, &'a Symbol)>),
    Anon(Vec<(usize, &'a Symbol)>),
}

pub fn analyze_action<'a>(alt: &'a Alternative) -> AlternativeAction<'a> {
    // We can't infer types for alternatives with actions
    if let Some(ref code) = alt.action {
        return AlternativeAction::User(code);
    }

    AlternativeAction::Default(analyze_expr(&alt.expr))
}

pub fn analyze_expr<'a>(expr: &'a ExprSymbol) -> Symbols<'a> {
    // First look for named symbols.
    let named_symbols: Vec<_> =
        expr.symbols
            .iter()
            .enumerate()
            .filter_map(|(idx, sym)| match sym.kind {
                SymbolKind::Name(id, ref sub) => Some((idx, id, &**sub)),
                _ => None,
            })
            .collect();
    if !named_symbols.is_empty() {
        return Symbols::Named(named_symbols);
    }

    // Otherwise, make a tuple of the items they chose with `<>`.
    let chosen_symbol_types: Vec<_> =
        expr.symbols
            .iter()
            .enumerate()
            .filter_map(|(idx, sym)| match sym.kind {
                SymbolKind::Choose(ref sub) => Some((idx, &**sub)),
                _ => None,
            })
            .collect();
    if !chosen_symbol_types.is_empty() {
        return Symbols::Anon(chosen_symbol_types);
    }

    // If they didn't choose anything with `<>`, make a tuple of everything.
    Symbols::Anon(expr.symbols.iter().enumerate().collect())
}