Académique Documents
Professionnel Documents
Culture Documents
Définition
Une grammaire est dite à contexte libre (CFG) si toutes ses productions ont la forme:
A = . A est un NTS, séquence non vide de TS et NTS
En EBNF le coté droit 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 ")".
E reconnu
revenir 1 arc État lecture
x
E/1 continuer à partir de là avec E
État réduire
E
( E )
E/3
x
stop E/1
Appel récursif
( E ) de l‘automate E
E/3
x
E/1
( E )
...
Automates à pile (suite)
x
E/1
E
( E )
E/3
x
stop E/1
( E )
E/3
...
x
E/1
Utilise une pile pour trouver le chemin
E x
( E ) de retour des états visités
E/3
(
stop
Comment fonctionne un PDA?
Exemple: ((x))
x
0 1 E/1
E x
( E )
2 3 4 E/3
(
stop
DFA DFA
(état) (état)
pile
Productions A = a | b C. 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)
Symbole de départ A A A
?
Quelle est a A c a A c
L‘alternative
qui convient? ? b b
Entrée a b b c a b b c a b b c
Il utilise deux variables pour les unités lexicales (pour la phase sémantique)
Chaque symbole non terminal est reconnu par une méthode avec le même nom
Initialisation de l‘analyseur
Simulation
Entrée restante
A = a B c. static void A () {
abbc
B = b b. Check(a);
bbc
B();
c
Check(c);
}
Exemple
A = a B | B b. First(aB) = {a}
B = c | d. First(Bb) = First(B) = {c, d}
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 { } est une expression EBNF
Exemple
A = a { B } b.
B = c | d.
Ou bien ...
static void A () { static void A () {
Check(a); Check(a);
while (la == c || la == d) B(); while (la != b && la != Token.EOF) B();
Check(b); check(b);
} }
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
C = A | B. static void C () {
if (firstA[la]) A();
else if (firstB[la]) B();
else Error("invalid C");
}
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) ...
Les codes des unités lexicales sont souvent choisis de telle sorte qu‘ils forment des intervalles
Exemple
Schéma plus efficace pour analyser les alternatives dans une itération
A = { a | B d }.
static void A () {
for (;;) {
if (la == a) Scan();
else if (la == b || la == c) { B(); Check(d); }
else break;
}
}
Optimisations
Modèle d’une itération fréquente
Exemple
{ separator } ident { "," ident }
Exemple d‘entrée: a , b , c :
Déterminer correctement les ensembles ‘First’
Grammaire
First
A = B a. C= De d et e (D est ‘annulable’!)
B = {b}c b et c | f. f
| [d] d et a (!) D = { d }.
| e.
e
Méthodes d‘analyse
static void A () { static void C () {
B(); Check(a); if (la == d || la == e) {
} D(); Check(e);
} else if (la == f) {
static void B () { Scan();
if (la == b || la == c) { } else Error("invalid C");
while (la == b) Scan(); }
Check(c);
} else if (la == d || la == a) { static void D () {
if (la == d) Scan(); while (la == d) Scan();
} else if (la == e) { }
Scan();
} else Error("invalid B");
}
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()
A
static void A () {
Check(a); B(); Check(c); A en exécution
} a B c
reconnaît a
call B() A
static void B () {
A en exécution
" pile"
Check(d); Check(e); a B c B en exécution
}
d e
A
reconnaît d et e A en exécution
Retour de B() a B c
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)
Pré condition pour l‘analyse descendante récursive
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 ].
Règles
Statement
Statement
On peut construire 2 arbres syntaxiques
if (expr1) if (expr2) stat1; else stat2; différents!
Statement
Statement
Problème des ‘Else’
Solution
Statement = "if" "(" Expr ")" Statement [ "else" Statement ]
| ... .
Statement
Statement
Autres exigences pour une grammaire
(Pré conditions pour les analyseurs syntaxiques)
Complétude
Pour chaque NTS il doit y avoir une production
A=aBC. erreur!
B=bb. pas de production pour C
Dérivabilité
Chaque NTS doit être dérivable (directement ou indirectement) en une chaîne de TS
A=aB|c. erreur!
B=bB. 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 => B 1 => B2 => ... => A)
A=ab|B. erreur!
B=bb|A. 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
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 ...
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.
static void A (BitArray sux) { sux ... successeurs de tous les NTS, qui sont
... en exécution
}
B eof B eof
a A b b C d e
... ...
a A f
... ...
Exemple
A = a b c. static void A (BitArray sux) {
Check(a, Add(sux, fs1)); static BitArray fs1 = new BitArray();
Check(b, Add(sux, fs2)); fs1[b] = true; fs1[c] = true;
Check(c, sux);
}
Rempli au début du programme
Exemple
A = a B c. static void A (BitArray sux) {
B = b b. Check(a, Add(sux, fs3)); fs3 = {b, c}
B(Add(sux, fs4)); fs4 = {c}
Check(c, sux);
}
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
Itérations
static void A (BitArray sux) {
A = { } .
for (;;) {
// the loop is entered even if la not in First()
if (la in First()) ... parse ...; // correct case 1
else if (la in First() Or sux) break; // correct case 2
else Error("...", sux Or First(a) Or First(b));// error case
}
... parse ...
}
Exemple
A = a B | b {c d}.
B = [b] d.
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
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 } "}".
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