Upload
lore
View
67
Download
0
Embed Size (px)
DESCRIPTION
Analyse lexicale. Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI) www.zegour.uuuq.com email: [email protected]. Analyse lexicale Taches d‘un scanner Grammaires Régulières et Automates finis Implémentation des scanners. 2.Saute les caractères non significatifs. blancs - PowerPoint PPT Presentation
Citation preview
Analyse lexicale
Pr ZEGOUR DJAMEL EDDINEEcole Supérieure d’Informatique (ESI)http://zegour.esi.dz/ email: [email protected]
Analyse lexicaleTaches d‘un scanner
Grammaires Régulières et Automates finis
Implémentation des scanners
Taches d’un scanner
1. Délivre les symboles terminaux (unités lexicales)
i f ( x = 3 )=
Source
scanner IF, LPAR, IDENT, EQ, NUMBER, RPAR, ..., EOF
Unités lexicales
Les unités lexicales ont une structure syntaxique
ident = letter { letter | digit }.number = digit { digit }.if = "i" "f".eql = "=" "="....
2. Saute les caractères non significatifs• blancs• Caractères de tabulation• Caractères de fin de lignes (CR, LF)• Commentaires
Pourquoi la phase lexicale est séparée de la phase syntaxique?Entrainerait une analyse syntaxique plus compliquée(Ex. distinction difficile entre les mots clés et les identificateurs)
Statement = ident "=" Expr ";"| "if" "(" Expr ")" ... .
On doit écrire ceci comme suit
Statement = "i" ( "f" "(" Expr ")" ...| letter {letter | digit} "=" Expr ";")
Le scanner doit éliminer les caractères non significatifsCes caractères peuvent apparaître partout => conduiraient vers des grammaires très complexes)
Statement = "if" {Blank} "(" {Blank} Expr {Blank} ")" {Blank} ... .Blank = " " | "\r" | "\n" | "\t" | Comment.
Les unités lexicales peuvent être decrites à l‘aide de grammaires régulières(Plus simple et plus efficace que les grammaires à contexte libre)
Analyse lexicaleTaches d‘un scanner
Grammaires Régulières et Automates finis
Implémentation des scanners
Grammaires régulières
DéfinitionUne grammaire est dite régulière si elle peut être décrite par des productions de la forme:
A = a.A = b B.
a, b des TSA, B des NTS
Exemple Grammaire pour les identificateurs
Ident = letter| letter Rest.
Rest = letter| digit| letter Rest| digit Rest.
Ex., dérivation de xy3
Ident => letter Rest => letter letter Rest => letter letter digit
Autre définitionUne grammaire est dite régulière si elle peut être décrite par une simple non récursive production EBNF.
Exemple Grammaire pour les identificateurs
Ident = letter { letter | digit }.
Exemples
Peut-on transformer la grammaire suivante en une grammaire régulière?
E = T { "+" T }.T = F { "*" F }.F = id.
Après substitution de F dans T
T = id { "*" id }.
Peut-on transformer la grammaire suivante en une grammaire régulière?
E = F { "*" F }.F = id | "(" E ")".
Après substitution de F dans E
E = ( id | "(" E ")" ) { "*" ( id | "(" E ")" ) }.
Substituer E dans E ne mène à rien.Récursion centrale ne peut être éliminée.La grammaire est non régulière.
Après substitution de T dans E
E = id { "*" id } { "+" id { "*" id } }.
La grammaire est régulière
Limitations des grammaires régulières
Les grammaires régulières ne traitent pas les structures emboîtéesRaison : ne peut éliminer la récursion centrale!
La récursion centrale est importante dans les langages de programmation.
Class => "class" "{" ... Class ... "}"
• Expressions emboîtées
• Instructions emboîtées
• Classes emboîtées
Expr => ... "(" Expr ")" ...
Statement => "do" Statement "while" "(" Expr ")"
Solution : utilisation des grammaires à contexte libre.
Les structures lexicales sont généralement régulièresnom letter { letter | digit }nombres digit { digit }
chaînes "\"" { pas de quote } "\""
Mot-clés letter { letter }
opérateurs ">" "="
Exception: commentaires emboîtés
/* ..... /* ... */ ..... */
Le scanner doit les traiter comme casspécial
Expressions régulièresAutre notation pour les grammaires régulières
Définition
1. (la chaîne vide) est une expression régulière
2. Un symbole terminal est une expression régulière
3. Si et sont des expressions régulières, les expressions suivantes sont régulières:
( | )()? | ()* | | | | ...()+ | | | ...
Exemples
"w" "h" "i" "l" "e" whileletter ( letter | digit )* namesdigit+ numbers
Automate Fini Déterministique (DFA)
Peut être utilisé pour analyser les langages réguliers
Exemple
0 1
État final
digit
letterletter
État initial est 0 par convention
Table de transition
letter digit
s0
s1
s1 error
s1 s1
"fini", car Peut être défini explicitement
Définition
Un automate fini déterministique est un 5 tuple (S, I, , s0, F)
• S Ensemble des états• I Ensemble des symboles d‘entrée• : S x I S Fonction de transition des états• s0 État initial• F Ensemble des états finaux
Un DFA reconnaît une phrase (sentence)• S‘il est dans un état final• Et il n‘y a plus de symboles d‘entrée Ou il n‘ y a pas de transition possible avec le prochain symbole d‘entrée
Le langage reconnu par une DFA estl‘ensemble de toutes les séquences desymboles pouvant être générées de l‘état initial vers l‘un des états finaux.
Le Scanner comme un DFALe scanner peut être vu comme un grand DFA
0
" "
1letter
letter
digit
2digit digit
3(
4>
5=
...
Exemple
Entrée: max >= 30
s0 s1m a x • Pas de transition avec " " dans s1
• ident reconnu
> =s0 s5 • Saute les blancs au début
• Ne s‘arrête pas en s4• Pas de transition avec " " dans s5• geq reconnu
s0 s23 0 • Saute les blancs au début
• Pas de transition avec " " dans s2• number reconnu
Après chaque unité lexicale reconnue le scanner recommence à l‘état initial s0
ident
number
lpar
gtr geq
Transformation: grammaire régulière vers DFA
Une grammaire régulière peut être transformée en un DFA comme suit
A = b C. A Cb
A = d. A dstop
Exemple
Grammaire
A = a B | b C | c.B = b B | c.C = a C | c.
Automate
A Ba
Cb
stopc
ac
bc
Automate Fini Non Déterministique (NDFA)
Exemple
0 1digit
2digit
digit
hex
H3
intNum
hexNum
intNum = digit { digit }.hexNum = digit { hex } "H".digit = "0" | "1" | ... | "9".hex = digit | "A" | ... | "F".
Non déterministique carIl y a 2 possibles transitionsavec digit dans s0
Chaque NDFA peut être transformé en un DFA équivalent
0 1digit2A,B,C,D,E,F
digit hex
H3
intNum
hexNum
H
Implémentation d‘un DFA (Variante 1)
Implémentation de comme une matrice
int[,] delta = new int[maxStates, maxSymbols];int lastState, state = 0; // DFA starts in state 0do {
int sym = next symbol;lastState = state;state = delta[state, sym];
} while (state != undefined);assert(lastState in F); // F is set of final statesreturn recognizedToken[lastState];
Exemple pour
0 2a 1 c
b
A = a { b } c.
A
a b c
0 1 - -1 - 1 22 - - - F
int[,] delta = { {1, -1, -1}, {-1, 1, 2}, {-1, -1, -1} };
Cette implémentation pourrait être très inefficace pour un véritable scanner
Implémentation d‘un DFA (Variante 2)
0 2a 1 c
b A
Coder les états dans le code source
int state = 0;loop:
for (;;) {char ch = read();switch (state) {
case 0: if (ch == 'a') { state = 1; break; } else break loop;
case 1: if (ch == 'b') { state = 1; break; }else if (ch == 'c') { state = 2; break; }else break loop;
case 2: return A;}
}return errorToken;
en Java c‘est plus fatiguant:
char ch = read();s0: if (ch == 'a') { ch = read(); goto s1; }
else goto err;s1: if (ch == 'b') { ch = read(); goto s1; }
else if (ch == 'c') { ch = read(); goto s2; }else goto err;
s2: return A;err: return errorToken;
Analyse lexicaleTaches d‘un scanner
Grammaires Régulières et Automates finis
Implémentation des scanners
Interface du Scanner
class Scanner {static void Init (TextReader r) {...}static Token Next () {...}
}
Scanner.Init(new StreamReader("myProg.zs"));
Initialisation du scanner
Token t;for (;;) {
t = Scanner.Next();...
}
Lecture des unités lexicales
Unités lexicales
class Token { public const int NONE = 0, IDENT = 1, ….
int kind; // token codeint line; // token line (for error messages)int col; // token column (for error messages)int val; // token value (for number and charCon)string str; // token string (for numbers and identifiers)
}
PLUS = 4, /* + */MINUS = 5, /* - */TIMES = 6, /* * */SLASH = 7, /* / */REM = 8, /* % */EQ = 9, /* == */GE = 10, /* >= */GT = 11, /* > */LE = 12, /* <= */LT = 13, /* < */NE = 14, /* != */AND = 15, /* && */OR = 16, /* || */
Exemple de codage pour un langage particulier
const int
NONE = 0, IDENT = 1,NUMBER = 2,CHARCONST = 3,
ASSIGN = 17,/* = */PPLUS = 18,/* ++ */MMINUS = 19,/* -- */SEMICOLON = 20,/* ; */COMMA = 21,/* , */PERIOD = 22,/* . */LPAR = 23,/* ( */RPAR = 24,/* ) */LBRACK = 25,/* [ */RBRACK = 26,/* ] */LBRACE = 27,/* { */RBRACE = 28,/* } */
BREAK = 29,CLASS = 30,CONST = 31,ELSE = 32,IF = 33,NEW = 34,READ = 35,RETURN = 36,VOID = 37,WHILE = 38,WRITE = 39,
EOF = 40;
Erreur Classes des unités Opérateurs et caractères spéciaux Mot-clés Fin de fichier
Implémentation des Scanner
Variables statiques dans le scanner
static TextReader input; // input streamstatic char ch; // next input character (still unprocessed)static int line, col; // line and column number of the character ch
const int EOF = '\u0080'; // character that is returned at the end of the file
Init()
public static void Init (TextReader r) {input = r;line = 1; col = 0;NextCh(); // reads the first character into ch and increments col to 1
}
NextCh()
static void NextCh() {try {
ch = (char) input.Read(); col++;if (ch == '\n') { line++; col = 0; }else if (ch == '\uffff') ch = EOF;
} catch (IOException e) { ch = EOF; }}
• ch = prochain caractère d‘entrée• retourne EOF si fin de fichier• incrémente line et col
Method Next()public static Token Next () {
while (ch <= ' ') NextCh(); // skip blanks, tabs, eolsToken t = new Token(); t.line = line, t.col = col;switch (ch) {
case 'a': ... case 'z': case 'A': ... case 'Z': ReadName(t); break;case '0': case '1': ... case '9': ReadNumber(t); break;case ';': NextCh(); t.kind = Token.SEMICOLON; break;case '.': NextCh(); t.kind = Token.PERIOD; break;case EOF: t.kind = Token.EOF; break; // no NextCh() any more...case '=': NextCh();
if (ch == '=') { NextCh(); t.kind = Token.EQ; } else t.kind = Token.ASSIGN;break;
case '&': NextCh();if (ch == '&') { NextCh(); t.kind = Token.AND; } else t.kind = NONE;break;
...case '/': NextCh();
if (ch == '/') {do NextCh(); while (ch != '\n' && ch != EOF);t = Next(); // call scanner recursively
} else t.kind = Token.SLASH;break;
default: NextCh(); t.kind = Token.NONE; break;}return t;
} // ch holds the next character that is still unprocessed
nom, mot-clésnombres
unité simple
unités composée
commentaire
caractère invalide
Autres méthodes
static void ReadName (Token t)
• Au début ch contient la première lettre du nom
• Lit les prochaines lettres, digits et '_' et les range dans t.str
• Chercher le nom dans la table des mots clés ( hash-code ou arbre de recherche binaire )si trouvé: t.kind = code du mot clé;sinon: t.kind = Token.IDENT;
• À la fin, ch contient le premier caractère après le nom
static void ReadNumber (Token t)
• Au début ch contient le premier chiffre du nombre
• Lit les prochains digits, les range dans t.str; puis convertit la chaîne en un nombre et range lavaleur dans t.val.Si débordement: Erreur
• t.kind = Token.NUMBER;
• A la fin ch contient le premier caractère après le nombre
Considérations d‘efficacité
Taille d‘un programme modeste
Aux environs de 1000 instructions environ 6000 unités environ 60000 caractères
La phase lexicale consomme un temps importantprend 20-30% du temps total de compilation
Faire attention à la programmation
Utiliser les variables globales :Ex.: ch est global et non un paramètre de NextCh()
Pour les grands fichiers d‘entrée, c‘est judicieux d‘utiliser des lectures bufférisées
Stream file = new FileStream("MyProg.zs");Stream buf = new BufferedStream(file);TextReader r = new StreamReader(buf);Scanner.Init(r);