datalog_structural_model/ir/ground.rs
1//! Ground (variable-free) term and atom value forms.
2
3use std::fmt;
4
5use super::symbols::{ConstantSymbol, PredicateSymbol};
6
7/// Ground term — the value form of a [`Term`](super::term::Term)
8/// with all variables substituted. Used as the cell type inside
9/// tuples so an evaluator can store both flat
10/// constant-only facts and quoted-triple facts (TT1).
11///
12/// `Constant` corresponds to the
13/// [`Term::Constant`](super::term::Term::Constant) form;
14/// `TripleTerm` is the value form of
15/// [`Term::TripleTerm`](super::term::Term::TripleTerm) —
16/// a [`GroundAtom`] whose argument list contains only further
17/// `GroundTerm` values (no variables, recursively).
18///
19/// Total order: `Constant(_) < TripleTerm(_)`. Within each
20/// variant, the inner symbol or atom's lexicographic order
21/// determines the result. This is the determinism contract
22/// an evaluator can rely on when serialising grounded facts.
23#[derive(
24 Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize,
25)]
26#[serde(try_from = "super::serde_input::GroundTermInput")]
27pub enum GroundTerm {
28 /// Ground constant — the value form of
29 /// [`Term::Constant`](super::term::Term::Constant).
30 Constant(ConstantSymbol),
31 /// Ground quoted-triple term — the value form of
32 /// [`Term::TripleTerm`](super::term::Term::TripleTerm).
33 /// Contains a [`GroundAtom`] whose argument list contains
34 /// only further `GroundTerm` values; recursive nesting is
35 /// bounded by
36 /// [`MAX_TRIPLE_TERM_NESTING`](super::term::MAX_TRIPLE_TERM_NESTING)
37 /// (enforced at IR
38 /// [`Program::validate`](super::program::Program::validate)
39 /// time, before grounding).
40 TripleTerm(Box<GroundAtom>),
41}
42
43impl GroundTerm {
44 /// Construct a flat-constant ground term. Convenience for
45 /// the common case where the underlying value is a plain
46 /// [`ConstantSymbol`].
47 #[must_use]
48 pub fn constant(c: ConstantSymbol) -> Self {
49 Self::Constant(c)
50 }
51
52 /// Pull out the wrapped [`ConstantSymbol`] when this is the
53 /// constant form. Returns `None` for [`GroundTerm::TripleTerm`].
54 #[must_use]
55 pub fn as_constant(&self) -> Option<&ConstantSymbol> {
56 match self {
57 Self::Constant(c) => Some(c),
58 Self::TripleTerm(_) => None,
59 }
60 }
61}
62
63impl From<ConstantSymbol> for GroundTerm {
64 fn from(c: ConstantSymbol) -> Self {
65 Self::Constant(c)
66 }
67}
68
69/// Ground form of an [`Atom`](super::atom::Atom) — the value
70/// form with no variables anywhere in the argument tree. Used
71/// inside [`GroundTerm::TripleTerm`] so quoted-triple facts can
72/// be stored alongside flat facts in a fact set (TT1).
73#[derive(
74 Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize,
75)]
76#[serde(deny_unknown_fields)]
77pub struct GroundAtom {
78 /// Predicate of the ground atom.
79 pub predicate: PredicateSymbol,
80 /// Ground arguments in positional order.
81 pub args: Vec<GroundTerm>,
82}
83
84impl GroundAtom {
85 /// Arity of the ground atom.
86 #[must_use]
87 pub fn arity(&self) -> usize {
88 self.args.len()
89 }
90}
91
92/// Display a [`GroundTerm`]: constants as their raw symbol,
93/// quoted-triple terms as `<< s p o >>` per N-Triples-star
94/// syntax (mirroring [`Term`](super::term::Term)'s display
95/// contract).
96impl fmt::Display for GroundTerm {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 match self {
99 Self::Constant(c) => write!(f, "{}", c.as_str()),
100 Self::TripleTerm(atom) => {
101 if atom.args.len() == 2 {
102 write!(
103 f,
104 "<< {} {} {} >>",
105 atom.args[0],
106 atom.predicate.as_str(),
107 atom.args[1],
108 )
109 } else {
110 write!(f, "<< {atom} >>")
111 }
112 }
113 }
114 }
115}
116
117/// Display a [`GroundAtom`] as `predicate(arg_1, arg_2, …)`.
118/// Used by [`GroundTerm`]'s [`std::fmt::Display`] impl for non-2-arity
119/// triple-term values.
120impl fmt::Display for GroundAtom {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 write!(f, "{}(", self.predicate.as_str())?;
123 for (idx, arg) in self.args.iter().enumerate() {
124 if idx > 0 {
125 write!(f, ", ")?;
126 }
127 write!(f, "{arg}")?;
128 }
129 write!(f, ")")
130 }
131}