ECC: C preprocessor only wipe off '\' concatenated macro defines.
[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                     if PPExtend:\r
356                         self.__SetCurrentCharValue(T_CHAR_SPACE)\r
357                 else:\r
358                     CommentObj.Content += self.__CurrentChar()\r
359 #                self.__SetCurrentCharValue(T_CHAR_SPACE)\r
360                 self.__GetOneChar()\r
361             # check for // comment\r
362             elif self.__CurrentChar() == T_CHAR_SLASH and self.__NextChar() == T_CHAR_SLASH:\r
363                 InComment = True\r
364                 DoubleSlashComment = True\r
365                 CommentObj = Comment('', (self.CurrentLineNumber, self.CurrentOffsetWithinLine), None, T_COMMENT_TWO_SLASH)\r
366             # check for '#' comment\r
367             elif self.__CurrentChar() == T_CHAR_HASH and not InString and not InCharLiteral:\r
368                 InComment = True\r
369                 HashComment = True \r
370                 PPDirectiveObj = PP_Directive('', (self.CurrentLineNumber, self.CurrentOffsetWithinLine), None)\r
371             # check for /* comment start\r
372             elif self.__CurrentChar() == T_CHAR_SLASH and self.__NextChar() == T_CHAR_STAR:\r
373                 CommentObj = Comment('', (self.CurrentLineNumber, self.CurrentOffsetWithinLine), None, T_COMMENT_SLASH_STAR)\r
374                 CommentObj.Content += self.__CurrentChar()\r
375 #                self.__SetCurrentCharValue( T_CHAR_SPACE)\r
376                 self.__GetOneChar()\r
377                 CommentObj.Content += self.__CurrentChar()\r
378 #                self.__SetCurrentCharValue( T_CHAR_SPACE)\r
379                 self.__GetOneChar()\r
380                 InComment = True\r
381             else:\r
382                 self.__GetOneChar()\r
383 \r
384         # restore from ListOfList to ListOfString\r
385         self.Profile.FileLinesList = ["".join(list) for list in self.Profile.FileLinesList]\r
386         self.Rewind()\r
387 \r
388     ## ParseFile() method\r
389     #\r
390     #   Parse the file profile buffer to extract fd, fv ... information\r
391     #   Exception will be raised if syntax error found\r
392     #\r
393     #   @param  self        The object pointer\r
394     #\r
395     def ParseFile(self):\r
396         self.PreprocessFile()\r
397         FileStringContents = ''\r
398         for fileLine in self.Profile.FileLinesList:\r
399             FileStringContents += fileLine\r
400         cStream = antlr3.StringStream(FileStringContents)\r
401         lexer = CLexer(cStream)\r
402         tStream = antlr3.CommonTokenStream(lexer)\r
403         parser = CParser(tStream)\r
404         parser.translation_unit()\r
405         \r
406     def CleanFileProfileBuffer(self):\r
407         FileProfile.CommentList = []\r
408         FileProfile.PPDirectiveList = []\r
409         FileProfile.PredicateExpressionList = []\r
410         FileProfile.FunctionDefinitionList = []\r
411         FileProfile.VariableDeclarationList = []\r
412         FileProfile.EnumerationDefinitionList = []\r
413         FileProfile.StructUnionDefinitionList = []\r
414         FileProfile.TypedefDefinitionList = []\r
415         FileProfile.FunctionCallingList = []\r
416         \r
417     def PrintFragments(self):\r
418         \r
419         print '################# ' + self.FileName + '#####################'\r
420         \r
421 #        print '/****************************************/'\r
422 #        print '/*************** COMMENTS ***************/'\r
423 #        print '/****************************************/'\r
424 #        for comment in FileProfile.CommentList:\r
425 #            print str(comment.StartPos) + comment.Content\r
426         \r
427         print '/****************************************/'\r
428         print '/********* PREPROCESS DIRECTIVES ********/'\r
429         print '/****************************************/'\r
430         for pp in FileProfile.PPDirectiveList:\r
431             print str(pp.StartPos) + pp.Content\r
432         \r
433         print '/****************************************/'\r
434         print '/********* VARIABLE DECLARATIONS ********/'\r
435         print '/****************************************/'\r
436         for var in FileProfile.VariableDeclarationList:\r
437             print str(var.StartPos) + var.Modifier + ' '+ var.Declarator\r
438             \r
439         print '/****************************************/'\r
440         print '/********* FUNCTION DEFINITIONS *********/'\r
441         print '/****************************************/'\r
442         for func in FileProfile.FunctionDefinitionList:\r
443             print str(func.StartPos) + func.Modifier + ' '+ func.Declarator + ' ' + str(func.NamePos)\r
444             \r
445         print '/****************************************/'\r
446         print '/************ ENUMERATIONS **************/'\r
447         print '/****************************************/'\r
448         for enum in FileProfile.EnumerationDefinitionList:\r
449             print str(enum.StartPos) + enum.Content\r
450         \r
451         print '/****************************************/'\r
452         print '/*********** STRUCTS/UNIONS *************/'\r
453         print '/****************************************/'\r
454         for su in FileProfile.StructUnionDefinitionList:\r
455             print str(su.StartPos) + su.Content\r
456             \r
457         print '/****************************************/'\r
458         print '/********* PREDICATE EXPRESSIONS ********/'\r
459         print '/****************************************/'\r
460         for predexp in FileProfile.PredicateExpressionList:\r
461             print str(predexp.StartPos) + predexp.Content\r
462         \r
463         print '/****************************************/'    \r
464         print '/************** TYPEDEFS ****************/'\r
465         print '/****************************************/'\r
466         for typedef in FileProfile.TypedefDefinitionList:\r
467             print str(typedef.StartPos) + typedef.ToType\r
468         \r
469 if __name__ == "__main__":\r
470     \r
471     collector = CodeFragmentCollector(sys.argv[1])\r
472     collector.PreprocessFile()\r
473     print "For Test."\r