PROJET CAML: Interpréteur de commandes LOGO réalisé en Caml

Transcription

PROJET CAML: Interpréteur de commandes LOGO réalisé en Caml
LARMURIER Cyril
LOPES Guillaume Le 09/01/2006
PROJET CAML:
Interpréteur de commandes LOGO
réalisé en Caml
Licence Informatique
Pour commencer, nous tenons à signaler que nous n'avons utilisé aucune variable globale
(excepté pour la constante Pi) et aucun champ mutable. De plus, nous avons essayé de rester le plus
proche du style fonctionnel. Le but du projet est de réaliser un interpréteur de commande LOGO en
Caml. Les types étant fournis dans les utilitaires, nous n'avons créé aucun type et nous n'avons pas
jugé utiles d'en créer. Pour coder cet interpréteur, il nous a fallu comprendre comment fonctionne le
langage LOGO qui nous a été fournis. Nous avons remarqué que le langage LOGO se découpe
selon 3 grands axes que sont les expressions, les primitives et les statements. En effet, un langage
LOGO est en fait une liste de statement (il y a aussi les déclarations de fonction que nous
détaillerons à la fin).
0. Les exceptions
Ici, nous décrivons les exceptions qui ont été créées et à quoi elles servent. Nous avons eu
besoin de créer huit exceptions que voici:
➢ exception Int_not_found : exception retournée quand on ne trouve pas d'entier, alors qu’on
s'attend à en trouver un.
➢ exception Bool_not_found : même chose que pour la précédente mais pour les booléens
➢ exception Neither_bool_or_int : exception retournée lorsque ni un booléen, ni un entier ne sont
trouvés.
➢ exception Var_undeclared : exception retournée lorsqu'une variable n'est pas déclarée dans la
liste de variables globales.
➢ exception Function_undeclared : exception retournée lorsqu'une fonction n'est pas déclarée dans
la liste de fonctions de l'environnement.
➢ exception Empty_list : exception retournée lorsqu'une liste est vide.
➢ exception Arguments_error : exception retournée quand une fonction est appelée avec un
mauvais nombre d'arguments.
➢ exception Not_in_function : exception retournée lorsqu'un Output ou un Stop est lancé hors d'une
fonction.
I. Les expressions
Pour pouvoir analyser les expressions, qui sont la base du langage, nous avons créé quatre
fonctions:
­> La fonction eval_expression qui a pour prototype
eval_expression e angle pen in_function v_list f_list f_value
­> La fonction int_of_exp qui a pour prototype int_of_exp e angle pen in_function v_list f_list f_value
­> La fonction bool_of_exp qui a pour prototype
bool_of_exp e angle pen in_function v_list f_list f_value
­> La fonction value_of_varexp qui a pour prototype value_of_varexp s v_list La fonction int_of_exp se contente de retourner un entier, tout en s'assurant qu’il n'y ait pas d'erreur.
La fonction bool_of_exp fait la même chose mais pour les booléens. Enfin, la fonction principale
eval_expression se contente d'appeler les deux fonctions précédentes en renvoyant leurs résultats en
type constant. Regardons de plus près les paramètres.
–
–
–
–
–
–
–
e est l'expression à analyser, elle est de type constant.
angle est l'angle de rotation de la tortue, c'est un entier.
pen est le booléen qui permet de savoir si le crayon est levé ou non.
in_function est le booléen qui permet de savoir si l’on se trouve à l'intérieur d'une fonction.
v_list est la liste de variables globales dans l'environnement, elle est de type (string*const)
list.
f_list est la liste de fonctions dans l'environnement, elle est de type (string*string list*stat list) list.
f_value est la valeur de retour d'une fonction, elle est de type const list.
Ici, seuls sont concernés les paramètres e et v_list, les autres sont utiles pour le cas du CallExp, que
nous expliciterons dans la partie statements (En effet, un CallExp est un appel de fonction qui
retourne une valeur, donc elle est quasiment identique au ProcCall qui se contente de réaliser un
appel de fonction).
Pour comprendre ce que fait val_of_varexp, il faut préciser que nous avons décidé de stocker les
variables globales dans une liste de couples. Dans la première composante du couple se trouve le
nom de la variable de type string, et dans la deuxième composante la valeur de la variable en type
const. Donc value_of_varexp se contente de rechercher le nom de la variable s dans la liste de
couple v_list et de retourner la deuxième composante, d'où l'utilisation de la fonction prédéfinie
List.assoc. On remarquera que la fonction retourne l'exception Var_undeclared si la variable ne se
trouve pas dans la liste. Cette fonction permet de traiter le cas de VarExp dans la fonction
eval_expression.
A partir de ces trois fonctions, nous pouvons traiter les expressions LOGO en Caml. Passons
maintenant aux primitives.
II. Les primitives
Les primitives en LOGO sont les fonctions qui permettent de faire des dessins avec la tortue
ou d'afficher des expressions à l'écran. Cette partie n'a pas été évidente car la librairie graphique de
Caml n'est pas très riche. Il nous a fallu déclarer une variable globale Pi et créer six fonctions:
­> La fonction home qui a pour prototype home()
val home : unit ­> unit ­> La fonction right qui a pour prototype right angle
val right : int ­> float * float
­> La fonction left qui a pour prototype left angle
val left : int ­> float * float
­> La fonction forward qui a pour prototype forward longueur (x,y) pen
val forward : int ­> float * float ­> bool ­> unit
­> La fonction print_const qui a pour prototype print_const constante
val print_const : Logosyntax.const ­> unit
­> La fonction eval_primitive qui a pour prototype eval_primitive prim exp_list angle pen in_function v_list f_list f_value
La fonction home permet de positionner la tortue au milieu de la fenêtre graphique sans dessiner.
Les fonctions left et right renvoient un couple de réels, qui représente la direction de la tortue pour
une rotation gauche ou droite respectivement. Elles prennent toutes deux en paramètre un entier
angle qui représente l'angle de rotation. Ces deux fonctions sont mutuellement récursives.
La fonction forward permet de dessiner un trait ou de juste déplacer la tortue. Elle prend en
paramètre un entier longueur qui représente la longueur du trait, un couple de réels qui représente la
direction, et un booléen pen qui permet de savoir si le crayon est levé ou non.
La fonction print_const permet d'afficher sur l'écran une constante. Elle prend en paramètre une
constante et renvoie du type unit.
Enfin, la fonction eval_primitive permet d'évaluer les primitives en utilisant les fonctions décrites
ci­dessus. Elle prend en paramètre une primitive prim, une liste d'expressions exp_list, et les autres
paramètres qui ont déjà été explicités. Elle renvoie un six­uplet composé dans l'ordre de angle, pen,
in_function, v_list, f_list et f_value. Ces variables ont déjà été décrites dans la partie I. Il est juste
inutile de noter que seules sont intéressantes les variables angle, pen et bien sûr, v_list. On rappelle
que angle est l'entier qui représente l'angle de rotation, pen est le booléen qui permet de savoir si le
crayon est levé ou non, et v_list est la liste de variables globales, qui est toujours de type (string * const) list. Précisons aussi comment l'angle est géré dans eval_primitive. Lorsque le
programme est lancé, la valeur de angle est de 360. Ensuite quand cette valeur rentre dans
eval_primitive, il y a deux possibilités : Soit on est dans le cas où l'utilisateur a demandé un left x et
donc la valeur angle sera décrémentée de x, soit l'utilisateur a demandé un right x et on
incrémentera angle de x. Pourquoi? En fait, dans eval_primitive, on ne fait que des appels à la
fonction right. En effet, on remarque que faire left 60 équivaut à faire right 300 ! Donc dans
eval_primitive, on calcule un seul angle qui sera analysé par la fonction right.
A partir de ces fonctions, nous avons pu interpréter les primitives LOGO en Caml et faire de beaux
dessins. Passons aux statements.
III. Les statements
Avant de commencer, indiquons comment nous avons géré le problème des fonctions et de
leur valeur de retour possible. Dans notre programme, toutes les fonctions déclarées dans un
programme Mini LOGO sont stockées dans une liste de triplet, ou dans le triplet se trouve, en
première composante le nom de la fonction (string), en deuxième composante se trouve la liste de
ses arguments (string list) et enfin en dernière composante se trouve la liste de statements (stat list),
qui représente les différentes actions faites par la fonction. Les valeurs de retour de fonctions sont
stockées dans une liste de const (const list), cette liste se trouve dans la dernière composante de
notre six­uplet retournée par la fonction eval_primitive (et qui sera retournée par les fonctions
suivantes aussi).
Pour gérer les statements, il nous as fallu créer six fonctions (nous ne parlons ici que des fonctions
principales) qui sont :
­> La fonction add_variable qui a pour prototype add_variable variable v_list ­> La fonction modif_variable qui a pour prototype modif_variable variable v_list ­> La fonction eval_repeat qui a pour prototype eval_repeat nb_tours s_list tmp_list angle pen in_function v_list f_list f_value.
­> La fonction eval_if qui a pour prototype eval_if stat_list angle pen in_function v_list f_list f_value
­> La fonction eval_procCall qui a pour prototype eval_procCall stat_list longueur angle pen in_function v_list f_list f_value
­> La fonction eval_stat qui a pour prototype
eval_stat instruction angle pen in_function v_list f_list f_value
Les fonctions add_variable et modif_variable sont triviales, elles prennent toutes les deux une
variable de type (string*const) et la liste de variable globale (string*const) list, et retournent la liste
de variable globale modifiée. La première se contente de rajouter une variable en tête de liste,
tandis que la deuxième cherche une variable dans la liste ayant le même nom que celle donnée en
paramètre et modifie sa valeur (const).
La fonction eval_repeat fonctionne avec une fonction auxiliaire qui est eval_repeat_aux qui n'est
pas explicitée ici, mais elle sert juste à trouver le nombre de tours(nb_tours). Les paramètres de
eval_repeat sont :
– nb_tours est un entier qui représente le nombre de tours de boucle que doit faire le
repeat.
– s_list est la stat list qui représente toutes les actions que doit effectuer le repeat.
– tmp_list est identique à s_list.
– angle, pen,in_function, v_list, f_list et f_value sont des paramètres déjà rencontrés
précédemment.
Que fait­on dans eval_repeat?
On appelle récursivement eval_repeat pour vider la tmp_list. Une fois qu'elle est vide, on rappelle
eval_repeat avec s_list à la place de tmp_list et on décrémente le nombre de tours nb_tours. Une
fois qu'il est égal à zéro, on renvoie le six­uplet.
La fonction eval_if est triviale, elle se contente de parcourir la stat_list en paramètre et d'appeler
eval_stat sur chaque statement. Elle renvoie elle aussi un six­uplet.
La fonction eval_procCall a pour paramètre :
– stat_list est une stat list qui représente tous les statements effectués dans une fonction.
– longueur qui représente le nombre de variables globales avant exécution de la fonction.
– in_function est un booléen qui permet de savoir si on se trouve dans une fonction.
– f_value est une liste de const qui est la valeur de retour d'une fonction.
Ces deux derniers paramètres sont utiles pour le Output et le Stop. Les autres paramètres ont déjà
été rencontrés. Avant de lancer procCall, on calcule la longueur de la liste de variable globale, et on
recherche à l'aide de la fonction return_function la fonction (le triplet de la liste de fonctions de
l'environnement) qui doit être utilisé.
La fonction eval_procCall se contente de faire un eval_stat sur chaque statement de sa liste de
statements. Une fois que la liste est vide, elle supprime les variables locales, qui ont été déclarées
localement dans la fonction, dans la liste de variables globales à l'aide de la fonction
supp_temporary_var (qui n'est pas explicitée ici vu sa trivialité). De plus, elle remet le booléen
in_function à false et stocke, s’il y en a une, la valeur de retour dans f_value et bien sûr renvoie un
six­uplet.
La fonction eval_stat se contente d'exécuter l'instruction donnée en paramètre en appelant l'une des
fonctions décrites ci­dessus. Elle renvoie un six­uplet.
Revenons sur ce six­uplet. Rappelons que c'est la base de notre programme, grâce à lui, nous
n'avons pas eu besoin de champ mutable, ni de variable globale. Il est composé de six composantes
qui sont dans l'ordre:
– angle est l'entier représentant l'angle de rotation de la tortue.
– pen est le booléen qui permet de savoir si le crayon est levé ou non.
– in_function est le booléen qui permet de savoir si l’on se trouve à l'intérieur d'une
fonction ou non .
– v_list est la liste de variables globales.
– f_list est la liste de fonctions de notre environnement.
–
f_value est la valeur de retour d'une fonction.
Revenons sur eval_expression ou nous n'avions pas traité le cas du CallExp. En fait, dans
eval_expression, int_of_exp et bool_of_exp, lorsque l’on tombe sur le cas d'un CallExp, on appelle
eval_procCall, qui va exécuter la fonction et renvoyer dans la dernière composante du six­uplet la
valeur de la fonction . Il suffira juste de récupérer la dernière composante du six_uplet.
Déclarations de fonctions
Juste un mot sur les déclarations de fonctions que nous utilisons dans la fonction
eval_command_aux. On a créé une fonction add_function qui ajoute une fonction dans la liste de
fonctions, et qui renvoie donc la nouvelle liste de fonctions créée.
Les deux dernières fonctions importantes sont eval_command_aux qui se contente de parcourir la
liste de commandes, et eval_command qui appelle eval_command_aux avec, dans cet ordre : la liste
de commande, un angle de 360 degrés, deux booléens à false et 3 listes vides. Notons que ces deux
fonctions renvoient un six­uplet, qui correspond donc à l'environnement du programme Mini
LOGO. Nous n'avons pas réussi à faire comme demander dans l'énoncé et renvoyer du type unit.
Mais il nous semble intéressant tout de même de garder ce six­uplet pour vérifier si le programme
fonctionne comme attendu.
En conclusion, notre interpréteur de commandes Mini LOGO réussi à gérer tout le langage,
sauf les fonctions récursives. Mais les appels de fonction dans une fonction, avec ou sans retour de
valeur, sont très bien gérés. Nous espérons réussir à résoudre le problème avant la soutenance du
projet. Nous sommes désolés d'avoir laissé cette erreur mais par manque de temps, nous n'avons pas
réussi à la corriger. Malgré cette erreur, tout le reste (sauf omission) fonctionne.
P.S : Vous trouverez ci­joint au rapport le code source et des fichiers logo sur lesquels notre
interpréteur fonctionne.

Documents pareils