Interfacing wamcc and C ----------------------- (written: Jan, 27th 1995) (updated: Mar, 7th 1995) (diaz@margaux.inria.fr) WARNING: this documentation is very provisory and a better inteface facility will be soon available. I- Calling C From Prolog ------------------------ I.1- Using pragma_c/1 --------------------- The predicate pragma_c(Code) allows the user to directly include C code in Prolog predicates. Code is an atom corresponding to the C code. Generally Code is the name of a Macro defined in the .usr file. pragma_c succeeds if the C succeeds. The C code fails if it calls 'fail'. Note that you can only call 'fail' in the macro. If you want to fail inside a function, this function must return a boolean which will be tested by the macro to call 'fail' if necessary. Prolog arguments are passed in registers A(0),...,A(n-1) (arity=n). Remarks: 1 - without looking at the C code produced, you only know the content of a A(i) register at the begining of a clause p(X,Y):- pragma_c('My_Macro'), q(...), ... you know that X is in A(0), Y in A(1) p(X,Y):- q(...), pragma_c('My_Macro'), ... when My_Macro is executed A(0) no longer contains X since A(0) has been set before the call to q/2. The solutions consists in invoking an intermediate predicate which only calls the macros, e.g : p(X,Y):- q(...), intermediate(X,Y), ... intermediate(_X,_Y):- pragma_c('My_Macro'). 2 - Any call to a C library function (e.g. printf) must be encapsulated into a call to a macro Libn(fct,arg1,...,argn) (n in 0..5 = nb of arguments) so, instead of: printf("\nC_usr: X is %s.\n",X); \ printf("\nC_usr: Y is %s.\n\n",Y); \ use: Lib2(printf,"\nC_usr: X is %s.\n",X); \ Lib2(printf,"\nC_usr: Y is %s.\n\n",Y); \ this is (for the moment) only required for Sparc machines because wamcc uses some registers which are destroyed by C lib functions. So on this machine Libn(..) macros save and restore these registers. 3- integers multiplications/divisions/modulos must be encapsulated into calls to M_Mul/M_Div/M_Mod (for the same reason as above) ex: int z=M_Mul(x,y); I.2- Getting A(i) Register Data from C -------------------------------------- The content of each argument is accessed with the Deref macro: Deref(A(i),word,tag,adr) where i is the argument number (0..arity-1) (type: int) word is the word associated to A(i) (type: WamWord) tag is the type of the datum (type: WamWord) adr is only valid for variables (type: WamWord *) and indicates the address of the variable Data types (see 'tag'): tag==REF a prolog variable (whose address is in 'adr') tag==INT an integer tag==CST an atom (also the empty list []) tag==LST a list [car|cdr] taf==STC a structure f(x1,...,xn) The macros UnTag_xxx (where xxx is INT/CST/REF/LST/REF) extract from 'word' the datum associated to the type. The result is: for REF: the address (same as the content of 'adr') for INT: the integer for CST: a pointer into the hash table where are stored the constants each record has a type AtomInf * typedef struct /* Atom information */ { /* ------------------------------ */ char *name; /* key is (the string) */ int type; /* IDENTIFIER, SYMBOL, SOLO, OTHER*/ Bool has_quote; /* does name contain any ' ? */ Bool needs_quote; /* does name need ' around it ? */ int length; /* its length (in characters) */ GVarElt g_elem; /* global variable element */ }AtomInf; There is a field 'name' containing a pointer to the string. for LST: a pointer to 2 consecutive words: the car and the cdr. If 'lst_adr' is this pointer then: Car(lst_adr) is a macro accessing to the car Cdr(lst_adr) is a macro accessing to the cdr example: WamWord *lst_adr=UnTag_LST(word); Deref(car(lst_adr),word,tag,adr) <... car treatment...> Deref(cdr(lst_adr),word,tag,adr) <... cdr treatment...> for STC: a pointer to n+1 consecutive words (1 for functor/n and n for the n sub-terms) If 'stc_adr' is this pointer then: Functor(stc_adr) is a macro accessing to the functor (AtomInf *). Arity(stc_adr) is a macro accessing to the arity 'n'. Arg(stc_adr,i) is a macro accessing to the ith arg (i in 0..n-1). Several examples can be found in Builtin/*.usr (in particular: write.usr and lib_inline.c): Example: We want to define a predicate p(X,Y) which must call a fct foo(int x,char *y) passing X (must be an integer) and Y an atom. in your file_name.pl write: p(X,Y):- pragma_c('P_2'). in your file_name.usr write: #define P_2 \ { \ int x; \ AtomInf *atom; \ char *y; \ \ Deref(A(0),word,tag,adr) \ x=UnTag_INT(word); \ Deref(A(1),word,tag,adr) \ atom=UnTag_CST(word); \ y=atom->name; \ foo(x,y); \ } I.3- Unifying A(i) Registers and C Data --------------------------------------- The following functions performing unifications return a boolean to indicate the issue of the unification. The unification of the A(i) register is as follows: a variable or an unknown datum in 'w' (type: WamWord): Unify(w,A(i)); However, if you know the type of the C datum you can use specific functions which are more efficient than the general unification fonction. an integer 'n': Get_Integer(n,A(i)); a constant 'atom': Get_Constant(atom,A(i)); (type of 'atom': AtomInf *). To create a new atom from a string 'str' use: atom=Create_Allocate_Atom(str); which adds the string 'str' to the hash table and returns atom, a pointer to the created structure. an empty list (particular case of constant): Get_Constant(atom_nil,A(i)); atom_nil is a global variable predefined or: Get_Nil(A(i)); a list [car|cdr]: Get_List(A(i)); see unification of sub-term instructions below. a structure f(t1,...,tm): Get_Structure(atom_f,m,A(i)); : atom_f is the atom corresponding to the functor 'f' (see above how to define new atoms from strings) a new variable (which will be also stored in A(j)): Unify_Variable(&A(j)); (this function does not return a value since it always succeeds) a variable already stored in A(j) (i.e. bound): Unify_Value(A(j)); an integer 'n': Unify_Integer(n); a constant 'atom': Unify_Constant(atom); an empty list: Unify_Constant(atom_nil); or Unify_Nil(); Example: square(X,Y):- pragma_c('Square_2'). #define Square_2 \ { \ int x; \ \ Deref(A(0),word,tag,adr) \ x=UnTag_INT(word); \ x=x*x; \ if (!Get_Integer(x,A(1))) \ fail } II. Calling Prolog From C ------------------------- II.1- The Prolog Code --------------------- - Define your Prolog code as a module (without :- main/1 declaration). - Define as public (:- public/1) all predicates that can be called from C. In the following we assume that your Prolog module is named 'modul.pl' and that you can call the predicate 'pred/n' II.2- The C Code ---------------- In the following we assume that the main C program is called prog.c The structure of the C code is as follows: /* prog.c */ #include "wam_interface.h" main(int argc,char *argv[]) { Pl_Init_Wam_And_Module(argc,argv,modul) : : /* call to pred/n */ Pl_ReInit_Wam <...load reg A(0)...> : <...load reg A(n-1)...> Pl_Query(PredHex,n,code_else) /* find the first solution */ /* or execute 'code_else' */ <..get the content of A(i) registers intially set to a variable> : : /* if other solutions wanted */ Pl_Next_Solution(code_else) <..get the content of A(i) registers intially set to a variable> : : Pl_Term_Wam } II.3- Loading A(i) Registers ---------------------------- Before calling a n-ary predicate, registers A(0)..A(n-1) must be initialized. The initialization of A(i) is as follows (the fonctions Put_... do not return any value): a new variable: Put_X_Variable(i,j); Also initialize A(j) with the same variable, you can thus use Put_Variable(i,i); if only A(i) must be initialized. a variable already stored in A(j) (i.e. bound): A(i)=A(j); an integer 'n': Put_Integer(n,i); a constant 'atom': Put_Constant(atom,i); an empty list: Put_Constant(atom_nil,i); or Put_Nil(i); a list [car|cdr]: Put_List(i) see above for the unification of sub terms. In this mode these instructions always succeed (i.e. they always return TRUE). a structure f(t1,...,tm): Put_Structure(atom_f,m,i) : atom_f is the atom corresponding to the functor 'f' (see above how to define new atoms from strings) II.4- Controling The Execution ------------------------------ The following macros are defined in wam_interface.h: Pl_Init_Wam_And_Module(argc,argv,m) Intialize the Prolog engine and the module 'm' (if more than 1 module are needed this macro can be easily modified). Must be called before any other interface function/macro. Pl_ReInit_Wam Reinitialize the Prolog engine (e.g. reset all stack pointers). Generally this is used before a new query (before the loading of A(i) registers for the new query). In fact for the first query, this call is useless. The effect of this macros is to re-initialize all stacks and thus the Prolog terms contained in the heap are lost. So it is not safe to keep pointers on them. Pl_Term_Wam Stop the Prolog engine. Must be called after any other interface function/macro. Pl_Query(predhex,arity,code_else) calls the predicate whose internal name is 'predhex' and arity is 'arity'. This predicate must be has been defined as public (see above). code_else is the code to execute if the query fails. The internal name of a predicate is the name provided by pl2hex utility (see wamcc user's manual). ex: % pl2hex append so, after loading A(0), A(1) and A(2) we can execute Pl_Query(X617070656E64,3,goto error;) Pl_Next_Solution(code_else) bactracks to find next solution of the last query. If no more alternatives exists, code_else is executed. II.5- Compiling the Files ------------------------- To produce the executable 'prog' you need to: - compile the Prolog module modul to produce the file a modul.o % wamcc modul % w_gcc -c modul.c - compile the C program 'prg.c' to produce the file a prog.o % w_gcc -c prog.c - link them together to provide the executable 'prog' % w_gcc -o prog prog.o modul.o -lwamcc It is possible to use a Makefile, and more, bmf_wamcc can generate it (see wamcc user's manual) For example: % bmf -v -o prog prog.o modul II.6- An Example ---------------- We want to be able to ask about the grand-parents of a data base (predicate anc/2). 'modul.pl' is the following program: /* modul.pl */ :- public anc/2. % we want to call it from C anc(X,Z):- father(X,Y), father(Y,Z). father(a,b). father(a,c). father(b,e). father(b,d). father(c,f). father(c,g). father(e,h). father(d,i). father(f,j). father(f,k). The prog.c file will ask about queries of the form 'anc(a,X)', and will find all solutions. /* prog.c */ #include "wam_interface.h" #define ANC X616E63 /* thanks to pl2hex anc */ int main(int argc,char *argv[]) { char str[10]; AtomInf *atom; WamWord word,tag,*adr; WamWord ans_word; Pl_Init_Wam_And_Module(argc,argv,modul) start: Pl_ReInit_Wam /* useful for next queries*/ Lib1(printf,"\nEnter a name (a/b/c/d)\n"); Lib1(gets,str); /* Read a constant */ atom=Create_Allocate_Atom(str); /* add this new constant */ Put_Constant(atom,0); /* A(0)=the atom created */ Put_X_Variable(1,1); /* A(1)=new var (unbound) */ ans_word=A(1); /* save word which will */ /* contain the answer */ Pl_Query(ANC,2,goto no_more;) /* query: anc(atom,Var) */ for(;;) { Deref(ans_word,word,tag,adr) /* get the aswer */ atom=UnTag_CST(word); /* It's an atom. */ Lib2(printf,"Solution : %s\n",atom->name); /* write the solution */ /* simpler: Simple_Write_Term(ans_word); which writes a Prolog term*/ Pl_Next_Solution(break;) /* find other solutions */ } no_more: Lib1(printf,"No more Solutions\n"); Lib1(printf,"Other Query : "); Lib1(gets,str); if (*str=='y') goto start; Pl_Term_Wam return 0; } Example of execution: % prog Enter a name (a/b/c/d) a Solution : e Solution : d Solution : f Solution : g No more Solutions Other Query : y Enter a name (a/b/c/d) b Solution : h Solution : i No more Solutions Other Query : n % III- Bugs --------- Please report (detailled) bugs to diaz@margaux.inria.fr.