Générateurs de compilateurs

publicité
Générateurs de compilateurs
Pr ZEGOUR DJAMEL EDDINE
Ecole Supérieure d’Informatique (ESI)
www.zegour.uuuq.com
email: [email protected]
Générateurs de compilateurs
Introduction
Yacc
Lex
Coco/R
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 , ...)
générateur
scanner
Spécification du scanner
Ex. grammaire régulière)
Spécification sémantique
(Ex. grammaire d’attribut)
générateur
de l’analyseur
Générateur
De compilateurs
Exemples
Yacc
Lex
Coco/R
...
scanner
Analyseur
compilateur
& éditeur de
liens
Classes utilisateur
• Table des symboles
• Générateur de code
• Programme principal
• ...
générateur d’analyseur syntaxique et sémantique pour C et Java
générateur de scanner pour C, Java et C#
générateur de scanner et d’analyseur pour Java, C#, Modula-2, Oberon, ...
compilateur
généré
Générateurs de compilateurs
Introduction
Yacc
Lex
Coco/R
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
Utilisation
sample.y
Nous décrivons ici la version de Java
Yacc
parser.java
javac
Versions actuelles
Bison
version GNU de Yacc
http://www.gnu.org/software/bison/bison.html
Byacc
Berkeley Yacc
http://byaccj.sourceforge.net/
parser.class
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)
Traduit vers
class parser {
...
public void yyparse() {
... parser ...
}
}
Yacc — Productions et actions sémantiques
Productions
Grammar
Production
Alternative
SemAction
NT
T
= {Production}.
= NT ":" Alternative {"|" Alternative} ";"
= {NT | T} [SemAction].
= "{" ... arbitrary Java statements ... "}".
= ident.
= ident | charConst.
Exemple
expr: term
{ $$ = $1; }
| expr '+' term { $$.ival = $1.ival + $3.ival; } ;
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 — 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
yylval = new parserval(n);
Accès dans une action sémantique
{ ... $1.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 — Variables et Méthodes Java
Sont déclarées après le second %%
%{ ... imports ... %}
... Unités ...
%%
... Productions ...
%%
... Déclarations Java ...
• 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
void yyerror(String msg) {...}
Pour afficher les messages d’erreur
int yylex() {...}
Scanner (retourne les codes des unités
et remplit yylval)
public static void main(String[] arg) {
... initializations for yylex ...
yyparse();
}
Programme principal
Exemple: Compilateur pour les expressions
/* 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
| expr '+' term
{ $$ = $1; }
{ $$.ival = $1.ival + $3.ival; } ;
term: factor
| term '*' factor
{ $$ = $1; }
{ $$.ival = $1.ival * $3.ival; } ;
factor: number
| '(' expr ')'
{ $$ = $1; }
{ $$ = $2; } ;
%%
int yylex() {...}
void yyerror(string msg) {...}
public static void main(String[] arg) {...}
arithmétiques
Conventions de codage des unités
• 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é.
Yacc — Précédence des opérateurs
La grammaire suivante spécifie explicitement la précédence des opérateurs
expr: term | expr '+' term ;
term: factor | term '*' factor ;
factor: number | '(' expr ')' ;
• '*' a une précédence sur '+'
• Les opérateurs sont associatifs à gauche: a*b*c == (a*b)*c
On peut aussi l’exprimer comme suit en Yacc:
%token number
%left
'+'
%left
'*'
• %left : l’opérateur est associatif gauche
a+b+c == (a+b)+c
%%
input: expr
{ System.out.println($1.ival); } ;
expr: number
| expr '+' expr
| expr '*' expr
| '(' expr ')'
{ $$ = $1; }
{ $$.ival = $1.ival + $3.ival; }
{ $$.ival = $1.ival * $3.ival; }
{ $$ = $2; }
%%
...
• 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 — 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 : error a)
• Il réduit error a à A et exécute l’action sémantique correspondante
Exemple
Statement = ...
| error ';' ;
Saute tout jusqu’au prochain ';'
Générateurs de compilateurs
Introduction
Yacc
Lex
Coco/R
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
Utilisation
Nous decrivons ici la version C
sample.l
Lex
sample.yy.c
sample.y
Yacc
sample.tab.c
include
C-Compiler
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/
version C# , dérivé de JLex
http://www.cybercom.net/~zbrad/DotNet/Lex
CsLex
sample.o
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;
char* yytext;
int yyleng;
int yylineno;
/* attribute if the token has a numeric value */
/* token text (attribute of ident, string, ...) */
/* lengh of the token text */
/* 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
.
x*
x+
x?
(...|...)
[...]
{...}
^
$
\udddd
la chaîne "abc"; tout caractère sauf ()[]{}*+?|^$.\ dénote lui-même
Tout caractère sauf \n (fin de ligne)
0 ou plusieurs répétitions de x
1 ou plusieurs répétitions de 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
caractère en Unicode
Générateurs de compilateurs
Introduction
Yacc
Lex
Coco/R
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/
Utilisation
main
grammaire
d’attribut
parser
Coco/R
scanner
Classes utilisateurs
(Ex. Table des symboles
csc
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>
= Term<out x>
{ '+' Term<out y>
}.
(. int y; .)
Term<out int x>
= Factor<out x>
{ '*' Factor<out y>
}.
(. int y; .)
Factor<out int x>
= number
| '(' Expr<out x> ')'.
END Calc.
(. x = x + y; .)
(. x = x * y; .)
(. x = Convert.ToInt32(t.val); .)
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
class Token {
int kind;
string val;
int pos;
int line;
int col;
}
(. x = Convert.ToInt32(t.val); .)
// token code
// token value
// token position in the source text (starting at 0)
// token line (starting at 1)
// token column (starting at 0)
Les symboles non terminaux
• Les NTS peuvent avoir des attributs d’entrée
Attr. formels: A<int x, char c> = ... .
Attr réels.:
... A<y, 'a'> ...
• Les NTS peuvent avoir des attributs de sortie
B<out int x, out int y> = ... .
... B<out z, out n> ...
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
Term<out int x>
= Factor<out x>
{ '*' Factor<out y>
}.
(. int y; .)
déclaration
(. x = x * y; .)
action sémantique
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 – les méthodes d’analyse
Chaque production est traduite en une méthode de l’analyseur
Expr<out int x>
= Term<out x>
{ '+' Term<out y>
}.
(. int 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;
}
}
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.
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 — Tests de grammaire
Test LL(1)
A = a [B] C d
| B a.
B = a b.
C = a [d].
Coco/R affiche les avertissements suivants
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".
DbgOption = "$debug".
...
(. option[print] = true; .)
(. 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
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
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
SemAction = "(." { ANY } ".)".
Tout unité qui n’est pas ".)" ou eof
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
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 — 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
public static Token
public static void
public static void
}
t;
la;
Parse () {...}
SemErr (string msg) {...}
Error message class
public class Errors {
public static int
public static string
public static void
public static void
public static void
public static void
}
count = 0;
errMsgFormat = "-- line {0} col {1}: {2}";
SynErr (int line, int col, int n);
SemErr (int line, int col, int n);
Error (int line, int col, string msg);
Exception (string msg);
Téléchargement