Académique Documents
Professionnel Documents
Culture Documents
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
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
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
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
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
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é.