|
7 | 7 | """Lower reactive Core IR into a self-contained HTML/JavaScript preview.""" |
8 | 8 |
|
9 | 9 | # pylint: disable=too-many-return-statements,too-many-branches |
| 10 | +# pylint: disable=too-many-instance-attributes,too-many-locals,too-many-statements |
10 | 11 |
|
11 | 12 | from __future__ import annotations |
12 | 13 |
|
@@ -125,7 +126,11 @@ def __init__(self) -> None: |
125 | 126 | self._ui_function_names: set[str] = set() |
126 | 127 | self._render_function = "" |
127 | 128 |
|
128 | | - def lower(self, program: IRProgram, modules: dict[str, IRProgram] | None = None) -> UILoweringResult: |
| 129 | + def lower( |
| 130 | + self, |
| 131 | + program: IRProgram, |
| 132 | + modules: dict[str, IRProgram] | None = None, |
| 133 | + ) -> UILoweringResult: |
129 | 134 | """Lower an IRProgram to UI output.""" |
130 | 135 | preamble = self._emit_preamble() |
131 | 136 | self._lower_imported_modules(modules or {}) |
@@ -208,12 +213,24 @@ def _lower_node(self, node: IRNode) -> None: |
208 | 213 | self._lower_render_block(node) |
209 | 214 | return |
210 | 215 | if isinstance(node, IRFunction): |
| 216 | + if self._is_ui_entry_function(node): |
| 217 | + self._ui_function_names.add(node.name) |
| 218 | + for child in node.body: |
| 219 | + self._lower_node(child) |
| 220 | + return |
211 | 221 | self._lower_function(node) |
212 | 222 | if not node.is_async: |
213 | 223 | self._ui_function_names.add(node.name) |
214 | 224 | for child in node.body: |
215 | 225 | self._lower_node(child) |
216 | 226 |
|
| 227 | + def _is_ui_entry_function(self, node: IRFunction) -> bool: |
| 228 | + """Return True for functions that serve as reactive UI entry containers.""" |
| 229 | + effects = getattr(node, "effects", None) |
| 230 | + if effects is None or not hasattr(effects, "names"): |
| 231 | + return False |
| 232 | + return "ui" in effects.names() |
| 233 | + |
217 | 234 | def _emit_preamble(self) -> str: |
218 | 235 | return """// Generated by Multilingual UI lowering |
219 | 236 | class ReactiveSignal { |
@@ -832,8 +849,16 @@ def _expr_to_js(self, node: IRNode | None) -> str: |
832 | 849 | return node.name |
833 | 850 | if isinstance(node, IRIndexAccess): |
834 | 851 | if isinstance(node.index, IRSliceExpr): |
835 | | - start = self._expr_to_js(node.index.start) if node.index.start is not None else "undefined" |
836 | | - stop = self._expr_to_js(node.index.stop) if node.index.stop is not None else "undefined" |
| 852 | + start = ( |
| 853 | + self._expr_to_js(node.index.start) |
| 854 | + if node.index.start is not None |
| 855 | + else "undefined" |
| 856 | + ) |
| 857 | + stop = ( |
| 858 | + self._expr_to_js(node.index.stop) |
| 859 | + if node.index.stop is not None |
| 860 | + else "undefined" |
| 861 | + ) |
837 | 862 | return f"{self._expr_to_js(node.obj)}.slice({start}, {stop})" |
838 | 863 | return f"{self._expr_to_js(node.obj)}[{self._expr_to_js(node.index)}]" |
839 | 864 | if isinstance(node, IRSliceExpr): |
@@ -946,6 +971,9 @@ def _call_name(self, node: IRNode | None) -> str | None: |
946 | 971 | return None |
947 | 972 |
|
948 | 973 |
|
949 | | -def lower_to_ui(program: IRProgram, modules: dict[str, IRProgram] | None = None) -> UILoweringResult: |
| 974 | +def lower_to_ui( |
| 975 | + program: IRProgram, |
| 976 | + modules: dict[str, IRProgram] | None = None, |
| 977 | +) -> UILoweringResult: |
950 | 978 | """Lower an IRProgram to a browser preview bundle.""" |
951 | 979 | return UILoweringPass().lower(program, modules=modules) |
0 commit comments