]> git.proxmox.com Git - mirror_edk2.git/blobdiff - BaseTools/Source/Python/Common/Expression.py
BaseTools: Add PcdValueCommon logic into C source CommonLib
[mirror_edk2.git] / BaseTools / Source / Python / Common / Expression.py
index e2889a8dd30e7e3ca9c1b80b74b1254e943b42df..ba83e02f75d3bfc00ef2993f80f7a64e221a01cc 100644 (file)
@@ -1,7 +1,7 @@
 ## @file\r
 # This file is used to parse and evaluate expression in directive or PCD value.\r
 #\r
-# Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+# Copyright (c) 2011 - 2017, Intel Corporation. All rights reserved.<BR>\r
 # This program and the accompanying materials\r
 # are licensed and made available under the terms and conditions of the BSD License\r
 # which accompanies this distribution.    The full text of the license may be found at\r
@@ -14,7 +14,6 @@
 #\r
 from Common.GlobalData import *\r
 from CommonDataClass.Exceptions import BadExpression\r
-from CommonDataClass.Exceptions import SymbolNotFound\r
 from CommonDataClass.Exceptions import WrnExpression\r
 from Misc import GuidStringToGuidStructureString\r
 \r
@@ -36,6 +35,8 @@ ERR_RELCMP_STR_OTHERS   = 'Operator taking Operand of string type and Boolean/Nu
 ERR_STRING_CMP          = 'Unicode string and general string cannot be compared: [%s %s %s]'\r
 ERR_ARRAY_TOKEN         = 'Bad C array or C format GUID token: [%s].'\r
 ERR_ARRAY_ELE           = 'This must be HEX value for NList or Array: [%s].'\r
+ERR_EMPTY_EXPR          = 'Empty expression is not allowed.'\r
+ERR_IN_OPERAND          = 'Macro after IN operator can only be: $(FAMILY), $(ARCH), $(TOOL_CHAIN_TAG) and $(TARGET).'\r
 \r
 ## SplitString\r
 #  Split string to list according double quote\r
@@ -75,6 +76,10 @@ def ReplaceExprMacro(String, Macros, ExceptionList = None):
             InQuote = True\r
         MacroStartPos = String.find('$(')\r
         if MacroStartPos < 0:\r
+            for Pcd in gPlatformPcds.keys():\r
+                if Pcd in String:\r
+                    if Pcd not in gConditionalPcds:\r
+                        gConditionalPcds.append(Pcd)\r
             continue\r
         RetStr = ''\r
         while MacroStartPos >= 0:\r
@@ -88,22 +93,29 @@ def ReplaceExprMacro(String, Macros, ExceptionList = None):
                 # If an undefined macro name appears in the constant-expression of\r
                 # !if or !elif, it is replaced by the integer constant 0.\r
                 RetStr += '0'\r
-            elif not InQuote and ExceptionList and Macro in ExceptionList:\r
+            elif not InQuote:\r
+                Tklst = RetStr.split()\r
+                if Tklst and Tklst[-1] in ['IN', 'in'] and ExceptionList and Macro not in ExceptionList:\r
+                    raise BadExpression(ERR_IN_OPERAND)\r
                 # Make sure the macro in exception list is encapsulated by double quote\r
                 # For example: DEFINE ARCH = IA32 X64\r
                 # $(ARCH) is replaced with "IA32 X64"\r
-                RetStr += '"' + Macros[Macro] + '"'\r
-            else:\r
-                if Macros[Macro].strip() != "":\r
+                if ExceptionList and Macro in ExceptionList:\r
+                    RetStr += '"' + Macros[Macro] + '"'\r
+                elif Macros[Macro].strip():\r
                     RetStr += Macros[Macro]\r
                 else:\r
                     RetStr += '""'\r
+            else:\r
+                RetStr += Macros[Macro]\r
             RetStr += String[MacroEndPos+1:]\r
             String = RetStr\r
             MacroStartPos = String.find('$(')\r
         StrList[i] = RetStr\r
     return ''.join(StrList)\r
 \r
+SupportedInMacroList = ['TARGET', 'TOOL_CHAIN_TAG', 'ARCH', 'FAMILY']\r
+\r
 class ValueExpression(object):\r
     # Logical operator mapping\r
     LogicalOperators = {\r
@@ -117,7 +129,7 @@ class ValueExpression(object):
         'IN' : 'in'\r
     }\r
 \r
