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 # 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
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.
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
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]*'
40 # Class to hold DEC file information
43 def __init__(self
, Filename
, FileContent2
):
44 self
.Filename
= Filename
45 self
.PackagePath
, self
.PackageFile
= os
.path
.split(Filename
)
51 self
.CurrentScope
= None
52 self
.Content
= FileContent2
54 self
.FileLines
= len(FileContent2
)
56 def GetNextLine(self
):
57 if self
.LineIndex
>= self
.FileLines
:
59 Line
= self
.Content
[self
.LineIndex
]
63 def UndoNextLine(self
):
64 if self
.LineIndex
> 0:
72 def SetNext(self
, Line
, HeadComment
, TailComment
):
74 self
.HeadComment
= HeadComment
75 self
.TailComment
= TailComment
77 def IsEndOfFile(self
):
78 return self
.LineIndex
>= self
.FileLines
85 # @param Root: Root must be absolute path
86 # @param Path: Path to be stripped
88 def StripRoot(Root
, Path
):
90 Root
= os
.path
.normpath(Root
)
91 Path
= os
.path
.normpath(Path
)
92 if not os
.path
.isabs(Root
):
94 if Path
.startswith(Root
):
95 Path
= Path
[len(Root
):]
96 if Path
and Path
[0] == os
.sep
:
103 # Split comments in a string
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
110 def CleanString(Line
, CommentCharacter
=TAB_COMMENT_SPLIT
, \
111 AllowCppStyleComment
=False):
117 # Replace EDK1's comment character
119 if AllowCppStyleComment
:
120 Line
= Line
.replace(TAB_COMMENT_EDK1_SPLIT
, CommentCharacter
)
122 # separate comments and statements
126 for Index
in range(0, len(Line
)):
127 if Line
[Index
] == '"':
128 InQuote
= not InQuote
130 if Line
[Index
] == CommentCharacter
and not InQuote
:
131 Comment
= Line
[Index
:].strip()
132 Line
= Line
[0:Index
].strip()
138 ## IsValidNumValUint8
140 # Check if Token is NumValUint8: <NumValUint8> ::= {<ShortNum>} {<UINT8>} {<Expression>}
142 # @param Token: Token to be checked
144 def IsValidNumValUint8(Token
):
148 Token
= Token
.strip()
149 if Token
.lower().startswith('0x'):
154 TokenValue
= int(Token
, Base
)
155 except BaseException
:
156 Valid
, Cause
= IsValidLogicalExpr(Token
, True)
161 if TokenValue
and (TokenValue
< 0 or TokenValue
> 0xFF):
168 # Check if Value has the format of <NumValUint8> ["," <NumValUint8>]{0,}
169 # <NumValUint8> ::= {<ShortNum>} {<UINT8>} {<Expression>}
171 # @param Value: Value to be checked
173 def IsValidNList(Value
):
174 Par
= ParserHelper(Value
)
178 Token
= Par
.GetToken(',')
179 if not IsValidNumValUint8(Token
):
191 # check Array is valid
193 # @param Array: The input Array
195 def IsValidCArray(Array
):
196 Par
= ParserHelper(Array
)
197 if not Par
.Expect('{'):
202 Token
= Par
.GetToken(',}')
204 # ShortNum, UINT8, Expression
206 if not IsValidNumValUint8(Token
):
212 elif Par
.Expect('}'):
223 # check PcdDatum is valid
225 # @param Type: The pcd Type
226 # @param Value: The pcd Value
228 def IsValidPcdDatum(Type
, Value
):
230 return False, ST
.ERR_DECPARSE_PCD_VALUE_EMPTY
233 if Type
not in ["UINT8", "UINT16", "UINT32", "UINT64", "VOID*", "BOOLEAN"]:
234 return False, ST
.ERR_DECPARSE_PCD_TYPE
236 if not ((Value
.startswith('L"') or Value
.startswith('"') and \
238 or (IsValidCArray(Value
)) or (IsValidCFormatGuid(Value
)) \
239 or (IsValidNList(Value
)) or (CheckGuidRegFormat(Value
))
241 return False, ST
.ERR_DECPARSE_PCD_VOID
% (Value
, Type
)
242 RealString
= Value
[Value
.find('"') + 1 :-1]
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']:
250 Valid
, Cause
= IsValidStringTest(Value
, True)
252 Valid
, Cause
= IsValidFeatureFlagExp(Value
, True)
256 if Value
and (Value
[0] == '-' or Value
[0] == '+'):
257 return False, ST
.ERR_DECPARSE_PCD_INT_NEGTIVE
% (Value
, Type
)
260 if Value
and not Value
.startswith('0x') \
261 and not Value
.startswith('0X'):
262 Value
= Value
.lstrip('0')
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)
280 def __init__(self
, String
, File
=''):
281 self
._String
= String
282 self
._StrLen
= len(String
)
291 self
.__SkipWhitespace
()
292 return self
._Index
>= self
._StrLen
298 def __SkipWhitespace(self
):
299 for Char
in self
._String
[self
._Index
:]:
300 if Char
not in ' \t':
306 # Expect char in string
308 # @param ExpectChar: char expected in index of string
310 def Expect(self
, ExpectChar
):
311 self
.__SkipWhitespace
()
312 for Char
in self
._String
[self
._Index
:]:
313 if Char
!= ExpectChar
:
319 # Index out of bound of String
325 # Get token until encounter StopChar, front whitespace is consumed
327 # @param StopChar: Get token until encounter char in StopChar
328 # @param StkipPair: Only can be ' or ", StopChar in SkipPair are skipped
330 def GetToken(self
, StopChar
='.,|\t ', SkipPair
='"'):
331 self
.__SkipWhitespace
()
332 PreIndex
= self
._Index
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
:
341 if Char
== '\\' and LastChar
== '\\':
345 return self
._String
[PreIndex
:self
._Index
]
349 # Assert char at current index of string is AssertChar, or will report
352 # @param AssertChar: AssertChar
353 # @param ErrorString: ErrorString
354 # @param ErrorLineNum: ErrorLineNum
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
)
363 # @param ErrorString: ErrorString
364 # @param ErrorLineNum: ErrorLineNum
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
)