Skip to content

Commit 1994571

Browse files
committed
fix struct in struct array parsing
1 parent 1b98081 commit 1994571

File tree

2 files changed

+89
-21
lines changed

2 files changed

+89
-21
lines changed

cstruct/c_parser.py

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
import re
2626
from collections import OrderedDict
27-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union
27+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, Union
2828

2929
from .base import DEFINES, ENUMS, STRUCTS, TYPEDEFS
3030
from .c_expr import c_eval
@@ -101,6 +101,30 @@ def __str__(self) -> str:
101101
return str(self.tokens)
102102

103103

104+
def parse_length(tokens: Tokens, next_token: str, vlen: int, flexible_array: bool) -> Tuple[str, int, bool]:
105+
t = next_token.split("[")
106+
if len(t) != 2:
107+
raise ParserError(f"Error parsing: `{next_token}`")
108+
next_token = t[0].strip()
109+
vlen_part = t[1]
110+
vlen_expr = []
111+
while not vlen_part.endswith("]"):
112+
vlen_expr.append(vlen_part.split("]")[0].strip())
113+
vlen_part = tokens.pop()
114+
t_vlen = vlen_part.split("]")[0].strip()
115+
vlen_expr.append(vlen_part.split("]")[0].strip())
116+
t_vlen = " ".join(vlen_expr)
117+
if not t_vlen:
118+
flexible_array = True
119+
vlen = 0
120+
else:
121+
try:
122+
vlen = c_eval(t_vlen)
123+
except (ValueError, TypeError):
124+
vlen = int(t_vlen)
125+
return next_token, vlen, flexible_array
126+
127+
104128
def parse_type(tokens: Tokens, __cls__: Type["AbstractCStruct"], byte_order: Optional[str], offset: int) -> "FieldType":
105129
if len(tokens) < 2:
106130
raise ParserError("Parsing error")
@@ -124,26 +148,7 @@ def parse_type(tokens: Tokens, __cls__: Type["AbstractCStruct"], byte_order: Opt
124148
c_type = "void *"
125149
# parse length
126150
if "[" in next_token:
127-
t = next_token.split("[")
128-
if len(t) != 2:
129-
raise ParserError(f"Error parsing: `{next_token}`")
130-
next_token = t[0].strip()
131-
vlen_part = t[1]
132-
vlen_expr = []
133-
while not vlen_part.endswith("]"):
134-
vlen_expr.append(vlen_part.split("]")[0].strip())
135-
vlen_part = tokens.pop()
136-
t_vlen = vlen_part.split("]")[0].strip()
137-
vlen_expr.append(vlen_part.split("]")[0].strip())
138-
t_vlen = " ".join(vlen_expr)
139-
if not t_vlen:
140-
flexible_array = True
141-
vlen = 0
142-
else:
143-
try:
144-
vlen = c_eval(t_vlen)
145-
except (ValueError, TypeError):
146-
vlen = int(t_vlen)
151+
next_token, vlen, flexible_array = parse_length(tokens, next_token, vlen, flexible_array)
147152
tokens.push(next_token)
148153
# resolve typedefs
149154
while c_type in TYPEDEFS:
@@ -421,6 +426,10 @@ def parse_struct(
421426
raise ParserError(f"Duplicate member `{vname}`")
422427
if vname in dir(__cls__):
423428
raise ParserError(f"Invalid reserved member name `{vname}`")
429+
# parse length
430+
if "[" in vname:
431+
vname, field_type.vlen, field_type.flexible_array = parse_length(tokens, vname, 1, flexible_array)
432+
flexible_array = flexible_array or field_type.flexible_array
424433
# anonymous nested union
425434
if vname == ";" and field_type.ref is not None and (__is_union__ or field_type.ref.__is_union__):
426435
# add the anonymous struct fields to the parent

tests/test_nested.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ class NestedUnion(cstruct.MemCStruct):
7070
"""
7171

7272

73+
class NestedStructArr(cstruct.MemCStruct):
74+
__byte_order__ = cstruct.LITTLE_ENDIAN
75+
__def__ = """
76+
struct NestedStructArr {
77+
struct b {
78+
int c;
79+
} bval[10];
80+
};
81+
"""
82+
83+
7384
class NestedAnonymousUnion(cstruct.MemCStruct):
7485
__byte_order__ = cstruct.LITTLE_ENDIAN
7586
__def__ = """
@@ -332,3 +343,51 @@ def test_nested_anonymous_struct_offset():
332343
assert o.aaa.pack() == b'3333'
333344
assert o.a_op.inspect() == "00000000 e6 07 00 00 |.... |\n"
334345
assert o.b_op.inspect() == "00000000 e6 07 00 |... |\n"
346+
347+
348+
def test_nested_struct_array():
349+
Nested = cstruct.parse(
350+
"""
351+
struct Nested {
352+
struct b {
353+
int c;
354+
} bval;
355+
int a;
356+
};
357+
""",
358+
__byte_order__=cstruct.LITTLE_ENDIAN,
359+
)
360+
assert len(Nested) == 8
361+
t = Nested()
362+
assert "a" in t.__fields_types__
363+
assert "bval" in t.__fields_types__
364+
365+
NestedArray = cstruct.parse(
366+
"""
367+
struct NestedArray {
368+
struct b {
369+
int c;
370+
} bval[2];
371+
int a;
372+
};
373+
""",
374+
__byte_order__=cstruct.LITTLE_ENDIAN,
375+
)
376+
t = NestedArray()
377+
assert "a" in t.__fields_types__
378+
assert "bval" in t.__fields_types__
379+
assert len(NestedArray) > len(Nested)
380+
t.bval[0].c = 10
381+
t.bval[1].c = 11
382+
assert t.bval[0].c == 10
383+
assert t.bval[1].c == 11
384+
assert len(t.bval) == 2
385+
386+
assert len(NestedStructArr) == 40
387+
t = NestedStructArr()
388+
assert "bval" in t.__fields_types__
389+
t.bval[0].c = 10
390+
t.bval[1].c = 11
391+
assert t.bval[0].c == 10
392+
assert t.bval[1].c == 11
393+
assert len(t.bval) == 10

0 commit comments

Comments
 (0)