IFT313 Introduction aux langages formels Froduald Kabanza Département dinformatique Université de Sherbrooke Analyseurs récursifs LL (1)

Slides:



Advertisements
Présentations similaires
Formation universitaire à .NET: Introduction à C#
Advertisements

Traitement sémantique et grammaire dattributs Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure dInformatique (ESI)
24 septembre 2007Cours de compilation 4 - Intranet1 Cours de compilation Techniques danalyse descendantes.
Chap 1 Grammaires et dérivations.
Introduction : Compilation et Traduction
IFT313 Introduction aux langages formels
IFT313 Introduction aux langages formels
IFT313 – Introduction aux langages formels Eric Beaudry Département dinformatique Université de Sherbrooke Laboratoire 4 – JFlex Été 2010.
Tests Programmation par contrats
IFT313 Introduction aux langages formels
IFT313 Introduction aux langages formels
Introduction : Compilation et Traduction
Récursivité.
IFT313 Introduction aux langages formels Froduald Kabanza Département dinformatique Université de Sherbrooke planiart.usherbrooke.ca/kabanza/cours/ift313.
Analyse  Syntactique (Compilers, Principles, Techniques and Tools, Aho, Sethi et Ullman, 1986)
IFT313 Introduction aux langages formels Froduald Kabanza Département dinformatique Université de Sherbrooke planiart.usherbrooke.ca/kabanza/cours/ift313.
Analyse lexicale Généralités Expressions rationnelles Automates finis
IFT313 Introduction aux langages formels Froduald Kabanza Département dinformatique Université de Sherbrooke planiart.usherbrooke.ca/kabanza/cours/ift313.
IFT451 Introduction aux langages formels Froduald Kabanza Département dinformatique Université de Sherbrooke planiart.usherbrooke.ca/kabanza/cours/ift313.
IFT313 Introduction aux langages formels
Bases de données lexicales
Introduction au paradigme objet Concepts importants surcharge (overload) redéfinition (override) Définition d’une classe Définition des attributs.
IFT313 Introduction aux langages formels
IFT313 Introduction aux langages formels
Faculté I&C, Claude Petitpierre, André Maurer 1 JavaCC Java compilers compiler (version générant du Javascript)
IFT313 Introduction aux langages formels
Test et débogage Tests unitaires. Gestion d’erreurs. Notion d’état, de pré-condition et de post-condition. Assertion. Traces de programme. Débogueur et.
IFT313 Introduction aux langages formels Froduald Kabanza Département dinformatique Université de Sherbrooke Révision de mi-session.
IFT313 Introduction aux langages formels Froduald Kabanza Département dinformatique Université de Sherbrooke JavaCC.
IFT313 Révision finale Département d’informatique
IFT313 Introduction aux langages formels
IFT313 Introduction aux langages formels Froduald Kabanza Département dinformatique Université de Sherbrooke Grammaires attribuées.
IFT313 Introduction aux langages formels
IFT313 Introduction aux langages formels Froduald Kabanza Département dinformatique Université de Sherbrooke planiart.usherbrooke.ca/kabanza/cours/ift313.
IFT313 Introduction aux langages formels
IFT313 Introduction aux langages formels Froduald Kabanza Département dinformatique Université de Sherbrooke Automates à pile LR Notion de poignée.
1 CSI3525: Concepts des Languages de Programmation Notes # 3: Description Syntaxique des Languages.
CSI3525: Concepts des Languages de Programmation
Traduction dirigée par la syntaxe
Contrôle de types Les types en programmation Expressions de types Un contrôleur de types Equivalence de types Conversions de types Généricité.
IFT 6800 Atelier en Technologies d’information
Applications des langages hors-contextes Les analyseurs syntactiques ascendants GNU Yacc et Bison.
Chapitre 3 Syntaxe et sémantique.
Analyse lexicale et syntaxique
Partie II Sémantique.
Analyse syntaxique Pr ZEGOUR DJAMEL EDDINE
Analyse lexicale Pr ZEGOUR DJAMEL EDDINE
Rappel Modèle analyse-synthèse de la compilation
Structures de données IFT-2000 Abder Alikacem La récursivité Semaine 5 Département dinformatique et de génie logiciel Édition Septembre 2009.
Structures de données IFT-2000 Abder Alikacem La récursivité Département d’informatique et de génie logiciel Édition Septembre 2009.
Programmation procédurale Transformations
IFT313 Introduction aux langage formels
Paradigmes des Langages de Programmation
LES PILES ET FILES.
Créer des packages.
Le langage Racket (Lisp)
Cours n° 3 Traitements en Java
IFT313 Introduction aux langage formels
CSI 3525, Implémentation des sous-programmes, page 1 Implémentation des sous-programmes L’environnement dans les langages structurés en bloc La structure.
ISBN Chapitre 10 L'implémentation des sous- programmes.
Tutorat en bio-informatique Le 12 octobre Exercices 3 et 4 (MAT1400) - solutions La série du binôme (chap. 8.8) –Trouver la série entière et le.
Héritage Conception par Objet et programmation Java
IFT313 – Introduction aux langages formels Eric Beaudry Département d’informatique Université de Sherbrooke Laboratoire sur JFLEX Été 2010.
3 Copyright © Oracle Corporation, Tous droits réservés. Créer des fonctions.
Chap 1 Grammaires et dérivations.
Chap 2. L'analyse descendante
Philippe Gandy - 15 septembre 2015 Basé sur les notes de cours de Daniel Morin et Roch Leclerc.
Java Réalisé par: Mouna POKORA. PLAN: Définition : Historique: Caractéristiques: un langage orienté objet: un langage interprété : un langage portable:
IFT313 Introduction aux langages formels
IFT313 Introduction aux langages formels
Transcription de la présentation:

