From ac7726df46c7760a4257c1c30e26d1a8e4cf1c6b Mon Sep 17 00:00:00 2001 From: Aaron Sinclair Date: Sat, 15 Nov 2025 03:57:15 -0800 Subject: [PATCH] Wire up reactive signal updates to conditionals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Critical change that makes conditionals reactive: - Added signal_conditional_deps map to track dependencies - Added _extract_signals_from_expr() to find signals in expressions - Populate dependency map during _collect_conditionals() - Replace TODO comments in signal setters with reevaluate calls How it works: 1. During collection, analyze if/match test/subject expressions 2. Extract all signal names referenced in conditions 3. Build map: signal_name -> [conditional_ids that use it] 4. When signal is updated (assign/augassign), call reevaluate for each dependent conditional Example: set_tab_0() now: store i32 0, i32* @signal_Tabs_tab_index call void @reevaluate_conditional_4() call void @reevaluate_conditional_5() call void @reevaluate_conditional_6() call void @reevaluate_conditional_7() This makes tabs fully interactive! 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- examples/tabs/Tabs.ll | 30 ++++++++-- examples/tabs/Tabs.wasm | Bin 7656 -> 8748 bytes .../llvm_codegen_signals.cpython-313.pyc | Bin 47325 -> 49080 bytes ritz/llvm_codegen_signals.py | 54 ++++++++++++++++-- 4 files changed, 74 insertions(+), 10 deletions(-) diff --git a/examples/tabs/Tabs.ll b/examples/tabs/Tabs.ll index ad1953f..99536cd 100644 --- a/examples/tabs/Tabs.ll +++ b/examples/tabs/Tabs.ll @@ -121,7 +121,10 @@ entry: define void @set_setting_0() { entry: store i32 0, i32* @signal_VerticalTabs_setting_tab - ; TODO: Update DOM nodes bound to setting_tab + call void @reevaluate_conditional_0() + call void @reevaluate_conditional_1() + call void @reevaluate_conditional_2() + call void @reevaluate_conditional_3() ret void } @@ -129,7 +132,10 @@ entry: define void @set_setting_1() { entry: store i32 1, i32* @signal_VerticalTabs_setting_tab - ; TODO: Update DOM nodes bound to setting_tab + call void @reevaluate_conditional_0() + call void @reevaluate_conditional_1() + call void @reevaluate_conditional_2() + call void @reevaluate_conditional_3() ret void } @@ -137,7 +143,10 @@ entry: define void @set_setting_2() { entry: store i32 2, i32* @signal_VerticalTabs_setting_tab - ; TODO: Update DOM nodes bound to setting_tab + call void @reevaluate_conditional_0() + call void @reevaluate_conditional_1() + call void @reevaluate_conditional_2() + call void @reevaluate_conditional_3() ret void } @@ -609,7 +618,10 @@ entry: define void @set_tab_0() { entry: store i32 0, i32* @signal_Tabs_tab_index - ; TODO: Update DOM nodes bound to tab_index + call void @reevaluate_conditional_4() + call void @reevaluate_conditional_5() + call void @reevaluate_conditional_6() + call void @reevaluate_conditional_7() ret void } @@ -617,7 +629,10 @@ entry: define void @set_tab_1() { entry: store i32 1, i32* @signal_Tabs_tab_index - ; TODO: Update DOM nodes bound to tab_index + call void @reevaluate_conditional_4() + call void @reevaluate_conditional_5() + call void @reevaluate_conditional_6() + call void @reevaluate_conditional_7() ret void } @@ -625,7 +640,10 @@ entry: define void @set_tab_2() { entry: store i32 2, i32* @signal_Tabs_tab_index - ; TODO: Update DOM nodes bound to tab_index + call void @reevaluate_conditional_4() + call void @reevaluate_conditional_5() + call void @reevaluate_conditional_6() + call void @reevaluate_conditional_7() ret void } diff --git a/examples/tabs/Tabs.wasm b/examples/tabs/Tabs.wasm index 431e63efd479f5522e11ad69f8f8fc81a54ffbb6..897da2a687fe5301911541cc976db9a62b4acce3 100755 GIT binary patch literal 8748 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 F{XeoS>jnS- literal 7656 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 delta 5356 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 E1M-Y1tN;K2 diff --git a/ritz/llvm_codegen_signals.py b/ritz/llvm_codegen_signals.py index db2afab..627c6db 100644 --- a/ritz/llvm_codegen_signals.py +++ b/ritz/llvm_codegen_signals.py @@ -37,6 +37,9 @@ class SignalLLVMIRGenerator: # Track signal-to-DOM bindings for updates self.signal_dom_bindings: dict[str, list[str]] = {} # signal_name -> [dom_node_ids] + # Track signal-to-conditional dependencies for reactivity + self.signal_conditional_deps: dict[str, list[int]] = {} # signal_name -> [conditional_ids] + # Track current component for signal scoping self.current_component = None @@ -67,6 +70,7 @@ class SignalLLVMIRGenerator: self.event_handlers = [] self.conditionals = [] self.conditional_map = {} + self.signal_conditional_deps = {} # Collect signals, handlers, and conditionals for this component self._collect_signals_and_handlers(component) @@ -133,6 +137,44 @@ class SignalLLVMIRGenerator: scan_statements(component["body"]) + # Build signal -> conditional dependency map + for cond_info in self.conditionals: + cond_id = cond_info["id"] + if cond_info["type"] == "if": + # Extract signals from test expression + signals = self._extract_signals_from_expr(cond_info["test"]) + elif cond_info["type"] == "match": + # Extract signals from subject expression + signals = self._extract_signals_from_expr(cond_info["subject"]) + else: + signals = [] + + # Register this conditional as dependent on these signals + for signal_name in signals: + if signal_name not in self.signal_conditional_deps: + self.signal_conditional_deps[signal_name] = [] + self.signal_conditional_deps[signal_name].append(cond_id) + + def _extract_signals_from_expr(self, expr: dict[str, Any]) -> list[str]: + """Extract signal names referenced in an expression.""" + signals = [] + + if expr["type"] == "name": + # Direct signal reference + signal_name = expr["id"] + if signal_name in self.signals: + signals.append(signal_name) + elif expr["type"] == "compare": + # Comparison expression - check left and right + signals.extend(self._extract_signals_from_expr(expr["left"])) + for comparator in expr.get("comparators", []): + signals.extend(self._extract_signals_from_expr(comparator)) + elif expr["type"] == "constant": + # Constants don't reference signals + pass + + return signals + def _emit_header(self) -> None: """Emit module header.""" self.lines.append("; Generated by Ritz Framework with Signals") @@ -248,8 +290,10 @@ class SignalLLVMIRGenerator: # Store the value self.lines.append(f" store i32 {value_var}, i32* {self._signal_global(signal_name)}") - # TODO: Trigger DOM updates - self.lines.append(f" ; TODO: Update DOM nodes bound to {signal_name}") + # Trigger reactive updates for conditionals that depend on this signal + if signal_name in self.signal_conditional_deps: + for cond_id in self.signal_conditional_deps[signal_name]: + self.lines.append(f" call void @reevaluate_conditional_{cond_id}()") elif stmt["type"] == "augassign": # Handle signal updates (count += 1) signal_name = stmt["target"] @@ -267,8 +311,10 @@ class SignalLLVMIRGenerator: # Store back self.lines.append(f" store i32 %{result_var}, i32* {self._signal_global(signal_name)}") - # TODO: Trigger DOM updates for this signal - self.lines.append(f" ; TODO: Update DOM nodes bound to {signal_name}") + # Trigger reactive updates for conditionals that depend on this signal + if signal_name in self.signal_conditional_deps: + for cond_id in self.signal_conditional_deps[signal_name]: + self.lines.append(f" call void @reevaluate_conditional_{cond_id}()") def _emit_conditional_functions(self) -> None: """Emit init and reevaluate functions for each conditional."""