]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/UPT/Library/ExpressionValidate.py
BaseTools: Refactor python print statements
[mirror_edk2.git] / BaseTools / Source / Python / UPT / Library / ExpressionValidate.py
1 ## @file
2 # This file is used to check PCD logical expression
3 #
4 # Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>
5 #
6 # This program and the accompanying materials are licensed and made available
7 # under the terms and conditions of the BSD License which accompanies this
8 # distribution. The full text of the license may be found at
9 # http://opensource.org/licenses/bsd-license.php
10 #
11 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 '''
15 ExpressionValidate
16 '''
17 from __future__ import print_function
18
19 ##
20 # Import Modules
21 #
22 import re
23 from Logger import StringTable as ST
24
25 ## IsValidBareCString
26 #
27 # Check if String is comprised by whitespace(0x20), !(0x21), 0x23 - 0x7E
28 # or '\n', '\t', '\f', '\r', '\b', '\0', '\\'
29 #
30 # @param String: string to be checked
31 #
32 def IsValidBareCString(String):
33 EscapeList = ['n', 't', 'f', 'r', 'b', '0', '\\', '"']
34 PreChar = ''
35 LastChar = ''
36 for Char in String:
37 LastChar = Char
38 if PreChar == '\\':
39 if Char not in EscapeList:
40 return False
41 if Char == '\\':
42 PreChar = ''
43 continue
44 else:
45 IntChar = ord(Char)
46 if IntChar != 0x20 and IntChar != 0x09 and IntChar != 0x21 \
47 and (IntChar < 0x23 or IntChar > 0x7e):
48 return False
49 PreChar = Char
50
51 # Last char cannot be \ if PreChar is not \
52 if LastChar == '\\' and PreChar == LastChar:
53 return False
54 return True
55
56 def _ValidateToken(Token):
57 Token = Token.strip()
58 Index = Token.find("\"")
59 if Index != -1:
60 return IsValidBareCString(Token[Index+1:-1])
61 return True
62
63 ## _ExprError
64 #
65 # @param Exception: Exception
66 #
67 class _ExprError(Exception):
68 def __init__(self, Error = ''):
69 Exception.__init__(self)
70 self.Error = Error
71
72 ## _ExprBase
73 #
74 class _ExprBase:
75 HEX_PATTERN = '[\t\s]*0[xX][a-fA-F0-9]+'
76 INT_PATTERN = '[\t\s]*[0-9]+'
77 MACRO_PATTERN = '[\t\s]*\$\(([A-Z][_A-Z0-9]*)\)'
78 PCD_PATTERN = \
79 '[\t\s]*[_a-zA-Z][a-zA-Z0-9_]*[\t\s]*\.[\t\s]*[_a-zA-Z][a-zA-Z0-9_]*'
80 QUOTED_PATTERN = '[\t\s]*L?"[^"]*"'
81 BOOL_PATTERN = '[\t\s]*(true|True|TRUE|false|False|FALSE)'
82 def __init__(self, Token):
83 self.Token = Token
84 self.Index = 0
85 self.Len = len(Token)
86
87 ## SkipWhitespace
88 #
89 def SkipWhitespace(self):
90 for Char in self.Token[self.Index:]:
91 if Char not in ' \t':
92 break
93 self.Index += 1
94
95 ## IsCurrentOp
96 #
97 # @param OpList: option list
98 #
99 def IsCurrentOp(self, OpList):
100 self.SkipWhitespace()
101 LetterOp = ["EQ", "NE", "GE", "LE", "GT", "LT", "NOT", "and", "AND",
102 "or", "OR", "XOR"]
103 OpMap = {
104 '|' : '|',
105 '&' : '&',
106 '!' : '=',
107 '>' : '=',
108 '<' : '='
109 }
110
111 for Operator in OpList:
112 if not self.Token[self.Index:].startswith(Operator):
113 continue
114
115 self.Index += len(Operator)
116 Char = self.Token[self.Index : self.Index + 1]
117
118 if (Operator in LetterOp and (Char == '_' or Char.isalnum())) \
119 or (Operator in OpMap and OpMap[Operator] == Char):
120 self.Index -= len(Operator)
121 break
122
123 return True
124
125 return False
126
127 ## _LogicalExpressionParser
128 #
129 # @param _ExprBase: _ExprBase object
130 #
131 class _LogicalExpressionParser(_ExprBase):
132 #
133 # STRINGITEM can only be logical field according to spec
134 #
135 STRINGITEM = -1
136
137 #
138 # Evaluate to True or False
139 #
140 LOGICAL = 0
141 REALLOGICAL = 2
142
143 #
144 # Just arithmetic expression
145 #
146 ARITH = 1
147
148 def __init__(self, Token):
149 _ExprBase.__init__(self, Token)
150 self.Parens = 0
151
152 def _CheckToken(self, MatchList):
153 for Match in MatchList:
154 if Match and Match.start() == 0:
155 if not _ValidateToken(
156 self.Token[self.Index:self.Index+Match.end()]
157 ):
158 return False
159
160 self.Index += Match.end()
161 if self.Token[self.Index - 1] == '"':
162 return True
163 if self.Token[self.Index:self.Index+1] == '_' or \
164 self.Token[self.Index:self.Index+1].isalnum():
165 self.Index -= Match.end()
166 return False
167
168 Token = self.Token[self.Index - Match.end():self.Index]
169 if Token.strip() in ["EQ", "NE", "GE", "LE", "GT", "LT",
170 "NOT", "and", "AND", "or", "OR", "XOR"]:
171 self.Index -= Match.end()
172 return False
173
174 return True
175
176 return False
177
178 def IsAtomicNumVal(self):
179 #
180 # Hex number
181 #
182 Match1 = re.compile(self.HEX_PATTERN).match(self.Token[self.Index:])
183
184 #
185 # Number
186 #
187 Match2 = re.compile(self.INT_PATTERN).match(self.Token[self.Index:])
188
189 #
190 # Macro
191 #
192 Match3 = re.compile(self.MACRO_PATTERN).match(self.Token[self.Index:])
193
194 #
195 # PcdName
196 #
197 Match4 = re.compile(self.PCD_PATTERN).match(self.Token[self.Index:])
198
199 return self._CheckToken([Match1, Match2, Match3, Match4])
200
201
202 def IsAtomicItem(self):
203 #
204 # Macro
205 #
206 Match1 = re.compile(self.MACRO_PATTERN).match(self.Token[self.Index:])
207
208 #
209 # PcdName
210 #
211 Match2 = re.compile(self.PCD_PATTERN).match(self.Token[self.Index:])
212
213 #
214 # Quoted string
215 #
216 Match3 = re.compile(self.QUOTED_PATTERN).\
217 match(self.Token[self.Index:].replace('\\\\', '//').\
218 replace('\\\"', '\\\''))
219
220 return self._CheckToken([Match1, Match2, Match3])
221
222 ## A || B
223 #
224 def LogicalExpression(self):
225 Ret = self.SpecNot()
226 while self.IsCurrentOp(['||', 'OR', 'or', '&&', 'AND', 'and', 'XOR', 'xor', '^']):
227 if self.Token[self.Index-1] == '|' and self.Parens <= 0:
228 raise _ExprError(ST.ERR_EXPR_OR % self.Token)
229 if Ret not in [self.ARITH, self.LOGICAL, self.REALLOGICAL, self.STRINGITEM]:
230 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
231 Ret = self.SpecNot()
232 if Ret not in [self.ARITH, self.LOGICAL, self.REALLOGICAL, self.STRINGITEM]:
233 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
234 Ret = self.REALLOGICAL
235 return Ret
236
237 def SpecNot(self):
238 if self.IsCurrentOp(["NOT", "!", "not"]):
239 return self.SpecNot()
240 return self.Rel()
241
242 ## A < B, A > B, A <= B, A >= B
243 #
244 def Rel(self):
245 Ret = self.Expr()
246 if self.IsCurrentOp(["<=", ">=", ">", "<", "GT", "LT", "GE", "LE",
247 "==", "EQ", "!=", "NE"]):
248 if Ret == self.STRINGITEM:
249 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
250 Ret = self.Expr()
251 if Ret == self.REALLOGICAL:
252 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
253 Ret = self.REALLOGICAL
254 return Ret
255
256 ## A + B, A - B
257 #
258 def Expr(self):
259 Ret = self.Factor()
260 while self.IsCurrentOp(["+", "-", "&", "|", "^", "XOR", "xor"]):
261 if self.Token[self.Index-1] == '|' and self.Parens <= 0:
262 raise _ExprError(ST.ERR_EXPR_OR)
263 if Ret == self.STRINGITEM or Ret == self.REALLOGICAL:
264 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
265 Ret = self.Factor()
266 if Ret == self.STRINGITEM or Ret == self.REALLOGICAL:
267 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
268 Ret = self.ARITH
269 return Ret
270
271 ## Factor
272 #
273 def Factor(self):
274 if self.IsCurrentOp(["("]):
275 self.Parens += 1
276 Ret = self.LogicalExpression()
277 if not self.IsCurrentOp([")"]):
278 raise _ExprError(ST.ERR_EXPR_RIGHT_PAREN % \
279 (self.Token, self.Token[self.Index:]))
280 self.Parens -= 1
281 return Ret
282
283 if self.IsAtomicItem():
284 if self.Token[self.Index - 1] == '"':
285 return self.STRINGITEM
286 return self.LOGICAL
287 elif self.IsAtomicNumVal():
288 return self.ARITH
289 else:
290 raise _ExprError(ST.ERR_EXPR_FACTOR % \
291 (self.Token[self.Index:], self.Token))
292
293 ## IsValidLogicalExpression
294 #
295 def IsValidLogicalExpression(self):
296 if self.Len == 0:
297 return False, ST.ERR_EXPRESS_EMPTY
298 try:
299 if self.LogicalExpression() not in [self.ARITH, self.LOGICAL, self.REALLOGICAL, self.STRINGITEM]:
300 return False, ST.ERR_EXPR_LOGICAL % self.Token
301 except _ExprError as XExcept:
302 return False, XExcept.Error
303 self.SkipWhitespace()
304 if self.Index != self.Len:
305 return False, (ST.ERR_EXPR_BOOLEAN % \
306 (self.Token[self.Index:], self.Token))
307 return True, ''
308
309 ## _ValidRangeExpressionParser
310 #
311 class _ValidRangeExpressionParser(_ExprBase):
312 INT_RANGE_PATTERN = '[\t\s]*[0-9]+[\t\s]*-[\t\s]*[0-9]+'
313 HEX_RANGE_PATTERN = \
314 '[\t\s]*0[xX][a-fA-F0-9]+[\t\s]*-[\t\s]*0[xX][a-fA-F0-9]+'
315 def __init__(self, Token):
316 _ExprBase.__init__(self, Token)
317 self.Parens = 0
318 self.HEX = 1
319 self.INT = 2
320 self.IsParenHappen = False
321 self.IsLogicalOpHappen = False
322
323 ## IsValidRangeExpression
324 #
325 def IsValidRangeExpression(self):
326 if self.Len == 0:
327 return False, ST.ERR_EXPR_RANGE_EMPTY
328 try:
329 if self.RangeExpression() not in [self.HEX, self.INT]:
330 return False, ST.ERR_EXPR_RANGE % self.Token
331 except _ExprError as XExcept:
332 return False, XExcept.Error
333
334 self.SkipWhitespace()
335 if self.Index != self.Len:
336 return False, (ST.ERR_EXPR_RANGE % self.Token)
337 return True, ''
338
339 ## RangeExpression
340 #
341 def RangeExpression(self):
342 Ret = self.Unary()
343 while self.IsCurrentOp(['OR', 'AND', 'and', 'or']):
344 self.IsLogicalOpHappen = True
345 if not self.IsParenHappen:
346 raise _ExprError(ST.ERR_PAREN_NOT_USED % self.Token)
347 self.IsParenHappen = False
348 Ret = self.Unary()
349
350 if self.IsCurrentOp(['XOR']):
351 Ret = self.Unary()
352
353 return Ret
354
355 ## Unary
356 #
357 def Unary(self):
358 if self.IsCurrentOp(["NOT"]):
359 return self.Unary()
360
361 return self.ValidRange()
362
363 ## ValidRange
364 #
365 def ValidRange(self):
366 Ret = -1
367 if self.IsCurrentOp(["("]):
368 self.IsLogicalOpHappen = False
369 self.IsParenHappen = True
370 self.Parens += 1
371 if self.Parens > 1:
372 raise _ExprError(ST.ERR_EXPR_RANGE_DOUBLE_PAREN_NESTED % self.Token)
373 Ret = self.RangeExpression()
374 if not self.IsCurrentOp([")"]):
375 raise _ExprError(ST.ERR_EXPR_RIGHT_PAREN % self.Token)
376 self.Parens -= 1
377 return Ret
378
379 if self.IsLogicalOpHappen:
380 raise _ExprError(ST.ERR_PAREN_NOT_USED % self.Token)
381
382 if self.IsCurrentOp(["LT", "GT", "LE", "GE", "EQ", "XOR"]):
383 IntMatch = \
384 re.compile(self.INT_PATTERN).match(self.Token[self.Index:])
385 HexMatch = \
386 re.compile(self.HEX_PATTERN).match(self.Token[self.Index:])
387 if HexMatch and HexMatch.start() == 0:
388 self.Index += HexMatch.end()
389 Ret = self.HEX
390 elif IntMatch and IntMatch.start() == 0:
391 self.Index += IntMatch.end()
392 Ret = self.INT
393 else:
394 raise _ExprError(ST.ERR_EXPR_RANGE_FACTOR % (self.Token[self.Index:], self.Token))
395 else:
396 IntRangeMatch = re.compile(
397 self.INT_RANGE_PATTERN).match(self.Token[self.Index:]
398 )
399 HexRangeMatch = re.compile(
400 self.HEX_RANGE_PATTERN).match(self.Token[self.Index:]
401 )
402 if HexRangeMatch and HexRangeMatch.start() == 0:
403 self.Index += HexRangeMatch.end()
404 Ret = self.HEX
405 elif IntRangeMatch and IntRangeMatch.start() == 0:
406 self.Index += IntRangeMatch.end()
407 Ret = self.INT
408 else:
409 raise _ExprError(ST.ERR_EXPR_RANGE % self.Token)
410
411 return Ret
412
413 ## _ValidListExpressionParser
414 #
415 class _ValidListExpressionParser(_ExprBase):
416 VALID_LIST_PATTERN = '(0[xX][0-9a-fA-F]+|[0-9]+)([\t\s]*,[\t\s]*(0[xX][0-9a-fA-F]+|[0-9]+))*'
417 def __init__(self, Token):
418 _ExprBase.__init__(self, Token)
419 self.NUM = 1
420
421 def IsValidListExpression(self):
422 if self.Len == 0:
423 return False, ST.ERR_EXPR_LIST_EMPTY
424 try:
425 if self.ListExpression() not in [self.NUM]:
426 return False, ST.ERR_EXPR_LIST % self.Token
427 except _ExprError as XExcept:
428 return False, XExcept.Error
429
430 self.SkipWhitespace()
431 if self.Index != self.Len:
432 return False, (ST.ERR_EXPR_LIST % self.Token)
433
434 return True, ''
435
436 def ListExpression(self):
437 Ret = -1
438 self.SkipWhitespace()
439 ListMatch = re.compile(self.VALID_LIST_PATTERN).match(self.Token[self.Index:])
440 if ListMatch and ListMatch.start() == 0:
441 self.Index += ListMatch.end()
442 Ret = self.NUM
443 else:
444 raise _ExprError(ST.ERR_EXPR_LIST % self.Token)
445
446 return Ret
447
448 ## _StringTestParser
449 #
450 class _StringTestParser(_ExprBase):
451 def __init__(self, Token):
452 _ExprBase.__init__(self, Token)
453
454 ## IsValidStringTest
455 #
456 def IsValidStringTest(self):
457 if self.Len == 0:
458 return False, ST.ERR_EXPR_EMPTY
459 try:
460 self.StringTest()
461 except _ExprError as XExcept:
462 return False, XExcept.Error
463 return True, ''
464
465 ## StringItem
466 #
467 def StringItem(self):
468 Match1 = re.compile(self.QUOTED_PATTERN)\
469 .match(self.Token[self.Index:].replace('\\\\', '//')\
470 .replace('\\\"', '\\\''))
471 Match2 = re.compile(self.MACRO_PATTERN).match(self.Token[self.Index:])
472 Match3 = re.compile(self.PCD_PATTERN).match(self.Token[self.Index:])
473 MatchList = [Match1, Match2, Match3]
474 for Match in MatchList:
475 if Match and Match.start() == 0:
476 if not _ValidateToken(
477 self.Token[self.Index:self.Index+Match.end()]
478 ):
479 raise _ExprError(ST.ERR_EXPR_STRING_ITEM % \
480 (self.Token, self.Token[self.Index:]))
481 self.Index += Match.end()
482 Token = self.Token[self.Index - Match.end():self.Index]
483 if Token.strip() in ["EQ", "NE"]:
484 raise _ExprError(ST.ERR_EXPR_STRING_ITEM % \
485 (self.Token, self.Token[self.Index:]))
486 return
487 else:
488 raise _ExprError(ST.ERR_EXPR_STRING_ITEM % \
489 (self.Token, self.Token[self.Index:]))
490
491 ## StringTest
492 #
493 def StringTest(self):
494 self.StringItem()
495 if not self.IsCurrentOp(["==", "EQ", "!=", "NE"]):
496 raise _ExprError(ST.ERR_EXPR_EQUALITY % \
497 (self.Token[self.Index:], self.Token))
498 self.StringItem()
499 if self.Index != self.Len:
500 raise _ExprError(ST.ERR_EXPR_BOOLEAN % \
501 (self.Token[self.Index:], self.Token))
502
503 ##
504 # Check syntax of string test
505 #
506 # @param Token: string test token
507 #
508 def IsValidStringTest(Token, Flag=False):
509 #
510 # Not do the check right now, keep the implementation for future enhancement.
511 #
512 if not Flag:
513 return True, ""
514 return _StringTestParser(Token).IsValidStringTest()
515
516
517 ##
518 # Check syntax of logical expression
519 #
520 # @param Token: expression token
521 #
522 def IsValidLogicalExpr(Token, Flag=False):
523 #
524 # Not do the check right now, keep the implementation for future enhancement.
525 #
526 if not Flag:
527 return True, ""
528 return _LogicalExpressionParser(Token).IsValidLogicalExpression()
529
530 ##
531 # Check syntax of range expression
532 #
533 # @param Token: range expression token
534 #
535 def IsValidRangeExpr(Token):
536 return _ValidRangeExpressionParser(Token).IsValidRangeExpression()
537
538 ##
539 # Check syntax of value list expression token
540 #
541 # @param Token: value list expression token
542 #
543 def IsValidListExpr(Token):
544 return _ValidListExpressionParser(Token).IsValidListExpression()
545
546 ##
547 # Check whether the feature flag expression is valid or not
548 #
549 # @param Token: feature flag expression
550 #
551 def IsValidFeatureFlagExp(Token, Flag=False):
552 #
553 # Not do the check right now, keep the implementation for future enhancement.
554 #
555 if not Flag:
556 return True, "", Token
557 else:
558 if Token in ['TRUE', 'FALSE', 'true', 'false', 'True', 'False',
559 '0x1', '0x01', '0x0', '0x00']:
560 return True, ""
561 Valid, Cause = IsValidStringTest(Token, Flag)
562 if not Valid:
563 Valid, Cause = IsValidLogicalExpr(Token, Flag)
564 if not Valid:
565 return False, Cause
566 return True, ""
567
568 if __name__ == '__main__':
569 # print IsValidRangeExpr('LT 9')
570 print(_LogicalExpressionParser('gCrownBayTokenSpaceGuid.PcdPciDevice1BridgeAddressLE0').IsValidLogicalExpression())
571
572
573