quantum-framework 0.18.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 (333) hide show
  1. quantum/__init__.py +1 -0
  2. quantum/cli/__init__.py +2 -0
  3. quantum/cli/admin.py +131 -0
  4. quantum/cli/check.py +223 -0
  5. quantum/cli/commands/__init__.py +15 -0
  6. quantum/cli/commands/build.py +414 -0
  7. quantum/cli/commands/dev.py +185 -0
  8. quantum/cli/commands/docs.py +329 -0
  9. quantum/cli/commands/lint.py +523 -0
  10. quantum/cli/commands/migrate.py +548 -0
  11. quantum/cli/commands/new.py +622 -0
  12. quantum/cli/commands/serve.py +190 -0
  13. quantum/cli/commands/test.py +193 -0
  14. quantum/cli/deploy.py +810 -0
  15. quantum/cli/hot_reload.py +951 -0
  16. quantum/cli/jobs.py +356 -0
  17. quantum/cli/mq.py +582 -0
  18. quantum/cli/pkg.py +390 -0
  19. quantum/cli/runner.py +565 -0
  20. quantum/cli/server_process.py +159 -0
  21. quantum/cli/utils.py +334 -0
  22. quantum/compiler/__init__.py +30 -0
  23. quantum/compiler/base_generator.py +367 -0
  24. quantum/compiler/cli.py +295 -0
  25. quantum/compiler/expression_transformer.py +444 -0
  26. quantum/compiler/javascript/__init__.py +10 -0
  27. quantum/compiler/javascript/generator.py +659 -0
  28. quantum/compiler/optimizer.py +270 -0
  29. quantum/compiler/python/__init__.py +10 -0
  30. quantum/compiler/python/generator.py +883 -0
  31. quantum/compiler/python/runtime.py +863 -0
  32. quantum/compiler/transpiler.py +330 -0
  33. quantum/core/__init__.py +3 -0
  34. quantum/core/ast_nodes.py +2619 -0
  35. quantum/core/config_env.py +54 -0
  36. quantum/core/docs_links.py +8 -0
  37. quantum/core/expression_diagnostics.py +87 -0
  38. quantum/core/expression_stdlib.py +208 -0
  39. quantum/core/expressions.py +677 -0
  40. quantum/core/features/agents/src/__init__.py +30 -0
  41. quantum/core/features/agents/src/ast_node.py +540 -0
  42. quantum/core/features/conditionals/src/__init__.py +8 -0
  43. quantum/core/features/conditionals/src/ast_node.py +68 -0
  44. quantum/core/features/data_import/src/__init__.py +0 -0
  45. quantum/core/features/data_import/src/ast_node.py +292 -0
  46. quantum/core/features/data_import/src/runtime.py +533 -0
  47. quantum/core/features/dump/src/__init__.py +12 -0
  48. quantum/core/features/dump/src/ast_node.py +106 -0
  49. quantum/core/features/dump/src/parser.py +61 -0
  50. quantum/core/features/dump/src/runtime.py +246 -0
  51. quantum/core/features/functions/src/__init__.py +8 -0
  52. quantum/core/features/functions/src/ast_node.py +149 -0
  53. quantum/core/features/game_engine_2d/src/__init__.py +19 -0
  54. quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
  55. quantum/core/features/game_engine_2d/src/parser.py +983 -0
  56. quantum/core/features/invocation/src/__init__.py +0 -0
  57. quantum/core/features/invocation/src/ast_node.py +147 -0
  58. quantum/core/features/invocation/src/runtime.py +332 -0
  59. quantum/core/features/knowledge_base/src/__init__.py +6 -0
  60. quantum/core/features/knowledge_base/src/ast_node.py +113 -0
  61. quantum/core/features/knowledge_base/src/parser.py +82 -0
  62. quantum/core/features/logging/src/__init__.py +12 -0
  63. quantum/core/features/logging/src/ast_node.py +111 -0
  64. quantum/core/features/logging/src/parser.py +50 -0
  65. quantum/core/features/logging/src/runtime.py +190 -0
  66. quantum/core/features/loops/src/__init__.py +8 -0
  67. quantum/core/features/loops/src/ast_node.py +60 -0
  68. quantum/core/features/query/src/__init__.py +0 -0
  69. quantum/core/features/query/src/query_validators.py +20 -0
  70. quantum/core/features/state_management/src/__init__.py +11 -0
  71. quantum/core/features/state_management/src/ast_node.py +121 -0
  72. quantum/core/features/terminal_engine/src/__init__.py +21 -0
  73. quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
  74. quantum/core/features/terminal_engine/src/parser.py +361 -0
  75. quantum/core/features/testing_engine/src/__init__.py +41 -0
  76. quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
  77. quantum/core/features/testing_engine/src/parser.py +604 -0
  78. quantum/core/features/theming/src/__init__.py +48 -0
  79. quantum/core/features/theming/src/ast_node.py +137 -0
  80. quantum/core/features/theming/src/presets.py +405 -0
  81. quantum/core/features/ui_engine/src/__init__.py +20 -0
  82. quantum/core/features/ui_engine/src/ast_nodes.py +1913 -0
  83. quantum/core/features/ui_engine/src/parser.py +1192 -0
  84. quantum/core/features/websocket/src/__init__.py +24 -0
  85. quantum/core/features/websocket/src/ast_node.py +247 -0
  86. quantum/core/guards.py +53 -0
  87. quantum/core/html_compat.py +299 -0
  88. quantum/core/parser.py +1551 -0
  89. quantum/core/parser_registry.py +213 -0
  90. quantum/core/parsers/__init__.py +76 -0
  91. quantum/core/parsers/ai/__init__.py +12 -0
  92. quantum/core/parsers/ai/agent_parser.py +106 -0
  93. quantum/core/parsers/ai/knowledge_parser.py +86 -0
  94. quantum/core/parsers/ai/llm_parser.py +78 -0
  95. quantum/core/parsers/ai/team_parser.py +88 -0
  96. quantum/core/parsers/base.py +322 -0
  97. quantum/core/parsers/composition/__init__.py +10 -0
  98. quantum/core/parsers/composition/import_parser.py +50 -0
  99. quantum/core/parsers/composition/slot_parser.py +48 -0
  100. quantum/core/parsers/control_flow/__init__.py +11 -0
  101. quantum/core/parsers/control_flow/if_parser.py +71 -0
  102. quantum/core/parsers/control_flow/loop_parser.py +105 -0
  103. quantum/core/parsers/control_flow/set_parser.py +93 -0
  104. quantum/core/parsers/data/__init__.py +12 -0
  105. quantum/core/parsers/data/data_parser.py +229 -0
  106. quantum/core/parsers/data/invoke_parser.py +133 -0
  107. quantum/core/parsers/data/query_parser.py +213 -0
  108. quantum/core/parsers/data/transaction_parser.py +86 -0
  109. quantum/core/parsers/events/__init__.py +9 -0
  110. quantum/core/parsers/events/dispatch_event_parser.py +44 -0
  111. quantum/core/parsers/forms/__init__.py +11 -0
  112. quantum/core/parsers/forms/action_parser.py +79 -0
  113. quantum/core/parsers/forms/flash_parser.py +34 -0
  114. quantum/core/parsers/forms/redirect_parser.py +33 -0
  115. quantum/core/parsers/functions/__init__.py +11 -0
  116. quantum/core/parsers/functions/function_parser.py +98 -0
  117. quantum/core/parsers/functions/param_parser.py +73 -0
  118. quantum/core/parsers/functions/return_parser.py +31 -0
  119. quantum/core/parsers/html/__init__.py +10 -0
  120. quantum/core/parsers/html/component_call_parser.py +130 -0
  121. quantum/core/parsers/html/html_parser.py +115 -0
  122. quantum/core/parsers/jobs/__init__.py +11 -0
  123. quantum/core/parsers/jobs/job_parser.py +71 -0
  124. quantum/core/parsers/jobs/schedule_parser.py +62 -0
  125. quantum/core/parsers/jobs/thread_parser.py +57 -0
  126. quantum/core/parsers/messaging/__init__.py +17 -0
  127. quantum/core/parsers/messaging/message_ack_parser.py +30 -0
  128. quantum/core/parsers/messaging/message_nack_parser.py +30 -0
  129. quantum/core/parsers/messaging/message_parser.py +114 -0
  130. quantum/core/parsers/messaging/queue_parser.py +61 -0
  131. quantum/core/parsers/messaging/websocket_parser.py +121 -0
  132. quantum/core/parsers/routing/__init__.py +9 -0
  133. quantum/core/parsers/routing/route_parser.py +41 -0
  134. quantum/core/parsers/scripting/__init__.py +12 -0
  135. quantum/core/parsers/scripting/pyclass_parser.py +62 -0
  136. quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
  137. quantum/core/parsers/scripting/pyimport_parser.py +52 -0
  138. quantum/core/parsers/scripting/python_parser.py +49 -0
  139. quantum/core/parsers/services/__init__.py +12 -0
  140. quantum/core/parsers/services/dump_parser.py +52 -0
  141. quantum/core/parsers/services/file_parser.py +48 -0
  142. quantum/core/parsers/services/log_parser.py +46 -0
  143. quantum/core/parsers/services/mail_parser.py +65 -0
  144. quantum/core/tiers.py +164 -0
  145. quantum/core/xml_lines.py +81 -0
  146. quantum/packages/__init__.py +28 -0
  147. quantum/packages/manager.py +413 -0
  148. quantum/packages/manifest.py +351 -0
  149. quantum/packages/registry.py +399 -0
  150. quantum/packages/resolver.py +336 -0
  151. quantum/plugins/__init__.py +33 -0
  152. quantum/plugins/hooks.py +329 -0
  153. quantum/plugins/loader.py +479 -0
  154. quantum/plugins/manifest.py +336 -0
  155. quantum/plugins/registry.py +371 -0
  156. quantum/runtime/__init__.py +25 -0
  157. quantum/runtime/action_handler.py +532 -0
  158. quantum/runtime/adapters/__init__.py +88 -0
  159. quantum/runtime/adapters/memory_adapter.py +690 -0
  160. quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
  161. quantum/runtime/adapters/redis_adapter.py +582 -0
  162. quantum/runtime/adapters/sqlite_adapter.py +414 -0
  163. quantum/runtime/agent_service.py +1133 -0
  164. quantum/runtime/ast_cache.py +506 -0
  165. quantum/runtime/auth_service.py +267 -0
  166. quantum/runtime/component.py +1048 -0
  167. quantum/runtime/component_composer.py +133 -0
  168. quantum/runtime/component_resolver.py +174 -0
  169. quantum/runtime/conditions.py +54 -0
  170. quantum/runtime/database_service.py +595 -0
  171. quantum/runtime/db_schema.py +100 -0
  172. quantum/runtime/dev_panel.py +178 -0
  173. quantum/runtime/email_service.py +162 -0
  174. quantum/runtime/error_handler.py +295 -0
  175. quantum/runtime/execution_context.py +283 -0
  176. quantum/runtime/executor_registry.py +178 -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 +264 -0
  184. quantum/runtime/executors/control_flow/__init__.py +12 -0
  185. quantum/runtime/executors/control_flow/if_executor.py +98 -0
  186. quantum/runtime/executors/control_flow/loop_executor.py +308 -0
  187. quantum/runtime/executors/control_flow/redirect_executor.py +43 -0
  188. quantum/runtime/executors/control_flow/set_executor.py +464 -0
  189. quantum/runtime/executors/data/__init__.py +12 -0
  190. quantum/runtime/executors/data/data_executor.py +156 -0
  191. quantum/runtime/executors/data/invoke_executor.py +228 -0
  192. quantum/runtime/executors/data/query_executor.py +290 -0
  193. quantum/runtime/executors/data/transaction_executor.py +91 -0
  194. quantum/runtime/executors/jobs/__init__.py +11 -0
  195. quantum/runtime/executors/jobs/job_executor.py +190 -0
  196. quantum/runtime/executors/jobs/schedule_executor.py +132 -0
  197. quantum/runtime/executors/jobs/thread_executor.py +127 -0
  198. quantum/runtime/executors/messaging/__init__.py +17 -0
  199. quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
  200. quantum/runtime/executors/messaging/message_executor.py +174 -0
  201. quantum/runtime/executors/messaging/queue_executor.py +103 -0
  202. quantum/runtime/executors/messaging/websocket_executor.py +197 -0
  203. quantum/runtime/executors/scripting/__init__.py +11 -0
  204. quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
  205. quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
  206. quantum/runtime/executors/scripting/python_executor.py +249 -0
  207. quantum/runtime/executors/services/__init__.py +12 -0
  208. quantum/runtime/executors/services/dump_executor.py +72 -0
  209. quantum/runtime/executors/services/file_executor.py +89 -0
  210. quantum/runtime/executors/services/log_executor.py +77 -0
  211. quantum/runtime/executors/services/mail_executor.py +81 -0
  212. quantum/runtime/expression_cache.py +498 -0
  213. quantum/runtime/file_upload_service.py +326 -0
  214. quantum/runtime/function_registry.py +118 -0
  215. quantum/runtime/game_builder.py +166 -0
  216. quantum/runtime/game_code_generator.py +2371 -0
  217. quantum/runtime/game_templates.py +2006 -0
  218. quantum/runtime/godot_code_generator.py +4681 -0
  219. quantum/runtime/godot_templates.py +1449 -0
  220. quantum/runtime/job_executor.py +1599 -0
  221. quantum/runtime/knowledge_service.py +546 -0
  222. quantum/runtime/llm_cache.py +100 -0
  223. quantum/runtime/llm_providers.py +704 -0
  224. quantum/runtime/llm_service.py +287 -0
  225. quantum/runtime/local_server.py +28 -0
  226. quantum/runtime/logging_setup.py +141 -0
  227. quantum/runtime/message_broker.py +364 -0
  228. quantum/runtime/message_queue_service.py +571 -0
  229. quantum/runtime/param_validation.py +218 -0
  230. quantum/runtime/pypy_compat.py +317 -0
  231. quantum/runtime/python_bridge.py +698 -0
  232. quantum/runtime/query_validators.py +304 -0
  233. quantum/runtime/renderer.py +734 -0
  234. quantum/runtime/service_container.py +470 -0
  235. quantum/runtime/table_params.py +163 -0
  236. quantum/runtime/terminal_builder.py +76 -0
  237. quantum/runtime/terminal_code_generator.py +607 -0
  238. quantum/runtime/terminal_templates.py +243 -0
  239. quantum/runtime/testing_builder.py +77 -0
  240. quantum/runtime/testing_code_generator.py +833 -0
  241. quantum/runtime/testing_templates.py +85 -0
  242. quantum/runtime/ui_builder.py +222 -0
  243. quantum/runtime/ui_console.py +405 -0
  244. quantum/runtime/ui_desktop.py +56 -0
  245. quantum/runtime/ui_form_rules.py +193 -0
  246. quantum/runtime/ui_html_adapter.py +2567 -0
  247. quantum/runtime/ui_html_templates.py +2106 -0
  248. quantum/runtime/ui_mobile_adapter.py +1832 -0
  249. quantum/runtime/ui_mobile_templates.py +1003 -0
  250. quantum/runtime/ui_page_renderer.py +392 -0
  251. quantum/runtime/ui_pager.py +73 -0
  252. quantum/runtime/ui_table_edit.py +139 -0
  253. quantum/runtime/ui_textual_adapter.py +1866 -0
  254. quantum/runtime/ui_textual_templates.py +45 -0
  255. quantum/runtime/ui_tokens.py +465 -0
  256. quantum/runtime/ui_validator.py +365 -0
  257. quantum/runtime/ui_view.py +221 -0
  258. quantum/runtime/validators.py +256 -0
  259. quantum/runtime/web_server.py +2131 -0
  260. quantum/runtime/websocket_adapter.py +501 -0
  261. quantum/runtime/websocket_service.py +585 -0
  262. quantum/runtime/websocket_transport.py +289 -0
  263. quantum/runtime/wsgi.py +101 -0
  264. quantum/services.py +109 -0
  265. quantum/utils/__init__.py +1 -0
  266. quantum_admin/__init__.py +4 -0
  267. quantum_admin/assets/quantum-admin-connectors.js +123 -0
  268. quantum_admin/assets/quantum-admin.css +2058 -0
  269. quantum_admin/assets/quantum-source-highlight.js +106 -0
  270. quantum_admin/backend/__init__.py +4 -0
  271. quantum_admin/backend/audit_service.py +346 -0
  272. quantum_admin/backend/auth_service.py +394 -0
  273. quantum_admin/backend/component_discovery.py +766 -0
  274. quantum_admin/backend/config_generator.py +586 -0
  275. quantum_admin/backend/connector_service.py +1016 -0
  276. quantum_admin/backend/crud.py +557 -0
  277. quantum_admin/backend/database.py +226 -0
  278. quantum_admin/backend/db_setup_service.py +193 -0
  279. quantum_admin/backend/deploy_service.py +698 -0
  280. quantum_admin/backend/docker_service.py +422 -0
  281. quantum_admin/backend/environment_service.py +418 -0
  282. quantum_admin/backend/git_service.py +370 -0
  283. quantum_admin/backend/health_service.py +390 -0
  284. quantum_admin/backend/job_service.py +347 -0
  285. quantum_admin/backend/log_service.py +331 -0
  286. quantum_admin/backend/main.py +14426 -0
  287. quantum_admin/backend/models.py +986 -0
  288. quantum_admin/backend/pipeline_service.py +812 -0
  289. quantum_admin/backend/resource_manager.py +1118 -0
  290. quantum_admin/backend/schemas.py +419 -0
  291. quantum_admin/backend/secret_manager.py +277 -0
  292. quantum_admin/backend/settings_service.py +488 -0
  293. quantum_admin/backend/template_service.py +710 -0
  294. quantum_admin/backend/test_execution_service.py +364 -0
  295. quantum_admin/backend/test_generator.py +782 -0
  296. quantum_admin/backend/webhook_service.py +454 -0
  297. quantum_admin/backend/websocket_manager.py +360 -0
  298. quantum_admin/components/admin/_layout/AdminShell.q +121 -0
  299. quantum_admin/components/admin/agents.q +107 -0
  300. quantum_admin/components/admin/app/[name].q +558 -0
  301. quantum_admin/components/admin/applications.q +160 -0
  302. quantum_admin/components/admin/component/[...path].q +205 -0
  303. quantum_admin/components/admin/components.q +59 -0
  304. quantum_admin/components/admin/connectors.q +283 -0
  305. quantum_admin/components/admin/dashboard.q +86 -0
  306. quantum_admin/components/admin/database.q +95 -0
  307. quantum_admin/components/admin/features.q +69 -0
  308. quantum_admin/components/admin/index.q +12 -0
  309. quantum_admin/components/admin/jobs.q +75 -0
  310. quantum_admin/components/admin/login.q +65 -0
  311. quantum_admin/components/admin/logout.q +11 -0
  312. quantum_admin/components/admin/settings.q +174 -0
  313. quantum_admin/components/admin/source.q +45 -0
  314. quantum_admin/components/admin/tests.q +73 -0
  315. quantum_admin/run.py +13 -0
  316. quantum_admin/services/__init__.py +16 -0
  317. quantum_admin/services/_base.py +84 -0
  318. quantum_admin/services/_component_test_generator.py +205 -0
  319. quantum_admin/services/_yaml_editor.py +82 -0
  320. quantum_admin/services/apps.py +330 -0
  321. quantum_admin/services/auth.py +61 -0
  322. quantum_admin/services/components.py +210 -0
  323. quantum_admin/services/connectors.py +164 -0
  324. quantum_admin/services/projects.py +189 -0
  325. quantum_admin/services/repository.py +223 -0
  326. quantum_admin/services/settings.py +150 -0
  327. quantum_admin/services/yaml_import.py +118 -0
  328. quantum_framework-0.18.0.dist-info/METADATA +281 -0
  329. quantum_framework-0.18.0.dist-info/RECORD +333 -0
  330. quantum_framework-0.18.0.dist-info/WHEEL +5 -0
  331. quantum_framework-0.18.0.dist-info/entry_points.txt +2 -0
  332. quantum_framework-0.18.0.dist-info/licenses/LICENSE +21 -0
  333. quantum_framework-0.18.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,444 @@
