Browse Source

notation infixe qui fonctionne ; nombres négatifs qui fonctionnent mieux ; parenthèses OK ; correction de bugs divers

master
bollet.c 9 months ago
parent
commit
d24a3057ed
  1. 138
      expression.py
  2. 82
      infixe.py
  3. 139
      test_interface.py

138
expression.py

@ -1,5 +1,6 @@
from Pile import Pile_chaine as Pile from Pile import Pile_chaine as Pile
from math import * from math import *
from infixe import estUnNombre
class Expression: class Expression:
def __init__(self, racine, gauche, droit): def __init__(self, racine, gauche, droit):
@ -11,24 +12,17 @@ class Expression:
def evalue(self): def evalue(self):
"""renvoie la valeur de l'expression """renvoie la valeur de l'expression
si 1 seul argument alors on prend le fils gauche""" si 1 seul argument alors on prend le fils gauche"""
#fonction unaires #fonctions
if self.racine == "sin": if self.racine == "sin": return sin(self.gauche.evalue())
return sin(self.gauche.evalue()) if self.racine == "cos": return cos(self.gauche.evalue())
if self.racine == "cos": if self.racine == "tan": return tan(self.gauche.evalue())
return cos(self.gauche.evalue()) if self.racine == "opp": return - self.gauche.evalue()
if self.racine == "tan": #operateurs
return tan(self.gauche.evalue()) if self.racine == "+": return self.gauche.evalue() + self.droit.evalue()
#fonctions binaires if self.racine == "-": return self.gauche.evalue() - self.droit.evalue()
if self.racine == "+": if self.racine == "*": return self.gauche.evalue() * self.droit.evalue()
return self.gauche.evalue() + self.droit.evalue() if self.racine == "/": return self.gauche.evalue() / self.droit.evalue()
if self.racine == "-": if self.racine == "^": return self.gauche.evalue() ** self.droit.evalue()
return self.gauche.evalue() - self.droit.evalue()
if self.racine == "*":
return self.gauche.evalue() * self.droit.evalue()
if self.racine == "/":
return self.gauche.evalue() / self.droit.evalue()
if self.racine == "^":
return self.gauche.evalue() ** self.droit.evalue()
if "." in self.racine: if "." in self.racine:
@ -37,22 +31,114 @@ class Expression:
def __str__(self): def __str__(self):
"""affiche l'expression""" """affiche l'expression"""
#temporaire priorite = {"opp":1,
if self.racine in ["+", "*", "-", "/", "^"]: "!":1,
return "(" + str(self.gauche) + self.racine + str(self.droit) + ")" "^":2,
"sin":3,
"cos":3,
"tan":3,
"*":4,
"/":4,
"+":5,
"-":5
}
associativite = {"opp":"droit",
"^":"droit",
"*":"gauche",
"/":"gauche",
"+":"gauche",
"-":"gauche"
}
pas_commutatif = ["^","/","-"]
if self.racine in ["opp","!", "^", "*", "/", "+", "-"]:
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] == "-":
gauche = parentheses(gauche)
if self.droit.racine == "opp" or self.droit.racine != "-" and self.droit.racine[0] == "-":
droit = parentheses(droit)
if priorite_racine < priorite_gauche:
gauche = parentheses(gauche)
if priorite_racine < priorite_droit:
droit = parentheses(droit)
if self.racine in pas_commutatif:
if priorite_racine == priorite_droit and associativite[self.racine] == "gauche":
droit = parentheses(droit)
if priorite_racine == priorite_gauche and associativite[self.racine] == "droit":
gauche = parentheses(gauche)
return gauche + self.racine + droit
if self.racine in ["sin","cos","tan"]: if self.racine in ["sin","cos","tan"]:
return self.racine + "(" + str(self.gauche) + ")" return self.racine + "(" + str(self.gauche) + ")"
return str(self.racine) return str(self.racine)
def npi2tree(liste_en_npi): def parentheses(txt):
"""ajoute des parentheses à txt"""
return "(" + txt + ")"
def npi2tree_original(liste_en_npi):
"""conversion d'une liste en NPI en un arbre""" """conversion d'une liste en NPI en un arbre"""
pile_expr = Pile() pile_expr = Pile()
for element in liste_en_npi: for element in liste_en_npi:
if element in ["+", "*","-","/", "^"]: if element in ["+", "*","-","/", "^"]:
pile_expr.empiler(Expression(element, pile_expr.depiler(),pile_expr.depiler())) fils_droit = pile_expr.depiler()
elif element in ["sin","cos","tan"]: 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)) pile_expr.empiler(Expression(element, pile_expr.depiler(),None))
else: else:
pile_expr.empiler(Expression(element, None, None)) 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"""
pile_expr = Pile()
for element in liste:
if element in ["+", "*","/", "^"]:
fils_gauche = pile_expr.depiler()
fils_droit = pile_expr.depiler()
if etat_affichage == "post-expr" or etat_affichage == "inf":
fils_gauche, fils_droit = fils_droit, fils_gauche
pile_expr.empiler(Expression(element, fils_gauche, fils_droit))
elif element == "-":
fils_gauche = pile_expr.depiler()
if pile_expr.est_vide():
# "-" est l'opposé
pile_expr.empiler(Expression("-", fils_gauche, None))
else:
# "-" est la soustraction
fils_droit = pile_expr.depiler()
if etat_affichage == "post-expr" or etat_affichage == "inf":
fils_gauche, fils_droit = fils_droit, fils_gauche
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() return pile_expr.sommet()

82
infixe.py

@ -0,0 +1,82 @@
from Pile import Pile_chaine as Pile
from math import *
def infixe2npi(liste_en_infixe):
"""conversion d'une liste en notation infixe à une liste en npi"""
pile_op = Pile()
sortie = []
fonctions = ["sin","cos","tan"]
operateurs = ["+", "-", "*", "/", "^"]
priorite = {"opp":1,
"^":1,
"*":2,
"/":2,
"+":3,
"-":3
}
associativite = {"opp": "droit",
"^":"droit",
"*":"gauche",
"/":"gauche",
"+":"gauche",
"-":"gauche"
}
last_token = None
for token in liste_en_infixe:
#nombre
if estUnNombre(token):
sortie.append(token)
#fonction
elif token in fonctions:
pile_op.empiler(token)
#operateur
elif token in operateurs:
if token == "-" and not (estUnNombre(last_token) or last_token == ")"):
# "-" est l'opposé, "_" pour prefixe & postfixe
pile_op.empiler("opp")
else:
try:
while pile_op.sommet() != "(" and (priorite[pile_op.sommet()] < priorite[token] or (priorite[pile_op.sommet()] == priorite[token] and associativite[token] == "gauche")):
sortie.append(pile_op.depiler())
except IndexError:
pass
pile_op.empiler(token)
# ","
elif token == ",":
while pile_op.sommet() != "(":
sortie.append(pile_op.depiler())
# "("
elif token == "(":
pile_op.empiler(token)
# ")"
elif token == ")":
try:
while pile_op.sommet() != "(":
if not pile_op.est_vide():
sortie.append(pile_op.depiler())
else:
raise SyntaxError
except IndexError:
raise SyntaxError
if pile_op.sommet() == "(":
pile_op.depiler()
if not pile_op.est_vide():
if pile_op.sommet() in fonctions:
sortie.append(pile_op.depiler())
last_token = token
while not pile_op.est_vide():
if pile_op.sommet() != "(":
sortie.append(pile_op.depiler())
else:
raise SyntaxError
return sortie
def estUnNombre(txt):
"""indique si la chaine de caractères txt est un nombre (entier (négatif) ou flottant)"""
if txt == None : return False
if txt[0] == "-" : txt = txt.replace('-','',1)
return txt.replace('.','',1).isdigit()

