## @file # This file is used to define comment parsing interface # # Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.
# # This program and the accompanying materials are licensed and made available # under the terms and conditions of the BSD License which accompanies this # distribution. The full text of the license may be found at # http://opensource.org/licenses/bsd-license.php # # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. # ''' CommentParsing ''' ## # Import Modules # import re from Library.String import GetSplitValueList from Library.String import CleanString2 from Library.DataType import HEADER_COMMENT_NOT_STARTED from Library.DataType import TAB_COMMENT_SPLIT from Library.DataType import HEADER_COMMENT_LICENSE from Library.DataType import HEADER_COMMENT_ABSTRACT from Library.DataType import HEADER_COMMENT_COPYRIGHT from Library.DataType import HEADER_COMMENT_DESCRIPTION from Library.DataType import TAB_SPACE_SPLIT from Library.DataType import TAB_COMMA_SPLIT from Library.DataType import SUP_MODULE_LIST from Library.DataType import TAB_VALUE_SPLIT from Library.DataType import TAB_PCD_VALIDRANGE from Library.DataType import TAB_PCD_VALIDLIST from Library.DataType import TAB_PCD_EXPRESSION from Library.DataType import TAB_PCD_PROMPT from Library.DataType import TAB_CAPHEX_START from Library.DataType import TAB_HEX_START from Library.DataType import PCD_ERR_CODE_MAX_SIZE from Library.ExpressionValidate import IsValidRangeExpr from Library.ExpressionValidate import IsValidListExpr from Library.ExpressionValidate import IsValidLogicalExpr from Object.POM.CommonObject import TextObject from Object.POM.CommonObject import PcdErrorObject import Logger.Log as Logger from Logger.ToolError import FORMAT_INVALID from Logger.ToolError import FORMAT_NOT_SUPPORTED from Logger import StringTable as ST ## ParseHeaderCommentSection # # Parse Header comment section lines, extract Abstract, Description, Copyright # , License lines # # @param CommentList: List of (Comment, LineNumber) # @param FileName: FileName of the comment # def ParseHeaderCommentSection(CommentList, FileName = None, IsBinaryHeader = False): Abstract = '' Description = '' Copyright = '' License = '' EndOfLine = "\n" if IsBinaryHeader: STR_HEADER_COMMENT_START = "@BinaryHeader" else: STR_HEADER_COMMENT_START = "@file" HeaderCommentStage = HEADER_COMMENT_NOT_STARTED # # first find the last copyright line # Last = 0 for Index in xrange(len(CommentList)-1, 0, -1): Line = CommentList[Index][0] if _IsCopyrightLine(Line): Last = Index break for Item in CommentList: Line = Item[0] LineNo = Item[1] if not Line.startswith(TAB_COMMENT_SPLIT) and Line: Logger.Error("\nUPT", FORMAT_INVALID, ST.ERR_INVALID_COMMENT_FORMAT, FileName, Item[1]) Comment = CleanString2(Line)[1] Comment = Comment.strip() # # if there are blank lines between License or Description, keep them as they would be # indication of different block; or in the position that Abstract should be, also keep it # as it indicates that no abstract # if not Comment and HeaderCommentStage not in [HEADER_COMMENT_LICENSE, \ HEADER_COMMENT_DESCRIPTION, HEADER_COMMENT_ABSTRACT]: continue if HeaderCommentStage == HEADER_COMMENT_NOT_STARTED: if Comment.startswith(STR_HEADER_COMMENT_START): HeaderCommentStage = HEADER_COMMENT_ABSTRACT else: License += Comment + EndOfLine else: if HeaderCommentStage == HEADER_COMMENT_ABSTRACT: # # in case there is no abstract and description # if not Comment: HeaderCommentStage = HEADER_COMMENT_DESCRIPTION elif _IsCopyrightLine(Comment): Result, ErrMsg = _ValidateCopyright(Comment) ValidateCopyright(Result, ST.WRN_INVALID_COPYRIGHT, FileName, LineNo, ErrMsg) Copyright += Comment + EndOfLine HeaderCommentStage = HEADER_COMMENT_COPYRIGHT else: Abstract += Comment + EndOfLine HeaderCommentStage = HEADER_COMMENT_DESCRIPTION elif HeaderCommentStage == HEADER_COMMENT_DESCRIPTION: # # in case there is no description # if _IsCopyrightLine(Comment): Result, ErrMsg = _ValidateCopyright(Comment) ValidateCopyright(Result, ST.WRN_INVALID_COPYRIGHT, FileName, LineNo, ErrMsg) Copyright += Comment + EndOfLine HeaderCommentStage = HEADER_COMMENT_COPYRIGHT else: Description += Comment + EndOfLine elif HeaderCommentStage == HEADER_COMMENT_COPYRIGHT: if _IsCopyrightLine(Comment): Result, ErrMsg = _ValidateCopyright(Comment) ValidateCopyright(Result, ST.WRN_INVALID_COPYRIGHT, FileName, LineNo, ErrMsg) Copyright += Comment + EndOfLine else: # # Contents after copyright line are license, those non-copyright lines in between # copyright line will be discarded # if LineNo > Last: if License: License += EndOfLine License += Comment + EndOfLine HeaderCommentStage = HEADER_COMMENT_LICENSE else: if not Comment and not License: continue License += Comment + EndOfLine return Abstract.strip(), Description.strip(), Copyright.strip(), License.strip() ## _IsCopyrightLine # check whether current line is copyright line, the criteria is whether there is case insensitive keyword "Copyright" # followed by zero or more white space characters followed by a "(" character # # @param LineContent: the line need to be checked # @return: True if current line is copyright line, False else # def _IsCopyrightLine (LineContent): LineContent = LineContent.upper() Result = False ReIsCopyrightRe = re.compile(r"""(^|\s)COPYRIGHT *\(""", re.DOTALL) if ReIsCopyrightRe.search(LineContent): Result = True return Result ## ParseGenericComment # # @param GenericComment: Generic comment list, element of # (CommentLine, LineNum) # @param ContainerFile: Input value for filename of Dec file # def ParseGenericComment (GenericComment, ContainerFile=None, SkipTag=None): if ContainerFile: pass HelpTxt = None HelpStr = '' for Item in GenericComment: CommentLine = Item[0] Comment = CleanString2(CommentLine)[1] if SkipTag is not None and Comment.startswith(SkipTag): Comment = Comment.replace(SkipTag, '', 1) HelpStr += Comment + '\n' if HelpStr: HelpTxt = TextObject() if HelpStr.endswith('\n') and not HelpStr.endswith('\n\n') and HelpStr != '\n': HelpStr = HelpStr[:-1] HelpTxt.SetString(HelpStr) return HelpTxt ## ParsePcdErrorCode # # @param Value: original ErrorCode value # @param ContainerFile: Input value for filename of Dec file # @param LineNum: Line Num # def ParsePcdErrorCode (Value = None, ContainerFile = None, LineNum = None): try: if Value.strip().startswith((TAB_HEX_START, TAB_CAPHEX_START)): Base = 16 else: Base = 10 ErrorCode = long(Value, Base) if ErrorCode > PCD_ERR_CODE_MAX_SIZE or ErrorCode < 0: Logger.Error('Parser', FORMAT_NOT_SUPPORTED, "The format %s of ErrorCode is not valid, should be UNIT32 type or long type" % Value, File = ContainerFile, Line = LineNum) # # To delete the tailing 'L' # return hex(ErrorCode)[:-1] except ValueError, XStr: if XStr: pass Logger.Error('Parser', FORMAT_NOT_SUPPORTED, "The format %s of ErrorCode is not valid, should be UNIT32 type or long type" % Value, File = ContainerFile, Line = LineNum) ## ParseDecPcdGenericComment # # @param GenericComment: Generic comment list, element of (CommentLine, # LineNum) # @param ContainerFile: Input value for filename of Dec file # def ParseDecPcdGenericComment (GenericComment, ContainerFile, TokenSpaceGuidCName, CName, MacroReplaceDict): HelpStr = '' PromptStr = '' PcdErr = None PcdErrList = [] ValidValueNum = 0 ValidRangeNum = 0 ExpressionNum = 0 for (CommentLine, LineNum) in GenericComment: Comment = CleanString2(CommentLine)[1] # # To replace Macro # MACRO_PATTERN = '[\t\s]*\$\([A-Z][_A-Z0-9]*\)' MatchedStrs = re.findall(MACRO_PATTERN, Comment) for MatchedStr in MatchedStrs: if MatchedStr: Macro = MatchedStr.strip().lstrip('$(').rstrip(')').strip() if Macro in MacroReplaceDict: Comment = Comment.replace(MatchedStr, MacroReplaceDict[Macro]) if Comment.startswith(TAB_PCD_VALIDRANGE): if ValidValueNum > 0 or ExpressionNum > 0: Logger.Error('Parser', FORMAT_NOT_SUPPORTED, ST.WRN_MULTI_PCD_RANGES, File = ContainerFile, Line = LineNum) else: PcdErr = PcdErrorObject() PcdErr.SetTokenSpaceGuidCName(TokenSpaceGuidCName) PcdErr.SetCName(CName) PcdErr.SetFileLine(Comment) PcdErr.SetLineNum(LineNum) ValidRangeNum += 1 ValidRange = Comment.replace(TAB_PCD_VALIDRANGE, "", 1).strip() Valid, Cause = _CheckRangeExpression(ValidRange) if Valid: ValueList = ValidRange.split(TAB_VALUE_SPLIT) if len(ValueList) > 1: PcdErr.SetValidValueRange((TAB_VALUE_SPLIT.join(ValueList[1:])).strip()) PcdErr.SetErrorNumber(ParsePcdErrorCode(ValueList[0], ContainerFile, LineNum)) else: PcdErr.SetValidValueRange(ValidRange) PcdErrList.append(PcdErr) else: Logger.Error("Parser", FORMAT_NOT_SUPPORTED, Cause, ContainerFile, LineNum) elif Comment.startswith(TAB_PCD_VALIDLIST): if ValidRangeNum > 0 or ExpressionNum > 0: Logger.Error('Parser', FORMAT_NOT_SUPPORTED, ST.WRN_MULTI_PCD_RANGES, File = ContainerFile, Line = LineNum) elif ValidValueNum > 0: Logger.Error('Parser', FORMAT_NOT_SUPPORTED, ST.WRN_MULTI_PCD_VALIDVALUE, File = ContainerFile, Line = LineNum) else: PcdErr = PcdErrorObject() PcdErr.SetTokenSpaceGuidCName(TokenSpaceGuidCName) PcdErr.SetCName(CName) PcdErr.SetFileLine(Comment) PcdErr.SetLineNum(LineNum) ValidValueNum += 1 ValidValueExpr = Comment.replace(TAB_PCD_VALIDLIST, "", 1).strip() Valid, Cause = _CheckListExpression(ValidValueExpr) if Valid: ValidValue = Comment.replace(TAB_PCD_VALIDLIST, "", 1).replace(TAB_COMMA_SPLIT, TAB_SPACE_SPLIT) ValueList = ValidValue.split(TAB_VALUE_SPLIT) if len(ValueList) > 1: PcdErr.SetValidValue((TAB_VALUE_SPLIT.join(ValueList[1:])).strip()) PcdErr.SetErrorNumber(ParsePcdErrorCode(ValueList[0], ContainerFile, LineNum)) else: PcdErr.SetValidValue(ValidValue) PcdErrList.append(PcdErr) else: Logger.Error("Parser", FORMAT_NOT_SUPPORTED, Cause, ContainerFile, LineNum) elif Comment.startswith(TAB_PCD_EXPRESSION): if ValidRangeNum > 0 or ValidValueNum > 0: Logger.Error('Parser', FORMAT_NOT_SUPPORTED, ST.WRN_MULTI_PCD_RANGES, File = ContainerFile, Line = LineNum) else: PcdErr = PcdErrorObject() PcdErr.SetTokenSpaceGuidCName(TokenSpaceGuidCName) PcdErr.SetCName(CName) PcdErr.SetFileLine(Comment) PcdErr.SetLineNum(LineNum) ExpressionNum += 1 Expression = Comment.replace(TAB_PCD_EXPRESSION, "", 1).strip() Valid, Cause = _CheckExpression(Expression) if Valid: ValueList = Expression.split(TAB_VALUE_SPLIT) if len(ValueList) > 1: PcdErr.SetExpression((TAB_VALUE_SPLIT.join(ValueList[1:])).strip()) PcdErr.SetErrorNumber(ParsePcdErrorCode(ValueList[0], ContainerFile, LineNum)) else: PcdErr.SetExpression(Expression) PcdErrList.append(PcdErr) else: Logger.Error("Parser", FORMAT_NOT_SUPPORTED, Cause, ContainerFile, LineNum) elif Comment.startswith(TAB_PCD_PROMPT): if PromptStr: Logger.Error('Parser', FORMAT_NOT_SUPPORTED, ST.WRN_MULTI_PCD_PROMPT, File = ContainerFile, Line = LineNum) PromptStr = Comment.replace(TAB_PCD_PROMPT, "", 1).strip() else: if Comment: HelpStr += Comment + '\n' # # remove the last EOL if the comment is of format 'FOO\n' # if HelpStr.endswith('\n'): if HelpStr != '\n' and not HelpStr.endswith('\n\n'): HelpStr = HelpStr[:-1] return HelpStr, PcdErrList, PromptStr ## ParseDecPcdTailComment # # @param TailCommentList: Tail comment list of Pcd, item of format (Comment, LineNum) # @param ContainerFile: Input value for filename of Dec file # @retVal SupModuleList: The supported module type list detected # @retVal HelpStr: The generic help text string detected # def ParseDecPcdTailComment (TailCommentList, ContainerFile): assert(len(TailCommentList) == 1) TailComment = TailCommentList[0][0] LineNum = TailCommentList[0][1] Comment = TailComment.lstrip(" #") ReFindFirstWordRe = re.compile(r"""^([^ #]*)""", re.DOTALL) # # get first word and compare with SUP_MODULE_LIST # MatchObject = ReFindFirstWordRe.match(Comment) if not (MatchObject and MatchObject.group(1) in SUP_MODULE_LIST): return None, Comment # # parse line, it must have supported module type specified # if Comment.find(TAB_COMMENT_SPLIT) == -1: Comment += TAB_COMMENT_SPLIT SupMode, HelpStr = GetSplitValueList(Comment, TAB_COMMENT_SPLIT, 1) SupModuleList = [] for Mod in GetSplitValueList(SupMode, TAB_SPACE_SPLIT): if not Mod: continue elif Mod not in SUP_MODULE_LIST: Logger.Error("UPT", FORMAT_INVALID, ST.WRN_INVALID_MODULE_TYPE%Mod, ContainerFile, LineNum) else: SupModuleList.append(Mod) return SupModuleList, HelpStr ## _CheckListExpression # # @param Expression: Pcd value list expression # def _CheckListExpression(Expression): ListExpr = '' if TAB_VALUE_SPLIT in Expression: ListExpr = Expression[Expression.find(TAB_VALUE_SPLIT)+1:] else: ListExpr = Expression return IsValidListExpr(ListExpr) ## _CheckExpreesion # # @param Expression: Pcd value expression # def _CheckExpression(Expression): Expr = '' if TAB_VALUE_SPLIT in Expression: Expr = Expression[Expression.find(TAB_VALUE_SPLIT)+1:] else: Expr = Expression return IsValidLogicalExpr(Expr, True) ## _CheckRangeExpression # # @param Expression: Pcd range expression # def _CheckRangeExpression(Expression): RangeExpr = '' if TAB_VALUE_SPLIT in Expression: RangeExpr = Expression[Expression.find(TAB_VALUE_SPLIT)+1:] else: RangeExpr = Expression return IsValidRangeExpr(RangeExpr) ## ValidateCopyright # # # def ValidateCopyright(Result, ErrType, FileName, LineNo, ErrMsg): if not Result: Logger.Warn("\nUPT", ErrType, FileName, LineNo, ErrMsg) ## _ValidateCopyright # # @param Line: Line that contains copyright information, # stripped # # @retval Result: True if line is conformed to Spec format, False else # @retval ErrMsg: the detailed error description # def _ValidateCopyright(Line): if Line: pass Result = True ErrMsg = '' return Result, ErrMsg def GenerateTokenList (Comment): # # Tokenize Comment using '#' and ' ' as token seperators # RelplacedComment = None while Comment != RelplacedComment: RelplacedComment = Comment Comment = Comment.replace('##', '#').replace(' ', ' ').replace(' ', '#').strip('# ') return Comment.split('#') # # Comment - Comment to parse # TypeTokens - A dictionary of type token synonyms # RemoveTokens - A list of tokens to remove from help text # ParseVariable - True for parsing [Guids]. Otherwise False # def ParseComment (Comment, UsageTokens, TypeTokens, RemoveTokens, ParseVariable): # # Initialize return values # Usage = None Type = None String = None Comment = Comment[0] NumTokens = 2 if ParseVariable: # # Remove white space around first instance of ':' from Comment if 'Variable' # is in front of ':' and Variable is the 1st or 2nd token in Comment. # List = Comment.split(':', 1) if len(List) > 1: SubList = GenerateTokenList (List[0].strip()) if len(SubList) in [1, 2] and SubList[-1] == 'Variable': if List[1].strip().find('L"') == 0: Comment = List[0].strip() + ':' + List[1].strip() # # Remove first instance of L" from Comment and put into String # if and only if L"" is the 1st token, the 2nd token. Or # L"" is the third token immediately following 'Variable:'. # End = -1 Start = Comment.find('Variable:L"') if Start >= 0: String = Comment[Start + 9:] End = String[2:].find('"') else: Start = Comment.find('L"') if Start >= 0: String = Comment[Start:] End = String[2:].find('"') if End >= 0: SubList = GenerateTokenList (Comment[:Start]) if len(SubList) < 2: Comment = Comment[:Start] + String[End + 3:] String = String[:End + 3] Type = 'Variable' NumTokens = 1 # # Initialze HelpText to Comment. # Content will be remove from HelpText as matching tokens are found # HelpText = Comment # # Tokenize Comment using '#' and ' ' as token seperators # List = GenerateTokenList (Comment) # # Search first two tokens for Usage and Type and remove any matching tokens # from HelpText # for Token in List[0:NumTokens]: if Usage == None and Token in UsageTokens: Usage = UsageTokens[Token] HelpText = HelpText.replace(Token, '') if Usage != None or not ParseVariable: for Token in List[0:NumTokens]: if Type == None and Token in TypeTokens: Type = TypeTokens[Token] HelpText = HelpText.replace(Token, '') if Usage != None: for Token in List[0:NumTokens]: if Token in RemoveTokens: HelpText = HelpText.replace(Token, '') # # If no Usage token is present and set Usage to UNDEFINED # if Usage == None: Usage = 'UNDEFINED' # # If no Type token is present and set Type to UNDEFINED # if Type == None: Type = 'UNDEFINED' # # If Type is not 'Variable:', then set String to None # if Type != 'Variable': String = None # # Strip ' ' and '#' from the beginning of HelpText # If HelpText is an empty string after all parsing is # complete then set HelpText to None # HelpText = HelpText.lstrip('# ') if HelpText == '': HelpText = None # # Return parsing results # return Usage, Type, String, HelpText