Analyse syntaxique Pr ZEGOUR DJAMEL EDDINE

Slides:



Advertisements
Présentations similaires
Premier programme en C :
Advertisements

Formation universitaire à .NET: Introduction à C#
Traitement sémantique et grammaire dattributs Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure dInformatique (ESI)
Vue générale Pr ZEGOUR DJAMEL EDDINE
Sémantique des déclarations pour le langage Z minimal
La classe String Attention ce n’est pas un type de base. Il s'agit d'une classe défini dans l’API Java (Dans le package java.lang) String s="aaa"; // s.
Cours n°2M2. IST-IE (S. Sidhom) UE 303 Promo. M2 IST-IE 2005/06 Conception dun système d'information multimédia Architecture trois-tiers : PHP/MySQL &
C.
Chap 1 Grammaires et dérivations.
Introduction : Compilation et Traduction
Algorithme et programmation
IFT313 Introduction aux langages formels
Tests Programmation par contrats
IFT313 Introduction aux langages formels
Points importants de la semaine Les commentaires. Les variables. Les instructions conditionnelles. Les instructions itératives (les boucles).
Introduction à la programmation (420-PK2-SL) cours 12 Gestion des applications Technologie de linformation (LEA.BW)
Introduction : Compilation et Traduction
Récursivité.
44 Contrôle du déroulement du programme. 4-2 Objectifs A la fin de ce cours, vous serez capables de : Utiliser les constructions de prise de décision.
Les méthodes en java Une méthode est un regroupement d’instructions ayant pour but de faire un traitement bien précis. Une méthode pour être utilisée.
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.
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)
Introduction à la programmation (Java)
Master 1 SIGLIS Java Lecteur Stéphane Tallard Chapitre 5 – Héritage, Interfaces et Listes génériques.
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 Froduald Kabanza Département dinformatique Université de Sherbrooke Analyseurs récursifs LL (1)
IFT313 Introduction aux langages formels Froduald Kabanza Département dinformatique Université de Sherbrooke Automates à pile LR Notion de poignée.
Expressions régulières et hash tables
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
LIFI-Java 2004 Séance du Jeudi 9 sept. Cours 1. La notion de langage Décrire une tâche à effectuer –programme Écrire à un haut niveau –facile pour lutilisateur.
Chapitre 9 Les sous-programmes.
Analyse lexicale et syntaxique
Partie II Sémantique.
Séance d’introduction
Analyse lexicale Pr ZEGOUR DJAMEL EDDINE
Table des symboles Pr ZEGOUR DJAMEL EDDINE
Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure dInformatique (ESI) Plate-forme.NET.
CSI1502 Introduction au génie logiciel
Faculté I&C, Claude Petitpierre, André Maurer JavaCC Java compilers compiler (version générant du Javascript)
Rappel Modèle analyse-synthèse de la compilation
Structures de données IFT-10541
Structures de données IFT-2000 Abder Alikacem La récursivité Semaine 5 Département dinformatique et de génie logiciel Édition Septembre 2009.
1111 Gestion des exceptions Objectifs À la fin de ce cours, vous serez capables de : • Expliquer les concepts de base de la gestion des exceptions.
Animateur : Med HAIJOUBI
GESTION ET TRAITEMENT DES ERREURS
Procédures et fonctions Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI)
Créer des packages.
Sémantique des expressions arithmétiques pour le langage Z minimal Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI)
Master 1 SIGLIS Java Lecteur Stéphane Tallard Les erreurs communes en Java.
Tables de compilation pour le langage Z minimal Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI)
Le langage Z minimal Pr ZEGOUR DJAMEL EDDINE
Structures de données avancées : Arbres ‘Left Leaning Red-Black’
L’analyse lexicale Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI)
Sémantique des instructions pour le langage Z minimal Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI)
Expressions sur les chaînes de caractères Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI)
1 Programmation en C++ Marianne Morris. 2 Intro générale à la programmation On a déjà étudié le langage assembleur Langage de bas niveau Meilleur que.
1 PHP 5 Notions fondamentales (niveau 1 – cours #2) Formation continue – Cégep de Sainte-Foy.
Cours 4 (14 octobre) Héritage. Chapitre III Héritage.
Introduction au langage C : Structures de contrôle 1 ère année Génie Informatique Dr Daouda Traoré Université de Ségou
Abdelkader Heni FUNDP Syntaxe et sémantique Abdelkader Heni FUNDP
Exception Handling "Unfortunately, it's almost accepted practice to ignore error conditions, as if we're in a state of denial about errors." Bruce Eckel.
Organisation de la mémoire pour le langage minimal Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI)
Les analyseurs Bottom-up Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI)
Transcription de la présentation:

