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)
 |