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''' 15DecParserMisc 16''' 17 18## Import modules 19# 20import os 21import Logger.Log as Logger 22from Logger.ToolError import FILE_PARSE_FAILURE 23from Logger import StringTable as ST 24from Library.DataType import TAB_COMMENT_SPLIT 25from Library.DataType import TAB_COMMENT_EDK1_SPLIT 26from Library.ExpressionValidate import IsValidBareCString 27from Library.ParserValidate import IsValidCFormatGuid 28from Library.ExpressionValidate import IsValidFeatureFlagExp 29from Library.ExpressionValidate import IsValidLogicalExpr 30from Library.ExpressionValidate import IsValidStringTest 31from Library.Misc import CheckGuidRegFormat 32 33TOOL_NAME = 'DecParser' 34VERSION_PATTERN = '[0-9]+(\.[0-9]+)?' 35CVAR_PATTERN = '[_a-zA-Z][a-zA-Z0-9_]*' 36PCD_TOKEN_PATTERN = '(0[xX]0*[a-fA-F0-9]{1,8})|([0-9]+)' 37MACRO_PATTERN = '[A-Z][_A-Z0-9]*' 38 39## FileContent 40# Class to hold DEC file information 41# 42class 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# 88def 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# 110def 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# 144def 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# 173def 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# 195def 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# 228def 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# 299class 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