]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/Common/Expression.py
BaseTools: Fix the bug for display incorrect *M flag in report
[mirror_edk2.git] / BaseTools / Source / Python / Common / Expression.py
CommitLineData
0d2711a6
LG
1## @file\r
2# This file is used to parse and evaluate expression in directive or PCD value.\r
3#\r
35f613d9 4# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>\r
0d2711a6
LG
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
0d2711a6 17from CommonDataClass.Exceptions import WrnExpression\r
726c501c
YZ
18from Misc import GuidStringToGuidStructureString, ParseFieldValue\r
19import Common.EdkLogger as EdkLogger\r
20import copy\r
0d2711a6
LG
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
d0acc87a 40ERR_EMPTY_EXPR = 'Empty expression is not allowed.'\r
64b2609f 41ERR_IN_OPERAND = 'Macro after IN operator can only be: $(FAMILY), $(ARCH), $(TOOL_CHAIN_TAG) and $(TARGET).'\r
0d2711a6
LG
42\r
43## SplitString\r
44# Split string to list according double quote\r
45# For example: abc"de\"f"ghi"jkl"mn will be: ['abc', '"de\"f"', 'ghi', '"jkl"', 'mn']\r
46#\r
47def SplitString(String):\r
ea927d2f
FY
48 # There might be escaped quote: "abc\"def\\\"ghi", 'abc\'def\\\'ghi'\r
49 Str = String\r
0d2711a6 50 RetList = []\r
ea927d2f
FY
51 InSingleQuote = False\r
52 InDoubleQuote = False\r
0d2711a6
LG
53 Item = ''\r
54 for i, ch in enumerate(Str):\r
ea927d2f
FY
55 if ch == '"' and not InSingleQuote:\r
56 if Str[i - 1] != '\\':\r
57 InDoubleQuote = not InDoubleQuote\r
58 if not InDoubleQuote:\r
59 Item += String[i]\r
60 RetList.append(Item)\r
61 Item = ''\r
62 continue\r
63 if Item:\r
64 RetList.append(Item)\r
65 Item = ''\r
66 elif ch == "'" and not InDoubleQuote:\r
67 if Str[i - 1] != '\\':\r
68 InSingleQuote = not InSingleQuote\r
69 if not InSingleQuote:\r
0d2711a6
LG
70 Item += String[i]\r
71 RetList.append(Item)\r
72 Item = ''\r
73 continue\r
74 if Item:\r
75 RetList.append(Item)\r
76 Item = ''\r
77 Item += String[i]\r
ea927d2f 78 if InSingleQuote or InDoubleQuote:\r
0d2711a6
LG
79 raise BadExpression(ERR_STRING_TOKEN % Item)\r
80 if Item:\r
81 RetList.append(Item)\r
82 return RetList\r
83\r
3be421e9
FY
84def SplitPcdValueString(String):\r
85 # There might be escaped comma in GUID() or DEVICE_PATH() or " "\r
86 # or ' ' or L' ' or L" "\r
87 Str = String\r
88 RetList = []\r
89 InParenthesis = 0\r
90 InSingleQuote = False\r
91 InDoubleQuote = False\r
92 Item = ''\r
93 for i, ch in enumerate(Str):\r
94 if ch == '(':\r
95 InParenthesis += 1\r
96 if ch == ')':\r
97 if InParenthesis:\r
98 InParenthesis -= 1\r
99 else:\r
100 raise BadExpression(ERR_STRING_TOKEN % Item)\r
101 if ch == '"' and not InSingleQuote:\r
102 if String[i-1] != '\\':\r
103 InDoubleQuote = not InDoubleQuote\r
104 if ch == "'" and not InDoubleQuote:\r
105 if String[i-1] != '\\':\r
106 InSingleQuote = not InSingleQuote\r
107 if ch == ',':\r
108 if InParenthesis or InSingleQuote or InDoubleQuote:\r
109 Item += String[i]\r
110 continue\r
111 elif Item:\r
112 RetList.append(Item)\r
113 Item = ''\r
114 continue\r
115 Item += String[i]\r
116 if InSingleQuote or InDoubleQuote or InParenthesis:\r
117 raise BadExpression(ERR_STRING_TOKEN % Item)\r
118 if Item:\r
119 RetList.append(Item)\r
120 return RetList\r
121\r
0d2711a6
LG
122## ReplaceExprMacro\r
123#\r
124def ReplaceExprMacro(String, Macros, ExceptionList = None):\r
125 StrList = SplitString(String)\r
126 for i, String in enumerate(StrList):\r
127 InQuote = False\r
128 if String.startswith('"'):\r
129 InQuote = True\r
130 MacroStartPos = String.find('$(')\r
131 if MacroStartPos < 0:\r
c8d07c5e
YZ
132 for Pcd in gPlatformPcds.keys():\r
133 if Pcd in String:\r
134 if Pcd not in gConditionalPcds:\r
135 gConditionalPcds.append(Pcd)\r
0d2711a6
LG
136 continue\r
137 RetStr = ''\r
138 while MacroStartPos >= 0:\r
139 RetStr = String[0:MacroStartPos]\r
140 MacroEndPos = String.find(')', MacroStartPos)\r
141 if MacroEndPos < 0:\r
142 raise BadExpression(ERR_MACRO_TOKEN % String[MacroStartPos:])\r
143 Macro = String[MacroStartPos+2:MacroEndPos]\r
144 if Macro not in Macros:\r
145 # From C reference manual:\r
146 # If an undefined macro name appears in the constant-expression of\r
147 # !if or !elif, it is replaced by the integer constant 0.\r
148 RetStr += '0'\r
64b2609f
LG
149 elif not InQuote:\r
150 Tklst = RetStr.split()\r
151 if Tklst and Tklst[-1] in ['IN', 'in'] and ExceptionList and Macro not in ExceptionList:\r
152 raise BadExpression(ERR_IN_OPERAND)\r
0d2711a6
LG
153 # Make sure the macro in exception list is encapsulated by double quote\r
154 # For example: DEFINE ARCH = IA32 X64\r
155 # $(ARCH) is replaced with "IA32 X64"\r
64b2609f
LG
156 if ExceptionList and Macro in ExceptionList:\r
157 RetStr += '"' + Macros[Macro] + '"'\r
158 elif Macros[Macro].strip():\r
0d2711a6
LG
159 RetStr += Macros[Macro]\r
160 else:\r
161 RetStr += '""'\r
64b2609f
LG
162 else:\r
163 RetStr += Macros[Macro]\r
0d2711a6
LG
164 RetStr += String[MacroEndPos+1:]\r
165 String = RetStr\r
166 MacroStartPos = String.find('$(')\r
167 StrList[i] = RetStr\r
168 return ''.join(StrList)\r
169\r
726c501c
YZ
170# transfer int to string for in/not in expression\r
171def IntToStr(Value):\r
172 StrList = []\r
173 while Value > 0:\r
174 StrList.append(chr(Value & 0xff))\r
175 Value = Value >> 8\r
176 Value = '"' + ''.join(StrList) + '"'\r
177 return Value\r
178\r
64b2609f
LG
179SupportedInMacroList = ['TARGET', 'TOOL_CHAIN_TAG', 'ARCH', 'FAMILY']\r
180\r
0d2711a6
LG
181class ValueExpression(object):\r
182 # Logical operator mapping\r
183 LogicalOperators = {\r
184 '&&' : 'and', '||' : 'or',\r
185 '!' : 'not', 'AND': 'and',\r
186 'OR' : 'or' , 'NOT': 'not',\r
187 'XOR': '^' , 'xor': '^',\r
188 'EQ' : '==' , 'NE' : '!=',\r
189 'GT' : '>' , 'LT' : '<',\r
190 'GE' : '>=' , 'LE' : '<=',\r
191 'IN' : 'in'\r
192 }\r
193\r
bc0d7233 194 NonLetterOpLst = ['+', '-', '*', '/', '%', '&', '|', '^', '~', '<<', '>>', '!', '=', '>', '<', '?', ':']\r
0d2711a6
LG
195\r
196 PcdPattern = re.compile(r'[_a-zA-Z][0-9A-Za-z_]*\.[_a-zA-Z][0-9A-Za-z_]*$')\r
197 HexPattern = re.compile(r'0[xX][0-9a-fA-F]+$')\r
198 RegGuidPattern = re.compile(r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}')\r
199\r
200 SymbolPattern = re.compile("("\r
201 "\$\([A-Z][A-Z0-9_]*\)|\$\(\w+\.\w+\)|\w+\.\w+|"\r
202 "&&|\|\||!(?!=)|"\r
203 "(?<=\W)AND(?=\W)|(?<=\W)OR(?=\W)|(?<=\W)NOT(?=\W)|(?<=\W)XOR(?=\W)|"\r
204 "(?<=\W)EQ(?=\W)|(?<=\W)NE(?=\W)|(?<=\W)GT(?=\W)|(?<=\W)LT(?=\W)|(?<=\W)GE(?=\W)|(?<=\W)LE(?=\W)"\r
205 ")")\r
206\r
207 @staticmethod\r
208 def Eval(Operator, Oprand1, Oprand2 = None):\r
209 WrnExp = None\r
d0acc87a 210\r
9efe8d60
YF
211 if Operator not in ["==", "!=", ">=", "<=", ">", "<", "in", "not in"] and \\r
212 (type(Oprand1) == type('') or type(Oprand2) == type('')):\r
213 raise BadExpression(ERR_STRING_EXPR % Operator)\r
726c501c
YZ
214 if Operator in ['in', 'not in']:\r
215 if type(Oprand1) != type(''):\r
216 Oprand1 = IntToStr(Oprand1)\r
217 if type(Oprand2) != type(''):\r
218 Oprand2 = IntToStr(Oprand2)\r
0d2711a6
LG
219 TypeDict = {\r
220 type(0) : 0,\r
221 type(0L) : 0,\r
222 type('') : 1,\r
223 type(True) : 2\r
224 }\r
225\r
226 EvalStr = ''\r
227 if Operator in ["!", "NOT", "not"]:\r
228 if type(Oprand1) == type(''):\r
229 raise BadExpression(ERR_STRING_EXPR % Operator)\r
230 EvalStr = 'not Oprand1'\r
5e06f1a0
YZ
231 elif Operator in ["~"]:\r
232 if type(Oprand1) == type(''):\r
233 raise BadExpression(ERR_STRING_EXPR % Operator)\r
234 EvalStr = '~ Oprand1'\r
0d2711a6
LG
235 else:\r
236 if Operator in ["+", "-"] and (type(True) in [type(Oprand1), type(Oprand2)]):\r
237 # Boolean in '+'/'-' will be evaluated but raise warning\r
238 WrnExp = WrnExpression(WRN_BOOL_EXPR)\r
239 elif type('') in [type(Oprand1), type(Oprand2)] and type(Oprand1)!= type(Oprand2):\r
240 # == between string and number/boolean will always return False, != return True\r
241 if Operator == "==":\r
242 WrnExp = WrnExpression(WRN_EQCMP_STR_OTHERS)\r
243 WrnExp.result = False\r
244 raise WrnExp\r
245 elif Operator == "!=":\r
246 WrnExp = WrnExpression(WRN_NECMP_STR_OTHERS)\r
247 WrnExp.result = True\r
248 raise WrnExp\r
249 else:\r
250 raise BadExpression(ERR_RELCMP_STR_OTHERS % Operator)\r
d0acc87a 251 elif TypeDict[type(Oprand1)] != TypeDict[type(Oprand2)]:\r
0d2711a6
LG
252 if Operator in ["==", "!=", ">=", "<=", ">", "<"] and set((TypeDict[type(Oprand1)], TypeDict[type(Oprand2)])) == set((TypeDict[type(True)], TypeDict[type(0)])):\r
253 # comparison between number and boolean is allowed\r
254 pass\r
d0acc87a 255 elif Operator in ['&', '|', '^', "and", "or"] and set((TypeDict[type(Oprand1)], TypeDict[type(Oprand2)])) == set((TypeDict[type(True)], TypeDict[type(0)])):\r
0d2711a6 256 # bitwise and logical operation between number and boolean is allowed\r
d0acc87a 257 pass\r
0d2711a6
LG
258 else:\r
259 raise BadExpression(ERR_EXPR_TYPE)\r
260 if type(Oprand1) == type('') and type(Oprand2) == type(''):\r
261 if (Oprand1.startswith('L"') and not Oprand2.startswith('L"')) or \\r
262 (not Oprand1.startswith('L"') and Oprand2.startswith('L"')):\r
263 raise BadExpression(ERR_STRING_CMP % (Oprand1, Operator, Oprand2))\r
264 if 'in' in Operator and type(Oprand2) == type(''):\r
265 Oprand2 = Oprand2.split()\r
266 EvalStr = 'Oprand1 ' + Operator + ' Oprand2'\r
267\r
268 # Local symbols used by built in eval function\r
269 Dict = {\r
270 'Oprand1' : Oprand1,\r
271 'Oprand2' : Oprand2\r
272 }\r
273 try:\r
274 Val = eval(EvalStr, {}, Dict)\r
275 except Exception, Excpt:\r
276 raise BadExpression(str(Excpt))\r
277\r
278 if Operator in ['and', 'or']:\r
279 if Val:\r
280 Val = True\r
281 else:\r
282 Val = False\r
d0acc87a 283\r
0d2711a6
LG
284 if WrnExp:\r
285 WrnExp.result = Val\r
286 raise WrnExp\r
287 return Val\r
288\r
289 def __init__(self, Expression, SymbolTable={}):\r
290 self._NoProcess = False\r
291 if type(Expression) != type(''):\r
292 self._Expr = Expression\r
293 self._NoProcess = True\r
294 return\r
295\r
296 self._Expr = ReplaceExprMacro(Expression.strip(),\r
297 SymbolTable,\r
64b2609f 298 SupportedInMacroList)\r
0d2711a6
LG
299\r
300 if not self._Expr.strip():\r
d0acc87a 301 raise BadExpression(ERR_EMPTY_EXPR)\r
0d2711a6
LG
302\r
303 #\r
304 # The symbol table including PCD and macro mapping\r
305 #\r
726c501c 306 self._Symb = copy.deepcopy(SymbolTable)\r
0d2711a6
LG
307 self._Symb.update(self.LogicalOperators)\r
308 self._Idx = 0\r
309 self._Len = len(self._Expr)\r
310 self._Token = ''\r
d0acc87a 311 self._WarnExcept = None\r
0d2711a6
LG
312\r
313 # Literal token without any conversion\r
314 self._LiteralToken = ''\r
315\r
316 # Public entry for this class\r
d0acc87a
LG
317 # @param RealValue: False: only evaluate if the expression is true or false, used for conditional expression\r
318 # True : return the evaluated str(value), used for PCD value\r
319 #\r
320 # @return: True or False if RealValue is False\r
321 # Evaluated value of string format if RealValue is True\r
322 #\r
4afd3d04 323 def __call__(self, RealValue=False, Depth=0):\r
0d2711a6
LG
324 if self._NoProcess:\r
325 return self._Expr\r
326\r
4afd3d04
LG
327 self._Depth = Depth\r
328\r
d0acc87a 329 self._Expr = self._Expr.strip()\r
4afd3d04 330 if RealValue and Depth == 0:\r
d0acc87a
LG
331 self._Token = self._Expr\r
332 if self.__IsNumberToken():\r
333 return self._Expr\r
35f613d9 334 Token = ''\r
25918452
LG
335 try:\r
336 Token = self._GetToken()\r
25918452
LG
337 except BadExpression:\r
338 pass\r
35f613d9 339 if type(Token) == type('') and Token.startswith('{') and Token.endswith('}') and self._Idx >= self._Len:\r
35f613d9 340 return self._Expr\r
d0acc87a
LG
341\r
342 self._Idx = 0\r
343 self._Token = ''\r
344\r
bc0d7233 345 Val = self._ConExpr()\r
d0acc87a
LG
346 RealVal = Val\r
347 if type(Val) == type(''):\r
348 if Val == 'L""':\r
349 Val = False\r
350 elif not Val:\r
351 Val = False\r
352 RealVal = '""'\r
726c501c 353 elif not Val.startswith('L"') and not Val.startswith('{') and not Val.startswith("L'"):\r
d0acc87a
LG
354 Val = True\r
355 RealVal = '"' + RealVal + '"'\r
0d2711a6
LG
356\r
357 # The expression has been parsed, but the end of expression is not reached\r
358 # It means the rest does not comply EBNF of <Expression>\r
359 if self._Idx != self._Len:\r
360 raise BadExpression(ERR_SNYTAX % self._Expr[self._Idx:])\r
361\r
d0acc87a
LG
362 if RealValue:\r
363 RetVal = str(RealVal)\r
364 elif Val:\r
365 RetVal = True\r
366 else:\r
367 RetVal = False\r
368\r
369 if self._WarnExcept:\r
370 self._WarnExcept.result = RetVal\r
371 raise self._WarnExcept\r
372 else:\r
373 return RetVal\r
0d2711a6
LG
374\r
375 # Template function to parse binary operators which have same precedence\r
376 # Expr [Operator Expr]*\r
377 def _ExprFuncTemplate(self, EvalFunc, OpLst):\r
378 Val = EvalFunc()\r
379 while self._IsOperator(OpLst):\r
380 Op = self._Token\r
bc0d7233
YF
381 if Op == '?':\r
382 Val2 = EvalFunc()\r
383 if self._IsOperator(':'):\r
384 Val3 = EvalFunc()\r
385 if Val:\r
386 Val = Val2\r
387 else:\r
388 Val = Val3\r
389 continue\r
d0acc87a
LG
390 try:\r
391 Val = self.Eval(Op, Val, EvalFunc())\r
392 except WrnExpression, Warn:\r
393 self._WarnExcept = Warn\r
394 Val = Warn.result\r
0d2711a6 395 return Val\r
bc0d7233
YF
396 # A [? B]*\r
397 def _ConExpr(self):\r
398 return self._ExprFuncTemplate(self._OrExpr, ['?', ':'])\r
0d2711a6
LG
399\r
400 # A [|| B]*\r
401 def _OrExpr(self):\r
402 return self._ExprFuncTemplate(self._AndExpr, ["OR", "or", "||"])\r
403\r
404 # A [&& B]*\r
405 def _AndExpr(self):\r
406 return self._ExprFuncTemplate(self._BitOr, ["AND", "and", "&&"])\r
407\r
408 # A [ | B]*\r
409 def _BitOr(self):\r
410 return self._ExprFuncTemplate(self._BitXor, ["|"])\r
411\r
412 # A [ ^ B]*\r
413 def _BitXor(self):\r
414 return self._ExprFuncTemplate(self._BitAnd, ["XOR", "xor", "^"])\r
415\r
416 # A [ & B]*\r
417 def _BitAnd(self):\r
418 return self._ExprFuncTemplate(self._EqExpr, ["&"])\r
419\r
420 # A [ == B]*\r
421 def _EqExpr(self):\r
422 Val = self._RelExpr()\r
423 while self._IsOperator(["==", "!=", "EQ", "NE", "IN", "in", "!", "NOT", "not"]):\r
424 Op = self._Token\r
425 if Op in ["!", "NOT", "not"]:\r
426 if not self._IsOperator(["IN", "in"]):\r
427 raise BadExpression(ERR_REL_NOT_IN)\r
428 Op += ' ' + self._Token\r
d0acc87a
LG
429 try:\r
430 Val = self.Eval(Op, Val, self._RelExpr())\r
431 except WrnExpression, Warn:\r
432 self._WarnExcept = Warn\r
433 Val = Warn.result\r
0d2711a6
LG
434 return Val\r
435\r
436 # A [ > B]*\r
437 def _RelExpr(self):\r
5e06f1a0
YZ
438 return self._ExprFuncTemplate(self._ShiftExpr, ["<=", ">=", "<", ">", "LE", "GE", "LT", "GT"])\r
439\r
440 def _ShiftExpr(self):\r
441 return self._ExprFuncTemplate(self._AddExpr, ["<<", ">>"])\r
0d2711a6
LG
442\r
443 # A [ + B]*\r
444 def _AddExpr(self):\r
5e06f1a0
YZ
445 return self._ExprFuncTemplate(self._MulExpr, ["+", "-"])\r
446\r
447 # A [ * B]*\r
448 def _MulExpr(self):\r
449 return self._ExprFuncTemplate(self._UnaryExpr, ["*", "/", "%"])\r
0d2711a6
LG
450\r
451 # [!]*A\r
452 def _UnaryExpr(self):\r
453 if self._IsOperator(["!", "NOT", "not"]):\r
454 Val = self._UnaryExpr()\r
d0acc87a
LG
455 try:\r
456 return self.Eval('not', Val)\r
457 except WrnExpression, Warn:\r
458 self._WarnExcept = Warn\r
459 return Warn.result\r
5e06f1a0
YZ
460 if self._IsOperator(["~"]):\r
461 Val = self._UnaryExpr()\r
462 try:\r
463 return self.Eval('~', Val)\r
464 except WrnExpression, Warn:\r
465 self._WarnExcept = Warn\r
466 return Warn.result\r
0d2711a6
LG
467 return self._IdenExpr()\r
468\r
469 # Parse identifier or encapsulated expression\r
470 def _IdenExpr(self):\r
471 Tk = self._GetToken()\r
472 if Tk == '(':\r
bc0d7233 473 Val = self._ConExpr()\r
0d2711a6
LG
474 try:\r
475 # _GetToken may also raise BadExpression\r
476 if self._GetToken() != ')':\r
477 raise BadExpression(ERR_MATCH)\r
478 except BadExpression:\r
479 raise BadExpression(ERR_MATCH)\r
480 return Val\r
481 return Tk\r
482\r
483 # Skip whitespace or tab\r
484 def __SkipWS(self):\r
485 for Char in self._Expr[self._Idx:]:\r
486 if Char not in ' \t':\r
487 break\r
488 self._Idx += 1\r
489\r
490 # Try to convert string to number\r
491 def __IsNumberToken(self):\r
492 Radix = 10\r
493 if self._Token.lower()[0:2] == '0x' and len(self._Token) > 2:\r
494 Radix = 16\r
35f613d9
YF
495 if self._Token.startswith('"') or self._Token.startswith('L"'):\r
496 Flag = 0\r
497 for Index in range(len(self._Token)):\r
498 if self._Token[Index] in ['"']:\r
ea927d2f
FY
499 if self._Token[Index - 1] == '\\':\r
500 continue\r
35f613d9
YF
501 Flag += 1\r
502 if Flag == 2 and self._Token.endswith('"'):\r
35f613d9
YF
503 return True\r
504 if self._Token.startswith("'") or self._Token.startswith("L'"):\r
726c501c
YZ
505 Flag = 0\r
506 for Index in range(len(self._Token)):\r
35f613d9 507 if self._Token[Index] in ["'"]:\r
ea927d2f
FY
508 if self._Token[Index - 1] == '\\':\r
509 continue\r
726c501c 510 Flag += 1\r
35f613d9 511 if Flag == 2 and self._Token.endswith("'"):\r
726c501c 512 return True\r
0d2711a6
LG
513 try:\r
514 self._Token = int(self._Token, Radix)\r
515 return True\r
516 except ValueError:\r
517 return False\r
518 except TypeError:\r
519 return False\r
520\r
521 # Parse array: {...}\r
522 def __GetArray(self):\r
523 Token = '{'\r
524 self._Idx += 1\r
525 self.__GetNList(True)\r
526 Token += self._LiteralToken\r
527 if self._Idx >= self._Len or self._Expr[self._Idx] != '}':\r
528 raise BadExpression(ERR_ARRAY_TOKEN % Token)\r
529 Token += '}'\r
530\r
531 # All whitespace and tabs in array are already stripped.\r
532 IsArray = IsGuid = False\r
533 if len(Token.split(',')) == 11 and len(Token.split(',{')) == 2 \\r
534 and len(Token.split('},')) == 1:\r
535 HexLen = [11,6,6,5,4,4,4,4,4,4,6]\r
536 HexList= Token.split(',')\r
537 if HexList[3].startswith('{') and \\r
538 not [Index for Index, Hex in enumerate(HexList) if len(Hex) > HexLen[Index]]:\r
539 IsGuid = True\r
540 if Token.lstrip('{').rstrip('}').find('{') == -1:\r
541 if not [Hex for Hex in Token.lstrip('{').rstrip('}').split(',') if len(Hex) > 4]:\r
542 IsArray = True\r
543 if not IsArray and not IsGuid:\r
544 raise BadExpression(ERR_ARRAY_TOKEN % Token)\r
545 self._Idx += 1\r
546 self._Token = self._LiteralToken = Token\r
547 return self._Token\r
548\r
549 # Parse string, the format must be: "..."\r
550 def __GetString(self):\r
551 Idx = self._Idx\r
552\r
553 # Skip left quote\r
554 self._Idx += 1\r
555\r
556 # Replace escape \\\", \"\r
ea927d2f
FY
557 if self._Expr[Idx] == '"':\r
558 Expr = self._Expr[self._Idx:].replace('\\\\', '//').replace('\\\"', '\\\'')\r
559 for Ch in Expr:\r
560 self._Idx += 1\r
561 if Ch == '"':\r
562 break\r
563 self._Token = self._LiteralToken = self._Expr[Idx:self._Idx]\r
564 if not self._Token.endswith('"'):\r
565 raise BadExpression(ERR_STRING_TOKEN % self._Token)\r
566 #Replace escape \\\', \'\r
567 elif self._Expr[Idx] == "'":\r
568 Expr = self._Expr[self._Idx:].replace('\\\\', '//').replace("\\\'", "\\\"")\r
569 for Ch in Expr:\r
570 self._Idx += 1\r
571 if Ch == "'":\r
572 break\r
573 self._Token = self._LiteralToken = self._Expr[Idx:self._Idx]\r
574 if not self._Token.endswith("'"):\r
575 raise BadExpression(ERR_STRING_TOKEN % self._Token)\r
0d2711a6
LG
576 self._Token = self._Token[1:-1]\r
577 return self._Token\r
578\r
579 # Get token that is comprised by alphanumeric, underscore or dot(used by PCD)\r
580 # @param IsAlphaOp: Indicate if parsing general token or script operator(EQ, NE...)\r
581 def __GetIdToken(self, IsAlphaOp = False):\r
582 IdToken = ''\r
583 for Ch in self._Expr[self._Idx:]:\r
bc0d7233 584 if not self.__IsIdChar(Ch) or ('?' in self._Expr and Ch == ':'):\r
0d2711a6
LG
585 break\r
586 self._Idx += 1\r
587 IdToken += Ch\r
588\r
589 self._Token = self._LiteralToken = IdToken\r
590 if not IsAlphaOp:\r
591 self.__ResolveToken()\r
592 return self._Token\r
593\r
594 # Try to resolve token\r
595 def __ResolveToken(self):\r
596 if not self._Token:\r
597 raise BadExpression(ERR_EMPTY_TOKEN)\r
598\r
599 # PCD token\r
600 if self.PcdPattern.match(self._Token):\r
601 if self._Token not in self._Symb:\r
64b2609f
LG
602 Ex = BadExpression(ERR_PCD_RESOLVE % self._Token)\r
603 Ex.Pcd = self._Token\r
604 raise Ex\r
4afd3d04 605 self._Token = ValueExpression(self._Symb[self._Token], self._Symb)(True, self._Depth+1)\r
0d2711a6
LG
606 if type(self._Token) != type(''):\r
607 self._LiteralToken = hex(self._Token)\r
608 return\r
609\r
610 if self._Token.startswith('"'):\r
611 self._Token = self._Token[1:-1]\r
612 elif self._Token in ["FALSE", "false", "False"]:\r
613 self._Token = False\r
614 elif self._Token in ["TRUE", "true", "True"]:\r
615 self._Token = True\r
616 else:\r
617 self.__IsNumberToken()\r
618\r
619 def __GetNList(self, InArray=False):\r
620 self._GetSingleToken()\r
621 if not self.__IsHexLiteral():\r
622 if InArray:\r
623 raise BadExpression(ERR_ARRAY_ELE % self._Token)\r
624 return self._Token\r
625\r
626 self.__SkipWS()\r
627 Expr = self._Expr[self._Idx:]\r
628 if not Expr.startswith(','):\r
629 return self._Token\r
630\r
631 NList = self._LiteralToken\r
632 while Expr.startswith(','):\r
633 NList += ','\r
634 self._Idx += 1\r
635 self.__SkipWS()\r
636 self._GetSingleToken()\r
637 if not self.__IsHexLiteral():\r
638 raise BadExpression(ERR_ARRAY_ELE % self._Token)\r
639 NList += self._LiteralToken\r
640 self.__SkipWS()\r
641 Expr = self._Expr[self._Idx:]\r
642 self._Token = self._LiteralToken = NList\r
643 return self._Token\r
644\r
645 def __IsHexLiteral(self):\r
646 if self._LiteralToken.startswith('{') and \\r
647 self._LiteralToken.endswith('}'):\r
648 return True\r
649\r
650 if self.HexPattern.match(self._LiteralToken):\r
651 Token = self._LiteralToken[2:]\r
0d2711a6
LG
652 if not Token:\r
653 self._LiteralToken = '0x0'\r
654 else:\r
35f613d9 655 self._LiteralToken = '0x' + Token\r
0d2711a6
LG
656 return True\r
657 return False\r
658\r
659 def _GetToken(self):\r
660 return self.__GetNList()\r
661\r
662 @staticmethod\r
663 def __IsIdChar(Ch):\r
5e06f1a0 664 return Ch in '._:' or Ch.isalnum()\r
0d2711a6
LG
665\r
666 # Parse operand\r
667 def _GetSingleToken(self):\r
668 self.__SkipWS()\r
669 Expr = self._Expr[self._Idx:]\r
670 if Expr.startswith('L"'):\r
671 # Skip L\r
672 self._Idx += 1\r
673 UStr = self.__GetString()\r
674 self._Token = 'L"' + UStr + '"'\r
726c501c
YZ
675 return self._Token\r
676 elif Expr.startswith("L'"):\r
677 # Skip L\r
678 self._Idx += 1\r
679 UStr = self.__GetString()\r
680 self._Token = "L'" + UStr + "'"\r
0d2711a6 681 return self._Token\r
726c501c
YZ
682 elif Expr.startswith("'"):\r
683 UStr = self.__GetString()\r
684 self._Token = "'" + UStr + "'"\r
726c501c
YZ
685 return self._Token\r
686 elif Expr.startswith('UINT'):\r
687 Re = re.compile('(?:UINT8|UINT16|UINT32|UINT64)\((.+)\)')\r
688 try:\r
689 RetValue = Re.search(Expr).group(1)\r
690 except:\r
691 raise BadExpression('Invalid Expression %s' % Expr)\r
692 Idx = self._Idx\r
693 for Ch in Expr:\r
694 self._Idx += 1\r
695 if Ch == '(':\r
696 Prefix = self._Expr[Idx:self._Idx - 1]\r
697 Idx = self._Idx\r
698 if Ch == ')':\r
699 TmpValue = self._Expr[Idx :self._Idx - 1]\r
700 TmpValue = ValueExpression(TmpValue)(True)\r
701 TmpValue = '0x%x' % int(TmpValue) if type(TmpValue) != type('') else TmpValue\r
702 break\r
703 self._Token, Size = ParseFieldValue(Prefix + '(' + TmpValue + ')')\r
704 return self._Token\r
0d2711a6
LG
705\r
706 self._Token = ''\r
707 if Expr:\r
708 Ch = Expr[0]\r
709 Match = self.RegGuidPattern.match(Expr)\r
710 if Match and not Expr[Match.end():Match.end()+1].isalnum() \\r
711 and Expr[Match.end():Match.end()+1] != '_':\r
712 self._Idx += Match.end()\r
4afd3d04 713 self._Token = ValueExpression(GuidStringToGuidStructureString(Expr[0:Match.end()]))(True, self._Depth+1)\r
0d2711a6
LG
714 return self._Token\r
715 elif self.__IsIdChar(Ch):\r
716 return self.__GetIdToken()\r
717 elif Ch == '"':\r
718 return self.__GetString()\r
719 elif Ch == '{':\r
720 return self.__GetArray()\r
721 elif Ch == '(' or Ch == ')':\r
722 self._Idx += 1\r
723 self._Token = Ch\r
724 return self._Token\r
725\r
726 raise BadExpression(ERR_VALID_TOKEN % Expr)\r
727\r
728 # Parse operator\r
729 def _GetOperator(self):\r
730 self.__SkipWS()\r
bc0d7233 731 LegalOpLst = ['&&', '||', '!=', '==', '>=', '<='] + self.NonLetterOpLst + ['?',':']\r
0d2711a6
LG
732\r
733 self._Token = ''\r
734 Expr = self._Expr[self._Idx:]\r
735\r
736 # Reach end of expression\r
737 if not Expr:\r
738 return ''\r
739\r
740 # Script operator: LT, GT, LE, GE, EQ, NE, and, or, xor, not\r
741 if Expr[0].isalpha():\r
742 return self.__GetIdToken(True)\r
743\r
744 # Start to get regular operator: +, -, <, > ...\r
745 if Expr[0] not in self.NonLetterOpLst:\r
746 return ''\r
747\r
748 OpToken = ''\r
749 for Ch in Expr:\r
750 if Ch in self.NonLetterOpLst:\r
d0acc87a 751 if '!' == Ch and OpToken:\r
0d2711a6
LG
752 break\r
753 self._Idx += 1\r
754 OpToken += Ch\r
755 else:\r
756 break\r
757\r
758 if OpToken not in LegalOpLst:\r
759 raise BadExpression(ERR_OPERATOR_UNSUPPORT % OpToken)\r
760 self._Token = OpToken\r
761 return OpToken\r
762\r
763 # Check if current token matches the operators given from OpList\r
764 def _IsOperator(self, OpList):\r
765 Idx = self._Idx\r
766 self._GetOperator()\r
767 if self._Token in OpList:\r
768 if self._Token in self.LogicalOperators:\r
769 self._Token = self.LogicalOperators[self._Token]\r
770 return True\r
771 self._Idx = Idx\r
772 return False\r
773\r
726c501c
YZ
774class ValueExpressionEx(ValueExpression):\r
775 def __init__(self, PcdValue, PcdType, SymbolTable={}):\r
776 ValueExpression.__init__(self, PcdValue, SymbolTable)\r
777 self.PcdValue = PcdValue\r
778 self.PcdType = PcdType\r
779\r
780 def __call__(self, RealValue=False, Depth=0):\r
781 PcdValue = self.PcdValue\r
782 try:\r
783 PcdValue = ValueExpression.__call__(self, RealValue, Depth)\r
35f613d9 784 if self.PcdType == 'VOID*' and (PcdValue.startswith("'") or PcdValue.startswith("L'")):\r
0e6b8673
FY
785 PcdValue, Size = ParseFieldValue(PcdValue)\r
786 PcdValueList = []\r
787 for I in range(Size):\r
788 PcdValueList.append('0x%02X'%(PcdValue & 0xff))\r
789 PcdValue = PcdValue >> 8\r
790 PcdValue = '{' + ','.join(PcdValueList) + '}'\r
35f613d9
YF
791 elif self.PcdType in ['UINT8', 'UINT16', 'UINT32', 'UINT64', 'BOOLEAN'] and (PcdValue.startswith("'") or \\r
792 PcdValue.startswith('"') or PcdValue.startswith("L'") or PcdValue.startswith('L"') or PcdValue.startswith('{')):\r
793 raise BadExpression\r
726c501c
YZ
794 except WrnExpression, Value:\r
795 PcdValue = Value.result\r
9efe8d60 796 except BadExpression, Value:\r
35f613d9
YF
797 if self.PcdType in ['UINT8', 'UINT16', 'UINT32', 'UINT64', 'BOOLEAN']:\r
798 PcdValue = PcdValue.strip()\r
799 if type(PcdValue) == type('') and PcdValue.startswith('{') and PcdValue.endswith('}'):\r
3be421e9 800 PcdValue = SplitPcdValueString(PcdValue[1:-1])\r
35f613d9
YF
801 if type(PcdValue) == type([]):\r
802 TmpValue = 0\r
726c501c 803 Size = 0\r
3be421e9 804 ValueType = ''\r
35f613d9 805 for Item in PcdValue:\r
3be421e9 806 Item = Item.strip()\r
0e6b8673
FY
807 if Item.startswith('UINT8'):\r
808 ItemSize = 1\r
3be421e9
FY
809 ValueType = 'UINT8'\r
810 elif Item.startswith('UINT16'):\r
35f613d9 811 ItemSize = 2\r
3be421e9 812 ValueType = 'UINT16'\r
35f613d9
YF
813 elif Item.startswith('UINT32'):\r
814 ItemSize = 4\r
3be421e9 815 ValueType = 'UINT32'\r
35f613d9
YF
816 elif Item.startswith('UINT64'):\r
817 ItemSize = 8\r
3be421e9
FY
818 ValueType = 'UINT64'\r
819 elif Item.startswith('"') or Item.startswith("'") or Item.startswith('L'):\r
820 ItemSize = 0\r
821 ValueType = 'VOID*'\r
35f613d9
YF
822 else:\r
823 ItemSize = 0\r
3be421e9
FY
824 ValueType = 'UINT8'\r
825 Item = ValueExpressionEx(Item, ValueType, self._Symb)(True)\r
35f613d9
YF
826\r
827 if ItemSize == 0:\r
3be421e9
FY
828 try:\r
829 tmpValue = int(Item, 16) if Item.upper().startswith('0X') else int(Item, 0)\r
830 if tmpValue > 255:\r
831 raise BadExpression("Byte array number %s should less than 0xFF." % Item)\r
832 except BadExpression, Value:\r
833 raise BadExpression(Value)\r
834 except ValueError:\r
835 pass\r
35f613d9
YF
836 ItemValue, ItemSize = ParseFieldValue(Item)\r
837 else:\r
838 ItemValue = ParseFieldValue(Item)[0]\r
839\r
840 if type(ItemValue) == type(''):\r
841 ItemValue = int(ItemValue, 16) if ItemValue.startswith('0x') else int(ItemValue)\r
842\r
843 TmpValue = (ItemValue << (Size * 8)) | TmpValue\r
844 Size = Size + ItemSize\r
845 else:\r
0e6b8673
FY
846 try:\r
847 TmpValue, Size = ParseFieldValue(PcdValue)\r
9efe8d60
YF
848 except BadExpression, Value:\r
849 raise BadExpression("Type: %s, Value: %s, %s" % (self.PcdType, PcdValue, Value))\r
35f613d9 850 if type(TmpValue) == type(''):\r
9efe8d60
YF
851 try:\r
852 TmpValue = int(TmpValue)\r
853 except:\r
854 raise BadExpression(Value)\r
35f613d9
YF
855 else:\r
856 PcdValue = '0x%0{}X'.format(Size) % (TmpValue)\r
857 if TmpValue < 0:\r
858 raise BadExpression('Type %s PCD Value is negative' % self.PcdType)\r
859 if self.PcdType == 'UINT8' and Size > 1:\r
860 raise BadExpression('Type %s PCD Value Size is Larger than 1 byte' % self.PcdType)\r
861 if self.PcdType == 'UINT16' and Size > 2:\r
862 raise BadExpression('Type %s PCD Value Size is Larger than 2 byte' % self.PcdType)\r
863 if self.PcdType == 'UINT32' and Size > 4:\r
864 raise BadExpression('Type %s PCD Value Size is Larger than 4 byte' % self.PcdType)\r
865 if self.PcdType == 'UINT64' and Size > 8:\r
866 raise BadExpression('Type %s PCD Value Size is Larger than 8 byte' % self.PcdType)\r
6f49996c 867 else:\r
35f613d9
YF
868 try:\r
869 TmpValue = long(PcdValue)\r
870 TmpList = []\r
871 if TmpValue.bit_length() == 0:\r
872 PcdValue = '{0x00}'\r
726c501c 873 else:\r
35f613d9
YF
874 for I in range((TmpValue.bit_length() + 7) / 8):\r
875 TmpList.append('0x%02x' % ((TmpValue >> I * 8) & 0xff))\r
876 PcdValue = '{' + ', '.join(TmpList) + '}'\r
877 except:\r
878 if PcdValue.strip().startswith('{'):\r
3be421e9
FY
879 PcdValueList = SplitPcdValueString(PcdValue.strip()[1:-1])\r
880 LabelDict = {}\r
881 NewPcdValueList = []\r
882 ReLabel = re.compile('LABEL\((\w+)\)')\r
883 ReOffset = re.compile('OFFSET_OF\((\w+)\)')\r
884 LabelOffset = 0\r
885 for Index, Item in enumerate(PcdValueList):\r
886 # compute byte offset of every LABEL\r
887 Item = Item.strip()\r
726c501c 888 try:\r
3be421e9
FY
889 LabelList = ReLabel.findall(Item)\r
890 for Label in LabelList:\r
891 if Label not in LabelDict.keys():\r
892 LabelDict[Label] = str(LabelOffset)\r
726c501c
YZ
893 except:\r
894 pass\r
3be421e9
FY
895 if Item.startswith('UINT8'):\r
896 LabelOffset = LabelOffset + 1\r
897 elif Item.startswith('UINT16'):\r
898 LabelOffset = LabelOffset + 2\r
899 elif Item.startswith('UINT32'):\r
900 LabelOffset = LabelOffset + 4\r
901 elif Item.startswith('UINT64'):\r
902 LabelOffset = LabelOffset + 8\r
903 else:\r
8bd72d7c 904 try:\r
8bd72d7c
KM
905 ItemValue, ItemSize = ParseFieldValue(Item)\r
906 LabelOffset = LabelOffset + ItemSize\r
35f613d9 907 except:\r
3be421e9
FY
908 LabelOffset = LabelOffset + 1\r
909\r
910 for Index, Item in enumerate(PcdValueList):\r
911 # for LABEL parse\r
912 Item = Item.strip()\r
913 try:\r
914 Item = ReLabel.sub('', Item)\r
915 except:\r
916 pass\r
917 try:\r
918 OffsetList = ReOffset.findall(Item)\r
919 except:\r
920 pass\r
921 for Offset in OffsetList:\r
922 if Offset in LabelDict.keys():\r
923 Re = re.compile('OFFSET_OF\(%s\)' % Offset)\r
924 Item = Re.sub(LabelDict[Offset], Item)\r
925 else:\r
926 raise BadExpression('%s not defined' % Offset)\r
927 NewPcdValueList.append(Item)\r
928\r
929 AllPcdValueList = []\r
930 for Item in NewPcdValueList:\r
931 Size = 0\r
932 ValueStr = ''\r
933 TokenSpaceGuidName = ''\r
934 if Item.startswith('GUID') and Item.endswith(')'):\r
35f613d9 935 try:\r
3be421e9 936 TokenSpaceGuidName = re.search('GUID\((\w+)\)', Item).group(1)\r
35f613d9
YF
937 except:\r
938 pass\r
3be421e9
FY
939 if TokenSpaceGuidName and TokenSpaceGuidName in self._Symb:\r
940 Item = 'GUID(' + self._Symb[TokenSpaceGuidName] + ')'\r
941 elif TokenSpaceGuidName:\r
942 raise BadExpression('%s not found in DEC file' % TokenSpaceGuidName)\r
943 Item, Size = ParseFieldValue(Item)\r
944 for Index in range(0, Size):\r
945 ValueStr = '0x%02X' % (int(Item) & 255)\r
946 Item >>= 8\r
947 AllPcdValueList.append(ValueStr)\r
948 continue\r
949 elif Item.startswith('DEVICE_PATH') and Item.endswith(')'):\r
950 Item, Size = ParseFieldValue(Item)\r
951 AllPcdValueList.append(Item[1:-1])\r
952 continue\r
953 else:\r
35f613d9 954 ValueType = ""\r
0e6b8673 955 if Item.startswith('UINT8'):\r
35f613d9
YF
956 ItemSize = 1\r
957 ValueType = "UINT8"\r
958 elif Item.startswith('UINT16'):\r
959 ItemSize = 2\r
960 ValueType = "UINT16"\r
961 elif Item.startswith('UINT32'):\r
962 ItemSize = 4\r
a35918ca 963 ValueType = "UINT32"\r
35f613d9
YF
964 elif Item.startswith('UINT64'):\r
965 ItemSize = 8\r
a35918ca 966 ValueType = "UINT64"\r
35f613d9
YF
967 else:\r
968 ItemSize = 0\r
969 if ValueType:\r
970 TmpValue = ValueExpressionEx(Item, ValueType, self._Symb)(True)\r
971 else:\r
972 TmpValue = ValueExpressionEx(Item, self.PcdType, self._Symb)(True)\r
973 Item = '0x%x' % TmpValue if type(TmpValue) != type('') else TmpValue\r
974 if ItemSize == 0:\r
975 ItemValue, ItemSize = ParseFieldValue(Item)\r
3be421e9
FY
976 if not (Item.startswith('"') or Item.startswith('L') or Item.startswith('{')) and ItemSize > 1:\r
977 raise BadExpression("Byte array number %s should less than 0xFF." % Item)\r
35f613d9
YF
978 else:\r
979 ItemValue = ParseFieldValue(Item)[0]\r
980 for I in range(0, ItemSize):\r
3be421e9 981 ValueStr = '0x%02X' % (int(ItemValue) & 255)\r
35f613d9 982 ItemValue >>= 8\r
3be421e9 983 AllPcdValueList.append(ValueStr)\r
35f613d9
YF
984 Size += ItemSize\r
985\r
3be421e9
FY
986 if Size > 0:\r
987 PcdValue = '{' + ','.join(AllPcdValueList) + '}'\r
0e6b8673 988 else:\r
9efe8d60 989 raise BadExpression("Type: %s, Value: %s, %s"%(self.PcdType, PcdValue, Value))\r
0e6b8673 990\r
35f613d9
YF
991 if PcdValue == 'True':\r
992 PcdValue = '1'\r
993 if PcdValue == 'False':\r
994 PcdValue = '0'\r
726c501c 995\r
726c501c
YZ
996 if RealValue:\r
997 return PcdValue\r
998\r
0d2711a6
LG
999if __name__ == '__main__':\r
1000 pass\r
d0acc87a
LG
1001 while True:\r
1002 input = raw_input('Input expr: ')\r
1003 if input in 'qQ':\r
1004 break\r
1005 try:\r
1006 print ValueExpression(input)(True)\r
1007 print ValueExpression(input)(False)\r
1008 except WrnExpression, Ex:\r
1009 print Ex.result\r
1010 print str(Ex)\r
1011 except Exception, Ex:\r
1012 print str(Ex)\r