Skip to main content

datalog_structural_model/ir/
atom.rs

1//! Datalog [`Atom`] — predicate applied to an ordered term list.
2
3use std::collections::BTreeSet;
4use std::fmt;
5
6use super::symbols::{PredicateSymbol, VariableSymbol};
7use super::term::Term;
8
9/// Datalog atom: `predicate(term_1, ..., term_n)`.
10#[derive(Clone, Debug, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
11#[serde(deny_unknown_fields)]
12pub struct Atom {
13    /// Predicate identifier.
14    pub predicate: PredicateSymbol,
15    /// Ordered list of terms.
16    pub terms: Vec<Term>,
17}
18
19impl Atom {
20    /// Arity of the atom.
21    #[must_use]
22    pub fn arity(&self) -> usize {
23        self.terms.len()
24    }
25
26    /// All variables that appear anywhere in this atom, including
27    /// variables nested inside [`Term::TripleTerm`] arguments
28    /// (recursively).
29    #[must_use]
30    pub(super) fn variables(&self) -> BTreeSet<VariableSymbol> {
31        let mut acc = BTreeSet::new();
32        self.collect_variables(&mut acc);
33        acc
34    }
35
36    fn collect_variables(&self, acc: &mut BTreeSet<VariableSymbol>) {
37        for term in &self.terms {
38            term.collect_variables(acc);
39        }
40    }
41
42    /// Maximum [`Term::TripleTerm`] nesting depth observed
43    /// anywhere in this atom's argument tree. `0` for a
44    /// plain-term atom; otherwise the depth of the deepest
45    /// nested triple-term.
46    #[must_use]
47    pub(super) fn nesting_depth(&self) -> usize {
48        self.terms
49            .iter()
50            .map(Term::nesting_depth)
51            .max()
52            .unwrap_or(0)
53    }
54}
55
56/// Display an [`Atom`] as `predicate(term_1, term_2, …)`. Used
57/// directly by [`Term`]'s [`std::fmt::Display`] impl for non-2-arity
58/// triple terms and available to error messages / debugging.
59impl fmt::Display for Atom {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        write!(f, "{}(", self.predicate.as_str())?;
62        for (idx, term) in self.terms.iter().enumerate() {
63            if idx > 0 {
64                write!(f, ", ")?;
65            }
66            write!(f, "{term}")?;
67        }
68        write!(f, ")")
69    }
70}