-    NonLetterOpLst = ['+', '-', '&', '|', '^', '!', '=', '>', '<']\r
+    NonLetterOpLst = ['+', '-', '*', '/', '%', '&', '|', '^', '~', '<<', '>>', '!', '=', '>', '<', '?', ':']\r
 \r
     PcdPattern = re.compile(r'[_a-zA-Z][0-9A-Za-z_]*\.[_a-zA-Z][0-9A-Za-z_]*$')\r
     HexPattern = re.compile(r'0[xX][0-9a-fA-F]+$')\r
@@ -133,7 +145,7 @@ class ValueExpression(object):
     @staticmethod\r
     def Eval(Operator, Oprand1, Oprand2 = None):\r
         WrnExp = None\r
-        \r
+\r
         if Operator not in ["==", "!=", ">=", "<=", ">", "<", "in", "not in"] and \\r
             (type(Oprand1) == type('') or type(Oprand2) == type('')):\r
             raise BadExpression(ERR_STRING_EXPR % Operator)\r
@@ -150,6 +162,10 @@ class ValueExpression(object):
             if type(Oprand1) == type(''):\r
                 raise BadExpression(ERR_STRING_EXPR % Operator)\r
             EvalStr = 'not Oprand1'\r
+        elif Operator in ["~"]:\r
+            if type(Oprand1) == type(''):\r
+                raise BadExpression(ERR_STRING_EXPR % Operator)\r
+            EvalStr = '~ Oprand1'\r
         else:\r
             if Operator in ["+", "-"] and (type(True) in [type(Oprand1), type(Oprand2)]):\r
                 # Boolean in '+'/'-' will be evaluated but raise warning\r
@@ -166,13 +182,13 @@ class ValueExpression(object):
                     raise WrnExp\r
                 else:\r
                     raise BadExpression(ERR_RELCMP_STR_OTHERS % Operator)\r
-            elif TypeDict[type(Oprand1)] != TypeDict[type(Oprand2)]: \r
+            elif TypeDict[type(Oprand1)] != TypeDict[type(Oprand2)]:\r
                 if Operator in ["==", "!=", ">=", "<=", ">", "<"] and set((TypeDict[type(Oprand1)], TypeDict[type(Oprand2)])) == set((TypeDict[type(True)], TypeDict[type(0)])):\r
                     # comparison between number and boolean is allowed\r
                     pass\r
-                elif Operator in ['&', '|', '^', "&&", "||"] and set((TypeDict[type(Oprand1)], TypeDict[type(Oprand2)])) == set((TypeDict[type(True)], TypeDict[type(0)])):\r
+                elif Operator in ['&', '|', '^', "and", "or"] and set((TypeDict[type(Oprand1)], TypeDict[type(Oprand2)])) == set((TypeDict[type(True)], TypeDict[type(0)])):\r
                     # bitwise and logical operation between number and boolean is allowed\r
-                    pass                \r
+                    pass\r
                 else:\r
                     raise BadExpression(ERR_EXPR_TYPE)\r
             if type(Oprand1) == type('') and type(Oprand2) == type(''):\r
@@ -198,7 +214,7 @@ class ValueExpression(object):
                 Val = True\r
             else:\r
                 Val = False\r
-        \r
+\r
         if WrnExp:\r
             WrnExp.result = Val\r
             raise WrnExp\r
@@ -213,11 +229,10 @@ class ValueExpression(object):
 \r
         self._Expr = ReplaceExprMacro(Expression.strip(),\r
                                   SymbolTable,\r
-                                  ['TARGET', 'TOOL_CHAIN_TAG', 'ARCH'])\r
+                                  SupportedInMacroList)\r
 \r
         if not self._Expr.strip():\r
-            self._NoProcess = True\r
-            return\r
+            raise BadExpression(ERR_EMPTY_EXPR)\r
 \r
         #\r
         # The symbol table including PCD and macro mapping\r
@@ -227,25 +242,69 @@ class ValueExpression(object):
         self._Idx = 0\r
         self._Len = len(self._Expr)\r
         self._Token = ''\r
+        self._WarnExcept = None\r
 \r
         # Literal token without any conversion\r
         self._LiteralToken = ''\r
 \r
     # Public entry for this class\r
-    def __call__(self):\r
+    #   @param RealValue: False: only evaluate if the expression is true or false, used for conditional expression\r
+    #                     True : return the evaluated str(value), used for PCD value\r
+    #\r
+    #   @return: True or False if RealValue is False\r
+    #            Evaluated value of string format if RealValue is True\r
+    #\r
+    def __call__(self, RealValue=False, Depth=0):\r
         if self._NoProcess:\r
             return self._Expr\r
 \r
