7. Extensions de Prolog avec des langages externes.. PrologIA HERITAGE II+
A!ociation
Prolog
HERITAGE
7. Extensions de Prolog avec des langages externes.
7.1. Principes des fonctions de communication de données
7.2. Fonctions de communication de données simples
7.3. Fonctions de communication de termes quelconques
7.4. Méthode des descripteurs
7.5. Données partagées
7.6. Ajout de fonctions externes
7.7. Ajout de fonctions externes à appel direct
Ce chapitre montre comment faire des liens avec des données externes ou comment ajouter de nouvelles règles prédéfinies écrites en C ou tout autre langage compatible avec C. Les exemples qui y sont décrits, se trouvent déjà dans le fichier expredef.c du kit. Un équivalent pour Fortran est donné dans le fichier fprouser.eg du kit.
Pour étendre Prolog avec des fonctions d'un langage externe, il est nécessaire de disposer d'un module de communication pour:
- créer un lien entre le prédicat prolog et la fonction externe,
- faire la transformation des données échangées, entre la structure Prolog et la structure externe.
Différentes méthodes sont proposées pour réaliser ces deux traitements:
- le lien se définit en C: c'est la méthode des descripteurs décrite dans ce chapitre,
- le lien se définit en Prolog: c'est la méthode des parasites exposée en annexe D,
- la transformation des données se fait en Prolog: c'est la méthode des descripteurs concernant les fonctions à appel direct, c'est à dire qui ne nécessitent pas d'interface C pour le passage d'arguments,
- la transformation des données se fait en C ou en Fortran: dans les autres cas.
Pour étendre Prolog avec certains types de données, qui seront alors partagées entre
Prolog et C, il suffira de:
- créer un lien entre le terme Prolog et la donnée externe, par la méthode des descripteurs.
© PrologIA
R 7 - 2
Manuel de référence
Nous débuterons ce chapitre par la description des fonctions de communication de termes, dans le cas où la récupération et l'affectation de la valeur des termes se font en C ou en Fortran. En effet ces fonctions sont utiles dans de nombreux cas. Nous exposerons ensuite, comment faire le lien avec des objets externes par la méthode des descripteurs. Nous terminerons en détaillant les possibilités offertes par cette méthode.
A!ociation
Prolog
HERITAGE
7.1. Principes des fonctions de communication de données
Nous allons décrire dans les paragraphes 7.2. et 7.3. suivants, les fonctions de communication qui sont utilisables lorsque dans une procédure d'un langage externe, il est nécessaire de connaître la valeur d'un argument du prédicat Prolog lié
à la procédure, ou bien de lui affecter une valeur.
Nous donnons ici quelques principes de fonctionnement communs à toutes ces fonctions.
Etant donné les différences de conventions entre langages en ce qui concerne le passage d'argument, un jeu de fonctions est fourni pour le langage C, et un jeu
équivalent est fourni pour le langage FORTRAN. Les routines FORTRAN ont les mêmes arguments, mais passés par adresse. Leur nom commence par la lettre "f" et ne contient pas de signe "_". Les routines FORTRAN peuvent également être utilisées pour des programmes PASCAL en déclarant tous les arguments en var.
Dans tous les cas, une donnée doit être transmise entre Prolog et un langage externe. Par conséquent, à l'appel de la fonction de communication, il est nécessaire de préciser le rang de l'argument du prédicat Prolog, et la structure de la donnée externe associée. Puis, selon le sens de communication demandé, il s'agira de
"copier" l'argument Prolog dans la structure externe choisie, ou bien il s'agira de construire dans les piles de Prolog le terme représenté par la structure externe et de l'unifier avec l'argument du prédicat associé.
D'autre part, il peut arriver que la communication ne puisse pas se faire. Il est alors nécessaire d'en être informé. Les fonctions de communication ont pour cela un argument qui sert d'indicateur. Il est nul quand la fonction a pu être réalisée, strictement positif quand une erreur doit être générée, et égal à -1 quand un
backtracking doit être provoqué.
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 3
Le système de gestion des erreurs de Prolog impose que le programme externe soit immédiatement quitté dès qu'une erreur ou un backtracking est notifié, autrement dit dès que l'indicateur devient non nul. En effet quand l'indicateur est non nul, cela reflète une situation anormale qui nécessite dans le programme Prolog un backtracking ou une erreur, qui pourront être réalisés seulement lorsque la machine
Prolog sera relancée, c'est à dire lorsque le programme externe se terminera. Il peut s'agir également d'une erreur plus grave, ayant pour conséquence l'arrêt de la machine Prolog.
7.2. Fonctions de communication de données simples
Les fonctions décrites ici conviennent dans les cas simples où le type des données à transférer est connu au moment de l'appel et correspond à un type du langage externe, typiquement les entiers, les réels et les chaînes de caractères. Pour les autres cas, d'autres fonctions de communication plus générales existent, elles sont décrites au § 7.3.
Chaque type de données a une fonction 1 qui lui est associée pour transférer cette donnée depuis Prolog vers le programme externe, et une autre pour la transférer depuis la fonction externe vers Prolog.
7.2.1. Test du type d'un argument
La fonction get_arg_type permet de tester le type d'un argument Prolog, afin de pouvoir choisir la procédure de communication appropriée pour récupérer sa valeur.
Cette fonction permet également de connaître la taille en octets qu'occuperait l'argument. Ceci est très utile en particulier pour connaître la taille à allouer pour récupérer une chaîne. La fonction renvoie une erreur uniquement lorsqu'il n'existe pas d'argument du rang demandé.
get_arg_type(no_arg, value, lgPtr, err) int no_arg; char *value; int *lgPtr; int *err; integer*4 function fgetargtype(no_arg, value, lgPtr, err) integer*4 no_arg,lgPtr,err; character** value;
no_arg
Entier donnant le rang de l'argument choisi dans le prédicat Prolog. Le premier argument a le rang 1, le second a le rang 2 et ainsi de suite.
1Des fonctions supplémentaires existent pour le transfert de chaînes. Voir l'annexe E.
© PrologIA
R 7 - 4
Manuel de référence value
Adresse d'une variable de type caractère dans laquelle sera écrit le "code" du type de l'argument. La signification des caractères retournés est la suivante:
"code" type
'I'
'R'
'S'
'N'
'E'
'V'
'D'
'T' entier réel chaîne identificateur
nil
variable libre liste n-uplet
Le codage des types est le même que celui choisi pour les procédures
get_term, put_term (cf. §7.2.1.), excepté pour l'identificateur nil qui est repéré par le 'E'.
lgPtr
Adresse d'une variable entière dans laquelle sera écrite la taille en octets occupée par l'argument. Lorsque l'argument n'est pas un terme simple, autrement dit lorsque c'est une liste ou un n-uplet, la taille est non spécifiée.
err
La variable pointée est différente de 0 si une erreur s'est produite, égale à 0 sinon.
La fonction retourne la valeur correspondant à
*err
.
7.2.2. Transfert de données simples de Prolog vers un autre langage.
Ces fonctions sont appelées par le programme externe pour obtenir les valeurs effectives des arguments du prédicat Prolog.
Si le type de l'argument effectif n'est pas celui attendu, ou bien s'il n'existe pas d'argument du rang demandé, la fonction de communication notifie une erreur en affectant à l'indicateur err une valeur non nulle. Si, pour un argument de type entier, la valeur ne peut être représentée dans le type externe associé, une erreur est notifiée de la même manière.
Attention: Pour un comportement correct du système de gestion des erreurs, on doit immédiatement sortir du programme externe si la
variable représentée par err est non nulle.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 5
Voici les fonctions disponibles pour les types simples:
Interface C: int get_integer(no_arg, value, err) int no_arg; long *value; int *err; int get_real(no_arg, value, err) int no_arg; float *value; int *err; int get_double(no_arg, value, err) int no_arg; double *value; int *err; int get_string(no_arg, value, err) int no_arg; char *value; int *err; int get_max_string(no_arg, lg_max, value, in_external_code, err) int no_arg; int lg_max; char *value; int in_external_code; int *err;
Interface Fortran: integer*4 function fgetinteger(no_arg, value, err) integer*4 no_arg,value,err integer*4 function fgetreal(no_arg, value, err) integer*4 no_arg,err real*4 value integer*4 function fgetdouble(no_arg, value, err) integer*4 no_arg,err real*8 value integer*4 function fgetstring(no_arg, lg, value, err) integer*4 no_arg,lg,err character** value integer*4 function fgetmaxstring(no_arg, lg_max, lg, value, in_external_code, err) integer*4 no_arg,lg_max,lg,in_external_code,err character** value
no_arg
Entier donnant le rang de l'argument choisi dans le prédicat Prolog. Le premier argument a le rang 1, le second a le rang 2 et ainsi de suite.
© PrologIA
R 7 - 6
Manuel de référence value
Adresse de la donnée qui doit recevoir la valeur de l'argument de rang no_arg dans le prédicat Prolog.
lg
Entier, utilisé dans les fonctions Fortran qui manipulent des chaînes, qui doit recevoir la longueur effective de la chaîne de caractères.
lg_max
est la taille de la zone de caractères.
in_external_code
est un booléen qui indique si la chaîne doit être en code caractère externe (ceci est utile lorsque l'option caractères ISO est activée et que l'on utilise des caractères non présents dans la première moitié du jeu ISO; voir Annexe E).
Si la valeur est 0 aucune transformation n'est faite sur la chaîne Prolog.
err
La variable pointée est différente de 0 si une erreur ou un backtracking a été demandé.
Ces fonctions retournent zéro lorsqu'une erreur s'est produite ou une valeur non nulle lorsqu'il n'y a pas eu d'erreur.
get_max_string copie la chaîne de caractères originale (depuis la mémoire de travail de Prolog) dans une zone définie dans le programme externe, pointée par value, d'une taille de lg_max caractères. Un caractère nul indique la fin de la chaîne. Une erreur est provoquée si la taille de la zone de caractères définie par lg_max est insuffisante pour la chaîne à récupérer.
get_string fait la même chose que get_max_string mais ne fait pas de test de débordement. Le tableau pointé par value doit donc avoir une taille suffisante pour contenir la chaîne. Pour éviter les risques d'écrasement mémoire, il est préférable d'utiliser get_max_string.
7.2.3. Transfert de données simples d'un langage externe vers Prolog
Ces fonctions sont appelées par le programme externe pour unifier une valeur avec un argument du prédicat Prolog associé. Si l'unification échoue, un backtracking est annoncé par la fonction de communication.
Si le type de l'argument effectif n'est pas celui attendu, ou bien s'il n'existe pas d'argument du rang demandé, la fonction de communication notifie une erreur en affectant à l'indicateur err une valeur non nulle.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 7
Attention: Pour un comportement correct du système de gestion des erreurs, on doit immédiatement sortir du programme externe si la
variable représentée par err est non nulle.
Voici les fonctions disponibles pour les types simples:
Interface C: int put_integer(no_arg, value, err) int no_arg; long value; int *err; int put_real(no_arg, value, err) int no_arg; float value; int *err; int put_double(no_arg, value, err) int no_arg; double value; int *err; int put_string(no_arg, value, err) int no_arg; char *value; int *err;
Interface Fortran: integer*4 function fputinteger(no_arg, value, err) integer*4 no_arg,value,err integer*4 function fputreal(no_arg, value, err) integer*4 no_arg,err real*4 value integer*4 function fputdouble(no_arg, value, err) integer*4 no_arg,err real*8 value integer*4 function fputstring(no_arg, lg, value, err) integer*4 no_arg,lg,err character** value
no_arg
Entier donnant le rang de l'argument choisi dans le prédicat Prolog. Le premier argument a le rang 1, le second a le rang 2 et ainsi de suite.
© PrologIA
R 7 - 8
Manuel de référence value
Valeur qui sera unifiée sur l'argument de rang no_arg dans le prédicat Prolog associé. Pour le transfert de données de type chaîne de caractères, value est l'adresse d'une chaîne de caractères terminée par zéro, définie dans le programme externe. La fonction put_string copie alors cette chaîne de caractères dans la mémoire de travail de Prolog avant d'unifier la valeur avec l'argument du prédicat associé.
err
La variable pointée est différente de 0 si une erreur ou un backtracking a été demandé.
Ces fonctions retournent zéro lorsqu'une erreur s'est produite ou une valeur non nulle lorsqu'il n'y a pas eu d'erreur.
A!ociation
Prolog
HERITAGE
7.3. Fonctions de communication de termes quelconques
Les termes dont il est question dans ce chapitre, sont des termes qui ne représentent pas des arbres infinis, et qui peuvent être d'un type Prolog quelconque. Par exemple, une liste de n-uplets contenant des variables est un tel terme.
Pour pouvoir communiquer ces termes, et en particulier des types de données qui sont propres à Prolog, il faut disposer d'une codification de ces données pour les identifier hors Prolog.
Pour cela, Prolog permet de choisir entre:
- des chaînes de caractères, qui existent dans tous les langages, mais qui nécessitent l'utilisation d'un analyseur pour identifier les termes représentés.
- une structure arborescente d'éléments (type, valeur) codée dans des tableaux de données communes à tous les langages. Pour identifier les termes représentés, il s'agira de parcourir cette structure.
7.3.1. Au moyen de chaînes de caractères
Les fonctions décrites ici sont simples à employer et faciles à mettre en œuvre. Elles peuvent être choisies par exemple pour une première réalisation d'interfaçage de
Prolog avec des langages externes.
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 9
Elles sont beaucoup moins efficaces que la solution proposée avec les structures de tableaux qui sera décrite au paragraphe suivant. Elles offrent moins de possibilités d'exploitation des arguments. Elles comportent une restriction sur la manipulation des variables: il n'est pas possible de voir ou d'imposer que plusieurs arguments du prédicat lié à la procédure externe, utilisent la même variable libre. En effet le dictionnaire des variables est local à la procédure de communication, c'est à dire local à un argument (et non pas local à la règle).
Interface C: get_strterm(no_arg, lg_max, value, in_external_code, err) int no_arg; int lg_max; char *value; int in_external_code; int *err; put_strterm(no_arg, value, in_external_code, err) int no_arg; char *value; int in_external_code; int *err;
Interface Fortran: integer*4 function fgetstrterm(no_arg, lg_max, lg, value, in_external_code, err) integer*4 no_arg,lg_max,lg,in_external_code,err character** value integer*4 function fputstrterm(no_arg, lg, value, in_external_code, err) integer*4 no_arg,lg_max,in_external_code,err character** value
no_arg
est le rang de l'argument choisi dans le prédicat Prolog. Le premier argument a le rang 1, le second a le rang 2 et ainsi de suite. S'il n'existe pas d'argument de ce rang, une erreur est annoncée.
value
est l'adresse d'une zone de caractères.
lg_max
est la taille de la zone de caractères.
© PrologIA
R 7 - 10
Manuel de référence in_external_code
est un booléen qui indique si la chaîne est (put_strterm), ou doit être
(get_strterm) en code caractère externe (ceci est utile lorsque l'option caractères ISO est activée et que l'on utilise des caractères non présents dans la première moitié du jeu ISO; voir Annexe E).
err
indique si une erreur s'est produite ou pas ou si un backtracking doit être généré.
Dans le cas du transfert de Prolog vers le langage externe, la zone de caractères pointée par value doit être suffisamment grande pour contenir la chaîne résultat;
Prolog transforme (à la manière du prédicat out) l'argument de rang no_arg en une chaîne qu'il copie à l'adresse value. Si la chaîne à copier est plus grande que lg_max, une erreur est signifiée.
Dans le cas du transfert du langage externe vers Prolog, Prolog analyse la chaîne contenue dans value, construit le terme Prolog qu'elle représente (à la manière du prédicat in) et l'unifie avec l'argument de rang no_arg.
Par exemple, dans une règle externe, pour unifier le troisième argument avec une liste de 5 entiers, on écrira : put_strterm( 3, "[1,2,3,4,5]", 0, &err);
Voyons ci-dessous un petit exemple d'utilisation, avec une partie en C à ajouter à
Prolog et la partie Prolog qui le teste. On suppose que le lien entre le prédicat
Prolog et la fonction C a été déclaré.
int test_ccom()
{ int err; char s[80]; get_strterm(1,80,s,1,&err); if (err) return err; fprintf(stderr,">>>>%s\n",s); put_strterm(2,s,1,&err); if (err) return err; return 0;
}
> test_ccom(term(1.2.x,"string",[]),z);
>>>>term(1.2.v64,"string",nil)
{z=term(1.2.v150,"string",nil)}
>
7.3.2. Au moyen de structures de tableaux
Les deux fonctions de communication de termes, get_term et put_term, décrites ici sont les fonctions de communication de données, les plus générales.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 11
Sachant que les fonctions de communication de termes simples sont typées, cette interface peut servir également dans le cas où le type de l'argument à traiter n'est pas connu à priori.
Dans le cas de la communication du langage externe vers Prolog, il s'agit d'unifier le terme proposé avec l'argument du prédicat Prolog associé. Si l'unification échoue, un backtracking est annoncé par la fonction de communication.
Les structures de Prolog sont transformées par ces procédures d'interface en une structure de tableaux facilement manipulable dans des langages algorithmiques typés comme Pascal ou C. Les structures obtenues peuvent être conservées d'une exécution à une autre et peuvent être écrites ou chargées à partir d'un fichier. Pour cela les termes de Prolog sont éclatés dans quatre tableaux:
Deux tableaux pour les constantes:
- un tableau de caractères pour les chaînes.
- un tableau de doubles pour les réels.
Deux tableaux pour coder respectivement le type et la valeur d'un terme et de ses sous-termes.
- un tableau de caractères pour les types (ou 'tags') de terme/sous-termes.
- un tableau d'entiers longs pour les valeurs de terme/sous-termes. La signification de chaque élément est donnée par le contenu de l'élément de même indice dans le tableau de types.
Le terme correspondant à l'argument no_arg est décrit par la première case des tableaux de types et de valeurs de terme/sous-termes:
tag_tab[0], val_tab[0]
7.3.2.1. Description du codage d'un terme
Un terme est représenté par un tag et une valeur entière dont l'interprétation dépend du tag.
© PrologIA
R 7 - 12
Manuel de référence
terme:
Entier:
Réel:
Réel:
Chaîne:
Ident: variable:
'R'
'X'
(tag, valeur)
'I' la valeur de l'entier. Si l'entier venant de Prolog est trop grand pour être codé, une erreur est notifiée.
'S' un index dans le tableau des réels doubles real_tab.
un index dans le tableau des réels doubles real_tab.
un index i dans le tableau des caractères str_tab.
»
i indique le premier caractère de la chaîne. La chaîne est terminée par un caractère NULL.
'N' un entier représentant l'identificateur de manière unique.
»
Cette représentation est unique tant que l'identificateur est déclaré utilisé (cf. set_permanent_symbol). Sinon il peut être supprimé par le récupérateur de mémoire.
'V' un numéro de variable libre.
»
Le numéro est unique pour un état donné de la machine (voir § 7.3). Si deux numéros sont différents, il s'agit de deux variables libres différentes (c'est à dire non liées entre elles). Dans un même appel de procédure externe, ces numéros sont cohérents pour différents appels des fonctions get_term et put_term. Entre deux activations ou deux états, il n'y a aucun lien entre ces numéros.
De nouvelles variables peuvent être créées en appelant
put_term avec des numéros non encore utilisés. Il est plus efficace de créer des numéros en ordre croissant par rapport aux numéros déjà attribués. Prolog attribue ces numéros par incréments de 1.
séquence: 'D' un index dans les tableaux de terme/sous-termes vers une séquence de sous-termes.
»
La séquence pointée est terminée par 'E' ou 'F' suivant le cas.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 13
terme:
n-uplet: fin liste:
(tag, valeur)
'T' un index dans les tableaux de terme/sous-termes
tag_tab et val_tab.
»
Pointe sur une suite de termes dont le premier est un entier représentant le nombre d'arguments n du n-uplet, suivi des n sous-termes représentant les arguments.
'E' indéfinie.
»
Marque la fin d'une séquence se terminant par nil.
Cette marque peut être le premier élément d'une séquence pointée par 'D'.
fin séquence:'F' indéfinie.
»
Marque la fin d'une séquence ne se terminant pas par
nil: le terme qui suit immédiatement cette marque est le dernier élément de la séquence. Cette marque ne peut
être le premier élément d'une séquence pointée par 'D'.
On a deux représentations équivalentes du terme 1.2.3.nil, l'une sous forme vectorisée, la deuxième étant de type paire pointée (ici une paire est représentée par 3 entrées). Prolog fournit toujours les données sous la première forme qui est plus compacte, la deuxième étant surtout utile pour construire des listes dont la longueur n'est pas connue au début de la construction.
Le couple ('N', 0) représente par convention l'identificateur nil. (dans ce qui suit, "-" représente des valeurs indéfinies) forme compacte
00 'D' 1
!
forme type paire pointée
'D' 1 terme: 1.2.3.nil
05
06
07
08
09
01 'I'
02 'I'
03 'I'
04 'E' -
1
2
3
'I' 1
'F' -
'D' 4
'I' 2
'F' -
'D' 7
'I' 3
'F' -
'N' 0 exemple:
Codage du terme 1.2.(3."Hi!".x).father(x,y).nil
© PrologIA
R 7 - 14
Manuel de référence
10
11
12
13
14
06
07
08
09 indice
00
01
02
03
04
05
-
'I'
'S'
'F'
'V'
'I'
'N'
'V'
'V' tag_tab
'D'
'I'
'I'
'D'
'T'
'E'
10 vers:
-
6
1
2 val_tab (32 bits)
1 argument de type séquence
* codage des sous-termes de l'arg.
...
vers: nil
(3."Hi!".x) father(x,y)
!
!
début sous-terme: father(x,y)
-
-
0
3
0
0
1
3
<entier représentant father> x y
* début sous-terme: (3."Hi!".x) référence vers 1er élt de str_tab x
00
01
02
03
04
Tableau des caractères indice caractère
'H'
-
'i'
'!' null (0)
Tableau des réels indice
00
01 -
réel
7.3.2.2. Identificateurs
Dans la structure de tableaux, un identificateur est codé par une clé (notamment un entier) dans le dictionnaire de Prolog. Au moment de la transformation des données par les fonctions d'interfaçage (get_term, put_term), des fonctions de conversions entre la clé et la représentation de l'identificateur (chaîne de caractères) peuvent être nécessaires.
La fonction pro_symbol, ou son équivalent en Fortran, permet à partir d'une chaîne de caractères, de construire l'identificateur Prolog correspondant (identificateur qui pourra par exemple faire partie d'un terme qui sera transmis à Prolog par la procédure put_term). La chaîne de caractères doit avoir la syntaxe d'un identificateur. Si la chaîne de caractères ne comprend pas de préfixe (c'est une notation abrégée), l'identificateur créé sera fonction du contexte courant (Voir à ce propos le Chapitre 3 de ce manuel). Si une erreur s'est produite, la fonction retourne la valeur 0.
© PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 15 long pro_symbol(str) char *str; integer*8 function fprosymbol(lg, str) integer*4 lg character** str
La fonction symbol_string, ou son équivalent en Fortran, permet d'obtenir la représentation, sous forme de chaîne, d'un identificateur Prolog. Cet identificateur aura été obtenu par une des fonctions de communication de termes, get_term par exemple. Les arguments fournis à la fonction sont la clé key
, l'adresse d'une zone mémoire où la chaîne pourra être copiée str
, la longueur maximum de la chaîne de caractères copiée lg_max
. La chaîne obtenue est la notation de l'identificateur en fonction du contexte courant, notation abrégée si possible. La fonction donnera dans le paramètre de sortie lg
, la longueur effective de la chaîne. La fonction retourne 0 si elle a pu s'exécuter, un entier positif correspondant à un numéro d'erreur si une erreur s'est produite.
NB: key
doit correspondre à un identificateur valide.
int symbol_string(key, str, lg, lg_max) long key; char *str; int *lg; int lg_max; integer*4 function fsymbolstring(key, str, lg, lg_max) integer*4 key,lg_max,lg character** str
Le caractère choisi pour noter la séparation entre le préfixe et le suffixe de la représentation d'un identificateur complet, peut être modifié depuis Prolog. La fonction suivante permet donc de connaître le caractère courant utilisé : char prefix_limit(); character*1 function fprefixlimit();
Les fonctions set_permanent_symbol et reset_permanent_symbol, ou leur
équivalent en Fortran, permettent de prévenir le récupérateur de mémoire de Prolog, dans le cas où il serait activé sur le dictionnaire, qu'un identificateur est utilisé et respectivement non utilisé, par une procédure externe.
NB: key
doit correspondre à un identificateur valide.
set_permanent_symbol(key) long key; reset_permanent_symbol(key) long key; integer*4 function fsetpermanentsymbol(key) integer*4 key integer*4 function fresetpermanentsymbol(key) integer*4 key
7.3.2.3. Description des fonctions de communication
© PrologIA
R 7 - 16
Manuel de référence
Les paramètres de ces fonctions sont:
- Le numéro de l'argument no_arg.
- La taille des tableaux: tab_size, str_tab_size, real_tab_size.
- Les tableaux utilisés pour coder le terme: tag_tab, val_tab, str_tab, real_tab.
Lorsqu'un tableau n'est pas référencé (par exemple s'il n'y a ni chaînes, ni réels), il peut être remplacé par NULL. Une erreur est signifiée lorsqu'un des tableaux n'a pas une taille suffisante pour coder le terme.
- Un entier indiquant la dernière case utilisée pour coder le terme dans les tableaux
tag_tab et val_tab: max_used. Dans l'exemple décrit plus haut, il vaut 13.
- L'indicateur err, indiquant si une erreur ou un backtracking doit être provoqué.
Cet indicateur doit être testé à chaque appel, et il faut sortir de la procédure externe immédiatement s'il est non nul.
int get_term(no_arg, tab_size, str_tab_size, real_tab_size, tag_tab, val_tab, str_tab, real_tab, max_used, err ); int no_arg; int tab_size, str_tab_size, real_tab_size; char tag_tab[]; long val_tab[]; char str_tab[]; double real_array[]; int * max_used; int *err; int put_term(no_arg, tab_size, str_tab_size, real_tab_size, tag_tab, val_tab, str_tab, real_tab, max_used, err ); int no_arg; int tab_size, str_tab_size, real_tab_size; char tag_tab[]; long val_tab[]; char str_tab[]; double real_array[]; int max_used; int *err; integer*4 function fgetterm(no_arg, tabsize, strtabsize, realtabsize, tagtab, valtab, strtab, realtab, maxused, err ); integer*4 function fputterm(no_arg, tabsize, strtabsize, realtabsize, tagtab, valtab, strtab, realtab, maxused, err );
Des exemples d'utilisation de ces procédures d'interface sera trouvé dans le fichier
expredef.c ou fprouser.eg du kit.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 17
7.4. Principe de la méthode des descripteurs
La méthode des descripteurs permet de définir des objets relais Prolog et leur lien avec un objet externe. Elle s'applique pour les fonctions externes et les données partagées. Dans cette méthode, la déclaration des objets et des liens se fait en C.
Nous verrons d'abord quels sont les éléments à décrire en C pour réaliser cette définition et ensuite les deux manières de les déclarer: statiquement ou dynamiquement.
7.4.1. Eléments descriptifs
Pour pouvoir réaliser cette association, il est nécessaire de connaître:
- l'objet externe: dans un langage tel que C, cela se traduit par son adresse et son type.
- l'objet Prolog: c'est à dire son nom, son type et sa taille ou son arité selon qu'il s'agisse d'un tableau ou respectivement d'un prédicat.
Nous identifions donc les éléments nécessaires suivants:
name
identifie le terme Prolog associé à l'objet externe. C'est un pointeur vers une chaîne de type C contenant la représentation d'un identificateur Prolog complet.
Il a une signification différente dans le cas d'un objet de type
SYMBOL_ARRAY
.
Il représente alors seulement le module auquel appartiennent les identificateurs du tableau. Il contient donc la représentation du préfixe de ces symboles.
type
définit le type de l'objet externe déclaré. Les valeurs possibles sont:
INT_ARRAY
pour un tableau d'entiers,
CHAR_ARRAY
pour un tableau de caractères,
STRING_ARRAY
pour un tableau de chaînes,
SINGLE_FLOAT_ARRAY pour un tableau de réels en simple précision,
DOUBLE_ARRAY
pour un tableau de réels en double précision,
SYMBOL_ARRAY
pour un tableau d'identificateurs,
C_FUNCTION
C_FUNCTION
ou
C_FUNCTION_PROTECTED
pour une fonction externe,
_
BACKTRACK
ou
C_FUNCTION
_
BACKTRACK_PROTECTED
pour une fonction externe non déterministe,
DIRECT_C_FUNCTION
pour une fonction externe à appel direct. Pour les tableaux de données, il est possible de leur ajouter la constante
OFFSET_ZERO_BASED
pour permettre en Prolog de les indexer à partir de zéro.
© PrologIA
R 7 - 18
Manuel de référence size
définit l'arité de la règle dans le cas d'une fonction externe, ou bien la taille du tableau s'il s'agit d'une donnée commune. N'est pas significatif dans le cas de
SYMBOL_ARRAY
.
adresse
Définit l'adresse effective de l'objet C déclaré (ou d'un sous-tableau de descripteurs pour
SYMBOL_ARRAY
).
7.4.2. Déclaration statique
La déclaration des objets externes et des objets Prolog associés, peut se faire statiquement par des tables de descripteurs qui sont parcourues par Prolog au moment de l'initialisation. Ces tables, au moment du chargement de Prolog, vont créer les liens et les déclarations Prolog nécessaires. Les objets Prolog ainsi créés et leur lien avec l'objet externe, sont permanents durant toute la session. En particulier, ils ne sont pas supprimés par les règles prédéfinies kill_module, kill_array,
suppress …
Un descripteur de référence externe (voir fichier proext.h) est une structure composée de quatre champs qui contiennent les éléments nécessaires que nous avons identifiés: typedef struct
{
char *name;
int type;
int size;
POINTER adresse;
} EXTERNAL_DESCRIPTOR;
Pour ajouter ces objets à Prolog, la machine Prolog doit être étendue par un ou plusieurs modules externes contenant la déclaration de ces descripteurs. Un descripteur doit être affecté à une table de descripteurs, qui doit être pointée par le tableau général PRO_EXTERNAL. Le programme prolink d'édition de liens, construit automatiquement ce tableau PRO_EXTERNAL dans le module prodesc, puis reconstruit la machine Prolog.
PRO_EXTERNAL est un tableau d'adresses de tables de descripteurs, il doit être terminé par une adresse nulle(0). Dans sa version initiale du kit, il contient la table du fichier prouser:
EXTERNAL_DESCRIPTOR *PRO_EXTERNAL[] = { prouser_desc,
0 };
Table de descripteurs
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 19
Une table de descripteurs est un tableau de descripteurs, terminé par un descripteur contenant 0 dans le champ name. Par exemple:
EXTERNAL_DESCRIPTOR sample_desc[] =
{
{":term_vars", C_FUNCTION, 2, (POINTER) term_vars},
{":qsort", DIRECT_C_FUNCTION, 1, (POINTER) quicksort},
{":enumerate", C_FUNCTION_BACKTRACK,3,(POINTER)enumerate},
{ 0, 0, 0, 0}
};
L'utilisateur peut créer autant de tables qu'il le désire; le plus naturel est d'en réaliser une par fichier externe.
7.4.3. Déclaration dynamique
La possibilité de créer des associations d'objets Prolog-C dynamiquement, est offerte grâce à la fonction PRO_BIND. Les objets externes doivent être connus de l'exécutable Prolog (c'est à dire que la machine Prolog doit être étendue par des modules externes contenant ces objets), par contre la création de l'objet Prolog et le lien avec cet objet sont faits à l'exécution de la fonction PRO_BIND.
Comme dans le cas de la déclaration statique, les objets Prolog ainsi créés et leur lien avec l'objet externe, sont permanents durant toute la session. Une fois la déclaration faite, il n'est pas possible d'en annuler l'effet.
La fonction PRO_BIND a 4 arguments, chaque argument représentant un élément nécessaire que nous avons identifié précédemment : int PRO_BIND(name, type, size, adresse) char *name; int type, size; void *adresse;
Par exemple, il est possible de créer dynamiquement une zone de données partagées. Dès que la fonction PRO_BIND a été appelée, le tableau est connu de
Prolog.
L'exemple suivant alloue une zone de 1000 réels commune à Prolog et C, et accessible dans Prolog sous le nom mymodule:data
#include <malloc.h>
#include "proext.h"
...
double *t; t = (double *) malloc( 1000*sizeof(double));
PRO_BIND( "mymodule:data"
, DOUBLE_ARRAY
, 1000
, (POINTER) t);
© PrologIA
R 7 - 20
Manuel de référence
à partir de l'exécution de cette séquence, le programme Prolog suivant s'exécute sans erreur, et référence la zone de données t[4]: assign(mymodule:data(5), 1.5e );
Dans les paragraphes suivants qui exposent les différents types d'objets accessibles par la méthode des descripteurs, les exemples sont décrits avec des déclarations statiques. Ils peuvent évidemment aussi être déclarés de manière dynamique.
A!ociation
Prolog
HERITAGE
7.5. Données partagées
Les données qui peuvent être partagées entre Prolog et C sont des données d'un type manipulable par les deux langages. Les données qui s'y prêtent sont les entiers, les réels, les caractères, les chaînes de caractères et les identificateurs.
Prolog peut être étendu par une ou plusieurs zones de données partagées. Ces zones de données sont assimilées à des tableaux et sont manipulées comme des tableaux dans les deux langages.
La définition de ces données communes se fait par la déclaration d'un descripteur dont le champ type aura une des valeurs suivantes:
INT_ARRAY
,
SINGLE_FLOAT_ARRAY
,
DOUBLE_ARRAY, CHAR_ARRAY
,
STRING_ARRAY
,
SYMBOL_ARRAY,
ou bien une de ces valeurs augmentée de
OFFSET_ZERO_BASED
.
Le champ name sera alors la représentation Prolog du tableau. La primitive
def_array ne doit pas être utilisée pour définir ce tableau. Le champ size indique le nombre d'éléments du tableau.
Le champ adresse est l'adresse de la zone mémoire réservée en C pour ce tableau.
Ce sera, en fonction du type de l'objet :
INT_ARRAY
l'adresse d'une zone statique d'entiers (type int du C).
CHAR_ARRAY
l'adresse d'une zone de caractères 2 (1 octet par caractère).
STRING_ARRAY
l'adresse d'un tableau de pointeurs vers des chaînes 3 terminées par le caractère nul. Il peut être déclaré sous la forme : char *tab[size];
2Attention au mode de codage des caratères. Voir annexe E.
3Attention au mode de codage des chaînes. Voir annexe E.
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 21
Attention: il n'y a pas de test de débordement sur la taille des chaînes manipulées par l'interface. Ne pas oublier d'allouer effectivement l'espace pour les chaînes, et d'initialiser le tableau de pointeurs.
SINGLE_FLOAT_ARRAY
l'adresse d'une zone de réels 32 bits IEEE.
DOUBLE_ARRAY
l'adresse d'une zone de réels 64 bits IEEE.
SYMBOL_ARRAY
l'adresse d'une sous-table d'associations (chaîne d'un identificateur,
représentation interne). La partie chaîne est initialisée par le programmeur, la partie représentation interne est initialisée par Prolog.
Pour ce type de descripteur, le champ name définit le préfixe par défaut pour les symboles de la table (c'est-à-dire que si la chaîne id_name correspond à un nom abrégé, c'est ce préfixe par défaut qui est utilisé).
Ce type d'objet permet de construire automatiquement une table des valeurs internes des identificateurs Prolog que l'on désire manipuler. Tous les identificateurs de la tables sont permanents (cf. set_permanent_symbol) par défaut.
7.5.1. Exemple de zone commune de données
On déclare ici deux tableaux partagés par C et Prolog. Le premier est un tableau de
100 entiers, nommé com en C et :com en Prolog. Le deuxième est un tableau de 3 chaînes de 10 caractères au plus, nommé m:str en Prolog, et str en C.
#include "proext.h" int com[100]; char *str[3] =
{
"0123456789",
" ",
" "
};
EXTERNAL_DESCRIPTOR descTable1 [] =
{{":com",INT_ARRAY+OFFSET_ZERO_BASED, 100, (POINTER)com },
{"m:str", STRING_ARRAY, 3, (POINTER) str },
{ 0, 0, 0, 0 }
};
Supposons que ces déclarations soient contenues dans le module source C table.c, il peut être lié à Prolog après l'avoir compilé, en exécutant prolink pour l'édition de liens avec le module objet de table et le descripteur descTable1. (Voir le manuel d'utilisation §2.8.)
© PrologIA
R 7 - 22
Manuel de référence
Au moment du chargement, Prolog créera automatiquement un lien avec le tableau
C. Ce tableau pourra être modifié et consulté à partir des commandes standard
Prolog assign et val. Il faut noter que pour les données partagées déclarées avec
OFFSET_ZERO_BASED les indices Prolog commencent à 0, que pour les autres tableaux ils commencent à 1, et que les indices C commencent à 0. Les commandes suivantes
C et Prolog ont strictement le même effet, et travaillent sur la même zone mémoire:
Prolog C assign(:com[4], 100); val(:com[4],_x) outl(_x); val(m:str[1],_x) outm(_x); assign(m:str[2],"abc"); com[4] = 100; printf("%ld\n",com[4]); printf("%s",str[0]); strcpy(str[1],"abc");
A!ociation
Prolog
HERITAGE
7.6. Ajout de fonctions externes
La définition d'une fonction externe se fait par la déclaration d'un descripteur. On distingue deux types de fonctions externes :
- les fonctions qui effectuent un traitement et se terminent; le champ type du descripteur vaudra alors
C_FUNCTION
ou
DIRECT_C_FUNCTION
. Les objets de type
DIRECT_C_FUNCTION
seront décrits au paragraphe 7.7.
- les fonctions non déterministes qui peuvent effectuer plusieurs traitements différents et donc retourner plusieurs résultats possibles; le champ type du descripteur vaudra alors
C_FUNCTION_BACKTRACK
.
On trouvera parmi les types d'objets possibles à déclarer par les descripteurs :
C_FUNCTION_PROTECTED
et
C_FUNCTION_BACKTRACK_PROTECTED
. Ces types identifient les mêmes objets que les types
C_FUNCTION
et
C_FUNCTION_BACKTRACK
, seulement leur "visibilité" change. En effet les objets de type
PROTECTED
seront cachés pour le mode de mise au point.
C_FUNCTION
La déclaration d'un objet de ce type, permet de créer automatiquement une règle relais Prolog, de nom name et d'arité size. L'exécution de cette règle
Prolog se fera par l'appel de la fonction C dont l'adresse se trouve dans le champ adresse. Les arguments de la règle peuvent être accédés depuis la fonction C par les procédures standard de communication de données get_...
et put_... . La fonction C pointée par adresse doit être déclarée de type int et est appelée par Prolog sans paramètres. Elle doit retourner la condition de terminaison (-1 pour ECHEC, 0 pour SUCCES, > 0 pour ERREUR).
Permet de construire des règles prédéfinies externes plus simplement que par l'utilisation directe des parasites (/?n) et de la procédure relais user_rule (cf.
Annexe D).
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 23
C_FUNCTION
_
BACKTRACK
La déclaration d'un objet de ce type, permet de créer automatiquement une règle relais Prolog, de nom name et d'arité size. L'exécution de cette règle
Prolog se fera par l'appel de la fonction C dont l'adresse se trouve dans le champ adresse. Les arguments de la règle peuvent être accédés depuis la fonction C par les procédures standard de communication de données get_...
et put_... . La fonction doit être déclarée de type int. Elle est appelée par
Prolog avec un seul paramètre de type long indiquant le numéro de l'appel courant. Elle doit retourner la condition de terminaison (ECHEC pour un
échec, SUCCES pour un succès, > 0 pour une erreur,
SUCCESS_END_OF_C_BACKTRACK pour indiquer le succès du dernier appel ou FAIL_END_OF_C_BACKTRACK pour indiquer l'échec du dernier appel). Lors d'une coupure, un appel supplémentaire à cette fonction sera fait avec en argument, la valeur conventionnelle CUT_OF_C_BACKTRACK. Le but de cet appel n'est pas de trouver une autre solution, mais de signifier qu'il n'y aura plus d'autres appels et ainsi permettre de terminer les traitements en cours (comme par exemple: libérer de la mémoire allouée, fermer des fichiers,
…). Pendant un appel à cette fonction, il est possible de mémoriser une valeur entière dans l'espace de Prolog, à l'aide de la fonction
store_C_backtrack_data(long ptr) ou de récupérer cette valeur par le retour de la fonction long restore_C_backtrack_data(void). Permet de construire des règles prédéfinies en langage externe non déterministes.
7.6.1. Exemple de déclaration de règle prédéfinie
Le fichier suivant, lié à Prolog, produit automatiquement la nouvelle règle prédéfinie
sys:reverse(s1,s2), unifiant s2 avec la chaîne s1 inversée, ainsi que la règle
sys:enumerate(x,b,e) qui unifie successivement x avec les entiers compris entre b et
e. Si l'on veut utiliser le nom abrégé, il faut rajouter le nom dans le contexte fermé
"sys" en effaçant la commande:
> add_implicit("sys","reverse")
add_implicit("sys","enumerate");
#include "proext.h"
#include <stdio.h>
#include <string.h>
#define MAX_STRING 512 int reverse();
EXTERNAL_DESCRIPTOR descTable2[] =
{{"sys:reverse", C_FUNCTION, 2, (POINTER) reverse},
{"sys:enumerate", C_FUNCTION_BACKTRACK, 3,
(POINTER) enumerate},
{ 0, 0, 0, 0 }
}; reverse()
© PrologIA
R 7 - 24
Manuel de référence
{ int err, i, j, lg; char c1, c[MAX_STRING]; if ( ! get_string(1,c,&err) ) return err; lg = strlen(c); for (i=0; i<lg/2; i++)
{ j = lg -1 -i; c1 = c[i]; c[i] = c[j]; c[j] = c1;
} if ( ! put_string(2,c,&err) ) return err; return 0;
} typedef struct { int direction, current, start, end;
} enumRecord, *EnumMemory; int enumerate(call) long call;
{
EnumMemory mem; int err; if (call==CUT_OF_C_BACKTRACK) /* cut */
{ mem = (EnumMemory) restore_C_backtrack_data(); free(mem); return;
} if (call==1) /*first call */
{
/* mem constituera la mémoire du prédicat d'un appel
à l'autre */ mem = (EnumMemory) malloc(sizeof(enumRecord)); get_integer(2, &mem->start, &err); if (err) { free(mem); return 253;} get_integer(3, &mem->end, &err); if (err) { free(mem); return 253;} mem->direction = (mem->start > mem->end) ? -1 : 1; mem->current = mem->start; store_C_backtrack_data(mem);
}
/* on récupère l'adresse des données qui a été mémorisée
*/ if (call != 1) mem = (EnumMemory) restore_C_backtrack_data();
/* instantiation du premier argument du prédicat */
© PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 25 put_integer(1,mem->current,&err); if (mem->current == mem->end)
{
/* c'est fini, il faut libérer */ free(mem); return SUCCESS_END_OF_C_BACKTRACK;
} else
/* on incrémente le compteur */ mem->current += mem->direction;
} return err;
Il faut maintenant compiler le fichier ci-dessus, faire l'édition de liens (voir le manuel d'utilisation §2.8.), puis activer le nouveau Prolog pour obtenir :
Prolog II+, ..
...
>sys:reverse("0123456",x);
{x="6543210"}
>sys:enumerate(x,-2,2);
{x=-2}
{x=-1}
{x=0}
{x=1}
{x=2}
>
7.7. Ajout de fonctions externes à appel direct
Les fonctions externes à appel direct, contrairement aux autres fonctions externes accessibles par la méthode des descripteurs, sont des fonctions qui n'ont pas d'interface C pour le passage des paramètres. Une telle fonction C peut être écrite avec ses paramètres, comme si elle devait être appelée par un programme C classique. En particulier cela peut être une fonction d'une librairie dont on n'a pas la maîtrise des sources. En effet, pour ce type de fonction, la transformation des données (Prolog - C et C - Prolog) se fera en Prolog, par l'intermédiaire de la règle prédéfinie callC.
Cette méthode permet donc de communiquer des types de données communs aux deux langages, à savoir : des entiers, des réels, des chaînes de caractères et des tableaux homogènes de ces types.
La déclaration d'une fonction externe à appel direct se fait par la déclaration d'un descripteur dont le champ type vaut
DIRECT_C_FUNCTION
.
© PrologIA
R 7 - 26
Manuel de référence
DIRECT_C_FUNCTION
La déclaration d'un objet de ce type permet de créer automatiquement une règle relais Prolog de nom name et d'arité size, qui ne peut être effacée qu'à travers la primitive callC et dont l'exécution consiste à appeler la fonction C pointée par le champ adresse du descripteur.
Cette fonction C ne doit pas avoir plus de 20 arguments. Si le champ size du descripteur vaut -1, le type et le nombre d'arguments sont variables suivant l'appel. Il est possible d'appeler sprintf par exemple (voir § 5.2 de ce manuel).
7.7.1. Primitive CallC
La primitive prédéfinie callC détermine, au moment de l'appel, le mode de passage des arguments à la fonction C, leur type et le type de la valeur de retour, d'après les arguments effectifs du prédicat Prolog et conformément aux conventions adoptées que nous décrirons dans le prochain paragraphe. Ceci permet d'appeler des fonctions avec nombre et type d'argument variables.
callC(t1) callC(t1,t2)
t1 est un terme représentant l'appel du prédicat Prolog, avec ses paramètres conformément aux conventions choisies. t2 est un terme représentant le résultat. Si t2 est nil le résultat de la fonction est ignoré. La forme callC(t1) est
équivalente à callC(t1,nil).
Les fonctions qui peuvent être appelées à travers ce prédicats, sont les fonctions décrites par l'utilisateur avec un descripteur dont le type est
DIRECT_C_FUNCTION
, ainsi que les fonctions sprintf et sscanf qui sont prédéclarées. Sous les systèmes permettant l'implantation de la primitive
lkload, il est possible d'appeler sans déclaration des fonctions existant dans l'environnement Prolog. La primitive callC réalise alors dynamiquement le lien lors du premier appel. Il faut pour cela que le fichier exécutable prolog se trouve dans le répertoire courant.
Exemples:
> callC(sscanf("123","%lf",<"R",y>));
{y=1.230000000000000e+02}
> eq(x,"123") callC(sscanf(x,"%2f",<"R",y>));
{x="123", y=1.200000000000000e+01}
> eq(f,"%x %o") callC(sscanf("12 12",f,<"I",x>,<"I",y>));
{x=18, y=10}
> callC(sprintf(<"",x,80>,"valeur: %ld",200));
{x="valeur: 200"}
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 27
7.7.2. Conventions Prolog pour la communication des données
Il s'agit ici de pouvoir exprimer en Prolog toutes les informations nécessaires pour pouvoir faire l'appel d'une fonction C, concernant ses arguments et sa valeur de retour. On s'intéressera donc :
au mode de passage des arguments,
au type des arguments et de la valeur de retour de la fonction,
à la valeur initiale des arguments,
à la valeur résultat des arguments et de la fonction,
à d'autres informations techniques liées au type de l'objet.
Plus qu'au mode de passage d'une donnée, on s'intéressera au rôle de la donnée, à savoir : donnée d'entrée et/ou de sortie.
En effet en Prolog, une donnée se voyant affecter une valeur, ne pourra plus en changer (sauf par backtracking, mais c'est assimilable en comparant avec C, à une autre exécution). Une procédure Prolog qui attendrait une valeur en entrée et un résultat en sortie, devrait utiliser deux données. C'est là la différence essentielle entre
Prolog et C, il est donc important de connaître si une donnée (initialisée ou pas) doit changer de valeur au cours de l'exécution de la fonction C. Dans la suite de ce chapitre, on fera donc la distinction entre argument avec valeur de retour et argument sans valeur de retour. Le mode de passage par adresse ou par valeur en sera déduit automatiquement.
Il est possible de passer en paramètre:
1. un entier long avec ou sans valeur de retour,
2. un réel double précision avec ou sans valeur de retour,
3. une chaîne de caractères avec ou sans valeur de retour,
4. un tableau d'entiers, de chaînes de caractères ou de réels double précision, avec ou sans valeur de retour.
Il est possible d'attendre en résultat de la fonction:
1. un entier long,
2. un réel double précision.
Voyons à présent, quelles sont les conventions. Pour les illustrer sur des exemples, on supposera qu'un lien a été déclaré entre le prédicat relaiProlog
et la fonction fonctionC
.
7.7.2.1. Convention pour des paramètres sans valeur de retour
Dans ce cas, il n'est pas utile de spécifier le type de la donnée, Prolog le connaît puisque la donnée a déjà une valeur.
Les données de type entier, réel ou chaîne sont représentées par la donnée Prolog elle même.
© PrologIA
R 7 - 28
Manuel de référence
Les données de type tableau homogène d'entiers, de réels ou de chaînes sont représentées par une liste Prolog (terminée éventuellement par nil), d'entiers de réels ou de chaînes.
Par exemple:
:relaiProlog(180, 3.14e0, "pi", 2.3.5.7.11.13.17.nil,
"b"."a"."ba".nil) est équivalent aux instructions C suivantes:
{ long arg1 = 180L; double arg2 = 3.14; char arg3[] = {'p','i','\0'}; long arg4[] = {2,3,5,7,11,11,13,17}; char *arg5[] = {"b","a","ba"}; fonctionC(arg1,arg2,arg3,arg4,arg5); }
Les chaînes et les tableaux sont toujours passés par adresse selon la convention habituelle en C. Prolog effectue de toute façon une copie des arguments dans une zone intermédiaire dont la taille peut être paramétrée sur la ligne de commande (voir le chapitre 2 du Manuel d'Utilisation).
7.7.2.2. Convention pour le retour de la fonction
Il est nécessaire de connaître le type du retour de la fonction, et de disposer d'un terme Prolog qui sera unifié avec sa valeur. La convention est la suivante: le terme qui représente le résultat doit être un doublet :
< type_résultat, variable_résultat >
- dont le premier élément type_résultat est une chaîne qui indique le type: "I" pour un retour entier, "R" ou "X" pour un retour réel.
- dont le deuxième élément variable_résultat sera unifié avec la valeur retournée par la fonction (entier ou réel).
7.7.2.3. Convention pour des paramètres avec valeur de retour
Pour une donnée C avec valeur de retour attendue, il est nécessaire d'avoir deux données Prolog: une première qui doit être connue au moment de l'appel et qui sert
à transmettre la valeur initiale à la fonction C, une deuxième qui sera unifiée avec la nouvelle valeur obtenue de la fonction C.
On distingue parmi les types de données : des types simples et des types qui nécessitent une allocation de zone mémoire.
Note : Certaines conventions avec valeur(s) de retour, spécifient des tailles de zones mémoire à allouer pour ranger le(s) résultat(s) des fonctions C appelées. L'utilisateur doit donc impérativement s'assurer que les zones sont suffisantes pour les résultats attendus. Il s'assurera
également que les types des arguments sont cohérents avec les déclarations faites en C.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 29
La convention pour les données de type simple, c'est à dire les entiers et les réels, est la suivante : l'argument effectif du prédicat Prolog est un doublet :
< valeur_initiale, variable_résultat >
- dont le premier élément valeur_initiale est la valeur initiale de la zone dont l'adresse est passée en paramètre à la fonction C. Si la valeur est quelconque on peut mettre comme valeur initiale la marque "I" (pour indiquer un entier) ou "R"
(pour un réel).
- dont le deuxième élément variable_résultat sera unifié avec la nouvelle valeur de l'argument rendue par la fonction (entier ou réel).
Par exemple:
:relaiProlog(<180,x>, <3.14e0,y>) est équivalent aux instructions C suivantes:
{ long arg1 = 180L; double arg2 = 3.14; fonctionC(&arg1,&arg2); } suivies des affectations des variables x et y avec les nouvelles valeurs de arg1 et arg2.
La convention pour les données qui nécessitent une zone mémoire dépend du type de la donnée :
Pour une chaîne de caractère, il est nécessaire d'avoir une zone de caractères, dont on doit définir la taille, susceptible de contenir dans un premier temps la chaîne initiale puis la chaîne résultat: l'argument effectif du prédicat Prolog est un triplet:
< valeur_initiale, variable_résultat, taille_max_résultat >
- dont l'élément taille_max_résultat est un entier spécifiant la taille de la zone à allouer pour l'opération, dont l'adresse est transmise à la fonction C.
- dont l'élément valeur_initiale est la valeur initiale copiée dans la zone allouée pour l'opération.
- dont l'élément variable_résultat sera unifié avec le résultat (chaîne Prolog).
Par exemple:
:relaiProlog(<"pi",x,100>) est équivalent aux instructions C suivantes:
{ char arg1[100]; strcpy(arg1,"pi"); fonctionC(arg1); } suivies de l'affectation de la variable x avec la nouvelle valeur de arg1.
© PrologIA
R 7 - 30
Manuel de référence
Pour un tableau d'entiers ou de réels, il est nécessaire d'avoir une zone d'entiers ou de réels qui tient lieu de tableau, dont on définit le nombre d'éléments, et susceptible de contenir dans un premier temps les éléments du tableau en entrée puis les éléments du tableau en sortie: l'argument effectif du prédicat Prolog est un quadruplet:
< valeur_initiale, variable_résultat, taille_résultat, taille_tableau >
- dont l'élément taille_tableau est un entier spécifiant le nombre d'éléments de la zone à allouer pour l'opération, dont l'adresse est transmise à la fonction C.
- dont l'élément taille_résultat indique le nombre d'éléments valides du tableau à prendre en compte pour le retour.
- dont l'élément valeur_initiale est la liste Prolog terminée par nil, des valeurs initiales des éléments du tableau. Le type de ces éléments détermine le type du paramètre.
- dont l'élément variable_résultat sera unifié avec le résultat (liste Prolog terminée par nil, d'entiers ou de réels).
Par exemple:
:relaiProlog(<2.4.6.8.12,x,1,5>, <0e0.nil,y,5,5>) est équivalent aux instructions C suivantes:
{ long arg1[]={2,4,5,6,8,12}; double arg2[5]; arg2[0] = 0.0; fonctionC(arg1,arg2); } suivies des affectations des variables x et y avec les nouvelles valeurs de arg1 et arg2.
Pour un tableau de chaînes de caractères, il est nécessaire d'avoir une zone de pointeurs pour représenter le tableau, dont on doit définir le nombre d'éléments, et
éventuellement une zone de caractères pour stocker tous les caractères de toutes les chaînes du tableau, dont on doit également définir la taille.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 31 l'argument effectif du prédicat Prolog est un quadruplet:
< valeur_initiale, variable_résultat, nbre_max_de_caractères, taille_tableau
>
- dont l'argument taille_tableau est un entier spécifiant le nombre d'éléments de la zone à allouer pour l'opération, dont l'adresse est transmise à la fonction C. La fin du tableau doit être indiquée par le pointeur NULL après le dernier élément.
- dont l'argument valeur_initiale est une liste Prolog terminée par nil de chaînes pointées par le tableau en entrée ("".nil au minimum).
- dont l'argument est un entier, il offre la possibilité de laisser à Prolog la gestion (allocation et libération) du buffer de caractères pour les chaînes en sortie. Si l'utilisateur gère le(s) espace(s) des chaînes en sortie,
nbre_max_de_caractères doit valoir 0. Si l'utilisateur laisse à Prolog le soin d'allouer et par la suite libérer un buffer de caractères, nbre_max_de_caractères doit être non nul, il indique alors le nombre de caractères total maximal nécessaire pour stocker tous les caractères de toutes les chaînes du tableau. La valeur du premier élément du tableau (pointeur de chaîne de caractères) sera dans ce cas l'adresse de cet espace (où est copiée la première chaîne en entrée).
- dont l'élément variable_résultat sera unifié avec le résultat, une liste Prolog terminée par nil, de chaînes.
Par exemple:
:relaiProlog(<"il"."était"."une"."fois",x,100,5>) est équivalent aux instructions C suivantes:
{ char * arg1[5]; char buffer[100]; strcpy(buffer,"il"); arg1[0] = buffer; arg1[1] = "était"; arg1[2] = "une"; arg1[3] = "fois"; fonctionC(arg1); } suivies de l'affectation de la variable x avec les nouvelles valeurs de arg1.
7.7.3. Exemple : méthode simple pour appeler une fonction C
Nous montrons ici des exemples avec divers types d'arguments, utilisant des fonctions déclarées par l'utilisateur, et une fonction système.
#include <stdio.h>
#include <string.h>
#include "proext.h"
#define REAL double
#define INTEGER long static REAL complete_example(ii1,io1,ri1,ro1,si1,so1,tii1, tri1,tio1,tro1,tsi1,tso1,reserved)
/*-------------------------------------------------------*/
INTEGER ii1,*io1;
REAL ri1,*ro1; char *si1; char *so1;
© PrologIA
R 7 - 32
Manuel de référence
REAL *tri1;
REAL *tro1;
INTEGER *tio1,*tii1; char **tsi1,**tso1; int reserved;
{ int i; printf("ii1= %d\n",ii1); printf("io1= %d\n",*io1); printf("ri1= %g\n",ri1); printf("ro1= %g\n",*ro1);
/*integer in input*/
/*integer in output*/
/*real in input*/
/*real in output*/ printf("si1= %s\n",si1); printf("so1= %s\n",so1);
/*string in input*/
/*string in output*/ for (i=0;i<2;i++) /*integer array*/ printf("tii1[%d]= %d\n",i,tii1[i]); /*in input*/ for (i=0;i<2;i++) /*real array*/ printf("tri1[%d]= %g\n",i,tri1[i]); /*in input*/ for (i=0;i<3;i++) /*integer array*/ printf("tio1[%d]= %d\n",i,tio1[i]); /*in output*/ for (i=0;tsi1[i];i++) /*string array*/ printf("tsi1[%d]= %s\n",i,tsi1[i]); /*in input*/ for (i=0;i<3;i++) /*real array*/ printf("tro1[%d]= %g\n",i,tro1[i]); /*in output*/
*io1 = 3333;
*ro1 = - 8888.; strcpy(so1,"bonjour"); for (i=0;i<10;i++) tio1[i] = i+200; for (i=0;i<10;i++) tro1[i] = (float) i +200.; for (i=0;tso1[i];i++)
/*integer in output*/
/*real in output*/
/*string in output*/
/*integer array*/
/*in output*/
/*real array*/
/*in output*/
/*string array*/ printf("strin(%d)=%s\n",i,tso1[i]);/*in output*/ if(reserved) /*space for strings reserved by prolog */
{ strcpy(tso1[0],"Zone1"); /*first pointer(tso1[0])*/ tso1[1] = tso1[0]+6;
/*initialized by Prolog */
/*others pointers initialized*/
/*by the user */ strcpy(tso1[1],"Zone2");
/*end of array */ tso1[2] = NULL;
} else /*space for strings reserved by the user */
{ tso1[0] = "Zone1"; tso1[1] = (char *) malloc(10); strcpy(tso1[1],"Zone2"); tso1[2] = NULL;
} return 1234.; /*returned value */
} static REAL average(tab,n)/*
----------------------------*/
INTEGER *tab,n;
{
INTEGER i,sum=0; for (i=0;i<n;i++) sum += tab[i]; return (sum/n);
© PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 33
} static mystrcmp(a1,a2)/*
------------------------*/ char **a1,**a2;
{ return strcmp(*a1,*a2);
} static quicksort(tab_in,tab_out)/*
-------------------------------*/ char **tab_in, **tab_out;
{ int i,nbr; for (nbr=0;tab_in[nbr];nbr++) tab_out[nbr] = tab_in[nbr]; tab_out[nbr] = NULL; qsort(tab_out,nbr,sizeof(char *),mystrcmp);
} static reverse(str)/*
------------------------*/ char *str;
{ char c;
INTEGER i,len; len = strlen(str); for (i=0;i<len/2;i++)
{ c = str[i]; str[i] = str[len - (i+1)]; str[len - (i+1) ] = c;
}
}
EXTERNAL_DESCRIPTOR calld_desc[] =
{
{":complete_example", DIRECT_C_FUNCTION, 13, (POINTER) complete_example},
{":average", DIRECT_C_FUNCTION, -1, (POINTER) average},
{":reverse", DIRECT_C_FUNCTION, 1, (POINTER) reverse},
{":qsort", DIRECT_C_FUNCTION, 2, (POINTER) quicksort},
{0, 0, 0, 0}
};
En supposant que le fichier ci-dessus s'appelle callc.c, il faut le compiler, faire l'édition de liens (voir le manuel d'utilisation §2.8.), puis activer le nouveau Prolog pour obtenir :
Prolog II+, ..
...
> eq(r_eserved,30) callC(complete_example
(
11, /*integer in input */
<155,i>, /* integer in output with initialization*/
© PrologIA
R 7 - 34
Manuel de référence
+13.0e0, /* real in input*/
<+222.0e0,r>,/* real in output with initialization*/
"str_input", /* string in input*/
<"ee",s,10>, /* string in output with initialization*/
123.456.nil, /*array of integers in input */
14.0e0.15.0e0.nil, /*array of reals in input */
<11.22.33,w,5,20>, /*array of integers in output
/*with initialization*/
<+44.0e0.+55.0e0.+66.0e0,t1,5,20>, /*array of reals in output with initialization*/
"list_str_in1"."list_str_in2".nil, /*array of STRINGS*/
/*in input */
<"list_str_out1"."list_str_out2"."list_str_out3".nil,
T_S,r_eserved,5>, r_eserved /*integer in input */
),
<"R",m>) /*result */
; ii1= 11 io1= 155 ri1= 13 ro1= 222 si1= str_input so1= ee tii1[0]= 123 tii1[1]= 456 tri1[0]= 14 tri1[1]= 15 tio1[0]= 11 tio1[1]= 22 tio1[2]= 33 tsi1[0]= list_str_in1 tsi1[1]= list_str_in2 tro1[0]= 44 tro1[1]= 55 tro1[2]= 66 strin(0)=list_str_out1 strin(1)=list_str_out2
{r_eserved=30,i=3333, r=-8.888000000000000e+03, s="bonjour", w=200.201.202.203.204.nil, t1=2.000000000000e+02.2.010000000000e+02.2.020000000000e+02.
2.030000000000e+02.2.040000000000e+02.nil,
T_S="Zone1" ."Zone2".nil, m=1.234000000000e+03
}
> callC(average(1.3.5.nil,3),<"R",x>);
{x=3.000000000000e+00}
> callC(reverse(<"abcde",s,20>));
{s="edcba"}
> callC(reverse(<"abcdef",s,20>));
{s="fedcba"}
> callC(qsort("qqq"."zzz"."aaa"."iii".nil,<"".nil,s,0,50>));
/*0 because strings not created (already exist)*/
{s="aaa"."iii"."qqq"."zzz".nil}
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
A!ociation
Prolog
R 7- 35
© PrologIA

Публичная ссылка обновлена
Публичная ссылка на ваш чат обновлена.