I would like to embed a Prolog interpreter within a C program, so that it can be used to evaluate business rules.
Both SWI-Prolog and GNU Prolog seem to hint at having such a feature, but both also seem to require the use of their own compiler and linker in order to do so, and don't have any clear examples or documentation of this usage - rather than providing a regular library to use and clear documentation.
What I'm searching for is a library that behaves similarly to how Python and Lua can be embedded in a C program, e.g.:
// Setup
PrologContext context = plNewContext();
plConsult(context, "businessRules.pl");
// Later in the program
if (plEvaluate(context, "can_view_page(%a)", path)) { ...
Unlike many other programming languages, Prolog is intended primarily as a declarative programming language. In prolog, logic is expressed as relations (called as Facts and Rules). Core heart of prolog lies at the logic being applied. Formulation or Computation is carried out by running a query over these relations.
If you want to exit SWI-Prolog, issue the command halt., or simply type CTRL-d at the SWI-Prolog prompt.
To check if a variable is bound to a list, you can use is_list/1 .
Aren't most prolog implementations written in C or C++? GNU Prolog is an open-source implementation of Prolog written in C. You can download the sources and peek inside :-) Hope this helps. I bet you'd get a lot out of Warren's Abstract Machine: A Tutorial Reconstruction, which discusses how to implement Prolog in a procedural language.
Consulting prolog files into other prolog program techniques. So far we have seen that we can write a program and the query on the console to execute. In some cases, we print something on the console, that are written in our prolog code. So here we will see that writing and reading tasks in more detail using prolog.
We can use put (C) to write one character at a time into the current output stream. The output stream can be a file or the console. This C can be a character or an ASCII code in other version of Prolog like SWI prolog, but in GNU prolog, it supports only the ASCII value. To use the character instead of ASCII, we can use put_char (C).
In terms of finding the code, the unification algorithm is easiest to find, then implementations with back chaining imbedded in applications. Finding a fully functional implementation of Prolog in a functional language with an REPL is the hardest.
Here is a small example that uses between/3
.
#include <stdio.h>
#include <SWI-Prolog.h>
int main()
{
char *argv[] = {"hello", "-q"};
PL_initialise(2, argv);
term_t a = PL_new_term_refs(3);
PL_put_integer(a, 11); // First argument to between/3
PL_put_integer(a+1, 17); // Second argument to between/3
static predicate_t p;
if(!p)
p = PL_predicate("between", 3, NULL) ; // predicate between/3
qid_t q = PL_open_query(NULL, PL_Q_NORMAL, p, a);
int tmp;
while(PL_next_solution(q)==TRUE){
PL_get_integer(a+2, &tmp); // Third argument to between/3
printf("%d\n", tmp);
}
PL_close_query(q);
return 0;
}
Compile it using swipl-ld
and execute
swipl-ld -o between between.c
./between
11
12
13
14
15
16
17
Another example if you want to load a file and then do a query.
#include <stdio.h>
#include <SWI-Prolog.h>
void consult_file(const char* filename){
/* PL_initialse before calling. */
predicate_t p = PL_predicate("consult", 1, NULL);
term_t a = PL_new_term_ref();
PL_put_atom_chars(a, filename);
PL_call_predicate(NULL, PL_Q_NORMAL, p, a);
}
int main()
{
char* argv[] = {"consult_file", "-q"};
PL_initialise(2, argv);
consult_file("digits.pl"); /* Load prolog file */
static predicate_t p;
/* third argument is module name if the predicate is in one.*/
if(!p) p= PL_predicate("digit", 1, NULL);
int tmp; term_t a = PL_new_term_ref();
qid_t q = PL_open_query(NULL, PL_Q_NORMAL, p, a);
while(PL_next_solution(q)==TRUE){ /* All solutions */
PL_get_integer(a, &tmp);
printf("%d ", tmp);
}
return PL_halt(0);
}
With digits.pl
file as follows we get the three numbers 1 2 3
as output.
digit(1).
digit(2).
digit(3).
I can't speak for GNU Prolog but SWI-Prolog is definitely well-documented.
Note, that various Prolog implementations will have their own specific interfaces, nothing is standardized. The ISO standard says nothing (on second thoughts, all the better actually. It would probably be a mess set in stone; nobody seems to be implementing the standardized modules approach either).
The undertaking is certainly fraught with uninteresting implementation details, but this is about C. In no way are special compilers or linkers needed.
But there is a much cooler way (i.e. one adapted to 2020 and how IT should actually work, namely as a bunch of communicating processes, each dedicated to a domain of the overall task): create a separate server process hosting Prolog to answer queries, (an SWI-Prolog engine, "Pengine") and access it via HTTP from your C-coded client process: Pengine. There is no example code on setting up the HTTP exchange from a C program though. It will depend on your HTTP client library.
(not really an answer; just a few notes on GNU Prolog vs SWI-Prolog in this scenario that don't fit a comment)
GNU Prolog provides several advantages that may or may not be relevant in your particular case. First, it can generate executable code (something that SWI-Prolog cannot do) that you can easily distribute to anyone. Second, it have (out-of-the-box) fewer dependencies, which greatly simplifies deployment. For example, on my macOS system:
$ otool -L `which gprolog`
/opt/local/bin/gprolog:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.50.4)
versus:
$ otool -L `which swipl`
/Users/pmoura/bin/swipl:
/opt/local/lib/libtcmalloc_minimal.4.dylib (compatibility version 10.0.0, current version 10.3.0)
@rpath/libswipl.8.dylib (compatibility version 8.0.0, current version 8.3.14)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
$ otool -L /Users/pmoura/lib/swipl/lib/x86_64-darwin/libswipl.8.3.14.dylib
/Users/pmoura/lib/swipl/lib/x86_64-darwin/libswipl.8.3.14.dylib:
@rpath/libswipl.8.dylib (compatibility version 8.0.0, current version 8.3.14)
/opt/local/lib/libncurses.6.dylib (compatibility version 6.0.0, current version 6.0.0)
/opt/local/lib/libform.6.dylib (compatibility version 6.0.0, current version 6.0.0)
/opt/local/lib/libgmp.10.dylib (compatibility version 15.0.0, current version 15.1.0)
/opt/local/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With