struct-frame 0.0.29__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.
- struct_frame/__init__.py +11 -0
- struct_frame/__main__.py +8 -0
- struct_frame/base.py +85 -0
- struct_frame/c_gen.py +252 -0
- struct_frame/generate.py +607 -0
- struct_frame/gql_gen.py +207 -0
- struct_frame/py_gen.py +213 -0
- struct_frame/ts_gen.py +260 -0
- struct_frame-0.0.29.dist-info/METADATA +311 -0
- struct_frame-0.0.29.dist-info/RECORD +12 -0
- struct_frame-0.0.29.dist-info/WHEEL +4 -0
- struct_frame-0.0.29.dist-info/licenses/LICENSE +21 -0
struct_frame/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .base import version, NamingStyleC, CamelToSnakeCase, pascalCase
|
|
2
|
+
|
|
3
|
+
from .c_gen import FileCGen
|
|
4
|
+
from .ts_gen import FileTsGen
|
|
5
|
+
from .py_gen import FilePyGen
|
|
6
|
+
from .gql_gen import FileGqlGen
|
|
7
|
+
|
|
8
|
+
from .generate import main
|
|
9
|
+
|
|
10
|
+
__all__ = ["main", "FileCGen", "FileTsGen", "FilePyGen", "FileGqlGen", "version",
|
|
11
|
+
"NamingStyleC", "CamelToSnakeCase", "pascalCase"]
|
struct_frame/__main__.py
ADDED
struct_frame/base.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
version = "0.0.1"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NamingStyle:
|
|
8
|
+
def enum_name(self, name):
|
|
9
|
+
return "_%s" % (name)
|
|
10
|
+
|
|
11
|
+
def struct_name(self, name):
|
|
12
|
+
return "_%s" % (name)
|
|
13
|
+
|
|
14
|
+
def union_name(self, name):
|
|
15
|
+
return "_%s" % (name)
|
|
16
|
+
|
|
17
|
+
def type_name(self, name):
|
|
18
|
+
return "%s" % (name)
|
|
19
|
+
|
|
20
|
+
def define_name(self, name):
|
|
21
|
+
return "%s" % (name)
|
|
22
|
+
|
|
23
|
+
def var_name(self, name):
|
|
24
|
+
return "%s" % (name)
|
|
25
|
+
|
|
26
|
+
def enum_entry(self, name):
|
|
27
|
+
return "%s" % (name)
|
|
28
|
+
|
|
29
|
+
def func_name(self, name):
|
|
30
|
+
return "%s" % (name)
|
|
31
|
+
|
|
32
|
+
def bytes_type(self, struct_name, name):
|
|
33
|
+
return "%s_%s_t" % (struct_name, name)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class NamingStyleC(NamingStyle):
|
|
37
|
+
def enum_name(self, name):
|
|
38
|
+
return self.underscore(name)
|
|
39
|
+
|
|
40
|
+
def struct_name(self, name):
|
|
41
|
+
return self.underscore(name)
|
|
42
|
+
|
|
43
|
+
def union_name(self, name):
|
|
44
|
+
return self.underscore(name)
|
|
45
|
+
|
|
46
|
+
def type_name(self, name):
|
|
47
|
+
return "%s_t" % self.underscore(name)
|
|
48
|
+
|
|
49
|
+
def define_name(self, name):
|
|
50
|
+
return self.underscore(name).upper()
|
|
51
|
+
|
|
52
|
+
def var_name(self, name):
|
|
53
|
+
return self.underscore(name)
|
|
54
|
+
|
|
55
|
+
def enum_entry(self, name):
|
|
56
|
+
return name.upper()
|
|
57
|
+
|
|
58
|
+
def func_name(self, name):
|
|
59
|
+
return self.underscore(name)
|
|
60
|
+
|
|
61
|
+
def bytes_type(self, struct_name, name):
|
|
62
|
+
return "%s_%s_t" % (self.underscore(struct_name), self.underscore(name))
|
|
63
|
+
|
|
64
|
+
def underscore(self, word):
|
|
65
|
+
word = str(word)
|
|
66
|
+
word = re.sub(r"([A-Z]+)([A-Z][a-z])", r'\1_\2', word)
|
|
67
|
+
word = re.sub(r"([a-z\d])([A-Z])", r'\1_\2', word)
|
|
68
|
+
word = word.replace("-", "_")
|
|
69
|
+
return word.lower()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def camelCase(st):
|
|
73
|
+
output = ''.join(x for x in st.title() if x.isalnum())
|
|
74
|
+
return output[0].lower() + output[1:]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def pascalCase(st):
|
|
78
|
+
return ''.join(x for x in st.title() if x.isalnum())
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
pattern = re.compile(r'(?<!^)(?=[A-Z])')
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def CamelToSnakeCase(data):
|
|
85
|
+
return pattern.sub('_', data).lower()
|
struct_frame/c_gen.py
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# kate: replace-tabs on; indent-width 4;
|
|
3
|
+
|
|
4
|
+
from struct_frame import version, NamingStyleC, CamelToSnakeCase, pascalCase
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
StyleC = NamingStyleC()
|
|
8
|
+
|
|
9
|
+
c_types = {"uint8": "uint8_t",
|
|
10
|
+
"int8": "int8_t",
|
|
11
|
+
"uint16": "uint16_t",
|
|
12
|
+
"int16": "int16_t",
|
|
13
|
+
"uint32": "uint32_t",
|
|
14
|
+
"int32": "int32_t",
|
|
15
|
+
"bool": "bool",
|
|
16
|
+
"float": "float",
|
|
17
|
+
"double": "double",
|
|
18
|
+
"uint64": 'uint64_t',
|
|
19
|
+
"int64": 'int64_t',
|
|
20
|
+
"string": "char", # Add string type support
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class EnumCGen():
|
|
25
|
+
@staticmethod
|
|
26
|
+
def generate(field):
|
|
27
|
+
leading_comment = field.comments
|
|
28
|
+
|
|
29
|
+
result = ''
|
|
30
|
+
if leading_comment:
|
|
31
|
+
for c in leading_comment:
|
|
32
|
+
result = '%s\n' % c
|
|
33
|
+
|
|
34
|
+
enumName = '%s%s' % (pascalCase(field.package), field.name)
|
|
35
|
+
result += 'typedef enum %s' % (enumName)
|
|
36
|
+
|
|
37
|
+
result += ' {\n'
|
|
38
|
+
|
|
39
|
+
enum_length = len(field.data)
|
|
40
|
+
enum_values = []
|
|
41
|
+
for index, (d) in enumerate(field.data):
|
|
42
|
+
leading_comment = field.data[d][1]
|
|
43
|
+
|
|
44
|
+
if leading_comment:
|
|
45
|
+
for c in leading_comment:
|
|
46
|
+
enum_values.append(c)
|
|
47
|
+
|
|
48
|
+
comma = ","
|
|
49
|
+
if index == enum_length - 1:
|
|
50
|
+
# last enum member should not end with a comma
|
|
51
|
+
comma = ""
|
|
52
|
+
|
|
53
|
+
enum_value = " %s_%s = %d%s" % (CamelToSnakeCase(
|
|
54
|
+
field.name).upper(), StyleC.enum_entry(d), field.data[d][0], comma)
|
|
55
|
+
|
|
56
|
+
enum_values.append(enum_value)
|
|
57
|
+
|
|
58
|
+
result += '\n'.join(enum_values)
|
|
59
|
+
result += '\n}'
|
|
60
|
+
|
|
61
|
+
result += ' %s;\n' % (enumName)
|
|
62
|
+
|
|
63
|
+
result += 'typedef uint8_t %s_t;' % (enumName)
|
|
64
|
+
|
|
65
|
+
# Add module-prefixed enum constants for compatibility
|
|
66
|
+
result += '\n\n/* Enum constants with module prefix */\n'
|
|
67
|
+
module_prefix = CamelToSnakeCase(field.package).upper()
|
|
68
|
+
for d in field.data:
|
|
69
|
+
# Use the already correct enum constant name
|
|
70
|
+
enum_constant = f"{CamelToSnakeCase(field.name).upper()}_{StyleC.enum_entry(d)}"
|
|
71
|
+
module_constant = f"{module_prefix}_{enum_constant}"
|
|
72
|
+
result += f'#define {module_constant:<35} {enum_constant}\n'
|
|
73
|
+
|
|
74
|
+
return result
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class FieldCGen():
|
|
78
|
+
@staticmethod
|
|
79
|
+
def generate(field):
|
|
80
|
+
result = ''
|
|
81
|
+
var_name = field.name
|
|
82
|
+
type_name = field.fieldType
|
|
83
|
+
|
|
84
|
+
# Handle basic type resolution
|
|
85
|
+
if type_name in c_types:
|
|
86
|
+
base_type = c_types[type_name]
|
|
87
|
+
else:
|
|
88
|
+
if field.isEnum:
|
|
89
|
+
base_type = '%s%s_t' % (pascalCase(field.package), type_name)
|
|
90
|
+
else:
|
|
91
|
+
base_type = '%s%s' % (pascalCase(field.package), type_name)
|
|
92
|
+
|
|
93
|
+
# Handle arrays
|
|
94
|
+
if field.is_array:
|
|
95
|
+
if field.fieldType == "string":
|
|
96
|
+
# String arrays need both array size and individual string size
|
|
97
|
+
if field.size_option is not None:
|
|
98
|
+
# Fixed string array: size_option strings, each element_size chars
|
|
99
|
+
declaration = f"char {var_name}[{field.size_option}][{field.element_size}];"
|
|
100
|
+
comment = f" // Fixed string array: {field.size_option} strings, each max {field.element_size} chars"
|
|
101
|
+
elif field.max_size is not None:
|
|
102
|
+
# Variable string array: count byte + max_size strings of element_size chars each
|
|
103
|
+
declaration = f"struct {{ uint8_t count; char data[{field.max_size}][{field.element_size}]; }} {var_name};"
|
|
104
|
+
comment = f" // Variable string array: up to {field.max_size} strings, each max {field.element_size} chars"
|
|
105
|
+
else:
|
|
106
|
+
declaration = f"char {var_name}[1][1];" # Fallback
|
|
107
|
+
comment = " // String array (error in size specification)"
|
|
108
|
+
else:
|
|
109
|
+
# Non-string arrays
|
|
110
|
+
if field.size_option is not None:
|
|
111
|
+
# Fixed array: always exact size
|
|
112
|
+
declaration = f"{base_type} {var_name}[{field.size_option}];"
|
|
113
|
+
comment = f" // Fixed array: always {field.size_option} elements"
|
|
114
|
+
elif field.max_size is not None:
|
|
115
|
+
# Variable array: count byte + max elements
|
|
116
|
+
declaration = f"struct {{ uint8_t count; {base_type} data[{field.max_size}]; }} {var_name};"
|
|
117
|
+
comment = f" // Variable array: up to {field.max_size} elements"
|
|
118
|
+
else:
|
|
119
|
+
declaration = f"{base_type} {var_name}[1];" # Fallback
|
|
120
|
+
comment = " // Array (error in size specification)"
|
|
121
|
+
|
|
122
|
+
result += f" {declaration}{comment}"
|
|
123
|
+
|
|
124
|
+
# Handle regular strings
|
|
125
|
+
elif field.fieldType == "string":
|
|
126
|
+
if field.size_option is not None:
|
|
127
|
+
# Fixed string: exactly size_option characters
|
|
128
|
+
declaration = f"char {var_name}[{field.size_option}];"
|
|
129
|
+
comment = f" // Fixed string: exactly {field.size_option} chars"
|
|
130
|
+
elif field.max_size is not None:
|
|
131
|
+
# Variable string: length byte + max characters
|
|
132
|
+
declaration = f"struct {{ uint8_t length; char data[{field.max_size}]; }} {var_name};"
|
|
133
|
+
comment = f" // Variable string: up to {field.max_size} chars"
|
|
134
|
+
else:
|
|
135
|
+
declaration = f"char {var_name}[1];" # Fallback
|
|
136
|
+
comment = " // String (error in size specification)"
|
|
137
|
+
|
|
138
|
+
result += f" {declaration}{comment}"
|
|
139
|
+
|
|
140
|
+
# Handle regular fields
|
|
141
|
+
else:
|
|
142
|
+
result += f" {base_type} {var_name};"
|
|
143
|
+
|
|
144
|
+
# Add leading comments
|
|
145
|
+
leading_comment = field.comments
|
|
146
|
+
if leading_comment:
|
|
147
|
+
for c in leading_comment:
|
|
148
|
+
result = c + "\n" + result
|
|
149
|
+
|
|
150
|
+
return result
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class MessageCGen():
|
|
154
|
+
@staticmethod
|
|
155
|
+
def generate(msg):
|
|
156
|
+
leading_comment = msg.comments
|
|
157
|
+
|
|
158
|
+
result = ''
|
|
159
|
+
if leading_comment:
|
|
160
|
+
for c in msg.comments:
|
|
161
|
+
result = '%s\n' % c
|
|
162
|
+
|
|
163
|
+
structName = '%s%s' % (pascalCase(msg.package), msg.name)
|
|
164
|
+
result += 'typedef struct %s {' % structName
|
|
165
|
+
|
|
166
|
+
result += '\n'
|
|
167
|
+
|
|
168
|
+
size = 1
|
|
169
|
+
if not msg.fields:
|
|
170
|
+
# Empty structs are not allowed in C standard.
|
|
171
|
+
# Therefore add a dummy field if an empty message occurs.
|
|
172
|
+
result += ' char dummy_field;'
|
|
173
|
+
else:
|
|
174
|
+
size = msg.size
|
|
175
|
+
|
|
176
|
+
result += '\n'.join([FieldCGen.generate(f)
|
|
177
|
+
for key, f in msg.fields.items()])
|
|
178
|
+
result += '\n}'
|
|
179
|
+
result += ' %s;\n\n' % structName
|
|
180
|
+
|
|
181
|
+
defineName = '%s_%s' % (CamelToSnakeCase(
|
|
182
|
+
msg.package).upper(), CamelToSnakeCase(msg.name).upper())
|
|
183
|
+
result += '#define %s_MAX_SIZE %d\n' % (defineName, size)
|
|
184
|
+
|
|
185
|
+
if msg.id:
|
|
186
|
+
result += '#define %s_MSG_ID %d\n' % (defineName, msg.id)
|
|
187
|
+
|
|
188
|
+
funcName = defineName.lower()
|
|
189
|
+
if msg.id:
|
|
190
|
+
result += 'MESSAGE_HELPER(%s, %s, %d, %d);\n\n' % (funcName, structName,
|
|
191
|
+
size, msg.id)
|
|
192
|
+
|
|
193
|
+
return result + '\n'
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def get_initializer(msg, null_init):
|
|
197
|
+
if not msg.fields:
|
|
198
|
+
return '{0}'
|
|
199
|
+
|
|
200
|
+
parts = []
|
|
201
|
+
for field in msg.fields:
|
|
202
|
+
parts.append(field.get_initializer(null_init))
|
|
203
|
+
return '{' + ', '.join(parts) + '}'
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class FileCGen():
|
|
207
|
+
@staticmethod
|
|
208
|
+
def generate(package):
|
|
209
|
+
yield '/* Automatically generated struct frame header */\n'
|
|
210
|
+
yield '/* Generated by %s at %s. */\n\n' % (version, time.asctime())
|
|
211
|
+
|
|
212
|
+
yield '#pragma once\n'
|
|
213
|
+
yield '#pragma pack(1)\n'
|
|
214
|
+
|
|
215
|
+
yield '#include "struct_frame.h"\n'
|
|
216
|
+
|
|
217
|
+
# include additional header files if available in the future
|
|
218
|
+
|
|
219
|
+
if package.enums:
|
|
220
|
+
yield '/* Enum definitions */\n'
|
|
221
|
+
for key, enum in package.enums.items():
|
|
222
|
+
yield EnumCGen.generate(enum) + '\n\n'
|
|
223
|
+
|
|
224
|
+
if package.messages:
|
|
225
|
+
yield '/* Struct definitions */\n'
|
|
226
|
+
# Need to sort messages to make sure dependecies are properly met
|
|
227
|
+
|
|
228
|
+
for key, msg in package.sortedMessages().items():
|
|
229
|
+
yield MessageCGen.generate(msg) + '\n'
|
|
230
|
+
yield '\n'
|
|
231
|
+
|
|
232
|
+
# Add default initializers if needed
|
|
233
|
+
# if package.messages:
|
|
234
|
+
# yield '/* Initializer values for message structs */\n'
|
|
235
|
+
# for key, msg in package.messages.items():
|
|
236
|
+
# identifier = '%s_%s_init_default' % (package.name, StyleC.struct_name(msg.name))
|
|
237
|
+
# yield '#define %-40s %s\n' % (identifier, MessageCGen.get_initializer(msg, False))
|
|
238
|
+
# for key, msg in package.messages.items():
|
|
239
|
+
# identifier = '%s_%s_init_zero' % (package.name, StyleC.struct_name(msg.name))
|
|
240
|
+
# yield '#define %-40s %s\n' % (identifier, msg.get_initializer(True))
|
|
241
|
+
# yield '\n'
|
|
242
|
+
|
|
243
|
+
if package.messages:
|
|
244
|
+
yield 'bool get_message_length(size_t msg_id, size_t* size){\n switch (msg_id)\n {\n'
|
|
245
|
+
for key, msg in package.sortedMessages().items():
|
|
246
|
+
name = '%s_%s' % (CamelToSnakeCase(
|
|
247
|
+
msg.package).upper(), CamelToSnakeCase(msg.name).upper())
|
|
248
|
+
if msg.id:
|
|
249
|
+
yield ' case %s_MSG_ID: *size = %s_MAX_SIZE; return true;\n' % (name, name)
|
|
250
|
+
|
|
251
|
+
yield ' default: break;\n } return false;\n}'
|
|
252
|
+
yield '\n'
|