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