Is there any reasonable way to embed a Prolog interpreter inside of a C program?

Viewed 590

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)) { ...
3 Answers

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)
Related