2 # This file is used to define comment parsing interface
4 # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
6 # This program and the accompanying materials are licensed and made available
7 # under the terms and conditions of the BSD License which accompanies this
8 # distribution. The full text of the license may be found at
9 # http://opensource.org/licenses/bsd-license.php
11 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
24 from Library
.StringUtils
import GetSplitValueList
25 from Library
.StringUtils
import CleanString2
26 from Library
.DataType
import HEADER_COMMENT_NOT_STARTED
27 from Library
.DataType
import TAB_COMMENT_SPLIT
28 from Library
.DataType
import HEADER_COMMENT_LICENSE
29 from Library
.DataType
import HEADER_COMMENT_ABSTRACT
30 from Library
.DataType
import HEADER_COMMENT_COPYRIGHT
31 from Library
.DataType
import HEADER_COMMENT_DESCRIPTION
32 from Library
.DataType
import TAB_SPACE_SPLIT
33 from Library
.DataType
import TAB_COMMA_SPLIT
34 from Library
.DataType
import SUP_MODULE_LIST
35 from Library
.DataType
import TAB_VALUE_SPLIT
36 from Library
.DataType
import TAB_PCD_VALIDRANGE
37 from Library
.DataType
import TAB_PCD_VALIDLIST
38 from Library
.DataType
import TAB_PCD_EXPRESSION
39 from Library
.DataType
import TAB_PCD_PROMPT
40 from Library
.DataType
import TAB_CAPHEX_START
41 from Library
.DataType
import TAB_HEX_START
42 from Library
.DataType
import PCD_ERR_CODE_MAX_SIZE
43 from Library
.ExpressionValidate
import IsValidRangeExpr
44 from Library
.ExpressionValidate
import IsValidListExpr
45 from Library
.ExpressionValidate
import IsValidLogicalExpr
46 from Object
.POM
.CommonObject
import TextObject
47 from Object
.POM
.CommonObject
import PcdErrorObject
48 import Logger
.Log
as Logger
49 from Logger
.ToolError
import FORMAT_INVALID
50 from Logger
.ToolError
import FORMAT_NOT_SUPPORTED
51 from Logger
import StringTable
as ST
53 ## ParseHeaderCommentSection
55 # Parse Header comment section lines, extract Abstract, Description, Copyright
58 # @param CommentList: List of (Comment, LineNumber)
59 # @param FileName: FileName of the comment
61 def ParseHeaderCommentSection(CommentList
, FileName
= None, IsBinaryHeader
= False):
68 STR_HEADER_COMMENT_START
= "@BinaryHeader"
70 STR_HEADER_COMMENT_START
= "@file"
71 HeaderCommentStage
= HEADER_COMMENT_NOT_STARTED
74 # first find the last copyright line
77 for Index
in range(len(CommentList
)-1, 0, -1):
78 Line
= CommentList
[Index
][0]
79 if _IsCopyrightLine(Line
):
83 for Item
in CommentList
:
87 if not Line
.startswith(TAB_COMMENT_SPLIT
) and Line
:
88 Logger
.Error("\nUPT", FORMAT_INVALID
, ST
.ERR_INVALID_COMMENT_FORMAT
, FileName
, Item
[1])
89 Comment
= CleanString2(Line
)[1]
90 Comment
= Comment
.strip()
92 # if there are blank lines between License or Description, keep them as they would be
93 # indication of different block; or in the position that Abstract should be, also keep it
94 # as it indicates that no abstract
96 if not Comment
and HeaderCommentStage
not in [HEADER_COMMENT_LICENSE
, \
97 HEADER_COMMENT_DESCRIPTION
, HEADER_COMMENT_ABSTRACT
]:
100 if HeaderCommentStage
== HEADER_COMMENT_NOT_STARTED
:
101 if Comment
.startswith(STR_HEADER_COMMENT_START
):
102 HeaderCommentStage
= HEADER_COMMENT_ABSTRACT
104 License
+= Comment
+ EndOfLine
106 if HeaderCommentStage
== HEADER_COMMENT_ABSTRACT
:
108 # in case there is no abstract and description
111 HeaderCommentStage
= HEADER_COMMENT_DESCRIPTION
112 elif _IsCopyrightLine(Comment
):
113 Result
, ErrMsg
= _ValidateCopyright(Comment
)
114 ValidateCopyright(Result
, ST
.WRN_INVALID_COPYRIGHT
, FileName
, LineNo
, ErrMsg
)
115 Copyright
+= Comment
+ EndOfLine
116 HeaderCommentStage
= HEADER_COMMENT_COPYRIGHT
118 Abstract
+= Comment
+ EndOfLine
119 HeaderCommentStage
= HEADER_COMMENT_DESCRIPTION
120 elif HeaderCommentStage
== HEADER_COMMENT_DESCRIPTION
:
122 # in case there is no description
124 if _IsCopyrightLine(Comment
):
125 Result
, ErrMsg
= _ValidateCopyright(Comment
)
126 ValidateCopyright(Result
, ST
.WRN_INVALID_COPYRIGHT
, FileName
, LineNo
, ErrMsg
)
127 Copyright
+= Comment
+ EndOfLine
128 HeaderCommentStage
= HEADER_COMMENT_COPYRIGHT
130 Description
+= Comment
+ EndOfLine
131 elif HeaderCommentStage
== HEADER_COMMENT_COPYRIGHT
:
132 if _IsCopyrightLine(Comment
):
133 Result
, ErrMsg
= _ValidateCopyright(Comment
)
134 ValidateCopyright(Result
, ST
.WRN_INVALID_COPYRIGHT
, FileName
, LineNo
, ErrMsg
)
135 Copyright
+= Comment
+ EndOfLine
138 # Contents after copyright line are license, those non-copyright lines in between
139 # copyright line will be discarded
144 License
+= Comment
+ EndOfLine
145 HeaderCommentStage
= HEADER_COMMENT_LICENSE
147 if not Comment
and not License
:
149 License
+= Comment
+ EndOfLine
151 return Abstract
.strip(), Description
.strip(), Copyright
.strip(), License
.strip()
154 # check whether current line is copyright line, the criteria is whether there is case insensitive keyword "Copyright"
155 # followed by zero or more white space characters followed by a "(" character
157 # @param LineContent: the line need to be checked
158 # @return: True if current line is copyright line, False else
160 def _IsCopyrightLine (LineContent
):
161 LineContent
= LineContent
.upper()
164 ReIsCopyrightRe
= re
.compile(r
"""(^|\s)COPYRIGHT *\(""", re
.DOTALL
)
165 if ReIsCopyrightRe
.search(LineContent
):
170 ## ParseGenericComment
172 # @param GenericComment: Generic comment list, element of
173 # (CommentLine, LineNum)
174 # @param ContainerFile: Input value for filename of Dec file
176 def ParseGenericComment (GenericComment
, ContainerFile
=None, SkipTag
=None):
182 for Item
in GenericComment
:
183 CommentLine
= Item
[0]
184 Comment
= CleanString2(CommentLine
)[1]
185 if SkipTag
is not None and Comment
.startswith(SkipTag
):
186 Comment
= Comment
.replace(SkipTag
, '', 1)
187 HelpStr
+= Comment
+ '\n'
190 HelpTxt
= TextObject()
191 if HelpStr
.endswith('\n') and not HelpStr
.endswith('\n\n') and HelpStr
!= '\n':
192 HelpStr
= HelpStr
[:-1]
193 HelpTxt
.SetString(HelpStr
)
199 # @param Value: original ErrorCode value
200 # @param ContainerFile: Input value for filename of Dec file
201 # @param LineNum: Line Num
203 def ParsePcdErrorCode (Value
= None, ContainerFile
= None, LineNum
= None):
205 if Value
.strip().startswith((TAB_HEX_START
, TAB_CAPHEX_START
)):
209 ErrorCode
= int(Value
, Base
)
210 if ErrorCode
> PCD_ERR_CODE_MAX_SIZE
or ErrorCode
< 0:
211 Logger
.Error('Parser',
212 FORMAT_NOT_SUPPORTED
,
213 "The format %s of ErrorCode is not valid, should be UNIT32 type or long type" % Value
,
214 File
= ContainerFile
,
216 return hex(ErrorCode
)
217 except ValueError as XStr
:
220 Logger
.Error('Parser',
221 FORMAT_NOT_SUPPORTED
,
222 "The format %s of ErrorCode is not valid, should be UNIT32 type or long type" % Value
,
223 File
= ContainerFile
,
226 ## ParseDecPcdGenericComment
228 # @param GenericComment: Generic comment list, element of (CommentLine,
230 # @param ContainerFile: Input value for filename of Dec file
232 def ParseDecPcdGenericComment (GenericComment
, ContainerFile
, TokenSpaceGuidCName
, CName
, MacroReplaceDict
):
241 for (CommentLine
, LineNum
) in GenericComment
:
242 Comment
= CleanString2(CommentLine
)[1]
246 MACRO_PATTERN
= '[\t\s]*\$\([A-Z][_A-Z0-9]*\)'
247 MatchedStrs
= re
.findall(MACRO_PATTERN
, Comment
)
248 for MatchedStr
in MatchedStrs
:
250 Macro
= MatchedStr
.strip().lstrip('$(').rstrip(')').strip()
251 if Macro
in MacroReplaceDict
:
252 Comment
= Comment
.replace(MatchedStr
, MacroReplaceDict
[Macro
])
253 if Comment
.startswith(TAB_PCD_VALIDRANGE
):
254 if ValidValueNum
> 0 or ExpressionNum
> 0:
255 Logger
.Error('Parser',
256 FORMAT_NOT_SUPPORTED
,
257 ST
.WRN_MULTI_PCD_RANGES
,
258 File
= ContainerFile
,
261 PcdErr
= PcdErrorObject()
262 PcdErr
.SetTokenSpaceGuidCName(TokenSpaceGuidCName
)
263 PcdErr
.SetCName(CName
)
264 PcdErr
.SetFileLine(Comment
)
265 PcdErr
.SetLineNum(LineNum
)
267 ValidRange
= Comment
.replace(TAB_PCD_VALIDRANGE
, "", 1).strip()
268 Valid
, Cause
= _CheckRangeExpression(ValidRange
)
270 ValueList
= ValidRange
.split(TAB_VALUE_SPLIT
)
271 if len(ValueList
) > 1:
272 PcdErr
.SetValidValueRange((TAB_VALUE_SPLIT
.join(ValueList
[1:])).strip())
273 PcdErr
.SetErrorNumber(ParsePcdErrorCode(ValueList
[0], ContainerFile
, LineNum
))
275 PcdErr
.SetValidValueRange(ValidRange
)
276 PcdErrList
.append(PcdErr
)
278 Logger
.Error("Parser",
279 FORMAT_NOT_SUPPORTED
,
283 elif Comment
.startswith(TAB_PCD_VALIDLIST
):
284 if ValidRangeNum
> 0 or ExpressionNum
> 0:
285 Logger
.Error('Parser',
286 FORMAT_NOT_SUPPORTED
,
287 ST
.WRN_MULTI_PCD_RANGES
,
288 File
= ContainerFile
,
290 elif ValidValueNum
> 0:
291 Logger
.Error('Parser',
292 FORMAT_NOT_SUPPORTED
,
293 ST
.WRN_MULTI_PCD_VALIDVALUE
,
294 File
= ContainerFile
,
297 PcdErr
= PcdErrorObject()
298 PcdErr
.SetTokenSpaceGuidCName(TokenSpaceGuidCName
)
299 PcdErr
.SetCName(CName
)
300 PcdErr
.SetFileLine(Comment
)
301 PcdErr
.SetLineNum(LineNum
)
303 ValidValueExpr
= Comment
.replace(TAB_PCD_VALIDLIST
, "", 1).strip()
304 Valid
, Cause
= _CheckListExpression(ValidValueExpr
)
306 ValidValue
= Comment
.replace(TAB_PCD_VALIDLIST
, "", 1).replace(TAB_COMMA_SPLIT
, TAB_SPACE_SPLIT
)
307 ValueList
= ValidValue
.split(TAB_VALUE_SPLIT
)
308 if len(ValueList
) > 1:
309 PcdErr
.SetValidValue((TAB_VALUE_SPLIT
.join(ValueList
[1:])).strip())
310 PcdErr
.SetErrorNumber(ParsePcdErrorCode(ValueList
[0], ContainerFile
, LineNum
))
312 PcdErr
.SetValidValue(ValidValue
)
313 PcdErrList
.append(PcdErr
)
315 Logger
.Error("Parser",
316 FORMAT_NOT_SUPPORTED
,
320 elif Comment
.startswith(TAB_PCD_EXPRESSION
):
321 if ValidRangeNum
> 0 or ValidValueNum
> 0:
322 Logger
.Error('Parser',
323 FORMAT_NOT_SUPPORTED
,
324 ST
.WRN_MULTI_PCD_RANGES
,
325 File
= ContainerFile
,
328 PcdErr
= PcdErrorObject()
329 PcdErr
.SetTokenSpaceGuidCName(TokenSpaceGuidCName
)
330 PcdErr
.SetCName(CName
)
331 PcdErr
.SetFileLine(Comment
)
332 PcdErr
.SetLineNum(LineNum
)
334 Expression
= Comment
.replace(TAB_PCD_EXPRESSION
, "", 1).strip()
335 Valid
, Cause
= _CheckExpression(Expression
)
337 ValueList
= Expression
.split(TAB_VALUE_SPLIT
)
338 if len(ValueList
) > 1:
339 PcdErr
.SetExpression((TAB_VALUE_SPLIT
.join(ValueList
[1:])).strip())
340 PcdErr
.SetErrorNumber(ParsePcdErrorCode(ValueList
[0], ContainerFile
, LineNum
))
342 PcdErr
.SetExpression(Expression
)
343 PcdErrList
.append(PcdErr
)
345 Logger
.Error("Parser",
346 FORMAT_NOT_SUPPORTED
,
350 elif Comment
.startswith(TAB_PCD_PROMPT
):
352 Logger
.Error('Parser',
353 FORMAT_NOT_SUPPORTED
,
354 ST
.WRN_MULTI_PCD_PROMPT
,
355 File
= ContainerFile
,
357 PromptStr
= Comment
.replace(TAB_PCD_PROMPT
, "", 1).strip()
360 HelpStr
+= Comment
+ '\n'
363 # remove the last EOL if the comment is of format 'FOO\n'
365 if HelpStr
.endswith('\n'):
366 if HelpStr
!= '\n' and not HelpStr
.endswith('\n\n'):
367 HelpStr
= HelpStr
[:-1]
369 return HelpStr
, PcdErrList
, PromptStr
371 ## ParseDecPcdTailComment
373 # @param TailCommentList: Tail comment list of Pcd, item of format (Comment, LineNum)
374 # @param ContainerFile: Input value for filename of Dec file
375 # @retVal SupModuleList: The supported module type list detected
376 # @retVal HelpStr: The generic help text string detected
378 def ParseDecPcdTailComment (TailCommentList
, ContainerFile
):
379 assert(len(TailCommentList
) == 1)
380 TailComment
= TailCommentList
[0][0]
381 LineNum
= TailCommentList
[0][1]
383 Comment
= TailComment
.lstrip(" #")
385 ReFindFirstWordRe
= re
.compile(r
"""^([^ #]*)""", re
.DOTALL
)
388 # get first word and compare with SUP_MODULE_LIST
390 MatchObject
= ReFindFirstWordRe
.match(Comment
)
391 if not (MatchObject
and MatchObject
.group(1) in SUP_MODULE_LIST
):
395 # parse line, it must have supported module type specified
397 if Comment
.find(TAB_COMMENT_SPLIT
) == -1:
398 Comment
+= TAB_COMMENT_SPLIT
399 SupMode
, HelpStr
= GetSplitValueList(Comment
, TAB_COMMENT_SPLIT
, 1)
401 for Mod
in GetSplitValueList(SupMode
, TAB_SPACE_SPLIT
):
404 elif Mod
not in SUP_MODULE_LIST
:
407 ST
.WRN_INVALID_MODULE_TYPE
%Mod
,
411 SupModuleList
.append(Mod
)
413 return SupModuleList
, HelpStr
415 ## _CheckListExpression
417 # @param Expression: Pcd value list expression
419 def _CheckListExpression(Expression
):
421 if TAB_VALUE_SPLIT
in Expression
:
422 ListExpr
= Expression
[Expression
.find(TAB_VALUE_SPLIT
)+1:]
424 ListExpr
= Expression
426 return IsValidListExpr(ListExpr
)
430 # @param Expression: Pcd value expression
432 def _CheckExpression(Expression
):
434 if TAB_VALUE_SPLIT
in Expression
:
435 Expr
= Expression
[Expression
.find(TAB_VALUE_SPLIT
)+1:]
438 return IsValidLogicalExpr(Expr
, True)
440 ## _CheckRangeExpression
442 # @param Expression: Pcd range expression
444 def _CheckRangeExpression(Expression
):
446 if TAB_VALUE_SPLIT
in Expression
:
447 RangeExpr
= Expression
[Expression
.find(TAB_VALUE_SPLIT
)+1:]
449 RangeExpr
= Expression
451 return IsValidRangeExpr(RangeExpr
)
457 def ValidateCopyright(Result
, ErrType
, FileName
, LineNo
, ErrMsg
):
459 Logger
.Warn("\nUPT", ErrType
, FileName
, LineNo
, ErrMsg
)
461 ## _ValidateCopyright
463 # @param Line: Line that contains copyright information, # stripped
465 # @retval Result: True if line is conformed to Spec format, False else
466 # @retval ErrMsg: the detailed error description
468 def _ValidateCopyright(Line
):
474 return Result
, ErrMsg
476 def GenerateTokenList (Comment
):
478 # Tokenize Comment using '#' and ' ' as token seperators
480 RelplacedComment
= None
481 while Comment
!= RelplacedComment
:
482 RelplacedComment
= Comment
483 Comment
= Comment
.replace('##', '#').replace(' ', ' ').replace(' ', '#').strip('# ')
484 return Comment
.split('#')
488 # Comment - Comment to parse
489 # TypeTokens - A dictionary of type token synonyms
490 # RemoveTokens - A list of tokens to remove from help text
491 # ParseVariable - True for parsing [Guids]. Otherwise False
493 def ParseComment (Comment
, UsageTokens
, TypeTokens
, RemoveTokens
, ParseVariable
):
495 # Initialize return values
506 # Remove white space around first instance of ':' from Comment if 'Variable'
507 # is in front of ':' and Variable is the 1st or 2nd token in Comment.
509 List
= Comment
.split(':', 1)
511 SubList
= GenerateTokenList (List
[0].strip())
512 if len(SubList
) in [1, 2] and SubList
[-1] == 'Variable':
513 if List
[1].strip().find('L"') == 0:
514 Comment
= List
[0].strip() + ':' + List
[1].strip()
517 # Remove first instance of L"<VariableName> from Comment and put into String
518 # if and only if L"<VariableName>" is the 1st token, the 2nd token. Or
519 # L"<VariableName>" is the third token immediately following 'Variable:'.
522 Start
= Comment
.find('Variable:L"')
524 String
= Comment
[Start
+ 9:]
525 End
= String
[2:].find('"')
527 Start
= Comment
.find('L"')
529 String
= Comment
[Start
:]
530 End
= String
[2:].find('"')
532 SubList
= GenerateTokenList (Comment
[:Start
])
534 Comment
= Comment
[:Start
] + String
[End
+ 3:]
535 String
= String
[:End
+ 3]
540 # Initialze HelpText to Comment.
541 # Content will be remove from HelpText as matching tokens are found
546 # Tokenize Comment using '#' and ' ' as token seperators
548 List
= GenerateTokenList (Comment
)
551 # Search first two tokens for Usage and Type and remove any matching tokens
554 for Token
in List
[0:NumTokens
]:
555 if Usage
is None and Token
in UsageTokens
:
556 Usage
= UsageTokens
[Token
]
557 HelpText
= HelpText
.replace(Token
, '')
558 if Usage
is not None or not ParseVariable
:
559 for Token
in List
[0:NumTokens
]:
560 if Type
is None and Token
in TypeTokens
:
561 Type
= TypeTokens
[Token
]
562 HelpText
= HelpText
.replace(Token
, '')
563 if Usage
is not None:
564 for Token
in List
[0:NumTokens
]:
565 if Token
in RemoveTokens
:
566 HelpText
= HelpText
.replace(Token
, '')
569 # If no Usage token is present and set Usage to UNDEFINED
575 # If no Type token is present and set Type to UNDEFINED
581 # If Type is not 'Variable:', then set String to None
583 if Type
!= 'Variable':
587 # Strip ' ' and '#' from the beginning of HelpText
588 # If HelpText is an empty string after all parsing is
589 # complete then set HelpText to None
591 HelpText
= HelpText
.lstrip('# ')
596 # Return parsing results
598 return Usage
, Type
, String
, HelpText