Browse Source

infixe qui fonctionne à peu près ; ajout fonction racine carrée (sqrt) ; nombres négatifs ne fonctionnent plus en infixe :(

master
bollet.c 9 months ago
parent
commit
eca820e87d
  1. 57
      expression.py
  2. 94
      infixe.py
  3. 46
      interface.py
  4. 45
      test_interface.py

57
expression.py

@ -17,6 +17,7 @@ class Expression:
if self.racine == "cos": return cos(self.gauche.evalue()) if self.racine == "cos": return cos(self.gauche.evalue())
if self.racine == "tan": return tan(self.gauche.evalue()) if self.racine == "tan": return tan(self.gauche.evalue())
if self.racine == "opp": return - self.gauche.evalue() if self.racine == "opp": return - self.gauche.evalue()
if self.racine == "sqrt": return sqrt(self.gauche.evalue())
#operateurs #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()
@ -37,6 +38,7 @@ class Expression:
"sin":3, "sin":3,
"cos":3, "cos":3,
"tan":3, "tan":3,
"sqrt":3,
"*":4, "*":4,
"/":4, "/":4,
"+":5, "+":5,
@ -88,7 +90,7 @@ class Expression:
return gauche + self.racine + droit return gauche + self.racine + droit
if self.racine in ["sin","cos","tan"]: if self.racine in ["sin","cos","tan","sqrt"]:
return self.racine + "(" + str(self.gauche) + ")" return self.racine + "(" + str(self.gauche) + ")"
return str(self.racine) return str(self.racine)
@ -115,7 +117,7 @@ def npi2tree(liste, etat_affichage):
"""conversion d'une liste en NPI ou NP en un arbre""" """conversion d'une liste en NPI ou NP en un arbre"""
pile_expr = Pile() pile_expr = Pile()
for element in liste: for element in liste:
if element in ["+", "*","/", "^"]: if element in ["+","-", "*","/", "^"]:
fils_gauche = pile_expr.depiler() fils_gauche = pile_expr.depiler()
fils_droit = pile_expr.depiler() fils_droit = pile_expr.depiler()
@ -124,21 +126,42 @@ def npi2tree(liste, etat_affichage):
pile_expr.empiler(Expression(element, fils_gauche, fils_droit)) pile_expr.empiler(Expression(element, fils_gauche, fils_droit))
elif element == "-": elif element in ["sin","cos","tan", "opp", "sqrt"]:
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)) pile_expr.empiler(Expression(element, pile_expr.depiler(), None))
else: elif estUnNombre(element):
pile_expr.empiler(Expression(element, None, None)) pile_expr.empiler(Expression(element, None, None))
else:
#à compléter
raise ValueError("Fonction inconnue","n'est pas une fonction connue, ou il manque des espaces.", element)
print("Taille de pile_expr à la fin de la boucle : " , pile_expr.taille())
if pile_expr.taille() == 2:
raise SyntaxError(("Il manque un opérateur. L'étoile ( « ★ » ) indique la position de l'opérateur manquant dans l'expression.", pile_expr))
if pile_expr.taille() > 2:
raise SyntaxError(("Il manque " + str(pile_expr.taille() - 1) + " opérateurs. L'étoile ( « ★ » ) indique la position des opérateurs manquants dans l'expression.", pile_expr))
return pile_expr.sommet() return pile_expr.sommet()
def fonctionsAvecArgument(lst_expr):
#la clarifier un peu, peut-être ?
"""vérifie que les fonctions aient des arguments, mêmes incorrects
ex : `sin ) 2 +` est OK.Plus tard l'expression sera vérifiée
par d'autres fonctions qui verront les erreurs (parenthèses..)
Le but est d'éviter que les expression comme `2 sin` soit vues justes
par shunting-yard"""
fonctions = ["sin","cos","tan", "sqrt"]
lst_expr_stripped = list(filter(lambda x: x != "+" and x != "-", lst_expr))
if lst_expr_stripped[-1] in fonctions: return False
#si à droite, pas un nombre ou une parenthèse fermante
fx_a_gauche = False
for terme in lst_expr_stripped:
if fx_a_gauche == True:
if terme in [")", "*", "/"]: return False
if terme in fonctions:
fx_a_gauche = True
#return not in fonctions
def inserer_dans_liste(element, lst, indice):
"""insère l'item `element` dans la liste `lst` à l'indice `indice`"""
return

94
infixe.py

