1# # @file
2# This file is used to parse and evaluate range expression in Pcd declaration.
3#
4# Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
5# SPDX-License-Identifier: BSD-2-Clause-Patent
6
7# # Import Modules
8#
9from __future__ import print_function
10from Common.GlobalData import *
11from CommonDataClass.Exceptions import BadExpression
12from CommonDataClass.Exceptions import WrnExpression
13import uuid
14from Common.Expression import PcdPattern, BaseExpression
15from Common.DataType import *
16from re import compile
17
18ERR_STRING_EXPR = 'This operator cannot be used in string expression: [%s].'
19ERR_SNYTAX = 'Syntax error, the rest of expression cannot be evaluated: [%s].'
20ERR_MATCH = 'No matching right parenthesis.'
21ERR_STRING_TOKEN = 'Bad string token: [%s].'
22ERR_MACRO_TOKEN = 'Bad macro token: [%s].'
23ERR_EMPTY_TOKEN = 'Empty token is not allowed.'
24ERR_PCD_RESOLVE = 'The PCD should be FeatureFlag type or FixedAtBuild type: [%s].'
25ERR_VALID_TOKEN = 'No more valid token found from rest of string: [%s].'
26ERR_EXPR_TYPE = 'Different types found in expression.'
27ERR_OPERATOR_UNSUPPORT = 'Unsupported operator: [%s]'
28ERR_REL_NOT_IN = 'Expect "IN" after "not" operator.'
29WRN_BOOL_EXPR = 'Operand of boolean type cannot be used in arithmetic expression.'
30WRN_EQCMP_STR_OTHERS = '== Comparison between Operand of string type and Boolean/Number Type always return False.'
31WRN_NECMP_STR_OTHERS = '!= Comparison between Operand of string type and Boolean/Number Type always return True.'
32ERR_RELCMP_STR_OTHERS = 'Operator taking Operand of string type and Boolean/Number Type is not allowed: [%s].'
33ERR_STRING_CMP = 'Unicode string and general string cannot be compared: [%s %s %s]'
34ERR_ARRAY_TOKEN = 'Bad C array or C format GUID token: [%s].'
35ERR_ARRAY_ELE = 'This must be HEX value for NList or Array: [%s].'
36ERR_EMPTY_EXPR = 'Empty expression is not allowed.'
37ERR_IN_OPERAND = 'Macro after IN operator can only be: $(FAMILY), $(ARCH), $(TOOL_CHAIN_TAG) and $(TARGET).'
38
39class RangeObject(object):
40    def __init__(self, start, end, empty = False):
41
42        if int(start) < int(end):
43            self.start = int(start)
44            self.end = int(end)
45        else:
46            self.start = int(end)
47            self.end = int(start)
48        self.empty = empty
49
50class RangeContainer(object):
51    def __init__(self):
52        self.rangelist = []
53
54    def push(self, RangeObject):
55        self.rangelist.append(RangeObject)
56        self.rangelist = sorted(self.rangelist, key = lambda rangeobj : rangeobj.start)
57        self.merge()
58
59    def pop(self):
60        for item in self.rangelist:
61            yield item
62
63    def __clean__(self):
64        newrangelist = []
65        for rangeobj in self.rangelist:
66            if rangeobj.empty == True:
67                continue
68            else:
69                newrangelist.append(rangeobj)
70        self.rangelist = newrangelist
71    def merge(self):
72        self.__clean__()
73        for i in range(0, len(self.rangelist) - 1):
74            if self.rangelist[i + 1].start > self.rangelist[i].end:
75                continue
76            else:
77                self.rangelist[i + 1].start = self.rangelist[i].start
78                self.rangelist[i + 1].end = self.rangelist[i + 1].end > self.rangelist[i].end and self.rangelist[i + 1].end or self.rangelist[i].end
79                self.rangelist[i].empty = True
80
81        self.__clean__()
82
83    def dump(self):
84        print("----------------------")
85        rangelist = ""
86        for object in self.rangelist:
87            rangelist = rangelist + "[%d , %d]" % (object.start, object.end)
88        print(rangelist)
89
90
91class XOROperatorObject(object):
92    def __init__(self):
93        pass
94    def Calculate(self, Operand, DataType, SymbolTable):
95        if isinstance(Operand, type('')) and not Operand.isalnum():
96            Expr = "XOR ..."
97            raise BadExpression(ERR_SNYTAX % Expr)
98        rangeId = str(uuid.uuid1())
99        rangeContainer = RangeContainer()
100        rangeContainer.push(RangeObject(0, int(Operand) - 1))
101        rangeContainer.push(RangeObject(int(Operand) + 1, MAX_VAL_TYPE[DataType]))
102        SymbolTable[rangeId] = rangeContainer
103        return rangeId
104
105class LEOperatorObject(object):
106    def __init__(self):
107        pass
108    def Calculate(self, Operand, DataType, SymbolTable):
109        if isinstance(Operand, type('')) and not Operand.isalnum():
110            Expr = "LE ..."
111            raise BadExpression(ERR_SNYTAX % Expr)
112        rangeId1 = str(uuid.uuid1())
113        rangeContainer = RangeContainer()
114        rangeContainer.push(RangeObject(0, int(Operand)))
115        SymbolTable[rangeId1] = rangeContainer
116        return rangeId1
117class LTOperatorObject(object):
118    def __init__(self):
119        pass
120    def Calculate(self, Operand, DataType, SymbolTable):
121        if isinstance(Operand, type('')) and not Operand.isalnum():
122            Expr = "LT ..."
123            raise BadExpression(ERR_SNYTAX % Expr)
124        rangeId1 = str(uuid.uuid1())
125        rangeContainer = RangeContainer()
126        rangeContainer.push(RangeObject(0, int(Operand) - 1))
127        SymbolTable[rangeId1] = rangeContainer
128        return rangeId1
129
130class GEOperatorObject(object):
131    def __init__(self):
132        pass
133    def Calculate(self, Operand, DataType, SymbolTable):
134        if isinstance(Operand, type('')) and not Operand.isalnum():
135            Expr = "GE ..."
136            raise BadExpression(ERR_SNYTAX % Expr)
137        rangeId1 = str(uuid.uuid1())
138        rangeContainer = RangeContainer()
139        rangeContainer.push(RangeObject(int(Operand), MAX_VAL_TYPE[DataType]))
140        SymbolTable[rangeId1] = rangeContainer
141        return rangeId1
142
143class GTOperatorObject(object):
144    def __init__(self):
145        pass
146    def Calculate(self, Operand, DataType, SymbolTable):
147        if isinstance(Operand, type('')) and not Operand.isalnum():
148            Expr = "GT ..."
149            raise BadExpression(ERR_SNYTAX % Expr)
150        rangeId1 = str(uuid.uuid1())
151        rangeContainer = RangeContainer()
152        rangeContainer.push(RangeObject(int(Operand) + 1, MAX_VAL_TYPE[DataType]))
153        SymbolTable[rangeId1] = rangeContainer
154        return rangeId1
155
156class EQOperatorObject(object):
157    def __init__(self):
158        pass
159    def Calculate(self, Operand, DataType, SymbolTable):
160        if isinstance(Operand, type('')) and not Operand.isalnum():
161            Expr = "EQ ..."
162            raise BadExpression(ERR_SNYTAX % Expr)
163        rangeId1 = str(uuid.uuid1())
164        rangeContainer = RangeContainer()
165        rangeContainer.push(RangeObject(int(Operand), int(Operand)))
166        SymbolTable[rangeId1] = rangeContainer
167        return rangeId1
168
169def GetOperatorObject(Operator):
170    if Operator == '>':
171        return GTOperatorObject()
172    elif Operator == '>=':
173        return GEOperatorObject()
174    elif Operator == '<':
175        return LTOperatorObject()
176    elif Operator == '<=':
177        return LEOperatorObject()
178    elif Operator == '==':
179        return EQOperatorObject()
180    elif Operator == '^':
181        return XOROperatorObject()
182    else:
183        raise BadExpression("Bad Operator")
184
185class RangeExpression(BaseExpression):
186    # Logical operator mapping
187    LogicalOperators = {
188        '&&' : 'and', '||' : 'or',
189        '!'  : 'not', 'AND': 'and',
190        'OR' : 'or' , 'NOT': 'not',
191        'XOR': '^'  , 'xor': '^',
192        'EQ' : '==' , 'NE' : '!=',
193        'GT' : '>'  , 'LT' : '<',
194        'GE' : '>=' , 'LE' : '<=',
195        'IN' : 'in'
196    }
197
198    NonLetterOpLst = ['+', '-', '&', '|', '^', '!', '=', '>', '<']
199
200    RangePattern = compile(r'[0-9]+ - [0-9]+')
201
202    def preProcessRangeExpr(self, expr):
203        # convert hex to int
204        # convert interval to object index. ex. 1 - 10 to a GUID
205        expr = expr.strip()
206        NumberDict = {}
207        for HexNumber in gHexPattern.findall(expr):
208            Number = str(int(HexNumber, 16))
209            NumberDict[HexNumber] = Number
210        for HexNum in NumberDict:
211            expr = expr.replace(HexNum, NumberDict[HexNum])
212
213        rangedict = {}
214        for validrange in self.RangePattern.findall(expr):
215            start, end = validrange.split(" - ")
216            start = start.strip()
217            end = end.strip()
218            rangeid = str(uuid.uuid1())
219            rangeContainer = RangeContainer()
220            rangeContainer.push(RangeObject(start, end))
221            self.operanddict[str(rangeid)] = rangeContainer
222            rangedict[validrange] = str(rangeid)
223
224        for validrange in rangedict:
225            expr = expr.replace(validrange, rangedict[validrange])
226
227        self._Expr = expr
228        return expr
229
230
231    def EvalRange(self, Operator, Oprand):
232
233        operatorobj = GetOperatorObject(Operator)
234        return operatorobj.Calculate(Oprand, self.PcdDataType, self.operanddict)
235
236    def Rangeintersection(self, Oprand1, Oprand2):
237        rangeContainer1 = self.operanddict[Oprand1]
238        rangeContainer2 = self.operanddict[Oprand2]
239        rangeContainer = RangeContainer()
240        for range1 in rangeContainer1.pop():
241            for range2 in rangeContainer2.pop():
242                start1 = range1.start
243                end1 = range1.end
244                start2 = range2.start
245                end2 = range2.end
246                if start1 >= start2:
247                    start1, start2 = start2, start1
248                    end1, end2 = end2, end1
249                if range1.empty:
250                    rangeid = str(uuid.uuid1())
251                    rangeContainer.push(RangeObject(0, 0, True))
252                if end1 < start2:
253                    rangeid = str(uuid.uuid1())
254                    rangeContainer.push(RangeObject(0, 0, True))
255                elif end1 == start2:
256                    rangeid = str(uuid.uuid1())
257                    rangeContainer.push(RangeObject(end1, end1))
258                elif end1 <= end2 and end1 > start2:
259                    rangeid = str(uuid.uuid1())
260                    rangeContainer.push(RangeObject(start2, end1))
261                elif end1 >= end2:
262                    rangeid = str(uuid.uuid1())
263                    rangeContainer.push(RangeObject(start2, end2))
264
265        self.operanddict[rangeid] = rangeContainer
266#        rangeContainer.dump()
267        return rangeid
268
269    def Rangecollections(self, Oprand1, Oprand2):
270
271        rangeContainer1 = self.operanddict[Oprand1]
272        rangeContainer2 = self.operanddict[Oprand2]
273        rangeContainer = RangeContainer()
274
275        for rangeobj in rangeContainer2.pop():
276            rangeContainer.push(rangeobj)
277        for rangeobj in rangeContainer1.pop():
278            rangeContainer.push(rangeobj)
279
280        rangeid = str(uuid.uuid1())
281        self.operanddict[rangeid] = rangeContainer
282
283#        rangeContainer.dump()
284        return rangeid
285
286
287    def NegativeRange(self, Oprand1):
288        rangeContainer1 = self.operanddict[Oprand1]
289
290
291        rangeids = []
292
293        for rangeobj in rangeContainer1.pop():
294            rangeContainer = RangeContainer()
295            rangeid = str(uuid.uuid1())
296            if rangeobj.empty:
297                rangeContainer.push(RangeObject(0, MAX_VAL_TYPE[self.PcdDataType]))
298            else:
299                if rangeobj.start > 0:
300                    rangeContainer.push(RangeObject(0, rangeobj.start - 1))
301                if rangeobj.end < MAX_VAL_TYPE[self.PcdDataType]:
302                    rangeContainer.push(RangeObject(rangeobj.end + 1, MAX_VAL_TYPE[self.PcdDataType]))
303            self.operanddict[rangeid] = rangeContainer
304            rangeids.append(rangeid)
305
306        if len(rangeids) == 0:
307            rangeContainer = RangeContainer()
308            rangeContainer.push(RangeObject(0, MAX_VAL_TYPE[self.PcdDataType]))
309            rangeid = str(uuid.uuid1())
310            self.operanddict[rangeid] = rangeContainer
311            return rangeid
312
313        if len(rangeids) == 1:
314            return rangeids[0]
315
316        re = self.Rangeintersection(rangeids[0], rangeids[1])
317        for i in range(2, len(rangeids)):
318            re = self.Rangeintersection(re, rangeids[i])
319
320        rangeid2 = str(uuid.uuid1())
321        self.operanddict[rangeid2] = self.operanddict[re]
322        return rangeid2
323
324    def Eval(self, Operator, Oprand1, Oprand2 = None):
325
326        if Operator in ["!", "NOT", "not"]:
327            if not gGuidPattern.match(Oprand1.strip()):
328                raise BadExpression(ERR_STRING_EXPR % Operator)
329            return self.NegativeRange(Oprand1)
330        else:
331            if Operator in ["==", ">=", "<=", ">", "<", '^']:
332                return self.EvalRange(Operator, Oprand1)
333            elif Operator == 'and' :
334                if not gGuidPatternEnd.match(Oprand1.strip()) or not gGuidPatternEnd.match(Oprand2.strip()):
335                    raise BadExpression(ERR_STRING_EXPR % Operator)
336                return self.Rangeintersection(Oprand1, Oprand2)
337            elif Operator == 'or':
338                if not gGuidPatternEnd.match(Oprand1.strip()) or not gGuidPatternEnd.match(Oprand2.strip()):
339                    raise BadExpression(ERR_STRING_EXPR % Operator)
340                return self.Rangecollections(Oprand1, Oprand2)
341            else:
342                raise BadExpression(ERR_STRING_EXPR % Operator)
343
344
345    def __init__(self, Expression, PcdDataType, SymbolTable = None):
346        if SymbolTable is None:
347            SymbolTable = {}
348        super(RangeExpression, self).__init__(self, Expression, PcdDataType, SymbolTable)
349        self._NoProcess = False
350        if not isinstance(Expression, type('')):
351            self._Expr = Expression
352            self._NoProcess = True
353            return
354
355        self._Expr = Expression.strip()
356
357        if not self._Expr.strip():
358            raise BadExpression(ERR_EMPTY_EXPR)
359
360        #
361        # The symbol table including PCD and macro mapping
362        #
363        self._Symb = SymbolTable
364        self._Symb.update(self.LogicalOperators)
365        self._Idx = 0
366        self._Len = len(self._Expr)
367        self._Token = ''
368        self._WarnExcept = None
369
370
371        # Literal token without any conversion
372        self._LiteralToken = ''
373
374        # store the operand object
375        self.operanddict = {}
376        # The Pcd max value depends on PcdDataType
377        self.PcdDataType = PcdDataType
378
379    # Public entry for this class
380    #   @param RealValue: False: only evaluate if the expression is true or false, used for conditional expression
381    #                     True : return the evaluated str(value), used for PCD value
382    #
383    #   @return: True or False if RealValue is False
384    #            Evaluated value of string format if RealValue is True
385    #
386    def __call__(self, RealValue = False, Depth = 0):
387        if self._NoProcess:
388            return self._Expr
389
390        self._Depth = Depth
391
392        self._Expr = self._Expr.strip()
393
394        self.preProcessRangeExpr(self._Expr)
395
396        # check if the expression does not need to evaluate
397        if RealValue and Depth == 0:
398            self._Token = self._Expr
399            if gGuidPatternEnd.match(self._Expr):
400                return [self.operanddict[self._Expr] ]
401
402            self._Idx = 0
403            self._Token = ''
404
405        Val = self._OrExpr()
406        RealVal = Val
407
408        RangeIdList = RealVal.split("or")
409        RangeList = []
410        for rangeid in RangeIdList:
411            RangeList.append(self.operanddict[rangeid.strip()])
412
413        return RangeList
414
415    # Template function to parse binary operators which have same precedence
416    # Expr [Operator Expr]*
417    def _ExprFuncTemplate(self, EvalFunc, OpSet):
418        Val = EvalFunc()
419        while self._IsOperator(OpSet):
420            Op = self._Token
421            try:
422                Val = self.Eval(Op, Val, EvalFunc())
423            except WrnExpression as Warn:
424                self._WarnExcept = Warn
425                Val = Warn.result
426        return Val
427
428    # A [|| B]*
429    def _OrExpr(self):
430        return self._ExprFuncTemplate(self._AndExpr, {"OR", "or"})
431
432    # A [&& B]*
433    def _AndExpr(self):
434        return self._ExprFuncTemplate(self._NeExpr, {"AND", "and"})
435
436    def _NeExpr(self):
437        Val = self._RelExpr()
438        while self._IsOperator({"!=", "NOT", "not"}):
439            Op = self._Token
440            if Op in ["!", "NOT", "not"]:
441                if not self._IsOperator({"IN", "in"}):
442                    raise BadExpression(ERR_REL_NOT_IN)
443                Op += ' ' + self._Token
444            try:
445                Val = self.Eval(Op, Val, self._RelExpr())
446            except WrnExpression as Warn:
447                self._WarnExcept = Warn
448                Val = Warn.result
449        return Val
450
451    # [!]*A
452    def _RelExpr(self):
453        if self._IsOperator({"NOT", "LE", "GE", "LT", "GT", "EQ", "XOR"}):
454            Token = self._Token
455            Val = self._NeExpr()
456            try:
457                return self.Eval(Token, Val)
458            except WrnExpression as Warn:
459                self._WarnExcept = Warn
460                return Warn.result
461        return self._IdenExpr()
462
463    # Parse identifier or encapsulated expression
464    def _IdenExpr(self):
465        Tk = self._GetToken()
466        if Tk == '(':
467            Val = self._OrExpr()
468            try:
469                # _GetToken may also raise BadExpression
470                if self._GetToken() != ')':
471                    raise BadExpression(ERR_MATCH)
472            except BadExpression:
473                raise BadExpression(ERR_MATCH)
474            return Val
475        return Tk
476
477    # Skip whitespace or tab
478    def __SkipWS(self):
479        for Char in self._Expr[self._Idx:]:
480            if Char not in ' \t':
481                break
482            self._Idx += 1
483
484    # Try to convert string to number
485    def __IsNumberToken(self):
486        Radix = 10
487        if self._Token.lower()[0:2] == '0x' and len(self._Token) > 2:
488            Radix = 16
489        try:
490            self._Token = int(self._Token, Radix)
491            return True
492        except ValueError:
493            return False
494        except TypeError:
495            return False
496
497    # Parse array: {...}
498    def __GetArray(self):
499        Token = '{'
500        self._Idx += 1
501        self.__GetNList(True)
502        Token += self._LiteralToken
503        if self._Idx >= self._Len or self._Expr[self._Idx] != '}':
504            raise BadExpression(ERR_ARRAY_TOKEN % Token)
505        Token += '}'
506
507        # All whitespace and tabs in array are already stripped.
508        IsArray = IsGuid = False
509        if len(Token.split(',')) == 11 and len(Token.split(',{')) == 2 \
510            and len(Token.split('},')) == 1:
511            HexLen = [11, 6, 6, 5, 4, 4, 4, 4, 4, 4, 6]
512            HexList = Token.split(',')
513            if HexList[3].startswith('{') and \
514                not [Index for Index, Hex in enumerate(HexList) if len(Hex) > HexLen[Index]]:
515                IsGuid = True
516        if Token.lstrip('{').rstrip('}').find('{') == -1:
517            if not [Hex for Hex in Token.lstrip('{').rstrip('}').split(',') if len(Hex) > 4]:
518                IsArray = True
519        if not IsArray and not IsGuid:
520            raise BadExpression(ERR_ARRAY_TOKEN % Token)
521        self._Idx += 1
522        self._Token = self._LiteralToken = Token
523        return self._Token
524
525    # Parse string, the format must be: "..."
526    def __GetString(self):
527        Idx = self._Idx
528
529        # Skip left quote
530        self._Idx += 1
531
532        # Replace escape \\\", \"
533        Expr = self._Expr[self._Idx:].replace('\\\\', '//').replace('\\\"', '\\\'')
534        for Ch in Expr:
535            self._Idx += 1
536            if Ch == '"':
537                break
538        self._Token = self._LiteralToken = self._Expr[Idx:self._Idx]
539        if not self._Token.endswith('"'):
540            raise BadExpression(ERR_STRING_TOKEN % self._Token)
541        self._Token = self._Token[1:-1]
542        return self._Token
543
544    # Get token that is comprised by alphanumeric, underscore or dot(used by PCD)
545    # @param IsAlphaOp: Indicate if parsing general token or script operator(EQ, NE...)
546    def __GetIdToken(self, IsAlphaOp = False):
547        IdToken = ''
548        for Ch in self._Expr[self._Idx:]:
549            if not self.__IsIdChar(Ch):
550                break
551            self._Idx += 1
552            IdToken += Ch
553
554        self._Token = self._LiteralToken = IdToken
555        if not IsAlphaOp:
556            self.__ResolveToken()
557        return self._Token
558
559    # Try to resolve token
560    def __ResolveToken(self):
561        if not self._Token:
562            raise BadExpression(ERR_EMPTY_TOKEN)
563
564        # PCD token
565        if PcdPattern.match(self._Token):
566            if self._Token not in self._Symb:
567                Ex = BadExpression(ERR_PCD_RESOLVE % self._Token)
568                Ex.Pcd = self._Token
569                raise Ex
570            self._Token = RangeExpression(self._Symb[self._Token], self._Symb)(True, self._Depth + 1)
571            if not isinstance(self._Token, type('')):
572                self._LiteralToken = hex(self._Token)
573                return
574
575        if self._Token.startswith('"'):
576            self._Token = self._Token[1:-1]
577        elif self._Token in ["FALSE", "false", "False"]:
578            self._Token = False
579        elif self._Token in ["TRUE", "true", "True"]:
580            self._Token = True
581        else:
582            self.__IsNumberToken()
583
584    def __GetNList(self, InArray = False):
585        self._GetSingleToken()
586        if not self.__IsHexLiteral():
587            if InArray:
588                raise BadExpression(ERR_ARRAY_ELE % self._Token)
589            return self._Token
590
591        self.__SkipWS()
592        Expr = self._Expr[self._Idx:]
593        if not Expr.startswith(','):
594            return self._Token
595
596        NList = self._LiteralToken
597        while Expr.startswith(','):
598            NList += ','
599            self._Idx += 1
600            self.__SkipWS()
601            self._GetSingleToken()
602            if not self.__IsHexLiteral():
603                raise BadExpression(ERR_ARRAY_ELE % self._Token)
604            NList += self._LiteralToken
605            self.__SkipWS()
606            Expr = self._Expr[self._Idx:]
607        self._Token = self._LiteralToken = NList
608        return self._Token
609
610    def __IsHexLiteral(self):
611        if self._LiteralToken.startswith('{') and \
612            self._LiteralToken.endswith('}'):
613            return True
614
615        if gHexPattern.match(self._LiteralToken):
616            Token = self._LiteralToken[2:]
617            Token = Token.lstrip('0')
618            if not Token:
619                self._LiteralToken = '0x0'
620            else:
621                self._LiteralToken = '0x' + Token.lower()
622            return True
623        return False
624
625    def _GetToken(self):
626        return self.__GetNList()
627
628    @staticmethod
629    def __IsIdChar(Ch):
630        return Ch in '._/:' or Ch.isalnum()
631
632    # Parse operand
633    def _GetSingleToken(self):
634        self.__SkipWS()
635        Expr = self._Expr[self._Idx:]
636        if Expr.startswith('L"'):
637            # Skip L
638            self._Idx += 1
639            UStr = self.__GetString()
640            self._Token = 'L"' + UStr + '"'
641            return self._Token
642
643        self._Token = ''
644        if Expr:
645            Ch = Expr[0]
646            Match = gGuidPattern.match(Expr)
647            if Match and not Expr[Match.end():Match.end() + 1].isalnum() \
648                and Expr[Match.end():Match.end() + 1] != '_':
649                self._Idx += Match.end()
650                self._Token = Expr[0:Match.end()]
651                return self._Token
652            elif self.__IsIdChar(Ch):
653                return self.__GetIdToken()
654            elif Ch == '(' or Ch == ')':
655                self._Idx += 1
656                self._Token = Ch
657                return self._Token
658
659        raise BadExpression(ERR_VALID_TOKEN % Expr)
660
661    # Parse operator
662    def _GetOperator(self):
663        self.__SkipWS()
664        LegalOpLst = ['&&', '||', '!=', '==', '>=', '<='] + self.NonLetterOpLst
665
666        self._Token = ''
667        Expr = self._Expr[self._Idx:]
668
669        # Reach end of expression
670        if not Expr:
671            return ''
672
673        # Script operator: LT, GT, LE, GE, EQ, NE, and, or, xor, not
674        if Expr[0].isalpha():
675            return self.__GetIdToken(True)
676
677        # Start to get regular operator: +, -, <, > ...
678        if Expr[0] not in self.NonLetterOpLst:
679            return ''
680
681        OpToken = ''
682        for Ch in Expr:
683            if Ch in self.NonLetterOpLst:
684                if '!' == Ch and OpToken:
685                    break
686                self._Idx += 1
687                OpToken += Ch
688            else:
689                break
690
691        if OpToken not in LegalOpLst:
692            raise BadExpression(ERR_OPERATOR_UNSUPPORT % OpToken)
693        self._Token = OpToken
694        return OpToken
695