1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2007-2013 EDF R&D
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License.
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # Lesser General Public License for more details.
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 from __future__ import absolute_import
21 from __future__ import print_function
23 from builtins import str
24 from builtins import object
29 from Noyau.N_CR import CR
30 from Extensions.i18n import tr
33 #def group(*choices): return '(' + ''.join(choices, '|') + ')'
34 #def any(*choices): return apply(group, choices) + '*'
35 #def maybe(*choices): return apply(group, choices) + '?'
37 Intnumber = r'[1-9]\d*'
38 Exponent = r'[eEdD][-+]?\d+'
39 Expfloat = r'[1-9]\d*' + Exponent
40 #Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent)
41 #Floatnumber = group(Pointfloat, Expfloat)
42 Pointfloat=r'(\d+\.\d*|\.\d+)([eEdD][-+]?\d+)?'
43 Floatnumber=r'((\d+\.\d*|\.\d+)([eEdD][-+]?\d+)?|[1-9]\d*[eEdD][-+]?\d+)'
46 pat_number = re.compile(r'^([+-]?)([0-9]+)(\.\d*)?(.*)')
47 pat_number_complet = re.compile(r'^([+-]?)([0-9]+)(\.\d*)?([eEdD][+-]?\d+)(.*)')
48 pat_constante = re.compile(r'^([+-]?)([a-zA-Z][a-zA-Z_0-9]*\s*)(.*)')
50 def cmp_function(arg1,arg2):
52 Fonction de comparaison permettant de classer les listes de
53 fonctions unaires et binaires selon la longueur de leurs arguments
54 On classe les arguments les plus longs en premier
56 if len(arg1) > len(arg2):
58 elif len(arg1) == len(arg2):
63 class InterpreteurException(Exception):
65 Classe servant a definir les exceptions levees par l'interpreteur de formule
67 def __init__(self,args=None):
73 class Interpreteur_Formule(object):
75 Cette classe sert a construire un interpreteur de formules Aster
77 l_fonctions_binaires = ['+','-','*','/','**','=','MOD','MIN','MAX','ATAN2']
78 l_fonctions_unaires = ['+','-','INT','REAL','AIMAG','ABS','SQRT','EXP','LOG',
79 'LOG10','SIN','COS','TAN','ASIN','ACOS','ATAN','SINH',
80 'COSH','TANH','HEAVYSID']
81 l_constantes = ['PI','RD_RG','DG_RD']
83 def __init__(self,formule=None,constantes=[],fonctions=[],parent=None):
85 Constructeur d'interpreteurs de formule Aster
86 - formule = tuple (nom,type,arguments,corps)
87 - constantes = liste des noms de constantes externes
88 - fonctions_unaires = dictionnaire {nom_fonction externe : nb arguments de cette fonction}
90 self.new_constantes = constantes
91 self.new_fonctions_unaires = fonctions
93 self.l_operateurs = []
97 self.set_formule(formule)
99 self.parent.enregistre(self)
101 def set_formule(self,formule):
103 Stocke formule (tuple) dans l'attribut t_formule
106 #if type(formule) != types.TupleType:
107 if type(formule) != types.tuple:
108 raise InterpreteurException(tr("La formule passee a l'interpreteur doit etre sous forme de tuple"))
109 self.t_formule = formule
112 self.ordonne_listes()
116 Initialise le cr,cad valorise les chaines debut et fin
118 nom = self.t_formule[0]
120 if nom[0] in ('+','-') : nom = nom[1:]
121 self.cr.debut = tr("Debut Fonction %s", nom)
122 self.cr.fin = tr("Fin Fonction %s", nom)
126 Retourne une liste de chaines de caracteres representant la formule
129 l_txt.append(self.t_formule[0])
130 for oper in self.l_operateurs:
131 # oper est ici une liste decrivant oper
134 txt.append(str(elem))
138 def report(self,decalage=1):
140 Retourne le rapport de FORMULE
142 txt = self.cr.report()
145 def enregistre(self,fils):
147 Enregistre un operateur fils dans la liste des children
149 self.l_children.append(fils)
154 Booleenne qui retourne 1 si la formule est valide, 0 sinon
157 self.l_operateurs = []
158 self.cr.purge() # on vide le cr
159 self.init_cr() # on initialise le cr
160 self.interprete_formule()
161 return self.cr.estvide()
163 def interprete_formule(self):
165 Realise l'interpretation du corps de la formule
167 texte = self.t_formule[3]
168 if not texte : return
169 if type(texte) != list:
171 for text_arg in texte:
172 text_arg = text_arg.replace('\n','')
174 text_arg = text_arg.replace(' ','')
176 self.l_operateurs.append(self.split_operateurs(text_arg))
177 except InterpreteurException as e:
178 self.cr.fatal(e.__str__())
180 def modify_listes(self):
182 Modifie la liste des constantes en lui ajoutant le nom des parametres
183 de la fonction a interpreter
185 args = self.t_formule[2]
186 # l'interpreteur de formule sert aussi a evaluer les EVAL
187 # dans ce cas il n'y a pas d'arguments puisque pas de fonction ...
189 args = args[1:-1] # on enleve les parentheses ouvrante et fermante
190 l_args = args.split(',')
192 typ,nom = arg.split(':')
194 self.l_constantes.append(nom)
195 # on considere que les fonctions unaires de base sont toutes a un seul argument :
197 self.d_fonctions_unaires = {}
198 for fct in self.l_fonctions_unaires:
199 self.d_fonctions_unaires[fct]=1
200 # on ajoute les constantes externes
201 for cte in self.new_constantes:
202 self.l_constantes.append(cte)
203 # on ajoute les fonctions unaires externes au dictionnaire des fonctions unaires
204 for new_fonc in self.new_fonctions_unaires:
205 self.d_fonctions_unaires[new_fonc[0]] = self.get_nb_args(new_fonc)
206 #self.d_fonctions_unaires.update(self.new_fonctions_unaires)
207 self.l_fonctions_unaires = list(self.d_fonctions_unaires.keys())
209 def ordonne_listes(self):
211 Ordonne les listes de fonctions unaires et binaires
213 self.l_fonctions_binaires.sort(cmp_function)
214 self.l_fonctions_unaires.sort(cmp_function)
215 self.l_constantes.sort(cmp_function)
218 def split_operateurs(self,texte):
220 Splite le texte passe en argument en operateurs plus elementaires.
221 N'analyse pas l'interieur des operateurs (ne fait qu'une passe)
224 texte = texte.strip()
225 # on recherche un nombre en debut de texte
227 oper,reste = self.cherche_nombre(texte)
228 except InterpreteurException as e:
229 raise InterpreteurException (e.__str__())
231 # on recherche une constante en debut de texte
233 oper,reste = self.cherche_constante(texte)
234 except InterpreteurException as e:
235 raise InterpreteurException (e.__str__())
237 # on recherche une expression entre parentheses...
239 oper,reste = self.cherche_expression_entre_parentheses(texte)
240 except InterpreteurException as e:
241 raise InterpreteurException(e.__str__())
243 # on recherche le debut d'un operateur unaire en debut de texte
245 oper,reste = self.cherche_operateur_unaire(texte)
246 except InterpreteurException as e:
247 raise InterpreteurException(e.__str__())
249 type_objet,nom_objet = self.get_type(texte)
250 if type_objet == 'constante':
251 raise InterpreteurException( "Constante %s inconnue" %nom_objet)
252 elif type_objet == 'fonction':
253 raise InterpreteurException( "Fonction %s inconnue dans %s" %(nom_objet,texte))
255 raise InterpreteurException( "Impossible d'interpreter : %s" %texte)
256 # on a trouve un operateur (nombre, constante ou unaire)
257 # il faut encore verifier que l'on est en fin de texte ou qu'il est bien suivi
258 # d'un operateur binaire
259 l_operateurs.append(oper)
261 texte = reste.strip()
262 oper,reste = self.cherche_operateur_binaire(texte)
264 # on a un reste et pas d'operateur binaire --> erreur
265 raise InterpreteurException("L'operateur %s doit etre suivi d'un operateur binaire" %l_operateurs[-1])
267 # on a bien trouve un operateur binaire:
268 l_operateurs.append(oper)
269 # il faut recommencer l'analyse du reste par split_operateurs ...
271 l_op = self.split_operateurs(reste)
272 except InterpreteurException as e:
273 raise InterpreteurException(e.__str__())
274 l_operateurs.extend(l_op)
277 # on a fini d'analyser texte
280 def cherche_nombre(self,texte):
282 Cherche un nombre en debut de texte
283 Retourne ce nombre et le reste ou None et le texte initial
284 Peut lever une InterpreteurException dans le cas ou le nombre n'est pas valide
286 texte = texte.strip()
287 m = pat_number_complet.match(texte)
289 # on a trouve un nombre avec exposant
290 l_groups = m.groups()
298 return nombre,l_groups[4]
300 m = pat_number.match(texte)
302 # on a trouve un nombre sans exposant
303 l_groups = m.groups()
309 # il faut verifier si ce nombre n'est pas suivi d'un exposant incomplet ...
310 reste = l_groups[3].strip()
312 return nombre,l_groups[3]
313 if reste[0] in ('e','E','d','D') :
314 raise InterpreteurException("La syntaxe de l'exposant de %s est erronee " %nb)
316 return nombre,l_groups[3]
318 # on n'a pas trouve de nombre
321 def cherche_constante_old(self,texte):
323 Recherche une constante en debut de texte parmi la liste des constantes.
324 Retourne le texte representant la constante et le reste du texte ou
325 Retourne None,texte si aucune constante trouvee
328 texte = texte.strip()
329 for cte in self.l_constantes:
330 index = texte.find(cte)
331 #if index == 0 : print 'on a trouve %s dans %s en %d' %(cte,texte,index)
334 zz,reste = texte.split(cte,1)
339 # aucune constante trouvee
342 def cherche_constante(self,texte):
344 Recherche une constante en debut de texte parmi la liste des constantes.
345 Retourne le texte representant la constante et le reste du texte ou
346 Retourne None,texte si aucune constante trouvee
349 texte = texte.strip()
350 m = pat_constante.match(texte)
352 # on a trouve un identificateur en debut de texte
353 l_groups = m.groups()
355 identificateur = l_groups[1].strip()
357 # il faut verifier qu'il ne s'agit pas d'un appel a une fonction
360 # --> appel de fonction
362 # il faut encore verifier qu'elle est bien dans la liste des constantes...
363 if identificateur not in self.l_constantes :
364 raise InterpreteurException("La constante %s est inconnue dans %s" %(identificateur,texte))
366 return sgn+identificateur,reste
368 # aucune constante trouvee
371 def cherche_args(self,texte):
373 Cherche au debut de texte une liste d'arguments entre parentheses
383 # on a atteint la fin de texte sans avoir trouve la parenthese fermante --> erreur
384 raise InterpreteurException("Manque parenthese fermante dans %s" %texte)
389 if (n+1 < len(texte)):
390 return texte[0:n+1],texte[n+1:]
392 # on a fini d'analyser le texte : reste = None
395 def cherche_operateur_unaire_old(self,texte):
397 Cherche dans texte un operateur unaire
400 texte = texte.strip()
401 for oper in self.l_fonctions_unaires:
402 index = texte.find(oper)
405 zz,reste = texte.split(oper,1)
408 #print 'on a trouve :',txt
412 args,reste = self.cherche_args(texte)
413 except InterpreteurException as e:
414 raise InterpreteurException(e.__str__())
416 # operateur unaire sans arguments
417 raise InterpreteurException('operateur unaire %s sans arguments' %operateur)
419 #operateur = operateur+args
420 args = self.split_args(txt,args,self.d_fonctions_unaires[operateur])
421 formule_operateur = (txt,'',self.t_formule[2],args)
422 operateur = Interpreteur_Formule(formule = formule_operateur,
423 constantes = self.new_constantes,
424 fonctions_unaires = self.new_fonctions_unaires,
426 operateur.interprete_formule()
428 return operateur,reste
430 # aucun operateur unaire trouve
433 def cherche_operateur_unaire(self,texte):
435 Cherche dans texte un operateur unaire
438 texte = texte.strip()
439 m = pat_constante.match(texte)
441 # on a trouve un identificateur en debut de texte
442 # il faut encore verifier que l'on a bien a faire a un appel de fonction ...
443 l_groups = m.groups()
445 identificateur = l_groups[1].strip()
448 args,reste = self.cherche_args(reste)
449 except InterpreteurException as e:
450 raise InterpreteurException (e.__str__())
452 # operateur unaire sans arguments
453 # en principe on ne doit jamais etre dans ce cas car il est deja trappe par cherche_constante ...
454 raise InterpreteurException ('Fonction %s sans arguments !' %identificateur)
456 # il faut encore verifier que l'on a bien a faire a une fonction connue
457 if identificateur not in self.l_fonctions_unaires:
458 raise InterpreteurException ('Fonction %s inconnue dans %s !' %(identificateur,texte))
459 args = self.split_args(identificateur,args,self.d_fonctions_unaires[identificateur])
460 formule_operateur = (sgn+identificateur,'',self.t_formule[2],args)
461 operateur = Interpreteur_Formule(formule = formule_operateur,
462 constantes = self.new_constantes,
463 fonctions = self.new_fonctions_unaires,
465 operateur.interprete_formule()
467 return operateur,reste
468 elif texte[0] == '-':
469 # Il faut pouvoir trapper les expressions du type exp(-(x+1)) ...
471 args,reste = self.cherche_args(texte[1:])
472 except InterpreteurException as e:
473 raise InterpreteurException (e.__str__())
475 # Il ne s'agit pas de '-' comme operateur unaire --> on retourne None
479 args = self.split_args(identificateur,args,self.d_fonctions_unaires[identificateur])
480 formule_operateur = (identificateur,'',self.t_formule[2],args)
481 operateur = Interpreteur_Formule(formule = formule_operateur,
482 constantes = self.new_constantes,
483 fonctions = self.new_fonctions_unaires,
485 operateur.interprete_formule()
487 return operateur,reste
491 def cherche_operateur_binaire(self,texte):
493 Cherche dans texte un operateur unaire
496 texte = texte.strip()
497 for oper in self.l_fonctions_binaires:
498 index = texte.find(oper)
499 #if index != -1 : print 'on a trouve %s dans %s en %d' %(oper,texte,index)
502 zz,reste = texte.split(oper,1)
507 # aucun operateur unaire trouve
510 def cherche_expression_entre_parentheses(self,texte):
512 Cherche en debut de texte une expression entre parentheses
514 args,reste = self.cherche_args(texte.strip())
518 # on a trouve une expression entre parentheses en debut de texte
519 # --> on retourne un objet Interpreteur_Formule
520 formule_operateur = ('','',self.t_formule[2],args[1:-1])
521 operateur = Interpreteur_Formule(formule = formule_operateur,
522 constantes = self.new_constantes,
523 fonctions = self.new_fonctions_unaires,
525 operateur.interprete_formule()
527 return operateur,reste
529 def split_args(self,nom_fonction,args,nb_args):
531 Tente de partager args en nb_args elements
532 Retourne une liste de chaines de caracteres (liste de longueur nb_args)
534 args = args[1:-1] # on enleve les parentheses ouvrante et fermante
535 if nb_args == 1 : return args
536 l_args = args.split(',')
537 if len(l_args) != nb_args:
538 raise InterpreteurException ("La fonction %s requiert %d arguments : %d fourni(s)" %(nom_fonction,nb_args,len(l_args)))
542 def get_type(self,texte):
544 Retourne le type de l'objet defini dans texte, a savoir:
550 texte = texte.strip()
552 return 'constante',texte
555 nom_oper,args = texte.split('(',1)
556 return 'fonction',nom_oper
558 def get_nb_args(self,formule):
560 Retourne le nombre d'arguments dans la definition de formule (sous forme de tuple)
562 args = formule[2][1:-1] # on enleve les parentheses ouvrante et fermante
563 l_args = args.split(',')
566 if __name__ == '__main__':
567 constantes = ['FREQ3','AMOR1']
568 fonctions_unaires=[('ACC','REEL','(REEL:x)','''bidon'''),]
569 f1 = ('f1','REEL','(REEL:x)','''SIN(x)+3*x''')
570 f2 = ('f2','REEL','(REEL:x)','''ATAN(x+3)+3*x''')
571 f3 = ('f3','REEL','(REEL:INST)','''ACC(INST,FREQ3,AMOR1)''')
572 f4 = ('f4','REEL','(REEL:INST)','''ACC(INST,FREQ2,AMOR1)''')
573 f5 = ('f5','REEL','(REEL:INST,REEL:Y)','''ACC(INST,FREQ3,AMOR1)+Y*INST''')
574 f6 = ('f6','REEL','(REEL:x)','''(x+ 3)/ 35.698''')
575 f7 = ('f7','REEL','(REEL:x)','''(x+ 3)/ 35.698E-10''')
576 f8 = ('f8','REEL','(REEL:x)','''(x+ 3)/ 35.698E''')
577 f9 = ('f9','REEL','(REEL:INSTA,REEl:INSTB)','''2.*SIN((PI/4)+((INSTA-INSTB)/2.))* COS((PI/4)-((INSTA+INSTB)/2.))''')
578 f10 = ('f10','REEL','(REEL:X)','''EXP(-(X+1))''')
579 for formule in (f1,f2,f3,f4,f5,f6,f7,f8,f9,f10):
580 i = Interpreteur_Formule(formule = formule,
581 constantes = constantes,
582 fonctions = fonctions_unaires)
584 print(('\nformule %s = %s' %(str(formule),txt)))
586 # print "\n\tPas d'erreur !"