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