1 # CONFIGURATION MANAGEMENT OF EDF VERSION
2 # ======================================================================
3 # COPYRIGHT (C) 1991 - 2002 EDF R&D WWW.CODE-ASTER.ORG
4 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
5 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
6 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
7 # (AT YOUR OPTION) ANY LATER VERSION.
9 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
10 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
11 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
12 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
14 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
15 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
16 # 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
19 # ======================================================================
20 import string,re,sys,exceptions,types
22 from Noyau.N_CR import CR
24 def group(*choices): return '(' + string.join(choices, '|') + ')'
25 def any(*choices): return apply(group, choices) + '*'
26 def maybe(*choices): return apply(group, choices) + '?'
28 Intnumber = r'[1-9]\d*'
29 Exponent = r'[eEdD][-+]?\d+'
30 Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent)
31 Expfloat = r'[1-9]\d*' + Exponent
32 Floatnumber = group(Pointfloat, Expfloat)
34 pat_number = re.compile(r'^([+-]?)([0-9]+)(\.\d*)?(.*)')
35 pat_number_complet = re.compile(r'^([+-]?)([0-9]+)(\.\d*)?([eEdD][+-]?\d+)(.*)')
36 pat_constante = re.compile(r'^([+-]?)([a-zA-Z][a-zA-Z_0-9]*\s*)(.*)')
38 def cmp_function(arg1,arg2):
40 Fonction de comparaison permettant de classer les listes de
41 fonctions unaires et binaires selon la longueur de leurs arguments
42 On classe les arguments les plus longs en premier
44 if len(arg1) > len(arg2):
46 elif len(arg1) == len(arg2):
51 class InterpreteurException(exceptions.Exception):
53 Classe servant à définir les exceptions levées par l'interpréteur de formule
55 def __init__(self,args=None):
61 class Interpreteur_Formule:
63 Cette classe sert à construire un interpréteur de formules Aster
65 l_fonctions_binaires = ['+','-','*','/','**','=','MOD','MIN','MAX','ATAN2']
66 l_fonctions_unaires = ['+','-','INT','REAL','AIMAG','ABS','SQRT','EXP','LOG',
67 'LOG10','SIN','COS','TAN','ASIN','ACOS','ATAN','SINH',
68 'COSH','TANH','HEAVYSID']
69 l_constantes = ['PI','RD_RG','DG_RD']
71 def __init__(self,formule=None,constantes=[],fonctions=[],parent=None):
73 Constructeur d'interpréteurs de formule Aster
74 - formule = tuple (nom,type,arguments,corps)
75 - constantes = liste des noms de constantes externes
76 - fonctions_unaires = dictionnaire {nom_fonction externe : nb arguments de cette fonction}
78 self.new_constantes = constantes
79 self.new_fonctions_unaires = fonctions
81 self.l_operateurs = []
85 self.set_formule(formule)
87 self.parent.enregistre(self)
89 def set_formule(self,formule):
91 Stocke formule (tuple) dans l'attribut t_formule
94 if type(formule) != types.TupleType:
95 raise InterpreteurException,"La formule passée à l'interpréteur doit être sous forme de tuple"
96 self.t_formule = formule
103 Initialise le cr,cad valorise les chaînes debut et fin
105 nom = self.t_formule[0]
107 if nom[0] in ('+','-') : nom = nom[1:]
108 self.cr.debut = "Début Fonction %s" %nom
109 self.cr.fin = "Fin Fonction %s" %nom
113 Retourne une liste de chaînes de caractères représentant la formule
116 l_txt.append(self.t_formule[0])
117 for oper in self.l_operateurs:
118 # oper est ici une liste décrivant oper
121 txt.append(str(elem))
125 def report(self,decalage=1):
127 Retourne le rapport de FORMULE
129 txt = self.cr.report()
132 def enregistre(self,fils):
134 Enregistre un opérateur fils dans la liste des children
136 self.l_children.append(fils)
141 Booléenne qui retourne 1 si la formule est valide, 0 sinon
144 self.l_operateurs = []
145 self.cr.purge() # on vide le cr
146 self.init_cr() # on initialise le cr
147 self.interprete_formule()
148 return self.cr.estvide()
150 def interprete_formule(self):
152 Réalise l'interprétation du corps de la formule
154 texte = self.t_formule[3]
\r
155 if not texte : return
156 if type(texte) != types.ListType:
158 for text_arg in texte:
159 text_arg = string.replace(text_arg,'\n','')
161 text_arg = string.replace(text_arg,' ','')
163 self.l_operateurs.append(self.split_operateurs(text_arg))
164 except InterpreteurException,e:
165 self.cr.fatal(str(e))
167 def modify_listes(self):
169 Modifie la liste des constantes en lui ajoutant le nom des paramètres
170 de la fonction à interpréter
172 args = self.t_formule[2]
173 # l'interpréteur de formule sert aussi à évaluer les EVAL
174 # dans ce cas il n'y a pas d'arguments puisque pas de fonction ...
176 args = args[1:-1] # on enlève les parenthèses ouvrante et fermante
177 l_args = string.split(args,',')
179 typ,nom = string.split(arg,':')
180 nom = string.strip(nom)
181 self.l_constantes.append(nom)
182 # on considère que les fonctions unaires de base sont toutes à un seul argument :
184 self.d_fonctions_unaires = {}
185 for fct in self.l_fonctions_unaires:
186 self.d_fonctions_unaires[fct]=1
187 # on ajoute les constantes externes
188 for cte in self.new_constantes:
189 self.l_constantes.append(cte)
190 # on ajoute les fonctions unaires externes au dictionnaire des fonctions unaires
191 for new_fonc in self.new_fonctions_unaires:
192 self.d_fonctions_unaires[new_fonc[0]] = self.get_nb_args(new_fonc)
193 #self.d_fonctions_unaires.update(self.new_fonctions_unaires)
194 self.l_fonctions_unaires = self.d_fonctions_unaires.keys()
196 def ordonne_listes(self):
198 Ordonne les listes de fonctions unaires et binaires
200 self.l_fonctions_binaires.sort(cmp_function)
201 self.l_fonctions_unaires.sort(cmp_function)
202 self.l_constantes.sort(cmp_function)
205 def split_operateurs(self,texte):
207 Splite le texte passé en argument en opérateurs plus élémentaires.
208 N'analyse pas l'intérieur des opérateurs (ne fait qu'une passe)
211 texte = string.strip(texte)
212 # on recherche un nombre en début de texte
214 oper,reste = self.cherche_nombre(texte)
215 except InterpreteurException,e:
216 raise InterpreteurException,str(e)
218 # on recherche une constante en début de texte
220 oper,reste = self.cherche_constante(texte)
221 except InterpreteurException,e:
222 raise InterpreteurException,str(e)
224 # on recherche une expression entre parenthèses...
226 oper,reste = self.cherche_expression_entre_parentheses(texte)
227 except InterpreteurException,e:
228 raise InterpreteurException,str(e)
230 # on recherche le début d'un opérateur unaire en début de texte
232 oper,reste = self.cherche_operateur_unaire(texte)
233 except InterpreteurException,e:
234 raise InterpreteurException,str(e)
236 type_objet,nom_objet = self.get_type(texte)
237 if type_objet == 'constante':
238 raise InterpreteurException, "Constante %s inconnue" %nom_objet
239 elif type_objet == 'fonction':
240 raise InterpreteurException, "Fonction %s inconnue dans %s" %(nom_objet,texte)
242 raise InterpreteurException, "Impossible d'interpréter : %s" %texte
243 # on a trouvé un opérateur (nombre, constante ou unaire)
244 # il faut encore vérifier que l'on est en fin de texte ou qu'il est bien suivi
245 # d'un opérateur binaire
246 l_operateurs.append(oper)
248 texte = string.strip(reste)
249 oper,reste = self.cherche_operateur_binaire(texte)
251 # on a un reste et pas d'opérateur binaire --> erreur
252 raise InterpreteurException,"L'opérateur %s doit être suivi d'un opérateur binaire" %l_operateurs[-1]
254 # on a bien trouvé un opérateur binaire:
255 l_operateurs.append(oper)
256 # il faut recommencer l'analyse du reste par split_operateurs ...
258 l_op = self.split_operateurs(reste)
259 except InterpreteurException,e:
260 raise InterpreteurException,str(e)
261 l_operateurs.extend(l_op)
264 # on a fini d'analyser texte
267 def cherche_nombre(self,texte):
269 Cherche un nombre en début de texte
270 Retourne ce nombre et le reste ou None et le texte initial
271 Peut lever une InterpreteurException dans le cas où le nombre n'est pas valide
273 texte = string.strip(texte)
274 m = pat_number_complet.match(texte)
276 # on a trouvé un nombre avec exposant
277 l_groups = m.groups()
285 return nombre,l_groups[4]
287 m = pat_number.match(texte)
289 # on a trouvé un nombre sans exposant
290 l_groups = m.groups()
296 # il faut vérifier si ce nombre n'est pas suivi d'un exposant incomplet ...
297 reste = string.strip(l_groups[3])
299 return nombre,l_groups[3]
300 if reste[0] in ('e','E','d','D') :
301 raise InterpreteurException,"La syntaxe de l'exposant de %s est erronée " %nb
303 return nombre,l_groups[3]
305 # on n'a pas trouvé de nombre
308 def cherche_constante_old(self,texte):
310 Recherche une constante en début de texte parmi la liste des constantes.
311 Retourne le texte représentant la constante et le reste du texte ou
312 Retourne None,texte si aucune constante trouvée
315 texte = string.strip(texte)
316 for cte in self.l_constantes:
317 index = string.find(texte,cte)
318 #if index == 0 : print 'on a trouvé %s dans %s en %d' %(cte,texte,index)
321 zz,reste = string.split(texte,cte,1)
326 # aucune constante trouvée
329 def cherche_constante(self,texte):
331 Recherche une constante en début de texte parmi la liste des constantes.
332 Retourne le texte représentant la constante et le reste du texte ou
333 Retourne None,texte si aucune constante trouvée
336 texte = string.strip(texte)
337 m = pat_constante.match(texte)
339 # on a trouvé un identificateur en début de texte
340 l_groups = m.groups()
342 identificateur = string.strip(l_groups[1])
344 # il faut vérifier qu'il ne s'agit pas d'un appel à une fonction
347 # --> appel de fonction
349 # il faut encore vérifier qu'elle est bien dans la liste des constantes...
350 if identificateur not in self.l_constantes :
351 raise InterpreteurException,"La constante %s est inconnue dans %s" %(identificateur,texte)
353 return sgn+identificateur,reste
355 # aucune constante trouvée
358 def cherche_args(self,texte):
360 Cherche au début de texte une liste d'arguments entre parenthèses
370 # on a atteint la fin de texte sans avoir trouvé la parenthèse fermante --> erreur
371 raise InterpreteurException,"Manque parenthèse fermante dans %s" %texte
376 if (n+1 < len(texte)):
377 return texte[0:n+1],texte[n+1:]
379 # on a fini d'analyser le texte : reste = None
382 def cherche_operateur_unaire_old(self,texte):
384 Cherche dans texte un operateur unaire
387 texte = string.strip(texte)
388 for oper in self.l_fonctions_unaires:
389 index = string.find(texte,oper)
392 zz,reste = string.split(texte,oper,1)
395 #print 'on a trouvé :',txt
399 args,reste = self.cherche_args(texte)
400 except InterpreteurException,e:
401 raise InterpreteurException,str(e)
403 # opérateur unaire sans arguments
404 raise InterpreteurException,'opérateur unaire %s sans arguments' %operateur
406 #operateur = operateur+args
407 args = self.split_args(txt,args,self.d_fonctions_unaires[operateur])
408 formule_operateur = (txt,'',self.t_formule[2],args)
409 operateur = Interpreteur_Formule(formule = formule_operateur,
410 constantes = self.new_constantes,
411 fonctions_unaires = self.new_fonctions_unaires,
413 operateur.interprete_formule()
415 return operateur,reste
417 # aucun opérateur unaire trouvé
420 def cherche_operateur_unaire(self,texte):
422 Cherche dans texte un operateur unaire
425 texte = string.strip(texte)
426 m = pat_constante.match(texte)
428 # on a trouvé un identificateur en début de texte
429 # il faut encore vérifier que l'on a bien à faire à un appel de fonction ...
430 l_groups = m.groups()
432 identificateur = string.strip(l_groups[1])
435 args,reste = self.cherche_args(reste)
436 except InterpreteurException,e:
437 raise InterpreteurException,str(e)
439 # opérateur unaire sans arguments
440 # en principe on ne doit jamais être dans ce cas car il est déjà trappé par cherche_constante ...
441 raise InterpreteurException,'Fonction %s sans arguments !' %identificateur
443 # il faut encore vérifier que l'on a bien à faire à une fonction connue
444 if identificateur not in self.l_fonctions_unaires:
445 raise InterpreteurException,'Fonction %s inconnue dans %s !' %(identificateur,texte)
446 args = self.split_args(identificateur,args,self.d_fonctions_unaires[identificateur])
447 formule_operateur = (sgn+identificateur,'',self.t_formule[2],args)
448 operateur = Interpreteur_Formule(formule = formule_operateur,
449 constantes = self.new_constantes,
450 fonctions = self.new_fonctions_unaires,
452 operateur.interprete_formule()
454 return operateur,reste
455 elif texte[0] == '-':
456 # Il faut pouvoir trapper les expressions du type exp(-(x+1)) ...
458 args,reste = self.cherche_args(texte[1:])
459 except InterpreteurException,e:
460 raise InterpreteurException,str(e)
462 # Il ne s'agit pas de '-' comme opérateur unaire --> on retourne None
466 args = self.split_args(identificateur,args,self.d_fonctions_unaires[identificateur])
467 formule_operateur = (identificateur,'',self.t_formule[2],args)
468 operateur = Interpreteur_Formule(formule = formule_operateur,
469 constantes = self.new_constantes,
470 fonctions = self.new_fonctions_unaires,
472 operateur.interprete_formule()
474 return operateur,reste
478 def cherche_operateur_binaire(self,texte):
480 Cherche dans texte un operateur unaire
483 texte = string.strip(texte)
484 for oper in self.l_fonctions_binaires:
485 index = string.find(texte,oper)
486 #if index != -1 : print 'on a trouvé %s dans %s en %d' %(oper,texte,index)
489 zz,reste = string.split(texte,oper,1)
494 # aucun opérateur unaire trouvé
497 def cherche_expression_entre_parentheses(self,texte):
499 Cherche en début de texte une expression entre parentheses
501 args,reste = self.cherche_args(string.strip(texte))
505 # on a trouvé une expression entre parenthèses en début de texte
506 # --> on retourne un objet Interpreteur_Formule
507 formule_operateur = ('','',self.t_formule[2],args[1:-1])
508 operateur = Interpreteur_Formule(formule = formule_operateur,
509 constantes = self.new_constantes,
510 fonctions = self.new_fonctions_unaires,
512 operateur.interprete_formule()
514 return operateur,reste
516 def split_args(self,nom_fonction,args,nb_args):
518 Tente de partager args en nb_args éléments
519 Retourne une liste de chaînes de caractères (liste de longueur nb_args)
521 args = args[1:-1] # on enlève les parenthèses ouvrante et fermante
522 if nb_args == 1 : return args
523 l_args = string.split(args,',')
524 if len(l_args) != nb_args:
525 raise InterpreteurException,"La fonction %s requiert %d arguments : %d fourni(s)" %(nom_fonction,nb_args,len(l_args))
529 def get_type(self,texte):
531 Retourne le type de l'objet défini dans texte, à savoir:
537 texte = string.strip(texte)
539 return 'constante',texte
542 nom_oper,args = string.split(texte,'(',1)
543 return 'fonction',nom_oper
545 def get_nb_args(self,formule):
547 Retourne le nombre d'arguments dans la définition de formule (sous forme de tuple)
549 args = formule[2][1:-1] # on enlève les parenthèses ouvrante et fermante
550 l_args = string.split(args,',')
553 if __name__ == '__main__':
554 constantes = ['FREQ3','AMOR1']
555 fonctions_unaires=[('ACC','REEL','(REEL:x)','''bidon'''),]
556 f1 = ('f1','REEL','(REEL:x)','''SIN(x)+3*x''')
557 f2 = ('f2','REEL','(REEL:x)','''ATAN(x+3)+3*x''')
558 f3 = ('f3','REEL','(REEL:INST)','''ACC(INST,FREQ3,AMOR1)''')
559 f4 = ('f4','REEL','(REEL:INST)','''ACC(INST,FREQ2,AMOR1)''')
560 f5 = ('f5','REEL','(REEL:INST,REEL:Y)','''ACC(INST,FREQ3,AMOR1)+Y*INST''')
561 f6 = ('f6','REEL','(REEL:x)','''(x+ 3)/ 35.698''')
562 f7 = ('f7','REEL','(REEL:x)','''(x+ 3)/ 35.698E-10''')
563 f8 = ('f8','REEL','(REEL:x)','''(x+ 3)/ 35.698E''')
564 f9 = ('f9','REEL','(REEL:INSTA,REEl:INSTB)','''2.*SIN((PI/4)+((INSTA-INSTB)/2.))* COS((PI/4)-((INSTA+INSTB)/2.))''')
565 f10 = ('f10','REEL','(REEL:X)','''EXP(-(X+1))''')
566 for formule in (f1,f2,f3,f4,f5,f6,f7,f8,f9,f10):
567 i = Interpreteur_Formule(formule = formule,
568 constantes = constantes,
569 fonctions = fonctions_unaires)
571 print '\nformule %s = %s' %(str(formule),txt)
573 print "\n\tPas d'erreur !"