From 6f0a29a5c0db32cf4703336688178deb71c38be0 Mon Sep 17 00:00:00 2001 From: Christian Caremoli <> Date: Tue, 20 Jun 2006 08:50:30 +0000 Subject: [PATCH] CCAR: 3 modifications modif 1 : suppression de l'appel au parseur dans INCLUDE_MATERIAU (Ihm/I_MACRO_ETAPE.py) modif 2 : modification du parseur python pour amelioration de performance (convert/convert_python.py convert/parseur_python.py) modif 3 : ne pas afficher des listes trop longues dans l'arbre (Ihm/I_MCSIMP.py, Ihm/I_ASSD.py) --- Ihm/I_ASSD.py | 4 + Ihm/I_MACRO_ETAPE.py | 23 +- Ihm/I_MCSIMP.py | 34 +-- convert/convert_python.py | 6 +- convert/parseur_python.py | 459 +++++++++++++++++++++++++------------- 5 files changed, 331 insertions(+), 195 deletions(-) diff --git a/Ihm/I_ASSD.py b/Ihm/I_ASSD.py index d7ade31b..8d30d613 100644 --- a/Ihm/I_ASSD.py +++ b/Ihm/I_ASSD.py @@ -25,6 +25,10 @@ from Noyau.N_VALIDATOR import ValError class ASSD: def __repr__(self): return "concept %s de type %s" % (self.get_name(),self.__class__.__name__) + + def __str__(self): + return self.get_name() or "" + #def __del__(self): # print "__del__",self diff --git a/Ihm/I_MACRO_ETAPE.py b/Ihm/I_MACRO_ETAPE.py index 6be22b12..258f44ca 100644 --- a/Ihm/I_MACRO_ETAPE.py +++ b/Ihm/I_MACRO_ETAPE.py @@ -59,9 +59,9 @@ class MACRO_ETAPE(I_ETAPE.ETAPE): def get_contexte_jdc(self,fichier,text): """ - Interprète text comme un texte de jdc et retourne le - contexte final - cad le dictionnaire des sd disponibles à la dernière étape + Interprète text comme un texte de jdc et retourne le contexte final. + + Le contexte final est le dictionnaire des sd disponibles à la dernière étape. Si text n'est pas un texte de jdc valide, retourne None ou leve une exception --> utilisée par ops.POURSUITE et INCLUDE @@ -98,12 +98,14 @@ class MACRO_ETAPE(I_ETAPE.ETAPE): if fichier is None:fichier="SansNom" # Il faut convertir le texte inclus en fonction du format - format=self.jdc.appli.format_fichier.get() - if convert.plugins.has_key(format): - # Le convertisseur existe on l'utilise - p=convert.plugins[format]() - p.text=text - text=p.convert('exec',self) + # sauf les INCLUDE_MATERIAU + if self.nom != "INCLUDE_MATERIAU": + format=self.jdc.appli.format_fichier.get() + if convert.plugins.has_key(format): + # Le convertisseur existe on l'utilise + p=convert.plugins[format]() + p.text=text + text=p.convert('exec',self) j=self.JdC_aux( procedure=text, nom=fichier, appli=self.jdc.appli, @@ -505,8 +507,7 @@ class MACRO_ETAPE(I_ETAPE.ETAPE): def make_contexte_include(self,fichier,text): """ - Cette méthode sert à créer un contexte en interprétant un texte source - Python + Cette méthode sert à créer un contexte en interprétant un texte source Python. """ #print "make_contexte_include",fichier # on récupère le contexte d'un nouveau jdc dans lequel on interprete text diff --git a/Ihm/I_MCSIMP.py b/Ihm/I_MCSIMP.py index 4e84c2a9..b2363984 100644 --- a/Ihm/I_MCSIMP.py +++ b/Ihm/I_MCSIMP.py @@ -76,8 +76,6 @@ class MCSIMP(I_OBJECT.OBJECT): return None elif type(self.valeur) == types.FloatType : # Traitement d'un flottant isolé - # txt = repr_float(self.valeur) - # Normalement str fait un travail correct txt = str(self.valeur) clefobj=self.GetNomConcept() if self.jdc.appli.dict_reels.has_key(clefobj): @@ -86,38 +84,28 @@ class MCSIMP(I_OBJECT.OBJECT): elif type(self.valeur) in (types.ListType,types.TupleType) : # Traitement des listes txt='(' - i=0 + sep='' for val in self.valeur: if type(val) == types.FloatType : - # CCAR : Normalement str fait un travail correct - #txt=txt + i*',' + repr_float(val) clefobj=self.GetNomConcept() if self.jdc.appli.dict_reels.has_key(clefobj): if self.jdc.appli.dict_reels[clefobj].has_key(val): - txt=txt + i*',' +self.jdc.appli.dict_reels[clefobj][val] + txt=txt + sep +self.jdc.appli.dict_reels[clefobj][val] else : - txt=txt + i*',' + str(val) + txt=txt + sep + str(val) else : - txt=txt + i*',' + str(val) - elif isinstance(val,ASSD): - txt = txt + i*',' + val.get_name() - #PN - # ajout du elif - elif type(val) == types.InstanceType and val.__class__.__name__ in ('PARAMETRE','PARAMETRE_EVAL'): - txt = txt + i*','+ str(val) + txt=txt + sep + str(val) else: - txt = txt + i*','+ myrepr.repr(val) - i=1 + txt = txt + sep+ str(val) + if len(txt) > 200: + #ligne trop longue, on tronque + txt=txt+" ..." + break + sep=',' txt=txt+')' - elif isinstance(self.valeur,ASSD): - # Cas des ASSD - txt=self.getval() - elif type(self.valeur) == types.InstanceType and self.valeur.__class__.__name__ in ('PARAMETRE','PARAMETRE_EVAL'): - # Cas des PARAMETRES - txt=str(self.valeur) else: # Traitement des autres cas - txt = myrepr.repr(self.valeur) + txt = str(self.valeur) # txt peut etre une longue chaine sur plusieurs lignes. # Il est possible de tronquer cette chaine au premier \n et diff --git a/convert/convert_python.py b/convert/convert_python.py index 999dac50..1c4cc788 100644 --- a/convert/convert_python.py +++ b/convert/convert_python.py @@ -111,10 +111,8 @@ class PythonParser: # Erreur lors de la conversion l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1], sys.exc_info()[2]) - self.cr.exception("Impossible de convertir le fichier python \ - qui doit contenir des erreurs.\n \ - On retourne le fichier non converti \n \ - Prévenir la maintenance. \n" + string.join(l)) + self.cr.exception("Impossible de convertir le fichier python qui doit contenir des erreurs.\n" + "On retourne le fichier non converti. Prévenir la maintenance.\n\n" + string.join(l)) # On retourne néanmoins le source initial non converti (au cas où) return self.text elif outformat == 'execnoparseur': diff --git a/convert/parseur_python.py b/convert/parseur_python.py index d65dbdfc..d79e8996 100644 --- a/convert/parseur_python.py +++ b/convert/parseur_python.py @@ -21,7 +21,99 @@ import sys,string,re import traceback +escapedQuotesRE = re.compile(r"(\\\\|\\\"|\\\')") +stringsAndCommentsRE = \ + re.compile("(\"\"\".*?\"\"\"|'''.*?'''|\"[^\"]*\"|\'[^\']*\'|#.*?\n)", re.DOTALL) +allchars = string.maketrans("", "") +allcharsExceptNewline = allchars[: allchars.index('\n')]+allchars[allchars.index('\n')+1:] +allcharsExceptNewlineTranstable = string.maketrans(allcharsExceptNewline, '*'*len(allcharsExceptNewline)) + +def maskStringsAndComments(src): + """Masque tous les caracteres de src contenus dans des commentaires ou des strings multilignes (triples + quotes et guillemets. + Le masquage est realise en remplacant les caracteres par des * + Attention : cette fonction doit etre utilisee sur un texte complet et pas ligne par ligne + """ + src = escapedQuotesRE.sub("**", src) + allstrings = stringsAndCommentsRE.split(src) + # every odd element is a string or comment + for i in xrange(1, len(allstrings), 2): + if allstrings[i].startswith("'''")or allstrings[i].startswith('"""'): + allstrings[i] = allstrings[i][:3]+ \ + allstrings[i][3:-3].translate(allcharsExceptNewlineTranstable)+ \ + allstrings[i][-3:] + else: + allstrings[i] = allstrings[i][0]+ \ + allstrings[i][1:-1].translate(allcharsExceptNewlineTranstable)+ \ + allstrings[i][-1] + + return "".join(allstrings) + +implicitContinuationChars = (('(', ')'), ('[', ']'), ('{', '}')) +linecontinueRE = re.compile(r"\\\s*(#.*)?$") +emptyHangingBraces = [0,0,0,0,0] + +class ParserException(Exception): pass +class FatalError(Exception): pass + +#commentaire double precede d'un nombre quelconque de blancs (pas multiligne) +pattern_2comments = re.compile(r"^\s*##.*") +#commentaire standard precede d'un nombre quelconque de blancs (pas multiligne) +pattern_comment = re.compile(r"^\s*#.*") +#fin de ligne ; suivi d'un nombre quelconque de blancs (pas multiligne) +pattern_fin = re.compile(r"; *$") +#pattern pour supprimer les blancs, tabulations et fins de ligne +pattern_blancs = re.compile(r"[\s\n]") +number_kw_pattern=re.compile(r""" +( + #groupe nombre decimal + (?: + #signe : on ignore le signe + + [-]? + #groupe (avec ?: n'apparait pas en tant que groupe dans le resultat) + (?: + #mantisse forme entiere.fractionnaire + \d+(?:\.\d*)? + | + #ou forme .fractionnaire + \.\d+ + ) + (?:[eE][+-]?\d+)? + ) + | + #argument keyword + [a-zA-Z_]\w*= +) +""",re.VERBOSE) + +def construit_genea(texte,liste_mc): + """Retourne un dictionnaire dont les cles sont des reels et les valeurs sont leurs representations textuelles. + Realise un filtrage sur les reels : + - Ne garde que les reels pour lesquels str ne donne pas une bonne representation. + - Ne garde que les reels derriere un argument keyword dont le nom est dans liste_mc + >>> s = 'a=+21.3e-5*85,b=-.1234,c=81.6 , d= -8 , e=_F(x=342.67,y=-1), f=+1.1, g=(1.3,-5,1.54E-3)' + >>> construit_genea(s,['a','x']) + {0.000213: '21.3e-5'} + """ + d={} + mot="" + #on masque les strings et commentaires pour ne pas identifier de faux reels + for m in number_kw_pattern.findall(maskStringsAndComments(texte)): + if m[-1] == '=': + #argument keyword + mot=m[:-1] + else: + if mot not in liste_mc:continue + #valeur + key=eval(m) + if str(key) != m: d[key]=m + return d + + class ENTITE_JDC : + """Classe de base pour tous les objets créés lors de la conversion + Tout objet dérivé est enregistré auprès de son père à sa création + """ def __init__(self,pere): self.texte = '' pere.l_objets.append(self) @@ -35,6 +127,7 @@ class ENTITE_JDC : """ texte = texte+'\n' self.texte = self.texte +texte + def __str__(self): return self.texte @@ -45,8 +138,11 @@ class COMMENTAIRE(ENTITE_JDC): Retourne une chaîne de caractères représentants self sous une forme interprétable par EFICAS """ - s='COMMENTAIRE("""'+self.texte+'""")\n\n' - return s + t=repr(self.texte) + return "COMMENTAIRE("+t+")\n" + + #s='COMMENTAIRE("""'+self.texte+'""")\n\n' + #return s def append_text(self,texte): """ @@ -92,7 +188,7 @@ class AFFECTATION(ENTITE_JDC): """ if texte[-1] == '\n' : texte = string.rstrip(texte[0:-1]) if texte[-1] == ';' : texte = string.rstrip(texte[0:-1]) - self.texte = self.texte+texte + self.texte = self.texte+texte+'\n' def __str__(self): """ @@ -100,17 +196,9 @@ class AFFECTATION(ENTITE_JDC): et exploitable par EFICAS """ nom,valeur = string.split(self.texte,'=',1) - # print nom,valeur n = string.rstrip(nom) nom = string.lstrip(n) if valeur[-1] == '\n': valeur = valeur[:-1] -# valeur = string.strip(valeur) - ## traitement des " -# if valeur[0]=='"': -# valeur=valeur[1:-1] -# if valeur[-1]=='"': -# valeur=valeur[0:-2] - return n + ' = PARAMETRE(nom=\''+nom+'\',valeur='+valeur+')\n' class COMMANDE_COMMENTARISEE(ENTITE_JDC): @@ -231,155 +319,179 @@ class PARSEUR_PYTHON: Eclate la chaine self.texte en self.l_objets une liste lignes d'instructions et de commentaires (parmi lesquels des instructions "commentarisées"). """ - #AY##l_lignes = open(self.fichier,'r').readlines() l_lignes = string.split(self.texte,'\n') commentaire_courant = None commande_courante = None affectation_courante = None commande_commentarisee_courante = None self.l_objets = [] - # PN pour corriger le pb des fins de ligne commentes - # par exemple P=1 # profondeur - liste=[] - for ligne in l_lignes : - if ligne.find("#") > 2 : - l1,l2=ligne.split("#",1) - queBlanc=1 - i=0 - while ( i < len(l1)) : - if l1[i] != " " : - queBlanc = 0 - break - i=i+1 - if queBlanc : - liste.append(ligne) - else : - # Il faut vérifier que le commentaire n 'est pas dans une chaine - ind1=ligne.find("'") - ind2=ligne.find("'",ind1+1) - if ind1 < ligne.find("#") < ind2 : - liste.append(ligne) - else: - l1=l1+"\n" - liste.append(l1) - l2="#"+l2 - liste.append(l2) - else : - liste.append(ligne) - # PN fin des commentaires - l_lignes=liste - cpt = 0 + #initialisation du nombre de parentheses non fermees et de commentaires non termines + #Attention a reinitialiser en fin de ligne logique + #Une ligne logique peut s'etendre sur plusieurs lignes physiques avec des caracteres de continuation + #explicites ou implicites + hangingBraces = list(emptyHangingBraces) + hangingComments = 0 + + #Masquage des commentaires et strings multilignes + srcMasked=maskStringsAndComments('\n'.join(l_lignes)) + #print srcMasked + masked_lines=srcMasked.split('\n') + lineno=0 + for ligne in l_lignes : - cpt = cpt+1 + line=masked_lines[lineno] + lineno=lineno+1 + #print "ligne:",line + # mise a jour du nombre total de parentheses ouvertes (non fermees) + # et du nombre de commentaires non termines + for i in range(len(implicitContinuationChars)): + contchar = implicitContinuationChars[i] + numHanging = hangingBraces[i] + hangingBraces[i] = numHanging+line.count(contchar[0]) - line.count(contchar[1]) + + hangingComments ^= line.count('"""') % 2 + hangingComments ^= line.count("'''") % 2 + #print hangingComments,hangingBraces + if hangingBraces[0] < 0 or hangingBraces[1] < 0 or hangingBraces[2] < 0: + raise ParserException() + if string.strip(ligne) == '': # il s'agit d'un saut de ligne # --> on l'ignore continue - else: - liste = string.split(ligne,'##',1) - if len(liste) > 1: - # on a trouvé un double commentaire dans la ligne - before,after = liste - if string.strip(before) == '': - # il s'agit d'une commande commentarisée - if commentaire_courant : - commentaire_courant = None - elif commande_courante : - # on a un objet commentarisé à l'intérieur d'une commande - # --> non traité pour l'instant - commande_courante.append_text(ligne) - elif commande_commentarisee_courante : - # commande_commentarisee en cours : on ajoute la ligne - commande_commentarisee_courante.append_text(ligne) - else: - # on crée un objet commande_commentarisee_courante - commande_commentarisee_courante = COMMANDE_COMMENTARISEE(self) - commande_commentarisee_courante.append_text(ligne) - # si la ligne courante se termine par un ';', on décide - par hypothèse et peut-être à tort - que - # la commande commentarisée courante est terminée !! - if re.search( '; *$', ligne ) != None : - commande_commentarisee_courante = None - continue - else: - # on a un double commentaire en fin de ligne - # --> pour l'instant on ne fait rien - pass - new_ligne = string.split(ligne,'#')[0] # on enlève toute la partie commentaire de la ligne - new_ligne = string.strip(new_ligne) - if new_ligne == '' : - # la ligne n'est qu'un commentaire précédé d'éventuels blancs - if commande_courante : - # il s'agit d'un commentaire à l'intérieur d'une commande --> on ne fait rien - commande_courante.append_text(ligne) - elif commentaire_courant : - # il s'agit de la nième ligne d'un commentaire entre deux commandes - # --> on ajoute cette ligne au commentaire courant - #print "ici1",ligne - commentaire_courant.append_text(ligne) - else : - # il s'agit d'un commentaire entre deux commandes - # --> on le crée et il devient le commentaire courant - commentaire_courant = COMMENTAIRE(self) - #print "ici2",ligne - commentaire_courant.append_text(ligne) + + if pattern_2comments.match(ligne): + #on a trouvé une commande commentarisée : double commentaire sans rien devant à part des blancs + if commentaire_courant: + #Si un commentaire ordinaire est en cours on le termine + commentaire_courant = None + + if commande_courante : + # on a un objet commentarisé à l'intérieur d'une commande + # --> non traité pour l'instant : on l'ajoute simplement a la commande courante comme + # un commentaire ordinaire + commande_courante.append_text(ligne) + elif commande_commentarisee_courante : + # commande_commentarisee en cours : on ajoute la ligne + commande_commentarisee_courante.append_text(ligne) else: - # la ligne contient des données autre qu'un éventuel commentaire - if commentaire_courant : - # on clôt un éventuel commentaire courant - commentaire_courant = None - if commande_courante : - commande_courante.append_text(ligne) - if commande_courante.get_nb_par() == 0: - # la commande courante est terminée (autant de parenthèses fermantes qu'ouvrantes) - try : - self.analyse_reel(commande_courante.texte) - except : - pass - commande_courante = None + # debut de commande commentarisée : on crée un objet commande_commentarisee_courante + commande_commentarisee_courante = COMMANDE_COMMENTARISEE(self) + commande_commentarisee_courante.append_text(ligne) + + #on passe à la ligne suivante + continue + + if pattern_comment.match(ligne): + #commentaire ordinaire avec seulement des blancs devant + if commande_commentarisee_courante : + # commande_commentarisee en cours : on la clot + commande_commentarisee_courante = None + + if commande_courante : + # il s'agit d'un commentaire à l'intérieur d'une commande --> on ne fait rien de special + #on l'ajoute au texte de la commande + commande_courante.append_text(ligne) + elif commentaire_courant : + # il s'agit de la nième ligne d'un commentaire entre deux commandes + # --> on ajoute cette ligne au commentaire courant + commentaire_courant.append_text(ligne) + else : + # il s'agit d'un nouveau commentaire entre deux commandes + # --> on le crée et il devient le commentaire courant + commentaire_courant = COMMENTAIRE(self) + commentaire_courant.append_text(ligne) + + #on passe à la ligne suivante + continue + + # la ligne contient des données autre qu'un éventuel commentaire + if commentaire_courant : + # on clôt un éventuel commentaire courant + commentaire_courant = None + + if commande_commentarisee_courante : + # on clôt une éventuelle commande commentarisee courante + commande_commentarisee_courante = None + + if commande_courante : + #on a une commande en cours. On l'enrichit ou on la termine + commande_courante.append_text(ligne) + if not linecontinueRE.search(line) and (hangingBraces == emptyHangingBraces) and not hangingComments: + #la commande est terminée + #print "fin de commande" + self.analyse_reel(commande_courante.texte) + commande_courante = None + + #on passe à la ligne suivante + continue + + if affectation_courante != None : + #poursuite d'une affectation + affectation_courante.append_text(ligne) + if not linecontinueRE.search(line) and (hangingBraces == emptyHangingBraces) and not hangingComments: + #L'affectation est terminée + affectation_courante=None + #on passe à la ligne suivante + continue + + # il peut s'agir d'une commande ou d'une affectation ... + # ou d'un EVAL !!! + if self.is_eval(ligne): + # --> affectation de type EVAL + if affectation_courante : affectation_courante = None + affectation = AFFECTATION_EVAL(self) + affectation.append_text(ligne) + #on passe à la ligne suivante + continue + + if self.is_affectation(ligne): + # --> affectation + text=ligne + #traitement des commentaires en fin de ligne + compos=line.find("#") + if compos > 2: + #commentaire en fin de ligne + #on cree un nouveau commentaire avant le parametre + COMMENTAIRE(self).append_text(ligne[compos:]) + text=ligne[:compos] + #si plusieurs instructions separees par des ; sur la meme ligne + inspos=line.find(";") + if inspos > 2: + #on garde seulement la premiere partie de la ligne + #si on a que des blancs apres le point virgule + if string.strip(text[inspos:]) == ";": + text=text[:inspos] else: - # il peut s'agir d'une commande ou d'une affectation ... - # ou de la poursuite d'une affectation !!!!! - # ou d'un EVAL !!! - if self.is_eval(new_ligne): - # --> affectation de type EVAL - if affectation_courante : affectation_courante = None - affectation = AFFECTATION_EVAL(self) - affectation.append_text(ligne) - elif self.is_affectation(new_ligne): - # --> affectation - affectation_courante = AFFECTATION(self) - affectation_courante.append_text(ligne) - elif self.is_commande(new_ligne): - # --> commande - commande_courante = COMMANDE(self) - commande_courante.append_text(ligne) - affectation_courante = None - if commande_courante.get_nb_par() == 0: - # la commande courante est terminée (autant de parenthèses fermantes qu'ouvrantes) - self.analyse_reel(commande_courante.texte) - commande_courante = None - else: - if commande_courante: - # commande en cours - commande_courante.append_text(ligne) - affectation_courante = None - if commande_courante.get_nb_par() == 0: - # la commande courante est terminée (autant de parenthèses fermantes qu'ouvrantes) - self.analyse_reel(commande_courante.texte) - commande_courante = None - else: - #print 'ici3',ligne - #e=ENTITE_JDC(self) - #e.append_text(ligne) - #--> poursuite d'une affectation - # PN -- pour Empecher une erreur pas propre - if affectation_courante != None : - affectation_courante.append_text(ligne) - #affectation_courante.append_text(ligne) + raise FatalError("Eficas ne peut pas traiter plusieurs instructions sur la meme ligne : %s" % ligne) + + affectation_courante = AFFECTATION(self) + affectation_courante.append_text(text) + if not linecontinueRE.search(line) and (hangingBraces == emptyHangingBraces) and not hangingComments: + #L'affectation est terminée + affectation_courante=None + #on passe à la ligne suivante + continue + if self.is_commande(ligne): + # --> nouvelle commande + affectation_courante = None + commande_courante = COMMANDE(self) + commande_courante.append_text(ligne) + #si la commande est complète, on la termine + if not linecontinueRE.search(line) and (hangingBraces == emptyHangingBraces) and not hangingComments: + #la commande est terminée + #print "fin de commande" + self.analyse_reel(commande_courante.texte) + commande_courante = None + #on passe à la ligne suivante + continue def enleve (self,texte) : + """Supprime de texte tous les caracteres blancs, fins de ligne, tabulations + Le nouveau texte est retourné + """ i=0 chaine="" while (i', 'exec') - + print a.dict_reels -- 2.39.2