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.
- quantum/__init__.py +1 -0
- quantum/cli/__init__.py +2 -0
- quantum/cli/commands/__init__.py +15 -0
- quantum/cli/commands/build.py +417 -0
- quantum/cli/commands/dev.py +185 -0
- quantum/cli/commands/docs.py +329 -0
- quantum/cli/commands/lint.py +523 -0
- quantum/cli/commands/migrate.py +527 -0
- quantum/cli/commands/new.py +622 -0
- quantum/cli/commands/serve.py +190 -0
- quantum/cli/commands/test.py +193 -0
- quantum/cli/deploy.py +810 -0
- quantum/cli/hot_reload.py +951 -0
- quantum/cli/jobs.py +356 -0
- quantum/cli/mq.py +582 -0
- quantum/cli/pkg.py +390 -0
- quantum/cli/runner.py +547 -0
- quantum/cli/server_process.py +159 -0
- quantum/cli/utils.py +334 -0
- quantum/compiler/__init__.py +30 -0
- quantum/compiler/base_generator.py +367 -0
- quantum/compiler/cli.py +295 -0
- quantum/compiler/expression_transformer.py +444 -0
- quantum/compiler/javascript/__init__.py +10 -0
- quantum/compiler/javascript/generator.py +659 -0
- quantum/compiler/optimizer.py +270 -0
- quantum/compiler/python/__init__.py +10 -0
- quantum/compiler/python/generator.py +883 -0
- quantum/compiler/python/runtime.py +863 -0
- quantum/compiler/transpiler.py +330 -0
- quantum/core/__init__.py +3 -0
- quantum/core/ast_nodes.py +2611 -0
- quantum/core/expression_diagnostics.py +87 -0
- quantum/core/expression_stdlib.py +148 -0
- quantum/core/expressions.py +591 -0
- quantum/core/features/agents/src/__init__.py +30 -0
- quantum/core/features/agents/src/ast_node.py +540 -0
- quantum/core/features/conditionals/src/__init__.py +8 -0
- quantum/core/features/conditionals/src/ast_node.py +68 -0
- quantum/core/features/data_fetching/src/__init__.py +21 -0
- quantum/core/features/data_fetching/src/ast_node.py +312 -0
- quantum/core/features/data_fetching/src/desktop_adapter.py +351 -0
- quantum/core/features/data_fetching/src/html_adapter.py +474 -0
- quantum/core/features/data_fetching/src/parser.py +225 -0
- quantum/core/features/data_import/src/__init__.py +0 -0
- quantum/core/features/data_import/src/ast_node.py +291 -0
- quantum/core/features/data_import/src/runtime.py +538 -0
- quantum/core/features/dump/src/__init__.py +12 -0
- quantum/core/features/dump/src/ast_node.py +106 -0
- quantum/core/features/dump/src/parser.py +61 -0
- quantum/core/features/dump/src/runtime.py +246 -0
- quantum/core/features/functions/src/__init__.py +8 -0
- quantum/core/features/functions/src/ast_node.py +149 -0
- quantum/core/features/game_engine_2d/src/__init__.py +19 -0
- quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
- quantum/core/features/game_engine_2d/src/parser.py +983 -0
- quantum/core/features/invocation/src/__init__.py +0 -0
- quantum/core/features/invocation/src/ast_node.py +146 -0
- quantum/core/features/invocation/src/runtime.py +327 -0
- quantum/core/features/knowledge_base/src/__init__.py +6 -0
- quantum/core/features/knowledge_base/src/ast_node.py +113 -0
- quantum/core/features/knowledge_base/src/parser.py +82 -0
- quantum/core/features/logging/src/__init__.py +12 -0
- quantum/core/features/logging/src/ast_node.py +111 -0
- quantum/core/features/logging/src/parser.py +50 -0
- quantum/core/features/logging/src/runtime.py +190 -0
- quantum/core/features/loops/src/__init__.py +8 -0
- quantum/core/features/loops/src/ast_node.py +60 -0
- quantum/core/features/query/src/__init__.py +0 -0
- quantum/core/features/query/src/database_service.py +322 -0
- quantum/core/features/query/src/query_validators.py +20 -0
- quantum/core/features/state_management/src/__init__.py +11 -0
- quantum/core/features/state_management/src/ast_node.py +228 -0
- quantum/core/features/terminal_engine/src/__init__.py +21 -0
- quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
- quantum/core/features/terminal_engine/src/parser.py +361 -0
- quantum/core/features/testing_engine/src/__init__.py +41 -0
- quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
- quantum/core/features/testing_engine/src/parser.py +604 -0
- quantum/core/features/theming/src/__init__.py +48 -0
- quantum/core/features/theming/src/ast_node.py +137 -0
- quantum/core/features/theming/src/presets.py +405 -0
- quantum/core/features/ui_engine/src/__init__.py +20 -0
- quantum/core/features/ui_engine/src/ast_nodes.py +1854 -0
- quantum/core/features/ui_engine/src/parser.py +1106 -0
- quantum/core/features/websocket/src/__init__.py +24 -0
- quantum/core/features/websocket/src/ast_node.py +247 -0
- quantum/core/html_compat.py +299 -0
- quantum/core/parser.py +1235 -0
- quantum/core/parser_registry.py +213 -0
- quantum/core/parsers/__init__.py +76 -0
- quantum/core/parsers/ai/__init__.py +12 -0
- quantum/core/parsers/ai/agent_parser.py +106 -0
- quantum/core/parsers/ai/knowledge_parser.py +86 -0
- quantum/core/parsers/ai/llm_parser.py +78 -0
- quantum/core/parsers/ai/team_parser.py +88 -0
- quantum/core/parsers/base.py +322 -0
- quantum/core/parsers/composition/__init__.py +10 -0
- quantum/core/parsers/composition/import_parser.py +50 -0
- quantum/core/parsers/composition/slot_parser.py +48 -0
- quantum/core/parsers/control_flow/__init__.py +11 -0
- quantum/core/parsers/control_flow/if_parser.py +68 -0
- quantum/core/parsers/control_flow/loop_parser.py +105 -0
- quantum/core/parsers/control_flow/set_parser.py +89 -0
- quantum/core/parsers/data/__init__.py +12 -0
- quantum/core/parsers/data/data_parser.py +217 -0
- quantum/core/parsers/data/invoke_parser.py +116 -0
- quantum/core/parsers/data/query_parser.py +188 -0
- quantum/core/parsers/data/transaction_parser.py +77 -0
- quantum/core/parsers/events/__init__.py +9 -0
- quantum/core/parsers/events/dispatch_event_parser.py +44 -0
- quantum/core/parsers/forms/__init__.py +11 -0
- quantum/core/parsers/forms/action_parser.py +54 -0
- quantum/core/parsers/forms/flash_parser.py +34 -0
- quantum/core/parsers/forms/redirect_parser.py +33 -0
- quantum/core/parsers/functions/__init__.py +11 -0
- quantum/core/parsers/functions/function_parser.py +137 -0
- quantum/core/parsers/functions/param_parser.py +66 -0
- quantum/core/parsers/functions/return_parser.py +31 -0
- quantum/core/parsers/html/__init__.py +10 -0
- quantum/core/parsers/html/component_call_parser.py +130 -0
- quantum/core/parsers/html/html_parser.py +115 -0
- quantum/core/parsers/jobs/__init__.py +11 -0
- quantum/core/parsers/jobs/job_parser.py +71 -0
- quantum/core/parsers/jobs/schedule_parser.py +62 -0
- quantum/core/parsers/jobs/thread_parser.py +57 -0
- quantum/core/parsers/messaging/__init__.py +17 -0
- quantum/core/parsers/messaging/message_ack_parser.py +30 -0
- quantum/core/parsers/messaging/message_nack_parser.py +30 -0
- quantum/core/parsers/messaging/message_parser.py +114 -0
- quantum/core/parsers/messaging/queue_parser.py +61 -0
- quantum/core/parsers/messaging/websocket_parser.py +121 -0
- quantum/core/parsers/persistence/__init__.py +9 -0
- quantum/core/parsers/persistence/persist_parser.py +64 -0
- quantum/core/parsers/routing/__init__.py +9 -0
- quantum/core/parsers/routing/route_parser.py +41 -0
- quantum/core/parsers/scripting/__init__.py +12 -0
- quantum/core/parsers/scripting/pyclass_parser.py +62 -0
- quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
- quantum/core/parsers/scripting/pyimport_parser.py +52 -0
- quantum/core/parsers/scripting/python_parser.py +49 -0
- quantum/core/parsers/services/__init__.py +12 -0
- quantum/core/parsers/services/dump_parser.py +52 -0
- quantum/core/parsers/services/file_parser.py +48 -0
- quantum/core/parsers/services/log_parser.py +46 -0
- quantum/core/parsers/services/mail_parser.py +65 -0
- quantum/core/tiers.py +82 -0
- quantum/packages/__init__.py +28 -0
- quantum/packages/manager.py +413 -0
- quantum/packages/manifest.py +351 -0
- quantum/packages/registry.py +399 -0
- quantum/packages/resolver.py +336 -0
- quantum/plugins/__init__.py +33 -0
- quantum/plugins/hooks.py +329 -0
- quantum/plugins/loader.py +479 -0
- quantum/plugins/manifest.py +336 -0
- quantum/plugins/registry.py +371 -0
- quantum/runtime/__init__.py +28 -0
- quantum/runtime/action_handler.py +443 -0
- quantum/runtime/adapters/__init__.py +88 -0
- quantum/runtime/adapters/memory_adapter.py +690 -0
- quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
- quantum/runtime/adapters/redis_adapter.py +582 -0
- quantum/runtime/adapters/sqlite_adapter.py +414 -0
- quantum/runtime/agent_service.py +1133 -0
- quantum/runtime/api_server.py +86 -0
- quantum/runtime/ast_cache.py +506 -0
- quantum/runtime/auth_service.py +267 -0
- quantum/runtime/component.py +990 -0
- quantum/runtime/component_composer.py +319 -0
- quantum/runtime/component_resolver.py +174 -0
- quantum/runtime/database_service.py +598 -0
- quantum/runtime/email_service.py +162 -0
- quantum/runtime/error_handler.py +295 -0
- quantum/runtime/execution_context.py +286 -0
- quantum/runtime/executor_registry.py +171 -0
- quantum/runtime/executors/__init__.py +71 -0
- quantum/runtime/executors/ai/__init__.py +12 -0
- quantum/runtime/executors/ai/agent_executor.py +217 -0
- quantum/runtime/executors/ai/knowledge_executor.py +114 -0
- quantum/runtime/executors/ai/llm_executor.py +153 -0
- quantum/runtime/executors/ai/team_executor.py +171 -0
- quantum/runtime/executors/base.py +262 -0
- quantum/runtime/executors/control_flow/__init__.py +11 -0
- quantum/runtime/executors/control_flow/if_executor.py +93 -0
- quantum/runtime/executors/control_flow/loop_executor.py +307 -0
- quantum/runtime/executors/control_flow/set_executor.py +412 -0
- quantum/runtime/executors/data/__init__.py +12 -0
- quantum/runtime/executors/data/data_executor.py +145 -0
- quantum/runtime/executors/data/invoke_executor.py +176 -0
- quantum/runtime/executors/data/query_executor.py +256 -0
- quantum/runtime/executors/data/transaction_executor.py +91 -0
- quantum/runtime/executors/jobs/__init__.py +11 -0
- quantum/runtime/executors/jobs/job_executor.py +190 -0
- quantum/runtime/executors/jobs/schedule_executor.py +132 -0
- quantum/runtime/executors/jobs/thread_executor.py +127 -0
- quantum/runtime/executors/messaging/__init__.py +17 -0
- quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
- quantum/runtime/executors/messaging/message_executor.py +174 -0
- quantum/runtime/executors/messaging/queue_executor.py +103 -0
- quantum/runtime/executors/messaging/websocket_executor.py +197 -0
- quantum/runtime/executors/scripting/__init__.py +11 -0
- quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
- quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
- quantum/runtime/executors/scripting/python_executor.py +249 -0
- quantum/runtime/executors/services/__init__.py +12 -0
- quantum/runtime/executors/services/dump_executor.py +72 -0
- quantum/runtime/executors/services/file_executor.py +89 -0
- quantum/runtime/executors/services/log_executor.py +77 -0
- quantum/runtime/executors/services/mail_executor.py +81 -0
- quantum/runtime/expression_cache.py +498 -0
- quantum/runtime/file_upload_service.py +326 -0
- quantum/runtime/function_registry.py +118 -0
- quantum/runtime/game_builder.py +166 -0
- quantum/runtime/game_code_generator.py +2371 -0
- quantum/runtime/game_templates.py +2006 -0
- quantum/runtime/godot_code_generator.py +4681 -0
- quantum/runtime/godot_templates.py +1449 -0
- quantum/runtime/job_executor.py +1599 -0
- quantum/runtime/knowledge_service.py +500 -0
- quantum/runtime/llm_cache.py +100 -0
- quantum/runtime/llm_providers.py +704 -0
- quantum/runtime/llm_service.py +287 -0
- quantum/runtime/logging_setup.py +140 -0
- quantum/runtime/message_broker.py +364 -0
- quantum/runtime/message_queue_service.py +571 -0
- quantum/runtime/param_validation.py +184 -0
- quantum/runtime/pypy_compat.py +315 -0
- quantum/runtime/python_bridge.py +698 -0
- quantum/runtime/query_validators.py +304 -0
- quantum/runtime/renderer.py +733 -0
- quantum/runtime/service_container.py +444 -0
- quantum/runtime/terminal_builder.py +76 -0
- quantum/runtime/terminal_code_generator.py +607 -0
- quantum/runtime/terminal_templates.py +243 -0
- quantum/runtime/testing_builder.py +77 -0
- quantum/runtime/testing_code_generator.py +833 -0
- quantum/runtime/testing_templates.py +85 -0
- quantum/runtime/ui_builder.py +188 -0
- quantum/runtime/ui_desktop_adapter.py +1730 -0
- quantum/runtime/ui_desktop_templates.py +307 -0
- quantum/runtime/ui_html_adapter.py +2691 -0
- quantum/runtime/ui_html_templates.py +2297 -0
- quantum/runtime/ui_mobile_adapter.py +1832 -0
- quantum/runtime/ui_mobile_templates.py +1003 -0
- quantum/runtime/ui_textual_adapter.py +1866 -0
- quantum/runtime/ui_textual_templates.py +45 -0
- quantum/runtime/ui_tokens.py +465 -0
- quantum/runtime/ui_validator.py +365 -0
- quantum/runtime/validators.py +256 -0
- quantum/runtime/web_server.py +1766 -0
- quantum/runtime/websocket_adapter.py +501 -0
- quantum/runtime/websocket_service.py +585 -0
- quantum/runtime/websocket_transport.py +289 -0
- quantum/runtime/wsgi.py +101 -0
- quantum/utils/__init__.py +1 -0
- quantum_framework-0.9.0.dist-info/METADATA +244 -0
- quantum_framework-0.9.0.dist-info/RECORD +262 -0
- quantum_framework-0.9.0.dist-info/WHEEL +5 -0
- quantum_framework-0.9.0.dist-info/entry_points.txt +2 -0
- quantum_framework-0.9.0.dist-info/licenses/LICENSE +21 -0
- 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}"
|
quantum/compiler/cli.py
ADDED
|
@@ -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()
|