use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RewriteRule<T: Clone + Eq> {
pub lhs: T,
pub rhs: T,
pub name: String,
pub conditions: Vec<T>,
}
impl<T: Clone + Eq> RewriteRule<T> {
pub fn new(name: impl Into<String>, lhs: T, rhs: T) -> Self {
RewriteRule {
lhs,
rhs,
name: name.into(),
conditions: Vec::new(),
}
}
pub fn conditional(name: impl Into<String>, lhs: T, rhs: T, conditions: Vec<T>) -> Self {
RewriteRule {
lhs,
rhs,
name: name.into(),
conditions,
}
}
pub fn is_conditional(&self) -> bool {
!self.conditions.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RewriteStrategy {
Innermost,
Outermost,
LeftmostInnermost,
LeftmostOutermost,
Parallel,
}
#[derive(Debug, Clone)]
pub struct RewriteSystem<T: Clone + Eq> {
pub rules: Vec<RewriteRule<T>>,
pub strategy: RewriteStrategy,
}
impl<T: Clone + Eq> RewriteSystem<T> {
pub fn new(strategy: RewriteStrategy) -> Self {
RewriteSystem {
rules: Vec::new(),
strategy,
}
}
pub fn add_rule(&mut self, rule: RewriteRule<T>) {
self.rules.push(rule);
}
}
#[derive(Debug, Clone)]
pub struct RewriteResult<T> {
pub term: T,
pub steps: Vec<(String, T)>,
pub converged: bool,
}
impl<T> RewriteResult<T> {
pub fn num_steps(&self) -> usize {
self.steps.len()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TermTree {
Leaf(String),
Node {
symbol: String,
children: Vec<TermTree>,
},
}
impl TermTree {
pub fn leaf(s: impl Into<String>) -> Self {
TermTree::Leaf(s.into())
}
pub fn node(symbol: impl Into<String>, children: Vec<TermTree>) -> Self {
TermTree::Node {
symbol: symbol.into(),
children,
}
}
pub fn is_variable(&self) -> bool {
match self {
TermTree::Leaf(s) => s.starts_with(|c: char| c.is_uppercase()),
TermTree::Node { .. } => false,
}
}
pub fn is_leaf(&self) -> bool {
matches!(self, TermTree::Leaf(_))
}
pub fn root_symbol(&self) -> Option<&str> {
match self {
TermTree::Leaf(s) => Some(s),
TermTree::Node { symbol, .. } => Some(symbol),
}
}
}
impl std::fmt::Display for TermTree {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TermTree::Leaf(s) => write!(f, "{s}"),
TermTree::Node { symbol, children } => {
if children.is_empty() {
write!(f, "{symbol}")
} else {
let args: Vec<String> = children.iter().map(|c| c.to_string()).collect();
write!(f, "{symbol}({})", args.join(", "))
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CriticalPair {
pub rule1: String,
pub rule2: String,
pub overlap: TermTree,
pub result1: TermTree,
pub result2: TermTree,
}
impl CriticalPair {
pub fn is_trivial(&self) -> bool {
self.result1 == self.result2
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConfluenceResult {
Confluent,
NotConfluent(CriticalPair),
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NormalizationOrder {
LeftToRight,
GreaterFirst,
WeightedPath {
weights: HashMap<String, u32>,
},
}