Vous êtes sur la page 1sur 3

Lycée Franklin Roosevelt MPI - Informatique

Année 2023  2024

TP 08 : Évaluation d'expression arithmétiques


(OCaml)

L'objectif de ce TP est d'écrire une fonction permettant de lire, parser, et évaluer une ex-
pression arithmétique. Dans un premier temps, nous considérerons des expressions utilisant des
parenthèses, de sorte à ce que nous n'ayons pas à nous soucier de questions de priorités d'opéra-
tions, puis dans un second temps nous nous intéresserons au cas où les expressions ne sont pas
nécessairement parenthésées et pour lequel l'expression doit être évaluée en suivant les ordres de
priorités d'opérations habituelles. Un chier ea.ml vous est fourni dans l'espace de classe.

1 Analyse Lexicale

1.1 Travail Préambulaire

Tout comme nous l'avons vu concernant la compilation d'un programme, la première étape
de notre problème consiste à eectuer une analyse lexicale. En partant d'une chaîne de caractères
sous forme de string, on veut séparer les termes de l'expression comme une suite de lexèmes.
Un lexème identie les symboles et ensemble de symboles devant fonctionnées ensemble. Ainsi, si
on part de l'expression 51+(723*42)/(5), on souhaite par exemple pouvoir considérer l'entier
723 comme un seul objet. Pour ce faire, on va utiliser le type lexeme suivant :
type lexeme = CONST of int | EOF | LPAR | RPAR | PLUS | TIMES | DIV | MINUS
Notons que le lexème EOF correspond au symbole End of File signiant la n de la chaîne
de caractères. Pour notre expression, on souhaite donc obtenir la liste : [MINUS; MINUS ; CONST
51; PLUS; LPAR; CONST 723; TIMES; CONST 42; DIV; LPAR; CONST 5; RPAR; EOF].
Notons qu'on ne s'intéresse pas ici encore au sens de l'expression, et des expressions fausses
doivent tout de même être décomposées en listes de lexèmes. Ainsi, "((25+*" doit être correcte-
ment décomposée en [LPAR; LPAR; CONST 25; PLUS; TIMES; EOF]. Toutefois, si un symbole
apparaît dans l'expression qui ne peut-être analysé, dans ce cas, il nous faut lever une exception.
Pour cela, on déclare l'exception suivante :
exception Erreur_lexicale

1. Revoir comment lever une exceptions.


2. Revoir le fonctionnement du mot clé and pour pouvoir dénir plusieurs fonctions récursives
simultanément.
3. Réviser le fonctionnement des strings en OCaml.
4. Écrire une fonction isoler_nombre : string -> int -> lexeme*int qui prend en entrée
une chaîne de caractère s et un entier i tels que la lettre à l'indice i existe dans s et soit
un chire. Elle renvoi un couple (CONST n, j) où n est le nombre commençant à la lettre
i dans s et j l'indice de position du dernier chire de ce nombre.

1.2 Reste de l'analyse

Notons que dans cette partie, les espaces entre diérents lexèmes devront être ignorés.

1
5. Écrire une fonction premier_lexeme : string > int > lexeme*int prenant en entrée
une chaîne de caractères s et un entier i et qui renvoie le lexème commençant à l'indice
i, ainsi que l'entier j , position du dernier caractère dudit lexème. Si aucun lexème n'est
reconnu, on lèvera une exception.
6. En déduire une fonction lexeur : string -> lexeme list qui prend en entrée une chaîne
de caractère et renvoie la liste de lexèmes correspondante. On lèvera une exception si
nécessaire.

2 Analyse syntaxique

On passe à présent à l'analyse syntaxique. Dans cette étape, on veut représenter notre liste
de lexème sous forme d'arbres, de telle sorte à révéler la structure sous-jacente à l'expression.
On utilisera pour cela le type ea pour expression arithmétiques de la façon suivante :
type op_binaire = Plus | Moins | Fois | Div
type ea = Const of int | Bin of op_binaire*ea*ea | Oppose of ea
Notons que si les noms sont proches, il ne s'agit pas des mêmes objects que les lexèmes ! Il
nous faut à présent considérer la grammaire G suivante qui nous permet de décrire les expression
arithmétiques de façon non-ambigüe.

S →E EOF
E →CONST n | MINUS E | LPAR B RPAR
B →E O E
O →PLUS | MINUS | TIMES | DIV

Si nous ne parvenons pas à analyser notre liste de lexèmes on pourra avoir recourt à l'exception
suivante :
exception Erreur_syntaxique

7. Écrire trois fonctions récursives de signatures :

parserE : lexeme list -> ea*lexeme list


parserB : lexeme list -> ea*lexeme list
parserO : lexeme list -> ea*lexeme list

telles que parserX l renvoit l'arbre syntaxique du plus grand préxe p de l qui est un mot
dérivé du non terminal X ainsi que la liste des lexèmes suivant. En cas d'impossibilité, on
lèvera l'exception appropriée.
Remarque : Il est important de dénir les fonctions simultanément car elles font appels
l'une à l'autre !
8. En déduire une fonction parseur : lexeme list -> ea qui renvoie l'arbre syntaxique
associé à la suite de lexème d'une expression arithmétique, ou qui lève une erreur en cas
de problème.

3 Évaluation

Il est maintenant temps d'évaluer notre expression.


9. Écrire une fonction evalue : string -> int qui évalue une expression arithmétique donné
sous forme de chaîne de caractères, ou qui lève les exceptions nécessaires en cas de problème.

2
4 Cas avec priorité d'opérations

On s'intéresse maintenant au cas où les expressions arithmétiques sont écrites sans nécessai-
rement avoir de parenthèses. Il nous faut donc considérer les priorités d'opérations. Pour ce faire,
on considère la grammaire suivante :

E →S EOF
S →T | T PLUS S | T MINUS S
T →F | F TIMES T | F DIV T
F →P | MINUS P
P →CONST n | LPAR S RPAR

10. Écrire trois fonctions récursives de signatures :

parserS : lexeme list -> ea*lexeme list


parserT : lexeme list -> ea*lexeme list
parserP : lexeme list -> ea*lexeme list

telles que parserX l renvoit l'arbre syntaxique du plus grand préxe p de l qui est un mot
dérivé du non terminal X ainsi que la liste des lexèmes suivant. En cas d'impossibilité, on
lèvera l'exception appropriée.
Remarque : Notons que nous n'avons pas de fonction parserF.
11. Écrire une fonction evalue_priorite : string -> int qui évalue une expression arith-
métique en utilisant les règles de priorité.

Vous aimerez peut-être aussi