92 lines
2.3 KiB
Rust
92 lines
2.3 KiB
Rust
|
|
//! Atoms represent predicates applied to terms.
|
||
|
|
|
||
|
|
use std::fmt;
|
||
|
|
|
||
|
|
use super::term::Term;
|
||
|
|
|
||
|
|
/// An atom is a predicate symbol applied to a tuple of terms.
|
||
|
|
/// Example: Parent(alice, bob) or Ancestor(?X, ?Y)
|
||
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||
|
|
pub struct Atom {
|
||
|
|
/// The predicate name.
|
||
|
|
pub predicate: String,
|
||
|
|
/// The arguments to the predicate.
|
||
|
|
pub terms: Vec<Term>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Atom {
|
||
|
|
/// Create a new atom with the given predicate and terms.
|
||
|
|
pub fn new(predicate: impl Into<String>, terms: Vec<Term>) -> Self {
|
||
|
|
Atom {
|
||
|
|
predicate: predicate.into(),
|
||
|
|
terms,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Returns the arity (number of arguments) of this atom.
|
||
|
|
pub fn arity(&self) -> usize {
|
||
|
|
self.terms.len()
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Returns true if this atom is ground (contains no variables).
|
||
|
|
pub fn is_ground(&self) -> bool {
|
||
|
|
self.terms.iter().all(|t| t.is_ground())
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get all variables in this atom.
|
||
|
|
pub fn variables(&self) -> Vec<&String> {
|
||
|
|
self.terms
|
||
|
|
.iter()
|
||
|
|
.filter_map(|t| match t {
|
||
|
|
Term::Variable(v) => Some(v),
|
||
|
|
_ => None,
|
||
|
|
})
|
||
|
|
.collect()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl fmt::Display for Atom {
|
||
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
|
|
write!(f, "{}(", self.predicate)?;
|
||
|
|
for (i, term) in self.terms.iter().enumerate() {
|
||
|
|
if i > 0 {
|
||
|
|
write!(f, ", ")?;
|
||
|
|
}
|
||
|
|
write!(f, "{}", term)?;
|
||
|
|
}
|
||
|
|
write!(f, ")")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use super::*;
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_atom_creation() {
|
||
|
|
let atom = Atom::new(
|
||
|
|
"Parent",
|
||
|
|
vec![Term::constant("alice"), Term::constant("bob")],
|
||
|
|
);
|
||
|
|
assert_eq!(atom.predicate, "Parent");
|
||
|
|
assert_eq!(atom.arity(), 2);
|
||
|
|
assert!(atom.is_ground());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_atom_with_variables() {
|
||
|
|
let atom = Atom::new("Ancestor", vec![Term::var("X"), Term::var("Y")]);
|
||
|
|
assert!(!atom.is_ground());
|
||
|
|
assert_eq!(atom.variables().len(), 2);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_atom_display() {
|
||
|
|
let atom = Atom::new(
|
||
|
|
"Parent",
|
||
|
|
vec![Term::constant("alice"), Term::constant("bob")],
|
||
|
|
);
|
||
|
|
assert_eq!(format!("{}", atom), "Parent(alice, bob)");
|
||
|
|
}
|
||
|
|
}
|