quantum-framework 0.9.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 (262) hide show
  1. quantum/__init__.py +1 -0
  2. quantum/cli/__init__.py +2 -0
  3. quantum/cli/commands/__init__.py +15 -0
  4. quantum/cli/commands/build.py +417 -0
  5. quantum/cli/commands/dev.py +185 -0
  6. quantum/cli/commands/docs.py +329 -0
  7. quantum/cli/commands/lint.py +523 -0
  8. quantum/cli/commands/migrate.py +527 -0
  9. quantum/cli/commands/new.py +622 -0
  10. quantum/cli/commands/serve.py +190 -0
  11. quantum/cli/commands/test.py +193 -0
  12. quantum/cli/deploy.py +810 -0
  13. quantum/cli/hot_reload.py +951 -0
  14. quantum/cli/jobs.py +356 -0
  15. quantum/cli/mq.py +582 -0
  16. quantum/cli/pkg.py +390 -0
  17. quantum/cli/runner.py +547 -0
  18. quantum/cli/server_process.py +159 -0
  19. quantum/cli/utils.py +334 -0
  20. quantum/compiler/__init__.py +30 -0
  21. quantum/compiler/base_generator.py +367 -0
  22. quantum/compiler/cli.py +295 -0
  23. quantum/compiler/expression_transformer.py +444 -0
  24. quantum/compiler/javascript/__init__.py +10 -0
  25. quantum/compiler/javascript/generator.py +659 -0
  26. quantum/compiler/optimizer.py +270 -0
  27. quantum/compiler/python/__init__.py +10 -0
  28. quantum/compiler/python/generator.py +883 -0
  29. quantum/compiler/python/runtime.py +863 -0
  30. quantum/compiler/transpiler.py +330 -0
  31. quantum/core/__init__.py +3 -0
  32. quantum/core/ast_nodes.py +2611 -0
  33. quantum/core/expression_diagnostics.py +87 -0
  34. quantum/core/expression_stdlib.py +148 -0
  35. quantum/core/expressions.py +591 -0
  36. quantum/core/features/agents/src/__init__.py +30 -0
  37. quantum/core/features/agents/src/ast_node.py +540 -0
  38. quantum/core/features/conditionals/src/__init__.py +8 -0
  39. quantum/core/features/conditionals/src/ast_node.py +68 -0
  40. quantum/core/features/data_fetching/src/__init__.py +21 -0
  41. quantum/core/features/data_fetching/src/ast_node.py +312 -0
  42. quantum/core/features/data_fetching/src/desktop_adapter.py +351 -0
  43. quantum/core/features/data_fetching/src/html_adapter.py +474 -0
  44. quantum/core/features/data_fetching/src/parser.py +225 -0
  45. quantum/core/features/data_import/src/__init__.py +0 -0
  46. quantum/core/features/data_import/src/ast_node.py +291 -0
  47. quantum/core/features/data_import/src/runtime.py +538 -0
  48. quantum/core/features/dump/src/__init__.py +12 -0
  49. quantum/core/features/dump/src/ast_node.py +106 -0
  50. quantum/core/features/dump/src/parser.py +61 -0
  51. quantum/core/features/dump/src/runtime.py +246 -0
  52. quantum/core/features/functions/src/__init__.py +8 -0
  53. quantum/core/features/functions/src/ast_node.py +149 -0
  54. quantum/core/features/game_engine_2d/src/__init__.py +19 -0
  55. quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
  56. quantum/core/features/game_engine_2d/src/parser.py +983 -0
  57. quantum/core/features/invocation/src/__init__.py +0 -0
  58. quantum/core/features/invocation/src/ast_node.py +146 -0
  59. quantum/core/features/invocation/src/runtime.py +327 -0
  60. quantum/core/features/knowledge_base/src/__init__.py +6 -0
  61. quantum/core/features/knowledge_base/src/ast_node.py +113 -0
  62. quantum/core/features/knowledge_base/src/parser.py +82 -0
  63. quantum/core/features/logging/src/__init__.py +12 -0
  64. quantum/core/features/logging/src/ast_node.py +111 -0
  65. quantum/core/features/logging/src/parser.py +50 -0
  66. quantum/core/features/logging/src/runtime.py +190 -0
  67. quantum/core/features/loops/src/__init__.py +8 -0
  68. quantum/core/features/loops/src/ast_node.py +60 -0
  69. quantum/core/features/query/src/__init__.py +0 -0
  70. quantum/core/features/query/src/database_service.py +322 -0
  71. quantum/core/features/query/src/query_validators.py +20 -0
  72. quantum/core/features/state_management/src/__init__.py +11 -0
  73. quantum/core/features/state_management/src/ast_node.py +228 -0
  74. quantum/core/features/terminal_engine/src/__init__.py +21 -0
  75. quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
  76. quantum/core/features/terminal_engine/src/parser.py +361 -0
  77. quantum/core/features/testing_engine/src/__init__.py +41 -0
  78. quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
  79. quantum/core/features/testing_engine/src/parser.py +604 -0
  80. quantum/core/features/theming/src/__init__.py +48 -0
  81. quantum/core/features/theming/src/ast_node.py +137 -0
  82. quantum/core/features/theming/src/presets.py +405 -0
  83. quantum/core/features/ui_engine/src/__init__.py +20 -0
  84. quantum/core/features/ui_engine/src/ast_nodes.py +1854 -0
  85. quantum/core/features/ui_engine/src/parser.py +1106 -0
  86. quantum/core/features/websocket/src/__init__.py +24 -0
  87. quantum/core/features/websocket/src/ast_node.py +247 -0
  88. quantum/core/html_compat.py +299 -0
  89. quantum/core/parser.py +1235 -0
  90. quantum/core/parser_registry.py +213 -0
  91. quantum/core/parsers/__init__.py +76 -0
  92. quantum/core/parsers/ai/__init__.py +12 -0
  93. quantum/core/parsers/ai/agent_parser.py +106 -0
  94. quantum/core/parsers/ai/knowledge_parser.py +86 -0
  95. quantum/core/parsers/ai/llm_parser.py +78 -0
  96. quantum/core/parsers/ai/team_parser.py +88 -0
  97. quantum/core/parsers/base.py +322 -0
  98. quantum/core/parsers/composition/__init__.py +10 -0
  99. quantum/core/parsers/composition/import_parser.py +50 -0
  100. quantum/core/parsers/composition/slot_parser.py +48 -0
  101. quantum/core/parsers/control_flow/__init__.py +11 -0
  102. quantum/core/parsers/control_flow/if_parser.py +68 -0
  103. quantum/core/parsers/control_flow/loop_parser.py +105 -0
  104. quantum/core/parsers/control_flow/set_parser.py +89 -0
  105. quantum/core/parsers/data/__init__.py +12 -0
  106. quantum/core/parsers/data/data_parser.py +217 -0
  107. quantum/core/parsers/data/invoke_parser.py +116 -0
  108. quantum/core/parsers/data/query_parser.py +188 -0
  109. quantum/core/parsers/data/transaction_parser.py +77 -0
  110. quantum/core/parsers/events/__init__.py +9 -0
  111. quantum/core/parsers/events/dispatch_event_parser.py +44 -0
  112. quantum/core/parsers/forms/__init__.py +11 -0
  113. quantum/core/parsers/forms/action_parser.py +54 -0
  114. quantum/core/parsers/forms/flash_parser.py +34 -0
  115. quantum/core/parsers/forms/redirect_parser.py +33 -0
  116. quantum/core/parsers/functions/__init__.py +11 -0
  117. quantum/core/parsers/functions/function_parser.py +137 -0
  118. quantum/core/parsers/functions/param_parser.py +66 -0
  119. quantum/core/parsers/functions/return_parser.py +31 -0
  120. quantum/core/parsers/html/__init__.py +10 -0
  121. quantum/core/parsers/html/component_call_parser.py +130 -0
  122. quantum/core/parsers/html/html_parser.py +115 -0
  123. quantum/core/parsers/jobs/__init__.py +11 -0
  124. quantum/core/parsers/jobs/job_parser.py +71 -0
  125. quantum/core/parsers/jobs/schedule_parser.py +62 -0
  126. quantum/core/parsers/jobs/thread_parser.py +57 -0
  127. quantum/core/parsers/messaging/__init__.py +17 -0
  128. quantum/core/parsers/messaging/message_ack_parser.py +30 -0
  129. quantum/core/parsers/messaging/message_nack_parser.py +30 -0
  130. quantum/core/parsers/messaging/message_parser.py +114 -0
  131. quantum/core/parsers/messaging/queue_parser.py +61 -0
  132. quantum/core/parsers/messaging/websocket_parser.py +121 -0
  133. quantum/core/parsers/persistence/__init__.py +9 -0
  134. quantum/core/parsers/persistence/persist_parser.py +64 -0
  135. quantum/core/parsers/routing/__init__.py +9 -0
  136. quantum/core/parsers/routing/route_parser.py +41 -0
  137. quantum/core/parsers/scripting/__init__.py +12 -0
  138. quantum/core/parsers/scripting/pyclass_parser.py +62 -0
  139. quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
  140. quantum/core/parsers/scripting/pyimport_parser.py +52 -0
  141. quantum/core/parsers/scripting/python_parser.py +49 -0
  142. quantum/core/parsers/services/__init__.py +12 -0
  143. quantum/core/parsers/services/dump_parser.py +52 -0
  144. quantum/core/parsers/services/file_parser.py +48 -0
  145. quantum/core/parsers/services/log_parser.py +46 -0
  146. quantum/core/parsers/services/mail_parser.py +65 -0
  147. quantum/core/tiers.py +82 -0
  148. quantum/packages/__init__.py +28 -0
  149. quantum/packages/manager.py +413 -0
  150. quantum/packages/manifest.py +351 -0
  151. quantum/packages/registry.py +399 -0
  152. quantum/packages/resolver.py +336 -0
  153. quantum/plugins/__init__.py +33 -0
  154. quantum/plugins/hooks.py +329 -0
  155. quantum/plugins/loader.py +479 -0
  156. quantum/plugins/manifest.py +336 -0
  157. quantum/plugins/registry.py +371 -0
  158. quantum/runtime/__init__.py +28 -0
  159. quantum/runtime/action_handler.py +443 -0
  160. quantum/runtime/adapters/__init__.py +88 -0
  161. quantum/runtime/adapters/memory_adapter.py +690 -0
  162. quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
  163. quantum/runtime/adapters/redis_adapter.py +582 -0
  164. quantum/runtime/adapters/sqlite_adapter.py +414 -0
  165. quantum/runtime/agent_service.py +1133 -0
  166. quantum/runtime/api_server.py +86 -0
  167. quantum/runtime/ast_cache.py +506 -0
  168. quantum/runtime/auth_service.py +267 -0
  169. quantum/runtime/component.py +990 -0
  170. quantum/runtime/component_composer.py +319 -0
  171. quantum/runtime/component_resolver.py +174 -0
  172. quantum/runtime/database_service.py +598 -0
  173. quantum/runtime/email_service.py +162 -0
  174. quantum/runtime/error_handler.py +295 -0
  175. quantum/runtime/execution_context.py +286 -0
  176. quantum/runtime/executor_registry.py +171 -0
  177. quantum/runtime/executors/__init__.py +71 -0
  178. quantum/runtime/executors/ai/__init__.py +12 -0
  179. quantum/runtime/executors/ai/agent_executor.py +217 -0
  180. quantum/runtime/executors/ai/knowledge_executor.py +114 -0
  181. quantum/runtime/executors/ai/llm_executor.py +153 -0
  182. quantum/runtime/executors/ai/team_executor.py +171 -0
  183. quantum/runtime/executors/base.py +262 -0
  184. quantum/runtime/executors/control_flow/__init__.py +11 -0
  185. quantum/runtime/executors/control_flow/if_executor.py +93 -0
  186. quantum/runtime/executors/control_flow/loop_executor.py +307 -0
  187. quantum/runtime/executors/control_flow/set_executor.py +412 -0
  188. quantum/runtime/executors/data/__init__.py +12 -0
  189. quantum/runtime/executors/data/data_executor.py +145 -0
  190. quantum/runtime/executors/data/invoke_executor.py +176 -0
  191. quantum/runtime/executors/data/query_executor.py +256 -0
  192. quantum/runtime/executors/data/transaction_executor.py +91 -0
  193. quantum/runtime/executors/jobs/__init__.py +11 -0
  194. quantum/runtime/executors/jobs/job_executor.py +190 -0
  195. quantum/runtime/executors/jobs/schedule_executor.py +132 -0
  196. quantum/runtime/executors/jobs/thread_executor.py +127 -0
  197. quantum/runtime/executors/messaging/__init__.py +17 -0
  198. quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
  199. quantum/runtime/executors/messaging/message_executor.py +174 -0
  200. quantum/runtime/executors/messaging/queue_executor.py +103 -0
  201. quantum/runtime/executors/messaging/websocket_executor.py +197 -0
  202. quantum/runtime/executors/scripting/__init__.py +11 -0
  203. quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
  204. quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
  205. quantum/runtime/executors/scripting/python_executor.py +249 -0
  206. quantum/runtime/executors/services/__init__.py +12 -0
  207. quantum/runtime/executors/services/dump_executor.py +72 -0
  208. quantum/runtime/executors/services/file_executor.py +89 -0
  209. quantum/runtime/executors/services/log_executor.py +77 -0
  210. quantum/runtime/executors/services/mail_executor.py +81 -0
  211. quantum/runtime/expression_cache.py +498 -0
  212. quantum/runtime/file_upload_service.py +326 -0
  213. quantum/runtime/function_registry.py +118 -0
  214. quantum/runtime/game_builder.py +166 -0
  215. quantum/runtime/game_code_generator.py +2371 -0
  216. quantum/runtime/game_templates.py +2006 -0
  217. quantum/runtime/godot_code_generator.py +4681 -0
  218. quantum/runtime/godot_templates.py +1449 -0
  219. quantum/runtime/job_executor.py +1599 -0
  220. quantum/runtime/knowledge_service.py +500 -0
  221. quantum/runtime/llm_cache.py +100 -0
  222. quantum/runtime/llm_providers.py +704 -0
  223. quantum/runtime/llm_service.py +287 -0
  224. quantum/runtime/logging_setup.py +140 -0
  225. quantum/runtime/message_broker.py +364 -0
  226. quantum/runtime/message_queue_service.py +571 -0
  227. quantum/runtime/param_validation.py +184 -0
  228. quantum/runtime/pypy_compat.py +315 -0
  229. quantum/runtime/python_bridge.py +698 -0
  230. quantum/runtime/query_validators.py +304 -0
  231. quantum/runtime/renderer.py +733 -0
  232. quantum/runtime/service_container.py +444 -0
  233. quantum/runtime/terminal_builder.py +76 -0
  234. quantum/runtime/terminal_code_generator.py +607 -0
  235. quantum/runtime/terminal_templates.py +243 -0
  236. quantum/runtime/testing_builder.py +77 -0
  237. quantum/runtime/testing_code_generator.py +833 -0
  238. quantum/runtime/testing_templates.py +85 -0
  239. quantum/runtime/ui_builder.py +188 -0
  240. quantum/runtime/ui_desktop_adapter.py +1730 -0
  241. quantum/runtime/ui_desktop_templates.py +307 -0
  242. quantum/runtime/ui_html_adapter.py +2691 -0
  243. quantum/runtime/ui_html_templates.py +2297 -0
  244. quantum/runtime/ui_mobile_adapter.py +1832 -0
  245. quantum/runtime/ui_mobile_templates.py +1003 -0
  246. quantum/runtime/ui_textual_adapter.py +1866 -0
  247. quantum/runtime/ui_textual_templates.py +45 -0
  248. quantum/runtime/ui_tokens.py +465 -0
  249. quantum/runtime/ui_validator.py +365 -0
  250. quantum/runtime/validators.py +256 -0
  251. quantum/runtime/web_server.py +1766 -0
  252. quantum/runtime/websocket_adapter.py +501 -0
  253. quantum/runtime/websocket_service.py +585 -0
  254. quantum/runtime/websocket_transport.py +289 -0
  255. quantum/runtime/wsgi.py +101 -0
  256. quantum/utils/__init__.py +1 -0
  257. quantum_framework-0.9.0.dist-info/METADATA +244 -0
  258. quantum_framework-0.9.0.dist-info/RECORD +262 -0
  259. quantum_framework-0.9.0.dist-info/WHEEL +5 -0
  260. quantum_framework-0.9.0.dist-info/entry_points.txt +2 -0
  261. quantum_framework-0.9.0.dist-info/licenses/LICENSE +21 -0
  262. quantum_framework-0.9.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,412 @@
