Home | History | Annotate | Download | only in Common
      1 ## @file

      2 # This file is used to parse and evaluate expression in directive or PCD value.

      3 #

      4 # Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>

      5 # This program and the accompanying materials

      6 # are licensed and made available under the terms and conditions of the BSD License

      7 # which accompanies this distribution.    The full text of the license may be found at

      8 # http://opensource.org/licenses/bsd-license.php

      9 #

     10 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

     11 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

     12 
     13 ## Import Modules

     14 #

     15 from Common.GlobalData import *
     16 from CommonDataClass.Exceptions import BadExpression
     17 from CommonDataClass.Exceptions import WrnExpression
     18 from Misc import GuidStringToGuidStructureString
     19 
     20 ERR_STRING_EXPR         = 'This operator cannot be used in string expression: [%s].'
     21 ERR_SNYTAX              = 'Syntax error, the rest of expression cannot be evaluated: [%s].'
     22 ERR_MATCH               = 'No matching right parenthesis.'
     23 ERR_STRING_TOKEN        = 'Bad string token: [%s].'
     24 ERR_MACRO_TOKEN         = 'Bad macro token: [%s].'
     25 ERR_EMPTY_TOKEN         = 'Empty token is not allowed.'
     26 ERR_PCD_RESOLVE         = 'PCD token cannot be resolved: [%s].'
     27 ERR_VALID_TOKEN         = 'No more valid token found from rest of string: [%s].'
     28 ERR_EXPR_TYPE           = 'Different types found in expression.'
     29 ERR_OPERATOR_UNSUPPORT  = 'Unsupported operator: [%s]'
     30 ERR_REL_NOT_IN          = 'Expect "IN" after "not" operator.'
     31 WRN_BOOL_EXPR           = 'Operand of boolean type cannot be used in arithmetic expression.'
     32 WRN_EQCMP_STR_OTHERS    = '== Comparison between Operand of string type and Boolean/Number Type always return False.'
     33 WRN_NECMP_STR_OTHERS    = '!= Comparison between Operand of string type and Boolean/Number Type always return True.'
     34 ERR_RELCMP_STR_OTHERS   = 'Operator taking Operand of string type and Boolean/Number Type is not allowed: [%s].'
     35 ERR_STRING_CMP          = 'Unicode string and general string cannot be compared: [%s %s %s]'
     36 ERR_ARRAY_TOKEN         = 'Bad C array or C format GUID token: [%s].'
     37 ERR_ARRAY_ELE           = 'This must be HEX value for NList or Array: [%s].'
     38 ERR_EMPTY_EXPR          = 'Empty expression is not allowed.'
     39 ERR_IN_OPERAND          = 'Macro after IN operator can only be: $(FAMILY), $(ARCH), $(TOOL_CHAIN_TAG) and $(TARGET).'
     40 
     41 ## SplitString

     42 #  Split string to list according double quote

     43 #  For example: abc"de\"f"ghi"jkl"mn will be: ['abc', '"de\"f"', 'ghi', '"jkl"', 'mn']

     44 #

     45 def SplitString(String):
     46     # There might be escaped quote: "abc\"def\\\"ghi"

     47     Str = String.replace('\\\\', '//').replace('\\\"', '\\\'')
     48     RetList = []
     49     InQuote = False
     50     Item = ''
     51     for i, ch in enumerate(Str):
     52         if ch == '"':
     53             InQuote = not InQuote
     54             if not InQuote:
     55                 Item += String[i]
     56                 RetList.append(Item)
     57                 Item = ''
     58                 continue
     59             if Item:
     60                 RetList.append(Item)
     61                 Item = ''
     62         Item += String[i]
     63     if InQuote:
     64         raise BadExpression(ERR_STRING_TOKEN % Item)
     65     if Item:
     66         RetList.append(Item)
     67     return RetList
     68 
     69 ## ReplaceExprMacro
     70 #
     71 def ReplaceExprMacro(String, Macros, ExceptionList = None):
     72     StrList = SplitString(String)
     73     for i, String in enumerate(StrList):
     74         InQuote = False
     75         if String.startswith('"'):
     76             InQuote = True
     77         MacroStartPos = String.find('$(')
     78         if MacroStartPos < 0:
     79             continue
     80         RetStr = ''
     81         while MacroStartPos >= 0:
     82             RetStr = String[0:MacroStartPos]
     83             MacroEndPos = String.find(')', MacroStartPos)
     84             if MacroEndPos < 0:
     85                 raise BadExpression(ERR_MACRO_TOKEN % String[MacroStartPos:])
     86             Macro = String[MacroStartPos+2:MacroEndPos]
     87             if Macro not in Macros:
     88                 # From C reference manual:
     89                 # If an undefined macro name appears in the constant-expression of
     90                 # !if or !elif, it is replaced by the integer constant 0.
     91                 RetStr += '0'
     92             elif not InQuote:
     93                 Tklst = RetStr.split()
     94                 if Tklst and Tklst[-1] in ['IN', 'in'] and ExceptionList and Macro not in ExceptionList:
     95                     raise BadExpression(ERR_IN_OPERAND)
     96                 # Make sure the macro in exception list is encapsulated by double quote
     97                 # For example: DEFINE ARCH = IA32 X64
     98                 # $(ARCH) is replaced with "IA32 X64"
     99                 if ExceptionList and Macro in ExceptionList:
    100                     RetStr += '"' + Macros[Macro] + '"'
    101                 elif Macros[Macro].strip():
    102                     RetStr += Macros[Macro]
    103                 else:
    104                     RetStr += '""'
    105             else:
    106                 RetStr += Macros[Macro]
    107             RetStr += String[MacroEndPos+1:]
    108             String = RetStr
    109             MacroStartPos = String.find('$(')
    110         StrList[i] = RetStr
    111     return ''.join(StrList)
    112 
    113 SupportedInMacroList = ['TARGET', 'TOOL_CHAIN_TAG', 'ARCH', 'FAMILY']
    114 
    115 class ValueExpression(object):
    116     # Logical operator mapping
    117     LogicalOperators = {
    118         '&&' : 'and', '||' : 'or',
    119         '!'  : 'not', 'AND': 'and',
    120         'OR' : 'or' , 'NOT': 'not',
    121         'XOR': '^'  , 'xor': '^',
    122         'EQ' : '==' , 'NE' : '!=',
    123         'GT' : '>'  , 'LT' : '<',
    124         'GE' : '>=' , 'LE' : '<=',
    125         'IN' : 'in'
    126     }
    127 
    128     NonLetterOpLst = ['+', '-', '&', '|', '^', '!', '=', '>', '<']
    129 
    130     PcdPattern = re.compile(r'[_a-zA-Z][0-9A-Za-z_]*\.[_a-zA-Z][0-9A-Za-z_]*$')
    131     HexPattern = re.compile(r'0[xX][0-9a-fA-F]+$')
    132     RegGuidPattern = re.compile(r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}')
    133 
    134     SymbolPattern = re.compile("("
    135                                  "\$\([A-Z][A-Z0-9_]*\)|\$\(\w+\.\w+\)|\w+\.\w+|"
    136                                  "&&|\|\||!(?!=)|"
    137                                  "(?<=\W)AND(?=\W)|(?<=\W)OR(?=\W)|(?<=\W)NOT(?=\W)|(?<=\W)XOR(?=\W)|"
    138                                  "(?<=\W)EQ(?=\W)|(?<=\W)NE(?=\W)|(?<=\W)GT(?=\W)|(?<=\W)LT(?=\W)|(?<=\W)GE(?=\W)|(?<=\W)LE(?=\W)"
    139                                ")")
    140 
    141     @staticmethod
    142     def Eval(Operator, Oprand1, Oprand2 = None):
    143         WrnExp = None
    144 
    145         if Operator not in ["==", "!=", ">=", "<=", ">", "<", "in", "not in"] and \
    146             (type(Oprand1) == type('') or type(Oprand2) == type('')):
    147             raise BadExpression(ERR_STRING_EXPR % Operator)
    148 
    149         TypeDict = {
    150             type(0)  : 0,
    151             type(0L) : 0,
    152             type('') : 1,
    153             type(True) : 2
    154         }
    155 
    156         EvalStr = ''
    157         if Operator in ["!", "NOT", "not"]:
    158             if type(Oprand1) == type(''):
    159                 raise BadExpression(ERR_STRING_EXPR % Operator)
    160             EvalStr = 'not Oprand1'
    161         else:
    162             if Operator in ["+", "-"] and (type(True) in [type(Oprand1), type(Oprand2)]):
    163                 # Boolean in '+'/'-' will be evaluated but raise warning
    164                 WrnExp = WrnExpression(WRN_BOOL_EXPR)
    165             elif type('') in [type(Oprand1), type(Oprand2)] and type(Oprand1)!= type(Oprand2):
    166                 # == between string and number/boolean will always return False, != return True
    167                 if Operator == "==":
    168                     WrnExp = WrnExpression(WRN_EQCMP_STR_OTHERS)
    169                     WrnExp.result = False
    170                     raise WrnExp
    171                 elif Operator == "!=":
    172                     WrnExp = WrnExpression(WRN_NECMP_STR_OTHERS)
    173                     WrnExp.result = True
    174                     raise WrnExp
    175                 else:
    176                     raise BadExpression(ERR_RELCMP_STR_OTHERS % Operator)
    177             elif TypeDict[type(Oprand1)] != TypeDict[type(Oprand2)]:
    178                 if Operator in ["==", "!=", ">=", "<=", ">", "<"] and set((TypeDict[type(Oprand1)], TypeDict[type(Oprand2)])) == set((TypeDict[type(True)], TypeDict[type(0)])):
    179                     # comparison between number and boolean is allowed
    180                     pass
    181                 elif Operator in ['&', '|', '^', "and", "or"] and set((TypeDict[type(Oprand1)], TypeDict[type(Oprand2)])) == set((TypeDict[type(True)], TypeDict[type(0)])):
    182                     # bitwise and logical operation between number and boolean is allowed
    183                     pass
    184                 else:
    185                     raise BadExpression(ERR_EXPR_TYPE)
    186             if type(Oprand1) == type('') and type(Oprand2) == type(''):
    187                 if (Oprand1.startswith('L"') and not Oprand2.startswith('L"')) or \
    188                     (not Oprand1.startswith('L"') and Oprand2.startswith('L"')):
    189                     raise BadExpression(ERR_STRING_CMP % (Oprand1, Operator, Oprand2))
    190             if 'in' in Operator and type(Oprand2) == type(''):
    191                 Oprand2 = Oprand2.split()
    192             EvalStr = 'Oprand1 ' + Operator + ' Oprand2'
    193 
    194         # Local symbols used by built in eval function
    195         Dict = {
    196             'Oprand1' : Oprand1,
    197             'Oprand2' : Oprand2
    198         }
    199         try:
    200             Val = eval(EvalStr, {}, Dict)
    201         except Exception, Excpt:
    202             raise BadExpression(str(Excpt))
    203 
    204         if Operator in ['and', 'or']:
    205             if Val:
    206                 Val = True
    207             else:
    208                 Val = False
    209 
    210         if WrnExp:
    211             WrnExp.result = Val
    212             raise WrnExp
    213         return Val
    214 
    215     def __init__(self, Expression, SymbolTable={}):
    216         self._NoProcess = False
    217         if type(Expression) != type(''):
    218             self._Expr = Expression
    219             self._NoProcess = True
    220             return
    221 
    222         self._Expr = ReplaceExprMacro(Expression.strip(),
    223                                   SymbolTable,
    224                                   SupportedInMacroList)
    225 
    226         if not self._Expr.strip():
    227             raise BadExpression(ERR_EMPTY_EXPR)
    228 
    229         #
    230         # The symbol table including PCD and macro mapping
    231         #
    232         self._Symb = SymbolTable
    233         self._Symb.update(self.LogicalOperators)
    234         self._Idx = 0
    235         self._Len = len(self._Expr)
    236         self._Token = ''
    237         self._WarnExcept = None
    238 
    239         # Literal token without any conversion
    240         self._LiteralToken = ''
    241 
    242     # Public entry for this class
    243     #   @param RealValue: False: only evaluate if the expression is true or false, used for conditional expression
    244     #                     True : return the evaluated str(value), used for PCD value
    245     #
    246     #   @return: True or False if RealValue is False
    247     #            Evaluated value of string format if RealValue is True
    248     #
    249     def __call__(self, RealValue=False, Depth=0):
    250         if self._NoProcess:
    251             return self._Expr
    252 
    253         self._Depth = Depth
    254 
    255         self._Expr = self._Expr.strip()
    256         if RealValue and Depth == 0:
    257             self._Token = self._Expr
    258             if self.__IsNumberToken():
    259                 return self._Expr
    260 
    261             try:
    262                 Token = self._GetToken()
    263                 if type(Token) == type('') and Token.startswith('{') and Token.endswith('}') and self._Idx >= self._Len:
    264                     return self._Expr
    265             except BadExpression:
    266                 pass
    267 
    268             self._Idx = 0
    269             self._Token = ''
    270 
    271         Val = self._OrExpr()
    272         RealVal = Val
    273         if type(Val) == type(''):
    274             if Val == 'L""':
    275                 Val = False
    276             elif not Val:
    277                 Val = False
    278                 RealVal = '""'
    279             elif not Val.startswith('L"') and not Val.startswith('{'):
    280                 Val = True
    281                 RealVal = '"' + RealVal + '"'
    282 
    283         # The expression has been parsed, but the end of expression is not reached
    284         # It means the rest does not comply EBNF of <Expression>
    285         if self._Idx != self._Len:
    286             raise BadExpression(ERR_SNYTAX % self._Expr[self._Idx:])
    287 
    288         if RealValue:
    289             RetVal = str(RealVal)
    290         elif Val:
    291             RetVal = True
    292         else:
    293             RetVal = False
    294 
    295         if self._WarnExcept:
    296             self._WarnExcept.result = RetVal
    297             raise self._WarnExcept
    298         else:
    299             return RetVal
    300 
    301     # Template function to parse binary operators which have same precedence
    302     # Expr [Operator Expr]*
    303     def _ExprFuncTemplate(self, EvalFunc, OpLst):
    304         Val = EvalFunc()
    305         while self._IsOperator(OpLst):
    306             Op = self._Token
    307             try:
    308                 Val = self.Eval(Op, Val, EvalFunc())
    309             except WrnExpression, Warn:
    310                 self._WarnExcept = Warn
    311                 Val = Warn.result
    312         return Val
    313 
    314     # A [|| B]*
    315     def _OrExpr(self):
    316         return self._ExprFuncTemplate(self._AndExpr, ["OR", "or", "||"])
    317 
    318     # A [&& B]*
    319     def _AndExpr(self):
    320         return self._ExprFuncTemplate(self._BitOr, ["AND", "and", "&&"])
    321 
    322     # A [ | B]*
    323     def _BitOr(self):
    324         return self._ExprFuncTemplate(self._BitXor, ["|"])
    325 
    326     # A [ ^ B]*
    327     def _BitXor(self):
    328         return self._ExprFuncTemplate(self._BitAnd, ["XOR", "xor", "^"])
    329 
    330     # A [ & B]*
    331     def _BitAnd(self):
    332         return self._ExprFuncTemplate(self._EqExpr, ["&"])
    333 
    334     # A [ == B]*
    335     def _EqExpr(self):
    336         Val = self._RelExpr()
    337         while self._IsOperator(["==", "!=", "EQ", "NE", "IN", "in", "!", "NOT", "not"]):
    338             Op = self._Token
    339             if Op in ["!", "NOT", "not"]:
    340                 if not self._IsOperator(["IN", "in"]):
    341                     raise BadExpression(ERR_REL_NOT_IN)
    342                 Op += ' ' + self._Token
    343             try:
    344                 Val = self.Eval(Op, Val, self._RelExpr())
    345             except WrnExpression, Warn:
    346                 self._WarnExcept = Warn
    347                 Val = Warn.result
    348         return Val
    349 
    350     # A [ > B]*
    351     def _RelExpr(self):
    352         return self._ExprFuncTemplate(self._AddExpr, ["<=", ">=", "<", ">", "LE", "GE", "LT", "GT"])
    353 
    354     # A [ + B]*
    355     def _AddExpr(self):
    356         return self._ExprFuncTemplate(self._UnaryExpr, ["+", "-"])
    357 
    358     # [!]*A
    359     def _UnaryExpr(self):
    360         if self._IsOperator(["!", "NOT", "not"]):
    361             Val = self._UnaryExpr()
    362             try:
    363                 return self.Eval('not', Val)
    364             except WrnExpression, Warn:
    365                 self._WarnExcept = Warn
    366                 return Warn.result
    367         return self._IdenExpr()
    368 
    369     # Parse identifier or encapsulated expression
    370     def _IdenExpr(self):
    371         Tk = self._GetToken()
    372         if Tk == '(':
    373             Val = self._OrExpr()
    374             try:
    375                 # _GetToken may also raise BadExpression
    376                 if self._GetToken() != ')':
    377                     raise BadExpression(ERR_MATCH)
    378             except BadExpression:
    379                 raise BadExpression(ERR_MATCH)
    380             return Val
    381         return Tk
    382 
    383     # Skip whitespace or tab
    384     def __SkipWS(self):
    385         for Char in self._Expr[self._Idx:]:
    386             if Char not in ' \t':
    387                 break
    388             self._Idx += 1
    389 
    390     # Try to convert string to number
    391     def __IsNumberToken(self):
    392         Radix = 10
    393         if self._Token.lower()[0:2] == '0x' and len(self._Token) > 2:
    394             Radix = 16
    395         try:
    396             self._Token = int(self._Token, Radix)
    397             return True
    398         except ValueError:
    399             return False
    400         except TypeError:
    401             return False
    402 
    403     # Parse array: {...}
    404     def __GetArray(self):
    405         Token = '{'
    406         self._Idx += 1
    407         self.__GetNList(True)
    408         Token += self._LiteralToken
    409         if self._Idx >= self._Len or self._Expr[self._Idx] != '}':
    410             raise BadExpression(ERR_ARRAY_TOKEN % Token)
    411         Token += '}'
    412 
    413         # All whitespace and tabs in array are already stripped.
    414         IsArray = IsGuid = False
    415         if len(Token.split(',')) == 11 and len(Token.split(',{')) == 2 \
    416             and len(Token.split('},')) == 1:
    417             HexLen = [11,6,6,5,4,4,4,4,4,4,6]
    418             HexList= Token.split(',')
    419             if HexList[3].startswith('{') and \
    420                 not [Index for Index, Hex in enumerate(HexList) if len(Hex) > HexLen[Index]]:
    421                 IsGuid = True
    422         if Token.lstrip('{').rstrip('}').find('{') == -1:
    423             if not [Hex for Hex in Token.lstrip('{').rstrip('}').split(',') if len(Hex) > 4]:
    424                 IsArray = True
    425         if not IsArray and not IsGuid:
    426             raise BadExpression(ERR_ARRAY_TOKEN % Token)
    427         self._Idx += 1
    428         self._Token = self._LiteralToken = Token
    429         return self._Token
    430 
    431     # Parse string, the format must be: "..."
    432     def __GetString(self):
    433         Idx = self._Idx
    434 
    435         # Skip left quote
    436         self._Idx += 1
    437 
    438         # Replace escape \\\", \"
    439         Expr = self._Expr[self._Idx:].replace('\\\\', '//').replace('\\\"', '\\\'')
    440         for Ch in Expr:
    441             self._Idx += 1
    442             if Ch == '"':
    443                 break
    444         self._Token = self._LiteralToken = self._Expr[Idx:self._Idx]
    445         if not self._Token.endswith('"'):
    446             raise BadExpression(ERR_STRING_TOKEN % self._Token)
    447         self._Token = self._Token[1:-1]
    448         return self._Token
    449 
    450     # Get token that is comprised by alphanumeric, underscore or dot(used by PCD)
    451     # @param IsAlphaOp: Indicate if parsing general token or script operator(EQ, NE...)
    452     def __GetIdToken(self, IsAlphaOp = False):
    453         IdToken = ''
    454         for Ch in self._Expr[self._Idx:]:
    455             if not self.__IsIdChar(Ch):
    456                 break
    457             self._Idx += 1
    458             IdToken += Ch
    459 
    460         self._Token = self._LiteralToken = IdToken
    461         if not IsAlphaOp:
    462             self.__ResolveToken()
    463         return self._Token
    464 
    465     # Try to resolve token
    466     def __ResolveToken(self):
    467         if not self._Token:
    468             raise BadExpression(ERR_EMPTY_TOKEN)
    469 
    470         # PCD token
    471         if self.PcdPattern.match(self._Token):
    472             if self._Token not in self._Symb:
    473                 Ex = BadExpression(ERR_PCD_RESOLVE % self._Token)
    474                 Ex.Pcd = self._Token
    475                 raise Ex
    476             self._Token = ValueExpression(self._Symb[self._Token], self._Symb)(True, self._Depth+1)
    477             if type(self._Token) != type(''):
    478                 self._LiteralToken = hex(self._Token)
    479                 return
    480 
    481         if self._Token.startswith('"'):
    482             self._Token = self._Token[1:-1]
    483         elif self._Token in ["FALSE", "false", "False"]:
    484             self._Token = False
    485         elif self._Token in ["TRUE", "true", "True"]:
    486             self._Token = True
    487         else:
    488             self.__IsNumberToken()
    489 
    490     def __GetNList(self, InArray=False):
    491         self._GetSingleToken()
    492         if not self.__IsHexLiteral():
    493             if InArray:
    494                 raise BadExpression(ERR_ARRAY_ELE % self._Token)
    495             return self._Token
    496 
    497         self.__SkipWS()
    498         Expr = self._Expr[self._Idx:]
    499         if not Expr.startswith(','):
    500             return self._Token
    501 
    502         NList = self._LiteralToken
    503         while Expr.startswith(','):
    504             NList += ','
    505             self._Idx += 1
    506             self.__SkipWS()
    507             self._GetSingleToken()
    508             if not self.__IsHexLiteral():
    509                 raise BadExpression(ERR_ARRAY_ELE % self._Token)
    510             NList += self._LiteralToken
    511             self.__SkipWS()
    512             Expr = self._Expr[self._Idx:]
    513         self._Token = self._LiteralToken = NList
    514         return self._Token
    515 
    516     def __IsHexLiteral(self):
    517         if self._LiteralToken.startswith('{') and \
    518             self._LiteralToken.endswith('}'):
    519             return True
    520 
    521         if self.HexPattern.match(self._LiteralToken):
    522             Token = self._LiteralToken[2:]
    523             Token = Token.lstrip('0')
    524             if not Token:
    525                 self._LiteralToken = '0x0'
    526             else:
    527                 self._LiteralToken = '0x' + Token.lower()
    528             return True
    529         return False
    530 
    531     def _GetToken(self):
    532         return self.__GetNList()
    533 
    534     @staticmethod
    535     def __IsIdChar(Ch):
    536         return Ch in '._/:' or Ch.isalnum()
    537 
    538     # Parse operand
    539     def _GetSingleToken(self):
    540         self.__SkipWS()
    541         Expr = self._Expr[self._Idx:]
    542         if Expr.startswith('L"'):
    543             # Skip L
    544             self._Idx += 1
    545             UStr = self.__GetString()
    546             self._Token = 'L"' + UStr + '"'
    547             return self._Token
    548 
    549         self._Token = ''
    550         if Expr:
    551             Ch = Expr[0]
    552             Match = self.RegGuidPattern.match(Expr)
    553             if Match and not Expr[Match.end():Match.end()+1].isalnum() \
    554                 and Expr[Match.end():Match.end()+1] != '_':
    555                 self._Idx += Match.end()
    556                 self._Token = ValueExpression(GuidStringToGuidStructureString(Expr[0:Match.end()]))(True, self._Depth+1)
    557                 return self._Token
    558             elif self.__IsIdChar(Ch):
    559                 return self.__GetIdToken()
    560             elif Ch == '"':
    561                 return self.__GetString()
    562             elif Ch == '{':
    563                 return self.__GetArray()
    564             elif Ch == '(' or Ch == ')':
    565                 self._Idx += 1
    566                 self._Token = Ch
    567                 return self._Token
    568 
    569         raise BadExpression(ERR_VALID_TOKEN % Expr)
    570 
    571     # Parse operator
    572     def _GetOperator(self):
    573         self.__SkipWS()
    574         LegalOpLst = ['&&', '||', '!=', '==', '>=', '<='] + self.NonLetterOpLst
    575 
    576         self._Token = ''
    577         Expr = self._Expr[self._Idx:]
    578 
    579         # Reach end of expression
    580         if not Expr:
    581             return ''
    582 
    583         # Script operator: LT, GT, LE, GE, EQ, NE, and, or, xor, not
    584         if Expr[0].isalpha():
    585             return self.__GetIdToken(True)
    586 
    587         # Start to get regular operator: +, -, <, > ...
    588         if Expr[0] not in self.NonLetterOpLst:
    589             return ''
    590 
    591         OpToken = ''
    592         for Ch in Expr:
    593             if Ch in self.NonLetterOpLst:
    594                 if '!' == Ch and OpToken:
    595                     break
    596                 self._Idx += 1
    597                 OpToken += Ch
    598             else:
    599                 break
    600 
    601         if OpToken not in LegalOpLst:
    602             raise BadExpression(ERR_OPERATOR_UNSUPPORT % OpToken)
    603         self._Token = OpToken
    604         return OpToken
    605 
    606     # Check if current token matches the operators given from OpList
    607     def _IsOperator(self, OpList):
    608         Idx = self._Idx
    609         self._GetOperator()
    610         if self._Token in OpList:
    611             if self._Token in self.LogicalOperators:
    612                 self._Token = self.LogicalOperators[self._Token]
    613             return True
    614         self._Idx = Idx
    615         return False
    616 
    617 if __name__ == '__main__':
    618     pass
    619     while True:
    620         input = raw_input('Input expr: ')
    621         if input in 'qQ':
    622             break
    623         try:
    624             print ValueExpression(input)(True)
    625             print ValueExpression(input)(False)
    626         except WrnExpression, Ex:
    627             print Ex.result
    628             print str(Ex)
    629         except Exception, Ex:
    630             print str(Ex)
    631