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