300 likes | 385 Views
Générateurs de compilateurs. Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI) www.zegour.uuuq.com email: d_zegour@esi.dz. Générateurs de compilateurs Introduction Yacc Lex Coco/R. générateur scanner. scanner. Spécification du scanner Ex. grammaire régulière).
E N D
Générateurs de compilateurs Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI) www.zegour.uuuq.com email: d_zegour@esi.dz
Générateurs de compilateurs Introduction Yacc Lex Coco/R
générateur scanner scanner Spécification du scanner Ex. grammaire régulière) générateur de l’analyseur Analyseur Spécification sémantique (Ex. grammaire d’attribut) Générateur De compilateurs Exemples Yacc générateur d’analyseur syntaxique et sémantique pour C et Java Lex générateur de scanner pour C, Java et C# Coco/R générateur de scanner et d’analyseur pour Java, C#, Modula-2, Oberon, ... ... Fonctionnement des générateurs de compilateurs Ils génèrent les parties d’un compilateur à partir d’une spécification concise (Parties générées : scanner, analyseur syntaxico-sémantique, générateur de code , ...) compilateur & éditeur de liens compilateur généré • Classes utilisateur • Table des symboles • Générateur de code • Programme principal • ...
Générateurs de compilateurs Introduction Yacc Lex Coco/R
Utilisation Yacc javac sample.y parser.java parser.class Versions actuelles Bison version GNU de Yacc http://www.gnu.org/software/bison/bison.html Byacc Berkeley Yacc http://byaccj.sourceforge.net/ Yacc - Yet another compiler compiler Histoire • 1975 développé aux laboratoires Bell ( ensemble avec C et Unix) • Génère des analyseurs LALR(1) • À l’origine sous Unix, Aujourd'hui aussi sous Windows, Linux • A l’origine pour C, Aujourd'hui pour Java Nous décrivons ici la version de Java
class parser { ... public void yyparse() { ... parser ... } } Traduit vers Langage d’entrée pour Yacc Format général %{package Java et les lignes ‘import’ %} Déclarations Yacc (unités, règles de précédence des opérateurs, ...) %% productions %% Déclarations Java (champs, méthodes)
Actions Sémantiques • Peuvent contenir des instructions Java • Peuvent apparaître seulement à la fin d’une alternative • Les attributs sont dénotés par des noms spéciaux: $$ attribut du coté gauche NTS $i attribut du i-ème symbole du coté droit ($1 = attr. du premier symbole, $2 = attr. du second symbole, ...) Yacc — Productions et actions sémantiques Productions Grammar = {Production}. Production = NT ":" Alternative {"|" Alternative} ";" Alternative = {NT | T} [SemAction]. SemAction = "{" ... arbitrary Java statements ... "}". NT = ident. T = ident | charConst. Exemple expr: term { $$ = $1; } | expr '+' term { $$.ival = $1.ival + $3.ival; } ;
Pour les symboles non terminaux • Chaque NTS a un attribut $$ de type parserval ( valeurs plus complexes rangées dans $$.obj) • Chaque affectation à $$ empile l’attribut du NTS dans une pile d’attributs.Les accès à $1, $2 se font en dépilant les attributs de la pile Yacc — Attributs Pour les symboles terminaux • Sont délivrés par le scanner (scanner développé manuellement ou généré avec Lex) • Chaque unité lexicale a un attribut de type parserval class parserval { int ival; // token value if the token should have an int attribute double dval; // token value if the token should have a double attribute String sval; // token value, e.g. for ident and string Object obj; // token value for more complex tokens parserval(int val) {...} // constructors parserval(double val) {...} parserval(String val) {...} parserval(Obj val) {...} } • Le scanner retourne les attributs dans la variable globale yylval Scanner Accès dans une action sémantique yylval = new parserval(n); { ... $1.ival ... }
Yacc — Variables et Méthodes Java Sont déclarées après le second %% %{ ... imports ... %} ... Unités ... %% ... Productions ... %% ... DéclarationsJava ... • Deviennent des champs et des méthodes pour l’analyseur • Peuvent être utilisées dans les actions sémantiques Au moins les méthodes suivantes doivent être implémentées dans les déclarations Java Pour afficher les messages d’erreur void yyerror(String msg) {...} Scanner (retourne les codes des unités et remplit yylval) int yylex() {...} public static void main(String[] arg) { ... initializations for yylex ... yyparse(); } Programme principal
Exemple: Compilateur pour les expressions arithmétiques Conventions de codage des unités /* declaration of all tokens which are not strings */ %token number %% /* productions: first NTS is the start symbol */ input: expr { System.out.println($1.ival); } ; expr: term { $$ = $1; } | expr '+' term { $$.ival = $1.ival + $3.ival; } ; term: factor { $$ = $1; } | term '*' factor { $$.ival = $1.ival * $3.ival; } ; factor: number { $$ = $1; } | '(' expr ')' { $$ = $2; } ; %% int yylex() {...} void yyerror(string msg) {...} public static void main(String[] arg) {...} • eof == 0 • Code des unités ‘caractère’ : • leurs valeurs Ascii • (Par exemple '+' == 43) • YYERRCODE == 256 • Autres unités sont numérotées • consécutivement commençant par 257 (Par exemple. nombre == 257);elles peuvent être accédées dans les • productions et dans yylex() utilisant • leur nom déclaré.
On peut aussi l’exprimer comme suit en Yacc: %token number %left '+' %left '*' %% input: expr { System.out.println($1.ival); } ; expr: number { $$ = $1; } | expr '+' expr { $$.ival = $1.ival + $3.ival; } | expr '*' expr { $$.ival = $1.ival * $3.ival; } | '(' expr ')' { $$ = $2; } %% ... • %left: l’opérateur est associatif gauchea+b+c == (a+b)+c • Les opérateurs sont déclarés en ordre • ascendant de priorité: '*' a une précédence sur '+' • Cette grammaire ne spécifie aucune • précédence d’opérateurs • La précédence est spécifiée par%left ou %right Yacc — Précédence des opérateurs La grammaire suivante spécifie explicitement la précédence des opérateurs • '*' a une précédence sur '+' • Les opérateurs sont associatifs à gauche: a*b*c == (a*b)*c expr: term | expr '+' term ; term: factor | term '*' factor ; factor: number | '(' expr ')' ;
Exemple Statement = ... | error ';' ; Saute tout jusqu’au prochain ';' Yacc — Traitement des erreurs Les alternatives ‘error’ Pour certains NTS (EX: Instructions, Expression, ...) l’utilisateur doit spécifier Les alternatives ‘error’ A: ... | ... | error a {...} ; a ... Séquence quelconque de symboles T et NT • Signification: S’il existe une erreur dans A l’analyseur effectue les actions suivantes: • Il dépile des états de la pile jusqu’à l’obtention d’un état dans lequel une action ‘décaler’ avec • l’unité error est valide • ‘Décaler’ error • Il saute les unités d’entrée jusqu’à ce qu’il détecte une séquence d’unités qui peut être • réduite à a ( le sommet de pile contient alors : errora) • Il réduit errora à A et exécute l’action sémantique correspondante
Générateurs de compilateurs Introduction Yacc Lex Coco/R
Utilisation Nous decrivons ici la version C Lex sample.l sample.yy.c include Yacc C-Compiler sample.y sample.tab.c sample.o Versions actuelles flex version GNU de Lex (pour C) http://www.gnu.org/software/flex/ JLex version Java avec légère différence dans la syntaxe d’entrée; incompatible avec Bison ou Byacc http://www.cs.princeton.edu/~appel/modern/java/JLex/ CsLex version C# , dérivé de JLex http://www.cybercom.net/~zbrad/DotNet/Lex Lex — Générateur de scanner Histoire • 1975 développé aux laboratoires Bell • génère un scanner en forme de DFA • A l’origine un outil de Unix, aujourd'hui aussi pour Windows • A l’origine pour C, Aujourd’hui aussi pour Java • Coopère généralement avec Yacc
Exemple de description Lex %{ ... e.g. include directives for token numbers exported by the parser ... %} /* macros */ delim [ \t\n] /* blank, tab, eol */ ws {delim}+ /* {...} ... use of a macro */ letter [A-Za-z] digit [0-9] id {letter} ({letter} | {digit})* number {digit}+ %% /* token declarations described as regular expressions */ {ws} {} /* no action */ if { return IF; } /* constants like IF are imported from the parser */ then { return THEN;} else { return ELSE; } {id} { yylval = storeId(yytext, yyleng); return ID; } {number} { yylval = convert(yytext, yyleng); return number; } < { return yytext[0]; } > { return yytext[0]; } . {} /* . denotes any character */ %% /* semantic routines */ int storeId(char* text, int len) {...} int convert(char* text, int len) {...}
Scanner généré La spécification du scanner est convertie en une fonction int yylex() {...} qui est incluse dans l’analyseur yylex() retourne aussi les attributs d’unités comme variables globales int yylval; /* attribute if the token has a numeric value */ char* yytext; /* token text (attribute of ident, string, ...) */ int yyleng; /* lengh of the token text */ int yylineno; /* line number of the token */ L’analyseur déclare (et exporte) les codes des unités %token IF %token THEN ...
Expressions régulières dans Lex Éléments des expressions régulières abc la chaîne "abc"; tout caractère sauf ()[]{}*+?|^$.\ dénote lui-même . Tout caractère sauf \n (fin de ligne) x* 0 ou plusieurs répétitions de x x+ 1 ou plusieurs répétitions de x x? 0 ou 1 occurrence de x ( occurrence optionnelle) (...|...) pour grouper des alternatives [...] ensemble de tous les caractères entre les crochets (Ex. [A-Za-z0-9$]) {...} Utilise d’une macro ^ ligne début $ ligne fin \udddd caractère en Unicode
Générateurs de compilateurs Introduction Yacc Lex Coco/R
Utilisation main grammaire d’attribut Coco/R parser csc scanner Classes utilisateurs (Ex. Table des symboles Coco/R – Compilateur de compilateur /Descente Récursive Histoire • 1980 développé à l’université de Linz (Rechenberg, Mössenböck) • génère un scanner et un analyseur à partir d’une grammaire d’attribut- scanner comme un DFA- Analyseur sous forme de ‘descente récursive’ • Il existe des versions pour C#, Java, C/C++, Delphi, Modula-2, Oberon, Python, ... • Publié sous GPL: http://www.ssw.uni-linz.ac.at/Research/Projects/Coco/
Exemple: Compilateur pour les Expressions arithmétiques COMPILER Calc /* grammar name = start symbol */ CHARACTERS /* character sets used in token declarations */ digit = '0' .. '9'. tab = '\t'. cr = '\r'. lf = '\n'. TOKENS /* declaration of all tokens which are not literals */ number = digit {digit}. COMMENTS /* declaration of comments */ FROM "//" TO cr lf FROM "/*" TO "*/" NESTED IGNORE tab cr lf /* these characters are ignored as white space */ PRODUCTIONS Calc (. int x; .) = "CALC" Expr<out x> (. System.Console.WriteLine(x); .) . Expr<out int x> (. int y; .) = Term<out x> { '+' Term<out y> (. x = x + y; .) }. Term<out int x> (. int y; .) = Factor<out x> { '*' Factor<out y> (. x = x * y; .) }. Factor<out int x> = number (. x = Convert.ToInt32(t.val); .) | '(' Expr<out x> ')'. END Calc.
Les symboles non terminaux • Les NTS peuvent avoir des attributs d’entrée Attr. formels: Attr réels.: A<int x, char c> = ... . ... A<y, 'a'> ... • Les NTS peuvent avoir des attributs de sortie B<outint x, outint y> = ... . ... B<out z, out n> ... Coco/R — les attributs Les symboles terminaux • Les symboles terminaux n’ont pas d’attribut explicite • Leurs valeurs peuvent être accédées dans les actions sémantiques utilisant les variables suivantes Token t; l’unité la plus récente reconnue Token la; la prochaine unité (lookahead) (non encore reconnue) Exemple Factor<out int x> = number (. x = Convert.ToInt32(t.val); .) class Token { int kind; // token code string val; // token value int pos; // token position in the source text (starting at 0) int line; // token line (starting at 1) int col; // token column (starting at 0) }
Déclarations sémantiques • Apparaissent au début de la spécification du compilateur • Sont utilisées pour déclarer les champs et les méthodes de l’analyseur • Les ‘import’ peuvent aussi être spécifiés COMPILER Sample using System.Collections; static IList myList; static void AddToList (int x) {...} CHARACTERS ... Bien sûr, les actions sémantiques peuvent aussi accéder aux champs et méthodes de classes autre que ceux de l'analyseur. Coco/R — Traitement sémantique Actions sémantiques • code Java entre (. et .) • Peuvent apparaître n’importe où dans les productions • Dans le coté gauche d’une production elles sont considérée comme des déclarations déclaration Term<out int x> (. int y; .) = Factor<out x> { '*' Factor<out y> (. x = x * y; .) }. action sémantique
Coco/R – les méthodes d’analyse Chaque production est traduite en une méthode de l’analyseur Expr<out int x>(. int y; .) = Term<out x> { '+' Term<out y>(. x += y; .) }. devient static void Expr (out int x) { int y; Term(out x); while (la.kind == plus) { Scan(); Term(out y); x += y; } }
Séparateurs faibles Les séparateurs au début d’une itération peuvent être marqués comme Weak (faibles) FormalPars = "(" Param { WEAK ',' Param } ')'. Si le séparateur manque ou est mal écrit, la boucle n'est pas terminée prématurément, mais l'analyseur synchronise avec Premier(Param) Inter Suivant({...}) Inter {eof} Coco/R – Traitement des erreurs syntaxiques L’analyseur utilise la technique des ‘ancres spéciaux’ Point de synchronisation Doivent être marqués par SYNC Statement = SYNC ( Assignment | IfSatement | ... ). if la.kind dans Suivant(SYNC) une erreur est reportée et des unités sont sautées jusqu’à la.kind dans Suivant(SYNC) Inter {eof} Les faux messages d'erreur sont supprimés si moins de 3 unités ont été reconnues depuis la dernière erreur.
Coco/R — Tests de grammaire Test LL(1) Coco/R affiche les avertissements suivants A = a [B] C d | B a. B = a b. C = a [d]. LL1 warning in A: a is start & successor of deletable structure LL1 warning in A: a is start of several alternatives LL1 warning in C: d is start & successor of deletable structure Test de complétude Existe-il une production pour chaque NTS? Test de non-redondance Est-ce que que la grammaire contient des productions non atteintes? Test de Dérivabilité Est-ce que chaque NTS peut être dérivé en une chaîne de symboles terminaux? Test de Non-circularité Y a t-il des NTS qui peuvent être dérivé (directement ou indirectement)en eux-mêmes?
Coco/R — Pragmas (Directives de compilation) • Pragmas sont des symboles terminaux • Qui peuvent apparaître n’importe où dans l’entrée • Qui ne font pas partie de la syntaxe • Qui doivent être traités sémantiquement • Ex. options du compilateur COMPILER X CHARACTERS ... TOKENS ... PRAGMAS PrintOption = "$print". (. option[print] = true; .) DbgOption = "$debug". (. option[debug] = true; .) ... Quand la string $print apparaît dans le texte d’entrée l’action sémantique option[print] = true; est exécutée
Dans les productions Il décrit tout unité qui ne peut être produite par les autres alternatives PlaceHolder = ident | ANY. Tout unité qui n’est pas ident ou eof Tout unité qui n’est pas ".)" ou eof SemAction = "(." { ANY } ".)". Coco/R — le symbole ‘ANY’ Dans la déclaration des ensembles de caractères Il décrit des ensemble de caractères complémentaires CHARACTERS letter = 'A' .. 'Z' + 'a' .. 'z'. noLetter = ANY - letter. ... Tous les caractères qui ne sont pas des lettres
Résolution de conflit par une vérification sémantique Factor = IF (IsCast()) '(' ident ')' Factor /* type cast */ | '(' Expr ')' /* nested expression */ | ... . static boolean IsCast () { Token x = Scanner.Peek(); if (x.kind == _ident) { Symbol s = Tab.Find(x.val); return s.kind == Symbol.Kinds.Type; } else return false; } Coco/R — Résolution des conflits LL(1) Résolution de conflit par un by a multi-symbol lookahead Statement = IF (IsAssignment()) Designator "=" Expr ";" | Designator "(" ActualParams ")" ";" | ... . static boolean IsAssignment () { Token x = la; while (x.kind != _assign && x.kind != _lpar) x = Scanner.Peek(); return x.kind == _assign; } Scanner.Peek() ... Lit les unités sans les enlever de l’entrée Les noms d’unités (_assign, _lpar, ...) sont générés à partir des sections TOKENS
Coco/R — les frames Le scanner et l’analyseur sont générés à partir de frames (fichier texte ordinaire) Ex. Scanner.frame public class Scanner { const char EOL = '\n'; const int eofSym = 0; -->declarations ... static Token NextToken () { while (ignore[ch]) NextCh(); -->scan1 t = new Token(); t.pos = pos; t.col = pos - lineStart + 1; t.line = line; int state = start[ch]; StringBuilder buf = new StringBuilder(16); -->scan2 ... } ... } Coco/R insère le code à ces positions En modifiant les frames le scanner et l’analyseur peuvent être adaptés aux besoins de l’utilisateur (à un certain degré)
Coco/R — Interfaces Scanner public class Scanner { public static void Init (string sourceFileName) {...} public static void Init (Stream s) {...} public static Token Scan () {...} public static Token Peek () {...} public static void ResetPeek () {...} } Parser public class Parser { public static Token t; public static Token la; public static void Parse () {...} public static void SemErr (string msg) {...} } Error message class public class Errors { public static int count = 0; public static string errMsgFormat = "-- line {0} col {1}: {2}"; public static void SynErr (int line, int col, int n); public static void SemErr (int line, int col, int n); public static void Error (int line, int col, string msg); public static void Exception (string msg); }