Commit ed1ca23b authored by Vincent's avatar Vincent

Merge branch 'commentaires-javadoc' into 'master'

Commentaires javadoc

See merge request !13
parents a46b8518 12ee1e34
Compilation
===========
Directives de compilation depuis le repertoire racine du projet :
$ javac -d bin/ source/*.java
Ceci produit les fichiers .class et les posent dans le répertoire bin.
Execution
=========
Pour executer, toujours depuis le repertoire racine :
$ java -cp bin/ CLIP [options]
Les options sont expliquées dans la javadoc et dans le pdf du projet.
Javadoc
=======
Pour générer la javadoc, depuis le repertoire racine :
la javadoc publique (seuls les attributs et méthodes publiques):
$ javadoc -d doc/ -encoding "utf-8" -docencoding "utf-8" -charset "utf-8" source/*.java
la javadoc complète (toutes les méthodes et tous les attributs documentés):
$ javadoc -private -d doc/ -encoding "utf-8" -docencoding "utf-8" -charset "utf-8" source/*.java
La javadoc se trouve alors dans le répertoire doc/.
Il suffit d'ouvrir le fichier index.html avec n'importe quel navigateur web.
class AnalyseCaractere {
/**
* Classe d'analyse alphabétique.
*
* <p>
* Cette classe fait l'analyse alphabétique de la chaîne de compilation.
* Elle s'assure que tous les caractères utilisés dans l'expression sont valides.
* Elle transforme la chaîne de caractères en liste de lexèmes.
* </p>
*
* <p>
* Elle n'analyse pas tout d'un bloc mais en flux tendu : elle produit les lexèmes au fur et à mesure qu'on lui demande.
* Ceci permet de faire le traitement en une seule passe.
* </p>
*/
public class AnalyseCaractere {
/** la chaîne à analyser. */
private String chaine;
/** l'indice de travail dans la chaîne de caractère.
* C'est l'indice du prochain lexème à fournir.
*/
private int indice;
private Lexeme next; // celui en cours de lecture. On ne l'a pas encore lu mais on peut connaitre son type.
private boolean finChaine; // true si on est à la fin de la chaine ou dans la partie commentaire.
/**
* lexème en cours de lecture.
* On l'a généré mais on ne l'a pas encore consommé.
*/
private Lexeme next;
/**
* Indique si on est en fin d'analyse.
* finChaine vaut <code>true</code> si on a atteint la partie commentaire
* ou si on a atteint la fin de la chaîne.
*/
private boolean finChaine;
/**
* Création de l'objet d'analyse de caractère.
*
* @param cmd la chaîne à analyser
*/
public AnalyseCaractere(String cmd) {
chaine = cmd;
indice = 0;
......@@ -15,6 +51,16 @@ class AnalyseCaractere {
/**
* Retourne le type du prochain lexème.
* <p>
* Cette méthode ne consomme pas le lexème mais indique simplement son type.
* </p>
*
* @return le type du prochain lexème
*
* @throws AnalyseException si le prochain caractère n'est pas autorisé par le langage
*/
public int getNextLexemeType() throws AnalyseException {
if (next == null)
next = getNextLexeme();
......@@ -24,6 +70,16 @@ class AnalyseCaractere {
}
/**
* Retourne le prochain lexème.
* <p>
* Cette méthode consomme le lexème renvoyé.
* </p>
*
* @return le prochain lexème analysé
*
* @throws AnalyseException si le prochain caractère n'est pas autorisé par le langage
*/
public Lexeme getNextLexeme() throws AnalyseException {
Lexeme lex = null;
if (next != null) {
......@@ -38,6 +94,8 @@ class AnalyseCaractere {
lex = new Lexeme(Lexeme.INT,lastChar);
if ('a' <= lastChar && lastChar <= 'z')
lex = new Lexeme(Lexeme.IDF, lastChar);
if ('A' <= lastChar && lastChar <= 'Z')
lex = new Lexeme(Lexeme.MAJ, lastChar);
switch(lastChar) {
case '+': lex = new Lexeme(Lexeme.ADD); break;
case '-': lex = new Lexeme(Lexeme.SUB); break;
......@@ -53,14 +111,6 @@ class AnalyseCaractere {
case '\t':
case ' ': lex = new Lexeme(Lexeme.ESP); break;
case ';': lex = new Lexeme(Lexeme.END); finChaine = true; break;
case 'I': case 'N': case 'T': // INT
case 'F': case 'R': case 'A': case 'C': // FRAC
case 'P': case 'O': case 'L': case 'Y': // POLY
case 'S': // ANS
case 'D': case 'E': case 'G': // DEG
case 'Q': case 'U': // QUO
case 'M': // REM
lex = new Lexeme(Lexeme.TYP, lastChar);
}
if (lex == null) throw new AnalyseException("caractère " + lastChar + " non autorisé.");
} else {
......
class AnalyseException extends Exception {
/**
* Classe permettant de générer une erreur d'analyse.
* <p>
* Elle est utilisée par toutes les analyses du projet.
* </p>
*/
public class AnalyseException extends Exception {
AnalyseException(String msg) {
/**
* Constructeur de l'exception.
*
* @param msg message d'erreur pour décrire le problème
*/
public AnalyseException(String msg) {
super(msg);
}
......
class AnalyseLexicale {
/**
* Classe d'analyse lexicale.
*
* <p>
* Son rôle est de concaténer les lexèmes (lus depuis un analyseur alphabétique) de même type ensemble :
* les chiffres avec les chiffres, les lettres d'identifiant ou majuscules ensemble.
* Elle supprime aussi les caractères non significatifs comme les espaces et les tabulations.
* </p>
*
* <p>
* Elle est basé sur un automate déterministe à quatre états :
* <ul>
* <li> un état <code>CARACT</code> pour les caractères non-spéciaux</li>
* <li> un état <code>NOMBRE</code> pour les caractères numériques</li>
* <li> un état <code>NOMVAR</code> pour les caractères d'identifiant de variable</li>
* <li> un état <code>MAJCAR</code> pour les caractères en majuscule</li>
* </ul>
* Une fois le lexème lexical formé, on vérifie qu'il est bien conforme aux prescriptions du langage.
* Un lexème de type <code>MAJ</code> est spécialisé en fonction de sa signification lexicale.
* </p>
*/
public class AnalyseLexicale {
/** état de l'automate représentant un caractère non-spécial. */
private static final int CARACT = 1;
/** état de l'automate représentant un nombre. */
private static final int NOMBRE = 2;
/** état de l'automate représentant un identifiant de variable. */
private static final int NOMVAR = 3;
private static final int NOMTYP = 4;
/** état de l'automate représentant des majuscules. */
private static final int MAJCAR = 4;
/** l'analyseur alphabétique qui fournit les lexèmes à analyser. */
private AnalyseCaractere flin;
/** le tampon pour stocker les chaînes de caractères en cours de construction. */
private StringBuffer buffer;
/** l'état actuel de l'automate lexical. */
private int etat;
private Lexeme next, nextnext;
/** le prochain lexème qui sera consommé. */
private Lexeme next;
/**
* le suivant de prochain lexème qui sera consommé.
* Il nous le faut car l'analyseur syntaxique est en fait LL(2) :
* il a besoin du type des deux prochains lexèmes pour fonctionner.
*/
private Lexeme nextnext;
/**
* Initialise l'analyseur lexical.
*
* @param ac l'analyseur alphabetique qui fournit les lexèmes à analyser
*/
public AnalyseLexicale(AnalyseCaractere ac) {
flin = ac;
buffer = new StringBuffer();
......@@ -24,7 +72,19 @@ class AnalyseLexicale {
/**
* Retourne le type du prochain lexème.
*
* <p>
* Ce lexème n'est pas consommé.
* </p>
*
* @return le type du prochain lexème
*
* @throws AnalyseException si l'analyseur alphabétique a levé une exception ou
* si un lexème n'est pas lexicalement bien formé
* (voir {@link #valideLexeme(Lexeme) valideLexeme})
*/
public int getNextLexemeType() throws AnalyseException {
if (next == null)
next = getNextLexeme();
......@@ -33,6 +93,18 @@ class AnalyseLexicale {
return next.getType();
}
/**
* Retourne le type du lexème suivant le prochain.
*
* <p> Aucun lexème n'est consommé avec l'appel à cette méthode. </p>
*
* @return le type du suivant du prochaine lexème
*
* @throws AnalyseException si l'analyseur alphabétique a levé une exception ou
* si un lexème n'est pas lexicalement bien formé
* (voir {@link #valideLexeme(Lexeme) valideLexeme})
*/
public int getNextNextLexemeType() throws AnalyseException {
if (nextnext != null) return nextnext.getType();
Lexeme lex = next;
......@@ -44,7 +116,22 @@ class AnalyseLexicale {
if (nextnext == null) return Lexeme.ERR;
return nextnext.getType();
}
/**
* Retourne le prochain lexème.
*
* <p>
* Cette méthode consomme le lexème renvoyé.
* C'est le coeur de l'automate lexical.
* </p>
*
* @return le prochain lexème
*
* @throws AnalyseException si l'analyseur alphabétique a levé une exception ou
* si un lexème n'est pas lexicalement bien formé
* (voir {@link #valideLexeme(Lexeme) valideLexeme})
*/
public Lexeme getNextLexeme() throws AnalyseException {
Lexeme lex = null;
if (next != null) {
......@@ -57,7 +144,7 @@ class AnalyseLexicale {
if (lextype == Lexeme.ERR) return null;
switch(etat) {
case CARACT:
case CARACT: // On était sur un caractère ordinaire.
lex = flin.getNextLexeme();
switch(lextype) {
case Lexeme.INT:
......@@ -68,16 +155,16 @@ class AnalyseLexicale {
buffer.append(lex.getStr());
etat = NOMVAR;
break;
case Lexeme.TYP:
case Lexeme.MAJ:
buffer.append(lex.getStr());
etat = NOMTYP;
etat = MAJCAR;
break;
default:
if (lextype != Lexeme.ESP) return valideLexeme(lex);
if (lextype != Lexeme.ESP) return lex;
}
break;
case NOMBRE:
case NOMBRE: // On était sur un caractère numérique.
if (lextype == Lexeme.INT) {
buffer.append(flin.getNextLexeme().getStr());
} else {
......@@ -88,34 +175,55 @@ class AnalyseLexicale {
}
break;
case NOMVAR:
case NOMVAR: // On était sur un caractère d'identifiant de variable.
if (lextype == Lexeme.IDF) {
buffer.append(flin.getNextLexeme().getStr());
} else {
lex = new Lexeme(Lexeme.IDF, buffer.toString());
buffer = new StringBuffer();
etat = CARACT;
return valideLexeme(lex);
return lex;
}
break;
case NOMTYP:
if (lextype == Lexeme.TYP) {
case MAJCAR: // On était sur un caractère majuscule.
if (lextype == Lexeme.MAJ) {
buffer.append(flin.getNextLexeme().getStr());
} else {
lex = new Lexeme(Lexeme.TYP, buffer.toString());
lex = new Lexeme(Lexeme.MAJ, buffer.toString());
buffer = new StringBuffer();
etat = CARACT;
return valideLexeme(lex);
}
break;
}
/*
Si on est arrivé là, c'est que
- on est en train de concaténer des lexèmes alphabétiques de même type entre eux.
- on est tombé sur un lexème sans signification (un espace)
On doit donc aller un cran plus loin.
*/
return getNextLexeme();
}
/**
* Valide lexicalement le lexème.
*
* <p>
* Cette méthode vérifie qu'un nombre non nul ne commence pas par zéro
* et sélectionne le bon type d'un lexème majuscule.
* En cas de problème, elle lève une <code>AnalyseException</code>.
* </p>
*
* @param lex le lexème à valider
*
* @return un lexème lexicalement valide
*
* @throws AnalyseException si le lexème n'est pas lexicalement valide
*/
private Lexeme valideLexeme(Lexeme lex) throws AnalyseException {
if (lex == null) return lex;
if (lex.getType() == Lexeme.INT) {
......@@ -123,14 +231,14 @@ class AnalyseLexicale {
if (nbStr.length() > 1 && nbStr.charAt(0) == '0')
throw new AnalyseException("Nombre commencant par zéro.");
}
if (lex.getType() == Lexeme.TYP) {
if (lex.getType() == Lexeme.MAJ) {
String str = lex.getStr();
if ("ANS".equals(str))
return new Lexeme(Lexeme.IDF, str);
if ("DEG".equals(str) || "QUO".equals(str) || "REM".equals(str))
return new Lexeme(Lexeme.FCT, str);
if ("INT".equals(str) || "FRAC".equals(str) || "POLY".equals(str))
return lex;
return new Lexeme(Lexeme.TYP, str);
throw new AnalyseException("Mot clé " + str + " inconnu.");
}
return lex;
......
/**
* Analyse sémantique des polynômes.
*
* <p>
* Cette analyse se base sur l'arbre abstrait crée par l'analyseur syntaxique.
* Elle se passe en deux temps : d'une part l'analyse sémantique statique et d'autre part
* l'analyse sémantique dynamique.
* </p>
*
* <p>
* L'analyse statique consiste à vérifier que les types de chaque opérande de chaque noeud de
* l'arbre abstrait sont cohérents. Elle vérifie aussi que les identifiants de variables ont bien
* été déclarés une et une seule fois.
* </p>
*
* <p>
* L'analyse dynamique construit le polynôme. Elle s'assure que les dénominateurs sont non-nuls et
* que les puissances sont des entiers positifs.
* </p>
*/
class AnalyseSemantique {
/** Arbre abstrait à analyser. */
private ArbreAbstrait arbre;
/**
* Entier représentant le type du résultat obtenu après génération.
* @see Variable
*/
private int typeResult;
/** la variable résultat obtenu après génération. */
private Variable resultat;
/**
* Préparation des analyses.
*
* @param as analyseur syntaxique qui fournit l'arbre abstrait à analyser
*/
public AnalyseSemantique(AnalyseSyntaxique as) {
arbre = as.getArbreAbstrait();
typeResult = -1;
......@@ -13,14 +47,28 @@ class AnalyseSemantique {
}
/***
*** A N A L Y S E S T A T I Q U E
***/
/**
* Analyse sémantique statique
*
* @return un entier représentant le type du résultat qui serait obtenu après génération.
*
* @throws AnalyseException si un type n'est pas cohérent dans l'arbre abstrait.
*/
public int getTypeRetour() throws AnalyseException {
if (typeResult == -1) typeResult = getType(arbre);
return typeResult;
}
/**
* Méthode récurssive d'analyse sémantique statique.
*
* @param aa arbre abstrait qui sera analysé
*
* @return un entier représentant le type du résultat qui serait obtenu après génération.
*
* @throws AnalyseException si un type n'est pas cohérent dans l'arbre abstrait.
*/
private int getType(ArbreAbstrait aa) throws AnalyseException {
int tg, td;
switch(aa.getLabel()) {
......@@ -162,24 +210,64 @@ class AnalyseSemantique {
/***
*** A N A L Y S E D Y N A M I Q U E
***/
/**
* Retourne la valeur du résultat s'il est de type entier.
*
* <p>
* Le comportement de cette méthode est indéterminé si le résultat de l'analyse n'est pas de type entier.
* </p>
*
* @return la valeur entière du résultat de l'analyse dynamique.
*
* @throws AnalyseException s'il y a un problème pendant la génération.
*/
public int getIntValue() throws AnalyseException {
verifieEvaluation(Variable.TYPE_ENT);
return resultat.getNbVal();
}
/**
* Retourne la valeur du résultat s'il est de type fraction.
*
* <p>
* Le comportement de cette méthode est indéterminé si le résultat de l'analyse n'est pas de type fraction.
* </p>
*
* @return la valeur fractionnaire du résultat de l'analyse dynamique.
*
* @throws AnalyseException s'il y a un problème pendant la génération.
*/
public Fraction getFracValue() throws AnalyseException {
verifieEvaluation(Variable.TYPE_FRAC);
return resultat.getFracVal();
}
/**
* Retourne la valeur du résultat s'il est de type polynôme.
*
* <p>
* Le comportement de cette méthode est indéterminé si le résultat de l'analyse n'est pas de type polynôme.
* </p>
*
* @return la valeur polynomiale du résultat de l'analyse dynamique.
*
* @throws AnalyseException s'il y a un problème pendant la génération.
*/
public Polynome getPolyValue() throws AnalyseException {
verifieEvaluation(Variable.TYPE_POLY);
return resultat.getPolyVal();
}
/**
* Génère la valeur du résultat de l'expression analysée.
*
* Cette méthode met aussi à jour la variable d'environnement <code>ANS</code> (type et valeur).
*
* @throws AnalyseException s'il y a un problème pendant la génération.
*/
public void calculeRetour() throws AnalyseException {
if (resultat == null) {
resultat = evaluate(arbre);
......@@ -189,13 +277,39 @@ class AnalyseSemantique {
}
/**
* Vérifie que le type du résultat est bien conforme à ce qu'on veut.
*
* <p>
* Le résultat est généré si besoin.
* </p>
*
* @param type le type souhaité du résultat de la génération.
*
* @throws AnalyseException si le type du résultat n'est pas celui souhaité.
*/
private void verifieEvaluation(int type) throws AnalyseException {
if (typeResult == -1) getTypeRetour();
if (typeResult != type)
throw new AnalyseException("L'expression n'est pas de type " + Variable.getSType(type) + ".");
calculeRetour();
}
/**
* Évalue la valeur du résultat d'un arbre abstrait.
*
* <p>
* Il s'agit du coeur de l'analyse sémantique dynamique.
* C'"est une fonction récurssive.
* </p>
*
* @param aa l'arbre abstrait qui sert de base à la génération du résultat.
*
* @return le résultat de la génération de l'expression.
*
* @throws AnalyseException si un problème de génération survient dans l'analyse sémantique.
*/
private Variable evaluate(ArbreAbstrait aa) throws AnalyseException {
Variable vg, vd;
switch(aa.getLabel()) {
......@@ -275,9 +389,6 @@ class AnalyseSemantique {
}
throw new AnalyseException("Erreur dans l'arbre abstrait: label inconnue.");
}
/***
*** fin d'analyse sémantique dynamique
***/
}
This diff is collapsed.
/**
* Arbre abstrait des polynômes.
*
* <p>
* Il est crée par l'analyseur syntaxique et utilisé par l'analyseur sémantique.
* </p>
*
* <p>
* Chaque noeud possède une étiquette sémantique décrivant son action.
* Chaque feuille possède une chaîne de caractères décrivant son information.
* </p>
*/
class ArbreAbstrait {
public static final int ADDIT = 0; // addition
public static final int SUBST = 1; // soustraction
public static final int MULTI = 2; // multiplication
public static final int DIVID = 3; // division
public static final int POWER = 4; // puissance
public static final int MINUS = 5; // moins unaire
public static final int LEAFN = 6; // feuille: nombre
public static final int LEAFX = 7; // feuille: variable X
public static final int TYPAG = 8; // feuille: typage
public static final int IDAFF = 9; // feuille: identifiant pour affectation (gauche du =)
public static final int IDVAL = 10; // feuille: identifiant pour valeur (droite du = ou dans expression)
public static final int DECLA = 11; // declaration d'identifiant
public static final int AFFEC = 12; // affectation
public static final int FONCT = 13; // fonction sur les polynomes
/** noeud représentant une addition. */
public static final int ADDIT = 0;
/** noeud représentant une soustraction. */
public static final int SUBST = 1;
/** noeud représentant une multiplication. */
public static final int MULTI = 2;
/** noeud représentant une division. */
public static final int DIVID = 3;
/** noeud représentant une puissance. */
public static final int POWER = 4;
/** noeud représentant un moins unaire. */
public static final int MINUS = 5;
/** feuille représentant un nombre. */
public static final int LEAFN = 6;
/** feuille représentant la variable X des polynômes. */
public static final int LEAFX = 7;
/** feuille représentant un type de variable. */
public static final int TYPAG = 8;
/** feuille représentant un identifiant en vue d'une affectation (à gauche du =). */
public static final int IDAFF = 9;
/** feuille représentant un identifiant en vue de connaître sa valeur (à droite du = ou dans une expression). */
public static final int IDVAL = 10;
/** noeud représentant une déclaration d'identifiant. */
public static final int DECLA = 11;
/** noeud représentant une affectation. */
public static final int AFFEC = 12;
/** feuille représentant une fonction du langage. */
public static final int FONCT = 13;
/** l'étiquette du noeud. */
private int label;
private ArbreAbstrait aagauche, aadroit;
/** le fils gauche du noeud. */
private ArbreAbstrait aagauche;
/** le fils droit du noeud. */
private ArbreAbstrait aadroit;
/** une chaîne représentant l'information de la feuille. */
private String leaf;
ArbreAbstrait(int t) {
/**
* Création d'un arbre abstrait défini par son type.
*
* @param t type de l'arbre crée.
*/
public ArbreAbstrait(int t) {
label = t;
aagauche = null;
aadroit = null;
......@@ -29,25 +83,51 @@ class ArbreAbstrait {
}
/**
* Définie le fils gauche de ce noeud.
*
* @param aa le fils gauche.
*/
public void setAAGauche(ArbreAbstrait aa) {
aagauche = aa;
}
/**
* Définie le fils droit de ce noeud.
*
* @param aa le fils droit.
*/
public void setAADroit(ArbreAbstrait aa) {
aadroit = aa;
}
/**
* Définie la chaîne représentant l'information de la feuille.
*
* @param str la chaîne de la feuille.
*/
public void setSLeaf(String str) {
leaf = str;
}
/**
* Retourne le type (l'étiquette) du noeud.
*
* @return le type du noeud.
*/
public int getLabel() {
return label;
}
/**
* Retourne une représentation de l'étiquette du noeud sous forme de chaîne de caractères.
*
* @return une chaîne représentant le type du noeud.
*/
public String getSLabel() {
switch(label) {
case ADDIT: return "{+}";
......@@ -69,25 +149,65 @@ class ArbreAbstrait {