chomchom 0.3.3

A simple parser generator for Rust
Documentation
use crate::grammar::{GrammarEBNF, Rstr, EBNF};
use crate::tokeniser::Token;
use std::collections::{BTreeMap, VecDeque};

pub fn parse(tokens: Vec<Token>) -> Result<GrammarEBNF, String> {
    let mut deq: VecDeque<Token> = tokens.into();
    let mut rules: BTreeMap<Rstr, Vec<EBNF>> = BTreeMap::new();
    let mut start_nonterm: Option<Rstr> = None;
    while !deq.is_empty() {
        let (nonterm, rule) = parse_rule(&mut deq)?;
        if start_nonterm.is_none() {
            start_nonterm = Some(nonterm.clone());
        }
        rules.entry(nonterm).or_default().push(rule);
    }

    Ok(GrammarEBNF {
        start_nonterm: start_nonterm
            .unwrap_or("No starting nonterminal.".into()),
        rules,
    })
}

fn parse_rule(tokens: &mut VecDeque<Token>) -> Result<(Rstr, EBNF), String> {
    let Token::Nonterminal(nonterm) = tokens
        .pop_front()
        .ok_or("Nonterminal on lhs of rule expected.")?
    else {
        return Err("Nonterminal on lhs of rule expected.".into());
    };
    if !matches!(tokens.pop_front(), Some(Token::Equal)) {
        return Err("Equal expected.".into());
    }
    let rule = parse_rhs(tokens)?;
    if parse_semicolon(tokens).is_err() {
        Err(format!("No semicolon after rule: {:?}", rule))
    } else {
        Ok((nonterm, rule))
    }
}

fn parse_rhs(tokens: &mut VecDeque<Token>) -> Result<EBNF, String> {
    parse_alternation(tokens)
}

fn parse_term(tokens: &mut VecDeque<Token>) -> Result<EBNF, String> {
    if let Some(token) = tokens.front() {
        match token {
            Token::LParen => {
                tokens.pop_front();
                let r = parse_rhs(tokens)?;
                if !matches!(tokens.pop_front(), Some(Token::RParen)) {
                    Err("No matching ')' found.".into())
                } else {
                    Ok(r)
                }
            }
            Token::LBracket => {
                tokens.pop_front();
                let r = parse_rhs(tokens)?;
                if !matches!(tokens.pop_front(), Some(Token::RBracket)) {
                    Err("No matching ')' found.".into())
                } else {
                    Ok(EBNF::Optional(Box::new(r)))
                }
            }
            Token::LBrace => {
                tokens.pop_front();
                let r = parse_rhs(tokens)?;
                if !matches!(tokens.pop_front(), Some(Token::RBrace)) {
                    Err("No matching '}' found.".into())
                } else {
                    Ok(EBNF::Iteration(Box::new(r)))
                }
            }
            Token::Nonterminal(_) => {
                let Some(Token::Nonterminal(pc)) = tokens.pop_front() else {
                    unreachable!()
                };
                Ok(EBNF::Non(pc))
            }
            Token::Terminal(_) => {
                let Some(Token::Terminal(pc)) = tokens.pop_front() else {
                    unreachable!()
                };
                Ok(EBNF::Term(pc))
            }
            Token::Epsilon => {
                tokens.pop_front();
                Ok(EBNF::Epsilon)
            }
            Token::RBrace => Err("lol".into()),
            idk => Err(format!("Unexpected '{:?}', {:?}", idk, tokens)),
        }
    } else {
        Err("wtf2".into())
    }
}

fn parse_factor(tokens: &mut VecDeque<Token>) -> Result<EBNF, String> {
    let term = parse_term(tokens)?;

    if let Some(token) = tokens.front() {
        match token {
            Token::Asterix => {
                tokens.pop_front();
                Ok(EBNF::Iteration(Box::new(term)))
            }
            Token::Plus => {
                tokens.pop_front();
                Ok(EBNF::OneOrMore(Box::new(term)))
            }
            _ => Ok(term),
        }
    } else {
        Err("Expected one of Asterix or Plus, found EOF".into())
    }
}

fn flaten_or(x: EBNF) -> Vec<EBNF> {
    match x {
        EBNF::Or(or) => or.into_iter().flat_map(flaten_or).collect(),
        els => vec![els],
    }
}

fn parse_alternation(tokens: &mut VecDeque<Token>) -> Result<EBNF, String> {
    let factor = parse_concatanation(tokens)?;
    if let Some(Token::Or) = tokens.front() {
        tokens.pop_front();
        let r = vec![factor, parse_alternation(tokens)?];
        Ok(EBNF::Or(r.into_iter().flat_map(flaten_or).collect()))
    } else {
        Ok(factor)
    }
}

fn flaten_cat(x: EBNF) -> Vec<EBNF> {
    match x {
        EBNF::Concat(cat) => cat.into_iter().flat_map(flaten_cat).collect(),
        els => vec![els],
    }
}

fn parse_concatanation(tokens: &mut VecDeque<Token>) -> Result<EBNF, String> {
    let factor = parse_factor(tokens)?;
    if let Some(t) = tokens.front() {
        if matches!(t, Token::Semicolon) {
            Ok(factor)
        } else {
            let c = parse_concatanation(tokens);
            match c {
                Ok(c) => {
                    let cs = vec![factor, c];
                    Ok(EBNF::Concat(
                        cs.into_iter().flat_map(flaten_cat).collect(),
                    ))
                }
                Err(_) => Ok(factor),
            }
        }
    } else {
        Ok(factor)
    }
}

fn parse_semicolon(tokens: &mut VecDeque<Token>) -> Result<(), String> {
    if let Some(Token::Semicolon) = tokens.pop_front() {
        Ok(())
    } else {
        Err("Semicolon not found.".into())
    }
}