2 # This file is used to parse and evaluate expression in directive or PCD value.
4 # Copyright (c) 2011 - 2017, 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
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.
15 from Common
.GlobalData
import *
16 from CommonDataClass
.Exceptions
import BadExpression
17 from CommonDataClass
.Exceptions
import WrnExpression
18 from Misc
import GuidStringToGuidStructureString
20 ERR_STRING_EXPR
= 'This operator cannot be used in string expression: [%s].'
21 ERR_SNYTAX
= 'Syntax error, the rest of expression cannot be evaluated: [%s].'
22 ERR_MATCH
= 'No matching right parenthesis.'
23 ERR_STRING_TOKEN
= 'Bad string token: [%s].'
24 ERR_MACRO_TOKEN
= 'Bad macro token: [%s].'
25 ERR_EMPTY_TOKEN
= 'Empty token is not allowed.'
26 ERR_PCD_RESOLVE
= 'PCD token cannot be resolved: [%s].'
27 ERR_VALID_TOKEN
= 'No more valid token found from rest of string: [%s].'
28 ERR_EXPR_TYPE
= 'Different types found in expression.'
29 ERR_OPERATOR_UNSUPPORT
= 'Unsupported operator: [%s]'
30 ERR_REL_NOT_IN
= 'Expect "IN" after "not" operator.'
31 WRN_BOOL_EXPR
= 'Operand of boolean type cannot be used in arithmetic expression.'
32 WRN_EQCMP_STR_OTHERS
= '== Comparison between Operand of string type and Boolean/Number Type always return False.'
33 WRN_NECMP_STR_OTHERS
= '!= Comparison between Operand of string type and Boolean/Number Type always return True.'
34 ERR_RELCMP_STR_OTHERS
= 'Operator taking Operand of string type and Boolean/Number Type is not allowed: [%s].'
35 ERR_STRING_CMP
= 'Unicode string and general string cannot be compared: [%s %s %s]'
36 ERR_ARRAY_TOKEN
= 'Bad C array or C format GUID token: [%s].'
37 ERR_ARRAY_ELE
= 'This must be HEX value for NList or Array: [%s].'
38 ERR_EMPTY_EXPR
= 'Empty expression is not allowed.'
39 ERR_IN_OPERAND
= 'Macro after IN operator can only be: $(FAMILY), $(ARCH), $(TOOL_CHAIN_TAG) and $(TARGET).'
42 # Split string to list according double quote
43 # For example: abc"de\"f"ghi"jkl"mn will be: ['abc', '"de\"f"', 'ghi', '"jkl"', 'mn']
45 def SplitString(String
):
46 # There might be escaped quote: "abc\"def\\\"ghi"
47 Str
= String
.replace('\\\\', '//').replace('\\\"', '\\\'')
51 for i
, ch
in enumerate(Str
):
64 raise BadExpression(ERR_STRING_TOKEN
% Item
)
71 def ReplaceExprMacro(String
, Macros
, ExceptionList
= None):
72 StrList
= SplitString(String
)
73 for i
, String
in enumerate(StrList
):
75 if String
.startswith('"'):
77 MacroStartPos
= String
.find('$(')
79 for Pcd
in gPlatformPcds
.keys():
81 if Pcd
not in gConditionalPcds
:
82 gConditionalPcds
.append(Pcd
)
85 while MacroStartPos
>= 0:
86 RetStr
= String
[0:MacroStartPos
]
87 MacroEndPos
= String
.find(')', MacroStartPos
)
89 raise BadExpression(ERR_MACRO_TOKEN
% String
[MacroStartPos
:])
90 Macro
= String
[MacroStartPos
+2:MacroEndPos
]
91 if Macro
not in Macros
:
92 # From C reference manual:
93 # If an undefined macro name appears in the constant-expression of
94 # !if or !elif, it is replaced by the integer constant 0.
97 Tklst
= RetStr
.split()
98 if Tklst
and Tklst
[-1] in ['IN', 'in'] and ExceptionList
and Macro
not in ExceptionList
:
99 raise BadExpression(ERR_IN_OPERAND
)
100 # Make sure the macro in exception list is encapsulated by double quote
101 # For example: DEFINE ARCH = IA32 X64
102 # $(ARCH) is replaced with "IA32 X64"
103 if ExceptionList
and Macro
in ExceptionList
:
104 RetStr
+= '"' + Macros
[Macro
] + '"'
105 elif Macros
[Macro
].strip():
106 RetStr
+= Macros
[Macro
]
110 RetStr
+= Macros
[Macro
]
111 RetStr
+= String
[MacroEndPos
+1:]
113 MacroStartPos
= String
.find('$(')
115 return ''.join(StrList
)
117 SupportedInMacroList
= ['TARGET', 'TOOL_CHAIN_TAG', 'ARCH', 'FAMILY']
119 class ValueExpression(object):
120 # Logical operator mapping
122 '&&' : 'and', '||' : 'or',
123 '!' : 'not', 'AND': 'and',
124 'OR' : 'or' , 'NOT': 'not',
125 'XOR': '^' , 'xor': '^',
126 'EQ' : '==' , 'NE' : '!=',
127 'GT' : '>' , 'LT' : '<',
128 'GE' : '>=' , 'LE' : '<=',
132 NonLetterOpLst
= ['+', '-', '*', '/', '%', '&', '|', '^', '~', '<<', '>>', '!', '=', '>', '<', '?', ':']
134 PcdPattern
= re
.compile(r
'[_a-zA-Z][0-9A-Za-z_]*\.[_a-zA-Z][0-9A-Za-z_]*$')
135 HexPattern
= re
.compile(r
'0[xX][0-9a-fA-F]+$')
136 RegGuidPattern
= re
.compile(r
'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}')
138 SymbolPattern
= re
.compile("("
139 "\$\([A-Z][A-Z0-9_]*\)|\$\(\w+\.\w+\)|\w+\.\w+|"
141 "(?<=\W)AND(?=\W)|(?<=\W)OR(?=\W)|(?<=\W)NOT(?=\W)|(?<=\W)XOR(?=\W)|"
142 "(?<=\W)EQ(?=\W)|(?<=\W)NE(?=\W)|(?<=\W)GT(?=\W)|(?<=\W)LT(?=\W)|(?<=\W)GE(?=\W)|(?<=\W)LE(?=\W)"
146 def Eval(Operator
, Oprand1
, Oprand2
= None):
149 if Operator
not in ["==", "!=", ">=", "<=", ">", "<", "in", "not in"] and \
150 (type(Oprand1
) == type('') or type(Oprand2
) == type('')):
151 raise BadExpression(ERR_STRING_EXPR
% Operator
)
161 if Operator
in ["!", "NOT", "not"]:
162 if type(Oprand1
) == type(''):
163 raise BadExpression(ERR_STRING_EXPR
% Operator
)
164 EvalStr
= 'not Oprand1'
165 elif Operator
in ["~"]:
166 if type(Oprand1
) == type(''):
167 raise BadExpression(ERR_STRING_EXPR
% Operator
)
168 EvalStr
= '~ Oprand1'
170 if Operator
in ["+", "-"] and (type(True) in [type(Oprand1
), type(Oprand2
)]):
171 # Boolean in '+'/'-' will be evaluated but raise warning
172 WrnExp
= WrnExpression(WRN_BOOL_EXPR
)
173 elif type('') in [type(Oprand1
), type(Oprand2
)] and type(Oprand1
)!= type(Oprand2
):
174 # == between string and number/boolean will always return False, != return True
176 WrnExp
= WrnExpression(WRN_EQCMP_STR_OTHERS
)
177 WrnExp
.result
= False
179 elif Operator
== "!=":
180 WrnExp
= WrnExpression(WRN_NECMP_STR_OTHERS
)
184 raise BadExpression(ERR_RELCMP_STR_OTHERS
% Operator
)
185 elif TypeDict
[type(Oprand1
)] != TypeDict
[type(Oprand2
)]:
186 if Operator
in ["==", "!=", ">=", "<=", ">", "<"] and set((TypeDict
[type(Oprand1
)], TypeDict
[type(Oprand2
)])) == set((TypeDict
[type(True)], TypeDict
[type(0)])):
187 # comparison between number and boolean is allowed
189 elif Operator
in ['&', '|', '^', "and", "or"] and set((TypeDict
[type(Oprand1
)], TypeDict
[type(Oprand2
)])) == set((TypeDict
[type(True)], TypeDict
[type(0)])):
190 # bitwise and logical operation between number and boolean is allowed
193 raise BadExpression(ERR_EXPR_TYPE
)
194 if type(Oprand1
) == type('') and type(Oprand2
) == type(''):
195 if (Oprand1
.startswith('L"') and not Oprand2
.startswith('L"')) or \
196 (not Oprand1
.startswith('L"') and Oprand2
.startswith('L"')):
197 raise BadExpression(ERR_STRING_CMP
% (Oprand1
, Operator
, Oprand2
))
198 if 'in' in Operator
and type(Oprand2
) == type(''):
199 Oprand2
= Oprand2
.split()
200 EvalStr
= 'Oprand1 ' + Operator
+ ' Oprand2'
202 # Local symbols used by built in eval function
208 Val
= eval(EvalStr
, {}, Dict
)
209 except Exception, Excpt
:
210 raise BadExpression(str(Excpt
))
212 if Operator
in ['and', 'or']:
223 def __init__(self
, Expression
, SymbolTable
={}):
224 self
._NoProcess
= False
225 if type(Expression
) != type(''):
226 self
._Expr
= Expression
227 self
._NoProcess
= True
230 self
._Expr
= ReplaceExprMacro(Expression
.strip(),
232 SupportedInMacroList
)
234 if not self
._Expr
.strip():
235 raise BadExpression(ERR_EMPTY_EXPR
)
238 # The symbol table including PCD and macro mapping
240 self
._Symb
= SymbolTable
241 self
._Symb
.update(self
.LogicalOperators
)
243 self
._Len
= len(self
._Expr
)
245 self
._WarnExcept
= None
247 # Literal token without any conversion
248 self
._LiteralToken
= ''
250 # Public entry for this class
251 # @param RealValue: False: only evaluate if the expression is true or false, used for conditional expression
252 # True : return the evaluated str(value), used for PCD value
254 # @return: True or False if RealValue is False
255 # Evaluated value of string format if RealValue is True
257 def __call__(self
, RealValue
=False, Depth
=0):
263 self
._Expr
= self
._Expr
.strip()
264 if RealValue
and Depth
== 0:
265 self
._Token
= self
._Expr
266 if self
.__IsNumberToken
():
270 Token
= self
._GetToken
()
271 if type(Token
) == type('') and Token
.startswith('{') and Token
.endswith('}') and self
._Idx
>= self
._Len
:
273 except BadExpression
:
279 Val
= self
._ConExpr
()
281 if type(Val
) == type(''):
287 elif not Val
.startswith('L"') and not Val
.startswith('{'):
289 RealVal
= '"' + RealVal
+ '"'
291 # The expression has been parsed, but the end of expression is not reached
292 # It means the rest does not comply EBNF of <Expression>
293 if self
._Idx
!= self
._Len
:
294 raise BadExpression(ERR_SNYTAX
% self
._Expr
[self
._Idx
:])
297 RetVal
= str(RealVal
)
304 self
._WarnExcept
.result
= RetVal
305 raise self
._WarnExcept
309 # Template function to parse binary operators which have same precedence
310 # Expr [Operator Expr]*
311 def _ExprFuncTemplate(self
, EvalFunc
, OpLst
):
313 while self
._IsOperator
(OpLst
):
317 if self
._IsOperator
(':'):
325 Val
= self
.Eval(Op
, Val
, EvalFunc())
326 except WrnExpression
, Warn
:
327 self
._WarnExcept
= Warn
332 return self
._ExprFuncTemplate
(self
._OrExpr
, ['?', ':'])
336 return self
._ExprFuncTemplate
(self
._AndExpr
, ["OR", "or", "||"])
340 return self
._ExprFuncTemplate
(self
._BitOr
, ["AND", "and", "&&"])
344 return self
._ExprFuncTemplate
(self
._BitXor
, ["|"])
348 return self
._ExprFuncTemplate
(self
._BitAnd
, ["XOR", "xor", "^"])
352 return self
._ExprFuncTemplate
(self
._EqExpr
, ["&"])
356 Val
= self
._RelExpr
()
357 while self
._IsOperator
(["==", "!=", "EQ", "NE", "IN", "in", "!", "NOT", "not"]):
359 if Op
in ["!", "NOT", "not"]:
360 if not self
._IsOperator
(["IN", "in"]):
361 raise BadExpression(ERR_REL_NOT_IN
)
362 Op
+= ' ' + self
._Token
364 Val
= self
.Eval(Op
, Val
, self
._RelExpr
())
365 except WrnExpression
, Warn
:
366 self
._WarnExcept
= Warn
372 return self
._ExprFuncTemplate
(self
._ShiftExpr
, ["<=", ">=", "<", ">", "LE", "GE", "LT", "GT"])
374 def _ShiftExpr(self
):
375 return self
._ExprFuncTemplate
(self
._AddExpr
, ["<<", ">>"])
379 return self
._ExprFuncTemplate
(self
._MulExpr
, ["+", "-"])
383 return self
._ExprFuncTemplate
(self
._UnaryExpr
, ["*", "/", "%"])
386 def _UnaryExpr(self
):
387 if self
._IsOperator
(["!", "NOT", "not"]):
388 Val
= self
._UnaryExpr
()
390 return self
.Eval('not', Val
)
391 except WrnExpression
, Warn
:
392 self
._WarnExcept
= Warn
394 if self
._IsOperator
(["~"]):
395 Val
= self
._UnaryExpr
()
397 return self
.Eval('~', Val
)
398 except WrnExpression
, Warn
:
399 self
._WarnExcept
= Warn
401 return self
._IdenExpr
()
403 # Parse identifier or encapsulated expression
405 Tk
= self
._GetToken
()
407 Val
= self
._ConExpr
()
409 # _GetToken may also raise BadExpression
410 if self
._GetToken
() != ')':
411 raise BadExpression(ERR_MATCH
)
412 except BadExpression
:
413 raise BadExpression(ERR_MATCH
)
417 # Skip whitespace or tab
419 for Char
in self
._Expr
[self
._Idx
:]:
420 if Char
not in ' \t':
424 # Try to convert string to number
425 def __IsNumberToken(self
):
427 if self
._Token
.lower()[0:2] == '0x' and len(self
._Token
) > 2:
430 self
._Token
= int(self
._Token
, Radix
)
438 def __GetArray(self
):
441 self
.__GetNList
(True)
442 Token
+= self
._LiteralToken
443 if self
._Idx
>= self
._Len
or self
._Expr
[self
._Idx
] != '}':
444 raise BadExpression(ERR_ARRAY_TOKEN
% Token
)
447 # All whitespace and tabs in array are already stripped.
448 IsArray
= IsGuid
= False
449 if len(Token
.split(',')) == 11 and len(Token
.split(',{')) == 2 \
450 and len(Token
.split('},')) == 1:
451 HexLen
= [11,6,6,5,4,4,4,4,4,4,6]
452 HexList
= Token
.split(',')
453 if HexList
[3].startswith('{') and \
454 not [Index
for Index
, Hex
in enumerate(HexList
) if len(Hex
) > HexLen
[Index
]]:
456 if Token
.lstrip('{').rstrip('}').find('{') == -1:
457 if not [Hex
for Hex
in Token
.lstrip('{').rstrip('}').split(',') if len(Hex
) > 4]:
459 if not IsArray
and not IsGuid
:
460 raise BadExpression(ERR_ARRAY_TOKEN
% Token
)
462 self
._Token
= self
._LiteralToken
= Token
465 # Parse string, the format must be: "..."
466 def __GetString(self
):
472 # Replace escape \\\", \"
473 Expr
= self
._Expr
[self
._Idx
:].replace('\\\\', '//').replace('\\\"', '\\\'')
478 self
._Token
= self
._LiteralToken
= self
._Expr
[Idx
:self
._Idx
]
479 if not self
._Token
.endswith('"'):
480 raise BadExpression(ERR_STRING_TOKEN
% self
._Token
)
481 self
._Token
= self
._Token
[1:-1]
484 # Get token that is comprised by alphanumeric, underscore or dot(used by PCD)
485 # @param IsAlphaOp: Indicate if parsing general token or script operator(EQ, NE...)
486 def __GetIdToken(self
, IsAlphaOp
= False):
488 for Ch
in self
._Expr
[self
._Idx
:]:
489 if not self
.__IsIdChar
(Ch
) or ('?' in self
._Expr
and Ch
== ':'):
494 self
._Token
= self
._LiteralToken
= IdToken
496 self
.__ResolveToken
()
499 # Try to resolve token
500 def __ResolveToken(self
):
502 raise BadExpression(ERR_EMPTY_TOKEN
)
505 if self
.PcdPattern
.match(self
._Token
):
506 if self
._Token
not in self
._Symb
:
507 Ex
= BadExpression(ERR_PCD_RESOLVE
% self
._Token
)
510 self
._Token
= ValueExpression(self
._Symb
[self
._Token
], self
._Symb
)(True, self
._Depth
+1)
511 if type(self
._Token
) != type(''):
512 self
._LiteralToken
= hex(self
._Token
)
515 if self
._Token
.startswith('"'):
516 self
._Token
= self
._Token
[1:-1]
517 elif self
._Token
in ["FALSE", "false", "False"]:
519 elif self
._Token
in ["TRUE", "true", "True"]:
522 self
.__IsNumberToken
()
524 def __GetNList(self
, InArray
=False):
525 self
._GetSingleToken
()
526 if not self
.__IsHexLiteral
():
528 raise BadExpression(ERR_ARRAY_ELE
% self
._Token
)
532 Expr
= self
._Expr
[self
._Idx
:]
533 if not Expr
.startswith(','):
536 NList
= self
._LiteralToken
537 while Expr
.startswith(','):
541 self
._GetSingleToken
()
542 if not self
.__IsHexLiteral
():
543 raise BadExpression(ERR_ARRAY_ELE
% self
._Token
)
544 NList
+= self
._LiteralToken
546 Expr
= self
._Expr
[self
._Idx
:]
547 self
._Token
= self
._LiteralToken
= NList
550 def __IsHexLiteral(self
):
551 if self
._LiteralToken
.startswith('{') and \
552 self
._LiteralToken
.endswith('}'):
555 if self
.HexPattern
.match(self
._LiteralToken
):
556 Token
= self
._LiteralToken
[2:]
557 Token
= Token
.lstrip('0')
559 self
._LiteralToken
= '0x0'
561 self
._LiteralToken
= '0x' + Token
.lower()
566 return self
.__GetNList
()
570 return Ch
in '._:' or Ch
.isalnum()
573 def _GetSingleToken(self
):
575 Expr
= self
._Expr
[self
._Idx
:]
576 if Expr
.startswith('L"'):
579 UStr
= self
.__GetString
()
580 self
._Token
= 'L"' + UStr
+ '"'
586 Match
= self
.RegGuidPattern
.match(Expr
)
587 if Match
and not Expr
[Match
.end():Match
.end()+1].isalnum() \
588 and Expr
[Match
.end():Match
.end()+1] != '_':
589 self
._Idx
+= Match
.end()
590 self
._Token
= ValueExpression(GuidStringToGuidStructureString(Expr
[0:Match
.end()]))(True, self
._Depth
+1)
592 elif self
.__IsIdChar
(Ch
):
593 return self
.__GetIdToken
()
595 return self
.__GetString
()
597 return self
.__GetArray
()
598 elif Ch
== '(' or Ch
== ')':
603 raise BadExpression(ERR_VALID_TOKEN
% Expr
)
606 def _GetOperator(self
):
608 LegalOpLst
= ['&&', '||', '!=', '==', '>=', '<='] + self
.NonLetterOpLst
+ ['?',':']
611 Expr
= self
._Expr
[self
._Idx
:]
613 # Reach end of expression
617 # Script operator: LT, GT, LE, GE, EQ, NE, and, or, xor, not
618 if Expr
[0].isalpha():
619 return self
.__GetIdToken
(True)
621 # Start to get regular operator: +, -, <, > ...
622 if Expr
[0] not in self
.NonLetterOpLst
:
627 if Ch
in self
.NonLetterOpLst
:
628 if '!' == Ch
and OpToken
:
635 if OpToken
not in LegalOpLst
:
636 raise BadExpression(ERR_OPERATOR_UNSUPPORT
% OpToken
)
637 self
._Token
= OpToken
640 # Check if current token matches the operators given from OpList
641 def _IsOperator(self
, OpList
):
644 if self
._Token
in OpList
:
645 if self
._Token
in self
.LogicalOperators
:
646 self
._Token
= self
.LogicalOperators
[self
._Token
]
651 if __name__
== '__main__':
654 input = raw_input('Input expr: ')
658 print ValueExpression(input)(True)
659 print ValueExpression(input)(False)
660 except WrnExpression
, Ex
:
663 except Exception, Ex
: