1## @file 2# This file is used to check PCD logical expression 3# 4# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> 5# 6# SPDX-License-Identifier: BSD-2-Clause-Patent 7 8''' 9ExpressionValidate 10''' 11from __future__ import print_function 12 13## 14# Import Modules 15# 16import re 17from Logger import StringTable as ST 18 19## IsValidBareCString 20# 21# Check if String is comprised by whitespace(0x20), !(0x21), 0x23 - 0x7E 22# or '\n', '\t', '\f', '\r', '\b', '\0', '\\' 23# 24# @param String: string to be checked 25# 26def IsValidBareCString(String): 27 EscapeList = ['n', 't', 'f', 'r', 'b', '0', '\\', '"'] 28 PreChar = '' 29 LastChar = '' 30 for Char in String: 31 LastChar = Char 32 if PreChar == '\\': 33 if Char not in EscapeList: 34 return False 35 if Char == '\\': 36 PreChar = '' 37 continue 38 else: 39 IntChar = ord(Char) 40 if IntChar != 0x20 and IntChar != 0x09 and IntChar != 0x21 \ 41 and (IntChar < 0x23 or IntChar > 0x7e): 42 return False 43 PreChar = Char 44 45 # Last char cannot be \ if PreChar is not \ 46 if LastChar == '\\' and PreChar == LastChar: 47 return False 48 return True 49 50def _ValidateToken(Token): 51 Token = Token.strip() 52 Index = Token.find("\"") 53 if Index != -1: 54 return IsValidBareCString(Token[Index+1:-1]) 55 return True 56 57## _ExprError 58# 59# @param Exception: Exception 60# 61class _ExprError(Exception): 62 def __init__(self, Error = ''): 63 Exception.__init__(self) 64 self.Error = Error 65 66## _ExprBase 67# 68class _ExprBase: 69 HEX_PATTERN = '[\t\s]*0[xX][a-fA-F0-9]+' 70 INT_PATTERN = '[\t\s]*[0-9]+' 71 MACRO_PATTERN = '[\t\s]*\$\(([A-Z][_A-Z0-9]*)\)' 72 PCD_PATTERN = \ 73 '[\t\s]*[_a-zA-Z][a-zA-Z0-9_]*[\t\s]*\.[\t\s]*[_a-zA-Z][a-zA-Z0-9_]*' 74 QUOTED_PATTERN = '[\t\s]*L?"[^"]*"' 75 BOOL_PATTERN = '[\t\s]*(true|True|TRUE|false|False|FALSE)' 76 def __init__(self, Token): 77 self.Token = Token 78 self.Index = 0 79 self.Len = len(Token) 80 81 ## SkipWhitespace 82 # 83 def SkipWhitespace(self): 84 for Char in self.Token[self.Index:]: 85 if Char not in ' \t': 86 break 87 self.Index += 1 88 89 ## IsCurrentOp 90 # 91 # @param OpList: option list 92 # 93 def IsCurrentOp(self, OpList): 94 self.SkipWhitespace() 95 LetterOp = ["EQ", "NE", "GE", "LE", "GT", "LT", "NOT", "and", "AND", 96 "or", "OR", "XOR"] 97 OpMap = { 98 '|' : '|', 99 '&' : '&', 100 '!' : '=', 101 '>' : '=', 102 '<' : '=' 103 } 104 105 for Operator in OpList: 106 if not self.Token[self.Index:].startswith(Operator): 107 continue 108 109 self.Index += len(Operator) 110 Char = self.Token[self.Index : self.Index + 1] 111 112 if (Operator in LetterOp and (Char == '_' or Char.isalnum())) \ 113 or (Operator in OpMap and OpMap[Operator] == Char): 114 self.Index -= len(Operator) 115 break 116 117 return True 118 119 return False 120 121## _LogicalExpressionParser 122# 123# @param _ExprBase: _ExprBase object 124# 125class _LogicalExpressionParser(_ExprBase): 126 # 127 # STRINGITEM can only be logical field according to spec 128 # 129 STRINGITEM = -1 130 131 # 132 # Evaluate to True or False 133 # 134 LOGICAL = 0 135 REALLOGICAL = 2 136 137 # 138 # Just arithmetic expression 139 # 140 ARITH = 1 141 142 def __init__(self, Token): 143 _ExprBase.__init__(self, Token) 144 self.Parens = 0 145 146 def _CheckToken(self, MatchList): 147 for Match in MatchList: 148 if Match and Match.start() == 0: 149 if not _ValidateToken( 150 self.Token[self.Index:self.Index+Match.end()] 151 ): 152 return False 153 154 self.Index += Match.end() 155 if self.Token[self.Index - 1] == '"': 156 return True 157 if self.Token[self.Index:self.Index+1] == '_' or \ 158 self.Token[self.Index:self.Index+1].isalnum(): 159 self.Index -= Match.end() 160 return False 161 162 Token = self.Token[self.Index - Match.end():self.Index] 163 if Token.strip() in ["EQ", "NE", "GE", "LE", "GT", "LT", 164 "NOT", "and", "AND", "or", "OR", "XOR"]: 165 self.Index -= Match.end() 166 return False 167 168 return True 169 170 return False 171 172 def IsAtomicNumVal(self): 173 # 174 # Hex number 175 # 176 Match1 = re.compile(self.HEX_PATTERN).match(self.Token[self.Index:]) 177 178 # 179 # Number 180 # 181 Match2 = re.compile(self.INT_PATTERN).match(self.Token[self.Index:]) 182 183 # 184 # Macro 185 # 186 Match3 = re.compile(self.MACRO_PATTERN).match(self.Token[self.Index:]) 187 188 # 189 # PcdName 190 # 191 Match4 = re.compile(self.PCD_PATTERN).match(self.Token[self.Index:]) 192 193 return self._CheckToken([Match1, Match2, Match3, Match4]) 194 195 196 def IsAtomicItem(self): 197 # 198 # Macro 199 # 200 Match1 = re.compile(self.MACRO_PATTERN).match(self.Token[self.Index:]) 201 202 # 203 # PcdName 204 # 205 Match2 = re.compile(self.PCD_PATTERN).match(self.Token[self.Index:]) 206 207 # 208 # Quoted string 209 # 210 Match3 = re.compile(self.QUOTED_PATTERN).\ 211 match(self.Token[self.Index:].replace('\\\\', '//').\ 212 replace('\\\"', '\\\'')) 213 214 return self._CheckToken([Match1, Match2, Match3]) 215 216 ## A || B 217 # 218 def LogicalExpression(self): 219 Ret = self.SpecNot() 220 while self.IsCurrentOp(['||', 'OR', 'or', '&&', 'AND', 'and', 'XOR', 'xor', '^']): 221 if self.Token[self.Index-1] == '|' and self.Parens <= 0: 222 raise _ExprError(ST.ERR_EXPR_OR % self.Token) 223 if Ret not in [self.ARITH, self.LOGICAL, self.REALLOGICAL, self.STRINGITEM]: 224 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token) 225 Ret = self.SpecNot() 226 if Ret not in [self.ARITH, self.LOGICAL, self.REALLOGICAL, self.STRINGITEM]: 227 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token) 228 Ret = self.REALLOGICAL 229 return Ret 230 231 def SpecNot(self): 232 if self.IsCurrentOp(["NOT", "!", "not"]): 233 return self.SpecNot() 234 return self.Rel() 235 236 ## A < B, A > B, A <= B, A >= B 237 # 238 def Rel(self): 239 Ret = self.Expr() 240 if self.IsCurrentOp(["<=", ">=", ">", "<", "GT", "LT", "GE", "LE", 241 "==", "EQ", "!=", "NE"]): 242 if Ret == self.STRINGITEM: 243 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token) 244 Ret = self.Expr() 245 if Ret == self.REALLOGICAL: 246 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token) 247 Ret = self.REALLOGICAL 248 return Ret 249 250 ## A + B, A - B 251 # 252 def Expr(self): 253 Ret = self.Factor() 254 while self.IsCurrentOp(["+", "-", "&", "|", "^", "XOR", "xor"]): 255 if self.Token[self.Index-1] == '|' and self.Parens <= 0: 256 raise _ExprError(ST.ERR_EXPR_OR) 257 if Ret == self.STRINGITEM or Ret == self.REALLOGICAL: 258 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token) 259 Ret = self.Factor() 260 if Ret == self.STRINGITEM or Ret == self.REALLOGICAL: 261 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token) 262 Ret = self.ARITH 263 return Ret 264 265 ## Factor 266 # 267 def Factor(self): 268 if self.IsCurrentOp(["("]): 269 self.Parens += 1 270 Ret = self.LogicalExpression() 271 if not self.IsCurrentOp([")"]): 272 raise _ExprError(ST.ERR_EXPR_RIGHT_PAREN % \ 273 (self.Token, self.Token[self.Index:])) 274 self.Parens -= 1 275 return Ret 276 277 if self.IsAtomicItem(): 278 if self.Token[self.Index - 1] == '"': 279 return self.STRINGITEM 280 return self.LOGICAL 281 elif self.IsAtomicNumVal(): 282 return self.ARITH 283 else: 284 raise _ExprError(ST.ERR_EXPR_FACTOR % \ 285 (self.Token[self.Index:], self.Token)) 286 287 ## IsValidLogicalExpression 288 # 289 def IsValidLogicalExpression(self): 290 if self.Len == 0: 291 return False, ST.ERR_EXPRESS_EMPTY 292 try: 293 if self.LogicalExpression() not in [self.ARITH, self.LOGICAL, self.REALLOGICAL, self.STRINGITEM]: 294 return False, ST.ERR_EXPR_LOGICAL % self.Token 295 except _ExprError as XExcept: 296 return False, XExcept.Error 297 self.SkipWhitespace() 298 if self.Index != self.Len: 299 return False, (ST.ERR_EXPR_BOOLEAN % \ 300 (self.Token[self.Index:], self.Token)) 301 return True, '' 302 303## _ValidRangeExpressionParser 304# 305class _ValidRangeExpressionParser(_ExprBase): 306 INT_RANGE_PATTERN = '[\t\s]*[0-9]+[\t\s]*-[\t\s]*[0-9]+' 307 HEX_RANGE_PATTERN = \ 308 '[\t\s]*0[xX][a-fA-F0-9]+[\t\s]*-[\t\s]*0[xX][a-fA-F0-9]+' 309 def __init__(self, Token): 310 _ExprBase.__init__(self, Token) 311 self.Parens = 0 312 self.HEX = 1 313 self.INT = 2 314 self.IsParenHappen = False 315 self.IsLogicalOpHappen = False 316 317 ## IsValidRangeExpression 318 # 319 def IsValidRangeExpression(self): 320 if self.Len == 0: 321 return False, ST.ERR_EXPR_RANGE_EMPTY 322 try: 323 if self.RangeExpression() not in [self.HEX, self.INT]: 324 return False, ST.ERR_EXPR_RANGE % self.Token 325 except _ExprError as XExcept: 326 return False, XExcept.Error 327 328 self.SkipWhitespace() 329 if self.Index != self.Len: 330 return False, (ST.ERR_EXPR_RANGE % self.Token) 331 return True, '' 332 333 ## RangeExpression 334 # 335 def RangeExpression(self): 336 Ret = self.Unary() 337 while self.IsCurrentOp(['OR', 'AND', 'and', 'or']): 338 self.IsLogicalOpHappen = True 339 if not self.IsParenHappen: 340 raise _ExprError(ST.ERR_PAREN_NOT_USED % self.Token) 341 self.IsParenHappen = False 342 Ret = self.Unary() 343 344 if self.IsCurrentOp(['XOR']): 345 Ret = self.Unary() 346 347 return Ret 348 349 ## Unary 350 # 351 def Unary(self): 352 if self.IsCurrentOp(["NOT"]): 353 return self.Unary() 354 355 return self.ValidRange() 356 357 ## ValidRange 358 # 359 def ValidRange(self): 360 Ret = -1 361 if self.IsCurrentOp(["("]): 362 self.IsLogicalOpHappen = False 363 self.IsParenHappen = True 364 self.Parens += 1 365 if self.Parens > 1: 366 raise _ExprError(ST.ERR_EXPR_RANGE_DOUBLE_PAREN_NESTED % self.Token) 367 Ret = self.RangeExpression() 368 if not self.IsCurrentOp([")"]): 369 raise _ExprError(ST.ERR_EXPR_RIGHT_PAREN % self.Token) 370 self.Parens -= 1 371 return Ret 372 373 if self.IsLogicalOpHappen: 374 raise _ExprError(ST.ERR_PAREN_NOT_USED % self.Token) 375 376 if self.IsCurrentOp(["LT", "GT", "LE", "GE", "EQ", "XOR"]): 377 IntMatch = \ 378 re.compile(self.INT_PATTERN).match(self.Token[self.Index:]) 379 HexMatch = \ 380 re.compile(self.HEX_PATTERN).match(self.Token[self.Index:]) 381 if HexMatch and HexMatch.start() == 0: 382 self.Index += HexMatch.end() 383 Ret = self.HEX 384 elif IntMatch and IntMatch.start() == 0: 385 self.Index += IntMatch.end() 386 Ret = self.INT 387 else: 388 raise _ExprError(ST.ERR_EXPR_RANGE_FACTOR % (self.Token[self.Index:], self.Token)) 389 else: 390 IntRangeMatch = re.compile( 391 self.INT_RANGE_PATTERN).match(self.Token[self.Index:] 392 ) 393 HexRangeMatch = re.compile( 394 self.HEX_RANGE_PATTERN).match(self.Token[self.Index:] 395 ) 396 if HexRangeMatch and HexRangeMatch.start() == 0: 397 self.Index += HexRangeMatch.end() 398 Ret = self.HEX 399 elif IntRangeMatch and IntRangeMatch.start() == 0: 400 self.Index += IntRangeMatch.end() 401 Ret = self.INT 402 else: 403 raise _ExprError(ST.ERR_EXPR_RANGE % self.Token) 404 405 return Ret 406 407## _ValidListExpressionParser 408# 409class _ValidListExpressionParser(_ExprBase): 410 VALID_LIST_PATTERN = '(0[xX][0-9a-fA-F]+|[0-9]+)([\t\s]*,[\t\s]*(0[xX][0-9a-fA-F]+|[0-9]+))*' 411 def __init__(self, Token): 412 _ExprBase.__init__(self, Token) 413 self.NUM = 1 414 415 def IsValidListExpression(self): 416 if self.Len == 0: 417 return False, ST.ERR_EXPR_LIST_EMPTY 418 try: 419 if self.ListExpression() not in [self.NUM]: 420 return False, ST.ERR_EXPR_LIST % self.Token 421 except _ExprError as XExcept: 422 return False, XExcept.Error 423 424 self.SkipWhitespace() 425 if self.Index != self.Len: 426 return False, (ST.ERR_EXPR_LIST % self.Token) 427 428 return True, '' 429 430 def ListExpression(self): 431 Ret = -1 432 self.SkipWhitespace() 433 ListMatch = re.compile(self.VALID_LIST_PATTERN).match(self.Token[self.Index:]) 434 if ListMatch and ListMatch.start() == 0: 435 self.Index += ListMatch.end() 436 Ret = self.NUM 437 else: 438 raise _ExprError(ST.ERR_EXPR_LIST % self.Token) 439 440 return Ret 441 442## _StringTestParser 443# 444class _StringTestParser(_ExprBase): 445 def __init__(self, Token): 446 _ExprBase.__init__(self, Token) 447 448 ## IsValidStringTest 449 # 450 def IsValidStringTest(self): 451 if self.Len == 0: 452 return False, ST.ERR_EXPR_EMPTY 453 try: 454 self.StringTest() 455 except _ExprError as XExcept: 456 return False, XExcept.Error 457 return True, '' 458 459 ## StringItem 460 # 461 def StringItem(self): 462 Match1 = re.compile(self.QUOTED_PATTERN)\ 463 .match(self.Token[self.Index:].replace('\\\\', '//')\ 464 .replace('\\\"', '\\\'')) 465 Match2 = re.compile(self.MACRO_PATTERN).match(self.Token[self.Index:]) 466 Match3 = re.compile(self.PCD_PATTERN).match(self.Token[self.Index:]) 467 MatchList = [Match1, Match2, Match3] 468 for Match in MatchList: 469 if Match and Match.start() == 0: 470 if not _ValidateToken( 471 self.Token[self.Index:self.Index+Match.end()] 472 ): 473 raise _ExprError(ST.ERR_EXPR_STRING_ITEM % \ 474 (self.Token, self.Token[self.Index:])) 475 self.Index += Match.end() 476 Token = self.Token[self.Index - Match.end():self.Index] 477 if Token.strip() in ["EQ", "NE"]: 478 raise _ExprError(ST.ERR_EXPR_STRING_ITEM % \ 479 (self.Token, self.Token[self.Index:])) 480 return 481 else: 482 raise _ExprError(ST.ERR_EXPR_STRING_ITEM % \ 483 (self.Token, self.Token[self.Index:])) 484 485 ## StringTest 486 # 487 def StringTest(self): 488 self.StringItem() 489 if not self.IsCurrentOp(["==", "EQ", "!=", "NE"]): 490 raise _ExprError(ST.ERR_EXPR_EQUALITY % \ 491 (self.Token[self.Index:], self.Token)) 492 self.StringItem() 493 if self.Index != self.Len: 494 raise _ExprError(ST.ERR_EXPR_BOOLEAN % \ 495 (self.Token[self.Index:], self.Token)) 496 497## 498# Check syntax of string test 499# 500# @param Token: string test token 501# 502def IsValidStringTest(Token, Flag=False): 503 # 504 # Not do the check right now, keep the implementation for future enhancement. 505 # 506 if not Flag: 507 return True, "" 508 return _StringTestParser(Token).IsValidStringTest() 509 510 511## 512# Check syntax of logical expression 513# 514# @param Token: expression token 515# 516def IsValidLogicalExpr(Token, Flag=False): 517 # 518 # Not do the check right now, keep the implementation for future enhancement. 519 # 520 if not Flag: 521 return True, "" 522 return _LogicalExpressionParser(Token).IsValidLogicalExpression() 523 524## 525# Check syntax of range expression 526# 527# @param Token: range expression token 528# 529def IsValidRangeExpr(Token): 530 return _ValidRangeExpressionParser(Token).IsValidRangeExpression() 531 532## 533# Check syntax of value list expression token 534# 535# @param Token: value list expression token 536# 537def IsValidListExpr(Token): 538 return _ValidListExpressionParser(Token).IsValidListExpression() 539 540## 541# Check whether the feature flag expression is valid or not 542# 543# @param Token: feature flag expression 544# 545def IsValidFeatureFlagExp(Token, Flag=False): 546 # 547 # Not do the check right now, keep the implementation for future enhancement. 548 # 549 if not Flag: 550 return True, "", Token 551 else: 552 if Token in ['TRUE', 'FALSE', 'true', 'false', 'True', 'False', 553 '0x1', '0x01', '0x0', '0x00']: 554 return True, "" 555 Valid, Cause = IsValidStringTest(Token, Flag) 556 if not Valid: 557 Valid, Cause = IsValidLogicalExpr(Token, Flag) 558 if not Valid: 559 return False, Cause 560 return True, "" 561 562if __name__ == '__main__': 563# print IsValidRangeExpr('LT 9') 564 print(_LogicalExpressionParser('gCrownBayTokenSpaceGuid.PcdPciDevice1BridgeAddressLE0').IsValidLogicalExpression()) 565 566 567 568