-        Val = self._OrExpr()\r
-        if type(Val) == type('') and Val == 'L""':\r
-            Val = ''\r
+        self._Depth = Depth\r
+\r
+        self._Expr = self._Expr.strip()\r
+        if RealValue and Depth == 0:\r
+            self._Token = self._Expr\r
+            if self.__IsNumberToken():\r
+                return self._Expr\r
+\r
+            try:\r
+                Token = self._GetToken()\r
+                if type(Token) == type('') and Token.startswith('{') and Token.endswith('}') and self._Idx >= self._Len:\r
+                    return self._Expr\r
+            except BadExpression:\r
+                pass\r
+\r
+            self._Idx = 0\r
+            self._Token = ''\r
+\r
+        Val = self._ConExpr()\r
+        RealVal = Val\r
+        if type(Val) == type(''):\r
+            if Val == 'L""':\r
+                Val = False\r
+            elif not Val:\r
+                Val = False\r
+                RealVal = '""'\r
+            elif not Val.startswith('L"') and not Val.startswith('{'):\r
+                Val = True\r
+                RealVal = '"' + RealVal + '"'\r
 \r
         # The expression has been parsed, but the end of expression is not reached\r
         # It means the rest does not comply EBNF of <Expression>\r
         if self._Idx != self._Len:\r
             raise BadExpression(ERR_SNYTAX % self._Expr[self._Idx:])\r
 \r
-        return Val\r
+        if RealValue:\r
+            RetVal = str(RealVal)\r
+        elif Val:\r
+            RetVal = True\r
+        else:\r
+            RetVal = False\r
+\r
+        if self._WarnExcept:\r
+            self._WarnExcept.result = RetVal\r
+            raise self._WarnExcept\r
+        else:\r
+            return RetVal\r
 \r
     # Template function to parse binary operators which have same precedence\r
     # Expr [Operator Expr]*\r
@@ -253,8 +312,24 @@ class ValueExpression(object):
         Val = EvalFunc()\r
         while self._IsOperator(OpLst):\r
             Op = self._Token\r
-            Val = self.Eval(Op, Val, EvalFunc())\r
+            if Op == '?':\r
+                Val2 = EvalFunc()\r
+                if self._IsOperator(':'):\r
+                    Val3 = EvalFunc()\r
+                if Val:\r
+                    Val = Val2\r
+                else:\r
+                    Val = Val3\r
+                continue\r
+            try:\r
+                Val = self.Eval(Op, Val, EvalFunc())\r
+            except WrnExpression, Warn:\r
+                self._WarnExcept = Warn\r
+                Val = Warn.result\r
         return Val\r
+    # A [? B]*\r
+    def _ConExpr(self):\r
+        return self._ExprFuncTemplate(self._OrExpr, ['?', ':'])\r
 \r
     # A [|| B]*\r
     def _OrExpr(self):\r
@@ -285,29 +360,51 @@ class ValueExpression(object):
                 if not self._IsOperator(["IN", "in"]):\r
                     raise BadExpression(ERR_REL_NOT_IN)\r
                 Op += ' ' + self._Token\r
-            Val = self.Eval(Op, Val, self._RelExpr())\r
+            try:\r
+                Val = self.Eval(Op, Val, self._RelExpr())\r
+            except WrnExpression, Warn:\r
+                self._WarnExcept = Warn\r
+                Val = Warn.result\r
         return Val\r
 \r
     # A [ > B]*\r
     def _RelExpr(self):\r
-        return self._ExprFuncTemplate(self._AddExpr, ["<=", ">=", "<", ">", "LE", "GE", "LT", "GT"])\r
+        return self._ExprFuncTemplate(self._ShiftExpr, ["<=", ">=", "<", ">", "LE", "GE", "LT", "GT"])\r
+\r
+    def _ShiftExpr(self):\r
+        return self._ExprFuncTemplate(self._AddExpr, ["<<", ">>"])\r
 \r
     # A [ + B]*\r
     def _AddExpr(self):\r
-        return self._ExprFuncTemplate(self._UnaryExpr, ["+", "-"])\r
+        return self._ExprFuncTemplate(self._MulExpr, ["+", "-"])\r
+\r
+    # A [ * B]*\r
+    def _MulExpr(self):\r
+        return self._ExprFuncTemplate(self._UnaryExpr, ["*", "/", "%"])\r
 \r
     # [!]*A\r
     def _UnaryExpr(self):\r
         if self._IsOperator(["!", "NOT", "not"]):\r
             Val = self._UnaryExpr()\r
