Semaine 8 – Interprète d’un mini-langage (3)

, par Pierre

L’interprète du mini-langage consiste essentiellement en la définition du type des programmes et d’une fonction run. Des instructions sans paramètres "Nord" "Sud" "Est" "Ouest" ont fait leur apparition de façon à déplacer un objet sur plan quadrillé. Pour cela, la fonction run prend non seulement en paramètre un programme du mini-langage mais aussi une fonction qui gère les déplacements (et qui pourrait éventuellement affecter les variables).

La fonction test_run montre un usage simple de la fonction run.

  1. exception VariableNonDeclaree;;
  2.  
  3. type expression = Ent of int
  4.                 | Plus of expression * expression
  5.                 | Mult of expression * expression
  6.                 | Div of expression * expression
  7.                 | Moins of expression * expression
  8.                 | Opp of expression
  9.                 | Egal of expression * expression
  10.                 | Sup of expression * expression
  11.                 | Inf of expression * expression
  12.                 | Var of string
  13. ;;
  14.  
  15. type instruction = Declaration of string
  16.                  | Affectation of string * expression
  17.                  | Bloc of instruction list
  18.                  | While of expression * instruction
  19.                  | If of expression * instruction
  20.                  | Print of expression
  21.                  | Println
  22.                  | Nord
  23.                  | Sud
  24.                  | Est
  25.                  | Ouest
  26. ;;
  27.  
  28. type direction = N | S | E | O;;
  29.  
  30. type variable = {nom: string;  mutable valeur: int};;
  31.  
  32. let run p deplacer =
  33.   let variables = [] in
  34.   let rec valeur_variable nom vars = match vars with
  35.     | [] -> raise VariableNonDeclaree
  36.     | {nom=n; valeur=v}::xs when n = nom -> v
  37.     | {nom=n; valeur=v}::xs -> valeur_variable nom xs
  38.   in
  39.   let rec affecter_variable nom va vars = match vars with
  40.     | [] -> raise VariableNonDeclaree
  41.     | c::xs when c.nom = nom -> c.valeur <- va
  42.     | c::xs -> affecter_variable nom va xs
  43.   in
  44.   let declarer_variable nom vars = {nom=nom; valeur=0}::vars
  45.   in
  46.   let rec eval expression vars = match expression with
  47.     | Ent n -> n
  48.     | Plus (e1, e2) -> (eval e1 vars) + (eval e2 vars)
  49.     | Mult (e1, e2) -> (eval e1 vars) * (eval e2 vars)
  50.     | Div (e1, e2) -> (eval e1 vars) / (eval e2 vars)
  51.     | Moins (e1, e2) -> (eval e1 vars) - (eval e2 vars)
  52.     | Opp e1 -> (-1) * (eval e1 vars)
  53.     | Egal (e1, e2) -> (match (eval e1 vars) = (eval e2 vars)
  54.                         with true -> 1
  55.                            | false -> 0)
  56.     | Sup (e1, e2)  -> (match (eval e1 vars) > (eval e2 vars)
  57.                         with true -> 1
  58.                            | false -> 0)
  59.     | Inf (e1, e2)  -> (match (eval e1 vars) < (eval e2 vars)
  60.                         with true -> 1
  61.                            | false -> 0)
  62.     | Var s -> valeur_variable s vars
  63.   in
  64.   let rec exec instruction vars =
  65.     match instruction with
  66.     | Declaration s -> declarer_variable s vars
  67.     | Affectation (s, e) -> affecter_variable s (eval e vars) vars; vars
  68.     | Print e -> print_int (eval e vars); vars
  69.     | Println -> print_string "\n"; vars
  70.     | Bloc [] -> vars
  71.     | Bloc (i::is) -> exec (Bloc is) (exec i vars)
  72.     | While (e, i) as w -> (match (eval e vars)
  73.                             with 0 -> vars
  74.                                | _ ->  exec w (exec i vars))
  75.     | If (e, i) -> (match (eval e vars) with
  76.                     | 0 -> vars
  77.                     | _ -> exec i vars)
  78.     | Nord -> deplacer N vars
  79.     | Sud -> deplacer S vars
  80.     | Est -> deplacer E vars
  81.     | Ouest -> deplacer O vars
  82.   in
  83.   exec p variables
  84. ;;
  85.  
  86. "x = 10;
  87. while (x < 40) {
  88. print x;
  89. println;
  90. x = x + 10;
  91. }";;
  92.  
  93. let test_run () =
  94.   let () = begin
  95.       Graphics.open_graph " 400x400";
  96.       Graphics.moveto (Graphics.size_x () / 2)
  97.                     (Graphics.size_y () / 2);
  98.     end
  99.   in
  100.   let deplacer direction variables =
  101.     let
  102.       aller (x, y) =
  103.       begin
  104.         Graphics.rlineto x y;
  105.         Unix.sleep 1;
  106.         variables
  107.       end
  108.     in
  109.     match direction with
  110.     | N -> aller(0, 10)
  111.     | S -> aller(0, -10)
  112.     | E -> aller(10, 0)
  113.     | O -> aller(-10, 0)
  114.   in
  115.   let p = Bloc [
  116.               Declaration "x";
  117.               Affectation ("x", Ent 0);
  118.               While (Inf (Var "x", Ent 10),
  119.                      Bloc [
  120.                          Est;
  121.                          Nord;
  122.                          Affectation ("x", Plus (Var "x", Ent 1))
  123.                     ]);
  124.               Affectation ("x", Ent 0);
  125.               While (Inf (Var "x", Ent 10),
  126.                      Bloc [
  127.                          Nord;
  128.                          Ouest;
  129.                        Affectation ("x", Plus (Var "x", Ent 1))
  130.                      ]);
  131.             Affectation ("x", Ent 0);
  132.             While (Inf (Var "x", Ent 10),
  133.                    Bloc [
  134.                        Ouest;
  135.                        Sud;
  136.                        Affectation ("x", Plus (Var "x", Ent 1))
  137.                      ]);
  138.             Affectation ("x", Ent 0);
  139.             While (Inf (Var "x", Ent 10),
  140.                    Bloc [
  141.                        Sud;
  142.                        Est;
  143.                        Affectation ("x", Plus (Var "x", Ent 1))
  144.                      ]);
  145.           ]
  146.     in
  147.     run p deplacer;;
  148.  
  149. let () = ignore (test_run ());
  150.          ignore (read_line());
  151. ;;

Télécharger

Question pour une prochaine fois

1. Écrire une fonction déplacer qui prend en paramètre un plateau de jeu représentant le quadrillage (notamment les déplacements possibles et impossibles), une direction et un paramètre supplémentaire non utilisé et déplace un objet sur le plateau selon les quatre directions cardinales, uniquement lorsque le déplacement est possible. Comment stocker la position de l’objet, pour la mettre à jour d’un appel à l’autre à la fonction déplacer ?

2. En ajoutant des instructions Gauche, Droite, Avancer, pour respectivement tourner de 90° à gauche, tourner de 90° à droite, ou avancer d’une case, vous pourriez représenter le déplacement du crayon à la première personne plutôt que de façon absolue avec les points cardinaux. Où stocker la direction dans laquelle va le crayon ? Pouvez-vous stocker cette information comme une variable d’environnement dans la liste des variables manipulée par l’interprète ? Peut-on faire en sorte que la valeur d’une variable d’environnement ne puisse jamais être écrite (directement) par le programme interprété (on admettra le masquage de son nom par une variable locale) ?

3. Pouvez vous afficher le code source du programme à l’écran, d’une façon élégante (en utilisant une syntaxe concrète plus agréable que la syntaxe abstraite) ?

Interprète graphique encore amélioré
Pourriez-vous afficher le code source du programme ? Et déplacer le vaisseau comme si c’était le crayon ?

4. Comment faire pour que le mini-langage admette des définitions et des appels de fonctions ?

Retour sur l’affichage en double buffer

L’affichage ocaml fonctionne bien avec un mécanisme de double buffer :

- un back buffer dans lequel on écrit lorsque on a appelé remember_mode avec comme argument true

  1. Graphics.remember_mode true;;


- un front buffer qui est simplement le contenu de l’écran et dans lequel on écrit lorsque on a appelé display_mode avec l’argument true

  1. Graphics.display_mode true;;

Pour dessiner un décor puis un ou plusieurs objets mobiles on va afficher un certain nombre d’images fixes dont la succession rapide donnera l’illusion d’animation. Ces images doivent êtres faites du décor par dessus lequel les objets mobiles auront été placés. Le plus simple est de n’écrire que sur l’écran et pas dans le back buffer. Le back buffer sera préparé avec le décor une fois pour toute. Après quoi on cesse d’y écrire et on dessine les objets mobiles uniquement à l’écran. Après la première scène, et à chaque fois après chaque scène, on recopie alors le contenu du back buffer à l’écran pour restaurer un décor vierge avant d’y dessiner les objets mobiles à leurs nouvelles positions. (L’idéal serait de bénéficier d’un troisième buffer pour préparer cette nouvelle scène avant de l’afficher mais le module graphics ne fournit pas cette facilité.) Pour restaurer le contenu du back buffer dans le front buffer (l’écran) on utilise la fonction synchronize

  1. Graphics.synchronize ();;

Ceci est une première solution pour réaliser des animations. On remarquera toutefois que chaque scène nécessite la recopie complète du back buffer dans le front buffer. Une solution plus élégante serait de ne rétablir l’image de fond (le décor) que là où la scène précédente contenait un objet qui doit être effacé dans la nouvelle scène. Pour approcher de ce résultat il faut utiliser la fonction

  1. Graphics.get_image x y largeur hauteur;;

Cette fonction a un comportement intéressant (mais non documenté ?). En effet seul le contenu du back buffer est lu par cette fonction (elle ne tient pas compte de ce qui est dans le front buffer.

  1. #load "graphics.cma";;
  2. Graphics.open_graph " 100x100";;
  3.  
  4. (* rectangle bleu back et front *)
  5. Graphics.set_color Graphics.blue;;
  6. Graphics.fill_rect 0 0 50 50;;
  7. let bleue = Graphics.dump_image (Graphics.get_image 0 0 4 4);;
  8.  
  9. (* rectangle vert front *)
  10. Graphics.set_color Graphics.green;;
  11. Graphics.remember_mode false;;
  12. Graphics.fill_rect 0 0 50 50;;
  13. let verte = Graphics.dump_image (Graphics.get_image 0 0 4 4);;
  14. verte = blue;; (* <-- true *)
  15.  
  16. (* rectangle vert back et front *)
  17. Graphics.remember_mode true;;
  18. Graphics.fill_rect 0 0 50 50;;
  19. let verte = Graphics.dump_image (Graphics.get_image 0 0 4 4);;
  20. verte = bleue;; (* <-- false *)
  21. Graphics.close_graph ();;

Télécharger

Par ailleurs cette fonction get_image vous sera utile pour composer vos effets de transparence. On peut d’ailleurs coupler la composition de la transparence à la restauration du décor en mettant une marge complètement transparente autour de nos images d’objets mobiles. Attention toutefois : deux objets du front ne seront pas transparents l’un pour l’autre.

Bluehats & UnivMobile , Présentation de la démarche design employée pour UnivMobile faite à la rencontre bluehats du 11 décembre 2019. [pdf, jpg]
Mon université en 2030, Texte d'une intervention que j'ai faite dans le cadre d'une soirée Cap 2030, organisée par le EdFab à Cap Digital le 27 février (...)
Revenu et logement, Je livre ici quelques éléments de comparaison concernant mon niveau de vie, pour couper court à quelques idées reçues, et un condensé de nombreuses (...)
Revenu et travail d’un enseignant-chercheur, Cet article complète l'article Revenu et logement, en détaillant un peu le budget de mon ménage, mon parcours d'enseignant-chercheur en terme de (...)
Cybersyn (el systemo synco), Au café, mardi 5 avril 2011, j'ai bien vu que, mis à part Antoine Allombert, personne ne connaissait l'histoire de l'extraordinaire projet chilien (...) [jpg, jpg, png]