use rand::prelude::*;
#[derive(Debug, Clone)]
pub struct Grammar(pub NonTerminal, pub Vec<Rule>);
#[derive(Debug, Clone)]
pub struct Rule(pub NonTerminal, pub RuleBody);
#[derive(Debug, Clone)]
pub enum RuleBody {
Or(Or),
Sequence(Sequence),
}
#[derive(Debug, Clone)]
pub struct Or(pub Vec<Sequence>);
#[derive(Debug, Clone)]
pub struct Sequence(pub Vec<Symbol>);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Symbol {
NonTerminal(NonTerminal),
Terminal(Terminal),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NonTerminal(pub String);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Terminal(pub String);
#[derive(Debug, Clone)]
pub struct Derivation(pub Vec<Symbol>, pub usize);
#[derive(Debug, Clone)]
pub struct Expression(pub Vec<Terminal>);
#[macro_export]
macro_rules! t_or_rule {
($nt:expr => $( $string:expr ),*) => {
$crate::Rule($crate::NonTerminal($nt.into()),
$crate::RuleBody::Or($crate::Or(vec![
$(
$crate::Sequence(vec![$crate::Symbol::Terminal($crate::Terminal($string.into()))])
),*
])))
};
}
#[macro_export]
macro_rules! nt_seq_rule {
($nt:expr => $( $string:expr ),*) => {
$crate::Rule($crate::NonTerminal($nt.into()),
$crate::RuleBody::Sequence($crate::Sequence(vec![
$(
$crate::Symbol::NonTerminal($crate::NonTerminal($string.into()))
),*
])))
};
}
#[macro_export]
macro_rules! grammar {
($start:ident $($rest:tt)*) => {
$crate::Grammar(
stringify!($start).into(),
$crate::rules!([$start $($rest)*] [] [])
)
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! rules {
([;] [$($rule:tt)*] [$($emitted:tt)*]) => {
vec![ $($emitted)* $crate::rule!($($rule)*) ]
};
([; $($rest:tt)*] [$($rule:tt)*] [$($emitted:tt)*]) => {
$crate::rules!([$($rest)*] [] [$($emitted)* $crate::rule!($($rule)*) ,])
};
([] [$($rule:tt)*] [$($emitted:tt)*]) => {
vec![ $($emitted)* $crate::rule!($($rule)*) ]
};
([$first:tt $($rest:tt)*] [] [$($emitted:tt)*]) => {
$crate::rules!([$($rest)*] [$first] [$($emitted)*])
};
([$first:tt $($rest:tt)*] [$($rule:tt)*] [$($emitted:tt)*]) => {
$crate::rules!([$($rest)*] [$($rule)* $first] [$($emitted)*])
};
}
#[macro_export]
macro_rules! rule {
($head:ident => $($nt:ident)*) => {
$crate::Rule($crate::NonTerminal(stringify!($head).into()),
$crate::RuleBody::Sequence($crate::Sequence(vec![
$(
$crate::Symbol::NonTerminal($crate::NonTerminal(stringify!($nt).into()))
),*
])))
};
($head:ident => $($t:literal),*) => {
$crate::Rule($crate::NonTerminal(stringify!($head).into()),
$crate::RuleBody::Or($crate::Or(vec![
$(
$crate::Sequence(vec![$crate::Symbol::Terminal($crate::Terminal($t.into()))])
),*
])))
};
}
impl Grammar {
pub fn gen(&self) -> String {
let d = self.start_derivation();
let expr = d.derive(self);
expr.to_string()
}
pub fn start_derivation(&self) -> Derivation {
Derivation(vec![Symbol::NonTerminal(self.0.clone())], 0)
}
fn choose_rule(&self, nt: NonTerminal) -> &Rule {
let mut rules = Vec::new();
for rule in self.1.iter() {
if rule.0 == nt {
rules.push(rule);
}
}
rules.shuffle(&mut rand::thread_rng());
rules.get(0).unwrap_or_else(|| panic!("no rule for {nt:?}"))
}
}
impl Derivation {
pub fn derive(mut self, grammar: &Grammar) -> Expression {
while !self.is_done() {
self.derive_step(grammar);
}
self.into()
}
pub fn derive_step(&mut self, grammar: &Grammar) {
if self.is_done() {
return;
}
let nt = self.find_nt();
if let Some(nt) = nt {
let rule = grammar.choose_rule(nt);
self.apply(rule);
}
}
pub fn is_done(&self) -> bool {
for symbol in &self.0[self.1..] {
if let Symbol::NonTerminal(_) = symbol {
return false;
}
}
true
}
fn find_nt(&self) -> Option<NonTerminal> {
for symbol in &self.0[self.1..] {
if let Symbol::NonTerminal(nt) = symbol {
return Some(nt.clone());
}
}
None
}
fn find(&self, nt: &NonTerminal) -> Option<usize> {
for (i, elem) in self.0.iter().enumerate() {
if let Symbol::NonTerminal(el) = elem {
if el == nt {
return Some(i);
}
}
}
None
}
fn apply(&mut self, rule: &Rule) {
match self.find(&rule.0) {
Some(index) => self.apply_at(rule, index),
None => panic!("rule cannot be applied"),
}
}
fn apply_at(&mut self, rule: &Rule, index: usize) {
let seq = match &rule.1 {
RuleBody::Sequence(seq) => seq.0.clone(),
RuleBody::Or(or) => or.choose_seq().0.clone(),
};
self.1 = index;
self.0.splice(index..(index + 1), seq);
}
}
impl From<Derivation> for Expression {
fn from(d: Derivation) -> Self {
Self(
d.0.into_iter()
.map(|sym| match sym {
Symbol::NonTerminal(_) => panic!("derivation is not complete"),
Symbol::Terminal(t) => t,
})
.collect(),
)
}
}
impl From<&str> for NonTerminal {
fn from(s: &str) -> Self {
NonTerminal(s.to_string())
}
}
impl std::fmt::Display for Expression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
for t in &self.0 {
write!(f, "{}", t.0)?;
}
Ok(())
}
}
impl std::fmt::Display for Derivation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
for t in &self.0 {
match t {
Symbol::NonTerminal(s) => write!(f, "{}", s.0)?,
Symbol::Terminal(s) => write!(f, "{}", s.0)?,
}
}
Ok(())
}
}
impl Or {
fn choose_seq(&self) -> &Sequence {
if self.0.is_empty() {
panic!("empty or");
}
let rand_index = rand::thread_rng().gen_range(0..self.0.len());
&self.0[rand_index]
}
}
#[cfg(test)]
mod tests {
use crate::*;
fn build_grammar() -> Grammar {
grammar! {
S => A N;
A => "a";
N => "0"
}
}
#[test]
fn it_works() {
let grammar = build_grammar();
assert_eq!(grammar.gen(), "a0");
}
mod grammar_macro {
use crate::*;
#[test]
fn small() {
let g: Grammar = grammar! {
A => B C;
B => "b";
C => "c"
};
assert_eq!(g.gen(), "bc");
let g: Grammar = grammar! {
A => B C;
B => "b";
C => "c";
};
assert_eq!(g.gen(), "bc");
let g: Grammar = grammar! {
A => B C;
B => D;
C => "c";
D => "d"
};
assert_eq!(g.gen(), "dc");
let g: Grammar = grammar! {
A => B C;
B => D;
C => "c";
D => "d", "x"
};
let word = g.gen();
assert!(word == "dc" || word == "xc");
}
#[test]
fn medium() {
let g = grammar! {
S => B A;
A => B A;
A => E;
E => "";
B => "a", "b", "c"
};
for _ in 0..100 {
let w = g.gen();
assert!(w.chars().all(|c| c == 'a' || c == 'b' || c == 'c'));
}
}
#[test]
fn large1() {
let _ = grammar! {
S => B A;
A => B A B;
A => E;
E => "";
B => X Y;
B => "a", "b", "c";
X => "x", "eks";
Y => ""
};
}
#[test]
fn large2() {
let _ = grammar! {
S => B A;
A => B A B;
A => E;
E => "";
B => X Y;
B => "a", "b", "c";
X => "x", "eks";
Y => "";
};
}
}
}