@ -3,12 +3,14 @@ from math import *
def infixe2npi(liste_en_infixe): def infixe2npi(liste_en_infixe):
"""conversion d'une liste en notation infixe à une liste en npi""" """conversion d'une liste en notation infixe à une liste en npi"""
pile_op = Pile() pile_op = Pile()
sortie = [] sortie = []
fonctions = ["sin","cos","tan"] fonctions = ["sin","cos","tan","sqrt", "opp"]
operateurs = ["+", "-", "*", "/", "^"] operateurs = ["+", "-", "*", "/", "^"]
priorite = {"opp":1, priorite = {"sin":1,
"opp":1,
"^":1, "^":1,
"*":2, "*":2,
"/":2, "/":2,
@ -23,18 +25,60 @@ def infixe2npi(liste_en_infixe):
"-":"gauche" "-":"gauche"
} }
last_token = None last_token = None
for token in liste_en_infixe: print(40*"=")
for index_token, token in enumerate(liste_en_infixe):
print(20*"-")
print("tour de boucle n°",index_token, "\npile_op \t",pile_op, "\nsortie \t", sortie)
#nombre #nombre
if estUnNombre(token): if estUnNombre(token):
if sortie != []:
if pile_op.est_vide():
#mul. implicite
sortie.append(token)
sortie.append("*")
else:
if last_token == pile_op.sommet():
#opération binaire normale ou fonction (avec avant elle ultimement une opération binaire)
sortie.append(token)
if last_token == sortie[-1]:
#on a affaire à une expression (chiffre (... 2), opération (... +), fonction (... fx)) qui est in fine un nombre
cleanFx(pile_op, sortie)
sortie.append(token)
sortie.append("*")
else:
sortie.append(token) sortie.append(token)
#fonction #fonction
elif token in fonctions: elif token in fonctions:
if sortie != []:
if pile_op.est_vide():
#mul. implicite
pile_op.empiler("*")
pile_op.empiler(token) 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
cleanFx(pile_op, sortie)
pile_op.empiler("*")
pile_op.empiler(token)
else:
pile_op.empiler(token)
#operateur #operateur
elif token in operateurs: elif token in operateurs:
if token == "-" and not (estUnNombre(last_token) or last_token == ")"): if not (estUnNombre(last_token) or last_token == ")"):
# "-" est l'opposé, "_" pour prefixe & postfixe #opérations unaires
if token == "-" :
#opposé
pile_op.empiler("opp") pile_op.empiler("opp")
elif token == "+":
#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)
else: else:
try: try:
while pile_op.sommet() != "(" and (priorite[pile_op.sommet()] < priorite[token] or (priorite[pile_op.sommet()] == priorite[token] and associativite[token] == "gauche")): while pile_op.sommet() != "(" and (priorite[pile_op.sommet()] < priorite[token] or (priorite[pile_op.sommet()] == priorite[token] and associativite[token] == "gauche")):
@ -42,37 +86,57 @@ def infixe2npi(liste_en_infixe):
except IndexError: except IndexError:
pass pass
pile_op.empiler(token) pile_op.empiler(token)
# ","
elif token == ",":
while pile_op.sommet() != "(":
sortie.append(pile_op.depiler())
# "(" # "("
elif token == "(": elif token == "(":
if sortie != []:
if pile_op.est_vide():
#mul. implicite
pile_op.empiler("*")
pile_op.empiler(token)
else:
if last_token == pile_op.sommet():
#parenthèse qui suit une fonction ou parenthèse normale
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
cleanFx(pile_op, sortie)
pile_op.empiler("*")
pile_op.empiler(token)
else:
pile_op.empiler(token) pile_op.empiler(token)
# ")" # ")"
elif token == ")": elif token == ")":
try: try:
while pile_op.sommet() != "(": while pile_op.sommet() != "(":
if not pile_op.est_vide(): if pile_op.est_vide():
sortie.append(pile_op.depiler()) raise SyntaxError(("Mauvais () detail", index_token))
else: else:
raise SyntaxError sortie.append(pile_op.depiler())
except IndexError: except IndexError:
raise SyntaxError raise SyntaxError(("Mauvais parenthésage.", index_token))
if pile_op.sommet() == "(": if pile_op.sommet() == "(":
pile_op.depiler() pile_op.depiler()
if not pile_op.est_vide(): if not pile_op.est_vide():
if pile_op.sommet() in fonctions: if pile_op.sommet() in fonctions:
sortie.append(pile_op.depiler()) sortie.append(pile_op.depiler())
else:
raise ValueError("Fonction inconnue","n'est pas une fonction connue, ou il manque des espaces.", index_token)
last_token = token last_token = token
while not pile_op.est_vide(): while not pile_op.est_vide():
if pile_op.sommet() != "(": if pile_op.sommet() != "(":
sortie.append(pile_op.depiler()) sortie.append(pile_op.depiler())
else: else:
raise SyntaxError raise SyntaxError(("Mauvais parenthésage", 0))
print("infixe2npi : ",sortie)
return sortie
def cleanFx(pile, sortie):
#doc bof
"""sort toutes les fonctions et opérateurs de la pile"""
while not pile.est_vide():
sortie.append(pile.depiler())
return sortie return sortie
def estUnNombre(txt): def estUnNombre(txt):

