:: Enseignements :: Master :: M2 :: 2012-2013 :: Machine Virtuelle (et bazard autour ...) ::
[LOGO]

Stack interpreter


Le but de ce TD est d'implanter un interpréteur à pile pour la langage Small. Cela nécessite dans un premier temps de transformer l'arbre de syntax abstrait (AST) en une suite d'instructions (opcodes) qui seront ensuite executées par l'interpreteur.
L'archive au format ZIP contenant une base de code (les classes de l'AST, le rewriter et le StackInterpreter) est diponible ici

Exercice 1 - Ré-écriture de l'AST

Le but de ce premier exercice est d'implanter un nouveau visiteur sur l'arbre (en fait deux, cf plus loin) qui permet de ré-écrire l'arbre sous forme d'une suite d'instructions puis d'exécuter les instructions.

  1. Dans la classe FunctionRewriter, à auoi servent les champs objectTable et writer ?
  2. A quoi servent les champs statiques UNARY_OPCODES et BINARY_OPCODES ?
  3. A quoi sert la méthode declaredVarNames ?
  4. On cherche à implanter les méthodes suffisantes pour exécuter l'exemple suivant
             def main(): 
               "hello"
             

    Comme pour le TD1, on supposera pour l'instant qu'il n'y a pas d'autre méthode que main.
    • A quoi sert la classe interne Env de la classe FunctionRewriter ainsi que ses méthodes push et pop ?
    • A quoi sert la méthode rewriteBlock ?
      Implanter celle-ci en pensant que la valeur d'un bloc d'expressions est la valeur de la dernière expression du bloc ou undefined si le block est vide.
    • Ecrire la méthode visitConst de l'exprVisitor.
      Tester avec la méthode DebugPrinter.dumpOpcodes que le code générée est bien correct.
    • Modifier la méthode StackInterpreter.execute pour exécuter le code généré.

Exercice 2 - Interpreteur à pile

On souhaite continuer l'exercice 1 pour que l'interpreteur (et le rewriter) reconnaisse l'ensemble de la syntaxe reconnue par l'ASTInterpreter écrit lors du TP précédent.

  1. Comment faire pour que la fonction spécial print soit reconnue au niveau de l'interpreteur ?
    Implanter celle-ci pour quelle affiche ses arguments.
             def main(): 
               print("hello")
             
    Vérifier que le code ci-dessus fonctionne.
  2. Ajouter la gestion des variables et vérifier que l'exemple suivant fonctionne.
              def main(): 
                a = 7
                print("hello", a)
             
  3. Vérifier que le code suivant fonctionne.
              def main(): 
                b = a = 7
                print("hello", a, b)
             
  4. Vérifier que le code suivant affiche undefined.
              def main():
                print(a)
             
  5. Implanter les opérations unaires.
    Rappel: + et - fonctionne uniquement sur les entiers et ! uniquement sur les booléens.
  6. Implanter les opérations binaires.
    Rappel: +, -, *, /, %, <, <=, >, >= travail sur des entiers et == et != travail sur tout les types (e.g. 2 == "hello" est valide et renvoie false).
  7. A quoi sert la méthode OpcodeWriter.patchAddress ? Comment l'utiliser ?
    Implanter la syntax du if
    Rappel: la branche else du if peut ne pas avoir d'expression, donc le code suivant est valide
                def main():
                  print(false?(true|))
              
    et doit afficher undefined.
  8. Ecrire le code correspondant à l'appel de fonction et à RET que vous pourrez tester avec les samples foobar.small, fib.small et sums.small.

Exercice 3 - Stack Trace

On souhaite maintenant que si une erreur se produit, l'interpreteur affiche les stack frame conduisant à l'erreur bref le stack trace.

  1. Comment marche la classe CompactLineNumbers, a quoi sert elle ?
    Même question avec OpcodeWriter.lineNumber ?
  2. Ecrire la méthode StackInterpreter.createStackTrace pour afficher pour chaque activation la fonction correspondante.
  3. Modifier la méthode createStackTrace pour afficher en plus les numéros des lignes.