diff --git a/expression.py b/expression.py index 8db045e..b80fd91 100644 --- a/expression.py +++ b/expression.py @@ -1,5 +1,5 @@ -from Pile import Pile_chaine as Pile from math import * +from Pile import Pile_chaine as Pile from infixe import estUnNombre class Expression: @@ -11,7 +11,7 @@ class Expression: def evalue(self): """renvoie la valeur de l'expression - si 1 seul argument alors on prend le fils gauche""" + s'il n'y a qu'un seul argument alors on prend le fils gauche""" #fonctions if self.racine == "sin": return sin(self.gauche.evalue()) if self.racine == "cos": return cos(self.gauche.evalue()) @@ -21,8 +21,7 @@ class Expression: if self.racine == "atan": return atan(self.gauche.evalue()) if self.racine == "opp": return - self.gauche.evalue() if self.racine == "sqrt": return sqrt(self.gauche.evalue()) - if self.racine == "factorielle": return factorial(self.gauche.evalue()) - + if self.racine == "factorielle": return gamma(self.gauche.evalue()) #operateurs if self.racine == "+": return self.gauche.evalue() + self.droit.evalue() if self.racine == "-": return self.gauche.evalue() - self.droit.evalue() @@ -59,29 +58,16 @@ class Expression: "/":"gauche", "+":"gauche", "-":"gauche"} - pas_commutatif = ["^","/","-"] - if self.racine in ["opp","!", "^", "*", "/", "+", "-"]: + pas_commutatif = {"^","/","-"} + if self.racine in {"!", "^", "*", "/", "+", "-"}: gauche = str(self.gauche) - - #definitivement à améliorer - if self.droit == None: - # racine est "-" (unaire) ou "opp" - priorite_racine = priorite[self.racine] - priorite_gauche = priorite.get(self.gauche.racine, -1) - #test - print(self.gauche.racine) - #test - if self.gauche.racine[0] == "-" or priorite_racine <= priorite_gauche: - gauche = parentheses(gauche) - return "-" + gauche - droit = str(self.droit) priorite_racine = priorite[self.racine] priorite_gauche = priorite.get(self.gauche.racine, -1) priorite_droit = priorite.get(self.droit.racine, -1) - if (self.gauche.racine == "opp" or (self.gauche.racine != "-" and self.gauche.racine[0] == "-")) and self.racine not in ["+","-"]: + if (self.gauche.racine == "opp" or (self.gauche.racine != "-" and self.gauche.racine[0] == "-")) and self.racine not in {"+","-"}: gauche = parentheses(gauche) if self.droit.racine == "opp" or (self.droit.racine != "-" and self.droit.racine[0] == "-"): droit = parentheses(droit) @@ -98,35 +84,31 @@ class Expression: return gauche + self.racine + droit - if self.racine in ["asin","acos","atan","sin","cos","tan","sqrt","factorielle"]: + if self.racine == "opp": + gauche = str(self.gauche) + + priorite_racine = priorite[self.racine] + priorite_gauche = priorite.get(self.gauche.racine, -1) + + if priorite_racine < priorite_gauche or self.gauche.racine == "opp": + gauche = parentheses(gauche) + return "-" + gauche + + if self.racine in {"sin","cos","tan","sqrt","factorielle","asin","acos","atan"}: return self.racine + "(" + str(self.gauche) + ")" + return str(self.racine) def parentheses(txt): """ajoute des parentheses à txt""" return "(" + txt + ")" -def npi2tree_original(liste_en_npi): - #obsolète (il faudrait mettre à jour la liste de fonctions) - """conversion d'une liste en NPI en un arbre""" - pile_expr = Pile() - for element in liste_en_npi: - if element in ["+", "*","-","/", "^"]: - fils_droit = pile_expr.depiler() - fils_gauche = pile_expr.depiler() - pile_expr.empiler(Expression(element, fils_gauche, fils_droit)) - elif element in ["sin","cos","tan", "opp"]: - pile_expr.empiler(Expression(element, pile_expr.depiler(),None)) - else: - pile_expr.empiler(Expression(element, None, None)) - return pile_expr.sommet() -#renommer la fonction !... -def npi2tree(liste, etat_affichage): - """conversion d'une liste en NPI ou NP en un arbre""" +def toTree(liste, etat_affichage): + """conversion d'une liste en NPI ou NP (pas en infixe) en un arbre""" pile_expr = Pile() for i,element in enumerate(liste): - if element in ["+","-", "*","/", "^"]: + if element in {"+","-", "*","/", "^"}: fils_gauche = pile_expr.depiler() fils_droit = pile_expr.depiler() @@ -135,9 +117,13 @@ def npi2tree(liste, etat_affichage): pile_expr.empiler(Expression(element, fils_gauche, fils_droit)) - elif element in ["asin","acos","atan","sin","cos","tan", "opp", "sqrt","factorielle"]: + elif element in {"opp","sin","cos","tan","sqrt","factorielle","asin","acos","atan"}: pile_expr.empiler(Expression(element, pile_expr.depiler(), None)) elif estUnNombre(element) or element in ["pi","e"]: + if "." in element: + element = str(float(element)) + else: + element = str(int(element)) pile_expr.empiler(Expression(element, None, None)) else: raise ValueError("Fonction inconnue","n'est pas une fonction connue, ou il manque des espaces.", i) diff --git a/infixe.py b/infixe.py index fdece50..cc8f5bf 100644 --- a/infixe.py +++ b/infixe.py @@ -1,14 +1,14 @@ -from Pile import Pile_chaine as Pile from math import * +from Pile import Pile_chaine as Pile def infixe2npi(liste_en_infixe): - """conversion d'une liste en notation infixe à une liste en npi""" + """conversion d'une liste en notation infixe en une liste en NPI""" pile_op = Pile() sortie = [] - fonctions = ["asin","acos","atan","sin","cos","tan","sqrt","factorielle"] - operateurs = ["+", "-", "*", "/", "^","opp"] + fonctions = {"sin","cos","tan","sqrt","factorielle","asin","acos","atan"} + operateurs = {"+", "-", "*", "/", "^","opp"} priorite = {"sin":1, "cos":1, "tan":1, @@ -31,27 +31,18 @@ def infixe2npi(liste_en_infixe): "-":"gauche"} last_token = None - print(40*"=") + position_dernier_token = "nulle part" for index_token, token in enumerate(liste_en_infixe): - print(20*"-") - print("token", token) - if estUnNombre(token) or token in ["pi","e"]: + if estUnNombre(token) or token in {"pi","e"}: if sortie != []: if pile_op.est_vide(): - #mul. implicite sortie.append(token) sortie.append("*") - print("c'est un nombre et il est dans le cas pile_op.est_vide()") else: - print(last_token, pile_op.sommet()) - if last_token == pile_op.sommet(): - #opération binaire normale ou fonction (avec avant elle ultimement une opération binaire) + if position_dernier_token == "pile_op": sortie.append(token) - print("c'est un nombre et il est dans le cas last_token == pile_op.sommet()") - if last_token == sortie[-1]: - print("c'est un nombre et il est dans le cas last_token == sortie[-1]") - #on a affaire à une expression (chiffre (... 2), opération (... +), fonction (... fx)) qui est in fine un nombre + if position_dernier_token == "sortie": cleanFx(pile_op, sortie, token) sortie.append(token) sortie.append("*") @@ -60,21 +51,17 @@ def infixe2npi(liste_en_infixe): sortie.append("*") else: sortie.append(token) - print("c'est un nombre et il est dans le cas sortie == []") + position_dernier_token = "sortie" - elif token in fonctions: + elif token in fonctions: if sortie != []: if pile_op.est_vide(): - #mul. implicite pile_op.empiler("*") pile_op.empiler(token) else: - if last_token == pile_op.sommet(): - #composition de fonctions, ou fonction qui suit un opérateur - pile_op.empiler(token) #normal - if last_token == sortie[-1]: - #le dernier element est l'argument de la fonction (ou - #des fonctions, composées) sur la pile - s'il y a des fonctions + if position_dernier_token == "pile_op": + pile_op.empiler(token) + if position_dernier_token == "sortie": cleanFx(pile_op, sortie, token) pile_op.empiler("*") pile_op.empiler(token) @@ -83,20 +70,15 @@ def infixe2npi(liste_en_infixe): pile_op.empiler(token) else: pile_op.empiler(token) - + position_dernier_token = "pile_op" elif token in operateurs: if not (estUnNombre(last_token) or last_token == ")"): #opérations unaires if token == "-" : - #opposé pile_op.empiler("opp") - print("'-' qui devient 'opp'") + position_dernier_token = "pile_op" elif token == "+": #identité - if last_token in fonctions: - print("'+' identité juste après une fonction") - #raise ValueError("Opérateur mal utilisé","est ici ambigu ; addition ou identité ?",index_token) - print("le plus est identité") pass else: raise ValueError("Opérateur mal utilisé","n'a ici qu'un seul opérande, quand il devrait en avoir deux.",index_token) @@ -107,32 +89,29 @@ def infixe2npi(liste_en_infixe): except IndexError: pass pile_op.empiler(token) - + position_dernier_token = "pile_op" elif token == "(": if sortie != []: if pile_op.est_vide(): - #mul. implicite pile_op.empiler("*") pile_op.empiler(token) - print("parenthèse insérée dans la pile AVEC une multiplication -- pile_op.est_vide()") else: - if last_token == pile_op.sommet(): - #parenthèse qui suit une fonction ou parenthèse normale - pile_op.empiler(token) #normal - print("parenthèse insérée dans la pile SANS une multiplication -- last_token == pile_op.sommet()") - if last_token == sortie[-1]: - #le dernier element est l'argument de la fonction (ou - #des fonctions, composées) sur la pile - s'il y a des fonctions + if position_dernier_token == "pile_op": + pile_op.empiler(token) + if position_dernier_token == "sortie": cleanFx(pile_op, sortie, token) pile_op.empiler("*") pile_op.empiler(token) - print("parenthèse insérée dans la pile AVEC une multiplication -- last_token == sortie[-1]") + if last_token == ")": + pile_op.empiler("*") + pile_op.empiler(token) else: pile_op.empiler(token) - print("parenthèse insérée dans la pile SANS une multiplication -- il n'y a rien en sortie") + position_dernier_token = "pile_op" elif token == ")": cleanFx(pile_op, sortie, ")") + position_dernier_token = "nulle part" else: raise ValueError("Fonction inconnue","n'est pas une fonction connue, ou il manque des espaces.", index_token) @@ -143,36 +122,34 @@ def infixe2npi(liste_en_infixe): pass else: last_token = token - print("état de la sortie après le token : ", sortie) - + while not pile_op.est_vide(): if pile_op.sommet() != "(": sortie.append(pile_op.depiler()) else: - raise SyntaxError(("Mauvais parenthésage", 0)) - print("infixe2npi : ",sortie) + raise SyntaxError("Mauvais parenthésage. Il manque une parenthèse droite.") return sortie def cleanFx(pile, sortie, token): - """dépile tous les tokens dans la sortie - - pas d'erreur de parenthésage si token != ")" - car on veut garder la multipication implicite""" + """dépile toutes les opérations/fx dans la sortie + jusqu'à pile vide ou "(". + - pas d'erreur de parenthésage si token qui appelle cleanFx + n'est pas ")", car ça permet la multipication implicite""" while not pile.est_vide() and pile.sommet() != "(": sortie.append(pile.depiler()) - if not pile.est_vide() and pile.sommet() == "(": - pile.depiler() - else: - if token == ")": + + if token == ")": + if not pile.est_vide() and pile.sommet() == "(": + pile.depiler() + else: raise SyntaxError("Mauvais parenthésage. Il manque une parenthèse gauche.") - if not pile.est_vide(): - if pile.sommet() in ["asin","acos","atan","sin","cos","tan","sqrt", "opp","factorielle"]: - sortie.append(pile.depiler()) - # ↓ sert à quelque chose ?? - #return sortie + if not pile.est_vide(): + if pile.sommet() in {"asin","acos","atan","sin","cos","tan","sqrt", "opp","factorielle"}: + sortie.append(pile.depiler()) def estUnNombre(txt): - """indique si la chaine de caractères txt est un nombre (entier (négatif) ou flottant)""" + """indique si la chaine de caractères txt est un nombre réel""" if txt == None : return False if txt[0] == "-" : txt = txt.replace('-','',1) return txt.replace('.','',1).isdigit() \ No newline at end of file diff --git a/test_interface.py b/interface.py similarity index 83% rename from test_interface.py rename to interface.py index 9e3a7ad..d70d437 100644 --- a/test_interface.py +++ b/interface.py @@ -1,14 +1,15 @@ +from math import * import tkinter as tk from tkinter import messagebox from Pile import Pile_chaine as Pile -from math import * -from expression import * -from infixe import infixe2npi +from expression import Expression, toTree +from infixe import infixe2npi, estUnNombre class ValeurSaisie: """classe d'une valeur saise par l'utilisateur""" def __init__(self, valeur, master): + """initialisation de la valeur""" self.valeur = valeur self.label_valeur = tk.Label(master, text=valeur) @@ -21,6 +22,7 @@ class ValeurSaisie: class Interface(tk.Frame): """interface de la calculatrice""" def __init__(self, master): + """initialisation de l'interface""" self.master = master tk.Frame.__init__(self, master) @@ -71,11 +73,13 @@ class Interface(tk.Frame): def ecrire_fx(self, nom): + """insère le nom de la fonction dans la barre de saisie""" self.saisie_expression.insert(tk.END, " "+nom+" ") if self.etat_affichage == "post-int": self.entrer() def dupliquer(self): + """duplique la dernière valeur entrée dans le stack""" if not self.stack.est_vide(): self.stack.empiler(ValeurSaisie(self.stack.sommet().valeur, self.frame_stack)) @@ -104,27 +108,26 @@ class Interface(tk.Frame): menu_help.add_command(label="À propos", command=self.about) menu_bar.add_cascade(label="Aide", menu=menu_help) - self.master.config(menu=menu_bar) + self.master['menu']=menu_bar def change_affichage(self, affichage): """change le mode d'affichage, - désactive les opérations de pile si pas dans le mode interactif + désactive les opérations de pile si l'on est pas dans le mode interactif et efface les calculs en cours""" self.etat_affichage = affichage etat_text = {"pre":"notation préfixée (polonaise)", "inf":"notation infixée", "post-int":"notation postfixée (polonaise inversée) -- mode interactif", - "post-expr": "notation postfixée (polonaise inversée) -- mode expression" - } + "post-expr": "notation postfixée (polonaise inversée) -- mode expression"} self.etat.config(text=etat_text[affichage]) if self.etat_affichage != "post-int": for child in self.frame_pile.winfo_children(): - child.configure(state='disable') + child['state']='disable' else: for child in self.frame_pile.winfo_children(): - child.configure(state='normal') + child['state']='normal' self.affichage_expression.config(text="") self.effacer_tt() @@ -132,32 +135,37 @@ class Interface(tk.Frame): def aide(self): """affiche l'aide""" - messagebox.showinfo("Aide", "...") + messagebox.showinfo("Aide", "42") + def about(self): """à propos de cette application""" messagebox.showinfo("À propos", "Ceci est une calculatrice.") + def entrer(self): - """si dans mode interactif alors ajoute nv terme - si dans autre mode alors evalue l'expression""" - try: - if len(self.saisie_expression.get()) > 0: + """selon le mode, on évalue l'expression ou on entre la valeur dans le stack""" + if len(self.saisie_expression.get()) > 0: + try: if self.etat_affichage == "post-int": try: self.entrer_terme_dans_stack(self.saisie_expression.get().replace(" ","")) except ValueError: - messagebox.showerror("Saisie invalide", "Le résultat n'est pas un nombre réel.") + messagebox.showerror("Saisie invalide", "Le résultat de l'opération n'est pas un nombre réel.") else: self.evaluer_expr() - except OverflowError: - messagebox.showerror("OverflowError", "Le résultat est trop élevé.") + except OverflowError: + messagebox.showerror("OverflowError", "Le résultat est trop élevé.") def entrer_terme_dans_stack(self, terme): - """si nombre : ajoute une nouvelle valeur dans le stack - ajoute un label avec cette valeur - si fonction : on l'évalue""" - if "." in terme: - terme = float(terme) + """si c'est un nombre ou une constante: on ajoute une nouvelle valeur dans le stack + si c'est une fonction : on l'évalue""" + if estUnNombre(terme): + if "." in terme: + terme = float(terme) + else: + terme = int(terme) + self.stack.empiler(ValeurSaisie(terme, self.frame_stack)) + elif terme in ["asin","acos","atan","sin","cos","tan","sqrt","!","factorielle","opp"]: self.evaluer_stack(terme,1) elif terme in ["+","-","/","*","^"]: self.evaluer_stack(terme,2) elif terme in ["moy3"]: self.evaluer_stack(terme,3) @@ -166,12 +174,8 @@ class Interface(tk.Frame): elif terme == "e": self.stack.empiler(ValeurSaisie(exp(1), self.frame_stack)) else: - try: - terme = int(terme) - self.stack.empiler(ValeurSaisie(terme, self.frame_stack)) - except ValueError: - messagebox.showerror("Saisie invalide", "Vous n'avez pas entré un nombre, ou la fonction est inconnue") - return + messagebox.showerror("Saisie invalide", "La fonction « "+terme+" » est inconnue.") + return self.saisie_expression.delete(0 ,'end') @@ -212,7 +216,7 @@ class Interface(tk.Frame): if op == "acos": res = acos(val) if op == "atan": res = atan(val) if op == "sqrt": res = sqrt(val) - if op == "!" or op == "factorielle": res = factorial(val) + if op == "!" or op == "factorielle": res = gamma(val + 1) if op == "opp": res = - val elif nb_arg == 2: @@ -246,21 +250,10 @@ class Interface(tk.Frame): return try: lst_expr = infixe2npi(lst_expr) - #except SyntaxError as s: - # explication, i_char = s.args[0] - # if explication == "manque op": - # messagebox.showerror("Erreur de syntaxe", - # "Il manque un opérateur. L'étoile ( « ★ » ) indique la position de l'opérateur manquant dans l'expression." - # + "\nL'expression : \n\n(" - # + " ".join(lst_expr[:i_char]) + ") ★ (" + " ".join(lst_expr[i_char:]) + ")") - # elif explication == "Mauvais () detail": - # messagebox.showerror("Erreur de syntaxe", explication - # + "\nLa parenthèse fautive entre 【 crochet 】: \n\n" - # + " ".join(lst_expr[:i_char]) - # + " 【 " + str(lst_expr[i_char]) + " 】 " + " ".join(lst_expr[i_char+1:])) - # else: - # messagebox.showerror("Erreur de syntaxe", explication) - # return + except SyntaxError as s: + explication = s.args[0] + messagebox.showerror("Erreur de syntaxe", explication) + return except ValueError as v: message, explication, i_char = v.args @@ -271,7 +264,7 @@ class Interface(tk.Frame): return try: - arbre_expr = npi2tree(lst_expr, self.etat_affichage) + arbre_expr = toTree(lst_expr, self.etat_affichage) self.affichage_expression.config(text = str(arbre_expr) + "=" + str(arbre_expr.evalue())) except IndexError: messagebox.showerror("Erreur","Erreur de syntaxe ici") @@ -291,7 +284,7 @@ class Interface(tk.Frame): except SyntaxError as s: explication = s.args[0] messagebox.showerror("Erreur de syntaxe", explication) - + def effacer_tt(self): """supprime tout le contenu de la pile (et les labels associés)""" while not self.stack.est_vide(): @@ -303,10 +296,9 @@ class Interface(tk.Frame): if not self.stack.est_vide(): self.stack.depiler() - if __name__ == "__main__": root = tk.Tk() root.title("Calculatrice") root.geometry("380x500") - hello_frame = Interface(root) - hello_frame.mainloop() \ No newline at end of file + app = Interface(root) + app.mainloop() \ No newline at end of file