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