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