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