use crate::symbol::{sentential_form, Symbol, SymbolError};
use crate::tokenizer;
use crate::tokenizer::TokenizerError;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::error::Error;
use std::fmt;
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ProductionError {
NoLhs,
NoRhs,
SymbolError(SymbolError),
FormatError(TokenizerError),
}
impl fmt::Display for ProductionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ProductionError::NoLhs => write!(f, "ProductionError: no lhs in production"),
ProductionError::NoRhs => write!(f, "ProductionError: no rhs in production"),
ProductionError::SymbolError(e) => {
write!(f, "ProductionError: symbol error encountered = {}", e)
}
ProductionError::FormatError(e) => write!(
f,
"ProductionError: bad formatted string encountered, error = {}",
e
),
}
}
}
impl Error for ProductionError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
ProductionError::SymbolError(e) => Some(e),
ProductionError::FormatError(e) => Some(e),
_ => None,
}
}
}
impl std::convert::From<TokenizerError> for ProductionError {
fn from(e: TokenizerError) -> Self {
ProductionError::FormatError(e)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ProductionPredicate {
LhsEquals(Vec<Symbol>),
RhsEquals(Vec<Symbol>),
RhsLengthEquals(usize),
RhsIsSuffixOf(Vec<Symbol>),
}
impl ProductionPredicate {
pub fn test(&self, p: &Production) -> bool {
match self {
ProductionPredicate::LhsEquals(symbols) => p.lhs() == *symbols,
ProductionPredicate::RhsEquals(symbols) => p.rhs() == *symbols,
ProductionPredicate::RhsLengthEquals(length) => p.rhs().len() == *length,
ProductionPredicate::RhsIsSuffixOf(symbols) => p.rhs().ends_with(&symbols),
}
}
}
impl fmt::Display for ProductionPredicate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ProductionPredicate::LhsEquals(symbols) => write!(
f,
"ProductionPredicate: lhs == {}",
sentential_form(symbols.clone())
),
ProductionPredicate::RhsEquals(symbols) => write!(
f,
"ProductionPredicate: rhs == {}",
sentential_form(symbols.clone())
),
ProductionPredicate::RhsIsSuffixOf(symbols) => write!(
f,
"ProductionPredicate: rhs suffix == {}",
sentential_form(symbols.clone())
),
ProductionPredicate::RhsLengthEquals(length) => {
write!(f, "ProductionPredicate: rhs length == {}", length)
}
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Production {
lhs: Vec<Symbol>,
rhs: Vec<Symbol>,
}
impl fmt::Display for Production {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for lhs in self.lhs() {
write!(f, "{} ", lhs)?;
}
write!(f, "->")?;
for rhs in self.rhs() {
write!(f, " {}", rhs)?;
}
Ok(())
}
}
impl std::convert::TryFrom<&str> for Production {
type Error = ProductionError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let p = Production::from_string(value)?;
let mut p_iter = p.iter();
if let Some(p) = p_iter.next() {
if p_iter.next().is_none() {
Ok(p.clone())
} else {
Err(ProductionError::FormatError(
TokenizerError::ProductionMultiple(value.to_string()),
))
}
} else {
Err(ProductionError::FormatError(
TokenizerError::ProductionEmpty(value.to_string()),
))
}
}
}
impl Production {
pub fn new<I>(lhs: I, rhs: I) -> Result<Production, ProductionError>
where
I: IntoIterator<Item = Symbol>,
{
let lhs: Vec<Symbol> = lhs.into_iter().collect();
let rhs: Vec<Symbol> = rhs.into_iter().collect();
if lhs.is_empty() {
return Err(ProductionError::NoLhs);
}
if rhs.is_empty() {
return Err(ProductionError::NoRhs);
}
Ok(Production { lhs: lhs, rhs: rhs })
}
pub fn new_from_string<'a, I>(lhs: I, rhs: I) -> Result<Production, ProductionError>
where
I: IntoIterator<Item = &'a str>,
{
let lhs = lhs.into_iter().try_fold(Vec::new(), |mut acc, s| {
let s = Symbol::new(s)?;
acc.push(s);
Ok(acc)
});
let rhs = rhs.into_iter().try_fold(Vec::new(), |mut acc, s| {
let s = Symbol::new(s)?;
acc.push(s);
Ok(acc)
});
match (lhs, rhs) {
(Ok(lhs), Ok(rhs)) => Production::new(lhs, rhs),
(Err(e), _) => Err(ProductionError::SymbolError(e)),
(_, Err(e)) => Err(ProductionError::SymbolError(e)),
}
}
pub fn lhs(&self) -> Vec<Symbol> {
self.lhs.clone()
}
pub fn rhs(&self) -> Vec<Symbol> {
self.rhs.clone()
}
pub fn symbols(&self) -> HashSet<Symbol> {
let mut symbols: Vec<Symbol> = self.lhs();
symbols.append(&mut self.rhs());
symbols.into_iter().collect()
}
pub fn from_string(string: &str) -> Result<Vec<Production>, ProductionError> {
tokenizer::productions_from_string(string)?
.iter()
.try_fold(Vec::new(), |mut acc, p| {
let p = Production::new_from_string::<Vec<&str>>(
p.0.iter().map(String::as_str).collect(),
p.1.iter().map(String::as_str).collect(),
)?;
acc.push(p);
Ok(acc)
})
}
pub fn from_iter<'a, I>(strings: I) -> Result<Vec<Production>, ProductionError>
where
I: IntoIterator<Item = &'a str>,
{
let mut p: Vec<Production> = Vec::new();
for string in strings {
p.append(&mut Production::from_string(string)?)
}
Ok(p)
}
pub fn such_that(predicate: ProductionPredicate) -> Box<dyn FnMut(&&Production) -> bool> {
Box::new(move |p| predicate.test(&p))
}
}
pub fn production(lhs: &str, rhs: &str) -> Production {
Production::new(
Symbol::from_string(lhs).unwrap(),
Symbol::from_string(rhs).unwrap(),
)
.unwrap()
}
pub fn productions(string: &str) -> Vec<Production> {
Production::from_string(string).unwrap()
}
pub fn production_table<I>(productions: I) -> String
where
I: IntoIterator<Item = Production>,
{
productions
.into_iter()
.enumerate()
.map(|(i, p)| format!("{}: {}", i, p))
.collect::<Vec<String>>()
.join("\n")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::symbol::symbol;
use std::convert::TryFrom;
use std::fmt::Write;
#[test]
fn from_string() {
let p_check = vec![
Production {
lhs: vec![symbol("S")],
rhs: vec![symbol("A"), symbol("B")],
},
Production {
lhs: vec![symbol("A")],
rhs: vec![symbol("a")],
},
Production {
lhs: vec![symbol("A")],
rhs: vec![symbol("B")],
},
Production {
lhs: vec![symbol("B")],
rhs: vec![symbol("b")],
},
];
assert_eq!(
Production::from_string("S -> A B\nA -> a | B\nB -> b").unwrap(),
p_check,
"Parsed production rules are not those expected"
);
}
#[test]
fn from_string_error() {
let error = TokenizerError::ProductionNoRhs("S".to_string());
let result = Production::from_string("S ->\n -> a | B\nB -> b");
assert!(
result.is_err(),
"Production from string on test input should return error"
);
let e = result.unwrap_err();
assert_eq!(
e,
ProductionError::FormatError(error),
"Creation of productions from test input returned the wrong error"
);
}
#[test]
fn from_iter() {
let p_check = vec![
Production {
lhs: vec![symbol("S")],
rhs: vec![symbol("A"), symbol("B")],
},
Production {
lhs: vec![symbol("A")],
rhs: vec![symbol("a")],
},
Production {
lhs: vec![symbol("B")],
rhs: vec![symbol("a")],
},
Production {
lhs: vec![symbol("B")],
rhs: vec![symbol("b")],
},
];
assert_eq!(
super::productions("S -> A B\nA -> a\nB -> a | b"),
p_check,
"Created production rules are not those expected"
);
}
#[test]
fn such_that() {
let filter = Production::such_that(ProductionPredicate::LhsEquals(vec![symbol("T")]));
let productions = Production::from_string("S -> A | B\nA -> a\nT -> t\nB -> B").unwrap();
let productions_iter = productions.clone();
let mut filtered = productions_iter.iter().filter(filter);
assert_eq!(
filtered.next(),
productions.get(3),
"Filtered productions on test input should return the T -> t production"
);
assert_eq!(
filtered.next(),
None,
"Filtered productions on test input should return no more productions"
);
}
#[test]
fn new() {
let p_check = Production {
lhs: vec![symbol("S")],
rhs: vec![symbol("A"), symbol("B")],
};
assert_eq!(
Production::new(p_check.lhs(), p_check.rhs()).unwrap(),
p_check,
"Created production rule is not the one expected"
);
}
#[test]
fn new_empty_side_lhs() {
let iter = vec![symbol("S")];
let result = Production::new(vec![], iter);
assert!(
result.is_err(),
"Creation of production rule should return an error"
);
let e = result.unwrap_err();
assert_eq!(
e,
ProductionError::NoLhs,
"Creation of production rule returned the wrong error"
);
}
#[test]
fn new_empty_side_rhs() {
let iter = vec![symbol("S")];
let result = Production::new(iter, vec![]);
assert!(
result.is_err(),
"Creation of production rule should return an error"
);
let e = result.unwrap_err();
assert_eq!(
e,
ProductionError::NoRhs,
"Creation of production rule returned the wrong error"
);
}
#[test]
fn new_from_string() {
let p_check = Production {
lhs: vec![symbol("S")],
rhs: vec![symbol("A"), symbol("B")],
};
assert_eq!(
Production::new_from_string(vec!["S"], vec!["A", "B"]).unwrap(),
p_check,
"Created production rule is not the one expected"
);
}
#[test]
fn new_from_string_error_lhs() {
let error = SymbolError::InvalidSymbol("\n".to_string());
let result = Production::new_from_string(vec!["\n"], vec!["A", "B"]);
assert!(
result.is_err(),
"Created production rule should return error"
);
let e = result.unwrap_err();
assert_eq!(
e,
ProductionError::SymbolError(error),
"Creation of production rule returned the wrong error"
);
}
#[test]
fn new_from_string_error_rhs() {
let error = SymbolError::InvalidSymbol("\n".to_string());
let result = Production::new_from_string(vec!["S"], vec!["\n"]);
assert!(
result.is_err(),
"Created production rule should return error"
);
let e = result.unwrap_err();
assert_eq!(
e,
ProductionError::SymbolError(error),
"Creation of production rule returned the wrong error"
);
}
#[test]
fn production_display() {
let mut buf = String::new();
let p = super::production("A", "B C");
let result = write!(buf, "{}", p);
assert!(result.is_ok());
assert_eq!(buf, "A -> B C")
}
#[test]
fn production_try_from() {
let result = Production::try_from("A -> B");
assert!(result.is_ok());
}
#[test]
fn production_try_from_multiple() {
let string = "A -> B\nA -> C";
let result = Production::try_from(string);
assert!(result.is_err());
let e = result.unwrap_err();
assert_eq!(
e,
ProductionError::FormatError(TokenizerError::ProductionMultiple(string.to_string()))
);
}
#[test]
fn production_try_from_error() {
let string = "A -> B -> C";
let result = Production::try_from(string);
assert!(result.is_err());
let e = result.unwrap_err();
assert_eq!(
e,
ProductionError::FormatError(TokenizerError::ProductionMultipleOneLine(0))
);
}
#[test]
fn production_try_from_no_productions() {
let result = Production::try_from("");
assert!(result.is_err());
let e = result.unwrap_err();
assert_eq!(
e,
ProductionError::FormatError(TokenizerError::ProductionEmpty("".to_string()))
);
}
#[test]
fn production_error_display_no_lhs() {
let mut buf = String::new();
let result = write!(buf, "{}", ProductionError::NoLhs);
assert!(result.is_ok());
assert_eq!(buf, "ProductionError: no lhs in production")
}
#[test]
fn production_error_display_no_rhs() {
let mut buf = String::new();
let result = write!(buf, "{}", ProductionError::NoRhs);
assert!(result.is_ok());
assert_eq!(buf, "ProductionError: no rhs in production")
}
#[test]
fn production_error_display_symbol_error() {
let mut buf = String::new();
let error = SymbolError::EmptySymbol;
let result = write!(buf, "{}", ProductionError::SymbolError(error.clone()));
assert!(result.is_ok());
assert_eq!(
buf,
format!("ProductionError: symbol error encountered = {}", error)
)
}
#[test]
fn production_error_display_format_error() {
let mut buf = String::new();
let error = TokenizerError::ProductionNoLhs;
let result = write!(buf, "{}", ProductionError::FormatError(error.clone()));
assert!(result.is_ok());
assert_eq!(
buf,
format!(
"ProductionError: bad formatted string encountered, error = {}",
error
)
)
}
#[test]
fn production_error_source() {
assert!(
ProductionError::FormatError(TokenizerError::ProductionNoLhs)
.source()
.is_some()
);
assert!(ProductionError::SymbolError(SymbolError::EmptySymbol)
.source()
.is_some());
}
#[test]
fn production_error_source_none() {
assert!(ProductionError::NoLhs.source().is_none());
assert!(ProductionError::NoRhs.source().is_none());
}
#[test]
fn predicate_lhs_equals() {
let predicate = ProductionPredicate::LhsEquals(vec![symbol("T")]);
assert!(
predicate.test(&Production {
lhs: vec![symbol("T")],
rhs: vec![]
}),
"Predicate should return true"
);
assert!(
!predicate.test(&Production {
lhs: vec![symbol("F")],
rhs: vec![]
}),
"Predicate should return false"
);
}
#[test]
fn predicate_rhs_equals() {
let predicate = ProductionPredicate::RhsEquals(vec![symbol("T")]);
assert!(
predicate.test(&Production {
lhs: vec![],
rhs: vec![symbol("T")]
}),
"Predicate should return true"
);
assert!(
!predicate.test(&Production {
lhs: vec![],
rhs: vec![symbol("F")]
}),
"Predicate should return false"
);
}
#[test]
fn predicate_rhs_length_equals() {
let predicate = ProductionPredicate::RhsLengthEquals(2);
assert!(
predicate.test(&Production {
lhs: vec![],
rhs: vec![symbol("T1"), symbol("T2")]
}),
"Predicate should return true"
);
assert!(
!predicate.test(&Production {
lhs: vec![],
rhs: vec![symbol("F")]
}),
"Predicate should return false"
);
}
#[test]
fn predicate_rhs_is_suffix_of() {
let predicate = ProductionPredicate::RhsIsSuffixOf(vec![symbol("T2"), symbol("T3")]);
assert!(
predicate.test(&Production {
lhs: vec![],
rhs: vec![symbol("T1"), symbol("T2"), symbol("T3")]
}),
"Predicate should return true"
);
assert!(
!predicate.test(&Production {
lhs: vec![],
rhs: vec![symbol("F")]
}),
"Predicate should return false"
);
}
#[test]
fn production_predicate_display_lhs_equals() {
let mut buf = String::new();
let result = write!(
buf,
"{}",
ProductionPredicate::LhsEquals(vec![symbol("A"), symbol("B")])
);
assert!(result.is_ok());
assert_eq!(buf, format!("ProductionPredicate: lhs == {}", "A B"))
}
#[test]
fn production_predicate_display_rhs_equals() {
let mut buf = String::new();
let result = write!(
buf,
"{}",
ProductionPredicate::RhsEquals(vec![symbol("A"), symbol("B")])
);
assert!(result.is_ok());
assert_eq!(buf, format!("ProductionPredicate: rhs == {}", "A B"))
}
#[test]
fn production_predicate_display_rhs_is_suffix_of() {
let mut buf = String::new();
let result = write!(
buf,
"{}",
ProductionPredicate::RhsIsSuffixOf(vec![symbol("A"), symbol("B")])
);
assert!(result.is_ok());
assert_eq!(buf, format!("ProductionPredicate: rhs suffix == {}", "A B"))
}
#[test]
fn production_predicate_display_rhs_length_equals() {
let mut buf = String::new();
let result = write!(buf, "{}", ProductionPredicate::RhsLengthEquals(2));
assert!(result.is_ok());
assert_eq!(buf, format!("ProductionPredicate: rhs length == {}", 2))
}
#[test]
fn production() {
let p_check = Production {
lhs: vec![symbol("S")],
rhs: vec![symbol("A"), symbol("B")],
};
assert_eq!(
super::production("S", "A B"),
p_check,
"Created production rule is not the one expected"
);
}
#[test]
fn productions() {
let p_check = vec![
Production {
lhs: vec![symbol("S")],
rhs: vec![symbol("A"), symbol("B")],
},
Production {
lhs: vec![symbol("A")],
rhs: vec![symbol("a")],
},
];
assert_eq!(
super::productions("S -> A B\nA -> a"),
p_check,
"Created production rules are not those expected"
);
}
#[test]
fn production_table() {
let p = super::productions(
"
A -> B C
B -> b
",
);
let result = super::production_table(p);
assert_eq!(result, "0: A -> B C\n1: B -> b");
}
}