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