Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b77c2ff3c | ||
|
|
d8a2e28fd5 |
No files matched your search
@@ -95,6 +95,26 @@ class RitzRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
js_create_text_with_int(prefixPtr, intValue) {
|
||||
const prefix = this.readString(prefixPtr);
|
||||
const text = prefix + intValue.toString();
|
||||
const textNode = document.createTextNode(text);
|
||||
const id = this.nextNodeId++;
|
||||
this.nodes.set(id, textNode);
|
||||
this._log(`createTextWithInt("${prefix}", ${intValue}) = "${text}" (id=${id})`);
|
||||
return id;
|
||||
}
|
||||
|
||||
js_update_text_with_int(nodeId, prefixPtr, intValue) {
|
||||
const node = this.nodes.get(nodeId);
|
||||
const prefix = this.readString(prefixPtr);
|
||||
const text = prefix + intValue.toString();
|
||||
if (node) {
|
||||
node.textContent = text;
|
||||
this._log(`updateTextWithInt(${nodeId}, "${prefix}", ${intValue}) = "${text}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Signal Management =====
|
||||
|
||||
updateSignals() {
|
||||
@@ -288,11 +308,13 @@ class RitzLoader {
|
||||
env: {
|
||||
js_create_element: (ptr) => runtime.js_create_element(ptr),
|
||||
js_create_text: (ptr) => runtime.js_create_text(ptr),
|
||||
js_create_text_with_int: (p, i) => runtime.js_create_text_with_int(p, i),
|
||||
js_append_child: (p, c) => runtime.js_append_child(p, c),
|
||||
js_set_attribute: (e, n, v) => runtime.js_set_attribute(e, n, v),
|
||||
js_set_root: (e) => runtime.js_set_root(e),
|
||||
js_bind_event: (e, n, h) => runtime.js_bind_event(e, n, h),
|
||||
js_update_text_content: (n, t) => runtime.js_update_text_content(n, t),
|
||||
js_update_text_with_int: (n, p, i) => runtime.js_update_text_with_int(n, p, i),
|
||||
js_create_marker: () => runtime.js_create_marker(),
|
||||
js_replace_at_marker: (m, o, n) => runtime.js_replace_at_marker(m, o, n),
|
||||
}
|
||||
|
||||
+694
-356
File diff suppressed because it is too large.
Load diff
+24
-8
@@ -49,11 +49,19 @@ def VerticalTabs():
|
||||
setting_tab = value
|
||||
|
||||
with Div(class_="vertical-tabs"):
|
||||
# Vertical tab menu - using reusable VTab component!
|
||||
# Vertical tab menu - inlined until we support function params
|
||||
with Div(class_="vertical-tab-menu"):
|
||||
yield VTab(label="⚙️ General", active=setting_tab == 0, onclick=lambda: set_setting(0))
|
||||
yield VTab(label="🔒 Privacy", active=setting_tab == 1, onclick=lambda: set_setting(1))
|
||||
yield VTab(label="🚀 Advanced", active=setting_tab == 2, onclick=lambda: set_setting(2))
|
||||
with Button(class_='vertical-tab active' if setting_tab == 0 else 'vertical-tab',
|
||||
onclick=lambda: set_setting(0)):
|
||||
yield "⚙️ General"
|
||||
|
||||
with Button(class_='vertical-tab active' if setting_tab == 1 else 'vertical-tab',
|
||||
onclick=lambda: set_setting(1)):
|
||||
yield "🔒 Privacy"
|
||||
|
||||
with Button(class_='vertical-tab active' if setting_tab == 2 else 'vertical-tab',
|
||||
onclick=lambda: set_setting(2)):
|
||||
yield "🚀 Advanced"
|
||||
|
||||
# Vertical tab content
|
||||
with Div(class_="vertical-tab-content"):
|
||||
@@ -106,11 +114,19 @@ def Tabs(orientation=0): # 0 = horizontal, 1 = vertical
|
||||
with P(class_="subtitle"):
|
||||
yield "Beautiful composition with TabMenu and TabPane components"
|
||||
|
||||
# Tab menu - using reusable HTab component!
|
||||
# Tab menu - inlined until we support function params
|
||||
with Ul(class_="tab-menu"):
|
||||
yield HTab(label="🏠 Home", active=tab_index == 0, onclick=lambda: set_tab(0))
|
||||
yield HTab(label="👤 Profile", active=tab_index == 1, onclick=lambda: set_tab(1))
|
||||
yield HTab(label="⚙️ Settings", active=tab_index == 2, onclick=lambda: set_tab(2))
|
||||
with Li(class_='tab active' if tab_index == 0 else 'tab'):
|
||||
with Button(onclick=lambda: set_tab(0)):
|
||||
yield "🏠 Home"
|
||||
|
||||
with Li(class_='tab active' if tab_index == 1 else 'tab'):
|
||||
with Button(onclick=lambda: set_tab(1)):
|
||||
yield "👤 Profile"
|
||||
|
||||
with Li(class_='tab active' if tab_index == 2 else 'tab'):
|
||||
with Button(onclick=lambda: set_tab(2)):
|
||||
yield "⚙️ Settings"
|
||||
|
||||
# Tab panes - match/case for clean switching
|
||||
with Div(class_="tab-content"):
|
||||
|
||||
Binary file not shown.
+117
-73
@@ -2,21 +2,21 @@
|
||||
; Components: TextInput
|
||||
|
||||
; String constants
|
||||
@.str.0 = private unnamed_addr constant [4 x i8] c"div\00"
|
||||
@.str.1 = private unnamed_addr constant [6 x i8] c"class\00"
|
||||
@.str.2 = private unnamed_addr constant [10 x i8] c"container\00"
|
||||
@.str.3 = private unnamed_addr constant [3 x i8] c"h1\00"
|
||||
@.str.4 = private unnamed_addr constant [17 x i8] c"Reactive Counter\00"
|
||||
@.str.5 = private unnamed_addr constant [2 x i8] c"p\00"
|
||||
@.str.6 = private unnamed_addr constant [13 x i8] c"instructions\00"
|
||||
@.str.7 = private unnamed_addr constant [65 x i8] c"Click buttons to see reactive updates (text input coming soon!):\00"
|
||||
@.str.8 = private unnamed_addr constant [13 x i8] c"button-group\00"
|
||||
@.str.9 = private unnamed_addr constant [7 x i8] c"button\00"
|
||||
@.str.10 = private unnamed_addr constant [6 x i8] c"click\00"
|
||||
@.str.11 = private unnamed_addr constant [10 x i8] c"decrement\00"
|
||||
@.str.12 = private unnamed_addr constant [4 x i8] c"−\00"
|
||||
@.str.13 = private unnamed_addr constant [16 x i8] c"counter-display\00"
|
||||
@.str.14 = private unnamed_addr constant [2 x i8] c"0\00"
|
||||
@.str.0 = private unnamed_addr constant [8 x i8] c"Count: \00"
|
||||
@.str.1 = private unnamed_addr constant [4 x i8] c"div\00"
|
||||
@.str.2 = private unnamed_addr constant [6 x i8] c"class\00"
|
||||
@.str.3 = private unnamed_addr constant [10 x i8] c"container\00"
|
||||
@.str.4 = private unnamed_addr constant [3 x i8] c"h1\00"
|
||||
@.str.5 = private unnamed_addr constant [17 x i8] c"Reactive Counter\00"
|
||||
@.str.6 = private unnamed_addr constant [2 x i8] c"p\00"
|
||||
@.str.7 = private unnamed_addr constant [13 x i8] c"instructions\00"
|
||||
@.str.8 = private unnamed_addr constant [65 x i8] c"Click buttons to see reactive updates (text input coming soon!):\00"
|
||||
@.str.9 = private unnamed_addr constant [13 x i8] c"button-group\00"
|
||||
@.str.10 = private unnamed_addr constant [7 x i8] c"button\00"
|
||||
@.str.11 = private unnamed_addr constant [6 x i8] c"click\00"
|
||||
@.str.12 = private unnamed_addr constant [10 x i8] c"decrement\00"
|
||||
@.str.13 = private unnamed_addr constant [4 x i8] c"−\00"
|
||||
@.str.14 = private unnamed_addr constant [16 x i8] c"counter-display\00"
|
||||
@.str.15 = private unnamed_addr constant [10 x i8] c"increment\00"
|
||||
@.str.16 = private unnamed_addr constant [2 x i8] c"+\00"
|
||||
@.str.17 = private unnamed_addr constant [5 x i8] c"todo\00"
|
||||
@@ -32,17 +32,22 @@
|
||||
; JavaScript bridge function declarations
|
||||
declare i32 @js_create_element(i8*)
|
||||
declare i32 @js_create_text(i8*)
|
||||
declare i32 @js_create_text_with_int(i8*, i32)
|
||||
declare void @js_append_child(i32, i32)
|
||||
declare void @js_set_attribute(i32, i8*, i8*)
|
||||
declare void @js_set_root(i32)
|
||||
declare void @js_bind_event(i32, i8*, i8*)
|
||||
declare void @js_update_text_content(i32, i8*)
|
||||
declare void @js_update_text_with_int(i32, i8*, i32)
|
||||
declare i32 @js_create_marker()
|
||||
declare void @js_replace_at_marker(i32, i32, i32)
|
||||
|
||||
; Signal storage for TextInput (reactive state in WASM memory)
|
||||
@signal_TextInput_count = global i32 0
|
||||
|
||||
; Reactive text node storage
|
||||
@reactive_text_0_node = global i32 0
|
||||
|
||||
; Signal helper functions
|
||||
define i32 @get_count() {
|
||||
entry:
|
||||
@@ -63,76 +68,115 @@ entry:
|
||||
%var0 = load i32, i32* @signal_TextInput_count
|
||||
%var1 = add i32 %var0, 1
|
||||
store i32 %var1, i32* @signal_TextInput_count
|
||||
%var2 = load i32, i32* @reactive_text_0_node
|
||||
%var3 = getelementptr inbounds [8 x i8], [8 x i8]* @.str.0, i32 0, i32 0
|
||||
%var4 = load i32, i32* @signal_TextInput_count
|
||||
call void @js_update_text_with_int(i32 %var2, i8* %var3, i32 %var4)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Event handler: decrement
|
||||
define void @decrement() {
|
||||
entry:
|
||||
%var2 = load i32, i32* @signal_TextInput_count
|
||||
%var3 = sub i32 %var2, 1
|
||||
store i32 %var3, i32* @signal_TextInput_count
|
||||
%var5 = load i32, i32* @signal_TextInput_count
|
||||
%var6 = sub i32 %var5, 1
|
||||
store i32 %var6, i32* @signal_TextInput_count
|
||||
%var7 = load i32, i32* @reactive_text_0_node
|
||||
%var8 = getelementptr inbounds [8 x i8], [8 x i8]* @.str.0, i32 0, i32 0
|
||||
%var9 = load i32, i32* @signal_TextInput_count
|
||||
call void @js_update_text_with_int(i32 %var7, i8* %var8, i32 %var9)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Component render function
|
||||
define i32 @render_TextInput() {
|
||||
entry:
|
||||
%var4 = call i32 @js_create_element(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.0, i32 0, i32 0))
|
||||
call void @js_set_attribute(i32 %var4, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i32 0, i32 0), i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.2, i32 0, i32 0))
|
||||
%var5 = call i32 @js_create_element(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.3, i32 0, i32 0))
|
||||
%var6 = call i32 @js_create_text(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @.str.4, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var5, i32 %var6)
|
||||
call void @js_append_child(i32 %var4, i32 %var5)
|
||||
%var7 = call i32 @js_create_element(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.5, i32 0, i32 0))
|
||||
call void @js_set_attribute(i32 %var7, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i32 0, i32 0), i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.6, i32 0, i32 0))
|
||||
%var8 = call i32 @js_create_text(i8* getelementptr inbounds ([65 x i8], [65 x i8]* @.str.7, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var7, i32 %var8)
|
||||
call void @js_append_child(i32 %var4, i32 %var7)
|
||||
%var9 = call i32 @js_create_element(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.0, i32 0, i32 0))
|
||||
call void @js_set_attribute(i32 %var9, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i32 0, i32 0), i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.8, i32 0, i32 0))
|
||||
%var10 = call i32 @js_create_element(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.9, i32 0, i32 0))
|
||||
call void @js_bind_event(i32 %var10, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.10, i32 0, i32 0), i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.11, i32 0, i32 0))
|
||||
%var11 = call i32 @js_create_text(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.12, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var10, i32 %var11)
|
||||
call void @js_append_child(i32 %var9, i32 %var10)
|
||||
%var12 = call i32 @js_create_element(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.5, i32 0, i32 0))
|
||||
call void @js_set_attribute(i32 %var12, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i32 0, i32 0), i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str.13, i32 0, i32 0))
|
||||
; TODO: Format f-string with signal count
|
||||
%var13 = call i32 @js_create_text(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.14, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var12, i32 %var13)
|
||||
call void @js_append_child(i32 %var9, i32 %var12)
|
||||
%var14 = call i32 @js_create_element(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.9, i32 0, i32 0))
|
||||
call void @js_bind_event(i32 %var14, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.10, i32 0, i32 0), i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.15, i32 0, i32 0))
|
||||
%var15 = call i32 @js_create_text(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.16, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var14, i32 %var15)
|
||||
call void @js_append_child(i32 %var9, i32 %var14)
|
||||
call void @js_append_child(i32 %var4, i32 %var9)
|
||||
%var16 = call i32 @js_create_element(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.5, i32 0, i32 0))
|
||||
call void @js_set_attribute(i32 %var16, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i32 0, i32 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.17, i32 0, i32 0))
|
||||
%var17 = call i32 @js_create_text(i8* getelementptr inbounds ([49 x i8], [49 x i8]* @.str.18, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var16, i32 %var17)
|
||||
call void @js_append_child(i32 %var4, i32 %var16)
|
||||
%var18 = call i32 @js_create_element(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.19, i32 0, i32 0))
|
||||
call void @js_set_attribute(i32 %var18, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i32 0, i32 0), i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.20, i32 0, i32 0))
|
||||
%var19 = call i32 @js_create_element(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.21, i32 0, i32 0))
|
||||
%var20 = call i32 @js_create_text(i8* getelementptr inbounds ([32 x i8], [32 x i8]* @.str.22, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var19, i32 %var20)
|
||||
call void @js_append_child(i32 %var18, i32 %var19)
|
||||
%var21 = call i32 @js_create_element(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.21, i32 0, i32 0))
|
||||
%var22 = call i32 @js_create_text(i8* getelementptr inbounds ([25 x i8], [25 x i8]* @.str.23, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var21, i32 %var22)
|
||||
call void @js_append_child(i32 %var18, i32 %var21)
|
||||
%var23 = call i32 @js_create_element(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.21, i32 0, i32 0))
|
||||
%var24 = call i32 @js_create_text(i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str.24, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var23, i32 %var24)
|
||||
call void @js_append_child(i32 %var18, i32 %var23)
|
||||
%var25 = call i32 @js_create_element(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.21, i32 0, i32 0))
|
||||
%var26 = call i32 @js_create_text(i8* getelementptr inbounds ([39 x i8], [39 x i8]* @.str.25, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var25, i32 %var26)
|
||||
call void @js_append_child(i32 %var18, i32 %var25)
|
||||
call void @js_append_child(i32 %var4, i32 %var18)
|
||||
ret i32 %var4
|
||||
%var11 = getelementptr inbounds [4 x i8], [4 x i8]* @.str.1, i32 0, i32 0
|
||||
%var10 = call i32 @js_create_element(i8* %var11)
|
||||
%var12 = getelementptr inbounds [6 x i8], [6 x i8]* @.str.2, i32 0, i32 0
|
||||
%var13 = getelementptr inbounds [10 x i8], [10 x i8]* @.str.3, i32 0, i32 0
|
||||
call void @js_set_attribute(i32 %var10, i8* %var12, i8* %var13)
|
||||
%var15 = getelementptr inbounds [3 x i8], [3 x i8]* @.str.4, i32 0, i32 0
|
||||
%var14 = call i32 @js_create_element(i8* %var15)
|
||||
%var16 = call i32 @js_create_text(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @.str.5, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var14, i32 %var16)
|
||||
call void @js_append_child(i32 %var10, i32 %var14)
|
||||
%var18 = getelementptr inbounds [2 x i8], [2 x i8]* @.str.6, i32 0, i32 0
|
||||
%var17 = call i32 @js_create_element(i8* %var18)
|
||||
%var19 = getelementptr inbounds [6 x i8], [6 x i8]* @.str.2, i32 0, i32 0
|
||||
%var20 = getelementptr inbounds [13 x i8], [13 x i8]* @.str.7, i32 0, i32 0
|
||||
call void @js_set_attribute(i32 %var17, i8* %var19, i8* %var20)
|
||||
%var21 = call i32 @js_create_text(i8* getelementptr inbounds ([65 x i8], [65 x i8]* @.str.8, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var17, i32 %var21)
|
||||
call void @js_append_child(i32 %var10, i32 %var17)
|
||||
%var23 = getelementptr inbounds [4 x i8], [4 x i8]* @.str.1, i32 0, i32 0
|
||||
%var22 = call i32 @js_create_element(i8* %var23)
|
||||
%var24 = getelementptr inbounds [6 x i8], [6 x i8]* @.str.2, i32 0, i32 0
|
||||
%var25 = getelementptr inbounds [13 x i8], [13 x i8]* @.str.9, i32 0, i32 0
|
||||
call void @js_set_attribute(i32 %var22, i8* %var24, i8* %var25)
|
||||
%var27 = getelementptr inbounds [7 x i8], [7 x i8]* @.str.10, i32 0, i32 0
|
||||
%var26 = call i32 @js_create_element(i8* %var27)
|
||||
%var28 = getelementptr inbounds [6 x i8], [6 x i8]* @.str.11, i32 0, i32 0
|
||||
%var29 = getelementptr inbounds [10 x i8], [10 x i8]* @.str.12, i32 0, i32 0
|
||||
call void @js_bind_event(i32 %var26, i8* %var28, i8* %var29)
|
||||
%var30 = call i32 @js_create_text(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.13, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var26, i32 %var30)
|
||||
call void @js_append_child(i32 %var22, i32 %var26)
|
||||
%var32 = getelementptr inbounds [2 x i8], [2 x i8]* @.str.6, i32 0, i32 0
|
||||
%var31 = call i32 @js_create_element(i8* %var32)
|
||||
%var33 = getelementptr inbounds [6 x i8], [6 x i8]* @.str.2, i32 0, i32 0
|
||||
%var34 = getelementptr inbounds [16 x i8], [16 x i8]* @.str.14, i32 0, i32 0
|
||||
call void @js_set_attribute(i32 %var31, i8* %var33, i8* %var34)
|
||||
%var35 = getelementptr inbounds [8 x i8], [8 x i8]* @.str.0, i32 0, i32 0
|
||||
%var36 = load i32, i32* @signal_TextInput_count
|
||||
%var37 = call i32 @js_create_text_with_int(i8* %var35, i32 %var36)
|
||||
store i32 %var37, i32* @reactive_text_0_node
|
||||
call void @js_append_child(i32 %var31, i32 %var37)
|
||||
call void @js_append_child(i32 %var22, i32 %var31)
|
||||
%var39 = getelementptr inbounds [7 x i8], [7 x i8]* @.str.10, i32 0, i32 0
|
||||
%var38 = call i32 @js_create_element(i8* %var39)
|
||||
%var40 = getelementptr inbounds [6 x i8], [6 x i8]* @.str.11, i32 0, i32 0
|
||||
%var41 = getelementptr inbounds [10 x i8], [10 x i8]* @.str.15, i32 0, i32 0
|
||||
call void @js_bind_event(i32 %var38, i8* %var40, i8* %var41)
|
||||
%var42 = call i32 @js_create_text(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.16, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var38, i32 %var42)
|
||||
call void @js_append_child(i32 %var22, i32 %var38)
|
||||
call void @js_append_child(i32 %var10, i32 %var22)
|
||||
%var44 = getelementptr inbounds [2 x i8], [2 x i8]* @.str.6, i32 0, i32 0
|
||||
%var43 = call i32 @js_create_element(i8* %var44)
|
||||
%var45 = getelementptr inbounds [6 x i8], [6 x i8]* @.str.2, i32 0, i32 0
|
||||
%var46 = getelementptr inbounds [5 x i8], [5 x i8]* @.str.17, i32 0, i32 0
|
||||
call void @js_set_attribute(i32 %var43, i8* %var45, i8* %var46)
|
||||
%var47 = call i32 @js_create_text(i8* getelementptr inbounds ([49 x i8], [49 x i8]* @.str.18, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var43, i32 %var47)
|
||||
call void @js_append_child(i32 %var10, i32 %var43)
|
||||
%var49 = getelementptr inbounds [3 x i8], [3 x i8]* @.str.19, i32 0, i32 0
|
||||
%var48 = call i32 @js_create_element(i8* %var49)
|
||||
%var50 = getelementptr inbounds [6 x i8], [6 x i8]* @.str.2, i32 0, i32 0
|
||||
%var51 = getelementptr inbounds [10 x i8], [10 x i8]* @.str.20, i32 0, i32 0
|
||||
call void @js_set_attribute(i32 %var48, i8* %var50, i8* %var51)
|
||||
%var53 = getelementptr inbounds [3 x i8], [3 x i8]* @.str.21, i32 0, i32 0
|
||||
%var52 = call i32 @js_create_element(i8* %var53)
|
||||
%var54 = call i32 @js_create_text(i8* getelementptr inbounds ([32 x i8], [32 x i8]* @.str.22, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var52, i32 %var54)
|
||||
call void @js_append_child(i32 %var48, i32 %var52)
|
||||
%var56 = getelementptr inbounds [3 x i8], [3 x i8]* @.str.21, i32 0, i32 0
|
||||
%var55 = call i32 @js_create_element(i8* %var56)
|
||||
%var57 = call i32 @js_create_text(i8* getelementptr inbounds ([25 x i8], [25 x i8]* @.str.23, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var55, i32 %var57)
|
||||
call void @js_append_child(i32 %var48, i32 %var55)
|
||||
%var59 = getelementptr inbounds [3 x i8], [3 x i8]* @.str.21, i32 0, i32 0
|
||||
%var58 = call i32 @js_create_element(i8* %var59)
|
||||
%var60 = call i32 @js_create_text(i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str.24, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var58, i32 %var60)
|
||||
call void @js_append_child(i32 %var48, i32 %var58)
|
||||
%var62 = getelementptr inbounds [3 x i8], [3 x i8]* @.str.21, i32 0, i32 0
|
||||
%var61 = call i32 @js_create_element(i8* %var62)
|
||||
%var63 = call i32 @js_create_text(i8* getelementptr inbounds ([39 x i8], [39 x i8]* @.str.25, i32 0, i32 0))
|
||||
call void @js_append_child(i32 %var61, i32 %var63)
|
||||
call void @js_append_child(i32 %var48, i32 %var61)
|
||||
call void @js_append_child(i32 %var10, i32 %var48)
|
||||
ret i32 %var10
|
||||
}
|
||||
|
||||
; Main entry point
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+219
-47
@@ -47,6 +47,13 @@ class SignalLLVMIRGenerator:
|
||||
self.signal_attribute_deps: dict[str, list[int]] = {} # signal_name -> [attr_ids]
|
||||
self.stmt_reactive_attrs: dict[int, list[int]] = {} # stmt id() -> [attr_ids]
|
||||
|
||||
# Track reactive text nodes (f-strings with signal dependencies)
|
||||
self.reactive_text_nodes: list[dict[str, Any]] = [] # List of reactive text node metadata
|
||||
self.reactive_text_map: dict[int, dict[str, Any]] = {} # text_id -> text_info
|
||||
self.next_reactive_text_id = 0
|
||||
self.signal_text_deps: dict[str, list[int]] = {} # signal_name -> [text_ids]
|
||||
self.stmt_reactive_text: dict[int, int] = {} # stmt id() -> text_id
|
||||
|
||||
# Track current component for signal scoping
|
||||
self.current_component = None
|
||||
|
||||
@@ -61,7 +68,7 @@ class SignalLLVMIRGenerator:
|
||||
def _signal_global(self, signal_name: str) -> str:
|
||||
"""Get the scoped global name for a signal."""
|
||||
if self.current_component:
|
||||
return f"@signal_{self.current_component['name']}_{signal_name}"
|
||||
return f"@signal_{self.current_component}_{signal_name}"
|
||||
return f"@signal_{signal_name}"
|
||||
|
||||
def generate(self) -> str:
|
||||
@@ -73,7 +80,7 @@ class SignalLLVMIRGenerator:
|
||||
# Process each component
|
||||
for component in self.components:
|
||||
# Set current component for signal scoping
|
||||
self.current_component = component
|
||||
self.current_component = component["name"]
|
||||
|
||||
# Reset component-specific state
|
||||
self.signals = {}
|
||||
@@ -90,11 +97,13 @@ class SignalLLVMIRGenerator:
|
||||
self._collect_signals_and_handlers(component)
|
||||
self._collect_conditionals(component)
|
||||
self._collect_reactive_attributes(component)
|
||||
self._collect_reactive_text_nodes(component)
|
||||
|
||||
# Generate component-specific code
|
||||
self._emit_signal_globals(component)
|
||||
self._emit_conditional_globals()
|
||||
self._emit_reactive_attribute_globals()
|
||||
self._emit_reactive_text_globals()
|
||||
self._emit_helper_functions()
|
||||
self._emit_event_handlers()
|
||||
self._emit_conditional_functions()
|
||||
@@ -259,6 +268,96 @@ class SignalLLVMIRGenerator:
|
||||
|
||||
scan_statements(component["body"])
|
||||
|
||||
def _collect_reactive_text_nodes(self, component: dict[str, Any]) -> None:
|
||||
"""Scan IR tree for text nodes (f-strings) that depend on signals."""
|
||||
|
||||
def scan_statements(stmts: list[dict[str, Any]]):
|
||||
for stmt in stmts:
|
||||
if stmt["type"] == "yield" and stmt.get("value"):
|
||||
value = stmt["value"]
|
||||
# Check if it's an f-string with signal dependencies
|
||||
if value["type"] == "fstring":
|
||||
signals = self._extract_signals_from_fstring(value)
|
||||
|
||||
if signals:
|
||||
# This text node is reactive!
|
||||
text_id = self.next_reactive_text_id
|
||||
self.next_reactive_text_id += 1
|
||||
|
||||
stmt_id = id(stmt)
|
||||
|
||||
text_info = {
|
||||
"id": text_id,
|
||||
"stmt_id": stmt_id,
|
||||
"fstring": value,
|
||||
"signals": signals,
|
||||
}
|
||||
|
||||
self.reactive_text_nodes.append(text_info)
|
||||
self.reactive_text_map[text_id] = text_info
|
||||
self.stmt_reactive_text[stmt_id] = text_id
|
||||
|
||||
# Register this text node as dependent on these signals
|
||||
for signal_name in signals:
|
||||
if signal_name not in self.signal_text_deps:
|
||||
self.signal_text_deps[signal_name] = []
|
||||
self.signal_text_deps[signal_name].append(text_id)
|
||||
|
||||
elif stmt["type"] == "with":
|
||||
# Recursively scan nested statements
|
||||
scan_statements(stmt.get("body", []))
|
||||
elif stmt["type"] == "if":
|
||||
scan_statements(stmt["body"])
|
||||
scan_statements(stmt.get("orelse", []))
|
||||
elif stmt["type"] == "match":
|
||||
for case in stmt["cases"]:
|
||||
scan_statements(case.get("body", []))
|
||||
|
||||
scan_statements(component["body"])
|
||||
|
||||
def _extract_signals_from_fstring(self, fstring: dict[str, Any]) -> list[str]:
|
||||
"""Extract signal names from an f-string expression."""
|
||||
signals = []
|
||||
for part in fstring.get("parts", []):
|
||||
if part["type"] == "formatted_value":
|
||||
signals.extend(self._extract_signals_from_expr(part["value"]))
|
||||
return signals
|
||||
|
||||
def _build_fstring_text(self, fstring: dict[str, Any]) -> tuple[str, str] | None:
|
||||
"""
|
||||
Analyze f-string and return (prefix_ptr, int_value) for simple "prefix{int}" pattern.
|
||||
Returns None if the pattern doesn't match (need full string concatenation).
|
||||
|
||||
For now, we only handle the simple case: "static_prefix{signal_value}"
|
||||
e.g., "Count: {count}" -> ("Count: ", count_value)
|
||||
"""
|
||||
parts = fstring.get("parts", [])
|
||||
|
||||
# Check for simple pattern: [constant, formatted_value]
|
||||
if len(parts) == 2 and parts[0]["type"] == "constant" and parts[1]["type"] == "formatted_value":
|
||||
# Get prefix string
|
||||
prefix = str(parts[0]["value"])
|
||||
prefix_str = self._get_or_create_string(prefix)
|
||||
prefix_var = self._new_var()
|
||||
self.lines.append(
|
||||
f" %{prefix_var} = getelementptr inbounds {self._string_type(prefix)}, "
|
||||
f"{self._string_type(prefix)}* {prefix_str}, i32 0, i32 0"
|
||||
)
|
||||
|
||||
# Get signal value
|
||||
value_expr = parts[1]["value"]
|
||||
if value_expr["type"] == "name":
|
||||
signal_name = value_expr["id"]
|
||||
if signal_name in self.signals:
|
||||
val_var = self._new_var()
|
||||
self.lines.append(
|
||||
f" %{val_var} = load i32, i32* @signal_{self.current_component}_{signal_name}"
|
||||
)
|
||||
return (f"%{prefix_var}", f"%{val_var}")
|
||||
|
||||
# TODO: Handle more complex patterns
|
||||
return None
|
||||
|
||||
def _emit_header(self) -> None:
|
||||
"""Emit module header."""
|
||||
self.lines.append("; Generated by Ritz Framework with Signals")
|
||||
@@ -325,11 +424,24 @@ class SignalLLVMIRGenerator:
|
||||
self.lines.append(f"@reactive_attr_{attr_id}_elem = global i32 0")
|
||||
self.lines.append("")
|
||||
|
||||
def _emit_reactive_text_globals(self) -> None:
|
||||
"""Emit global storage for reactive text node references."""
|
||||
if not self.reactive_text_nodes:
|
||||
return
|
||||
|
||||
self.lines.append("; Reactive text node storage")
|
||||
for text_info in self.reactive_text_nodes:
|
||||
text_id = text_info["id"]
|
||||
# Store text node reference so we can update its content later
|
||||
self.lines.append(f"@reactive_text_{text_id}_node = global i32 0")
|
||||
self.lines.append("")
|
||||
|
||||
def _emit_function_declarations(self) -> None:
|
||||
"""Emit JS bridge function declarations."""
|
||||
self.lines.append("; JavaScript bridge function declarations")
|
||||
self.lines.append("declare i32 @js_create_element(i8*)")
|
||||
self.lines.append("declare i32 @js_create_text(i8*)")
|
||||
self.lines.append("declare i32 @js_create_text_with_int(i8*, i32)") # Create text with prefix + int
|
||||
self.lines.append("declare void @js_append_child(i32, i32)")
|
||||
self.lines.append("declare void @js_set_attribute(i32, i8*, i8*)")
|
||||
self.lines.append("declare void @js_set_root(i32)")
|
||||
@@ -337,6 +449,7 @@ class SignalLLVMIRGenerator:
|
||||
"declare void @js_bind_event(i32, i8*, i8*)"
|
||||
) # elem, event_name, handler_name
|
||||
self.lines.append("declare void @js_update_text_content(i32, i8*)") # Update DOM node text
|
||||
self.lines.append("declare void @js_update_text_with_int(i32, i8*, i32)") # Update with prefix + int
|
||||
self.lines.append("declare i32 @js_create_marker()") # Create invisible marker element
|
||||
self.lines.append("declare void @js_replace_at_marker(i32, i32, i32)") # marker, old, new
|
||||
self.lines.append("")
|
||||
@@ -444,6 +557,11 @@ class SignalLLVMIRGenerator:
|
||||
if signal_name in self.signal_attribute_deps:
|
||||
for attr_id in self.signal_attribute_deps[signal_name]:
|
||||
self._emit_update_reactive_attribute(attr_id)
|
||||
|
||||
# Trigger reactive updates for text nodes that depend on this signal
|
||||
if signal_name in self.signal_text_deps:
|
||||
for text_id in self.signal_text_deps[signal_name]:
|
||||
self._emit_update_reactive_text_node(text_id)
|
||||
elif stmt["type"] == "augassign":
|
||||
# Handle signal updates (count += 1)
|
||||
signal_name = stmt["target"]
|
||||
@@ -471,6 +589,11 @@ class SignalLLVMIRGenerator:
|
||||
for attr_id in self.signal_attribute_deps[signal_name]:
|
||||
self._emit_update_reactive_attribute(attr_id)
|
||||
|
||||
# Trigger reactive updates for text nodes that depend on this signal
|
||||
if signal_name in self.signal_text_deps:
|
||||
for text_id in self.signal_text_deps[signal_name]:
|
||||
self._emit_update_reactive_text_node(text_id)
|
||||
|
||||
def _emit_update_reactive_attribute(self, attr_id: int) -> None:
|
||||
"""Emit inline code to update a reactive attribute."""
|
||||
# Look up attribute by ID
|
||||
@@ -489,13 +612,42 @@ class SignalLLVMIRGenerator:
|
||||
value_ptr = self._emit_string_ifexp(expression)
|
||||
# Set the attribute
|
||||
name_str = self._get_or_create_string(attr_name)
|
||||
attr_name_ptr = self._new_var()
|
||||
self.lines.append(
|
||||
f" call void @js_set_attribute(i32 %{elem_var}, "
|
||||
f"i8* getelementptr inbounds ({self._string_type(attr_name)}, {self._string_type(attr_name)}* {name_str}, i32 0, i32 0), "
|
||||
f"i8* %{value_ptr})"
|
||||
f" %{attr_name_ptr} = getelementptr inbounds {self._string_type(attr_name)}, "
|
||||
f"{self._string_type(attr_name)}* {name_str}, i32 0, i32 0"
|
||||
)
|
||||
self.lines.append(
|
||||
f" call void @js_set_attribute(i32 %{elem_var}, i8* %{attr_name_ptr}, i8* %{value_ptr})"
|
||||
)
|
||||
# Could add support for other expression types here
|
||||
|
||||
def _emit_update_reactive_text_node(self, text_id: int) -> None:
|
||||
"""Emit inline code to update a reactive text node."""
|
||||
# Look up text node by ID
|
||||
text_info = self.reactive_text_map.get(text_id)
|
||||
if not text_info:
|
||||
return # Text node not found
|
||||
|
||||
# Load the text node reference
|
||||
node_var = self._new_var()
|
||||
self.lines.append(f" %{node_var} = load i32, i32* @reactive_text_{text_id}_node")
|
||||
|
||||
# Build the f-string text
|
||||
result = self._build_fstring_text(text_info["fstring"])
|
||||
|
||||
if result:
|
||||
# Simple "prefix{int}" pattern - use specialized function
|
||||
prefix_ptr, int_val = result
|
||||
self.lines.append(
|
||||
f" call void @js_update_text_with_int(i32 %{node_var}, i8* {prefix_ptr}, i32 {int_val})"
|
||||
)
|
||||
else:
|
||||
# Complex pattern - would need full string concatenation
|
||||
# For now, just update with empty string
|
||||
empty_ptr = self._emit_string_ptr("")
|
||||
self.lines.append(f" call void @js_update_text_content(i32 %{node_var}, i8* {empty_ptr})")
|
||||
|
||||
def _emit_conditional_functions(self) -> None:
|
||||
"""Emit init and reevaluate functions for each conditional."""
|
||||
for cond_info in self.conditionals:
|
||||
@@ -760,8 +912,8 @@ class SignalLLVMIRGenerator:
|
||||
|
||||
# Create wrapper div for the branch content
|
||||
wrapper_var = self._new_var()
|
||||
div_str = self._get_or_create_string("div")
|
||||
self.lines.append(f" %{wrapper_var} = call i32 @js_create_element(i8* getelementptr inbounds ({self._string_type('div')}, {self._string_type('div')}* {div_str}, i32 0, i32 0))")
|
||||
div_ptr = self._emit_string_ptr("div")
|
||||
self.lines.append(f" %{wrapper_var} = call i32 @js_create_element(i8* {div_ptr})")
|
||||
|
||||
# Push wrapper onto parent stack so child elements append to it
|
||||
self.parent_element_stack.append(wrapper_var)
|
||||
@@ -1019,15 +1171,14 @@ class SignalLLVMIRGenerator:
|
||||
true_str = str(expr["body"]["value"])
|
||||
false_str = str(expr["orelse"]["value"])
|
||||
|
||||
true_str_global = self._get_or_create_string(true_str)
|
||||
false_str_global = self._get_or_create_string(false_str)
|
||||
# Emit string pointers
|
||||
true_ptr = self._emit_string_ptr(true_str)
|
||||
false_ptr = self._emit_string_ptr(false_str)
|
||||
|
||||
# Use select with inline getelementptr
|
||||
# Use select instruction
|
||||
result_var = self._new_var()
|
||||
self.lines.append(
|
||||
f" %{result_var} = select i1 %{cond_var}, "
|
||||
f"i8* getelementptr inbounds ({self._string_type(true_str)}, {self._string_type(true_str)}* {true_str_global}, i32 0, i32 0), "
|
||||
f"i8* getelementptr inbounds ({self._string_type(false_str)}, {self._string_type(false_str)}* {false_str_global}, i32 0, i32 0)"
|
||||
f" %{result_var} = select i1 %{cond_var}, i8* {true_ptr}, i8* {false_ptr}"
|
||||
)
|
||||
|
||||
return result_var
|
||||
@@ -1124,10 +1275,8 @@ class SignalLLVMIRGenerator:
|
||||
|
||||
# Create element
|
||||
elem_var = self._new_var()
|
||||
tag_str = self._get_or_create_string(tag)
|
||||
self.lines.append(
|
||||
f" %{elem_var} = call i32 @js_create_element(i8* getelementptr inbounds ({self._string_type(tag)}, {self._string_type(tag)}* {tag_str}, i32 0, i32 0))"
|
||||
)
|
||||
tag_ptr = self._emit_string_ptr(tag)
|
||||
self.lines.append(f" %{elem_var} = call i32 @js_create_element(i8* {tag_ptr})")
|
||||
|
||||
# Handle onclick prop specially
|
||||
onclick_handler = None
|
||||
@@ -1141,31 +1290,25 @@ class SignalLLVMIRGenerator:
|
||||
attr_name = prop_name.rstrip("_")
|
||||
if prop_value["type"] == "constant":
|
||||
value = str(prop_value["value"])
|
||||
name_str = self._get_or_create_string(attr_name)
|
||||
value_str = self._get_or_create_string(value)
|
||||
name_ptr = self._emit_string_ptr(attr_name)
|
||||
value_ptr = self._emit_string_ptr(value)
|
||||
self.lines.append(
|
||||
f" call void @js_set_attribute(i32 %{elem_var}, "
|
||||
f"i8* getelementptr inbounds ({self._string_type(attr_name)}, {self._string_type(attr_name)}* {name_str}, i32 0, i32 0), "
|
||||
f"i8* getelementptr inbounds ({self._string_type(value)}, {self._string_type(value)}* {value_str}, i32 0, i32 0))"
|
||||
f" call void @js_set_attribute(i32 %{elem_var}, i8* {name_ptr}, i8* {value_ptr})"
|
||||
)
|
||||
elif prop_value["type"] == "ifexp":
|
||||
# Handle ternary expression for attribute value
|
||||
value_ptr = self._emit_string_ifexp(prop_value)
|
||||
name_str = self._get_or_create_string(attr_name)
|
||||
name_ptr = self._emit_string_ptr(attr_name)
|
||||
self.lines.append(
|
||||
f" call void @js_set_attribute(i32 %{elem_var}, "
|
||||
f"i8* getelementptr inbounds ({self._string_type(attr_name)}, {self._string_type(attr_name)}* {name_str}, i32 0, i32 0), "
|
||||
f"i8* %{value_ptr})"
|
||||
f" call void @js_set_attribute(i32 %{elem_var}, i8* {name_ptr}, i8* %{value_ptr})"
|
||||
)
|
||||
|
||||
# Bind onclick if present
|
||||
if onclick_handler:
|
||||
event_str = self._get_or_create_string("click")
|
||||
handler_str = self._get_or_create_string(onclick_handler)
|
||||
event_ptr = self._emit_string_ptr("click")
|
||||
handler_ptr = self._emit_string_ptr(onclick_handler)
|
||||
self.lines.append(
|
||||
f" call void @js_bind_event(i32 %{elem_var}, "
|
||||
f"i8* getelementptr inbounds ({self._string_type('click')}, {self._string_type('click')}* {event_str}, i32 0, i32 0), "
|
||||
f"i8* getelementptr inbounds ({self._string_type(onclick_handler)}, {self._string_type(onclick_handler)}* {handler_str}, i32 0, i32 0))"
|
||||
f" call void @js_bind_event(i32 %{elem_var}, i8* {event_ptr}, i8* {handler_ptr})"
|
||||
)
|
||||
|
||||
# Store element reference if this element has reactive attributes
|
||||
@@ -1207,28 +1350,47 @@ class SignalLLVMIRGenerator:
|
||||
return text_var
|
||||
|
||||
elif value["type"] == "fstring":
|
||||
# Handle f-strings with signal references
|
||||
# For now, create text with signal value
|
||||
# TODO: Implement proper string concatenation
|
||||
parts = value["parts"]
|
||||
# Handle f-strings - check if reactive
|
||||
stmt_id = id(stmt)
|
||||
|
||||
if stmt_id in self.stmt_reactive_text:
|
||||
# This is a reactive text node!
|
||||
text_id = self.stmt_reactive_text[stmt_id]
|
||||
text_info = self.reactive_text_map[text_id]
|
||||
|
||||
# Build the initial text from f-string
|
||||
result = self._build_fstring_text(text_info["fstring"])
|
||||
|
||||
# Find signal references
|
||||
for part in parts:
|
||||
if part["type"] == "formatted_value":
|
||||
if part["value"]["type"] == "name":
|
||||
signal_name = part["value"]["id"]
|
||||
if signal_name in self.signals:
|
||||
# Load signal value and create text node
|
||||
# This is simplified - real impl would concat strings
|
||||
text_var = self._new_var()
|
||||
if result:
|
||||
# Simple "prefix{int}" pattern
|
||||
prefix_ptr, int_val = result
|
||||
self.lines.append(
|
||||
f" ; TODO: Format f-string with signal {signal_name}"
|
||||
f" %{text_var} = call i32 @js_create_text_with_int(i8* {prefix_ptr}, i32 {int_val})"
|
||||
)
|
||||
# For now, just create placeholder text
|
||||
placeholder = self._get_or_create_string("0")
|
||||
else:
|
||||
# Complex pattern - create empty for now
|
||||
empty_ptr = self._emit_string_ptr("")
|
||||
self.lines.append(f" %{text_var} = call i32 @js_create_text(i8* {empty_ptr})")
|
||||
|
||||
# Store the text node reference for reactive updates
|
||||
self.lines.append(f" store i32 %{text_var}, i32* @reactive_text_{text_id}_node")
|
||||
|
||||
return text_var
|
||||
else:
|
||||
# Non-reactive f-string (no signal dependencies)
|
||||
result = self._build_fstring_text(value)
|
||||
text_var = self._new_var()
|
||||
if result:
|
||||
# Simple "prefix{int}" pattern
|
||||
prefix_ptr, int_val = result
|
||||
self.lines.append(
|
||||
f" %{text_var} = call i32 @js_create_text(i8* getelementptr inbounds ([2 x i8], [2 x i8]* {placeholder}, i32 0, i32 0))"
|
||||
f" %{text_var} = call i32 @js_create_text_with_int(i8* {prefix_ptr}, i32 {int_val})"
|
||||
)
|
||||
else:
|
||||
# Complex pattern - create empty for now
|
||||
empty_ptr = self._emit_string_ptr("")
|
||||
self.lines.append(f" %{text_var} = call i32 @js_create_text(i8* {empty_ptr})")
|
||||
return text_var
|
||||
|
||||
# Fallback: create empty text
|
||||
@@ -1256,6 +1418,16 @@ class SignalLLVMIRGenerator:
|
||||
byte_length = len(value.encode("utf-8")) + 1 # +1 for null terminator
|
||||
return f"[{byte_length} x i8]"
|
||||
|
||||
def _emit_string_ptr(self, value: str) -> str:
|
||||
"""Emit code to get a pointer to a string constant and return the variable name."""
|
||||
str_global = self._get_or_create_string(value)
|
||||
ptr_var = self._new_var()
|
||||
self.lines.append(
|
||||
f" %{ptr_var} = getelementptr inbounds {self._string_type(value)}, "
|
||||
f"{self._string_type(value)}* {str_global}, i32 0, i32 0"
|
||||
)
|
||||
return f"%{ptr_var}"
|
||||
|
||||
def _new_var(self) -> str:
|
||||
"""Generate a new SSA variable name."""
|
||||
var_name = f"var{self.next_var_id}"
|
||||
|
||||
Reference in new issue
Block a user