From db09baf5c4025252da5fb21071e7bd4a94ac77fc Mon Sep 17 00:00:00 2001 From: SylvanCourtiol Date: Mon, 24 May 2021 19:01:55 +0200 Subject: [PATCH] =?UTF-8?q?Prototype=20+=20programme=20de=20d=C3=A9mo=20+?= =?UTF-8?q?=20ex=C3=A9cutable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Median3Entiers.lir | 37 ++ demo.lir | 13 + etatCivil.lir | 28 ++ factorielle.lir | 35 ++ prototype/interpreteurLIR.jar | Bin 0 -> 116955 bytes prototype/lanceInterpreteurLIR.bat | 2 + src/interpreteurlir/Analyseur.java | 10 +- src/interpreteurlir/Contexte.java | 1 - src/interpreteurlir/ExecutionException.java | 1 + .../InterpreteurException.java | 1 + .../donnees/Identificateur.java | 28 +- .../donnees/IdentificateurChaine.java | 2 +- .../donnees/IdentificateurEntier.java | 6 +- src/interpreteurlir/donnees/Variable.java | 6 +- .../donnees/litteraux/Booleen.java | 48 ++ .../donnees/litteraux/Chaine.java | 48 +- .../donnees/litteraux/Entier.java | 83 ++-- .../donnees/litteraux/Litteral.java | 14 +- .../donnees/litteraux/tests/TestBooleen.java | 50 ++ .../donnees/litteraux/tests/TestChaine.java | 247 +++++---- .../donnees/litteraux/tests/TestEntier.java | 42 +- .../litteraux/tests/TestLitteraux.java | 343 ++++++------- .../donnees/tests/TestIdentificateur.java | 229 ++++----- .../tests/TestIdentificateurChaine.java | 8 +- .../donnees/tests/TestVariable.java | 40 +- .../expressions/Expression.java | 70 ++- .../expressions/ExpressionBooleenne.java | 287 +++++++++++ .../expressions/ExpressionChaine.java | 42 +- .../expressions/ExpressionEntier.java | 70 +-- .../expressions/tests/TestExpression.java | 43 -- .../tests/TestExpressionBooleenne.java | 467 ++++++++++++++++++ .../tests/TestExpressionChaine.java | 29 ++ .../tests/TestExpressionEntier.java | 12 +- src/interpreteurlir/motscles/Commande.java | 7 +- .../motscles/CommandeCharge.java | 167 +++++++ .../motscles/CommandeDebut.java | 3 +- .../motscles/CommandeEfface.java | 4 +- src/interpreteurlir/motscles/CommandeFin.java | 3 +- .../motscles/CommandeLance.java | 7 +- .../motscles/CommandeListe.java | 16 +- .../motscles/CommandeSauve.java | 86 ++++ .../motscles/instructions/Instruction.java | 13 +- .../instructions/InstructionAffiche.java | 12 +- .../instructions/InstructionEntre.java | 11 +- .../instructions/InstructionProcedure.java | 4 +- .../instructions/InstructionRetour.java | 4 +- .../motscles/instructions/InstructionSi.java | 94 ++++ .../instructions/InstructionStop.java | 8 +- .../instructions/InstructionVaen.java | 11 +- .../motscles/instructions/InstructionVar.java | 8 +- .../instructions/tests/TestInstruction.java | 95 ++-- .../tests/TestInstructionAffiche.java | 12 +- .../tests/TestInstructionEntre.java | 2 - .../instructions/tests/TestInstructionSi.java | 184 +++++++ .../tests/TestInstructionStop.java | 18 +- .../tests/TestInstructionVar.java | 59 +-- .../motscles/tests/EssaiCommande.java | 8 +- .../motscles/tests/TestCommande.java | 176 +++---- .../motscles/tests/TestCommandeCharge.java | 111 +++++ .../motscles/tests/TestCommandeDebut.java | 3 + .../motscles/tests/TestCommandeEfface.java | 6 +- .../motscles/tests/TestCommandeFin.java | 8 +- .../motscles/tests/TestCommandeLance.java | 41 +- .../motscles/tests/TestCommandeListe.java | 6 + .../motscles/tests/TestCommandeSauve.java | 176 +++++++ src/interpreteurlir/programmes/Programme.java | 31 +- .../programmes/tests/TestProgramme.java | 56 ++- .../tests/ProgrammeDeTest.java | 69 +++ src/interpreteurlir/tests/TestContexte.java | 4 +- 69 files changed, 2805 insertions(+), 1060 deletions(-) create mode 100644 Median3Entiers.lir create mode 100644 demo.lir create mode 100644 etatCivil.lir create mode 100644 factorielle.lir create mode 100644 prototype/interpreteurLIR.jar create mode 100644 prototype/lanceInterpreteurLIR.bat create mode 100644 src/interpreteurlir/donnees/litteraux/Booleen.java create mode 100644 src/interpreteurlir/donnees/litteraux/tests/TestBooleen.java create mode 100644 src/interpreteurlir/expressions/ExpressionBooleenne.java create mode 100644 src/interpreteurlir/expressions/tests/TestExpressionBooleenne.java create mode 100644 src/interpreteurlir/motscles/CommandeCharge.java create mode 100644 src/interpreteurlir/motscles/CommandeSauve.java create mode 100644 src/interpreteurlir/motscles/instructions/InstructionSi.java create mode 100644 src/interpreteurlir/motscles/instructions/tests/TestInstructionSi.java create mode 100644 src/interpreteurlir/motscles/tests/TestCommandeCharge.java create mode 100644 src/interpreteurlir/motscles/tests/TestCommandeSauve.java create mode 100644 src/interpreteurlir/tests/ProgrammeDeTest.java diff --git a/Median3Entiers.lir b/Median3Entiers.lir new file mode 100644 index 0000000..39c9af1 --- /dev/null +++ b/Median3Entiers.lir @@ -0,0 +1,37 @@ +10 affiche "Bienvenue dans le programme Median3Entiers.lir" +20 affiche +30 affiche "Entrez le premier entier : " +40 entre premier +50 affiche "Entrez le deuxième entier : " +60 entre deuxieme +70 affiche "Entrez le troisième entier : " +80 entre troisieme +90 procedure 1000 +100 affiche "Median( " +110 affiche premier +120 affiche ", " +130 affiche deuxieme +140 affiche ", " +150 affiche troisieme +160 affiche ") = " +170 affiche median +180 affiche +250 stop + +1000 si premier <= deuxieme vaen 1100 +1010 si deuxieme <= troisieme vaen 1200 +1020 vaen 1520 + +1100 si deuxieme <= troisieme vaen 1520 +1110 si premier <= troisieme vaen 1540 +1120 vaen 1500 + +1200 si premier <= troisieme vaen 1500 +1220 vaen 1540 + +1500 var median = premier +1510 vaen 1550 +1520 var median = deuxieme +1530 vaen 1550 +1540 var median = troisieme +1550 retour \ No newline at end of file diff --git a/demo.lir b/demo.lir new file mode 100644 index 0000000..0c64a91 --- /dev/null +++ b/demo.lir @@ -0,0 +1,13 @@ +10 affiche "Entre ton nom : " +20 entre $nom +30 affiche "Bienvenue "+$nom +35 affiche +40 var an=2021 +50 affiche "Quelle est ton année de naissance ? " +60 entre naissance +65 si naissance > an vaen 50 +70 affiche "Tu as autour de " +80 affiche an-naissance +90 affiche "ans " +100 affiche +200 stop \ No newline at end of file diff --git a/etatCivil.lir b/etatCivil.lir new file mode 100644 index 0000000..363473d --- /dev/null +++ b/etatCivil.lir @@ -0,0 +1,28 @@ +10 affiche "Bienvenue dans le programme" +20 affiche +30 var instant = 2021 +40 procedure 500 +50 var $message = "Vous êtes " + $prenom +60 var $message = $message + " " +65 var $message = $message + $nom +70 affiche $message +80 affiche +90 affiche "age : " +100 affiche age +110 affiche " ans" +120 vaen 150 +124 affiche +125 affiche "erreur vaen si affiché" +150 affiche +200 affiche "Merci d'avoir utilisé ce programme !" +400 stop +500 affiche "Saisissez votre nom : " +510 entre $nom +520 affiche "Saisissez votre prénom : " +530 entre $prenom +540 affiche "Saisissez votre année de naissance (entier) : " +550 entre naissance +560 procedure 1000 +570 retour +1000 var age = instant - naissance +1010 retour diff --git a/factorielle.lir b/factorielle.lir new file mode 100644 index 0000000..07f530f --- /dev/null +++ b/factorielle.lir @@ -0,0 +1,35 @@ +10 affiche "Bienvenue dans le programme factorielle.lir !" +20 affiche +30 affiche "Entrez un entier : " +40 entre entier +45 procedure 500 +50 procedure 1000 +60 affiche entier +70 affiche "! = " +80 affiche factorielle +90 affiche +200 stop + +500 si entier >= 0 vaen 600 +510 affiche "n! est définie sur l'ensemble des entiers naturels" +520 stop +600 retour + +1000 var factorielle = 1 +1010 var entierCourant = 2 +1011 var ancienFactorielle = factorielle +1012 var test = factorielle +1015 si entierCourant > entier vaen 1100 +1016 si ancienFactorielle <> test vaen 1060 +1017 var ancienFactorielle = factorielle +1020 var factorielle = factorielle * entierCourant +1025 var test = factorielle / entierCourant +1030 var entierCourant = entierCourant + 1 +1040 vaen 1015 +1050 vaen 1100 +1060 affiche "dépassement de la capacité des entiers pour " +1070 affiche entier +1080 affiche "!" +1090 affiche +1095 stop +1100 retour \ No newline at end of file diff --git a/prototype/interpreteurLIR.jar b/prototype/interpreteurLIR.jar new file mode 100644 index 0000000000000000000000000000000000000000..8f3cede0eb3c3e26511eb4d4a7f90bc1fddb4181 GIT binary patch literal 116955 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

