statable 3.1.0__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.
Files changed (117) hide show
  1. codegen/__init__.py +38 -0
  2. codegen/c_code_generator.py +1752 -0
  3. codegen/code_merger.py +371 -0
  4. codegen/code_templates.py +509 -0
  5. codegen/config.py +167 -0
  6. codegen/enum_generator.py +377 -0
  7. codegen/event_queue_generator.py +303 -0
  8. codegen/interrupt_generator.py +345 -0
  9. codegen/naming_convention.py +171 -0
  10. codegen/osal_generator.py +282 -0
  11. codegen/role_function_generator.py +1244 -0
  12. codegen/sample_data.py +269 -0
  13. codegen/state_actions_generator.py +395 -0
  14. codegen/struct_generator.py +721 -0
  15. codegen/timer_generator.py +299 -0
  16. codegen/transition_generator.py +907 -0
  17. codegen/type_mapper.py +69 -0
  18. codegen/validate/__init__.py +38 -0
  19. codegen/validate/change_actions.py +90 -0
  20. codegen/validate/change_applier.py +416 -0
  21. codegen/validate/clipboard_manager.py +47 -0
  22. codegen/validate/data/__init__.py +30 -0
  23. codegen/validate/data/action_definitions.py +200 -0
  24. codegen/validate/data/keywords.py +46 -0
  25. codegen/validate/data/prompt_templates.py +176 -0
  26. codegen/validate/data/validation_rules.py +205 -0
  27. codegen/validate/items/__init__.py +30 -0
  28. codegen/validate/items/base_validator.py +25 -0
  29. codegen/validate/items/cell_validator.py +275 -0
  30. codegen/validate/items/custom_type_validator.py +75 -0
  31. codegen/validate/items/event_validator.py +78 -0
  32. codegen/validate/items/flag_validator.py +74 -0
  33. codegen/validate/items/interrupt_validator.py +75 -0
  34. codegen/validate/items/queue_validator.py +73 -0
  35. codegen/validate/items/role_function_validator.py +175 -0
  36. codegen/validate/items/state_validator.py +88 -0
  37. codegen/validate/items/timer_validator.py +120 -0
  38. codegen/validate/items/transition_validator.py +113 -0
  39. codegen/validate/items/variable_validator.py +132 -0
  40. codegen/validate/logger.py +52 -0
  41. codegen/validate/models.py +177 -0
  42. codegen/validate/prompt_generator.py +724 -0
  43. codegen/validate/response_parser.py +154 -0
  44. codegen/validate/response_validator.py +275 -0
  45. codegen/validate/validation_dialog.py +322 -0
  46. codegen/validate/validator.py +94 -0
  47. codegen/variable_generator.py +419 -0
  48. statable/__init__.py +71 -0
  49. statable/__main__.py +49 -0
  50. statable/cli.py +251 -0
  51. statable/global_defs.py +256 -0
  52. statable/mermaid_gen.py +144 -0
  53. statable/model.py +426 -0
  54. statable/parser.py +64 -0
  55. statable/sample_data.py +435 -0
  56. statable/shared/__init__.py +24 -0
  57. statable/shared/condition_library.py +63 -0
  58. statable/shared/literal_library.py +66 -0
  59. statable/shared/role_function_library.py +150 -0
  60. statable/state_machine.py +121 -0
  61. statable/xml_io.py +1164 -0
  62. statable-3.1.0.dist-info/METADATA +151 -0
  63. statable-3.1.0.dist-info/RECORD +117 -0
  64. statable-3.1.0.dist-info/WHEEL +5 -0
  65. statable-3.1.0.dist-info/entry_points.txt +2 -0
  66. statable-3.1.0.dist-info/top_level.txt +3 -0
  67. statable_gui/__init__.py +23 -0
  68. statable_gui/action_edit_dialog.py +214 -0
  69. statable_gui/code_generation_dialog.py +486 -0
  70. statable_gui/code_generation_settings_dialog.py +522 -0
  71. statable_gui/common_widgets.py +537 -0
  72. statable_gui/condition_builder_dialog.py +500 -0
  73. statable_gui/condition_edit_dialog.py +123 -0
  74. statable_gui/condition_template_dialog.py +58 -0
  75. statable_gui/config.py +21 -0
  76. statable_gui/dialogs.py +439 -0
  77. statable_gui/event_definition_dialog.py +605 -0
  78. statable_gui/event_delivery_settings_dialog.py +191 -0
  79. statable_gui/event_queue_dialog.py +245 -0
  80. statable_gui/global_defs.py +167 -0
  81. statable_gui/global_defs_dialog.py +796 -0
  82. statable_gui/i18n/__init__.py +118 -0
  83. statable_gui/interrupt_handler_edit_dialog.py +810 -0
  84. statable_gui/layer_settings_dialog.py +180 -0
  85. statable_gui/libcntrl/__init__.py +23 -0
  86. statable_gui/libcntrl/condition_library.py +13 -0
  87. statable_gui/libcntrl/literal_library.py +13 -0
  88. statable_gui/libcntrl/literal_management_dialog.py +185 -0
  89. statable_gui/libcntrl/role_function_edit_dialog.py +145 -0
  90. statable_gui/libcntrl/role_function_library.py +13 -0
  91. statable_gui/literal_definition_dialog.py +62 -0
  92. statable_gui/logger.py +83 -0
  93. statable_gui/main_window.py +1380 -0
  94. statable_gui/matrix_table.py +413 -0
  95. statable_gui/preference_keys.py +18 -0
  96. statable_gui/preferences.py +74 -0
  97. statable_gui/role_function_dialog.py +244 -0
  98. statable_gui/sample_data.py +11 -0
  99. statable_gui/state_actions_dialog.py +398 -0
  100. statable_gui/symbol_picker.py +170 -0
  101. statable_gui/traceball.py +37 -0
  102. statable_gui/transition_editor_direct/__init__.py +15 -0
  103. statable_gui/transition_editor_direct/actions_tab.py +856 -0
  104. statable_gui/transition_editor_direct/code_widget.py +344 -0
  105. statable_gui/transition_editor_direct/coverage_analyzer.py +163 -0
  106. statable_gui/transition_editor_direct/dialog.py +335 -0
  107. statable_gui/transition_editor_direct/draft.py +270 -0
  108. statable_gui/transition_editor_direct/edit_dialogs.py +160 -0
  109. statable_gui/transition_editor_direct/overview_tab.py +173 -0
  110. statable_gui/transition_editor_direct/palette_widget.py +233 -0
  111. statable_gui/transition_editor_direct/relations_edit_dialog.py +156 -0
  112. statable_gui/transition_editor_direct/relations_tab.py +230 -0
  113. statable_gui/transition_editor_direct/system_global_dialog.py +52 -0
  114. statable_gui/transition_editor_direct/transition_actions_dialog.py +109 -0
  115. statable_gui/transition_editor_direct/transitions_tab.py +620 -0
  116. statable_gui/validation_dialog.py +285 -0
  117. statable_gui/widgets.py +1315 -0
