Skip to content

Commit 7963a3b

Browse files
committed
support enum with explicit type (C++11)
1 parent c68c9a4 commit 7963a3b

5 files changed

Lines changed: 58 additions & 18 deletions

File tree

cstruct/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ def parse(
182182
if __cls__ is None:
183183
__cls__ = MemCStruct
184184
cls_def = parse_struct_def(__struct__, __cls__=__cls__, process_muliple_definition=True, **kargs)
185+
print('!!!!!', cls_def, kargs)
185186
if cls_def is None:
186187
return None
187188
return cls_def['__cls__'].parse(cls_def, **kargs)

cstruct/abstract.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@
3030
import hashlib
3131
from io import StringIO
3232
from enum import IntEnum, EnumMeta, _EnumDict
33-
from .base import STRUCTS, ENUMS, DEFAULT_ENUM_SIZE
33+
import struct
34+
from .base import STRUCTS, ENUMS, DEFAULT_ENUM_SIZE, ENUM_SIZE_TO_C_TYPE
3435
from .c_parser import parse_struct, parse_struct_def, parse_enum_def, parse_enum, Tokens
3536
from .field import calculate_padding, FieldType
36-
from .exceptions import CStructException
37+
from .native_types import get_native_type
38+
from .exceptions import CStructException, ParserError
3739

3840
__all__ = ['CStructMeta', 'AbstractCStruct', 'CEnumMeta', 'AbstractCEnum']
3941

@@ -315,11 +317,19 @@ def __prepare__(metacls, cls, bases, **kwds):
315317

316318
def __new__(metacls: Type["CEnumMeta"], cls: str, bases: Tuple[Type, ...], classdict: _EnumDict, **kwds: Any) -> "CEnumMeta":
317319
inst = super().__new__(metacls, cls, bases, classdict, **kwds)
318-
319320
if len(inst) > 0:
320-
if "__size__" not in classdict:
321+
if classdict.get("__native_format__"): # data type specified
322+
inst.__size__ = struct.calcsize(classdict["__native_format__"])
323+
elif "__size__" in classdict: # size specified
324+
try:
325+
inst.__native_format__ = get_native_type(ENUM_SIZE_TO_C_TYPE[inst.__size__]).native_format
326+
except KeyError:
327+
raise ParserError(f"Enum has invalid size. Needs to be in {ENUM_SIZE_TO_C_TYPE.keys()}")
328+
else: # default
321329
inst.__size__ = DEFAULT_ENUM_SIZE
330+
inst.__native_format__ = get_native_type(ENUM_SIZE_TO_C_TYPE[inst.__size__]).native_format
322331
print(f"Warning: __size__ not specified for enum {cls}. Will default to {DEFAULT_ENUM_SIZE} bytes")
332+
323333
if not classdict.get("__anonymous__", False):
324334
ENUMS[cls] = inst
325335
return inst
@@ -341,6 +351,7 @@ def parse(
341351
__enum__: Union[str, Tokens, Dict[str, Any]],
342352
__name__: Optional[str] = None,
343353
__size__: Optional[int] = None,
354+
__native_format__: Optional[str] = None,
344355
**kargs: Dict[str, Any],
345356
) -> Type["AbstractCEnum"]:
346357
"""
@@ -350,14 +361,16 @@ def parse(
350361
__enum__: Definition of the enum in C syntax
351362
__name__: Name of the new Enum. If empty, a name based on the __enum__ hash is generated
352363
__size__: Number of bytes that the enum should be read as
364+
__native_format__: struct module format
353365
354366
Returns:
355367
cls: A new class mapping the definition
356368
"""
357-
358369
cls_kargs: Dict[str, Any] = dict(kargs)
359370
if __size__ is not None:
360371
cls_kargs['__size__'] = __size__
372+
if __native_format__ is not None:
373+
cls_kargs['__native_format__'] = __native_format__
361374

362375
if isinstance(__enum__, (str, Tokens)):
363376
cls_kargs.update(parse_enum_def(__enum__, __cls__=cls, **cls_kargs))

cstruct/c_parser.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from .field import calculate_padding, Kind, FieldType
3030
from .c_expr import c_eval
3131
from .exceptions import CStructException, ParserError
32+
from .native_types import get_native_type
3233

3334
if TYPE_CHECKING:
3435
from .abstract import AbstractCStruct, AbstractCEnum
@@ -58,6 +59,16 @@ def __init__(self, text: str) -> None:
5859
def pop(self) -> str:
5960
return self.tokens.pop(0)
6061

62+
def pop_c_type(self) -> str:
63+
c_type = self.pop()
64+
if c_type in ['signed', 'unsigned'] and len(self) > 1:
65+
# short int, long int, or long long
66+
c_type = c_type + " " + self.pop()
67+
elif c_type in ['short', 'long'] and len(self) > 1 and self.get() in ['int', 'long']:
68+
# short int, long int, or long long
69+
c_type = c_type + " " + self.pop()
70+
return c_type
71+
6172
def get(self) -> str:
6273
return self.tokens[0]
6374

@@ -203,11 +214,16 @@ def parse_struct_def(
203214
if result:
204215
result['__cls__'].parse(result, **kargs)
205216
name = tokens.pop()
217+
native_format = None
218+
if tokens.get() == ':': # enumeration type declaration
219+
tokens.pop() # pop ":"
220+
type_ = get_native_type(tokens.pop_c_type())
221+
native_format = type_.native_format
206222
if tokens.get() == '{': # named enum
207223
tokens.pop() # pop "{"
208-
result = parse_enum(tokens, __name__=name)
224+
result = parse_enum(tokens, __name__=name, native_format=native_format)
209225
elif name == '{': # unnamed enum
210-
result = parse_enum(tokens)
226+
result = parse_enum(tokens, native_format=native_format)
211227
else:
212228
raise ParserError(f"{name} definition expected")
213229

@@ -244,9 +260,14 @@ def parse_enum_def(__def__: Union[str, Tokens], **kargs: Any) -> Optional[Dict[s
244260
raise ParserError(f"enum expected - {kind}")
245261

246262
name = tokens.pop()
263+
native_format = None
264+
if tokens.get() == ':': # enumeration type declaration
265+
tokens.pop() # pop ":"
266+
type_ = get_native_type(tokens.pop_c_type())
267+
native_format = type_.native_format
247268
if tokens.get() == '{': # named enum
248269
tokens.pop() # pop "{"
249-
return parse_enum(tokens, __name__=name)
270+
return parse_enum(tokens, __name__=name, native_format=native_format)
250271
elif name == '{': # unnamed enum
251272
return parse_enum(tokens)
252273
else:
@@ -256,6 +277,7 @@ def parse_enum_def(__def__: Union[str, Tokens], **kargs: Any) -> Optional[Dict[s
256277
def parse_enum(
257278
__enum__: Union[str, Tokens],
258279
__name__: Optional[str] = None,
280+
native_format: Optional[str] = None,
259281
**kargs: Any,
260282
) -> Optional[Dict[str, Any]]:
261283
"""
@@ -264,6 +286,7 @@ def parse_enum(
264286
Args:
265287
__enum__: definition of the enum in C syntax
266288
__name__: enum name
289+
native_format: struct module format
267290
268291
Returns:
269292
dict: the parsed definition
@@ -283,7 +306,6 @@ def parse_enum(
283306
break
284307

285308
name = tokens.pop()
286-
287309
next_token = tokens.pop()
288310
if next_token in {",", "}"}: # enum-constant without explicit value
289311
if len(constants) == 0:
@@ -324,6 +346,7 @@ def parse_enum(
324346
'__is_union__': False,
325347
'__is_enum__': True,
326348
'__name__': __name__,
349+
'__native_format__': native_format,
327350
'__cls__': CEnum,
328351
}
329352
return result

cstruct/field.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import struct
2727
from enum import Enum
2828
from typing import Optional, Any, List, Type, TYPE_CHECKING
29-
from .base import NATIVE_ORDER, ENUM_SIZE_TO_C_TYPE
29+
from .base import NATIVE_ORDER
3030
from .native_types import get_native_type
3131
from .exceptions import ParserError
3232

@@ -193,10 +193,7 @@ def native_format(self) -> str:
193193
except KeyError:
194194
raise ParserError(f"Unknow type `{self.c_type}`")
195195
elif self.is_enum:
196-
try:
197-
return get_native_type(ENUM_SIZE_TO_C_TYPE[self.ref.size]).native_format
198-
except KeyError:
199-
raise ParserError(f"Enum has invalid size. Needs to be in {ENUM_SIZE_TO_C_TYPE.keys()}")
196+
return self.ref.__native_format__
200197
else:
201198
return 'c'
202199

tests/test_cenum.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ class HtmlFont(CEnum):
2929
"""
3030

3131

32+
class EnumWithType(CEnum):
33+
__def__ = """
34+
enum EnumWithType : int { a, b, c, d};
35+
"""
36+
37+
3238
class StructWithEnum(cstruct.MemCStruct):
3339
__byte_order__ = cstruct.LITTLE_ENDIAN
3440
__def__ = """
@@ -94,7 +100,7 @@ def test_sizeof():
94100
assert cstruct.sizeof("enum HtmlFont") == 2
95101

96102

97-
# def test_type():
98-
# color = cstruct.parse("enum Color : short { red, green, blue };")
99-
# assert color.__size__ == 2
100-
# assert cstruct.sizeof("enum Color") == 2
103+
def test_type():
104+
color = cstruct.parse("enum Color : unsigned short { red, green, blue };")
105+
assert color.__size__ == 2
106+
assert cstruct.sizeof("enum Color") == 2

0 commit comments

Comments
 (0)