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 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
//! swipl-macros provides procedural macros for the swipl crate.
#![doc(html_root_url = "https://terminusdb-labs.github.io/swipl-rs/swipl_macros/")]
mod kw;
mod util;
mod atom;
mod blob;
mod functor;
mod pred;
mod predicate;
mod prolog;
mod term;
use proc_macro::TokenStream;
/// Define prolog predicates to be used from rust code.
///
/// The `prolog!` macro takes a block of function declaration. Each
/// declaration is of the format `fn <predicate>(..args..);`, where
/// args is a list of argument names. Optionally, a visibility
/// specifier like `pub` may be used. This much like an ordinary rust
/// function declaration, except that the arguments are typeless.
///
/// Each function may be annotated with a `#[name("..name..")]`
/// attribute, which specifies what the name of this predicate is in
/// prolog. When omitted, the function name will be used.
///
/// Each function may be annotated with a `#[module("..module..")]`
/// attribute, which specifies what prolog module this predicate is
/// in. When omitted, no module is assumed.
///
/// For each function declaration, a function will be generated which
/// takes a context argument, followed by all declared arguments, and
/// returns a query opened in that context.
///
/// Example:
/// ```ignore
/// prolog! {
/// fn writeq(term);
/// #[name("nl")]
/// fn print_a_newline();
/// #[module("zlib")]
/// pub fn zopen(stream, zstream, options);
/// }
/// ```
#[proc_macro]
pub fn prolog(stream: TokenStream) -> TokenStream {
prolog::prolog_macro(stream)
}
/// Generate an inline callable predicate.
///
/// Predicates are specified in prolog style. You can either use the
/// syntax `predicate/arity` or `module:predicate/arity`.
///
/// This macro will generate code that generates a `CallablePredicate`
/// object. The actual prolog predicate is only looked up at first
/// call of this generated code. Each subsequent call will reuse the
/// lookup.
///
/// example:
/// ```ignore
/// let writeq = pred!(writeq/1);
/// context.call_once(writeq, [&term])?;
/// ```
/// ```ignore
/// context.call_once(pred!(user:nl/0), [])?;
/// ```
#[proc_macro]
pub fn pred(stream: TokenStream) -> TokenStream {
pred::pred_macro(stream)
}
/// Define foreign predicates written in rust for use in prolog.
///
/// The `predicates!` macro takes an arbitrary amount of predicate
/// definitions. These definitions may be semidet or
/// nondet. Optionally, a visibility specifier like `pub` may be used
/// to change the visibility of the generated functions. These
/// definitions look somewhat like ordinary rust functions. However,
/// their argument list is completely untyped, as each argument is
/// known to be a `&Term`, except for the first argument which is a
/// context object. As there always needs to be a context to call a
/// predicate, this first argument is required, even if it is unused.
///
/// For each definition, a registration function will be
/// generated. This function will be named `register_<name>`, where
/// name is the name of the defined predicate, and this function will
/// take zero arguments. After calling this function, the predicate is
/// registered and may be used from prolog code.
///
/// Each definition may optionally be annotated with
/// `#[module("<module>")]` and/or `#[name("<name>")]` To change the
/// module this predicate will be registered in, or the name of the
/// predicate in prolog. By default, the predicate name will be the
/// definition name, and the module will be the context module at the
/// time of generation. For foreign libraries, this context module is
/// whatever module did the first import of this library. Otherwise
/// it's usually 'user'.
///
/// # Semideterministic predicates
/// The first kind of predicate that you can define is a semidet
/// predicate. Semidet, or semideterministic, means that this
/// predicate is only going to have one result, and it could either be
/// success or failure. Note that this also covers the deterministic
/// case - to implement a deterministic predicate, just ensure that
/// your predicate does not fail.
///
/// Semidet predicates return a `PrologResult<()>`, which also happens
/// to be the type returned by most of the functions in the `swipl`
/// library. This means you can easily handle failure and exception of
/// things you call using rust's `?` syntax, or make use of the various combinators that are defined on `context` objects and in the `result` module.
///
/// ## Examples
/// ```ignore
/// predicates! {
/// semidet fn unify_with_foo(context, term) {
/// let atom = context.new_atom("foo");
/// term.unify(&atom)
/// }
///
/// #[module("some_module")]
/// #[name("some_alternate_name")]
/// pub semidet fn term_is_42(_context, term) {
/// let num: u64 = term.get::<u64>()?;
///
/// into_prolog_result(num == 42)
/// }
///
/// semidet fn just_fail(_context) {
/// Err(PrologError::Failure)
/// }
///
/// pub semidet fn throw_if_not_42(_context, term) {
/// let num: u64 = term.get::<u64>()?;
/// if num != 42 {
/// context.raise_exception(&term!{context: error(this_is_not_the_answer, _)})
/// } else {
/// Ok(())
/// }
/// }
/// }
/// ```
/// To register these defined predicates, their register function has to be called:
/// ```ignore
/// register_unify_with_foo();
/// register_term_is_42();
/// register_just_fail();
/// register_throw_if_not_42();
/// ```
///
/// # Nondeterministic predicates
/// Nondet or nondeterministic predicates are a bit more complex to
/// implement. Instead of just one block which returns success or
/// failure, nondet predicates are implemented with two bodies, a
/// setup block and a call block.
///
/// In the setup block, you create a state object which will be
/// available in the call block. The call block is then called with
/// this state object. As long as the call block returns true, the
/// predicate call is considered to still have choice points and will
/// be called unless the caller does a cut, which will clean up the
/// state object automatically.
///
/// ## The state type
/// Nondeterministic predicate definitions require you to specify a
/// type argument as part of the function signature. This specifies
/// the type of the state object, and is required to implement the
/// auto-traits `Send` and `Unpin`.
///
/// ## Setup
/// The setup block is called at the start of a predicate
/// invocation. It is to return a `PrologResult<Option<StateObject>>`,
/// where `StateObject` is your state object type.
///
/// You can return from this block in three ways:
/// - Return an exception or failure. The predicate will error or fail accordingly and the call block will not be invoked.
/// - Return `None`. The call block will also not be invoked, but the
/// predicate will return success. This is useful to handle predicate
/// inputs which allow your predicate to behave in a semidet manner.
/// - Return `Some(object)`. This returns a state object for use in
/// the call block. After this, the call block will be invoked.
///
/// ## Call
/// The call block is called each time the next result is required from this predicate. This happens on the first call to this predicate (except if the setup returned early as described above), and subsequently upon backtracking. The call block is given a mutable borrow of the state object, and is therefore able to both inspect and modify it.
///
/// you can return from this block in three ways:
/// - Return an exception or failure. Thep redicate will error or fail accordingly, and the call block will not be invoked again.
/// - Return false, signaling that this was the last succesful call to this predicate.
/// - Return true, signaling that there's more results available upon backtracking.
///
/// After exception, failure or returning false to signal the last succesful call, the state object will be cleaned up automatically.
///
/// ## Examples
/// ```ignore
/// predicates!{
/// nondet fn unify_with_bar_baz<Vec<String>>(context, term) {
/// setup => {
/// Ok(Some(vec!["bar", "baz"]))
/// },
/// call(v) => {
/// let next = v.pop().unwrap();
/// let atom = context.new_atom(next);
/// term.unify(&atom)?;
///
/// Ok(!v.is_empty())
/// }
/// }
///
/// nondet fn fail_early<()>(_context) {
/// setup => {
/// Err(PrologError::Failure)
/// },
/// call(_) => {
/// // We never get here
/// }
/// }
///
/// nondet fn succeed_early<()>(_context) {
/// setup => {
/// Ok(None)
/// },
/// call(_) => {
/// // We never get here
/// }
/// }
/// }
/// ```
#[proc_macro]
pub fn predicates(stream: TokenStream) -> TokenStream {
predicate::predicates_macro(stream)
}
/// Generate a term from a rust expression.
///
/// This macro takes two arguments, a context to generate the term in,
/// and a rust expression representing the prolog term to generate..
///
/// The macro returns a `PrologResult<Term>` containing new term,
/// created through `context.new_term_ref()`, which contains a prolog
/// term corresponding to the description. The `term!` macro cannot
/// actually fail with a PrologFailure, but it is possible for a
/// resource limit exception to be triggered by one of the underlying
/// calls into prolog.
///
/// # Examples
/// Generate a nested functor term:
/// ```ignore
/// let term = term!{context: foo(bar(baz, quux))}?;
///```
///
/// Embed a value in the term:
/// ```ignore
/// let num = 42;
/// let term = term!{context: foo(#42)}?;
/// ```
///
/// Embed a term in the term:
/// ```ignore
/// let inner = context.new_term_ref();
/// let term = term!{context: foo(#&inner)}?;
/// ```
#[proc_macro]
pub fn term(stream: TokenStream) -> TokenStream {
term::term_macro(stream)
}
/// Define an arc blob.
///
/// See the [swipl::blob](https://terminusdb-labs.github.io/swipl-rs/swipl/blob/index.html) module documentation for more details.
#[proc_macro_attribute]
pub fn arc_blob(attr: TokenStream, item: TokenStream) -> TokenStream {
blob::arc_blob_macro(attr, item)
}
/// Define a wrapped arc blob.
///
/// See the [swipl::blob](https://terminusdb-labs.github.io/swipl-rs/swipl/blob/index.html) module documentation for more details.
#[proc_macro]
pub fn wrapped_arc_blob(item: TokenStream) -> TokenStream {
blob::wrapped_arc_blob_macro(item)
}
/// Define a clone blob.
///
/// See the [swipl::blob](https://terminusdb-labs.github.io/swipl-rs/swipl/blob/index.html) module documentation for more details.
#[proc_macro_attribute]
pub fn clone_blob(attr: TokenStream, item: TokenStream) -> TokenStream {
blob::clone_blob_macro(attr, item)
}
/// Define a wrapped clone blob.
///
/// See the [swipl::blob](https://terminusdb-labs.github.io/swipl-rs/swipl/blob/index.html) module documentation for more details.
#[proc_macro]
pub fn wrapped_clone_blob(item: TokenStream) -> TokenStream {
blob::wrapped_clone_blob_macro(item)
}
/// Create a static atom.
///
/// The atom will only actually be created on first invocation. Each
/// subsequent invocation will reuse the earlier retrieved atom. This
/// is convenient for code that uses the exact same atom on repeated
/// invocations.
#[proc_macro]
pub fn atom(item: TokenStream) -> TokenStream {
atom::atom_macro(item)
}
/// Create a static functor.
///
/// The functor will only actually be created on first
/// invocation. Each subsequent invocation will reuse the earlier
/// retrieved functor. This is convenient for code that uses the exact
/// same functor on repeated invocations.
#[proc_macro]
pub fn functor(item: TokenStream) -> TokenStream {
functor::functor_macro(item)
}