https://bugzilla.tianocore.org/show_bug.cgi?id=1373 Replace BSD 2-Clause License with BSD+Patent License. This change is based on the following emails: https://lists.01.org/pipermail/edk2-devel/2019-February/036260.html https://lists.01.org/pipermail/edk2-devel/2018-October/030385.html RFCs with detailed process for the license change: V3: https://lists.01.org/pipermail/edk2-devel/2019-March/038116.html V2: https://lists.01.org/pipermail/edk2-devel/2019-March/037669.html V1: https://lists.01.org/pipermail/edk2-devel/2019-March/037500.html Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Bob Feng <bob.c.feng@intel.com>
		
			
				
	
	
		
			365 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			365 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| ## @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 = '[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)
 |