]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/UPT/Parser/DecParserMisc.py
BaseTools: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / BaseTools / Source / Python / UPT / Parser / DecParserMisc.py
CommitLineData
4234283c
LG
1## @file\r
2# This file is used to define helper class and function for DEC parser\r
3#\r
f7496d71 4# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>\r
4234283c 5#\r
2e351cbe 6# SPDX-License-Identifier: BSD-2-Clause-Patent\r
4234283c
LG
7\r
8'''\r
9DecParserMisc\r
10'''\r
11\r
12## Import modules\r
13#\r
14import os\r
15import Logger.Log as Logger\r
16from Logger.ToolError import FILE_PARSE_FAILURE\r
17from Logger import StringTable as ST\r
18from Library.DataType import TAB_COMMENT_SPLIT\r
19from Library.DataType import TAB_COMMENT_EDK1_SPLIT\r
20from Library.ExpressionValidate import IsValidBareCString\r
21from Library.ParserValidate import IsValidCFormatGuid\r
421ccda3 22from Library.ExpressionValidate import IsValidFeatureFlagExp\r
4234283c
LG
23from Library.ExpressionValidate import IsValidLogicalExpr\r
24from Library.ExpressionValidate import IsValidStringTest\r
25from Library.Misc import CheckGuidRegFormat\r
26\r
27TOOL_NAME = 'DecParser'\r
28VERSION_PATTERN = '[0-9]+(\.[0-9]+)?'\r
29CVAR_PATTERN = '[_a-zA-Z][a-zA-Z0-9_]*'\r
30PCD_TOKEN_PATTERN = '(0[xX]0*[a-fA-F0-9]{1,8})|([0-9]+)'\r
31MACRO_PATTERN = '[A-Z][_A-Z0-9]*'\r
32\r
33## FileContent\r
34# Class to hold DEC file information\r
35#\r
36class FileContent:\r
37 def __init__(self, Filename, FileContent2):\r
38 self.Filename = Filename\r
39 self.PackagePath, self.PackageFile = os.path.split(Filename)\r
40 self.LineIndex = 0\r
41 self.CurrentLine = ''\r
42 self.NextLine = ''\r
43 self.HeadComment = []\r
44 self.TailComment = []\r
45 self.CurrentScope = None\r
46 self.Content = FileContent2\r
47 self.Macros = {}\r
48 self.FileLines = len(FileContent2)\r
49\r
50 def GetNextLine(self):\r
51 if self.LineIndex >= self.FileLines:\r
52 return ''\r
53 Line = self.Content[self.LineIndex]\r
54 self.LineIndex += 1\r
55 return Line\r
56\r
57 def UndoNextLine(self):\r
58 if self.LineIndex > 0:\r
59 self.LineIndex -= 1\r
60\r
61 def ResetNext(self):\r
62 self.HeadComment = []\r
63 self.TailComment = []\r
64 self.NextLine = ''\r
65\r
66 def SetNext(self, Line, HeadComment, TailComment):\r
67 self.NextLine = Line\r
68 self.HeadComment = HeadComment\r
69 self.TailComment = TailComment\r
70\r
71 def IsEndOfFile(self):\r
72 return self.LineIndex >= self.FileLines\r
73\r
74\r
75## StripRoot\r
76#\r
77# Strip root path\r
78#\r
79# @param Root: Root must be absolute path\r
80# @param Path: Path to be stripped\r
81#\r
82def StripRoot(Root, Path):\r
83 OrigPath = Path\r
84 Root = os.path.normpath(Root)\r
85 Path = os.path.normpath(Path)\r
86 if not os.path.isabs(Root):\r
87 return OrigPath\r
88 if Path.startswith(Root):\r
89 Path = Path[len(Root):]\r
90 if Path and Path[0] == os.sep:\r
91 Path = Path[1:]\r
92 return Path\r
93 return OrigPath\r
94\r
95## CleanString\r
96#\r
97# Split comments in a string\r
98# Remove spaces\r
99#\r
100# @param Line: The string to be cleaned\r
f7496d71 101# @param CommentCharacter: Comment char, used to ignore comment content,\r
4234283c
LG
102# default is DataType.TAB_COMMENT_SPLIT\r
103#\r
104def CleanString(Line, CommentCharacter=TAB_COMMENT_SPLIT, \\r
105 AllowCppStyleComment=False):\r
106 #\r
107 # remove whitespace\r
108 #\r
109 Line = Line.strip()\r
110 #\r
111 # Replace EDK1's comment character\r
112 #\r
113 if AllowCppStyleComment:\r
114 Line = Line.replace(TAB_COMMENT_EDK1_SPLIT, CommentCharacter)\r
115 #\r
116 # separate comments and statements\r
117 #\r
118 Comment = ''\r
119 InQuote = False\r
120 for Index in range(0, len(Line)):\r
121 if Line[Index] == '"':\r
122 InQuote = not InQuote\r
123 continue\r
124 if Line[Index] == CommentCharacter and not InQuote:\r
125 Comment = Line[Index:].strip()\r
126 Line = Line[0:Index].strip()\r
127 break\r
128\r
129 return Line, Comment\r
130\r
131\r
421ccda3 132## IsValidNumValUint8\r
4234283c 133#\r
421ccda3 134# Check if Token is NumValUint8: <NumValUint8> ::= {<ShortNum>} {<UINT8>} {<Expression>}\r
4234283c
LG
135#\r
136# @param Token: Token to be checked\r
137#\r
421ccda3
HC
138def IsValidNumValUint8(Token):\r
139 Valid = True\r
140 Cause = ""\r
141 TokenValue = None\r
4234283c 142 Token = Token.strip()\r
421ccda3
HC
143 if Token.lower().startswith('0x'):\r
144 Base = 16\r
145 else:\r
146 Base = 10\r
4234283c 147 try:\r
174a9d3c 148 TokenValue = int(Token, Base)\r
4234283c 149 except BaseException:\r
421ccda3
HC
150 Valid, Cause = IsValidLogicalExpr(Token, True)\r
151 if Cause:\r
152 pass\r
153 if not Valid:\r
4234283c 154 return False\r
421ccda3
HC
155 if TokenValue and (TokenValue < 0 or TokenValue > 0xFF):\r
156 return False\r
157 else:\r
158 return True\r
4234283c
LG
159\r
160## IsValidNList\r
161#\r
421ccda3
HC
162# Check if Value has the format of <NumValUint8> ["," <NumValUint8>]{0,}\r
163# <NumValUint8> ::= {<ShortNum>} {<UINT8>} {<Expression>}\r
4234283c
LG
164#\r
165# @param Value: Value to be checked\r
166#\r
167def IsValidNList(Value):\r
168 Par = ParserHelper(Value)\r
169 if Par.End():\r
170 return False\r
171 while not Par.End():\r
421ccda3
HC
172 Token = Par.GetToken(',')\r
173 if not IsValidNumValUint8(Token):\r
4234283c
LG
174 return False\r
175 if Par.Expect(','):\r
176 if Par.End():\r
177 return False\r
178 continue\r
179 else:\r
180 break\r
181 return Par.End()\r
182\r
183## IsValidCArray\r
184#\r
185# check Array is valid\r
186#\r
187# @param Array: The input Array\r
188#\r
189def IsValidCArray(Array):\r
190 Par = ParserHelper(Array)\r
191 if not Par.Expect('{'):\r
192 return False\r
193 if Par.End():\r
194 return False\r
195 while not Par.End():\r
421ccda3 196 Token = Par.GetToken(',}')\r
4234283c 197 #\r
421ccda3 198 # ShortNum, UINT8, Expression\r
4234283c 199 #\r
421ccda3 200 if not IsValidNumValUint8(Token):\r
4234283c
LG
201 return False\r
202 if Par.Expect(','):\r
203 if Par.End():\r
204 return False\r
205 continue\r
206 elif Par.Expect('}'):\r
207 #\r
208 # End of C array\r
209 #\r
210 break\r
211 else:\r
212 return False\r
213 return Par.End()\r
214\r
215## IsValidPcdDatum\r
216#\r
217# check PcdDatum is valid\r
218#\r
219# @param Type: The pcd Type\r
220# @param Value: The pcd Value\r
221#\r
222def IsValidPcdDatum(Type, Value):\r
421ccda3
HC
223 if not Value:\r
224 return False, ST.ERR_DECPARSE_PCD_VALUE_EMPTY\r
225 Valid = True\r
226 Cause = ""\r
4234283c
LG
227 if Type not in ["UINT8", "UINT16", "UINT32", "UINT64", "VOID*", "BOOLEAN"]:\r
228 return False, ST.ERR_DECPARSE_PCD_TYPE\r
229 if Type == "VOID*":\r
230 if not ((Value.startswith('L"') or Value.startswith('"') and \\r
231 Value.endswith('"'))\r
232 or (IsValidCArray(Value)) or (IsValidCFormatGuid(Value)) \\r
233 or (IsValidNList(Value)) or (CheckGuidRegFormat(Value))\r
234 ):\r
235 return False, ST.ERR_DECPARSE_PCD_VOID % (Value, Type)\r
236 RealString = Value[Value.find('"') + 1 :-1]\r
237 if RealString:\r
238 if not IsValidBareCString(RealString):\r
239 return False, ST.ERR_DECPARSE_PCD_VOID % (Value, Type)\r
240 elif Type == 'BOOLEAN':\r
241 if Value in ['TRUE', 'FALSE', 'true', 'false', 'True', 'False',\r
242 '0x1', '0x01', '1', '0x0', '0x00', '0']:\r
243 return True, ""\r
421ccda3 244 Valid, Cause = IsValidStringTest(Value, True)\r
4234283c 245 if not Valid:\r
421ccda3 246 Valid, Cause = IsValidFeatureFlagExp(Value, True)\r
4234283c
LG
247 if not Valid:\r
248 return False, Cause\r
249 else:\r
250 if Value and (Value[0] == '-' or Value[0] == '+'):\r
251 return False, ST.ERR_DECPARSE_PCD_INT_NEGTIVE % (Value, Type)\r
252 try:\r
253 StrVal = Value\r
254 if Value and not Value.startswith('0x') \\r
255 and not Value.startswith('0X'):\r
256 Value = Value.lstrip('0')\r
257 if not Value:\r
258 return True, ""\r
174a9d3c
ZF
259 Value = int(Value, 0)\r
260 MAX_VAL_TYPE = {"BOOLEAN": 0x01, 'UINT8': 0xFF, 'UINT16': 0xFFFF, 'UINT32': 0xFFFFFFFF,\r
261 'UINT64': 0xFFFFFFFFFFFFFFFF}\r
262 if Value > MAX_VAL_TYPE[Type]:\r
4234283c
LG
263 return False, ST.ERR_DECPARSE_PCD_INT_EXCEED % (StrVal, Type)\r
264 except BaseException:\r
421ccda3
HC
265 Valid, Cause = IsValidLogicalExpr(Value, True)\r
266 if not Valid:\r
267 return False, Cause\r
f7496d71 268\r
4234283c
LG
269 return True, ""\r
270\r
271## ParserHelper\r
272#\r
273class ParserHelper:\r
274 def __init__(self, String, File=''):\r
275 self._String = String\r
276 self._StrLen = len(String)\r
277 self._Index = 0\r
278 self._File = File\r
279\r
280 ## End\r
281 #\r
282 # End\r
283 #\r
284 def End(self):\r
285 self.__SkipWhitespace()\r
286 return self._Index >= self._StrLen\r
287\r
288 ## __SkipWhitespace\r
289 #\r
290 # Skip whitespace\r
291 #\r
292 def __SkipWhitespace(self):\r
293 for Char in self._String[self._Index:]:\r
294 if Char not in ' \t':\r
295 break\r
296 self._Index += 1\r
297\r
298 ## Expect\r
299 #\r
300 # Expect char in string\r
301 #\r
302 # @param ExpectChar: char expected in index of string\r
303 #\r
304 def Expect(self, ExpectChar):\r
305 self.__SkipWhitespace()\r
306 for Char in self._String[self._Index:]:\r
307 if Char != ExpectChar:\r
308 return False\r
309 else:\r
310 self._Index += 1\r
311 return True\r
312 #\r
313 # Index out of bound of String\r
314 #\r
315 return False\r
316\r
317 ## GetToken\r
318 #\r
319 # Get token until encounter StopChar, front whitespace is consumed\r
320 #\r
321 # @param StopChar: Get token until encounter char in StopChar\r
322 # @param StkipPair: Only can be ' or ", StopChar in SkipPair are skipped\r
323 #\r
324 def GetToken(self, StopChar='.,|\t ', SkipPair='"'):\r
325 self.__SkipWhitespace()\r
326 PreIndex = self._Index\r
327 InQuote = False\r
328 LastChar = ''\r
329 for Char in self._String[self._Index:]:\r
330 if Char == SkipPair and LastChar != '\\':\r
331 InQuote = not InQuote\r
332 if Char in StopChar and not InQuote:\r
333 break\r
334 self._Index += 1\r
335 if Char == '\\' and LastChar == '\\':\r
336 LastChar = ''\r
337 else:\r
338 LastChar = Char\r
339 return self._String[PreIndex:self._Index]\r
340\r
341 ## AssertChar\r
342 #\r
f7496d71 343 # Assert char at current index of string is AssertChar, or will report\r
4234283c
LG
344 # error message\r
345 #\r
346 # @param AssertChar: AssertChar\r
347 # @param ErrorString: ErrorString\r
348 # @param ErrorLineNum: ErrorLineNum\r
349 #\r
350 def AssertChar(self, AssertChar, ErrorString, ErrorLineNum):\r
351 if not self.Expect(AssertChar):\r
352 Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._File,\r
353 Line=ErrorLineNum, ExtraData=ErrorString)\r
354\r
355 ## AssertEnd\r
356 #\r
357 # @param ErrorString: ErrorString\r
358 # @param ErrorLineNum: ErrorLineNum\r
359 #\r
360 def AssertEnd(self, ErrorString, ErrorLineNum):\r
361 self.__SkipWhitespace()\r
362 if self._Index != self._StrLen:\r
363 Logger.Error(TOOL_NAME, FILE_PARSE_FAILURE, File=self._File,\r
364 Line=ErrorLineNum, ExtraData=ErrorString)\r