2 # This file is used to define helper class and function for DEC parser
4 # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
6 # SPDX-License-Identifier: BSD-2-Clause-Patent
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
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]*'
34 # Class to hold DEC file information
37 def __init__(self
, Filename
, FileContent2
):
38 self
.Filename
= Filename
39 self
.PackagePath
, self
.PackageFile
= os
.path
.split(Filename
)
45 self
.CurrentScope
= None
46 self
.Content
= FileContent2
48 self
.FileLines
= len(FileContent2
)
50 def GetNextLine(self
):
51 if self
.LineIndex
>= self
.FileLines
:
53 Line
= self
.Content
[self
.LineIndex
]
57 def UndoNextLine(self
):
58 if self
.LineIndex
> 0:
66 def SetNext(self
, Line
, HeadComment
, TailComment
):
68 self
.HeadComment
= HeadComment
69 self
.TailComment
= TailComment
71 def IsEndOfFile(self
):
72 return self
.LineIndex
>= self
.FileLines
79 # @param Root: Root must be absolute path
80 # @param Path: Path to be stripped
82 def StripRoot(Root
, Path
):
84 Root
= os
.path
.normpath(Root
)
85 Path
= os
.path
.normpath(Path
)
86 if not os
.path
.isabs(Root
):
88 if Path
.startswith(Root
):
89 Path
= Path
[len(Root
):]
90 if Path
and Path
[0] == os
.sep
:
97 # Split comments in a string
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
104 def CleanString(Line
, CommentCharacter
=TAB_COMMENT_SPLIT
, \
105 AllowCppStyleComment
=False):
111 # Replace EDK1's comment character
113 if AllowCppStyleComment
:
114 Line
= Line
.replace(TAB_COMMENT_EDK1_SPLIT
, CommentCharacter
)
116 # separate comments and statements
120 for Index
in range(0, len(Line
)):
121 if Line
[Index
] == '"':
122 InQuote
= not InQuote
124 if Line
[Index
] == CommentCharacter
and not InQuote
:
125 Comment
= Line
[Index
:].strip()
126 Line
= Line
[0:Index
].strip()
132 ## IsValidNumValUint8
134 # Check if Token is NumValUint8: <NumValUint8> ::= {<ShortNum>} {<UINT8>} {<Expression>}
136 # @param Token: Token to be checked
138 def IsValidNumValUint8(Token
):
142 Token
= Token
.strip()
143 if Token
.lower().startswith('0x'):
148 TokenValue
= int(Token
, Base
)
149 except BaseException
:
150 Valid
, Cause
= IsValidLogicalExpr(Token
, True)
155 if TokenValue
and (TokenValue
< 0 or TokenValue
> 0xFF):
162 # Check if Value has the format of <NumValUint8> ["," <NumValUint8>]{0,}
163 # <NumValUint8> ::= {<ShortNum>} {<UINT8>} {<Expression>}
165 # @param Value: Value to be checked
167 def IsValidNList(Value
):
168 Par
= ParserHelper(Value
)
172 Token
= Par
.GetToken(',')
173 if not IsValidNumValUint8(Token
):
185 # check Array is valid
187 # @param Array: The input Array
189 def IsValidCArray(Array
):
190 Par
= ParserHelper(Array
)
191 if not Par
.Expect('{'):
196 Token
= Par
.GetToken(',}')
198 # ShortNum, UINT8, Expression
200 if not IsValidNumValUint8(Token
):
206 elif Par
.Expect('}'):
217 # check PcdDatum is valid
219 # @param Type: The pcd Type
220 # @param Value: The pcd Value
222 def IsValidPcdDatum(Type
, Value
):
224 return False, ST
.ERR_DECPARSE_PCD_VALUE_EMPTY
227 if Type
not in ["UINT8", "UINT16", "UINT32", "UINT64", "VOID*", "BOOLEAN"]:
228 return False, ST
.ERR_DECPARSE_PCD_TYPE
230 if not ((Value
.startswith('L"') or Value
.startswith('"') and \
232 or (IsValidCArray(Value
)) or (IsValidCFormatGuid(Value
)) \
233 or (IsValidNList(Value
)) or (CheckGuidRegFormat(Value
))
235 return False, ST
.ERR_DECPARSE_PCD_VOID
% (Value
, Type
)
236 RealString
= Value
[Value
.find('"') + 1 :-1]
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']:
244 Valid
, Cause
= IsValidStringTest(Value
, True)
246 Valid
, Cause
= IsValidFeatureFlagExp(Value
, True)
250 if Value
and (Value
[0] == '-' or Value
[0] == '+'):
251 return False, ST
.ERR_DECPARSE_PCD_INT_NEGTIVE
% (Value
, Type
)
254 if Value
and not Value
.startswith('0x') \
255 and not Value
.startswith('0X'):
256 Value
= Value
.lstrip('0')
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)
274 def __init__(self
, String
, File
=''):
275 self
._String
= String
276 self
._StrLen
= len(String
)
285 self
.__SkipWhitespace
()
286 return self
._Index
>= self
._StrLen
292 def __SkipWhitespace(self
):
293 for Char
in self
._String
[self
._Index
:]:
294 if Char
not in ' \t':
300 # Expect char in string
302 # @param ExpectChar: char expected in index of string
304 def Expect(self
, ExpectChar
):
305 self
.__SkipWhitespace
()
306 for Char
in self
._String
[self
._Index
:]:
307 if Char
!= ExpectChar
:
313 # Index out of bound of String
319 # Get token until encounter StopChar, front whitespace is consumed
321 # @param StopChar: Get token until encounter char in StopChar
322 # @param StkipPair: Only can be ' or ", StopChar in SkipPair are skipped
324 def GetToken(self
, StopChar
='.,|\t ', SkipPair
='"'):
325 self
.__SkipWhitespace
()
326 PreIndex
= self
._Index
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
:
335 if Char
== '\\' and LastChar
== '\\':
339 return self
._String
[PreIndex
:self
._Index
]
343 # Assert char at current index of string is AssertChar, or will report
346 # @param AssertChar: AssertChar
347 # @param ErrorString: ErrorString
348 # @param ErrorLineNum: ErrorLineNum
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
)
357 # @param ErrorString: ErrorString
358 # @param ErrorLineNum: ErrorLineNum
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
)