s~W8zb8USwjALt>HnTY0c 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 literal 0 HcmV?d00001 diff --git a/prototype/lanceInterpreteurLIR.bat b/prototype/lanceInterpreteurLIR.bat new file mode 100644 index 0000000..46d9782 --- /dev/null +++ b/prototype/lanceInterpreteurLIR.bat @@ -0,0 +1,2 @@ +java -jar interpreteurLIR.jar +cmd /k java -jar interpreteurLIR.jar \ No newline at end of file diff --git a/src/interpreteurlir/Analyseur.java b/src/interpreteurlir/Analyseur.java index fb2c05b..6b7921b 100644 --- a/src/interpreteurlir/Analyseur.java +++ b/src/interpreteurlir/Analyseur.java @@ -1,5 +1,5 @@ /** - * Analyseur.java 9 mai 2021 + * Analyseur.java 9 mai 2021 * IUT Rodez info1 2020-2021, pas de copyright, aucun droit */ package interpreteurlir; @@ -143,6 +143,10 @@ public class Analyseur { .getConstructor(classeArg, classeContexte) .newInstance(arguments, contexteGlobal); feedback(cmd.executer()); + if (motCle.equals("lance") + || (motCle.equals("affiche") && !arguments.isBlank())) { + System.out.println(); + } } catch ( InvocationTargetException | IllegalAccessException | InstantiationException | NoSuchMethodException | InterpreteurException | ExecutionException lancee) { @@ -213,14 +217,14 @@ public class Analyseur { * Recherche l'instruction correspondant au mot clé. *

- * @param aTester + * @param aTester chaîne à tester * @return true si le prédicat est vérifié * false sinon */ @@ -77,17 +77,15 @@ implements Comparable { public static boolean isAlphanumerique(String aTester) { int index; for (index = 0 ; - index < aTester.length() - && Character.isLetterOrDigit(aTester.charAt(index)) ; - index++) - ; // Corps vide + index < aTester.length() + && Character.isLetterOrDigit(aTester.charAt(index)) ; + index++) + ; /* empty body */ - return index >= aTester.length(); + return index >= aTester.length(); } - /** - * @return la valeur de nom - */ + /** @return la valeur de nom */ public String getNom() { return nom; } diff --git a/src/interpreteurlir/donnees/IdentificateurChaine.java b/src/interpreteurlir/donnees/IdentificateurChaine.java index 0277202..2d535fd 100644 --- a/src/interpreteurlir/donnees/IdentificateurChaine.java +++ b/src/interpreteurlir/donnees/IdentificateurChaine.java @@ -39,7 +39,7 @@ public class IdentificateurChaine extends Identificateur { * @return true si l'identificateur est bien un identificateur d'entier * false sinon */ - private static boolean isIdentificateurChaine(String identificateur) { + public static boolean isIdentificateurChaine(String identificateur) { return identificateur.length() >= 2 && identificateur.charAt(0) == '$' diff --git a/src/interpreteurlir/donnees/IdentificateurEntier.java b/src/interpreteurlir/donnees/IdentificateurEntier.java index e3e273d..41b8b47 100644 --- a/src/interpreteurlir/donnees/IdentificateurEntier.java +++ b/src/interpreteurlir/donnees/IdentificateurEntier.java @@ -21,10 +21,10 @@ import interpreteurlir.InterpreteurException; public class IdentificateurEntier extends Identificateur { /** - * Instantiation de cet identificateur d'entier avec le nom spécififié + * Instantiation de cet identificateur d'entier avec le nom spécifié * en argument. Lève une exception si l'identificateur n'est pas * valide. - * @param identificateur a instancier + * @param identificateur à instancier * @throws InterpreteurException si l'identificateur est invalide */ public IdentificateurEntier(String identificateur) { @@ -49,7 +49,7 @@ public class IdentificateurEntier extends Identificateur { * @return true si l'identificateur est bien un identificateur d'entier * false sinon */ - private static boolean isIdentificateurEntier(String aTester) { + public static boolean isIdentificateurEntier(String aTester) { return aTester.length() <= 25 && Character.isLetter(aTester.charAt(0)) && isAlphanumerique(aTester.substring(1)); } diff --git a/src/interpreteurlir/donnees/Variable.java b/src/interpreteurlir/donnees/Variable.java index 7216469..6541870 100644 --- a/src/interpreteurlir/donnees/Variable.java +++ b/src/interpreteurlir/donnees/Variable.java @@ -65,7 +65,7 @@ public class Variable extends Object implements Comparable { /** * Modifie la valeur de cette variable - * @param nouvelleValeur + * @param nouvelleValeur valeur à affecter à cette variable */ public void setValeur(Litteral nouvelleValeur) { if (isVariable(identificateur, nouvelleValeur)) { @@ -86,6 +86,10 @@ public class Variable extends Object implements Comparable { return identificateur.toString() + " = " + valeur.toString(); } + /* + * non javadoc + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ public int compareTo(Variable aComparer) { return identificateur.compareTo(aComparer.identificateur); } diff --git a/src/interpreteurlir/donnees/litteraux/Booleen.java b/src/interpreteurlir/donnees/litteraux/Booleen.java new file mode 100644 index 0000000..bc83855 --- /dev/null +++ b/src/interpreteurlir/donnees/litteraux/Booleen.java @@ -0,0 +1,48 @@ +/** + * Booleen.java 21 mai 2021 + * IUT Rodez info1 2020-2021, pas de copyright, aucun droit + */ +package interpreteurlir.donnees.litteraux; + +import interpreteurlir.InterpreteurException; + +/** + * Constante littérale de type booléen + * + * @author Nicolas Caminade + * @author Sylvan Courtiol + * @author Pierre Debas + * @author Heia Dexter + * @author Lucas Vabre + */ +public class Booleen extends Litteral { + + /** + * Initialise ce booléen + * @param unBooleen valeur du booléen à construire + * @throws InterpreteurException si la condition n'est pas un + * + */ + public Booleen(Boolean unBooleen) { + super(); + valeur = unBooleen; + } + + /* non javadoc + * @see interpreteurlir.donnees.litteraux.Litteral#getValeur() + */ + @Override + public Boolean getValeur() { + return (Boolean) super.valeur; + } + + /* non javadoc + * @deprecated + * @see Litteral#compareTo(Litteral) + */ + @Override + public int compareTo(Litteral autre) { + return (Boolean)this.valeur == (Boolean)autre.valeur ? 0 : 1; // égalité + } + +} diff --git a/src/interpreteurlir/donnees/litteraux/Chaine.java b/src/interpreteurlir/donnees/litteraux/Chaine.java index bca64e4..0813c68 100644 --- a/src/interpreteurlir/donnees/litteraux/Chaine.java +++ b/src/interpreteurlir/donnees/litteraux/Chaine.java @@ -21,11 +21,11 @@ public class Chaine extends Litteral { /** Erreur chaîne trop longue */ private static final String ERREUR_LG_MAX = - "Longueur maximale d'une chaîne dépassée"; + "longueur maximale d'une chaîne dépassée"; /** Erreur constante littéral chaîne invalide */ private static final String ERREUR_INVALIDE = - "syntaxe incorrecte pour une constante de type chaîne : "; + " n'est pas une constante de type chaîne"; /** * initialise cette chaîne avec une valeur par défaut. @@ -35,22 +35,33 @@ public class Chaine extends Litteral { } /** - * Initialise une chaîne avec la séquence de caractères passée en argument. - * @param uneValeur + * Initialise une chaîne avec la séquence + * de caractères passée en argument. + * @param uneValeur valeur de la chaine à construire (entre guillemets) */ public Chaine(String uneValeur) { uneValeur = uneValeur.trim(); - if (!uneValeur.startsWith("\"") || !uneValeur.endsWith("\"")) { - throw new InterpreteurException(ERREUR_INVALIDE - + uneValeur); - } - - uneValeur = uneValeur.substring(1, uneValeur.length() - 1); - - if (uneValeur.length() > LG_MAX_CHAINE) - throw new InterpreteurException(ERREUR_LG_MAX); - - valeur = uneValeur; + if (!isChaine(uneValeur)) { + throw new InterpreteurException(uneValeur + ERREUR_INVALIDE); + } + + uneValeur = uneValeur.substring(1, uneValeur.length() - 1); + + if (uneValeur.length() > LG_MAX_CHAINE) + throw new InterpreteurException(ERREUR_LG_MAX); + + valeur = uneValeur; + } + + /** + * Prédicat de validité d'une constante littérale de type chaîne. + * @param uneValeur valeur aTester + * @return true si uneValeur est une chaîne sinon false + */ + public static boolean isChaine(String uneValeur) { + uneValeur = uneValeur.trim(); + return uneValeur.length() >= 2 + && uneValeur.startsWith("\"") && uneValeur.endsWith("\""); } /** @@ -61,16 +72,15 @@ public class Chaine extends Litteral { * @return une nouvelle Chaîne. */ public static Chaine concatener(Chaine a, Chaine b) { - - return new Chaine("\"" + a.valeur + b.valeur + "\""); + return new Chaine("\"" + a.valeur + b.valeur + "\""); } /* non javadoc - * @see interpreteurlir.donnees.litteraux.Litteral#compareTo(interpreteurlir.donnees.litteraux.Litteral) + * @see Litteral#compareTo(Litteral) */ @Override public int compareTo(Litteral autre) { - return this.valeur.toString().compareTo(autre.valeur.toString()); + return valeur.toString().compareTo(autre.valeur.toString()); } /* non javadoc diff --git a/src/interpreteurlir/donnees/litteraux/Entier.java b/src/interpreteurlir/donnees/litteraux/Entier.java index f5fe21d..d726ad0 100644 --- a/src/interpreteurlir/donnees/litteraux/Entier.java +++ b/src/interpreteurlir/donnees/litteraux/Entier.java @@ -17,73 +17,46 @@ import interpreteurlir.InterpreteurException; * @author Lucas Vabre */ public class Entier extends Litteral { - - private static final int LONG_CH_MAX = 11; - - /** Valeur entière minimale */ - public static int VALEUR_MIN = Integer.MIN_VALUE; - - /** Valeur entière minimale */ - public static int VALEUR_MAX = Integer.MAX_VALUE; /** * Initialisation de cet entier avec une valeur passée en argument - * @param unEntier - * @throws InterpreteurException lorsque entier n'est pas un Entier + * @param unEntier valeur de l'entier à construire */ - @SuppressWarnings("boxing") public Entier(int unEntier) { - if (! isEntier(unEntier)) { - throw new InterpreteurException("Erreur. " + unEntier - + " n'est pas un entier. "); - } super.valeur = unEntier; } /** * Initialisation de cet entier avec une valeur passée en argument - * @param uneValeur + * @param uneValeur chaîne contenant l'entier à construire */ public Entier(String uneValeur) { if (!isEntier(uneValeur)) { throw new InterpreteurException(uneValeur - + " n'est pas un nombre entier. "); + + " n'est pas un nombre entier"); } valeur = Integer.valueOf(uneValeur); } - private static boolean isEntier(String uneValeur) { - if (uneValeur != null && !uneValeur.isBlank() - && uneValeur.length() <= LONG_CH_MAX - && (isChiffre(uneValeur.charAt(0)) - || uneValeur.length() > 1 && (uneValeur.charAt(0) == '-' - || uneValeur.charAt(0) == '+'))) { - int i; - for (i = 1; i < uneValeur.length() && isChiffre(uneValeur.charAt(i)); - i++) - ; /* corps vide */ - if ((uneValeur.startsWith("+214748364") - || uneValeur.startsWith("214748364")) - && uneValeur.charAt(uneValeur.length() - 1) > '7' - || uneValeur.startsWith("-214748364") - && uneValeur.charAt(uneValeur.length() - 1) > '8' - ) - return false; - - return i >= uneValeur.length(); + /** + * Prédicat de validité d'une chaîne en tant que nombre entier signé + * @param uneValeur la chaîne à tester + * @return true si uneValeur est un entier sinon false + */ + public static boolean isEntier(String uneValeur) { + if (uneValeur == null) { + return false; } - return false; - } - - private static boolean isChiffre(char caractere) { - return '0' <= caractere && caractere <= '9'; - } - - private static boolean isEntier(int entier) { - return VALEUR_MIN <= entier && entier <= VALEUR_MAX; + try { + Integer.valueOf(uneValeur.trim()); + } catch (NumberFormatException lancee) { + return false; + } + + return true; } /* non javadoc @@ -101,10 +74,10 @@ public class Entier extends Litteral { /** * Compare cet entier à un autre entier - * @param autre - * @return une valeur < 0 lorsque autre > cet entier - * une valeur > 0 lorsque autre < cet entier - * une valeur = 0 lorsque autre et cet entier sont égaux + * @param autre entier avec lequel this est comparé + * @return une {@code valeur < 0} lorsque {@code autre > cet entier} + * une {@code valeur > 0} lorsque {@code autre < cet entier} + * une {@code valeur = 0} lorsque autre et cet entier sont égaux */ public int compareTo(Entier autre) { return ((Integer) valeur).compareTo(autre.getValeur()); @@ -151,7 +124,7 @@ public class Entier extends Litteral { */ public static Entier quotient(Entier premier, Entier second) { if (second.compareTo(new Entier (0)) == 0) { - throw new ExecutionException("Erreur. Division par 0. "); + throw new ExecutionException("division par 0"); } return new Entier((int) premier.getValeur() / (int) second.getValeur()); } @@ -165,8 +138,16 @@ public class Entier extends Litteral { */ public static Entier reste(Entier premier, Entier second) { if (second.compareTo(new Entier (0)) == 0) { - throw new ExecutionException("Erreur. Division par 0. "); + throw new ExecutionException("division par 0"); } return new Entier((int) premier.getValeur() % (int) second.getValeur()); } + + /* non javadoc + * @see Litteral#compareTo(Litteral) + */ + @Override + public int compareTo(Litteral autre) { + return ((Integer)valeur).compareTo((Integer)autre.valeur); + } } diff --git a/src/interpreteurlir/donnees/litteraux/Litteral.java b/src/interpreteurlir/donnees/litteraux/Litteral.java index 953915e..fd54e2a 100644 --- a/src/interpreteurlir/donnees/litteraux/Litteral.java +++ b/src/interpreteurlir/donnees/litteraux/Litteral.java @@ -13,7 +13,7 @@ package interpreteurlir.donnees.litteraux; * @author Heïa Dexter * @author Lucas Vabre */ -public class Litteral implements Comparable { +public abstract class Litteral implements Comparable { /** valeur de ce littéral */ protected Object valeur; @@ -27,9 +27,9 @@ public class Litteral implements Comparable { /** * Initialise cette valeur avec un objet argument. - * @param valeur + * @param valeur valeur du littéral à construire */ - public Litteral(Object valeur) { // TODO public >>> protected + public Litteral(Object valeur) { super(); this.valeur = valeur; } @@ -53,11 +53,5 @@ public class Litteral implements Comparable { * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override - public int compareTo(Litteral autre) { - - if (autre.valeur.getClass() == this.valeur.getClass()) - return 0; - - return this.valeur.hashCode() - autre.valeur.hashCode(); - } + public abstract int compareTo(Litteral autre); } \ No newline at end of file diff --git a/src/interpreteurlir/donnees/litteraux/tests/TestBooleen.java b/src/interpreteurlir/donnees/litteraux/tests/TestBooleen.java new file mode 100644 index 0000000..60ed3ab --- /dev/null +++ b/src/interpreteurlir/donnees/litteraux/tests/TestBooleen.java @@ -0,0 +1,50 @@ +/** + * TestBooleen.java 21 mai 2021 + * IUT Rodez info1 2020-2021, pas de copyright, aucun droit + */ +package interpreteurlir.donnees.litteraux.tests; + +import interpreteurlir.donnees.litteraux.Booleen; +import static info1.outils.glg.Assertions.*; + +/** + * Tests unitaires des méthodes de la classe Booleen + * + * @author Nicolas Caminade + * @author Sylvan Courtiol + * @author Pierre Debas + * @author Heia Dexter + * @author Lucas Vabre + */ +public class TestBooleen { + + private static final Booleen[] FIXTURE = { + new Booleen(false), + new Booleen(true), + new Booleen(1<2), + new Booleen(1>=2), + new Booleen("bob".equals("bob")), + new Booleen(Character.isDigit('a')), + new Booleen(!Double.isNaN(1/0.0)), + }; + + /** + * Tests unitaire de {@link Booleen#getValeur()} + */ + public void testGetValeur() { + final Boolean[] ATTENDUS = { + false, + true, + true, + false, + true, + false, + true + }; + System.out.println("\tExécution du test de getValeur"); + for (int i = 0 ; i < ATTENDUS.length ; i++) { + assertTrue(ATTENDUS[i].compareTo(FIXTURE[i].getValeur()) + == 0); + } + } +} diff --git a/src/interpreteurlir/donnees/litteraux/tests/TestChaine.java b/src/interpreteurlir/donnees/litteraux/tests/TestChaine.java index f7f4966..2aeee55 100644 --- a/src/interpreteurlir/donnees/litteraux/tests/TestChaine.java +++ b/src/interpreteurlir/donnees/litteraux/tests/TestChaine.java @@ -10,7 +10,8 @@ import interpreteurlir.donnees.litteraux.Chaine; import static info1.outils.glg.Assertions.*; /** - * Tests unitaires de Chaine + * Tests unitaires de la classe Chaine + * * @author Nicolas Caminade * @author Sylvan Courtiol * @author Pierre Debas @@ -18,134 +19,118 @@ import static info1.outils.glg.Assertions.*; * @author Lucas Vabre */ public class TestChaine { - - - /** test de Chaine(String) */ - public static void testChaine() { - - final String[] VALIDE = { - "\"arztyehjklmpoijhghnbghjklmpoiuytrf" + - "ghjnklmpoiuytrezaqsdfghnjklmpjbfrtyu\"", - "\"\"","\"coucou \"", - "\"" + 42 + "\"" - }; - - final String INVALIDE = - "arztyehjklmpoijhghnbghjklmpoiuytrf" + - "yeryghjnklmpoiuytrezaqsdfghnjklmpjbfrtyu"; - System.out.println("test de Chaine(String)"); - - for (String aTester : VALIDE) - new Chaine(aTester); - - try { - new Chaine(INVALIDE); - throw new RuntimeException("Instanciation interdite"); - } catch (InterpreteurException lancee) { - System.out.println("Revoi d'exception OK\nfin du test"); - } - } - - /** test de compareTo */ - public static void testCompareTo() { - final Chaine[][] EGALITES = { - {new Chaine("\"coucou\""), new Chaine("\"coucou\"")}, - {new Chaine("\" \""), new Chaine("\" \"")}, - {new Chaine("\"\""), new Chaine()} - }; - - final Chaine[][] DIFFERENCES = { - {new Chaine("\"coucou\""), new Chaine("\"camomille\"")}, - {new Chaine("\"tarentule\""), new Chaine("\"coucou\"")}, - {new Chaine("\"coucou\""), new Chaine("\" \"")}, - {new Chaine("\"coucou\""), new Chaine()}, - {new Chaine("\" \""), new Chaine()} - }; - - System.out.println("test de compareTo(Chaine)\nAvec égalités"); - - for (Chaine[] couple : EGALITES) { - - try { - assert couple[0].compareTo(couple[1]) == 0; - } catch (AssertionError lancee) { - System.err.println("Echec du test"); - } - - } - - System.out.println("Avec des inégalités"); - for (Chaine[] couple : DIFFERENCES) { - try { - assert couple[0].compareTo(couple[1]) > 0; - } catch (AssertionError lancee) { - System.err.println("Echec du test"); - } - } - System.out.println("fin du test"); - - } - - /** test de toString */ - public static void testToString() { - final Chaine[] A_AFFICHER = { - new Chaine(), new Chaine("\" \""), - new Chaine("\"coucou\""), - new Chaine("\" coucou \""), - new Chaine("\"coucou monsieur\"") - }; - - final String[] AFFICHAGE_GUILLEMETS = { - "\"\"", "\" \"", "\"coucou\"", "\" coucou \"", - "\"coucou monsieur\"" - }; - - System.out.println("test de toString"); - for (int i = 0 ; i < A_AFFICHER.length ; i++) { - - try { - assert A_AFFICHER[i].toString().equals(AFFICHAGE_GUILLEMETS[i]); - } catch (AssertionError lancee) { - System.err.println("Echec du test a l'indice " + i); - } - } - System.out.println("==>test terminé\n"); - } - - /** - * Tests unitaires de concaténer - */ - public void testConcatener() { - final Chaine[] ATTENDU = { - new Chaine(), - new Chaine("\"Bonjour le monde ! \""), - new Chaine("\" \""), - new Chaine("\"3,1415\""), - }; - - final Chaine[][] A_CONCATENER = { - {new Chaine(), new Chaine("\"\"")}, - {new Chaine("\"Bonjour \""), new Chaine("\"le monde ! \"")}, - {new Chaine("\"\""), new Chaine("\" \"")}, - {new Chaine("\"3,\""), new Chaine("\"1415\"")}, - }; - - System.out.println("\tExécution du test de concaténer"); - for (int numTest = 0 ; numTest < ATTENDU.length ; numTest++ ) { - assertTrue(ATTENDU[numTest].compareTo(Chaine.concatener( - A_CONCATENER[numTest][0], A_CONCATENER[numTest][1])) - == 0); - } - } - - /** - * Lancement des tests - * @param args non utilisés - */ - public static void main(String[] args) { - testChaine(); - testCompareTo(); - testToString(); - } -} + + /** Test unitaire de {@link Chaine#Chaine(String)} */ + public static void testChaine() { + + final String[] VALIDE = { + "\"arztyehjklmpoijhghnbghjklmpoiuytrf" + + "ghjnklmpoiuytrezaqsdfghnjklmpjbfrtyu\"", + "\"\"", + "\"coucou \"", + "\"" + 42 + "\"" + }; + + final String INVALIDE = + "arztyehjklmpoijhghnbghjklmpoiuytrf" + + "yeryghjnklmpoiuytrezaqsdfghnjklmpjbfrtyu"; + + System.out.println("\tExécution du test de Chaine(String)"); + + for(String aTester : VALIDE) { + try { + new Chaine(aTester); + } catch (InterpreteurException lancee) { + echec(); + } + } + + try { + new Chaine(INVALIDE); + echec(); + } catch (InterpreteurException lancee) { + // Test OK + } + } + + /** Test unitaire de {@link Chaine#compareTo(Litteral)} */ + public static void testCompareTo() { + final Chaine[][] EGALITES = { + { new Chaine("\"coucou\""), new Chaine("\"coucou\"") }, + { new Chaine("\" \""), new Chaine("\" \"") }, + { new Chaine("\"\""), new Chaine() } + }; + + final Chaine[][] DIFFERENCES = { + { new Chaine("\"coucou\""), new Chaine("\"camomille\"") }, + { new Chaine("\"tarentule\""), new Chaine("\"coucou\"") }, + { new Chaine("\"coucou\""), new Chaine("\" \"") }, + { new Chaine("\"coucou\""), new Chaine() }, + { new Chaine("\" \""), new Chaine() } + }; + + System.out.println("\tExécution du test de compareTo(Chaine)\n" + + "\tAvec égalités"); + + for (Chaine[] couple : EGALITES) { + assertEquivalence(couple[0].compareTo(couple[1]), 0); + } + + System.out.println("\tAvec des inégalités"); + for (Chaine[] couple : DIFFERENCES) { + assertTrue(couple[0].compareTo(couple[1]) > 0); + } + } + + /** Test unitaire de {@link Chaine#toString()} */ + public static void testToString() { + final Chaine[] A_AFFICHER = { + new Chaine(), + new Chaine("\" \""), + new Chaine("\"coucou\""), + new Chaine("\" coucou \""), + new Chaine("\"coucou monsieur\"") + }; + + final String[] AFFICHAGE_GUILLEMETS = { + "\"\"", + "\" \"", + "\"coucou\"", + "\" coucou \"", + "\"coucou monsieur\"" + }; + + System.out.println("\tExécution du test de toString"); + for (int i = 0 ; i < A_AFFICHER.length ; i++) { + assertTrue(AFFICHAGE_GUILLEMETS[i] + .equals(A_AFFICHER[i].toString())); + } + } + + /** + * Tests unitaires de concaténer + */ + public static void testConcatener() { + final Chaine[] ATTENDU = { + new Chaine(), + new Chaine("\"Bonjour le monde ! \""), + new Chaine("\" \""), + new Chaine("\"3,1415\""), + }; + + final Chaine[][] A_CONCATENER = { + { new Chaine(), new Chaine("\"\"") }, + { new Chaine("\"Bonjour \""), new Chaine("\"le monde ! \"") }, + { new Chaine("\"\""), new Chaine("\" \"") }, + { new Chaine("\"3,\""), new Chaine("\"1415\"") }, + }; + + System.out.println("\tExécution du test de concaténer"); + for (int numTest = 0 ; numTest < ATTENDU.length ; numTest++ ) { + assertTrue(ATTENDU[numTest].compareTo( + Chaine.concatener(A_CONCATENER[numTest][0], + A_CONCATENER[numTest][1])) == 0); + } + } +} \ No newline at end of file diff --git a/src/interpreteurlir/donnees/litteraux/tests/TestEntier.java b/src/interpreteurlir/donnees/litteraux/tests/TestEntier.java index bc1ca7f..db064fc 100644 --- a/src/interpreteurlir/donnees/litteraux/tests/TestEntier.java +++ b/src/interpreteurlir/donnees/litteraux/tests/TestEntier.java @@ -40,22 +40,6 @@ public class TestEntier { new Entier(179892), }; - /** Jeu d'entiers correctement instanciés */ - private final Entier[] ENTIERS_STRING = { - - new Entier("1"), - new Entier("-4587"), - new Entier("-569"), - new Entier("-3"), - new Entier("0"), - new Entier("2"), - new Entier("78"), - new Entier("781"), - new Entier("179892"), - new Entier("-2147483648"), - new Entier("2147483647"), - }; - /** Jeu d'integers correspondants */ private static final int[] INT_VALIDES = { MIN_VALUE, @@ -94,6 +78,7 @@ public class TestEntier { "+", }; + System.out.println("\tExécution du test de Entier(String)"); for (int i = 0; i < INVALIDES.length; i++) { try { new Entier(INVALIDES[i]); @@ -108,6 +93,7 @@ public class TestEntier { * Test unitaire de la méthode toString() */ public void testToString() { + System.out.println("\tExécution du test de Entier(String)"); for (int i = 0; i < INT_VALIDES.length; i ++) { assertTrue(ENTIERS_INT[i].toString() .compareTo(Integer.toString(INT_VALIDES[i])) == 0); @@ -115,12 +101,12 @@ public class TestEntier { } /** - * Test unitaire de la méthode toCompareTo() + * Test unitaire de la méthode compareTo() */ public void testCompareTo() { final Entier REF_MIN = new Entier(MIN_VALUE); final Entier REF_MAX = new Entier(MAX_VALUE); - + System.out.println("\tExécution du test de compareTo()"); for (int i = 2; i < ENTIERS_INT.length; i++) { assertTrue(REF_MIN.compareTo(ENTIERS_INT[i]) < 0); assertTrue(REF_MAX.compareTo(ENTIERS_INT[i]) > 0); @@ -149,14 +135,14 @@ public class TestEntier { 781, 179892, }; - + System.out.println("\tExécution du test de getValeur()"); for (int i = 0; i < ENTIERS_INT.length; i++) { assertTrue(ENTIERS_INT[i].getValeur().compareTo(ATTENDUS[i]) == 0); } } /** - * Test unitaire de la méthode somme() + * Test unitaire de la méthode somme(Entier, Entier) */ public void testSomme() { final Entier[] ATTENDUS = { @@ -172,7 +158,7 @@ public class TestEntier { new Entier(1562), new Entier(359784), }; - + System.out.println("\tExécution du test de somme(Entier, Entier)"); for (int i = 0; i < ENTIERS_INT.length; i++) { assertTrue(somme(ENTIERS_INT[i], ENTIERS_INT[i]) .compareTo(ATTENDUS[i]) == 0); @@ -184,7 +170,7 @@ public class TestEntier { */ public void testSoustrait() { Entier zero = new Entier(0); - + System.out.println("\tExécution du test de soustrait(Entier, Entier)"); for (int i = 0; i < ENTIERS_INT.length; i++) { assertTrue(soustrait(ENTIERS_INT[i], ENTIERS_INT[i]) .compareTo(zero) == 0); @@ -208,7 +194,7 @@ public class TestEntier { new Entier(781 * 781), new Entier(179892 * 179892), }; - + System.out.println("\tExécution du test de multiplie(Entier, Entier)"); for (int i = 0; i < ENTIERS_INT.length; i++) { assertTrue(multiplie(ENTIERS_INT[i], ENTIERS_INT[i]) .compareTo(ATTENDUS[i]) == 0); @@ -234,7 +220,7 @@ public class TestEntier { new Entier(390), new Entier(89946) }; - + System.out.println("\tExécution du test de quotient(Entier, Entier)"); for (int i = 0; i < ENTIERS_INT.length; i++) { assertTrue(quotient(ENTIERS_INT[i], DIVISEUR) .compareTo(ATTENDUS[i]) == 0); @@ -246,7 +232,8 @@ public class TestEntier { */ public void testQuotientParZero() { final Entier DIVISEUR = new Entier(0); - + System.out.println("\tExécution du test de " + + "quotient(Entier, Entier) par 0"); for (int i = 0; i < ENTIERS_INT.length; i++) { try { quotient(ENTIERS_INT[i], DIVISEUR); @@ -276,7 +263,7 @@ public class TestEntier { new Entier(1), new Entier(0) }; - + System.out.println("\tExécution du test de reste(Entier, Entier)"); for (int i = 0; i < ENTIERS_INT.length; i++) { assertTrue(reste(ENTIERS_INT[i], DIVISEUR) .compareTo(ATTENDUS[i]) == 0); @@ -288,7 +275,8 @@ public class TestEntier { */ public void testResteParZero() { final Entier DIVISEUR = new Entier(0); - + System.out.println("\tExécution du test de " + + "reste(Entier, Entier) par 0"); for (int i = 0; i < ENTIERS_INT.length; i++) { try { reste(ENTIERS_INT[i], DIVISEUR); diff --git a/src/interpreteurlir/donnees/litteraux/tests/TestLitteraux.java b/src/interpreteurlir/donnees/litteraux/tests/TestLitteraux.java index d045778..fd12a00 100644 --- a/src/interpreteurlir/donnees/litteraux/tests/TestLitteraux.java +++ b/src/interpreteurlir/donnees/litteraux/tests/TestLitteraux.java @@ -1,171 +1,172 @@ -/** - * TestLitteraux.java 7 mai 2021 - * IUT info1 2020-2021, pas de copyright, aucun droit - */ -package interpreteurlir.donnees.litteraux.tests; - -import interpreteurlir.donnees.litteraux.Litteral; - -/** - * Test unitaires des constantes littérales de l'interpréteurlir - * @author Nicolas Caminade - * @author Sylvan Courtiol - * @author Pierre Debas - * @author Heïa Dexter - * @author Lucas Vabre - */ -public class TestLitteraux { - - /** Jeux de littéraux pour test. */ - private static final Litteral[] VALIDES = { - /* Caractères */ - new Litteral('a'), - new Litteral('!'), - new Litteral('\"'), - new Litteral('1'), - new Litteral('\t'), - /* Chaînes */ - new Litteral("ceci est une chaine"), - new Litteral("bonjour"), - new Litteral(" bonjour "), - new Litteral(""), - new Litteral(" "), - /* Entier */ - new Litteral(123), - new Litteral(-123), - new Litteral(0), - new Litteral(Integer.MAX_VALUE), - /* Double */ - new Litteral(14.258), - new Litteral(-14.128), - new Litteral(0.0), - new Litteral(Double.NaN), - new Litteral(Double.NEGATIVE_INFINITY), - new Litteral(Double.MAX_VALUE), - new Litteral(Double.MIN_VALUE), - new Litteral(Double.MIN_NORMAL), - /* Boolean */ - new Litteral(true), - new Litteral(false), - new Litteral(3 >= 4), - new Litteral(true), - new Litteral(true), - new Litteral(true) - }; - - /** test de getValeur */ - public static void testGetValeur() { - - final Object[] VALEURS_ATTENDUES = { - 'a', '!', '\"', '1', '\t' ,"ceci est une chaîne", "bonjour", - " bonjour ", "", " ", 123, -123, 0, 2147483647, 14.258, -14.128, - 0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MAX_VALUE, - Double.MIN_VALUE, Double.MIN_NORMAL, true, false, false, true, true, - true - }; - - System.out.println("test de getValeur\n"); - - for (int i = 0 ; i < VALIDES.length ; i++) { - - try { - assert (VALIDES[i].getValeur().equals(VALEURS_ATTENDUES[i])); - } catch (AssertionError lancee) { - System.err.println("Echec du test a l'indice " + i); - } - } - System.out.println("==>test terminé\n"); - } - - /** test de toString */ - public static void testToString() { - - final String[] STRING_ATTENDUE = { - "a", "!", "\"", "1", "\t", "ceci est une chaîne", "bonjour", - " bonjour ", "", " ", "123", "-123", "0", "2147483647", "14.258", - "-14.128", "0.0", "NaN", "-Infinity", "1.7976931348623157E308", - "4.9E-324", "2.2250738585072014E-308", "true", "false", "false", - "true", "true", "true" - }; - - System.out.println("test de toString\n"); - - for (int i = 0 ; i < VALIDES.length ; i++ ) { - - try { - assert (VALIDES[i].toString().equals(STRING_ATTENDUE[i])); - } catch (AssertionError lancee) { - System.err.println("Echec du test a l'indice " + i); - } - } - System.out.println("==>test terminé\n"); - } - - /** test de compareTo */ - public static void testCompareTo() { - - final Litteral[] MEMES_TYPES = { - new Litteral('a'), - new Litteral('!'), - new Litteral('\"'), - new Litteral('Z'), - new Litteral('s'), - new Litteral("bonjour"), - new Litteral("bonjour"), - new Litteral("arar"), - new Litteral("zarar za "), - new Litteral("CAFE_BABE"), - new Litteral(123), - new Litteral(123), - new Litteral(0), - new Litteral(-123), - new Litteral(Double.MAX_VALUE), - new Litteral(Double.NaN), - new Litteral(12.3), - new Litteral(Double.NaN), - new Litteral(45.7), - new Litteral(-12.6), - new Litteral(0.0), - new Litteral(Double.MIN_NORMAL), - new Litteral(false), - new Litteral(false), - new Litteral(true), - new Litteral(true), - new Litteral(true), - new Litteral(true) - }; - - System.out.println("test de compareTo\nAvec des types identiques"); - - for (int i = 0 ; i < VALIDES.length ; i++ ) { - - try { - assert (VALIDES[i].compareTo(MEMES_TYPES[i]) == 0); - } catch (AssertionError lancee) { - System.err.println("Echec du test a l'indice " + i); - } - } - System.out.println("Avec des types différents"); - - for (int i = 0 ; i < VALIDES.length ; i++ ) { - - try { - assert (VALIDES[i].compareTo(MEMES_TYPES[MEMES_TYPES.length - - (i + 1)]) != 0); - } catch (AssertionError lancee) { - System.err.println("Echec du test a l'indice " + i); - } - } - System.out.println("==>test terminé\n"); - } - - /** - * Lancement des test - * @param args non utilisé - */ - public static void main(String[] args) { - testGetValeur(); - testToString(); - testCompareTo(); - } -} \ No newline at end of file +// Classe testée passé en abstract +///** +// * TestLitteraux.java 7 mai 2021 +// * IUT info1 2020-2021, pas de copyright, aucun droit +// */ +//package interpreteurlir.donnees.litteraux.tests; +// +//import interpreteurlir.donnees.litteraux.Litteral; +// +///** +// * Test unitaires des constantes littérales de l'interpréteurlir +// * @author Nicolas Caminade +// * @author Sylvan Courtiol +// * @author Pierre Debas +// * @author Heïa Dexter +// * @author Lucas Vabre +// */ +//public class TestLitteraux { +// +// /** Jeux de littéraux pour test. */ +// private static final Litteral[] VALIDES = { +// /* Caractères */ +// new Litteral('a'), +// new Litteral('!'), +// new Litteral('\"'), +// new Litteral('1'), +// new Litteral('\t'), +// /* Chaînes */ +// new Litteral("ceci est une chaine"), +// new Litteral("bonjour"), +// new Litteral(" bonjour "), +// new Litteral(""), +// new Litteral(" "), +// /* Entier */ +// new Litteral(123), +// new Litteral(-123), +// new Litteral(0), +// new Litteral(Integer.MAX_VALUE), +// /* Double */ +// new Litteral(14.258), +// new Litteral(-14.128), +// new Litteral(0.0), +// new Litteral(Double.NaN), +// new Litteral(Double.NEGATIVE_INFINITY), +// new Litteral(Double.MAX_VALUE), +// new Litteral(Double.MIN_VALUE), +// new Litteral(Double.MIN_NORMAL), +// /* Boolean */ +// new Litteral(true), +// new Litteral(false), +// new Litteral(3 >= 4), +// new Litteral(true), +// new Litteral(true), +// new Litteral(true) +// }; +// +// /** test de getValeur */ +// public static void testGetValeur() { +// +// final Object[] VALEURS_ATTENDUES = { +// 'a', '!', '\"', '1', '\t' ,"ceci est une chaîne", "bonjour", +// " bonjour ", "", " ", 123, -123, 0, 2147483647, 14.258, -14.128, +// 0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MAX_VALUE, +// Double.MIN_VALUE, Double.MIN_NORMAL, true, false, false, true, true, +// true +// }; +// +// System.out.println("test de getValeur\n"); +// +// for (int i = 0 ; i < VALIDES.length ; i++) { +// +// try { +// assert (VALIDES[i].getValeur().equals(VALEURS_ATTENDUES[i])); +// } catch (AssertionError lancee) { +// System.err.println("Echec du test a l'indice " + i); +// } +// } +// System.out.println("==>test terminé\n"); +// } +// +// /** test de toString */ +// public static void testToString() { +// +// final String[] STRING_ATTENDUE = { +// "a", "!", "\"", "1", "\t", "ceci est une chaîne", "bonjour", +// " bonjour ", "", " ", "123", "-123", "0", "2147483647", "14.258", +// "-14.128", "0.0", "NaN", "-Infinity", "1.7976931348623157E308", +// "4.9E-324", "2.2250738585072014E-308", "true", "false", "false", +// "true", "true", "true" +// }; +// +// System.out.println("test de toString\n"); +// +// for (int i = 0 ; i < VALIDES.length ; i++ ) { +// +// try { +// assert (VALIDES[i].toString().equals(STRING_ATTENDUE[i])); +// } catch (AssertionError lancee) { +// System.err.println("Echec du test a l'indice " + i); +// } +// } +// System.out.println("==>test terminé\n"); +// } +// +// /** test de compareTo */ +// public static void testCompareTo() { +// +// final Litteral[] MEMES_TYPES = { +// new Litteral('a'), +// new Litteral('!'), +// new Litteral('\"'), +// new Litteral('Z'), +// new Litteral('s'), +// new Litteral("bonjour"), +// new Litteral("bonjour"), +// new Litteral("arar"), +// new Litteral("zarar za "), +// new Litteral("CAFE_BABE"), +// new Litteral(123), +// new Litteral(123), +// new Litteral(0), +// new Litteral(-123), +// new Litteral(Double.MAX_VALUE), +// new Litteral(Double.NaN), +// new Litteral(12.3), +// new Litteral(Double.NaN), +// new Litteral(45.7), +// new Litteral(-12.6), +// new Litteral(0.0), +// new Litteral(Double.MIN_NORMAL), +// new Litteral(false), +// new Litteral(false), +// new Litteral(true), +// new Litteral(true), +// new Litteral(true), +// new Litteral(true) +// }; +// +// System.out.println("test de compareTo\nAvec des types identiques"); +// +// for (int i = 0 ; i < VALIDES.length ; i++ ) { +// +// try { +// assert (VALIDES[i].compareTo(MEMES_TYPES[i]) == 0); +// } catch (AssertionError lancee) { +// System.err.println("Echec du test a l'indice " + i); +// } +// } +// System.out.println("Avec des types différents"); +// +// for (int i = 0 ; i < VALIDES.length ; i++ ) { +// +// try { +// assert (VALIDES[i].compareTo(MEMES_TYPES[MEMES_TYPES.length +// - (i + 1)]) != 0); +// } catch (AssertionError lancee) { +// System.err.println("Echec du test a l'indice " + i); +// } +// } +// System.out.println("==>test terminé\n"); +// } +// +// /** +// * Lancement des test +// * @param args non utilisé +// */ +// public static void main(String[] args) { +// testGetValeur(); +// testToString(); +// testCompareTo(); +// } +//} \ No newline at end of file diff --git a/src/interpreteurlir/donnees/tests/TestIdentificateur.java b/src/interpreteurlir/donnees/tests/TestIdentificateur.java index 0d42314..e288928 100644 --- a/src/interpreteurlir/donnees/tests/TestIdentificateur.java +++ b/src/interpreteurlir/donnees/tests/TestIdentificateur.java @@ -1,115 +1,118 @@ -/** - * TestIdentificateur.java 8 mai 2021 - * IUT-Rodez info1 2020-2021, pas de droits, pas de copyrights - */ -package interpreteurlir.donnees.tests; +// Classe testée passé en abstract -import static info1.outils.glg.Assertions.*; +///** +// * TestIdentificateur.java 8 mai 2021 +// * IUT-Rodez info1 2020-2021, pas de droits, pas de copyrights +// */ +//package interpreteurlir.donnees.tests; +// +//import static info1.outils.glg.Assertions.*; +// +//import interpreteurlir.InterpreteurException; +//import interpreteurlir.donnees.Identificateur; +// +///** +// * Test de la classe donnees.Identificateur +// * @author Nicolas Caminade +// * @author Sylvan Courtiol +// * @author Pierre Debas +// * @author Heia Dexter +// * @author Lucas Vabre +// */ +//public class TestIdentificateur { +// +// /** Jeu d'identificateurs correctement instanciés */ +// public static final Identificateur[] FIXTURE = { +// new Identificateur("b"), +// new Identificateur("A"), +// new Identificateur("zalpha"), +// new Identificateur("Alpha"), +// new Identificateur("Alpha5"), +// new Identificateur("jeSuisUnTresLongIdentifi"), +// new Identificateur("$b"), +// new Identificateur("z"), +// new Identificateur("$zalpha"), +// new Identificateur("$Alpha"), +// new Identificateur("$Alpha5"), +// new Identificateur("$jeSuisUnTresLongIdentifi") +// +// }; +// +// /** +// * Test de Identificateur(String identificateur) +// */ +// public static void testIdentificateurString() { +// final String[] INVALIDE = { +// null, +// "", +// +// // Fait au maximum 25 caractères +// "$jeSuisUnTresLongIdentificateur", // 30 char +// "$jeSuisUnTresLongIdentifica", +// +// // Espaces +// "id 3a", +// "$id 3a", +// " ", +// "$ ", +// +// // caractères d'échapements +// "\t", +// "\n", +// "$\t", +// "$\n", +// +// // , cas particulier +// "$" +// }; +// +// for(int noJeu = 0 ; noJeu < INVALIDE.length ; noJeu++) { +// try { +// new Identificateur(INVALIDE[noJeu]); +// echec(); +// } catch (InterpreteurException lancee) { +// // Test OK +// } +// } +// } +// +// /** +// * Test de compareTo(Identificateur aComparer) +// */ +// public static void testCompareTo() { +// final Identificateur REF_MIN = new Identificateur("$AAAAAAAAAAAAAAAAAAAAAAAA"); +// final Identificateur REF_MAX = new Identificateur("zzzzzzzzzzzzzzzzzzzzzzzz"); +// +// for(int noJeu = 0; noJeu < FIXTURE.length; noJeu++) { +// assertTrue(FIXTURE[noJeu].compareTo(REF_MIN) >= 0); +// assertTrue(FIXTURE[noJeu].compareTo(REF_MAX) <= 0); +// assertTrue(FIXTURE[noJeu].compareTo(FIXTURE[noJeu]) == 0); +// } +// } +// +// /** +// * Tests unitaires de toString +// */ +// public static void testToString() { +// final String[] CHAINES_VALIDES = { +// "b", +// "A", +// "zalpha", +// "Alpha", +// "Alpha5", +// "jeSuisUnTresLongIdentifi", +// "$b", +// "z", +// "$zalpha", +// "$Alpha", +// "$Alpha5", +// "$jeSuisUnTresLongIdentifi" +// }; +// +// for (int noJeu = 0 ; noJeu < CHAINES_VALIDES.length ; noJeu++) { +// assertEquivalence(CHAINES_VALIDES[noJeu], +// FIXTURE[noJeu].toString()); +// } +// } +//} -import interpreteurlir.InterpreteurException; -import interpreteurlir.donnees.Identificateur; - -/** - * Test de la classe donnees.Identificateur - * @author Nicolas Caminade - * @author Sylvan Courtiol - * @author Pierre Debas - * @author Heia Dexter - * @author Lucas Vabre - */ -public class TestIdentificateur { - - /** Jeu d'identificateurs correctement instanciés */ - public static final Identificateur[] FIXTURE = { - new Identificateur("b"), - new Identificateur("A"), - new Identificateur("zalpha"), - new Identificateur("Alpha"), - new Identificateur("Alpha5"), - new Identificateur("jeSuisUnTresLongIdentifi"), - new Identificateur("$b"), - new Identificateur("z"), - new Identificateur("$zalpha"), - new Identificateur("$Alpha"), - new Identificateur("$Alpha5"), - new Identificateur("$jeSuisUnTresLongIdentifi") - - }; - - /** - * Test de Identificateur(String identificateur) - */ - public static void testIdentificateurString() { - final String[] INVALIDE = { - null, - "", - - // Fait au maximum 25 caractères - "$jeSuisUnTresLongIdentificateur", // 30 char - "$jeSuisUnTresLongIdentifica", - - // Espaces - "id 3a", - "$id 3a", - " ", - "$ ", - - // caractères d'échapements - "\t", - "\n", - "$\t", - "$\n", - - // , cas particulier - "$" - }; - - for(int noJeu = 0 ; noJeu < INVALIDE.length ; noJeu++) { - try { - new Identificateur(INVALIDE[noJeu]); - echec(); - } catch (InterpreteurException lancee) { - // Test OK - } - } - } - - /** - * Test de compareTo(Identificateur aComparer) - */ - public static void testCompareTo() { - final Identificateur REF_MIN = new Identificateur("$AAAAAAAAAAAAAAAAAAAAAAAA"); - final Identificateur REF_MAX = new Identificateur("zzzzzzzzzzzzzzzzzzzzzzzz"); - - for(int noJeu = 0; noJeu < FIXTURE.length; noJeu++) { - assertTrue(FIXTURE[noJeu].compareTo(REF_MIN) >= 0); - assertTrue(FIXTURE[noJeu].compareTo(REF_MAX) <= 0); - assertTrue(FIXTURE[noJeu].compareTo(FIXTURE[noJeu]) == 0); - } - } - - /** - * Tests unitaires de toString - */ - public static void testToString() { - final String[] CHAINES_VALIDES = { - "b", - "A", - "zalpha", - "Alpha", - "Alpha5", - "jeSuisUnTresLongIdentifi", - "$b", - "z", - "$zalpha", - "$Alpha", - "$Alpha5", - "$jeSuisUnTresLongIdentifi" - }; - - for (int noJeu = 0 ; noJeu < CHAINES_VALIDES.length ; noJeu++) { - assertEquivalence(CHAINES_VALIDES[noJeu], - FIXTURE[noJeu].toString()); - } - } -} diff --git a/src/interpreteurlir/donnees/tests/TestIdentificateurChaine.java b/src/interpreteurlir/donnees/tests/TestIdentificateurChaine.java index f507c16..16dcfdf 100644 --- a/src/interpreteurlir/donnees/tests/TestIdentificateurChaine.java +++ b/src/interpreteurlir/donnees/tests/TestIdentificateurChaine.java @@ -31,11 +31,11 @@ public class TestIdentificateurChaine { }; /** - * Tests unitaires du constructeur IdentificateurEntier(String identificateur) + * Tests unitaires du constructeur + * IdentificateurEntier(String identificateur) */ public static void testIdentificateurChaineString() { final String[] INVALIDE = { - null, "", // Commence par une lettre @@ -62,7 +62,8 @@ public class TestIdentificateurChaine { "$", "$1" }; - + System.out.println("\tExécution du test de " + + "IdentificateurEntier(String identificateur)"); for(int noJeu = 0; noJeu < INVALIDE.length ; noJeu++) { try { new IdentificateurChaine(INVALIDE[noJeu]); @@ -87,6 +88,7 @@ public class TestIdentificateurChaine { "$jeSuisUnTresLongIdentifi" }; + System.out.println("\tExécution du test de getNom()"); for (int noJeu = 0 ; noJeu < NOM_VALIDES.length ; noJeu++) { assertEquivalence(NOM_VALIDES[noJeu], FIXTURE[noJeu].getNom()); } diff --git a/src/interpreteurlir/donnees/tests/TestVariable.java b/src/interpreteurlir/donnees/tests/TestVariable.java index ff3e91a..66a8ae6 100644 --- a/src/interpreteurlir/donnees/tests/TestVariable.java +++ b/src/interpreteurlir/donnees/tests/TestVariable.java @@ -63,36 +63,24 @@ public class TestVariable { new Chaine("\"titi\n\"") }; -// /** Jeu d'entiers valides */ -// private static final Entier[] VALEUR_ENTIER = { -// // TODO: jeu d'entiers valide -// }; - /** Jeu de variables chaîne valides*/ - private static Variable[] fixtureChaine = new Variable[ID_CHAINE.length]; - - /* Jeu de variables entières valides*/ - //private static Variable[] fixtureEntier= new Variable[ID_ENTIER.length]; + private static Variable[] fixtureChaine = new Variable[ID_CHAINE.length]; private static void fixtureReload() { for (int i = 0; i < ID_CHAINE.length; i++) { fixtureChaine[i] = new Variable(ID_CHAINE[i], VALEURS_CHAINE[i]); } - - //TODO reload fixtureEntier } /** * Test unitaire du constructeur Variable(Identificateur, Littéral) */ public static void testVariableIdentificateurChaineLitteral() { - + System.out.println("\tExécution du test de " + + "Variable(Identificateur, Littéral)"); for (int noJeu = 0; noJeu < VALEURS_CHAINE.length; noJeu++) { try { - //new Variable(ID_CHAINE[noJeu], VALEURS_CHAINE[noJeu]); // bouchon new Variable(ID_ENTIER[noJeu], VALEURS_CHAINE[noJeu]); - // TODO tester avec la classe Entier - // new Variable(ID_CHAINE[noJeu], VALEURS_ENTIER[noJeu]); echec(); } catch (InterpreteurException lancee) { // test OK @@ -106,32 +94,21 @@ public class TestVariable { public static void testGetIdentificateurChaine() { fixtureReload(); + System.out.println("\tExécution du test de getIdentificateur()"); + for (int i = 0; i < VALEURS_CHAINE.length; i++ ) { assertTrue(ID_CHAINE[i].compareTo(fixtureChaine[i] .getIdentificateur()) == 0); } } - /** - * Test unitaire de getIdentificateur() d'une variable entière - */ - public static void testGetIdentificateurEntier() { -// fixtureReload(); -// -// for (int i = 0; i < VALEURS_Entier.length; i++ ) { -// assertTrue(ID_ENTIER[i].compareTo(fixtureEntier[i] -// .getIdentificateur()) == 0); -// } - - echec(); - } - /** * Test unitaire de getValeur() d'une variable chaîne */ public static void testGetValeurChaine() { fixtureReload(); + System.out.println("\tExécution du test de getValeur()"); for (int i = 0; i < VALEURS_CHAINE.length; i++ ) { assertTrue(VALEURS_CHAINE[i] .compareTo(fixtureChaine[i].getValeur()) == 0); @@ -153,6 +130,7 @@ public class TestVariable { new Chaine("\"-5 + 962\"") }; + System.out.println("\tExécution du test de setValeur()"); for (int i = 0; i < NOUVELLE_CHAINE.length; i++) { fixtureChaine[i].setValeur(NOUVELLE_CHAINE[i]); assertTrue(NOUVELLE_CHAINE[i] @@ -178,7 +156,7 @@ public class TestVariable { "$MichelSardou = \"tata\t\"", "$PhilippePoutou2022 = \"titi\n\"" }; - + System.out.println("\tExécution du test de toString()"); for (int noJeu = 0; noJeu < fixtureChaine.length; noJeu++ ) { assertEquivalence(fixtureChaine[noJeu].toString(), ATTENDUS[noJeu]); @@ -199,12 +177,12 @@ public class TestVariable { = new Variable(new IdentificateurChaine("$z"), new Chaine("\"Max\"")); + System.out.println("\tExécution du test de compareTo"); for(int noJeu = 0; noJeu < fixtureChaine.length; noJeu++) { assertTrue(fixtureChaine[noJeu].compareTo(REF_MIN) > 0); assertTrue(fixtureChaine[noJeu].compareTo(REF_MAX) < 0); assertTrue(fixtureChaine[noJeu].compareTo(fixtureChaine[noJeu]) == 0); } - // TODO Faire le même test pour les variables contenant des entiers } } diff --git a/src/interpreteurlir/expressions/Expression.java b/src/interpreteurlir/expressions/Expression.java index 70b97dc..fc14f23 100644 --- a/src/interpreteurlir/expressions/Expression.java +++ b/src/interpreteurlir/expressions/Expression.java @@ -19,7 +19,7 @@ import interpreteurlir.donnees.litteraux.Litteral; * @author Heïa Dexter * @author Lucas Vabre */ -public class Expression { +public abstract class Expression { /** Index de l'operande gauche */ protected static final int INDEX_OPERANDE_G = 0; @@ -29,6 +29,12 @@ public class Expression { /** Index de de l'identificateur pour l'affectation */ protected static final int INDEX_AFFECTATION = 2; + + /** Index du premier symbole de l'opérateur */ + protected static final int INDEX_OPERATEUR_G = 0; + + /** Index du second symbole de l'opérateur */ + protected static final int INDEX_OPERATEUR_D = 1; /** Contexte global pour accéder aux données. */ protected static Contexte contexteGlobal; @@ -39,6 +45,12 @@ public class Expression { /** Littéraux opérandes de cette expression */ protected Litteral[] litterauxOperandes; + /** + * Opérateur de cette expression potentiellement + * composé de plusieurs symboles + */ + protected char[] operateur; + /** * Initialise une expression par défaut avec les liens nécessaires à * son calcul. @@ -48,6 +60,10 @@ public class Expression { final int NB_IDENTIFICATEUR = 3; final int NB_LITTERAL = 2; + operateur = new char[2]; + operateur[INDEX_OPERATEUR_G] = '\u0000'; + operateur[INDEX_OPERATEUR_D] = '\u0000'; + identificateursOperandes = new Identificateur[NB_IDENTIFICATEUR]; litterauxOperandes = new Litteral[NB_LITTERAL]; } @@ -77,20 +93,28 @@ public class Expression { Identificateur affect = identificateursOperandes[INDEX_AFFECTATION]; resultat.append(affect == null ? "" : (affect.toString() + " = ")); - Identificateur gaucheId = identificateursOperandes[INDEX_OPERANDE_G]; - Litteral gaucheLitteral = litterauxOperandes[INDEX_OPERANDE_G]; - resultat.append(gaucheId != null ? gaucheId.toString() - : gaucheLitteral.toString()); + if (litterauxOperandes[INDEX_OPERANDE_G] != null) { + resultat.append(litterauxOperandes[INDEX_OPERANDE_G]); + } else { + resultat.append(identificateursOperandes[INDEX_OPERANDE_G]); + } + resultat.append(" "); - Identificateur droiteId = identificateursOperandes[INDEX_OPERANDE_D]; - Litteral droiteLitteral = litterauxOperandes[INDEX_OPERANDE_D]; - if (droiteId != null || droiteLitteral != null) { - resultat.append(" + "); - resultat.append(droiteId != null ? droiteId.toString() - : droiteLitteral.toString()); + if (operateur[INDEX_OPERATEUR_G] != '\u0000') { + resultat.append(operateur[INDEX_OPERATEUR_G]); + } + if (operateur[INDEX_OPERATEUR_D] != '\u0000') { + resultat.append(operateur[INDEX_OPERATEUR_D]); } - return resultat.toString(); + resultat.append(" "); + if (litterauxOperandes[INDEX_OPERANDE_D] != null) { + resultat.append(litterauxOperandes[INDEX_OPERANDE_D]); + } else if (identificateursOperandes[INDEX_OPERANDE_D] != null) { + resultat.append(identificateursOperandes[INDEX_OPERANDE_D]); + } + + return resultat.toString().trim(); } /** @@ -129,4 +153,26 @@ public class Expression { return new ExpressionEntier(aTraiter); } } + + /** + * Détermine l'index du caractère en dehors des constantes littérales + * @param aTraiter chaîne à traiter + * @param caractere opérateur à chercher hors guillemet + * @return index dans à traiter du plus sinon -1 si aucun plus + */ + public static int detecterCaractere(String aTraiter, char caractere) { + char[] aTester = aTraiter.toCharArray(); + int indexPlus; + int nbGuillemet = 0; + for (indexPlus = 0 ; + indexPlus < aTester.length + && (aTester[indexPlus] != caractere || nbGuillemet % 2 != 0) ; + indexPlus++) { + + if (aTester[indexPlus] == '"') { + nbGuillemet++; + } + } + return indexPlus >= aTester.length ? -1 : indexPlus; + } } diff --git a/src/interpreteurlir/expressions/ExpressionBooleenne.java b/src/interpreteurlir/expressions/ExpressionBooleenne.java new file mode 100644 index 0000000..bc329ee --- /dev/null +++ b/src/interpreteurlir/expressions/ExpressionBooleenne.java @@ -0,0 +1,287 @@ +/** + * ExpressionBooleenne.java 21 mai 2021 + * IUT Rodez info1 2020-2021, pas de copyright, aucun droit + */ +package interpreteurlir.expressions; + +import static interpreteurlir.donnees.IdentificateurChaine + .isIdentificateurChaine; +import static interpreteurlir.donnees.IdentificateurEntier + .isIdentificateurEntier; +import static interpreteurlir.donnees.litteraux.Entier.isEntier; +import static interpreteurlir.donnees.litteraux.Chaine.isChaine; + +import interpreteurlir.InterpreteurException; +import interpreteurlir.donnees.IdentificateurChaine; +import interpreteurlir.donnees.IdentificateurEntier; +import interpreteurlir.donnees.litteraux.*; + +/** + * Expression de type Booleen qui peut être évaluée. + *

+ * Syntaxe d'une expression logique : opérande1 oprel opérande2 + *

+ * Les expressions logiques concerneront donc toujours deux opérandes + * séparés par un opérateur relationnel (notation infixe). Un opérande + * est soit une constante, soit un identificateur. + *

+ * L'’opérateur relationnel oprel est un symbole parmi : {@code = <> < <= > >=} + * + * @author Nicolas Caminade + * @author Sylvan Courtiol + * @author Pierre Debas + * @author Heïa Dexter + * @author Lucas Vabre + */ +public class ExpressionBooleenne extends Expression { + + /** Liste des opérateurs relationnels utilisés */ + private static char[] OPERATEURS = { '<', '>', '=' }; + + private static final String ERREUR_ARGUMENT = "une expression ne peut être " + + "vide"; + private static final String ERREUR_SYNTAXE = + "usage \n" + + "avec oprel comme opérateur relationnel " + + "un des symboles suivants : <> < <= > >=" + + "et comme opérandes des constantes, " + + "ou alors des identificateurs"; + private static final String ERREUR_TYPE = "opérande invalide " + + "ou type incompatible"; + private static final String ERREUR_OPERATEUR = "opérateur inconnu"; + + + /** + * Initialise une expression de type Booleen avec les liens + * nécessaires à son calcul. + * + * @param texteExpression l'expression booléenne lue sous forme + * de chaîne + * @throws InterpreteurException si l'expression est vide ou les + * types des opérandes sont incompatibles + * ou si l'opérande droit est manquant + */ + public ExpressionBooleenne(String texteExpression) { + super(); + + String gauche; + String droite; + String aTraiter; + + if (texteExpression == null || texteExpression.isBlank()) { + throw new InterpreteurException(ERREUR_ARGUMENT); + } + + aTraiter = texteExpression.trim(); + + int[] indexOperateurs = affecterOperateur(aTraiter, + detecterOperateurs(aTraiter)); + + if (indexOperateurs[INDEX_OPERATEUR_D] > aTraiter.length() - 2 + || indexOperateurs[INDEX_OPERATEUR_G] <= 0) { + throw new InterpreteurException(aTraiter + ERREUR_SYNTAXE); + } + + gauche = aTraiter.substring(0, indexOperateurs[INDEX_OPERATEUR_G]) + .trim(); + droite = aTraiter.substring(indexOperateurs[INDEX_OPERATEUR_D] + 1) + .trim(); + + if (!isMemeType(gauche, droite)) { + throw new InterpreteurException(ERREUR_TYPE); + } + + initialiserOperande(gauche, INDEX_OPERANDE_G); + initialiserOperande(droite, INDEX_OPERANDE_D); + } + + /** + * Affecte les symboles de l'opérateur à partir des index de indexOperateur + * correspondant à des caractères dans aTraiter. + * @param aTraiter chaîne contenant les opérateurs + * @param indexOperateurs index des symboles de l'opérateur. + * @return tableau d'index avec l'index de début de l'opérateur en indice 0 + * et l'index de la fin de l'opérateur en indice 1. + * Les index peuvent être égaux. + * @throws InterpreteurException si les symboles de l'opérateur ne + * se suivent pas dans aTraiter + * + */ + private int[] affecterOperateur(String aTraiter, int[] indexOperateurs) { + operateur[INDEX_OPERATEUR_G] = indexOperateurs[INDEX_OPERATEUR_G] <= 0 + ? '\u0000' + : aTraiter.charAt(indexOperateurs[INDEX_OPERATEUR_G]); + operateur[INDEX_OPERATEUR_D] = indexOperateurs[INDEX_OPERATEUR_D] <= 0 + ? '\u0000' + : aTraiter.charAt(indexOperateurs[INDEX_OPERATEUR_D]); + + if (indexOperateurs[INDEX_OPERATEUR_G] <= 0) { + indexOperateurs[INDEX_OPERATEUR_G] = + indexOperateurs[INDEX_OPERATEUR_D]; + + } else if (indexOperateurs[INDEX_OPERATEUR_D] <= 0) { + indexOperateurs[INDEX_OPERATEUR_D] = + indexOperateurs[INDEX_OPERATEUR_G]; + } + + if (indexOperateurs[INDEX_OPERATEUR_D] + - indexOperateurs[INDEX_OPERATEUR_G] > 1 + || !isOperateurValide()) { + throw new InterpreteurException(ERREUR_OPERATEUR); + } + return indexOperateurs; + } + + /** + * Prédicat de validité de concordance des symboles + * de l'opérateur à faire un opérateur valide + * @return true si opérateur formé par les symboles est valide, false sinon + */ + private boolean isOperateurValide() { + + final String[] OPERATEUR_VALIDE = { + "<", ">", "<=", ">=", "=", "<>" + }; + + String aTester = ""; + if (operateur[INDEX_OPERATEUR_G] != '\u0000') { + aTester = aTester + operateur[INDEX_OPERATEUR_G]; + } + if (operateur[INDEX_OPERATEUR_D] != '\u0000') { + aTester = aTester + operateur[INDEX_OPERATEUR_D]; + } + + int index; + for (index = 0 ; + index < OPERATEUR_VALIDE.length + && !OPERATEUR_VALIDE[index].equals(aTester); + index++) + ; /* empty body */ + return index < OPERATEUR_VALIDE.length; + } + + /** + * Prédicat de validité de compatibilité + * entre les opérandes gauche et droite + * @param gauche opérande gauche + * @param droit opérande droit + * @return true si deux opérandes sont de même type + * sinon false + */ + private static boolean isMemeType(String gauche, String droit) { + return ( (isIdentificateurEntier(gauche) || isEntier(gauche)) + && (isIdentificateurEntier(droit) || isEntier(droit))) + || + ( (isIdentificateurChaine(gauche) || isChaine(gauche))) + && (isIdentificateurChaine(droit) || isChaine(droit)); + + } + + + /** + * Détecte les opérateurs d'une expression logique + * @param aTraiter chaine dont on cherche les opérateurs + * @return les indexes de début et de fin du premier et unique + * opérateur trouvé + * @throws InterpreteurException s'il l'opérateur est invalide + * ou inexistant + */ + private static int[] detecterOperateurs(String aTraiter) { + int[] index = new int[2]; + char charCourant; + index[INDEX_OPERATEUR_G] = -1; + index[INDEX_OPERATEUR_D] = -1; + int nbGuillemet = 0; + + for (int i = 0 ; i < aTraiter.length() - 1 ; i++) { + charCourant = aTraiter.charAt(i); + + if (charCourant == '"') { + nbGuillemet++; + } + + if (index[INDEX_OPERATEUR_G] < 0 && (nbGuillemet & 1) == 0 + && ( charCourant == OPERATEURS[0] + || charCourant == OPERATEURS[1])) { + index[INDEX_OPERATEUR_G] = i; + } else if ((nbGuillemet & 1) == 0 + && (charCourant == OPERATEURS[1] + || charCourant == OPERATEURS[2])) { + index[INDEX_OPERATEUR_D] = i; + } + } + + if (index[INDEX_OPERATEUR_G] == index[INDEX_OPERATEUR_D]) { + throw new InterpreteurException(ERREUR_OPERATEUR); + } + + return index; + } + + /** + * Initialise l'opérande à sa place dans l'Expression. + * @param operande + * @param index + * @throws IllegalArgumentException si index invalide + */ + private void initialiserOperande(String operande, int index) { + if (INDEX_OPERANDE_G != index && INDEX_OPERANDE_D != index) { + throw new IllegalArgumentException("index invalide"); + } + + if (isIdentificateurEntier(operande)) { + identificateursOperandes[index] = + new IdentificateurEntier(operande); + } else if (isIdentificateurChaine(operande)) { + identificateursOperandes[index] = + new IdentificateurChaine(operande); + } else if (isChaine(operande)) { + litterauxOperandes[index] = new Chaine(operande); + } else { + litterauxOperandes[index] = new Entier(operande); + } + } + + + /* non javadoc + * @see interpreteurlir.expressions.Expression#calculer() + */ + @Override + public Booleen calculer() { + Litteral gauche = litterauxOperandes[INDEX_OPERANDE_G] != null + ? litterauxOperandes[INDEX_OPERANDE_G] + : contexteGlobal.lireValeurVariable( + identificateursOperandes[INDEX_OPERANDE_G]); + Litteral droite = litterauxOperandes[INDEX_OPERANDE_D] != null + ? litterauxOperandes[INDEX_OPERANDE_D] + : contexteGlobal.lireValeurVariable( + identificateursOperandes[INDEX_OPERANDE_D]); + + return new Booleen(calculAvecOperateur(gauche, droite)); + } + + /** + * Calcule la valeur de l'expression selon l'opérateur + * à partir de l'opérande gauche et droite + * Les opérande doivent être du même type. + * @param gauche opérande gauche + * @param droite opérande droite + * @return true si expression true sinon false + */ + private boolean calculAvecOperateur(Litteral gauche, Litteral droite) { + boolean resultat = false; + if (operateur[INDEX_OPERATEUR_G] == OPERATEURS[0]) { + resultat = gauche.compareTo(droite) < 0; + } else if (operateur[INDEX_OPERATEUR_G] == OPERATEURS[1]) { + resultat = gauche.compareTo(droite) > 0; + } + + if (operateur[INDEX_OPERATEUR_D] == OPERATEURS[1]) { + resultat = resultat || gauche.compareTo(droite) > 0; + } else if (operateur[INDEX_OPERATEUR_D] == OPERATEURS[2]) { + resultat = resultat || gauche.compareTo(droite) == 0; + } + + return resultat; + } +} \ No newline at end of file diff --git a/src/interpreteurlir/expressions/ExpressionChaine.java b/src/interpreteurlir/expressions/ExpressionChaine.java index 9825882..f9c6f94 100644 --- a/src/interpreteurlir/expressions/ExpressionChaine.java +++ b/src/interpreteurlir/expressions/ExpressionChaine.java @@ -19,8 +19,11 @@ import interpreteurlir.donnees.litteraux.Chaine; */ public class ExpressionChaine extends Expression { + /** Opérateur possible pour ce type d'expression */ + private static final char OPERATEUR = '+'; + /** - * Initalise une expression de type Chaine + * Initialise une expression de type Chaine * avec les liens nécessaires à son calcul. * @param texteExpression texte suivant la syntaxe d'une expression * @throws InterpreteurException si texteExpression n'est pas valide @@ -50,39 +53,18 @@ public class ExpressionChaine extends Expression { } /* Traitement du nombre d'opérande */ - int indexPlus = indexOperateur(aTraiter, '+'); + int indexPlus = detecterCaractere(aTraiter, '+'); gauche = aTraiter; if (indexPlus > -1) { gauche = aTraiter.substring(0, indexPlus); droite = aTraiter.substring(indexPlus + 1, aTraiter.length()); + operateur[INDEX_OPERANDE_G] = OPERATEUR; initialiserOperande(droite, INDEX_OPERANDE_D); } initialiserOperande(gauche, INDEX_OPERANDE_G); } - /** - * Détermine l'index de l'opérateur en dehors des constantes littérales - * @param aTraiter chaîne à traiter - * @param operateur opérateur à chercher hors guillemet - * @return index dans à traiter du plus sinon -1 si aucun plus - */ - public static int indexOperateur(String aTraiter, char operateur) { - char[] aTester = aTraiter.toCharArray(); - int indexPlus; - int nbGuillemet = 0; - for (indexPlus = 0 ; - indexPlus < aTester.length - && (aTester[indexPlus] != operateur || nbGuillemet % 2 != 0) ; - indexPlus++) { - - if (aTester[indexPlus] == '"') { - nbGuillemet++; - } - } - return indexPlus >= aTester.length ? -1 : indexPlus; - } - /** * Initialise l'opérande à sa place dans l'expression. * @param operande représentation texte de l'opérande @@ -95,7 +77,7 @@ public class ExpressionChaine extends Expression { throw new IllegalArgumentException("index invalide"); } - if (operandeEstLitteral(operande)) { + if (Chaine.isChaine(operande)) { litterauxOperandes[index] = new Chaine(operande); } else { identificateursOperandes[index] = @@ -103,14 +85,6 @@ public class ExpressionChaine extends Expression { } } - /** - * Détermine le genre de l'opérande (Chaine ou IdentificateurChaine). - * @param operande représentation texte de l'opérande - * @return true si operande est du genre Litteral sinon false - */ - private static boolean operandeEstLitteral(String operande) { - return operande.trim().startsWith("\""); - } /* non javadoc * @see interpreteurlir.expressions.Expression#calculer() @@ -149,6 +123,6 @@ public class ExpressionChaine extends Expression { } return valeur; - } + } } diff --git a/src/interpreteurlir/expressions/ExpressionEntier.java b/src/interpreteurlir/expressions/ExpressionEntier.java index 98ca19b..cbcdeeb 100644 --- a/src/interpreteurlir/expressions/ExpressionEntier.java +++ b/src/interpreteurlir/expressions/ExpressionEntier.java @@ -7,11 +7,8 @@ package interpreteurlir.expressions; import interpreteurlir.ExecutionException; import interpreteurlir.InterpreteurException; import interpreteurlir.donnees.Identificateur; -import interpreteurlir.donnees.IdentificateurChaine; import interpreteurlir.donnees.IdentificateurEntier; -import interpreteurlir.donnees.litteraux.Chaine; import interpreteurlir.donnees.litteraux.Entier; -import interpreteurlir.donnees.litteraux.Litteral; /** * Expression de type Entier qui peut être calculer. @@ -32,12 +29,11 @@ public class ExpressionEntier extends Expression { /** Erreur opérande attendue */ private static final String OPERANDE_D_MANQUANT = - " : opérande droit attendu"; + " attend un opérande droit"; - private char operateur; /** - * Initalise une expression de type Entier avec les liens nécessaires à son + * Initialise une expression de type Entier avec les liens nécessaires à son * calcule. * @param texteExpression texte suivant la syntaxe d'une expression * @throws InterpreteurException si texteExpression n'est pas valide @@ -49,8 +45,9 @@ public class ExpressionEntier extends Expression { String droite; String aTraiter; - if (texteExpression == null || texteExpression.isBlank()) + if (texteExpression == null || texteExpression.isBlank()) { throw new InterpreteurException(ERREUR_VIDE); + } aTraiter = texteExpression.trim(); @@ -58,7 +55,8 @@ public class ExpressionEntier extends Expression { int indexEgal = aTraiter.indexOf('='); if (indexEgal > 0) { identificateursOperandes[INDEX_AFFECTATION] = - new IdentificateurEntier(aTraiter.substring(0, indexEgal).trim()); + new IdentificateurEntier(aTraiter.substring(0, indexEgal) + .trim()); aTraiter = aTraiter.substring(indexEgal + 1).trim(); } @@ -67,11 +65,12 @@ public class ExpressionEntier extends Expression { int indexOperateur = detecterOperateur(aTraiter); gauche = aTraiter.trim(); if (indexOperateur > 0) { - operateur = aTraiter.charAt(indexOperateur); + operateur[INDEX_OPERATEUR_G] = aTraiter.charAt(indexOperateur); gauche = aTraiter.substring(0, indexOperateur).trim(); - if (aTraiter.length() - 1 <= indexOperateur) + if (aTraiter.length() - 1 <= indexOperateur) { throw new ExecutionException(aTraiter + OPERANDE_D_MANQUANT); + } droite = aTraiter.substring(indexOperateur + 1).trim(); initialiserOperande(droite, INDEX_OPERANDE_D); @@ -88,9 +87,11 @@ public class ExpressionEntier extends Expression { */ private static int detecterOperateur(String expression) { for (int i = 1 ; i < expression.length() ; i++) { - for (char operateur : OPERATEURS) - if (operateur == expression.charAt(i)) + for (char operateur : OPERATEURS) { + if (operateur == expression.charAt(i)) { return i; + } + } } return -1; @@ -98,15 +99,15 @@ public class ExpressionEntier extends Expression { /** * Initialise l'opérande à sa place dans l'expression. - * @param droite - * @param indexOperandeD + * @param operande opérande à initialiser + * @param index de l'opérande */ private void initialiserOperande(String operande, int index) { if (INDEX_OPERANDE_G != index && INDEX_OPERANDE_D != index) { throw new IllegalArgumentException("index invalide"); } - if (operandeEstLitteral(operande)) { + if (Entier.isEntier(operande)) { litterauxOperandes[index] = new Entier(operande); } else { identificateursOperandes[index] = @@ -115,17 +116,6 @@ public class ExpressionEntier extends Expression { } - /** - * Détermine si l'opérande est un littéral de type entier - * @param operande à tester - * @return true si l'operande commence par +, - ou un chiffre, - * false dans le cas contraire. - */ - private static boolean operandeEstLitteral(String operande) { - char aTester = operande.charAt(0); - return Character.isDigit(aTester) || aTester == '-' || aTester == '+'; - } - /* non javadoc * @see interpreteurlir.expressions.Expression#calculer() */ @@ -154,7 +144,7 @@ public class ExpressionEntier extends Expression { /* Calcul de la valeur */ valeur = operandeD == null ? operandeG - : switch (operateur) { + : switch (operateur[INDEX_OPERATEUR_G]) { case '+' -> Entier.somme(operandeG, operandeD); case '-' -> Entier.soustrait(operandeG, operandeD); case '*' -> Entier.multiplie(operandeG, operandeD); @@ -172,30 +162,4 @@ public class ExpressionEntier extends Expression { return valeur; } - /* - * Non - javadoc - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - StringBuilder resultat = new StringBuilder(""); - - Identificateur affect = identificateursOperandes[INDEX_AFFECTATION]; - resultat.append(affect == null ? "" : (affect.toString() + " = ")); - - Identificateur gaucheId = identificateursOperandes[INDEX_OPERANDE_G]; - Litteral gaucheLitteral = litterauxOperandes[INDEX_OPERANDE_G]; - resultat.append(gaucheId != null ? gaucheId.toString() - : gaucheLitteral.toString()); - - Identificateur droiteId = identificateursOperandes[INDEX_OPERANDE_D]; - Litteral droiteLitteral = litterauxOperandes[INDEX_OPERANDE_D]; - if (droiteId != null || droiteLitteral != null) { - resultat.append(" " + operateur + " "); - resultat.append(droiteId != null ? droiteId.toString() - : droiteLitteral.toString()); - } - - return resultat.toString(); - } } \ No newline at end of file diff --git a/src/interpreteurlir/expressions/tests/TestExpression.java b/src/interpreteurlir/expressions/tests/TestExpression.java index 81027c0..9d464be 100644 --- a/src/interpreteurlir/expressions/tests/TestExpression.java +++ b/src/interpreteurlir/expressions/tests/TestExpression.java @@ -21,23 +21,6 @@ import interpreteurlir.Contexte; */ public class TestExpression { - /** Jeu d'essai d'expression typée */ - private Expression[] fixture = { - new ExpressionChaine("$chaine = \"texte\""), - new ExpressionChaine("$chaine=\"tata\""), - new ExpressionChaine(" $tata \t "), - new ExpressionChaine("\"une chaine de texte\""), - new ExpressionChaine("$chaine= \"toto\"+\"titi\""), - new ExpressionChaine("$chaine= $toto +\"titi\""), - new ExpressionChaine("$chaine= \"toto\"+ $titi"), - new ExpressionChaine("$chaine=$toto +$titi"), - new ExpressionChaine(" \"toto\"+\"titi\""), - new ExpressionChaine("$toto +\"titi\""), - new ExpressionChaine("\"toto\"+ $titi"), - new ExpressionChaine("$toto + $titi"), - // TODO expression entière - }; - /** * Tests unitaires de {@link Expression#referencerContexte(Contexte)} */ @@ -58,32 +41,6 @@ public class TestExpression { } } - /** - * Tests unitaires de {@link Expression#toString()} - */ - public void testToString() { - final String[] chaineAttendue = { - "$chaine = \"texte\"", - "$chaine = \"tata\"", - "$tata", - "\"une chaine de texte\"", - "$chaine = \"toto\" + \"titi\"", - "$chaine = $toto + \"titi\"", - "$chaine = \"toto\" + $titi", - "$chaine = $toto + $titi", - "\"toto\" + \"titi\"", - "$toto + \"titi\"", - "\"toto\" + $titi", - "$toto + $titi" - }; - - System.out.println("\tExécution du test de Expression#toString()"); - for (int numTest = 0 ; numTest < chaineAttendue.length ; numTest++) { - assertEquivalence(chaineAttendue[numTest], - fixture[numTest].toString()); - } - } - /** * Tests unitaires de {@link Expression#determinerTypeExpression(String)} */ diff --git a/src/interpreteurlir/expressions/tests/TestExpressionBooleenne.java b/src/interpreteurlir/expressions/tests/TestExpressionBooleenne.java new file mode 100644 index 0000000..c5aea0d --- /dev/null +++ b/src/interpreteurlir/expressions/tests/TestExpressionBooleenne.java @@ -0,0 +1,467 @@ +/** + * TestExpressionBooleenne.java 21 mai 2021 + * IUT Rodez info1 2020-2021, pas de copyright, aucun droit + */ +package interpreteurlir.expressions.tests; + +import interpreteurlir.Contexte; +import interpreteurlir.InterpreteurException; +import interpreteurlir.donnees.IdentificateurChaine; +import interpreteurlir.donnees.IdentificateurEntier; +import interpreteurlir.donnees.litteraux.Chaine; +import interpreteurlir.donnees.litteraux.Entier; +import interpreteurlir.expressions.Expression; +import interpreteurlir.expressions.ExpressionBooleenne; +import static info1.outils.glg.Assertions.*; + +/** + * Tests unitaires des méthodes de la classe ExpressionBooleenne + * + * @author Nicolas Caminade + * @author Sylvan Courtiol + * @author Pierre Debas + * @author Heia Dexter + * @author Lucas Vabre + */ +public class TestExpressionBooleenne { + + private final ExpressionBooleenne[] FIXTURE_LITTERALE = { + /* Expression logique sur des Entiers AVEC ESPACES */ + new ExpressionBooleenne("1 = 1"), // true + new ExpressionBooleenne("1 = 2"), // false + new ExpressionBooleenne("1 < 2"), + new ExpressionBooleenne("1 < 1"), + new ExpressionBooleenne("1 <> 2"), + new ExpressionBooleenne("1 <> 1"), + new ExpressionBooleenne("1 <= 1"), + new ExpressionBooleenne("1 <= 5"), + new ExpressionBooleenne("1 > -3"), + new ExpressionBooleenne("1 > 56"), + new ExpressionBooleenne("1 >= 1"), + new ExpressionBooleenne("1 >= 45"), + /* Expression logique sur des Entiers SANS ESPACES */ + new ExpressionBooleenne("1=1"), + new ExpressionBooleenne("1=2"), + new ExpressionBooleenne("1<2"), + new ExpressionBooleenne("1<1"), + new ExpressionBooleenne("1<>2"), + new ExpressionBooleenne("1<>1"), + new ExpressionBooleenne("1<=1"), + new ExpressionBooleenne("1<=5"), + new ExpressionBooleenne("1>-3"), + new ExpressionBooleenne("1>56"), + new ExpressionBooleenne("1>=1"), + new ExpressionBooleenne("1>=45"), + /* Expression logique sur des Entiers MOITIE ESPACES */ + new ExpressionBooleenne(" 1=1"), + new ExpressionBooleenne("1=2 "), + new ExpressionBooleenne("1 <2"), + new ExpressionBooleenne("1< 1"), + new ExpressionBooleenne("1 <>2"), + new ExpressionBooleenne("1<> 1"), + new ExpressionBooleenne(" 1<=1 "), + new ExpressionBooleenne("1 <= 5"), + new ExpressionBooleenne(" 1 > -3 "), + new ExpressionBooleenne("1>56\t"), + new ExpressionBooleenne(" 1 >= 1 "), + new ExpressionBooleenne(" 1 >= 45 "), + + /* Expression logique sur des Chaines AVEC ESPACES */ + new ExpressionBooleenne("\"TATA\" = \"TATA\""), + new ExpressionBooleenne("\"TATA\" = \"TITI\""), + new ExpressionBooleenne("\"TATA\" < \"TITI\""), + new ExpressionBooleenne("\"TOTO\" < \"TITI\""), + new ExpressionBooleenne("\"TOTO\" <> \"TATA\""), + new ExpressionBooleenne("\"TATA\" <> \"TATA\""), + new ExpressionBooleenne("\"TATA\" <= \"TATA\""), + new ExpressionBooleenne("\"TITI\" <= \"TATA\""), + new ExpressionBooleenne("\"TATA\" > \"FOO BAR\""), + new ExpressionBooleenne("\"FOO BAR\" > \"TATA\""), + new ExpressionBooleenne("\"TATA\" >= \"TATA\""), + new ExpressionBooleenne("\"FOO BAR\" >= \"TATA\""), + /* Expression logique sur des Chaines SANS ESPACES */ + new ExpressionBooleenne("\"TATA\"=\"TATA\""), + new ExpressionBooleenne("\"TATA\"=\"TITI\""), + new ExpressionBooleenne("\"TATA\"<\"TITI\""), + new ExpressionBooleenne("\"TOTO\"<\"TITI\""), + new ExpressionBooleenne("\"TOTO\"<>\"TATA\""), + new ExpressionBooleenne("\"TATA\"<>\"TATA\""), + new ExpressionBooleenne("\"TATA\"<=\"TATA\""), + new ExpressionBooleenne("\"TITI\"<=\"TATA\""), + new ExpressionBooleenne("\"TATA\">\"FOO BAR\""), + new ExpressionBooleenne("\"FOO BAR\">\"TATA\""), + new ExpressionBooleenne("\"TATA\">=\"TATA\""), + new ExpressionBooleenne("\"FOO BAR\">=\"TATA\""), + /* Expression logique sur des Chaines MOITIE ESPACES */ + new ExpressionBooleenne(" \"TATA\" = \"TATA\""), + new ExpressionBooleenne("\"TATA\" = \"TITI\""), + new ExpressionBooleenne("\"TATA\" < \"TITI\""), + new ExpressionBooleenne("\"TOTO\" < \"TITI\" "), + new ExpressionBooleenne(" \"TOTO\"<> \"TATA\" "), + new ExpressionBooleenne("\"TATA\" <> \"TATA\" "), + new ExpressionBooleenne(" \"TATA\" <=\"TATA\""), + new ExpressionBooleenne("\"TITI\" <= \"TATA\""), + new ExpressionBooleenne(" \"TATA\" > \"FOO BAR\""), + new ExpressionBooleenne(" \"FOO BAR \" > \"TATA\""), + new ExpressionBooleenne("\"TATA\" >= \"TATA\""), + new ExpressionBooleenne(" \"FOO BAR\" >= \"TATA\""), + /* Expression logique sur des Chaines AVEC OPERATEURS */ + new ExpressionBooleenne("\"FOO BAR\"<>\"TATA=TOTO\""), + new ExpressionBooleenne("\"FOO BAR=FLEMME\">\"TOTO\""), + new ExpressionBooleenne("\"FOO BAR > FLEMME\"=\"TOTO\""), + new ExpressionBooleenne("\"FOO BAR<>FLEMME\">\"TOTO\""), + + }; + + private final ExpressionBooleenne[] FIXTURE_ID = { + /* Expression logique sur des IdEntier et Entiers */ + new ExpressionBooleenne("marcel <= 10"), // true + new ExpressionBooleenne("marcel > j34n"), // false + new ExpressionBooleenne("2 = pi3rr3"), + new ExpressionBooleenne("j34n = pi3rr3"), + /* Expression logique sur des IdChaine et Chaines */ + new ExpressionBooleenne("$sanchis < $barrios"), + new ExpressionBooleenne("$servieres > \"Windows\""), + new ExpressionBooleenne("$barrios <> $servieres"), + new ExpressionBooleenne("\"coucou\" = $barrios"), + }; + + /** + * Tests unitaire de {@link ExpressionBooleenne#ExpressionBooleenne(String)} + */ + public void testExpressionBooleenne() { + + final String[] INVALIDES = { + /* Pas d'opérateur */ + "", + "2 5", + "\"John Doe\"", + "\"Foo bar\" $serpillere", + "entier -20", + /* Opérateurs invalides */ + "-89 + 67", + "-8979 % 7", + "35 * 12", + "89 / 12", + "65 - 74", + "\"Foo bar\" + $serpillere", + "ab >> cd", + /* Expressions logiques avec opérateurs invalides */ + "78 < = 45", + "entier > = 56", + "\"Foo bar\" < > $serpillere", + "$coucou >< $dollarchaine", + "78 => 45", + "32 =< 61", + "32 == 61", + /* Plus de 2 opérandes et 1 opérateur */ + "65 <> 45 = 45", + "entier > 85 && 45 = 12", + "entier <= 85 || 45 <> 12", + /* Caractères entre les opérandes et l'opérateur */ + "\"Foo bar\" . > $serpillere", + "\"Foo bar\" < _ $serpillere", + "\"Foo bar\" + $balai > serpillere", + /* opérande manquant */ + ">= entier", + "entier =", + "<>", + /* Incompatibilité entre types d'opérandes */ + "\"Foo bar\" > serpillere", + "serpillere <> \"Foo bar\"", + "15 > $coucou", + "\"coucou\" <> 45", + "$coucou = entier" + }; + + System.out.println("\tExécution du test de ExpressionBooleenne()"); + + for (String aTester : INVALIDES) { + try { + new ExpressionBooleenne(aTester); + echec(); + } catch (InterpreteurException lancee) { + // Test OK + } + } + + try { + /* Expression logique sur des Entiers AVEC ESPACES */ + new ExpressionBooleenne("1 = 1"); // true + new ExpressionBooleenne("1 = 2"); // false + new ExpressionBooleenne("1 < 2"); + new ExpressionBooleenne("1 < 1"); + new ExpressionBooleenne("1 <> 2"); + new ExpressionBooleenne("1 <> 1"); + new ExpressionBooleenne("1 <= 1"); + new ExpressionBooleenne("1 <= 5"); + new ExpressionBooleenne("1 > -3"); + new ExpressionBooleenne("1 > 56"); + new ExpressionBooleenne("1 >= 1"); + new ExpressionBooleenne("1 >= 45"); + /* Expression logique sur des Entiers SANS ESPACES */ + new ExpressionBooleenne("1=1"); + new ExpressionBooleenne("1=2"); + new ExpressionBooleenne("1<2"); + new ExpressionBooleenne("1<1"); + new ExpressionBooleenne("1<>2"); + new ExpressionBooleenne("1<>1"); + new ExpressionBooleenne("1<=1"); + new ExpressionBooleenne("1<=5"); + new ExpressionBooleenne("1>-3"); + new ExpressionBooleenne("1>56"); + new ExpressionBooleenne("1>=1"); + new ExpressionBooleenne("1>=45"); + /* Expression logique sur des Entiers MOITIE ESPACES */ + new ExpressionBooleenne(" 1=1"); + new ExpressionBooleenne("1=2 "); + new ExpressionBooleenne("1 <2"); + new ExpressionBooleenne("1< 1"); + new ExpressionBooleenne("1 <>2"); + new ExpressionBooleenne("1<> 1"); + new ExpressionBooleenne(" 1<=1 "); + new ExpressionBooleenne("1 <= 5"); + new ExpressionBooleenne(" 1 > -3 "); + new ExpressionBooleenne("1>56\t"); + new ExpressionBooleenne(" 1 >= 1 "); + new ExpressionBooleenne(" 1 >= 45 "); + + /* Expression logique sur des Chaines AVEC ESPACES */ + new ExpressionBooleenne("\"TATA\" = \"TATA\""); + new ExpressionBooleenne("\"TATA\" = \"TITI\""); + new ExpressionBooleenne("\"TATA\" < \"TITI\""); + new ExpressionBooleenne("\"TOTO\" < \"TITI\""); + new ExpressionBooleenne("\"TOTO\" <> \"TATA\""); + new ExpressionBooleenne("\"TATA\" <> \"TATA\""); + new ExpressionBooleenne("\"TATA\"<=\"TATA\""); + new ExpressionBooleenne("\"TITI\" <= \"TATA\""); + new ExpressionBooleenne("\"TATA\" > \"FOO BAR\""); + new ExpressionBooleenne("\"FOO BAR\" > \"TATA\""); + new ExpressionBooleenne("\"TATA\" >= \"TATA\""); + new ExpressionBooleenne("\"FOO BAR\" >= \"TATA\""); + /* Expression logique sur des Chaines SANS ESPACES */ + new ExpressionBooleenne("\"TATA\"=\"TATA\""); + new ExpressionBooleenne("\"TATA\"=\"TITI\""); + new ExpressionBooleenne("\"TATA\"<\"TITI\""); + new ExpressionBooleenne("\"TOTO\"<\"TITI\""); + new ExpressionBooleenne("\"TOTO\"<>\"TATA\""); + new ExpressionBooleenne("\"TATA\"<>\"TATA\""); + new ExpressionBooleenne("\"TATA\"<=\"TATA\""); + new ExpressionBooleenne("\"TITI\"<=\"TATA\""); + new ExpressionBooleenne("\"TATA\">\"FOO BAR\""); + new ExpressionBooleenne("\"FOO BAR\">\"TATA\""); + new ExpressionBooleenne("\"TATA\">=\"TATA\""); + new ExpressionBooleenne("\"FOO BAR\">=\"TATA\""); + /* Expression logique sur des Chaines MOITIE ESPACES */ + new ExpressionBooleenne(" \"TATA\" = \"TATA\""); + new ExpressionBooleenne("\"TATA\" = \"TITI\""); + new ExpressionBooleenne("\"TATA\" < \"TITI\""); + new ExpressionBooleenne("\"TOTO\" < \"TITI\" "); + new ExpressionBooleenne(" \"TOTO\"<> \"TATA\" "); + new ExpressionBooleenne("\"TATA\" <> \"TATA\" "); + new ExpressionBooleenne(" \"TATA\" <=\"TATA\""); + new ExpressionBooleenne("\"TITI\" <= \"TATA\""); + new ExpressionBooleenne(" \"TATA\" > \"FOO BAR\""); + new ExpressionBooleenne(" \"FOO BAR \" > \"TATA\""); + new ExpressionBooleenne("\"TATA\" >= \"TATA\""); + new ExpressionBooleenne(" \"FOO BAR\" >= \"TATA\""); + /* Expression logique sur des Chaines AVEC OPERATEURS */ + new ExpressionBooleenne("\"FOO BAR\"<>\"TATA=TOTO\""); + new ExpressionBooleenne("\"FOO BAR=FLEMME\">\"TOTO\""); + new ExpressionBooleenne("\"FOO BAR > FLEMME\"=\"TOTO\""); + new ExpressionBooleenne("\"FOO BAR<>FLEMME\">\"TOTO\""); + + /* Expression logique sur des IdEntier et Entiers */ + new ExpressionBooleenne("marcel <= 10"); // true + new ExpressionBooleenne("marcel > j34n"); // false + new ExpressionBooleenne("2 = pi3rr3"); + new ExpressionBooleenne("j34n = pi3rr3"); + /* Expression logique sur des IdChaine et Chaines */ + new ExpressionBooleenne("$sanchis < $barrios"); + new ExpressionBooleenne("$servieres > \"Windows\""); + new ExpressionBooleenne("$barrios <> $servieres"); + new ExpressionBooleenne("\"coucou\" = $barrios"); + } catch (InterpreteurException lancee) { + echec(); + } + } + + /** + * Tests unitaire de {@link ExpressionBooleenne#calculer()} + */ + public void testCalculer() { + + final Boolean[] VALEUR_ATTENDU_ID = { + true, false, true, false, true, false, true, false + }; + + + Contexte contexteGlobal = new Contexte(); + contexteGlobal.ajouterVariable(new IdentificateurEntier("marcel"), + new Entier(0)); + contexteGlobal.ajouterVariable(new IdentificateurEntier("j34n"), + new Entier(1)); + contexteGlobal.ajouterVariable(new IdentificateurEntier("pi3rr3"), + new Entier(2)); + contexteGlobal.ajouterVariable(new IdentificateurChaine("$sanchis"), + new Chaine("\"coucou\"")); + contexteGlobal.ajouterVariable(new IdentificateurChaine("$barrios"), + new Chaine("\"java\"")); + contexteGlobal.ajouterVariable(new IdentificateurChaine("$servieres"), + new Chaine("\"WinDesign\"")); + Expression.referencerContexte(contexteGlobal); + + System.out.println("\tExécution du test de Calculer()"); + for (int numTest = 0 ; numTest < VALEUR_ATTENDU_ID.length ; numTest++) { + assertEquivalence(VALEUR_ATTENDU_ID[numTest], + FIXTURE_ID[numTest].calculer().getValeur()); + } + + final ExpressionBooleenne[] A_TESTER = { + new ExpressionBooleenne("1=1"), + new ExpressionBooleenne("1=2"), + new ExpressionBooleenne("1<2"), + new ExpressionBooleenne("1<1"), + new ExpressionBooleenne("1<>2"), + new ExpressionBooleenne("1<>1"), + new ExpressionBooleenne("1<=1"), + new ExpressionBooleenne("1<=5"), + new ExpressionBooleenne("1>-3"), + new ExpressionBooleenne("1>56"), + new ExpressionBooleenne("1>=1"), + new ExpressionBooleenne("1>=45"), + new ExpressionBooleenne("\"TATA\" = \"TATA\""), + new ExpressionBooleenne("\"TATA\" = \"TITI\""), + new ExpressionBooleenne("\"TATA\" < \"TITI\""), + new ExpressionBooleenne("\"TOTO\" < \"TITI\""), + new ExpressionBooleenne("\"TOTO\" <> \"TATA\""), + new ExpressionBooleenne("\"TATA\" <> \"TATA\""), + new ExpressionBooleenne("\"TATA\"<=\"TATA\""), + new ExpressionBooleenne("\"TITI\" <= \"TATA\""), + new ExpressionBooleenne("\"TATA\" > \"FOO BAR\""), + new ExpressionBooleenne("\"FOO BAR\" > \"TATA\""), + new ExpressionBooleenne("\"TATA\" >= \"TATA\""), + new ExpressionBooleenne("\"FOO BAR\" >= \"TATA\""), + }; + + final Boolean[] VALEUR_ATTENDU_L = { + true, false, true, false, true, false, + true, true, true, false, true, false, + + true, false, true, false, true, false, + true, false, true, false, true, false + }; + + for (int numTest = 0 ; numTest < A_TESTER.length ; numTest++) { + assertEquivalence(VALEUR_ATTENDU_L[numTest], + A_TESTER[numTest].calculer().getValeur()); + } + } + + /** + * Tests unitaire de {@link ExpressionBooleenne#toString()} + */ + public void testToString() { + System.out.println("\tExécution du test de toString()"); + + final String[] ATTENDU_L = { + "1 = 1", + "1 = 2", + "1 < 2", + "1 < 1", + "1 <> 2", + "1 <> 1", + "1 <= 1", + "1 <= 5", + "1 > -3", + "1 > 56", + "1 >= 1", + "1 >= 45", + "1 = 1", + "1 = 2", + "1 < 2", + "1 < 1", + "1 <> 2", + "1 <> 1", + "1 <= 1", + "1 <= 5", + "1 > -3", + "1 > 56", + "1 >= 1", + "1 >= 45", + "1 = 1", + "1 = 2", + "1 < 2", + "1 < 1", + "1 <> 2", + "1 <> 1", + "1 <= 1", + "1 <= 5", + "1 > -3", + "1 > 56", + "1 >= 1", + "1 >= 45", + "\"TATA\" = \"TATA\"", + "\"TATA\" = \"TITI\"", + "\"TATA\" < \"TITI\"", + "\"TOTO\" < \"TITI\"", + "\"TOTO\" <> \"TATA\"", + "\"TATA\" <> \"TATA\"", + "\"TATA\" <= \"TATA\"", + "\"TITI\" <= \"TATA\"", + "\"TATA\" > \"FOO BAR\"", + "\"FOO BAR\" > \"TATA\"", + "\"TATA\" >= \"TATA\"", + "\"FOO BAR\" >= \"TATA\"", + "\"TATA\" = \"TATA\"", + "\"TATA\" = \"TITI\"", + "\"TATA\" < \"TITI\"", + "\"TOTO\" < \"TITI\"", + "\"TOTO\" <> \"TATA\"", + "\"TATA\" <> \"TATA\"", + "\"TATA\" <= \"TATA\"", + "\"TITI\" <= \"TATA\"", + "\"TATA\" > \"FOO BAR\"", + "\"FOO BAR\" > \"TATA\"", + "\"TATA\" >= \"TATA\"", + "\"FOO BAR\" >= \"TATA\"", + "\"TATA\" = \"TATA\"", + "\"TATA\" = \"TITI\"", + "\"TATA\" < \"TITI\"", + "\"TOTO\" < \"TITI\"", + "\"TOTO\" <> \"TATA\"", + "\"TATA\" <> \"TATA\"", + "\"TATA\" <= \"TATA\"", + "\"TITI\" <= \"TATA\"", + "\"TATA\" > \"FOO BAR\"", + "\"FOO BAR \" > \"TATA\"", + "\"TATA\" >= \"TATA\"", + "\"FOO BAR\" >= \"TATA\"", + "\"FOO BAR\" <> \"TATA=TOTO\"", + "\"FOO BAR=FLEMME\" > \"TOTO\"", + "\"FOO BAR > FLEMME\" = \"TOTO\"", + "\"FOO BAR<>FLEMME\" > \"TOTO\"", + }; + + for (int numTest = 0 ; numTest < ATTENDU_L.length ; numTest++) { + assertEquivalence(ATTENDU_L[numTest], + FIXTURE_LITTERALE[numTest].toString()); + } + + final String[] ATTENDU_I = { + "marcel <= 10", + "marcel > j34n", + "2 = pi3rr3", + "j34n = pi3rr3", + "$sanchis < $barrios", + "$servieres > \"Windows\"", + "$barrios <> $servieres", + "\"coucou\" = $barrios", + }; + + for (int numTest = 0 ; numTest < ATTENDU_I.length ; numTest++) { + assertEquivalence(ATTENDU_I[numTest], + FIXTURE_ID[numTest].toString()); + } + } +} \ No newline at end of file diff --git a/src/interpreteurlir/expressions/tests/TestExpressionChaine.java b/src/interpreteurlir/expressions/tests/TestExpressionChaine.java index 08d0788..439f90c 100644 --- a/src/interpreteurlir/expressions/tests/TestExpressionChaine.java +++ b/src/interpreteurlir/expressions/tests/TestExpressionChaine.java @@ -141,4 +141,33 @@ public class TestExpressionChaine { } } + + /** + * Tests unitaires de {@link ExpressionChaine#toString()} + */ + public void testToString() { + final String[] chaineAttendue = { + "$chaine = \"texte\"", + "$chaine = \"tata\"", + "$tata", + "\"une chaine de texte\"", + "$chaine = \"toto\" + \"titi\"", + "$chaine = $toto + \"titi\"", + "$chaine = \"toto\" + $titi", + "$chaine = $toto + $titi", + "\"toto\" + \"titi\"", + "$toto + \"titi\"", + "\"toto\" + $titi", + "$toto + $titi", + "\"ab=bc\"", + "$chaine = \"ab+cd\" + $toto", + }; + + System.out.println("\tExécution du test de " + + "ExpressionChaine#toString()"); + for (int numTest = 0 ; numTest < chaineAttendue.length ; numTest++) { + assertEquivalence(chaineAttendue[numTest], + fixture[numTest].toString()); + } + } } diff --git a/src/interpreteurlir/expressions/tests/TestExpressionEntier.java b/src/interpreteurlir/expressions/tests/TestExpressionEntier.java index dbf5937..8c5ff81 100644 --- a/src/interpreteurlir/expressions/tests/TestExpressionEntier.java +++ b/src/interpreteurlir/expressions/tests/TestExpressionEntier.java @@ -9,10 +9,7 @@ import static info1.outils.glg.Assertions.*; import interpreteurlir.Contexte; import interpreteurlir.ExecutionException; import interpreteurlir.InterpreteurException; -import interpreteurlir.donnees.Identificateur; -import interpreteurlir.donnees.IdentificateurChaine; import interpreteurlir.donnees.IdentificateurEntier; -import interpreteurlir.donnees.litteraux.Chaine; import interpreteurlir.donnees.litteraux.Entier; import interpreteurlir.expressions.Expression; import interpreteurlir.expressions.ExpressionEntier; @@ -67,7 +64,8 @@ public class TestExpressionEntier { "divisionRatee = 5 /", "ratee=*7", }; - + System.out.println("\tExécution du test de " + + "ExpressionEntier#ExpressionEntier(String)"); for (String invalide : INVALIDES) { try { new ExpressionEntier(invalide); @@ -99,6 +97,9 @@ public class TestExpressionEntier { new Entier(0) // Bouchon }; + System.out.println("\tExécution du test de " + + "ExpressionEntier#calculer()"); + /* Exception levée si contexte non référencé */ try { FIXTURE[0].calculer(); @@ -153,6 +154,9 @@ public class TestExpressionEntier { "modulo = 12 % 0" }; + System.out.println("\tExécution du test de " + + "ExpressionEntier#toString()"); + for (int i = 0 ; i < FIXTURE.length ; i++) { assertTrue(FIXTURE[i].toString().equals(ATTENDUES[i])); } diff --git a/src/interpreteurlir/motscles/Commande.java b/src/interpreteurlir/motscles/Commande.java index 4cfa3a1..ac74d4c 100644 --- a/src/interpreteurlir/motscles/Commande.java +++ b/src/interpreteurlir/motscles/Commande.java @@ -19,7 +19,7 @@ import interpreteurlir.programmes.Programme; * @author Heïa Dexter * @author Lucas Vabre */ -public class Commande { +public abstract class Commande { /** référence du programme global */ protected static Programme programmeGlobal; @@ -52,10 +52,7 @@ public class Commande { * @return true si la commande affiche un feedback directement sur la sortie * standard, sinon false */ - public boolean executer() { - // pas de comportement pour une Commande générale - return false; // pas de feedback - } + public abstract boolean executer(); /** * Référence le programme pour accéder et modifier le programme chargé. diff --git a/src/interpreteurlir/motscles/CommandeCharge.java b/src/interpreteurlir/motscles/CommandeCharge.java new file mode 100644 index 0000000..1086a2d --- /dev/null +++ b/src/interpreteurlir/motscles/CommandeCharge.java @@ -0,0 +1,167 @@ +/** + * CommandeCharge.java 21 mai 2021 + * IUT Rodez info1 2020-2021, pas de copyright, aucun droit + */ +package interpreteurlir.motscles; + +import interpreteurlir.Contexte; +import interpreteurlir.ExecutionException; +import interpreteurlir.InterpreteurException; +import interpreteurlir.motscles.instructions.Instruction; +import interpreteurlir.programmes.Etiquette; +import interpreteurlir.Analyseur; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; + +/** + * Charge les lignes de programme dans le fichier texte indiqué en argument + * + * @author Nicolas Caminade + * @author Sylvan Courtiol + * @author Pierre Debas + * @author Heia Dexter + * @author Lucas Vabre + */ +public class CommandeCharge extends Commande{ + + /** Chemin du fichier dans lequel sera le programme chargé */ + private String cheminFichier; + + /** + * Initialise la commande Charge a partir de ses argument et + * de son contexte passé en paramètre. + * @param arguments Chemin du fichier dans lequel sera le programme chargé + * @param contexte Contexte du programme + * @throws InterpreteurException Si l'argument est null, vide, + * contient uniquement des espaces ou + * ne se termine pas par ".lir". + */ + public CommandeCharge(String arguments, Contexte contexte ) { + super(arguments, contexte); + + final String extension = ".lir"; + + if (arguments == null + || arguments.isEmpty() + || arguments.isBlank() + || !arguments.trim().endsWith(extension)) { + + throw new InterpreteurException("\t" + arguments + + " n'est pas un chemin valide"); + } + + this.cheminFichier = arguments.trim(); + } + + /** + * Charge le programme contenu dans le fichier de this + * @return false car elle n'affiche aucun feedback directement + * @throws InterpreteurException Si le fichier a charger n'as pas été trouvé + */ + public boolean executer() { + + programmeGlobal.raz(); + + /* Chemin du fichier */ + String nomFichier = new File(cheminFichier).getAbsolutePath(); + + /* Fichier logique en entrée */ + BufferedReader entree; + + entree = null; + try { + entree = new BufferedReader( + new InputStreamReader( + new FileInputStream(nomFichier))); + analyserFichier(entree); + entree.close(); + } catch (IOException e) { + throw new InterpreteurException(nomFichier + " est introuvable"); + } + + return false; + } + + /** + * Analyse chaque ligne de l'entrée et les ajoute dans programme global + * @param entree Tampon du fichier à lire + * @throws IOException Problème de lecture du fichier + */ + private void analyserFichier(BufferedReader entree) throws IOException { + + /* Index de la ligne découpée */ + final int ETIQUETTE = 0; + final int MOT_CLE = 1; + final int ARGUMENT = 2; + + String ligneLue; + int numLigne = 0; + + do { + ligneLue = entree.readLine(); + if (ligneLue != null && !ligneLue.isBlank()) { + numLigne++; + + String[] decoupage = splitter(ligneLue); + + Class aAjouter; + try { + aAjouter = Analyseur + .rechercheInstruction(decoupage[MOT_CLE]); + + Class classeArg = String.class; + Class classeContexte = Contexte.class; + Instruction inst = (Instruction)aAjouter + .getConstructor(classeArg, classeContexte) + .newInstance(decoupage[ARGUMENT], contexte); + + Etiquette etiquette = new Etiquette(decoupage[ETIQUETTE]); + + programmeGlobal.ajouterLigne(etiquette, inst); + } catch (InvocationTargetException| IllegalAccessException + |InstantiationException | NoSuchMethodException + |InterpreteurException | ExecutionException lancee) { + programmeGlobal.raz(); + throw new InterpreteurException(ligneLue + " => ligne " + + numLigne); + } + } + } while (ligneLue != null); + } + + /** + * Sépare la ligne en etiquette/mot clé/argument + * @param ligneLue + * @return Tableau comportant en : + *

  • indice 1 : l'étiquette
  • + *
  • indice 2 : mot clé
  • + *
  • indice 3 : argument
+ * @throws InterpreteurException Si la ligne ne contient pas les 2 éléments: + *
  • Etiquette
  • + *
  • Mot clé
+ */ + private static String[] splitter(String ligneLue) { + /* Sépare l'étiquette, la commande et l'argument */ + String[] ligne = ligneLue.split(" ", 3); + + if (ligne.length < 2) { + programmeGlobal.raz(); + throw new InterpreteurException(ligneLue + " n'est pas " + + "une ligne valide"); + } + + String[] decoupage = new String[3]; + + /* Ajouter la ligne dans le contexte */ + decoupage[0] = ligne[0]; + decoupage[1] = ligne[1]; + decoupage[2] = ligne.length >= 3 ? ligne[2] : ""; + + return decoupage; + } +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/CommandeDebut.java b/src/interpreteurlir/motscles/CommandeDebut.java index 90fa70c..657e854 100644 --- a/src/interpreteurlir/motscles/CommandeDebut.java +++ b/src/interpreteurlir/motscles/CommandeDebut.java @@ -39,7 +39,6 @@ public class CommandeDebut extends Commande { } } - /** * Commande d'exécution de la commande. * Efface le contexte. @@ -52,4 +51,4 @@ public class CommandeDebut extends Commande { return false; } -} +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/CommandeEfface.java b/src/interpreteurlir/motscles/CommandeEfface.java index 4c8274e..78f5802 100644 --- a/src/interpreteurlir/motscles/CommandeEfface.java +++ b/src/interpreteurlir/motscles/CommandeEfface.java @@ -21,7 +21,7 @@ public class CommandeEfface extends Commande { /** Erreur nombre incorrect d'arguments */ private static final String ERREUR_NB_ARGS = - "nombre d'arguments incorrect. Syntaxe attendue ==> :"; + "usage efface <étiquette_début>:<étiquette_fin>"; /** Plage de suppression des lignes de code */ private Etiquette[] plageSuppression; @@ -31,7 +31,7 @@ public class CommandeEfface extends Commande { * contexte passés en paramètres. Modifie le programme global référencé * par l'analyseur. * @param arguments lignes à effacer (tout le programme si vide) - * @param contexte + * @param contexte référence du contexte global */ public CommandeEfface(String arguments, Contexte contexte) { super(arguments, contexte); diff --git a/src/interpreteurlir/motscles/CommandeFin.java b/src/interpreteurlir/motscles/CommandeFin.java index 0f33707..146d697 100644 --- a/src/interpreteurlir/motscles/CommandeFin.java +++ b/src/interpreteurlir/motscles/CommandeFin.java @@ -35,7 +35,6 @@ public class CommandeFin extends Commande { throw new InterpreteurException(ERREUR_ARGUMENTS); } } - /** * Commande d'exécution de la commande. @@ -51,4 +50,4 @@ public class CommandeFin extends Commande { System.exit(0); return true; } -} +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/CommandeLance.java b/src/interpreteurlir/motscles/CommandeLance.java index 5531e8d..a6e4628 100644 --- a/src/interpreteurlir/motscles/CommandeLance.java +++ b/src/interpreteurlir/motscles/CommandeLance.java @@ -6,7 +6,6 @@ package interpreteurlir.motscles; import interpreteurlir.Contexte; import interpreteurlir.InterpreteurException; -import interpreteurlir.programmes.Programme; import interpreteurlir.programmes.Etiquette; /** @@ -25,8 +24,8 @@ public class CommandeLance extends Commande { /** * Initialise la commande lance avec ses arguments et le contexte - * @param arguments - * @param contexte + * @param arguments vide ou étiquette de lancement + * @param contexte référence du contexte global * @throws InterpreteurException en cas d'erreur de syntaxe à * la création d'une étiquette */ @@ -67,4 +66,4 @@ public class CommandeLance extends Commande { return true; } -} +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/CommandeListe.java b/src/interpreteurlir/motscles/CommandeListe.java index 6747953..8e6db8d 100644 --- a/src/interpreteurlir/motscles/CommandeListe.java +++ b/src/interpreteurlir/motscles/CommandeListe.java @@ -28,8 +28,8 @@ public class CommandeListe extends Commande { /** * Initialise la commande liste avec ses arguments et le contexte * - * @param arguments - * @param contexte + * @param arguments arguments vide ou contenant les étiquettes à afficher + * @param contexte référence du contexte global * @throws InterpreteurException en cas d'erreur de syntaxe lors * de l'instanciation des étiquettes */ @@ -39,10 +39,10 @@ public class CommandeListe extends Commande { final int ARGS_DEBUT = 0; final int ARGS_FIN = 1; - final String ERREUR_INTERVALLE = "usage :" - + " avec " - + " < " - + " "; + final String ERREUR_INTERVALLE = "usage liste <étiquette_début>:" + + "<étiquette_fin> avec " + + "<étiquette_début> <= " + + "<étiquette_fin> "; if (arguments.isBlank()) { debut = new Etiquette(VALEUR_ETIQUETTE_MIN); @@ -75,16 +75,16 @@ public class CommandeListe extends Commande { * dans la classe {@link Commande} */ public boolean executer() { - final String ERREUR = "erreur exécution"; if (programmeGlobal == null) { throw new RuntimeException(ERREUR); } + if (debut != null || fin != null) { System.out.print(programmeGlobal.listeBornee(debut, fin)); } return true; } -} +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/CommandeSauve.java b/src/interpreteurlir/motscles/CommandeSauve.java new file mode 100644 index 0000000..3c5542b --- /dev/null +++ b/src/interpreteurlir/motscles/CommandeSauve.java @@ -0,0 +1,86 @@ +/** + * CommandeSauve.java 21 mai 2021 + * IUT Rodez info1 2020-2021, pas de copyright, aucun droit + */ +package interpreteurlir.motscles; + +import java.io.PrintStream; + +import interpreteurlir.Contexte; +import interpreteurlir.ExecutionException; +import interpreteurlir.InterpreteurException; +import interpreteurlir.programmes.Programme; + +/** + * Commande sauve prenant en argument le chemin du fichier + * (avec une extension .lir) dans lequel est enregistré le programme chargé. + * @author Nicolas Caminade + * @author Sylvan Courtiol + * @author Pierre Debas + * @author Heia Dexter + * @author Lucas Vabre + */ +public class CommandeSauve extends Commande { + + /** chemin du fichier d'extension .lir pour la sauvegarde */ + private String cheminFichier; + + /** + * Initialise une commande sauve avec un chemin de fichier .lir passé en + * argument. + * @param arguments chemin du fichier dans lequel + * la sauvegarde sera effectuée + * @param contexte référence du contexte global + * @throws InterpreteurException si le chemin du fichier n'a pas + * l'extension .lir + * ou si arguments est chaîne blanche + */ + public CommandeSauve(String arguments, Contexte contexte) { + super(arguments, contexte); + + final String EXTENSION = ".lir"; + final String USAGE = "usage sauve .lir"; + + arguments = arguments.trim(); + + if (arguments.isBlank() || !arguments.endsWith(EXTENSION)) { + throw new InterpreteurException(USAGE); + } + // else + + cheminFichier = arguments; + } + + /** + * Commande d'exécution de la commande. + * Sauvegarde le programme référencé dans la classe Commande + * dans le fichier de cette CommandeSauve. + * @return false car aucun feedback afficher directement + * @throws ExecutionException si l'enregistrement est impossible + * @throws RuntimeException si aucun programme référencé dans la classe + * Commande avec la méthode + * {@link Commande#referencerProgramme(Programme)} + */ + @Override + public boolean executer() { + final String MSG_ERREUR = "impossible de sauvegarder le programme " + + "dans le fichier (le chemin est peut-être invalide)"; + + if (programmeGlobal == null) { + throw new RuntimeException("Programme non référencé dans Commande"); + } + + PrintStream aEcrire = null; + + try { + aEcrire = new PrintStream(cheminFichier); + aEcrire.print(programmeGlobal.toString()); + aEcrire.close(); + } catch (Exception lancee) { + throw new ExecutionException(MSG_ERREUR); + } + + return false; + } + +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/instructions/Instruction.java b/src/interpreteurlir/motscles/instructions/Instruction.java index 787aaaf..33d6850 100644 --- a/src/interpreteurlir/motscles/instructions/Instruction.java +++ b/src/interpreteurlir/motscles/instructions/Instruction.java @@ -17,10 +17,7 @@ import interpreteurlir.motscles.Commande; * @author Heïa Dexter * @author Lucas Vabre */ -public class Instruction extends Commande { - - /** Contexte d'exécution de cette instruction */ - protected Contexte contexteGlobal; +public abstract class Instruction extends Commande { /** Expression qui sera exécutée par la commande */ protected Expression aExecuter; @@ -41,16 +38,12 @@ public class Instruction extends Commande { * @see interpreteurlir.motscles.Commande#executer() */ @Override - public boolean executer() { - return super.executer(); - } + public abstract boolean executer(); /* * Non - javadoc * @see java.lang.Object#toString() */ @Override - public String toString() { - return getClass().getSimpleName() + " " + aExecuter; - } + public abstract String toString(); } diff --git a/src/interpreteurlir/motscles/instructions/InstructionAffiche.java b/src/interpreteurlir/motscles/instructions/InstructionAffiche.java index 56aa71e..e4a752e 100644 --- a/src/interpreteurlir/motscles/instructions/InstructionAffiche.java +++ b/src/interpreteurlir/motscles/instructions/InstructionAffiche.java @@ -7,7 +7,6 @@ package interpreteurlir.motscles.instructions; import interpreteurlir.Contexte; import interpreteurlir.InterpreteurException; import interpreteurlir.expressions.Expression; -import interpreteurlir.expressions.ExpressionChaine; /** * Affiche sur la sortie standard une expression passée en argument. Cette @@ -21,8 +20,9 @@ import interpreteurlir.expressions.ExpressionChaine; */ public class InstructionAffiche extends Instruction { - /** Erreur d'affectation illegale */ - private static final String AFFECTATION_ILLEGALE = "affectation illegale"; + /** Erreur d'affectation illégale */ + private static final String AFFECTATION_ILLEGALE = + "affectation impossible avec la commande affiche"; /** * Initialise cette InstructionAffiche à partir de son contexte global @@ -37,8 +37,9 @@ public class InstructionAffiche extends Instruction { public InstructionAffiche(String arguments, Contexte contexte) { super(arguments, contexte); - if (ExpressionChaine.indexOperateur(arguments, '=') >= 0) + if (Expression.detecterCaractere(arguments, '=') >= 0) { throw new InterpreteurException(AFFECTATION_ILLEGALE); + } aExecuter = arguments.isBlank() ? null @@ -70,5 +71,4 @@ public class InstructionAffiche extends Instruction { : " " + aExecuter.toString()); } - -} +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/instructions/InstructionEntre.java b/src/interpreteurlir/motscles/instructions/InstructionEntre.java index 50f0c8e..b9905b6 100644 --- a/src/interpreteurlir/motscles/instructions/InstructionEntre.java +++ b/src/interpreteurlir/motscles/instructions/InstructionEntre.java @@ -44,7 +44,7 @@ public class InstructionEntre extends Instruction { */ public InstructionEntre(String arguments, Contexte contexte) { super(arguments, contexte); - final String ERREUR_ARG = "Entre attend un identificateur en argument"; + final String ERREUR_ARG = "usage entre "; if (arguments.isBlank()) { throw new InterpreteurException(ERREUR_ARG); @@ -68,8 +68,10 @@ public class InstructionEntre extends Instruction { */ public boolean executer() { - final String MESSAGE_ERREUR_TYPE = "Le type saisi ne correspond" - + " pas au type demandé"; + final String MESSAGE_ERREUR_TYPE = "type saisi " + + "et type demandé incompatibles"; + + @SuppressWarnings("resource") // ne pas fermer sinon crash Scanner entree = new Scanner(System.in); String valeurSaisie = entree.nextLine(); @@ -84,6 +86,7 @@ public class InstructionEntre extends Instruction { } catch (InterpreteurException lancee) { throw new ExecutionException(MESSAGE_ERREUR_TYPE); } + return false; } @@ -94,4 +97,4 @@ public class InstructionEntre extends Instruction { public String toString() { return "entre " + id; } -} +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/instructions/InstructionProcedure.java b/src/interpreteurlir/motscles/instructions/InstructionProcedure.java index 44a9170..83d939a 100644 --- a/src/interpreteurlir/motscles/instructions/InstructionProcedure.java +++ b/src/interpreteurlir/motscles/instructions/InstructionProcedure.java @@ -5,7 +5,6 @@ package interpreteurlir.motscles.instructions; import interpreteurlir.Contexte; -import interpreteurlir.ExecutionException; import interpreteurlir.InterpreteurException; import interpreteurlir.programmes.Etiquette; @@ -36,7 +35,7 @@ public class InstructionProcedure extends Instruction { public InstructionProcedure(String arguments, Contexte contexte) { super(arguments, contexte); - final String ERREUR_ARG = "procedure attend une étiquette en argument"; + final String ERREUR_ARG = "usage procedure <étiquette>"; if(arguments.isBlank()) { throw new InterpreteurException(ERREUR_ARG); @@ -63,7 +62,6 @@ public class InstructionProcedure extends Instruction { * de classe de Commande. */ public boolean executer() { - final String ERREUR_REFERENCEMENT = "Le programme doit être référencé " + "dans la classe commande"; diff --git a/src/interpreteurlir/motscles/instructions/InstructionRetour.java b/src/interpreteurlir/motscles/instructions/InstructionRetour.java index af519ea..ed9590d 100644 --- a/src/interpreteurlir/motscles/instructions/InstructionRetour.java +++ b/src/interpreteurlir/motscles/instructions/InstructionRetour.java @@ -32,7 +32,7 @@ public class InstructionRetour extends Instruction { public InstructionRetour(String arguments, Contexte contexte) { super(arguments, contexte); - final String ERREUR_ARG = "L'instruction retour n'a pas d'arguments"; + final String ERREUR_ARG = "l'instruction retour n'a pas d'arguments"; if (!arguments.isBlank()) { throw new InterpreteurException(ERREUR_ARG); @@ -70,4 +70,4 @@ public class InstructionRetour extends Instruction { return false; } -} +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/instructions/InstructionSi.java b/src/interpreteurlir/motscles/instructions/InstructionSi.java new file mode 100644 index 0000000..8907a0b --- /dev/null +++ b/src/interpreteurlir/motscles/instructions/InstructionSi.java @@ -0,0 +1,94 @@ +/** + * InstructionSi.java 22 mai 2021 + * IUT Rodez info1 2020-2021, pas de copyright, aucun droit + */ +package interpreteurlir.motscles.instructions; + +import interpreteurlir.Contexte; +import interpreteurlir.ExecutionException; +import interpreteurlir.InterpreteurException; +import interpreteurlir.expressions.ExpressionBooleenne; +import interpreteurlir.programmes.Etiquette; + +/** + * Instruction de saut conditionnel. + * La syntaxe est "si expression_booléenne vaen etiquette". + * @author Nicolas Caminade + * @author Sylvan Courtiol + * @author Pierre Debas + * @author Heïa Dexter + * @author Lucas Vabre + */ +public class InstructionSi extends Instruction { + + /** expression booléenne de this qui est la condition du saut */ + private ExpressionBooleenne condition; + + /** etiquette pour le saut conditionnel */ + private Etiquette saut; + + /** + * Initialise une instruction de saut conditionnel à partir des arguments + * @param arguments chaîne argument de la forme + * "si expression_booléenne vaen etiquette" + * @param contexte contexte global + * @throws InterpreteurException si syntaxe invalide (si ... vaen ...) + * ou si expression_booléenne invalide + * ou si etiquette invalide + */ + public InstructionSi(String arguments, Contexte contexte) { + super(arguments, contexte); + final String ERR_SYNTAXE = "usage si " + + " vaen <étiquette>"; + + arguments = arguments.trim(); + if (arguments.isBlank()) { + throw new InterpreteurException(ERR_SYNTAXE); + } + + int indexVaen = arguments.lastIndexOf("vaen"); + if (indexVaen < 1) { + throw new InterpreteurException(ERR_SYNTAXE); + } + + String expression = arguments.substring(0, indexVaen); + String etiquette = arguments.substring(indexVaen + 4, + arguments.length()); + + condition = new ExpressionBooleenne(expression); + saut = new Etiquette(etiquette); + + } + + /** + * Execution de l'instruction : + * Realise un saut a l'étiquette spécifiée + * si l'expression booléenne est true. + * @return false car aucun feedback affiché directement + * @throws RuntimeException si un programme n'est pas référencé en membre + * de classe de Commande. + * @throws ExecutionException si l'étiquette n'existe pas dans le programme + */ + @Override + public boolean executer() { + final String ERREUR_REFERENCEMENT = "Le programme doit être référencé " + + "dans la classe commande"; + + if (programmeGlobal == null) { + throw new RuntimeException(ERREUR_REFERENCEMENT); + } + + if (condition.calculer().getValeur()) { + programmeGlobal.vaen(saut); + } + return false; + } + + /* non javadoc + * @see interpreteurlir.motscles.instructions.Instruction#toString() + */ + @Override + public String toString() { + return "si " + condition + " vaen " + saut; + } +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/instructions/InstructionStop.java b/src/interpreteurlir/motscles/instructions/InstructionStop.java index c28510b..5235532 100644 --- a/src/interpreteurlir/motscles/instructions/InstructionStop.java +++ b/src/interpreteurlir/motscles/instructions/InstructionStop.java @@ -6,7 +6,6 @@ package interpreteurlir.motscles.instructions; import interpreteurlir.Contexte; import interpreteurlir.InterpreteurException; -import interpreteurlir.programmes.Programme; /** * Instruction stop servant à marquer la fin d'un programme de l'interpréteur @@ -22,7 +21,8 @@ public class InstructionStop extends Instruction { /** Message d'erreur si instruction passée avec des arguments */ private static final String ERREUR_ARGUMENTS = - "l'instruction stop n'accepte pas d'arguments"; + "l'instruction stop n'a pas d'arguments"; + /** * Initialise cette instruction stop à partir des arguments, du contexte * et du programme passés en paramètres. Cette instruction ne modifie que @@ -46,7 +46,7 @@ public class InstructionStop extends Instruction { public boolean executer() { final String ERREUR_REFERENCEMENT = "Le programme doit être référencé " - + "dans la classe commande"; + + "dans la classe commande"; if (programmeGlobal == null) { throw new RuntimeException(ERREUR_REFERENCEMENT); @@ -64,4 +64,4 @@ public class InstructionStop extends Instruction { public String toString() { return "stop"; } -} +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/instructions/InstructionVaen.java b/src/interpreteurlir/motscles/instructions/InstructionVaen.java index a4f43cc..1e6e7be 100644 --- a/src/interpreteurlir/motscles/instructions/InstructionVaen.java +++ b/src/interpreteurlir/motscles/instructions/InstructionVaen.java @@ -30,7 +30,7 @@ public class InstructionVaen extends Instruction { public InstructionVaen(String arguments, Contexte contexte) { super(arguments, contexte); - final String ERREUR_ARG = "vaen attend une étiquette en argument"; + final String ERREUR_ARG = "usage vaen <étiquette>"; if (arguments.isBlank()) { throw new InterpreteurException(ERREUR_ARG); @@ -49,16 +49,15 @@ public class InstructionVaen extends Instruction { /** * Execution de l'instruction : - * Realise un saut a l'étiquette spécifiée. - * L'appel s'empile sur le contexte appellant pour ce qui est du - * compteur ordinal. + * Réalise un saut à l'étiquette spécifiée. * @return false car aucun feedback affiché directement * @throws RuntimeException si un programme n'est pas référencé en membre * de classe de Commande. */ public boolean executer() { - final String ERREUR = "erreur exécution"; + final String ERREUR = "Le programme doit être référencé " + + "dans la classe commande"; if (programmeGlobal == null) { throw new RuntimeException(ERREUR); @@ -69,4 +68,4 @@ public class InstructionVaen extends Instruction { return false; } -} +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/instructions/InstructionVar.java b/src/interpreteurlir/motscles/instructions/InstructionVar.java index 0f88fa4..7aba799 100644 --- a/src/interpreteurlir/motscles/instructions/InstructionVar.java +++ b/src/interpreteurlir/motscles/instructions/InstructionVar.java @@ -7,7 +7,6 @@ package interpreteurlir.motscles.instructions; import interpreteurlir.Contexte; import interpreteurlir.InterpreteurException; import interpreteurlir.expressions.Expression; -import interpreteurlir.expressions.ExpressionChaine; /** * Instruction de déclaration et d'affectation de variables. La syntaxe de @@ -30,10 +29,11 @@ public class InstructionVar extends Instruction { */ public InstructionVar(String arguments, Contexte contexte) { super(arguments, contexte); + final String USAGE = "usage var = "; if (arguments == null || arguments.isBlank() - || ExpressionChaine.indexOperateur(arguments, '=') <= 0) - throw new InterpreteurException("erreur de syntaxe"); + || Expression.detecterCaractere(arguments, '=') <= 0) + throw new InterpreteurException(USAGE); aExecuter = Expression.determinerTypeExpression(arguments.trim()); } @@ -56,4 +56,4 @@ public class InstructionVar extends Instruction { public String toString() { return "var " + aExecuter; } -} +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/instructions/tests/TestInstruction.java b/src/interpreteurlir/motscles/instructions/tests/TestInstruction.java index 5156d8c..3655fff 100644 --- a/src/interpreteurlir/motscles/instructions/tests/TestInstruction.java +++ b/src/interpreteurlir/motscles/instructions/tests/TestInstruction.java @@ -1,52 +1,45 @@ -/** - * TestInstruction.java 9 mai 2021 - * IUT info1 2020-2021, pas de copyright, aucun droit - */ -package interpreteurlir.motscles.instructions.tests; +// Classe testée passé en abstract -import interpreteurlir.Contexte; -import interpreteurlir.motscles.instructions.Instruction; - -/** - * Tests unitaires des instructions - * @author Nicolas Caminade - * @author Sylvan Courtiol - * @author Pierre Debas - * @author Heïa Dexter - * @author Lucas Vabre - */ -public class TestInstruction { - - /** - * Test du constructeur - */ - public static void testInstruction() { - System.out.println("Test du constructeur"); - Instruction aTester = new Instruction("Bonjour", new Contexte()); - System.out.println("==> OK\n"); - } - - /** - * Test de toString() - */ - public static void testToString() { - System.out.println("Test de toString()"); - Instruction aTester = new Instruction("Bonjour", new Contexte()); - - if (!aTester.toString().equals("Instruction null")) - System.err.println("Echec du test"); - else - System.out.println("==> OK\n"); - - System.out.println(aTester); - } - - /** - * Lancement des tests - * @param args non utilisé - */ - public static void main(String[] args) { - testInstruction(); - testToString(); - } -} +///** +// * TestInstruction.java 9 mai 2021 +// * IUT info1 2020-2021, pas de copyright, aucun droit +// */ +//package interpreteurlir.motscles.instructions.tests; +// +//import static info1.outils.glg.Assertions.*; +//import interpreteurlir.Contexte; +//import interpreteurlir.InterpreteurException; +//import interpreteurlir.motscles.instructions.Instruction; +// +///** +// * Tests unitaires des instructions +// * +// * @author Nicolas Caminade +// * @author Sylvan Courtiol +// * @author Pierre Debas +// * @author Heïa Dexter +// * @author Lucas Vabre +// */ +//public class TestInstruction { +// +// /** +// * Test unitaire de {@link Instruction#Instruction(String, Contexte)} +// */ +// public static void testInstruction() { +// System.out.println("\tExécution du test de Instruction()"); +// try { +// new Instruction("Bonjour", new Contexte()); +// } catch (InterpreteurException lancee) { +// echec(); +// } +// } +// +// /** +// * Test unitaire de {@link Instruction#toString()} +// */ +// public static void testToString() { +// System.out.println("\tExécution du test de toString()"); +// Instruction aTester = new Instruction("Bonjour", new Contexte()); +// assertEquivalence(aTester.toString(), "Instruction null"); +// } +//} diff --git a/src/interpreteurlir/motscles/instructions/tests/TestInstructionAffiche.java b/src/interpreteurlir/motscles/instructions/tests/TestInstructionAffiche.java index ba1d0c5..d518002 100644 --- a/src/interpreteurlir/motscles/instructions/tests/TestInstructionAffiche.java +++ b/src/interpreteurlir/motscles/instructions/tests/TestInstructionAffiche.java @@ -56,7 +56,7 @@ public class TestInstructionAffiche { }; Expression.referencerContexte(CONTEXTE_GBL); - System.out.println("Exécution du test de InstructionAffiche(String" + System.out.println("\tExécution du test de InstructionAffiche(String" + ", Contexte)"); for (String argInvalide : INVALIDES) { try { @@ -73,12 +73,14 @@ public class TestInstructionAffiche { */ public static void testExecuter() { - System.out.println("Exécution du test de executer()\nTEST VISUEL SUR " + System.out.println("\tExécution du test de executer()\nTEST VISUEL SUR " + "CONSOLE :"); Expression.referencerContexte(CONTEXTE_GBL); - for (InstructionAffiche aLancer : FIXTURE) - aLancer.executer(); + for (InstructionAffiche aLancer : FIXTURE) { + System.out.println("\n\ttest visuel suivant : "); + aLancer.executer(); + } System.out.println(); } @@ -100,7 +102,7 @@ public class TestInstructionAffiche { "affiche \"300000000000000000 ça passe\"" }; - System.out.println("Exécution du test de toString()"); + System.out.println("\tExécution du test de toString()"); for (int i = 0 ; i < FIXTURE.length ; i++) { assertTrue(FIXTURE[i].toString().compareTo(ATTENDUS[i]) == 0); } diff --git a/src/interpreteurlir/motscles/instructions/tests/TestInstructionEntre.java b/src/interpreteurlir/motscles/instructions/tests/TestInstructionEntre.java index b7dd480..90b97d7 100644 --- a/src/interpreteurlir/motscles/instructions/tests/TestInstructionEntre.java +++ b/src/interpreteurlir/motscles/instructions/tests/TestInstructionEntre.java @@ -8,7 +8,6 @@ import interpreteurlir.motscles.instructions.InstructionEntre; import interpreteurlir.Contexte; import interpreteurlir.ExecutionException; import interpreteurlir.InterpreteurException; -import interpreteurlir.donnees.litteraux.Entier; import static info1.outils.glg.Assertions.*; /** @@ -117,5 +116,4 @@ public class TestInstructionEntre { System.out.println("Contexte : \n" + CONTEXTE_GLB); } - } diff --git a/src/interpreteurlir/motscles/instructions/tests/TestInstructionSi.java b/src/interpreteurlir/motscles/instructions/tests/TestInstructionSi.java new file mode 100644 index 0000000..bdb7056 --- /dev/null +++ b/src/interpreteurlir/motscles/instructions/tests/TestInstructionSi.java @@ -0,0 +1,184 @@ +/** + * TestInstructionSi.java 22 mai 2021 + * IUT Rodez info1 2020-2021, pas de copyright, aucun droit + */ +package interpreteurlir.motscles.instructions.tests; + +import interpreteurlir.motscles.Commande; +import interpreteurlir.motscles.instructions.InstructionSi; +import interpreteurlir.motscles.instructions.InstructionVar; +import interpreteurlir.programmes.*; +import interpreteurlir.Contexte; +import interpreteurlir.InterpreteurException; +import interpreteurlir.donnees.*; +import interpreteurlir.donnees.litteraux.*; +import interpreteurlir.expressions.Expression; + +import static info1.outils.glg.Assertions.*; + +/** + * Tests unitaires de {@link InstructionSi} + * @author Nicolas Caminade + * @author Sylvan Courtiol + * @author Pierre Debas + * @author Heïa Dexter + * @author Lucas Vabre + */ +public class TestInstructionSi { + + /** contexte pour les tests */ + private Contexte contexte = new Contexte(); + + /** programme pour les tests */ + private Programme prog = new Programme(); + + /** Jeu de donnée d'instruction si vaen valides pour les tests*/ + private InstructionSi[] fixture = { + new InstructionSi("45 = 2 vaen 15", contexte), + new InstructionSi("age >= 130 vaen 1000", contexte), + new InstructionSi("$prenom <>\"défaut\" vaen 16", contexte), + new InstructionSi("resultat < 20 vaen 17", contexte), + new InstructionSi("resultat < moyenne vaen 18", contexte), + new InstructionSi("age > 20 vaen 19", contexte), + new InstructionSi("\"tata\" = \"tata\"vaen 20", contexte), + new InstructionSi("\"toto \" > $toto vaen1502", contexte), + new InstructionSi("-5 <= 0 vaen 21", contexte), + new InstructionSi("resultat < 20 vaen 22", contexte), + }; + + /** + * Tests unitaires de {@link InstructionSi#InstructionSi(String, Contexte)} + */ + public void testInstructionSiStringContexte() { + final String[] ARGS_INVALIDES = { + "", + " \t", + " entier < index", + "vaen 1050", + "age = 10 vaen", + " $prenom = \"défaut\" va 10", + "$prenom <> $nom goto 45", + "$prenom <> $nom vaen dix", + "$prenom != $nom vaen 45", + /* erreur de type */ + "$prenom <> 5 vaen 450", + "age > \"\" vaen 450", + "age >= $prenom vaen 450", + "\"dix\" = 10 vaen 4500", + }; + + System.out.println("\tExécution du test de " + + "InstructionSi#InstructionSi(String, Contexte)"); + + for (String aTester : ARGS_INVALIDES) { + try { + new InstructionSi(aTester, contexte); + echec(); + } catch (InterpreteurException lancee) { + // testok + } + } + + try { + new InstructionSi("45 = 2 vaen 15", contexte); + new InstructionSi("age >= 130 vaen 1000", contexte); + new InstructionSi("$prenom <>\"défaut\" vaen 15", contexte); + new InstructionSi("resultat < 20 vaen 15", contexte); + new InstructionSi("resultat < moyenne vaen 15", contexte); + new InstructionSi("age > 20 vaen 15", contexte); + new InstructionSi("\"tata\" = \"tata\"vaen 15", contexte); + new InstructionSi("\"toto \" > $toto vaen1502", contexte); + new InstructionSi("-5 <= 0 vaen 15", contexte); + new InstructionSi("resultat < 20 vaen 15", contexte); + new InstructionSi("$chaine <= \"vaen 15\" vaen 15", contexte); + } catch (InterpreteurException lancee) { + echec(); + } + } + + /** + * Tests unitaires de {@link InstructionSi#toString()} + */ + public void testToString() { + final String[] ATTENDU = { + "si 45 = 2 vaen 15", + "si age >= 130 vaen 1000", + "si $prenom <> \"défaut\" vaen 16", + "si resultat < 20 vaen 17", + "si resultat < moyenne vaen 18", + "si age > 20 vaen 19", + "si \"tata\" = \"tata\" vaen 20", + "si \"toto \" > $toto vaen 1502", + "si -5 <= 0 vaen 21", + "si resultat < 20 vaen 22", + }; + System.out.println("\tExécution du test de InstructionSi#toString()"); + + for (int numTest = 0 ; numTest < ATTENDU.length ; numTest++) { + assertEquivalence(ATTENDU[numTest], fixture[numTest].toString()); + } + } + + /** + * Tests unitaires de {@link InstructionSi#executer()} + */ + public void testExecuter() { + Commande.referencerProgramme(prog); + prog.ajouterLigne(new Etiquette(15), + new InstructionVar("valeur = valeur -1", contexte)); + prog.ajouterLigne(new Etiquette(16), + new InstructionVar("valeur = valeur -1", contexte)); + prog.ajouterLigne(new Etiquette(17), + new InstructionVar("valeur = valeur -1", contexte)); + prog.ajouterLigne(new Etiquette(18), + new InstructionVar("valeur = valeur -1", contexte)); + prog.ajouterLigne(new Etiquette(19), + new InstructionVar("valeur = valeur -1", contexte)); + prog.ajouterLigne(new Etiquette(20), + new InstructionVar("valeur = valeur -1", contexte)); + prog.ajouterLigne(new Etiquette(21), + new InstructionVar("valeur = valeur -1", contexte)); + prog.ajouterLigne(new Etiquette(22), + new InstructionVar("valeur = valeur -1", contexte)); + prog.ajouterLigne(new Etiquette(1000), + new InstructionVar("valeur = valeur -1", contexte)); + prog.ajouterLigne(new Etiquette(1502), + new InstructionVar("valeur = valeur -1", contexte)); + Expression.referencerContexte(contexte); + + + final int[] VALEUR_ATTENDU = { + 0, // pas de saut + 0, + -9, // saut en 16 + -8, // saut en 17 + 0, + -6, // saut en 19 + -5, // saut en 20 + -1, // saut en 1502 + -4, // saut en 21 + -3, // saut en 22 + }; + + System.out.println("\tExécution du test de InstructionSi#executer()"); + + for (int numTest = 0 ; numTest < VALEUR_ATTENDU.length ; numTest++) { + /* initialisation du contexte */ + contexte.raz(); + contexte.ajouterVariable(new IdentificateurEntier("moyenne"), + new Entier("-2")); + contexte.ajouterVariable(new IdentificateurEntier("age"), + new Entier("99")); + contexte.ajouterVariable(new IdentificateurChaine("$toto"), + new Chaine("\"toto\"")); + + fixture[numTest].executer(); + assertEquivalence(VALEUR_ATTENDU[numTest], + ((Integer)contexte.lireValeurVariable( + new IdentificateurEntier("valeur")) + .getValeur()).intValue()); + } + } + + +} diff --git a/src/interpreteurlir/motscles/instructions/tests/TestInstructionStop.java b/src/interpreteurlir/motscles/instructions/tests/TestInstructionStop.java index c689480..f8ce5e0 100644 --- a/src/interpreteurlir/motscles/instructions/tests/TestInstructionStop.java +++ b/src/interpreteurlir/motscles/instructions/tests/TestInstructionStop.java @@ -44,7 +44,7 @@ public class TestInstructionStop { "entier = 2 + 3" }; - System.out.println("Exécution du test de InstructionStop" + System.out.println("\tExécution du test de InstructionStop" + "(String, Contexte)"); for (String aTester : INVALIDES) { try { @@ -59,32 +59,34 @@ public class TestInstructionStop { /** Test de executer() */ public static void testExecuter() { Programme pgmTest = new Programme(); - System.out.println("Exécution du test de executer()\ntestVisuel\n"); + System.out.println("\tExécution du test de executer()\nTest Visuels\n"); Commande.referencerProgramme(pgmTest); Expression.referencerContexte(CONTEXTE_TESTS); pgmTest.ajouterLigne(new Etiquette(10), - new InstructionAffiche("Bonjour", CONTEXTE_TESTS)); + new InstructionAffiche("\"Bonjour\"", CONTEXTE_TESTS)); pgmTest.ajouterLigne(new Etiquette(20), - new InstructionAffiche("Comment", CONTEXTE_TESTS)); + new InstructionAffiche("\"Comment\"", CONTEXTE_TESTS)); pgmTest.ajouterLigne(new Etiquette(30), - new InstructionAffiche("Allez", CONTEXTE_TESTS)); + new InstructionAffiche("\"Allez\"", CONTEXTE_TESTS)); pgmTest.ajouterLigne(new Etiquette(40), - new InstructionAffiche("Vous", CONTEXTE_TESTS)); + new InstructionAffiche("\"Vous\"", CONTEXTE_TESTS)); pgmTest.ajouterLigne(new Etiquette(45), new InstructionStop("", CONTEXTE_TESTS)); pgmTest.ajouterLigne(new Etiquette(50), - new InstructionAffiche("foobar", CONTEXTE_TESTS)); + new InstructionAffiche("\"foobar\"", CONTEXTE_TESTS)); System.out.println(pgmTest); System.out.println("lancement du programme : ne doit pas " + "afficher foobar"); pgmTest.lancer(); + + System.out.println(); } /** Tests de toString() */ public static void testToString() { final String ATTENDUE = "stop"; - System.out.println("Exécution du test de toString()"); + System.out.println("\tExécution du test de toString()"); for (InstructionStop valide : FIXTURE) assertTrue(valide.toString().compareTo(ATTENDUE) == 0); } diff --git a/src/interpreteurlir/motscles/instructions/tests/TestInstructionVar.java b/src/interpreteurlir/motscles/instructions/tests/TestInstructionVar.java index d0991ca..c371e21 100644 --- a/src/interpreteurlir/motscles/instructions/tests/TestInstructionVar.java +++ b/src/interpreteurlir/motscles/instructions/tests/TestInstructionVar.java @@ -4,12 +4,14 @@ */ package interpreteurlir.motscles.instructions.tests; +import static info1.outils.glg.Assertions.*; import interpreteurlir.Contexte; import interpreteurlir.InterpreteurException; import interpreteurlir.motscles.instructions.InstructionVar; /** - * Tests unitaires de l'instruction var + * Tests unitaires de la classe InstructionVar + * * @author Nicolas Caminade * @author Sylvan Courtiol * @author Pierre Debas @@ -25,59 +27,50 @@ public class TestInstructionVar { }; /** - * Test du constructeur InstructionVar + * Test unitaire de {@link InstructionVar#InstructionVar(String, Contexte)} */ public static void testInstructionVar() { final String[] EXPRESSIONS_INVALIDES = { - "bonjour", "", null, "$toto $tata", + "bonjour", "", "$toto $tata", }; - System.out.println("Test du constructeur\navec expressions invalides"); + System.out.println("\tExécution du test de InstructionVar(String, " + + "Contexte)"); + for (String aTester : EXPRESSIONS_INVALIDES) { try { new InstructionVar(aTester, new Contexte()); - throw new RuntimeException("Echec du test"); + echec(); } catch (InterpreteurException lancee) { - System.out.println(aTester + " ==> OK"); + // Test OK } } - System.out.println("Avec expressions valides"); - for (String aTester : VALIDES) - new InstructionVar(aTester, new Contexte()); - - System.out.println("Fin du test\n"); + for (String aTester : VALIDES) { + try { + new InstructionVar(aTester, new Contexte()); + } catch (InterpreteurException lancee) { + echec(); + } + } } /** - * Test de toString + * Test unitaire de {@link InstructionVar#toString()} */ public static void testToString() { final String[] CHAINES_ATTENDUES = { - "var $toto = $tata", "var entier=2+2", - "var $coucou = $toto + \\\"titi\\\"", + "var $toto = $tata", + "var entier = 2 + 2", + "var $coucou = $toto + \"titi\"", "var anneeNaissance = 1898" }; - System.out.println("Test de toString()"); + System.out.println("\tExécution du tes de toString()"); for (int i = 0 ; i < VALIDES.length ; i++) { - - try { - assert VALIDES[i].toString().equals(CHAINES_ATTENDUES[i]); - } catch (AssertionError lancee) { - System.err.println("Echec du test à l'indice " + i); - } + InstructionVar aTester = new InstructionVar(VALIDES[i], + new Contexte()); + assertTrue(CHAINES_ATTENDUES[i].equals(aTester.toString())); } - System.out.println("Fin du test\n"); } - - /** - * Lancement des tests - * @param args - */ - public static void main(String[] args) { - testInstructionVar(); - testToString(); - } - -} +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/tests/EssaiCommande.java b/src/interpreteurlir/motscles/tests/EssaiCommande.java index f8bdf7f..b4fae71 100644 --- a/src/interpreteurlir/motscles/tests/EssaiCommande.java +++ b/src/interpreteurlir/motscles/tests/EssaiCommande.java @@ -7,6 +7,7 @@ package interpreteurlir.motscles.tests; import interpreteurlir.Contexte; import interpreteurlir.InterpreteurException; import interpreteurlir.motscles.*; +import interpreteurlir.programmes.Programme; /** * Essais des commandes (création + éxécution) @@ -24,6 +25,7 @@ public class EssaiCommande { */ public static void main(String[] args) { Contexte contexte = new Contexte(); + Commande.referencerProgramme(new Programme()); /* Erreur dans commande */ System.out.println("? debut args"); @@ -49,9 +51,11 @@ public class EssaiCommande { System.out.println("? debut"); feedback(new CommandeDebut("", contexte).executer()); System.out.println("? defs"); - feedback(new CommandeDefs( "", contexte).executer()); + feedback(new CommandeDefs("", contexte).executer()); + System.out.println("? liste"); + feedback(new CommandeListe("", contexte).executer()); System.out.println("? fin"); - feedback(new CommandeFin( "", contexte).executer()); + feedback(new CommandeFin("", contexte).executer()); System.err.println("Erreur, la commande fin n'a pas quitter"); diff --git a/src/interpreteurlir/motscles/tests/TestCommande.java b/src/interpreteurlir/motscles/tests/TestCommande.java index f056649..4127e33 100644 --- a/src/interpreteurlir/motscles/tests/TestCommande.java +++ b/src/interpreteurlir/motscles/tests/TestCommande.java @@ -1,88 +1,90 @@ -/** - * TestCommande.java 7 mai 2021 - * IUT Rodez info1 2020-2021, pas de copyright, aucun droit - */ -package interpreteurlir.motscles.tests; +// Classe testée passé en abstract -import static info1.outils.glg.Assertions.*; - -import interpreteurlir.Contexte; -import interpreteurlir.expressions.Expression; -import interpreteurlir.motscles.Commande; -import interpreteurlir.programmes.Programme; - -/** - * Tests unitaires de {@link interpreteurlir.motscles.Commande} - * @author Nicolas Caminade - * @author Sylvan Courtiol - * @author Pierre Debas - * @author Heïa Dexter - * @author Lucas Vabre - */ -public class TestCommande { - - /** Jeux d'essais de Commande valides pour les tests */ - private Commande[] fixture = { - new Commande("", new Contexte()), - new Commande("coucou", new Contexte()), - new Commande("$chaine = \"toto\" + $tata", new Contexte()) - }; - - /** - * Tests unitaires de {@link Commande#referencerProgramme(Programme)} - */ - public void testReferencerProgramme() { - - Programme reference = new Programme(); - Programme[] programmes = { - null, reference, reference, new Programme() - }; - - boolean[] resultatAttendu = { false, true, true, false }; - - System.out.println("\tExécution du test de " - + "Commande#referencerProgramme(Programme)"); - for (int numTest = 0 ; numTest < programmes.length ; numTest++) { - assertTrue( Commande.referencerProgramme(programmes[numTest]) - == resultatAttendu[numTest]); - } - } - - /** - * Tests unitaires de {@link Commande#Commande(String, Contexte)} - */ - public void testCommandeStringContexte() { - System.out.println( - "\tExécution du test de Commande#Commande(String, Contexte)"); - - /* Tests Commande invalide */ - String[] arguments = { null, null, "" }; - Contexte[] contexte = { null, new Contexte(), null}; - for (int numTest = 0 ; numTest < arguments.length ; numTest++) { - try { - new Commande(arguments[numTest], contexte[numTest]); - echec(); - } catch (NullPointerException lancee) { - } - } - - try { - new Commande("", new Contexte()); - new Commande("coucou", new Contexte()); - new Commande("$chaine = \"toto\" + $tata", new Contexte()); - } catch (NullPointerException e) { - echec(); - } - } - - - /** - * Tests unitaires de {@link Commande#executer()} - */ - public void testExecuter() { - System.out.println("\tExécution du test de Commande#executer()"); - for (Commande aTester : fixture) { - assertFalse(aTester.executer()); - } - } -} +///** +// * TestCommande.java 7 mai 2021 +// * IUT Rodez info1 2020-2021, pas de copyright, aucun droit +// */ +//package interpreteurlir.motscles.tests; +// +//import static info1.outils.glg.Assertions.*; +// +//import interpreteurlir.Contexte; +//import interpreteurlir.expressions.Expression; +//import interpreteurlir.motscles.Commande; +//import interpreteurlir.programmes.Programme; +// +///** +// * Tests unitaires de {@link interpreteurlir.motscles.Commande} +// * @author Nicolas Caminade +// * @author Sylvan Courtiol +// * @author Pierre Debas +// * @author Heïa Dexter +// * @author Lucas Vabre +// */ +//public class TestCommande { +// +// /** Jeux d'essais de Commande valides pour les tests */ +// private Commande[] fixture = { +// new Commande("", new Contexte()), +// new Commande("coucou", new Contexte()), +// new Commande("$chaine = \"toto\" + $tata", new Contexte()) +// }; +// +// /** +// * Tests unitaires de {@link Commande#referencerProgramme(Programme)} +// */ +// public void testReferencerProgramme() { +// +// Programme reference = new Programme(); +// Programme[] programmes = { +// null, reference, reference, new Programme() +// }; +// +// boolean[] resultatAttendu = { false, true, true, false }; +// +// System.out.println("\tExécution du test de " +// + "Commande#referencerProgramme(Programme)"); +// for (int numTest = 0 ; numTest < programmes.length ; numTest++) { +// assertTrue( Commande.referencerProgramme(programmes[numTest]) +// == resultatAttendu[numTest]); +// } +// } +// +// /** +// * Tests unitaires de {@link Commande#Commande(String, Contexte)} +// */ +// public void testCommandeStringContexte() { +// System.out.println( +// "\tExécution du test de Commande#Commande(String, Contexte)"); +// +// /* Tests Commande invalide */ +// String[] arguments = { null, null, "" }; +// Contexte[] contexte = { null, new Contexte(), null}; +// for (int numTest = 0 ; numTest < arguments.length ; numTest++) { +// try { +// new Commande(arguments[numTest], contexte[numTest]); +// echec(); +// } catch (NullPointerException lancee) { +// } +// } +// +// try { +// new Commande("", new Contexte()); +// new Commande("coucou", new Contexte()); +// new Commande("$chaine = \"toto\" + $tata", new Contexte()); +// } catch (NullPointerException e) { +// echec(); +// } +// } +// +// +// /** +// * Tests unitaires de {@link Commande#executer()} +// */ +// public void testExecuter() { +// System.out.println("\tExécution du test de Commande#executer()"); +// for (Commande aTester : fixture) { +// assertFalse(aTester.executer()); +// } +// } +//} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/tests/TestCommandeCharge.java b/src/interpreteurlir/motscles/tests/TestCommandeCharge.java new file mode 100644 index 0000000..59dcc81 --- /dev/null +++ b/src/interpreteurlir/motscles/tests/TestCommandeCharge.java @@ -0,0 +1,111 @@ +/** + * TestCommandeCharge.java 21 mai 2021 + * IUT Rodez info1 2020-2021, pas de copyright, aucun droit + */ +package interpreteurlir.motscles.tests; + +import interpreteurlir.Contexte; +import interpreteurlir.InterpreteurException; +import interpreteurlir.motscles.Commande; +import interpreteurlir.motscles.CommandeCharge; +import interpreteurlir.motscles.CommandeListe; +import interpreteurlir.programmes.Programme; + +import static info1.outils.glg.Assertions.*; + +/** + * Tests unitaires de {@link interpreteurlir.motscles.CommandeCharge} + * @author Nicolas Caminade + * @author Sylvan Courtiol + * @author Pierre Debas + * @author Heia Dexter + * @author Lucas Vabre + */ +public class TestCommandeCharge { + + /** Contexte pour tests */ + private final static Contexte CONTEXTE_TESTS = new Contexte(); + + /** Programme global pour tests */ + private static Programme progGlobal = new Programme(); + + /** jeu de test valide */ + public static final CommandeCharge[] FIXTURE = { + new CommandeCharge("F:\\Programmation\\WorkspaceInterpreteurLIR" + + "\\outilTest\\dossierFichier\\" + + "lefichier1.lir", CONTEXTE_TESTS), + new CommandeCharge("dossierFichier\\lefichier2.lir", + CONTEXTE_TESTS), + new CommandeCharge("dossierFichier\\lefichier3.lir", + CONTEXTE_TESTS), + new CommandeCharge("dossierFichier\\test\\lefichier4.lir", + CONTEXTE_TESTS), + new CommandeCharge("dossierFichier\\test\\test2\\lefichier5.lir", + CONTEXTE_TESTS), + new CommandeCharge(" dossierFichier\\lefichier6.lir ", + CONTEXTE_TESTS), + new CommandeCharge("dossierFichier\\test\\test2\\..\\lefichier7.lir", + CONTEXTE_TESTS) + }; + + /** + * Tests unitaires de + * {@link CommandeCharge#CommandeCharge(String, Contexte)} + */ + public static void testCommandeCharge() { + + final String[] INVALIDE = { + null, + " ", + "", + "lefichier", + "dossier\\ lefichier", + "dossier \\lefichier", + }; + + for (int i = 0; i < INVALIDE.length ; i++) { + try { + new CommandeCharge(INVALIDE[i], CONTEXTE_TESTS); + echec(); + } catch (InterpreteurException | NullPointerException lancee) { + // Test OK + } + } + } + + /** + * Tests unitaires de + * {@link CommandeCharge#executer()} + */ + public static void testExecuter() { + + Commande.referencerProgramme(progGlobal); + + final CommandeCharge[] ERREUR = { + new CommandeCharge("dossierFichier\\erreur1.lir", + CONTEXTE_TESTS), + new CommandeCharge("dossierFichier\\erreur2.lir", + CONTEXTE_TESTS) + }; + + final int NB_TESTS = FIXTURE.length; + System.out.println("\nTest valides de CommandeCharge#executer():"); + for (int i = 0; i < NB_TESTS ; i++) { + System.out.println("Test " + (i+1) + '\\' + NB_TESTS + ":"); + FIXTURE[i].executer(); + new CommandeListe("", CONTEXTE_TESTS).executer(); + } + + System.out.println("\nTest invalides de CommandeCharge#executer():"); + for(int i = 0; i < ERREUR.length ; i++) { + try { + ERREUR[i].executer(); + echec(); + } catch (InterpreteurException lancee) { + // Test OK + new CommandeListe("", CONTEXTE_TESTS).executer(); + } + } + } + +} \ No newline at end of file diff --git a/src/interpreteurlir/motscles/tests/TestCommandeDebut.java b/src/interpreteurlir/motscles/tests/TestCommandeDebut.java index b69546f..c1be46b 100644 --- a/src/interpreteurlir/motscles/tests/TestCommandeDebut.java +++ b/src/interpreteurlir/motscles/tests/TestCommandeDebut.java @@ -8,7 +8,9 @@ import static info1.outils.glg.Assertions.*; import interpreteurlir.InterpreteurException; import interpreteurlir.Contexte; +import interpreteurlir.motscles.Commande; import interpreteurlir.motscles.CommandeDebut; +import interpreteurlir.programmes.Programme; /** * Tests unitaires de {@link interpreteurlir.motscles.CommandeDebut} @@ -59,6 +61,7 @@ public class TestCommandeDebut { * Tests unitaires de {@link CommandeDebut#executer()} */ public void testExecuter() { + Commande.referencerProgramme(new Programme()); System.out.println("\tExécution du test de CommandeDebut#executer()"); for (CommandeDebut cmd : fixture) { assertFalse(cmd.executer()); diff --git a/src/interpreteurlir/motscles/tests/TestCommandeEfface.java b/src/interpreteurlir/motscles/tests/TestCommandeEfface.java index 7d0257a..63d6386 100644 --- a/src/interpreteurlir/motscles/tests/TestCommandeEfface.java +++ b/src/interpreteurlir/motscles/tests/TestCommandeEfface.java @@ -54,14 +54,14 @@ public class TestCommandeEfface { "\'a\' : 99" }; - System.out.println("Exécution du test de CommandeEfface" + System.out.println("\tExécution du test de CommandeEfface" + "(String, Contexte)"); for (String aTester : INVALIDES) { try { new CommandeEfface(aTester, CONTEXTE_TESTS); echec(); } catch (InterpreteurException e) { - // TODO: handle exception + // test OK } } } @@ -69,7 +69,7 @@ public class TestCommandeEfface { /** Test de executer() */ public static void testExecuter() { - System.out.println("Exécution du test d'executer()\nTest visuel :"); + System.out.println("\tExécution du test d'executer()\nTest visuel :"); Commande.referencerProgramme(PGM_TESTS); PGM_TESTS.ajouterLigne(new Etiquette(10), new InstructionAffiche("Bonjour", CONTEXTE_TESTS)); diff --git a/src/interpreteurlir/motscles/tests/TestCommandeFin.java b/src/interpreteurlir/motscles/tests/TestCommandeFin.java index 327d758..0e6940b 100644 --- a/src/interpreteurlir/motscles/tests/TestCommandeFin.java +++ b/src/interpreteurlir/motscles/tests/TestCommandeFin.java @@ -19,11 +19,6 @@ import interpreteurlir.motscles.CommandeFin; * @author Lucas Vabre */ public class TestCommandeFin { - - /** Jeux d'essais de Commande valides pour les tests */ - private CommandeFin[] fixture = { - new CommandeFin("", new Contexte()), - }; /** * Tests unitaires de {@link CommandeFin#CommandeFin(String, Contexte)} @@ -61,7 +56,6 @@ public class TestCommandeFin { System.out.println("\tLe programme doit s'éteindre en affichant un " + "message d'aurevoir :"); System.out.println("Test exécuter désactiver"); -// fixture[0].executer(); - echec(); + //fixture[0].executer(); } } diff --git a/src/interpreteurlir/motscles/tests/TestCommandeLance.java b/src/interpreteurlir/motscles/tests/TestCommandeLance.java index 1f7abe7..b969620 100644 --- a/src/interpreteurlir/motscles/tests/TestCommandeLance.java +++ b/src/interpreteurlir/motscles/tests/TestCommandeLance.java @@ -8,10 +8,6 @@ import interpreteurlir.Contexte; import interpreteurlir.InterpreteurException; import interpreteurlir.expressions.Expression; import interpreteurlir.motscles.CommandeLance; -import interpreteurlir.motscles.instructions.Instruction; -import interpreteurlir.motscles.instructions.InstructionVar; -import interpreteurlir.programmes.Etiquette; -import interpreteurlir.programmes.Programme; import static info1.outils.glg.Assertions.*; @@ -29,7 +25,6 @@ import info1.outils.glg.TestException; public class TestCommandeLance { private Contexte contexteTest = new Contexte(); - private Programme programmeTest = new Programme(); private final CommandeLance[] FIXTURE = { new CommandeLance("", contexteTest), @@ -47,37 +42,7 @@ public class TestCommandeLance { "20", "70", "40" - }; - - private final Etiquette[] JEU_ETIQUETTES = { - new Etiquette(1), - new Etiquette(10), - new Etiquette(13), - new Etiquette(25), - new Etiquette(31), - new Etiquette(40), - new Etiquette(78), - new Etiquette(89) - }; - - private final Instruction[] JEU_INSTRUCTIONS = { - new InstructionVar("$res = \"1 \"", contexteTest), - new InstructionVar("$res = $res + \"10 \"", contexteTest), - new InstructionVar("$res = $res + \"13 \"", contexteTest), - new InstructionVar("$res = $res + \"25 \"", contexteTest), - new InstructionVar("$res = $res + \"31 \"", contexteTest), - new InstructionVar("$res = $res + \"40 \"", contexteTest), - new InstructionVar("$res = $res + \"78 \"", contexteTest), - new InstructionVar("$res = $res + \"89 \"", contexteTest) - }; - - private void ecrireProgrammeTest() { - for (int i = 0 ; i < JEU_ETIQUETTES.length ; i++) { - programmeTest.ajouterLigne(JEU_ETIQUETTES[i], - JEU_INSTRUCTIONS[i]); - } - } - + }; /** * Test unitaire de @@ -95,6 +60,9 @@ public class TestCommandeLance { Expression.referencerContexte(contexteTest); + System.out.println("\tExécution du test de " + + "CommandeLance#CommandeLance(String, Contexte)"); + for (int i = 0; i < ARGS_INVALIDES.length; i++) { try { new CommandeLance(ARGS_INVALIDES[i], contexteTest); @@ -122,6 +90,7 @@ public class TestCommandeLance { //ecrireProgrammeTest(); Expression.referencerContexte(contexteTest); + System.out.println("\tExécution du test de CommandeLance#executer()"); for (int i = 0 ; i < FIXTURE.length ; i++) { try { FIXTURE[i].executer(); diff --git a/src/interpreteurlir/motscles/tests/TestCommandeListe.java b/src/interpreteurlir/motscles/tests/TestCommandeListe.java index 67013de..1986278 100644 --- a/src/interpreteurlir/motscles/tests/TestCommandeListe.java +++ b/src/interpreteurlir/motscles/tests/TestCommandeListe.java @@ -91,6 +91,9 @@ public class TestCommandeListe { "1:100000" }; + System.out.println("\tExécution du test de " + + "CommandeListe#CommandeListe(String, Contexte)"); + for (int i = 0; i < ARGS_INVALIDES.length; i++) { try { new CommandeListe(ARGS_INVALIDES[i], contexteTest); @@ -130,6 +133,9 @@ public class TestCommandeListe { Commande.referencerProgramme(programmeTest); Expression.referencerContexte(contexteTest); + System.out.println("\tExécution du test de " + + "CommandeListe#executer()"); + for (int i = 0 ; i < FIXTURE.length ; i++) { try { FIXTURE[i].executer(); diff --git a/src/interpreteurlir/motscles/tests/TestCommandeSauve.java b/src/interpreteurlir/motscles/tests/TestCommandeSauve.java new file mode 100644 index 0000000..64951e0 --- /dev/null +++ b/src/interpreteurlir/motscles/tests/TestCommandeSauve.java @@ -0,0 +1,176 @@ +/** + * TestCommandeSauve.java 21 mai 2021 + * IUT Rodez info1 2020-2021, pas de copyright, aucun droit + */ +package interpreteurlir.motscles.tests; + +import static info1.outils.glg.Assertions.*; + +import interpreteurlir.motscles.Commande; +import interpreteurlir.motscles.CommandeSauve; +import interpreteurlir.programmes.Programme; +import interpreteurlir.Contexte; +import interpreteurlir.ExecutionException; +import interpreteurlir.InterpreteurException; +import interpreteurlir.tests.ProgrammeDeTest; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.InputStreamReader; + +/** + * Tests unitaires de {@link CommandeSauve} + * @author Nicolas Caminade + * @author Sylvan Courtiol + * @author Pierre Debas + * @author Heia Dexter + * @author Lucas Vabre + */ +public class TestCommandeSauve { + + /** contexte pour les tests */ + private Contexte contexte = new Contexte(); + + /** Programme pour les tests */ + private Programme progGlobal = new Programme(); + + /** Jeu de donnée de commandeSauve valides pour les tests */ + private CommandeSauve[] fixture = { + /* chemin valide */ + new CommandeSauve("monProgramme.lir", contexte), + new CommandeSauve("programmationLIR\\monProgramme.lir", contexte), + new CommandeSauve("D:\\testInterpreteurLIR\\test1.lir", contexte), + new CommandeSauve(" D:\\testInterpreteurLIR\\test2.lir\t", contexte), + + /* chemin invalide à l'exécution*/ + new CommandeSauve("\\\\monProgramme.lir", contexte), + new CommandeSauve("monPro//??!gr<>amme.lir", contexte), + /* chemin inexistant */ + new CommandeSauve("D:\\testInterpreteurLIR\\dossierNonCree\\test1.lir", + contexte), + /* lecteur inexistant */ + new CommandeSauve("X:\\testInterpreteurLIR\\test1.lir", contexte), + }; + + /** + * Tests unitaires de {@link CommandeSauve#CommandeSauve(String, Contexte)} + */ + public void testCommandeSauveStringContexte() { + final String[] ARGS_INVALIDES = { + "", + " \t ", + "D:\\utilisateurs\\defaut\\bureau\\", + "D:\\utilisateurs\\defaut\\bureau\\monProgramme.txt", + "D:\\utilisateurs\\defaut\\bureau\\monProgramme", + "nouveau dossier\\monProgramme.java", + "nouveau dossier\\monProgramme", + "monProgramme.class", + "monProgramme" + }; + + System.out.println("\tExécution du test de " + + "CommandeSauve#CommandeSauve(String, Contexte)"); + + for (String aTester : ARGS_INVALIDES) { + try { + new CommandeSauve(aTester, contexte); + echec(); + } catch (InterpreteurException e) { + // test ok + } + } + + try { + /* chemin valide */ + new CommandeSauve("monProgramme.lir", contexte); + new CommandeSauve("programmationLIR\\monProgramme.lir", contexte); + new CommandeSauve("D:\\testInterpreteurLIR\\test1.lir", contexte); + new CommandeSauve(" D:\\testInterpreteurLIR\\test2.lir\t", + contexte); + /* chemin invalide à l'exécution*/ + new CommandeSauve("\\\\monProgramme.lir", contexte); + new CommandeSauve("monPro//??!gr<>amme.lir", contexte); + /* chemin inexistant */ + new CommandeSauve("D:\\testInterpreteurLIR\\dossierNonCree\\" + + "test1.lir", contexte); + /* lecteur inexistant */ + new CommandeSauve("X:\\testInterpreteurLIR\\test1.lir", contexte); + } catch (InterpreteurException e) { + echec(); + } + + } + + /** + * Tests unitaires de {@link CommandeSauve#executer()} + */ + public void testExecuter() { + final int INDEX_INVALIDES = 4; + + Commande.referencerProgramme(progGlobal); + System.out.println("\tExécution du test de CommandeSauve#executer()"); + + /* Tests des chemins invalides */ + for (int index = INDEX_INVALIDES ; index < fixture.length ; index++) { + try { + fixture[index].executer(); + echec(); + } catch (ExecutionException lancee) { + // test OK + } + } + + + try { + assertFalse(fixture[0].executer()); + assertEquivalence(progGlobal.toString(), + lireFichier("monProgramme.lir")); + assertFalse(fixture[2].executer()); + assertEquivalence(progGlobal.toString(), + lireFichier("D:\\testInterpreteurLIR\\test1.lir")); + + ProgrammeDeTest.genererProgramme(progGlobal, contexte); + + assertFalse(fixture[1].executer()); + assertEquivalence(progGlobal.toString(), + lireFichier("programmationLIR\\monProgramme.lir")); + + assertFalse(fixture[3].executer()); + assertEquivalence(progGlobal.toString(), + lireFichier("D:\\testInterpreteurLIR\\test2.lir")); + + } catch (ExecutionException lancee) { + echec(); + } + + } + + /** + * Lit un fichier et retourne le contenu entier du fichier + * @param cheminFichier chemin du fichier à lire + * @return contenu du fichier + */ + private static String lireFichier(String cheminFichier) { + BufferedReader aTester; + StringBuilder contenu = new StringBuilder(""); + + aTester = null; + try { + aTester = new BufferedReader( + new InputStreamReader( + new FileInputStream(cheminFichier))); + String ligneLue; + do { + ligneLue = aTester.readLine(); + if (ligneLue != null) { + contenu.append(ligneLue).append("\n"); + } + } while (ligneLue != null); + aTester.close(); + } catch (Exception e) { + echec(); + } + + return contenu.toString(); + } +} diff --git a/src/interpreteurlir/programmes/Programme.java b/src/interpreteurlir/programmes/Programme.java index e7ea785..47931f7 100644 --- a/src/interpreteurlir/programmes/Programme.java +++ b/src/interpreteurlir/programmes/Programme.java @@ -30,7 +30,7 @@ import static interpreteurlir.programmes.Etiquette.VALEUR_ETIQUETTE_MIN; public class Programme { private static final String ERREUR_INTERVALLE = "erreur dans l'intervalle " - + "d'étiquettes. "; + + "d'étiquettes"; /** Pile LIFO pour la gestion des étiquettes */ private Stack compteurOrdinnal; @@ -42,15 +42,13 @@ public class Programme { /** * Initialisation de ce programme sans lignes de code - *

- * */ public Programme() { super(); + lignesCode = new TreeMap(); enExecution = false; compteurOrdinnal = new Stack(); - } /** @@ -91,11 +89,8 @@ public class Programme { @Override public String toString() { - Object[] tableauEtiquette - = lignesCode.keySet().toArray(); - - Object[] tableauInstruction - = lignesCode.values().toArray(); + Object[] tableauEtiquette = lignesCode.keySet().toArray(); + Object[] tableauInstruction = lignesCode.values().toArray(); StringBuilder aAfficher = new StringBuilder(""); @@ -150,9 +145,8 @@ public class Programme { } } while (lignesRestantes); - return aAfficher.toString().equals("") - ? "Aucune ligne de code dans cet intervalle.\n" - : aAfficher.toString(); + return aAfficher.toString().equals("") ? "aucune ligne à afficher\n" + : aAfficher.toString(); } /** @@ -236,8 +230,18 @@ public class Programme { /** * Change le compteur ordinal avec l'étiquette argument * @param destination étiquette où continuer l'exécution + * @throws ExecutionException si aucune instruction dans le programme + * n'a comme étiquette destination */ public void vaen(Etiquette destination) { + final String ERR_ETIQUETTE = "vaen impossible car l'étiquette " + + destination + + " ne correspond à aucune instruction"; + if (!lignesCode.containsKey(destination)) { + throw new ExecutionException(ERR_ETIQUETTE); + } + // else + if (!compteurOrdinnal.isEmpty()) { compteurOrdinnal.pop(); } @@ -258,12 +262,11 @@ public class Programme { /** * 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 " + final String ERREUR_RETOUR = "retour nécessite un appel de " + "procédure au préalable"; try { compteurOrdinnal.pop(); diff --git a/src/interpreteurlir/programmes/tests/TestProgramme.java b/src/interpreteurlir/programmes/tests/TestProgramme.java index 6674842..496aad5 100644 --- a/src/interpreteurlir/programmes/tests/TestProgramme.java +++ b/src/interpreteurlir/programmes/tests/TestProgramme.java @@ -10,6 +10,9 @@ import interpreteurlir.ExecutionException; import interpreteurlir.InterpreteurException; import interpreteurlir.expressions.Expression; import interpreteurlir.motscles.instructions.*; +import interpreteurlir.motscles.instructions.tests.TestInstructionStop; +import interpreteurlir.motscles.instructions.tests.TestInstructionVaen; + import static info1.outils.glg.Assertions.*; /** @@ -156,7 +159,7 @@ public class TestProgramme { public void testListeBornee() { final String[] TEXTES_ATTENDUS = { - "Aucune ligne de code dans cet intervalle.\n", + "aucune ligne à afficher\n", "1 var $toto = \"toto\"\n" + "5 var $agreuagreu = \"agreu\"\n" + "10 var tata = 0 + 0\n" @@ -202,13 +205,6 @@ public class TestProgramme { */ public void testEffacer() { -// final Etiquette[][] BORNES = { -// { new Etiquette(6), new Etiquette(6) }, -// { new Etiquette(1), new Etiquette(90) }, -// { new Etiquette(31), new Etiquette(39) }, -// { new Etiquette(9), new Etiquette(41) } -// }; - final String[] TEXTES_ATTENDUS = { "1 var $toto = \"toto\"\n" + "5 var $agreuagreu = \"agreu\"\n" @@ -256,13 +252,11 @@ public class TestProgramme { /** * Test unitaire de {@link Programme#stop()} + * @see TestInstructionStop#testExecuter() */ public void testStop() { - //TODO écrire les tests lorsque l'instruction stop sera définie - - System.out.println("\tExécution du test de stop() : "); - - echec(); + System.out.println("\tExécution du test de Programme#stop() " + + ": voir TestInstructionStop#testExecuter()"); } /** @@ -271,7 +265,7 @@ public class TestProgramme { public void testLancerEtiquette() { final Etiquette[] ETIQUETTES_DEPART = { new Etiquette(1), - new Etiquette(9), + new Etiquette(10), new Etiquette(25), new Etiquette(90) }; @@ -297,7 +291,6 @@ public class TestProgramme { * Test unitaire de {@link Programme#lancer()} */ public void testLancer() { - Expression.referencerContexte(contexteTest); contexteTest.raz(); @@ -314,12 +307,33 @@ public class TestProgramme { * Test unitaire de {@link Programme#appelProcedure(Etiquette)} */ public void testAppelProcedure() { - //TODO écrire les tests System.out.println("\tExécution du test de appelProcedure(Etiquette) " + ": "); - echec(); + /* Cas Valides */ + try { + /* Simulation du lancement du programme */ + programmeTest.appelProcedure(new Etiquette(1)); + /* Lancement de 2 procédures */ + programmeTest.appelProcedure(new Etiquette(100)); + programmeTest.appelProcedure(new Etiquette(50)); + } catch (InterpreteurException lancee) { + echec(); + } + + /* Cas Invalides */ + try { + /* Simulation du lancement du programme */ + programmeTest.appelProcedure(new Etiquette(1)); + + /* Lancement de 2 procédures */ + programmeTest.appelProcedure(new Etiquette(-30)); + programmeTest.appelProcedure(new Etiquette(10000000)); + echec(); + } catch (InterpreteurException lancee) { + /* Test OK */ + } } /** @@ -352,13 +366,11 @@ public class TestProgramme { /** * Test unitaire de {@link Programme#vaen(Etiquette)} + * @see TestInstructionVaen#testExecuter() */ public void testVaen() { - //TODO écrire les tests - System.out.println("\tExécution du test de vaen(Etiquette) " - + ": "); - - echec(); + + ": voir TestInstructionVaen#testExecuter()"); + } } diff --git a/src/interpreteurlir/tests/ProgrammeDeTest.java b/src/interpreteurlir/tests/ProgrammeDeTest.java new file mode 100644 index 0000000..d0258c5 --- /dev/null +++ b/src/interpreteurlir/tests/ProgrammeDeTest.java @@ -0,0 +1,69 @@ +/** + * ProgrammeDeTest.java 18 mai 2021 + * IUT Rodez info1 2020-2021, pas de copyright, aucun droit + */ +package interpreteurlir.tests; + +import interpreteurlir.Contexte; +import interpreteurlir.motscles.instructions.*; +import interpreteurlir.programmes.Etiquette; +import interpreteurlir.programmes.Programme; + +/** + * Permet de générer un programme pour les tests contenant plusieurs lignes. + * @author Nicolas Caminade + * @author Sylvan Courtiol + * @author Pierre Debas + * @author Heïa Dexter + * @author Lucas Vabre + */ +public class ProgrammeDeTest { + + /** + * Génère en ajoutant au programme des lignes + * pour les tests + * @param aGenerer programme auquel est ajouté les lignes + * @param contexte contexte pour les instructions + */ + public static void genererProgramme(Programme aGenerer, Contexte contexte) { + Object[][] lignes = { + {new Etiquette(10), new InstructionAffiche("\"Bienvenue dans le programme\"", contexte)}, + {new Etiquette(20), new InstructionAffiche("", contexte)}, + {new Etiquette(30), new InstructionVar("instant = 2021", contexte)}, + {new Etiquette(40), new InstructionProcedure("500", contexte)}, + {new Etiquette(50), new InstructionVar("$message = \"Vous êtes \" + $prenom", contexte)}, + {new Etiquette(60), new InstructionVar("$message = $message+ \" \"", contexte)}, + {new Etiquette(65), new InstructionVar("$message = $message+ $nom", contexte)}, + {new Etiquette(70), new InstructionAffiche("$message", contexte)}, + {new Etiquette(80), new InstructionAffiche("", contexte)}, + {new Etiquette(90), new InstructionAffiche("\"age : \"", contexte)}, + {new Etiquette(100), new InstructionAffiche("age", contexte)}, + {new Etiquette(110), new InstructionAffiche("\" ans\"", contexte)}, + {new Etiquette(120), new InstructionVaen("130", contexte)}, + {new Etiquette(124), new InstructionAffiche("", contexte)}, + {new Etiquette(125), new InstructionAffiche("\"erreur vaen si affiché\"", contexte)}, + {new Etiquette(150), new InstructionAffiche("", contexte)}, + {new Etiquette(200), new InstructionAffiche("\"Merci d'avoir utilisé ce programme !\"", contexte)}, + {new Etiquette(400), new InstructionStop("", contexte)}, + // procedure saisie + {new Etiquette(500), new InstructionAffiche("\"Saisissez votre nom : \"", contexte)}, + {new Etiquette(510), new InstructionEntre("$nom", contexte)}, + {new Etiquette(520), new InstructionAffiche("\"Saisissez votre prénom : \"", contexte)}, + {new Etiquette(530), new InstructionEntre("$prenom", contexte)}, + {new Etiquette(540), new InstructionAffiche("\"Saisissez votre année de naissance (entier) : \"", contexte)}, + {new Etiquette(550), new InstructionEntre("naissance", contexte)}, + {new Etiquette(560), new InstructionProcedure("1000", contexte)}, + {new Etiquette(570), new InstructionRetour("", contexte)}, + // procedure calcul age + {new Etiquette(1000), new InstructionVar("age = instant - naissance", contexte)}, + {new Etiquette(1010), new InstructionRetour("", contexte)}, + + }; + + for (int index = 0 ; index < lignes.length ; index++) { + aGenerer.ajouterLigne((Etiquette)lignes[index][0], + (Instruction)lignes[index][1]); + } + } + +} diff --git a/src/interpreteurlir/tests/TestContexte.java b/src/interpreteurlir/tests/TestContexte.java index c2869bd..0b70d95 100644 --- a/src/interpreteurlir/tests/TestContexte.java +++ b/src/interpreteurlir/tests/TestContexte.java @@ -86,7 +86,6 @@ public class TestContexte { "aucune variable n'est définie\n", "aucune variable n'est définie\n", "aucune variable n'est définie\n", - // TODO refaire quand totalement fonctionnel }; System.out.println("\tExécution du test de Contexte#toString()"); @@ -136,7 +135,8 @@ public class TestContexte { assertEquivalence(fixture[0].lireValeurVariable( new IdentificateurChaine("$chaine")).getValeur(), ""); assertEquivalence(fixture[0].lireValeurVariable( - new IdentificateurEntier("entier")).getValeur(), Integer.valueOf(0)); + new IdentificateurEntier("entier")).getValeur(), + Integer.valueOf(0)); // lire valeur par défaut dans contexte non vide fixture[1].ajouterVariable(new IdentificateurChaine("$zoro"),