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. 134
      expression.py
  2. 82
      infixe.py
  3. 137
      test_interface.py

134
expression.py

@ -1,5 +1,6 @@
from Pile import Pile_chaine as Pile
from math import *
from infixe import estUnNombre
class Expression:
def __init__(self, racine, gauche, droit):
@ -11,24 +12,17 @@ class Expression:
def evalue(self):
"""renvoie la valeur de l'expression
si 1 seul argument alors on prend le fils gauche"""
#fonction unaires
if self.racine == "sin":
return sin(self.gauche.evalue())
if self.racine == "cos":
return cos(self.gauche.evalue())
if self.racine == "tan":
return tan(self.gauche.evalue())
#fonctions binaires
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 self.racine == "/":
return self.gauche.evalue() / self.droit.evalue()
if self.racine == "^":
return self.gauche.evalue() ** self.droit.evalue()
#fonctions
if self.racine == "sin": return sin(self.gauche.evalue())
if self.racine == "cos": return cos(self.gauche.evalue())
if self.racine == "tan": return tan(self.gauche.evalue())
if self.racine == "opp": return - self.gauche.evalue()
#operateurs
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 self.racine == "/": return self.gauche.evalue() / self.droit.evalue()
if self.racine == "^": return self.gauche.evalue() ** self.droit.evalue()
if "." in self.racine:
@ -37,22 +31,114 @@ class Expression:
def __str__(self):
"""affiche l'expression"""
#temporaire
if self.racine in ["+", "*", "-", "/", "^"]:
return "(" + str(self.gauche) + self.racine + str(self.droit) + ")"
priorite = {"opp":1,
"!":1,
"^":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"]:
return self.racine + "(" + str(self.gauche) + ")"
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"""
pile_expr = Pile()
for element in liste_en_npi:
if element in ["+", "*","-","/", "^"]:
pile_expr.empiler(Expression(element, pile_expr.depiler(),pile_expr.depiler()))
elif element in ["sin","cos","tan"]:
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"""
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()

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

137
test_interface.py

@ -3,6 +3,8 @@ from tkinter import messagebox
from Pile import Pile_chaine as Pile
from math import *
from expression import *
from infixe import infixe2npi
class ValeurSaisie:
"""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.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 = 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 = 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_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")
def create_menu_bar(self):
@ -109,18 +111,45 @@ class Interface(tk.Frame):
self.effacer_tt()
self.saisie_expression.delete(0 ,'end')
def do_something(self):
pass
def aide(self):
"""affiche l'aide"""
messagebox.showinfo("Aide", "...")
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"""
if self.etat_affichage == "post-int":
self.entrer_terme_dans_stack()
if len(self.saisie_expression.get()) > 0:
self.evaluer_expr()
def entrer_terme_dans_stack(self):
"""si nombre : ajoute une nouvelle valeur dans le 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(self, op, nb_arg):
"""affiche une erreur s'il n'y a pas assez d'arguments ;
application de l'operation aux nb_arg dernières valeurs du stack ;
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"""
if nb_arg == -1:
if self.stack.est_vide():
@ -149,91 +178,50 @@ class Interface(tk.Frame):
if nb_arg == 1:
val = self.stack.depiler().valeur
if op == "sin":
res = sin(val)
if op == "cos":
res = cos(val)
if op == "tan":
res = tan(val)
if op == "!":
res = factorial(val)
if op == "sin": res = sin(val)
if op == "cos": res = cos(val)
if op == "tan": res = tan(val)
if op == "sqrt": res = sqrt(val)
if op == "!": res = factorial(val)
elif nb_arg == 2:
droite = self.stack.depiler().valeur
gauche = self.stack.depiler().valeur
if op == "+":
res = gauche + droite
if op == "-":
res = gauche - droite
if op == "*":
res = gauche * droite
if op == "/":
res = gauche / droite
if op == "^":
res = gauche ** droite
if op == "+": res = gauche + droite
if op == "-": res = gauche - droite
if op == "*": res = gauche * droite
if op == "/": res = gauche / droite
if op == "^": res = gauche ** droite
elif nb_arg == 3:
terme3 = self.stack.depiler().valeur
terme2 = self.stack.depiler().valeur
terme1 = self.stack.depiler().valeur
if op == "moy3":
res = (terme1 + terme2 + terme3)/3
if op == "moy3": res = (terme1 + terme2 + terme3)/3
self.stack.empiler(ValeurSaisie(res, self.frame_stack))
def entrer(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):
def evaluer_expr(self):
"""evalue la chaine de caractère dans le champ de saisie
comme une expression en notation polonaise ou npi selon l'affichage"""
#simplifier les conditions (plus tard) - ça m'a l'air pas mal
selon l'affichage / la notation choisie (prefixe/infixe/postfixe)"""
lst_expr = self.saisie_expression.get().split()
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:
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()))
except IndexError:
messagebox.showerror("Erreur","Erreur")
def entrer_terme_dans_stack(self):
"""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')
messagebox.showerror("Erreur","Erreur de syntaxe")
except ValueError:
messagebox.showerror("Saisie invalide", "Vous n'avez pas entré un nombre, ou la fonction est inconnue")
def effacer_tt(self):
"""supprime tout le contenu de la pile (et les labels associés)"""
@ -241,7 +229,6 @@ class Interface(tk.Frame):
self.stack.depiler()
self.frame_stack.config(height = 1)
def effacer_dernier(self):
"""supprime la dernière valeur entrée (et le label associés)"""
if not self.stack.est_vide():

Loading…
Cancel
Save