Browse Source

correction de cleanfx;affichage : suppression de zéros avant et après un nombre, signe moins mieux géré ; factorielle -> fonction gamma

master
bollet.c 9 months ago
parent
commit
2626b16d18
  1. 66
      expression.py
  2. 87
      infixe.py
  3. 76
      interface.py

66
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)

87
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:
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 token == ")":
if not pile.est_vide() and pile.sommet() == "(":
pile.depiler()
else:
if token == ")":
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"]:
if pile.sommet() in {"asin","acos","atan","sin","cos","tan","sqrt", "opp","factorielle"}:
sortie.append(pile.depiler())
# ↓ sert à quelque chose ??
#return sortie
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()

76
test_interface.py → 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,19 +135,20 @@ 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:
"""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:
@ -152,12 +156,16 @@ class Interface(tk.Frame):
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"""
"""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,11 +174,7 @@ 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")
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")
@ -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()
app = Interface(root)
app.mainloop()
Loading…
Cancel
Save