1## @file 2# This file is used to define helper class and function for DEC parser 3# 4# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> 5# 6# SPDX-License-Identifier: BSD-2-Clause-Patent 7 8''' 9DecParserMisc 10''' 11 12## Import modules 13# 14import os 15import Logger.Log as Logger 16from Logger.ToolError import FILE_PARSE_FAILURE 17from Logger import StringTable as ST 18from Library.DataType import TAB_COMMENT_SPLIT 19from Library.DataType import TAB_COMMENT_EDK1_SPLIT 20from Library.ExpressionValidate import IsValidBareCString 21from Library.ParserValidate import IsValidCFormatGuid 22from Library.ExpressionValidate import IsValidFeatureFlagExp 23from Library.ExpressionValidate import IsValidLogicalExpr 24from Library.ExpressionValidate import IsValidStringTest 25from Library.Misc import CheckGuidRegFormat 26 27TOOL_NAME = 'DecParser' 28VERSION_PATTERN = '[0-9]+(\.[0-9]+)?' 29CVAR_PATTERN = '[_a-zA-Z][a-zA-Z0-9_]*' 30PCD_TOKEN_PATTERN = '(0[xX]0*[a-fA-F0-9]{1,8})|([0-9]+)' 31MACRO_PATTERN = '[A-Z][_A-Z0-9]*' 32 33## FileContent 34# Class to hold DEC file information 35# 36class FileContent: 37 def __init__(self, Filename, FileContent2): 38 self.Filename = Filename 39 self.PackagePath, self.PackageFile = os.path.split(Filename) 40 self.LineIndex = 0 41 self.CurrentLine = '' 42 self.NextLine = '' 43 self.HeadComment = [] 44 self.TailComment = [] 45 self.CurrentScope = None 46 self.Content = FileContent2 47 self.Macros = {} 48 self.FileLines = len(FileContent2) 49 50 def GetNextLine(self): 51 if self.LineIndex >= self.FileLines: 52 return '' 53 Line = self.Content[self.LineIndex] 54 self.LineIndex += 1 55 return Line 56 57 def UndoNextLine(self): 58 if self.LineIndex > 0: 59 self.LineIndex -= 1 60 61 def ResetNext(self): 62 self.HeadComment = [] 63 self.TailComment = [] 64 self.NextLine = '' 65 66 def SetNext(self, Line, HeadComment, TailComment): 67 self.NextLine = Line 68 self.HeadComment = HeadComment 69 self.TailComment = TailComment 70 71 def IsEndOfFile(self): 72 return self.LineIndex >= self.FileLines 73 74 75## StripRoot 76# 77# Strip root path 78# 79# @param Root: Root must be absolute path 80# @param Path: Path to be stripped 81# 82def StripRoot(Root, Path): 83 OrigPath = Path 84 Root = os.path.normpath(Root) 85 Path = os.path.normpath(Path) 86 if not os.path.isabs(Root): 87 return OrigPath 88 if Path.startswith(Root): 89 Path = Path[len(Root):] 90 if Path and Path[0] == os.sep: 91 Path = Path[1:] 92 return Path 93 return OrigPath 94 95## CleanString 96# 97# Split comments in a string 98# Remove spaces 99# 100# @param Line: The string to be cleaned 101# @param CommentCharacter: Comment char, used to ignore comment content, 102# default is DataType.TAB_COMMENT_SPLIT 103# 104def CleanString(Line, CommentCharacter=TAB_COMMENT_SPLIT, \ 105 AllowCppStyleComment=False): 106 # 107 # remove whitespace 108 # 109 Line = Line.strip() 110 # 111 # Replace EDK1's comment character 112 # 113 if AllowCppStyleComment: 114 Line = Line.replace(TAB_COMMENT_EDK1_SPLIT, CommentCharacter) 115 # 116 # separate comments and statements 117 # 118 Comment = '' 119 InQuote = False 120 for Index in range(0, len(Line)): 121 if Line[Index] == '"': 122 InQuote = not InQuote 123 continue 124 if Line[Index] == CommentCharacter and not InQuote: 125 Comment = Line[Index:].strip() 126 Line = Line[0:Index].strip() 127 break 128 129 return Line, Comment 130 131 132## IsValidNumValUint8 133# 134# Check if Token is NumValUint8: <NumValUint8> ::= {<ShortNum>} {<UINT8>} {<Expression>} 135# 136# @param Token: Token to be checked 137# 138def IsValidNumValUint8(Token): 139 Valid = True 140 Cause = "" 141 TokenValue = None 142 Token = Token.strip() 143 if Token.lower().startswith('0x'): 144 Base = 16 145 else: 146 Base = 10 147 try: 148 TokenValue = int(Token, Base) 149 except BaseException: 150 Valid, Cause = IsValidLogicalExpr(Token, True) 151 if Cause: 152 pass 153 if not Valid: 154 return False 155 if TokenValue and (TokenValue < 0 or TokenValue > 0xFF): 156 return False 157 else: 158 return True 159 160## IsValidNList 161# 162# Check if Value has the format of <NumValUint8> ["," <NumValUint8>]{0,} 163# <NumValUint8> ::= {<ShortNum>} {<UINT8>} {<Expression>} 164# 165# @param Value: Value to be checked 166# 167def IsValidNList(Value): 168 Par = ParserHelper(Value) 169 if Par.End(): 170 return False 171 while not Par.End(): 172 Token = Par.GetToken(',') 173 if not IsValidNumValUint8(Token): 174 return False 175 if Par.Expect(','): 176 if Par.End(): 177 return False 178 continue 179 else: 180 break 181 return Par.End() 182 183## IsValidCArray 184# 185# check Array is valid 186# 187# @param Array: The input Array 188# 189def IsValidCArray(Array): 190 Par = ParserHelper(Array) 191 if not Par.Expect('{'): 192 return False 193 if Par.End(): 194 return False 195 while not Par.End(): 196 Token = Par.GetToken(',}') 197 # 198 # ShortNum, UINT8, Expression 199 # 200 if not IsValidNumValUint8(Token): 201 return False 202 if Par.Expect(','): 203 if Par.End(): 204 return False 205 continue 206 elif Par.Expect('}'): 207 # 208 # End of C array 209 # 210 break 211 else: 212 return False 213 return Par.End() 214 215## IsValidPcdDatum 216# 217# check PcdDatum is valid 218# 219# @param Type: The pcd Type 220# @param Value: The pcd Value 221# 222def IsValidPcdDatum(Type, Value): 223 if not Value: 224 return False, ST.ERR_DECPARSE_PCD_VALUE_EMPTY 225 Valid = True 226 Cause = "" 227 if Type not in ["UINT8", "UINT16", "UINT32", "UINT64", "VOID*", "BOOLEAN"]: 228 return False, ST.ERR_DECPARSE_PCD_TYPE 229 if Type == "VOID*": 230 if not ((Value.startswith('L"') or Value.startswith('"') and \ 231 Value.endswith('"')) 232 or (IsValidCArray(Value)) or (IsValidCFormatGuid(Value)) \ 233 or (IsValidNList(Value)) or (CheckGuidRegFormat(Value)) 234 ): 235 return False, ST.ERR_DECPARSE_PCD_VOID % (Value, Type) 236 RealString = Value[Value.find('"') + 1 :-1] 237 if RealString: 238 if not IsValidBareCString(RealString): 239 return False, ST.ERR_DECPARSE_PCD_VOID % (Value, Type) 240 elif Type == 'BOOLEAN': 241 if Value in ['TRUE', 'FALSE', 'true', 'false', 'True', 'False', 242 '0x1', '0x01', '1', '0x0', '0x00', '0']: 243 return True, "" 244 Valid, Cause = IsValidStringTest(Value, True) 245 if not Valid: 246 Valid, Cause = IsValidFeatureFlagExp(Value, True) 247 if not Valid: 248 return False, Cause 249 else: 250 if Value and (Value[0] == '-' or Value[0] == '+'): 251 return False, ST.ERR_DECPARSE_PCD_INT_NEGTIVE % (Value, Type) 252 try: 253 StrVal = Value 254 if Value and not Value.startswith('0x') \ 255 and not Value.startswith('0X'): 256 Value = Value.lstrip('0') 257 if not Value: 258 return True, "" 259 Value = int(Value, 0) 260 MAX_VAL_TYPE = {"BOOLEAN": 0x01, 'UINT8': 0xFF, 'UINT16': 0xFFFF, 'UINT32': 0xFFFFFFFF, 261 'UINT64': 0xFFFFFFFFFFFFFFFF} 262 if Value > MAX_VAL_TYPE[Type]: 263 return False, ST.ERR_DECPARSE_PCD_INT_EXCEED % (StrVal, Type) 264 except BaseException: 265 Valid, Cause = IsValidLogicalExpr(Value, True) 266 if not Valid: 267 return False, Cause 268 269 return True, "" 270 271## ParserHelper 272# 273class ParserHelper: 274 def __init__(self, String, File=''): 275 self._String = String 276 self._StrLen = len(String) 277 self._Index = 0 278 self._File = File 279 280 ## End 281 # 282 # End 283 # 284 def End(self): 285 self.__SkipWhitespace() 286 return self._Index >= self._StrLen 287 288 ## __SkipWhitespace 289 # 290 # Skip whitespace 291 # 292 def __SkipWhitespace(self): 293 for Char in self._String[self._Index:]: 294 if Char not in ' \t': 295 break 296 self._Index += 1 297 298 ## Expect 299 # 300 # Expect char in string 301 # 302 # @param ExpectChar: char expected in index of string 303 # 304 def Expect(self, ExpectChar): 305 self.__SkipWhitespace() 306 for Char in self._String[self._Index:]: 307 if Char != ExpectChar: 308 return False 309 else: 310 self._Index += 1 311 return True 312 # 313 # Index out of bound of String 314 # 315 return False 316 317 ## GetToken 318 # 319 # Get token until encounter StopChar, front whitespace is consumed 320 # 321 # @param StopChar: Get token until encounter char in StopChar 322 # @param StkipPair: Only can be ' or ", StopChar in SkipPair are skipped 323 # 324 def GetToken(self, StopChar='.,|\t ', SkipPair='"'): 325 self.__SkipWhitespace() 326 PreIndex = self._Index 327 InQuote = False 328 LastChar = '' 329 for Char in self._String[self._Index:]: 330 if Char == SkipPair and LastChar != '\\': 331 InQuote = not InQuote 332 if Char in StopChar and not InQuote: 333 break 334 self._Index += 1 335 if Char == '\\' and LastChar == '\\': 336 LastChar = '' 337 else: 338 LastChar = Char 339 return self._String[PreIndex:self._Index] 340 341 ## AssertChar 342 # 343 # Assert char at current index of string is AssertChar, or will report 344 # error message 345 # 346 # @param AssertChar: AssertChar 347 # @param ErrorString: ErrorString 348 # @param ErrorLineNum: ErrorLineNum 349 # 350 def AssertChar(self, AssertChar, ErrorString, ErrorLineNum): 351 if not self.Expect(AssertChar): 352 Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._File, 353 Line=ErrorLineNum, ExtraData=ErrorString) 354 355 ## AssertEnd 356 # 357 # @param ErrorString: ErrorString 358 # @param ErrorLineNum: ErrorLineNum 359 # 360 def AssertEnd(self, ErrorString, ErrorLineNum): 361 self.__SkipWhitespace() 362 if self._Index != self._StrLen: 363 Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._File, 364 Line=ErrorLineNum, ExtraData=ErrorString) 365