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,150 @@
1
+ # statable/shared/role_function_library.py
2
+ """
3
+ Shared role function library
4
+
5
+ Version History
6
+ ---------------
7
+ v1.0 - Initial shared library.
8
+ v1.5 - Added reserved signature fields
9
+ (return_type / arg1_type / arg1_name / arg2_type / arg2_name).
10
+ These are not used by the code generator (its C signature is
11
+ fixed), and they are not exposed in the GUI. They are kept
12
+ here only so that XML I/O round-trips old project files
13
+ (tests/data/v22_features_test3.xml) without canonical
14
+ differences. See xml_io.py v3.8.2.
15
+ """
16
+
17
+ from dataclasses import dataclass, field
18
+ from typing import List, Dict, Optional
19
+
20
+
21
+ @dataclass
22
+ class RoleFunction:
23
+ """Shared role function definition.
24
+
25
+ namespace: layer name / feature group name (e.g., "Driver")
26
+ - Referenceable as `Driver.Init`
27
+ - Empty string means no layer
28
+
29
+ [v1.5]
30
+ Reserved signature fields are present but not used:
31
+ * return_type / arg1_type / arg1_name / arg2_type / arg2_name
32
+ Their only purpose is XML round-trip preservation.
33
+ """
34
+ name: str
35
+ namespace: str = ""
36
+ description: str = ""
37
+ title: str = ""
38
+ # [v1.5] Reserved signature fields (not exposed in GUI,
39
+ # not consumed by codegen; preserved for XML round-trip).
40
+ return_type: str = ""
41
+ arg1_type: str = ""
42
+ arg1_name: str = ""
43
+ arg2_type: str = ""
44
+ arg2_name: str = ""
45
+ # [v1.4] GUI symbol tracking
46
+ used_global_vars: List[str] = field(default_factory=list)
47
+ used_events: List[str] = field(default_factory=list)
48
+ used_literals: List[str] = field(default_factory=list)
49
+
50
+ def __post_init__(self):
51
+ if not self.title:
52
+ self.title = self.qualified_name
53
+
54
+ @property
55
+ def qualified_name(self) -> str:
56
+ """GUI display name: 'Driver.Init' or 'Init'"""
57
+ if self.namespace:
58
+ return f"{self.namespace}.{self.name}"
59
+ return self.name
60
+
61
+ def to_dict(self) -> dict:
62
+ return {
63
+ 'name': self.name,
64
+ 'namespace': self.namespace,
65
+ 'description': self.description,
66
+ 'title': self.title,
67
+ # [v1.5] Reserved signature fields
68
+ 'return_type': self.return_type,
69
+ 'arg1_type': self.arg1_type,
70
+ 'arg1_name': self.arg1_name,
71
+ 'arg2_type': self.arg2_type,
72
+ 'arg2_name': self.arg2_name,
73
+ # [v1.4] symbol references
74
+ 'used_global_vars': list(self.used_global_vars),
75
+ 'used_events': list(self.used_events),
76
+ 'used_literals': list(self.used_literals),
77
+ }
78
+
79
+ @classmethod
80
+ def from_dict(cls, data: dict) -> 'RoleFunction':
81
+ return cls(
82
+ name=data.get('name', ''),
83
+ namespace=data.get('namespace', ''),
84
+ description=data.get('description', ''),
85
+ title=data.get('title', ''),
86
+ return_type=data.get('return_type', ''),
87
+ arg1_type=data.get('arg1_type', ''),
88
+ arg1_name=data.get('arg1_name', ''),
89
+ arg2_type=data.get('arg2_type', ''),
90
+ arg2_name=data.get('arg2_name', ''),
91
+ used_global_vars=list(data.get('used_global_vars', [])),
92
+ used_events=list(data.get('used_events', [])),
93
+ used_literals=list(data.get('used_literals', [])),
94
+ )
95
+
96
+
97
+ class RoleFunctionLibrary:
98
+ """Role function library shared across the project"""
99
+
100
+ def __init__(self):
101
+ self.role_functions: Dict[str, RoleFunction] = {}
102
+
103
+ def _key(self, rf: RoleFunction) -> str:
104
+ """Unique key in the library: 'Driver.Init'"""
105
+ return rf.qualified_name
106
+
107
+ def add(self, rf: RoleFunction):
108
+ key = self._key(rf)
109
+ if key in self.role_functions:
110
+ raise ValueError(f"Role function '{key}' already exists")
111
+ self.role_functions[key] = rf
112
+
113
+ def remove(self, name: str):
114
+ """name can be either qualified_name or a bare name"""
115
+ if name in self.role_functions:
116
+ del self.role_functions[name]
117
+ return
118
+ # Search by bare name
119
+ for key, rf in list(self.role_functions.items()):
120
+ if rf.name == name:
121
+ del self.role_functions[key]
122
+ return
123
+
124
+ def get(self, name: str) -> Optional[RoleFunction]:
125
+ """name can be either qualified_name or a bare name"""
126
+ if name in self.role_functions:
127
+ return self.role_functions[name]
128
+ for rf in self.role_functions.values():
129
+ if rf.name == name:
130
+ return rf
131
+ return None
132
+
133
+ def list_all(self) -> List[RoleFunction]:
134
+ return list(self.role_functions.values())
135
+
136
+ def to_dict(self) -> dict:
137
+ return {
138
+ 'role_functions': [rf.to_dict() for rf in self.role_functions.values()]
139
+ }
140
+
141
+ @classmethod
142
+ def from_dict(cls, data: dict) -> 'RoleFunctionLibrary':
143
+ lib = cls()
144
+ for item in data.get('role_functions', []):
145
+ rf = RoleFunction.from_dict(item)
146
+ try:
147
+ lib.add(rf)
148
+ except ValueError:
149
+ pass # Duplicates ignored
150
+ return lib
@@ -0,0 +1,121 @@
1
+ # statable/state_machine.py
2
+ from typing import Dict, List, Optional, Tuple
3
+
4
+ from .model import (
5
+ State, Event, Transition, RoleFunction,
6
+ ActionStep, TransitionRelation,
7
+ )
8
+
9
+
10
+ class StateMachine:
11
+ def __init__(self):
12
+ self.states: Dict[str, State] = {}
13
+ self.events: Dict[str, Event] = {}
14
+ self.transitions: List[Transition] = []
15
+ self.role_functions: Dict[str, RoleFunction] = {}
16
+ self.initial_state: Optional[str] = None
17
+
18
+ # Layer settings
19
+ self.layer_priority: int = 5
20
+ self.layer_description: str = ""
21
+ self.layer_name: str = ""
22
+
23
+ # === v2.2: per-cell metadata ===
24
+ # Key: (source_state_name, event_name)
25
+ # Note: event_name may be "" for completion transitions.
26
+ self.cell_actions: Dict[Tuple[str, str], List[ActionStep]] = {}
27
+ self.cell_relations: Dict[Tuple[str, str], List[TransitionRelation]] = {}
28
+
29
+ # ------------------------------------------------------------------
30
+ # Existing API (unchanged)
31
+ # ------------------------------------------------------------------
32
+ def add_state(self, state: State):
33
+ if state.name in self.states:
34
+ raise ValueError(f"State '{state.name}' already exists")
35
+ self.states[state.name] = state
36
+
37
+ def add_event(self, event: Event):
38
+ if event.name in self.events:
39
+ raise ValueError(f"Event '{event.name}' already exists")
40
+ self.events[event.name] = event
41
+
42
+ def remove_event(self, name: str):
43
+ if name in self.events:
44
+ del self.events[name]
45
+ self.transitions = [t for t in self.transitions if t.event != name]
46
+ # v2.2: drop cell metadata that referenced this event
47
+ for key in [k for k in self.cell_actions if k[1] == name]:
48
+ del self.cell_actions[key]
49
+ for key in [k for k in self.cell_relations if k[1] == name]:
50
+ del self.cell_relations[key]
51
+
52
+ def add_transition(self, trans: Transition):
53
+ if trans.source not in self.states:
54
+ raise ValueError(f"Source state '{trans.source}' not defined")
55
+ if trans.target and trans.target not in self.states:
56
+ raise ValueError(f"Target state '{trans.target}' not defined")
57
+ if trans.event and trans.event not in self.events:
58
+ raise ValueError(f"Event '{trans.event}' not defined")
59
+ self.transitions.append(trans)
60
+
61
+ def remove_transition(self, trans: Transition):
62
+ if trans in self.transitions:
63
+ self.transitions.remove(trans)
64
+
65
+ def set_initial(self, state_name: str):
66
+ if state_name not in self.states:
67
+ raise ValueError(f"State '{state_name}' not defined")
68
+ self.initial_state = state_name
69
+
70
+ def add_role_function(self, rf: RoleFunction):
71
+ if rf.name in self.role_functions:
72
+ raise ValueError(f"Role function '{rf.name}' already exists")
73
+ self.role_functions[rf.name] = rf
74
+
75
+ def remove_role_function(self, name: str):
76
+ if name in self.role_functions:
77
+ del self.role_functions[name]
78
+
79
+ def get_transitions_for_cell(self, source: str, event: str) -> List[Transition]:
80
+ return [t for t in self.transitions
81
+ if t.source == source and t.event == event]
82
+
83
+ def get_transitions_for_event(self, event: str) -> List[Transition]:
84
+ return [t for t in self.transitions if t.event == event]
85
+
86
+ # ------------------------------------------------------------------
87
+ # v2.2: cell action / relation accessors
88
+ # ------------------------------------------------------------------
89
+ def get_actions_for_cell(self, source: str, event: str) -> List[ActionStep]:
90
+ return self.cell_actions.get((source, event), [])
91
+
92
+ def set_actions_for_cell(self, source: str, event: str,
93
+ actions: List[ActionStep]) -> None:
94
+ key = (source, event)
95
+ if actions:
96
+ self.cell_actions[key] = list(actions)
97
+ else:
98
+ self.cell_actions.pop(key, None)
99
+
100
+ def get_relations_for_cell(self, source: str, event: str
101
+ ) -> List[TransitionRelation]:
102
+ return self.cell_relations.get((source, event), [])
103
+
104
+ def set_relations_for_cell(self, source: str, event: str,
105
+ relations: List[TransitionRelation]) -> None:
106
+ key = (source, event)
107
+ if relations:
108
+ self.cell_relations[key] = list(relations)
109
+ else:
110
+ self.cell_relations.pop(key, None)
111
+
112
+ def get_cell_keys(self) -> List[Tuple[str, str]]:
113
+ """All (source, event) keys that have actions or relations."""
114
+ keys = set(self.cell_actions.keys()) | set(self.cell_relations.keys())
115
+ return sorted(keys)
116
+
117
+ def remove_cell_metadata(self, source: str, event: str) -> None:
118
+ """Remove all cell-level actions / relations for the given cell."""
119
+ key = (source, event)
120
+ self.cell_actions.pop(key, None)
121
+ self.cell_relations.pop(key, None)