use crate::grammar::{Grammar, Symbol};
pub trait GrammarDef {
type Symbol: Copy;
type Terminal: Clone;
fn start(&self) -> Self::Symbol;
fn num_productions(&self, symbol: Self::Symbol) -> usize;
fn production(&self, symbol: Self::Symbol, index: usize) -> &[Self::Symbol];
fn is_terminal(&self, symbol: Self::Symbol) -> bool {
self.num_productions(symbol) == 0
}
fn terminal_value(&self, symbol: Self::Symbol) -> Self::Terminal;
}
impl<T: Clone> GrammarDef for Grammar<T> {
type Symbol = Symbol;
type Terminal = T;
fn start(&self) -> Symbol {
Symbol::NonTerminal(self.start())
}
fn num_productions(&self, symbol: Symbol) -> usize {
match symbol {
Symbol::Terminal(_) => 0,
Symbol::NonTerminal(i) => self.rules()[i].productions.len(),
}
}
fn production(&self, symbol: Symbol, index: usize) -> &[Symbol] {
match symbol {
Symbol::NonTerminal(i) => &self.rules()[i].productions[index],
Symbol::Terminal(_) => &[],
}
}
fn is_terminal(&self, symbol: Symbol) -> bool {
matches!(symbol, Symbol::Terminal(_))
}
fn terminal_value(&self, symbol: Symbol) -> T {
match symbol {
Symbol::Terminal(idx) => self.terminals()[idx].clone(),
_ => panic!("terminal_value called on non-terminal"),
}
}
}
#[cfg(test)]
mod test {
use super::*;
fn simple_grammar() -> Grammar<&'static str> {
Grammar::builder()
.rule("s", &[&["a"], &["b"], &["c"]])
.start("s")
.build()
}
#[test]
fn start_returns_non_terminal() {
let g = simple_grammar();
assert_eq!(GrammarDef::start(&g), Symbol::NonTerminal(0));
}
#[test]
fn num_productions_for_terminal() {
let g = simple_grammar();
assert_eq!(g.num_productions(Symbol::Terminal(0)), 0);
}
#[test]
fn num_productions_for_non_terminal() {
let g = simple_grammar();
assert_eq!(g.num_productions(Symbol::NonTerminal(0)), 3);
}
#[test]
fn is_terminal() {
let g = simple_grammar();
assert!(g.is_terminal(Symbol::Terminal(0)));
assert!(!g.is_terminal(Symbol::NonTerminal(0)));
}
#[test]
fn terminal_value() {
let g = simple_grammar();
assert_eq!(GrammarDef::terminal_value(&g, Symbol::Terminal(0)), "a");
assert_eq!(GrammarDef::terminal_value(&g, Symbol::Terminal(1)), "b");
assert_eq!(GrammarDef::terminal_value(&g, Symbol::Terminal(2)), "c");
}
}