oxilean_std/logic_programming/
types.rs1use std::collections::HashMap;
4
5#[derive(Debug, Clone, PartialEq)]
9pub enum LpTerm {
10 Atom(String),
12 Var(String),
14 Compound {
16 functor: String,
18 args: Vec<LpTerm>,
20 },
21 Integer(i64),
23 Float(f64),
25 List(Vec<LpTerm>, Option<Box<LpTerm>>),
27}
28
29impl LpTerm {
30 pub fn atom(s: impl Into<String>) -> Self {
32 Self::Atom(s.into())
33 }
34
35 pub fn var(s: impl Into<String>) -> Self {
37 Self::Var(s.into())
38 }
39
40 pub fn compound(functor: impl Into<String>, args: Vec<LpTerm>) -> Self {
42 Self::Compound {
43 functor: functor.into(),
44 args,
45 }
46 }
47
48 pub fn list(items: Vec<LpTerm>) -> Self {
50 Self::List(items, None)
51 }
52
53 pub fn list_with_tail(items: Vec<LpTerm>, tail: LpTerm) -> Self {
55 Self::List(items, Some(Box::new(tail)))
56 }
57
58 pub fn is_ground(&self) -> bool {
60 match self {
61 Self::Var(_) => false,
62 Self::Atom(_) | Self::Integer(_) | Self::Float(_) => true,
63 Self::Compound { args, .. } => args.iter().all(|a| a.is_ground()),
64 Self::List(items, tail) => {
65 items.iter().all(|a| a.is_ground()) && tail.as_ref().map_or(true, |t| t.is_ground())
66 }
67 }
68 }
69
70 pub fn variables(&self) -> Vec<String> {
72 let mut vars = Vec::new();
73 self.collect_vars(&mut vars);
74 vars
75 }
76
77 fn collect_vars(&self, out: &mut Vec<String>) {
78 match self {
79 Self::Var(v) => out.push(v.clone()),
80 Self::Compound { args, .. } => {
81 for a in args {
82 a.collect_vars(out);
83 }
84 }
85 Self::List(items, tail) => {
86 for a in items {
87 a.collect_vars(out);
88 }
89 if let Some(t) = tail {
90 t.collect_vars(out);
91 }
92 }
93 _ => {}
94 }
95 }
96}
97
98#[derive(Debug, Clone)]
102pub struct LpClause {
103 pub head: LpTerm,
105 pub body: Vec<LpTerm>,
107}
108
109impl LpClause {
110 pub fn fact(head: LpTerm) -> Self {
112 Self { head, body: vec![] }
113 }
114
115 pub fn rule(head: LpTerm, body: Vec<LpTerm>) -> Self {
117 Self { head, body }
118 }
119
120 pub fn is_fact(&self) -> bool {
122 self.body.is_empty()
123 }
124}
125
126#[derive(Debug, Clone, Default)]
128pub struct LpDatabase {
129 pub clauses: Vec<LpClause>,
131}
132
133impl LpDatabase {
134 pub fn new() -> Self {
136 Self { clauses: vec![] }
137 }
138
139 pub fn add_fact(&mut self, head: LpTerm) {
141 self.clauses.push(LpClause::fact(head));
142 }
143
144 pub fn add_rule(&mut self, head: LpTerm, body: Vec<LpTerm>) {
146 self.clauses.push(LpClause::rule(head, body));
147 }
148
149 pub fn matching_clauses(&self, goal: &LpTerm) -> Vec<&LpClause> {
151 let (goal_f, goal_a) = functor_arity(goal);
152 self.clauses
153 .iter()
154 .filter(|c| {
155 let (hf, ha) = functor_arity(&c.head);
156 hf == goal_f && ha == goal_a
157 })
158 .collect()
159 }
160}
161
162pub(super) fn functor_arity(t: &LpTerm) -> (&str, usize) {
164 match t {
165 LpTerm::Atom(s) => (s.as_str(), 0),
166 LpTerm::Compound { functor, args } => (functor.as_str(), args.len()),
167 LpTerm::Integer(_) => ("$int", 0),
168 LpTerm::Float(_) => ("$float", 0),
169 LpTerm::Var(_) => ("$var", 0),
170 LpTerm::List(items, None) => ("$list", items.len()),
171 LpTerm::List(items, Some(_)) => ("$list_partial", items.len()),
172 }
173}
174
175#[derive(Debug, Clone, Default)]
179pub struct Substitution {
180 pub bindings: HashMap<String, LpTerm>,
182}
183
184impl Substitution {
185 pub fn new() -> Self {
187 Self {
188 bindings: HashMap::new(),
189 }
190 }
191
192 pub fn bind(&mut self, var: impl Into<String>, term: LpTerm) {
194 self.bindings.insert(var.into(), term);
195 }
196
197 pub fn extend(&self, other: &Substitution) -> Substitution {
199 let mut result = self.clone();
200 for (k, v) in &other.bindings {
201 result.bindings.insert(k.clone(), v.clone());
202 }
203 result
204 }
205
206 pub fn lookup(&self, var: &str) -> Option<&LpTerm> {
208 self.bindings.get(var)
209 }
210
211 pub fn deref(&self, var: &str) -> LpTerm {
213 match self.bindings.get(var) {
214 None => LpTerm::Var(var.to_string()),
215 Some(LpTerm::Var(v2)) if v2 != var => self.deref(v2),
216 Some(t) => t.clone(),
217 }
218 }
219}
220
221#[derive(Debug, Clone)]
225pub struct Query {
226 pub goals: Vec<LpTerm>,
228}
229
230impl Query {
231 pub fn new(goals: Vec<LpTerm>) -> Self {
233 Self { goals }
234 }
235
236 pub fn single(goal: LpTerm) -> Self {
238 Self { goals: vec![goal] }
239 }
240}
241
242#[derive(Debug, Clone)]
246pub enum ResolutionResult {
247 Success(Substitution),
249 Failure,
251 Error(String),
253}
254
255#[derive(Debug, Clone)]
259pub struct SolveConfig {
260 pub max_depth: usize,
262 pub max_solutions: usize,
264 pub occurs_check: bool,
266}
267
268impl Default for SolveConfig {
269 fn default() -> Self {
270 Self {
271 max_depth: 512,
272 max_solutions: 1024,
273 occurs_check: false,
274 }
275 }
276}
277
278impl SolveConfig {
279 pub fn new() -> Self {
281 Self::default()
282 }
283}