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