| ## @file | |
| # This file is used to define helper class and function for DEC parser | |
| # | |
| # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> | |
| # | |
| # SPDX-License-Identifier: BSD-2-Clause-Patent | |
| ''' | |
| DecParserMisc | |
| ''' | |
| ## Import modules | |
| # | |
| import os | |
| import Logger.Log as Logger | |
| from Logger.ToolError import FILE_PARSE_FAILURE | |
| from Logger import StringTable as ST | |
| from Library.DataType import TAB_COMMENT_SPLIT | |
| from Library.DataType import TAB_COMMENT_EDK1_SPLIT | |
| from Library.ExpressionValidate import IsValidBareCString | |
| from Library.ParserValidate import IsValidCFormatGuid | |
| from Library.ExpressionValidate import IsValidFeatureFlagExp | |
| from Library.ExpressionValidate import IsValidLogicalExpr | |
| from Library.ExpressionValidate import IsValidStringTest | |
| from Library.Misc import CheckGuidRegFormat | |
| TOOL_NAME = 'DecParser' | |
| VERSION_PATTERN = r'[0-9]+(\.[0-9]+)?' | |
| CVAR_PATTERN = '[_a-zA-Z][a-zA-Z0-9_]*' | |
| PCD_TOKEN_PATTERN = '(0[xX]0*[a-fA-F0-9]{1,8})|([0-9]+)' | |
| MACRO_PATTERN = '[A-Z][_A-Z0-9]*' | |
| ## FileContent | |
| # Class to hold DEC file information | |
| # | |
| class FileContent: | |
| def __init__(self, Filename, FileContent2): | |
| self.Filename = Filename | |
| self.PackagePath, self.PackageFile = os.path.split(Filename) | |
| self.LineIndex = 0 | |
| self.CurrentLine = '' | |
| self.NextLine = '' | |
| self.HeadComment = [] | |
| self.TailComment = [] | |
| self.CurrentScope = None | |
| self.Content = FileContent2 | |
| self.Macros = {} | |
| self.FileLines = len(FileContent2) | |
| def GetNextLine(self): | |
| if self.LineIndex >= self.FileLines: | |
| return '' | |
| Line = self.Content[self.LineIndex] | |
| self.LineIndex += 1 | |
| return Line | |
| def UndoNextLine(self): | |
| if self.LineIndex > 0: | |
| self.LineIndex -= 1 | |
| def ResetNext(self): | |
| self.HeadComment = [] | |
| self.TailComment = [] | |
| self.NextLine = '' | |
| def SetNext(self, Line, HeadComment, TailComment): | |
| self.NextLine = Line | |
| self.HeadComment = HeadComment | |
| self.TailComment = TailComment | |
| def IsEndOfFile(self): | |
| return self.LineIndex >= self.FileLines | |
| ## StripRoot | |
| # | |
| # Strip root path | |
| # | |
| # @param Root: Root must be absolute path | |
| # @param Path: Path to be stripped | |
| # | |
| def StripRoot(Root, Path): | |
| OrigPath = Path | |
| Root = os.path.normpath(Root) | |
| Path = os.path.normpath(Path) | |
| if not os.path.isabs(Root): | |
| return OrigPath | |
| if Path.startswith(Root): | |
| Path = Path[len(Root):] | |
| if Path and Path[0] == os.sep: | |
| Path = Path[1:] | |
| return Path | |
| return OrigPath | |
| ## CleanString | |
| # | |
| # Split comments in a string | |
| # Remove spaces | |
| # | |
| # @param Line: The string to be cleaned | |
| # @param CommentCharacter: Comment char, used to ignore comment content, | |
| # default is DataType.TAB_COMMENT_SPLIT | |
| # | |
| def CleanString(Line, CommentCharacter=TAB_COMMENT_SPLIT, \ | |
| AllowCppStyleComment=False): | |
| # | |
| # remove whitespace | |
| # | |
| Line = Line.strip() | |
| # | |
| # Replace EDK1's comment character | |
| # | |
| if AllowCppStyleComment: | |
| Line = Line.replace(TAB_COMMENT_EDK1_SPLIT, CommentCharacter) | |
| # | |
| # separate comments and statements | |
| # | |
| Comment = '' | |
| InQuote = False | |
| for Index in range(0, len(Line)): | |
| if Line[Index] == '"': | |
| InQuote = not InQuote | |
| continue | |
| if Line[Index] == CommentCharacter and not InQuote: | |
| Comment = Line[Index:].strip() | |
| Line = Line[0:Index].strip() | |
| break | |
| return Line, Comment | |
| ## IsValidNumValUint8 | |
| # | |
| # Check if Token is NumValUint8: <NumValUint8> ::= {<ShortNum>} {<UINT8>} {<Expression>} | |
| # | |
| # @param Token: Token to be checked | |
| # | |
| def IsValidNumValUint8(Token): | |
| Valid = True | |
| Cause = "" | |
| TokenValue = None | |
| Token = Token.strip() | |
| if Token.lower().startswith('0x'): | |
| Base = 16 | |
| else: | |
| Base = 10 | |
| try: | |
| TokenValue = int(Token, Base) | |
| except BaseException: | |
| Valid, Cause = IsValidLogicalExpr(Token, True) | |
| if Cause: | |
| pass | |
| if not Valid: | |
| return False | |
| if TokenValue and (TokenValue < 0 or TokenValue > 0xFF): | |
| return False | |
| else: | |
| return True | |
| ## IsValidNList | |
| # | |
| # Check if Value has the format of <NumValUint8> ["," <NumValUint8>]{0,} | |
| # <NumValUint8> ::= {<ShortNum>} {<UINT8>} {<Expression>} | |
| # | |
| # @param Value: Value to be checked | |
| # | |
| def IsValidNList(Value): | |
| Par = ParserHelper(Value) | |
| if Par.End(): | |
| return False | |
| while not Par.End(): | |
| Token = Par.GetToken(',') | |
| if not IsValidNumValUint8(Token): | |
| return False | |
| if Par.Expect(','): | |
| if Par.End(): | |
| return False | |
| continue | |
| else: | |
| break | |
| return Par.End() | |
| ## IsValidCArray | |
| # | |
| # check Array is valid | |
| # | |
| # @param Array: The input Array | |
| # | |
| def IsValidCArray(Array): | |
| Par = ParserHelper(Array) | |
| if not Par.Expect('{'): | |
| return False | |
| if Par.End(): | |
| return False | |
| while not Par.End(): | |
| Token = Par.GetToken(',}') | |
| # | |
| # ShortNum, UINT8, Expression | |
| # | |
| if not IsValidNumValUint8(Token): | |
| return False | |
| if Par.Expect(','): | |
| if Par.End(): | |
| return False | |
| continue | |
| elif Par.Expect('}'): | |
| # | |
| # End of C array | |
| # | |
| break | |
| else: | |
| return False | |
| return Par.End() | |
| ## IsValidPcdDatum | |
| # | |
| # check PcdDatum is valid | |
| # | |
| # @param Type: The pcd Type | |
| # @param Value: The pcd Value | |
| # | |
| def IsValidPcdDatum(Type, Value): | |
| if not Value: | |
| return False, ST.ERR_DECPARSE_PCD_VALUE_EMPTY | |
| Valid = True | |
| Cause = "" | |
| if Type not in ["UINT8", "UINT16", "UINT32", "UINT64", "VOID*", "BOOLEAN"]: | |
| return False, ST.ERR_DECPARSE_PCD_TYPE | |
| if Type == "VOID*": | |
| if not ((Value.startswith('L"') or Value.startswith('"') and \ | |
| Value.endswith('"')) | |
| or (IsValidCArray(Value)) or (IsValidCFormatGuid(Value)) \ | |
| or (IsValidNList(Value)) or (CheckGuidRegFormat(Value)) | |
| ): | |
| return False, ST.ERR_DECPARSE_PCD_VOID % (Value, Type) | |
| RealString = Value[Value.find('"') + 1 :-1] | |
| if RealString: | |
| if not IsValidBareCString(RealString): | |
| return False, ST.ERR_DECPARSE_PCD_VOID % (Value, Type) | |
| elif Type == 'BOOLEAN': | |
| if Value in ['TRUE', 'FALSE', 'true', 'false', 'True', 'False', | |
| '0x1', '0x01', '1', '0x0', '0x00', '0']: | |
| return True, "" | |
| Valid, Cause = IsValidStringTest(Value, True) | |
| if not Valid: | |
| Valid, Cause = IsValidFeatureFlagExp(Value, True) | |
| if not Valid: | |
| return False, Cause | |
| else: | |
| if Value and (Value[0] == '-' or Value[0] == '+'): | |
| return False, ST.ERR_DECPARSE_PCD_INT_NEGTIVE % (Value, Type) | |
| try: | |
| StrVal = Value | |
| if Value and not Value.startswith('0x') \ | |
| and not Value.startswith('0X'): | |
| Value = Value.lstrip('0') | |
| if not Value: | |
| return True, "" | |
| Value = int(Value, 0) | |
| MAX_VAL_TYPE = {"BOOLEAN": 0x01, 'UINT8': 0xFF, 'UINT16': 0xFFFF, 'UINT32': 0xFFFFFFFF, | |
| 'UINT64': 0xFFFFFFFFFFFFFFFF} | |
| if Value > MAX_VAL_TYPE[Type]: | |
| return False, ST.ERR_DECPARSE_PCD_INT_EXCEED % (StrVal, Type) | |
| except BaseException: | |
| Valid, Cause = IsValidLogicalExpr(Value, True) | |
| if not Valid: | |
| return False, Cause | |
| return True, "" | |
| ## ParserHelper | |
| # | |
| class ParserHelper: | |
| def __init__(self, String, File=''): | |
| self._String = String | |
| self._StrLen = len(String) | |
| self._Index = 0 | |
| self._File = File | |
| ## End | |
| # | |
| # End | |
| # | |
| def End(self): | |
| self.__SkipWhitespace() | |
| return self._Index >= self._StrLen | |
| ## __SkipWhitespace | |
| # | |
| # Skip whitespace | |
| # | |
| def __SkipWhitespace(self): | |
| for Char in self._String[self._Index:]: | |
| if Char not in ' \t': | |
| break | |
| self._Index += 1 | |
| ## Expect | |
| # | |
| # Expect char in string | |
| # | |
| # @param ExpectChar: char expected in index of string | |
| # | |
| def Expect(self, ExpectChar): | |
| self.__SkipWhitespace() | |
| for Char in self._String[self._Index:]: | |
| if Char != ExpectChar: | |
| return False | |
| else: | |
| self._Index += 1 | |
| return True | |
| # | |
| # Index out of bound of String | |
| # | |
| return False | |
| ## GetToken | |
| # | |
| # Get token until encounter StopChar, front whitespace is consumed | |
| # | |
| # @param StopChar: Get token until encounter char in StopChar | |
| # @param StkipPair: Only can be ' or ", StopChar in SkipPair are skipped | |
| # | |
| def GetToken(self, StopChar='.,|\t ', SkipPair='"'): | |
| self.__SkipWhitespace() | |
| PreIndex = self._Index | |
| InQuote = False | |
| LastChar = '' | |
| for Char in self._String[self._Index:]: | |
| if Char == SkipPair and LastChar != '\\': | |
| InQuote = not InQuote | |
| if Char in StopChar and not InQuote: | |
| break | |
| self._Index += 1 | |
| if Char == '\\' and LastChar == '\\': | |
| LastChar = '' | |
| else: | |
| LastChar = Char | |
| return self._String[PreIndex:self._Index] | |
| ## AssertChar | |
| # | |
| # Assert char at current index of string is AssertChar, or will report | |
| # error message | |
| # | |
| # @param AssertChar: AssertChar | |
| # @param ErrorString: ErrorString | |
| # @param ErrorLineNum: ErrorLineNum | |
| # | |
| def AssertChar(self, AssertChar, ErrorString, ErrorLineNum): | |
| if not self.Expect(AssertChar): | |
| Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._File, | |
| Line=ErrorLineNum, ExtraData=ErrorString) | |
| ## AssertEnd | |
| # | |
| # @param ErrorString: ErrorString | |
| # @param ErrorLineNum: ErrorLineNum | |
| # | |
| def AssertEnd(self, ErrorString, ErrorLineNum): | |
| self.__SkipWhitespace() | |
| if self._Index != self._StrLen: | |
| Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._File, | |
| Line=ErrorLineNum, ExtraData=ErrorString) |