1
1
#!/usr/bin/env python
2
- # Copyright (c) 2013-2019 Andrea Bonomi <[email protected] >
2
+ # Copyright (c) 2013-2025 Andrea Bonomi <[email protected] >
3
3
#
4
4
# Published under the terms of the MIT license.
5
5
#
24
24
25
25
import re
26
26
from collections import OrderedDict
27
- from typing import TYPE_CHECKING , Any , Dict , List , Optional , Tuple , Type , Union
27
+ from typing import (
28
+ TYPE_CHECKING ,
29
+ Any ,
30
+ Callable ,
31
+ Dict ,
32
+ List ,
33
+ Optional ,
34
+ Tuple ,
35
+ Type ,
36
+ Union ,
37
+ )
28
38
29
39
from .base import DEFINES , ENUMS , STRUCTS , TYPEDEFS
30
40
from .c_expr import c_eval
31
- from .exceptions import CStructException , ParserError
41
+ from .exceptions import CStructException , EvalError , ParserError
32
42
from .field import FieldType , Kind , calculate_padding
33
43
from .native_types import get_native_type
34
44
41
51
SPACES = [" " , "\t " , "\n " ]
42
52
43
53
44
- class Tokens ( object ) :
54
+ class Tokens :
45
55
def __init__ (self , text : str ) -> None :
46
56
# remove the comments
47
57
text = re .sub (r"//.*?$|/\*.*?\*/" , "" , text , flags = re .S | re .MULTILINE )
@@ -59,7 +69,7 @@ def __init__(self, text: str) -> None:
59
69
text = "\n " .join (lines )
60
70
self .tokens = self .tokenize (text )
61
71
62
- def tokenize (self , text ) -> List [str ]:
72
+ def tokenize (self , text : str ) -> List [str ]:
63
73
tokens : List [str ] = []
64
74
t : List [str ] = []
65
75
for c in text :
@@ -72,7 +82,7 @@ def tokenize(self, text) -> List[str]:
72
82
else :
73
83
t .append (c )
74
84
if t :
75
- tokens .append (t . getvalue ( ))
85
+ tokens .append ("" . join ( t ))
76
86
return tokens
77
87
78
88
def pop (self ) -> str :
@@ -101,7 +111,8 @@ def __str__(self) -> str:
101
111
return str (self .tokens )
102
112
103
113
104
- def parse_length (tokens : Tokens , next_token : str , vlen : int , flexible_array : bool ) -> Tuple [str , int , bool ]:
114
+ def parse_length (tokens : Tokens , next_token : str , flexible_array : bool ) -> Tuple [str , Union [int , Callable [[], int ]], bool ]:
115
+ # Extract t_vlen
105
116
t = next_token .split ("[" )
106
117
if len (t ) != 2 :
107
118
raise ParserError (f"Error parsing: `{ next_token } `" )
@@ -114,14 +125,19 @@ def parse_length(tokens: Tokens, next_token: str, vlen: int, flexible_array: boo
114
125
t_vlen = vlen_part .split ("]" )[0 ].strip ()
115
126
vlen_expr .append (vlen_part .split ("]" )[0 ].strip ())
116
127
t_vlen = " " .join (vlen_expr )
128
+ # Evaluate t_vlen
129
+ vlen : Union [int , Callable [[], int ]]
117
130
if not t_vlen :
131
+ # If the length expression is empty, this is a flex array
118
132
flexible_array = True
119
133
vlen = 0
120
134
else :
135
+ # Evaluate the length expression
136
+ # If the length expression is not a constant, it is evaluated at runtime
121
137
try :
122
- vlen = c_eval (t_vlen )
123
- except ( ValueError , TypeError ) :
124
- vlen = int (t_vlen )
138
+ vlen = int ( c_eval (t_vlen ) )
139
+ except EvalError :
140
+ vlen = lambda : int (c_eval ( t_vlen ) )
125
141
return next_token , vlen , flexible_array
126
142
127
143
@@ -133,7 +149,7 @@ def parse_type(tokens: Tokens, __cls__: Type["AbstractCStruct"], byte_order: Opt
133
149
if c_type in ["signed" , "unsigned" , "struct" , "union" , "enum" ] and len (tokens ) > 1 :
134
150
c_type = c_type + " " + tokens .pop ()
135
151
136
- vlen = 1
152
+ vlen : Union [ int , Callable [[], int ]] = 1
137
153
flexible_array = False
138
154
139
155
if not c_type .endswith ("{" ):
@@ -148,20 +164,21 @@ def parse_type(tokens: Tokens, __cls__: Type["AbstractCStruct"], byte_order: Opt
148
164
c_type = "void *"
149
165
# parse length
150
166
if "[" in next_token :
151
- next_token , vlen , flexible_array = parse_length (tokens , next_token , vlen , flexible_array )
167
+ next_token , vlen , flexible_array = parse_length (tokens , next_token , flexible_array )
152
168
tokens .push (next_token )
153
169
# resolve typedefs
154
170
while c_type in TYPEDEFS :
155
171
c_type = TYPEDEFS [c_type ]
156
172
157
173
# calculate fmt
174
+ ref : Union [None , Type [AbstractCEnum ], Type [AbstractCStruct ]]
158
175
if c_type .startswith ("struct " ) or c_type .startswith ("union " ): # struct/union
159
176
c_type , tail = c_type .split (" " , 1 )
160
177
kind = Kind .STRUCT if c_type == "struct" else Kind .UNION
161
178
if tokens .get () == "{" : # Named nested struct
162
179
tokens .push (tail )
163
180
tokens .push (c_type )
164
- ref : Union [ Type [ AbstractCEnum ], Type [ AbstractCStruct ]] = __cls__ .parse (tokens , __name__ = tail , __byte_order__ = byte_order )
181
+ ref = __cls__ .parse (tokens , __name__ = tail , __byte_order__ = byte_order )
165
182
elif tail == "{" : # Unnamed nested struct
166
183
tokens .push (tail )
167
184
tokens .push (c_type )
@@ -428,7 +445,7 @@ def parse_struct(
428
445
raise ParserError (f"Invalid reserved member name `{ vname } `" )
429
446
# parse length
430
447
if "[" in vname :
431
- vname , field_type .vlen , field_type .flexible_array = parse_length (tokens , vname , 1 , flexible_array )
448
+ vname , field_type .vlen_ex , field_type .flexible_array = parse_length (tokens , vname , flexible_array )
432
449
flexible_array = flexible_array or field_type .flexible_array
433
450
# anonymous nested union
434
451
if vname == ";" and field_type .ref is not None and (__is_union__ or field_type .ref .__is_union__ ):
0 commit comments