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 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 != -1:
296
- self._take(end - self._pos + 2)
297
- else:
298
- self._take(len(self.data) - self._pos)
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._skip_whitespace_and_comments()
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
- while not self._at(TokenType.SEMICOLON, TokenType.EOF):
145
- self._pos += 1
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
- if value[0] in ('"', "'"):
178
- quote = value[0]
179
- if value[-1] != quote:
180
- raise self._error("unterminated string literal", token=token)
181
-
182
- # Remove the surrounding quotes and flatten escape sequences
183
- value = value[1:-1].encode().decode("unicode_escape")
184
- elif value[:2].lower() in ("b'", 'b"'):
185
- quote = value[1]
186
- if value[-1] != quote:
187
- raise self._error("unterminated bytes literal", token=token)
188
-
189
- # Remove the leading b and surrounding quotes and flatten escape sequences
190
- value = value[2:-1].encode().decode("unicode_escape")
191
- value = ast.literal_eval(f"b{value!r}")
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.dev14
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=NDUfAWgyxgRNRuVO8ExXQ51LqQXS2mv9bywVa_M7tFQ,19665
8
- dissect/cstruct/parser.py,sha256=5vz1frbenevKyn-CobKyp9_22dnEbFej_nUXQeL5W8U,21368
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.dev14.dist-info/licenses/COPYRIGHT,sha256=H-18RXfshdH9AdHwW2eO1Xa-5s6tY5eipHh5c0whDu4,316
25
- dissect_cstruct-5.0.dev14.dist-info/licenses/LICENSE,sha256=PhUqiw6jAh2KbBdVRPBq_hfAvfcTBin7nZ3CK7NQbTM,11341
26
- dissect_cstruct-5.0.dev14.dist-info/METADATA,sha256=0EhDz7fNITZQWzSYoy46-j0nObu4B1Am3-nEipozmxc,9256
27
- dissect_cstruct-5.0.dev14.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
28
- dissect_cstruct-5.0.dev14.dist-info/entry_points.txt,sha256=z53zqZqwD2OLrAkRwrP4wTeiU9CQe7xrMly0T2c0_wQ,71
29
- dissect_cstruct-5.0.dev14.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
30
- dissect_cstruct-5.0.dev14.dist-info/RECORD,,
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,,