Travail pratique #1
IFT-2030 September 28, 2005
¡¡ Dˆ u le 21 octobre ` a midi !!
1 Survol
Ce TP vise `a am´eliorer la compr´ehension des langages fonctionnels en utilisant un langage de programmation fonctionnel (Haskell) et en ´ecrivant une partie d’un interpr´eteur d’un langage de programmation fonctionnel (en l’occurence une sorte de Lisp). Les ´etapes de ce travail sont les suivantes:
1. Parfaire sa connaissance de Haskell.
2. Lire et comprendre cette donn´ee. Cela prendra probablement une partie importante du temps total.
3. Lire, trouver, et comprendre les parties importantes du code fourni.
4. Compl´eter le code fourni.
5. ´Ecrire un rapport. Il doit d´ecrire votre exp´erience pendant les 4 points pr´ec´edents: probl`emes rencontr´es, surprises, choix que vous avez dˆu faire, options que vous avez sciemment rejet´ees, etc... Le rapport ne doit pas exc´eder 5 pages.
Ce travail est `a faire en groupes de 2 ´etudiants. Le rapport (au format PDF exclusivement) et le code sont `a remettre par remise ´electronique avant la date indiqu´ee. Aucun retard ne sera accept´e. Indiquez clairement votre nom au d´ebut de chaque fichier.
Si un ´etudiant pr´ef`ere travailler seul, libre `a lui, mais l’´evaluation de son travail n’en tiendra pas compte. Si un ´etudiant ne trouve pas de partenaire, il doit me contacter au plus tard lundi 10 octobreavantle cours. Des groupes de 3 ou plus sontexclus.
2 Une sorte de Lisp
Vous allez implanter une variante du langage Lisp. Ce langage comprendra des expressions de la forme suivante:
e::=n Un entier sign´e en d´ecimal
|x Une variable
|(op e1 e2) Op´eration arithm´etique, comme dans le TP1
|(e0e1 ... en) Un appel de fonction; les arguments sontcurried
|(lambda(x1... xn)e) Une fonction; les arguments sontcurried
|(let(d1... dn)e) Ajout de d´eclarations locales
|cons|nil|car|cdr Fonctions pr´ed´efinies de manipulation de listes
|(ifnile en ec) Evalue´ en sieestniletec sinon d::= (x e)
|((x x1 ... xn)e)
Les fonctions n’ont en r´ealit´e qu’un seul argument: la syntaxe offre la possibilit´e de d´eclarer et de passer plusieurs arguments, mais ce n’est que du sucre syntac- tique pour des d´efinitions et des appels en formecurried. Plus pr´ecis´ement, les equivalences suivantes sont vraies:
(e0 e1 e2... en) ⇐⇒ (..((e0 e1)e2)... en)
(lambda(x1 ... xn)e) ⇐⇒ (lambda(x1)...(lambda(xn)e)..) La fonction pr´ed´efinie consconstruit une paire (comme le (,) de Haskell), car en extrait la valeur de gauche (commefst) etcdr en extrait la valeur de droite (commesnd). nilest une constante pr´ed´efinie qui est utilis´ee pour repr´esenter la liste vide. consest aussi utilis´e pour cr´eer des listes, ainsi, si l’on fait abstraction des diff´erences de typage, cons est comme l’op´erateur (:) de Haskell, nil est comme [],carest commehead, etcdr est commetail.
La formeletest utilis´ee pour donner des noms `a des d´efinitions locale. Ex- emple:
(let((x2) (y 3)) (+x y)) ∗ 5
Vu que beaucoup de d´efinitions locales sont des fonctions, la forme letaccepte une syntaxe particuli`ere pour d´efinir des fonctions:
(let((y 10) ((div2x) (/ x2))) (div2y)) ∗ 5
Cette syntaxe se r´eduit `a la syntaxe plus rudimentaire avec l’´equivalence suiv- ante:
(let(((x x1 ... xn)e))e) ⇐⇒ (let((x(lambda(x1 ... xn)e)))e) Les d´efinitions d’unletpeuvent ˆetre mutuellement r´ecursives. Exemple:
(let(((evenx) (ifnilx1 (odd(cdrx)))) ((oddx) (ifnilx0 (even(cdrx))))) (odd(cons0 (cons0 (cons0 nil)))))
∗ 1
2.1 S´ emantique dynamique
Les valeurs manipul´ees sont les entiers, les fonctions, la valeurnilnot´ee [], et les paires not´ees [v1 . v2]. De plus la notation de paires est ´etendue de sorte que [v1 .[v2 .[v3 . v4]]] se note [v1v2v3 . v4] et qu’un “.[]” final peut s’´eliminer, de sorte que [v1v2] est ´equivalent `a [v1 [v2.[]]].
Les r`egles d’´evaluation fondamentales sont les suivantes:
((lambda(x)e1)e2) e1[e2/x]
(let((x1e1)...(xn en))e) e[e1, ..., en/x1, ..., xn]
o`u la notation e1[e2/x] repr´esente l’expression e1 o`u chaque occurence libre de xest remplac´ee pare2. Dans votre code, au lieu de la substitution, vous devrez utiliser une ´evaluation bas´ee sur l’usage d’un environnement qui garde trace de la valeur de chacune des variables d´efinies `a un moment donn´e.
En plus des deux r`eglesβ ci-dessus, les diff´erentes constantes pr´ed´efinies se comportent comme suit:
(+n1 n2) n1+n2
(−n1 n2) n1−n2 (∗n1 n2) n1∗n2 (/ n1 n2) n1/n2
nil []
(conse1 e2) [e1 . e2] (car[e1 . e2]) e1
(cdr [e1 . e2]) e2
(ifnil[]en ec) en
(ifnil[e1 . e2]en ec) ec
3 Implantation
L’implantation du langage fonctionne comme suit:
• Une premi`ere phase d’analyse lexiale et syntaxique transforme le code source en un arbre comme d´ecrit ci-dessous. Ce n’est pas encore un arbre de syntaxe abstraite. Cette phase est d´ej`a implant´ee.
• Une deuxieme phase, appel´ee compile, termine l’analyse syntaxique en transformant cet arbre en un vrai arbre de syntaxe abstraite.
• Ensuite une troisi`eme phase, nomm´eeoptimize, essaie de simplifier un peu le code pour qu’il puisse s’ex´ecuter plus efficacement.
• Finalement, une fonction eval proc`ede `a l’´evaluation de l’expression par interpr´etation. Cette fonction est d´ej`a implant´ee.
3.1 Analyse lexicale et syntaxique
L’analyse lexicale et syntaxique est d´ej`a implant´ee pour vous. Elle est plus g´en´erale que n´ecessaire et accepte n’importe quelle expression de la fome suiv- ante:
e::=n|x
|‘(’{e}‘)’
nest un entier sign´e en d´ecimal. Il est repr´esent´e dans l’arbre en Haskell par:
Snumn.
xest un symbole qui peut ˆetre compos´e d’un nombre quelconque de caract`eres alphanum´eriques et/ou de ponctuation. Par exemple‘+’est un symbole,
‘¡=’est un symbole,‘voiture’est un symbole, et‘a+b’est aussi un symbole.
Dans l’arbre en Haskell, un symbole est repr´esent´e par: Ssymx.
‘(’ {e} ‘)’ est une liste d’expressions. Dans l’arbre en Haskell, les listes d’expressions sont repr´esent´ees par des listes simplement chaˆın´ees con- stitu´ees de pairesSconshead tailet du marqueur de finSnil. head est le premier ´el´ement de la liste ettailest le reste de la liste.
Par exemple l’analyseur syntaxique transforme l’expression(+ 2 3)dans l’arbre suivant en Haskell:
Scons (Ssym” + ”) (Scons (Snum2)
(Scons(Snum3)Snil))
L’analyseur lexical consid`ere qu’un caract`ere‘;’commence un commentaire, qui se termine `a la fin de la ligne.
3.2 Le travail
Ce que vous devez faire est de transformer les arbres renvoy´es par l’analyseur syntaxique dans une repr´esentation interm´ediaire qui est une sorte d’arbre de syntaxe abstraite. Cette repr´esentation interm´ediaire est d´efinie par le type:
data Lexp = Lnum Int
| Lvar Var
| Llambda Var Lexp
| Lapp Lexp Lexp
-- D´eclaration simple d’une variable non-r´ecursive.
| Llet Var Lexp Lexp
-- D´eclaration d’une liste de variables qui peuvent ^etre -- mutuellement r´ecursives.
| Lfix [(Var,Lexp)] Lexp deriving (Show, Eq)
Dans cette repr´esentation, cons, ifnil, +, ... sont simplement des variables pr´ed´efinies. La diff´erence entre leLletet le Lfixest que le Lletne d´efini qu’une seule variable, qu’elle ne peut pas ˆetre r´ecursive, et qu’on pr´esume que ce genre de d´efinition peut ˆetre ´evalu´e plus efficacement.
Le fonctioncompiledevra transformer un code source tel que:
(let(((f1x) 1) ((f2x)x)) . . .)
en une forme telle que:
Lfix[(”f1”,Llambda”x” (Lnum 1)), (”f2”,Llambda”x” (Lvar”x”))]
. . .
et la fonctionoptimizedevra le transformer en un code tel que:
Llet”f1”Llambda”x” (Lnum 1) (Llet”f2” Llambda”x” (Lvar”x”)
. . .)
Bien sˆur,optimizene doit appliquer cette transformation que lorsqu’elle est valide, c’est `a dire, seulement pour les variables non-r´ecursives. Pour cela vous aurez besoin d’une fonction auxiliairefreevars que vous devrez aussi implanter et qui renvoie la liste des variables libres d’une expression.
4 Cadeaux
Comme mentionn´e, l’analyseur lexical et l’analyseur syntaxique sont d´ej`a four- nis. Dans le fichierslip.hs, vous trouverez les d´eclarations suivantes:
Sexp est le type des arbres, il d´efini les diff´erents noeuds qui peuvent y ap- paraˆıtre.
readSexp est la fonction d’analyse syntaxique.
showSexp est un pretty-printer qui imprime une expression sous sa forme
“originale”.
Lexp est le type de la repr´esentation interm´ediaire sp´ecialis´ee.
compile est la fonction qui transforme une expression de typeSexpenLexp.
optimize est la fonction qui transforme une expression de type Lexp en une autre expression de typeLexp´equivalente mais plus simple.
Valueest le type du r´esultat de l’´evaluation d’une expression.
env0est l’environnement initial.
eval est la fonction d’´evaluation qui transforme une expression de type Lexp en un valeur de typeValue.
main et test sont les fonctions principales qui lient le tout. En premi`ere ap- proximation, elles correspondent `a
eval env0(optimize(compile(readSexp hcodei))) Voil`a ci-dessous une session d’exemple sur une machine Unix:
% hugs sol.hs
__ __ __ __ ____ ___ _________________________________________
|| || || || || || ||__ Hugs 98: Based on the Haskell 98 standard
||___|| ||__|| ||__|| __|| Copyright (c) 1994-2003
||---|| ___|| World Wide Web: http://haskell.org/hugs
|| || Report bugs to: [email protected]
|| || Version: November 2003 _________________________________________
Haskell 98 mode: Restart with command line option -98 to enable extensions Type :? for help
Main> test "1"
1
Main> main "exemples.slip"
[3,3,1,[[6 7 8 9] . 10]]
Main> [Leaving Hugs]
%
4.1 Le reste
Ce qui manque et que vous devez donc implanter, c’est la fonction de compilation compile, la fonction de simplificationoptimizeet la fonction auxiliairefreevars.
Vous devez les implanter sans rien modifier au reste du code. Vous pouvez bien sˆur ajouter de nouvelles fonctions et de nouveaux types.
4.2 Remise
Vous devez remettre deux fichiers:
slip.hsest une copie du fichier slip.hs`a laquelle vous avez ajout´e le code n´ecessaire pourcompile,freevars etoptimize.
rapport.pdfqui est donc le rapport.
La commande pour remettre ces fichier est donc la suivante:
% remise ift2030 tp1 slip.hs rapport.pdf
5 Note
La note sera bas´ee sur des tests automatiques qui v´erifient le bon fonctionnement de votre code, sur le style, o`u l’´el´egance et la simplicit´e sont beaucoup plus pris´ees que l’efficacit´e, et sur la qualit´e du rapport.
Tout usage de mat´eriel (code ou texte) emprunt´e `a quelqu’un d’autre (ou trouv´e sur le web) doit ˆetre dˆument mentionn´e, sans quoi cela sera consid´er´e comme du plagiat.
6 D´ etails
• Le code ne doit en aucun cas d´epasser 80 colonnes.
• V´erifiez la page web du cours, pour d’´eventuels errata, et d’autres indica- tions suppl´ementaires.