Lucrarea nr. 12
Interfata intre SWI-Prolog si C




Legatura se poate face in mod dinamic sau in mod static. In cazul legarii dinamice, extensiile C (care sunt interpretate ca niste predicate PROLOG) sunt compilate intr-o biblioteca dinamica (fisier .so), care poate fi incarcata cu ajutorul predicatului predefinit:

    load_foreign_library( Fisier_so )

In cazul legarii statice, sursele C si Prolog sunt compilate impreuna cu comanda 'plld'pentru a genera un executabil.

Tipurile C corespondente celor din Prolog sunt definite in "SWI-Prolog.h". Cele mai importante sunt 'term_t', 'atom_t' si 'functor_t'. Aceste tipuri sunt de fapt referinte la structuri opace folosite in Prolog.

Aceste referinte se creeaza prin urmatoarele functii:

    term_t PL_new_term_ref();
    atom_t PL_new_atom(char*);
    functor_t PL_new_functor(atom_t name, int arity);

Pentru crearea termenilor sunt prevazute functiile 'PL_put_...':

    void PL_put_atom(term_t t, atom_t a);
    void PL_put_integer(term_t t, long i);
    void PL_put_float(term_t t, double f);
    void PL_cons_functor(term_t h, functor_t f, ... /* args */);
    void PL_cons_list(term_t list, term_t head, term_t tail);
    void PL_put_nil(term_t list);

Tipul unui termen se poate obtine cu:

    int PL_term_type(term_t);
        /* returneaza: PL_VARIABLE PL_ATOM PL_STRING PL_INTEGER PL_FLOAT PL_TERM */

Valoarea unui termen se obtine cu ajutorul functiilor:

    int PL_get_atom(term_t t, atom_t *a);
    int PL_get_atom_chars(term_t t, char **s);
    int PL_get_integer(term_t t, int *i);
    int PL_get_float(term_t t, double *f);
    int PL_get_functor(term_t t, functor_t *f);
    int PL_get_arg(int index, term_t t, term_t a);
    int PL_get_list(term_t list, term_t head, term_t tail);
        /* returneaza TRUE daca corespunde tipul */

    char* PL_atom_chars(atom_t);
    atom_t PL_functor_name(functor_t f);
    int PL_functor_arity(functor_t f);

Unificarea termenilor se face prin:

    int PL_unify(term_t t1, term_t t2);
    int PL_unify_atom(term_t t, atom_t a);
    int PL_unify_atom_chars(term_t t, const char *chars);
    int PL_unify_integer(term_t t, long n);
    int PL_unify_float(term_t t, double f);
    int PL_unify_functor(term_t t, functor_t f);
    int PL_unify_arg(int index, term_t t, term_t a);
    int PL_unify_list(term_t list, term_t head, term_t tail);
    int PL_unify_nil(term_t list);

Din limbajul C, predicatele PROLOG pot fi apelate cu:

    int PL_call(term_t goal, module_t m);
        /* al doilea argument deobicei este NULL */

Invers, pentru ca functii scrise in C sa fie apelabile in Prolog, ele trebuie sa fie 'inregistrate'.

Daca utilizam modul de legare dinamica, atunci biblioteca .so (scrisa in C) trebuie sa contina o functie 'install_t install()' care este formata din apeluri la functia:

    int PL_register_foreign(name, arity, function, flags);

In modul static, aceasta este rezolvata printr-un apel la functia:

    void PL_register_extensions(PL_extension *e);

unde argumentul are forma:

    PL_extension e[] =
    {
        /*{ "name", arity,  function, PL_FA_<flags> },*/
        { NULL, 0,  NULL,  0 }    /* terminating line */
    };

O functie C apelabila in Prolog trebuie sa aiba tipul 'foreign_t', avand numarul de argumente egal cu aritatea declarata. Daca functia este nedeterminista (in flags este setat PL_FA_NONDETERMINISTIC), atunci ea mai are un ultim argument in plus, 'foreign_t handle', care poate avea valorile: PL_FIRST_CALL, PL_REDO si PL_CUTTED.

Revenirea din functie se face prin 'PL_succedd()', 'PL_fail()', sau (in plus pentru o functie nedeterminista) 'PL_retry_address(void * context)'. Contextul
salvat (daca este posibila revenirea la o noua solutie) se restaureaza la un apel PL_REDO cu ajutorul functiei:

    void * PL_foreign_context_address(control_t handle)

Un exemplu pentru modul in care se implementeaza o astfel de functie este dat in fisierul "dltest.c".

Problema: Construiti un program (executabil), care dupa ce saluta utilizatorul citeste in mod repetat expresii, afisind rezultatul evaluarii expresiei. Programul se termina cind utilizatorul introduce stop.

Exemplu:
    shell$ calc

    Hello john
    > 1+3
    4
    > stop

    shell$

Observatii: