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,322 @@
1
+ """
2
+ Base Tag Parser - Abstract base class for all tag parsers
3
+
4
+ All parsers inherit from BaseTagParser and implement the parse() method.
5
+ The ParserRegistry uses the 'tag_names' property to dispatch parsing to the correct parser.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import Any, List, Optional, TYPE_CHECKING
10
+ from xml.etree import ElementTree as ET
11
+
12
+ if TYPE_CHECKING:
13
+ from quantum.core.ast_nodes import QuantumNode
14
+
15
+
16
+ class ParserError(Exception):
17
+ """Base exception for parser errors"""
18
+ pass
19
+
20
+
21
+ class BaseTagParser(ABC):
22
+ """
23
+ Abstract base class for tag-specific parsers.
24
+
25
+ Each parser handles one or more XML tags and implements
26
+ the parse() method to convert them to AST nodes.
27
+
28
+ Example:
29
+ class LoopParser(BaseTagParser):
30
+ @property
31
+ def tag_names(self) -> List[str]:
32
+ return ['loop']
33
+
34
+ def parse(self, element: ET.Element) -> LoopNode:
35
+ # Parse loop element
36
+ pass
37
+ """
38
+
39
+ def __init__(self, main_parser: 'QuantumParser'):
40
+ """
41
+ Initialize parser with reference to main parser.
42
+
43
+ Args:
44
+ main_parser: The QuantumParser instance (provides helpers and registry)
45
+ """
46
+ self._parser = main_parser
47
+
48
+ @property
49
+ def parser(self) -> 'QuantumParser':
50
+ """Access to the main parser"""
51
+ return self._parser
52
+
53
+ @property
54
+ @abstractmethod
55
+ def tag_names(self) -> List[str]:
56
+ """
57
+ List of tag names this parser handles (without q: prefix).
58
+
59
+ Returns:
60
+ List of tag names (e.g., ['loop', 'for', 'while'])
61
+ """
62
+ pass
63
+
64
+ @abstractmethod
65
+ def parse(self, element: ET.Element) -> 'QuantumNode':
66
+ """
67
+ Parse the element and return AST node.
68
+
69
+ Args:
70
+ element: XML element to parse
71
+
72
+ Returns:
73
+ AST node representing the element
74
+ """
75
+ pass
76
+
77
+ # ==========================================================================
78
+ # Helper methods - Common utilities available to all parsers
79
+ # ==========================================================================
80
+
81
+ def get_element_name(self, element: ET.Element) -> str:
82
+ """
83
+ Get element name without namespace prefix.
84
+
85
+ Args:
86
+ element: XML element
87
+
88
+ Returns:
89
+ Tag name without namespace
90
+ """
91
+ return self._parser._get_element_name(element)
92
+
93
+ def find_element(self, parent: ET.Element, tag: str) -> Optional[ET.Element]:
94
+ """
95
+ Find a child element by tag name.
96
+
97
+ Args:
98
+ parent: Parent element
99
+ tag: Tag name to find
100
+
101
+ Returns:
102
+ Element or None
103
+ """
104
+ return self._parser._find_element(parent, tag)
105
+
106
+ def find_all_elements(self, parent: ET.Element, tag: str) -> List[ET.Element]:
107
+ """
108
+ Find all child elements by tag name.
109
+
110
+ Args:
111
+ parent: Parent element
112
+ tag: Tag name to find
113
+
114
+ Returns:
115
+ List of elements
116
+ """
117
+ return self._parser._find_all_elements(parent, tag)
118
+
119
+ def parse_statement(self, element: ET.Element) -> Optional['QuantumNode']:
120
+ """
121
+ Parse a child statement using the registry.
122
+
123
+ Args:
124
+ element: Child element to parse
125
+
126
+ Returns:
127
+ AST node or None
128
+ """
129
+ return self._parser._parse_statement(element)
130
+
131
+ def parse_statements(self, parent: ET.Element) -> List['QuantumNode']:
132
+ """
133
+ Parse all direct children as statements.
134
+
135
+ Args:
136
+ parent: Parent element
137
+
138
+ Returns:
139
+ List of AST nodes
140
+ """
141
+ from quantum.core.features.conditionals.src.ast_node import IfNode
142
+
143
+ statements = []
144
+ for child in parent:
145
+ nome = self.get_element_name(child)
146
+
147
+ # `<q:else>` / `<q:elseif>` como IRMAO de `<q:if>`.
148
+ #
149
+ # A forma que funcionava era so a ANINHADA (o else dentro do if);
150
+ # a irma — `</q:if>` e depois `<q:else>` — nao tinha parser
151
+ # registrado (IfParser.tag_names = ['if']), caia no fallback e
152
+ # era DESCARTADA sem uma palavra. A documentacao ensinava a forma
153
+ # irma em dezenas de exemplos (inclusive o passo 3 do quick-start)
154
+ # e 7 arquivos .q entregues a usavam, entao o else simplesmente
155
+ # nao acontecia e ninguem era avisado.
156
+ #
157
+ # Agora um else/elseif que segue um q:if e anexado a ele — as duas
158
+ # formas passam a significar a mesma coisa. Um else/elseif SEM um
159
+ # if antes e um erro de verdade, e agora e dito em voz alta.
160
+ if nome in ('else', 'elseif'):
161
+ anterior = statements[-1] if statements else None
162
+ if isinstance(anterior, IfNode):
163
+ self._attach_else_branch(anterior, nome, child)
164
+ continue
165
+ from quantum.core.parser import QuantumParseError
166
+ raise QuantumParseError(
167
+ f"<q:{nome}> has no matching <q:if> before it. "
168
+ f"Put it inside the if, or right after it: "
169
+ f"<q:if ...>...</q:if> <q:{nome}>...</q:{nome}>"
170
+ )
171
+
172
+ node = self.parse_statement(child)
173
+ if node is not None:
174
+ statements.append(node)
175
+ return statements
176
+
177
+ def _attach_else_branch(self, if_node, kind: str, element: 'ET.Element'):
178
+ """Anexa um else/elseif IRMAO ao IfNode anterior — mesmo resultado do
179
+ aninhado, que o IfParser ja monta com estes mesmos metodos."""
180
+ if kind == 'elseif':
181
+ condicao = self.get_attr(element, 'condition', '')
182
+ corpo = []
183
+ for filho in element:
184
+ stmt = self.parse_statement(filho)
185
+ if stmt:
186
+ corpo.append(stmt)
187
+ if_node.add_elseif_block(condicao, corpo)
188
+ else:
189
+ for filho in element:
190
+ stmt = self.parse_statement(filho)
191
+ if stmt:
192
+ if_node.add_else_statement(stmt)
193
+
194
+ def get_attr(self, element: ET.Element, name: str, default: str = None) -> Optional[str]:
195
+ """
196
+ Get element attribute with optional default.
197
+
198
+ Args:
199
+ element: XML element
200
+ name: Attribute name
201
+ default: Default value
202
+
203
+ Returns:
204
+ Attribute value or default
205
+ """
206
+ return element.get(name, default)
207
+
208
+ def get_bool_attr(self, element: ET.Element, name: str, default: bool = False) -> bool:
209
+ """
210
+ Get boolean attribute.
211
+
212
+ Args:
213
+ element: XML element
214
+ name: Attribute name
215
+ default: Default value
216
+
217
+ Returns:
218
+ Boolean value
219
+ """
220
+ value = element.get(name, '').lower()
221
+ if value in ('true', '1', 'yes'):
222
+ return True
223
+ if value in ('false', '0', 'no'):
224
+ return False
225
+ return default
226
+
227
+ def get_int_attr(self, element: ET.Element, name: str, default: int = 0) -> int:
228
+ """
229
+ Get integer attribute.
230
+
231
+ Args:
232
+ element: XML element
233
+ name: Attribute name
234
+ default: Default value
235
+
236
+ Returns:
237
+ Integer value
238
+ """
239
+ try:
240
+ return int(element.get(name, default))
241
+ except (ValueError, TypeError):
242
+ return default
243
+
244
+ def get_text(self, element: ET.Element) -> str:
245
+ """
246
+ Get element text content.
247
+
248
+ Args:
249
+ element: XML element
250
+
251
+ Returns:
252
+ Text content (empty string if None)
253
+ """
254
+ return element.text or ''
255
+
256
+ def require_attr(self, element: ET.Element, name: str) -> str:
257
+ """
258
+ Get required attribute, raise error if missing.
259
+
260
+ Args:
261
+ element: XML element
262
+ name: Attribute name
263
+
264
+ Returns:
265
+ Attribute value
266
+
267
+ Raises:
268
+ ParserError: If attribute is missing
269
+ """
270
+ value = element.get(name)
271
+ if value is None:
272
+ tag = self.get_element_name(element)
273
+ raise ParserError(f"<q:{tag}> requires '{name}' attribute")
274
+ return value
275
+
276
+ def is_html_element(self, element: ET.Element) -> bool:
277
+ """
278
+ Check if element is an HTML element.
279
+
280
+ Args:
281
+ element: XML element
282
+
283
+ Returns:
284
+ True if HTML element
285
+ """
286
+ return self._parser._is_html_element(element)
287
+
288
+ def parse_param(self, element: ET.Element) -> 'QuantumParam':
289
+ """
290
+ Parse a q:param element.
291
+
292
+ Args:
293
+ element: Param element
294
+
295
+ Returns:
296
+ QuantumParam node
297
+ """
298
+ return self._parser._parse_param(element)
299
+
300
+ def parse_child(self, element: ET.Element) -> Optional['QuantumNode']:
301
+ """
302
+ Alias for parse_statement. Parse a child element using the registry.
303
+
304
+ Args:
305
+ element: Child element to parse
306
+
307
+ Returns:
308
+ AST node or None
309
+ """
310
+ return self.parse_statement(element)
311
+
312
+ def _get_element_name(self, element: ET.Element) -> str:
313
+ """
314
+ Alias for get_element_name. Get element name without namespace prefix.
315
+
316
+ Args:
317
+ element: XML element
318
+
319
+ Returns:
320
+ Tag name without namespace
321
+ """
322
+ return self.get_element_name(element)
@@ -0,0 +1,10 @@
1
+ """
2
+ Component Composition Parsers
3
+
4
+ Parsers for component composition tags: import, slot
5
+ """
6
+
7
+ from .import_parser import ImportParser
8
+ from .slot_parser import SlotParser
9
+
10
+ __all__ = ['ImportParser', 'SlotParser']
@@ -0,0 +1,50 @@
1
+ """
2
+ Import Parser - Parse q:import statements
3
+ """
4
+
5
+ from typing import List
6
+ from xml.etree import ElementTree as ET
7
+ from quantum.core.parsers.base import BaseTagParser
8
+ from quantum.core.ast_nodes import ImportNode
9
+
10
+
11
+ class ImportParser(BaseTagParser):
12
+ """
13
+ Parser for q:import statements.
14
+
15
+ Examples:
16
+ <q:import component="Header" />
17
+ <q:import component="Button" from="./ui" />
18
+ <q:import component="AdminLayout" as="Layout" />
19
+
20
+ Game Engine Imports:
21
+ <q:import behavior="PlayerBehavior" from="./behaviors" />
22
+ <q:import prefab="KoopaGreen" from="./prefabs" />
23
+ <q:import tilemap="yi1" from="./levels" />
24
+ """
25
+
26
+ @property
27
+ def tag_names(self) -> List[str]:
28
+ return ['import']
29
+
30
+ def parse(self, element: ET.Element) -> ImportNode:
31
+ """Parse q:import element"""
32
+ component = element.get('component')
33
+ behavior = element.get('behavior')
34
+ prefab = element.get('prefab')
35
+ tilemap = element.get('tilemap')
36
+ from_path = element.get('from')
37
+ alias = element.get('as')
38
+
39
+ if not component and not behavior and not prefab and not tilemap:
40
+ from quantum.core.parser import QuantumParseError
41
+ raise QuantumParseError("q:import requires one of 'component', 'behavior', 'prefab', or 'tilemap' attribute")
42
+
43
+ return ImportNode(
44
+ component=component,
45
+ from_path=from_path,
46
+ alias=alias,
47
+ behavior=behavior,
48
+ prefab=prefab,
49
+ tilemap=tilemap
50
+ )
@@ -0,0 +1,48 @@
1
+ """
2
+ Slot Parser - Parse q:slot statements
3
+ """
4
+
5
+ from typing import List
6
+ from xml.etree import ElementTree as ET
7
+ from quantum.core.parsers.base import BaseTagParser
8
+ from quantum.core.ast_nodes import SlotNode, TextNode
9
+
10
+
11
+ class SlotParser(BaseTagParser):
12
+ """
13
+ Parser for q:slot statements.
14
+
15
+ Examples:
16
+ <q:slot /> <!-- Default slot -->
17
+ <q:slot name="header" />
18
+ <q:slot name="footer">
19
+ <p>Default footer content</p>
20
+ </q:slot>
21
+ """
22
+
23
+ @property
24
+ def tag_names(self) -> List[str]:
25
+ return ['slot']
26
+
27
+ def parse(self, element: ET.Element) -> SlotNode:
28
+ """Parse q:slot element"""
29
+ name = element.get('name', 'default')
30
+
31
+ # Parse default content (children of slot)
32
+ default_content = []
33
+
34
+ if element.text and element.text.strip():
35
+ default_content.append(TextNode(element.text))
36
+
37
+ for child in element:
38
+ child_node = self.parse_child(child)
39
+ if child_node:
40
+ default_content.append(child_node)
41
+
42
+ if child.tail and child.tail.strip():
43
+ default_content.append(TextNode(child.tail))
44
+
45
+ return SlotNode(
46
+ name=name,
47
+ default_content=default_content
48
+ )
@@ -0,0 +1,11 @@
1
+ """
2
+ Control Flow Parsers
3
+
4
+ Parsers for control flow statements: if, loop, set.
5
+ """
6
+
7
+ from .if_parser import IfParser
8
+ from .loop_parser import LoopParser
9
+ from .set_parser import SetParser
10
+
11
+ __all__ = ['IfParser', 'LoopParser', 'SetParser']
@@ -0,0 +1,68 @@
1
+ """
2
+ If Parser - Parse q:if statements
3
+
4
+ Handles conditional statements with elseif and else blocks.
5
+ """
6
+
7
+ from typing import List
8
+ from xml.etree import ElementTree as ET
9
+ from quantum.core.parsers.base import BaseTagParser
10
+ from quantum.core.features.conditionals.src.ast_node import IfNode
11
+
12
+
13
+ class IfParser(BaseTagParser):
14
+ """
15
+ Parser for q:if statements.
16
+
17
+ Supports:
18
+ - Simple if conditions
19
+ - Multiple elseif blocks
20
+ - Optional else block
21
+ """
22
+
23
+ @property
24
+ def tag_names(self) -> List[str]:
25
+ return ['if']
26
+
27
+ def parse(self, element: ET.Element) -> IfNode:
28
+ """
29
+ Parse q:if statement with elseif and else blocks.
30
+
31
+ Args:
32
+ element: XML element for q:if
33
+
34
+ Returns:
35
+ IfNode AST node
36
+ """
37
+ condition = self.get_attr(element, 'condition', '')
38
+ if_node = IfNode(condition)
39
+
40
+ for child in element:
41
+ child_type = self.get_element_name(child)
42
+
43
+ if child_type == 'elseif':
44
+ # Parse elseif block
45
+ elseif_condition = self.get_attr(child, 'condition', '')
46
+ elseif_body = []
47
+
48
+ for elseif_child in child:
49
+ statement = self.parse_statement(elseif_child)
50
+ if statement:
51
+ elseif_body.append(statement)
52
+
53
+ if_node.add_elseif_block(elseif_condition, elseif_body)
54
+
55
+ elif child_type == 'else':
56
+ # Parse else block
57
+ for else_child in child:
58
+ statement = self.parse_statement(else_child)
59
+ if statement:
60
+ if_node.add_else_statement(statement)
61
+
62
+ else:
63
+ # Main if block statement
64
+ statement = self.parse_statement(child)
65
+ if statement:
66
+ if_node.add_if_statement(statement)
67
+
68
+ return if_node
@@ -0,0 +1,105 @@
1
+ """
2
+ Loop Parser - Parse q:loop statements
3
+
4
+ Handles various loop types: range, array, list, query.
5
+ """
6
+
7
+ from typing import List
8
+ from xml.etree import ElementTree as ET
9
+ from quantum.core.parsers.base import BaseTagParser, ParserError
10
+ from quantum.core.features.loops.src.ast_node import LoopNode
11
+ from quantum.core.ast_nodes import TextNode
12
+
13
+
14
+ class LoopParser(BaseTagParser):
15
+ """
16
+ Parser for q:loop statements.
17
+
18
+ Supports:
19
+ - Range loops (from/to)
20
+ - Array iteration
21
+ - List iteration (with delimiter)
22
+ - Query result iteration
23
+ """
24
+
25
+ @property
26
+ def tag_names(self) -> List[str]:
27
+ return ['loop']
28
+
29
+ def parse(self, element: ET.Element) -> LoopNode:
30
+ """
31
+ Parse q:loop statement with various types.
32
+
33
+ Args:
34
+ element: XML element for q:loop
35
+
36
+ Returns:
37
+ LoopNode AST node
38
+ """
39
+ # Check for query attribute (shorthand syntax)
40
+ query_attr = self.get_attr(element, 'query')
41
+
42
+ if query_attr:
43
+ # <q:loop query="users"> shorthand: the query name doubles as the
44
+ # row variable, so the body reads {users.name}.
45
+ #
46
+ # `var=` is honoured here when given. It used to be read only in
47
+ # the branch below, so <q:loop query="projects" var="p"> silently
48
+ # discarded the name and {p.name} rendered as literal text with no
49
+ # error anywhere — found by writing a real screen against it
50
+ # (FRAMEWORK_PLAN.md Fase 4). Omitting var= keeps the old
51
+ # behaviour exactly.
52
+ loop_type = 'query'
53
+ var_name = self.get_attr(element, 'var') or query_attr
54
+ loop_node = LoopNode(loop_type, var_name)
55
+ loop_node.query_name = query_attr
56
+ else:
57
+ # Traditional syntax
58
+ loop_type = self.get_attr(element, 'type')
59
+ if not loop_type:
60
+ # No explicit type: infer from attributes present instead of
61
+ # always defaulting to 'range'. A bare <q:loop items="..."
62
+ # var="..."> with no type= used to be misclassified as a range
63
+ # loop and fail validation ("Range loop requires 'from'
64
+ # attribute") — confirmed breaking examples in multiple
65
+ # unrelated features (agents, data_import, scripting).
66
+ loop_type = 'array' if self.get_attr(element, 'items') else 'range'
67
+ var_name = self.get_attr(element, 'var')
68
+
69
+ if not var_name:
70
+ raise ParserError("Loop requires 'var' attribute")
71
+
72
+ loop_node = LoopNode(loop_type, var_name)
73
+
74
+ # Configure based on loop type
75
+ if loop_type == 'range':
76
+ loop_node.from_value = self.get_attr(element, 'from')
77
+ loop_node.to_value = self.get_attr(element, 'to')
78
+ loop_node.step_value = self.get_int_attr(element, 'step', 1)
79
+
80
+ elif loop_type == 'array':
81
+ loop_node.items = self.get_attr(element, 'items')
82
+ loop_node.index_name = self.get_attr(element, 'index')
83
+
84
+ elif loop_type == 'list':
85
+ loop_node.items = self.get_attr(element, 'items')
86
+ loop_node.delimiter = self.get_attr(element, 'delimiter', ',')
87
+ loop_node.index_name = self.get_attr(element, 'index')
88
+
89
+ elif loop_type == 'query':
90
+ loop_node.index_name = self.get_attr(element, 'index')
91
+
92
+ # Parse loop body - add text content before first child
93
+ if element.text and element.text.strip():
94
+ loop_node.add_statement(TextNode(element.text))
95
+
96
+ # Parse loop body statements
97
+ for child in element:
98
+ statement = self.parse_statement(child)
99
+ if statement:
100
+ loop_node.add_statement(statement)
101
+ # Add tail text after child element
102
+ if child.tail and child.tail.strip():
103
+ loop_node.add_statement(TextNode(child.tail))
104
+
105
+ return loop_node