"ExoLexParse" : Exercices sur le couplage Analyseur Lexical et Analyseur Syntaxique (Corrigé)



0. Sommaire
1. Lexer manuel
2. Premiers pas Bison
3. Combo Flex+Bison
4. Flexification

0. Sommaire et Objectifs

  1. Analyse lexicale manuelle

    Structure basique de yylex(), préparation au couplage avec bison.

  2. Premiers pas Bison sans Flex (== avec Analyse lexicale manuelle)

    Format spécification Bison, couplage yyparse() et yylex(), tokens.

  3. Utilisation combinée de Flex et Bison

    Makefile et prototypes de spécification du cours, gestion d'erreurs.

  4. Flexification de "Bison sans Flex"

    Exemple corrigé d'une version combinée Flex et Bison des 2 premiers exercices


1. Analyse lexicale manuelle

L'objectif de cet exercice est de démystifier ce qu'est un analyseur lexical et de pouvoir en cas de besoin écrire le sien sans utiliser lex/flex.

1.1) Prise en main

Les éléments suivants sont fournis:

Lire et comprendre le code de l'analyseur.

Compiler, exécuter et expliquer ...

1.2) Améliorations simples

Implanter un analyseur lexical yylex1.c contenant les affichages suivants:

L'exercice devient vite fastidieux ! C'est pourquoi on peut aimer l'outil flex même avec une utilisation simpliste des expressions régulières.

Corrigé : yylex1.c


2. Premiers pas Bison Sans Flex (== avec Analyseur lexicale manuelle)

En reprenant l'analyseur lexical simple de l'exercice précédent, on se concentre ici sur l'utilisation de bison pour développer une analyse syntaxique au dessus de l'analyse lexicale.

2.1) Prise en mains

On reprend les éléments fournis dans l'exercice précédent

On donne de plus une spécification simple Bison d'analyseur syntaxique gram0.bison

a) Lire et comprendre :

b) Compiler, adapter et exécuter :

Corrigé : yylex0-bis.c.

L'analyseur lexical seul s'obtient avec gcc -DFLEXALONE yylex0-bis.c.

2.2) Améliorations simples

Ajouter les catégories lexicales ART, TRANS_VRB, COMM_NOUN, PROP_NOUN comme dans l'exercice précédent.

On peut reprendre le corrigé de l'exercice précédent yylex1.c ou sa forme adaptée yylex1-bis.c.

Enrichissez par étape la grammaire "gram0.bison" pour :

2.3) Flexification

Refaire l'exercice en remplaçant l'analyseur lexical manuel par un analyseur lexical Flex.
Ceci constitue l'exercice 4. Flexification de "Bison sans Flex" à traiter après l'exercice 3.


3. Utilisation combinée de Flex et Bison

L'écriture manuelle d'un analyseur lexical est peu optimisée, et rapidement fastidieuse. Il est plus commode de le préparer avec Flex; puis de le combiner avec un analyseur syntaxique préparé par Bison.

3.1) Makefile et prototypes de spécification

On recommande le schéma générique suivant pour la construction d'analyseurs syntaxiques/lexicaux Flex+Bison :

Fichiers Prototypes :

On suppose alors que :

Analyser les fichiers fournis.

3.2) Prise en main sur un exemple simple

On se donne les spécifications et fichiers de test suivants :

Analyser les fichiers fournis.
Créer l'analyseur lexical seul : > make lex-flexbison0
Créer l'analyseur syntaxique > make flexbison0 , ou > make parse-flexbison0
Exécuter et comprendre les résultats.

3.3) Grammaire plus étendue

Améliorer la grammaire précédente en définissant un Text comme une succession de Sentence.
Chaque Sentence a une forme simple :

Corrigé : flexbison1.bison et flexbison1.flex.

3.4) Traitement d'erreurs au niveau syntaxique : Explications

