GENERATEUR DE COMPILATEUR JAVA

Transcription

GENERATEUR DE COMPILATEUR JAVA
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Session 1996
Travail de diplôme
François Marcos
Ecole d’Ingénieurs de Genève
GENERATEUR DE
COMPILATEUR JAVA
1
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Table des matières
Table des matières
..........................................................
1
Remerciements
.............................................................
2
Préambule
................................................................
2
Introduction
...............................................................
2
1. Le langage Java, Internet
World et
Wide
le Web
....................................
5
2. L'outil HyperGOS et les transformations
.................................
effectuées
7
2.1 L’analyseur lexical ............................................................................................................ 7
2.2 HyperGOS ......................................................................................................................... 9
2.3 Les modifications d'HyperGOS ..................................................................................... 10
2.3.1 L'interface ................................................................................................................................10
2.3.2 La génération de code .............................................................................................................11
3. La génération dejavaGOS
l'outilà partir d'HyperGOS
...............................
12
3.1 Vérification de la syntaxe ............................................................................................... 13
3.2 Découpage en sous-expressions...................................................................................... 13
3.3 Recherche des premiers de chaque règle ...................................................................... 16
3.3.1 Création de la table des symboles ..........................................................................................16
3.3.2 Recherche des parties droites de chaque symbole................................................................17
3.3.3 Vérification de la grammaire .................................................................................................17
3.3.4 Recherche des premiers ..........................................................................................................19
3.4 Génération de code.......................................................................................................... 20
4. Interface de javaGOS
......................................................
22
4.1 Concept d’une page HTML............................................................................................ 22
4.2 Concept d’une Applet ..................................................................................................... 24
5. Exemple de génération d’un compilateur
.........................................
25
6. Conclusion
..............................................................
27
Annexe 1 - Code
............................................................
29
2
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Annexe 2 - Bibliographie
......................................................
52
Annexe 3 - Glossaire
.........................................................
52
3
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Remerciements
Je tiens en premier lieu à remercier M. Fontannaz qui a sans hésiter accepté de nous
suivre dans un travail de diplôme extra-muros et qui nous a conseillés de manière toujours très judicieuse.
Je remercie également M. Guyot pour les sujets qu'il a proposés ainsi que pour son suivi
et ses conseils tout au long de ce travail, spécialement en ce qui concerne la compréhension d'HyperGOS et d'HyperCard.
Enfin, un grand merci à MM. Bonjour, Falquet et Le Grand qui m'ont accueilli au CUI
dans une excellente ambiance de travail.
Préambule
Ce travail a été réalisé dans le cadre d’un travail de diplôme à l’Ecole d’Ingénieurs de
Genève. Il a pour but la modification d’un "compilateur de compilateurs" pour qu’il
génère du code java. L’équipe "Bases de Données" du Centre Universitaire
d’Informatique de l’Université de Genève, dirigée par le professeur Léonard, nous a
accueillis durant la durée de ce travail. Nous avons travaillé avec du matériel Apple
Macintosh et le “Java Development Kit” de SUN ainsi qu'HyperCard 2.2 pour le logiciel.
Les conventions typographiques utilisées dans ce document sont les suivantes:
• Courier 12 pour le code.
• Times 12 normal pour le texte.
• Times 12 italique pour les mots anglais.
4
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Introduction
L'énoncé original a été rédigé comme suit:
Le générateur de compilateurs HyperGOS est une application HyperCard. La génération
se fait en descente récursive sur la base d'une description donnée en BNF d'un langage
X. Sur ce langage X, on désire avoir un outil OX. La description de cet outil se fait en
insérant des instructions ou des méthodes dans la grammaire du langage X. HyperGOS
génère alors un outil OX pour le langage X. Cet outil OX accepte une phrase PX et effectuera le travail O dessus. Nous avons de petits exemples (calculatrices, indentation,
mini LOGO, langage LAZY, ...)
Travail à effectuer:
• Modifier la phase de génération OX (qui actuellement génère du code HyperTalk) et la remplacer par une génération de code java.
• Ecrire un analyseur lexical.
• Démontrer que le tout fonctionne en générant des compilateurs pour les exemples mentionnés.
• Créer une interface HTML pour chaque compilateur (un champ pour la phrase
PX, un bouton pour déclencher l'outil OX, un champ pour le résultat, ...).
1. Le langage Java, Internet et le World Wide Web
Afin de bien comprendre les implications du langage java et son interaction avec Internet, un bref rappel historique s'impose.
Dans le dernier tiers des années soixante, en pleine guerre froide, le DoD (Department
of Defense) lance un projet de recherche dont le thème est l'interconnexion de réseaux.
De ce projet, naîtra le concept d'internet (inter-networking). Au début réservé aux militaires, les milieux scientifiques américains vont très vite profiter de cette technologie et
participer à son développement. C'est ainsi que peu à peu le nombre de réseaux interconnectés croît et s'étend à travers tous les Etats-Unis. C'est en 1988 que la liaison entre
le réseau universitaire américain (.edu) et des services de courrier électronique commerciaux est établie. En novembre 1990 au CERN, deux chercheurs rédigent une proposition concernant le développement d'un système d'hypertexte distribué. Internet est le
support par excellence pour cette nouvelle technologie. C'est de ce travail que naîtra le
WWW (World Wide Web) que nous connaissons aujourd'hui. Signalons que c'est cette
même année que la Suisse est officiellement connectée a Internet (le CERN lui l'était
depuis 1985).
C'est sans aucun doute le WWW et les logiciels de navigation tels que Mosaic qui sont
responsables du succès d'Internet auprès du grand public. Auparavant, pour accéder à
des informations stockées sur des machines lointaines, on utilisait des programmes tels
que "ftp" pour le transfert de fichiers, "vn" pour lire les News, etc. Tous fonctionnent sur
des terminaux texte et sont peu conviviaux. L'immense révolution apportée par le Web
est sa simplicité d'utilisation.
Dès lors, le nombre de machines et d'individus utilisant Internet ne cesse de grandir.
Actuellement, on estime que le nombre de machines augmente de 15% par mois et
qu'avec ce taux de croissance, le nombre d'utilisateurs devrait atteindre celui de la po5
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
pulation humaine aux alentours de 2001. Il est certain que ce ne sont que des projections
et qu'il est peu probable que tous les habitants des pays en voie de développement aient
accès à une machine connectée à Internet d'ici 5 ans, mais elles démontrent bien que le
succès remporté est immense et va grandissant.
Les problèmes posés par cette croissance phénoménale sont nombreux, mais un des
principaux est la puissance exigée par les serveurs. Si l'informatique a longtemps été
axée autours de structures centralisées, de centres de calcul, il n'est plus envisageable
aujourd'hui face à une demande croissant exponentiellement, de tout concentrer en un
point. Les informations délivrées par le WWW sont statiques, et toute transformation
doit être faite par le serveur, ce qui à terme ne sera peut-être plus possible.
Dans ce contexte, il semble que java puisse être une solution à ce problème. Java est né
de travaux de recherche chez SUN pour définir un langage de pilotage d'appareils ménagers. Les contraintes étaient une très grande portabilité, une grande sécurité, des caractéristiques multitâches.
Toutes ces caractéristiques se retrouvent dans les éléments suivants:
•
•
•
•
•
•
•
•
•
Typage fort
Pas de pointeurs explicites
Verrous
Signaux
Moniteurs
Multi-Thread
Langage Objet
Gestion de la mémoire automatique (Garbage collection)
Génération de bytecode plutôt que de code machine natif
C’est sans doute le dernier point qui est le plus intéressant. Du code java compilé sur
une machine pourra donc être exécuté sur n’importe quelle autre plate-forme à l’unique
condition qu’une machine virtuelle java soit disponible pour cette machine. Du code
java transmis dans une page Web va donc être exécuté sur la machine cliente, peu importe sa nature, et par là même pourra effectuer des tâches qui incombaient auparavant
au serveur.
Cette extrême portabilité peut également être exploitée hors du contexte d’Internet pour
créer des applications pouvant fonctionner sur diverses plates-formes sans même avoir à
les recompiler.
Il est évident que le prix à payer pour cette souplesse est relativement important du
point de vue de la vitesse d’exécution, mais de gros progrès sont à faire dans ce domaine (machines virtuelles offrant une compilation Just In Time, un peu comme dans
certaines implémentations de SmallTalk par exemple).
6
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
2. L'outil HyperGOS et les transformations effectuées
HyperGOS est ce que l’on pourrait appeler un "compilateur de compilateurs". En d'autres termes, on fournit à cet outil une grammaire sous forme BNF (Backus-Naur Form).
Au sein de cette grammaire vont être insérées des actions à effectuer lors de l'analyse
d'un code source. Après traitement, HyperGOS va délivrer un compilateur apte à analyser le langage dont on lui a fourni la grammaire et qui effectuera les actions qu'on aura
insérées dans celle-ci.
On devra fournir au compilateur ainsi généré un analyseur lexical capable de décomposer le code à analyser en lexèmes et à en indiquer leurs types (mots réservés, identificateurs, valeurs numériques, etc.). Lors de notre prise en main d'HyperGOS, celui-ci était
écrit en HyperTalk (langage de l'application Hypercard) et après analyse, le compilateur
généré était également en Hypercard. Nous nous sommes donc attachés à le transformer
afin que l'application écrite en HyperTalk génère du code java.
2.1 L’analyseur lexical
Nous avons dans un premier temps écrit l'analyseur lexical indispensable. Les langages
à analyser étant relativement restreints, nous n’avons pas eu à implémenter des fonctionnalités d’analyse lexicale très avancées. HyperGOS n'est pas comparable en termes
de complexité à YACC (Yet Another Compiler Compiler) par exemple. Il n'est pas envisagé de générer un compilateur Pascal, Ada ou C avec cet outil. Mais c'est justement
cette simplicité qui lui permet d'être très flexible et de pouvoir générer de petits compilateurs dans des temps relativement courts (évaluation d’expressions mathématiques,
mini-logo, etc.).
Notre analyseur a été réalisé sous forme d’une classe java. Il offre aux autres classes qui
voudraient l’utiliser une série de méthodes définies comme suit:
public
public
public
public
public
public
String s ()
String t ()
String s0 ()
String t0 ()
void clear (String text)
void next ()
• La méthode s retourne une chaîne contenant le symbole courant (lexème).
• La méthode t retourne une chaîne contenant le type du symbole courant
(unité lexicale).
• La méthode s0 retourne une chaîne contenant le symbole précédent.
• La méthode t0 retourne une chaîne contenant le type du symbole précédent.
• La méthode clear initialise l’analyseur lexical pour la chaîne "text".
• La méthode next fait avancer l’analyseur d’un symbole.
Ces quelques méthodes vont suffire à HyperGOS pour analyser les langages qu'on lui
fournit. Nous n'avons pas lors de l'analyse lexicale, comme dans le cas de compilateurs
complexes, à créer de table de symboles. La phase d'analyse lexicale se contente de parcourir le texte source en repérant les différents lexèmes et en les identifiant.
7
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Nous nous sommes appuyés pour écrire cet analyseur lexical sur une classe standard
fournie avec le kit de développement java: StringTokenizer. Cette classe nous
offre toute une série de facilités pour parcourir un texte et pour en extraire des éléments.
Malheureusement, StringTokenizer ne nous permet pas de connaître de symboles
en avant. Nous avons donc étendu cette classe en lui ajoutant la méthode
whatIsNextToken qui nous fournit le prochain symbole sans pour autant le consommer.
public String whatIsNextToken() {
int c;
String s;
c = currentPosition;
s = nextToken();
currentPosition = c;
return (s);
}
La méthode principale de l'analyseur lexical est la méthode next() qui va trouver le
prochain lexème ainsi que son type. Ainsi si nous avons une instance An de la classe
AnaLex, après un appel à An.next() nous pourrons grâce à An.s() et An.t()
récupérer les données utiles au traitement.
8
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
2.2 HyperGOS
Nous allons ici brièvement décrire l'interface d'HyperGOS ainsi que son fonctionnement.
La génération d'un compilateur se fait en plusieurs étapes (cf. figure 1):
•
•
•
•
•
On introduit la grammaire dans le champ 1.
On vérifie la syntaxe avec le bouton "Syntaxe OK".
On découpe la grammaire en sous-expressions avec le bouton "subexp".
On recherche ensuite les premiers de chaque règle avec le bouton “First of”
On génère le compilateur avec le bouton “generate”.
Le code généré est automatiquement associé au bouton “Compile”, permettant ainsi
d’utiliser immédiatement le compilateur créé.
C hamp
contenanta
l
grammaire à
analyser.
(C hamp 1)
C hamp
contenanta
l
phrase à
l
analyser par e
compilat
eur
généré
Figure 1 - Interface d’HyperGOS
9
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
2.3 Les modifications d'HyperGOS
2.3.1 L'interface
Comme nous l'avons indiqué précédemment, HyperGOS est une application écrite en
HyperTalk (langage natif d'HyperCard) et générant du code HyperTalk. Nous l'avons
adaptée afin qu'elle génère du code java. Sur la figure 2 ci-dessous, nous pouvons déjà
observer que des champs ont été rajoutés. Le champ 1 contient le code java qui va précéder les méthodes qui vont êtres générées à partir de la grammaire (importations, déclarations diverses). Le champ 2 contient le code qui va suivre les méthodes générées
(méthode principale, boot-strap de l'application, etc.). Enfin, le champ 3 contiendra le
code de l'application, c'est à dire le contenu du premier champ, les méthodes générées et
le contenu du deuxième champ.
Les autres éléments de l'interface n'ont pas été modifiés.
Champ 1
Champ 2
Champ 3
Figure 2 - Interface d’HyperGOS générant du java
10
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
2.3.2 La génération de code
Une fois l'interface modifiée, nous avons travaillé sur la génération du code. Les instructions utilisées lors de la génération de code HyperTalk sont relativement communes
(if, while, procédures, etc.).
Prenons par exemple la génération d’un test (if):
Voici la version originale HyperTalk:
wr return&"if s="&s&" then"
Et voici après modification la génération du même test en java:
wr return&"if (An.s().equals("&s&")) {"
Nous avons ainsi substitué toutes les instructions HyperTalk par les instructions correspondantes en java et obtenu, après quelques mises au point, une version d’HyperGOS
générant du code java.
11
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
3. La génération de l'outil javaGOS à partir d'HyperGOS
Une fois HyperGOS modifié pour générer du java, nous nous sommes penchés sur son
utilisation pour générer un outil équivalent mais écrit en java. Cette approche est couramment utilisée en compilation et est nommée bootstrapping. On peut illustrer ce processus par un diagramme dit diagramme en "T" [Tha79]:
(b)
L
A
(a)
BN F
Hyper
Talk
H yper
Talk
H yper
Talk
Nous avons au départ un outil (a) écrit en HyperTalk, acceptant la BNF du langage L en
entrée. En sortie, nous obtenons un outil (b) écrit en HyperTalk, acceptant le langage L
en entrée et effectuant les actions A en sortie.
L
BNF
BNF
Java
Java
A
Java
Java
Hyper
Talk
Sur ce second schéma, nous voyons clairement que nous avons transformé le premier
outil pour qu’il génère du java et qu’ensuite nous l’avons utilisé pour créer un outil écrit
en java et générant du java.
Comme nous l’avons vu précédemment, le fonctionnement d’HyperGOS peut se subdiviser en quatre étapes:
•
•
•
•
Vérification de la syntaxe avec le bouton "Syntaxe OK".
Découpage de la grammaire en sous-expressions avec le bouton "subexp".
Recherche des premiers de chaque règle avec le bouton "First of"
Génération du compilateur avec le bouton "generate".
Il faudra donc décrire pour chacune de ces étapes un langage capable d'analyser le code
source fourni et insérer des actions effectuant les modifications nécessaires.
12
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
3.1 Vérification de la syntaxe
Cette première étape vérifie que la grammaire entrée est syntaxiquement correcte. Pour
décrire des langages de manière formelle, nous utilisons le formalisme BNF. BNF étant
lui même un langage, il peut donc se définir lui-même. Voici cette définition [Wir82]:
syntaxe
regle
expression
terme
facteur
= {regle}.
= identificateur "=" expression ".".
= terme {"|" terme}.
= facteur {facteur}.
= identificateur
|
chaine
|
"("expression")" | "["expresion"]" |
"{"expression"}".
Si nous fournissons cette grammaire à HyperGOS, nous aurons en sortie un outil capable de parcourir une grammaire BNF et d'indiquer si elle est correcte ou non. Notons
deux points :
• HyperGOS utilise les caractères "{" et "}" pour définir des actions. Nous
remplacerons donc ceux-ci par "<" et ">" lors de l'écriture d'une grammaire.
• La grammaire ci-dessus doit être étendue pour pouvoir supporter la définition
des actions.
En définitive, nous soumettrons donc la grammaire suivante à HyperGOS:
startrule = < nterm "=" exp "." > .
exp = term < "|" term > .
term = factor < factor > .
factor = ( nterm )
| ( sterm )
| ( aterm )
| ( "<" exp ">" )
| ( "[" exp "]" )
| ( "(" exp ")" ) .
sterm = "Q" .
nterm = "N" .
aterm = "A" .
3.2 Découpage en sous-expressions
La seconde étape consiste à découper la grammaire en sous-expressions. Pour des raisons de facilité, HyperGOS transforme les expressions telles que:
a=b{c{d}}.
en
a=b{a_1}.
a_1=c{a_2}.
a_2=d.
13
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Ces deux formes sont entièrement équivalentes, mais ce découpage nous prépare le travail pour les phases suivantes.
Ce traitement peut être effectué en ajoutant des actions à la grammaire que nous avons
vue précédemment:
startrule = <
exp = term
%{rulename = An.symb();}
nterm "=" exp
%{Text2.appendText("\n");}
"." > .
< "|" term > .
term = factor < factor > .
factor =
|
|
|
( nterm )
( sterm )
( aterm )
(
%{int pos;}
%{Sub += 1;}
%{Text2.addText(rulename+"\u005F"+Sub);}
%{pos = Text2.curPos();}
%{Text2.appendText("\n"+rulename+"\u005F"+Sub+"=");}
"<" exp
%{Text2.addText(".");}
%{Text2.replaceText("", Text2.curPos()-2,
Text2.curPos()-1);}
%{Text2.textPos(pos);}
%{Text2.addText(An.s);}
">" )
| (
%{int pos;}
%{Sub += 1;}
%{Text2.addText(rulename+"\u005F"+Sub);}
%{pos = Text2.curPos();}
%{Text2.appendText("\n"+rulename+"\u005F"+Sub+"=");}
"[" exp
%{Text2.addText(".");}
%{Text2.replaceText("", Text2.curPos()-2,
Text2.curPos()-1);}
%{Text2.textPos(pos);}
%{Text2.addText(An.s);}
"]" )
| (
%{int pos;}
%{Sub += 1;}
%{Text2.addText(rulename+"\u005F"+Sub);}
%{pos = Text2.curPos();}
%{Text2.appendText("\n"+rulename+"\u005F"+Sub+"=");}
"(" exp
14
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
%{Text2.addText(".");}
%{Text2.replaceText("", Text2.curPos()-2,
Text2.curPos()-1);}
%{Text2.textPos(pos);}
%{Text2.addText(An.symb);}
")" ) .
sterm = "Q" .
nterm = "N" .
aterm = "A" .
Nous voyons que les traitements pour "<", "(" et "[" sont similaires. Nous allons
détailler uniquement le premier de ceux-ci ("<"):
• On crée une variable locale.
• On incrémente une variable globale qui compte le nombre de sous-expressions crées.
• On ajoute une chaîne contenant le nom de la nouvelle règle. Ainsi si nous avions au
départ a=<<b>>., nous aurons à ce moment là a=<a_1.
• On sauvegarde la position du curseur.
• On ajoute une ligne et la chaîne a_1=, ce qui nous donne:
a=<a_1
a_1=
• On appelle exp pour continuer la suite de l’analyse. exp va analyser le deuxième
signe “<“ et va rappeler term qui va lui-même rappeler factor. Nous aurons
donc un appel récursif indirect à factor.
• factor va effectuer la même opération que précédemment. La situation est alors la
suivante:
a=<a_1
a_1=<a_2
a_2=
• On appelle à nouveau exp qui à son tour va appeler term qui va appeler factor
qui cette fois va appeler nterm.
a=<a_1
a_1=<a_2
a_2=b
• A la fin de l’exécution de nterm, on va remonter dans la troisième puis dans la
deuxième instance de factor. Nous voyons dans la grammaire que nous ajoutons
un point, nous restaurons la position du curseur et nous refermons les expressions.
15
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
a=<a_1
a_1=<a_2>
a_2=b.
• En remontant dans la première instance de factor, on rajoute à nouveau un point et
un “>“, pour obtenir:
a=<a_1>
a_1=<a_2>.
a_2=b.
• Pour terminer, en parcourant la règle de départ, on ajoute le "." final.
a=<a_1>.
a_1=<a_2>.
a_2=b.
Voilà comment nous avons découpé la grammaire de départ en sous-expressions.
3.3 Recherche des premiers de chaque règle
L’ultime étape avant la génération de code dans HyperGOS consiste à créer une série de
routines capables de rechercher les premiers de chaque règle. L'option choisie dans la
version java est quelque peu différente.
En effet HyperTalk est un langage interprété . Un programme peut donc générer du code
et lancer son exécution. Dans le cas de java, le code doit être compilé au préalable.
Nous avons donc créé une classe java capable de retrouver les premiers de chaque règle.
Notons tout de même deux pistes. La société Netscape a défini pour ses logiciels de
navigation un langage de scriptage appelé javascript. Celui-ci étant interprété, il serait
possible de générer des routines javascript capables de rechercher les premiers de chaque règle. Il devrait également être possible de générer du code java et de le compiler au
cours de l’exécution étant donné que le compilateur est une classe comme une autre.
Nous nous sommes orientés vers une autre méthode pour effectuer cette recherche.
Nous allons de plus profiter de ce traitement pour effectuer d’autres vérifications sur la
grammaire comme suggéré dans [Cun80] (recherche de boucles non-productives et de
récursivité à gauche).
Comme dans le cas de l’analyseur lexical, la recherche des premiers est une classe.
Celle-ci accepte lors de son instantiation une grammaire en entrée. On suppose que cette
grammaire est passée par les deux étapes précédentes et donc qu’elle est syntaxiquement correcte et découpée en sous-expressions.
3.3.1 Création de la table des symboles
La première étape consiste à créer une table des symboles. Les entrées de cette table
sont définies comme suit:
class SymbolTableEntry {
16
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
SymbolTableEntry (String n, String t, int nb) {
name = n;
type = t;
number = nb;
}
String name;
Hashtable RightPart;
String type;
String prod;
boolean removed;
int number;
}
Elles permettent donc de stocker les éléments suivants:
• Un nom (name)
• Un numéro d’ordre (number)
• La partie droite du symbole (RightPart)
• Un drapeau (removed)
• Une chaîne (prod) indiquant si le symbole produit un terminal ou pas
• Le type du symbole (type)
La grammaire va être parcourue une première fois pour remplir la table des symboles.
C’est la méthode fillSymbolTable qui va effectuer cette tâche.
Notons que les symboles ne sont pas numérotés dans l’ordre dans lequel ils apparaissent
mais selon le schéma suivant:
Symboles non-terminaux
Symboles terminaux
0
n
<---------------------------------|----------------------->
Cette numérotation est nécessaire à l’algorithme de recherche des premiers.
3.3.2 Recherche des parties droites de chaque symbole
La grammaire est composée de règles. Chaque règle doit commencer par un nonterminal. La partie droite d’un non-terminal est l’ensemble des symboles présents à
droite du signe “=“.
En balayant la grammaire une seconde fois, on est à même de déterminer les parties
droites des divers symboles.
3.3.3 Vérification de la grammaire
3.3.3.1 Recherche des symboles pouvant produire la chaîne vide
17
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Nous devons ensuite déterminer quels symboles non-terminaux peuvent produire la
chaîne vide. L’algorithme utilisé est le suivant:
On parcourt la grammaire à la recherche d’un symbole a pouvant générer la chaîne
vide(a=<b>. p.ex.). Lorsque celui-ci se présente, toutes les règles ayant a en partie
gauche sont effacées de la liste en positionnant le drapeau removed, et a est effacé de
toutes les parties droites des autres symboles. Cette opération ayant pu produire de nouveaux symboles ayant une partie droite vide, on répétera donc cette séquence autant de
fois que nécessaire.
3.3.3.2 Recherche des boucles non-productives
Après avoir enlevé tous les symboles pouvant produire la chaîne vide, il ne devrait subsister dans la grammaire que des symboles produisant des terminaux. Il peut malgré tout
subsister des boucles non-productives du type:
d=e.
e=d.
où d et e sont non-productifs.
Pour vérifier cela, on va chercher tout les symboles dont la partie droite contient au
moins un symbole terminal (a=“A”. p.ex.). Le symbole a étant productif, toutes les
règles ayant a en partie gauche sont éliminées et a est remplacé dans toutes les parties
droites des autres symboles par un terminal (peu importe lequel).
En répétant cette opération autant de fois que nécessaire, on aura retrouvé tous les symboles productifs. Si à ce stade il reste des symboles dans la liste, c’est qu’il y a une ou
plusieurs boucles non-productives dans la grammaire.
18
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
3.3.4 Recherche des premiers
La dernière étape est la recherche des symboles premiers proprement dite. On va créer
une matrice booléenne de la forme suivante:
non-term.
non-term
|term.
ABCDE......Z|abcde......z
A.
B .
C .
D
.
E
.
.
.
. diagonale
. principale
.
.
.
.
.
.
Z
.
La matrice est initialisée comme suit:
• Une case vaut 1 si le symbole associé à la colonne est "premier direct" pour le
non-terminal associé à la ligne.
Les "premiers directs" sont déterminés de la manière suivante:
prenons a=bcd|ef.
Dans ce cas, b et e sont les premiers directs. Mais si b peut produire la chaîne vide, c
est également premier direct de a.
Après avoir rempli la matrice, on effectue la fermeture transitive de la matrice des symboles premiers directs en appliquant l’algorithme ci-dessous:
Pour chaque ligne I de la matrice on effectue les opérations suivantes:
• Pour toute valeur 1 située dans la zone de gauche (non-terminaux), on repère
le nom de la colonne, par exemple J.
• La ligne complète correspondant à J est ajoutée (opération logique “ou”) à la
ligne I, ce qui peut créer de nouvelles valeurs 1 dans la zone de gauche.
Si à un moment quelconque un 1 apparaît dans la diagonale principale, c’est que la
grammaire est récursive à gauche, ce qui n’est pas souhaitable et doit être signalé.
Une fois le traitement terminé, pour chaque ligne associée à un non-terminal, nous pouvons connaître les terminaux qui sont premiers.
19
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
3.4 Génération de code
La génération de code se fait par une descente récursive de la grammaire. Le but est
alors de modéliser le comportement des différents éléments de la BNF par des instructions java.
Le premier élément que nous trouvons lorsque nous examinons une grammaire est une
règle. Cette règle se compose comme nous l’avons vu précédemment d’un non-terminal,
du signe ‘=‘, d’une expression et pour finir d’un point.
Nous modéliserons chaque règle par une méthode ayant le même nom que le symbole
en partie gauche.
Chaque expression est une suite de termes sous la forme term<"|"term>. Nous ne
pouvons suivre le chemin term que si le symbole courant est premier de la règle courante. Nous allons donc mettre en place un test:
if (An.s().equals(first1)||An.s.equals(first2).... {
...
}
while An.s.equals("|") {
if (An.s().equals(first1)||An.s.equals(first2)....) {
...
}
}
Chaque term est une suite d’un ou plusieurs facteurs. term = factor <factor>.
On ne générera donc pas de code, toute la prise en charge étant faite dans factor.
Le dernier élément est ainsi factor. Celui-ci analyse les types des symboles qui lui
sont soumis et génère le code en conséquence.
A ce stade, les différents symboles sont les suivants:
•
•
•
•
•
un terminal
un non-terminal
un "<"
un "["
une action
On ne peut plus trouver de parenthèses car elles ont été éliminées lors du découpage en
sous-expressions.
Un terminal devra simplement générer le passage au symbole suivant, c’est-à-dire un
test:
if (An.s().equals("terminal") {
An.next();
}
Un non-terminal générera un appel à la méthode du même nom:
20
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
non-terminal();
Un “<exp>“ indique une répétition de l’expression zéro ou plusieurs fois. Nous allons
devoir boucler tant que le symbole courant est un terminal de la règle courante:
while (An.s().equals(first1)||An.s.equals(first2)....) {
...
}
Le code à l’intérieur de la boucle va être généré par un appel récursif à exp.
Un “[exp]” indique un passage zéro ou une fois dans l’expression. Le code généré
sera donc un test unique:
if (An.s().equals(first1)||An.s.equals(first2)....) {
...
}
Enfin, les actions seront recopiées directement.
Si l’on a %{System.err.(“Erreur fatale”);} le code généré sera:
System.err.(“Erreur fatale”);
21
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
4. Interface de javaGOS
Nous avons mis en place pour javaGOS l’interface la plus simple possible. Nous ne
retrouvons que 4 zones de texte ainsi qu’un bouton servant à lancer le processus
d’analyse et de génération de code.
Zone
contenantla
grammair
e
originale.
Zone
contenantla
grammair
e
découpée en
sousexpressions.
Zone
contenantle
code généré.
Zone
d’affichage de
messages.
Figure 3 - Interface de javaGOS
4.1 Concept d’une page HTML
Les documents accessibles sur le WWW sont écrits au format HTML (HyperText Markup Language). La mise en forme du texte et des divers éléments contenus dans une
page se fait au moyen de balises. Prenons par exemple le texte suivant:
javaGOS = Generateur d’Outils Syntaxiques
Pour obtenir le même type d’effets en HTML, nous écrirons:
javaGOS = <B>Generateur</B> <U>d’Outils>/U> <I>Syntaxiques</I>
Nous voyons bien là ce que sont ces balises. <B> signifie que le texte qui va suivre apparaîtra en gras alors que </B> indique que la mise en gras est terminée. Le raisonnement est le même pour <U> et <I> qui signifient respectivement souligné et italique.
22
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Dans notre cas, une balise spéciale indique qu’une applet java doit être insérée dans la
page. Il s’agit de la balise <APPLET>. Sa syntaxe est la suivante:
'<' 'APPLET'
['CODEBASE' '=' codebaseURL]
'CODE' '=' appletFile
['ALT' '=' alternateText]
['NAME' '=' appletInstanceName]
'WIDTH' '=' pixels 'HEIGHT' '=' pixels
['ALIGN' '=' alignment]
['VSPACE' '=' pixels] ['HSPACE' '=' pixels]
'>'
['<' 'PARAM' 'NAME' '=' appletAttribute1 'VALUE' '=' value
'>']
['<' 'PARAM' 'NAME' '=' appletAttribute2 'VALUE' '=' value
'>']
...
[alternateHTML]
'</APPLET>'
Nous voyons que comme toute balise, elle a la forme <APPLET>...</APPLET>. Les
paramètres disponibles sont les suivants:
CODEBASE =
Paramètre optionnel. Il indique le chemin d’accès à
l’applet si celui-ci est différent de celui de la page
courante.
CODE =
Nom de l’applet à charger.
ALT =
Paramètre optionnel. Texte à afficher par les logiciels de navigation supportant la balise <APPLET>
mais ne pouvant pas exécuter de java.
NAME =
Paramètre optionnel. Attribue un nom précis à
l’instance de l’applet qui va être créée permettant
ainsi à deux applets résidant dans la même page de
s'identifier.
WIDTH =
Valeur en pixels de la largeur de la zone
d’affichage à réserver pour l’applet lors de la mise
en page.
HEIGHT =
Valeur en pixels de la hauteur de la zone
d’affichage à réserver pour l’applet lors de la mise
en page.
ALIGN =
Paramètre optionnel. Alignement de l’applet dans
la page (gauche, droit, haut, bas, etc.).
VSPACE =
Paramètre optionnel. Taille en pixels d’une zone
libre qui doit être réservée au dessus et au dessous
de l’applet.
23
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Paramètre optionnel. Taille en pixels d’une zone
libre qui doit être réservée à gauche et à droite de
l’applet.
HSPACE =
<PARAM NAME = VALUE =..> Paramètre optionnel. Paramètre devant être passé à
l’applet lors de son lancement. Celle-ci pourra s’en
saisir au moyen de la méthode getParameter().
4.2 Concept d’une Applet
L’intérêt de java dans le cadre d’Internet est de pouvoir transmettre du code exécutable
à l'intérieur de documents statiques, autrement dit dans des pages HTML. Un objet
standard appelé Applet a été défini par SUN pour cet usage. Cet objet inclus une série
de méthodes nécessaires à son exécution et à son interaction avec son environnement
(en l’occurrence le logiciel de navigation).
Ces méthodes sont les suivantes:
public void init() {
}
public void start() {
}
public void stop() {
}
public void destroy() {
}
public void paint (Graphics g) {
}
Elles permettent au logiciel de navigation de contrôler le comportement de l’applet.
L’ordre et les conditions d’appel sont les suivants:
init()
Cette méthode n’est appelée qu’une fois, pour réaliser
l’initialisation de l’applet.
start()
Cette méthode est appelée après l’initialisation ou lorsque
l’applet redevient visible.
stop()
Cette méthode est appelée lorsque l’applet n’est plus visible.
destroy()
Cette méthode est appelée lors de l’arrêt du logiciel de navigation ou lorsqu’un rechargement a été demandé.
paint(Graphics g) Cette méthode permet au logiciel de navigation de demander
à l’applet de reconstruire sa zone d’affichage g.
24
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
5. Exemple de génération d’un compilateur
Pour valider les différentes étapes, nous avons généré de petits compilateurs. Nous allons exposer ici la démarche nécessaire à la génération de l’un d’eux.
La grammaire employée est la suivante:
startrule = exp %{System.out.println(pile.pop());} .
exp = term < ( "+" term %pile.addx(); ) | ( "-" term
%pile.subx(); ) > .
term = factor [ ( "*" factor %pile.mulx(); ) |
( "/" factor %pile.divx(); ) ] .
factor = number | "(" exp ")" .
number = "VAL" %{pile.push((new
Float(An.t0())).floatValue());} .
Il s’agit de l’évaluation d’une expression mathématique. La première étape a donc été
d’écrire la grammaire puis de définir les actions à effectuer. Celles-ci sont au nombre de
6. Elles sont implémentées dans une classe externe pile.
addx()
Dépilement et addition des 2 nombres au sommet de la pile et
empilement du résultat.
subx()
Dépilement et soustraction des 2 nombres au sommet de la pile et
empilement du résultat.
mulx()
Dépilement et multiplication des 2 nombres au sommet de la pile
et empilement du résultat.
divx()
Dépilement et division des 2 nombres au sommet de la pile et
empilement du résultat.
pop()
Dépilement d’un nombre.
push(Float f)
Empilement d’un nombre.
En introduisant cette grammaire dans notre outil, nous allons obtenir une série de routines qui vont nous permettre d’analyser une expression. En fin d’analyse, nous aurons le
résultat au sommet de la pile. Nous ne présentons pas ici le code généré car celui-ci est
relativement grand et difficile à lire.
La dernière étape avant de pouvoir utiliser l’outil est de lui fournir une interface. A cet
effet, nous avons réutilisé le code employé pour l’interface de javaGOS. Nous disposons alors d’une zone de texte pour saisir l’expression et d’un bouton pour en démarrer
l’analyse. Le résultat est affiché sur stdout c’est-à-dire la console de sortie standard.
25
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Figure 4 - Exemple d’outil généré. Evaluation d’une expression.
26
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
6. Conclusion
Au départ, nous ne voulions que modifier l’outil HyperGOS afin qu’il génère du java.
En le portant entièrement en java, nous avons supprimé sa dépendance envers sa plateforme de départ, le Macintosh. Nous avons ainsi rendu cet outil accessible à travers Internet par toute personne à l’unique condition qu’elle possède un logiciel de navigation
capable d’exécuter du bytecode java.
Ce type de distribution de logiciels est complètement nouveau et pourtant il y a déjà une
multitude de produits écrits en java à disposition. Citons parmi eux:
Jell .......... http://www.blackdown.org/~kbs/jell.html
CUP........ http://www.cc.gatech.edu/gvu/people/Faculty/hudson/java_cup/home.html
jax........... http://www.blackdown.org/~kbs/jax.html
JavaLex .. http://www.cs.princeton.edu/~ejberk/JavaLex/JavaLex.html
CUP est un générateur d’analyseurs syntaxiques basé sur une technique ascendante
(LALR). Sa philosophie et son fonctionnement sont très similaires à ceux de YACC.
Jell est également un générateur d’analyseurs syntaxiques mais il produit des analyseurs
en descente récursive et s’applique aux langages LL(1), comme javaGOS.
Jax et JavaLex quant à eux sont des outils similaires à Lex, capables de générer des
analyseurs lexicaux.
A la différence de javaGOS, Jell et CUP ne sont pas des applets mais des applications
java. Celles-ci ne fonctionnent donc pas directement dans un logiciel de navigation mais
doivent être chargées sur la machine locale puis exécutées depuis un interprèteur de
commandes.
Ainsi nous voyons javaGOS comme complémentaire plutôt que concurrent à ces autres
programmes.
Son portage n'a pas été un problème majeur. Les quelques difficultés rencontrées ont été
principalement dûes à certains aspects spécifiques d'HyperCard.
Le langage java nous a relativement séduit. Il mêle les caractéristiques rigoureuses de
certains langages temps-réel à un langage objet. Nous pensons qu'une fois arrivé à maturité java rencontrera un succès réel, bien au delà du phénomène de mode qui l'entoure
actuellement.
Pour finir, il y a encore diverses améliorations possibles pour javaGOS. Citons parmi
celles-ci:
• L'interface qui est actuellement un peu fruste. Un habillage plus élégant serait le
bienvenu.
• L'analyse de la grammaire pourrait aussi être améliorée. Toute erreur provoque
l'arrêt complet du processus. Un analyseur plus sophistiqué pourrait être à même
de récupérer en cas d'erreur évitant ainsi d'avoir à analyser la grammaire N+1
fois si l'on a N erreurs.
27
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Annexe 1 - Code
Attention, une grande partie de ce code a été généré automatiquement. Il n’est donc pas
indenté et par conséquent peu lisible.
1.1 Vérification de la syntaxe
import java.util.*;
import java.awt.*;
public class Syntax {
newTextArea Messages;
AnaLex An;
Syntax (newTextArea t, String gram) {
Messages = t;
An = new AnaLex();
An.clear(gram);
An.next();
}
public void startrule () throws subException{
echo("\nEnter in startrule");
// début de la règle
while ((false)|(An.s().equals("N"))) {
if ((false)|(An.s().equals("N"))) {
startrule_2();
} // end if exp
} // end <>
} // fin de la règle startrule
public void startrule_2 () throws subException{
echo("\nEnter in startrule_2");
// début de la règle
if ((false)|(An.s().equals("N"))) {
nterm();
if (An.s().equals("=")) {
An.next();
}
else {
error("startrule_2","T","=");
} // end if factor
exp();
if (An.s().equals(".")) {
An.next();
}
else {
28
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
error("startrule_2","T",".");
} // end if factor
} // end if exp
} // fin de la règle startrule_2
public void exp () throws subException{
echo("\nEnter in exp");
// début de la règle
if
((false)|(An.s().equals("N"))|(An.s().equals("Q"))|(An.s().
equals("A"))|(An.s().equals("<"))|(An.s().equals("["))|(An.
s().equals("("))) {
term();
while ((false)|(An.s().equals("|"))) {
if ((false)|(An.s().equals("|"))) {
exp_4();
} // end if exp
} // end <>
} // end if exp
} // fin de la règle exp
public void exp_4 () throws subException{
echo("\nEnter in exp_4");
// début de la règle
if (An.s().equals("|")) {
An.next();
}
else {
error("exp_4","T","|");
} // end if factor
term();
} // fin de la règle exp_4
public void term () throws subException{
echo("\nEnter in term");
// début de la règle
if
((false)|(An.s().equals("N"))|(An.s().equals("Q"))|(An.s().
equals("A"))|(An.s().equals("<"))|(An.s().equals("["))|(An.
s().equals("("))) {
factor();
while
((false)|(An.s().equals("N"))|(An.s().equals("Q"))|(An.s().
equals("A"))|(An.s().equals("<"))|(An.s().equals("["))|(An.
s().equals("("))) {
if
((false)|(An.s().equals("N"))|(An.s().equals("Q"))|(An.s().
29
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
equals("A"))|(An.s().equals("<"))|(An.s().equals("["))|(An.
s().equals("("))) {
term_6();
} // end if exp
} // end <>
} // end if exp
} // fin de la règle term
public void term_6 () throws subException{
echo("\nEnter in term_6");
// début de la règle
if
((false)|(An.s().equals("N"))|(An.s().equals("Q"))|(An.s().
equals("A"))|(An.s().equals("<"))|(An.s().equals("["))|(An.
s().equals("("))) {
factor();
} // end if exp
} // fin de la règle term_6
public void factor () throws subException{
echo("\nEnter in factor");
// début de la règle
if ((false)|(An.s().equals("N"))) {
factor_8();
}
else {
if ((false)|(An.s().equals("Q"))) {
factor_9();
}
else {
if ((false)|(An.s().equals("A"))) {
factor_10();
}
else {
if ((false)|(An.s().equals("<"))) {
factor_11();
}
else {
if ((false)|(An.s().equals("["))) {
factor_12();
}
else {
if ((false)|(An.s().equals("("))) {
factor_13();
} // end if exp
} // end if exp
} // end if exp
} // end if exp
30
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
} // end if exp
} // end if exp
} // fin de la règle factor
public void factor_8 () throws subException{
echo("\nEnter in factor_8");
// début de la règle
if ((false)|(An.s().equals("N"))) {
nterm();
} // end if exp
} // fin de la règle factor_8
public void factor_9 () throws subException{
echo("\nEnter in factor_9");
// début de la règle
if ((false)|(An.s().equals("Q"))) {
sterm();
} // end if exp
} // fin de la règle factor_9
public void factor_10 () throws subException{
echo("\nEnter in factor_10");
// début de la règle
if ((false)|(An.s().equals("A"))) {
aterm();
} // end if exp
} // fin de la règle factor_10
public void factor_11 () throws subException{
echo("\nEnter in factor_11");
// début de la règle
if (An.s().equals("<")) {
An.next();
}
else {
error("factor_11","T","<");
} // end if factor
exp();
if (An.s().equals(">")) {
An.next();
}
else {
error("factor_11","T",">");
} // end if factor
} // fin de la règle factor_11
public void factor_12 () throws subException{
echo("\nEnter in factor_12");
31
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
// début de la règle
if (An.s().equals("[")) {
An.next();
}
else {
error("factor_12","T","[");
} // end if factor
exp();
if (An.s().equals("]")) {
An.next();
}
else {
error("factor_12","T","]");
} // end if factor
} // fin de la règle factor_12
public void factor_13 () throws subException{
echo("\nEnter in factor_13");
// début de la règle
if (An.s().equals("(")) {
An.next();
}
else {
error("factor_13","T","(");
} // end if factor
exp();
if (An.s().equals(")")) {
An.next();
}
else {
error("factor_13","T",")");
} // end if factor
} // fin de la règle factor_13
public void sterm () throws subException{
echo("\nEnter in sterm");
// début de la règle
if (An.s().equals("Q")) {
An.next();
}
else {
error("sterm","T","Q");
} // end if factor
} // fin de la règle sterm
public void nterm () throws subException{
echo("\nEnter in nterm");
// début de la règle
32
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
if (An.s().equals("N")) {
An.next();
}
else {
error("nterm","T","N");
} // end if factor
} // fin de la règle nterm
public void aterm () throws subException{
echo("\nEnter in aterm");
// début de la règle
if (An.s().equals("A")) {
An.next();
}
else {
error("aterm","T","A");
} // end if factor
} // fin de la règle aterm
public void error (String rule, String typeSymbol, String
lexem) throws subException{
echo("\nErreur: "+rule+'|'+typeSymbol+'|'+lexem+"\n");
throw new subException("Erreur de syntaxe");
} /* error() */
public void echo (String x) {
Messages.appendText(x);
} /* echo(String x) */
} /* Syntax */
1.2 Découpage en sous-expressions
import java.util.*;
import java.awt.*;
public class subexp {
newTextArea Text2, Messages;
AnaLexs An = new AnaLexs();
int Sub = 0;
String rulename;
subexp (newTextArea msg, newTextArea out, String grammar) {
Messages = msg;
Text2 = out;
33
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
An.clear(grammar);
An.next();
Text2.setText(An.symb());
}
public void startrule () throws subException{
echo("\nEnter in startrule");
// début de la règle
while ((false)|(An.s().equals("N"))) {
if ((false)|(An.s().equals("N"))) {
startrule_2();
} // end if exp
} // end <>
} // fin de la règle startrule
public void startrule_2 () throws subException{
echo("\nEnter in startrule_2");
// début de la règle
rulename = An.symb();
nterm();
if (An.s().equals("=")) {
An.next();
if
((An.s0().equals("N")||An.s0().equals("Q")||An.s0().equals(
")"))
&&(An.s().equals("N")||An.s().equals("Q")||An.s().equals(")
"))) {
Text2.addText(" ");
}
if (An.s().equals("(")||An.s().equals(")")) {
Text2.addText(" ");
}
else {
Text2.addText(An.symb());
}
}
else {
error("startrule_2","T","=");
} // end if factor
exp();
Text2.appendText("\n");
if (An.s().equals(".")) {
An.next();
if
((An.s0().equals("N")||An.s0().equals("Q")||An.s0().equals(
")"))
34
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
&&(An.s().equals("N")||An.s().equals("Q")||An.s().equals(")
"))) {
Text2.addText(" ");
}
if (An.s().equals("(")||An.s().equals(")")) {
Text2.addText(" ");
}
else {
Text2.addText(An.symb());
}
}
else {
error("startrule_2","T",".");
} // end if factor
} // fin de la règle startrule_2
public void exp () throws subException{
echo("\nEnter in exp");
// début de la règle
if
((false)|(An.s().equals("N"))|(An.s().equals("Q"))|(An.s().
equals("A"))|(An.s().equals("<"))|(An.s().equals("["))|(An.
s().equals("("))) {
term();
while ((false)|(An.s().equals("|"))) {
if ((false)|(An.s().equals("|"))) {
exp_4();
} // end if exp
} // end <>
} // end if exp
} // fin de la règle exp
public void exp_4 () throws subException{
echo("\nEnter in exp_4");
// début de la règle
if (An.s().equals("|")) {
An.next();
if
((An.s0().equals("N")||An.s0().equals("Q")||An.s0().equals(
")"))
&&(An.s().equals("N")||An.s().equals("Q")||An.s().equals(")
"))) {
Text2.addText(" ");
}
if (An.s().equals("(")||An.s().equals(")")) {
Text2.addText(" ");
35
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
}
else {
Text2.addText(An.symb());
}
}
else {
error("exp_4","T","|");
} // end if factor
term();
} // fin de la règle exp_4
public void term () throws subException{
echo("\nEnter in term");
// début de la règle
if
((false)|(An.s().equals("N"))|(An.s().equals("Q"))|(An.s().
equals("A"))|(An.s().equals("<"))|(An.s().equals("["))|(An.
s().equals("("))) {
factor();
while
((false)|(An.s().equals("N"))|(An.s().equals("Q"))|(An.s().
equals("A"))|(An.s().equals("<"))|(An.s().equals("["))|(An.
s().equals("("))) {
if
((false)|(An.s().equals("N"))|(An.s().equals("Q"))|(An.s().
equals("A"))|(An.s().equals("<"))|(An.s().equals("["))|(An.
s().equals("("))) {
term_6();
} // end if exp
} // end <>
} // end if exp
} // fin de la règle term
public void term_6 () throws subException{
echo("\nEnter in term_6");
// début de la règle
if
((false)|(An.s().equals("N"))|(An.s().equals("Q"))|(An.s().
equals("A"))|(An.s().equals("<"))|(An.s().equals("["))|(An.
s().equals("("))) {
factor();
} // end if exp
} // fin de la règle term_6
public void factor () throws subException{
echo("\nEnter in factor");
// début de la règle
if ((false)|(An.s().equals("N"))) {
36
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
factor_8();
}
else {
if ((false)|(An.s().equals("Q")))
factor_9();
}
else {
if ((false)|(An.s().equals("A")))
factor_10();
}
else {
if ((false)|(An.s().equals("<")))
factor_11();
}
else {
if ((false)|(An.s().equals("[")))
factor_12();
}
else {
if ((false)|(An.s().equals("(")))
factor_13();
} // end if exp
} // end if exp
} // end if exp
} // end if exp
} // end if exp
} // end if exp
} // fin de la règle factor
{
{
{
{
{
public void factor_8 () throws subException{
echo("\nEnter in factor_8");
// début de la règle
if ((false)|(An.s().equals("N"))) {
nterm();
} // end if exp
} // fin de la règle factor_8
public void factor_9 () throws subException{
echo("\nEnter in factor_9");
// début de la règle
if ((false)|(An.s().equals("Q"))) {
sterm();
} // end if exp
} // fin de la règle factor_9
public void factor_10 () throws subException{
echo("\nEnter in factor_10");
// début de la règle
37
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
if ((false)|(An.s().equals("A"))) {
aterm();
} // end if exp
} // fin de la règle factor_10
public void factor_11 () throws subException{
echo("\nEnter in factor_11");
// début de la règle
int pos;
Sub += 1;
Text2.addText(rulename+"\u005F"+Sub);
pos = Text2.curPos();
Text2.appendText("\n"+rulename+"\u005F"+Sub+"=");
if (An.s().equals("<")) {
An.next();
if
((An.s0().equals("N")||An.s0().equals("Q")||An.s0().equals(
")"))
&&(An.s().equals("N")||An.s().equals("Q")||An.s().equals(")
"))) {
Text2.addText(" ");
}
if (An.s().equals("(")||An.s().equals(")")) {
Text2.addText(" ");
}
else {
Text2.addText(An.symb());
}
}
else {
error("factor_11","T","<");
} // end if factor
exp();
Text2.addText(".");
Text2.replaceText("", Text2.curPos()-2, Text2.curPos()-1);
Text2.textPos(pos);
Text2.addText(An.s);
if (An.s().equals(">")) {
An.next();
if
((An.s0().equals("N")||An.s0().equals("Q")||An.s0().equals(
")"))
&&(An.s().equals("N")||An.s().equals("Q")||An.s().equals(")
"))) {
Text2.addText(" ");
}
38
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
if (An.s().equals("(")||An.s().equals(")")) {
Text2.addText(" ");
}
else {
Text2.addText(An.symb());
}
}
else {
error("factor_11","T",">");
} // end if factor
} // fin de la règle factor_11
public void factor_12 () throws subException{
echo("\nEnter in factor_12");
// début de la règle
int pos;
Sub += 1;
Text2.addText(rulename+"\u005F"+Sub);
pos = Text2.curPos();
Text2.appendText("\n"+rulename+"\u005F"+Sub+"=");
if (An.s().equals("[")) {
An.next();
if
((An.s0().equals("N")||An.s0().equals("Q")||An.s0().equals(
")"))
&&(An.s().equals("N")||An.s().equals("Q")||An.s().equals(")
"))) {
Text2.addText(" ");
}
if (An.s().equals("(")||An.s().equals(")")) {
Text2.addText(" ");
}
else {
Text2.addText(An.symb());
}
}
else {
error("factor_12","T","[");
} // end if factor
exp();
Text2.addText(".");
Text2.replaceText("", Text2.curPos()-2, Text2.curPos()-1);
Text2.textPos(pos);
Text2.addText(An.s);
if (An.s().equals("]")) {
An.next();
39
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
if
((An.s0().equals("N")||An.s0().equals("Q")||An.s0().equals(
")"))
&&(An.s().equals("N")||An.s().equals("Q")||An.s().equals(")
"))) {
Text2.addText(" ");
}
if (An.s().equals("(")||An.s().equals(")")) {
Text2.addText(" ");
}
else {
Text2.addText(An.symb());
}
}
else {
error("factor_12","T","]");
} // end if factor
} // fin de la règle factor_12
public void factor_13 () throws subException{
echo("\nEnter in factor_13");
// début de la règle
int pos;
Sub += 1;
Text2.addText(rulename+"\u005F"+Sub);
pos = Text2.curPos();
Text2.appendText("\n"+rulename+"\u005F"+Sub+"=");
if (An.s().equals("(")) {
An.next();
if
((An.s0().equals("N")||An.s0().equals("Q")||An.s0().equals(
")"))
&&(An.s().equals("N")||An.s().equals("Q")||An.s().equals(")
"))) {
Text2.addText(" ");
}
if (An.s().equals("(")||An.s().equals(")")) {
Text2.addText(" ");
}
else {
Text2.addText(An.symb());
}
}
else {
error("factor_13","T","(");
} // end if factor
40
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
exp();
Text2.addText(".");
Text2.replaceText("", Text2.curPos()-2, Text2.curPos()-1);
Text2.textPos(pos);
Text2.addText(An.symb);
if (An.s().equals(")")) {
An.next();
if
((An.s0().equals("N")||An.s0().equals("Q")||An.s0().equals(
")"))
&&(An.s().equals("N")||An.s().equals("Q")||An.s().equals(")
"))) {
Text2.addText(" ");
}
if (An.s().equals("(")||An.s().equals(")")) {
Text2.addText(" ");
}
else {
Text2.addText(An.symb());
}
}
else {
error("factor_13","T",")");
} // end if factor
} // fin de la règle factor_13
public void sterm () throws subException{
echo("\nEnter in sterm");
// début de la règle
if (An.s().equals("Q")) {
An.next();
if
((An.s0().equals("N")||An.s0().equals("Q")||An.s0().equals(
")"))
&&(An.s().equals("N")||An.s().equals("Q")||An.s().equals(")
"))) {
Text2.addText(" ");
}
if (An.s().equals("(")||An.s().equals(")")) {
Text2.addText(" ");
}
else {
Text2.addText(An.symb());
}
}
else {
41
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
error("sterm","T","Q");
} // end if factor
} // fin de la règle sterm
public void nterm () throws subException{
echo("\nEnter in nterm");
// début de la règle
if (An.s().equals("N")) {
An.next();
if
((An.s0().equals("N")||An.s0().equals("Q")||An.s0().equals(
")"))
&&(An.s().equals("N")||An.s().equals("Q")||An.s().equals(")
"))) {
Text2.addText(" ");
}
if (An.s().equals("(")||An.s().equals(")")) {
Text2.addText(" ");
}
else {
Text2.addText(An.symb());
}
}
else {
error("nterm","T","N");
} // end if factor
} // fin de la règle nterm
public void aterm () throws subException{
echo("\nEnter in aterm");
// début de la règle
if (An.s().equals("A")) {
An.next();
if
((An.s0().equals("N")||An.s0().equals("Q")||An.s0().equals(
")"))
&&(An.s().equals("N")||An.s().equals("Q")||An.s().equals(")
"))) {
Text2.addText(" ");
}
if (An.s().equals("(")||An.s().equals(")")) {
Text2.addText(" ");
}
else {
Text2.addText(An.symb());
}
42
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
}
else {
error("aterm","T","A");
} // end if factor
} // fin de la règle aterm
public void error (String rule, String typeSymbol, String
lexem) throws subException{
echo("\nErreur: "+rule+'|'+typeSymbol+'|'+lexem+"\n");
throw new subException("Erreur durant le traitement des
sous-expressions");
} /* error() */
public void echo (String x) {
Messages.appendText(x);
} /* echo(String x) */
} /* subexp */
1.3 Recherche des premiers
import java.util.*;
import java.lang.Integer;
class SymbolTableEntry {
SymbolTableEntry (String n, String t, int nb) {
name = n;
type = t;
number = nb;
}
String name;
Hashtable RightPart = new Hashtable();
String type;
String prod = "";
boolean removed;
int number;
}
public class firstof {
Hashtable SymbolTable = new Hashtable(); // General symbols table
43
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Hashtable TTable = new Hashtable();
AnaLexf An = new AnaLexf();
int T;
int NT;
boolean table [][];
String rules;
SymbolTableEntry CurrentRule;
newTextArea Messages;
firstof (String r) throws subException{
rules = r;
An.clear(rules);
fillSymbolTable();
fillRightPart();
checkGrammar();
locateFirsts();
}
public Vector firsts(String symb) {
int row =
((SymbolTableEntry)SymbolTable.get("NT"+symb)).number;
Vector v = new Vector();
for (int i = NT; i < NT+T; i++) {
if (table[row][i]) {
v.addElement(((SymbolTableEntry)TTable.get(new Integer(i))).name);
}
}
return v;
}
String factor2 () {
String temp;
if
(An.s().equals("<")||An.s().equals("[")||An.s().equals("(")
) {
An.next();
temp = An.t()+An.s();
An.next();
An.next();
}
else if (An.t().equals("NT")||An.t().equals("T")) {
temp = An.t()+An.s();
44
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
An.next();
}
else if (An.s().equals(".")) {
temp = An.t()+An.s();
An.next();
}
else {
temp = "";
}
return temp;
} // End factor2
void term2 () {
boolean cont = true;
SymbolTableEntry temp = (SymbolTableEntry) SymbolTable.get(factor2());
table[CurrentRule.number][temp.number] = true;
cont = !(temp.prod.equals("Y"));
while ((!An.s().equals("|"))&&(!An.s().equals("."))) {
temp = (SymbolTableEntry) SymbolTable.get(factor2());
if (cont) {
table[CurrentRule.number][temp.number] = true;
cont = !(temp.prod.equals("Y"));
} // End if
} // End while
} // End term
void locateFirsts () throws subException{
table = new boolean[NT][NT+T];
An.next();
while (!An.s().equals("")) {
if (An.t().equals("NT")) {
CurrentRule = (SymbolTableEntry) SymbolTable.get(An.t()+An.s());
An.next();
if (An.s().equals("=")) {
An.next();
term2();
while (An.s().equals("|")) {
An.next();
term2();
}
if (An.s().equals(".")) {
An.next();
45
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
}
}
}
}
for (int i = 0; i<NT; i++) {
for (int j = 0; j<NT; j++) {
if (table[i][j]) {
table[i][j] = false;
for (int k = 0; k<NT+T; k++) {
boolean temp = table[i][k];
table[i][k] = table[i][k]||table[j][k];
if ((table[i][k] != temp)&&(k<j)) {
j = k;
}
}
if (table[i][i]) {
throw new subException("\nGrammaire recursive
a gauche");
}
}
}
}
}
void checkGrammar () throws subException{
boolean modif = true; // Finding non-productives symbols
while (modif) {
modif = false;
Enumeration e = SymbolTable.elements();
while (e.hasMoreElements()) { // Scan the table
SymbolTableEntry current = (SymbolTableEntry)
e.nextElement();
if ((current.prod.equals("N"))&&(!current.removed))
{ // Can this symbol produce the empty string ?
current.removed = true; // Then mark it as removed.
modif = true;
Enumeration e1 = SymbolTable.elements(); // And
remove it from every right-part.
while (e1.hasMoreElements()) {
SymbolTableEntry curr = (SymbolTableEntry)
e1.nextElement();
curr.RightPart.remove(current.type+current.name);
}
46
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
}
}
e = SymbolTable.elements();
while (e.hasMoreElements()) { // Re-scan the table
SymbolTableEntry current = (SymbolTableEntry)
e.nextElement();
if
((current.RightPart.isEmpty())&&(current.prod.equals("?")))
{ // Is there a symbol with an empty right-part now ?
modif = true;
current.prod = "N"; // Then mark it.
}
else if
((current.RightPart.isEmpty())&&(current.prod.equals("Y")))
{
throw new subException("\nErreur: le symbole
"+current.name+" a ete marque prod. de term. et a une partie droite vide");
}
}
}
modif = true; // Finding productives symbols
while (modif) {
modif = false;
Enumeration e = SymbolTable.elements();
while (e.hasMoreElements()) { // Scan the table
SymbolTableEntry current = (SymbolTableEntry)
e.nextElement();
if ((current.prod.equals("Y"))&&(!current.removed))
{ // Can this symbol produce a terminal symbol ?
current.removed = true; // Then mark it as removed.
modif = true;
Enumeration e1 = SymbolTable.elements(); // And
replace it in every right-part by a terminal symbol.
while (e1.hasMoreElements()) {
SymbolTableEntry curr = (SymbolTableEntry)
e1.nextElement();
if
((curr.RightPart.containsKey(current.type+current.name))&&(
curr.prod.equals("?"))) {
curr.prod = "Y";
}
}
}
}
}
47
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Enumeration e = SymbolTable.elements(); // Finding nonproductives loops
while (e.hasMoreElements()) { // Scan the table
SymbolTableEntry current = (SymbolTableEntry)
e.nextElement();
if (current.type.equals("NT")) {
if (!(current.removed)) {
throw new subException("\nErreur en:
"+current.name+". La grammaire doit contenir des boucles
non-productives");
}
else if
((!current.prod.equals("Y"))&&(!current.prod.equals("N")))
{
throw new subException("\nLe symbole
"+current.name+" "+current.type+" "+current.prod+" demeure
indetermine");
}
}
}
An.clear(rules);
} // End checkGrammar
void fillSymbolTable () throws subException{ // Search
all symbols and place them in the table
An.next();
while (!An.s().equals("")) {
if
((An.t().equals("T")||An.t().equals("NT"))&&(!SymbolTable.c
ontainsKey(An.t()+An.s()))) {
if (An.t().equals("T")) {
T += 1;
}
else {
NT += 1;
}
SymbolTable.put(An.t()+An.s(), new SymbolTableEntry(An.s(), An.t(), 0));
if (An.s0().equals("<")||An.s0().equals("[")) {
((SymbolTableEntry)SymbolTable.get(An.t()+An.s())).prod="N"
;
}
}
An.next();
}
48
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
An.clear(rules);
Enumeration e = SymbolTable.elements();
{
int NT1 = 0;
int T1 = 0;
while (e.hasMoreElements()) {
SymbolTableEntry current = (SymbolTableEntry)
e.nextElement();
if (current.type.equals("NT")) {
current.number=NT1;
NT1 += 1;
}
else if (current.type.equals("T")) {
current.number=T1+NT;
TTable.put(new Integer(current.number), current);
T1 += 1;
}
else {
throw new subException("\nLe symbole
"+current.name+" n'a pas de type");
}
}
}
}
void factor () {
if (An.s().equals("<")||An.s().equals("[")) {
An.next();
CurrentRule.RightPart.put(An.t()+An.s(), An.s());
An.next();
An.next();
}
else if (An.s().equals("(")) {
An.next();
CurrentRule.RightPart.put(An.t()+An.s(), An.s());
if (!CurrentRule.prod.equals("Y")) {
CurrentRule.prod = "?";
}
An.next();
An.next();
}
else if (An.t().equals("NT")) {
CurrentRule.RightPart.put(An.t()+An.s(), An.s());
if
(!CurrentRule.prod.equals("Y")&&!CurrentRule.prod.equals("N
")) {
CurrentRule.prod = "?";
49
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
}
An.next();
}
else if (An.t().equals("T")) {
CurrentRule.RightPart.put(An.t()+An.s(), An.s());
CurrentRule.prod = "Y";
An.next();
}
} // End factor
void term () {
factor(); //Starting term
while ((!An.s().equals("|"))&&(!An.s().equals("."))) {
factor();
}
} // End term
void fillRightPart () {
An.next(); //Starting fillRightPart
while (!An.s().equals("")) {
if (An.t().equals("NT")) {
CurrentRule = (SymbolTableEntry) SymbolTable.get(An.t()+An.s());
An.next();
if (An.s().equals("=")) {
An.next();
term();
while (An.s().equals("|")) {
An.next();
term();
} // End while
if (An.s().equals(".")) {
An.next();
if (CurrentRule.prod.equals("")) {
CurrentRule.prod = "N";
} // End if
} // End if
} // End if
} // End if
} // End while
An.clear(rules);
} // End fillRightPart
} // End firstof
50
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
Annexe 2 - Bibliographie
[Wir82]
Programming in MODULA-2
Niklaus Wirth
Ed. Springer-Verlag - 1982
ISBN 3-540-11674-5
[Tha79]
Conception et implantation de langages de programmation:
Une introduction à la compilation
Daniel Thalmann & Bernard Levrat
Ed. Gaëtan Morin - 1979
ISBN 0-88612-020-9
[Cun80]
Comprendre la Compilation
P. Y. Cunin, M. Griffiths, J. Voiron
Ed. Springer-Verlag - 1980
ISBN 3-540-10327-9
Ces ouvrages nous ont aidé durant ce travail:
Java in a Nutshell
David Flanagan
Ed. O‘Reilly & Associates, Inc - 1996
ISBN 1-56592-183-6
teach yourself Java in 21 days
Laura Lemay and Charles L. Perkins
Ed. Sams.net - 1996
ISBN 1-57521-030-4
Programming with Java!
Tim Ritchey
Ed. New Riders - 1995
ISBN 1-56205-533-X
Compilateurs, principes, techniques et outils
Alfred Aho/Ravi Sethi/Jeffrey Ullman
Ed. InterEditions 1989
ISBN 2-7296-0295-X
Annexe 3 - Glossaire
Bytecode:
Code généré après une compilation mais ne correspondant à aucune
machine existante. Ce code doit donc encore être traduit avant d’être
exécuté.
FTP:
File Transfer Protocol, protocole de transfert de fichiers sur des réseaux de type internet.
51
Ecole d'Ingénieurs de Genève
François Marcos
Session 1996
Travail de diplôme
internet:
Technique d'interconnexion de réseaux principalement basée sur les
protocoles TCP et IP
Internet:
Réseau mondial formé d'une multitude de réseaux locaux interconnectés entre eux.
IP:
Internet Protocol, protocole d'adressage de machines et de fragmentation de paquets utilisé dans les réseaux de type internet. Chaque
machine connectée à un de ces réseaux doit avoir une adresse IP
unique.
Just In Time:
Méthode de compilation. Le code est compilé au moment de l'exécution, "juste à temps".
Multi-Thread:
Possibilité offerte par certains systèmes d’exploitation permettant à
une tâche de lancer des sous-tâches (threads) qui pourront s’exécuter
en parallèle. La majorité des systèmes d’exploitation actuels sont
multi-threads.
TCP:
Transmission Control Protocol, protocole de connexion, de contrôle
d'erreur et de contrôle de flux utilisé dans les réseaux de type internet, au dessus de la couche IP.
World Wide Web: Système d'hypertexte dont les différents documents peuvent être
répartis sur un réseau. Ce système est né au CERN en 1990.
YACC:
Acronyme signifiant Yet Another Compiler Compiler. Cet outil permet de générer des compilateurs pour un langage donné à partir
d'une grammaire. Son histoire est intimement liée à celle d'Unix.
52