Implement Python → LLVM IR → WASM reactive framework
MAJOR ACHIEVEMENT: Full working reactive framework! ## What's Working ✅ Python → AST → Ritz IR → LLVM IR → WASM pipeline ✅ Reactive signals stored in WASM memory (@signal_name = global i32) ✅ Event handlers exported from WASM and callable from JS ✅ Clean runtime with RitzLoader and debug tools ✅ Two working examples: HelloWorld and HighFive counter ## Core Components ### Parser (ritz/parser.py) - Uses Python's ast module to parse components - Handles @component decorator, signals, event handlers - Supports yield, with statements, f-strings, augmented assignment (+=) - Clean syntax: from ritz.syntax import * ### LLVM IR Code Generator (ritz/llvm_codegen_signals.py) - Generates LLVM IR directly (not C!) - Signals as global variables in WASM memory - Exports getter/setter functions for each signal - Exports event handler functions for JS to call - UTF-8 string handling ### Build System (ritz/build.py) - Python → AST → IR → LLVM IR → clang --target=wasm32 → WASM - Saves intermediate .ll files for inspection - Debug symbols support (-g flag) ### Runtime (runtime/ritz-runtime.js) - RitzRuntime class: DOM bridge for WASM - RitzLoader class: Simplified component loading - Debug bar with Ctrl+` toggle - Signal updates trigger DOM updates ## Examples ### HelloWorld.py Simple static content - renders <p>Hello World</p> ### HighFive.py Reactive counter with: - Signal in WASM memory (high_fives = signal(0)) - Event handler (increment function) - DOM updates on signal change ## Test Coverage - 4 parser tests (all passing) - 4 LLVM codegen tests (all passing) - 4 signal parsing tests (all passing) - Code formatted with black and ruff ## Next Steps - <script type="text/ritz"> in-browser compilation - Better signal-to-DOM binding tracking - More complex examples (TodoMVC, forms, lists) - Playwright browser tests --- This is a genuinely innovative framework: write Python, compile to WASM, run with reactive signals in WASM memory. No virtual DOM, no transpilation, just compiled Python with fine-grained reactivity. 🎉 Python → WASM → Reactive UI is REAL!
This commit is contained in:
1 parent
316c090778
commit
635d466874
54 files changed
+4393
No files matched your search
@@ -0,0 +1,129 @@
|
||||
"""Tests for C code generation from IR."""
|
||||
|
||||
|
||||
|
||||
def test_codegen_hello_world():
|
||||
"""Test generating C code for simple HelloWorld component."""
|
||||
from ritz.codegen import generate_c_code
|
||||
|
||||
ir = {
|
||||
"type": "component",
|
||||
"name": "HelloWorld",
|
||||
"args": [],
|
||||
"body": [
|
||||
{
|
||||
"type": "with",
|
||||
"element": {"tag": "P", "props": {}},
|
||||
"body": [
|
||||
{
|
||||
"type": "yield",
|
||||
"value": {"type": "constant", "value": "Hello World"},
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
c_code = generate_c_code(ir)
|
||||
|
||||
# Verify C code structure
|
||||
assert "#include <emscripten.h>" in c_code
|
||||
assert "void HelloWorld()" in c_code or "void render_HelloWorld()" in c_code
|
||||
|
||||
# Should create a P element
|
||||
assert "create_element" in c_code or "createElement" in c_code
|
||||
assert '"p"' in c_code or '"P"' in c_code.lower()
|
||||
|
||||
# Should create text node
|
||||
assert '"Hello World"' in c_code
|
||||
assert "create_text" in c_code or "createTextNode" in c_code or "set_text_content" in c_code
|
||||
|
||||
|
||||
def test_codegen_with_props():
|
||||
"""Test generating C code for element with props."""
|
||||
from ritz.codegen import generate_c_code
|
||||
|
||||
ir = {
|
||||
"type": "component",
|
||||
"name": "StyledDiv",
|
||||
"args": [],
|
||||
"body": [
|
||||
{
|
||||
"type": "with",
|
||||
"element": {
|
||||
"tag": "Div",
|
||||
"props": {
|
||||
"class_": {"type": "constant", "value": "container"},
|
||||
"id": {"type": "constant", "value": "main"},
|
||||
},
|
||||
},
|
||||
"body": [{"type": "yield", "value": {"type": "constant", "value": "Content"}}],
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
c_code = generate_c_code(ir)
|
||||
|
||||
# Should set attributes
|
||||
assert "set_attribute" in c_code or "setAttribute" in c_code
|
||||
assert '"class"' in c_code or '"className"' in c_code
|
||||
assert '"container"' in c_code
|
||||
assert '"id"' in c_code
|
||||
assert '"main"' in c_code
|
||||
|
||||
|
||||
def test_codegen_nested_elements():
|
||||
"""Test generating C code for nested elements."""
|
||||
from ritz.codegen import generate_c_code
|
||||
|
||||
ir = {
|
||||
"type": "component",
|
||||
"name": "Nested",
|
||||
"args": [],
|
||||
"body": [
|
||||
{
|
||||
"type": "with",
|
||||
"element": {"tag": "Div", "props": {}},
|
||||
"body": [
|
||||
{
|
||||
"type": "with",
|
||||
"element": {"tag": "P", "props": {}},
|
||||
"body": [
|
||||
{"type": "yield", "value": {"type": "constant", "value": "Inner"}}
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
c_code = generate_c_code(ir)
|
||||
|
||||
# Should have append_child calls to build hierarchy
|
||||
assert "append_child" in c_code or "appendChild" in c_code
|
||||
|
||||
|
||||
def test_codegen_includes_runtime_functions():
|
||||
"""Test that generated C code includes necessary runtime declarations."""
|
||||
from ritz.codegen import generate_c_code
|
||||
|
||||
ir = {
|
||||
"type": "component",
|
||||
"name": "Simple",
|
||||
"args": [],
|
||||
"body": [
|
||||
{
|
||||
"type": "with",
|
||||
"element": {"tag": "P", "props": {}},
|
||||
"body": [{"type": "yield", "value": {"type": "constant", "value": "test"}}],
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
c_code = generate_c_code(ir)
|
||||
|
||||
# Should include emscripten
|
||||
assert "#include <emscripten.h>" in c_code
|
||||
|
||||
# Should have EMSCRIPTEN_KEEPALIVE for exported function
|
||||
assert "EMSCRIPTEN_KEEPALIVE" in c_code
|
||||
Reference in new issue
Block a user