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