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 text_arg = string.replace(text_arg,' ','')
151 self.l_operateurs.append(self.split_operateurs(text_arg))
152 except InterpreteurException,e:
153 self.cr.fatal(str(e))
155 def modify_listes(self):
157 Modifie la liste des constantes en lui ajoutant le nom des paramètres
158 de la fonction à interpréter
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 ...
164 args = args[1:-1] # on enlève les parenthèses ouvrante et fermante
165 l_args = string.split(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 :
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()
184 def ordonne_listes(self):
186 Ordonne les listes de fonctions unaires et binaires
188 self.l_fonctions_binaires.sort(cmp_function)
189 self.l_fonctions_unaires.sort(cmp_function)
190 self.l_constantes.sort(cmp_function)
193 def split_operateurs(self,texte):
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)
199 texte = string.strip(texte)
200 # on recherche un nombre en début de texte
202 oper,reste = self.cherche_nombre(texte)
203 except InterpreteurException,e:
204 raise InterpreteurException,str(e)
206 # on recherche une constante en début de texte
208 oper,reste = self.cherche_constante(texte)
209 except InterpreteurException,e:
210 raise InterpreteurException,str(e)
212 # on recherche une expression entre parenthèses...
214 oper,reste = self.cherche_expression_entre_parentheses(texte)
215 except InterpreteurException,e:
216 raise InterpreteurException,str(e)
218 # on recherche le début d'un opérateur unaire en début de texte
220 oper,reste = self.cherche_operateur_unaire(texte)
221 except InterpreteurException,e:
222 raise InterpreteurException,str(e)
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)
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)
236 texte = string.strip(reste)
237 oper,reste = self.cherche_operateur_binaire(texte)
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]
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 ...
246 l_op = self.split_operateurs(reste)
247 except InterpreteurException,e:
248 raise InterpreteurException,str(e)
249 l_operateurs.extend(l_op)
252 # on a fini d'analyser texte
255 def cherche_nombre(self,texte):
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
261 texte = string.strip(texte)
262 m = pat_number_complet.match(texte)
264 # on a trouvé un nombre avec exposant
265 l_groups = m.groups()
273 return nombre,l_groups[4]
275 m = pat_number.match(texte)
277 # on a trouvé un nombre sans exposant
278 l_groups = m.groups()
284 # il faut vérifier si ce nombre n'est pas suivi d'un exposant incomplet ...
285 reste = string.strip(l_groups[3])
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
291 return nombre,l_groups[3]
293 # on n'a pas trouvé de nombre
296 def cherche_constante_old(self,texte):
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
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)
309 zz,reste = string.split(texte,cte,1)
314 # aucune constante trouvée
317 def cherche_constante(self,texte):
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
324 texte = string.strip(texte)
325 m = pat_constante.match(texte)
327 # on a trouvé un identificateur en début de texte
328 l_groups = m.groups()
330 identificateur = string.strip(l_groups[1])
332 # il faut vérifier qu'il ne s'agit pas d'un appel à une fonction
335 # --> appel de fonction
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)
341 return sgn+identificateur,reste
343 # aucune constante trouvée
346 def cherche_args(self,texte):
348 Cherche au début de texte une liste d'arguments entre parenthèses
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
364 if (n+1 < len(texte)):
365 return texte[0:n+1],texte[n+1:]
367 # on a fini d'analyser le texte : reste = None
370 def cherche_operateur_unaire_old(self,texte):
372 Cherche dans texte un operateur unaire
375 texte = string.strip(texte)
376 for oper in self.l_fonctions_unaires:
377 index = string.find(texte,oper)
380 zz,reste = string.split(texte,oper,1)
383 #print 'on a trouvé :',txt
387 args,reste = self.cherche_args(texte)
388 except InterpreteurException,e:
389 raise InterpreteurException,str(e)
391 # opérateur unaire sans arguments
392 raise InterpreteurException,'opérateur unaire %s sans arguments' %operateur
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,
401 operateur.interprete_formule()
403 return operateur,reste
405 # aucun opérateur unaire trouvé
408 def cherche_operateur_unaire(self,texte):
410 Cherche dans texte un operateur unaire
413 texte = string.strip(texte)
414 m = pat_constante.match(texte)
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()
420 identificateur = string.strip(l_groups[1])
423 args,reste = self.cherche_args(reste)
424 except InterpreteurException,e:
425 raise InterpreteurException,str(e)
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
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,
440 operateur.interprete_formule()
442 return operateur,reste
443 elif texte[0] == '-':
444 # Il faut pouvoir trapper les expressions du type exp(-(x+1)) ...
446 args,reste = self.cherche_args(texte[1:])
447 except InterpreteurException,e:
448 raise InterpreteurException,str(e)
450 # Il ne s'agit pas de '-' comme opérateur unaire --> on retourne None
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,
460 operateur.interprete_formule()
462 return operateur,reste
466 def cherche_operateur_binaire(self,texte):
468 Cherche dans texte un operateur unaire
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)
477 zz,reste = string.split(texte,oper,1)
482 # aucun opérateur unaire trouvé
485 def cherche_expression_entre_parentheses(self,texte):
487 Cherche en début de texte une expression entre parentheses
489 args,reste = self.cherche_args(string.strip(texte))
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,
500 operateur.interprete_formule()
502 return operateur,reste
504 def split_args(self,nom_fonction,args,nb_args):
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)
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))
517 def get_type(self,texte):
519 Retourne le type de l'objet défini dans texte, à savoir:
525 texte = string.strip(texte)
527 return 'constante',texte
530 nom_oper,args = string.split(texte,'(',1)
531 return 'fonction',nom_oper
533 def get_nb_args(self,formule):
535 Retourne le nombre d'arguments dans la définition de formule (sous forme de tuple)
537 args = formule[2][1:-1] # on enlève les parenthèses ouvrante et fermante
538 l_args = string.split(args,',')
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)
559 print '\nformule %s = %s' %(str(formule),txt)
561 print "\n\tPas d'erreur !"