www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 6109acc9474b1c9f6d0af0dbb9e3c3377117d206
parent 702fb1bf3f6ac72b090737279ac6e25d7c0bb164
Author: Georges Dupéron <jahvascriptmaniac+github@free.fr>
Date:   Sun,  9 Jan 2011 20:16:40 +0100

Conversion vers la syntaxe markdown… 90%

Diffstat:
Mlisp/notes/soutenance.markdown | 118+++++++++++++++++++++++++++++++++++++++++++------------------------------------
1 file changed, 65 insertions(+), 53 deletions(-)

diff --git a/lisp/notes/soutenance.markdown b/lisp/notes/soutenance.markdown @@ -1,22 +1,18 @@ -Choses à ne pas oublier à dire lors de la soutenance (keywords: compilation lafourcade um2 m1) - Tests unitaires =============== - * «tests de régression» : on a pu modifier des morceaux de code qui avait déjà les tests et voir que ça tournait toujours (après modifs) * framework extensible : possibilité d'ajouter de nouveaux types de tests (ex. `deftest-error`, `deftest-equiv`) * séparation des tests en modules / sous-modules, possibilité de lancer des tests de certains (sous-)modules seulement. * Statistiques : contabilisation du nombre de tests, d'erreurs, etc. -mini-meval -========== - -C'est un méta-évaluateur 'naïf' mais qui supporte pratiquement tout LISP sauf CLOS (Common Lisp Object System) +`mini-meval` +============ +C'est un méta-évaluateur «naïf» mais qui supporte pratiquement tout LISP sauf CLOS (Common Lisp Object System) Syntaxe supportée par mini-meval et le simplificateur ===================================================== - -* Le `eval-when` +* Les macros +* Les `eval-when` * Les `tagbody` et `go`, les `throw` et `catch`, les `block` et `return` et `return-from` et le `unwind-protect` * Les `let`, `let*`, `flet`, `labels`, `macrolet`, * Les `macro` @@ -24,66 +20,82 @@ Syntaxe supportée par mini-meval et le simplificateur * Dans la `lambda-list`: `&optional`, `&rest`, `&key`, `&allow-other-keys`, `&aux`, mais pas les `&body` * Plus `progn`, `if`, `#'`, `quote`, etc. -lisp2li -======= +`lisp2li` +========= +On a choisi de ne pas utiliser le li proposé par les encadrants, mais au contraire d'utiliser du LISP (très) simplifié qui est donc exécutable ce qui nous a permis de faire des tests unitaires pour voir si la transformation préservait le sens du programme. -On a choisi de ne pas utiliser le li du prof, mais au contraire d'utiliser du LISP (très) simplifié qui est donc exécutable ce qui nous a permis de faire des tests unitaires pour voir si la transformation préservait le sens du programme. +Cette transformation est assurée par la fonction squash-lisp décrite ci-dessous. -Cette transformation est assurée par la fonction squash-lisp : - -squash-lisp -=========== +`squash-lisp` +============= * En 3 passes : * Passe 1 : * macro-expansion (on utilise `mini-meval`) et `eval-when` * simplification globale de la syntaxe : * - (let (a (b 2) c) (list a b c)) - -> (let ((a nil) (b 2) (c nil)) (list a b c)) + (let (a (b 2) c) (list a b c)) + -> (let ((a nil) (b 2) (c nil)) (list a b c)) * unification de la syntaxe des let, let*, flet, labels. * tous les appels de fonction sont transformés en funcall. - * ré-écriture des tagbody/go, throw/catch, block/return(-from) en termes de unwind/unwind-protect, jump/jump-label + variations. - * noms uniques pour les jump/jump-label. - * toutes les constantes sont emballées dans (quote). - * toutes les lectures de variables sont emballées dans (get-var) + * ré-écriture des `tagbody`/`go`, `throw`/`catch`, `block`/`return`/`return-from` en termes de `unwind`/`unwind-protect`, + `jump`/`jump-label` plus quelques variations de ces derniers. + * noms uniques pour les étiquettes des `jump`/`jump-label`. + * toutes les constantes sont emballées dans `(quote)`. + * toutes les lectures de variables sont emballées dans `(get-var)`. * Passe 2 : - * noms uniques pour les variables locales (il n'y a donc plus besoin de connaître l'imbrication des environnements) - * toutes les déclarations de variables locales (let,let*,flet,labels,lambda) sont transformées en simple-let - * le simple-let ne fait que déclarer, il n'afecte pas de valeurs : - (let ((a nil) (b 2)) (list a b)) -> (simple-let (a b) (setq a nil) (setq b 2) (list a b)) - * simplification de la lambda-list (élimination de &optional &rest &key &allow-other-keys &aux) - * suppression des paramètres de la lambda : - (lambda (x y) (+ x y)) -> (simple-lambda (simple-let (x y) (setq x (get-param 0)) (setq y (get-param 1)) (+ x y))) + * noms uniques pour les variables locales. Il n'y a donc plus besoin de connaître l'imbrication des environnements pour savoir à quelle + définition fait référence l'utilisation d'une variable. + * toutes les déclarations de variables locales (`let`,`let*`,`flet`,`labels`,`lambda`) sont transformées en `simple-let` + * le `simple-let` ne fait que déclarer, il n'afecte pas de valeurs : + (let ((a nil) (b 2)) (list a b)) + -> (simple-let (a b) (setq a nil) (setq b 2) (list a b)) + * simplification de la lambda-list (élimination de `&optional`, `&rest`, `&key`, `&allow-other-keys`, `&aux`) + * suppression des paramètres de la `lambda` : + ```lisp + (lambda (x y) (+ x y)) + -> (simple-lambda (simple-let (x y) (setq x (get-param 0)) (setq y (get-param 1)) (+ x y))) +``` * Passe 3 - * On lorsqu'une variable à l'intérieur d'un lambda référence une déclaration à l'extérieur du lambda, on la marque comme étant capturée. - * On fusionne tous les let d'une lambda en les remontant dans un let à la racine de la lamdba. + * On lorsqu'une variable à l'intérieur d'une `lambda` référence une déclaration à l'extérieur de la `lambda`, on la marque comme étant *capturée*. + * On fusionne tous les `let` d'une `lambda` en les remontant dans un `let` unique à la racine de la `lamdba`. * On sort toutes les lambdas (fonctions anonymes), on les nomme avec un symbole unique, et on les met au top-level. -match -===== -* on a fait une fonction de pattern matching qui utilise une syntaxe proche de expressions régulières pour reconnaître des expressions LISP - qui ont une certaine forme / motif . On peut 'capturer' des portions de ce motif dans des variables qui sont accessibles dans le corps des - match. -* On a aussi 'cond-match' qui est un dérivé d'un 'case' mais où chaque test est un match sur une expression. -* On a aussi un 'match-automaton' qui est une sorte d'automate où les transitions ne sont pas des lettres mais des motifs. On avance dans - une liste en sélectionnant à chaque fois la première transition qui match avec l'élément en cours. On peut 'sauvegarder' des données à - chaque transition qui seront mises côte à côte dans une liste. -* defmatch permet de définir une famille de fonctions, dont une seule est exécutée à l'appel (celle qui match avec le(s?) paramètres). -* Pas d'optimisation à la compilation de quelle fonction match : la syntaxe de match est assez lourde, et vu qu'on ne match pas sur la forme - de l'appel de fonction, mais sur les valeurs passées en paramètre, l'optimisation n'est pas faisable 90% du temps. +`pattern-match` +=============== +* Nous avons cherché des bibliothèques de pattern-matching pour LISP, mais la seule qui était suffisemment puissante pour nos besoins + utilisait une syntaxe très lourde qui prenait pratiquement autant de place que des tests du type `(if (eq (car expr) progn) ...)`. +* Nous avons fait une fonction de pattern matching, `pattern-match` qui utilise une syntaxe proche de celle des expressions régulières pour + reconnaître des expressions LISP qui ont une certaine forme (suivant un motif). On peut «capturer» des portions de ce motif dans des + variables qui sont accessibles dans le corps du match. Exemple : + (match (lambda :params @ :body _*) expr + (format t "La liste des paramètres est : ~a" params) + (format t "Le premier élément du corps est : ~a" (car body))) +* On a aussi `cond-match` qui est un dérivé d'un `case` mais où chaque test est un motif qui doit correspondre avec l'expression donnée au + début du `cond-match`. +* On a aussi `match-automaton` qui est une sorte d'automate où les transitions ne sont pas des lettres mais des motifs. On avance dans une + liste en sélectionnant à chaque fois la première transition qui match avec l'élément en cours. On peut «collecter» des données à chaque + transition qui seront mises côte à côte dans une liste, renvoyée par le `match-automaton`. +* `defmatch` permet de définir une famille de fonctions, dont une seule est exécutée à l'appel (celle qui match avec le paramètre). +* Pas d'optimisation à la compilation permettant de détecter de manière statuque quelle fonction de la famille exécuter : la syntaxe de + match est assez lourde, et vu qu'on ne match pas sur la forme de l'appel de fonction, mais sur les valeurs passées en paramètre, + l'optimisation n'est pas faisable 90% du temps (on ne sait pas à la «compilation» quelle sera la valeur du paramètre). compilation =========== -* On compile vers de l'assembleur assez proche de l'x86. On avait déjà un système d'exploitation assez simple qui tournait de manière - autonome sur un PC. On a donc stotché ensemble une REPL compilée et le SE de manière à avoir une REPL autonome qui ne dépend de rien. - le système d'exploitation sous-jacent fournit juste de quoi faire des entrées-sorties. +* On compile vers de l'assembleur assez proche de l'x86. +* Le code généré reste exécutable, tout comme le lisp simplifié. On utilise la même technique : emballer le code généré dans un `macrolet`. +* On avait déjà un système d'exploitation assez simple qui tournait de manière autonome sur un PC. +* On a donc stotché ensemble une REPL compilée et le SE de manière à avoir une REPL autonome qui ne dépend de rien. +* Le système d'exploitation sous-jacent fournit juste de quoi faire des entrées-sorties. -garbage collector -================= +Ramasse-miettes +=============== * gestion du tas -* on a un gc très simpliste qui copie les données d'une zone de la mémoire vers une autre et vice versa à chaque fois qu'elle est pleine. La - raison est que les autres gc nécessite une occupation variable de la mémoire pour l'exécution du gc qui peut être aussi grosse que la - mémoire occupée (O(n)). +* On a un gc très simpliste qui copie les données d'une zone de la mémoire vers une autre et vice versa à chaque fois qu'elle est pleine. +* Ce type de gc s'appelle un [two-finger garbage collector](http://en.wikipedia.org/wiki/Cheney's_algorithm "Article wikipédia"). +* La raison de ce choix de modèle de gc est que les autres types de gc nécessitent une occupation variable de la mémoire pour l'exécution du + gc (nécessaire pour le parcours en largeur/profondeur) qui peut être aussi grosse que la mémoire occupée (O(n)) dans le pire des cas. -= fonctions LISP = -* On a notre propre fonction 'read' et notre propre fonction 'format' pour être autonomes. +Implémentation de fonctions LISP +================================ +* On a notre propre fonction `read` et notre propre fonction `format` pour être autonomes.