1pub mod pretty;
16pub use pretty::PrettyPrint;
17
18use bumpalo::Bump;
19use rustc_hash::FxHashMap;
20use std::cell::RefCell;
21use std::mem;
22use std::num::NonZeroUsize;
23use std::sync::{Arc, Mutex};
24
25const INTERNER_DEFAULT_CAPACITY: NonZeroUsize = NonZeroUsize::new(4096).unwrap();
26
27pub struct Interner {
31 map: FxHashMap<&'static str, u32>,
32 vec: Vec<&'static str>,
33 buf: String,
34 full: Vec<String>,
35}
36
37impl Interner {
38 fn new_global_interner() -> Arc<Mutex<Interner>> {
39 Arc::new(Mutex::new(Interner::with_capacity(
40 INTERNER_DEFAULT_CAPACITY.into(),
41 )))
42 }
43
44 pub fn with_capacity(cap: usize) -> Interner {
45 let cap = cap.next_power_of_two();
46 let mut interner = Interner {
47 map: FxHashMap::default(),
48 vec: Vec::new(),
49 buf: String::with_capacity(cap),
50 full: Vec::new(),
51 };
52 interner.intern("_");
53 interner
54 }
55
56 pub fn intern(&mut self, name: &str) -> u32 {
58 if let Some(&id) = self.map.get(name) {
59 return id;
60 }
61 let name = unsafe { self.alloc(name) };
62 let id = self.map.len() as u32;
63 self.map.insert(name, id);
64 self.vec.push(name);
65 debug_assert!(self.lookup(id).expect("expected to find name") == name);
66 debug_assert!(self.intern(name) == id);
67 id
68 }
69
70 pub fn lookup_name_index(&self, name: &str) -> Option<u32> {
71 self.map.get(name).copied()
72 }
73
74 fn lookup(&self, id: u32) -> Option<&'static str> {
75 self.vec.get(id as usize).copied()
76 }
77
78 unsafe fn alloc(&mut self, name: &str) -> &'static str {
84 let cap = self.buf.capacity();
85 if cap < self.buf.len() + name.len() {
86 let new_cap = (cap.max(name.len()) + 1).next_power_of_two();
87 let new_buf = String::with_capacity(new_cap);
88 let old_buf = mem::replace(&mut self.buf, new_buf);
89 self.full.push(old_buf);
90 }
91 let interned = {
92 let start = self.buf.len();
93 self.buf.push_str(name);
94 &self.buf[start..]
95 };
96 unsafe { &*(interned as *const str) }
99 }
100}
101
102pub struct Arena {
103 pub(crate) bump: Bump,
104 pub(crate) interner: Arc<Mutex<Interner>>,
105 pub(crate) predicate_syms: RefCell<Vec<PredicateSym>>,
106 pub(crate) function_syms: RefCell<Vec<FunctionSym>>,
107}
108
109impl<'arena> Arena {
110 pub fn new(interner: Arc<Mutex<Interner>>) -> Self {
111 Self {
112 bump: Bump::new(),
113 interner,
114 predicate_syms: RefCell::new(Vec::new()),
115 function_syms: RefCell::new(Vec::new()),
116 }
117 }
118
119 pub fn new_with_global_interner() -> Self {
120 Self::new(Interner::new_global_interner())
121 }
122
123 pub fn intern(&'arena self, name: &str) -> u32 {
124 self.interner.lock().unwrap().intern(name)
125 }
126
127 pub fn lookup_opt(&'arena self, name: &str) -> Option<u32> {
129 self.interner.lock().unwrap().map.get(name).copied()
130 }
131
132 pub fn name(&'arena self, name: &str) -> Const<'arena> {
133 Const::Name(self.intern(name))
134 }
135
136 pub fn variable(&'arena self, name: &str) -> &'arena BaseTerm<'arena> {
137 self.alloc(BaseTerm::Variable(self.variable_sym(name)))
138 }
139
140 pub fn const_(&'arena self, c: Const<'arena>) -> &'arena BaseTerm<'arena> {
141 self.alloc(BaseTerm::Const(c))
142 }
143
144 pub fn atom(
145 &'arena self,
146 p: PredicateIndex,
147 args: &[&'arena BaseTerm<'arena>],
148 ) -> &'arena Atom<'arena> {
149 self.alloc(Atom {
150 sym: p,
151 args: self.alloc_slice_copy(args),
152 })
153 }
154
155 pub fn apply_fn(
156 &'arena self,
157 fun: FunctionIndex,
158 args: &[&'arena BaseTerm<'arena>],
159 ) -> &'arena BaseTerm<'arena> {
160 let args = self.alloc_slice_copy(args);
163 self.alloc(BaseTerm::ApplyFn(fun, args))
164 }
165
166 pub fn alloc<T>(&self, x: T) -> &mut T {
167 self.bump.alloc(x)
168 }
169
170 pub fn alloc_slice_copy<T: Copy>(&self, x: &[T]) -> &[T] {
171 self.bump.alloc_slice_copy(x)
172 }
173
174 pub fn alloc_str(&'arena self, s: &str) -> &'arena str {
175 self.bump.alloc_str(s)
176 }
177
178 pub fn new_query(&'arena self, p: PredicateIndex) -> Atom<'arena> {
179 let arity = self.predicate_syms.borrow()[p.0].arity;
180 let args: Vec<_> = match arity {
181 Some(arity) => (0..arity).map(|_i| &ANY_VAR_TERM).collect(),
182 None => Vec::new(),
183 };
184
185 let args = self.alloc_slice_copy(&args);
186 Atom { sym: p, args }
187 }
188
189 pub fn lookup_name(&self, name_index: u32) -> Option<&'static str> {
191 self.interner.lock().unwrap().lookup(name_index)
192 }
193
194 pub fn lookup_name_index(&self, name: &str) -> Option<u32> {
196 self.interner.lock().unwrap().lookup_name_index(name)
197 }
198
199 pub fn predicate_name(&self, predicate_index: PredicateIndex) -> Option<&'static str> {
201 let syms = self.predicate_syms.borrow();
202 let i = predicate_index.0;
203 if i >= syms.len() {
204 return None;
205 }
206 let n = syms[i].name;
207 self.interner.lock().unwrap().lookup(n)
208 }
209
210 pub fn predicate_arity(&self, predicate_index: PredicateIndex) -> Option<u8> {
212 self.predicate_syms
213 .borrow()
214 .get(predicate_index.0)
215 .and_then(|s| s.arity)
216 }
217
218 pub fn function_name(&self, function_index: FunctionIndex) -> Option<&'static str> {
220 let syms = self.function_syms.borrow();
221 let i = function_index.0;
222 if i >= syms.len() {
223 return None;
224 }
225 let n = syms[i].name;
226 self.interner.lock().unwrap().lookup(n)
227 }
228
229 pub fn lookup_predicate_sym(&'arena self, predicate_name: u32) -> Option<PredicateIndex> {
231 for (index, p) in self.predicate_syms.borrow().iter().enumerate() {
232 if p.name == predicate_name {
233 return Some(PredicateIndex(index));
234 }
235 }
236 None
237 }
238
239 pub fn variable_sym(&'arena self, name: &str) -> VariableIndex {
241 let n = self.interner.lock().unwrap().intern(name);
242 VariableIndex(n)
243 }
244
245 pub fn function_sym(&'arena self, name: &str, arity: Option<u8>) -> FunctionIndex {
247 let n = self.interner.lock().unwrap().intern(name);
248 let f = FunctionSym { name: n, arity };
249 for (index, f) in self.function_syms.borrow().iter().enumerate() {
250 if f.name == n {
251 return FunctionIndex(index);
252 }
253 }
254
255 self.function_syms.borrow_mut().push(f);
256 FunctionIndex(self.function_syms.borrow().len() - 1)
257 }
258
259 pub fn predicate_sym(&'arena self, name: &str, arity: Option<u8>) -> PredicateIndex {
261 let n = self.interner.lock().unwrap().intern(name);
262 let p = PredicateSym { name: n, arity };
263 for (index, p) in self.predicate_syms.borrow().iter().enumerate() {
264 if p.name == n {
265 return PredicateIndex(index);
266 }
267 }
268
269 self.predicate_syms.borrow_mut().push(p);
270 PredicateIndex(self.predicate_syms.borrow().len() - 1)
271 }
272
273 pub fn copy_function_sym<'src>(
274 &'arena self,
275 src: &'src Arena,
276 f: FunctionIndex,
277 ) -> FunctionIndex {
278 let function_sym = &src.function_syms.borrow()[f.0];
279 let name = src
280 .lookup_name(function_sym.name)
281 .expect("expected to find name");
282 self.function_sym(name, function_sym.arity)
283 }
284
285 pub fn copy_predicate_sym<'src>(
286 &'arena self,
287 src: &'src Arena,
288 p: PredicateIndex, ) -> PredicateIndex {
290 let predicate_sym = &src.predicate_syms.borrow()[p.0];
291 let name = src
292 .lookup_name(predicate_sym.name)
293 .expect("expected to find name");
294 self.predicate_sym(name, predicate_sym.arity)
295 }
296
297 pub fn copy_atom<'src>(
299 &'arena self,
300 src: &'src Arena,
301 atom: &'src Atom<'src>,
302 ) -> &'arena Atom<'arena> {
303 let args: Vec<_> = atom
304 .args
305 .iter()
306 .map(|arg| self.copy_base_term(src, arg))
307 .collect();
308 let args = self.alloc_slice_copy(&args);
309 self.alloc(Atom {
311 sym: self.copy_predicate_sym(src, atom.sym),
312 args,
313 })
314 }
315
316 pub fn copy_base_term<'src>(
318 &'arena self,
319 src: &'src Arena,
320 b: &'src BaseTerm<'src>,
321 ) -> &'arena BaseTerm<'arena> {
322 match b {
323 BaseTerm::Const(c) =>
324 {
326 self.alloc(BaseTerm::Const(*self.copy_const(src, c)))
327 }
328 BaseTerm::Variable(v) => {
329 let name = src
330 .interner
331 .lock()
332 .unwrap()
333 .lookup(v.0)
334 .expect("expected to find name")
335 .to_string();
336 let v = self.variable_sym(&name);
337 self.alloc(BaseTerm::Variable(v))
338 }
339 BaseTerm::ApplyFn(fun, args) => {
340 let fun = self.copy_function_sym(src, *fun);
341 let args: Vec<_> = args.iter().map(|a| self.copy_base_term(src, a)).collect();
343 let args = self.alloc_slice_copy(&args);
344 self.alloc(BaseTerm::ApplyFn(fun, args))
345 }
346 }
347 }
348
349 pub fn copy_const<'src>(
351 &'arena self,
352 src: &'src Arena,
353 c: &'src Const<'src>,
354 ) -> &'arena Const<'arena> {
355 match c {
356 Const::Name(name) => {
357 let name = src
358 .interner
359 .lock()
360 .unwrap()
361 .lookup(*name)
362 .expect("expected to find name");
363 let name = self.interner.lock().unwrap().intern(name);
364 self.alloc(Const::Name(name))
365 }
366 Const::Bool(b) => self.alloc(Const::Bool(*b)),
367 Const::Number(n) => self.alloc(Const::Number(*n)),
368 Const::Float(f) => self.alloc(Const::Float(*f)),
369 Const::Time(t) => self.alloc(Const::Time(*t)),
370 Const::Duration(d) => self.alloc(Const::Duration(*d)),
371 Const::String(s) => {
372 let s = self.alloc_str(s);
373 self.alloc(Const::String(s))
374 }
375 Const::Bytes(b) => {
376 let b = self.alloc_slice_copy(b);
377 self.alloc(Const::Bytes(b))
378 }
379 Const::List(cs) => {
380 let cs: Vec<_> = cs.iter().map(|c| self.copy_const(src, c)).collect();
381 let cs = self.alloc_slice_copy(&cs);
382 self.alloc(Const::List(cs))
383 }
384 Const::Map { keys, values } => {
385 let keys: Vec<_> = keys.iter().map(|c| self.copy_const(src, c)).collect();
386 let keys = self.alloc_slice_copy(&keys);
387
388 let values: Vec<_> = values.iter().map(|c| self.copy_const(src, c)).collect();
389 let values = self.alloc_slice_copy(&values);
390
391 self.alloc(Const::Map { keys, values })
392 }
393 Const::Struct { fields, values } => {
394 let fields: Vec<_> = fields.iter().map(|s| self.alloc_str(s)).collect();
395 let fields = self.alloc_slice_copy(&fields);
396
397 let values: Vec<_> = values.iter().map(|c| self.copy_const(src, c)).collect();
398 let values = self.alloc_slice_copy(&values);
399
400 self.alloc(Const::Struct { fields, values })
401 }
402 }
403 }
404
405 pub fn copy_transform<'src>(
406 &'arena self,
407 src: &'src Arena,
408 stmt: &'src TransformStmt<'src>,
409 ) -> &'arena TransformStmt<'arena> {
410 let TransformStmt { var, app } = stmt;
411 let var = var.map(|s| self.alloc_str(s));
412 let app = self.copy_base_term(src, app);
413 self.alloc(TransformStmt { var, app })
414 }
415
416 pub fn copy_clause<'src>(
417 &'arena self,
418 src: &'src Arena,
419 src_clause: &'src Clause<'src>,
420 ) -> &'arena Clause<'arena> {
421 let Clause {
422 head,
423 head_time,
424 premises,
425 transform,
426 } = src_clause;
427 let premises: Vec<_> = premises.iter().map(|x| self.copy_term(src, x)).collect();
428 let transform: Vec<_> = transform
429 .iter()
430 .map(|x| self.copy_transform(src, x))
431 .collect();
432 self.alloc(Clause {
433 head: self.copy_atom(src, head),
434 head_time: *head_time,
435 premises: self.alloc_slice_copy(&premises),
436 transform: self.alloc_slice_copy(&transform),
437 })
438 }
439
440 fn copy_term<'src>(
441 &'arena self,
442 src: &'src Arena,
443 term: &'src Term<'src>,
444 ) -> &'arena Term<'arena> {
445 match term {
446 Term::Atom(atom) => {
447 let atom = self.copy_atom(src, atom);
448 self.alloc(Term::Atom(atom))
449 }
450 Term::NegAtom(atom) => {
451 let atom = self.copy_atom(src, atom);
452 self.alloc(Term::NegAtom(atom))
453 }
454 Term::Eq(left, right) => {
455 let left = self.copy_base_term(src, left);
456 let right = self.copy_base_term(src, right);
457 self.alloc(Term::Eq(left, right))
458 }
459 Term::Ineq(left, right) => {
460 let left = self.copy_base_term(src, left);
461 let right = self.copy_base_term(src, right);
462 self.alloc(Term::Ineq(left, right))
463 }
464 Term::TemporalAtom(atom, interval) => {
465 let atom = self.copy_atom(src, atom);
466 self.alloc(Term::TemporalAtom(atom, *interval))
467 }
468 }
469 }
470}
471
472#[derive(Debug)]
483pub struct Unit<'a> {
484 pub decls: &'a [&'a Decl<'a>],
485 pub clauses: &'a [&'a Clause<'a>],
486}
487
488#[derive(Debug, Clone, Copy, PartialEq, Eq)]
494pub enum TemporalBound {
495 Timestamp(i64),
497 Variable(VariableIndex),
499 NegInf,
501 PosInf,
503}
504
505#[derive(Debug, Clone, Copy, PartialEq, Eq)]
508pub struct Interval {
509 pub start: TemporalBound,
510 pub end: TemporalBound,
511}
512
513#[derive(Debug, Clone, Copy, PartialEq)]
517pub struct Decl<'a> {
518 pub atom: &'a Atom<'a>,
519 pub descr: &'a [&'a Atom<'a>],
520 pub bounds: Option<&'a [&'a BoundDecl<'a>]>,
521 pub constraints: Option<&'a Constraints<'a>>,
522 pub is_temporal: bool,
523}
524
525#[derive(Debug, PartialEq)]
526pub struct BoundDecl<'a> {
527 pub base_terms: &'a [&'a BaseTerm<'a>],
528}
529
530#[derive(Debug, Clone, PartialEq)]
532pub struct Constraints<'a> {
533 pub consequences: &'a [&'a Atom<'a>],
535 pub alternatives: &'a [&'a [&'a Atom<'a>]],
537}
538
539#[derive(Debug)]
540pub struct Clause<'a> {
541 pub head: &'a Atom<'a>,
542 pub head_time: Option<Interval>,
544 pub premises: &'a [&'a Term<'a>],
545 pub transform: &'a [&'a TransformStmt<'a>],
546}
547
548#[derive(Debug)]
549pub struct TransformStmt<'a> {
550 pub var: Option<&'a str>,
551 pub app: &'a BaseTerm<'a>,
552}
553
554#[derive(Debug, Clone, Copy, PartialEq, Eq)]
556pub enum Term<'a> {
557 Atom(&'a Atom<'a>),
558 NegAtom(&'a Atom<'a>),
559 Eq(&'a BaseTerm<'a>, &'a BaseTerm<'a>),
560 Ineq(&'a BaseTerm<'a>, &'a BaseTerm<'a>),
561 TemporalAtom(&'a Atom<'a>, Interval),
563}
564
565impl std::fmt::Display for Term<'_> {
566 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
567 match self {
568 Term::Atom(atom) => write!(f, "{atom}"),
569 Term::NegAtom(atom) => write!(f, "!{atom}"),
570 Term::Eq(left, right) => write!(f, "{left} = {right}"),
571 Term::Ineq(left, right) => write!(f, "{left} != {right}"),
572 Term::TemporalAtom(atom, interval) => {
573 write!(f, "{atom}@[{}, {}]", interval.start, interval.end)
574 }
575 }
576 }
577}
578
579impl std::fmt::Display for TemporalBound {
580 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
581 match self {
582 TemporalBound::Timestamp(nanos) => write!(f, "t#{nanos}"),
583 TemporalBound::Variable(v) => write!(f, "{v}"),
584 TemporalBound::NegInf | TemporalBound::PosInf => write!(f, "_"),
585 }
586 }
587}
588
589impl<'a> Term<'a> {
590 pub fn apply_subst(
591 &'a self,
592 arena: &'a Arena,
593 subst: &FxHashMap<u32, &'a BaseTerm<'a>>,
594 ) -> &'a Term<'a> {
595 &*arena.alloc(match self {
596 Term::Atom(atom) => Term::Atom(atom.apply_subst(arena, subst)),
597 Term::NegAtom(atom) => Term::NegAtom(atom.apply_subst(arena, subst)),
598 Term::Eq(left, right) => Term::Eq(
599 left.apply_subst(arena, subst),
600 right.apply_subst(arena, subst),
601 ),
602 Term::Ineq(left, right) => Term::Ineq(
603 left.apply_subst(arena, subst),
604 right.apply_subst(arena, subst),
605 ),
606 Term::TemporalAtom(atom, interval) => {
607 Term::TemporalAtom(atom.apply_subst(arena, subst), *interval)
608 }
609 })
610 }
611}
612
613#[derive(Debug, Clone, Copy, PartialEq, Eq)]
614pub enum BaseTerm<'a> {
615 Const(Const<'a>),
616 Variable(VariableIndex),
617 ApplyFn(FunctionIndex, &'a [&'a BaseTerm<'a>]),
618}
619
620impl<'arena> BaseTerm<'arena> {
621 pub fn apply_subst(
622 &'arena self,
623 arena: &'arena Arena,
624 subst: &FxHashMap<u32, &'arena BaseTerm<'arena>>,
625 ) -> &'arena BaseTerm<'arena> {
626 match self {
627 BaseTerm::Const(_) => self,
628 BaseTerm::Variable(v) => subst.get(&v.0).unwrap_or(&self),
629 BaseTerm::ApplyFn(fun, args) => {
630 let args: Vec<&'arena BaseTerm<'arena>> = args
631 .iter()
632 .map(|arg| arg.apply_subst(arena, subst))
633 .collect();
634 let args = arena.alloc_slice_copy(&args);
635 arena.alloc(BaseTerm::ApplyFn(*fun, args))
636 }
637 }
638 }
639}
640
641impl std::fmt::Display for BaseTerm<'_> {
642 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
643 match self {
644 BaseTerm::Const(c) => write!(f, "{c}"),
645 BaseTerm::Variable(v) => write!(f, "{v}"),
646 BaseTerm::ApplyFn(fun, args) => {
647 write!(
648 f,
649 "{fun}({})",
650 args.iter()
651 .map(|x| x.to_string())
652 .collect::<Vec<_>>()
653 .join(",")
654 )
655 }
656 }
657 }
658}
659#[derive(Debug, Clone, Copy, PartialEq)]
660pub enum Const<'a> {
661 Name(u32),
662 Bool(bool),
663 Number(i64),
664 Float(f64),
665 String(&'a str),
666 Bytes(&'a [u8]),
667 Time(i64),
669 Duration(i64),
671 List(&'a [&'a Const<'a>]),
672 Map {
673 keys: &'a [&'a Const<'a>],
674 values: &'a [&'a Const<'a>],
675 },
676 Struct {
677 fields: &'a [&'a str],
678 values: &'a [&'a Const<'a>],
679 },
680}
681
682impl Eq for Const<'_> {}
683
684impl std::fmt::Display for Const<'_> {
685 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
686 match *self {
687 Const::Name(v) => write!(f, "n${v}"),
688 Const::Bool(v) => write!(f, "{v}"),
689 Const::Number(v) => write!(f, "{v}"),
690 Const::Float(v) => write!(f, "{v}"),
691 Const::String(v) => write!(f, "{v}"),
692 Const::Bytes(v) => write!(f, "{v:?}"),
693 Const::Time(v) => write!(f, "t#{v}"),
694 Const::Duration(v) => write!(f, "d#{v}"),
695 Const::List(v) => {
696 write!(
697 f,
698 "[{}]",
699 v.iter()
700 .map(|x| x.to_string())
701 .collect::<Vec<_>>()
702 .join(", ")
703 )
704 }
705 Const::Map { keys, values } => {
706 if keys.is_empty() {
707 write!(f, "fn:map()")
708 } else {
709 write!(f, "[")?;
710 for (i, (k, v)) in keys.iter().zip(values.iter()).enumerate() {
711 if i > 0 {
712 write!(f, ", ")?;
713 }
714 write!(f, "{k}: {v}")?;
715 }
716 write!(f, "]")
717 }
718 }
719 Const::Struct { fields, values } => {
720 write!(f, "{{")?;
721 for (i, (field, val)) in fields.iter().zip(values.iter()).enumerate() {
722 if i > 0 {
723 write!(f, ", ")?;
724 }
725 write!(f, "{field}: {val}")?;
726 }
727 write!(f, "}}")
728 }
729 }
730 }
731}
732
733#[derive(Debug, Clone, PartialEq, Eq, Hash)]
734pub struct PredicateSym {
735 pub name: u32,
736 pub arity: Option<u8>,
737}
738
739#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
740pub struct PredicateIndex(usize);
741
742impl std::fmt::Display for PredicateIndex {
743 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
744 write!(f, "p${}", self.0)
745 }
746}
747
748#[derive(Debug, Clone, PartialEq, Eq, Hash)]
749pub struct FunctionSym {
750 pub name: u32,
751 pub arity: Option<u8>,
752}
753
754#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
755pub struct FunctionIndex(usize);
756
757impl std::fmt::Display for FunctionIndex {
758 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
759 write!(f, "f${}", self.0)
760 }
761}
762
763#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
764pub struct VariableIndex(pub u32);
765
766impl std::fmt::Display for VariableIndex {
767 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
768 if self.0 == 0 {
769 write!(f, "_")
770 } else {
771 write!(f, "v${}", self.0)
772 }
773 }
774}
775
776#[derive(Debug, Clone, Copy, PartialEq, Eq)]
777pub struct Atom<'a> {
778 pub sym: PredicateIndex,
779 pub args: &'a [&'a BaseTerm<'a>],
780}
781
782impl<'a> Atom<'a> {
783 pub fn matches(&'a self, query_args: &[&BaseTerm]) -> bool {
787 for (fact_arg, query_arg) in self.args.iter().zip(query_args.iter()) {
788 if let BaseTerm::Const(_) = query_arg
789 && fact_arg != query_arg
790 {
791 return false;
792 }
793 }
794 true
795 }
796
797 pub fn apply_subst(
798 &'a self,
799 arena: &'a Arena,
800 subst: &FxHashMap<u32, &'a BaseTerm<'a>>,
801 ) -> &'a Atom<'a> {
802 let args: Vec<&'a BaseTerm<'a>> = self
803 .args
804 .iter()
805 .map(|arg| arg.apply_subst(arena, subst))
806 .collect();
807 arena.atom(self.sym, &args)
808 }
809}
810
811impl std::fmt::Display for Atom<'_> {
812 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
813 write!(f, "{}(", self.sym)?;
814 for (i, arg) in self.args.iter().enumerate() {
815 if i > 0 {
816 write!(f, ", ")?;
817 }
818 write!(f, "{arg}")?;
819 }
820 write!(f, ")")
821 }
822}
823
824static ANY_VAR_TERM: BaseTerm = BaseTerm::Variable(VariableIndex(0));
825
826#[cfg(test)]
827mod tests {
828 use super::*;
829 use googletest::prelude::*;
830
831 #[test]
832 fn copying_atom_works() {
833 let arena = Arena::new_with_global_interner();
834 let foo = arena.const_(arena.name("/foo"));
835 let bar = arena.predicate_sym("bar", Some(1));
836 let head = arena.atom(bar, &[foo]);
837 assert_that!(head.to_string(), eq("p$0(n$1)"));
838 }
839
840 #[test]
841 fn atom_display_works() {
842 let arena = Arena::new_with_global_interner();
843 let bar = arena.const_(arena.name("/bar"));
844 let sym = arena.predicate_sym("foo", Some(1));
845 let atom = Atom { sym, args: &[bar] };
846 assert_that!(atom, displays_as(eq("p$0(n$1)")));
847
848 let tests = vec![
849 (Term::Atom(&atom), "p$0(n$1)"),
850 (Term::NegAtom(&atom), "!p$0(n$1)"),
851 (Term::Eq(bar, bar), "n$1 = n$1"),
852 (Term::Ineq(bar, bar), "n$1 != n$1"),
853 ];
854 for (term, s) in tests {
855 assert_that!(term, displays_as(eq(s)));
856 }
857 }
858
859 #[test]
860 fn new_query_works() {
861 let arena = Arena::new_with_global_interner();
862
863 let pred = arena.predicate_sym("foo", Some(1));
864 let query = arena.new_query(pred);
865 assert_that!(query, displays_as(eq("p$0(_)")));
866
867 let pred = arena.predicate_sym("bar", Some(2));
868 let query = arena.new_query(pred);
869 assert_that!(query, displays_as(eq("p$1(_, _)")));
870
871 let pred = arena.predicate_sym("frob", None);
872 let query = arena.new_query(pred);
873 assert_that!(query, displays_as(eq("p$2()")));
874 }
875
876 #[test]
877 fn subst_works() {
878 let arena = Arena::new_with_global_interner();
879 let atom = arena.atom(arena.predicate_sym("foo", Some(1)), &[arena.variable("x")]);
880
881 let mut subst = FxHashMap::default();
882 subst.insert(arena.variable_sym("x").0, arena.const_(arena.name("/bar")));
883
884 let subst_atom = atom.apply_subst(&arena, &subst);
885 assert_that!(arena.name("/bar"), displays_as(eq("n$3")));
886 assert_that!(subst_atom, displays_as(eq("p$0(n$3)")));
887 }
888
889 #[test]
890 fn do_intern_beyond_initial_capacity() {
891 let arena = Arena::new_with_global_interner();
892
893 let p = arena.predicate_sym("/foo", Some(1));
894 let mut name = "".to_string();
895 for _ in 0..INTERNER_DEFAULT_CAPACITY.into() {
896 name += "a";
897 }
898 arena.interner.lock().unwrap().intern(&name);
899 assert_that!(arena.predicate_name(p), eq(Some("/foo")));
900 }
901
902 #[test]
903 fn pretty_print_works() {
904 let arena = Arena::new_with_global_interner();
905 let foo = arena.const_(arena.name("/foo"));
906 let bar_pred = arena.predicate_sym("bar", Some(1));
907 let x_var = arena.variable("X");
908 let head = arena.atom(bar_pred, &[x_var]); let premise = Term::Eq(x_var, foo);
911 let premise_ref = arena.alloc(premise);
912
913 let clause = Clause {
914 head,
915 head_time: None,
916 premises: arena.alloc_slice_copy(&[premise_ref]),
917 transform: &[],
918 };
919
920 assert_that!(clause.pretty(&arena).to_string(), eq("bar(X) :- X = /foo."));
921
922 let fun = arena.function_sym("f", Some(1));
923 let app = arena.apply_fn(fun, &[x_var]);
924 assert_that!(app.pretty(&arena).to_string(), eq("f(X)"));
925 }
926}