IFT313 Introduction aux langages formels Froduald Kabanza Département dinformatique Université de Sherbrooke Analyseurs récursifs LL (1)

Sujets Cest quoi un analyseur syntaxique récursif ? Comment le programmer ? Comment fonctionne un générateur danalyseur syntaxique récursif ? IFT3132© Froduald Kabanza

Objectifs Pouvoir programmer un analyseur syntaxique récursif pour une grammaire donnée. Connaître les fondements dun générateur danalyseur syntaxique LL tel que JavaCC. IFT3133© Froduald Kabanza

IFT313© Froduald Kabanza4 Références [2] Appel, A. and Palsberg. J. Modern Compiler Implementation in Java. Second Edition. Cambridge, – Section 3.2 [4] Aho, A., Lam, M., Sethi R., Ullman J. Compilers: Principles, Techniques, and Tools, 2 nd Edition. Addison Wesley, – Section 4.4.1

Rappel : Analyseur LL(1) non récursif Un analyseur syntaxique LL non récursif exécute une boucle dans laquelle, à chaque étape, soit il prédit la production à appliquer ou il reconnaît (match) le prochain lexème (token). Pour cette raison, on lappelle souvent en anglais « predictive parser » ou « predict-match parser ». Un générateur danalyseur syntaxique non récursif : Prend une grammaire comme entrée. Produit, à partir de la grammaire, une table danalyse qui prédit la production à appliquer en fonction du non terminal au sommet de la pile et du prochain lexème (token). Le générateur a accès à du code pour un driver LL(1) (qui est essentiellement un automate à pile LL(1)) Lanalyseur pour la grammaire dentrée est obtenu en combinant le driver et la table danalyse. IFT3135© Froduald Kabanza

Rappel : Exemple G = (V, A, R, E) : V = {E, E, T, T, F} A = {(, ), +, *, n} R = { E TE E + TE | ε T FT *FT | ε F n } Table danalyse n+* E E TE $ E E +TE E ε T T FT T T ε T *FT T ε F F nF (E) () E TE IFT3136© Froduald Kabanza

return true Pile Étape Règle Algorithm LLDriver 0. stack = ($S); a = in.read(); x=stack.top(); while (true) { 1. if (x = = $) && (a= = $) return true ; 2. if (x = = a) && (a != $) { pop a from stack; a = in.read(); continue;} 3. if x is a nonterminal { if M[x,a] is error exit with error; let x y in M[x,a] pop x from stack; push y on stack; continue; } 4. exit with error;} Entrée Entrée : n+n*n $E $ET $ETF $ETn $ET $E $E T+ $E T $ETF $ETn $ET $ETF* $ETF $ETn $ET $E $ n+n*n$ +n*n$ n*n$ *n$ n$ $ E TE T FT F n T ε E +TE T FT F n T *FT F n T ε E ε n+* E E TE $ E E +TE E ε T T FT T T ε T *FT T ε F F nF (E) () E TE IFT3137© Froduald Kabanza

Analyse LL(1) descendante récursive On peut aussi définir un analyseur LL(1) directement à partir des règles de productions et de la table danalyse, sans utiliser le driver LL1. Lidée est de simuler directement la dérivation la plus à gauche : En associant des fonctions danalyse aux différents symboles de la grammaire (terminaux et non terminaux). En faisant les appels de fonctions selon la structure de la grammaire. Aux terminaux on associe une fonction match(Token) qui va matcher le prochain token. A chaque non terminal X, on associe une fonction X() dont le corps appelle des fonctions correspondant aux parties droites des règles dont X est la partie gauche. IFT3138© Froduald Kabanza

Exemple G = (V, A, R, S) : V = {S, T, L, E} A ={if, else, {, }, ;, =, ), (, id, print} R = { S if T S else S S { S L | print(E) T id = = id) L } | ;S L id } Exemple de mot généré : if (id = = id) { print(id); print(id) } else print(id) IFT3139© Froduald Kabanza

Analyseur LL(1) récursif G = (V, A, R, E) : V = { S, T, L, E } A = { if, else, {, }, ;, =, ), (, id, print } R = { S if T S else S S { S L | print(E) T id = = id) L } | ;S L id } Token a; // Variable globale : contiendra le prochain token void match (GrammarSymbol x) { if (x.equals(a.text()) a = getNextToken(); else error();} void S() { switch (a) case if : match(if); T(); S(); match(;); match(else); S(); match(;); break; case { : match({); S(); L(); break; case print : match(print); match((); E(); match()); break; default: error();} void T() { switch (a) case ( : match((); match(id); match(=); match(=); match(id); match()); break; default : error();} Note : En pratique ; sera représenté par un symbole (ex. SEMI). Idem pour {, }, (, ). Ce nest pas fait ici pour une question de clarté IFT31310© Froduald Kabanza

Analyseur LL(1) récursif (suite) G = (V, A, R, S) : V = { S, T, L, E } A = { if, else, {, }, ;, =, ), (, id, print } R = { S if T S else S S { S L | print(E) T id = = id) L } | ;S L id } void L() { switch (a) case } : match(}); break; case ; : match(;); S(); L(); break; default : error();} void E() { switch (a) case id : match(id); break; default : error();} void main () // Point dentrée du parseur { a = getNextToken(); S(); // fonction danalyse pour le symbole de départ System.out.print(Accepte : entrée correcte); } IFT31311© Froduald Kabanza

Exercices Pour vous convaincre que ça marche, simulez lanalyseur sur les entrées suivantes : Entrée incorrecte syntaxiquement : if else (id = = id) Entrée correcte syntaxiquement : if (id = = id) { print(id); print(id) } else print(id); Modifiez le parseur pour quil imprime la dérivation de lentrée. Implémentez-le en Java. IFT31312© Froduald Kabanza

Observations Il est facile décrire un analyseur syntaxique récursif manuellement. Pour que lapproche précédente fonctionne il faut que : 1.La partie droite de chaque production commence par un terminal Parce que le switch de chaque fonction X() se fait sur les terminaux qui commencent les partie droite des production dont X est la partie gauche. 2.Deux productions ayant la même partie gauche doivent avoir des parties droites commençant par des préfixes différents. Parce que les deux règles ont la même fonction danalyse (c-à-d., la fonction correspondant au non terminal dans la partie gauche de chaque production). Si elle partagent le même préfixe, le switch ne pourra pas tenir compte des deux à la fois. IFT31313© Froduald Kabanza

Observations Il est facile décrire un analyseur syntaxique récursif manuellement. Pour que lapproche précédente fonctionne il faut que : 1.La partie droite de chaque production commence par un terminal. 2.Deux productions ayant la même partie gauche doivent avoir des parties droites commençant par des préfixes différents. Ces conditions nous garantissent que la fonction danalyse pour chaque non terminal est déterministe. En dautre mots, on peut prédire la production appropriée, simplement en lisant le prochain token. IFT31314© Froduald Kabanza

Observations Il est très facile décrire un analyseur syntaxique récursif manuellement. Pour que lapproche précédente fonctionne il faut que : 1.La partie droite de chaque production commence par un terminal 2.Deux productions ayant la même partie gauche doivent avoir des parties droites commençant par des préfixes différents. Ces conditions nous garantissent que la fonction danalyse pour chaque non terminal est déterministe. Nous avons vu que seulement la première condition nest pas nécessairement requise pour un parseur LL(1) non récursif. Comment généraliser lapproche récursive pour que la condition 1 ne soit pas nécessaire ? IFT31315© Froduald Kabanza

Exemple G = (V, A, R, E) : V = {E, E, T, T, F} A = {(, ), +, *, n} R = { E TE E + TE | ε T FT *FT | ε F n } Avec lapproche précédente on sattendrait à quelque chose du genre : void E() { switch (a) case ?? : T(); Eprime(); break; default : error()} Mais quest-ce quon met aux endroits indiqués par « ?? » ? Vu que la production E TE ne commence pas par un terminal, notre approche ne fonctionne plus. Pour résoudre ce problème, il faut utiliser la table danalyse LL(1) de la grammaire, pour implémenter les cas de linstruction switch. De cette façon, on obtient un parser LL(1) récursif, équivalent au parser LL(1) non récursif. Cette grammaire illustre les limites de lapproche précédente. Par exemple, quelle est la fonction danalyse pour le non-terminal E ? IFT31316© Froduald Kabanza