139
test_interface.py

@ -3,6 +3,8 @@ from tkinter import messagebox
from Pile import Pile_chaine as Pile from Pile import Pile_chaine as Pile
from math import * from math import *
from expression import * from expression import *
from infixe import infixe2npi
class ValeurSaisie: class ValeurSaisie:
"""classe d'une valeur saise par l'utilisateur""" """classe d'une valeur saise par l'utilisateur"""
@ -60,13 +62,13 @@ class Interface(tk.Frame):
fonctions = tk.LabelFrame(commandes, text="fonctions", padx=5, pady=5) fonctions = tk.LabelFrame(commandes, text="fonctions", padx=5, pady=5)
fonctions.pack(fill="both") fonctions.pack(fill="both")
btn_sinus = tk.Button(fonctions, text='sin', command=lambda : self.evaluer("sin",1)) btn_sinus = tk.Button(fonctions, text='sin', command=lambda : self.evaluer_stack("sin",1))
btn_sinus.pack(side="left") btn_sinus.pack(side="left")
btn_sinus = tk.Button(fonctions, text='cos', command=lambda : self.evaluer("cos",1)) btn_sinus = tk.Button(fonctions, text='cos', command=lambda : self.evaluer_stack("cos",1))
btn_sinus.pack(side="left") btn_sinus.pack(side="left")
btn_sinus = tk.Button(fonctions, text='tan', command=lambda : self.evaluer("tan",1)) btn_sinus = tk.Button(fonctions, text='tan', command=lambda : self.evaluer_stack("tan",1))
btn_sinus.pack(side="left") btn_sinus.pack(side="left")
btn_pow = tk.Button(fonctions, text='^', command=lambda : self.evaluer("^",2)) btn_pow = tk.Button(fonctions, text='^', command=lambda : self.evaluer_stack("^",2))
btn_pow.pack(side="right") btn_pow.pack(side="right")
def create_menu_bar(self): def create_menu_bar(self):
@ -109,18 +111,45 @@ class Interface(tk.Frame):
self.effacer_tt() self.effacer_tt()
self.saisie_expression.delete(0 ,'end') self.saisie_expression.delete(0 ,'end')
def do_something(self):
pass
def aide(self): def aide(self):
"""affiche l'aide""" """affiche l'aide"""
messagebox.showinfo("Aide", "...") messagebox.showinfo("Aide", "...")
def about(self): def about(self):
"""à propos de cette application""" """à propos de cette application"""
messagebox.showinfo("À propos", "Ceci est une calculatrice.") 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"""
if self.etat_affichage == "post-int":
self.entrer_terme_dans_stack()
if len(self.saisie_expression.get()) > 0:
self.evaluer_expr()
def evaluer(self, op, nb_arg): def entrer_terme_dans_stack(self):
"""affiche une erreur s'il n'y a pas assez d'arguments ; """si nombre : ajoute une nouvelle valeur dans le stack
application de l'operation aux nb_arg dernières valeurs du stack ; ajoute un label avec cette valeur
si fonction : on l'évalue"""
valeur_a_ajouter = self.saisie_expression.get().replace(" ", "")
if valeur_a_ajouter != "":
if "." in valeur_a_ajouter:
valeur_a_ajouter = float(valeur_a_ajouter)
self.stack.empiler(ValeurSaisie(valeur_a_ajouter, self.frame_stack))
elif valeur_a_ajouter in ["sin","cos","tan","!"]: self.evaluer_stack(valeur_a_ajouter,1)
elif valeur_a_ajouter in ["+","-","/","*","^"]: self.evaluer_stack(valeur_a_ajouter,2)
elif valeur_a_ajouter in ["moy3"]: self.evaluer_stack(valeur_a_ajouter,3)
elif valeur_a_ajouter in ["somme","produit"]: self.evaluer_stack(valeur_a_ajouter,-1)
elif valeur_a_ajouter == "pi": self.stack.empiler(ValeurSaisie(pi, self.frame_stack))
else:
try:
valeur_a_ajouter = int(valeur_a_ajouter)
self.stack.empiler(ValeurSaisie(valeur_a_ajouter, self.frame_stack))
except ValueError:
messagebox.showerror("Saisie invalide", "Vous n'avez pas entré un nombre, ou la fonction est inconnue")
self.saisie_expression.delete(0 ,'end')
def evaluer_stack(self, op, nb_arg):
"""application de l'operation aux nb_arg dernières valeurs du stack ;
si nb_arg == -1 alors toutes les valeurs du stack sont utilisées""" si nb_arg == -1 alors toutes les valeurs du stack sont utilisées"""
if nb_arg == -1: if nb_arg == -1:
if self.stack.est_vide(): if self.stack.est_vide():
@ -149,99 +178,57 @@ class Interface(tk.Frame):
if nb_arg == 1: if nb_arg == 1:
val = self.stack.depiler().valeur val = self.stack.depiler().valeur
if op == "sin": if op == "sin": res = sin(val)
res = sin(val) if op == "cos": res = cos(val)
if op == "cos": if op == "tan": res = tan(val)
res = cos(val) if op == "sqrt": res = sqrt(val)
if op == "tan": if op == "!": res = factorial(val)
res = tan(val)
if op == "!":
res = factorial(val)
elif nb_arg == 2: elif nb_arg == 2:
droite = self.stack.depiler().valeur droite = self.stack.depiler().valeur
gauche = self.stack.depiler().valeur gauche = self.stack.depiler().valeur
if op == "+": if op == "+": res = gauche + droite
res = gauche + droite if op == "-": res = gauche - droite
if op == "-": if op == "*": res = gauche * droite
res = gauche - droite if op == "/": res = gauche / droite
if op == "*": if op == "^": res = gauche ** droite
res = gauche * droite
if op == "/":
res = gauche / droite
if op == "^":
res = gauche ** droite
elif nb_arg == 3: elif nb_arg == 3:
terme3 = self.stack.depiler().valeur terme3 = self.stack.depiler().valeur
terme2 = self.stack.depiler().valeur terme2 = self.stack.depiler().valeur
terme1 = self.stack.depiler().valeur terme1 = self.stack.depiler().valeur
if op == "moy3": if op == "moy3": res = (terme1 + terme2 + terme3)/3
res = (terme1 + terme2 + terme3)/3
self.stack.empiler(ValeurSaisie(res, self.frame_stack)) self.stack.empiler(ValeurSaisie(res, self.frame_stack))
def entrer(self): def evaluer_expr(self):
"""si dans mode npi: si dans interactif alors ajoute nv terme
si dans expression alors evalue l'expression"""
#completer les différents cas pour les autres notations
if self.etat_affichage == "post-int":
self.entrer_terme_dans_stack()
if self.etat_affichage == "post-expr":
self.evaluer_prepostfixe()
if self.etat_affichage == "pre":
self.evaluer_prepostfixe()
def evaluer_prepostfixe(self):
"""evalue la chaine de caractère dans le champ de saisie """evalue la chaine de caractère dans le champ de saisie
comme une expression en notation polonaise ou npi selon l'affichage""" selon l'affichage / la notation choisie (prefixe/infixe/postfixe)"""
#simplifier les conditions (plus tard) - ça m'a l'air pas mal
lst_expr = self.saisie_expression.get().split() lst_expr = self.saisie_expression.get().split()
if self.etat_affichage == "pre": if self.etat_affichage == "pre":
lst_expr = lst_expr.reverse() lst_expr.reverse()
if self.etat_affichage == "inf":
try:
lst_expr = infixe2npi(lst_expr)
except SyntaxError:
messagebox.showerror("Erreur de syntaxe", "L'expression est mal parenthésée, ou vous avez oublié une espace")
return
try: try:
arbre_expr = npi2tree(lst_expr) arbre_expr = npi2tree(lst_expr, self.etat_affichage)
self.affichage_expression.config(text = str(arbre_expr) + "=" + str(arbre_expr.evalue())) self.affichage_expression.config(text = str(arbre_expr) + "=" + str(arbre_expr.evalue()))
except IndexError: except IndexError:
messagebox.showerror("Erreur","Erreur") messagebox.showerror("Erreur","Erreur de syntaxe")
except ValueError:
def entrer_terme_dans_stack(self): messagebox.showerror("Saisie invalide", "Vous n'avez pas entré un nombre, ou la fonction est inconnue")
"""si nombre : ajoute une nouvelle valeur dans le stack
ajoute un label avec cette valeur
si fonction : appelle la fonction évaluer"""
valeur_a_ajouter = self.saisie_expression.get().replace(" ", "")
if valeur_a_ajouter != "":
if "." in valeur_a_ajouter:
valeur_a_ajouter = float(valeur_a_ajouter)
self.stack.empiler(ValeurSaisie(valeur_a_ajouter, self.frame_stack))
elif valeur_a_ajouter in ["sin","cos","tan","!"]:
self.evaluer(valeur_a_ajouter,1)
elif valeur_a_ajouter in ["+","-","/","*","^"]:
self.evaluer(valeur_a_ajouter,2)
elif valeur_a_ajouter in ["moy3"]:
self.evaluer(valeur_a_ajouter,3)
elif valeur_a_ajouter in ["somme","produit"]:
self.evaluer(valeur_a_ajouter,-1)
elif valeur_a_ajouter == "pi":
self.stack.empiler(ValeurSaisie(pi, self.frame_stack))
else:
try:
valeur_a_ajouter = int(valeur_a_ajouter)
self.stack.empiler(ValeurSaisie(valeur_a_ajouter, self.frame_stack))
except ValueError:
messagebox.showerror("Saisie invalide", "Vous n'avez pas entré un nombre, ou la fonction est inconnue")
self.saisie_expression.delete(0 ,'end')
def effacer_tt(self): def effacer_tt(self):
"""supprime tout le contenu de la pile (et les labels associés)""" """supprime tout le contenu de la pile (et les labels associés)"""
while not self.stack.est_vide(): while not self.stack.est_vide():
self.stack.depiler() self.stack.depiler()
self.frame_stack.config(height = 1) self.frame_stack.config(height = 1)
def effacer_dernier(self): def effacer_dernier(self):
"""supprime la dernière valeur entrée (et le label associés)""" """supprime la dernière valeur entrée (et le label associés)"""
if not self.stack.est_vide(): if not self.stack.est_vide():

Loading…
Cancel
Save