Analyse syntaxique Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI) www.zegour.uuuq.com email: d_zegour@esi.dz

Analyse syntaxique Grammaires contexte-libre et Automates à pile(PDA) Analyse descendante récursive Propriétés LL(1) Traitement des erreurs

Grammaires à contexte libre Problème Les grammaires régulières ne traitent pas la récursion centrale E = x | "(" E ")". On utilise alors les grammaires à contexte libre Définition Une grammaire est dite à contexte libre (CFG) si toutes ses productions ont la forme: A = a. A est un NTS, a séquence non vide de TS et NTS En EBNF le coté droit a peut aussi contenir les meta-symboles |, (), [] and {} Exemple Expr = Term { ( "+" | "-" ) Term }. Term = Factor { ( "*" | "/" ) Factor }. Factor = id | "(" Expr ")". Récursion centrale indirecte Les grammaires à contexte libre peuvent être reconnues par les automates à pile (PDA)

Automates à pile :Push-Down Automaton(PDA) Caractéristiques Permet les transitions avec des symboles terminaux et des symboles non terminaux Utilise une pile pour sauvegarder les états visités Exemple E = x | "(" E ")". x E reconnu revenir 1 arc continuer à partir de là avec E E/1 ( E ) E/3 stop État lecture État réduire E/1 ( E ) E/3 Appel récursif de l‘automate E x E/1 ( E ) x ...

Automates à pile (suite) x E/1 E ( E ) E/3 x stop E/1 ( E ) E/3 ... Peut être simplifié comme suit x E/1 Utilise une pile pour trouver le chemin de retour des états visités E x ( E ) E/3 ( stop

Comment fonctionne un PDA? Exemple: ((x)) x E/1 1 E x ( E ) 2 3 E/3 4 ( stop Les états visités sont rangés dans une pile Pile Reste à analyser 2 ( 2 ( x E/1 1 0 . ( ( x ) ) 0 2 . ( x ) ) 0 2 2 . x ) ) 0 2 2 1 . ) ) 0 2 2 . E ) ) 0 2 2 3 . ) ) 0 2 2 3 4 . ) 0 2 . E ) 0 2 3 . ) 0 2 3 4 . 0 . E stop E 3 E 3 E ) E/3 4 ) E/3 4

Grammaires régulières et Grammaires à contexte libre Utilisées pour Lexique Syntaxe Reconnues par DFA (sans pile) PDA (avec pile) DFA (état) Entrée pile Productions A = a | b C. A = a.