Analyse syntaxique LL(1) récursif En général, pour avoir un analyseur syntaxique récursif, il faut utiliser une table danalyse LL(1) afin dimplémenter les cas du switch: Pour une fonction danalyse X() donnée, les cas de linstruction switch correspondent aux tokens a, tels que les entrées [X,a] sont non vides dans la table danalyse. La séquence dappels pour chaque chaque cas est une séquence de match et de fonction danalyse correspondants à la partie droite de la production dans lentrée [X,a] de la table danalyse. IFT31317© Froduald Kabanza

Exemple G = (V, A, R, E) : V = {E, E, T, T, F} A = {(, ), +, *, n} R = { E TE E + TE | ε T FT *FT | ε F n } Table danalyse n+* E E TE $ E E +TE E ε T T FT T T ε T *FT T ε F F nF (E) () E TE void E() { switch (a) case n : T(); Eprime(); break; case ( : T(); Eprime(); break; default : error()} void Eprime() { switch (a) case + : match(+); T();E(); break; case ) : break; case EOF : break; default : error()} IFT31318© Froduald Kabanza

Stratégies de recouvrement derreurs Une erreur apparaît lorsque la chaîne dentrée nest pas syntaxiquement correcte, c-à-d. elle nest pas dérivable de la grammaire. En pratique, on ne veut pas arrêter lanalyse à la toute première erreur. On veut continuer lanalyse syntaxique jusquà un certain nombre derreurs préfixé ou jusquà un certain niveau de sévérité de lerreur. Les stratégies de recouvrement typiques consistent à réparer la chaîne dentrée pour que lanalyse continue. En particulier : On peut insérer des tokens. Supprimer des tokens. Remplacer des tokens. IFT31319© Froduald Kabanza

Recouvrement derreurs par insertion de tokens Pour insérer un token manquant de linput, on na pas besoin de lajouter explicitement à la chaîne dentrée. Il suffit de prétendre que le token est présent, imprimer un message approprié et retourner normalement tel quillustré par les exemples suivants pour E() et Eprime(). void E() { switch (a) case n : T(); Eprime(); break; case ( : T(); Eprime(); break; default : print(Expected num or );} void Eprime() { switch (a) case + : match(+); T();E(); break; case ) : break; case EOF : break; default : print(Expected +, ), or EOF.); } IFT31320© Froduald Kabanza

Recouvrement derreurs par insertion de tokens Le recouvrement derreurs par insertion de tokens est à utiliser avec précaution parce que une cascade derreurs risque de mener à une à une boucle sans fin : tokens sont insérés (ou supposés présents) sans cesse, de sorte que la chaine dentrée nest jamais vidée. void E() { switch (a) case n : T(); Eprime(); break; case ( : T(); Eprime(); break; default : print(Expected num or );} void Eprime() { switch (a) case + : match(+); T();E(); break; case ) : break; case EOF : break; default : print(Expected +, ), or EOF.); } IFT31321© Froduald Kabanza

Recouvrement derreurs par suppression de tokens Le recouvrement derreurs par suppression de tokens est plus sécuritaire parce quil garantie toujours que la chaîne dentrée va être vidée. Pour une fonction danalyse X(), la stratégie est, en cas derreur, de sauter (supprimer) les prochains tokens jusquau premier token qui est dans Follow(X). void Eprime() { switch (a) case + : match(+); T();E(); break; case ) : break; case EOF : break; default : print(Expected +, ), or EOF.); skipTo(Follow[Eprime]);} Follow[Eprime] = { ), $ } skipTo(A) supprime les prochains tokens jusquau premier dans A. IFT31322© Froduald Kabanza

Générateurs danalyseurs LL(1) récursifs Un générateur danalyseur LL(1) récursif reçoit comme entrée une grammaire et donne comme sortie un analyseur LL(1) récursif correspondant. Pour ce faire : Il génère une table danalyse LL(1) Génère un patron (template) des fonctions danalyse à partir des règles de production, utilisant la table danalyse pour implémenter le switch. Ajoute le code pour la méthode (fonction) match. Il ny a plus de pile explicite. Elle est implicitement implémentée par la pile dappels des fonctions (la pile de récursivité). JavaCC et ANTLR sont des exemple de générateurs danalyseurs LL récursifs. IFT31323© Froduald Kabanza