Initial commit - some junk

This commit is contained in:
A2IA committed 2025-11-15 08:07:09 +00:00
commit 316c090778
9 files changed
+2272

No files matched your search

+522
View File
@@ -0,0 +1,522 @@
# Ritz Framework Research & Architecture
## Core Insight: Python-Executable, WASM-Compilable
The breakthrough: Our framework uses **valid Python syntax** that can execute in a Python interpreter for debugging/prototyping, but compiles to WebAssembly for production. Developers write Python, debug in Python, ship as WASM.
## The Yield-Based Component Model
### Pure Python Implementation (for prototyping/debugging)
```python
# This is ACTUAL Python that runs in CPython!
from ritz import component, signal, Main, P, Label, Button
@component
def HighFiveApp():
high_fives = signal(0)
def increment():
high_fives.value += 1
with Main():
yield P('Give me a high 5 bro!')
yield Label(f'High fives: {high_fives}')
yield Button('Click me', onclick=increment)
# Can execute in Python for debugging:
# python app.py -> generates HTML/IR for verification
```
### Complex Example with Mixed Content
```python
@component
def BlogPost(title, author, content):
with Article(class_='blog-post'):
with Header():
yield H1(title)
yield 'By ' # Raw strings!
yield Span(author, class_='author')
yield ' on '
yield Time(content.date)
with Section():
yield from content.paragraphs # Composition via yield from
for comment in content.comments:
yield Comment(comment.author, comment.text)
```
### Why This Architecture Works
1. **Debugging**: Run components in Python interpreter to see generated HTML/IR
2. **Type Checking**: Use Python's type hints for compile-time validation
3. **Testing**: Standard Python test frameworks work
4. **Compilation**: Parser only needs to handle subset of Python, not full language
## Compilation Pipeline
### 1. Parser (Python → AST) - Using Python's `ast` Module!
**BREAKTHROUGH**: Since our components are valid Python, we can use Python's own `ast` module to parse them! No custom parser needed!
```python
import ast
import inspect
def parse_component(func):
"""Parse a component function using Python's AST"""
source = inspect.getsource(func)
tree = ast.parse(source)
# Walk the AST and transform to our IR
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef):
# Found our component function
if has_component_decorator(node):
return transform_to_ritz_ast(node)
def transform_to_ritz_ast(node):
"""Transform Python AST to Ritz IR"""
component = {
'type': 'component',
'name': node.name,
'body': []
}
for stmt in node.body:
if isinstance(stmt, ast.With):
# with Element(): ...
component['body'].append(process_with_statement(stmt))
elif isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Yield):
# yield Element()
component['body'].append(process_yield(stmt.value))
return component
```
This gives us:
- **Zero parsing overhead** - Python does it for us
- **Perfect Python compatibility** - It IS Python
- **Error messages for free** - Python's parser handles syntax errors
- **Type hints support** - Can extract from AST
### 2. LLVM IR Generation
```cpp
// codegen.cpp
class RitzCodeGen {
llvm::LLVMContext context;
llvm::IRBuilder<> builder;
llvm::Module* module;
// Compile @component function to WASM
llvm::Function* compileComponent(ASTNode* node) {
auto func = llvm::Function::Create(
llvm::FunctionType::get(builder.getVoidTy(), false),
llvm::Function::ExternalLinkage,
node->component.name,
module
);
auto entry = llvm::BasicBlock::Create(context, "entry", func);
builder.SetInsertPoint(entry);
// Process yields - each becomes DOM operations
for (auto stmt : node->component.body) {
if (stmt->type == AST_YIELD) {
compileYield(stmt);
}
}
builder.CreateRetVoid();
return func;
}
void compileYield(ASTNode* node) {
// yield P('text') compiles to:
// 1. Create P element
// 2. Create text node
// 3. Append text to P
// 4. Append P to current parent
auto create_p = builder.CreateCall(
module->getFunction("dom_create_element"),
builder.getInt32(TAG_P)
);
// ... rest of DOM operations
}
};
```
### 3. Reactive Runtime (in WASM)
```c
// runtime.c - Compiles to WASM
typedef struct {
int32_t value;
uint32_t subscribers[MAX_SUBSCRIBERS];
uint32_t subscriber_count;
uint32_t dom_node_id; // Direct DOM node reference
} Signal;
Signal* create_signal(int32_t initial) {
Signal* sig = (Signal*)wasm_alloc(sizeof(Signal));
sig->value = initial;
sig->subscriber_count = 0;
return sig;
}
void signal_set(Signal* sig, int32_t value) {
sig->value = value;
// Direct DOM update - no virtual DOM!
if (sig->dom_node_id) {
dom_ops[op_count++] = OP_UPDATE_TEXT;
dom_ops[op_count++] = sig->dom_node_id;
dom_ops[op_count++] = (uint32_t)int_to_string(value);
}
// Notify subscribers
for (int i = 0; i < sig->subscriber_count; i++) {
call_effect(sig->subscribers[i]);
}
}
```
## DOM Bridge Architecture
### WASM Side - Accumulate Operations
```c
// Opcodes for DOM operations
enum DOMOp {
OP_CREATE_ELEMENT = 0x01,
OP_CREATE_TEXT = 0x02,
OP_SET_ATTRIBUTE = 0x03,
OP_APPEND_CHILD = 0x04,
OP_UPDATE_TEXT = 0x05,
OP_ADD_EVENT = 0x06,
OP_REMOVE = 0x07,
OP_BATCH_END = 0xFF
};
// Global operation buffer
uint32_t dom_ops[MAX_OPS];
uint32_t op_count = 0;
// Batch operations then flush
void flush_dom_ops() {
// Call JS with pointer to operation buffer
js_flush_dom_ops(dom_ops, op_count);
op_count = 0;
}
```
### JavaScript Side - Execute Operations
```javascript
// bridge.js
const nodeMap = new Map();
let nodeIdCounter = 1;
function flushDOMOps(ptr, len) {
const ops = new Uint32Array(wasmMemory.buffer, ptr, len);
const stack = [];
for (let i = 0; i < len;) {
switch(ops[i++]) {
case 0x01: { // CREATE_ELEMENT
const tagId = ops[i++];
const el = document.createElement(TAG_NAMES[tagId]);
const id = nodeIdCounter++;
nodeMap.set(id, el);
stack.push(id);
break;
}
case 0x05: { // UPDATE_TEXT
const nodeId = ops[i++];
const textPtr = ops[i++];
const text = readString(textPtr);
nodeMap.get(nodeId).textContent = text;
break;
}
// ... other operations
}
}
}
```
## Implementation Roadmap
### Phase 1: Python Prototype (Week 1)
- [x] Define component syntax with yield
- [ ] Build Python runtime for testing (framework.py)
- [ ] Create HTML generation from component tree
- [ ] Signal/reactive state in pure Python
### Phase 2: Parser (Week 2)
- [ ] Python subset grammar definition
- [ ] Parser using ANTLR or hand-written recursive descent
- [ ] AST generation from Python code
- [ ] Semantic analysis (type checking, reactive dependency tracking)
### Phase 3: WASM Compiler (Week 3-4)
- [ ] LLVM setup and IR generation
- [ ] Compile components to WASM functions
- [ ] Reactive runtime in C/Rust
- [ ] DOM operation batching
### Phase 4: JavaScript Bridge (Week 5)
- [ ] DOM operation interpreter in JS
- [ ] Event handler callbacks
- [ ] String passing optimization
- [ ] Memory management
### Phase 5: Optimization (Week 6)
- [ ] Template compilation (static vs dynamic parts)
- [ ] Signal-to-DOM direct mapping
- [ ] Batch operation compression
- [ ] Bundle size optimization
## Key Technical Decisions
### Why Yield?
- Natural Python syntax for building trees
- Composable via `yield from`
- Clear execution order
- No magic - just generators
### Why Compile vs Interpret?
- 10-100x performance improvement
- 5-10x smaller bundle (no Python runtime)
- Direct DOM updates (no virtual DOM)
- Type safety at compile time
### Why LLVM?
- Mature toolchain
- Excellent WASM backend
- Optimization passes
- Debugging support
## Performance Targets
Based on research of SolidJS, Leptos, and Dioxus:
- **Bundle size**: < 200KB compressed
- **Startup time**: < 500ms on slow 3G
- **Update latency**: Within 10% of vanilla JS
- **Memory usage**: 70% less than React
- **DOM mutations**: 99% fewer than virtual DOM
## Critical Implementation Details
### Signal Subscription Model
```c
// Each signal tracks its subscribers
// Each effect tracks its dependencies
// Bidirectional relationship for cleanup
typedef struct Effect {
void (*fn)(void);
Signal** dependencies;
uint32_t dep_count;
} Effect;
// Global context during effect execution
Effect* current_effect = NULL;
// When signal.get() called, register dependency
int32_t signal_get(Signal* sig) {
if (current_effect) {
add_dependency(current_effect, sig);
}
return sig->value;
}
```
### Memory Management
- Bump allocator for per-frame allocations
- Generation-based GC for long-lived objects
- Reference counting for shared resources
- No Rc/RefCell exposed to users
### String Handling
- String table in WASM memory
- Pass indices, not pointers
- Intern common strings
- UTF-8 throughout
## Revolutionary Debugging Story
### Python Debugging in WASM!
**THE INSIGHT**: We're not debugging JavaScript - we're debugging Python code running in WASM! This means we can:
```python
# This is what you see in DevTools
@component
def Counter():
count = signal(0) # <-- Breakpoint here shows Python source!
def increment():
count += 1 # <-- Step through Python, not JS!
print(f"Count: {count}") # <-- See Python values
```
### How It Works
1. **DWARF Debug Symbols**: LLVM generates standard DWARF symbols in WASM
2. **Source Maps on Steroids**: Map WASM instructions → Python source lines
3. **Symbol Translation**:
```
WASM offset 0x1234 → Counter.py:15:8 → "count += 1"
```
4. **Browser Integration**: Chrome DevTools already supports WASM debugging!
5. **Live Editing**: Recompile just the changed component, hot-reload the WASM module
### Debugging Options
```javascript
// In browser console
wasm.debug.setBreakpoint('Counter.py', 15)
wasm.debug.stepInto()
wasm.debug.inspect('count') // Shows Signal { value: 5, subscribers: [...] }
// Or even connect Python debugger!
# pdb remote debugging over WebSocket
import pdb; pdb.set_trace() # This could actually work!
```
## Modular Loading Architecture
### Progressive WASM Bundles
Instead of one monolithic WASM file, split into modules:
```
bootstrap.wasm (50KB)
├── Minimal runtime
├── Login component
└── Module loader
app.wasm (200KB) - Loaded after auth
├── Main application
├── Core components
└── Router
lazy/
├── admin.wasm (150KB) - Admin panel
├── analytics.wasm (100KB) - Analytics dashboard
├── reports.wasm (120KB) - Report generation
└── [feature].wasm - Feature-specific modules
```
### Security Through Architecture
```python
# bootstrap.py - Compiles to bootstrap.wasm
@component
def LoginPage():
with Main():
yield LoginForm()
# No app code here - not even downloaded!
# After successful auth
async def load_app():
# Server only sends this to authenticated users
app_module = await fetch('/wasm/app.wasm', {
'headers': {'Authorization': f'Bearer {token}'}
})
wasm.load_module(app_module)
```
### Implementation
```javascript
// module-loader.js
class RitzModuleLoader {
constructor() {
this.modules = new Map();
this.cache = new Cache('ritz-modules-v1');
}
async loadModule(name, url) {
// Check cache first
const cached = await this.cache.match(url);
if (cached) {
const wasm = await WebAssembly.instantiate(cached);
this.modules.set(name, wasm.instance);
return;
}
// Download and cache
const response = await fetch(url);
const bytes = await response.arrayBuffer();
// Cache for next time
await this.cache.put(url, new Response(bytes));
// Instantiate
const wasm = await WebAssembly.instantiate(bytes, {
env: this.getImports(name)
});
this.modules.set(name, wasm.instance);
}
}
```
### Route-Based Code Splitting
```python
# router.py
@component
def Router():
current_route = signal(window.location.pathname)
with Main():
if current_route == '/admin':
# Lazy load admin module
yield LazyLoad('admin.wasm', AdminPanel)
elif current_route == '/reports':
# Only load if needed
yield LazyLoad('reports.wasm', ReportsView)
else:
# Main app is already loaded
yield Dashboard()
```
## Next Steps (Updated)
1. ✅ **Prototype**: Build framework.py with working signals and components
2. **AST Parser**: Use Python's `ast` module to parse components
3. **Compiler Pipeline**:
- Python AST → Ritz IR
- Ritz IR → LLVM IR
- LLVM IR → WASM with debug symbols
4. **Debugging Infrastructure**:
- Generate source maps
- Embed Python source in WASM
- Browser DevTools integration
5. **Module System**:
- Build tool for splitting modules
- Runtime module loader
- Caching strategy
6. **Test**: Port TodoMVC with lazy-loaded features
## References
- SolidJS: Fine-grained reactivity without virtual DOM
- Leptos: Rust signals with Copy + 'static
- Dioxus Sledgehammer: Batched DOM operations
- LLVM WebAssembly Backend: https://llvm.org/docs/WebAssemblyUsage.html
- Binaryen: WASM optimizer and code generator
## Core Innovation
**Python syntax → WASM performance**. Write and debug in Python, ship as WASM. No virtual DOM, direct signal-to-DOM updates, 99% fewer mutations than React. The future of web frameworks: compiled, reactive, Python-like.