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