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