@@ -0,0 +1,419 @@
1
+ # codegen/variable_generator.py
2
+ """
3
+ Variable and flag generation module (multi-layer state machine support)
4
+
5
+ [v1.5 fix]
6
+ - _generate_normal_init initializes custom struct variables with memset
7
+ (v1.4 used `ctx->data.system_status = 0;` which caused compile errors)
8
+
9
+ [v1.6 sec 9.8 #92 fix]
10
+ - Fix access macro field name
11
+ DATA_COUNTER(ctx) ((ctx)->data.COUNTER) -> bug
12
+ DATA_COUNTER(ctx) ((ctx)->data.counter) -> fixed
13
+ Macro name is uppercase (to_upper_snake); field name uses sanitize_identifier.
14
+
15
+ [v2.0 fix]
16
+ - Correctly recognize standard types with `_t` suffix (uint32_t / uint8_t, etc.)
17
+ Old: DEFAULT_INIT_VALUES had 'uint32' but not 'uint32_t'
18
+ -> standard types were treated as custom types, generating redundant memset
19
+ New: added _t-suffixed types to DEFAULT_INIT_VALUES
20
+ _normalize_type_for_init strips volatile / const qualifiers before lookup
21
+ """
22
+
23
+ import sys
24
+ import os
25
+ import logging
26
+ from string import Template
27
+ from typing import Dict, Callable, List, Any, Optional
28
+
29
+ _this_dir = os.path.dirname(os.path.abspath(__file__))
30
+ _parent_dir = os.path.dirname(_this_dir)
31
+ for _p in (_this_dir, _parent_dir):
32
+ if _p not in sys.path:
33
+ sys.path.insert(0, _p)
34
+
35
+ from statable.global_defs import SystemVariable, EventFlag, GlobalDefinitions
36
+
37
+ try:
38
+ from .type_mapper import CTypeMapper
39
+ from .naming_convention import CNamingConvention
40
+ from .code_templates import CodeTemplates
41
+ except ImportError:
42
+ from type_mapper import CTypeMapper
43
+ from naming_convention import CNamingConvention
44
+ from code_templates import CodeTemplates
45
+
46
+ logger = logging.getLogger(__name__)
47
+
48
+
49
+ class VariableGenerator:
50
+ """Variable and flag generation class (multi-layer state machine support)"""
51
+
52
+ INIT_TEMPLATES = {
53
+ 'comment': (
54
+ '/**\n'
55
+ ' * @brief System context initialization\n'
56
+ ' * @param ctx System context pointer\n'
57
+ ' */\n'
58
+ ),
59
+ 'signature': Template(
60
+ 'void $func_name(SystemContext_t *ctx)\n'
61
+ ),
62
+ 'function_open': '{\n',
63
+ 'null_check': Template(
64
+ ' /* NULL check */\n'
65
+ ' if (ctx == NULL) {\n'
66
+ ' $log_error("NULL pointer: ctx");\n'
67
+ ' return;\n'
68
+ ' }\n'
69
+ ),
70
+ 'entry_log': Template(
71
+ ' $log_debug("Enter $func_name");\n'
72
+ ),
73
+ 'variables_comment': (
74
+ ' /* Initialize global variables */\n'
75
+ ),
76
+ 'flags_comment': (
77
+ ' /* Initialize event flags */\n'
78
+ ),
79
+ 'pending_event_comment': (
80
+ ' /* Initialize pending event */\n'
81
+ ),
82
+ 'pending_event_init': (
83
+ ' ctx->pending_event = 0;\n'
84
+ ' ctx->pending_event_valid = false;\n'
85
+ ),
86
+ 'exit_log': Template(
87
+ ' $log_debug("Exit $func_name");\n'
88
+ ),
89
+ 'function_close': '}\n',
90
+ }
91
+
92
+ INIT_FUNCTION_STEPS = [
93
+ {'action': 'template', 'key': 'comment'},
94
+ {'action': 'template', 'key': 'signature',
95
+ 'format': {'func_name': '{func_name}'}},
96
+ {'action': 'template', 'key': 'function_open'},
97
+ {'action': 'template', 'key': 'null_check',
98
+ 'format': {'log_error': '{log_error}'}},
99
+ {'action': 'blank'},
100
+ {'action': 'template', 'key': 'entry_log',
101
+ 'format': {'func_name': '{func_name}', 'log_debug': '{log_debug}'}},
102
+ {'action': 'blank'},
103
+ {'action': 'template', 'key': 'variables_comment'},
104
+ {'action': 'loop', 'source': 'variables', 'generator': 'init'},
105
+ {'action': 'blank'},
106
+ {'action': 'template', 'key': 'flags_comment'},
107
+ {'action': 'loop', 'source': 'flags', 'generator': 'init'},
108
+ {'action': 'blank'},
109
+ # [F-3 Step 2] pending_event init removed: per-layer
110
+ # slots are reset by SystemContext_InitQueues().
111
+ {'action': 'blank'},
112
+ {'action': 'template', 'key': 'exit_log',
113
+ 'format': {'func_name': '{func_name}', 'log_debug': '{log_debug}'}},
114
+ {'action': 'template', 'key': 'function_close'},
115
+ ]
116
+
117
+ # ================================================================
118
+ # v2.0 fix: add standard types with _t suffix
119
+ # Consistent with type_mapper.py TYPE_MAPPING:
120
+ # 'uint32' -> 'uint32_t' is converted, so
121
+ # generated code must treat 'uint32_t' as a standard type
122
+ # ================================================================
123
+ DEFAULT_INIT_VALUES = {
124
+ # ---- Signed integers ----
125
+ 'int': '0', 'int8': '0', 'int16': '0', 'int32': '0', 'int64': '0',
126
+ 'int8_t': '0', 'int16_t': '0', 'int32_t': '0', 'int64_t': '0',
127
+ 'short': '0', 'long': '0',
128
+ # ---- Unsigned integers ----
129
+ 'uint': '0', 'uint8': '0', 'uint16': '0', 'uint32': '0', 'uint64': '0',
130
+ 'uint8_t': '0', 'uint16_t': '0', 'uint32_t': '0', 'uint64_t': '0',
131
+ 'unsigned': '0', 'unsigned int': '0', 'size_t': '0',
132
+ # ---- Floating point ----
133
+ 'float': '0.0f', 'double': '0.0',
134
+ # ---- Boolean / character ----
135
+ 'bool': 'false', '_Bool': 'false',
136
+ 'char': '0', 'string': 'NULL',
137
+ }
138
+
139
+ # Type qualifiers (may appear at the beginning)
140
+ _TYPE_QUALIFIERS = ('volatile', 'const', 'static')
141
+
142
+ # v1.6 sec 9.8 #92: separate macro name and field name
143
+ MACRO_TEMPLATES = {
144
+ 'data_macro': Template(
145
+ '#define DATA_$macro_name(ctx) ((ctx)->data.$field_name)\n'
146
+ ),
147
+ 'flag_macro': Template(
148
+ '#define FLAG_$macro_name(ctx) ((ctx)->flags.$field_name)\n'
149
+ ),
150
+ }
151
+
152
+ INIT_CODE_TEMPLATES = {
153
+ 'array_init': Template(
154
+ ' memset(ctx->data.$var_name, 0, sizeof(ctx->data.$var_name));\n'
155
+ ),
156
+ 'struct_init': Template(
157
+ ' memset(&ctx->data.$var_name, 0, sizeof(ctx->data.$var_name));\n'
158
+ ),
159
+ 'normal_init': Template(
160
+ ' ctx->data.$var_name = $init_value;\n'
161
+ ),
162
+ 'flag_init': Template(
163
+ ' ctx->flags.$flag_name = 0;\n'
164
+ ),
165
+ }
166
+
167
+ VARIABLE_TYPE_DETECTORS = {
168
+ 'array': lambda v: getattr(v, 'array_size', 0) > 0,
169
+ 'normal': lambda v: True,
170
+ }
171
+
172
+ def __init__(self):
173
+ self.mapper = CTypeMapper()
174
+ self.naming = CNamingConvention()
175
+ self.templates = CodeTemplates()
176
+ self.strings = self.templates.STRINGS
177
+ self.formats = self.templates.FORMATS
178
+
179
+ self.step_executors = {
180
+ 'template': self._execute_template_step,
181
+ 'blank': self._execute_blank_step,
182
+ 'loop': self._execute_loop_step,
183
+ }
184
+
185
+ def _log_debug(self, message, level='debug'):
186
+ log_func = getattr(logger, level, logger.debug)
187
+ log_func(message)
188
+
189
+ def _detect_variable_type(self, var) -> str:
190
+ for var_type, detector in self.VARIABLE_TYPE_DETECTORS.items():
191
+ if detector(var):
192
+ return var_type
193
+ return 'normal'
194
+
195
+ def _build_context(self, global_defs: GlobalDefinitions) -> dict:
196
+ return {
197
+ 'func_name': self.templates.FUNCTION_NAMES['system_context_init'],
198
+ 'log_debug': self.strings['log_debug'],
199
+ 'log_error': self.strings['log_error'],
200
+ 'variables': getattr(global_defs, 'variables', []),
201
+ 'flags': getattr(global_defs, 'flags', []),
202
+ }
203
+
204
+ def _replace_placeholders(self, template: str, params: dict) -> str:
205
+ result = template
206
+ for key, value in params.items():
207
+ result = result.replace('$' + key, str(value))
208
+ return result
209
+
210
+ def _resolve_value(self, value, context: dict) -> str:
211
+ if isinstance(value, str) and value.startswith('{') and value.endswith('}'):
212
+ key = value[1:-1]
213
+ return str(context.get(key, value))
214
+ return str(value)
215
+
216
+ def _execute_template_step(self, step: dict, context: dict) -> List[str]:
217
+ template_key = step.get('key', '')
218
+ template = self.INIT_TEMPLATES.get(template_key, '')
219
+ if not template:
220
+ return []
221
+
222
+ format_params = step.get('format', {})
223
+ resolved = {}
224
+ for k, v in format_params.items():
225
+ resolved[k] = self._resolve_value(v, context)
226
+
227
+ if isinstance(template, Template):
228
+ return [template.substitute(resolved).rstrip('\n')]
229
+
230
+ return [self._replace_placeholders(template, resolved).rstrip('\n')]
231
+
232
+ def _execute_blank_step(self, step: dict, context: dict) -> List[str]:
233
+ return [""]
234
+
235
+ def _execute_loop_step(self, step: dict, context: dict) -> List[str]:
236
+ source_key = step.get('source', '')
237
+ generator_key = step.get('generator', '')
238
+ items = context.get(source_key, [])
239
+ results = []
240
+ for item in items:
241
+ if generator_key == 'init':
242
+ results.append(self._generate_init_code(item))
243
+ elif generator_key == 'macro':
244
+ results.append(self._generate_access_macro(item))
245
+ return [r for r in results if r]
246
+
247
+ # ================================================================
248
+ # v1.6 sec 9.8 #92: macro generation (macro name = uppercase,
249
+ # field name = original)
250
+ # ================================================================
251
+ def _generate_data_macro(self, var) -> str:
252
+ """
253
+ Generate data access macro.
254
+
255
+ [v1.6 fix]
256
+ Old: DATA_COUNTER(ctx) ((ctx)->data.COUNTER) -> uppercase field (bug)
257
+ New: DATA_COUNTER(ctx) ((ctx)->data.counter) -> original field
258
+ """
259
+ raw_name = getattr(var, 'name', 'unnamed')
260
+ macro_name = self.naming.to_upper_snake(raw_name)
261
+ field_name = self.naming.sanitize_identifier(raw_name)
262
+ return self.MACRO_TEMPLATES['data_macro'].substitute(
263
+ macro_name=macro_name, field_name=field_name
264
+ ).rstrip('\n')
265
+
266
+ def _generate_flag_macro(self, flag) -> str:
267
+ """
268
+ Generate flag access macro.
269
+
270
+ [v1.6 fix]
271
+ Old: FLAG_EVT_INIT_DONE(ctx) ((ctx)->flags.EVT_INIT_DONE) -> bug
272
+ New: FLAG_EVT_INIT_DONE(ctx) ((ctx)->flags.EVT_INIT_DONE) -> matches
273
+ * Field name may not be sanitize_identifier-ed on the struct side
274
+ """
275
+ raw_name = getattr(flag, 'name', 'unnamed')
276
+ macro_name = self.naming.to_upper_snake(raw_name)
277
+ field_name = self.naming.sanitize_identifier(raw_name)
278
+ return self.MACRO_TEMPLATES['flag_macro'].substitute(
279
+ macro_name=macro_name, field_name=field_name
280
+ ).rstrip('\n')
281
+
282
+ def _generate_access_macro(self, item) -> str:
283
+ item_class = item.__class__.__name__
284
+ if item_class == 'SystemVariable':
285
+ return self._generate_data_macro(item)
286
+ elif item_class == 'EventFlag':
287
+ return self._generate_flag_macro(item)
288
+ return ""
289
+
290
+ # ================================================================
291
+ # v2.0 addition: normalize type name
292
+ # ================================================================
293
+ def _normalize_type_for_init(self, var_type: str) -> str:
294
+ """
295
+ Normalize type name so it can be looked up in DEFAULT_INIT_VALUES.
296
+
297
+ Examples:
298
+ 'volatile uint32_t' -> 'uint32_t'
299
+ 'uint32_t' -> 'uint32_t'
300
+ 'const uint8_t' -> 'uint8_t'
301
+ 'MyCustomType' -> 'MyCustomType' (unchanged)
302
+ """
303
+ if not var_type:
304
+ return ''
305
+ tokens = var_type.strip().split()
306
+ # Strip leading qualifiers
307
+ while tokens and tokens[0] in self._TYPE_QUALIFIERS:
308
+ tokens.pop(0)
309
+ # Sign qualifiers such as 'unsigned int' are preserved
310
+ normalized = ' '.join(tokens)
311
+ return normalized
312
+
313
+ # ================================================================
314
+ # Initialization code generation
315
+ # ================================================================
316
+ def _generate_array_init(self, var) -> str:
317
+ var_name = self.naming.sanitize_identifier(getattr(var, 'name', 'unnamed'))
318
+ return self.INIT_CODE_TEMPLATES['array_init'].substitute(
319
+ var_name=var_name
320
+ ).rstrip('\n')
321
+
322
+ def _generate_normal_init(self, var) -> str:
323
+ """
324
+ [v1.5 fix]
325
+ - Custom types (not in DEFAULT_INIT_VALUES) use memset
326
+ - Primitive types use direct assignment (= 0 etc.)
327
+
328
+ [v2.0 fix]
329
+ - Correctly recognize standard types with _t suffix (uint32_t etc.)
330
+ - Strip volatile / const qualifiers before lookup
331
+ - Prevents redundant memset generation
332
+ """
333
+ var_name = self.naming.sanitize_identifier(getattr(var, 'name', 'unnamed'))
334
+ raw_type = getattr(var, 'type', 'void')
335
+ normalized_type = self._normalize_type_for_init(raw_type)
336
+
337
+ # v2.0: custom type check (using normalized type name)
338
+ if normalized_type not in self.DEFAULT_INIT_VALUES:
339
+ self._log_debug(
340
+ f"_generate_normal_init: '{var_name}' "
341
+ f"(type='{raw_type}' -> '{normalized_type}') "
342
+ f"is a custom type -> using memset"
343
+ )
344
+ return self.INIT_CODE_TEMPLATES['struct_init'].substitute(
345
+ var_name=var_name
346
+ ).rstrip('\n')
347
+
348
+ # Standard type: = 0 / = false / = 0.0f etc.
349
+ init_value = getattr(var, 'default_value', '') or \
350
+ self.DEFAULT_INIT_VALUES.get(normalized_type, '0')
351
+ return self.INIT_CODE_TEMPLATES['normal_init'].substitute(
352
+ var_name=var_name, init_value=init_value
353
+ ).rstrip('\n')
354
+
355
+ def _generate_flag_init(self, flag) -> str:
356
+ flag_name = self.naming.sanitize_identifier(getattr(flag, 'name', 'unnamed'))
357
+ return self.INIT_CODE_TEMPLATES['flag_init'].substitute(
358
+ flag_name=flag_name
359
+ ).rstrip('\n')
360
+
361
+ def _generate_init_code(self, item) -> str:
362
+ item_class = item.__class__.__name__
363
+ if item_class == 'SystemVariable':
364
+ var_type = self._detect_variable_type(item)
365
+ if var_type == 'array':
366
+ return self._generate_array_init(item)
367
+ else:
368
+ return self._generate_normal_init(item)
369
+ elif item_class == 'EventFlag':
370
+ return self._generate_flag_init(item)
371
+ return ""
372
+
373
+ # ================================================================
374
+ # Public API
375
+ # ================================================================
376
+ def generate_init_function(self, global_defs: GlobalDefinitions) -> str:
377
+ self._log_debug("=== generate_init_function START ===")
378
+ lines = []
379
+ context = self._build_context(global_defs)
380
+
381
+ for step in self.INIT_FUNCTION_STEPS:
382
+ executor = self.step_executors.get(step.get('action', ''))
383
+ if executor:
384
+ lines.extend(executor(step, context))
385
+
386
+ result = '\n'.join(lines)
387
+ self._log_debug(f"=== generate_init_function END: {len(result)} chars ===")
388
+ return result
389
+
390
+ def generate_all_macros(self, global_defs: GlobalDefinitions) -> str:
391
+ self._log_debug("=== generate_all_macros START ===")
392
+ lines = []
393
+ for var in getattr(global_defs, 'variables', []):
394
+ lines.append(self._generate_data_macro(var))
395
+ for flag in getattr(global_defs, 'flags', []):
396
+ lines.append(self._generate_flag_macro(flag))
397
+ result = '\n'.join(lines)
398
+ self._log_debug(f"=== generate_all_macros END: {len(lines)} macros ===")
399
+ return result
400
+
401
+ def generate_variable(self, var_type: str, item) -> str:
402
+ if var_type == 'macro':
403
+ return self._generate_access_macro(item)
404
+ elif var_type == 'init':
405
+ return self._generate_init_code(item)
406
+ elif var_type == 'global':
407
+ return self._generate_normal_init(item)
408
+ elif var_type == 'flag':
409
+ return self._generate_flag_init(item)
410
+ raise ValueError(f"Unknown variable type: {var_type}")
411
+
412
+ def generate_all(self, global_defs: GlobalDefinitions) -> Dict[str, str]:
413
+ self._log_debug("=== generate_all START (variable_generator) ===")
414
+ result = {
415
+ 'init_function': self.generate_init_function(global_defs),
416
+ 'macros': self.generate_all_macros(global_defs),
417
+ }
418
+ self._log_debug("=== generate_all END (variable_generator) ===")
419
+ return result
statable/__init__.py ADDED
@@ -0,0 +1,71 @@
1
+ # statable package
2
+ """
3
+ StaTable data model layer
4
+
5
+ Re-exports main classes for convenient external use.
6
+
7
+ [v1.5 added]
8
+ - Re-export only lightweight-dependency modules to avoid circular imports
9
+ - xml_io / sample_data are intentionally excluded
10
+ (they have heavy dependencies and can cause circular references)
11
+ """
12
+
13
+ # model.py (no dependencies) -- load first
14
+ from .model import (
15
+ State,
16
+ Event,
17
+ Transition,
18
+ StateType,
19
+ EventKind,
20
+ RoleFunction,
21
+ EventDeliveryType,
22
+ EventSourceLayer,
23
+ )
24
+
25
+ # state_machine.py (depends on model)
26
+ from .state_machine import StateMachine
27
+
28
+ # global_defs.py (no dependencies)
29
+ from .global_defs import (
30
+ GlobalDefinitions,
31
+ SystemVariable,
32
+ EventFlag,
33
+ InterruptHandlerDef,
34
+ InterruptAction,
35
+ DevicePlaceholderDef,
36
+ TimerBaseDef,
37
+ TimerDerivedDef,
38
+ EventQueueDef,
39
+ CustomTypeDef,
40
+ StructMemberDef,
41
+ )
42
+
43
+ __version__ = "3.1.0"
44
+
45
+ __all__ = [
46
+ # model
47
+ 'State',
48
+ 'Event',
49
+ 'Transition',
50
+ 'StateType',
51
+ 'EventKind',
52
+ 'RoleFunction',
53
+ 'EventDeliveryType',
54
+ 'EventSourceLayer',
55
+ # state_machine
56
+ 'StateMachine',
57
+ # global_defs
58
+ 'GlobalDefinitions',
59
+ 'SystemVariable',
60
+ 'EventFlag',
61
+ 'InterruptHandlerDef',
62
+ 'InterruptAction',
63
+ 'DevicePlaceholderDef',
64
+ 'TimerBaseDef',
65
+ 'TimerDerivedDef',
66
+ 'EventQueueDef',
67
+ 'CustomTypeDef',
68
+ 'StructMemberDef',
69
+ # version
70
+ '__version__',
71
+ ]
statable/__main__.py ADDED
@@ -0,0 +1,49 @@
1
+ """Entry point for `python -m statable`.
2
+
3
+ Launches the StaTable GUI, or prints a friendly error if the
4
+ GUI extras are not installed.
5
+
6
+ pip install statable[gui] # to enable GUI
7
+ python -m statable # launch
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import sys
12
+
13
+
14
+ def main() -> int:
15
+ try:
16
+ from PySide6.QtWidgets import QApplication
17
+ except ImportError:
18
+ sys.stderr.write(
19
+ "error: The StaTable GUI requires PySide6.\n"
20
+ " Install with: pip install \"statable[gui]\"\n"
21
+ " For CLI-only usage: statable-cli --help\n"
22
+ )
23
+ return 1
24
+
25
+ try:
26
+ from statable_gui.main_window import MainWindow
27
+ except ImportError as exc:
28
+ sys.stderr.write(
29
+ f"error: Failed to import GUI module: {exc}\n"
30
+ " The GUI files may be missing from the install.\n"
31
+ )
32
+ return 1
33
+
34
+ app = QApplication(sys.argv)
35
+
36
+ # [v3.1] Apply saved language preference (en / zh_CN)
37
+ try:
38
+ from statable_gui.i18n import install_translator
39
+ install_translator(app)
40
+ except Exception:
41
+ pass # i18n is optional; fall back to English
42
+
43
+ window = MainWindow()
44
+ window.show()
45
+ return app.exec()
46
+
47
+
48
+ if __name__ == "__main__":
49
+ sys.exit(main())