46
interface.py

@ -1,46 +0,0 @@
import tkinter as tk
from expression import *
class Interface(tk.Frame):
def __init__(self, master):
self.master = master
tk.Frame.__init__(self, master)
self.saisie_expression = tk.Entry(self.master)
self.saisie_expression.grid(row=0, column=0)
self.saisie_expression.bind("<Return>", lambda e : self.evaluer())
self.saisie_expression.bind("<KP_Enter>", lambda e : self.evaluer())
btn_calculer = tk.Button(self.master, text='évaluer', command=self.evaluer)
btn_calculer.grid(row=0, column=2)
self.affichage_expression = tk.Label(self.master)
self.affichage_expression.grid(row=1, column=0)
tk.Label(self.master, text = "=").grid(row=1, column=1)
self.res = tk.Label(self.master)
self.res.grid(row=1, column=2)
def evaluer(self):
lst_expr = self.saisie_expression.get().split()
try:
arbre_expr = npi2tree(lst_expr)
self.affichage_expression.config(text = str(arbre_expr))
self.res.config(text = arbre_expr.evalue())
except IndexError:
self.affichage_expression.config(text = "Erreur")
self.res.config(text = "Erreur")
if __name__ == "__main__":
root = tk.Tk()
root.title("Calculatrice")
root.geometry("350x500")
hello_frame = Interface(root)
hello_frame.mainloop()

45
test_interface.py

@ -1,10 +1,11 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox from tkinter import messagebox, font
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 from infixe import infixe2npi
from popup import MsgBox
class ValeurSaisie: class ValeurSaisie:
"""classe d'une valeur saise par l'utilisateur""" """classe d'une valeur saise par l'utilisateur"""
@ -134,7 +135,7 @@ class Interface(tk.Frame):
if "." in valeur_a_ajouter: if "." in valeur_a_ajouter:
valeur_a_ajouter = float(valeur_a_ajouter) valeur_a_ajouter = float(valeur_a_ajouter)
self.stack.empiler(ValeurSaisie(valeur_a_ajouter, self.frame_stack)) 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 ["sin","cos","tan","sqrt","!","opp"]: self.evaluer_stack(valeur_a_ajouter,1)
elif valeur_a_ajouter in ["+","-","/","*","^"]: self.evaluer_stack(valeur_a_ajouter,2) 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 ["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 in ["somme","produit"]: self.evaluer_stack(valeur_a_ajouter,-1)
@ -183,6 +184,7 @@ class Interface(tk.Frame):
if op == "tan": res = tan(val) if op == "tan": res = tan(val)
if op == "sqrt": res = sqrt(val) if op == "sqrt": res = sqrt(val)
if op == "!": res = factorial(val) if op == "!": res = factorial(val)
if op == "opp": res = - val
elif nb_arg == 2: elif nb_arg == 2:
droite = self.stack.depiler().valeur droite = self.stack.depiler().valeur
@ -212,16 +214,49 @@ class Interface(tk.Frame):
if self.etat_affichage == "inf": if self.etat_affichage == "inf":
try: try:
lst_expr = infixe2npi(lst_expr) lst_expr = infixe2npi(lst_expr)
except SyntaxError: except SyntaxError as s:
messagebox.showerror("Erreur de syntaxe", "L'expression est mal parenthésée, ou vous avez oublié une espace") 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 ValueError as v:
message, explication, i_char = v.args
messagebox.showerror(message,"Le terme « " + lst_expr[i_char] + " » "
+ explication + "\n\n"
+ "Dans l'expression, entre 【 crochet 】 :\n" + " ".join(lst_expr[:i_char])
+ "" + str(lst_expr[i_char]) + "" + " ".join(lst_expr[i_char+1:]))
return return
try: try:
arbre_expr = npi2tree(lst_expr, self.etat_affichage) 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 de syntaxe") messagebox.showerror("Erreur","Erreur de syntaxe")
except ValueError: except ValueError:
messagebox.showerror("Saisie invalide", "Vous n'avez pas entré un nombre, ou la fonction est inconnue") #faire un truc plus beau
messagebox.showerror("Saisie invalide", "Vous n'avez pas entré un nombre, ou la fonction est inconnue.")
except SyntaxError as s:
explication, pile_expr = s.args[0]
print("pile_expr depuis evaluer_expr",pile_expr)
lst_pile_expr = []
while not pile_expr.est_vide():
lst_pile_expr.append(str(pile_expr.depiler()))
lst_pile_expr.reverse()
print(lst_pile_expr)
messagebox.showerror("Erreur de syntaxe", explication
+"\nL'expression : \n\n("
+ ") ★ (".join(lst_pile_expr) + ")")
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)"""

Loading…
Cancel
Save