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 ErrorCode
= '0x%x' % ErrorCode
218 except ValueError as XStr
:
221 Logger
.Error('Parser',
222 FORMAT_NOT_SUPPORTED
,
223 "The format %s of ErrorCode is not valid, should be UNIT32 type or long type" % Value
,
224 File
= ContainerFile
,
227 ## ParseDecPcdGenericComment
229 # @param GenericComment: Generic comment list, element of (CommentLine,
231 # @param ContainerFile: Input value for filename of Dec file
233 def ParseDecPcdGenericComment (GenericComment
, ContainerFile
, TokenSpaceGuidCName
, CName
, MacroReplaceDict
):
242 for (CommentLine
, LineNum
) in GenericComment
:
243 Comment
= CleanString2(CommentLine
)[1]
247 MACRO_PATTERN
= '[\t\s]*\$\([A-Z][_A-Z0-9]*\)'
248 MatchedStrs
= re
.findall(MACRO_PATTERN
, Comment
)
249 for MatchedStr
in MatchedStrs
:
251 Macro
= MatchedStr
.strip().lstrip('$(').rstrip(')').strip()
252 if Macro
in MacroReplaceDict
:
253 Comment
= Comment
.replace(MatchedStr
, MacroReplaceDict
[Macro
])
254 if Comment
.startswith(TAB_PCD_VALIDRANGE
):
255 if ValidValueNum
> 0 or ExpressionNum
> 0:
256 Logger
.Error('Parser',
257 FORMAT_NOT_SUPPORTED
,
258 ST
.WRN_MULTI_PCD_RANGES
,
259 File
= ContainerFile
,
262 PcdErr
= PcdErrorObject()
263 PcdErr
.SetTokenSpaceGuidCName(TokenSpaceGuidCName
)
264 PcdErr
.SetCName(CName
)
265 PcdErr
.SetFileLine(Comment
)
266 PcdErr
.SetLineNum(LineNum
)
268 ValidRange
= Comment
.replace(TAB_PCD_VALIDRANGE
, "", 1).strip()
269 Valid
, Cause
= _CheckRangeExpression(ValidRange
)
271 ValueList
= ValidRange
.split(TAB_VALUE_SPLIT
)
272 if len(ValueList
) > 1:
273 PcdErr
.SetValidValueRange((TAB_VALUE_SPLIT
.join(ValueList
[1:])).strip())
274 PcdErr
.SetErrorNumber(ParsePcdErrorCode(ValueList
[0], ContainerFile
, LineNum
))
276 PcdErr
.SetValidValueRange(ValidRange
)
277 PcdErrList
.append(PcdErr
)
279 Logger
.Error("Parser",
280 FORMAT_NOT_SUPPORTED
,
284 elif Comment
.startswith(TAB_PCD_VALIDLIST
):
285 if ValidRangeNum
> 0 or ExpressionNum
> 0:
286 Logger
.Error('Parser',
287 FORMAT_NOT_SUPPORTED
,
288 ST
.WRN_MULTI_PCD_RANGES
,
289 File
= ContainerFile
,
291 elif ValidValueNum
> 0:
292 Logger
.Error('Parser',
293 FORMAT_NOT_SUPPORTED
,
294 ST
.WRN_MULTI_PCD_VALIDVALUE
,
295 File
= ContainerFile
,
298 PcdErr
= PcdErrorObject()
299 PcdErr
.SetTokenSpaceGuidCName(TokenSpaceGuidCName
)
300 PcdErr
.SetCName(CName
)
301 PcdErr
.SetFileLine(Comment
)
302 PcdErr
.SetLineNum(LineNum
)
304 ValidValueExpr
= Comment
.replace(TAB_PCD_VALIDLIST
, "", 1).strip()
305 Valid
, Cause
= _CheckListExpression(ValidValueExpr
)
307 ValidValue
= Comment
.replace(TAB_PCD_VALIDLIST
, "", 1).replace(TAB_COMMA_SPLIT
, TAB_SPACE_SPLIT
)
308 ValueList
= ValidValue
.split(TAB_VALUE_SPLIT
)
309 if len(ValueList
) > 1:
310 PcdErr
.SetValidValue((TAB_VALUE_SPLIT
.join(ValueList
[1:])).strip())
311 PcdErr
.SetErrorNumber(ParsePcdErrorCode(ValueList
[0], ContainerFile
, LineNum
))
313 PcdErr
.SetValidValue(ValidValue
)
314 PcdErrList
.append(PcdErr
)
316 Logger
.Error("Parser",
317 FORMAT_NOT_SUPPORTED
,
321 elif Comment
.startswith(TAB_PCD_EXPRESSION
):
322 if ValidRangeNum
> 0 or ValidValueNum
> 0:
323 Logger
.Error('Parser',
324 FORMAT_NOT_SUPPORTED
,
325 ST
.WRN_MULTI_PCD_RANGES
,
326 File
= ContainerFile
,
329 PcdErr
= PcdErrorObject()
330 PcdErr
.SetTokenSpaceGuidCName(TokenSpaceGuidCName
)
331 PcdErr
.SetCName(CName
)
332 PcdErr
.SetFileLine(Comment
)
333 PcdErr
.SetLineNum(LineNum
)
335 Expression
= Comment
.replace(TAB_PCD_EXPRESSION
, "", 1).strip()
336 Valid
, Cause
= _CheckExpression(Expression
)
338 ValueList
= Expression
.split(TAB_VALUE_SPLIT
)
339 if len(ValueList
) > 1:
340 PcdErr
.SetExpression((TAB_VALUE_SPLIT
.join(ValueList
[1:])).strip())
341 PcdErr
.SetErrorNumber(ParsePcdErrorCode(ValueList
[0], ContainerFile
, LineNum
))
343 PcdErr
.SetExpression(Expression
)
344 PcdErrList
.append(PcdErr
)
346 Logger
.Error("Parser",
347 FORMAT_NOT_SUPPORTED
,
351 elif Comment
.startswith(TAB_PCD_PROMPT
):
353 Logger
.Error('Parser',
354 FORMAT_NOT_SUPPORTED
,
355 ST
.WRN_MULTI_PCD_PROMPT
,
356 File
= ContainerFile
,
358 PromptStr
= Comment
.replace(TAB_PCD_PROMPT
, "", 1).strip()
361 HelpStr
+= Comment
+ '\n'
364 # remove the last EOL if the comment is of format 'FOO\n'
366 if HelpStr
.endswith('\n'):
367 if HelpStr
!= '\n' and not HelpStr
.endswith('\n\n'):
368 HelpStr
= HelpStr
[:-1]
370 return HelpStr
, PcdErrList
, PromptStr
372 ## ParseDecPcdTailComment
374 # @param TailCommentList: Tail comment list of Pcd, item of format (Comment, LineNum)
375 # @param ContainerFile: Input value for filename of Dec file
376 # @retVal SupModuleList: The supported module type list detected
377 # @retVal HelpStr: The generic help text string detected
379 def ParseDecPcdTailComment (TailCommentList
, ContainerFile
):
380 assert(len(TailCommentList
) == 1)
381 TailComment
= TailCommentList
[0][0]
382 LineNum
= TailCommentList
[0][1]
384 Comment
= TailComment
.lstrip(" #")
386 ReFindFirstWordRe
= re
.compile(r
"""^([^ #]*)""", re
.DOTALL
)
389 # get first word and compare with SUP_MODULE_LIST
391 MatchObject
= ReFindFirstWordRe
.match(Comment
)
392 if not (MatchObject
and MatchObject
.group(1) in SUP_MODULE_LIST
):
396 # parse line, it must have supported module type specified
398 if Comment
.find(TAB_COMMENT_SPLIT
) == -1:
399 Comment
+= TAB_COMMENT_SPLIT
400 SupMode
, HelpStr
= GetSplitValueList(Comment
, TAB_COMMENT_SPLIT
, 1)
402 for Mod
in GetSplitValueList(SupMode
, TAB_SPACE_SPLIT
):
405 elif Mod
not in SUP_MODULE_LIST
:
408 ST
.WRN_INVALID_MODULE_TYPE
%Mod
,
412 SupModuleList
.append(Mod
)
414 return SupModuleList
, HelpStr
416 ## _CheckListExpression
418 # @param Expression: Pcd value list expression
420 def _CheckListExpression(Expression
):
422 if TAB_VALUE_SPLIT
in Expression
:
423 ListExpr
= Expression
[Expression
.find(TAB_VALUE_SPLIT
)+1:]
425 ListExpr
= Expression
427 return IsValidListExpr(ListExpr
)
431 # @param Expression: Pcd value expression
433 def _CheckExpression(Expression
):
435 if TAB_VALUE_SPLIT
in Expression
:
436 Expr
= Expression
[Expression
.find(TAB_VALUE_SPLIT
)+1:]
439 return IsValidLogicalExpr(Expr
, True)
441 ## _CheckRangeExpression
443 # @param Expression: Pcd range expression
445 def _CheckRangeExpression(Expression
):
447 if TAB_VALUE_SPLIT
in Expression
:
448 RangeExpr
= Expression
[Expression
.find(TAB_VALUE_SPLIT
)+1:]
450 RangeExpr
= Expression
452 return IsValidRangeExpr(RangeExpr
)
458 def ValidateCopyright(Result
, ErrType
, FileName
, LineNo
, ErrMsg
):
460 Logger
.Warn("\nUPT", ErrType
, FileName
, LineNo
, ErrMsg
)
462 ## _ValidateCopyright
464 # @param Line: Line that contains copyright information, # stripped
466 # @retval Result: True if line is conformed to Spec format, False else
467 # @retval ErrMsg: the detailed error description
469 def _ValidateCopyright(Line
):
475 return Result
, ErrMsg
477 def GenerateTokenList (Comment
):
479 # Tokenize Comment using '#' and ' ' as token separators
481 ReplacedComment
= None
482 while Comment
!= ReplacedComment
:
483 ReplacedComment
= Comment
484 Comment
= Comment
.replace('##', '#').replace(' ', ' ').replace(' ', '#').strip('# ')
485 return Comment
.split('#')
489 # Comment - Comment to parse
490 # TypeTokens - A dictionary of type token synonyms
491 # RemoveTokens - A list of tokens to remove from help text
492 # ParseVariable - True for parsing [Guids]. Otherwise False
494 def ParseComment (Comment
, UsageTokens
, TypeTokens
, RemoveTokens
, ParseVariable
):
496 # Initialize return values
507 # Remove white space around first instance of ':' from Comment if 'Variable'
508 # is in front of ':' and Variable is the 1st or 2nd token in Comment.
510 List
= Comment
.split(':', 1)
512 SubList
= GenerateTokenList (List
[0].strip())
513 if len(SubList
) in [1, 2] and SubList
[-1] == 'Variable':
514 if List
[1].strip().find('L"') == 0:
515 Comment
= List
[0].strip() + ':' + List
[1].strip()
518 # Remove first instance of L"<VariableName> from Comment and put into String
519 # if and only if L"<VariableName>" is the 1st token, the 2nd token. Or
520 # L"<VariableName>" is the third token immediately following 'Variable:'.
523 Start
= Comment
.find('Variable:L"')
525 String
= Comment
[Start
+ 9:]
526 End
= String
[2:].find('"')
528 Start
= Comment
.find('L"')
530 String
= Comment
[Start
:]
531 End
= String
[2:].find('"')
533 SubList
= GenerateTokenList (Comment
[:Start
])
535 Comment
= Comment
[:Start
] + String
[End
+ 3:]
536 String
= String
[:End
+ 3]
541 # Initialize HelpText to Comment.
542 # Content will be remove from HelpText as matching tokens are found
547 # Tokenize Comment using '#' and ' ' as token separators
549 List
= GenerateTokenList (Comment
)
552 # Search first two tokens for Usage and Type and remove any matching tokens
555 for Token
in List
[0:NumTokens
]:
556 if Usage
is None and Token
in UsageTokens
:
557 Usage
= UsageTokens
[Token
]
558 HelpText
= HelpText
.replace(Token
, '')
559 if Usage
is not None or not ParseVariable
:
560 for Token
in List
[0:NumTokens
]:
561 if Type
is None and Token
in TypeTokens
:
562 Type
= TypeTokens
[Token
]
563 HelpText
= HelpText
.replace(Token
, '')
564 if Usage
is not None:
565 for Token
in List
[0:NumTokens
]:
566 if Token
in RemoveTokens
:
567 HelpText
= HelpText
.replace(Token
, '')
570 # If no Usage token is present and set Usage to UNDEFINED
576 # If no Type token is present and set Type to UNDEFINED
582 # If Type is not 'Variable:', then set String to None
584 if Type
!= 'Variable':
588 # Strip ' ' and '#' from the beginning of HelpText
589 # If HelpText is an empty string after all parsing is
590 # complete then set HelpText to None
592 HelpText
= HelpText
.lstrip('# ')
597 # Return parsing results
599 return Usage
, Type
, String
, HelpText