Home | History | Annotate | Download | only in Parser
      1 ## @file

      2 # This file is used to define helper class and function for DEC parser

      3 #

      4 # Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>

      5 #

      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

     10 #

     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.

     13 
     14 '''
     15 DecParserMisc
     16 '''
     17 
     18 ## Import modules

     19 #

     20 import os
     21 import Logger.Log as Logger
     22 from Logger.ToolError import FILE_PARSE_FAILURE
     23 from Logger import StringTable as ST
     24 from Library.DataType import TAB_COMMENT_SPLIT
     25 from Library.DataType import TAB_COMMENT_EDK1_SPLIT
     26 from Library.ExpressionValidate import IsValidBareCString
     27 from Library.ParserValidate import IsValidCFormatGuid
     28 from Library.ExpressionValidate import IsValidFeatureFlagExp
     29 from Library.ExpressionValidate import IsValidLogicalExpr
     30 from Library.ExpressionValidate import IsValidStringTest
     31 from Library.Misc import CheckGuidRegFormat
     32 
     33 TOOL_NAME = 'DecParser'
     34 VERSION_PATTERN = '[0-9]+(\.[0-9]+)?'
     35 CVAR_PATTERN = '[_a-zA-Z][a-zA-Z0-9_]*'
     36 PCD_TOKEN_PATTERN = '(0[xX]0*[a-fA-F0-9]{1,8})|([0-9]+)'
     37 MACRO_PATTERN = '[A-Z][_A-Z0-9]*'
     38 
     39 ## FileContent

     40 # Class to hold DEC file information

     41 #

     42 class FileContent:
     43     def __init__(self, Filename, FileContent2):
     44         self.Filename = Filename
     45         self.PackagePath, self.PackageFile = os.path.split(Filename)
     46         self.LineIndex = 0
     47         self.CurrentLine = ''
     48         self.NextLine = ''
     49         self.HeadComment = []
     50         self.TailComment = []
     51         self.CurrentScope = None
     52         self.Content = FileContent2
     53         self.Macros = {}
     54         self.FileLines = len(FileContent2)
     55 
     56     def GetNextLine(self):
     57         if self.LineIndex >= self.FileLines:
     58             return ''
     59         Line = self.Content[self.LineIndex]
     60         self.LineIndex += 1
     61         return Line
     62 
     63     def UndoNextLine(self):
     64         if self.LineIndex > 0:
     65             self.LineIndex -= 1
     66 
     67     def ResetNext(self):
     68         self.HeadComment = []
     69         self.TailComment = []
     70         self.NextLine = ''
     71 
     72     def SetNext(self, Line, HeadComment, TailComment):
     73         self.NextLine = Line
     74         self.HeadComment = HeadComment
     75         self.TailComment = TailComment
     76 
     77     def IsEndOfFile(self):
     78         return self.LineIndex >= self.FileLines
     79 
     80 
     81 ## StripRoot

     82 #

     83 # Strip root path

     84 #

     85 # @param Root: Root must be absolute path

     86 # @param Path: Path to be stripped

     87 #

     88 def StripRoot(Root, Path):
     89     OrigPath = Path
     90     Root = os.path.normpath(Root)
     91     Path = os.path.normpath(Path)
     92     if not os.path.isabs(Root):
     93         return OrigPath
     94     if Path.startswith(Root):
     95         Path = Path[len(Root):]
     96         if Path and Path[0] == os.sep:
     97             Path = Path[1:]
     98         return Path
     99     return OrigPath
    100 
    101 ## CleanString

    102 #

    103 # Split comments in a string

    104 # Remove spaces

    105 #

    106 # @param Line:              The string to be cleaned

    107 # @param CommentCharacter:  Comment char, used to ignore comment content, 

    108 #                           default is DataType.TAB_COMMENT_SPLIT

    109 #

    110 def CleanString(Line, CommentCharacter=TAB_COMMENT_SPLIT, \
    111                 AllowCppStyleComment=False):
    112     #

    113     # remove whitespace

    114     #

    115     Line = Line.strip()
    116     #

    117     # Replace EDK1's comment character

    118     #

    119     if AllowCppStyleComment:
    120         Line = Line.replace(TAB_COMMENT_EDK1_SPLIT, CommentCharacter)
    121     #

    122     # separate comments and statements

    123     #

    124     Comment = ''
    125     InQuote = False
    126     for Index in range(0, len(Line)):
    127         if Line[Index] == '"':
    128             InQuote = not InQuote
    129             continue
    130         if Line[Index] == CommentCharacter and not InQuote:
    131             Comment = Line[Index:].strip()
    132             Line = Line[0:Index].strip()
    133             break
    134 
    135     return Line, Comment
    136 
    137 
    138 ## IsValidNumValUint8

    139 #

    140 # Check if Token is NumValUint8: <NumValUint8> ::= {<ShortNum>} {<UINT8>} {<Expression>}

    141 #

    142 # @param Token: Token to be checked

    143 #

    144 def IsValidNumValUint8(Token):
    145     Valid = True
    146     Cause = ""
    147     TokenValue = None
    148     Token = Token.strip()
    149     if Token.lower().startswith('0x'):
    150         Base = 16
    151     else:
    152         Base = 10
    153     try:
    154         TokenValue = long(Token, Base)
    155     except BaseException:
    156         Valid, Cause = IsValidLogicalExpr(Token, True)
    157         if Cause:
    158             pass
    159     if not Valid:
    160         return False
    161     if TokenValue and (TokenValue < 0 or TokenValue > 0xFF):
    162         return False
    163     else:
    164         return True
    165 
    166 ## IsValidNList

    167 #

    168 # Check if Value has the format of <NumValUint8> ["," <NumValUint8>]{0,}

    169 # <NumValUint8> ::= {<ShortNum>} {<UINT8>} {<Expression>}

    170 #

    171 # @param Value: Value to be checked

    172 #

    173 def IsValidNList(Value):
    174     Par = ParserHelper(Value)
    175     if Par.End():
    176         return False
    177     while not Par.End():
    178         Token = Par.GetToken(',')
    179         if not IsValidNumValUint8(Token):
    180             return False
    181         if Par.Expect(','):
    182             if Par.End():
    183                 return False
    184             continue
    185         else:
    186             break
    187     return Par.End()
    188 
    189 ## IsValidCArray

    190 #

    191 # check Array is valid

    192 #

    193 # @param Array:    The input Array

    194 #

    195 def IsValidCArray(Array):
    196     Par = ParserHelper(Array)
    197     if not Par.Expect('{'):
    198         return False
    199     if Par.End():
    200         return False
    201     while not Par.End():
    202         Token = Par.GetToken(',}')
    203         #

    204         # ShortNum, UINT8, Expression

    205         #

    206         if not IsValidNumValUint8(Token):
    207             return False
    208         if Par.Expect(','):
    209             if Par.End():
    210                 return False
    211             continue
    212         elif Par.Expect('}'):
    213             #

    214             # End of C array

    215             #

    216             break
    217         else:
    218             return False
    219     return Par.End()
    220 
    221 ## IsValidPcdDatum

    222 #

    223 # check PcdDatum is valid

    224 #

    225 # @param Type:    The pcd Type

    226 # @param Value:    The pcd Value

    227 #

    228 def IsValidPcdDatum(Type, Value):
    229     if not Value:
    230         return False, ST.ERR_DECPARSE_PCD_VALUE_EMPTY
    231     Valid = True
    232     Cause = ""
    233     if Type not in ["UINT8", "UINT16", "UINT32", "UINT64", "VOID*", "BOOLEAN"]:
    234         return False, ST.ERR_DECPARSE_PCD_TYPE
    235     if Type == "VOID*":
    236         if not ((Value.startswith('L"') or Value.startswith('"') and \
    237                  Value.endswith('"'))
    238                 or (IsValidCArray(Value)) or (IsValidCFormatGuid(Value)) \
    239                 or (IsValidNList(Value)) or (CheckGuidRegFormat(Value))
    240                ):
    241             return False, ST.ERR_DECPARSE_PCD_VOID % (Value, Type)
    242         RealString = Value[Value.find('"') + 1 :-1]
    243         if RealString:
    244             if not IsValidBareCString(RealString):
    245                 return False, ST.ERR_DECPARSE_PCD_VOID % (Value, Type)
    246     elif Type == 'BOOLEAN':
    247         if Value in ['TRUE', 'FALSE', 'true', 'false', 'True', 'False',
    248                      '0x1', '0x01', '1', '0x0', '0x00', '0']:
    249             return True, ""
    250         Valid, Cause = IsValidStringTest(Value, True)
    251         if not Valid:
    252             Valid, Cause = IsValidFeatureFlagExp(Value, True)
    253         if not Valid:
    254             return False, Cause
    255     else:
    256         if Value and (Value[0] == '-' or Value[0] == '+'):
    257             return False, ST.ERR_DECPARSE_PCD_INT_NEGTIVE % (Value, Type)
    258         try:
    259             StrVal = Value
    260             if Value and not Value.startswith('0x') \
    261                 and not Value.startswith('0X'):
    262                 Value = Value.lstrip('0')
    263                 if not Value:
    264                     return True, ""
    265             Value = long(Value, 0)
    266             TypeLenMap = {
    267                 #

    268                 # 0x00 - 0xff

    269                 #

    270                 'UINT8'  : 2,
    271                 #

    272                 # 0x0000 - 0xffff

    273                 #

    274                 'UINT16' : 4,
    275                 #

    276                 # 0x00000000 - 0xffffffff

    277                 #

    278                 'UINT32' : 8,
    279                 #

    280                 # 0x0 - 0xffffffffffffffff

    281                 #

    282                 'UINT64' : 16
    283             }
    284             HexStr = hex(Value)
    285             #

    286             # First two chars of HexStr are 0x and tail char is L

    287             #

    288             if TypeLenMap[Type] < len(HexStr) - 3:
    289                 return False, ST.ERR_DECPARSE_PCD_INT_EXCEED % (StrVal, Type)
    290         except BaseException:
    291             Valid, Cause = IsValidLogicalExpr(Value, True)
    292         if not Valid:
    293             return False, Cause
    294         
    295     return True, ""
    296 
    297 ## ParserHelper

    298 #

    299 class ParserHelper:
    300     def __init__(self, String, File=''):
    301         self._String = String
    302         self._StrLen = len(String)
    303         self._Index = 0
    304         self._File = File
    305 
    306     ## End

    307     #

    308     # End

    309     #

    310     def End(self):
    311         self.__SkipWhitespace()
    312         return self._Index >= self._StrLen
    313 
    314     ## __SkipWhitespace

    315     #

    316     # Skip whitespace

    317     #

    318     def __SkipWhitespace(self):
    319         for Char in self._String[self._Index:]:
    320             if Char not in ' \t':
    321                 break
    322             self._Index += 1
    323 
    324     ## Expect

    325     #

    326     # Expect char in string

    327     #

    328     # @param ExpectChar: char expected in index of string

    329     #

    330     def Expect(self, ExpectChar):
    331         self.__SkipWhitespace()
    332         for Char in self._String[self._Index:]:
    333             if Char != ExpectChar:
    334                 return False
    335             else:
    336                 self._Index += 1
    337                 return True
    338         #

    339         # Index out of bound of String

    340         #

    341         return False
    342 
    343     ## GetToken

    344     #

    345     # Get token until encounter StopChar, front whitespace is consumed

    346     #

    347     # @param StopChar: Get token until encounter char in StopChar

    348     # @param StkipPair: Only can be ' or ", StopChar in SkipPair are skipped

    349     #

    350     def GetToken(self, StopChar='.,|\t ', SkipPair='"'):
    351         self.__SkipWhitespace()
    352         PreIndex = self._Index
    353         InQuote = False
    354         LastChar = ''
    355         for Char in self._String[self._Index:]:
    356             if Char == SkipPair and LastChar != '\\':
    357                 InQuote = not InQuote
    358             if Char in StopChar and not InQuote:
    359                 break
    360             self._Index += 1
    361             if Char == '\\' and LastChar == '\\':
    362                 LastChar = ''
    363             else:
    364                 LastChar = Char
    365         return self._String[PreIndex:self._Index]
    366 
    367     ## AssertChar
    368     #
    369     # Assert char at current index of string is AssertChar, or will report 
    370     # error message
    371     #
    372     # @param AssertChar: AssertChar
    373     # @param ErrorString: ErrorString
    374     # @param ErrorLineNum: ErrorLineNum
    375     #
    376     def AssertChar(self, AssertChar, ErrorString, ErrorLineNum):
    377         if not self.Expect(AssertChar):
    378             Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._File,
    379                          Line=ErrorLineNum, ExtraData=ErrorString)
    380 
    381     ## AssertEnd
    382     #
    383     # @param ErrorString: ErrorString
    384     # @param ErrorLineNum: ErrorLineNum
    385     #
    386     def AssertEnd(self, ErrorString, ErrorLineNum):
    387         self.__SkipWhitespace()
    388         if self._Index != self._StrLen:
    389             Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._File,
    390                          Line=ErrorLineNum, ExtraData=ErrorString)
    391