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