From eca820e87dee85cfeee4284ca533dc7b285ab150 Mon Sep 17 00:00:00 2001 From: "bollet.c" <> Date: Wed, 21 Feb 2024 22:08:57 +0100 Subject: [PATCH] =?UTF-8?q?infixe=20qui=20fonctionne=20=C3=A0=20peu=20pr?= =?UTF-8?q?=C3=A8s=20;=20ajout=20fonction=20racine=20carr=C3=A9e=20(sqrt)?= =?UTF-8?q?=20;=20nombres=20n=C3=A9gatifs=20ne=20fonctionnent=20plus=20en?= =?UTF-8?q?=20infixe=20:(?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expression.py | 61 ++++++++++++++++++-------- infixe.py | 108 ++++++++++++++++++++++++++++++++++++---------- interface.py | 46 -------------------- test_interface.py | 47 +++++++++++++++++--- 4 files changed, 169 insertions(+), 93 deletions(-) delete mode 100644 interface.py diff --git a/expression.py b/expression.py index 5264dad..ff42224 100644 --- a/expression.py +++ b/expression.py @@ -16,7 +16,8 @@ class Expression: 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() + if self.racine == "opp": return - self.gauche.evalue() + if self.racine == "sqrt": return sqrt(self.gauche.evalue()) #operateurs 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, "cos":3, "tan":3, + "sqrt":3, "*":4, "/":4, "+":5, @@ -88,7 +90,7 @@ class Expression: 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 str(self.racine) @@ -115,7 +117,7 @@ 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 ["+", "*","/", "^"]: + if element in ["+","-", "*","/", "^"]: fils_gauche = 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)) - 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"]: + elif element in ["sin","cos","tan", "opp", "sqrt"]: pile_expr.empiler(Expression(element, pile_expr.depiler(), None)) - else: + elif estUnNombre(element): pile_expr.empiler(Expression(element, None, None)) - return pile_expr.sommet() \ No newline at end of file + 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() + +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 + \ No newline at end of file diff --git a/infixe.py b/infixe.py index e833ed8..287fa73 100644 --- a/infixe.py +++ b/infixe.py @@ -3,12 +3,14 @@ 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"] + fonctions = ["sin","cos","tan","sqrt", "opp"] operateurs = ["+", "-", "*", "/", "^"] - priorite = {"opp":1, + priorite = {"sin":1, + "opp":1, "^":1, "*":2, "/":2, @@ -23,18 +25,60 @@ def infixe2npi(liste_en_infixe): "-":"gauche" } 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 - if estUnNombre(token): - sortie.append(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) #fonction - elif token in fonctions: - pile_op.empiler(token) + 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 + cleanFx(pile_op, sortie) + pile_op.empiler("*") + pile_op.empiler(token) + else: + 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") + if not (estUnNombre(last_token) or last_token == ")"): + #opérations unaires + if token == "-" : + #opposé + 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: try: 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: pass pile_op.empiler(token) - # "," - elif token == ",": - while pile_op.sommet() != "(": - sortie.append(pile_op.depiler()) # "(" elif token == "(": - pile_op.empiler(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) # ")" elif token == ")": try: while pile_op.sommet() != "(": - if not pile_op.est_vide(): - sortie.append(pile_op.depiler()) + if pile_op.est_vide(): + raise SyntaxError(("Mauvais () detail", index_token)) else: - raise SyntaxError + sortie.append(pile_op.depiler()) except IndexError: - raise SyntaxError + raise SyntaxError(("Mauvais parenthésage.", index_token)) if pile_op.sommet() == "(": pile_op.depiler() if not pile_op.est_vide(): if pile_op.sommet() in fonctions: 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 - + while not pile_op.est_vide(): if pile_op.sommet() != "(": sortie.append(pile_op.depiler()) 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 def estUnNombre(txt): diff --git a/interface.py b/interface.py deleted file mode 100644 index 81d9924..0000000 --- a/interface.py +++ /dev/null @@ -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("", lambda e : self.evaluer()) - self.saisie_expression.bind("", 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() \ No newline at end of file diff --git a/test_interface.py b/test_interface.py index c8f0b0e..99b2eec 100644 --- a/test_interface.py +++ b/test_interface.py @@ -1,10 +1,11 @@ import tkinter as tk -from tkinter import messagebox +from tkinter import messagebox, font from Pile import Pile_chaine as Pile from math import * from expression import * from infixe import infixe2npi +from popup import MsgBox class ValeurSaisie: """classe d'une valeur saise par l'utilisateur""" @@ -134,7 +135,7 @@ class Interface(tk.Frame): 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 ["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 ["moy3"]: self.evaluer_stack(valeur_a_ajouter,3) 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 == "sqrt": res = sqrt(val) if op == "!": res = factorial(val) + if op == "opp": res = - val elif nb_arg == 2: droite = self.stack.depiler().valeur @@ -212,16 +214,49 @@ class Interface(tk.Frame): 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 + 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 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 + try: 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 de syntaxe") 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): """supprime tout le contenu de la pile (et les labels associés)"""