Analyse syntaxique Grammaires contexte-libre et Automates à pile(PDA Analyse descendante récursive Propriétés LL(1) Traitement des erreurs

Analyse descendante récursive Technique d‘analyse Top-down (de haut en bas) L‘arbre syntaxique est construit du symbole initial(axiome) vers la phrase (top-down) Exemple grammaire Entrée A = a A c | b b. a b b c Symbole de départ Entrée a b c A ? Quelle est L‘alternative qui convient? a b A c ? a b A c La bonne alternative est sélectionnée utilisant ... L‘unité lexicale courante de l‘entrée Les premiers symboles terminaux des alternatives d‘une production

Variables statiques de l‘analyseur syntaxique Unité lexicale courante A tout moment l‘analyseur connaît la prochaine unité lexicale static int la; // code de l‘unité lexicale courante Il utilise deux variables pour les unités lexicales (pour la phase sémantique) static Token token; // unité déjà reconnue static Token laToken; // unité courante non encore reconnue Ces variables sont mises à jour dans la méthode Scan() static void Scan () { token = laToken; laToken = Scanner.Next(); la = laToken.kind; } Entrée Déjà reconnues Scan() est appelée au début de l‘analyse. La première unité est dans la ident assign plus token laToken la

Comment analyser les symboles terminaux? Modèle Symbole à analyser: a Action de l‘analyseur: Check(a); On a besoin des méthodes suivantes static void Check (int expected) { if (la == expected) Scan(); // recognized => read ahead else Error( ); } public static void Error (string msg) { Console.WriteLine("– line {0}, col {1}: {2}", laToken.line, laToken.col, msg); throw new Exception("Panic Mode"); public static string[] names = {"?", "identifier", "number", ..., "+", "-", ...}; Ordonné par code Token.names[expected] + " expected" Dans la class Token: Les noms des symboles terminaux sont déclarés comme des constantes dans la classe Token public const int NONE = 0, IDENT = 1, NUMBER = 2, ..., PLUS = 4, MINUS = 5, ... ;

Comment analyser les symboles non terminaux? Modèle Symbole à analyser : A Action de l‘analyseur : A(); // Appel à la méthode d‘analyse de A Chaque symbole non terminal est reconnu par une méthode avec le même nom private static void A() { ... parsing actions for the right-hand side of A ... } Initialisation de l‘analyseur public static void Parse () { Scan(); // initialise token, laToken et la Program(); // appelle la méthode d’analyse de l’axiome Check(Token.EOF); // à la fin, l’entrée doit être vide }

Comment analyser les séquences? Modèle production: A = a B c. Méthode de l‘analyseur: static void A () { // la contains a terminal start symbol of A Check(a); B(); Check(c); // la contains a follower of A } Simulation A = a B c. B = b b. static void A () { Check(a); B(); Check(c); } static void B() { Check(b); a b b c Entrée restante b b c b b c c b c c

Comment analyser les alternatives Modèle a | b | g a, b, g sont des expressions EBNF Action de l‘analyseur if (la in First(a)) { ... parse a ... } else if (la in First(b)) { ... parse b ... } else if (la in First(g)) { ... parse g ... } else Error("..."); // find a meaninful error message Exemple A = a B | B b. B = c | d. First(aB) = {a} First(Bb) = First(B) = {c, d} static void A () { if (la == a) { Check(a); B(); } else if (la == c || la == d) { Check(b); } else Error ("invalid start of A"); } static void B () { if (la == c) Check(c); else if (la == d) Check(d); else Error ("invalid start of B"); exemples: analyser a d et c b analyser b b

Comment analyser les Options EBNF Modèle [ a ] a est une expression EBNF Action de l‘analyseur if (la in First(a)) { ... parse a ... } // no error branch! Exemple A = [ a b ] c. static void A () { if (la == a) { Check(a); Check(b); } Check(c); Exemple: analyser a b c analyser c

Comment analyser les itérations EBNF Modèle { a } a est une expression EBNF Action de l‘analyseur while (la in First(a)) { ... parse a ... } Exemple A = a { B } b. B = c | d. static void A () { Check(a); while (la == c || la == d) B(); Check(b); } Exemple: analyser a c d c b analyser a b static void A () { Check(a); while (la != b && la != Token.EOF) B(); check(b); } Ou bien ... Sans EOF: danger d‘une boucle infinie, si b n‘existe pas dans l‘entrée

Cas des ensembles ‘First’ grands Si l‘ensemble First a plus de 4 : utiliser la classe BitArray Exemple: First(A) = {a, b, c, d, e} First(B) = {f, g, h, i, j} Les ensembles First sont initialisés au début du programme using System.Collections; static BitArray firstA = new BitArray(Token.names.Length); firstA[a] = true; firstA[b] = true; firstA[c] = true; firstA[d] = true; firstA[e] = true; static BitArray firstB = new BitArray(Token.names.Length); firstB[f] = true; firstB[g] = true; firstB[h] = true; firstB[i] = true; firstB[j] = true; Exemple static void C () { if (firstA[la]) A(); else if (firstB[la]) B(); else Error("invalid C"); } C = A | B.

Cas des ensembles ‘First’ grands Si l‘ensemble a moins de 4 éléments: utiliser des vérifications explicites (plus rapide) Exemple : First(A) = {a, b, c} if (la == a || la == b || la == c) ... Si l‘ensemble est un intervalle: utiliser un test d‘intervalle if (a <= la && la <= c) ... Les codes des unités lexicales sont souvent choisis de telle sorte qu‘ils forment des intervalles Exemple First(A) = { a, c, d } First(B) = { a, d } First(C) = { b, e } const int a = 0, d = 1, c = 2, b = 3, e = 4, First(A) First(B) First(C)

Optimisations Éviter les multiples vérifications A = a | b. A = { a | B d }. B = b | c. static void A () { while (la == a || la == b || la == c) { if (la == a) Scan(); else { // no check any more B(); Check(d); } // no error case } static void A () { if (la == a) Scan(); // no Check(a); else if (la == b) Scan(); else Error("invalid A"); } Schéma plus efficace pour analyser les alternatives dans une itération static void A () { for (;;) { if (la == a) Scan(); else if (la == b || la == c) { B(); Check(d); } else break; } A = { a | B d }.

Optimisations Modèle d’une itération fréquente Exemple a { separator a } ident { "," ident } for (;;) { ... parse a ... if (la == separator) Scan(); else break; } Check(ident); if (la == Token.COMMA) Scan(); else break; Exemple d‘entrée: a , b , c :

Déterminer correctement les ensembles ‘First’ Grammaire Méthodes d‘analyse static void A () { B(); Check(a); } static void B () { if (la == b || la == c) { while (la == b) Scan(); Check(c); } else if (la == d || la == a) { if (la == d) Scan(); } else if (la == e) { Scan(); } else Error("invalid B"); b et c d et a (!) e First C = D e | f. D = { d }. d et e (D est ‘annulable’!) f static void C () { if (la == d || la == e) { D(); Check(e); } else if (la == f) { Scan(); } else Error("invalid C"); } static void D () { while (la == d) Scan(); A = B a. B = { b } c | [ d ] | e.

Descente récursive et arbre syntaxique L‘arbre syntaxique est construit implicitement Représente les méthodes actives à un moment donné Représente les productions utilisées Exemple A = a B c. B = d e. call A() static void A () { Check(a); B(); Check(c); } a B c A A en exécution " pile" reconnaît a call B() static void B () { Check(d); Check(e); } a B c A d e A en exécution B en exécution reconnaît d et e Retour de B() a B c A A en exécution

Analyse syntaxique Grammaires contexte-libre et Automates à pile(PDA Analyse descendante récursive Propriétés LL(1) Traitement des erreurs

Propriétés LL(1) Définition En d‘autres termes Pré condition pour l‘analyse descendante récursive LL(1) ... peut être analysé de gauche ( Left) à droite avec des dérivations canoniques gauche (Left) ( le NTS le plus à gauche est dérivé en premier ) et utilise une seule unité lexicale (1) de l‘entrée Définition 1. Une grammaire est LL(1) si toutes ses productions sont LL(1). 2. Une production A est LL(1) si pour toutes ses alternatives a1 | a2 | ... | an la condition suivante est vérifiée: First(ai) Intersection First(aj) = {} ( pour tout i et j) [ Au plus un ak peut être vide. Et dans ce cas First(ai) Intersection Follow(A) = {} ( pour tout i #k)] En d‘autres termes Les symboles terminaux de début de toutes les alternatives d‘une production doivent être disjoints deux à deux. L‘analyseur doit être capable de choisir l‘une des alternatives par la consultation de la l‘unité lexicale courante.

Comment éliminer les conflits LL(1) ? Factorisation IfStatement = "if" "(" Expr ")" Statement | "if" "(" Expr ")" Statement "else" Statement. Extraire la séquence commune de début IfStatement = "if" "(" Expr ")" Statement ( | "else" Statement ). ... Ou en EBNF IfStatement = "if" "(" Expr ")" Statement [ "else" Statement ]. Quelquefois les non terminaux doivent être remplacés avant factorisation Statement = Designator "=" Expr ";" | ident "(" [ ActualParameters ] ")" ";". Designator = ident { "." ident }. Remplacer Designator dans Statement Statement = ident { "." ident } "=" Expr ";" | ident "(" [ ActualParameters ] ")" ";". ensuite factoriser Statement = ident ( { "." ident } "=" Expr ";" | "(" [ ActualParameters ] ")" ";" ).

Comment éliminer la Récursion gauche La récursion à gauche est toujours un conflit LL(1) Par exemple IdentList = ident | IdentList "," ident. génère les phrases suivantes ident ident "," ident ident "," ident "," ident ... Peut toujours être remplacée par une itération IdentList = ident { "," ident }.

Conflits LL(1) cachés Les itérations et les options EBNF sont des alternatives cachées A = [ a ] b. Idem A = a b | b. a et b sont des expressions EBNF A = [ a ] b. First(a) Inter First(b) doit être {} A = { a } b. First(a) Inter First(b) doit être {} Règles A = a [ b ]. First(b) Inter Follow(A) doit être {} A = a { b }. First(b) Inter Follow(A) doit être {} A = a | . First(a) Inter Follow(A) doit être {}

Éliminer les conflits LL(1) cachés Name = [ ident "." ] ident. Où est le conflit et comment l‘éliminer? Name = ident [ "." ident ]. Cette nouvelle production est elle LL(1) ? Nous devons vérifier si First("." ident) Intersection Follow(Name) = {} Prog = Declarations ";" Statements. Declarations = D { ";" D }. Où est le conflit et comment l‘éliminer? Remplacer Declarations dans Prog Prog = D { ";" D } ";" Statements. First(";" D) Inter First(";" Statements) # {} Prog = D ";" { D ";" } Statements. Nous devons encore vérifier si First(D ";") Inter First(Statements) = {}

Problème des ‘Else’ L‘ instruction If en Java C‘est un conflit LL(1) ! Statement = "if" "(" Expr ")" Statement [ "else" Statement ] | ... . C‘est un conflit LL(1) ! First("else" Statement) Inter Follow(Statement) = {"else"} C‘est même une ambiguïté qui ne peut être éliminée if (expr1) if (expr2) stat1; else stat2; Statement On peut construire 2 arbres syntaxiques différents!

Problème des ‘Else’ Solution Si la prochaine unité est "else" Statement = "if" "(" Expr ")" Statement [ "else" Statement ] | ... . Si la prochaine unité est "else" L‘analyseur prend comme option: le "else" est associé au dernier "if" Solution if (expr1) if (expr2) stat1; else stat2; Statement

Autres exigences pour une grammaire (Pré conditions pour les analyseurs syntaxiques) Complétude Pour chaque NTS il doit y avoir une production A = a B C . B = b b . erreur! pas de production pour C Dérivabilité Chaque NTS doit être dérivable (directement ou indirectement) en une chaîne de TS A = a B | c . B = b B . erreur! B ne peut être dérivé en une chaîne de TS Non-circularité Un NTS ne doit pas être dérivable (directement ou pas) en lui-même (A => B1 => B2 => ... => A) A = a b | B . B = b b | A . erreur! cette grammaire est circulaire car A => B => A

Analyse syntaxique Grammaires contexte-libre et Automates à pile(PDA Analyse descendante récursive Propriétés LL(1) Traitement des erreurs

Objectifs du traitement des erreurs syntaxiques Exigences 1. Déterminer le maximum d‘erreurs en une seule compilation 2. Pas de bug (quelque soit l‘erreur) 3. Ne pas ralentir l‘exécution en traitant les erreurs 4. Ne pas gonfler le code Techniques pour l‘analyse descendante récursive Mode panique Utilisation des symboles de reprise (ancres) Utilisation des symboles spéciaux de reprise

Mode panique L'analyseur abandonne après la première erreur Avantages static void Error (string msg) { Console.WriteLine("-- line {0}, col {1}: {2}", laToken.line, laToken.col, msg); throw new Exception("Panic Mode - exiting after first error"); } Avantages économique suffisant pour les langages de commandes ou pour les interpréteurs Inconvénients Non appropriée pour la production des compilateurs de qualité

Traitement des erreurs utilisant les ancres Exemple Entrée attendue: a b c d ... Entrée réelle: a x y d ... Récupération(synchronise l‘entrée restante avec la grammaire) 1. Trouver l’ "unité de reprise " (ancre) avec laquelle l‘analyseur peut continuer après l‘erreur. Quelles sont les unités avec lesquelles l‘analyseur peut continuer dans la situation donnée? c successeur de b (qui était attendu à la position de l‘erreur) d successeur de b c ... Les unités de reprise (ancre) à cette position sont {c, d, ...} 2. Sauter les unités jusqu‘à ce que une unité de reprise soit trouvée . x et y sont sautées dans l‘exemple, mais d est un ancre; l‘analyseur peut continuer avec. 3. Conduire l'analyseur à la position dans la grammaire où il peut continuer. (remonter dans l‘arbre syntaxique)

Détermination des ancres Chaque méthode d‘analyse d‘un non terminal A possède les successeurs courant de A comme paramètres static void A (BitArray sux) { ... } sux ... successeurs de tous les NTS, qui sont en exécution a A b B eof ... C d e f Dépendant du contexte courant, suxA peut dénoter différents ensembles suxA = {b, eof} suxA = {f, d, e, eof} sux contient toujours eof (le successeur du symbole de départ)

Traitement des symboles terminaux Grammaire Action de l‘analyseur A = ... a s1 s2 ... sn . check(a, suxA Union First(s1) Union First(s2) ... Union First(sn)); Peut être déterminé au moment de la compilation Doit être déterminé au moment de l‘exécution si : TS ou NTS static void Check (int expected, BitArray sux) {...} Exemple A = a b c. static void A (BitArray sux) { Check(a, Add(sux, fs1)); Check(b, Add(sux, fs2)); Check(c, sux); } static BitArray Add (BitArray a, BitArray b) { BitArray c = (BitArray) a.Clone(); c.Or(b); return c; static BitArray fs1 = new BitArray(); fs1[b] = true; fs1[c] = true; Rempli au début du programme

Traitement des symboles non terminaux Grammaire Action de l‘analyseur A = ... B s1 s2 ... sn . B(suxA Union First(s1) Union First(s2) ... Union First(sn)); Exemple A = a B c. B = b b. static void A (BitArray sux) { Check(a, Add(sux, fs3)); B(Add(sux, fs4)); Check(c, sux); } static void B (BitArray sux) { Check(b, Add(sux, fs5)); Check(b, sux); fs3 = {b, c} fs4 = {c} fs5 = {b} La méthode d‘analyse pour l‘axiome S est appelée S(fs0); où fs0 = {eof}

Sauter les unités invalides Les erreurs sont détectées dans check() static void Check (int expected, BitArray sux) { if (la == expected) Scan(); else Error(Token.names[expected] + " expected", sux); } Après l‘affichage du message les unités sont sautées jusqu‘à la rencontre d‘une unité de reprise static void Error (string msg, BitArray sux) { Console.WriteLine("-- line {0}, col {1}: {2}", laToken.line, laToken.col, msg); errors++; while (!sux[la]) Scan(); // while (la # sux) Scan(); } static int errors = 0; // number of syntax errors detected

Synchronisation avec la grammaire Exemple b c d B a e A eof x Entrée suxA = {eof} suxB = {e, eof} A = a B e. B = b c d. static void A (BitArray sux) { Check(a, Add(sux, fs1)); B(Add(sux, fs2)); Check(e, sux); } static void B (BitArray sux) { Check(b, Add(sux, fs3)); Check(c, Add(sux, fs4)); Check(d, sux); L‘erreur est détecté ici; ancres = {d, e, eof} 1. x est sauté; la == e ( dans ancres) 2. L‘analyseur continue: Check(d, sux); 3. Détecte de nouveau une erreur; ancres = {e, eof} 4. aucune unité est sautée, car la == e (dans ancres) 5. L‘analyseur retourne de B() et lance Check(e, sux); 6. Recouvrement réussi! Une fois l‘erreur détectée l‘analyseur avance jusqu‘à trouver un endroit dans la grammaire qui concorde avec l‘unité courante.

Supprimer les faux messages d‘erreur Durant le recouvrement de l‘erreur l‘analyseur produit des faux messages d‘erreur Résolu par une simple heuristique Si moins de 3 unités sont reconnues correctement depuis la dernière erreur, l'analyseur suppose que la nouvelle erreur est une fausse erreur. Les fausses erreurs ne sont pas affichées static int errDist = 3; // next error should be reported static void Scan () { ... errDist++; // one more token recognized } public static void Error (string msg, BitArray sux) { if (errDist >= 3) { Console.WriteLine("-- line {0}, col {1}: {2}", laToken.line, laToken.col, msg); errors++; while (!sux[la]) Scan(); errDist = 0; // counting is restarted

Traitement des alternatives A = a | b | g . a, b, g sont des expressions EBNF static void A (BitArray sux) { // the error check is already done here so that the parser can synchronize with // the starts of the alternatives in case of an error if (la not in (First(a) Or First(b) Or First(g))) Error("invalid A", sux Or First(a) Or First(b) Or First(g)); // la matches one of the alternatives or is a legal successor of A if (la in First(a)) ... parse a ... else if (la in First(b)) ... parse b ... else ... parse g ... // no error check here; any errors have already been reported } First(a) Or First(b) Or First(g) peut être déterminé au moment de la compilation sux Or ... est déterminé au moment de l‘exécution

Traitement des Options et Itérations EBNF static void A (BitArray sux) { // error check already done here, so that the parser can // synchronize with the start of a in case of an error if (la not in (First(a) Or First(b))) Error("...", sux Or First(a) Or First(b)); // la matches a or b or is a successor of A if (la in First(a)) ... parse a ...; ... parse b ... } A = [ a ] b. Itérations A = { a } b. static void A (BitArray sux) { for (;;) { // the loop is entered even if la not in First(a) if (la in First(a)) ... parse a ...; // correct case 1 else if (la in First(b) Or sux) break; // correct case 2 else Error("...", sux Or First(a) Or First(b)); // error case } ... parse b ...

Exemple A = a B | b {c d}. B = [b] d. static void A (BitArray sux) { if (la != a && la != b) Error("invalid A", Add(sux, fs1)); // fs1 = {a, b} if (la == a) { Scan(); B(sux); } else if (la == b) { Scan(); for (;;) { if (la == c) { Check(d, Add(sux, fs2)); // fs2 = {c} } else if (sux[la]) { break; } else { Error("c expected", Add(sux, fs2)); } static void B (BitArray sux) { if (la != b && la != d) Error("invalid B", Add(sux, fs3)); // fs3 = {b, d} if (la == b) Scan(); Check(d, sux); }

En résumé Traitement des erreurs avec des symboles de reprises (ancres) Avantage + applicable automatiquement Inconvénients - Ralentit l‘analyse gonfle le code de l‘analyseur complexe

Traitement des erreurs avec des ancres spéciaux Le traitement est seulement fait à des positions particulières Les mot-clés apparaissent à des positions uniques dans la grammaire Exemple Début d‘instruction: if, while, do, ... Début de déclaration: public, static, void, ... Problème: ident peut figurer à plusieurs endroits! ident n‘est pas un ancre sûr. Il est donc omis de l‘ensemble des ancres Ensemble d‘ancres Code à insérer aux points de synchronisation ... if (la not in expectedSymbols) { Error("..."); // no successor sets; no skipping of tokens in Error() while (la not in (expectedSymbols Or {eof})) Scan(); } L‘ensemble des ancres à ce point de synchronisation Pour éviter la boucle infinie Pas besoin de passer les ensembles successeur aux méthodes de l‘analyseur Les ensembles d‘ancres sont connus avant l‘exécution Après une erreur l‘analyseur ignore des unités jusqu‘au prochain point de synchronisation

Exemple Synchronisation au début d‘une instruction static void Statement () { if (!firstStat[la]) { Error("invalid start of statement"); while (!firstStat[la] && la != Token.EOF) Scan(); errDist = 0; } if (la == Token.IF) { Scan(); Check(Token.LPAR); Conditions(); Check(Token.RPAR); Statement(); if (la == Token.ELSE) { Scan(); Statement(); } } else if (la == Token.WHILE) { ... static BitArray firstStat = new BitArray(); firstStat[Token.WHILE] = true; firstStat[Token.IF] = true; ... le reste de l‘analyseur reste inchangé (comme s‘il n‘ y a pas de traitement d‘erreur) Pas de synchronisation dans Error() public static void Error (string msg) { if (errDist >= 3) { Console.WriteLine("-- line {0}, col {1}: {2}", laToken.line, laToken.col, msg); errors++; } errDist = 0; heuristics with errDist can also be applied here

Exemple de recouvrement static void Statement () { if (!firstStat[la]) { Error("invalid start of statement"); while (!firstStat[la] && la != Token.EOF) Scan(); errDist = 0; } if (la == Token.IF) { Scan(); Check(Token.LPAR); Condition(); Check(Token.RPAR); Statement(); if (la == Token.ELSE) { Scan(); Statement(); } ... static void Check (int expected) { if (la == expected) Scan(); else Error(...); } public static void Error (string msg) { if (errDist >= 3) { Console.WriteLine(...); errors++; } errDist = 0; Entrée erronée: if a > b then max = a; la action if Scan(); if dans firstStat , ok a Check(LPAR); message d‘erreur: ‘(‘ attendue Condition(); reconnaît a > b then check(RPAR); message d‘erreur: ‘)’ attendue Statement(); then ne correspond pas, donc erreur, mais pas de message d‘erreur then est sauté; synchronisation avec ident (si dans firstStat) max

Synchronisation au début d‘une itération Exemple Block = "{" { Statement } "}". Modèle standard dans ce cas static void Block () { Check(Token.LBRACE); while (firstStat[la]) Statement(); Check(Token.RBRACE); } Problème: si la prochaine unité ne correspond pas à une instruction la boucle n‘est pas exécutée. Le point de synchronisation dans Statement n‘est jamais atteint.

Synchronisation au début d‘une itération Exemple Block = "{" { Statement } "}". C‘est meilleur de synchroniser au début de l‘itération static void Block() { Check(Token.LBRACE); for (;;) { if (la in First(Statement)) Statement(); // correct case 1 else if (la in {rbrace, eof}) break; // correct case 2 else { // error case Error("invalid start of Statement"); do Scan(); while (la  (First(Statement) union {rbrace, eof})); errDist = 0; } Check(Token.RBRACE); Pas de synchronisation dans Statement() static void Statement () { if (la == Token.IF) { Scan(); ... }

En résumé Traitement des erreurs avec des symboles de reprises spéciaux Avantages + ne ralentit pas l‘exécution de l‘analyseur + ne gonfle pas le code de l‘analyseur + simple Inconvénients - demande plus d‘expérience