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,883 @@
1
+ """
2
+ Python Code Generator
3
+ =====================
4
+
5
+ Generates Python code from Quantum AST nodes.
6
+ """
7
+
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import Optional, List, Set, Any
11
+ from dataclasses import dataclass
12
+
13
+ # Add paths
14
+
15
+ from quantum.compiler.base_generator import CodeGenerator, GeneratorContext
16
+ from quantum.compiler.expression_transformer import ExpressionTransformer
17
+ from quantum.core.ast_nodes import (
18
+ QuantumNode, ComponentNode, ApplicationNode, SetNode, LoopNode,
19
+ IfNode, FunctionNode, HTMLNode, TextNode, QueryNode, ActionNode,
20
+ ImportNode, SlotNode, ComponentCallNode, PythonNode, PyImportNode,
21
+ RedirectNode, FlashNode, DocTypeNode, CommentNode, QueryParamNode
22
+ )
23
+
24
+
25
+ class PythonGenerator(CodeGenerator):
26
+ """
27
+ Generates Python code from Quantum AST.
28
+
29
+ Produces clean, readable Python that mirrors the Quantum structure
30
+ while being fully native Python.
31
+ """
32
+
33
+ def __init__(self):
34
+ super().__init__()
35
+ self.expr_transformer = ExpressionTransformer(target='python')
36
+ self.imports: Set[str] = set()
37
+ self.component_name: str = ''
38
+ self.in_render: bool = False
39
+ self.functions: List[str] = []
40
+
41
+ def file_extension(self) -> str:
42
+ return '.py'
43
+
44
+ def generate_header(self) -> str:
45
+ """Generate Python file header."""
46
+ lines = [
47
+ '"""',
48
+ 'Generated by Quantum Transpiler',
49
+ 'Do not edit directly - changes will be overwritten.',
50
+ '"""',
51
+ '',
52
+ ]
53
+ return '\n'.join(lines)
54
+
55
+ def generate_footer(self) -> str:
56
+ """Generate file footer."""
57
+ return ''
58
+
59
+ def transpile_expression(self, expr: str) -> str:
60
+ """Transpile Quantum expression to Python."""
61
+ return self.expr_transformer.transform(expr)
62
+
63
+ # =========================================================================
64
+ # Component & Application
65
+ # =========================================================================
66
+
67
+ def visit_ComponentNode(self, node: ComponentNode) -> str:
68
+ """Generate Python class for component."""
69
+ self.component_name = node.name or 'Component'
70
+ self.imports.add('from quantum.compiler.python.runtime import Component, escape, bind')
71
+
72
+ # Get statements/children - ComponentNode uses 'statements'
73
+ statements = getattr(node, 'statements', None) or getattr(node, 'children', None) or []
74
+
75
+ # Emit imports first
76
+ for imp in sorted(self.imports):
77
+ self.emit(imp)
78
+ self.emit_blank()
79
+
80
+ # Class definition
81
+ self.emit(f'class {self.component_name}(Component):')
82
+ self.indent()
83
+ self.emit(f'"""Quantum component: {self.component_name}"""')
84
+ self.emit_blank()
85
+
86
+ # Render method
87
+ self.emit('def render(self) -> str:')
88
+ self.indent()
89
+ self.in_render = True
90
+
91
+ # Initialize HTML builder
92
+ self.emit('_html = []')
93
+ self.emit_blank()
94
+
95
+ # Initialize variables from props
96
+ self.emit('# Props')
97
+ self.emit('for _k, _v in self.props.items():')
98
+ self.indent()
99
+ self.emit('locals()[_k] = _v')
100
+ self.dedent()
101
+ self.emit_blank()
102
+
103
+ # Process statements
104
+ self.push_scope()
105
+ self.emit('# Component body')
106
+ for child in statements:
107
+ self.visit(child)
108
+
109
+ self.pop_scope()
110
+ self.emit_blank()
111
+
112
+ # Return joined HTML
113
+ self.emit("return '\\n'.join(_html)")
114
+
115
+ self.in_render = False
116
+ self.dedent()
117
+ self.dedent()
118
+
119
+ return self.component_name
120
+
121
+ def visit_ApplicationNode(self, node: ApplicationNode) -> str:
122
+ """Generate Python code for application."""
123
+ app_name = node.name or 'Application'
124
+ app_type = getattr(node, 'type', 'web')
125
+
126
+ self.imports.add('from quantum.compiler.python.runtime import Component, escape')
127
+
128
+ # Emit imports
129
+ for imp in sorted(self.imports):
130
+ self.emit(imp)
131
+ self.emit_blank()
132
+
133
+ self.emit(f'# Application: {app_name} ({app_type})')
134
+ self.emit_blank()
135
+
136
+ # Process children (components, routes, etc.)
137
+ for child in node.children or []:
138
+ self.visit(child)
139
+
140
+ return app_name
141
+
142
+ # =========================================================================
143
+ # State Management
144
+ # =========================================================================
145
+
146
+ def visit_SetNode(self, node: SetNode) -> str:
147
+ """Generate variable assignment."""
148
+ name = node.name
149
+ value = getattr(node, 'value', None)
150
+ scope = getattr(node, 'scope', 'local')
151
+
152
+ if value is None:
153
+ self.emit(f'{name} = None')
154
+ elif self.is_expression(str(value)):
155
+ expr = self.transpile_expression(str(value))
156
+ self.emit(f'{name} = {expr}')
157
+ elif self.is_literal(str(value)):
158
+ self.emit(f'{name} = {value}')
159
+ else:
160
+ # String value
161
+ self.emit(f'{name} = {repr(value)}')
162
+
163
+ self.declare_var(name)
164
+ return name
165
+
166
+ # =========================================================================
167
+ # Control Flow
168
+ # =========================================================================
169
+
170
+ def visit_LoopNode(self, node: LoopNode) -> str:
171
+ """Generate for loop."""
172
+ # Get loop variable - LoopNode uses var_name
173
+ var = getattr(node, 'var_name', None) or getattr(node, 'var', None) or 'item'
174
+ index_var = getattr(node, 'index_name', None) or getattr(node, 'index', None)
175
+
176
+ # Get collection or range - LoopNode uses 'items' for collection
177
+ collection = getattr(node, 'items', None) or getattr(node, 'collection', None)
178
+ from_val = getattr(node, 'from_value', None) or getattr(node, 'from_val', None)
179
+ to_val = getattr(node, 'to_value', None) or getattr(node, 'to_val', None)
180
+ step = getattr(node, 'step_value', None) or getattr(node, 'step', None)
181
+
182
+ # Get body - LoopNode uses 'body' not 'children'
183
+ body = getattr(node, 'body', None) or getattr(node, 'children', None) or []
184
+
185
+ if collection:
186
+ # Collection-based loop
187
+ coll_expr = self.transpile_expression(str(collection))
188
+
189
+ if index_var:
190
+ self.emit(f'for {index_var}, {var} in enumerate({coll_expr}):')
191
+ else:
192
+ self.emit(f'for {var} in {coll_expr}:')
193
+ else:
194
+ # Range-based loop
195
+ start = from_val if from_val is not None else 0
196
+ # Quantum loops are inclusive, Python range is exclusive
197
+ end_expr = f'{to_val} + 1' if to_val is not None else '0'
198
+
199
+ if self.is_expression(str(start)):
200
+ start = self.transpile_expression(str(start))
201
+ if to_val and self.is_expression(str(to_val)):
202
+ end_expr = f'({self.transpile_expression(str(to_val))}) + 1'
203
+
204
+ if step:
205
+ self.emit(f'for {var} in range({start}, {end_expr}, {step}):')
206
+ else:
207
+ self.emit(f'for {var} in range({start}, {end_expr}):')
208
+
209
+ self.indent()
210
+ self.push_scope()
211
+ self.declare_var(var)
212
+ if index_var:
213
+ self.declare_var(index_var)
214
+
215
+ # Process body
216
+ for child in body:
217
+ self.visit(child)
218
+
219
+ self.pop_scope()
220
+ self.dedent()
221
+
222
+ return ''
223
+
224
+ def visit_IfNode(self, node: IfNode) -> str:
225
+ """Generate if/elif/else."""
226
+ condition = getattr(node, 'condition', None) or getattr(node, 'test', None)
227
+
228
+ if condition:
229
+ cond_expr = self.transpile_expression(str(condition))
230
+ self.emit(f'if {cond_expr}:')
231
+ else:
232
+ self.emit('if True:')
233
+
234
+ self.indent()
235
+ self.push_scope()
236
+
237
+ # Process if-body (IfNode uses if_body, not children)
238
+ if_body = getattr(node, 'if_body', None) or getattr(node, 'children', None) or []
239
+ has_content = False
240
+
241
+ for child in if_body:
242
+ self.visit(child)
243
+ has_content = True
244
+
245
+ if not has_content:
246
+ self.emit('pass')
247
+
248
+ self.pop_scope()
249
+ self.dedent()
250
+
251
+ # Handle elseif blocks
252
+ elseif_blocks = getattr(node, 'elseif_blocks', []) or []
253
+ for elseif in elseif_blocks:
254
+ elseif_cond = elseif.get('condition', '')
255
+ if elseif_cond:
256
+ cond_expr = self.transpile_expression(str(elseif_cond))
257
+ self.emit(f'elif {cond_expr}:')
258
+ else:
259
+ self.emit('elif True:')
260
+
261
+ self.indent()
262
+ elseif_body = elseif.get('body', []) or []
263
+ if elseif_body:
264
+ for child in elseif_body:
265
+ self.visit(child)
266
+ else:
267
+ self.emit('pass')
268
+ self.dedent()
269
+
270
+ # Handle else body
271
+ else_body = getattr(node, 'else_body', []) or []
272
+ if else_body:
273
+ self.emit('else:')
274
+ self.indent()
275
+ for child in else_body:
276
+ self.visit(child)
277
+ self.dedent()
278
+
279
+ return ''
280
+
281
+ # =========================================================================
282
+ # Functions
283
+ # =========================================================================
284
+
285
+ def visit_FunctionNode(self, node: FunctionNode) -> str:
286
+ """Generate function definition."""
287
+ name = node.name or 'unnamed_function'
288
+ params = getattr(node, 'params', []) or []
289
+
290
+ # Build parameter list
291
+ param_list = []
292
+ for param in params:
293
+ if hasattr(param, 'name'):
294
+ # QuantumParam object
295
+ param_name = param.name
296
+ default = getattr(param, 'default', None)
297
+ if default is not None:
298
+ param_list.append(f'{param_name}={repr(default)}')
299
+ else:
300
+ param_list.append(param_name)
301
+ elif isinstance(param, str):
302
+ # String parameter
303
+ param_list.append(param.strip())
304
+ elif isinstance(param, dict):
305
+ # Dict parameter
306
+ param_name = param.get('name', '')
307
+ default = param.get('default')
308
+ if default is not None:
309
+ param_list.append(f'{param_name}={repr(default)}')
310
+ else:
311
+ param_list.append(param_name)
312
+
313
+ param_str = ', '.join(param_list)
314
+ self.emit(f'def {name}({param_str}):')
315
+
316
+ self.indent()
317
+ self.push_scope()
318
+
319
+ # Declare parameters
320
+ for param in params:
321
+ if hasattr(param, 'name'):
322
+ self.declare_var(param.name)
323
+ elif isinstance(param, str):
324
+ param_name = param.split('=')[0].strip()
325
+ self.declare_var(param_name)
326
+
327
+ # Process body (FunctionNode uses 'body' not 'children')
328
+ body = getattr(node, 'body', None) or getattr(node, 'children', None) or []
329
+ has_return = False
330
+ for child in body:
331
+ child_type = type(child).__name__
332
+ if child_type == 'ReturnNode':
333
+ has_return = True
334
+ self.visit(child)
335
+
336
+ # Add implicit return if needed
337
+ if not has_return:
338
+ self.emit('return None')
339
+
340
+ self.pop_scope()
341
+ self.dedent()
342
+ self.emit_blank()
343
+
344
+ self.functions.append(name)
345
+ return name
346
+
347
+ def visit_ReturnNode(self, node) -> str:
348
+ """Generate return statement."""
349
+ value = getattr(node, 'value', None)
350
+
351
+ if value:
352
+ expr = self.transpile_expression(str(value))
353
+ self.emit(f'return {expr}')
354
+ else:
355
+ self.emit('return None')
356
+
357
+ return ''
358
+
359
+ # =========================================================================
360
+ # HTML Rendering
361
+ # =========================================================================
362
+
363
+ def visit_HTMLNode(self, node: HTMLNode) -> str:
364
+ """Generate HTML element."""
365
+ tag = node.tag or 'div'
366
+ attributes = getattr(node, 'attributes', {}) or {}
367
+ children = node.children or []
368
+
369
+ # Build attributes string
370
+ attr_parts = []
371
+ for key, value in attributes.items():
372
+ if value is None:
373
+ attr_parts.append(key)
374
+ elif self.is_expression(str(value)):
375
+ expr = self.transpile_expression(str(value))
376
+ attr_parts.append(f'{key}="{{escape({expr})}}"')
377
+ else:
378
+ attr_parts.append(f'{key}="{value}"')
379
+
380
+ attr_str = ' ' + ' '.join(attr_parts) if attr_parts else ''
381
+
382
+ # Self-closing tags
383
+ self_closing = tag.lower() in ('br', 'hr', 'img', 'input', 'meta', 'link', 'area', 'base', 'col', 'embed', 'param', 'source', 'track', 'wbr')
384
+
385
+ if self_closing:
386
+ self.emit(f'_html.append(f\'<{tag}{attr_str} />\')')
387
+ elif not children:
388
+ self.emit(f'_html.append(f\'<{tag}{attr_str}></{tag}>\')')
389
+ else:
390
+ self.emit(f'_html.append(f\'<{tag}{attr_str}>\')')
391
+
392
+ # Process children
393
+ for child in children:
394
+ self.visit(child)
395
+
396
+ self.emit(f"_html.append('</{tag}>')")
397
+
398
+ return ''
399
+
400
+ def visit_TextNode(self, node: TextNode) -> str:
401
+ """Generate text content."""
402
+ content = getattr(node, 'content', '') or getattr(node, 'text', '') or ''
403
+
404
+ if not content.strip():
405
+ return ''
406
+
407
+ # Check for databinding
408
+ if '{' in content and '}' in content:
409
+ # Complex expression - use bind()
410
+ self.emit(f"_html.append(bind({repr(content)}, locals()))")
411
+ else:
412
+ # Plain text
413
+ self.emit(f"_html.append({repr(content)})")
414
+
415
+ return ''
416
+
417
+ def visit_DocTypeNode(self, node: DocTypeNode) -> str:
418
+ """Generate DOCTYPE."""
419
+ self.emit("_html.append('<!DOCTYPE html>')")
420
+ return ''
421
+
422
+ def visit_CommentNode(self, node: CommentNode) -> str:
423
+ """Generate HTML comment."""
424
+ content = getattr(node, 'content', '') or getattr(node, 'text', '') or ''
425
+ self.emit(f"_html.append('<!-- {content} -->')")
426
+ return ''
427
+
428
+ # =========================================================================
429
+ # Database
430
+ # =========================================================================
431
+
432
+ def visit_QueryNode(self, node: QueryNode) -> str:
433
+ """Generate database query."""
434
+ name = node.name or 'result'
435
+ datasource = getattr(node, 'datasource', 'default')
436
+
437
+ # Get SQL from content or children
438
+ sql_parts = []
439
+ params = []
440
+
441
+ # Check for raw SQL content
442
+ sql_content = getattr(node, 'sql', None) or getattr(node, 'content', None)
443
+ if sql_content:
444
+ sql_parts.append(sql_content.strip())
445
+
446
+ # Check children for params and additional SQL
447
+ for child in node.children or []:
448
+ child_type = type(child).__name__
449
+ if child_type == 'QueryParamNode':
450
+ value = getattr(child, 'value', None)
451
+ if value:
452
+ params.append(self.transpile_expression(str(value)))
453
+ sql_parts.append('?')
454
+ elif child_type == 'TextNode':
455
+ text = getattr(child, 'content', '') or getattr(child, 'text', '')
456
+ if text.strip():
457
+ sql_parts.append(text.strip())
458
+
459
+ sql = ' '.join(sql_parts).strip()
460
+
461
+ # Clean up SQL
462
+ sql = ' '.join(sql.split()) # Normalize whitespace
463
+
464
+ self.imports.add('from quantum.compiler.python.runtime import Database')
465
+
466
+ self.emit(f'# Query: {name}')
467
+ self.emit(f'_db = Database.get_connection({repr(datasource)})')
468
+
469
+ if params:
470
+ self.emit(f'{name} = _db.execute({repr(sql)}, [{", ".join(params)}]).fetchall()')
471
+ else:
472
+ self.emit(f'{name} = _db.execute({repr(sql)}).fetchall()')
473
+
474
+ self.declare_var(name)
475
+ return name
476
+
477
+ def visit_QueryParamNode(self, node: QueryParamNode) -> str:
478
+ """Query params are handled in visit_QueryNode."""
479
+ return ''
480
+
481
+ # =========================================================================
482
+ # Actions & Redirects
483
+ # =========================================================================
484
+
485
+ def visit_ActionNode(self, node: ActionNode) -> str:
486
+ """Generate action handler."""
487
+ name = getattr(node, 'name', 'action')
488
+ method = getattr(node, 'method', 'POST')
489
+
490
+ self.imports.add('from quantum.compiler.python.runtime import request, redirect, flash')
491
+
492
+ self.emit(f'# Action: {name}')
493
+ self.emit(f'def action_{name}():')
494
+ self.indent()
495
+ self.emit(f'"""Handle {method} action"""')
496
+
497
+ self.push_scope()
498
+
499
+ for child in node.children or []:
500
+ self.visit(child)
501
+
502
+ self.pop_scope()
503
+ self.dedent()
504
+ self.emit_blank()
505
+
506
+ return f'action_{name}'
507
+
508
+ def visit_RedirectNode(self, node: RedirectNode) -> str:
509
+ """Generate redirect."""
510
+ url = getattr(node, 'url', '/') or getattr(node, 'to', '/')
511
+ status = getattr(node, 'status', 302) or getattr(node, 'code', 302)
512
+
513
+ self.imports.add('from quantum.compiler.python.runtime import redirect')
514
+
515
+ if self.is_expression(str(url)):
516
+ url_expr = self.transpile_expression(str(url))
517
+ self.emit(f'return redirect({url_expr}, code={status})')
518
+ else:
519
+ self.emit(f'return redirect({repr(url)}, code={status})')
520
+
521
+ return ''
522
+
523
+ def visit_FlashNode(self, node: FlashNode) -> str:
524
+ """Generate flash message."""
525
+ message = getattr(node, 'message', '')
526
+ category = getattr(node, 'category', 'info') or getattr(node, 'type', 'info')
527
+
528
+ self.imports.add('from quantum.compiler.python.runtime import flash')
529
+
530
+ if self.is_expression(str(message)):
531
+ msg_expr = self.transpile_expression(str(message))
532
+ self.emit(f'flash({msg_expr}, {repr(category)})')
533
+ else:
534
+ self.emit(f'flash({repr(message)}, {repr(category)})')
535
+
536
+ return ''
537
+
538
+ # =========================================================================
539
+ # Imports & Python Integration
540
+ # =========================================================================
541
+
542
+ def visit_ImportNode(self, node: ImportNode) -> str:
543
+ """Generate component import."""
544
+ name = getattr(node, 'name', None)
545
+ path = getattr(node, 'path', None) or getattr(node, 'from', None)
546
+
547
+ if path and name:
548
+ # Convert path to Python import
549
+ module_path = path.replace('/', '.').replace('.q', '')
550
+ self.imports.add(f'from {module_path} import {name}')
551
+
552
+ return ''
553
+
554
+ def visit_PyImportNode(self, node: PyImportNode) -> str:
555
+ """Generate Python import."""
556
+ module = getattr(node, 'module', '')
557
+ names = getattr(node, 'names', '') or getattr(node, 'import', '')
558
+ alias = getattr(node, 'as', None) or getattr(node, 'alias', None)
559
+
560
+ if names:
561
+ if isinstance(names, str):
562
+ names = names.replace(' ', '')
563
+ self.imports.add(f'from {module} import {names}')
564
+ elif alias:
565
+ self.imports.add(f'import {module} as {alias}')
566
+ else:
567
+ self.imports.add(f'import {module}')
568
+
569
+ return ''
570
+
571
+ def visit_PythonNode(self, node: PythonNode) -> str:
572
+ """Generate inline Python code."""
573
+ code = getattr(node, 'code', '') or getattr(node, 'content', '')
574
+
575
+ if code:
576
+ # Emit raw Python code
577
+ for line in code.strip().split('\n'):
578
+ self.emit(line)
579
+
580
+ return ''
581
+
582
+ # =========================================================================
583
+ # Component Calls
584
+ # =========================================================================
585
+
586
+ def visit_ComponentCallNode(self, node: ComponentCallNode) -> str:
587
+ """Generate component instantiation."""
588
+ name = getattr(node, 'name', 'Component')
589
+ props = getattr(node, 'props', {}) or getattr(node, 'attributes', {}) or {}
590
+
591
+ # Build props
592
+ prop_parts = []
593
+ for key, value in props.items():
594
+ if self.is_expression(str(value)):
595
+ expr = self.transpile_expression(str(value))
596
+ prop_parts.append(f'{key}={expr}')
597
+ else:
598
+ prop_parts.append(f'{key}={repr(value)}')
599
+
600
+ props_str = ', '.join(prop_parts)
601
+
602
+ if self.in_render:
603
+ self.emit(f'_html.append({name}({props_str}).render())')
604
+ else:
605
+ self.emit(f'{name}({props_str})')
606
+
607
+ return ''
608
+
609
+ def visit_SlotNode(self, node: SlotNode) -> str:
610
+ """Generate slot placeholder."""
611
+ name = getattr(node, 'name', 'default')
612
+
613
+ self.emit(f"# Slot: {name}")
614
+ self.emit(f"if '{name}' in self.props.get('_slots', {{}}):")
615
+ self.indent()
616
+ self.emit(f"_html.append(self.props['_slots']['{name}'])")
617
+ self.dedent()
618
+
619
+ # Default content
620
+ if node.children:
621
+ self.emit('else:')
622
+ self.indent()
623
+ for child in node.children:
624
+ self.visit(child)
625
+ self.dedent()
626
+
627
+ return ''
628
+
629
+ # =========================================================================
630
+ # Data Nodes (Phase 2)
631
+ # =========================================================================
632
+
633
+ def visit_DataNode(self, node) -> str:
634
+ """Generate data import code."""
635
+ name = getattr(node, 'name', 'data')
636
+ source = getattr(node, 'source', '')
637
+ data_type = getattr(node, 'data_type', None) or getattr(node, 'type', 'json')
638
+
639
+ self.imports.add('from quantum.compiler.python.runtime import load_data')
640
+
641
+ source_expr = self.transpile_expression(str(source)) if self.is_expression(str(source)) else repr(source)
642
+
643
+ self.emit(f'# Data import: {name}')
644
+ self.emit(f'{name} = load_data({source_expr}, type={repr(data_type)})')
645
+ self.declare_var(name)
646
+
647
+ return name
648
+
649
+ def visit_InvokeNode(self, node) -> str:
650
+ """Generate HTTP/service invocation."""
651
+ name = getattr(node, 'name', 'result')
652
+ url = getattr(node, 'url', None)
653
+ method = getattr(node, 'method', 'GET')
654
+ function = getattr(node, 'function', None)
655
+ component = getattr(node, 'component', None)
656
+
657
+ self.imports.add('from quantum.compiler.python.runtime import invoke')
658
+
659
+ self.emit(f'# Invoke: {name}')
660
+
661
+ if function:
662
+ # Local function call
663
+ func_expr = self.transpile_expression(str(function)) if self.is_expression(str(function)) else function
664
+ self.emit(f'{name} = {func_expr}()')
665
+ elif component:
666
+ # Component instantiation
667
+ comp_expr = self.transpile_expression(str(component)) if self.is_expression(str(component)) else component
668
+ self.emit(f'{name} = {comp_expr}().render()')
669
+ elif url:
670
+ # HTTP request
671
+ url_expr = self.transpile_expression(str(url)) if self.is_expression(str(url)) else repr(url)
672
+
673
+ # Build headers and body from children
674
+ headers = {}
675
+ body = None
676
+ for child in getattr(node, 'children', []) or []:
677
+ child_type = type(child).__name__
678
+ if child_type == 'InvokeHeaderNode':
679
+ headers[getattr(child, 'name', '')] = getattr(child, 'value', '')
680
+ elif child_type == 'InvokeBodyNode':
681
+ body = getattr(child, 'content', None)
682
+
683
+ headers_str = repr(headers) if headers else '{}'
684
+ body_str = self.transpile_expression(str(body)) if body and self.is_expression(str(body)) else repr(body) if body else 'None'
685
+
686
+ self.emit(f'{name} = invoke({url_expr}, method={repr(method)}, headers={headers_str}, body={body_str})')
687
+ else:
688
+ self.emit(f'{name} = None # No target specified')
689
+
690
+ self.declare_var(name)
691
+ return name
692
+
693
+ def visit_FileNode(self, node) -> str:
694
+ """Generate file operation code."""
695
+ action = getattr(node, 'action', 'upload')
696
+ file_var = getattr(node, 'file', '')
697
+ destination = getattr(node, 'destination', './uploads/')
698
+ result_var = getattr(node, 'result', None) or getattr(node, 'name', 'file_result')
699
+
700
+ self.imports.add('from quantum.compiler.python.runtime import handle_file')
701
+
702
+ file_expr = self.transpile_expression(str(file_var)) if self.is_expression(str(file_var)) else file_var
703
+ dest_expr = self.transpile_expression(str(destination)) if self.is_expression(str(destination)) else repr(destination)
704
+
705
+ self.emit(f'# File operation: {action}')
706
+ self.emit(f'{result_var} = handle_file({file_expr}, action={repr(action)}, destination={dest_expr})')
707
+ self.declare_var(result_var)
708
+
709
+ return result_var
710
+
711
+ def visit_MailNode(self, node) -> str:
712
+ """Generate email sending code."""
713
+ to = getattr(node, 'to', '')
714
+ from_addr = getattr(node, 'from_', '') or getattr(node, 'sender', '')
715
+ subject = getattr(node, 'subject', '')
716
+ body_content = None
717
+
718
+ # Get body from children
719
+ for child in getattr(node, 'children', []) or []:
720
+ if hasattr(child, 'content'):
721
+ body_content = child.content
722
+ break
723
+ elif hasattr(child, 'text'):
724
+ body_content = child.text
725
+ break
726
+
727
+ self.imports.add('from quantum.compiler.python.runtime import send_mail')
728
+
729
+ to_expr = self.transpile_expression(str(to)) if self.is_expression(str(to)) else repr(to)
730
+ from_expr = self.transpile_expression(str(from_addr)) if self.is_expression(str(from_addr)) else repr(from_addr)
731
+ subject_expr = self.transpile_expression(str(subject)) if self.is_expression(str(subject)) else repr(subject)
732
+ body_expr = self.transpile_expression(str(body_content)) if body_content and self.is_expression(str(body_content)) else repr(body_content or '')
733
+
734
+ self.emit(f'# Send email')
735
+ self.emit(f'send_mail(to={to_expr}, from_addr={from_expr}, subject={subject_expr}, body={body_expr})')
736
+
737
+ return ''
738
+
739
+ # =========================================================================
740
+ # Async Nodes (Phase 3)
741
+ # =========================================================================
742
+
743
+ def visit_JobNode(self, node) -> str:
744
+ """Generate job queue code."""
745
+ name = getattr(node, 'name', 'job')
746
+ queue = getattr(node, 'queue', 'default')
747
+ action = getattr(node, 'action', 'define')
748
+ priority = getattr(node, 'priority', 0)
749
+ delay = getattr(node, 'delay', None)
750
+
751
+ self.imports.add('from quantum.compiler.python.runtime import JobQueue')
752
+
753
+ self.emit(f'# Job: {name}')
754
+
755
+ if action == 'define':
756
+ self.emit(f'@JobQueue.task(queue={repr(queue)}, priority={priority})')
757
+ self.emit(f'def job_{name}(**params):')
758
+ self.indent()
759
+ self.emit('"""Job task"""')
760
+ self.push_scope()
761
+ for child in getattr(node, 'children', []) or []:
762
+ self.visit(child)
763
+ self.pop_scope()
764
+ self.dedent()
765
+ self.emit_blank()
766
+ elif action == 'dispatch':
767
+ delay_str = f', delay={repr(delay)}' if delay else ''
768
+ self.emit(f'JobQueue.dispatch({repr(name)}{delay_str})')
769
+ elif action == 'cancel':
770
+ self.emit(f'JobQueue.cancel({repr(name)})')
771
+
772
+ return f'job_{name}'
773
+
774
+ def visit_ScheduleNode(self, node) -> str:
775
+ """Generate scheduled task code."""
776
+ name = getattr(node, 'name', 'task')
777
+ interval = getattr(node, 'interval', None)
778
+ cron = getattr(node, 'cron', None)
779
+ at = getattr(node, 'at', None)
780
+ enabled = getattr(node, 'enabled', True)
781
+
782
+ self.imports.add('from quantum.compiler.python.runtime import Scheduler')
783
+
784
+ self.emit(f'# Scheduled task: {name}')
785
+
786
+ schedule_args = []
787
+ if interval:
788
+ schedule_args.append(f'interval={repr(interval)}')
789
+ elif cron:
790
+ schedule_args.append(f'cron={repr(cron)}')
791
+ elif at:
792
+ schedule_args.append(f'at={repr(at)}')
793
+
794
+ schedule_args.append(f'enabled={enabled}')
795
+ args_str = ', '.join(schedule_args)
796
+
797
+ self.emit(f'@Scheduler.schedule({args_str})')
798
+ self.emit(f'def scheduled_{name}():')
799
+ self.indent()
800
+ self.emit(f'"""Scheduled task: {name}"""')
801
+ self.push_scope()
802
+ for child in getattr(node, 'children', []) or []:
803
+ self.visit(child)
804
+ self.pop_scope()
805
+ self.dedent()
806
+ self.emit_blank()
807
+
808
+ return f'scheduled_{name}'
809
+
810
+ def visit_ThreadNode(self, node) -> str:
811
+ """Generate async thread code."""
812
+ name = getattr(node, 'name', 'thread')
813
+ action = getattr(node, 'action', 'run')
814
+ priority = getattr(node, 'priority', 'normal')
815
+ timeout = getattr(node, 'timeout', None)
816
+ on_complete = getattr(node, 'on_complete', None) or getattr(node, 'onComplete', None)
817
+
818
+ self.imports.add('from quantum.compiler.python.runtime import AsyncThread')
819
+
820
+ self.emit(f'# Thread: {name}')
821
+
822
+ if action == 'run':
823
+ timeout_str = f', timeout={repr(timeout)}' if timeout else ''
824
+ callback_str = f', on_complete={on_complete}' if on_complete else ''
825
+
826
+ self.emit(f'def thread_{name}_work():')
827
+ self.indent()
828
+ self.emit('"""Thread work function"""')
829
+ self.push_scope()
830
+ for child in getattr(node, 'children', []) or []:
831
+ self.visit(child)
832
+ self.pop_scope()
833
+ self.dedent()
834
+ self.emit_blank()
835
+
836
+ self.emit(f'thread_{name} = AsyncThread({repr(name)}, thread_{name}_work{timeout_str}{callback_str})')
837
+ self.emit(f'thread_{name}.start()')
838
+ elif action == 'join':
839
+ self.emit(f'thread_{name}.join()')
840
+ elif action == 'terminate':
841
+ self.emit(f'thread_{name}.terminate()')
842
+
843
+ return f'thread_{name}'
844
+
845
+ def visit_ParallelNode(self, node) -> str:
846
+ """Generate parallel execution code."""
847
+ name = getattr(node, 'name', 'parallel')
848
+ max_workers = getattr(node, 'max_workers', None) or getattr(node, 'maxWorkers', 4)
849
+
850
+ self.imports.add('from quantum.compiler.python.runtime import parallel_execute')
851
+
852
+ self.emit(f'# Parallel execution: {name}')
853
+ self.emit(f'def parallel_{name}_tasks():')
854
+ self.indent()
855
+ self.emit('tasks = []')
856
+
857
+ # Collect child tasks
858
+ for idx, child in enumerate(getattr(node, 'children', []) or []):
859
+ child_type = type(child).__name__
860
+ if child_type == 'ThreadNode' or child_type == 'TaskNode':
861
+ self.emit(f'def _task_{idx}():')
862
+ self.indent()
863
+ self.visit(child)
864
+ self.dedent()
865
+ self.emit(f'tasks.append(_task_{idx})')
866
+ else:
867
+ self.visit(child)
868
+
869
+ self.emit(f'return parallel_execute(tasks, max_workers={max_workers})')
870
+ self.dedent()
871
+ self.emit_blank()
872
+ self.emit(f'{name}_results = parallel_{name}_tasks()')
873
+ self.declare_var(f'{name}_results')
874
+
875
+ return name
876
+
877
+ # =========================================================================
878
+ # Comments
879
+ # =========================================================================
880
+
881
+ def emit_comment(self, comment: str):
882
+ """Emit Python comment."""
883
+ self.emit(f'# {comment}')