1
+ """
2
+ Set Executor - Execute q:set statements
3
+
4
+ Handles variable assignment and all operations:
5
+ - assign, increment, decrement
6
+ - add, multiply (arithmetic)
7
+ - append, prepend, remove, clear, sort, etc. (array)
8
+ - merge, setProperty, deleteProperty, clone (object)
9
+ - uppercase, lowercase, trim, format (string)
10
+ """
11
+
12
+ from typing import Any, List, Dict, Type
13
+ import json
14
+ from quantum.runtime.executors.base import BaseExecutor, ExecutorError
15
+ from quantum.core.features.state_management.src.ast_node import SetNode
16
+ from quantum.core.expressions import coerce_number
17
+
18
+
19
+ class SetExecutor(BaseExecutor):
20
+ """
21
+ Executor for q:set statements.
22
+
23
+ Supports:
24
+ - Basic assignment with type conversion
25
+ - Increment/decrement operations
26
+ - Array operations (append, remove, sort, etc.)
27
+ - Object operations (merge, setProperty, etc.)
28
+ - String transformations (uppercase, trim, etc.)
29
+ - Validation rules
30
+ """
31
+
32
+ @property
33
+ def handles(self) -> List[Type]:
34
+ return [SetNode]
35
+
36
+ def execute(self, node: SetNode, exec_context) -> Any:
37
+ """
38
+ Execute q:set statement.
39
+
40
+ Args:
41
+ node: SetNode to execute
42
+ exec_context: Execution context
43
+
44
+ Returns:
45
+ None (set doesn't return a value)
46
+ """
47
+ try:
48
+ context = exec_context.get_all_variables()
49
+
50
+ # Handle different operations
51
+ if node.operation == "assign":
52
+ value = self._execute_assign(node, context)
53
+ elif node.operation == "increment":
54
+ value = self._execute_increment(node, exec_context, node.step)
55
+ elif node.operation == "decrement":
56
+ value = self._execute_decrement(node, exec_context, node.step)
57
+ elif node.operation in ["add", "multiply"]:
58
+ value = self._execute_arithmetic(node, exec_context, context)
59
+ elif node.operation in ["append", "prepend", "remove", "removeAt", "clear", "sort", "reverse", "unique"]:
60
+ value = self._execute_array_operation(node, exec_context)
61
+ elif node.operation in ["merge", "setProperty", "deleteProperty", "clone"]:
62
+ value = self._execute_object_operation(node, exec_context, context)
63
+ elif node.operation in ["uppercase", "lowercase", "trim", "format"]:
64
+ value = self._execute_transformation(node, exec_context)
65
+ else:
66
+ raise ExecutorError(f"Unsupported operation: {node.operation}")
67
+
68
+ # Validate the value
69
+ self._validate_value(node, value)
70
+
71
+ # Set the variable in the appropriate scope
72
+ self._set_variable(node, value, exec_context)
73
+
74
+ return None # q:set doesn't return a value
75
+
76
+ except Exception as e:
77
+ raise ExecutorError(f"Set execution error for '{node.name}': {e}")
78
+
79
+ def _execute_assign(self, node: SetNode, context: Dict[str, Any]) -> Any:
80
+ """Execute assign operation"""
81
+ value_expr = node.value if node.value is not None else node.default
82
+
83
+ if value_expr is None:
84
+ if not node.nullable:
85
+ raise ExecutorError(f"Variable '{node.name}' cannot be null")
86
+ return None
87
+
88
+ # Process databinding
89
+ processed_value = self.apply_databinding(value_expr, context)
90
+
91
+ # Convert to appropriate type
92
+ return self._convert_to_type(processed_value, node.type)
93
+
94
+ def _numeric_or_fail(self, value: Any, op: str, where: str) -> Any:
95
+ """Coerce a numeric-looking value, or fail loudly.
96
+
97
+ A .q attribute is a string by nature — `<q:set name="estoque"
98
+ value="7" />` stores "7" unless the author remembered type="number".
99
+ Refusing to add to that made increment/decrement unusable in the
100
+ ordinary case, so it is coerced, the same way {a + b} coerces.
101
+ """
102
+ coerced = coerce_number(value)
103
+ if isinstance(coerced, bool) or not isinstance(coerced, (int, float)):
104
+ raise ExecutorError(
105
+ f"Cannot {op} non-numeric value {value!r} ({where}). "
106
+ f"Set it with type=\"number\", or give q:set a numeric value."
107
+ )
108
+ return coerced
109
+
110
+ def _current_numeric(self, node: SetNode, exec_context, op: str):
111
+ """(value, exists) for the variable being incremented/decremented.
112
+
113
+ The previous version wrapped BOTH the lookup and the type check in one
114
+ `try: ... except Exception: return step`, so the ExecutorError it
115
+ raised for a non-numeric value was caught by its own handler one line
116
+ later and turned into the step itself. A stock of "7" decremented by 5
117
+ became -5 — the detection was correct and then discarded. Missing and
118
+ non-numeric are separated here so only the first one falls back.
119
+ """
120
+ try:
121
+ current = exec_context.get_variable(node.name)
122
+ except Exception:
123
+ return None, False
124
+ if current is None or current == '':
125
+ return None, False
126
+ return self._numeric_or_fail(current, op, f"current value of {node.name!r}"), True
127
+
128
+ def _execute_increment(self, node: SetNode, exec_context, step: int) -> Any:
129
+ """Execute increment operation"""
130
+ if node.value:
131
+ context = exec_context.get_all_variables()
132
+ base_value = self.apply_databinding(node.value, context)
133
+ return self._numeric_or_fail(base_value, 'increment', 'value=') + step
134
+
135
+ current, exists = self._current_numeric(node, exec_context, 'increment')
136
+ return (current + step) if exists else step
137
+
138
+ def _execute_decrement(self, node: SetNode, exec_context, step: int) -> Any:
139
+ """Execute decrement operation"""
140
+ if node.value:
141
+ context = exec_context.get_all_variables()
142
+ base_value = self.apply_databinding(node.value, context)
143
+ return self._numeric_or_fail(base_value, 'decrement', 'value=') - step
144
+
145
+ current, exists = self._current_numeric(node, exec_context, 'decrement')
146
+ return (current - step) if exists else -step
147
+
148
+ def _execute_arithmetic(self, node: SetNode, exec_context, context: Dict[str, Any]) -> Any:
149
+ """Execute arithmetic operations (add, multiply)"""
150
+ try:
151
+ current_value = exec_context.get_variable(node.name)
152
+ except Exception:
153
+ current_value = 0
154
+ if current_value is None or current_value == '':
155
+ current_value = 0
156
+
157
+ # Same coercion as increment/decrement: a .q attribute is a string by
158
+ # nature, so add/multiply on a variable set without type="number" used
159
+ # to fail even though the value was plainly numeric.
160
+ current_value = self._numeric_or_fail(
161
+ current_value, node.operation, f"current value of {node.name!r}"
162
+ )
163
+
164
+ operand_expr = node.value
165
+ if not operand_expr:
166
+ raise ExecutorError("Arithmetic operation requires a value")
167
+
168
+ fresh_context = exec_context.get_all_variables()
169
+ processed = self.apply_databinding(operand_expr, fresh_context)
170
+ operand = self._convert_to_type(processed, "number")
171
+
172
+ if node.operation == "add":
173
+ return current_value + operand
174
+ elif node.operation == "multiply":
175
+ return current_value * operand
176
+
177
+ return current_value
178
+
179
+ def _execute_array_operation(self, node: SetNode, exec_context) -> Any:
180
+ """Execute array operations"""
181
+ try:
182
+ current_value = exec_context.get_variable(node.name)
183
+ except Exception:
184
+ current_value = []
185
+
186
+ if not isinstance(current_value, list):
187
+ raise ExecutorError(f"Cannot perform array operation on non-array: {type(current_value)}")
188
+
189
+ result = current_value.copy()
190
+
191
+ # Resolve databinding in value
192
+ resolved_value = node.value
193
+ if resolved_value and node.operation in ("append", "prepend", "remove"):
194
+ context = exec_context.get_all_variables()
195
+ resolved = self.apply_databinding(resolved_value, context)
196
+ if resolved is not None:
197
+ resolved_value = resolved
198
+
199
+ if node.operation == "append":
200
+ if resolved_value:
201
+ result.append(resolved_value)
202
+ elif node.operation == "prepend":
203
+ if resolved_value:
204
+ result.insert(0, resolved_value)
205
+ elif node.operation == "remove":
206
+ if resolved_value and resolved_value in result:
207
+ result.remove(resolved_value)
208
+ elif node.operation == "removeAt":
209
+ if node.index is not None:
210
+ idx = int(node.index)
211
+ if 0 <= idx < len(result):
212
+ result.pop(idx)
213
+ elif node.operation == "clear":
214
+ result = []
215
+ elif node.operation == "sort":
216
+ result.sort()
217
+ elif node.operation == "reverse":
218
+ result.reverse()
219
+ elif node.operation == "unique":
220
+ result = list(dict.fromkeys(result))
221
+
222
+ return result
223
+
224
+ def _execute_object_operation(self, node: SetNode, exec_context, context: Dict[str, Any]) -> Any:
225
+ """Execute object operations"""
226
+ try:
227
+ current_value = exec_context.get_variable(node.name)
228
+ except Exception:
229
+ current_value = {}
230
+
231
+ if not isinstance(current_value, dict):
232
+ raise ExecutorError(f"Cannot perform object operation on non-object: {type(current_value)}")
233
+
234
+ result = current_value.copy()
235
+
236
+ if node.operation == "merge":
237
+ if node.value:
238
+ try:
239
+ merge_data = json.loads(node.value)
240
+ result.update(merge_data)
241
+ except json.JSONDecodeError:
242
+ raise ExecutorError(f"Invalid JSON for merge: {node.value}")
243
+ elif node.operation == "setProperty":
244
+ if node.key and node.value:
245
+ result[node.key] = node.value
246
+ elif node.operation == "deleteProperty":
247
+ if node.key and node.key in result:
248
+ del result[node.key]
249
+ elif node.operation == "clone":
250
+ if node.source:
251
+ try:
252
+ source_obj = exec_context.get_variable(node.source)
253
+ if isinstance(source_obj, dict):
254
+ result = source_obj.copy()
255
+ except Exception:
256
+ pass
257
+
258
+ return result
259
+
260
+ def _execute_transformation(self, node: SetNode, exec_context) -> Any:
261
+ """Execute string transformation operations"""
262
+ if node.value:
263
+ context = exec_context.get_all_variables()
264
+ processed_value = self.apply_databinding(node.value, context)
265
+ value_str = str(processed_value)
266
+ else:
267
+ try:
268
+ current_value = exec_context.get_variable(node.name)
269
+ value_str = str(current_value)
270
+ except Exception:
271
+ value_str = ""
272
+
273
+ if node.operation == "uppercase":
274
+ return value_str.upper()
275
+ elif node.operation == "lowercase":
276
+ return value_str.lower()
277
+ elif node.operation == "trim":
278
+ return value_str.strip()
279
+ elif node.operation == "format":
280
+ return value_str
281
+
282
+ return value_str
283
+
284
+ def _convert_to_type(self, value: Any, target_type: str) -> Any:
285
+ """Convert value to target type"""
286
+ if value is None:
287
+ return None
288
+
289
+ try:
290
+ if target_type == "string":
291
+ return str(value)
292
+ elif target_type in ("integer", "number"):
293
+ try:
294
+ return int(value)
295
+ except (ValueError, TypeError):
296
+ return float(value)
297
+ elif target_type == "decimal":
298
+ return float(value)
299
+ elif target_type == "boolean":
300
+ if isinstance(value, bool):
301
+ return value
302
+ if isinstance(value, str):
303
+ return value.lower() in ['true', '1', 'yes']
304
+ return bool(value)
305
+ elif target_type == "array":
306
+ if isinstance(value, list):
307
+ return value
308
+ if isinstance(value, str):
309
+ return json.loads(value)
310
+ return [value]
311
+ elif target_type == "object":
312
+ if isinstance(value, dict):
313
+ return value
314
+ if isinstance(value, str):
315
+ return json.loads(value)
316
+ return {}
317
+ elif target_type == "json":
318
+ if isinstance(value, (dict, list)):
319
+ return value
320
+ if isinstance(value, str):
321
+ return json.loads(value)
322
+ return value
323
+ else:
324
+ return value
325
+ except Exception as e:
326
+ raise ExecutorError(f"Type conversion error to '{target_type}': {e}")
327
+
328
+ def _validate_value(self, node: SetNode, value: Any):
329
+ """Validate value against set_node rules"""
330
+ # Import validators lazily to avoid circular imports
331
+ from quantum.runtime.validators import QuantumValidators, ValidationError
332
+
333
+ # Check required
334
+ if node.required:
335
+ is_valid, error = QuantumValidators.validate_required(value)
336
+ if not is_valid:
337
+ raise ExecutorError(error)
338
+
339
+ # Check nullable
340
+ if not node.nullable and value is None:
341
+ raise ExecutorError(f"Variable '{node.name}' cannot be null")
342
+
343
+ # Skip other validations if null and nullable
344
+ if value is None and node.nullable:
345
+ return
346
+
347
+ # Check validate_rule
348
+ if node.validate_rule:
349
+ if node.validate_rule == 'cpf':
350
+ is_valid, error = QuantumValidators.validate_cpf(str(value))
351
+ elif node.validate_rule == 'cnpj':
352
+ is_valid, error = QuantumValidators.validate_cnpj(str(value))
353
+ else:
354
+ is_valid, error = QuantumValidators.validate(value, node.validate_rule)
355
+
356
+ if not is_valid:
357
+ raise ExecutorError(error)
358
+
359
+ # Check pattern
360
+ if node.pattern and not node.validate_rule:
361
+ is_valid, error = QuantumValidators.validate(value, node.pattern)
362
+ if not is_valid:
363
+ raise ExecutorError(error)
364
+
365
+ # Check range
366
+ if node.range:
367
+ is_valid, error = QuantumValidators.validate_range(value, node.range)
368
+ if not is_valid:
369
+ raise ExecutorError(error)
370
+
371
+ # Check enum
372
+ if node.enum:
373
+ is_valid, error = QuantumValidators.validate_enum(value, node.enum)
374
+ if not is_valid:
375
+ raise ExecutorError(error)
376
+
377
+ # Check min/max
378
+ if node.min or node.max:
379
+ is_valid, error = QuantumValidators.validate_min_max(
380
+ value, min_val=node.min, max_val=node.max
381
+ )
382
+ if not is_valid:
383
+ raise ExecutorError(error)
384
+
385
+ # Check minlength/maxlength
386
+ if node.type == "string" and (node.minlength or node.maxlength):
387
+ minlen = int(node.minlength) if node.minlength else None
388
+ maxlen = int(node.maxlength) if node.maxlength else None
389
+
390
+ is_valid, error = QuantumValidators.validate_length(
391
+ value, minlength=minlen, maxlength=maxlen
392
+ )
393
+ if not is_valid:
394
+ raise ExecutorError(error)
395
+
396
+ def _set_variable(self, node: SetNode, value: Any, exec_context):
397
+ """Set variable in the appropriate scope"""
398
+ is_update_operation = node.operation in [
399
+ "increment", "decrement", "add", "multiply",
400
+ "append", "prepend", "remove", "removeAt",
401
+ "clear", "sort", "reverse", "unique",
402
+ "merge", "setProperty", "deleteProperty",
403
+ "uppercase", "lowercase", "trim", "format"
404
+ ]
405
+
406
+ if is_update_operation or (exec_context.has_variable(node.name) and node.scope == "local"):
407
+ exec_context.update_variable(node.name, value)
408
+ else:
409
+ actual_scope = node.scope
410
+ if node.scope == "local" and exec_context.parent is not None:
411
+ actual_scope = "function"
412
+ exec_context.set_variable(node.name, value, scope=actual_scope)
@@ -0,0 +1,12 @@
1
+ """
2
+ Data Executors
3
+
4
+ Executors for data operations: query, invoke, data, transaction.
5
+ """
6
+
7
+ from .query_executor import QueryExecutor
8
+ from .invoke_executor import InvokeExecutor
9
+ from .data_executor import DataExecutor
10
+ from .transaction_executor import TransactionExecutor
11
+
12
+ __all__ = ['QueryExecutor', 'InvokeExecutor', 'DataExecutor', 'TransactionExecutor']
@@ -0,0 +1,145 @@
1
+ """
2
+ Data Executor - Execute q:data statements
3
+
4
+ Handles data import from CSV, JSON, XML sources with transformations.
5
+ """
6
+
7
+ from typing import Any, List, Dict, Type
8
+ from quantum.runtime.executors.base import BaseExecutor, ExecutorError
9
+ from quantum.core.ast_nodes import DataNode
10
+
11
+
12
+ class DataExecutor(BaseExecutor):
13
+ """
14
+ Executor for q:data statements.
15
+
16
+ Supports:
17
+ - CSV import with column definitions
18
+ - JSON import from files or URLs
19
+ - XML import with XPath
20
+ - Data transformations (filter, sort, limit, compute)
21
+ - Response caching with TTL
22
+ """
23
+
24
+ @property
25
+ def handles(self) -> List[Type]:
26
+ return [DataNode]
27
+
28
+ def execute(self, node: DataNode, exec_context) -> Any:
29
+ """
30
+ Execute data import.
31
+
32
+ Args:
33
+ node: DataNode with import configuration
34
+ exec_context: Execution context
35
+
36
+ Returns:
37
+ None (stores result in context)
38
+ """
39
+ try:
40
+ context = exec_context.get_all_variables()
41
+
42
+ # Resolve source
43
+ source = self.apply_databinding(node.source, context)
44
+
45
+ # Build parameters
46
+ params = self._build_params(node, context)
47
+
48
+ # Execute data import
49
+ result = self.services.data_import.import_data(
50
+ node.data_type,
51
+ source,
52
+ params,
53
+ context=exec_context
54
+ )
55
+
56
+ # Store result
57
+ self._store_result(node, result, exec_context)
58
+
59
+ except Exception as e:
60
+ raise ExecutorError(f"Data import error in '{node.name}': {e}")
61
+
62
+ def _build_params(self, node: DataNode, context: Dict[str, Any]) -> Dict[str, Any]:
63
+ """Build parameters for data import"""
64
+ params = {
65
+ 'cache': node.cache,
66
+ 'ttl': node.ttl,
67
+ 'delimiter': node.delimiter,
68
+ 'quote': node.quote,
69
+ 'header': node.header,
70
+ 'encoding': node.encoding,
71
+ 'skip_rows': node.skip_rows,
72
+ 'xpath': node.xpath,
73
+ 'namespace': node.namespace,
74
+ 'columns': [],
75
+ 'fields': [],
76
+ 'transforms': [],
77
+ 'headers': []
78
+ }
79
+
80
+ # Add column definitions (for CSV)
81
+ for col in node.columns:
82
+ params['columns'].append({
83
+ 'name': col.name,
84
+ 'type': col.col_type,
85
+ 'required': col.required,
86
+ 'default': col.default
87
+ })
88
+
89
+ # Add field definitions (for XML)
90
+ for field in node.fields:
91
+ params['fields'].append({
92
+ 'name': field.name,
93
+ 'xpath': field.xpath,
94
+ 'type': field.field_type
95
+ })
96
+
97
+ # Add HTTP headers
98
+ for header in node.headers:
99
+ resolved_value = self.apply_databinding(header.value, context)
100
+ params['headers'].append({
101
+ 'name': header.name,
102
+ 'value': resolved_value
103
+ })
104
+
105
+ # Add transformations
106
+ for transform in node.transforms:
107
+ for operation in transform.operations:
108
+ op_dict = {
109
+ 'type': operation.__class__.__name__.replace('Node', '').lower()
110
+ }
111
+
112
+ if hasattr(operation, 'condition'):
113
+ op_dict['condition'] = operation.condition
114
+ elif hasattr(operation, 'by'):
115
+ op_dict['by'] = operation.by
116
+ op_dict['order'] = operation.order
117
+ elif hasattr(operation, 'value'):
118
+ op_dict['value'] = operation.value
119
+ elif hasattr(operation, 'field'):
120
+ op_dict['field'] = operation.field
121
+ op_dict['expression'] = operation.expression
122
+ op_dict['type'] = operation.comp_type
123
+
124
+ params['transforms'].append(op_dict)
125
+
126
+ return params
127
+
128
+ def _store_result(self, node: DataNode, result, exec_context):
129
+ """Store data import result in context"""
130
+ exec_context.set_variable(node.name, result.data, scope="component")
131
+
132
+ result_dict = {
133
+ 'success': result.success,
134
+ 'data': result.data,
135
+ 'error': result.error,
136
+ 'recordCount': result.recordCount,
137
+ 'loadTime': result.loadTime,
138
+ 'cached': result.cached,
139
+ 'source': result.source
140
+ }
141
+
142
+ exec_context.set_variable(f"{node.name}_result", result_dict, scope="component")
143
+
144
+ if node.result:
145
+ exec_context.set_variable(node.result, result_dict, scope="component")