1
+ """
2
+ Expression Transformer
3
+ ======================
4
+
5
+ Transforms Quantum expressions to target language syntax.
6
+ Handles differences between Python and JavaScript expression syntax.
7
+ """
8
+
9
+ import ast
10
+ import re
11
+ from typing import Optional, Dict, Any
12
+
13
+
14
+ class ExpressionTransformer:
15
+ """
16
+ Transforms Quantum expressions to target language.
17
+
18
+ Quantum expressions are Python-like but need transformation for:
19
+ - JavaScript: == becomes ===, and/or become &&/||
20
+ - Both: property access, method calls, list comprehensions
21
+ """
22
+
23
+ # Built-in function mappings
24
+ PYTHON_BUILTINS = {
25
+ 'len', 'str', 'int', 'float', 'bool', 'list', 'dict', 'set',
26
+ 'sum', 'min', 'max', 'abs', 'round', 'range', 'enumerate',
27
+ 'sorted', 'reversed', 'zip', 'map', 'filter', 'any', 'all',
28
+ 'upper', 'lower', 'strip', 'split', 'join', 'replace',
29
+ 'startswith', 'endswith', 'format', 'isinstance', 'type',
30
+ }
31
+
32
+ # Python to JavaScript function mappings
33
+ PY_TO_JS_FUNCTIONS = {
34
+ 'len': lambda arg: f'{arg}.length',
35
+ 'str': lambda arg: f'String({arg})',
36
+ 'int': lambda arg: f'parseInt({arg})',
37
+ 'float': lambda arg: f'parseFloat({arg})',
38
+ 'bool': lambda arg: f'Boolean({arg})',
39
+ 'abs': lambda arg: f'Math.abs({arg})',
40
+ 'round': lambda arg: f'Math.round({arg})',
41
+ 'min': lambda *args: f'Math.min({", ".join(args)})',
42
+ 'max': lambda *args: f'Math.max({", ".join(args)})',
43
+ 'sum': lambda arg: f'{arg}.reduce((a, b) => a + b, 0)',
44
+ 'sorted': lambda arg: f'[...{arg}].sort()',
45
+ 'reversed': lambda arg: f'[...{arg}].reverse()',
46
+ 'range': '_range', # Special handling needed
47
+ 'enumerate': '_enumerate', # Special handling needed
48
+ }
49
+
50
+ # Python string methods to JavaScript
51
+ PY_TO_JS_METHODS = {
52
+ 'upper': 'toUpperCase',
53
+ 'lower': 'toLowerCase',
54
+ 'strip': 'trim',
55
+ 'lstrip': 'trimStart',
56
+ 'rstrip': 'trimEnd',
57
+ 'split': 'split',
58
+ 'join': '_join', # Special: "sep".join(list) -> list.join("sep")
59
+ 'replace': 'replace',
60
+ 'startswith': 'startsWith',
61
+ 'endswith': 'endsWith',
62
+ 'find': 'indexOf',
63
+ 'count': '_count', # Special handling
64
+ 'isdigit': '_isDigit', # Special handling
65
+ 'isalpha': '_isAlpha', # Special handling
66
+ }
67
+
68
+ def __init__(self, target: str = 'python'):
69
+ """
70
+ Initialize the transformer.
71
+
72
+ Args:
73
+ target: Target language ('python' or 'javascript')
74
+ """
75
+ self.target = target.lower()
76
+
77
+ def transform(self, expr: str) -> str:
78
+ """
79
+ Transform a Quantum expression to target language.
80
+
81
+ Args:
82
+ expr: The expression to transform (may have {})
83
+
84
+ Returns:
85
+ Transformed expression string
86
+ """
87
+ if not expr:
88
+ return ''
89
+
90
+ expr = str(expr).strip()
91
+
92
+ # Remove { } wrapper if present
93
+ if expr.startswith('{') and expr.endswith('}'):
94
+ expr = expr[1:-1].strip()
95
+
96
+ if not expr:
97
+ return ''
98
+
99
+ # Quick path: if no transformation needed
100
+ if self.target == 'python' and not self._needs_transformation(expr):
101
+ return expr
102
+
103
+ try:
104
+ # Parse as Python AST
105
+ tree = ast.parse(expr, mode='eval')
106
+ return self._transform_node(tree.body)
107
+ except SyntaxError:
108
+ # Fallback: return as-is with basic transformations
109
+ return self._basic_transform(expr)
110
+
111
+ def _needs_transformation(self, expr: str) -> bool:
112
+ """Check if expression needs any transformation."""
113
+ # For Python target, most expressions work as-is
114
+ if self.target == 'python':
115
+ return False
116
+ # For JavaScript, check for Python-specific syntax
117
+ return any(x in expr for x in ['and ', 'or ', 'not ', ' in ', 'True', 'False', 'None'])
118
+
119
+ def _basic_transform(self, expr: str) -> str:
120
+ """Apply basic transformations without full AST parsing."""
121
+ if self.target == 'javascript':
122
+ # Replace Python operators with JavaScript equivalents
123
+ expr = re.sub(r'\band\b', '&&', expr)
124
+ expr = re.sub(r'\bor\b', '||', expr)
125
+ expr = re.sub(r'\bnot\b', '!', expr)
126
+ expr = re.sub(r'\bTrue\b', 'true', expr)
127
+ expr = re.sub(r'\bFalse\b', 'false', expr)
128
+ expr = re.sub(r'\bNone\b', 'null', expr)
129
+ # == to === (careful with !=)
130
+ expr = re.sub(r'([^!<>=])={2}([^=])', r'\1===\2', expr)
131
+ expr = re.sub(r'!=', '!==', expr)
132
+ return expr
133
+
134
+ def _transform_node(self, node: ast.expr) -> str:
135
+ """Transform an AST node to target language."""
136
+ method_name = f'_transform_{type(node).__name__}'
137
+ transformer = getattr(self, method_name, self._transform_default)
138
+ return transformer(node)
139
+
140
+ def _transform_default(self, node: ast.expr) -> str:
141
+ """Default transformation: use Python's unparse."""
142
+ result = ast.unparse(node)
143
+ if self.target == 'javascript':
144
+ return self._basic_transform(result)
145
+ return result
146
+
147
+ # =========================================================================
148
+ # AST Node Transformers
149
+ # =========================================================================
150
+
151
+ def _transform_Constant(self, node: ast.Constant) -> str:
152
+ """Transform constants."""
153
+ value = node.value
154
+ if value is True:
155
+ return 'true' if self.target == 'javascript' else 'True'
156
+ elif value is False:
157
+ return 'false' if self.target == 'javascript' else 'False'
158
+ elif value is None:
159
+ return 'null' if self.target == 'javascript' else 'None'
160
+ elif isinstance(value, str):
161
+ # Use repr for proper escaping
162
+ return repr(value)
163
+ else:
164
+ return str(value)
165
+
166
+ def _transform_Name(self, node: ast.Name) -> str:
167
+ """Transform variable names."""
168
+ name = node.id
169
+ if self.target == 'javascript':
170
+ # Map Python built-in names to JavaScript
171
+ mappings = {'True': 'true', 'False': 'false', 'None': 'null'}
172
+ return mappings.get(name, name)
173
+ return name
174
+
175
+ def _transform_BinOp(self, node: ast.BinOp) -> str:
176
+ """Transform binary operations."""
177
+ left = self._transform_node(node.left)
178
+ right = self._transform_node(node.right)
179
+ op = self._get_binop(node.op)
180
+ return f'({left} {op} {right})'
181
+
182
+ def _get_binop(self, op: ast.operator) -> str:
183
+ """Get binary operator string."""
184
+ ops = {
185
+ ast.Add: '+',
186
+ ast.Sub: '-',
187
+ ast.Mult: '*',
188
+ ast.Div: '/',
189
+ ast.FloorDiv: '//' if self.target == 'python' else 'Math.floor(/',
190
+ ast.Mod: '%',
191
+ ast.Pow: '**',
192
+ ast.BitOr: '|',
193
+ ast.BitXor: '^',
194
+ ast.BitAnd: '&',
195
+ ast.LShift: '<<',
196
+ ast.RShift: '>>',
197
+ }
198
+ return ops.get(type(op), '+')
199
+
200
+ def _transform_Compare(self, node: ast.Compare) -> str:
201
+ """Transform comparison operations."""
202
+ left = self._transform_node(node.left)
203
+ parts = [left]
204
+
205
+ for op, comparator in zip(node.ops, node.comparators):
206
+ comp_str = self._transform_node(comparator)
207
+
208
+ if isinstance(op, ast.Eq):
209
+ parts.append('===' if self.target == 'javascript' else '==')
210
+ elif isinstance(op, ast.NotEq):
211
+ parts.append('!==' if self.target == 'javascript' else '!=')
212
+ elif isinstance(op, ast.Lt):
213
+ parts.append('<')
214
+ elif isinstance(op, ast.LtE):
215
+ parts.append('<=')
216
+ elif isinstance(op, ast.Gt):
217
+ parts.append('>')
218
+ elif isinstance(op, ast.GtE):
219
+ parts.append('>=')
220
+ elif isinstance(op, ast.In):
221
+ if self.target == 'javascript':
222
+ # x in y -> y.includes(x)
223
+ return f'{comp_str}.includes({left})'
224
+ parts.append('in')
225
+ elif isinstance(op, ast.NotIn):
226
+ if self.target == 'javascript':
227
+ return f'!{comp_str}.includes({left})'
228
+ parts.append('not in')
229
+ elif isinstance(op, ast.Is):
230
+ parts.append('===' if self.target == 'javascript' else 'is')
231
+ elif isinstance(op, ast.IsNot):
232
+ parts.append('!==' if self.target == 'javascript' else 'is not')
233
+ else:
234
+ parts.append('==')
235
+
236
+ parts.append(comp_str)
237
+
238
+ return ' '.join(parts)
239
+
240
+ def _transform_BoolOp(self, node: ast.BoolOp) -> str:
241
+ """Transform boolean operations (and/or)."""
242
+ if isinstance(node.op, ast.And):
243
+ op = '&&' if self.target == 'javascript' else 'and'
244
+ else:
245
+ op = '||' if self.target == 'javascript' else 'or'
246
+
247
+ values = [self._transform_node(v) for v in node.values]
248
+ return f' {op} '.join(f'({v})' for v in values)
249
+
250
+ def _transform_UnaryOp(self, node: ast.UnaryOp) -> str:
251
+ """Transform unary operations."""
252
+ operand = self._transform_node(node.operand)
253
+
254
+ if isinstance(node.op, ast.Not):
255
+ op = '!' if self.target == 'javascript' else 'not '
256
+ elif isinstance(node.op, ast.USub):
257
+ op = '-'
258
+ elif isinstance(node.op, ast.UAdd):
259
+ op = '+'
260
+ elif isinstance(node.op, ast.Invert):
261
+ op = '~'
262
+ else:
263
+ op = ''
264
+
265
+ return f'{op}{operand}'
266
+
267
+ def _transform_IfExp(self, node: ast.IfExp) -> str:
268
+ """Transform ternary/conditional expressions."""
269
+ test = self._transform_node(node.test)
270
+ body = self._transform_node(node.body)
271
+ orelse = self._transform_node(node.orelse)
272
+
273
+ if self.target == 'javascript':
274
+ return f'({test} ? {body} : {orelse})'
275
+ else:
276
+ return f'({body} if {test} else {orelse})'
277
+
278
+ def _transform_Attribute(self, node: ast.Attribute) -> str:
279
+ """Transform attribute access."""
280
+ value = self._transform_node(node.value)
281
+ attr = node.attr
282
+
283
+ if self.target == 'javascript':
284
+ # Map Python methods to JavaScript
285
+ if attr in self.PY_TO_JS_METHODS:
286
+ js_method = self.PY_TO_JS_METHODS[attr]
287
+ if not js_method.startswith('_'):
288
+ return f'{value}.{js_method}'
289
+ # Special handling needed - return as-is for now
290
+ return f'{value}.{attr}'
291
+
292
+ return f'{value}.{attr}'
293
+
294
+ def _transform_Subscript(self, node: ast.Subscript) -> str:
295
+ """Transform subscript access (indexing)."""
296
+ value = self._transform_node(node.value)
297
+ slice_node = node.slice
298
+
299
+ if isinstance(slice_node, ast.Constant):
300
+ index = self._transform_node(slice_node)
301
+ return f'{value}[{index}]'
302
+ elif isinstance(slice_node, ast.Slice):
303
+ return self._transform_slice(value, slice_node)
304
+ else:
305
+ index = self._transform_node(slice_node)
306
+ return f'{value}[{index}]'
307
+
308
+ def _transform_slice(self, value: str, node: ast.Slice) -> str:
309
+ """Transform slice operations."""
310
+ lower = self._transform_node(node.lower) if node.lower else ''
311
+ upper = self._transform_node(node.upper) if node.upper else ''
312
+ step = self._transform_node(node.step) if node.step else ''
313
+
314
+ if self.target == 'javascript':
315
+ if step:
316
+ # JavaScript doesn't have native slice with step
317
+ return f'{value}.slice({lower or 0}, {upper or "undefined"})'
318
+ return f'{value}.slice({lower or 0}, {upper or "undefined"})'
319
+ else:
320
+ return f'{value}[{lower}:{upper}{":" + step if step else ""}]'
321
+
322
+ def _transform_Call(self, node: ast.Call) -> str:
323
+ """Transform function calls."""
324
+ args = [self._transform_node(arg) for arg in node.args]
325
+ kwargs = [f'{kw.arg}={self._transform_node(kw.value)}' for kw in node.keywords]
326
+ all_args = ', '.join(args + kwargs)
327
+
328
+ # Handle method calls
329
+ if isinstance(node.func, ast.Attribute):
330
+ obj = self._transform_node(node.func.value)
331
+ method = node.func.attr
332
+
333
+ if self.target == 'javascript':
334
+ # Special method mappings
335
+ if method == 'join':
336
+ # Python: "sep".join(list) -> JS: list.join("sep")
337
+ return f'{args[0]}.join({obj})'
338
+ elif method in self.PY_TO_JS_METHODS:
339
+ js_method = self.PY_TO_JS_METHODS[method]
340
+ if not js_method.startswith('_'):
341
+ return f'{obj}.{js_method}({all_args})'
342
+
343
+ return f'{obj}.{method}({all_args})'
344
+
345
+ # Handle regular function calls
346
+ if isinstance(node.func, ast.Name):
347
+ func_name = node.func.id
348
+
349
+ if self.target == 'javascript' and func_name in self.PY_TO_JS_FUNCTIONS:
350
+ mapping = self.PY_TO_JS_FUNCTIONS[func_name]
351
+ if callable(mapping):
352
+ return mapping(*args)
353
+ # Special handling marker
354
+ if mapping == '_range':
355
+ return self._js_range(args)
356
+ elif mapping == '_enumerate':
357
+ return f'{args[0]}.entries()'
358
+
359
+ return f'{func_name}({all_args})'
360
+
361
+ # Fallback
362
+ func = self._transform_node(node.func)
363
+ return f'{func}({all_args})'
364
+
365
+ def _js_range(self, args: list) -> str:
366
+ """Generate JavaScript range equivalent."""
367
+ if len(args) == 1:
368
+ return f'Array.from({{length: {args[0]}}}, (_, i) => i)'
369
+ elif len(args) == 2:
370
+ return f'Array.from({{length: {args[1]} - {args[0]}}}, (_, i) => i + {args[0]})'
371
+ else:
372
+ return f'Array.from({{length: Math.ceil(({args[1]} - {args[0]}) / {args[2]})}}, (_, i) => {args[0]} + i * {args[2]})'
373
+
374
+ def _transform_List(self, node: ast.List) -> str:
375
+ """Transform list literals."""
376
+ elements = [self._transform_node(el) for el in node.elts]
377
+ return f'[{", ".join(elements)}]'
378
+
379
+ def _transform_Dict(self, node: ast.Dict) -> str:
380
+ """Transform dictionary literals."""
381
+ pairs = []
382
+ for key, value in zip(node.keys, node.values):
383
+ k = self._transform_node(key) if key else '...'
384
+ v = self._transform_node(value)
385
+ if self.target == 'javascript' and isinstance(key, ast.Constant) and isinstance(key.value, str):
386
+ # JavaScript object literal with string keys
387
+ pairs.append(f'{key.value}: {v}')
388
+ else:
389
+ pairs.append(f'{k}: {v}')
390
+
391
+ if self.target == 'javascript':
392
+ return '{' + ', '.join(pairs) + '}'
393
+ else:
394
+ return '{' + ', '.join(pairs) + '}'
395
+
396
+ def _transform_ListComp(self, node: ast.ListComp) -> str:
397
+ """Transform list comprehensions."""
398
+ if self.target == 'javascript':
399
+ return self._listcomp_to_js(node)
400
+ else:
401
+ return ast.unparse(node)
402
+
403
+ def _listcomp_to_js(self, node: ast.ListComp) -> str:
404
+ """Convert list comprehension to JavaScript map/filter."""
405
+ gen = node.generators[0]
406
+ var = self._transform_node(gen.target)
407
+ iterable = self._transform_node(gen.iter)
408
+ expr = self._transform_node(node.elt)
409
+
410
+ if gen.ifs:
411
+ # Has filter condition
412
+ conditions = [self._transform_node(if_) for if_ in gen.ifs]
413
+ filter_expr = ' && '.join(f'({c})' for c in conditions)
414
+ return f'{iterable}.filter({var} => {filter_expr}).map({var} => {expr})'
415
+ else:
416
+ return f'{iterable}.map({var} => {expr})'
417
+
418
+ def _transform_Lambda(self, node: ast.Lambda) -> str:
419
+ """Transform lambda expressions."""
420
+ args = ', '.join(arg.arg for arg in node.args.args)
421
+ body = self._transform_node(node.body)
422
+
423
+ if self.target == 'javascript':
424
+ return f'({args}) => {body}'
425
+ else:
426
+ return f'lambda {args}: {body}'
427
+
428
+ def _transform_JoinedStr(self, node: ast.JoinedStr) -> str:
429
+ """Transform f-strings."""
430
+ parts = []
431
+ for value in node.values:
432
+ if isinstance(value, ast.Constant):
433
+ parts.append(str(value.value))
434
+ elif isinstance(value, ast.FormattedValue):
435
+ expr = self._transform_node(value.value)
436
+ if self.target == 'javascript':
437
+ parts.append('${' + expr + '}')
438
+ else:
439
+ parts.append('{' + expr + '}')
440
+
441
+ if self.target == 'javascript':
442
+ return '`' + ''.join(parts) + '`'
443
+ else:
444
+ return "f'" + ''.join(parts) + "'"
@@ -0,0 +1,10 @@
1
+ """
2
+ JavaScript Code Generator
3
+ =========================
4
+
5
+ Generates JavaScript code from Quantum AST.
6
+ """
7
+
8
+ from .generator import JavaScriptGenerator
9
+
10
+ __all__ = ['JavaScriptGenerator']