]> SALOME platform Git repositories - tools/eficas.git/blob - Extensions/interpreteur_formule.py
Salome HOME
CCAR: Modified Files:
[tools/eficas.git] / Extensions / interpreteur_formule.py
1 #@ MODIF interpreteur_formule Accas  DATE 02/07/2001   AUTEUR D6BHHJP J.P.LEFEBVRE 
2 #            CONFIGURATION MANAGEMENT OF EDF VERSION
3 # ======================================================================
4 # COPYRIGHT (C) 1991 - 2001  EDF R&D                  WWW.CODE-ASTER.ORG
5 #              SEE THE FILE "LICENSE.TERMS" FOR INFORMATION ON USAGE AND
6 #              REDISTRIBUTION OF THIS FILE.
7 # ======================================================================
8 import string,re,sys,exceptions,types
9
10 from Noyau.N_CR import CR
11
12 def group(*choices): return '(' + string.join(choices, '|') + ')'
13 def any(*choices): return apply(group, choices) + '*'
14 def maybe(*choices): return apply(group, choices) + '?'
15
16 Intnumber = r'[1-9]\d*'
17 Exponent = r'[eEdD][-+]?\d+'
18 Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent)
19 Expfloat = r'[1-9]\d*' + Exponent
20 Floatnumber = group(Pointfloat, Expfloat)
21
22 pat_number = re.compile(r'^([+-]?)([0-9]+)(\.\d*)?(.*)')
23 pat_number_complet = re.compile(r'^([+-]?)([0-9]+)(\.\d*)?([eEdD][+-]?\d+)(.*)')
24 pat_constante = re.compile(r'^([+-]?)([a-zA-Z][a-zA-Z_0-9]*\s*)(.*)')
25
26 def cmp_function(arg1,arg2):
27     """
28     Fonction de comparaison permettant de classer les listes de
29     fonctions unaires et binaires selon la longueur de leurs arguments
30     On classe les arguments les plus longs en premier
31     """
32     if len(arg1) > len(arg2):
33         return -1
34     elif len(arg1) == len(arg2):
35         return 0
36     else:
37         return 1
38     
39 class InterpreteurException(exceptions.Exception):
40     """
41     Classe servant à définir les exceptions levées par l'interpréteur de formule
42     """
43     def __init__(self,args=None):
44         self.args = args
45
46     def __str__(self):
47         return self.args
48
49 class Interpreteur_Formule:
50     """
51     Cette classe sert à construire un interpréteur de formules Aster
52     """
53     l_fonctions_binaires = ['+','-','*','/','**','=','MOD','MIN','MAX','ATAN2']
54     l_fonctions_unaires = ['+','-','INT','REAL','AIMAG','ABS','SQRT','EXP','LOG',
55                            'LOG10','SIN','COS','TAN','ASIN','ACOS','ATAN','SINH',
56                            'COSH','TANH','HEAVYSID']
57     l_constantes = ['PI','RD_RG','DG_RD']
58  
59     def __init__(self,formule=None,constantes=[],fonctions=[],parent=None):
60         """
61         Constructeur d'interpréteurs de formule Aster
62         - formule = tuple (nom,type,arguments,corps)
63         - constantes = liste des noms de constantes externes
64         - fonctions_unaires = dictionnaire {nom_fonction externe : nb arguments de cette fonction}
65         """
66         self.new_constantes = constantes
67         self.new_fonctions_unaires = fonctions
68         self.cr = CR()
69         self.l_operateurs = []
70         self.parent = parent
71         self.l_children = []
72         if formule :
73             self.set_formule(formule)
74         if self.parent :
75             self.parent.enregistre(self)
76
77     def set_formule(self,formule):
78         """
79         Stocke formule (tuple) dans l'attribut t_formule
80         Méthode externe
81         """
82         if type(formule) != types.TupleType:
83             raise InterpreteurException,"La formule passée à l'interpréteur doit être sous forme de tuple"
84         self.t_formule = formule
85         self.init_cr()
86         self.modify_listes()
87         self.ordonne_listes()
88
89     def init_cr(self):
90         """
91         Initialise le cr,cad valorise les chaînes debut et fin
92         """
93         nom = self.t_formule[0]
94         if nom :
95             if nom[0] in ('+','-') : nom = nom[1:]
96         self.cr.debut = "Début Fonction %s" %nom
97         self.cr.fin = "Fin Fonction %s" %nom
98         
99     def str(self):
100         """
101         Retourne une liste de chaînes de caractères représentant la formule
102         """
103         l_txt = []
104         l_txt.append(self.t_formule[0])
105         for oper in self.l_operateurs:
106             # oper est ici une liste décrivant oper
107             txt = []
108             for elem in oper:
109                 txt.append(str(elem))
110             l_txt.append(txt)
111         return l_txt
112
113     def report(self,decalage=1):
114         """
115         Retourne le rapport de FORMULE
116         """
117         txt = self.cr.report()
118         return txt
119     
120     def enregistre(self,fils):
121         """
122         Enregistre un opérateur fils dans la liste des children
123         """
124         self.l_children.append(fils)
125         self.cr.add(fils.cr)
126         
127     def isvalid(self):
128         """
129         Booléenne qui retourne 1 si la formule est valide, 0 sinon
130         Méthode externe
131         """
132         self.l_operateurs = []
133         self.cr.purge() # on vide le cr 
134         self.init_cr() # on initialise le cr
135         self.interprete_formule()
136         return self.cr.estvide()
137
138     def interprete_formule(self):
139         """
140         Réalise l'interprétation du corps de la formule
141         """
142         texte = self.t_formule[3]\r
143         if not texte : return
144         if type(texte) != types.ListType:
145             texte = [texte,]
146         for text_arg in texte:
147             text_arg = string.replace(text_arg,'\n','')
148             # Enleve les espaces
149             text_arg = string.replace(text_arg,' ','')
150             try:
151                 self.l_operateurs.append(self.split_operateurs(text_arg))
152             except InterpreteurException,e:
153                 self.cr.fatal(str(e))
154
155     def modify_listes(self):
156         """
157         Modifie la liste des constantes en lui ajoutant le nom des paramètres
158         de la fonction à interpréter
159         """
160         args = self.t_formule[2]
161         # l'interpréteur de formule sert aussi à évaluer les EVAL
162         # dans ce cas il n'y a pas d'arguments puisque pas de fonction ...
163         if args :
164             args = args[1:-1] # on enlève les parenthèses ouvrante et fermante
165             l_args = string.split(args,',')
166             for arg in l_args:
167                 typ,nom = string.split(arg,':')
168                 nom = string.strip(nom)
169                 self.l_constantes.append(nom)
170         # on considère que les fonctions unaires de base sont toutes à un seul argument :
171         l_f = []
172         self.d_fonctions_unaires = {}
173         for fct in self.l_fonctions_unaires:
174             self.d_fonctions_unaires[fct]=1
175         # on ajoute les constantes externes
176         for cte in self.new_constantes:
177             self.l_constantes.append(cte)
178         # on ajoute les fonctions unaires externes au dictionnaire des fonctions unaires
179         for new_fonc in self.new_fonctions_unaires:
180             self.d_fonctions_unaires[new_fonc[0]] = self.get_nb_args(new_fonc)
181         #self.d_fonctions_unaires.update(self.new_fonctions_unaires)
182         self.l_fonctions_unaires = self.d_fonctions_unaires.keys()
183         
184     def ordonne_listes(self):
185         """
186         Ordonne les listes de fonctions unaires et binaires
187         """
188         self.l_fonctions_binaires.sort(cmp_function)
189         self.l_fonctions_unaires.sort(cmp_function)
190         self.l_constantes.sort(cmp_function)
191         
192
193     def split_operateurs(self,texte):
194         """
195         Splite le texte passé en argument en opérateurs plus élémentaires.
196         N'analyse pas l'intérieur des opérateurs (ne fait qu'une passe)
197         """
198         l_operateurs = []
199         texte = string.strip(texte)
200         # on recherche un nombre en début de texte
201         try:
202             oper,reste = self.cherche_nombre(texte)
203         except InterpreteurException,e:
204             raise InterpreteurException,str(e)
205         if not oper :
206             # on recherche une constante en début de texte
207             try:
208                 oper,reste = self.cherche_constante(texte)
209             except InterpreteurException,e:
210                 raise InterpreteurException,str(e)
211             if not oper :
212                 # on recherche une expression entre parenthèses...
213                 try:
214                     oper,reste = self.cherche_expression_entre_parentheses(texte)
215                 except InterpreteurException,e:
216                     raise InterpreteurException,str(e)
217                 if not oper :
218                     # on recherche le début d'un opérateur unaire en début de texte
219                     try:
220                         oper,reste = self.cherche_operateur_unaire(texte)
221                     except InterpreteurException,e:
222                         raise InterpreteurException,str(e)
223                     if not oper :
224                         type_objet,nom_objet = self.get_type(texte)
225                         if type_objet == 'constante':
226                             raise InterpreteurException, "Constante %s inconnue" %nom_objet
227                         elif type_objet == 'fonction':
228                             raise InterpreteurException, "Fonction %s inconnue dans %s" %(nom_objet,texte)
229                         else:
230                             raise InterpreteurException, "Impossible d'interpréter : %s" %texte
231         # on a trouvé un opérateur (nombre, constante ou unaire)
232         # il faut encore vérifier que l'on est en fin de texte ou qu'il est bien suivi
233         # d'un opérateur binaire
234         l_operateurs.append(oper)
235         if reste :
236             texte = string.strip(reste)
237             oper,reste = self.cherche_operateur_binaire(texte)
238             if not oper :
239                 # on a un reste et pas d'opérateur binaire --> erreur
240                 raise InterpreteurException,"L'opérateur %s doit être suivi d'un opérateur binaire" %l_operateurs[-1]
241             else:
242                 # on a bien trouvé un opérateur binaire:
243                 l_operateurs.append(oper)
244                 # il faut recommencer l'analyse du reste par split_operateurs ...
245                 try:
246                     l_op = self.split_operateurs(reste)
247                 except InterpreteurException,e:
248                     raise InterpreteurException,str(e)
249                 l_operateurs.extend(l_op)
250                 return l_operateurs
251         else:
252             # on a fini d'analyser texte
253             return l_operateurs
254
255     def cherche_nombre(self,texte):
256         """
257         Cherche un nombre en début de texte
258         Retourne ce nombre et le reste ou None et le texte initial
259         Peut lever une InterpreteurException dans le cas où le nombre n'est pas valide
260         """
261         texte = string.strip(texte)
262         m = pat_number_complet.match(texte)
263         if m:
264             # on a trouvé un nombre avec exposant
265             l_groups = m.groups()
266             sgn = l_groups[0]
267             nb = l_groups[1]
268             if l_groups[2]:
269                 nb = nb+l_groups[2]
270             if l_groups[3]:
271                 nb = nb+l_groups[3]
272             nombre = sgn+nb
273             return nombre,l_groups[4]
274         else:
275             m = pat_number.match(texte)
276             if m :
277                 # on a trouvé un nombre sans exposant
278                 l_groups = m.groups()
279                 sgn = l_groups[0]
280                 nb = l_groups[1]
281                 if l_groups[2]:
282                     nb = nb+l_groups[2]
283                 nombre = sgn+nb
284                 # il faut vérifier si ce nombre n'est pas suivi d'un exposant incomplet ...
285                 reste = string.strip(l_groups[3])
286                 if reste == '':
287                     return nombre,l_groups[3]
288                 if reste[0] in ('e','E','d','D') :
289                     raise InterpreteurException,"La syntaxe de l'exposant de %s est erronée " %nb
290                 else:
291                     return nombre,l_groups[3]
292             else:
293                 # on n'a pas trouvé de nombre
294                 return None,texte
295         
296     def cherche_constante_old(self,texte):
297         """
298         Recherche une constante en début de texte parmi la liste des constantes.
299         Retourne le texte représentant la constante et le reste du texte ou
300         Retourne None,texte si aucune constante trouvée
301         """
302         txt = None
303         texte = string.strip(texte)
304         for cte in self.l_constantes:
305             index = string.find(texte,cte)
306             #if index == 0 : print 'on a trouvé %s dans %s en %d' %(cte,texte,index)
307             if index == 0 :
308                 txt = cte
309                 zz,reste = string.split(texte,cte,1)
310                 break
311         if txt :
312             return txt,reste
313         else:
314             # aucune constante trouvée
315             return None,texte
316
317     def cherche_constante(self,texte):
318         """
319         Recherche une constante en début de texte parmi la liste des constantes.
320         Retourne le texte représentant la constante et le reste du texte ou
321         Retourne None,texte si aucune constante trouvée
322         """
323         txt = None
324         texte = string.strip(texte)
325         m = pat_constante.match(texte)
326         if m :
327             # on a trouvé un identificateur en début de texte
328             l_groups = m.groups()
329             sgn = l_groups[0]
330             identificateur = string.strip(l_groups[1])
331             reste = l_groups[2]
332             # il faut vérifier qu'il ne s'agit pas d'un appel à une fonction
333             if reste :
334                 if reste[0] == '(' :
335                     # --> appel de fonction
336                     return None,texte
337             # il faut encore vérifier qu'elle est bien dans la liste des constantes...
338             if identificateur not in self.l_constantes :
339                 raise InterpreteurException,"La constante %s est inconnue dans %s" %(identificateur,texte)
340             else:
341                 return sgn+identificateur,reste
342         else:
343             # aucune constante trouvée
344             return None,texte
345         
346     def cherche_args(self,texte):
347         """
348         Cherche au début de texte une liste d'arguments entre parenthèses
349         """
350         if texte[0]!='(':
351             return None,texte
352         else:
353             n=0
354             cpt=1
355             while cpt != 0:
356                 n=n+1
357                 if n>= len(texte):
358                     # on a atteint la fin de texte sans avoir trouvé la parenthèse fermante --> erreur
359                     raise InterpreteurException,"Manque parenthèse fermante dans %s" %texte
360                 if texte[n] == '(':
361                     cpt=cpt+1
362                 elif texte[n]==')':
363                     cpt=cpt-1
364             if (n+1 < len(texte)):
365                 return texte[0:n+1],texte[n+1:]
366             else:
367                 # on a fini d'analyser le texte : reste = None
368                 return texte,None
369                     
370     def cherche_operateur_unaire_old(self,texte):
371         """
372         Cherche dans texte un operateur unaire
373         """
374         txt = None
375         texte = string.strip(texte)
376         for oper in self.l_fonctions_unaires:
377             index = string.find(texte,oper)
378             if index == 0 :
379                 txt = oper
380                 zz,reste = string.split(texte,oper,1)
381                 break
382         if txt :
383             #print 'on a trouvé :',txt
384             operateur = txt
385             texte = reste
386             try:
387                 args,reste = self.cherche_args(texte)
388             except InterpreteurException,e:
389                 raise InterpreteurException,str(e)
390             if not args :
391                 # opérateur unaire sans arguments
392                 raise InterpreteurException,'opérateur unaire  %s sans arguments' %operateur
393             else:
394                 #operateur = operateur+args
395                 args = self.split_args(txt,args,self.d_fonctions_unaires[operateur])
396                 formule_operateur = (txt,'',self.t_formule[2],args)
397                 operateur = Interpreteur_Formule(formule = formule_operateur,
398                                                  constantes = self.new_constantes,
399                                                  fonctions_unaires = self.new_fonctions_unaires,
400                                                  parent = self)
401                 operateur.interprete_formule()
402                 texte = reste
403                 return operateur,reste
404         else:
405             # aucun opérateur unaire trouvé
406             return None,texte
407
408     def cherche_operateur_unaire(self,texte):
409         """
410         Cherche dans texte un operateur unaire
411         """
412         txt = None
413         texte = string.strip(texte)
414         m = pat_constante.match(texte)
415         if m :
416             # on a trouvé un identificateur en début de texte
417             # il faut encore vérifier que l'on a bien à faire à un appel de fonction ...
418             l_groups = m.groups()
419             sgn = l_groups[0]
420             identificateur = string.strip(l_groups[1])
421             reste = l_groups[2]
422             try:
423                 args,reste = self.cherche_args(reste)
424             except InterpreteurException,e:
425                 raise InterpreteurException,str(e)
426             if not args :
427                 # opérateur unaire sans arguments
428                 # en principe on ne doit jamais être dans ce cas car il est déjà trappé par cherche_constante ...
429                 raise InterpreteurException,'Fonction %s sans arguments !' %identificateur
430             else:
431                 # il faut encore vérifier que l'on a bien à faire à une fonction connue
432                 if identificateur not in self.l_fonctions_unaires:
433                     raise InterpreteurException,'Fonction %s inconnue dans %s !' %(identificateur,texte)
434                 args = self.split_args(identificateur,args,self.d_fonctions_unaires[identificateur])
435                 formule_operateur = (sgn+identificateur,'',self.t_formule[2],args)
436                 operateur = Interpreteur_Formule(formule = formule_operateur,
437                                                  constantes = self.new_constantes,
438                                                  fonctions = self.new_fonctions_unaires,
439                                                  parent = self)
440                 operateur.interprete_formule()
441                 texte = reste
442                 return operateur,reste
443         elif texte[0] == '-':
444             # Il faut pouvoir trapper les expressions du type exp(-(x+1)) ...
445             try :
446                args,reste = self.cherche_args(texte[1:])
447             except InterpreteurException,e:
448                 raise InterpreteurException,str(e)
449             if not args :
450                # Il ne s'agit pas de '-' comme opérateur unaire --> on retourne None
451                return None,texte
452             else:
453                identificateur = '-'
454                args = self.split_args(identificateur,args,self.d_fonctions_unaires[identificateur])
455                formule_operateur = (identificateur,'',self.t_formule[2],args)
456                operateur = Interpreteur_Formule(formule = formule_operateur,
457                                                  constantes = self.new_constantes,
458                                                  fonctions = self.new_fonctions_unaires,
459                                                  parent = self)
460                operateur.interprete_formule()
461                texte = reste
462                return operateur,reste
463         else:
464             return None,texte
465             
466     def cherche_operateur_binaire(self,texte):
467         """
468         Cherche dans texte un operateur unaire
469         """
470         txt = None
471         texte = string.strip(texte)
472         for oper in self.l_fonctions_binaires:
473             index = string.find(texte,oper)
474             #if index != -1 : print 'on a trouvé %s dans %s en %d' %(oper,texte,index)
475             if index == 0 :
476                 txt = oper
477                 zz,reste = string.split(texte,oper,1)
478                 break
479         if txt :
480             return txt,reste
481         else:
482             # aucun opérateur unaire trouvé
483             return None,texte
484
485     def cherche_expression_entre_parentheses(self,texte):
486         """
487         Cherche en début de texte une expression entre parentheses
488         """
489         args,reste = self.cherche_args(string.strip(texte))
490         if not args :
491             return None,texte
492         else:
493             # on a trouvé une expression entre parenthèses en début de texte
494             # --> on retourne un objet Interpreteur_Formule
495             formule_operateur = ('','',self.t_formule[2],args[1:-1])
496             operateur = Interpreteur_Formule(formule = formule_operateur,
497                                              constantes = self.new_constantes,
498                                              fonctions = self.new_fonctions_unaires,
499                                              parent = self)
500             operateur.interprete_formule()
501             texte = reste
502             return operateur,reste
503             
504     def split_args(self,nom_fonction,args,nb_args):
505         """
506         Tente de partager args en nb_args éléments
507         Retourne une liste de chaînes de caractères (liste de longueur nb_args)
508         """
509         args = args[1:-1] # on enlève les parenthèses ouvrante et fermante
510         if nb_args == 1 : return args
511         l_args = string.split(args,',')
512         if len(l_args) != nb_args:
513             raise InterpreteurException,"La fonction %s requiert %d arguments : %d fourni(s)" %(nom_fonction,nb_args,len(l_args))
514         else:
515             return l_args
516
517     def get_type(self,texte):
518         """
519         Retourne le type de l'objet défini dans texte, à savoir:
520         - constante
521         - fonction
522         - unknown
523         et son nom
524         """
525         texte = string.strip(texte)
526         if '(' not in texte:
527             return 'constante',texte
528         if texte[-1] != ')':
529             return 'unknown',''
530         nom_oper,args = string.split(texte,'(',1)
531         return 'fonction',nom_oper
532
533     def get_nb_args(self,formule):
534         """
535         Retourne le nombre d'arguments dans la définition de formule (sous forme de tuple)
536         """
537         args = formule[2][1:-1] # on enlève les parenthèses ouvrante et fermante
538         l_args = string.split(args,',')
539         return len(l_args)
540
541 if __name__ == '__main__':
542     constantes = ['FREQ3','AMOR1']
543     fonctions_unaires=[('ACC','REEL','(REEL:x)','''bidon'''),]
544     f1 = ('f1','REEL','(REEL:x)','''SIN(x)+3*x''')
545     f2 = ('f2','REEL','(REEL:x)','''ATAN(x+3)+3*x''')
546     f3 = ('f3','REEL','(REEL:INST)','''ACC(INST,FREQ3,AMOR1)''')
547     f4 = ('f4','REEL','(REEL:INST)','''ACC(INST,FREQ2,AMOR1)''')
548     f5 = ('f5','REEL','(REEL:INST,REEL:Y)','''ACC(INST,FREQ3,AMOR1)+Y*INST''')
549     f6 = ('f6','REEL','(REEL:x)','''(x+ 3)/ 35.698''')
550     f7 = ('f7','REEL','(REEL:x)','''(x+ 3)/ 35.698E-10''')
551     f8 = ('f8','REEL','(REEL:x)','''(x+ 3)/ 35.698E''')
552     f9 = ('f9','REEL','(REEL:INSTA,REEl:INSTB)','''2.*SIN((PI/4)+((INSTA-INSTB)/2.))* COS((PI/4)-((INSTA+INSTB)/2.))''')
553     f10 = ('f10','REEL','(REEL:X)','''EXP(-(X+1))''')
554     for formule in (f1,f2,f3,f4,f5,f6,f7,f8,f9,f10):
555         i = Interpreteur_Formule(formule = formule,
556                                  constantes = constantes,
557                                  fonctions = fonctions_unaires)
558         txt = i.str()
559         print '\nformule %s = %s' %(str(formule),txt)
560         if i.isvalid() :
561             print "\n\tPas d'erreur !"
562         else:
563             print i.report()