mirror of https://github.com/acidanthera/audk.git
568 lines
18 KiB
Python
568 lines
18 KiB
Python
## @file
|
|
# This file is used to check PCD logical expression
|
|
#
|
|
# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
|
|
#
|
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
'''
|
|
ExpressionValidate
|
|
'''
|
|
from __future__ import print_function
|
|
|
|
##
|
|
# Import Modules
|
|
#
|
|
import re
|
|
from Logger import StringTable as ST
|
|
|
|
## IsValidBareCString
|
|
#
|
|
# Check if String is comprised by whitespace(0x20), !(0x21), 0x23 - 0x7E
|
|
# or '\n', '\t', '\f', '\r', '\b', '\0', '\\'
|
|
#
|
|
# @param String: string to be checked
|
|
#
|
|
def IsValidBareCString(String):
|
|
EscapeList = ['n', 't', 'f', 'r', 'b', '0', '\\', '"']
|
|
PreChar = ''
|
|
LastChar = ''
|
|
for Char in String:
|
|
LastChar = Char
|
|
if PreChar == '\\':
|
|
if Char not in EscapeList:
|
|
return False
|
|
if Char == '\\':
|
|
PreChar = ''
|
|
continue
|
|
else:
|
|
IntChar = ord(Char)
|
|
if IntChar != 0x20 and IntChar != 0x09 and IntChar != 0x21 \
|
|
and (IntChar < 0x23 or IntChar > 0x7e):
|
|
return False
|
|
PreChar = Char
|
|
|
|
# Last char cannot be \ if PreChar is not \
|
|
if LastChar == '\\' and PreChar == LastChar:
|
|
return False
|
|
return True
|
|
|
|
def _ValidateToken(Token):
|
|
Token = Token.strip()
|
|
Index = Token.find("\"")
|
|
if Index != -1:
|
|
return IsValidBareCString(Token[Index+1:-1])
|
|
return True
|
|
|
|
## _ExprError
|
|
#
|
|
# @param Exception: Exception
|
|
#
|
|
class _ExprError(Exception):
|
|
def __init__(self, Error = ''):
|
|
Exception.__init__(self)
|
|
self.Error = Error
|
|
|
|
## _ExprBase
|
|
#
|
|
class _ExprBase:
|
|
HEX_PATTERN = '[\t\s]*0[xX][a-fA-F0-9]+'
|
|
INT_PATTERN = '[\t\s]*[0-9]+'
|
|
MACRO_PATTERN = '[\t\s]*\$\(([A-Z][_A-Z0-9]*)\)'
|
|
PCD_PATTERN = \
|
|
'[\t\s]*[_a-zA-Z][a-zA-Z0-9_]*[\t\s]*\.[\t\s]*[_a-zA-Z][a-zA-Z0-9_]*'
|
|
QUOTED_PATTERN = '[\t\s]*L?"[^"]*"'
|
|
BOOL_PATTERN = '[\t\s]*(true|True|TRUE|false|False|FALSE)'
|
|
def __init__(self, Token):
|
|
self.Token = Token
|
|
self.Index = 0
|
|
self.Len = len(Token)
|
|
|
|
## SkipWhitespace
|
|
#
|
|
def SkipWhitespace(self):
|
|
for Char in self.Token[self.Index:]:
|
|
if Char not in ' \t':
|
|
break
|
|
self.Index += 1
|
|
|
|
## IsCurrentOp
|
|
#
|
|
# @param OpList: option list
|
|
#
|
|
def IsCurrentOp(self, OpList):
|
|
self.SkipWhitespace()
|
|
LetterOp = ["EQ", "NE", "GE", "LE", "GT", "LT", "NOT", "and", "AND",
|
|
"or", "OR", "XOR"]
|
|
OpMap = {
|
|
'|' : '|',
|
|
'&' : '&',
|
|
'!' : '=',
|
|
'>' : '=',
|
|
'<' : '='
|
|
}
|
|
|
|
for Operator in OpList:
|
|
if not self.Token[self.Index:].startswith(Operator):
|
|
continue
|
|
|
|
self.Index += len(Operator)
|
|
Char = self.Token[self.Index : self.Index + 1]
|
|
|
|
if (Operator in LetterOp and (Char == '_' or Char.isalnum())) \
|
|
or (Operator in OpMap and OpMap[Operator] == Char):
|
|
self.Index -= len(Operator)
|
|
break
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
## _LogicalExpressionParser
|
|
#
|
|
# @param _ExprBase: _ExprBase object
|
|
#
|
|
class _LogicalExpressionParser(_ExprBase):
|
|
#
|
|
# STRINGITEM can only be logical field according to spec
|
|
#
|
|
STRINGITEM = -1
|
|
|
|
#
|
|
# Evaluate to True or False
|
|
#
|
|
LOGICAL = 0
|
|
REALLOGICAL = 2
|
|
|
|
#
|
|
# Just arithmetic expression
|
|
#
|
|
ARITH = 1
|
|
|
|
def __init__(self, Token):
|
|
_ExprBase.__init__(self, Token)
|
|
self.Parens = 0
|
|
|
|
def _CheckToken(self, MatchList):
|
|
for Match in MatchList:
|
|
if Match and Match.start() == 0:
|
|
if not _ValidateToken(
|
|
self.Token[self.Index:self.Index+Match.end()]
|
|
):
|
|
return False
|
|
|
|
self.Index += Match.end()
|
|
if self.Token[self.Index - 1] == '"':
|
|
return True
|
|
if self.Token[self.Index:self.Index+1] == '_' or \
|
|
self.Token[self.Index:self.Index+1].isalnum():
|
|
self.Index -= Match.end()
|
|
return False
|
|
|
|
Token = self.Token[self.Index - Match.end():self.Index]
|
|
if Token.strip() in ["EQ", "NE", "GE", "LE", "GT", "LT",
|
|
"NOT", "and", "AND", "or", "OR", "XOR"]:
|
|
self.Index -= Match.end()
|
|
return False
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
def IsAtomicNumVal(self):
|
|
#
|
|
# Hex number
|
|
#
|
|
Match1 = re.compile(self.HEX_PATTERN).match(self.Token[self.Index:])
|
|
|
|
#
|
|
# Number
|
|
#
|
|
Match2 = re.compile(self.INT_PATTERN).match(self.Token[self.Index:])
|
|
|
|
#
|
|
# Macro
|
|
#
|
|
Match3 = re.compile(self.MACRO_PATTERN).match(self.Token[self.Index:])
|
|
|
|
#
|
|
# PcdName
|
|
#
|
|
Match4 = re.compile(self.PCD_PATTERN).match(self.Token[self.Index:])
|
|
|
|
return self._CheckToken([Match1, Match2, Match3, Match4])
|
|
|
|
|
|
def IsAtomicItem(self):
|
|
#
|
|
# Macro
|
|
#
|
|
Match1 = re.compile(self.MACRO_PATTERN).match(self.Token[self.Index:])
|
|
|
|
#
|
|
# PcdName
|
|
#
|
|
Match2 = re.compile(self.PCD_PATTERN).match(self.Token[self.Index:])
|
|
|
|
#
|
|
# Quoted string
|
|
#
|
|
Match3 = re.compile(self.QUOTED_PATTERN).\
|
|
match(self.Token[self.Index:].replace('\\\\', '//').\
|
|
replace('\\\"', '\\\''))
|
|
|
|
return self._CheckToken([Match1, Match2, Match3])
|
|
|
|
## A || B
|
|
#
|
|
def LogicalExpression(self):
|
|
Ret = self.SpecNot()
|
|
while self.IsCurrentOp(['||', 'OR', 'or', '&&', 'AND', 'and', 'XOR', 'xor', '^']):
|
|
if self.Token[self.Index-1] == '|' and self.Parens <= 0:
|
|
raise _ExprError(ST.ERR_EXPR_OR % self.Token)
|
|
if Ret not in [self.ARITH, self.LOGICAL, self.REALLOGICAL, self.STRINGITEM]:
|
|
raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
|
|
Ret = self.SpecNot()
|
|
if Ret not in [self.ARITH, self.LOGICAL, self.REALLOGICAL, self.STRINGITEM]:
|
|
raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
|
|
Ret = self.REALLOGICAL
|
|
return Ret
|
|
|
|
def SpecNot(self):
|
|
if self.IsCurrentOp(["NOT", "!", "not"]):
|
|
return self.SpecNot()
|
|
return self.Rel()
|
|
|
|
## A < B, A > B, A <= B, A >= B
|
|
#
|
|
def Rel(self):
|
|
Ret = self.Expr()
|
|
if self.IsCurrentOp(["<=", ">=", ">", "<", "GT", "LT", "GE", "LE",
|
|
"==", "EQ", "!=", "NE"]):
|
|
if Ret == self.STRINGITEM:
|
|
raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
|
|
Ret = self.Expr()
|
|
if Ret == self.REALLOGICAL:
|
|
raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
|
|
Ret = self.REALLOGICAL
|
|
return Ret
|
|
|
|
## A + B, A - B
|
|
#
|
|
def Expr(self):
|
|
Ret = self.Factor()
|
|
while self.IsCurrentOp(["+", "-", "&", "|", "^", "XOR", "xor"]):
|
|
if self.Token[self.Index-1] == '|' and self.Parens <= 0:
|
|
raise _ExprError(ST.ERR_EXPR_OR)
|
|
if Ret == self.STRINGITEM or Ret == self.REALLOGICAL:
|
|
raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
|
|
Ret = self.Factor()
|
|
if Ret == self.STRINGITEM or Ret == self.REALLOGICAL:
|
|
raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
|
|
Ret = self.ARITH
|
|
return Ret
|
|
|
|
## Factor
|
|
#
|
|
def Factor(self):
|
|
if self.IsCurrentOp(["("]):
|
|
self.Parens += 1
|
|
Ret = self.LogicalExpression()
|
|
if not self.IsCurrentOp([")"]):
|
|
raise _ExprError(ST.ERR_EXPR_RIGHT_PAREN % \
|
|
(self.Token, self.Token[self.Index:]))
|
|
self.Parens -= 1
|
|
return Ret
|
|
|
|
if self.IsAtomicItem():
|
|
if self.Token[self.Index - 1] == '"':
|
|
return self.STRINGITEM
|
|
return self.LOGICAL
|
|
elif self.IsAtomicNumVal():
|
|
return self.ARITH
|
|
else:
|
|
raise _ExprError(ST.ERR_EXPR_FACTOR % \
|
|
(self.Token[self.Index:], self.Token))
|
|
|
|
## IsValidLogicalExpression
|
|
#
|
|
def IsValidLogicalExpression(self):
|
|
if self.Len == 0:
|
|
return False, ST.ERR_EXPRESS_EMPTY
|
|
try:
|
|
if self.LogicalExpression() not in [self.ARITH, self.LOGICAL, self.REALLOGICAL, self.STRINGITEM]:
|
|
return False, ST.ERR_EXPR_LOGICAL % self.Token
|
|
except _ExprError as XExcept:
|
|
return False, XExcept.Error
|
|
self.SkipWhitespace()
|
|
if self.Index != self.Len:
|
|
return False, (ST.ERR_EXPR_BOOLEAN % \
|
|
(self.Token[self.Index:], self.Token))
|
|
return True, ''
|
|
|
|
## _ValidRangeExpressionParser
|
|
#
|
|
class _ValidRangeExpressionParser(_ExprBase):
|
|
INT_RANGE_PATTERN = '[\t\s]*[0-9]+[\t\s]*-[\t\s]*[0-9]+'
|
|
HEX_RANGE_PATTERN = \
|
|
'[\t\s]*0[xX][a-fA-F0-9]+[\t\s]*-[\t\s]*0[xX][a-fA-F0-9]+'
|
|
def __init__(self, Token):
|
|
_ExprBase.__init__(self, Token)
|
|
self.Parens = 0
|
|
self.HEX = 1
|
|
self.INT = 2
|
|
self.IsParenHappen = False
|
|
self.IsLogicalOpHappen = False
|
|
|
|
## IsValidRangeExpression
|
|
#
|
|
def IsValidRangeExpression(self):
|
|
if self.Len == 0:
|
|
return False, ST.ERR_EXPR_RANGE_EMPTY
|
|
try:
|
|
if self.RangeExpression() not in [self.HEX, self.INT]:
|
|
return False, ST.ERR_EXPR_RANGE % self.Token
|
|
except _ExprError as XExcept:
|
|
return False, XExcept.Error
|
|
|
|
self.SkipWhitespace()
|
|
if self.Index != self.Len:
|
|
return False, (ST.ERR_EXPR_RANGE % self.Token)
|
|
return True, ''
|
|
|
|
## RangeExpression
|
|
#
|
|
def RangeExpression(self):
|
|
Ret = self.Unary()
|
|
while self.IsCurrentOp(['OR', 'AND', 'and', 'or']):
|
|
self.IsLogicalOpHappen = True
|
|
if not self.IsParenHappen:
|
|
raise _ExprError(ST.ERR_PAREN_NOT_USED % self.Token)
|
|
self.IsParenHappen = False
|
|
Ret = self.Unary()
|
|
|
|
if self.IsCurrentOp(['XOR']):
|
|
Ret = self.Unary()
|
|
|
|
return Ret
|
|
|
|
## Unary
|
|
#
|
|
def Unary(self):
|
|
if self.IsCurrentOp(["NOT"]):
|
|
return self.Unary()
|
|
|
|
return self.ValidRange()
|
|
|
|
## ValidRange
|
|
#
|
|
def ValidRange(self):
|
|
Ret = -1
|
|
if self.IsCurrentOp(["("]):
|
|
self.IsLogicalOpHappen = False
|
|
self.IsParenHappen = True
|
|
self.Parens += 1
|
|
if self.Parens > 1:
|
|
raise _ExprError(ST.ERR_EXPR_RANGE_DOUBLE_PAREN_NESTED % self.Token)
|
|
Ret = self.RangeExpression()
|
|
if not self.IsCurrentOp([")"]):
|
|
raise _ExprError(ST.ERR_EXPR_RIGHT_PAREN % self.Token)
|
|
self.Parens -= 1
|
|
return Ret
|
|
|
|
if self.IsLogicalOpHappen:
|
|
raise _ExprError(ST.ERR_PAREN_NOT_USED % self.Token)
|
|
|
|
if self.IsCurrentOp(["LT", "GT", "LE", "GE", "EQ", "XOR"]):
|
|
IntMatch = \
|
|
re.compile(self.INT_PATTERN).match(self.Token[self.Index:])
|
|
HexMatch = \
|
|
re.compile(self.HEX_PATTERN).match(self.Token[self.Index:])
|
|
if HexMatch and HexMatch.start() == 0:
|
|
self.Index += HexMatch.end()
|
|
Ret = self.HEX
|
|
elif IntMatch and IntMatch.start() == 0:
|
|
self.Index += IntMatch.end()
|
|
Ret = self.INT
|
|
else:
|
|
raise _ExprError(ST.ERR_EXPR_RANGE_FACTOR % (self.Token[self.Index:], self.Token))
|
|
else:
|
|
IntRangeMatch = re.compile(
|
|
self.INT_RANGE_PATTERN).match(self.Token[self.Index:]
|
|
)
|
|
HexRangeMatch = re.compile(
|
|
self.HEX_RANGE_PATTERN).match(self.Token[self.Index:]
|
|
)
|
|
if HexRangeMatch and HexRangeMatch.start() == 0:
|
|
self.Index += HexRangeMatch.end()
|
|
Ret = self.HEX
|
|
elif IntRangeMatch and IntRangeMatch.start() == 0:
|
|
self.Index += IntRangeMatch.end()
|
|
Ret = self.INT
|
|
else:
|
|
raise _ExprError(ST.ERR_EXPR_RANGE % self.Token)
|
|
|
|
return Ret
|
|
|
|
## _ValidListExpressionParser
|
|
#
|
|
class _ValidListExpressionParser(_ExprBase):
|
|
VALID_LIST_PATTERN = '(0[xX][0-9a-fA-F]+|[0-9]+)([\t\s]*,[\t\s]*(0[xX][0-9a-fA-F]+|[0-9]+))*'
|
|
def __init__(self, Token):
|
|
_ExprBase.__init__(self, Token)
|
|
self.NUM = 1
|
|
|
|
def IsValidListExpression(self):
|
|
if self.Len == 0:
|
|
return False, ST.ERR_EXPR_LIST_EMPTY
|
|
try:
|
|
if self.ListExpression() not in [self.NUM]:
|
|
return False, ST.ERR_EXPR_LIST % self.Token
|
|
except _ExprError as XExcept:
|
|
return False, XExcept.Error
|
|
|
|
self.SkipWhitespace()
|
|
if self.Index != self.Len:
|
|
return False, (ST.ERR_EXPR_LIST % self.Token)
|
|
|
|
return True, ''
|
|
|
|
def ListExpression(self):
|
|
Ret = -1
|
|
self.SkipWhitespace()
|
|
ListMatch = re.compile(self.VALID_LIST_PATTERN).match(self.Token[self.Index:])
|
|
if ListMatch and ListMatch.start() == 0:
|
|
self.Index += ListMatch.end()
|
|
Ret = self.NUM
|
|
else:
|
|
raise _ExprError(ST.ERR_EXPR_LIST % self.Token)
|
|
|
|
return Ret
|
|
|
|
## _StringTestParser
|
|
#
|
|
class _StringTestParser(_ExprBase):
|
|
def __init__(self, Token):
|
|
_ExprBase.__init__(self, Token)
|
|
|
|
## IsValidStringTest
|
|
#
|
|
def IsValidStringTest(self):
|
|
if self.Len == 0:
|
|
return False, ST.ERR_EXPR_EMPTY
|
|
try:
|
|
self.StringTest()
|
|
except _ExprError as XExcept:
|
|
return False, XExcept.Error
|
|
return True, ''
|
|
|
|
## StringItem
|
|
#
|
|
def StringItem(self):
|
|
Match1 = re.compile(self.QUOTED_PATTERN)\
|
|
.match(self.Token[self.Index:].replace('\\\\', '//')\
|
|
.replace('\\\"', '\\\''))
|
|
Match2 = re.compile(self.MACRO_PATTERN).match(self.Token[self.Index:])
|
|
Match3 = re.compile(self.PCD_PATTERN).match(self.Token[self.Index:])
|
|
MatchList = [Match1, Match2, Match3]
|
|
for Match in MatchList:
|
|
if Match and Match.start() == 0:
|
|
if not _ValidateToken(
|
|
self.Token[self.Index:self.Index+Match.end()]
|
|
):
|
|
raise _ExprError(ST.ERR_EXPR_STRING_ITEM % \
|
|
(self.Token, self.Token[self.Index:]))
|
|
self.Index += Match.end()
|
|
Token = self.Token[self.Index - Match.end():self.Index]
|
|
if Token.strip() in ["EQ", "NE"]:
|
|
raise _ExprError(ST.ERR_EXPR_STRING_ITEM % \
|
|
(self.Token, self.Token[self.Index:]))
|
|
return
|
|
else:
|
|
raise _ExprError(ST.ERR_EXPR_STRING_ITEM % \
|
|
(self.Token, self.Token[self.Index:]))
|
|
|
|
## StringTest
|
|
#
|
|
def StringTest(self):
|
|
self.StringItem()
|
|
if not self.IsCurrentOp(["==", "EQ", "!=", "NE"]):
|
|
raise _ExprError(ST.ERR_EXPR_EQUALITY % \
|
|
(self.Token[self.Index:], self.Token))
|
|
self.StringItem()
|
|
if self.Index != self.Len:
|
|
raise _ExprError(ST.ERR_EXPR_BOOLEAN % \
|
|
(self.Token[self.Index:], self.Token))
|
|
|
|
##
|
|
# Check syntax of string test
|
|
#
|
|
# @param Token: string test token
|
|
#
|
|
def IsValidStringTest(Token, Flag=False):
|
|
#
|
|
# Not do the check right now, keep the implementation for future enhancement.
|
|
#
|
|
if not Flag:
|
|
return True, ""
|
|
return _StringTestParser(Token).IsValidStringTest()
|
|
|
|
|
|
##
|
|
# Check syntax of logical expression
|
|
#
|
|
# @param Token: expression token
|
|
#
|
|
def IsValidLogicalExpr(Token, Flag=False):
|
|
#
|
|
# Not do the check right now, keep the implementation for future enhancement.
|
|
#
|
|
if not Flag:
|
|
return True, ""
|
|
return _LogicalExpressionParser(Token).IsValidLogicalExpression()
|
|
|
|
##
|
|
# Check syntax of range expression
|
|
#
|
|
# @param Token: range expression token
|
|
#
|
|
def IsValidRangeExpr(Token):
|
|
return _ValidRangeExpressionParser(Token).IsValidRangeExpression()
|
|
|
|
##
|
|
# Check syntax of value list expression token
|
|
#
|
|
# @param Token: value list expression token
|
|
#
|
|
def IsValidListExpr(Token):
|
|
return _ValidListExpressionParser(Token).IsValidListExpression()
|
|
|
|
##
|
|
# Check whether the feature flag expression is valid or not
|
|
#
|
|
# @param Token: feature flag expression
|
|
#
|
|
def IsValidFeatureFlagExp(Token, Flag=False):
|
|
#
|
|
# Not do the check right now, keep the implementation for future enhancement.
|
|
#
|
|
if not Flag:
|
|
return True, "", Token
|
|
else:
|
|
if Token in ['TRUE', 'FALSE', 'true', 'false', 'True', 'False',
|
|
'0x1', '0x01', '0x0', '0x00']:
|
|
return True, ""
|
|
Valid, Cause = IsValidStringTest(Token, Flag)
|
|
if not Valid:
|
|
Valid, Cause = IsValidLogicalExpr(Token, Flag)
|
|
if not Valid:
|
|
return False, Cause
|
|
return True, ""
|
|
|
|
if __name__ == '__main__':
|
|
# print IsValidRangeExpr('LT 9')
|
|
print(_LogicalExpressionParser('gCrownBayTokenSpaceGuid.PcdPciDevice1BridgeAddressLE0').IsValidLogicalExpression())
|
|
|
|
|
|
|