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