use std::fmt; use crate::util::intersperse_option; #[derive(Debug, Clone)] pub struct Representation { production_output: EBNF, } impl Representation { pub fn show(&self) -> String { self.production_output.to_string() } pub fn production(&self) -> EBNF { self.production_output.clone() } pub fn new() -> Self { Self { production_output: EBNF::None, } } pub fn with_production(self, production_output: EBNF) -> Self { Self { production_output, ..self } } } #[derive(Debug, Clone)] pub enum EBNF { None, Nonterminal(String), CharTerminal(char), StringTerminal(String), LabeledTerminal(String), Alternation(Vec), Repeated(Box), } impl fmt::Display for EBNF { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { EBNF::None => write!(f, "none"), EBNF::CharTerminal(ch) => write!(f, "'{ch}'"), EBNF::Alternation(items) => { for item in intersperse_option(items.iter()) { match item { None => write!(f, " | ")?, Some(item) => write!(f, "{item}")?, } } write!(f, "") } EBNF::Nonterminal(name) => write!(f, "{name}"), EBNF::StringTerminal(term) => write!(f, r#""{term}""#), EBNF::LabeledTerminal(s) => write!(f, "<{s}>"), EBNF::Repeated(inner) => write!(f, "[ {inner} ]") } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_ebnf_print() { let example = EBNF::Alternation(vec![ EBNF::CharTerminal('f'), EBNF::CharTerminal('a'), EBNF::CharTerminal('k'), EBNF::CharTerminal('e'), ]); assert_eq!(example.to_string(), "'f' | 'a' | 'k' | 'e'"); let example = EBNF::Alternation(vec![ EBNF::Nonterminal("other-rule".into()), EBNF::CharTerminal('q'), EBNF::CharTerminal('m'), EBNF::StringTerminal("focus".into()), ]); assert_eq!(example.to_string(), "other-rule | 'q' | 'm' | \"focus\""); } }