-            return self.Eval('not', Val)\r
+            try:\r
+                return self.Eval('not', Val)\r
+            except WrnExpression, Warn:\r
+                self._WarnExcept = Warn\r
+                return Warn.result\r
+        if self._IsOperator(["~"]):\r
+            Val = self._UnaryExpr()\r
+            try:\r
+                return self.Eval('~', Val)\r
+            except WrnExpression, Warn:\r
+                self._WarnExcept = Warn\r
+                return Warn.result\r
         return self._IdenExpr()\r
 \r
     # Parse identifier or encapsulated expression\r
     def _IdenExpr(self):\r
         Tk = self._GetToken()\r
         if Tk == '(':\r
-            Val = self._OrExpr()\r
+            Val = self._ConExpr()\r
             try:\r
                 # _GetToken may also raise BadExpression\r
                 if self._GetToken() != ')':\r
@@ -389,7 +486,7 @@ class ValueExpression(object):
     def __GetIdToken(self, IsAlphaOp = False):\r
         IdToken = ''\r
         for Ch in self._Expr[self._Idx:]:\r
-            if not self.__IsIdChar(Ch):\r
+            if not self.__IsIdChar(Ch) or ('?' in self._Expr and Ch == ':'):\r
                 break\r
             self._Idx += 1\r
             IdToken += Ch\r
@@ -407,8 +504,10 @@ class ValueExpression(object):
         # PCD token\r
         if self.PcdPattern.match(self._Token):\r
             if self._Token not in self._Symb:\r
-                raise SymbolNotFound(ERR_PCD_RESOLVE % self._Token)\r
-            self._Token = ValueExpression(self._Symb[self._Token], self._Symb)()\r
+                Ex = BadExpression(ERR_PCD_RESOLVE % self._Token)\r
+                Ex.Pcd = self._Token\r
+                raise Ex\r
+            self._Token = ValueExpression(self._Symb[self._Token], self._Symb)(True, self._Depth+1)\r
             if type(self._Token) != type(''):\r
                 self._LiteralToken = hex(self._Token)\r
                 return\r
@@ -459,7 +558,7 @@ class ValueExpression(object):
             if not Token:\r
                 self._LiteralToken = '0x0'\r
             else:\r
-                self._LiteralToken = '0x' + Token\r
+                self._LiteralToken = '0x' + Token.lower()\r
             return True\r
         return False\r
 \r
@@ -468,7 +567,7 @@ class ValueExpression(object):
 \r
     @staticmethod\r
     def __IsIdChar(Ch):\r
-        return Ch in '._/:' or Ch.isalnum()\r
+        return Ch in '._:' or Ch.isalnum()\r
 \r
     # Parse operand\r
     def _GetSingleToken(self):\r
@@ -488,7 +587,7 @@ class ValueExpression(object):
             if Match and not Expr[Match.end():Match.end()+1].isalnum() \\r
                 and Expr[Match.end():Match.end()+1] != '_':\r
                 self._Idx += Match.end()\r
-                self._Token = ValueExpression(GuidStringToGuidStructureString(Expr[0:Match.end()]))()\r
+                self._Token = ValueExpression(GuidStringToGuidStructureString(Expr[0:Match.end()]))(True, self._Depth+1)\r
                 return self._Token\r
             elif self.__IsIdChar(Ch):\r
                 return self.__GetIdToken()\r
@@ -506,7 +605,7 @@ class ValueExpression(object):
     # Parse operator\r
     def _GetOperator(self):\r
         self.__SkipWS()\r
-        LegalOpLst = ['&&', '||', '!=', '==', '>=', '<='] + self.NonLetterOpLst\r
+        LegalOpLst = ['&&', '||', '!=', '==', '>=', '<='] + self.NonLetterOpLst + ['?',':']\r
 \r
         self._Token = ''\r
         Expr = self._Expr[self._Idx:]\r
@@ -526,7 +625,7 @@ class ValueExpression(object):
         OpToken = ''\r
         for Ch in Expr:\r
             if Ch in self.NonLetterOpLst:\r
-                if '!' == Ch and OpToken in ['!=', '!']:\r
+                if '!' == Ch and OpToken:\r
                     break\r
                 self._Idx += 1\r
                 OpToken += Ch\r
@@ -551,5 +650,15 @@ class ValueExpression(object):
 \r
 if __name__ == '__main__':\r
     pass\r
-\r
-\r
+    while True:\r
+        input = raw_input('Input expr: ')\r
+        if input in 'qQ':\r
+            break\r
+        try:\r
+            print ValueExpression(input)(True)\r
+            print ValueExpression(input)(False)\r
+        except WrnExpression, Ex:\r
+            print Ex.result\r
+            print str(Ex)\r
+        except Exception, Ex:\r
+            print str(Ex)\r