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
10 from Noyau.N_CR import CR
12 def group(*choices): return '(' + string.join(choices, '|') + ')'
13 def any(*choices): return apply(group, choices) + '*'
14 def maybe(*choices): return apply(group, choices) + '?'
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)
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*)(.*)')
26 def cmp_function(arg1,arg2):
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
32 if len(arg1) > len(arg2):
34 elif len(arg1) == len(arg2):
39 class InterpreteurException(exceptions.Exception):
41 Classe servant à définir les exceptions levées par l'interpréteur de formule
43 def __init__(self,args=None):
49 class Interpreteur_Formule:
51 Cette classe sert à construire un interpréteur de formules Aster
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']
59 def __init__(self,formule=None,constantes=[],fonctions=[],parent=None):
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}
66 self.new_constantes = constantes
67 self.new_fonctions_unaires = fonctions
69 self.l_operateurs = []
73 self.set_formule(formule)
75 self.parent.enregistre(self)
77 def set_formule(self,formule):
79 Stocke formule (tuple) dans l'attribut t_formule
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
91 Initialise le cr,cad valorise les chaînes debut et fin
93 nom = self.t_formule[0]
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
101 Retourne une liste de chaînes de caractères représentant la formule
104 l_txt.append(self.t_formule[0])
105 for oper in self.l_operateurs:
106 # oper est ici une liste décrivant oper
109 txt.append(str(elem))
113 def report(self,decalage=1):
115 Retourne le rapport de FORMULE
117 txt = self.cr.report()
120 def enregistre(self,fils):
122 Enregistre un opérateur fils dans la liste des children
124 self.l_children.append(fils)
129 Booléenne qui retourne 1 si la formule est valide, 0 sinon
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()
138 def interprete_formule(self):
140 Réalise l'interprétation du corps de la formule
142 texte = self.t_formule[3]
\r
143 if not texte : return
144 if type(texte) != types.ListType:
146 for text_arg in texte:
147 text_arg = string.replace(text_arg,'\n','')
149 self.l_operateurs.append(self.split_operateurs(text_arg))
150 except InterpreteurException,e:
151 self.cr.fatal(str(e))
153 def modify_listes(self):
155 Modifie la liste des constantes en lui ajoutant le nom des paramètres
156 de la fonction à interpréter
158 args = self.t_formule[2]
159 # l'interpréteur de formule sert aussi à évaluer les EVAL
160 # dans ce cas il n'y a pas d'arguments puisque pas de fonction ...
162 args = args[1:-1] # on enlève les parenthèses ouvrante et fermante
163 l_args = string.split(args,',')
165 typ,nom = string.split(arg,':')
166 nom = string.strip(nom)
167 self.l_constantes.append(nom)
168 # on considère que les fonctions unaires de base sont toutes à un seul argument :
170 self.d_fonctions_unaires = {}
171 for fct in self.l_fonctions_unaires:
172 self.d_fonctions_unaires[fct]=1
173 # on ajoute les constantes externes
174 for cte in self.new_constantes:
175 self.l_constantes.append(cte)
176 # on ajoute les fonctions unaires externes au dictionnaire des fonctions unaires
177 for new_fonc in self.new_fonctions_unaires:
178 self.d_fonctions_unaires[new_fonc[0]] = self.get_nb_args(new_fonc)
179 #self.d_fonctions_unaires.update(self.new_fonctions_unaires)
180 self.l_fonctions_unaires = self.d_fonctions_unaires.keys()
182 def ordonne_listes(self):
184 Ordonne les listes de fonctions unaires et binaires
186 self.l_fonctions_binaires.sort(cmp_function)
187 self.l_fonctions_unaires.sort(cmp_function)
188 self.l_constantes.sort(cmp_function)
191 def split_operateurs(self,texte):
193 Splite le texte passé en argument en opérateurs plus élémentaires.
194 N'analyse pas l'intérieur des opérateurs (ne fait qu'une passe)
197 texte = string.strip(texte)
198 # on recherche un nombre en début de texte
200 oper,reste = self.cherche_nombre(texte)
201 except InterpreteurException,e:
202 raise InterpreteurException,str(e)
204 # on recherche une constante en début de texte
206 oper,reste = self.cherche_constante(texte)
207 except InterpreteurException,e:
208 raise InterpreteurException,str(e)
210 # on recherche une expression entre parenthèses...
212 oper,reste = self.cherche_expression_entre_parentheses(texte)
213 except InterpreteurException,e:
214 raise InterpreteurException,str(e)
216 # on recherche le début d'un opérateur unaire en début de texte
218 oper,reste = self.cherche_operateur_unaire(texte)
219 except InterpreteurException,e:
220 raise InterpreteurException,str(e)
222 type_objet,nom_objet = self.get_type(texte)
223 if type_objet == 'constante':
224 raise InterpreteurException, "Constante %s inconnue" %nom_objet
225 elif type_objet == 'fonction':
226 raise InterpreteurException, "Fonction %s inconnue dans %s" %(nom_objet,texte)
228 raise InterpreteurException, "Impossible d'interpréter : %s" %texte
229 # on a trouvé un opérateur (nombre, constante ou unaire)
230 # il faut encore vérifier que l'on est en fin de texte ou qu'il est bien suivi
231 # d'un opérateur binaire
232 l_operateurs.append(oper)
234 texte = string.strip(reste)
235 oper,reste = self.cherche_operateur_binaire(texte)
237 # on a un reste et pas d'opérateur binaire --> erreur
238 raise InterpreteurException,"L'opérateur %s doit être suivi d'un opérateur binaire" %l_operateurs[-1]
240 # on a bien trouvé un opérateur binaire:
241 l_operateurs.append(oper)
242 # il faut recommencer l'analyse du reste par split_operateurs ...
244 l_op = self.split_operateurs(reste)
245 except InterpreteurException,e:
246 raise InterpreteurException,str(e)
247 l_operateurs.extend(l_op)
250 # on a fini d'analyser texte
253 def cherche_nombre(self,texte):
255 Cherche un nombre en début de texte
256 Retourne ce nombre et le reste ou None et le texte initial
257 Peut lever une InterpreteurException dans le cas où le nombre n'est pas valide
259 texte = string.strip(texte)
260 m = pat_number_complet.match(texte)
262 # on a trouvé un nombre avec exposant
263 l_groups = m.groups()
271 return nombre,l_groups[4]
273 m = pat_number.match(texte)
275 # on a trouvé un nombre sans exposant
276 l_groups = m.groups()
282 # il faut vérifier si ce nombre n'est pas suivi d'un exposant incomplet ...
283 reste = string.strip(l_groups[3])
285 return nombre,l_groups[3]
286 if reste[0] in ('e','E','d','D') :
287 raise InterpreteurException,"La syntaxe de l'exposant de %s est erronée " %nb
289 return nombre,l_groups[3]
291 # on n'a pas trouvé de nombre
294 def cherche_constante_old(self,texte):
296 Recherche une constante en début de texte parmi la liste des constantes.
297 Retourne le texte représentant la constante et le reste du texte ou
298 Retourne None,texte si aucune constante trouvée
301 texte = string.strip(texte)
302 for cte in self.l_constantes:
303 index = string.find(texte,cte)
304 #if index == 0 : print 'on a trouvé %s dans %s en %d' %(cte,texte,index)
307 zz,reste = string.split(texte,cte,1)
312 # aucune constante trouvée
315 def cherche_constante(self,texte):
317 Recherche une constante en début de texte parmi la liste des constantes.
318 Retourne le texte représentant la constante et le reste du texte ou
319 Retourne None,texte si aucune constante trouvée
322 texte = string.strip(texte)
323 m = pat_constante.match(texte)
325 # on a trouvé un identificateur en début de texte
326 l_groups = m.groups()
328 identificateur = string.strip(l_groups[1])
330 # il faut vérifier qu'il ne s'agit pas d'un appel à une fonction
333 # --> appel de fonction
335 # il faut encore vérifier qu'elle est bien dans la liste des constantes...
336 if identificateur not in self.l_constantes :
337 raise InterpreteurException,"La constante %s est inconnue dans %s" %(identificateur,texte)
339 return sgn+identificateur,reste
341 # aucune constante trouvée
344 def cherche_args(self,texte):
346 Cherche au début de texte une liste d'arguments entre parenthèses
356 # on a atteint la fin de texte sans avoir trouvé la parenthèse fermante --> erreur
357 raise InterpreteurException,"Manque parenthèse fermante dans %s" %texte
362 if (n+1 < len(texte)):
363 return texte[0:n+1],texte[n+1:]
365 # on a fini d'analyser le texte : reste = None
368 def cherche_operateur_unaire_old(self,texte):
370 Cherche dans texte un operateur unaire
373 texte = string.strip(texte)
374 for oper in self.l_fonctions_unaires:
375 index = string.find(texte,oper)
378 zz,reste = string.split(texte,oper,1)
381 #print 'on a trouvé :',txt
385 args,reste = self.cherche_args(texte)
386 except InterpreteurException,e:
387 raise InterpreteurException,str(e)
389 # opérateur unaire sans arguments
390 raise InterpreteurException,'opérateur unaire %s sans arguments' %operateur
392 #operateur = operateur+args
393 args = self.split_args(txt,args,self.d_fonctions_unaires[operateur])
394 formule_operateur = (txt,'',self.t_formule[2],args)
395 operateur = Interpreteur_Formule(formule = formule_operateur,
396 constantes = self.new_constantes,
397 fonctions_unaires = self.new_fonctions_unaires,
399 operateur.interprete_formule()
401 return operateur,reste
403 # aucun opérateur unaire trouvé
406 def cherche_operateur_unaire(self,texte):
408 Cherche dans texte un operateur unaire
411 texte = string.strip(texte)
412 m = pat_constante.match(texte)
414 # on a trouvé un identificateur en début de texte
415 # il faut encore vérifier que l'on a bien à faire à un appel de fonction ...
416 l_groups = m.groups()
418 identificateur = string.strip(l_groups[1])
421 args,reste = self.cherche_args(reste)
422 except InterpreteurException,e:
423 raise InterpreteurException,str(e)
425 # opérateur unaire sans arguments
426 # en principe on ne doit jamais être dans ce cas car il est déjà trappé par cherche_constante ...
427 raise InterpreteurException,'Fonction %s sans arguments !' %identificateur
429 # il faut encore vérifier que l'on a bien à faire à une fonction connue
430 if identificateur not in self.l_fonctions_unaires:
431 raise InterpreteurException,'Fonction %s inconnue dans %s !' %(identificateur,texte)
432 args = self.split_args(identificateur,args,self.d_fonctions_unaires[identificateur])
433 formule_operateur = (sgn+identificateur,'',self.t_formule[2],args)
434 operateur = Interpreteur_Formule(formule = formule_operateur,
435 constantes = self.new_constantes,
436 fonctions = self.new_fonctions_unaires,
438 operateur.interprete_formule()
440 return operateur,reste
441 elif texte[0] == '-':
442 # Il faut pouvoir trapper les expressions du type exp(-(x+1)) ...
444 args,reste = self.cherche_args(texte[1:])
445 except InterpreteurException,e:
446 raise InterpreteurException,str(e)
448 # Il ne s'agit pas de '-' comme opérateur unaire --> on retourne None
452 args = self.split_args(identificateur,args,self.d_fonctions_unaires[identificateur])
453 formule_operateur = (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
464 def cherche_operateur_binaire(self,texte):
466 Cherche dans texte un operateur unaire
469 texte = string.strip(texte)
470 for oper in self.l_fonctions_binaires:
471 index = string.find(texte,oper)
472 #if index != -1 : print 'on a trouvé %s dans %s en %d' %(oper,texte,index)
475 zz,reste = string.split(texte,oper,1)
480 # aucun opérateur unaire trouvé
483 def cherche_expression_entre_parentheses(self,texte):
485 Cherche en début de texte une expression entre parentheses
487 args,reste = self.cherche_args(string.strip(texte))
491 # on a trouvé une expression entre parenthèses en début de texte
492 # --> on retourne un objet Interpreteur_Formule
493 formule_operateur = ('','',self.t_formule[2],args[1:-1])
494 operateur = Interpreteur_Formule(formule = formule_operateur,
495 constantes = self.new_constantes,
496 fonctions = self.new_fonctions_unaires,
498 operateur.interprete_formule()
500 return operateur,reste
502 def split_args(self,nom_fonction,args,nb_args):
504 Tente de partager args en nb_args éléments
505 Retourne une liste de chaînes de caractères (liste de longueur nb_args)
507 args = args[1:-1] # on enlève les parenthèses ouvrante et fermante
508 if nb_args == 1 : return args
509 l_args = string.split(args,',')
510 if len(l_args) != nb_args:
511 raise InterpreteurException,"La fonction %s requiert %d arguments : %d fourni(s)" %(nom_fonction,nb_args,len(l_args))
515 def get_type(self,texte):
517 Retourne le type de l'objet défini dans texte, à savoir:
523 texte = string.strip(texte)
525 return 'constante',texte
528 nom_oper,args = string.split(texte,'(',1)
529 return 'fonction',nom_oper
531 def get_nb_args(self,formule):
533 Retourne le nombre d'arguments dans la définition de formule (sous forme de tuple)
535 args = formule[2][1:-1] # on enlève les parenthèses ouvrante et fermante
536 l_args = string.split(args,',')
539 if __name__ == '__main__':
540 constantes = ['FREQ3','AMOR1']
541 fonctions_unaires=[('ACC','REEL','(REEL:x)','''bidon'''),]
542 f1 = ('f1','REEL','(REEL:x)','''SIN(x)+3*x''')
543 f2 = ('f2','REEL','(REEL:x)','''ATAN(x+3)+3*x''')
544 f3 = ('f3','REEL','(REEL:INST)','''ACC(INST,FREQ3,AMOR1)''')
545 f4 = ('f4','REEL','(REEL:INST)','''ACC(INST,FREQ2,AMOR1)''')
546 f5 = ('f5','REEL','(REEL:INST,REEL:Y)','''ACC(INST,FREQ3,AMOR1)+Y*INST''')
547 f6 = ('f6','REEL','(REEL:x)','''(x+ 3)/ 35.698''')
548 f7 = ('f7','REEL','(REEL:x)','''(x+ 3)/ 35.698E-10''')
549 f8 = ('f8','REEL','(REEL:x)','''(x+ 3)/ 35.698E''')
550 f9 = ('f9','REEL','(REEL:INSTA,REEl:INSTB)','''2.*SIN((PI/4)+((INSTA-INSTB)/2.))* COS((PI/4)-((INSTA+INSTB)/2.))''')
551 f10 = ('f10','REEL','(REEL:X)','''EXP(-(X+1))''')
552 for formule in (f1,f2,f3,f4,f5,f6,f7,f8,f9,f10):
553 i = Interpreteur_Formule(formule = formule,
554 constantes = constantes,
555 fonctions = fonctions_unaires)
557 print '\nformule %s = %s' %(str(formule),txt)
559 print "\n\tPas d'erreur !"