Salome HOME
cht version
[tools/eficas.git] / Traducteur / parseur.py
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2007-2017   EDF R&D
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 #
20 import re,string
21
22 debug=0
23
24 escapedQuotesRE = re.compile(r"(\\\\|\\\"|\\\')")
25 stringsAndCommentsRE =  \
26       re.compile("(\"\"\".*?\"\"\"|'''.*?'''|\"[^\"]*\"|\'[^\']*\'|#.*?\n)", re.DOTALL)
27
28 import six
29 if six.PY2 :
30     allchars = string.maketrans(u"", "")
31     allcharsExceptNewline = allchars[: allchars.index('\n')]+allchars[allchars.index('\n')+1:]
32     allcharsExceptNewlineTranstable = string.maketrans(allcharsExceptNewline, '*'*len(allcharsExceptNewline))
33 else :
34     allchars=bytes.maketrans(b"",b"")
35     allcharsExceptNewline = allchars[: allchars.index(b'\n')]+allchars[allchars.index(b'\n')+1:]
36     allcharsExceptNewlineTranstable = bytes.maketrans(allcharsExceptNewline, b'*'*len(allcharsExceptNewline))
37
38
39 #------------------------------
40 def maskStringsAndComments(src):
41 #------------------------------
42     """Remplace tous les caracteres dans commentaires et strings par des * """
43
44     src = escapedQuotesRE.sub("**", src)
45     allstrings = stringsAndCommentsRE.split(src)
46     # every odd element is a string or comment
47     for i in range(1, len(allstrings), 2):
48         if allstrings[i].startswith("'''")or allstrings[i].startswith('"""'):
49             allstrings[i] = allstrings[i][:3]+ \
50                            allstrings[i][3:-3].translate(allcharsExceptNewlineTranstable)+ \
51                            allstrings[i][-3:]
52         else:
53             allstrings[i] = allstrings[i][0]+ \
54                            allstrings[i][1:-1].translate(allcharsExceptNewlineTranstable)+ \
55                            allstrings[i][-1]
56
57     return "".join(allstrings)
58
59 #un nombre queconque de blancs,un nom,des blancs
60 pattern_oper   = re.compile(r"^\s*(.*?=\s*)?([a-zA-Z_]\w*)(\s*)(\()(.*)",re.DOTALL)
61 pattern_proc   = re.compile(r"^\s*([a-zA-Z_]\w*)(\s*)(\()(.*)",re.DOTALL)
62
63 implicitContinuationChars = (('(', ')'), ('[', ']'), ('{', '}'))
64 linecontinueRE = re.compile(r"\\\s*(#.*)?$")
65 emptyHangingBraces = [0,0,0,0,0]
66
67 #--------------------------------------
68 class UnbalancedBracesException: pass
69 #--------------------------------------
70
71 #-----------
72 class Node:
73 #-----------
74     def __init__(self):
75         self.childNodes=[]
76
77     def addChild(self,node):
78         self.childNodes.append(node)
79
80
81 #-------------------
82 class FactNode(Node):
83 #-------------------
84     pass
85
86
87 #-------------------
88 class JDCNode(Node):
89 #-------------------
90     def __init__(self,src):
91         Node.__init__(self)
92         self.src=src
93
94 #-------------------
95 class Command(Node):
96 #-------------------
97     def __init__(self,name,lineno,colno,firstparen):
98         Node.__init__(self)
99         self.name=name
100         self.lineno=lineno
101         self.colno=colno
102         self.firstparen=firstparen
103
104 #-------------------
105 class Keyword(Node):
106 #-------------------
107     def __init__(self,name,lineno,colno,endline,endcol):
108         Node.__init__(self)
109         self.name=name
110         self.lineno=lineno
111         self.colno=colno
112         self.endline=endline
113         self.endcol=endcol
114
115     def getText(self,jdc):
116         if self.endline > self.lineno:
117             debut=jdc.getLines()[self.lineno-1][self.colno:]
118             fin  = jdc.getLines()[self.endline-1][:self.endcol]
119             texte=debut
120             lignecourante=self.lineno
121             while  lignecourante < self.endline -1  :
122                 texte = texte + jdc.getLines()[lignecourante]
123                 lignecourante = lignecourante + 1
124             if chaineBlanche(fin) == 0 :
125                 texte=texte + fin
126             if texte[-1] == "\n" :
127                 texte=texte[0:-1]
128         else:
129             texte = jdc.getLines()[self.lineno-1][self.colno:self.endcol]
130         return texte
131
132 #-------------------------
133 def chaineBlanche(texte) :
134 #-------------------------
135 # retourne 1 si la chaine est composee de " "
136 # retourne 0 sinon
137     bool = 1 ;
138     for i in range(len(texte)) :
139         if texte[i] != " " : bool = 0
140     return bool
141
142 #-------------------
143 def printNode(node):
144 #-------------------
145     if hasattr(node,'name'):
146         print (node.name)
147     else:
148         print ("pas de nom pour:",node)
149     for c in node.childNodes:
150         printNode(c)
151
152 #------------------------
153 def parser(src,atraiter):
154 #------------------------
155     """Parse le texte src et retourne un arbre syntaxique (root).
156
157        Cet arbre syntaxique a comme noeuds (childNodes) les commandes a traiter (liste atraiter)
158     """
159     lines=src.splitlines(1)
160     maskedSrc=maskStringsAndComments(src)
161     maskedLines=maskedSrc.splitlines(1)
162
163     root=JDCNode(src)
164
165     # (a) dans un premier temps on extrait les commandes et on les insere
166     #     dans un arbre (root)  les noeuds fils sont stockes dans
167     #     root.childNodes (liste)
168     lineno=0
169     for line in maskedLines:
170         lineno=lineno+1
171         if debug:print ("line",lineno,":",line)
172         m=pattern_proc.match(line)
173         if m and (m.group(1) in atraiter):
174             if debug:print (m.start(3),m.end(3),m.start(4))
175             root.addChild(Command(m.group(1),lineno,m.start(1),m.end(3)))
176         else:
177             m=pattern_oper.match(line)
178             if m and (m.group(2) in atraiter):
179                 root.addChild(Command(m.group(2),lineno,m.start(2),m.end(4)))
180
181     #(b) dans un deuxieme temps , on recupere le texte complet de la commande
182     #    jusqu'a la  derniere parenthese fermante
183
184     # iterateur sur les lignes physiques masquees
185     iterlines=iter(maskedLines)
186
187     linenum=0
188     for c in root.childNodes:
189         lineno=c.lineno
190         colno=c.colno                       # debut de la commande
191         while linenum < lineno:
192             line=iterlines.__next__()
193             linenum=linenum+1
194             if linenum != lineno:
195                 if debug:print ("line %s:"%linenum, line)
196         tmp = []
197         hangingBraces = list(emptyHangingBraces)
198         hangingComments = 0
199         while 1:
200             # update hanging braces
201             for i in range(len(implicitContinuationChars)):
202                 contchar = implicitContinuationChars[i]
203                 numHanging = hangingBraces[i]
204
205                 hangingBraces[i] = numHanging+line.count(contchar[0]) - \
206                                 line.count(contchar[1])
207
208             hangingComments ^= line.count('"""') % 2
209             hangingComments ^= line.count("'''") % 2
210
211             if hangingBraces[0] < 0 or hangingBraces[1] < 0 or hangingBraces[2] < 0:
212                 raise UnbalancedBracesException()
213
214             if linecontinueRE.search(line):
215                 tmp.append(lines[linenum-1])
216             elif hangingBraces != emptyHangingBraces:
217                 tmp.append(lines[linenum-1])
218             elif hangingComments:
219                 tmp.append(lines[linenum-1])
220             else:
221                 tmp.append(lines[linenum-1])
222                 src="".join(tmp)
223                 c.src=src
224                 c.endline=linenum
225                 decal=len(line)-line.rindex(')')
226                 c.lastParen=len(src)-decal
227                 if debug:print ("logical line %s %s:" % (c.lineno,c.endline),src)
228                 break
229             line=iterlines.__next__()
230             linenum=linenum+1
231
232     return root
233
234
235 #-----------------
236 def lastParen(src):
237 #-----------------
238     """Retourne la position de la derniere parenthese fermante dans src a partir du debut de la string
239
240        La string doit contenir la premiere parenthese ouvrante
241     """
242
243     src=maskStringsAndComments(src)
244     level=0
245     i,n=0,len(src)
246     while i < n:
247         ch=src[i]
248         i=i+1
249         if ch in ('(','['):
250             level=level+1
251         if ch in (')',']'):
252             if level == 0:
253                 raise UnbalancedBracesException()
254             level=level-1
255             if level == 0:
256                 #derniere parenthese fermante
257                 return i
258
259 #-------------------
260 def lastParen2(src):
261 #-------------------
262     """Retourne la position de la derniere parenthese fermante dans src a partir du debut de la string
263
264        La string ne contient pas la premiere parenthese ouvrante
265     """
266     src=maskStringsAndComments(src)
267     level=1
268     i,n=0,len(src)
269     while i < n:
270         ch=src[i]
271         i=i+1
272         if ch in ('(','['):
273             level=level+1
274         if ch in (')',']'):
275             if level == 0:
276                 raise UnbalancedBracesException()
277             level=level-1
278             if level == 0:
279                 #derniere parenthese fermante
280                 return i