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,367 @@
1
+ """
2
+ Base Code Generator
3
+ ===================
4
+
5
+ Abstract base class for all code generators (Python, JavaScript, etc.)
6
+ Uses visitor pattern to traverse AST and generate code.
7
+ """
8
+
9
+ from abc import ABC, abstractmethod
10
+ from typing import Any, Dict, List, Optional, Set
11
+ from dataclasses import dataclass, field
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ # Add parent to path for imports
16
+
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
+ @dataclass
26
+ class Scope:
27
+ """Represents a variable scope."""
28
+ variables: Dict[str, str] = field(default_factory=dict) # name -> type hint
29
+ parent: Optional['Scope'] = None
30
+
31
+ def declare(self, name: str, type_hint: str = 'Any'):
32
+ """Declare a variable in this scope."""
33
+ self.variables[name] = type_hint
34
+
35
+ def lookup(self, name: str) -> Optional[str]:
36
+ """Look up a variable, checking parent scopes."""
37
+ if name in self.variables:
38
+ return self.variables[name]
39
+ if self.parent:
40
+ return self.parent.lookup(name)
41
+ return None
42
+
43
+ def is_declared(self, name: str) -> bool:
44
+ """Check if variable is declared in any scope."""
45
+ return self.lookup(name) is not None
46
+
47
+
48
+ @dataclass
49
+ class GeneratorContext:
50
+ """Context for code generation."""
51
+ component_name: str = ''
52
+ function_name: str = ''
53
+ in_loop: bool = False
54
+ in_conditional: bool = False
55
+ in_html: bool = False
56
+ imports: Set[str] = field(default_factory=set)
57
+ functions: List[str] = field(default_factory=list)
58
+ html_var: str = '_html'
59
+
60
+
61
+ class CodeGenerator(ABC):
62
+ """
63
+ Abstract base class for code generators.
64
+
65
+ Implements visitor pattern for AST traversal and provides
66
+ common utilities for code emission and scope management.
67
+ """
68
+
69
+ def __init__(self):
70
+ self.indent_level: int = 0
71
+ self.indent_str: str = " "
72
+ self.output_lines: List[str] = []
73
+ self.scope: Scope = Scope()
74
+ self.context: GeneratorContext = GeneratorContext()
75
+ self._visitor_cache: Dict[str, callable] = {}
76
+
77
+ # =========================================================================
78
+ # Abstract methods - must be implemented by subclasses
79
+ # =========================================================================
80
+
81
+ @abstractmethod
82
+ def file_extension(self) -> str:
83
+ """Return the file extension for this generator."""
84
+ pass
85
+
86
+ @abstractmethod
87
+ def generate_header(self) -> str:
88
+ """Generate file header (imports, comments, etc.)"""
89
+ pass
90
+
91
+ @abstractmethod
92
+ def generate_footer(self) -> str:
93
+ """Generate file footer."""
94
+ pass
95
+
96
+ @abstractmethod
97
+ def transpile_expression(self, expr: str) -> str:
98
+ """Transpile a Quantum expression to target language."""
99
+ pass
100
+
101
+ # =========================================================================
102
+ # Core generation methods
103
+ # =========================================================================
104
+
105
+ def generate(self, node: QuantumNode) -> str:
106
+ """
107
+ Generate code for an AST node and return the complete output.
108
+
109
+ Args:
110
+ node: The root AST node to generate code for
111
+
112
+ Returns:
113
+ Complete generated source code as a string
114
+ """
115
+ self.output_lines = []
116
+ self.indent_level = 0
117
+ self.scope = Scope()
118
+ self.context = GeneratorContext()
119
+
120
+ # Generate header
121
+ header = self.generate_header()
122
+ if header:
123
+ self.output_lines.append(header)
124
+
125
+ # Visit the AST
126
+ self.visit(node)
127
+
128
+ # Generate footer
129
+ footer = self.generate_footer()
130
+ if footer:
131
+ self.output_lines.append(footer)
132
+
133
+ return '\n'.join(self.output_lines)
134
+
135
+ def visit(self, node: QuantumNode) -> Optional[str]:
136
+ """
137
+ Visit an AST node using the visitor pattern.
138
+
139
+ Dispatches to visit_NodeType method based on node class.
140
+
141
+ Args:
142
+ node: The AST node to visit
143
+
144
+ Returns:
145
+ Optional string result from the visitor
146
+ """
147
+ if node is None:
148
+ return None
149
+
150
+ node_type = type(node).__name__
151
+
152
+ # Check cache first
153
+ if node_type not in self._visitor_cache:
154
+ method_name = f'visit_{node_type}'
155
+ visitor = getattr(self, method_name, None)
156
+ self._visitor_cache[node_type] = visitor
157
+
158
+ visitor = self._visitor_cache[node_type]
159
+
160
+ if visitor:
161
+ return visitor(node)
162
+ else:
163
+ return self.generic_visit(node)
164
+
165
+ def generic_visit(self, node: QuantumNode) -> Optional[str]:
166
+ """
167
+ Fallback visitor for unhandled node types.
168
+
169
+ Override in subclass to provide custom fallback behavior.
170
+ """
171
+ node_type = type(node).__name__
172
+ self.emit_comment(f"TODO: Unhandled node type: {node_type}")
173
+
174
+ # Try to visit children if they exist
175
+ if hasattr(node, 'children') and node.children:
176
+ for child in node.children:
177
+ self.visit(child)
178
+
179
+ return None
180
+
181
+ def visit_children(self, node: QuantumNode) -> List[Optional[str]]:
182
+ """Visit all children of a node."""
183
+ results = []
184
+ if hasattr(node, 'children') and node.children:
185
+ for child in node.children:
186
+ result = self.visit(child)
187
+ results.append(result)
188
+ return results
189
+
190
+ # =========================================================================
191
+ # Code emission utilities
192
+ # =========================================================================
193
+
194
+ def emit(self, code: str):
195
+ """
196
+ Emit a line of code with proper indentation.
197
+
198
+ Args:
199
+ code: The code to emit (without leading indentation)
200
+ """
201
+ if code:
202
+ indent = self.indent_str * self.indent_level
203
+ self.output_lines.append(f"{indent}{code}")
204
+ else:
205
+ self.output_lines.append('')
206
+
207
+ def emit_raw(self, code: str):
208
+ """Emit code without any indentation."""
209
+ self.output_lines.append(code)
210
+
211
+ def emit_blank(self):
212
+ """Emit a blank line."""
213
+ self.output_lines.append('')
214
+
215
+ def emit_comment(self, comment: str):
216
+ """Emit a comment (format depends on target language)."""
217
+ # Default to Python-style comments
218
+ self.emit(f"# {comment}")
219
+
220
+ def emit_block(self, lines: List[str]):
221
+ """Emit multiple lines with current indentation."""
222
+ for line in lines:
223
+ self.emit(line)
224
+
225
+ # =========================================================================
226
+ # Indentation management
227
+ # =========================================================================
228
+
229
+ def indent(self):
230
+ """Increase indentation level."""
231
+ self.indent_level += 1
232
+
233
+ def dedent(self):
234
+ """Decrease indentation level."""
235
+ self.indent_level = max(0, self.indent_level - 1)
236
+
237
+ class indented:
238
+ """Context manager for indented blocks."""
239
+
240
+ def __init__(self, generator: 'CodeGenerator'):
241
+ self.generator = generator
242
+
243
+ def __enter__(self):
244
+ self.generator.indent()
245
+ return self
246
+
247
+ def __exit__(self, *args):
248
+ self.generator.dedent()
249
+
250
+ # =========================================================================
251
+ # Scope management
252
+ # =========================================================================
253
+
254
+ def push_scope(self):
255
+ """Enter a new variable scope."""
256
+ self.scope = Scope(parent=self.scope)
257
+
258
+ def pop_scope(self):
259
+ """Exit current scope and return to parent."""
260
+ if self.scope.parent:
261
+ self.scope = self.scope.parent
262
+
263
+ def declare_var(self, name: str, type_hint: str = 'Any'):
264
+ """Declare a variable in current scope."""
265
+ self.scope.declare(name, type_hint)
266
+
267
+ def is_declared(self, name: str) -> bool:
268
+ """Check if variable is declared in any visible scope."""
269
+ return self.scope.is_declared(name)
270
+
271
+ class scoped:
272
+ """Context manager for scoped blocks."""
273
+
274
+ def __init__(self, generator: 'CodeGenerator'):
275
+ self.generator = generator
276
+
277
+ def __enter__(self):
278
+ self.generator.push_scope()
279
+ return self
280
+
281
+ def __exit__(self, *args):
282
+ self.generator.pop_scope()
283
+
284
+ # =========================================================================
285
+ # Expression utilities
286
+ # =========================================================================
287
+
288
+ def extract_expression(self, value: str) -> str:
289
+ """
290
+ Extract expression from Quantum {expr} format.
291
+
292
+ Args:
293
+ value: Value that may contain {expression}
294
+
295
+ Returns:
296
+ The expression without braces, or original if no braces
297
+ """
298
+ if not value:
299
+ return ''
300
+
301
+ value = str(value).strip()
302
+
303
+ if value.startswith('{') and value.endswith('}'):
304
+ return value[1:-1].strip()
305
+
306
+ return value
307
+
308
+ def is_expression(self, value: str) -> bool:
309
+ """Check if value is a Quantum expression {expr}."""
310
+ if not value:
311
+ return False
312
+ value = str(value).strip()
313
+ return value.startswith('{') and value.endswith('}')
314
+
315
+ def is_literal(self, value: str) -> bool:
316
+ """Check if value is a literal (number, string, boolean)."""
317
+ if not value:
318
+ return False
319
+
320
+ value = str(value).strip()
321
+
322
+ # Check for numeric
323
+ try:
324
+ float(value)
325
+ return True
326
+ except ValueError:
327
+ pass
328
+
329
+ # Check for boolean
330
+ if value.lower() in ('true', 'false'):
331
+ return True
332
+
333
+ # Check for quoted string
334
+ if (value.startswith('"') and value.endswith('"')) or \
335
+ (value.startswith("'") and value.endswith("'")):
336
+ return True
337
+
338
+ return False
339
+
340
+ # =========================================================================
341
+ # Helper methods
342
+ # =========================================================================
343
+
344
+ def sanitize_identifier(self, name: str) -> str:
345
+ """
346
+ Sanitize a name to be a valid identifier.
347
+
348
+ Args:
349
+ name: The name to sanitize
350
+
351
+ Returns:
352
+ A valid identifier string
353
+ """
354
+ import re
355
+ # Replace invalid characters with underscore
356
+ sanitized = re.sub(r'[^a-zA-Z0-9_]', '_', name)
357
+ # Ensure doesn't start with number
358
+ if sanitized and sanitized[0].isdigit():
359
+ sanitized = '_' + sanitized
360
+ return sanitized or '_unnamed'
361
+
362
+ def generate_temp_var(self, prefix: str = '_temp') -> str:
363
+ """Generate a unique temporary variable name."""
364
+ if not hasattr(self, '_temp_counter'):
365
+ self._temp_counter = 0
366
+ self._temp_counter += 1
367
+ return f"{prefix}_{self._temp_counter}"
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Quantum Transpiler CLI
4
+ ======================
5
+
6
+ Command-line interface for compiling Quantum files.
7
+
8
+ Usage:
9
+ quantum compile app.q -o app.py
10
+ quantum compile app.q --target=javascript -o app.js
11
+ quantum compile src/ -o dist/ --watch
12
+ """
13
+
14
+ import sys
15
+ import os
16
+ import time
17
+ import argparse
18
+ from pathlib import Path
19
+ from typing import Optional
20
+
21
+ # Add src to path
22
+
23
+ from quantum.compiler.transpiler import Transpiler, CompilationResult
24
+
25
+
26
+ def create_parser() -> argparse.ArgumentParser:
27
+ """Create argument parser."""
28
+ parser = argparse.ArgumentParser(
29
+ prog='quantum compile',
30
+ description='Quantum Transpiler - Compile .q files to Python/JavaScript'
31
+ )
32
+
33
+ parser.add_argument(
34
+ 'source',
35
+ help='Source file or directory'
36
+ )
37
+
38
+ parser.add_argument(
39
+ '-o', '--output',
40
+ help='Output file or directory'
41
+ )
42
+
43
+ parser.add_argument(
44
+ '--target',
45
+ choices=['python', 'javascript', 'js'],
46
+ default='python',
47
+ help='Target language (default: python)'
48
+ )
49
+
50
+ parser.add_argument(
51
+ '--watch', '-w',
52
+ action='store_true',
53
+ help='Watch for file changes'
54
+ )
55
+
56
+ parser.add_argument(
57
+ '--optimize',
58
+ action='store_true',
59
+ help='Enable optimizations'
60
+ )
61
+
62
+ parser.add_argument(
63
+ '--sourcemap',
64
+ action='store_true',
65
+ help='Generate source maps'
66
+ )
67
+
68
+ parser.add_argument(
69
+ '--strict',
70
+ action='store_true',
71
+ help='Treat warnings as errors'
72
+ )
73
+
74
+ parser.add_argument(
75
+ '--check',
76
+ action='store_true',
77
+ help='Check only, do not generate output'
78
+ )
79
+
80
+ parser.add_argument(
81
+ '--quiet', '-q',
82
+ action='store_true',
83
+ help='Minimal output'
84
+ )
85
+
86
+ parser.add_argument(
87
+ '--verbose', '-v',
88
+ action='store_true',
89
+ help='Verbose output'
90
+ )
91
+
92
+ return parser
93
+
94
+
95
+ def compile_file(
96
+ source: Path,
97
+ output: Optional[Path],
98
+ transpiler: Transpiler,
99
+ check_only: bool = False,
100
+ quiet: bool = False,
101
+ verbose: bool = False
102
+ ) -> bool:
103
+ """
104
+ Compile a single file.
105
+
106
+ Returns:
107
+ True if successful
108
+ """
109
+ result = transpiler.compile_file(str(source))
110
+
111
+ if not result.success:
112
+ if not quiet:
113
+ print(f"ERROR: {source}")
114
+ for error in result.errors:
115
+ print(f" {error}")
116
+ return False
117
+
118
+ if check_only:
119
+ if not quiet:
120
+ print(f"OK: {source}")
121
+ return True
122
+
123
+ # Determine output path
124
+ if output is None:
125
+ ext = transpiler._get_generator().file_extension()
126
+ output = source.with_suffix(ext)
127
+
128
+ # Write output
129
+ output.parent.mkdir(parents=True, exist_ok=True)
130
+ output.write_text(result.code, encoding='utf-8')
131
+
132
+ if not quiet:
133
+ stats = result.stats or {}
134
+ time_ms = stats.get('compile_time_ms', 0)
135
+ print(f"OK: {source} -> {output} ({time_ms:.1f}ms)")
136
+
137
+ if verbose:
138
+ print(f" Lines: {stats.get('source_lines', 0)} -> {stats.get('output_lines', 0)}")
139
+
140
+ # Write source map
141
+ if result.sourcemap and transpiler.sourcemap:
142
+ map_path = output.with_suffix(output.suffix + '.map')
143
+ map_path.write_text(result.sourcemap, encoding='utf-8')
144
+ if verbose:
145
+ print(f" Source map: {map_path}")
146
+
147
+ # Warnings
148
+ if result.warnings and not quiet:
149
+ for warning in result.warnings:
150
+ print(f" WARNING: {warning}")
151
+
152
+ return True
153
+
154
+
155
+ def compile_directory(
156
+ source_dir: Path,
157
+ output_dir: Path,
158
+ transpiler: Transpiler,
159
+ check_only: bool = False,
160
+ quiet: bool = False,
161
+ verbose: bool = False
162
+ ) -> int:
163
+ """
164
+ Compile all .q files in a directory.
165
+
166
+ Returns:
167
+ Number of errors
168
+ """
169
+ errors = 0
170
+ compiled = 0
171
+
172
+ for q_file in source_dir.rglob('*.q'):
173
+ rel_path = q_file.relative_to(source_dir)
174
+ ext = transpiler._get_generator().file_extension()
175
+ output_file = output_dir / rel_path.with_suffix(ext)
176
+
177
+ if compile_file(q_file, output_file, transpiler, check_only, quiet, verbose):
178
+ compiled += 1
179
+ else:
180
+ errors += 1
181
+
182
+ if not quiet:
183
+ print(f"\nCompiled {compiled} file(s), {errors} error(s)")
184
+
185
+ return errors
186
+
187
+
188
+ def watch_directory(
189
+ source_dir: Path,
190
+ output_dir: Path,
191
+ transpiler: Transpiler,
192
+ quiet: bool = False,
193
+ verbose: bool = False
194
+ ):
195
+ """Watch directory for changes and recompile."""
196
+ try:
197
+ from watchdog.observers import Observer
198
+ from watchdog.events import FileSystemEventHandler, FileModifiedEvent, FileCreatedEvent
199
+ except ImportError:
200
+ print("ERROR: watchdog package required for --watch")
201
+ print(" Install: pip install watchdog")
202
+ sys.exit(1)
203
+
204
+ class QuantumHandler(FileSystemEventHandler):
205
+ def on_modified(self, event):
206
+ if event.is_directory:
207
+ return
208
+ if event.src_path.endswith('.q'):
209
+ self._compile(Path(event.src_path))
210
+
211
+ def on_created(self, event):
212
+ if event.is_directory:
213
+ return
214
+ if event.src_path.endswith('.q'):
215
+ self._compile(Path(event.src_path))
216
+
217
+ def _compile(self, q_file: Path):
218
+ rel_path = q_file.relative_to(source_dir)
219
+ ext = transpiler._get_generator().file_extension()
220
+ output_file = output_dir / rel_path.with_suffix(ext)
221
+ compile_file(q_file, output_file, transpiler, False, quiet, verbose)
222
+
223
+ # Initial compile
224
+ print("Initial compilation...")
225
+ compile_directory(source_dir, output_dir, transpiler, False, quiet, verbose)
226
+
227
+ # Start watching
228
+ print(f"\nWatching {source_dir} for changes... (Ctrl+C to stop)")
229
+
230
+ observer = Observer()
231
+ observer.schedule(QuantumHandler(), str(source_dir), recursive=True)
232
+ observer.start()
233
+
234
+ try:
235
+ while True:
236
+ time.sleep(1)
237
+ except KeyboardInterrupt:
238
+ observer.stop()
239
+ print("\nStopped.")
240
+
241
+ observer.join()
242
+
243
+
244
+ def main(argv=None):
245
+ """Main entry point."""
246
+ parser = create_parser()
247
+ args = parser.parse_args(argv)
248
+
249
+ # Normalize target
250
+ target = args.target
251
+ if target == 'js':
252
+ target = 'javascript'
253
+
254
+ # Create transpiler
255
+ transpiler = Transpiler(
256
+ target=target,
257
+ optimize=args.optimize,
258
+ sourcemap=args.sourcemap,
259
+ strict=args.strict
260
+ )
261
+
262
+ source = Path(args.source)
263
+
264
+ if not source.exists():
265
+ print(f"ERROR: Source not found: {source}")
266
+ sys.exit(1)
267
+
268
+ if source.is_file():
269
+ # Single file
270
+ output = Path(args.output) if args.output else None
271
+ success = compile_file(
272
+ source, output, transpiler,
273
+ args.check, args.quiet, args.verbose
274
+ )
275
+ sys.exit(0 if success else 1)
276
+
277
+ elif source.is_dir():
278
+ # Directory
279
+ if not args.output:
280
+ output_dir = source.parent / (source.name + '_compiled')
281
+ else:
282
+ output_dir = Path(args.output)
283
+
284
+ if args.watch:
285
+ watch_directory(source, output_dir, transpiler, args.quiet, args.verbose)
286
+ else:
287
+ errors = compile_directory(
288
+ source, output_dir, transpiler,
289
+ args.check, args.quiet, args.verbose
290
+ )
291
+ sys.exit(1 if errors > 0 else 0)
292
+
293
+
294
+ if __name__ == '__main__':
295
+ main()