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