dissect.cstruct 5.0.dev14__py3-none-any.whl → 5.0.dev15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dissect/cstruct/lexer.py +23 -10
- dissect/cstruct/parser.py +35 -19
- {dissect_cstruct-5.0.dev14.dist-info → dissect_cstruct-5.0.dev15.dist-info}/METADATA +1 -1
- {dissect_cstruct-5.0.dev14.dist-info → dissect_cstruct-5.0.dev15.dist-info}/RECORD +9 -9
- {dissect_cstruct-5.0.dev14.dist-info → dissect_cstruct-5.0.dev15.dist-info}/WHEEL +0 -0
- {dissect_cstruct-5.0.dev14.dist-info → dissect_cstruct-5.0.dev15.dist-info}/entry_points.txt +0 -0
- {dissect_cstruct-5.0.dev14.dist-info → dissect_cstruct-5.0.dev15.dist-info}/licenses/COPYRIGHT +0 -0
- {dissect_cstruct-5.0.dev14.dist-info → dissect_cstruct-5.0.dev15.dist-info}/licenses/LICENSE +0 -0
- {dissect_cstruct-5.0.dev14.dist-info → dissect_cstruct-5.0.dev15.dist-info}/top_level.txt +0 -0
dissect/cstruct/lexer.py
CHANGED
|
@@ -125,6 +125,7 @@ _SINGLE_CHARS = {
|
|
|
125
125
|
|
|
126
126
|
_RE_IDENTIFIER = re.compile(r"[a-zA-Z_.][a-zA-Z0-9_.]*")
|
|
127
127
|
_RE_WHITESPACE = re.compile(r"[ \t\r\n]+")
|
|
128
|
+
_RE_DEFINE_LITERAL = re.compile(r"""(["'])(?:\\.|(?!\1)[^\\\n])*\1""")
|
|
128
129
|
|
|
129
130
|
IDENTIFIER_TYPES = set(_C_KEYWORDS.values()) | {TokenType.IDENTIFIER}
|
|
130
131
|
|
|
@@ -292,10 +293,10 @@ class Lexer:
|
|
|
292
293
|
if peek == "*":
|
|
293
294
|
self._take(2) # Consume /*
|
|
294
295
|
end = self.data.find("*/", self._pos)
|
|
295
|
-
if end
|
|
296
|
-
self.
|
|
297
|
-
|
|
298
|
-
|
|
296
|
+
if end == -1:
|
|
297
|
+
raise self._error("unterminated comment")
|
|
298
|
+
|
|
299
|
+
self._take(end - self._pos + 2)
|
|
299
300
|
|
|
300
301
|
elif peek == "/":
|
|
301
302
|
self._take(2) # Consume //
|
|
@@ -390,6 +391,23 @@ class Lexer:
|
|
|
390
391
|
|
|
391
392
|
return result
|
|
392
393
|
|
|
394
|
+
def _read_define_value(self) -> str:
|
|
395
|
+
"""Read the value of a ``#define`` up to the end of the line."""
|
|
396
|
+
value = []
|
|
397
|
+
|
|
398
|
+
while not self.eof and (ch := self._current()) != "\n":
|
|
399
|
+
if ch == "/" and self._peek() in ("/", "*"):
|
|
400
|
+
self._skip_comment()
|
|
401
|
+
value.append(" ")
|
|
402
|
+
elif ch in ('"', "'"):
|
|
403
|
+
if not (match := _RE_DEFINE_LITERAL.match(self.data, self._pos)):
|
|
404
|
+
raise self._error("unterminated string literal")
|
|
405
|
+
value.append(self._take(match.end() - self._pos))
|
|
406
|
+
else:
|
|
407
|
+
value.append(self._read_until("\n\"'/") or self._take())
|
|
408
|
+
|
|
409
|
+
return "".join(value).strip()
|
|
410
|
+
|
|
393
411
|
def _read_preprocessor(self) -> None:
|
|
394
412
|
"""Read a preprocessor directive starting with ``#``."""
|
|
395
413
|
line = self._line
|
|
@@ -423,12 +441,7 @@ class Lexer:
|
|
|
423
441
|
raise self._error("expected identifier after '#define'", lineno=line)
|
|
424
442
|
self._emit(TokenType.IDENTIFIER, name, line)
|
|
425
443
|
|
|
426
|
-
self.
|
|
427
|
-
if self.eof or self._line != line:
|
|
428
|
-
# No value, just a simple macro definition
|
|
429
|
-
return
|
|
430
|
-
|
|
431
|
-
if (value := self._read_until("\n")).strip():
|
|
444
|
+
if value := self._read_define_value():
|
|
432
445
|
self._emit(TokenType.STRING, value, line)
|
|
433
446
|
|
|
434
447
|
elif token_type == TokenType.PP_INCLUDE:
|
dissect/cstruct/parser.py
CHANGED
|
@@ -20,6 +20,10 @@ if TYPE_CHECKING:
|
|
|
20
20
|
from dissect.cstruct.types import BaseType, Enum, Flag
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
# A string or the name of a constant in the value of a define
|
|
24
|
+
_RE_DEFINE_STRING = re.compile(r"""\s*(?:([bB]?)(["'])((?:\\.|(?!\2)[^\\\n])*)\2|([a-zA-Z_.][a-zA-Z0-9_.]*))""")
|
|
25
|
+
|
|
26
|
+
|
|
23
27
|
class Parser(TokenCursor):
|
|
24
28
|
"""Base class for definition parsers.
|
|
25
29
|
|
|
@@ -42,7 +46,7 @@ class Parser(TokenCursor):
|
|
|
42
46
|
|
|
43
47
|
def _join_line_continuations(string: str) -> str:
|
|
44
48
|
# Join lines ending with backslash
|
|
45
|
-
return re.sub(r"\\\n", "", string)
|
|
49
|
+
return re.sub(r"\\\r?\n", "", string)
|
|
46
50
|
|
|
47
51
|
|
|
48
52
|
class CStyleParser(Parser):
|
|
@@ -138,11 +142,16 @@ class CStyleParser(Parser):
|
|
|
138
142
|
elif token.type == TokenType.TYPEDEF:
|
|
139
143
|
self._parse_typedef()
|
|
140
144
|
elif token.type in (TokenType.STRUCT, TokenType.UNION):
|
|
141
|
-
self._parse_struct_or_union()
|
|
145
|
+
type_ = self._parse_struct_or_union()
|
|
142
146
|
|
|
143
147
|
# Skip variable declarations after struct/union definitions
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
has_variable = self._at(TokenType.IDENTIFIER, TokenType.STAR)
|
|
149
|
+
while has_variable:
|
|
150
|
+
_, _, bits = self._parse_field_name(type_)
|
|
151
|
+
if bits is not None:
|
|
152
|
+
raise self._error("variables cannot have bitfields")
|
|
153
|
+
|
|
154
|
+
has_variable = self._match(TokenType.COMMA)
|
|
146
155
|
|
|
147
156
|
self._expect(TokenType.SEMICOLON)
|
|
148
157
|
elif token.type in (TokenType.ENUM, TokenType.FLAG):
|
|
@@ -174,21 +183,28 @@ class CStyleParser(Parser):
|
|
|
174
183
|
value = self._take().value
|
|
175
184
|
|
|
176
185
|
if value:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
186
|
+
# Adjacent strings and constants that are strings are concatenated like in C
|
|
187
|
+
strings = []
|
|
188
|
+
pos = 0
|
|
189
|
+
if '"' in value or "'" in value:
|
|
190
|
+
while pos < len(value) and (match := _RE_DEFINE_STRING.match(value, pos)):
|
|
191
|
+
is_bytes, _, string, name = match.groups()
|
|
192
|
+
if name is None:
|
|
193
|
+
# Flatten escape sequences
|
|
194
|
+
string = string.encode().decode("unicode_escape")
|
|
195
|
+
strings.append(string.encode("latin-1") if is_bytes else string)
|
|
196
|
+
elif isinstance(constant := self.cs.consts.get(name), (str, bytes)):
|
|
197
|
+
strings.append(constant)
|
|
198
|
+
else:
|
|
199
|
+
break
|
|
200
|
+
|
|
201
|
+
pos = match.end()
|
|
202
|
+
|
|
203
|
+
if strings and pos == len(value):
|
|
204
|
+
try:
|
|
205
|
+
value = strings[0][:0].join(strings)
|
|
206
|
+
except TypeError:
|
|
207
|
+
raise self._error("cannot concatenate strings and bytes", token=token) from None
|
|
192
208
|
else:
|
|
193
209
|
try:
|
|
194
210
|
# Lazy mode, try to evaluate as a Python literal first (for simple constants)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dissect.cstruct
|
|
3
|
-
Version: 5.0.
|
|
3
|
+
Version: 5.0.dev15
|
|
4
4
|
Summary: A Dissect module implementing a parser for C-like structures: structure parsing in Python made easy
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -4,8 +4,8 @@ dissect/cstruct/compiler.py,sha256=0u58J5pB4izosBj8G4wiBI1JSAAgHFMLM63IaSG4YO4,5
|
|
|
4
4
|
dissect/cstruct/cstruct.py,sha256=mJerzPouWV29RdK5LqjGitDDMvNKcadoT2hj1zpExnM,26551
|
|
5
5
|
dissect/cstruct/exception.py,sha256=RqD08LuBkbMqa3iQ1A05DNN8XgEmQR_SUnguwe9LGo8,316
|
|
6
6
|
dissect/cstruct/expression.py,sha256=Y27-N48BomBoRFozbWhGDF_DS2lfklRwWVUbCzGnxHE,10873
|
|
7
|
-
dissect/cstruct/lexer.py,sha256=
|
|
8
|
-
dissect/cstruct/parser.py,sha256=
|
|
7
|
+
dissect/cstruct/lexer.py,sha256=FbAoUVGAoH4tvnLGBwVoEf6FAxfpz9QQXpJGzykAoc4,20237
|
|
8
|
+
dissect/cstruct/parser.py,sha256=92SCg1lldmFy4CUZjf3gWz9UJaGdYeprL1PV-JXAmY4,22122
|
|
9
9
|
dissect/cstruct/util.py,sha256=vAf-kdOkk3zprW_gD6QmQeN3VIRgQqhPS4B_ha1jEpQ,18040
|
|
10
10
|
dissect/cstruct/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
dissect/cstruct/tools/stubgen.py,sha256=MeZkLmSeNSLNcAEwlAdIGv1lKvlvK3UQ4WN85ovsxTM,7672
|
|
@@ -21,10 +21,10 @@ dissect/cstruct/types/pointer.py,sha256=FNuB9bfENWRebbKX-R6Vs6xWPMHepjvOhhomeUhr
|
|
|
21
21
|
dissect/cstruct/types/structure.py,sha256=mxDvurQ0oAjI2flykhAPlT-03280cDpwGTz0-bqbr-0,39558
|
|
22
22
|
dissect/cstruct/types/void.py,sha256=fV4hKk50uop_EKbR1nfy1cdtAzvUYrSymXsdakCyFk8,1222
|
|
23
23
|
dissect/cstruct/types/wchar.py,sha256=VrnPXsP_lsyA-1xr4D2aSIVijumMhUuZWNlDHcn12-s,3964
|
|
24
|
-
dissect_cstruct-5.0.
|
|
25
|
-
dissect_cstruct-5.0.
|
|
26
|
-
dissect_cstruct-5.0.
|
|
27
|
-
dissect_cstruct-5.0.
|
|
28
|
-
dissect_cstruct-5.0.
|
|
29
|
-
dissect_cstruct-5.0.
|
|
30
|
-
dissect_cstruct-5.0.
|
|
24
|
+
dissect_cstruct-5.0.dev15.dist-info/licenses/COPYRIGHT,sha256=H-18RXfshdH9AdHwW2eO1Xa-5s6tY5eipHh5c0whDu4,316
|
|
25
|
+
dissect_cstruct-5.0.dev15.dist-info/licenses/LICENSE,sha256=PhUqiw6jAh2KbBdVRPBq_hfAvfcTBin7nZ3CK7NQbTM,11341
|
|
26
|
+
dissect_cstruct-5.0.dev15.dist-info/METADATA,sha256=9aR35qikIsQ5Y746lVzVxt7NV1pYQ7GyzCiHbtB0BXE,9256
|
|
27
|
+
dissect_cstruct-5.0.dev15.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
28
|
+
dissect_cstruct-5.0.dev15.dist-info/entry_points.txt,sha256=z53zqZqwD2OLrAkRwrP4wTeiU9CQe7xrMly0T2c0_wQ,71
|
|
29
|
+
dissect_cstruct-5.0.dev15.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
|
30
|
+
dissect_cstruct-5.0.dev15.dist-info/RECORD,,
|
|
File without changes
|
{dissect_cstruct-5.0.dev14.dist-info → dissect_cstruct-5.0.dev15.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{dissect_cstruct-5.0.dev14.dist-info → dissect_cstruct-5.0.dev15.dist-info}/licenses/COPYRIGHT
RENAMED
|
File without changes
|
{dissect_cstruct-5.0.dev14.dist-info → dissect_cstruct-5.0.dev15.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|