2f9798d123831135e650a0bff1081179c84523df
[people/mcb30/basetools.git] / Source / Python / Ecc / CodeFragmentCollector.py
1 ## @file\r
2 # preprocess source file\r
3 #\r
4 #  Copyright (c) 2007, Intel Corporation\r
5 #\r
6 #  All rights reserved. This program and the accompanying materials\r
7 #  are licensed and made available under the terms and conditions of the BSD License\r
8 #  which accompanies this distribution.  The full text of the license may be found at\r
9 #  http://opensource.org/licenses/bsd-license.php\r
10 #\r
11 #  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 #  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13 #\r
14 \r
15 ##\r
16 # Import Modules\r
17 #\r
18 \r
19 import re\r
20 import os\r
21 import sys\r
22 \r
23 import antlr3\r
24 from CLexer import CLexer\r
25 from CParser import CParser\r
26 \r
27 import FileProfile\r
28 from CodeFragment import Comment\r
29 from CodeFragment import PP_Directive\r
30 from ParserWarning import Warning\r
31 \r
32 \r
33 ##define T_CHAR_SPACE                ' '\r
34 ##define T_CHAR_NULL                 '\0'\r
35 ##define T_CHAR_CR                   '\r'\r
36 ##define T_CHAR_TAB                  '\t'\r
37 ##define T_CHAR_LF                   '\n'\r
38 ##define T_CHAR_SLASH                '/'\r
39 ##define T_CHAR_BACKSLASH            '\\'\r
40 ##define T_CHAR_DOUBLE_QUOTE         '\"'\r
41 ##define T_CHAR_SINGLE_QUOTE         '\''\r
42 ##define T_CHAR_STAR                 '*'\r
43 ##define T_CHAR_HASH                 '#'\r
44 \r
45 (T_CHAR_SPACE, T_CHAR_NULL, T_CHAR_CR, T_CHAR_TAB, T_CHAR_LF, T_CHAR_SLASH, \\r
46 T_CHAR_BACKSLASH, T_CHAR_DOUBLE_QUOTE, T_CHAR_SINGLE_QUOTE, T_CHAR_STAR, T_CHAR_HASH) = \\r
47 (' ', '\0', '\r', '\t', '\n', '/', '\\', '\"', '\'', '*', '#')\r
48 \r
49 SEPERATOR_TUPLE = ('=', '|', ',', '{', '}') \r
50 \r
51 (T_COMMENT_TWO_SLASH, T_COMMENT_SLASH_STAR) = (0, 1)\r
52 \r
53 (T_PP_INCLUDE, T_PP_DEFINE, T_PP_OTHERS) = (0, 1, 2)\r
54 \r
55 ## The collector for source code fragments.\r
56 #\r
57 # PreprocessFile method should be called prior to ParseFile\r
58 #\r
59 # GetNext*** procedures mean these procedures will get next token first, then make judgement.\r
60 # Get*** procedures mean these procedures will make judgement on current token only.\r
61 #        \r
62 class CodeFragmentCollector:\r
63     ## The constructor\r
64     #\r
65     #   @param  self        The object pointer\r
66     #   @param  FileName    The file that to be parsed\r
67     #\r
68     def __init__(self, FileName):\r
69         self.Profile = FileProfile.FileProfile(FileName)\r
70         self.Profile.FileLinesList.append(list(T_CHAR_LF))\r
71         self.FileName = FileName\r
72         self.CurrentLineNumber = 1\r
73         self.CurrentOffsetWithinLine = 0\r
74 \r
75         self.__Token = ""\r
76         self.__SkippedChars = ""\r
77 \r
78     ## __IsWhiteSpace() method\r
79     #\r
80     #   Whether char at current FileBufferPos is whitespace\r
81     #\r
82     #   @param  self        The object pointer\r
83     #   @param  Char        The char to test\r
84     #   @retval True        The char is a kind of white space\r
85     #   @retval False       The char is NOT a kind of white space\r
86     #\r
87     def __IsWhiteSpace(self, Char):\r
88         if Char in (T_CHAR_NULL, T_CHAR_CR, T_CHAR_SPACE, T_CHAR_TAB, T_CHAR_LF):\r
89             return True\r
90         else:\r
91             return False\r
92 \r
93     ## __SkipWhiteSpace() method\r
94     #\r
95     #   Skip white spaces from current char, return number of chars skipped\r
96     #\r
97     #   @param  self        The object pointer\r
98     #   @retval Count       The number of chars skipped\r
99     #\r
100     def __SkipWhiteSpace(self):\r
101         Count = 0\r
102         while not self.__EndOfFile():\r
103             Count += 1\r
104             if self.__CurrentChar() in (T_CHAR_NULL, T_CHAR_CR, T_CHAR_LF, T_CHAR_SPACE, T_CHAR_TAB):\r
105                 self.__SkippedChars += str(self.__CurrentChar())\r
106                 self.__GetOneChar()\r
107 \r
108             else:\r
109                 Count = Count - 1\r
110                 return Count\r
111 \r
112     ## __EndOfFile() method\r
113     #\r
114     #   Judge current buffer pos is at file end\r
115     #\r
116     #   @param  self        The object pointer\r
117     #   @retval True        Current File buffer position is at file end\r
118     #   @retval False       Current File buffer position is NOT at file end\r
119     #\r
120     def __EndOfFile(self):\r
121         NumberOfLines = len(self.Profile.FileLinesList)\r
122         SizeOfLastLine = len(self.Profile.FileLinesList[-1])\r
123         if self.CurrentLineNumber == NumberOfLines and self.CurrentOffsetWithinLine >= SizeOfLastLine - 1:\r
124             return True\r
125         else:\r
126             return False\r
127 \r
128     ## __EndOfLine() method\r
129     #\r
130     #   Judge current buffer pos is at line end\r
131     #\r
132     #   @param  self        The object pointer\r
133     #   @retval True        Current File buffer position is at line end\r
134     #   @retval False       Current File buffer position is NOT at line end\r
135     #\r
136     def __EndOfLine(self):\r
137         SizeOfCurrentLine = len(self.Profile.FileLinesList[self.CurrentLineNumber - 1])\r
138         if self.CurrentOffsetWithinLine >= SizeOfCurrentLine - 1:\r
139             return True\r
140         else:\r
141             return False\r
142     \r
143     ## Rewind() method\r
144     #\r
145     #   Reset file data buffer to the initial state\r
146     #\r
147     #   @param  self        The object pointer\r
148     #\r
149     def Rewind(self):\r
150         self.CurrentLineNumber = 1\r
151         self.CurrentOffsetWithinLine = 0\r
152     \r
153     ## __UndoOneChar() method\r
154     #\r
155     #   Go back one char in the file buffer\r
156     #\r
157     #   @param  self        The object pointer\r
158     #   @retval True        Successfully go back one char\r
159     #   @retval False       Not able to go back one char as file beginning reached\r
160     #    \r
161     def __UndoOneChar(self):\r
162         \r
163         if self.CurrentLineNumber == 1 and self.CurrentOffsetWithinLine == 0:\r
164             return False\r
165         elif self.CurrentOffsetWithinLine == 0:\r
166             self.CurrentLineNumber -= 1\r
167             self.CurrentOffsetWithinLine = len(self.__CurrentLine()) - 1\r
168         else:\r
169             self.CurrentOffsetWithinLine -= 1\r
170         return True\r
171         \r
172     ## __GetOneChar() method\r
173     #\r
174     #   Move forward one char in the file buffer\r
175     #\r
176     #   @param  self        The object pointer\r
177     #  \r
178     def __GetOneChar(self):\r
179         if self.CurrentOffsetWithinLine == len(self.Profile.FileLinesList[self.CurrentLineNumber - 1]) - 1:\r
180                 self.CurrentLineNumber += 1\r
181                 self.CurrentOffsetWithinLine = 0\r
182         else:\r
183                 self.CurrentOffsetWithinLine += 1\r
184 \r
185     ## __CurrentChar() method\r
186     #\r
187     #   Get the char pointed to by the file buffer pointer\r
188     #\r
189     #   @param  self        The object pointer\r
190     #   @retval Char        Current char\r
191     #  \r
192     def __CurrentChar(self):\r
193         CurrentChar = self.Profile.FileLinesList[self.CurrentLineNumber - 1][self.CurrentOffsetWithinLine]\r
194 #        if CurrentChar > 255:\r
195 #            raise Warning("Non-Ascii char found At Line %d, offset %d" % (self.CurrentLineNumber, self.CurrentOffsetWithinLine), self.FileName, self.CurrentLineNumber)\r
196         return CurrentChar\r
197     \r
198     ## __NextChar() method\r
199     #\r
200     #   Get the one char pass the char pointed to by the file buffer pointer\r
201     #\r
202     #   @param  self        The object pointer\r
203     #   @retval Char        Next char\r
204     #\r
205     def __NextChar(self):\r
206         if self.CurrentOffsetWithinLine == len(self.Profile.FileLinesList[self.CurrentLineNumber - 1]) - 1:\r
207             return self.Profile.FileLinesList[self.CurrentLineNumber][0]\r
208         else:\r
209             return self.Profile.FileLinesList[self.CurrentLineNumber - 1][self.CurrentOffsetWithinLine + 1]\r
210         \r
211     ## __SetCurrentCharValue() method\r
212     #\r
213     #   Modify the value of current char\r
214     #\r
215     #   @param  self        The object pointer\r
216     #   @param  Value       The new value of current char\r
217     #\r
218     def __SetCurrentCharValue(self, Value):\r
219         self.Profile.FileLinesList[self.CurrentLineNumber - 1][self.CurrentOffsetWithinLine] = Value\r
220         \r
221     ## __SetCharValue() method\r
222     #\r
223     #   Modify the value of current char\r
224     #\r
225     #   @param  self        The object pointer\r
226     #   @param  Value       The new value of current char\r
227     #\r
228     def __SetCharValue(self, Line, Offset, Value):\r
229         self.Profile.FileLinesList[Line - 1][Offset] = Value\r
230         \r
231     ## __CurrentLine() method\r
232     #\r
233     #   Get the list that contains current line contents\r
234     #\r
235     #   @param  self        The object pointer\r
236     #   @retval List        current line contents\r
237     #\r
238     def __CurrentLine(self):\r
239         return self.Profile.FileLinesList[self.CurrentLineNumber - 1]\r
240     \r
241     ## __InsertComma() method\r
242     #\r
243     #   Insert ',' to replace PP\r
244     #\r
245     #   @param  self        The object pointer\r
246     #   @retval List        current line contents\r
247     #\r
248     def __InsertComma(self, Line):\r
249         \r
250         \r
251         if self.Profile.FileLinesList[Line - 1][0] != T_CHAR_HASH:\r
252             BeforeHashPart = str(self.Profile.FileLinesList[Line - 1]).split(T_CHAR_HASH)[0]\r
253             if BeforeHashPart.rstrip().endswith(T_CHAR_COMMA) or BeforeHashPart.rstrip().endswith(';'):\r
254                 return\r
255         \r
256         if Line - 2 >= 0 and str(self.Profile.FileLinesList[Line - 2]).rstrip().endswith(','):\r
257             return\r
258         \r
259         if Line - 2 >= 0 and str(self.Profile.FileLinesList[Line - 2]).rstrip().endswith(';'):\r
260             return\r
261         \r
262         if str(self.Profile.FileLinesList[Line]).lstrip().startswith(',') or str(self.Profile.FileLinesList[Line]).lstrip().startswith(';'):\r
263             return\r
264         \r
265         self.Profile.FileLinesList[Line - 1].insert(self.CurrentOffsetWithinLine, ',')\r
266         \r
267     ## PreprocessFile() method\r
268     #\r
269     #   Preprocess file contents, replace comments with spaces.\r
270     #   In the end, rewind the file buffer pointer to the beginning\r
271     #   BUGBUG: No !include statement processing contained in this procedure\r
272     #   !include statement should be expanded at the same FileLinesList[CurrentLineNumber - 1]\r
273     #\r
274     #   @param  self        The object pointer\r
275     #   \r
276     def PreprocessFile(self):\r
277 \r
278         self.Rewind()\r
279         InComment = False\r
280         DoubleSlashComment = False\r
281         HashComment = False\r
282         PPExtend = False\r
283         CommentObj = None\r
284         PPDirectiveObj = None\r
285         # HashComment in quoted string " " is ignored.\r
286         InString = False\r
287         InCharLiteral = False \r
288 \r
289         while not self.__EndOfFile():\r
290             \r
291             if self.__CurrentChar() == T_CHAR_DOUBLE_QUOTE and not InComment:\r
292                 InString = not InString\r
293                 \r
294             if self.__CurrentChar() == T_CHAR_SINGLE_QUOTE and not InComment:\r
295                 InCharLiteral = not InCharLiteral\r
296             # meet new line, then no longer in a comment for // and '#'\r
297             if self.__CurrentChar() == T_CHAR_LF:\r
298                 if HashComment and PPDirectiveObj != None:\r
299                     if PPDirectiveObj.Content.rstrip(T_CHAR_CR).endswith(T_CHAR_BACKSLASH):\r
300                         PPDirectiveObj.Content += T_CHAR_LF\r
301                         PPExtend = True\r
302                     else:\r
303                         PPExtend = False\r
304                         \r
305                 EndLinePos = (self.CurrentLineNumber, self.CurrentOffsetWithinLine)\r
306                 \r
307                 if InComment and DoubleSlashComment:\r
308                     InComment = False\r
309                     DoubleSlashComment = False\r
310                     CommentObj.Content += T_CHAR_LF\r
311                     CommentObj.EndPos = EndLinePos\r
312                     FileProfile.CommentList.append(CommentObj)\r
313                     CommentObj = None\r
314                 if InComment and HashComment and not PPExtend:\r
315                     InComment = False\r
316                     HashComment = False\r
317                     PPDirectiveObj.Content += T_CHAR_LF\r
318                     PPDirectiveObj.EndPos = EndLinePos\r
319                     FileProfile.PPDirectiveList.append(PPDirectiveObj)\r
320                     PPDirectiveObj = None\r
321                 \r
322                 if InString or InCharLiteral:\r
323                     CurrentLine = "".join(self.__CurrentLine())\r
324                     if CurrentLine.rstrip(T_CHAR_LF).rstrip(T_CHAR_CR).endswith(T_CHAR_BACKSLASH):\r
325                         SlashIndex = CurrentLine.rindex(T_CHAR_BACKSLASH)\r
326                         self.__SetCharValue(self.CurrentLineNumber, SlashIndex, T_CHAR_SPACE)\r
327                 \r
328                 self.CurrentLineNumber += 1\r
329                 self.CurrentOffsetWithinLine = 0    \r
330             # check for */ comment end\r
331             elif InComment and not DoubleSlashComment and not HashComment and self.__CurrentChar() == T_CHAR_STAR and self.__NextChar() == T_CHAR_SLASH:\r
332                 CommentObj.Content += self.__CurrentChar()\r
333 #                self.__SetCurrentCharValue(T_CHAR_SPACE)\r
334                 self.__GetOneChar()\r
335                 CommentObj.Content += self.__CurrentChar()\r
336 #                self.__SetCurrentCharValue(T_CHAR_SPACE)\r
337                 CommentObj.EndPos = (self.CurrentLineNumber, self.CurrentOffsetWithinLine)\r
338                 FileProfile.CommentList.append(CommentObj)\r
339                 CommentObj = None\r
340                 self.__GetOneChar()\r
341                 InComment = False\r
342             # set comments to spaces\r
343             elif InComment:                   \r
344                 if HashComment:\r
345                     # // follows hash PP directive\r
346                     if self.__CurrentChar() == T_CHAR_SLASH and self.__NextChar() == T_CHAR_SLASH:\r
347                         InComment = False\r
348                         HashComment = False\r
349                         PPDirectiveObj.EndPos = (self.CurrentLineNumber, self.CurrentOffsetWithinLine - 1)\r
350                         FileProfile.PPDirectiveList.append(PPDirectiveObj)\r
351                         PPDirectiveObj = None\r
352                         continue\r
353                     else:\r
354                         PPDirectiveObj.Content += self.__CurrentChar()\r
355                     self.__SetCurrentCharValue(T_CHAR_SPACE)\r
356                 else:\r
357                     CommentObj.Content += self.__CurrentChar()\r
358 #                self.__SetCurrentCharValue(T_CHAR_SPACE)\r
359                 self.__GetOneChar()\r
360             # check for // comment\r
361             elif self.__CurrentChar() == T_CHAR_SLASH and self.__NextChar() == T_CHAR_SLASH:\r
362                 InComment = True\r
363                 DoubleSlashComment = True\r
364                 CommentObj = Comment('', (self.CurrentLineNumber, self.CurrentOffsetWithinLine), None, T_COMMENT_TWO_SLASH)\r
365             # check for '#' comment\r
366             elif self.__CurrentChar() == T_CHAR_HASH and not InString and not InCharLiteral:\r
367                 InComment = True\r
368                 HashComment = True \r
369                 PPDirectiveObj = PP_Directive('', (self.CurrentLineNumber, self.CurrentOffsetWithinLine), None)\r
370             # check for /* comment start\r
371             elif self.__CurrentChar() == T_CHAR_SLASH and self.__NextChar() == T_CHAR_STAR:\r
372                 CommentObj = Comment('', (self.CurrentLineNumber, self.CurrentOffsetWithinLine), None, T_COMMENT_SLASH_STAR)\r
373                 CommentObj.Content += self.__CurrentChar()\r
374 #                self.__SetCurrentCharValue( T_CHAR_SPACE)\r
375                 self.__GetOneChar()\r
376                 CommentObj.Content += self.__CurrentChar()\r
377 #                self.__SetCurrentCharValue( T_CHAR_SPACE)\r
378                 self.__GetOneChar()\r
379                 InComment = True\r
380             else:\r
381                 self.__GetOneChar()\r
382 \r
383         # restore from ListOfList to ListOfString\r
384         self.Profile.FileLinesList = ["".join(list) for list in self.Profile.FileLinesList]\r
385         self.Rewind()\r
386 \r
387     ## ParseFile() method\r
388     #\r
389     #   Parse the file profile buffer to extract fd, fv ... information\r
390     #   Exception will be raised if syntax error found\r
391     #\r
392     #   @param  self        The object pointer\r
393     #\r
394     def ParseFile(self):\r
395         self.PreprocessFile()\r
396         FileStringContents = ''\r
397         for fileLine in self.Profile.FileLinesList:\r
398             FileStringContents += fileLine\r
399         cStream = antlr3.StringStream(FileStringContents)\r
400         lexer = CLexer(cStream)\r
401         tStream = antlr3.CommonTokenStream(lexer)\r
402         parser = CParser(tStream)\r
403         parser.translation_unit()\r
404         \r
405     def CleanFileProfileBuffer(self):\r
406         FileProfile.CommentList = []\r
407         FileProfile.PPDirectiveList = []\r
408         FileProfile.PredicateExpressionList = []\r
409         FileProfile.FunctionDefinitionList = []\r
410         FileProfile.VariableDeclarationList = []\r
411         FileProfile.EnumerationDefinitionList = []\r
412         FileProfile.StructUnionDefinitionList = []\r
413         FileProfile.TypedefDefinitionList = []\r
414         FileProfile.FunctionCallingList = []\r
415         \r
416     def PrintFragments(self):\r
417         \r
418         print '################# ' + self.FileName + '#####################'\r
419         \r
420 #        print '/****************************************/'\r
421 #        print '/*************** COMMENTS ***************/'\r
422 #        print '/****************************************/'\r
423 #        for comment in FileProfile.CommentList:\r
424 #            print str(comment.StartPos) + comment.Content\r
425         \r
426         print '/****************************************/'\r
427         print '/********* PREPROCESS DIRECTIVES ********/'\r
428         print '/****************************************/'\r
429         for pp in FileProfile.PPDirectiveList:\r
430             print str(pp.StartPos) + pp.Content\r
431         \r
432         print '/****************************************/'\r
433         print '/********* VARIABLE DECLARATIONS ********/'\r
434         print '/****************************************/'\r
435         for var in FileProfile.VariableDeclarationList:\r
436             print str(var.StartPos) + var.Modifier + ' '+ var.Declarator\r
437             \r
438         print '/****************************************/'\r
439         print '/********* FUNCTION DEFINITIONS *********/'\r
440         print '/****************************************/'\r
441         for func in FileProfile.FunctionDefinitionList:\r
442             print str(func.StartPos) + func.Modifier + ' '+ func.Declarator + ' ' + str(func.NamePos)\r
443             \r
444         print '/****************************************/'\r
445         print '/************ ENUMERATIONS **************/'\r
446         print '/****************************************/'\r
447         for enum in FileProfile.EnumerationDefinitionList:\r
448             print str(enum.StartPos) + enum.Content\r
449         \r
450         print '/****************************************/'\r
451         print '/*********** STRUCTS/UNIONS *************/'\r
452         print '/****************************************/'\r
453         for su in FileProfile.StructUnionDefinitionList:\r
454             print str(su.StartPos) + su.Content\r
455             \r
456         print '/****************************************/'\r
457         print '/********* PREDICATE EXPRESSIONS ********/'\r
458         print '/****************************************/'\r
459         for predexp in FileProfile.PredicateExpressionList:\r
460             print str(predexp.StartPos) + predexp.Content\r
461         \r
462         print '/****************************************/'    \r
463         print '/************** TYPEDEFS ****************/'\r
464         print '/****************************************/'\r
465         for typedef in FileProfile.TypedefDefinitionList:\r
466             print str(typedef.StartPos) + typedef.ToType\r
467         \r
468 if __name__ == "__main__":\r
469     \r
470     collector = CodeFragmentCollector(sys.argv[1])\r
471     collector.PreprocessFile()\r
472     print "For Test."\r