+ else:
+ # Cas 5 : le concept est produit par une autre etape
+ # On ne fait rien
+ return
+
+ def issubstep(self, etape):
+ """
+ Cette methode retourne un entier indiquant si etape est une
+ sous etape de la macro self ou non
+ 1 = oui
+ 0 = non
+ """
+ if etape in self.etapes:
+ return 1
+ for etap in self.etapes:
+ if etap.issubstep(etape):
+ return 1
+ return 0
+
+ def register(self, etape):
+ """
+ Enregistrement de etape dans le contexte de la macro : liste etapes
+ et demande d enregistrement global aupres du JDC
+ """
+ self.etapes.append(etape)
+ self.index_etapes[etape] = len(self.etapes) - 1
+ idetape = self.jdc.g_register(etape)
+ return idetape
+
+ def reg_sd(self, sd):
+ """
+ Methode appelee dans l __init__ d un ASSD a sa creation pour
+ s enregistrer (reserve aux ASSD crees au sein d'une MACRO)
+ """
+ return self.jdc.o_register(sd)
+
+ def create_sdprod(self, etape, nomsd):
+ """
+ Cette methode doit fabriquer le concept produit retourne
+ par l'etape etape et le nommer.
+
+ Elle est appelee a l'initiative de l'etape
+ pendant le processus de construction de cette etape : methode __call__
+ de la classe CMD (OPER ou MACRO)
+ Ce travail est realise par le contexte superieur (etape.parent)
+ car dans certains cas, le concept ne doit pas etre fabrique mais
+ l'etape doit simplement utiliser un concept preexistant.
+ - Cas 1 : etape.reuse != None : le concept est reutilise
+ - Cas 2 : l'etape appartient a une macro qui a declare un concept
+ de sortie qui doit etre produit par cette etape.
+ """
+ if nomsd in self.Outputs:
+ # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le creer
+ # Il faut quand meme appeler la fonction sd_prod si elle existe.
+ # get_type_produit le fait et donne le type attendu par la commande
+ # pour verification ulterieure.
+ sdprod = etape.get_type_produit_brut()
+ sd = self.Outputs[nomsd]
+ # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
+ # Cette règle est normalement coherente avec les règles de
+ # verification des mots-cles
+ if not issubclass(sdprod, sd.__class__):
+ raise AsException(
+ "Le type du concept produit %s devrait etre une sur classe de %s" % (sd.__class__, sdprod))
+ # La propriete du concept est transferee a l'etape avec le type
+ # attendu par l'etape
+ etape.sd = sd
+ sd.etape = etape
+ if self.reuse == sd and etape.reuse != sd \
+ and getattr(sd, "executed", 0) == 1: # n'a pas ete pas detruit
+ raise AsException("Le concept '%s' est reentrant dans la macro-commande %s. "
+ "Il devrait donc l'etre dans %s (produit sous le nom '%s')."
+ % (sd.nom, self.nom, etape.nom, nomsd))
+ # On donne au concept le type produit par la sous commande.
+ # Le principe est le suivant : apres avoir verifie que le type deduit par la sous commande
+ # est bien coherent avec celui initialement affecte par la macro (voir ci dessus)
+ # on affecte au concept ce type car il peut etre plus precis
+ # (derive, en general)
+ sd.__class__ = sdprod
+ # On force egalement le nom stocke dans l'attribut sdnom : on lui donne le nom
+ # du concept associe a nomsd
+ etape.sdnom = sd.nom
+ # pour l'ajouter au contexte de la macro
+ self.g_context[sd.nom] = sd
+ elif etape.definition.reentrant != 'n' and etape.reuse != None:
+ # On est dans le cas d'une commande avec reutilisation d'un concept existant
+ # get_sd_prod fait le necessaire : verifications, associations, etc. mais ne cree
+ # pas un nouveau concept. Il retourne le concept reutilise
+ sd = etape.get_sd_prod()
+ # Dans le cas d'un concept nomme automatiquement : _xxx, __xxx,
+ # On force le nom stocke dans l'attribut sdnom de l'objet etape : on lui donne le nom
+ # du concept reutilise (sd ou etape.reuse c'est pareil)
+ # Ceci est indispensable pour eviter des erreurs lors des verifications des macros
+ # En effet une commande avec reutilisation d'un concept verifie que le nom de
+ # la variable a gauche du signe = est le meme que celui du concept reutilise.
+ # Lorsqu'une telle commande apparait dans une macro, on supprime
+ # cette verification.
+ if (etape.sdnom == '' or etape.sdnom[0] == '_'):
+ etape.sdnom = sd.nom
+ else:
+ # On est dans le cas de la creation d'un nouveau concept
+ sd = etape.get_sd_prod()
+ if sd != None:
+ self.NommerSdprod(sd, nomsd)
+ return sd
+
+ def NommerSdprod(self, sd, sdnom, restrict='non'):
+ """
+ Cette methode est appelee par les etapes internes de la macro.
+ La macro appelle le JDC pour valider le nommage.
+ On considère que l'espace de nom est unique et gere par le JDC.
+ Si le nom est deja utilise, l'appel lève une exception.
+ Si restrict=='non', on insère le concept dans le contexte du parent de la macro.
+ Si restrict=='oui', on insère le concept uniquement dans le contexte de la macro.
+ """
+ # Normalement, lorsqu'on appelle cette methode, on ne veut nommer que des concepts nouvellement crees.
+ # Le filtrage sur les concepts a creer ou a ne pas creer est fait dans la methode
+ # create_sdprod. La seule chose a verifier apres conversion eventuelle du nom
+ # est de verifier que le nom n'est pas deja attribue. Ceci est fait en delegant
+ # au JDC par l'intermediaire du parent.
+ if sdnom in self.Outputs :
+ # Il s'agit d'un concept de sortie de la macro produit par une
+ # sous commande
+ sdnom = self.Outputs[sdnom].nom
+ elif len(sdnom) > 0:
+ if sdnom[0] in ('_', '.') and sdnom[1:].isdigit():
+ # il est deja de la forme _9000012 ou .9000017
+ pass
+ elif sdnom[0] == '_':
+ # Si le nom du concept commence par le caractère '_', on lui attribue
+ # un identificateur JEVEUX construit par gcncon.
+ # nom commençant par __ : il s'agit de concepts qui seront detruits
+ # nom commençant par _ : il s'agit de concepts intermediaires
+ # qui seront gardes
+ if len(sdnom) > 1 and sdnom[1] == '_':
+ sdnom = self.gcncon('.')
+ else:
+ sdnom = self.gcncon('_')
+ elif self.nom in ('INCLUDE', 'MACR_RECAL'):
+ # dans le cas d'INCLUDE, on passe
+ # MACR_RECAL fonctionne comme INCLUDE
+ pass
+ else:
+ # On est dans le cas d'un nom de concept global
+ # XXX a voir, creation de CO() dans CALC_ESSAI (sdls139a)
+ if not sd.is_typco():
+ raise AsException(
+ "Resultat non declare par la macro %s : %s" % (self.nom, sdnom))
+ self.last = sdnom
+ if restrict == 'non':
+ # On demande le nommage au parent mais sans ajout du concept dans le contexte du parent
+ # car on va l'ajouter dans le contexte de la macro
+ self.parent.NommerSdprod(sd, sdnom, restrict='oui')
+ # On ajoute dans le contexte de la macro les concepts nommes
+ # Ceci est indispensable pour les CO (macro) dans un INCLUDE
+ self.g_context[sdnom] = sd
+ else:
+ # La demande de nommage vient probablement d'une macro qui a mis
+ # le concept dans son contexte. On ne traite plus que le nommage (restrict="oui")
+ self.parent.NommerSdprod(sd, sdnom, restrict='oui')
+
+ def delete_concept_after_etape(self, etape, sd):
+ """
+ Met a jour les etapes de la MACRO qui sont après etape suite a
+ la disparition du concept sd
+ """
+ # Cette methode est definie dans le noyau mais ne sert que pendant la phase de creation
+ # des etapes et des concepts. Il n'y a aucun traitement particulier a realiser
+ # Dans d'autres conditions, il faudrait surcharger cette methode.
+ return
+
+ def get_created_sd(self):
+ """Retourne la liste des sd reellement produites par l'etape.
+ Si reuse est present, `self.sd` a ete creee avant, donc n'est pas dans
+ cette liste."""
+ sdprods = self.sdprods[:]
+ if not self.reuse and self.sd:
+ sdprods.append(self.sd)
+ return sdprods
+
+ def get_last_concept(self):
+ """Retourne le dernier concept produit dans la macro.
+ Peut-etre utile pour acceder au contenu 'fortran' dans une
+ clause 'except'."""
+ return self.g_context.get(self.last, None)
+
+ def accept(self, visitor):
+ """
+ Cette methode permet de parcourir l'arborescence des objets
+ en utilisant le pattern VISITEUR
+ """
+ visitor.visitMACRO_ETAPE(self)
+
+ def update_context(self, d):
+ """
+ Met a jour le contexte contenu dans le dictionnaire d
+ Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte
+ Une fonction enregistree dans op_init peut egalement modifier le contexte
+ """
+ if type(self.definition.op_init) == types.FunctionType:
+ self.definition.op_init(*(self, d))
+ if self.sd != None:
+ d[self.sd.nom] = self.sd
+ for co in self.sdprods:
+ d[co.nom] = co
+
+ def make_include(self, unite=None, fname=None):
+ """Inclut un fichier dont l'unite logique est `unite` ou de nom `fname`"""
+ if unite is not None:
+ warn("'unite' is deprecated, please use 'fname' instead",
+ DeprecationWarning, stacklevel=2)
+ fname = 'fort.%s' % unite
+ if not fname:
+ return
+ f, text = self.get_file(fic_origine=self.parent.nom, fname=fname)
+ self.fichier_init = f
+ if f == None:
+ return
+ self.make_contexte(f, text)
+
+ def make_poursuite(self):
+ """Inclut un fichier poursuite"""
+ raise NotImplementedError('this method must be derivated (in Eficas)')
+
+ def make_contexte(self, f, text):
+ """
+ Interprete le texte fourni (text) issu du fichier f
+ dans le contexte du parent.
+ Cette methode est utile pour le fonctionnement des
+ INCLUDE
+ """
+ # on execute le texte fourni dans le contexte forme par
+ # le contexte de l etape pere (global au sens Python)
+ # et le contexte de l etape (local au sens Python)
+ code = compile(text, f, 'exec')
+ d = self.g_context = self.macro_const_context
+ globs = self.get_global_contexte()
+ d.update(globs)
+ exec(code, globs, d)
+ # pour ne pas conserver des references sur tout
+ self.macro_const_context = {}
+
+ def get_global_contexte(self):
+ """
+ Cette methode retourne le contexte global fourni
+ par le parent(self) a une etape fille (l'appelant) pour
+ realiser des evaluations de texte Python (INCLUDE,...)
+ """
+ # Le contexte global est forme par concatenation du contexte
+ # du parent de self et de celui de l'etape elle meme (self)
+ # Pour les concepts, cela ne doit rien changer. Mais pour les constantes,
+ # les valeurs de get_contexte_avant sont moins recentes que dans
+ # get_global_contexte. On prend donc la precaution de ne pas ecraser
+ # ce qui y est deja.
+ d = self.parent.get_global_contexte()
+ d.update(self.g_context)
+ d.update([(k, v) for k, v in list(self.parent.get_contexte_avant(self).items())
+ if d.get(k) is None])
+ return d
+
+ def get_contexte_courant(self, etape_fille_du_jdc=None):
+ """
+ Retourne le contexte tel qu'il est au moment de l'execution de
+ l'etape courante.
+ """
+ ctx = {}
+ # update car par ricochet on modifierait jdc.current_context
+ ctx.update(self.parent.get_contexte_courant(self))
+ # on peut mettre None car toujours en PAR_LOT='NON', donc la dernière
+ ctx.update(self.get_contexte_avant(None))
+ return ctx
+
+ def get_concept(self, nomsd):
+ """
+ Methode pour recuperer un concept a partir de son nom
+ dans le contexte du jdc connu avant l'execution de la macro courante.
+ """
+ # chercher dans self.get_contexte_avant, puis si non trouve
+ # self.parent.get_concept est peut-etre plus performant
+ co = self.get_contexte_courant().get(nomsd.strip(), None)
+ if not isinstance(co, ASSD):
+ co = None
+ return co
+
+ def get_concept_by_type(self, nomsd, typesd, etape=None):
+ """
+ Methode pour recuperer un concept a partir de son nom et de son type.
+ Il aura comme père 'etape' (ou la macro courante si etape est absente).
+ """
+ return self.parent.get_concept_by_type(nomsd, typesd, etape=etape or self)
+
+ def copy(self):
+ """ Methode qui retourne une copie de self non enregistree auprès du JDC
+ et sans sd
+ On surcharge la methode de ETAPE pour exprimer que les concepts crees
+ par la MACRO d'origine ne sont pas crees par la copie mais eventuellement
+ seulement utilises
+ """
+ etape = N_ETAPE.ETAPE.copy(self)
+ etape.sdprods = []
+ return etape
+
+ def copy_intern(self, etape):
+ """ Cette methode effectue la recopie des etapes internes d'une macro
+ passee en argument (etape)
+ """
+ self.etapes = []
+ self.index_etapes = {}
+ for etp in etape.etapes:
+ new_etp = etp.copy()
+ new_etp.copy_reuse(etp)
+ new_etp.copy_sdnom(etp)
+ new_etp.reparent(self)
+ if etp.sd:
+ new_sd = etp.sd.__class__(etape=new_etp)
+ new_etp.sd = new_sd
+ if etp.reuse:
+ new_sd.set_name(etp.sd.nom)
+ else:
+ self.NommerSdprod(new_sd, etp.sd.nom)
+ new_etp.copy_intern(etp)
+ self.etapes.append(new_etp)
+ self.index_etapes[new_etp] = len(self.etapes) - 1
+
+ def reset_jdc(self, new_jdc):
+ """
+ Reinitialise l'etape avec un nouveau jdc parent new_jdc
+ """
+ if self.sd and self.reuse == None:
+ self.parent.NommerSdprod(self.sd, self.sd.nom)
+ for concept in self.sdprods:
+ self.parent.NommerSdprod(concept, concept.nom)
+
+ def reparent(self, parent):
+ """
+ Cette methode sert a reinitialiser la parente de l'objet
+ """
+ N_ETAPE.ETAPE.reparent(self, parent)
+ # on ne change pas la parente des concepts. On s'assure uniquement que
+ # le jdc en reference est le bon
+ for concept in self.sdprods:
+ concept.jdc = self.jdc
+ for e in self.etapes:
+ e.reparent(self)
+
+ def update_const_context(self, d):
+ """
+ Met a jour le contexte des constantes pour l'evaluation de
+ formules dans la macro.
+ """
+ # Dans le jdc, const_context est mis a jour par exec_compile
+ # Dans la macro, on n'a pas le code a compiler pour recupèrer les
+ # constantes locales a la macro. On demande donc explicitement de
+ # definir les constantes "locales".
+ self.macro_const_context.update(d)
+
+ def sd_accessible(self):
+ """On peut acceder aux "valeurs" (jeveux) des ASSD dans
+ les macro-commandes qui sont localement en PAR_LOT="NON"
+ sauf pour INCLUDE.
+ """
+ if CONTEXT.debug:
+ print((' `- MACRO sd_accessible :', self.nom))
+ return self.parent.sd_accessible() or not self.is_include()