]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/Common/Expression.py
BaseTools: Customize deepcopy function.
[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 try:
443 Val = self.Eval(Op, Val, EvalFunc())
444 except WrnExpression as Warn:
445 self._WarnExcept = Warn
446 Val = Warn.result
447 return Val
448 # A [? B]*
449 def _ConExpr(self):
450 return self._ExprFuncTemplate(self._OrExpr, {'?', ':'})
451
452 # A [|| B]*
453 def _OrExpr(self):
454 return self._ExprFuncTemplate(self._AndExpr, {"OR", "or", "||"})
455
456 # A [&& B]*
457 def _AndExpr(self):
458 return self._ExprFuncTemplate(self._BitOr, {"AND", "and", "&&"})
459
460 # A [ | B]*
461 def _BitOr(self):
462 return self._ExprFuncTemplate(self._BitXor, {"|"})
463
464 # A [ ^ B]*
465 def _BitXor(self):
466 return self._ExprFuncTemplate(self._BitAnd, {"XOR", "xor", "^"})
467
468 # A [ & B]*
469 def _BitAnd(self):
470 return self._ExprFuncTemplate(self._EqExpr, {"&"})
471
472 # A [ == B]*
473 def _EqExpr(self):
474 Val = self._RelExpr()
475 while self._IsOperator({"==", "!=", "EQ", "NE", "IN", "in", "!", "NOT", "not"}):
476 Op = self._Token
477 if Op in {"!", "NOT", "not"}:
478 if not self._IsOperator({"IN", "in"}):
479 raise BadExpression(ERR_REL_NOT_IN)
480 Op += ' ' + self._Token
481 try:
482 Val = self.Eval(Op, Val, self._RelExpr())
483 except WrnExpression as Warn:
484 self._WarnExcept = Warn
485 Val = Warn.result
486 return Val
487
488 # A [ > B]*
489 def _RelExpr(self):
490 return self._ExprFuncTemplate(self._ShiftExpr, {"<=", ">=", "<", ">", "LE", "GE", "LT", "GT"})
491
492 def _ShiftExpr(self):
493 return self._ExprFuncTemplate(self._AddExpr, {"<<", ">>"})
494
495 # A [ + B]*
496 def _AddExpr(self):
497 return self._ExprFuncTemplate(self._MulExpr, {"+", "-"})
498
499 # A [ * B]*
500 def _MulExpr(self):
501 return self._ExprFuncTemplate(self._UnaryExpr, {TAB_STAR, "/", "%"})
502
503 # [!]*A
504 def _UnaryExpr(self):
505 if self._IsOperator({"!", "NOT", "not"}):
506 Val = self._UnaryExpr()
507 try:
508 return self.Eval('not', Val)
509 except WrnExpression as Warn:
510 self._WarnExcept = Warn
511 return Warn.result
512 if self._IsOperator({"~"}):
513 Val = self._UnaryExpr()
514 try:
515 return self.Eval('~', Val)
516 except WrnExpression as Warn:
517 self._WarnExcept = Warn
518 return Warn.result
519 return self._IdenExpr()
520
521 # Parse identifier or encapsulated expression
522 def _IdenExpr(self):
523 Tk = self._GetToken()
524 if Tk == '(':
525 Val = self._ConExpr()
526 try:
527 # _GetToken may also raise BadExpression
528 if self._GetToken() != ')':
529 raise BadExpression(ERR_MATCH)
530 except BadExpression:
531 raise BadExpression(ERR_MATCH)
532 return Val
533 return Tk
534
535 # Skip whitespace or tab
536 def __SkipWS(self):
537 for Char in self._Expr[self._Idx:]:
538 if Char not in ' \t':
539 break
540 self._Idx += 1
541
542 # Try to convert string to number
543 def __IsNumberToken(self):
544 Radix = 10
545 if self._Token.lower()[0:2] == '0x' and len(self._Token) > 2:
546 Radix = 16
547 if self._Token.startswith('"') or self._Token.startswith('L"'):
548 Flag = 0
549 for Index in range(len(self._Token)):
550 if self._Token[Index] in {'"'}:
551 if self._Token[Index - 1] == '\\':
552 continue
553 Flag += 1
554 if Flag == 2 and self._Token.endswith('"'):
555 return True
556 if self._Token.startswith("'") or self._Token.startswith("L'"):
557 Flag = 0
558 for Index in range(len(self._Token)):
559 if self._Token[Index] in {"'"}:
560 if self._Token[Index - 1] == '\\':
561 continue
562 Flag += 1
563 if Flag == 2 and self._Token.endswith("'"):
564 return True
565 try:
566 self._Token = int(self._Token, Radix)
567 return True
568 except ValueError:
569 return False
570 except TypeError:
571 return False
572
573 # Parse array: {...}
574 def __GetArray(self):
575 Token = '{'
576 self._Idx += 1
577 self.__GetNList(True)
578 Token += self._LiteralToken
579 if self._Idx >= self._Len or self._Expr[self._Idx] != '}':
580 raise BadExpression(ERR_ARRAY_TOKEN % Token)
581 Token += '}'
582
583 # All whitespace and tabs in array are already stripped.
584 IsArray = IsGuid = False
585 if len(Token.split(',')) == 11 and len(Token.split(',{')) == 2 \
586 and len(Token.split('},')) == 1:
587 HexLen = [11, 6, 6, 5, 4, 4, 4, 4, 4, 4, 6]
588 HexList= Token.split(',')
589 if HexList[3].startswith('{') and \
590 not [Index for Index, Hex in enumerate(HexList) if len(Hex) > HexLen[Index]]:
591 IsGuid = True
592 if Token.lstrip('{').rstrip('}').find('{') == -1:
593 if not [Hex for Hex in Token.lstrip('{').rstrip('}').split(',') if len(Hex) > 4]:
594 IsArray = True
595 if not IsArray and not IsGuid:
596 raise BadExpression(ERR_ARRAY_TOKEN % Token)
597 self._Idx += 1
598 self._Token = self._LiteralToken = Token
599 return self._Token
600
601 # Parse string, the format must be: "..."
602 def __GetString(self):
603 Idx = self._Idx
604
605 # Skip left quote
606 self._Idx += 1
607
608 # Replace escape \\\", \"
609 if self._Expr[Idx] == '"':
610 Expr = self._Expr[self._Idx:].replace('\\\\', '//').replace('\\\"', '\\\'')
611 for Ch in Expr:
612 self._Idx += 1
613 if Ch == '"':
614 break
615 self._Token = self._LiteralToken = self._Expr[Idx:self._Idx]
616 if not self._Token.endswith('"'):
617 raise BadExpression(ERR_STRING_TOKEN % self._Token)
618 #Replace escape \\\', \'
619 elif self._Expr[Idx] == "'":
620 Expr = self._Expr[self._Idx:].replace('\\\\', '//').replace("\\\'", "\\\"")
621 for Ch in Expr:
622 self._Idx += 1
623 if Ch == "'":
624 break
625 self._Token = self._LiteralToken = self._Expr[Idx:self._Idx]
626 if not self._Token.endswith("'"):
627 raise BadExpression(ERR_STRING_TOKEN % self._Token)
628 self._Token = self._Token[1:-1]
629 return self._Token
630
631 # Get token that is comprised by alphanumeric, underscore or dot(used by PCD)
632 # @param IsAlphaOp: Indicate if parsing general token or script operator(EQ, NE...)
633 def __GetIdToken(self, IsAlphaOp = False):
634 IdToken = ''
635 for Ch in self._Expr[self._Idx:]:
636 if not self.__IsIdChar(Ch) or ('?' in self._Expr and Ch == ':'):
637 break
638 self._Idx += 1
639 IdToken += Ch
640
641 self._Token = self._LiteralToken = IdToken
642 if not IsAlphaOp:
643 self.__ResolveToken()
644 return self._Token
645
646 # Try to resolve token
647 def __ResolveToken(self):
648 if not self._Token:
649 raise BadExpression(ERR_EMPTY_TOKEN)
650
651 # PCD token
652 if PcdPattern.match(self._Token):
653 if self._Token not in self._Symb:
654 Ex = BadExpression(ERR_PCD_RESOLVE % self._Token)
655 Ex.Pcd = self._Token
656 raise Ex
657 self._Token = ValueExpression(self._Symb[self._Token], self._Symb)(True, self._Depth+1)
658 if not isinstance(self._Token, type('')):
659 self._LiteralToken = hex(self._Token)
660 return
661
662 if self._Token.startswith('"'):
663 self._Token = self._Token[1:-1]
664 elif self._Token in {"FALSE", "false", "False"}:
665 self._Token = False
666 elif self._Token in {"TRUE", "true", "True"}:
667 self._Token = True
668 else:
669 self.__IsNumberToken()
670
671 def __GetNList(self, InArray=False):
672 self._GetSingleToken()
673 if not self.__IsHexLiteral():
674 if InArray:
675 raise BadExpression(ERR_ARRAY_ELE % self._Token)
676 return self._Token
677
678 self.__SkipWS()
679 Expr = self._Expr[self._Idx:]
680 if not Expr.startswith(','):
681 return self._Token
682
683 NList = self._LiteralToken
684 while Expr.startswith(','):
685 NList += ','
686 self._Idx += 1
687 self.__SkipWS()
688 self._GetSingleToken()
689 if not self.__IsHexLiteral():
690 raise BadExpression(ERR_ARRAY_ELE % self._Token)
691 NList += self._LiteralToken
692 self.__SkipWS()
693 Expr = self._Expr[self._Idx:]
694 self._Token = self._LiteralToken = NList
695 return self._Token
696
697 def __IsHexLiteral(self):
698 if self._LiteralToken.startswith('{') and \
699 self._LiteralToken.endswith('}'):
700 return True
701
702 if gHexPattern.match(self._LiteralToken):
703 Token = self._LiteralToken[2:]
704 if not Token:
705 self._LiteralToken = '0x0'
706 else:
707 self._LiteralToken = '0x' + Token
708 return True
709 return False
710
711 def _GetToken(self):
712 return self.__GetNList()
713
714 @staticmethod
715 def __IsIdChar(Ch):
716 return Ch in '._:' or Ch.isalnum()
717
718 # Parse operand
719 def _GetSingleToken(self):
720 self.__SkipWS()
721 Expr = self._Expr[self._Idx:]
722 if Expr.startswith('L"'):
723 # Skip L
724 self._Idx += 1
725 UStr = self.__GetString()
726 self._Token = 'L"' + UStr + '"'
727 return self._Token
728 elif Expr.startswith("L'"):
729 # Skip L
730 self._Idx += 1
731 UStr = self.__GetString()
732 self._Token = "L'" + UStr + "'"
733 return self._Token
734 elif Expr.startswith("'"):
735 UStr = self.__GetString()
736 self._Token = "'" + UStr + "'"
737 return self._Token
738 elif Expr.startswith('UINT'):
739 Re = re.compile('(?:UINT8|UINT16|UINT32|UINT64)\((.+)\)')
740 try:
741 RetValue = Re.search(Expr).group(1)
742 except:
743 raise BadExpression('Invalid Expression %s' % Expr)
744 Idx = self._Idx
745 for Ch in Expr:
746 self._Idx += 1
747 if Ch == '(':
748 Prefix = self._Expr[Idx:self._Idx - 1]
749 Idx = self._Idx
750 if Ch == ')':
751 TmpValue = self._Expr[Idx :self._Idx - 1]
752 TmpValue = ValueExpression(TmpValue)(True)
753 TmpValue = '0x%x' % int(TmpValue) if not isinstance(TmpValue, type('')) else TmpValue
754 break
755 self._Token, Size = ParseFieldValue(Prefix + '(' + TmpValue + ')')
756 return self._Token
757
758 self._Token = ''
759 if Expr:
760 Ch = Expr[0]
761 Match = gGuidPattern.match(Expr)
762 if Match and not Expr[Match.end():Match.end()+1].isalnum() \
763 and Expr[Match.end():Match.end()+1] != '_':
764 self._Idx += Match.end()
765 self._Token = ValueExpression(GuidStringToGuidStructureString(Expr[0:Match.end()]))(True, self._Depth+1)
766 return self._Token
767 elif self.__IsIdChar(Ch):
768 return self.__GetIdToken()
769 elif Ch == '"':
770 return self.__GetString()
771 elif Ch == '{':
772 return self.__GetArray()
773 elif Ch == '(' or Ch == ')':
774 self._Idx += 1
775 self._Token = Ch
776 return self._Token
777
778 raise BadExpression(ERR_VALID_TOKEN % Expr)
779
780 # Parse operator
781 def _GetOperator(self):
782 self.__SkipWS()
783 LegalOpLst = ['&&', '||', '!=', '==', '>=', '<='] + self.NonLetterOpLst + ['?', ':']
784
785 self._Token = ''
786 Expr = self._Expr[self._Idx:]
787
788 # Reach end of expression
789 if not Expr:
790 return ''
791
792 # Script operator: LT, GT, LE, GE, EQ, NE, and, or, xor, not
793 if Expr[0].isalpha():
794 return self.__GetIdToken(True)
795
796 # Start to get regular operator: +, -, <, > ...
797 if Expr[0] not in self.NonLetterOpLst:
798 return ''
799
800 OpToken = ''
801 for Ch in Expr:
802 if Ch in self.NonLetterOpLst:
803 if Ch in ['!', '~'] and OpToken:
804 break
805 self._Idx += 1
806 OpToken += Ch
807 else:
808 break
809
810 if OpToken not in LegalOpLst:
811 raise BadExpression(ERR_OPERATOR_UNSUPPORT % OpToken)
812 self._Token = OpToken
813 return OpToken
814
815 class ValueExpressionEx(ValueExpression):
816 def __init__(self, PcdValue, PcdType, SymbolTable={}):
817 ValueExpression.__init__(self, PcdValue, SymbolTable)
818 self.PcdValue = PcdValue
819 self.PcdType = PcdType
820
821 def __call__(self, RealValue=False, Depth=0):
822 PcdValue = self.PcdValue
823 if "{CODE(" not in PcdValue:
824 try:
825 PcdValue = ValueExpression.__call__(self, RealValue, Depth)
826 if self.PcdType == TAB_VOID and (PcdValue.startswith("'") or PcdValue.startswith("L'")):
827 PcdValue, Size = ParseFieldValue(PcdValue)
828 PcdValueList = []
829 for I in range(Size):
830 PcdValueList.append('0x%02X'%(PcdValue & 0xff))
831 PcdValue = PcdValue >> 8
832 PcdValue = '{' + ','.join(PcdValueList) + '}'
833 elif self.PcdType in TAB_PCD_NUMERIC_TYPES and (PcdValue.startswith("'") or \
834 PcdValue.startswith('"') or PcdValue.startswith("L'") or PcdValue.startswith('L"') or PcdValue.startswith('{')):
835 raise BadExpression
836 except WrnExpression as Value:
837 PcdValue = Value.result
838 except BadExpression as Value:
839 if self.PcdType in TAB_PCD_NUMERIC_TYPES:
840 PcdValue = PcdValue.strip()
841 if PcdValue.startswith('{') and PcdValue.endswith('}'):
842 PcdValue = SplitPcdValueString(PcdValue[1:-1])
843 if isinstance(PcdValue, type([])):
844 TmpValue = 0
845 Size = 0
846 ValueType = ''
847 for Item in PcdValue:
848 Item = Item.strip()
849 if Item.startswith(TAB_UINT8):
850 ItemSize = 1
851 ValueType = TAB_UINT8
852 elif Item.startswith(TAB_UINT16):
853 ItemSize = 2
854 ValueType = TAB_UINT16
855 elif Item.startswith(TAB_UINT32):
856 ItemSize = 4
857 ValueType = TAB_UINT32
858 elif Item.startswith(TAB_UINT64):
859 ItemSize = 8
860 ValueType = TAB_UINT64
861 elif Item[0] in {'"', "'", 'L'}:
862 ItemSize = 0
863 ValueType = TAB_VOID
864 else:
865 ItemSize = 0
866 ValueType = TAB_UINT8
867 Item = ValueExpressionEx(Item, ValueType, self._Symb)(True)
868 if ItemSize == 0:
869 try:
870 tmpValue = int(Item, 0)
871 if tmpValue > 255:
872 raise BadExpression("Byte array number %s should less than 0xFF." % Item)
873 except BadExpression as Value:
874 raise BadExpression(Value)
875 except ValueError:
876 pass
877 ItemValue, ItemSize = ParseFieldValue(Item)
878 else:
879 ItemValue = ParseFieldValue(Item)[0]
880
881 if isinstance(ItemValue, type('')):
882 ItemValue = int(ItemValue, 0)
883
884 TmpValue = (ItemValue << (Size * 8)) | TmpValue
885 Size = Size + ItemSize
886 else:
887 try:
888 TmpValue, Size = ParseFieldValue(PcdValue)
889 except BadExpression as Value:
890 raise BadExpression("Type: %s, Value: %s, %s" % (self.PcdType, PcdValue, Value))
891 if isinstance(TmpValue, type('')):
892 try:
893 TmpValue = int(TmpValue)
894 except:
895 raise BadExpression(Value)
896 else:
897 PcdValue = '0x%0{}X'.format(Size) % (TmpValue)
898 if TmpValue < 0:
899 raise BadExpression('Type %s PCD Value is negative' % self.PcdType)
900 if self.PcdType == TAB_UINT8 and Size > 1:
901 raise BadExpression('Type %s PCD Value Size is Larger than 1 byte' % self.PcdType)
902 if self.PcdType == TAB_UINT16 and Size > 2:
903 raise BadExpression('Type %s PCD Value Size is Larger than 2 byte' % self.PcdType)
904 if self.PcdType == TAB_UINT32 and Size > 4:
905 raise BadExpression('Type %s PCD Value Size is Larger than 4 byte' % self.PcdType)
906 if self.PcdType == TAB_UINT64 and Size > 8:
907 raise BadExpression('Type %s PCD Value Size is Larger than 8 byte' % self.PcdType)
908 else:
909 try:
910 TmpValue = int(PcdValue)
911 TmpList = []
912 if TmpValue.bit_length() == 0:
913 PcdValue = '{0x00}'
914 else:
915 for I in range((TmpValue.bit_length() + 7) / 8):
916 TmpList.append('0x%02x' % ((TmpValue >> I * 8) & 0xff))
917 PcdValue = '{' + ', '.join(TmpList) + '}'
918 except:
919 if PcdValue.strip().startswith('{'):
920 PcdValueList = SplitPcdValueString(PcdValue.strip()[1:-1])
921 LabelDict = {}
922 NewPcdValueList = []
923 LabelOffset = 0
924 for Item in PcdValueList:
925 # compute byte offset of every LABEL
926 LabelList = _ReLabel.findall(Item)
927 Item = _ReLabel.sub('', Item)
928 Item = Item.strip()
929 if LabelList:
930 for Label in LabelList:
931 if not IsValidCName(Label):
932 raise BadExpression('%s is not a valid c variable name' % Label)
933 if Label not in LabelDict:
934 LabelDict[Label] = str(LabelOffset)
935 if Item.startswith(TAB_UINT8):
936 LabelOffset = LabelOffset + 1
937 elif Item.startswith(TAB_UINT16):
938 LabelOffset = LabelOffset + 2
939 elif Item.startswith(TAB_UINT32):
940 LabelOffset = LabelOffset + 4
941 elif Item.startswith(TAB_UINT64):
942 LabelOffset = LabelOffset + 8
943 else:
944 try:
945 ItemValue, ItemSize = ParseFieldValue(Item)
946 LabelOffset = LabelOffset + ItemSize
947 except:
948 LabelOffset = LabelOffset + 1
949
950 for Item in PcdValueList:
951 # for LABEL parse
952 Item = Item.strip()
953 try:
954 Item = _ReLabel.sub('', Item)
955 except:
956 pass
957 try:
958 OffsetList = _ReOffset.findall(Item)
959 except:
960 pass
961 # replace each offset, except errors
962 for Offset in OffsetList:
963 try:
964 Item = Item.replace('OFFSET_OF({})'.format(Offset), LabelDict[Offset])
965 except:
966 raise BadExpression('%s not defined' % Offset)
967
968 NewPcdValueList.append(Item)
969
970 AllPcdValueList = []
971 for Item in NewPcdValueList:
972 Size = 0
973 ValueStr = ''
974 TokenSpaceGuidName = ''
975 if Item.startswith(TAB_GUID) and Item.endswith(')'):
976 try:
977 TokenSpaceGuidName = re.search('GUID\((\w+)\)', Item).group(1)
978 except:
979 pass
980 if TokenSpaceGuidName and TokenSpaceGuidName in self._Symb:
981 Item = 'GUID(' + self._Symb[TokenSpaceGuidName] + ')'
982 elif TokenSpaceGuidName:
983 raise BadExpression('%s not found in DEC file' % TokenSpaceGuidName)
984 Item, Size = ParseFieldValue(Item)
985 for Index in range(0, Size):
986 ValueStr = '0x%02X' % (int(Item) & 255)
987 Item >>= 8
988 AllPcdValueList.append(ValueStr)
989 continue
990 elif Item.startswith('DEVICE_PATH') and Item.endswith(')'):
991 Item, Size = ParseFieldValue(Item)
992 AllPcdValueList.append(Item[1:-1])
993 continue
994 else:
995 ValueType = ""
996 if Item.startswith(TAB_UINT8):
997 ItemSize = 1
998 ValueType = TAB_UINT8
999 elif Item.startswith(TAB_UINT16):
1000 ItemSize = 2
1001 ValueType = TAB_UINT16
1002 elif Item.startswith(TAB_UINT32):
1003 ItemSize = 4
1004 ValueType = TAB_UINT32
1005 elif Item.startswith(TAB_UINT64):
1006 ItemSize = 8
1007 ValueType = TAB_UINT64
1008 else:
1009 ItemSize = 0
1010 if ValueType:
1011 TmpValue = ValueExpressionEx(Item, ValueType, self._Symb)(True)
1012 else:
1013 TmpValue = ValueExpressionEx(Item, self.PcdType, self._Symb)(True)
1014 Item = '0x%x' % TmpValue if not isinstance(TmpValue, type('')) else TmpValue
1015 if ItemSize == 0:
1016 ItemValue, ItemSize = ParseFieldValue(Item)
1017 if Item[0] not in {'"', 'L', '{'} and ItemSize > 1:
1018 raise BadExpression("Byte array number %s should less than 0xFF." % Item)
1019 else:
1020 ItemValue = ParseFieldValue(Item)[0]
1021 for I in range(0, ItemSize):
1022 ValueStr = '0x%02X' % (int(ItemValue) & 255)
1023 ItemValue >>= 8
1024 AllPcdValueList.append(ValueStr)
1025 Size += ItemSize
1026
1027 if Size > 0:
1028 PcdValue = '{' + ','.join(AllPcdValueList) + '}'
1029 else:
1030 raise BadExpression("Type: %s, Value: %s, %s"%(self.PcdType, PcdValue, Value))
1031
1032 if PcdValue == 'True':
1033 PcdValue = '1'
1034 if PcdValue == 'False':
1035 PcdValue = '0'
1036
1037 if RealValue:
1038 return PcdValue
1039
1040 if __name__ == '__main__':
1041 pass
1042 while True:
1043 input = raw_input('Input expr: ')
1044 if input in 'qQ':
1045 break
1046 try:
1047 print(ValueExpression(input)(True))
1048 print(ValueExpression(input)(False))
1049 except WrnExpression as Ex:
1050 print(Ex.result)
1051 print(str(Ex))
1052 except Exception as Ex:
1053 print(str(Ex))