Un interpréteur Pseudo-Pascal
La solution complète est
ici.
Contexte informatique des TP
Ce TP reprend la présentation des TP inaugurée la semaine dernière.
L'exercice consiste à écrire le fichier manquant d'un programme
complet.
Ici, le programme complet est un interpréteur pseudo-pascal qui prend
un fichier source en argument.
Vous n'avez à écrire ni analyseur syntaxique ni analyseur lexical,
ni glue code qui met tout ça ensemble.
C'est déjà fait et present dans une archive tar.
Ici l'archive s'appele zyvai.tar, c'est donc un interpréteur
où il ne manque que l'interprète...Il faut d'abord récupérer l'archive, puis la défaire :
# tar xf zyvai.tar
Cette opération crée un sous-répertoire zyvai qui contient
tout ce qui est nécessaire pour le TP, en particulier un
Makefile et un .depend.
# cd zyvai
# make zyvai
ocamlc -c misc.mli
ocamlc -c misc.ml
ocamlc -c location.mli
ocamlc -c location.ml
ocamlc -c pp.mli
ocamlc -c print.ml
ocamlc -c lexer.mli
ocamlc -c lexer.ml
ocamlc -c ll.mli
ocamlc -c ll.ml
ocamlc -c interpret.mli
make: *** No rule to make target `interpret.ml', needed by `interpret.cmo'. Stop.
Et ça rate parce qu'il manque justement le fichier
interpret.ml.
Mais un fichier squelette.ml est fourni.
Ce fichier est in interpréteur extrêmement incomplet qui ne sait
qu'interpréter l'instruction writeln.
open Pp
exception PasEncore
type valeur = Vint of int
type erreur = Type of type_expr
exception Error of erreur
(* Évaluation des expressions, type Pp.expression -> valeur *)
let rec expr e = raise PasEncore
(* Évaluation des expressions de type entier, type Pp.expression -> int *)
and expr_int e = match expr e with
| Vint i -> i
| _ -> raise (Error (Type Integer))
let instr = function
| Writeln_int e ->
let i = expr_int e in
print_int i ;
print_newline ()
| _ -> raise PasEncore
let instrs is = List.iter instr is
let eval {global_vars = globs ; definitions = defs ; main = is} =
instrs is |
(Noter que le type Pp.program est un type enregistrement.)
Il faut le récupérer, puis le renommer :
# mv squelette.ml zyvai/interpret.ml
Ce qu'il faut faire
L'implémentation interpret.ml doit être conforme à
l'interface
interpret.mli. C'est à dire qu'elle exporte une fonction
eval qui prend un programme (type Pp.program) et
renvoie ``()'' :
val eval : Pp.program -> unit |
Pour écrire interpret.ml,
on s'appuiera sur la syntaxe
abstraite (déjà présente dans pp.mli) et la
description sémantique
informelle données dans
le poly.
La démarche suivante est suggérée.
-
Commencer par définir un type des valeurs (booléens, entiers,
tableaux). Définir également le type des erreurs
(qui l'on complètera au fur et à mesure).
- Définir l'évaluation des expressions arithmétiques sans
variables, à partir
du type des expressions
de pp.mli.
On peut ensuite essayer l'interprète minimal en lui donnant un
programme minimal, a.p par exemple :
program
begin
writeln(1+2+3+4)
end.
On essaiera :
# ./zyvai a.p
10
- Définir la structure des environnements, compte tenu du langage
traité, il faudra concevoir un environnement en trois parties :
definitions de fonctions et procédures, variables globales, et
variables locales --- un enregistrement s'impose.
Chacune des parties peut être mise en oeuvre à l'aide de listes
(cf. le module List).
Concevoir également les fonctions de base qui
travaillent sur ces environnements,
(chercher une variable par exemple).
Il faut aussi se souvenir que l'on peut
affecter les variables en
Pseudo-Pascal.
- Étendre la fonction eval minimale pour tenir compte des
variables globales, modifier l'évaluateur des expressions entières
pour traiter le cas des variables, ainsi que l'évaluateur des
instructions pout tenir compte de l'affectation de variable et de
la séquence.
Tester à l'aide d'un exemple simple, par exemple b.p.
On obtiendra encore :
program
var x,s : integer ;
begin
x := 0 ; s:= 0 ;
x := x+1 ; s := s+x ;
x := x+1 ; s := s+x ;
x := x+1 ; s := s+x ;
x := x+1 ; s := s+x ;
writeln(s)
end.
# ./zyvai b.p
10
- Attaquez vous ensuite aux appels de fonction et de procédure,
c'est à dire principalement traiter la gestion de la partie locale de
l'environnement.
Compléter enfin l'interprète en traitant toutes les constructions et
notamment les tableaux.
Vous pouvez à tout moment lancer une procédure de test complète, par
``make oki''.
L'interprète est alors lancé sur une série de programmes
(fact.p, ...) en prenant une série d'entrées
(fact.in, ...) et sa sortie est confrontée à une sortie de
référence (fact.ref, ...).
L'ensemble des fichiers utiles aux tests se trouve dans le
sous-répertoire test.
- Terminer par les aspects esthétiques : beau code, belle gestion
des erreurs.
La gestion des erreurs, fera la part des erreurs dans le programme interprété
et des eventuels bugs de l'interpréteur
(par exemple, en cas d'erreur d'indice dans un tableau, mieux vaut un
message un peu explicatif qu'un accès illégal en Caml).
Ce document a été traduit de LATEX par
HEVEA.