1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
//! Prolog predicates.
//!
//! A prolog predicate is a core datatype in prolog. It is a
//! combination of a functor and a module. Just like functors and
//! modules, predicates are not reference-counted and are never
//! garbage collected.
//!
//! This module provides functors and types for intearcting with
//! prolog predicates.
use std::convert::TryInto;
use super::atom::*;
use super::engine::*;
use super::fli::*;
use super::functor::*;
use super::module::*;
/// A wrapper for a prolog predicate.
#[derive(Clone, Copy)]
pub struct Predicate {
predicate: predicate_t,
}
// a `predicate_t` is a pointer which is normally not send or
// sync. SWI-Prolog however guarantees that this pointer will remain
// valid.
unsafe impl Send for Predicate {}
unsafe impl Sync for Predicate {}
impl Predicate {
/// Wrap a `predicate_t`, which is how the SWI-Prolog fli represents predicates.
///
/// # Safety
/// This is unsafe because no check is done to ensure that the
/// predicate_t indeed points at a valid predicate. The caller
/// will have to ensure that this is the case.
pub unsafe fn wrap(predicate: predicate_t) -> Self {
Self { predicate }
}
/// Create a new predicate from the given functor and module.
///
/// This will panic if no prolog engine is active on this thread.
pub fn new(functor: Functor, module: Module) -> Self {
assert_some_engine_is_active();
let predicate = unsafe { PL_pred(functor.functor_ptr(), module.module_ptr()) };
unsafe { Self::wrap(predicate) }
}
/// Return the underlying `predicate_t` which SWI-Prolog uses to refer to the predicate.
pub fn predicate_ptr(&self) -> predicate_t {
self.predicate
}
/// Retrieve the name of this predicate as an atom and pass it into the given function.
///
/// The atom does not outlive this call, and the reference count
/// is never incremented. This may be slightly faster in some
/// cases than returning the name directly.
///
/// This will panic if no prolog engine is active on this thread.
pub fn with_name<F, R>(&self, func: F) -> R
where
F: Fn(&Atom) -> R,
{
assert_some_engine_is_active();
let mut atom: atom_t = 0;
unsafe {
PL_predicate_info(
self.predicate,
&mut atom,
std::ptr::null_mut(),
std::ptr::null_mut(),
);
}
let atom = unsafe { Atom::wrap(atom) };
let result = func(&atom);
std::mem::forget(atom);
result
}
/// Retrieve the name of this predicate as an atom.
///
/// This will panic if no prolog engine is active on this thread.
pub fn name(&self) -> Atom {
self.with_name(|n| n.clone())
}
/// Retrieve the name of this predicate as a string.
///
/// This will panic if no prolog engine is active on this thread.
pub fn name_string(&self) -> String {
self.with_name(|n| n.name())
}
/// Retrieve the arity of this predicate.
///
/// This will panic if no prolog engine is active on this thread.
pub fn arity(&self) -> u16 {
assert_some_engine_is_active();
let mut arity = 0;
unsafe {
PL_predicate_info(
self.predicate,
std::ptr::null_mut(),
&mut arity,
std::ptr::null_mut(),
);
}
arity.try_into().unwrap()
}
/// Retrieve the module of this predicate.
///
/// this will panic if no prolog engine is active on this thread.
pub fn module(&self) -> Module {
assert_some_engine_is_active();
let mut module: module_t = std::ptr::null_mut();
unsafe {
PL_predicate_info(
self.predicate,
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut module,
);
Module::wrap(module)
}
}
}