//! Terms represent values in the chase: constants or labeled nulls. use std::fmt; /// A term is either a constant (from the input) or a null (invented during chase). #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Term { /// A constant value from the domain. Constant(String), /// A labeled null (invented value) created during the chase. /// The usize is a unique identifier for this null. Null(usize), /// A variable (used in rule bodies/heads, not in instances). Variable(String), } impl Term { /// Create a new constant term. pub fn constant(value: impl Into) -> Self { Term::Constant(value.into()) } /// Create a new null term with the given id. pub fn null(id: usize) -> Self { Term::Null(id) } /// Create a new variable term. pub fn var(name: impl Into) -> Self { Term::Variable(name.into()) } /// Returns true if this term is a variable. pub fn is_variable(&self) -> bool { matches!(self, Term::Variable(_)) } /// Returns true if this term is ground (not a variable). pub fn is_ground(&self) -> bool { !self.is_variable() } } impl fmt::Display for Term { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Term::Constant(c) => write!(f, "{}", c), Term::Null(id) => write!(f, "⊥{}", id), Term::Variable(v) => write!(f, "?{}", v), } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_term_creation() { let c = Term::constant("alice"); let n = Term::null(1); let v = Term::var("X"); assert!(matches!(c, Term::Constant(_))); assert!(matches!(n, Term::Null(1))); assert!(matches!(v, Term::Variable(_))); } #[test] fn test_term_properties() { let c = Term::constant("alice"); let v = Term::var("X"); assert!(c.is_ground()); assert!(!c.is_variable()); assert!(!v.is_ground()); assert!(v.is_variable()); } }