]>
Commit | Line | Data |
---|---|---|
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 | 15 | from __future__ import print_function\r |
82a6a960 BF |
16 | from Common.GlobalData import *\r |
17 | from CommonDataClass.Exceptions import BadExpression\r | |
18 | from CommonDataClass.Exceptions import WrnExpression\r | |
19 | import uuid\r | |
ccaa7754 | 20 | from Common.Expression import PcdPattern, BaseExpression\r |
25598f8b | 21 | from Common.DataType import *\r |
938cf4c3 | 22 | from re import compile\r |
82a6a960 BF |
23 | \r |
24 | ERR_STRING_EXPR = 'This operator cannot be used in string expression: [%s].'\r | |
25 | ERR_SNYTAX = 'Syntax error, the rest of expression cannot be evaluated: [%s].'\r | |
26 | ERR_MATCH = 'No matching right parenthesis.'\r | |
27 | ERR_STRING_TOKEN = 'Bad string token: [%s].'\r | |
28 | ERR_MACRO_TOKEN = 'Bad macro token: [%s].'\r | |
29 | ERR_EMPTY_TOKEN = 'Empty token is not allowed.'\r | |
fa6e2804 | 30 | ERR_PCD_RESOLVE = 'The PCD should be FeatureFlag type or FixedAtBuild type: [%s].'\r |
82a6a960 BF |
31 | ERR_VALID_TOKEN = 'No more valid token found from rest of string: [%s].'\r |
32 | ERR_EXPR_TYPE = 'Different types found in expression.'\r | |
33 | ERR_OPERATOR_UNSUPPORT = 'Unsupported operator: [%s]'\r | |
34 | ERR_REL_NOT_IN = 'Expect "IN" after "not" operator.'\r | |
35 | WRN_BOOL_EXPR = 'Operand of boolean type cannot be used in arithmetic expression.'\r | |
36 | WRN_EQCMP_STR_OTHERS = '== Comparison between Operand of string type and Boolean/Number Type always return False.'\r | |
37 | WRN_NECMP_STR_OTHERS = '!= Comparison between Operand of string type and Boolean/Number Type always return True.'\r | |
38 | ERR_RELCMP_STR_OTHERS = 'Operator taking Operand of string type and Boolean/Number Type is not allowed: [%s].'\r | |
39 | ERR_STRING_CMP = 'Unicode string and general string cannot be compared: [%s %s %s]'\r | |
40 | ERR_ARRAY_TOKEN = 'Bad C array or C format GUID token: [%s].'\r | |
41 | ERR_ARRAY_ELE = 'This must be HEX value for NList or Array: [%s].'\r | |
42 | ERR_EMPTY_EXPR = 'Empty expression is not allowed.'\r | |
43 | ERR_IN_OPERAND = 'Macro after IN operator can only be: $(FAMILY), $(ARCH), $(TOOL_CHAIN_TAG) and $(TARGET).'\r | |
44 | \r | |
82a6a960 BF |
45 | class 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 | |
56 | class 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 | |
97 | class 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 | |
111 | class 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 | |
123 | class 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 |
136 | class 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 | 149 | class 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 | 162 | class 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 |
175 | def 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 | 191 | class 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 | |
fb0b35e0 | 293 | def NegativeRange(self, Oprand1):\r |
82a6a960 | 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 | 334 | raise BadExpression(ERR_STRING_EXPR % Operator)\r |
fb0b35e0 | 335 | return self.NegativeRange(Oprand1)\r |
82a6a960 BF |
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 |