1 # -*- coding: utf-8 -*-
2 # CONFIGURATION MANAGEMENT OF EDF VERSION
3 # ======================================================================
4 # COPYRIGHT (C) 1991 - 2002 EDF R&D WWW.CODE-ASTER.ORG
5 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
6 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
7 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
8 # (AT YOUR OPTION) ANY LATER VERSION.
10 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
11 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
12 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
13 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
15 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
16 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
17 # 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
20 # ======================================================================
21 import string,re,sys,exceptions,types
23 from Noyau.N_CR import CR
25 def group(*choices): return '(' + string.join(choices, '|') + ')'
26 def any(*choices): return apply(group, choices) + '*'
27 def maybe(*choices): return apply(group, choices) + '?'
29 Intnumber = r'[1-9]\d*'
30 Exponent = r'[eEdD][-+]?\d+'
31 Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent)
32 Expfloat = r'[1-9]\d*' + Exponent
33 Floatnumber = group(Pointfloat, Expfloat)
35 pat_number = re.compile(r'^([+-]?)([0-9]+)(\.\d*)?(.*)')
36 pat_number_complet = re.compile(r'^([+-]?)([0-9]+)(\.\d*)?([eEdD][+-]?\d+)(.*)')
37 pat_constante = re.compile(r'^([+-]?)([a-zA-Z][a-zA-Z_0-9]*\s*)(.*)')
39 def cmp_function(arg1,arg2):
41 Fonction de comparaison permettant de classer les listes de
42 fonctions unaires et binaires selon la longueur de leurs arguments
43 On classe les arguments les plus longs en premier
45 if len(arg1) > len(arg2):
47 elif len(arg1) == len(arg2):
52 class InterpreteurException(exceptions.Exception):
54 Classe servant à définir les exceptions levées par l'interpréteur de formule
56 def __init__(self,args=None):
62 class Interpreteur_Formule:
64 Cette classe sert à construire un interpréteur de formules Aster
66 l_fonctions_binaires = ['+','-','*','/','**','=','MOD','MIN','MAX','ATAN2']
67 l_fonctions_unaires = ['+','-','INT','REAL','AIMAG','ABS','SQRT','EXP','LOG',
68 'LOG10','SIN','COS','TAN','ASIN','ACOS','ATAN','SINH',
69 'COSH','TANH','HEAVYSID']
70 l_constantes = ['PI','RD_RG','DG_RD']
72 def __init__(self,formule=None,constantes=[],fonctions=[],parent=None):
74 Constructeur d'interpréteurs de formule Aster
75 - formule = tuple (nom,type,arguments,corps)
76 - constantes = liste des noms de constantes externes
77 - fonctions_unaires = dictionnaire {nom_fonction externe : nb arguments de cette fonction}
79 self.new_constantes = constantes
80 self.new_fonctions_unaires = fonctions
82 self.l_operateurs = []
86 self.set_formule(formule)
88 self.parent.enregistre(self)
90 def set_formule(self,formule):
92 Stocke formule (tuple) dans l'attribut t_formule
95 if type(formule) != types.TupleType:
96 raise InterpreteurException,"La formule passée à l'interpréteur doit être sous forme de tuple"
97 self.t_formule = formule
100 self.ordonne_listes()
104 Initialise le cr,cad valorise les chaînes debut et fin
106 nom = self.t_formule[0]
108 if nom[0] in ('+','-') : nom = nom[1:]
109 self.cr.debut = "Début Fonction %s" %nom
110 self.cr.fin = "Fin Fonction %s" %nom
114 Retourne une liste de chaînes de caractères représentant la formule
117 l_txt.append(self.t_formule[0])
118 for oper in self.l_operateurs:
119 # oper est ici une liste décrivant oper
122 txt.append(str(elem))
126 def report(self,decalage=1):
128 Retourne le rapport de FORMULE
130 txt = self.cr.report()
133 def enregistre(self,fils):
135 Enregistre un opérateur fils dans la liste des children
137 self.l_children.append(fils)
142 Booléenne qui retourne 1 si la formule est valide, 0 sinon
145 self.l_operateurs = []
146 self.cr.purge() # on vide le cr
147 self.init_cr() # on initialise le cr
148 self.interprete_formule()
149 return self.cr.estvide()
151 def interprete_formule(self):
153 Réalise l'interprétation du corps de la formule
155 texte = self.t_formule[3]
\r
156 if not texte : return
157 if type(texte) != types.ListType:
159 for text_arg in texte:
160 text_arg = string.replace(text_arg,'\n','')
162 text_arg = string.replace(text_arg,' ','')
164 self.l_operateurs.append(self.split_operateurs(text_arg))
165 except InterpreteurException,e:
166 self.cr.fatal(str(e))
168 def modify_listes(self):
170 Modifie la liste des constantes en lui ajoutant le nom des paramètres
171 de la fonction à interpréter
173 args = self.t_formule[2]
174 # l'interpréteur de formule sert aussi à évaluer les EVAL
175 # dans ce cas il n'y a pas d'arguments puisque pas de fonction ...
177 args = args[1:-1] # on enlève les parenthèses ouvrante et fermante
178 l_args = string.split(args,',')
180 typ,nom = string.split(arg,':')
181 nom = string.strip(nom)
182 self.l_constantes.append(nom)
183 # on considère que les fonctions unaires de base sont toutes à un seul argument :
185 self.d_fonctions_unaires = {}
186 for fct in self.l_fonctions_unaires:
187 self.d_fonctions_unaires[fct]=1
188 # on ajoute les constantes externes
189 for cte in self.new_constantes:
190 self.l_constantes.append(cte)
191 # on ajoute les fonctions unaires externes au dictionnaire des fonctions unaires
192 for new_fonc in self.new_fonctions_unaires:
193 self.d_fonctions_unaires[new_fonc[0]] = self.get_nb_args(new_fonc)
194 #self.d_fonctions_unaires.update(self.new_fonctions_unaires)
195 self.l_fonctions_unaires = self.d_fonctions_unaires.keys()
197 def ordonne_listes(self):
199 Ordonne les listes de fonctions unaires et binaires
201 self.l_fonctions_binaires.sort(cmp_function)
202 self.l_fonctions_unaires.sort(cmp_function)
203 self.l_constantes.sort(cmp_function)
206 def split_operateurs(self,texte):
208 Splite le texte passé en argument en opérateurs plus élémentaires.
209 N'analyse pas l'intérieur des opérateurs (ne fait qu'une passe)
212 texte = string.strip(texte)
213 # on recherche un nombre en début de texte
215 oper,reste = self.cherche_nombre(texte)
216 except InterpreteurException,e:
217 raise InterpreteurException,str(e)
219 # on recherche une constante en début de texte
221 oper,reste = self.cherche_constante(texte)
222 except InterpreteurException,e:
223 raise InterpreteurException,str(e)
225 # on recherche une expression entre parenthèses...
227 oper,reste = self.cherche_expression_entre_parentheses(texte)
228 except InterpreteurException,e:
229 raise InterpreteurException,str(e)
231 # on recherche le début d'un opérateur unaire en début de texte
233 oper,reste = self.cherche_operateur_unaire(texte)
234 except InterpreteurException,e:
235 raise InterpreteurException,str(e)
237 type_objet,nom_objet = self.get_type(texte)
238 if type_objet == 'constante':
239 raise InterpreteurException, "Constante %s inconnue" %nom_objet
240 elif type_objet == 'fonction':
241 raise InterpreteurException, "Fonction %s inconnue dans %s" %(nom_objet,texte)
243 raise InterpreteurException, "Impossible d'interpréter : %s" %texte
244 # on a trouvé un opérateur (nombre, constante ou unaire)
245 # il faut encore vérifier que l'on est en fin de texte ou qu'il est bien suivi
246 # d'un opérateur binaire
247 l_operateurs.append(oper)
249 texte = string.strip(reste)
250 oper,reste = self.cherche_operateur_binaire(texte)
252 # on a un reste et pas d'opérateur binaire --> erreur
253 raise InterpreteurException,"L'opérateur %s doit être suivi d'un opérateur binaire" %l_operateurs[-1]
255 # on a bien trouvé un opérateur binaire:
256 l_operateurs.append(oper)
257 # il faut recommencer l'analyse du reste par split_operateurs ...
259 l_op = self.split_operateurs(reste)
260 except InterpreteurException,e:
261 raise InterpreteurException,str(e)
262 l_operateurs.extend(l_op)
265 # on a fini d'analyser texte
268 def cherche_nombre(self,texte):
270 Cherche un nombre en début de texte
271 Retourne ce nombre et le reste ou None et le texte initial
272 Peut lever une InterpreteurException dans le cas où le nombre n'est pas valide
274 texte = string.strip(texte)
275 m = pat_number_complet.match(texte)
277 # on a trouvé un nombre avec exposant
278 l_groups = m.groups()
286 return nombre,l_groups[4]
288 m = pat_number.match(texte)
290 # on a trouvé un nombre sans exposant
291 l_groups = m.groups()
297 # il faut vérifier si ce nombre n'est pas suivi d'un exposant incomplet ...
298 reste = string.strip(l_groups[3])
300 return nombre,l_groups[3]
301 if reste[0] in ('e','E','d','D') :
302 raise InterpreteurException,"La syntaxe de l'exposant de %s est erronée " %nb
304 return nombre,l_groups[3]
306 # on n'a pas trouvé de nombre
309 def cherche_constante_old(self,texte):
311 Recherche une constante en début de texte parmi la liste des constantes.
312 Retourne le texte représentant la constante et le reste du texte ou
313 Retourne None,texte si aucune constante trouvée
316 texte = string.strip(texte)
317 for cte in self.l_constantes:
318 index = string.find(texte,cte)
319 #if index == 0 : print 'on a trouvé %s dans %s en %d' %(cte,texte,index)
322 zz,reste = string.split(texte,cte,1)
327 # aucune constante trouvée
330 def cherche_constante(self,texte):
332 Recherche une constante en début de texte parmi la liste des constantes.
333 Retourne le texte représentant la constante et le reste du texte ou
334 Retourne None,texte si aucune constante trouvée
337 texte = string.strip(texte)
338 m = pat_constante.match(texte)
340 # on a trouvé un identificateur en début de texte
341 l_groups = m.groups()
343 identificateur = string.strip(l_groups[1])
345 # il faut vérifier qu'il ne s'agit pas d'un appel à une fonction
348 # --> appel de fonction
350 # il faut encore vérifier qu'elle est bien dans la liste des constantes...
351 if identificateur not in self.l_constantes :
352 raise InterpreteurException,"La constante %s est inconnue dans %s" %(identificateur,texte)
354 return sgn+identificateur,reste
356 # aucune constante trouvée
359 def cherche_args(self,texte):
361 Cherche au début de texte une liste d'arguments entre parenthèses
371 # on a atteint la fin de texte sans avoir trouvé la parenthèse fermante --> erreur
372 raise InterpreteurException,"Manque parenthèse fermante dans %s" %texte
377 if (n+1 < len(texte)):
378 return texte[0:n+1],texte[n+1:]
380 # on a fini d'analyser le texte : reste = None
383 def cherche_operateur_unaire_old(self,texte):
385 Cherche dans texte un operateur unaire
388 texte = string.strip(texte)
389 for oper in self.l_fonctions_unaires:
390 index = string.find(texte,oper)
393 zz,reste = string.split(texte,oper,1)
396 #print 'on a trouvé :',txt
400 args,reste = self.cherche_args(texte)
401 except InterpreteurException,e:
402 raise InterpreteurException,str(e)
404 # opérateur unaire sans arguments
405 raise InterpreteurException,'opérateur unaire %s sans arguments' %operateur
407 #operateur = operateur+args
408 args = self.split_args(txt,args,self.d_fonctions_unaires[operateur])
409 formule_operateur = (txt,'',self.t_formule[2],args)
410 operateur = Interpreteur_Formule(formule = formule_operateur,
411 constantes = self.new_constantes,
412 fonctions_unaires = self.new_fonctions_unaires,
414 operateur.interprete_formule()
416 return operateur,reste
418 # aucun opérateur unaire trouvé
421 def cherche_operateur_unaire(self,texte):
423 Cherche dans texte un operateur unaire
426 texte = string.strip(texte)
427 m = pat_constante.match(texte)
429 # on a trouvé un identificateur en début de texte
430 # il faut encore vérifier que l'on a bien à faire à un appel de fonction ...
431 l_groups = m.groups()
433 identificateur = string.strip(l_groups[1])
436 args,reste = self.cherche_args(reste)
437 except InterpreteurException,e:
438 raise InterpreteurException,str(e)
440 # opérateur unaire sans arguments
441 # en principe on ne doit jamais être dans ce cas car il est déjà trappé par cherche_constante ...
442 raise InterpreteurException,'Fonction %s sans arguments !' %identificateur
444 # il faut encore vérifier que l'on a bien à faire à une fonction connue
445 if identificateur not in self.l_fonctions_unaires:
446 raise InterpreteurException,'Fonction %s inconnue dans %s !' %(identificateur,texte)
447 args = self.split_args(identificateur,args,self.d_fonctions_unaires[identificateur])
448 formule_operateur = (sgn+identificateur,'',self.t_formule[2],args)
449 operateur = Interpreteur_Formule(formule = formule_operateur,
450 constantes = self.new_constantes,
451 fonctions = self.new_fonctions_unaires,
453 operateur.interprete_formule()
455 return operateur,reste
456 elif texte[0] == '-':
457 # Il faut pouvoir trapper les expressions du type exp(-(x+1)) ...
459 args,reste = self.cherche_args(texte[1:])
460 except InterpreteurException,e:
461 raise InterpreteurException,str(e)
463 # Il ne s'agit pas de '-' comme opérateur unaire --> on retourne None
467 args = self.split_args(identificateur,args,self.d_fonctions_unaires[identificateur])
468 formule_operateur = (identificateur,'',self.t_formule[2],args)
469 operateur = Interpreteur_Formule(formule = formule_operateur,
470 constantes = self.new_constantes,
471 fonctions = self.new_fonctions_unaires,
473 operateur.interprete_formule()
475 return operateur,reste
479 def cherche_operateur_binaire(self,texte):
481 Cherche dans texte un operateur unaire
484 texte = string.strip(texte)
485 for oper in self.l_fonctions_binaires:
486 index = string.find(texte,oper)
487 #if index != -1 : print 'on a trouvé %s dans %s en %d' %(oper,texte,index)
490 zz,reste = string.split(texte,oper,1)
495 # aucun opérateur unaire trouvé
498 def cherche_expression_entre_parentheses(self,texte):
500 Cherche en début de texte une expression entre parentheses
502 args,reste = self.cherche_args(string.strip(texte))
506 # on a trouvé une expression entre parenthèses en début de texte
507 # --> on retourne un objet Interpreteur_Formule
508 formule_operateur = ('','',self.t_formule[2],args[1:-1])
509 operateur = Interpreteur_Formule(formule = formule_operateur,
510 constantes = self.new_constantes,
511 fonctions = self.new_fonctions_unaires,
513 operateur.interprete_formule()
515 return operateur,reste
517 def split_args(self,nom_fonction,args,nb_args):
519 Tente de partager args en nb_args éléments
520 Retourne une liste de chaînes de caractères (liste de longueur nb_args)
522 args = args[1:-1] # on enlève les parenthèses ouvrante et fermante
523 if nb_args == 1 : return args
524 l_args = string.split(args,',')
525 if len(l_args) != nb_args:
526 raise InterpreteurException,"La fonction %s requiert %d arguments : %d fourni(s)" %(nom_fonction,nb_args,len(l_args))
530 def get_type(self,texte):
532 Retourne le type de l'objet défini dans texte, à savoir:
538 texte = string.strip(texte)
540 return 'constante',texte
543 nom_oper,args = string.split(texte,'(',1)
544 return 'fonction',nom_oper
546 def get_nb_args(self,formule):
548 Retourne le nombre d'arguments dans la définition de formule (sous forme de tuple)
550 args = formule[2][1:-1] # on enlève les parenthèses ouvrante et fermante
551 l_args = string.split(args,',')
554 if __name__ == '__main__':
555 constantes = ['FREQ3','AMOR1']
556 fonctions_unaires=[('ACC','REEL','(REEL:x)','''bidon'''),]
557 f1 = ('f1','REEL','(REEL:x)','''SIN(x)+3*x''')
558 f2 = ('f2','REEL','(REEL:x)','''ATAN(x+3)+3*x''')
559 f3 = ('f3','REEL','(REEL:INST)','''ACC(INST,FREQ3,AMOR1)''')
560 f4 = ('f4','REEL','(REEL:INST)','''ACC(INST,FREQ2,AMOR1)''')
561 f5 = ('f5','REEL','(REEL:INST,REEL:Y)','''ACC(INST,FREQ3,AMOR1)+Y*INST''')
562 f6 = ('f6','REEL','(REEL:x)','''(x+ 3)/ 35.698''')
563 f7 = ('f7','REEL','(REEL:x)','''(x+ 3)/ 35.698E-10''')
564 f8 = ('f8','REEL','(REEL:x)','''(x+ 3)/ 35.698E''')
565 f9 = ('f9','REEL','(REEL:INSTA,REEl:INSTB)','''2.*SIN((PI/4)+((INSTA-INSTB)/2.))* COS((PI/4)-((INSTA+INSTB)/2.))''')
566 f10 = ('f10','REEL','(REEL:X)','''EXP(-(X+1))''')
567 for formule in (f1,f2,f3,f4,f5,f6,f7,f8,f9,f10):
568 i = Interpreteur_Formule(formule = formule,
569 constantes = constantes,
570 fonctions = fonctions_unaires)
572 print '\nformule %s = %s' %(str(formule),txt)
574 print "\n\tPas d'erreur !"