parse_enum(__enum__, __name__=None, native_format=None, **kargs)

Parser for C-like enum syntax.

Parameters:
  • __enum__ (Union[str, Tokens]) –

    definition of the enum in C syntax

  • __name__ (Optional[str], default: None ) –

    enum name

  • native_format (Optional[str], default: None ) –

    struct module format

Returns:
  • dict( Optional[Dict[str, Any]] ) –

    the parsed definition

cstruct/c_parser.py
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
def parse_enum(
    __enum__: Union[str, Tokens],
    __name__: Optional[str] = None,
    native_format: Optional[str] = None,
    **kargs: Any,
) -> Optional[Dict[str, Any]]:
    """
    Parser for C-like enum syntax.

    Args:
        __enum__:       definition of the enum in C syntax
        __name__:       enum name
        native_format:  struct module format

    Returns:
        dict: the parsed definition
    """
    from .cenum import CEnum

    constants: Dict[str, int] = OrderedDict()

    if isinstance(__enum__, Tokens):
        tokens = __enum__
    else:
        tokens = Tokens(__enum__)

    while len(tokens):
        if tokens.get() == "}":
            tokens.pop()
            break

        name = tokens.pop()
        next_token = tokens.pop()
        if next_token in {",", "}"}:  # enum-constant without explicit value
            if len(constants) == 0:
                value = 0
            else:
                value = next(reversed(constants.values())) + 1
        elif next_token == "=":  # enum-constant with explicit value
            exp_elems = []
            next_token = tokens.pop()
            while next_token not in {",", "}"}:
                exp_elems.append(next_token)
                if len(tokens) > 0:
                    next_token = tokens.pop()
                else:
                    break

            if len(exp_elems) == 0:
                raise ParserError("enum is missing value expression")

            int_expr = " ".join(exp_elems)
            try:
                value = c_eval(int_expr)
            except (ValueError, TypeError):
                value = int(int_expr)
        else:
            raise ParserError(f"`{__enum__}` is not a valid enum expression")

        if name in constants:
            raise ParserError(f"duplicate enum name `{name}`")
        constants[name] = value

        if next_token == "}":
            break

    result = {
        "__constants__": constants,
        "__is_struct__": False,
        "__is_union__": False,
        "__is_enum__": True,
        "__name__": __name__,
        "__native_format__": native_format,
        "__cls__": CEnum,
    }
    return result

parse_struct(__struct__, __cls__, __is_union__=False, __byte_order__=None, __name__=None, **kargs)

Parser for C-like struct syntax.

Parameters:
  • __struct__ (Union[str, Tokens]) –

    definition of the struct/union in C syntax

  • __cls__ (Type[AbstractCStruct]) –

    base class (MemCStruct or CStruct)

  • __is_union__ (bool, default: False ) –

    True for union, False for struct

  • __byte_order__ (Optional[str], default: None ) –

    byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER

  • __name__ (Optional[str], default: None ) –

    struct/union name

Returns:
  • dict( Dict[str, Any] ) –

    the parsed definition

cstruct/c_parser.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
def parse_struct(
    __struct__: Union[str, Tokens],
    __cls__: Type["AbstractCStruct"],
    __is_union__: bool = False,
    __byte_order__: Optional[str] = None,
    __name__: Optional[str] = None,
    **kargs: Any,
) -> Dict[str, Any]:
    """
    Parser for C-like struct syntax.

    Args:
        __struct__:     definition of the struct/union in C syntax
        __cls__:        base class (MemCStruct or CStruct)
        __is_union__:   True for union, False for struct
        __byte_order__: byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER
        __name__:       struct/union name

    Returns:
        dict: the parsed definition
    """
    # naive C struct parsing
    from .abstract import AbstractCStruct
    from .mem_cstruct import MemCStruct

    if __cls__ is None or __cls__ == AbstractCStruct:
        __cls__ = MemCStruct
    __is_union__ = bool(__is_union__)
    fields_types: Dict[str, FieldType] = OrderedDict()
    flexible_array: bool = False
    offset: int = 0
    max_alignment: int = 0
    anonymous: int = 0
    if isinstance(__struct__, Tokens):
        tokens = __struct__
    else:
        tokens = Tokens(__struct__)
    while len(tokens):
        if tokens.get() == "}":
            tokens.pop()
            break
        # flexible array member must be the last member of such a struct
        if flexible_array:
            raise CStructException("Flexible array member must be the last member of such a struct")
        field_type = parse_type(tokens, __cls__, __byte_order__, offset)
        vname = tokens.pop()
        if vname in fields_types:
            raise ParserError(f"Duplicate member `{vname}`")
        if vname in dir(__cls__):
            raise ParserError(f"Invalid reserved member name `{vname}`")
        # parse length
        if "[" in vname:
            vname, field_type.vlen, field_type.flexible_array = parse_length(tokens, vname, 1, flexible_array)
            flexible_array = flexible_array or field_type.flexible_array
        # anonymous nested union
        if vname == ";" and field_type.ref is not None and (__is_union__ or field_type.ref.__is_union__):
            # add the anonymous struct fields to the parent
            for nested_field_name, nested_field_type in field_type.ref.__fields_types__.items():
                if nested_field_name in fields_types:
                    raise ParserError(f"Duplicate member `{nested_field_name}`")
                # set the corret offset
                nested_field_type = nested_field_type.copy()
                nested_field_type.base_offset = offset + nested_field_type.base_offset
                nested_field_type.offset = offset + nested_field_type.offset
                fields_types[nested_field_name] = nested_field_type
            vname = f"__anonymous{anonymous}"
            anonymous += 1
            tokens.push(";")
        fields_types[vname] = field_type
        # calculate the max field size (for the alignment)
        max_alignment = max(max_alignment, field_type.alignment)
        # align struct if byte order is native
        if not __is_union__:  # C struct
            field_type.align_filed_offset()
            offset = field_type.offset + field_type.vsize
        t = tokens.pop()
        if t != ";":
            raise ParserError(f"`;` expected but `{t}` found")

    if __is_union__:  # C union
        # Calculate the sizeof union as size of its largest element
        size = max([x.vsize for x in fields_types.values()])
    else:  # C struct
        # add padding to struct if byte order is native
        size = offset + calculate_padding(__byte_order__, max_alignment, offset)

    # Prepare the result
    result = {
        "__fields__": list(fields_types.keys()),
        "__fields_types__": fields_types,
        "__size__": size,
        "__is_struct__": not __is_union__,
        "__is_union__": __is_union__,
        "__is_enum__": False,
        "__byte_order__": __byte_order__,
        "__alignment__": max_alignment,
        "__name__": __name__,
        "__cls__": __cls__,
    }
    return result