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