Files
interpreteur-lir/src/interpreteurlir/programmes/Programme.java
T
SylvanCourtiol 60219ea6d9 Prototype 2.0
2021-05-16 18:17:28 +02:00

280 lines
8.8 KiB
Java

/**
* Programme.java 14 mai 2021
* IUT-Rodez info1 2020-2021, pas de droits, pas de copyrights
*/
package interpreteurlir.programmes;
import java.util.EmptyStackException;
import java.util.Map;
import java.util.Stack;
import java.util.TreeMap;
import interpreteurlir.ExecutionException;
import interpreteurlir.InterpreteurException;
import interpreteurlir.motscles.instructions.Instruction;
import static interpreteurlir.programmes.Etiquette.VALEUR_ETIQUETTE_MAX;
import static interpreteurlir.programmes.Etiquette.VALEUR_ETIQUETTE_MIN;
/**
* Enregistrement des lignes de code (instruction associée à une
* étiquette) et gestion de l'exécution des lignes de code dans
* l'ordre des étiquettes
*
* @author Nicolas Caminade
* @author Sylvan Courtiol
* @author Pierre Debas
* @author Heia Dexter
* @author Lucas Vabre
*/
public class Programme {
private static final String ERREUR_INTERVALLE = "erreur dans l'intervalle "
+ "d'étiquettes. ";
/** Pile LIFO pour la gestion des étiquettes */
private Stack<Etiquette> compteurOrdinnal;
/** Détermine la poursuite d'exécution de ce programme */
private boolean enExecution;
private TreeMap<Etiquette, Instruction> lignesCode;
/**
* Initialisation de ce programme sans lignes de code
* <p>
*
*/
public Programme() {
super();
lignesCode = new TreeMap<Etiquette, Instruction>();
enExecution = false;
compteurOrdinnal = new Stack<Etiquette>();
}
/**
* Remise à zero de ce programme
* <p>
* Vide ce programme de toute lignes de code (instruction associée
* à une étiquette)
*/
public void raz() {
lignesCode.clear();
compteurOrdinnal.clear();
}
/**
* Ajoute une ligne de code (instruction associée à une étiquette)
* à ce programme
* <p>
* Une ligne ajoutée à une même étiquette écrase le contenu associé
* à cette dernière
*
* @param etiquette pour donner l'ordre d'exécution de ce programme
* @param instruction associée une étiquette à insérer dans ce
* programme
* @throws NullPointerException si l'étiquette ou l'instruction
* est nulle
*/
public void ajouterLigne(Etiquette etiquette, Instruction instruction) {
if (etiquette == null || instruction == null) {
throw new NullPointerException();
}
lignesCode.put(etiquette, instruction);
}
/* non javadoc
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
Object[] tableauEtiquette
= lignesCode.keySet().toArray();
Object[] tableauInstruction
= lignesCode.values().toArray();
StringBuilder aAfficher = new StringBuilder("");
for (int i = 0; i < tableauEtiquette.length; i++) {
aAfficher.append(tableauEtiquette[i] + " "
+ tableauInstruction[i] + '\n');
}
return aAfficher.toString();
}
/**
* Liste des lignes de code comprise entre les étiquettes de
* début et les étiquettes de fin
* <p>
* S'il n'y aucune ligne de code dans l'intervalle, alors la
* chaîne renvoyée contient un message qui l'indique.
*
* @param debut étiquette à partir de laquelle le programme
* est listé
* @param fin dernière étiquette associée à son contenu à lister
* @return la représentation texte des lignes de code comprise
* entre les étiquettes de début et les étiquettes de fin
* @throws InterpreteurException si fin est strictement inférieur
* à debut
*/
public String listeBornee(Etiquette debut, Etiquette fin) {
if (fin.compareTo(debut) < 0) {
throw new InterpreteurException(ERREUR_INTERVALLE);
}
StringBuilder aAfficher = new StringBuilder("");
Etiquette cleCourante = debut;
Instruction instCourante = null;
Map.Entry<Etiquette, Instruction> entreeCourante;
boolean lignesRestantes;
do {
entreeCourante = lignesCode.ceilingEntry(cleCourante);
lignesRestantes = entreeCourante != null;
if (lignesRestantes) {
cleCourante = entreeCourante.getKey();
instCourante = entreeCourante.getValue();
lignesRestantes = cleCourante.compareTo(fin) <= 0;
}
if (lignesRestantes) {
aAfficher.append(cleCourante + " " + instCourante + '\n');
cleCourante = new Etiquette(cleCourante.getValeur() + 1);
}
} while (lignesRestantes);
return aAfficher.toString().equals("")
? "Aucune ligne de code dans cet intervalle.\n"
: aAfficher.toString();
}
/**
* Efface les lignes de code comprises entre les étiquettes debut
* et fin
*
* @param debut étiquette à partir de laquelle le programme
* est à effacer
* @param fin dernière étiquette associée à son contenu à effacer
* @throws InterpreteurException si fin est strictement inférieur
* à debut
*/
public void effacer(Etiquette debut, Etiquette fin) {
if (fin.compareTo(debut) < 0) {
throw new InterpreteurException(ERREUR_INTERVALLE);
}
Etiquette cleCourante = debut;
boolean lignesRestantes;
do {
cleCourante = lignesCode.ceilingKey(cleCourante);
lignesRestantes = cleCourante != null
&& cleCourante.compareTo(fin) <= 0;
if (lignesRestantes) {
lignesCode.remove(cleCourante);
cleCourante = new Etiquette(cleCourante.getValeur() + 1);
}
} while (lignesRestantes);
}
/**
* Arrête l'exécution du programme
*/
public void stop() {
enExecution = false;
}
/**
* Boucle d'exécution du programme
*/
private void execution() {
Etiquette etiquetteCourante;
while (enExecution) {
etiquetteCourante = compteurOrdinnal.pop();
etiquetteCourante = lignesCode.ceilingKey(etiquetteCourante);
enExecution = etiquetteCourante != null
&& etiquetteCourante.getValeur() + 1 <= VALEUR_ETIQUETTE_MAX;
if (enExecution) {
compteurOrdinnal.push(
new Etiquette(etiquetteCourante.getValeur() + 1));
lignesCode.get(etiquetteCourante).executer();
}
}
}
/**
* Lance l'exécution du programme à partir de l'étiquette
* passée en argument
*
* @param etiquetteDepart étiquette à partir de laquelle
* l'exécution du programme est lancée
*/
public void lancer(Etiquette etiquetteDepart) {
compteurOrdinnal.clear();
compteurOrdinnal.push(etiquetteDepart);
enExecution = true;
execution();
}
/**
* Lance l'exécution du programme à partir de l'étiquette
* la plus petite
*/
public void lancer() {
lancer(new Etiquette(VALEUR_ETIQUETTE_MIN));
}
/**
* Change le compteur ordinal avec l'étiquette argument
* @param destination étiquette où continuer l'exécution
*/
public void vaen(Etiquette destination) {
if (!compteurOrdinnal.isEmpty()) {
compteurOrdinnal.pop();
}
compteurOrdinnal.push(destination);
enExecution = true;
execution();
}
/**
* Appel une procédure en empilant l'étiquette de départ dans
* le compteurOrdinal
* @param depart étiquette du début de la procédure
*/
public void appelProcedure(Etiquette depart) {
compteurOrdinnal.push(depart);
}
/**
* Retour d'une procédure en dépilant l'étiquette de départ dans
* le compteurOrdinal
* @param depart étiquette du début de la procédure
* @throws ExecutionException lorsque retourProcedure vide le
* compteurOrdinnal
*/
public void retourProcedure() {
final String ERREUR_RETOUR = "erreur retour nécessite un appel de "
+ "procédure au préalable";
try {
compteurOrdinnal.pop();
} catch (EmptyStackException lancee) {
// empty body
}
if (compteurOrdinnal.isEmpty()) {
throw new ExecutionException(ERREUR_RETOUR);
}
}
}