Skip to topic | Skip to bottom
Home
Projects
Projects.LooAstr1.7 - 13 Mar 2006 - 13:43 - Main.akimtopic end

Start of topic | Skip to actions

LooAst

Le projet LooAst vise l'implémentation de l'AST (Abstract Syntax Tree) d'un micro langage. Ce sujet de IsoLoo est volontairement simplifié à l'extrême : il s'agit d'exercer votre aptitude à implémenter élégamment en Eiffel, et tirant partie de ses fonctionnalités telles que la conception par contrat.

Le langage Loo

Voici un exemple du langage Loo :

let
  a := 42
in
  let
    b := if a = 0 then 42 else 51
    res := a + b
  in
    -- On n'a pas de test de différence en Loo,
    -- il faut donc écrire (a = b) = 0 pour
    -- tester si a /= b.
    if (a + b = 93) = 0 then
      res := -1  -- ceci n'est pas un opérateur unaire
                 -- mais bien l'entier `-1'.
    else
      res := 1
    res
  end
end

Lorsqu'on l'exécute, ce programme doit afficher

1

La syntaxe de Loo

Voici la grammaire du langage :

<program> ::= <expression>
<expression> ::= <integer>
               | <variable>
               | <variable> := <expression>
               | <expression> <op> <expression>
               | if <expression> then <expression> else <expression>
               | let <declaration>+ in <expression>+ end
<declaration> ::= <variable> := <expression>
<op> ::= + | - | * | /
       | < | =

Par convention d'écriture de grammaire, les parenthèses servent à grouper, le point d'interrogation signifie optionnel (i.e., 0 ou 1 fois), et le + représente la répétition 1 ou plusieurs fois.

Il ne sera pas demandé d'écrire d'analyseur syntaxique (ou /parser/) de Loo. Les programmes seront construits à l'intérieur de votre projet directement sous la forme d'arbre. Cela signifie en particulier qu'aucun programme mal formé syntaxiquement ne pourra être rencontré.

La syntaxe abstraite de Loo (LooAst)

L'exercice demandé est l'implémentation d'une hiérarchie de classes permettant de représenter un programme Loo. Ces classes sont très simplement définies par la grammaire.

Par exemple, en pseudo Eiffel :

class LOO_LET

inherit LOO_EXPRESSION
  redefine print, type, eval
 end

feature

  declarations : LINKED_LIST[LOO_DECLARATION]
  expressions  : LINKED_LIST[LOO_EXPRESSION]


  make (d: LINKED_LIST[LOO_DECLARATION],
        e: LINKED_LIST[LOO_EXPRESSION]) is ...

  -- Afficher cette expression ainsi :
  --  let
  --    declarations
  --  in
  --    expressions
  --  end
  display is ...

  -- Vérifier le typage, et calculer le type de cette
  -- expression.
  type (e: LOO_ENVIRONMENT) : LOO_TYPE is ...

  -- Évaluer et retourner la valeur de l'expression
  -- dans cet environnement (ou Void dans le cas des
  -- instructions).
  eval (e: LOO_ENVIRONMENT) : reference INTEGER is ...

end

Le typage de Loo

En Loo n'existe que deux types : le type Integer pour les expressions ayant une valeur entière, et le type Void pour les instructions qui, précisément, ne peuvent pas avoir de valeur.

Les règles sont les suivantes.

  • programme
         <program> ::= <expression>
    
    L'expression doit être de type entier.
  • entier littéral
         <expression> ::= <integer>
    
    L'expression résultante est de type entier.
  • valeur de variable
         <expression> ::= <variable>
    
    Puisque les variables sont seulement de type entier, cette expression est de type entier. La variable doit avoir été définie.
  • affectation
         <expression> ::= <variable> := <expression>
    
    La valeur affectée doit être entière. L'affectation est une instruction, donc de type Void.
  • opérations binaires
         <expression> ::= <expression> <op> <expression>
    
    Elles nécessitent toutes que les parties droite et gauche soient entières, et retourne un entier.
  • instruction et expression conditionnelles
         <expression> ::= if <expression> then <expression> else <expression>
    
    Derrière cette seule syntaxe se cachent deux opérations. Si les deux opérations (du then et du else) sont des instructions, alors le résultat est de type Void également ; ce cas s'apparente au if de Java, C, etc. Si par contre elles sont toutes les deux des expressions à valeur entières, alors le résultat est entier également ; ceci correspond à l'opérateur ? : de Java, C, etc.
    Bien entendu le test doit être de valeur entière.
    Tout autre cas est une erreur.
  • introduction d'une portée
         <expression> ::= let <declaration>+ in <expression>+ end
    
    Aucune restriction sur les expressions (entre le in et le end). Le type de l'ensemble est celui de la dernière expression : il y a donc des expressions et des instructions de type let.
  • déclarations
         <declaration> ::= <variable> := <expression>
    
    La variable ne doit pas avoir été définie dans la portée courante, et l'expression doit être de type entier.

La sémantique de Loo

  • programme
         <program> ::= <expression>
    
    La valeur du programme (qui sera affichée) est la valeur de l'expression.
  • entier littéral
         <expression> ::= <integer>
    
    La valeur de l'expression est celle de l'entier.
  • valeur de variable
         <expression> ::= <variable>
    
    Retourne la valeur courante de la variable.
  • affectation
         <expression> ::= <variable> := <expression>
    
    Met à jour la valeur courante de la variable.
  • opérations binaires
         <expression> ::= <expression> <op> <expression>
    
    Pour l'arithmétique les sémantiques sont respectées, pour les comparaisons, le résultat est 0 pour le faux, et 1 pour le vrai.
  • instruction et expression conditionnelles
         <expression> ::= if <expression> then <expression> else <expression>
    
    Si le test s'évalue à faux (0), alors le partie else est évaluée (et le cas échéant la valeur du if entier est celle-ci). Autrement, c'est la partie then qui est évaluée.
  • introduction d'une portée
         <expression> ::= let <declaration>+ in <expression>+ end
    
    Dans le cas d'une expression, la valeur est celle de la dernière expression.
  • déclarations
         <declaration> ::= <variable> := <expression>
    
    Initialise la valeur de la variable à celle de l'expression.

L'exercice demandé

Les fonctionnalités citées ci-dessus sont demandées dans l'ordre des sous-sections suivantes.

L'interface utilisateur

Il ne vous est pas demandé d'écrire un /analyseur syntaxique/ (/parser/ en anglais) ; en faire un est un exercice à part entière, qui relève plus de cours tels que THL ou GRANA que LOO. Par conséquent, l'utilisateur ne "tape pas" de programme Loo : ces programmes sont prédéfinis dans votre programme, et l'utilisateur ne fait qu'en choisir un.

Il est stipulé que dans votre rendu *il doit y avoir au moins dix programmes prédéfinis* : cinq corrects (qui affichent un résultat entier), et cinq incorrects (qui engendrent une exception). Ils doivent être réellement différents les uns des autres : ils doivent tester des aspects différents de votre compilateur.

Je suggère que votre classe maîtresse (lancée par l'exécution) aient plusieurs méthodes de création de programmes, par exemple :

  correct_bin : LOO_EXP is
  do
    Result := create {LOO_BIN}.make ("*",
                                     create {LOO_INT}.make (6),
                                     create {LOO_INT}.make (7))
  end

  undeclared_variable : LOO_EXP is
  do
     Result := create {LOO_VAR}.make ("toto")
  end

Le corps de la routine de création, après avoir demandé le numéro du programme voulu, effectue:

inspect num
  when 1 then exp := correct_bin
  when 2 then exp := undeclared_var
  ...
end

Il s'agira ensuite d'exécuter les opérations suivantes.

Affichage, display

Il s'agit simplement de réécrire l'AST sous forme humaine. Auparavant nous avions suggéré d'utiliser le nom print, ce qui est impossible : il est frozen dans ANY. Cependant si vous voulez faire les choses "bien", cherchez print_on.

Évaluation, eval

Implémenter une évaluation. Il faut prendre garde à :

  • gérer correctement les environnements
  • vérifier par contrats que le typage est correct
  • vérifier par contrats que les déclarations sont correctes (on ne définit pas deux fois un nom dans un même étage de déclaration)

Tout programme incorrect doit donner lieu à un viol de contrat. Voici quelques exemples incorrects en syntaxe Loo :

a + b

let
  a := 1
  a := 1
in
  a + a
end

let
  a := 0
  b := 0
in
  a := b := 1
  a
end

Dans ce dernier cas le problème est lié au typage : b : 1= est une instruction qui n'a pas de valeur. Une affectation nécessite que l'expression ait une valeur, i.e., soit typé comme un entier.

Les comparaisons, = et <, renvoient des entiers : 0 (faux) et 1 (vrai).

Contrôle de type : type

Vérifier le typage de l'arbre avant de l'évaluer. Si les règles ne sont pas vérifiées, faire un joli message d'erreur. En aucune situation il ne doit être possible de voir à nouveau un contrat violé.

L'environnement

Une partie significative de cet exercice consiste en la conception d'un environnement, ou encore /table des symboles/. Cette structure se comporte comme un tableau associatif auquel sont adjointes des opérations de gestion de portées. Cette structure est indépendante des types des clefs et des valeurs, elle est /générique/. Il est donc expressément demandé d'implémenter cette structure d'une façon générique, même si, en effet, votre programme utilisera exclusivement des clefs chaînes de caractères et des valeurs entières.

Sa signature est par conséquent :

class LOO_ENVIRONMENT[KEY->HASHABLE, VALUE]

feature

  -- Entrée dans une nouvelle portée, prête à
  -- masquer temporairement les variables précédentes.
  push is ...

  -- Sortie de la dernière portée.
  pop is ...

  insert (k: KEY, v: VALUE)
  -- k doit exister auparavant.
  set (k:KEY, v: VALUE)
  has (k: KEY) : BOOLEAN
  -- k doit exister dans l'environnement.
  get (k: KEY) : VALUE

end

Le rôle de l'environnement est compréhensible dans l'exemple ci-dessous :

let
  a := 1
in
  let
    a := 2
  in
    a
  end
  a
end

dont le résultat est 1 et non pas 2.

Vous pouvez (devriez) introduire une classe (non générique) pour le cas utilisé par votre propre programme :

class LOO_VARIABLES
  inherit LOO_ENVIRONMENT[STRING, INTEGER] end
end

Règles d'évaluation du projet

Les règles pour le rendu sont décrites dans IsoLoo. La date est le 16 février 12h.

Le contrat est (avec les coûts) :

  • D: -2: Rendu correct, selon la norme (delivery)
  • R: un fichier texte ou HTML est livré, et décrit (README) :
    • RC: -1: comment compiler votre application
    • RR: -1: comment la faire fonctionner (run)
    • RD: -2: quels sont les grands axes de votre modélisation (design)
    • RH: -2: votre hiérarchie (UML est bien sûr le bienvenu)
  • H: -2 la hiérarchie est correcte (utilisation judicieuse de classes abstraites etc.)
  • G: -2: l'environnement est implémenté de façon générique
  • T: -2: il y a au moins 10 tests (5 ok; 5 ko) réellement différents
  • Y: +4: vous avez implémenté type, l'optionnel contrôle de types
  • C: -4: les contrats Eiffel sont largement utilisés (préconditions etc.) et il n'est jamais possible de faire échouer votre programme d'une façon inattendue.
  • Q: le code doit être de bonne qualité :
    • QR: -1: lisible et compréhensible (readable)
    • GM: -1: pas de méthodes monstres
    • QC: -1: des commentaires bien placés
    • QD: -2: pas de duplication de code
    • QE: -2: pas d'erreur d'encapsulation (les détails d'implémentation d'une classe ne regarde pas les autres)

La qualité des commentaires et des contrats sera en particulier évaluée grâce à l'outil short.


to top


You are here: Projects > LooAst

to top

Copyright © 1999-2010 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback