Search code examples
clistgnu-prolog

Getting list elements from Prolog in a C interface


I try to implement a C interface using a Prolog script based on GNU Prolog. My problem is to get single elements of a nested Prolog list.

Actually my C code looks like

...
int func;
PlTerm arg[10];
PlTerm *sol_gb;
PlBool res;
int nmb; 
char *strHead;
char *strTail;
PlLong nummero;
PlTerm pl_nummero;

Pl_Start_Prolog(argc, argv);


Pl_Query_Begin(PL_TRUE);

arg[0] = Pl_Mk_String(strRName);
arg[1] = Pl_Mk_Variable();
arg[2] = Pl_Mk_Variable();
arg[3] = Pl_Mk_String("true");

res = Pl_Query_Call(func, 4, arg);

sol_gb = Pl_Rd_List(arg[2]);
nmb = Pl_List_Length(sol_gb[0]);

strHead = Pl_Write_To_String(sol_gb[0]);      
printf("strHead = %s\n",strHead);
strTail = Pl_Write_To_String(sol_gb[1]);      
printf("strTail = %s\n",strTail);
...

The Prolog list returned in arg[2] looks like

[ [ Spezial Bolognese, 
    [2, ,Zwiebeln,300,gramm,Hackfleisch,10, ,Tomaten,
    100,ml,Sahne,500,gramm,Spaghetti] 
  ],
  [ Spaghetti Bolognese,
    [2, ,Zwiebeln gehackt,300,gramm,Hackfleisch,10, ,Fleischtomaten,
     100,ml,Sahne,500,gramm,Spaghetti]
  ]
]

The output of the conversion into a String is

strHead = [Spezial Bolognese,[2, ,Zwiebeln gehackt,300,gramm,Hackfleisch,
          10, ,Fleischtomaten,100,ml,Sahne,500,gramm,Spaghetti]]

strTail = [[Spaghetti Bolognese,[2, ,Zwiebeln gehackt,300,gramm,Hackfleisch,
          10, ,Fleischtomaten,100,ml,Sahne,500,gramm,Spaghetti]]]

So I assume, I am "nearly there" but as I have to re-activate my C knowledge I do not get the solution how to enter in the next level of the list to get finally each element as string ("Spezial Bolognese", next step: "2", "Zwiebeln" etc.).

How can I step through the Prolog list in C?

I would be very happy about every hint, thank you again!


Solution

  • To get the content of a list from the C-code you can use 2 kind of functions.

    First possibility (simple since the list is seen as a flat object but requires more memory and needs a proper list, i.e. does not work for lists not terminated by [])

    int Pl_Rd_Proper_List_Check(PlTerm the_prolog_list, PlTerm *the_array_receiving_arguments);
    

    this functions receives an array (it is up to you to ensure it is large enough), stores each element of the list in the array and returns the total number of elements. Example:

    PlTerm list = ...some Prolog list...
    int nElem = Pl_List_Length(list);
    PlTerm *elem = (PlTerm *) calloc(nElem, sizeof(PlTerm));
    Pl_Rd_Proper_List_Check(list, elem);
    int i;
    for(i = 0; i < nElem; i++) { 
       // here there is an argument in elem[i], let's print it
       Pl_Write(elem[i]);
    }
    

    Second possibility (more general but see a list as a chained list, each cell contains the head and a tail (a list))

    PlTerm *Pl_Rd_List(PlTerm the_prolog_list);
    

    This function returns an array of 2 elements corresponding to the head and the tail of the received list. This function should be called on each element of the list; either you know the number of elements or you test the end of the list (e.g. waiting for the end of list atom []). Here is a code doing this (it is expected to be inside the above loop since we know the second argument of the list is a nested list.

    PlTerm list = ... some Prolog list...;
    while(!Pl_Un_Atom(Pl_Atom_Nil(), list)) {
       PlTerm *lst_arg = Pl_Rd_List(list); // [0] = head element, [1] = tail list
       // here there is an argument in lst_arg[0], let's print it
       Pl_Write(lst_arg[0]);
       list = lst_arg[1];
    }
    

    In your example, the first list looks like:

    [ 'Spezial Bolognese', 
        [2,' ','Zwiebeln',
         300,'gramm','Hackfleisch',
         10,' ','Tomaten',
         100,'ml','Sahne',
         500,'gramm','Spaghetti'] 
    ]
    

    so the second element is a nested list. The following code uses the first method for the above list (which has 2 elements), and the second method for the nested list:

    nElem = Pl_List_Length(sol_gb[0]);
    PlTerm *elem = (PlTerm *) calloc(nElem, sizeof(PlTerm));
    Pl_Rd_Proper_List_Check(sol_gb[0], elem);
    int i;
    for(i = 0; i < nmb; i++) { 
       if (i != 1) { 
          Pl_Write(elem[i]);
          printf("\n");
       } else {               // we know it is a list
          printf("(");
          PlTerm list = elem[i];
          while(!Pl_Un_Atom(Pl_Atom_Nil(), list)) {
              PlTerm *lst_arg = Pl_Rd_List(list); // [0] = head element, [1] = tail list
              printf(" ");
              Pl_Write(lst_arg[0]);
              list = lst_arg[1];
          }
          printf(" )\n");
       }
    }
    

    Here should be the output

    Spezial Bolognese
    ( 2   Zwiebeln 300 gramm Hackfleisch 10   Tomaten 100 ml Sahne 500 gramm Spaghetti )