]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/UPT/Library/ExpressionValidate.py
Fix the typo for the structure definition of EFI_ADAPTER_INFO_NETWORK_BOOT in Adapter...
[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, 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 for Operator in OpList:
110 if not self.Token[self.Index:].startswith(Operator):
111 continue
112 self.Index += len(Operator)
113 Char = self.Token[self.Index : self.Index + 1]
114 if (Operator in LetterOp and (Char == '_' or Char.isalnum())) \
115 or (Operator in OpMap and OpMap[Operator] == Char):
116 self.Index -= len(Operator)
117 break
118 return True
119 return False
120
121 ## _LogicalExpressionParser
122 #
123 # @param _ExprBase: _ExprBase object
124 #
125 class _LogicalExpressionParser(_ExprBase):
126 #
127 # STRINGITEM can only be logical field according to spec
128 #
129 STRINGITEM = -1
130
131 #
132 # Evaluate to True or False
133 #
134 LOGICAL = 0
135 REALLOGICAL = 2
136
137 #
138 # Just arithmetic expression
139 #
140 ARITH = 1
141
142 def __init__(self, Token):
143 _ExprBase.__init__(self, Token)
144 self.Parens = 0
145
146 def _CheckToken(self, MatchList):
147 for Match in MatchList:
148 if Match and Match.start() == 0:
149 if not _ValidateToken(
150 self.Token[self.Index:self.Index+Match.end()]
151 ):
152 return False
153
154 self.Index += Match.end()
155 if self.Token[self.Index - 1] == '"':
156 return True
157 if self.Token[self.Index:self.Index+1] == '_' or \
158 self.Token[self.Index:self.Index+1].isalnum():
159 self.Index -= Match.end()
160 return False
161
162 Token = self.Token[self.Index - Match.end():self.Index]
163 if Token.strip() in ["EQ", "NE", "GE", "LE", "GT", "LT",
164 "NOT", "and", "AND", "or", "OR", "XOR"]:
165 self.Index -= Match.end()
166 return False
167
168 return True
169 return False
170
171 def IsAtomicNumVal(self):
172 #
173 # Hex number
174 #
175 Match1 = re.compile(self.HEX_PATTERN).match(self.Token[self.Index:])
176
177 #
178 # Number
179 #
180 Match2 = re.compile(self.INT_PATTERN).match(self.Token[self.Index:])
181
182 #
183 # Macro
184 #
185 Match3 = re.compile(self.MACRO_PATTERN).match(self.Token[self.Index:])
186
187 #
188 # PcdName
189 #
190 Match4 = re.compile(self.PCD_PATTERN).match(self.Token[self.Index:])
191
192 return self._CheckToken([Match1, Match2, Match3, Match4])
193
194
195 def IsAtomicItem(self):
196 #
197 # Macro
198 #
199 Match1 = re.compile(self.MACRO_PATTERN).match(self.Token[self.Index:])
200
201 #
202 # PcdName
203 #
204 Match2 = re.compile(self.PCD_PATTERN).match(self.Token[self.Index:])
205
206 #
207 # Quoted string
208 #
209 Match3 = re.compile(self.QUOTED_PATTERN).\
210 match(self.Token[self.Index:].replace('\\\\', '//').\
211 replace('\\\"', '\\\''))
212
213 return self._CheckToken([Match1, Match2, Match3])
214
215 ## A || B
216 #
217 def LogicalExpression(self):
218 Ret = self.SpecNot()
219 while self.IsCurrentOp(['||', 'OR', 'or', '&&', 'AND', 'and', 'XOR']):
220 if self.Token[self.Index-1] == '|' and self.Parens <= 0:
221 raise _ExprError(ST.ERR_EXPR_OR)
222 if Ret == self.ARITH:
223 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
224 Ret = self.SpecNot()
225 if Ret == self.ARITH:
226 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
227 Ret = self.REALLOGICAL
228 return Ret
229
230 def SpecNot(self):
231 if self.IsCurrentOp(["NOT", "!"]):
232 return self.SpecNot()
233 return self.Rel()
234
235 ## A < B, A > B, A <= B, A >= b
236 #
237 def Rel(self):
238 Ret = self.Expr()
239 if self.IsCurrentOp(["<=", ">=", ">", "<", "GT", "LT", "GE", "LE",
240 "==", "EQ", "!=", "NE"]):
241 if Ret == self.STRINGITEM or Ret == self.REALLOGICAL:
242 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
243 Ret = self.Expr()
244 if Ret == self.STRINGITEM or Ret == self.REALLOGICAL:
245 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
246 Ret = self.REALLOGICAL
247 return Ret
248
249 ## A + B, A - B
250 #
251 def Expr(self):
252 Ret = self.Factor()
253 while self.IsCurrentOp(["+", "-", "&", "|", "^"]):
254 if self.Token[self.Index-1] == '|' and self.Parens <= 0:
255 raise _ExprError(ST.ERR_EXPR_OR)
256 if Ret == self.STRINGITEM or Ret == self.REALLOGICAL:
257 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
258 Ret = self.Factor()
259 if Ret == self.STRINGITEM or Ret == self.REALLOGICAL:
260 raise _ExprError(ST.ERR_EXPR_LOGICAL % self.Token)
261 Ret = self.ARITH
262 return Ret
263
264 ## Factor
265 #
266 def Factor(self):
267 if self.IsCurrentOp(["("]):
268 self.Parens += 1
269 Ret = self.LogicalExpression()
270 if not self.IsCurrentOp([")"]):
271 raise _ExprError(ST.ERR_EXPR_RIGHT_PAREN % \
272 (self.Token, self.Token[self.Index:]))
273 self.Parens -= 1
274 return Ret
275
276 if self.IsAtomicItem():
277 if self.Token[self.Index - 1] == '"':
278 return self.STRINGITEM
279 return self.LOGICAL
280 elif self.IsAtomicNumVal():
281 return self.ARITH
282 else:
283 raise _ExprError(ST.ERR_EXPR_FACTOR % \
284 (self.Token, self.Token[self.Index:]))
285
286 ## IsValidLogicalExpression
287 #
288 def IsValidLogicalExpression(self):
289 if self.Len == 0:
290 return False, ST.ERR_EXPR_EMPTY
291 try:
292 if self.LogicalExpression() == self.ARITH:
293 return False, ST.ERR_EXPR_LOGICAL % self.Token
294 except _ExprError, XExcept:
295 return False, XExcept.Error
296 self.SkipWhitespace()
297 if self.Index != self.Len:
298 return False, (ST.ERR_EXPR_BOOLEAN % \
299 (self.Token[self.Index:], self.Token))
300 return True, ''
301
302 ## _ValidRangeExpressionParser
303 #
304 class _ValidRangeExpressionParser(_ExprBase):
305 INT_RANGE_PATTERN = '[\t\s]*[0-9]+[\t\s]*-[\t\s]*[0-9]+'
306 HEX_RANGE_PATTERN = \
307 '[\t\s]*0[xX][a-fA-F0-9]+[\t\s]*-[\t\s]*0[xX][a-fA-F0-9]+'
308 def __init__(self, Token):
309 _ExprBase.__init__(self, Token)
310
311 ## IsValidRangeExpression
312 #
313 def IsValidRangeExpression(self):
314 if self.Len == 0:
315 return False
316 try:
317 self.RangeExpression()
318 except _ExprError:
319 return False
320 self.SkipWhitespace()
321 if self.Index != self.Len:
322 return False
323 return True
324
325 ## RangeExpression
326 #
327 def RangeExpression(self):
328 self.Unary()
329 while self.IsCurrentOp(['OR', 'AND', 'XOR']):
330 self.Unary()
331
332 ## Unary
333 #
334 def Unary(self):
335 if self.IsCurrentOp(["NOT", "-"]):
336 return self.Unary()
337 return self.ValidRange()
338
339 ## ValidRange
340 #
341 def ValidRange(self):
342 if self.IsCurrentOp(["("]):
343 self.RangeExpression()
344 if not self.IsCurrentOp([")"]):
345 raise _ExprError('')
346 return
347
348 if self.IsCurrentOp(["LT", "GT", "LE", "GE", "EQ"]):
349 IntMatch = \
350 re.compile(self.INT_PATTERN).match(self.Token[self.Index:])
351 HexMatch = \
352 re.compile(self.HEX_PATTERN).match(self.Token[self.Index:])
353 if HexMatch and HexMatch.start() == 0:
354 self.Index += HexMatch.end()
355 elif IntMatch and IntMatch.start() == 0:
356 self.Index += IntMatch.end()
357 else:
358 raise _ExprError('')
359 else:
360 IntRangeMatch = re.compile(
361 self.INT_RANGE_PATTERN).match(self.Token[self.Index:]
362 )
363 HexRangeMatch = re.compile(
364 self.HEX_RANGE_PATTERN).match(self.Token[self.Index:]
365 )
366 if HexRangeMatch and HexRangeMatch.start() == 0:
367 self.Index += HexRangeMatch.end()
368 elif IntRangeMatch and IntRangeMatch.start() == 0:
369 self.Index += IntRangeMatch.end()
370 else:
371 raise _ExprError('')
372
373 if self.Token[self.Index:self.Index+1] == '_' or \
374 self.Token[self.Index:self.Index+1].isalnum():
375 raise _ExprError('')
376
377 ## _StringTestParser
378 #
379 class _StringTestParser(_ExprBase):
380 def __init__(self, Token):
381 _ExprBase.__init__(self, Token)
382
383 ## IsValidStringTest
384 #
385 def IsValidStringTest(self):
386 if self.Len == 0:
387 return False, ST.ERR_EXPR_EMPTY
388 try:
389 self.StringTest()
390 except _ExprError, XExcept:
391 return False, XExcept.Error
392 return True, ''
393
394 ## StringItem
395 #
396 def StringItem(self):
397 Match1 = re.compile(self.QUOTED_PATTERN)\
398 .match(self.Token[self.Index:].replace('\\\\', '//')\
399 .replace('\\\"', '\\\''))
400 Match2 = re.compile(self.MACRO_PATTERN).match(self.Token[self.Index:])
401 Match3 = re.compile(self.PCD_PATTERN).match(self.Token[self.Index:])
402 MatchList = [Match1, Match2, Match3]
403 for Match in MatchList:
404 if Match and Match.start() == 0:
405 if not _ValidateToken(
406 self.Token[self.Index:self.Index+Match.end()]
407 ):
408 raise _ExprError(ST.ERR_EXPR_STRING_ITEM % \
409 (self.Token, self.Token[self.Index:]))
410 self.Index += Match.end()
411 Token = self.Token[self.Index - Match.end():self.Index]
412 if Token.strip() in ["EQ", "NE"]:
413 raise _ExprError(ST.ERR_EXPR_STRING_ITEM % \
414 (self.Token, self.Token[self.Index:]))
415 return
416 else:
417 raise _ExprError(ST.ERR_EXPR_STRING_ITEM % \
418 (self.Token, self.Token[self.Index:]))
419
420 ## StringTest
421 #
422 def StringTest(self):
423 self.StringItem()
424 if not self.IsCurrentOp(["==", "EQ", "!=", "NE"]):
425 raise _ExprError(ST.ERR_EXPR_EQUALITY % \
426 (self.Token, self.Token[self.Index:]))
427 self.StringItem()
428 if self.Index != self.Len:
429 raise _ExprError(ST.ERR_EXPR_BOOLEAN % \
430 (self.Token[self.Index:], self.Token))
431
432 ##
433 # Check syntax of logical expression
434 #
435 # @param Token: expression token
436 #
437 def IsValidLogicalExpr(Token, Flag=False):
438 #
439 # Not do the check right now, keep the implementation for future enhancement.
440 #
441 if not Flag:
442 return True, ""
443 return _LogicalExpressionParser(Token).IsValidLogicalExpression()
444
445 ##
446 # Check syntax of string test
447 #
448 # @param Token: string test token
449 #
450 def IsValidStringTest(Token, Flag=False):
451 #
452 # Not do the check right now, keep the implementation for future enhancement.
453 #
454 if not Flag:
455 return True, ""
456 return _StringTestParser(Token).IsValidStringTest()
457
458 ##
459 # Check syntax of range expression
460 #
461 # @param Token: range expression token
462 #
463 def IsValidRangeExpr(Token):
464 return _ValidRangeExpressionParser(Token).IsValidRangeExpression()
465
466 ##
467 # Check whether the feature flag expression is valid or not
468 #
469 # @param Token: feature flag expression
470 #
471 def IsValidFeatureFlagExp(Token, Flag=False):
472 #
473 # Not do the check right now, keep the implementation for future enhancement.
474 #
475 if not Flag:
476 return True, "", Token
477 else:
478 if Token in ['TRUE', 'FALSE', 'true', 'false', 'True', 'False',
479 '0x1', '0x01', '0x0', '0x00']:
480 return True, ""
481 Valid, Cause = IsValidStringTest(Token, Flag)
482 if not Valid:
483 Valid, Cause = IsValidLogicalExpr(Token, Flag)
484 if not Valid:
485 return False, Cause
486 return True, ""
487
488 if __name__ == '__main__':
489 print _LogicalExpressionParser('a ^ b > a + b').IsValidLogicalExpression()