Il ne s'agit pas ici des techniques de traces (echo(),print(),...) utilisées lors de la mise au point d'une grammaire, mais de la gestion des erreurs lexicales ou syntaxiques dans les données analysées par un analyseur déjà au point. Comment localiser l'erreur ? Comment la remonter vers l'utilisateur ? Peut-on continuer l'analyse après une première erreur ? ...

Le problème devient vite assez complexe à gérer (et la documentation pas très accessible). On se contentera ici de donner un schéma relativement simple de traitement des erreurs syntaxiques qui permet de couvrir une base déjà assez large de problèmes.

a) Erreurs lexicales

La détection d'erreurs lexicales peut être facilement gérée par l'utilisation d'une règle-balai dans Flex. On a alors deux stratégies :

En pratique, le meilleur choix est généralement de ne pas cacher l'erreur lexicale et de reporter au niveau syntaxique. Dans un cas, comme dans l'autre on tente de poursuivre l'analyse lexicale pour détecter plusieurs erreurs dans une même passe.

b) Traitement d'erreurs syntaxiques avec Bison

Quand l'analyseur syntaxique Bison détecte une portion de texte erronée, c'est-à-dire qu'aucune règle de grammaire ne permet de traiter le token lu pour espérer reconstruire un arbre syntaxique complet, on a alors :

c) Exemples de resynchronisation après erreur

Texte :   /* mot vide */
        | Texte  Ligne  '\n'  { printf("Ligne Correcte"); }
        | Texte  error  '\n'  { yyerrok; printf("Ligne Incorrecte ignorée");}
;

Ici les fins de lignes servent de resynchronisation après erreur.

Quelques variations :

Instr : ...
   | error ';' /* on error, skip until ';' is read */
;

Parent_expr : '(' expr ')'
            | '(' error ')'  /* resync with the next ')',   */
                             /* may be not the matching one */
;

3.5) Traitement d'erreurs au niveau syntaxique : Pratique

Améliorer la grammaire traitant un Text en ajoutant un traitement d'erreur au niveau syntaxique. Faire en sorte que l'analyseur ne s'arrête pas à la première erreur mais analyse l'ensemble du texte en signalant des erreurs successives.
Mettre en particulier en oeuvre la remontée d'erreur avec la règle-balai au niveau lexical.
Expérimenter éventuellement l'option Flex %option yylineno pour remonter et imprimer un numéro de ligne en cas d'erreur.

Étendre pas à pas la grammaire pour faire disparaître des cas d'erreur. Par exemple gérer les ponctuations (, ' ...),

Corrigé :
flexbison2.flex

flexbison2.bison

4. Flexification de "Bison sans Flex"

A titre d'exemple, on donne ici le corrigé d'une version Flex+bison de l'exercice 2. Premiers pas : Bison sans Flex.
Ceci illustre l'utilisation du Makefile générique et des fichiers prototypes proposés comme modèles pour le cours.

4.1) Spécification Flex

Écrire une spécification Flex équivalente à l'analyseur lexical manuel yylex0.c de l'exercice 1.
Utiliser le nom gram0.flex.

Corrigé : gram0.flex.

4.2) Analyseur lexical seul

Construire et tester l'analyseur lexical seul avec "gram0.flex".

> make lex-gram0
> lex-gram0 < test.data

4.3) Analyseur syntaxique

Construire et tester l'analyseur syntaxique avec "gram0.flex" et la spécification "gram0.bison" de l'exercice 2.

> make gram0
> gram0 < test.data

4.4) Améliorations

Étendre la spécification Flex en ajoutant les autres catégories lexicales comme dans l'exercice 1 avec yylex1.c.

Corrigé : gram1.flex.

Construire et tester les versions successives des grammaires de l'exercice 2.

Corrigés des grammaires de l'exercice 2 : gram1.bison, gram2.bison, gram3.bison, gram4.bison, gram5.bison

--- pour X = 1..5
> ln -s gram1.flex gramX.flex
> make gramX
> gramX < test.data

CSC 4508, TELECOM SudParis, F. Meunier, P. Hennequin
Last modified: Mars 2015