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 import string,re,sys,types
22 from Noyau.N_CR import CR
23 from Extensions.i18n import tr
26 #def group(*choices): return '(' + string.join(choices, '|') + ')'
27 #def any(*choices): return apply(group, choices) + '*'
28 #def maybe(*choices): return apply(group, choices) + '?'
30 Intnumber = r'[1-9]\d*'
31 Exponent = r'[eEdD][-+]?\d+'
32 Expfloat = r'[1-9]\d*' + Exponent
33 #Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent)
34 #Floatnumber = group(Pointfloat, Expfloat)
35 Pointfloat=r'(\d+\.\d*|\.\d+)([eEdD][-+]?\d+)?'
36 Floatnumber=r'((\d+\.\d*|\.\d+)([eEdD][-+]?\d+)?|[1-9]\d*[eEdD][-+]?\d+)'
39 pat_number = re.compile(r'^([+-]?)([0-9]+)(\.\d*)?(.*)')
40 pat_number_complet = re.compile(r'^([+-]?)([0-9]+)(\.\d*)?([eEdD][+-]?\d+)(.*)')
41 pat_constante = re.compile(r'^([+-]?)([a-zA-Z][a-zA-Z_0-9]*\s*)(.*)')
43 def cmp_function(arg1,arg2):
45 Fonction de comparaison permettant de classer les listes de
46 fonctions unaires et binaires selon la longueur de leurs arguments
47 On classe les arguments les plus longs en premier
49 if len(arg1) > len(arg2):
51 elif len(arg1) == len(arg2):
56 class InterpreteurException(Exception):
58 Classe servant a definir les exceptions levees par l'interpreteur de formule
60 def __init__(self,args=None):
66 class Interpreteur_Formule:
68 Cette classe sert a construire un interpreteur de formules Aster
70 l_fonctions_binaires = ['+','-','*','/','**','=','MOD','MIN','MAX','ATAN2']
71 l_fonctions_unaires = ['+','-','INT','REAL','AIMAG','ABS','SQRT','EXP','LOG',
72 'LOG10','SIN','COS','TAN','ASIN','ACOS','ATAN','SINH',
73 'COSH','TANH','HEAVYSID']
74 l_constantes = ['PI','RD_RG','DG_RD']
76 def __init__(self,formule=None,constantes=[],fonctions=[],parent=None):
78 Constructeur d'interpreteurs de formule Aster
79 - formule = tuple (nom,type,arguments,corps)
80 - constantes = liste des noms de constantes externes
81 - fonctions_unaires = dictionnaire {nom_fonction externe : nb arguments de cette fonction}
83 self.new_constantes = constantes
84 self.new_fonctions_unaires = fonctions
86 self.l_operateurs = []
90 self.set_formule(formule)
92 self.parent.enregistre(self)
94 def set_formule(self,formule):
96 Stocke formule (tuple) dans l'attribut t_formule
99 #if type(formule) != types.TupleType:
100 if type(formule) != types.tuple:
101 raise InterpreteurException(tr("La formule passee a l'interpreteur doit etre sous forme de tuple"))
102 self.t_formule = formule
105 self.ordonne_listes()
109 Initialise le cr,cad valorise les chaines debut et fin
111 nom = self.t_formule[0]
113 if nom[0] in ('+','-') : nom = nom[1:]
114 self.cr.debut = tr("Debut Fonction %s", nom)
115 self.cr.fin = tr("Fin Fonction %s", nom)
119 Retourne une liste de chaines de caracteres representant la formule
122 l_txt.append(self.t_formule[0])
123 for oper in self.l_operateurs:
124 # oper est ici une liste decrivant oper
127 txt.append(str(elem))
131 def report(self,decalage=1):
133 Retourne le rapport de FORMULE
135 txt = self.cr.report()
138 def enregistre(self,fils):
140 Enregistre un operateur fils dans la liste des children
142 self.l_children.append(fils)
147 Booleenne qui retourne 1 si la formule est valide, 0 sinon
150 self.l_operateurs = []
151 self.cr.purge() # on vide le cr
152 self.init_cr() # on initialise le cr
153 self.interprete_formule()
154 return self.cr.estvide()
156 def interprete_formule(self):
158 Realise l'interpretation du corps de la formule
160 texte = self.t_formule[3]
161 if not texte : return
162 if type(texte) != types.ListType:
164 for text_arg in texte:
165 text_arg = string.replace(text_arg,'\n','')
167 text_arg = string.replace(text_arg,' ','')
169 self.l_operateurs.append(self.split_operateurs(text_arg))
170 except InterpreteurException as e:
171 self.cr.fatal(e.__str__())
173 def modify_listes(self):
175 Modifie la liste des constantes en lui ajoutant le nom des parametres
176 de la fonction a interpreter
178 args = self.t_formule[2]
179 # l'interpreteur de formule sert aussi a evaluer les EVAL
180 # dans ce cas il n'y a pas d'arguments puisque pas de fonction ...
182 args = args[1:-1] # on enleve les parentheses ouvrante et fermante
183 l_args = string.split(args,',')
185 typ,nom = string.split(arg,':')
186 nom = string.strip(nom)
187 self.l_constantes.append(nom)
188 # on considere que les fonctions unaires de base sont toutes a un seul argument :
190 self.d_fonctions_unaires = {}
191 for fct in self.l_fonctions_unaires:
192 self.d_fonctions_unaires[fct]=1
193 # on ajoute les constantes externes
194 for cte in self.new_constantes:
195 self.l_constantes.append(cte)
196 # on ajoute les fonctions unaires externes au dictionnaire des fonctions unaires
197 for new_fonc in self.new_fonctions_unaires:
198 self.d_fonctions_unaires[new_fonc[0]] = self.get_nb_args(new_fonc)
199 #self.d_fonctions_unaires.update(self.new_fonctions_unaires)
200 self.l_fonctions_unaires = self.d_fonctions_unaires.keys()
202 def ordonne_listes(self):
204 Ordonne les listes de fonctions unaires et binaires
206 self.l_fonctions_binaires.sort(cmp_function)
207 self.l_fonctions_unaires.sort(cmp_function)
208 self.l_constantes.sort(cmp_function)
211 def split_operateurs(self,texte):
213 Splite le texte passe en argument en operateurs plus elementaires.
214 N'analyse pas l'interieur des operateurs (ne fait qu'une passe)
217 texte = string.strip(texte)
218 # on recherche un nombre en debut de texte
220 oper,reste = self.cherche_nombre(texte)
221 except InterpreteurException as e:
222 raise InterpreteurException (e.__str__())
224 # on recherche une constante en debut de texte
226 oper,reste = self.cherche_constante(texte)
227 except InterpreteurException as e:
228 raise InterpreteurException (e.__str__())
230 # on recherche une expression entre parentheses...
232 oper,reste = self.cherche_expression_entre_parentheses(texte)
233 except InterpreteurException as e:
234 raise InterpreteurException(e.__str__())
236 # on recherche le debut d'un operateur unaire en debut de texte
238 oper,reste = self.cherche_operateur_unaire(texte)
239 except InterpreteurException as e:
240 raise InterpreteurException(e.__str__())
242 type_objet,nom_objet = self.get_type(texte)
243 if type_objet == 'constante':
244 raise InterpreteurException( "Constante %s inconnue" %nom_objet)
245 elif type_objet == 'fonction':
246 raise InterpreteurException( "Fonction %s inconnue dans %s" %(nom_objet,texte))
248 raise InterpreteurException( "Impossible d'interpreter : %s" %texte)
249 # on a trouve un operateur (nombre, constante ou unaire)
250 # il faut encore verifier que l'on est en fin de texte ou qu'il est bien suivi
251 # d'un operateur binaire
252 l_operateurs.append(oper)
254 texte = string.strip(reste)
255 oper,reste = self.cherche_operateur_binaire(texte)
257 # on a un reste et pas d'operateur binaire --> erreur
258 raise InterpreteurException("L'operateur %s doit etre suivi d'un operateur binaire" %l_operateurs[-1])
260 # on a bien trouve un operateur binaire:
261 l_operateurs.append(oper)
262 # il faut recommencer l'analyse du reste par split_operateurs ...
264 l_op = self.split_operateurs(reste)
265 except InterpreteurException as e:
266 raise InterpreteurException(e.__str__())
267 l_operateurs.extend(l_op)
270 # on a fini d'analyser texte
273 def cherche_nombre(self,texte):
275 Cherche un nombre en debut de texte
276 Retourne ce nombre et le reste ou None et le texte initial
277 Peut lever une InterpreteurException dans le cas ou le nombre n'est pas valide
279 texte = string.strip(texte)
280 m = pat_number_complet.match(texte)
282 # on a trouve un nombre avec exposant
283 l_groups = m.groups()
291 return nombre,l_groups[4]
293 m = pat_number.match(texte)
295 # on a trouve un nombre sans exposant
296 l_groups = m.groups()
302 # il faut verifier si ce nombre n'est pas suivi d'un exposant incomplet ...
303 reste = string.strip(l_groups[3])
305 return nombre,l_groups[3]
306 if reste[0] in ('e','E','d','D') :
307 raise InterpreteurException("La syntaxe de l'exposant de %s est erronee " %nb)
309 return nombre,l_groups[3]
311 # on n'a pas trouve de nombre
314 def cherche_constante_old(self,texte):
316 Recherche une constante en debut de texte parmi la liste des constantes.
317 Retourne le texte representant la constante et le reste du texte ou
318 Retourne None,texte si aucune constante trouvee
321 texte = string.strip(texte)
322 for cte in self.l_constantes:
323 index = string.find(texte,cte)
324 #if index == 0 : print 'on a trouve %s dans %s en %d' %(cte,texte,index)
327 zz,reste = string.split(texte,cte,1)
332 # aucune constante trouvee
335 def cherche_constante(self,texte):
337 Recherche une constante en debut de texte parmi la liste des constantes.
338 Retourne le texte representant la constante et le reste du texte ou
339 Retourne None,texte si aucune constante trouvee
342 texte = string.strip(texte)
343 m = pat_constante.match(texte)
345 # on a trouve un identificateur en debut de texte
346 l_groups = m.groups()
348 identificateur = string.strip(l_groups[1])
350 # il faut verifier qu'il ne s'agit pas d'un appel a une fonction
353 # --> appel de fonction
355 # il faut encore verifier qu'elle est bien dans la liste des constantes...
356 if identificateur not in self.l_constantes :
357 raise InterpreteurException("La constante %s est inconnue dans %s" %(identificateur,texte))
359 return sgn+identificateur,reste
361 # aucune constante trouvee
364 def cherche_args(self,texte):
366 Cherche au debut de texte une liste d'arguments entre parentheses
376 # on a atteint la fin de texte sans avoir trouve la parenthese fermante --> erreur
377 raise InterpreteurException("Manque parenthese fermante dans %s" %texte)
382 if (n+1 < len(texte)):
383 return texte[0:n+1],texte[n+1:]
385 # on a fini d'analyser le texte : reste = None
388 def cherche_operateur_unaire_old(self,texte):
390 Cherche dans texte un operateur unaire
393 texte = string.strip(texte)
394 for oper in self.l_fonctions_unaires:
395 index = string.find(texte,oper)
398 zz,reste = string.split(texte,oper,1)
401 #print 'on a trouve :',txt
405 args,reste = self.cherche_args(texte)
406 except InterpreteurException as e:
407 raise InterpreteurException(e.__str__())
409 # operateur unaire sans arguments
410 raise InterpreteurException('operateur unaire %s sans arguments' %operateur)
412 #operateur = operateur+args
413 args = self.split_args(txt,args,self.d_fonctions_unaires[operateur])
414 formule_operateur = (txt,'',self.t_formule[2],args)
415 operateur = Interpreteur_Formule(formule = formule_operateur,
416 constantes = self.new_constantes,
417 fonctions_unaires = self.new_fonctions_unaires,
419 operateur.interprete_formule()
421 return operateur,reste
423 # aucun operateur unaire trouve
426 def cherche_operateur_unaire(self,texte):
428 Cherche dans texte un operateur unaire
431 texte = string.strip(texte)
432 m = pat_constante.match(texte)
434 # on a trouve un identificateur en debut de texte
435 # il faut encore verifier que l'on a bien a faire a un appel de fonction ...
436 l_groups = m.groups()
438 identificateur = string.strip(l_groups[1])
441 args,reste = self.cherche_args(reste)
442 except InterpreteurException as e:
443 raise InterpreteurException (e.__str__())
445 # operateur unaire sans arguments
446 # en principe on ne doit jamais etre dans ce cas car il est deja trappe par cherche_constante ...
447 raise InterpreteurException ('Fonction %s sans arguments !' %identificateur)
449 # il faut encore verifier que l'on a bien a faire a une fonction connue
450 if identificateur not in self.l_fonctions_unaires:
451 raise InterpreteurException ('Fonction %s inconnue dans %s !' %(identificateur,texte))
452 args = self.split_args(identificateur,args,self.d_fonctions_unaires[identificateur])
453 formule_operateur = (sgn+identificateur,'',self.t_formule[2],args)
454 operateur = Interpreteur_Formule(formule = formule_operateur,
455 constantes = self.new_constantes,
456 fonctions = self.new_fonctions_unaires,
458 operateur.interprete_formule()
460 return operateur,reste
461 elif texte[0] == '-':
462 # Il faut pouvoir trapper les expressions du type exp(-(x+1)) ...
464 args,reste = self.cherche_args(texte[1:])
465 except InterpreteurException as e:
466 raise InterpreteurException (e.__str__())
468 # Il ne s'agit pas de '-' comme operateur unaire --> on retourne None
472 args = self.split_args(identificateur,args,self.d_fonctions_unaires[identificateur])
473 formule_operateur = (identificateur,'',self.t_formule[2],args)
474 operateur = Interpreteur_Formule(formule = formule_operateur,
475 constantes = self.new_constantes,
476 fonctions = self.new_fonctions_unaires,
478 operateur.interprete_formule()
480 return operateur,reste
484 def cherche_operateur_binaire(self,texte):
486 Cherche dans texte un operateur unaire
489 texte = string.strip(texte)
490 for oper in self.l_fonctions_binaires:
491 index = string.find(texte,oper)
492 #if index != -1 : print 'on a trouve %s dans %s en %d' %(oper,texte,index)
495 zz,reste = string.split(texte,oper,1)
500 # aucun operateur unaire trouve
503 def cherche_expression_entre_parentheses(self,texte):
505 Cherche en debut de texte une expression entre parentheses
507 args,reste = self.cherche_args(string.strip(texte))
511 # on a trouve une expression entre parentheses en debut de texte
512 # --> on retourne un objet Interpreteur_Formule
513 formule_operateur = ('','',self.t_formule[2],args[1:-1])
514 operateur = Interpreteur_Formule(formule = formule_operateur,
515 constantes = self.new_constantes,
516 fonctions = self.new_fonctions_unaires,
518 operateur.interprete_formule()
520 return operateur,reste
522 def split_args(self,nom_fonction,args,nb_args):
524 Tente de partager args en nb_args elements
525 Retourne une liste de chaines de caracteres (liste de longueur nb_args)
527 args = args[1:-1] # on enleve les parentheses ouvrante et fermante
528 if nb_args == 1 : return args
529 l_args = string.split(args,',')
530 if len(l_args) != nb_args:
531 raise InterpreteurException ("La fonction %s requiert %d arguments : %d fourni(s)" %(nom_fonction,nb_args,len(l_args)))
535 def get_type(self,texte):
537 Retourne le type de l'objet defini dans texte, a savoir:
543 texte = string.strip(texte)
545 return 'constante',texte
548 nom_oper,args = string.split(texte,'(',1)
549 return 'fonction',nom_oper
551 def get_nb_args(self,formule):
553 Retourne le nombre d'arguments dans la definition de formule (sous forme de tuple)
555 args = formule[2][1:-1] # on enleve les parentheses ouvrante et fermante
556 l_args = string.split(args,',')
559 if __name__ == '__main__':
560 constantes = ['FREQ3','AMOR1']
561 fonctions_unaires=[('ACC','REEL','(REEL:x)','''bidon'''),]
562 f1 = ('f1','REEL','(REEL:x)','''SIN(x)+3*x''')
563 f2 = ('f2','REEL','(REEL:x)','''ATAN(x+3)+3*x''')
564 f3 = ('f3','REEL','(REEL:INST)','''ACC(INST,FREQ3,AMOR1)''')
565 f4 = ('f4','REEL','(REEL:INST)','''ACC(INST,FREQ2,AMOR1)''')
566 f5 = ('f5','REEL','(REEL:INST,REEL:Y)','''ACC(INST,FREQ3,AMOR1)+Y*INST''')
567 f6 = ('f6','REEL','(REEL:x)','''(x+ 3)/ 35.698''')
568 f7 = ('f7','REEL','(REEL:x)','''(x+ 3)/ 35.698E-10''')
569 f8 = ('f8','REEL','(REEL:x)','''(x+ 3)/ 35.698E''')
570 f9 = ('f9','REEL','(REEL:INSTA,REEl:INSTB)','''2.*SIN((PI/4)+((INSTA-INSTB)/2.))* COS((PI/4)-((INSTA+INSTB)/2.))''')
571 f10 = ('f10','REEL','(REEL:X)','''EXP(-(X+1))''')
572 for formule in (f1,f2,f3,f4,f5,f6,f7,f8,f9,f10):
573 i = Interpreteur_Formule(formule = formule,
574 constantes = constantes,
575 fonctions = fonctions_unaires)
577 print ('\nformule %s = %s' %(str(formule),txt))
579 # print "\n\tPas d'erreur !"