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