rip-lang 2.5.1 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/INTERNALS.md CHANGED
@@ -76,7 +76,7 @@ class BinaryOp {
76
76
  ["+", left, right] // That's it!
77
77
  ```
78
78
 
79
- **Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~14,000 LOC—**smaller, yet includes a full reactive framework, template engine, and component system.**
79
+ **Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~11,000 LOC—**smaller, yet includes a full reactive runtime.**
80
80
 
81
81
  ## S-Expression Structure
82
82
 
@@ -40,7 +40,7 @@ class BinaryOp {
40
40
  ["+", left, right] // That's it!
41
41
  ```
42
42
 
43
- **Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~14,000 LOC—smaller, yet includes a complete reactive framework with signals, templates, and components.
43
+ **Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~11,000 LOC—smaller, yet includes a complete reactive runtime with state, computed values, and effects.
44
44
 
45
45
  ## The Fundamental Rule
46
46
 
@@ -421,8 +421,8 @@ counter = ->
421
421
  |-----------|------------------|-----|------------|
422
422
  | **Lexer+Rewriter** | 3,558 LOC | 3,537 LOC | Expanded syntax |
423
423
  | **Parser Generator** | 2,285 LOC (Jison) | ~1,000 LOC (Solar) | Built-in, ~250× faster! |
424
- | **Compiler** | 10,346 LOC (AST Nodes) | 7,965 LOC (S-expressions) | +Reactive framework! |
425
- | **Total** | **17,760 LOC** | **~14,000 LOC** | **Smaller + full framework** |
424
+ | **Compiler** | 10,346 LOC (AST Nodes) | 5,500 LOC (S-expressions) | +Reactive runtime! |
425
+ | **Total** | **17,760 LOC** | **~11,000 LOC** | **Smaller + reactive runtime** |
426
426
 
427
427
  ## Feature Comparison Table
428
428
 
@@ -470,24 +470,20 @@ counter = ->
470
470
 
471
471
  > **v2.5.1 - Production-Ready with Fine-Grained Reactivity**
472
472
 
473
- ## Summary Matrix
473
+ ## Summary
474
474
 
475
475
  | Layer | Syntax | Runtime | Features | DX | Score |
476
476
  |-------|--------|---------|----------|-----|-------|
477
477
  | **Reactivity** | A+ | A+ | A+ | A+ | **A+** |
478
- | **Templates** | A+ | A | A | A+ | **A** |
479
- | **Components** | A | A | A- | A | **A-** |
480
-
481
- *Components A- due to missing SSR/Hydration. Context API and Error Primitives are implemented.*
482
478
 
483
479
  ## Reactivity ⭐⭐⭐⭐⭐ (Production-Ready)
484
480
 
485
481
  **This is genuinely excellent.**
486
482
 
487
483
  ```coffee
488
- count := 0 # Signal (state)
489
- doubled ~= count * 2 # Derived (auto-tracks)
490
- effect -> console.log count # Effect (auto-runs)
484
+ count := 0 # State
485
+ doubled ~= count * 2 # Computed (auto-tracks)
486
+ log ~> console.log count # Effect (auto-runs)
491
487
  ```
492
488
 
493
489
  | Aspect | Rating | Notes |
@@ -499,83 +495,32 @@ effect -> console.log count # Effect (auto-runs)
499
495
 
500
496
  **Competitive with:** SolidJS signals, Vue 3 refs, Preact signals
501
497
 
502
- ## Templates ⭐⭐⭐⭐⭐ (Great DX, Fast Runtime)
503
-
504
- **Innovative syntax with fine-grained performance.**
505
-
506
- ```coffee
507
- render
508
- div#main.card ...props
509
- h1.title "Hello, #{name}!"
510
- input value <=> username, @keydown.enter: submit
511
- button.('btn', isActive && 'active') @click.prevent: handleClick, "Submit"
512
- ```
513
-
514
- | Aspect | Rating | Notes |
515
- |--------|--------|-------|
516
- | Syntax | A+ | Indentation-based, clean, intuitive |
517
- | Features | A | Classes, IDs, events, modifiers, spread, two-way binding |
518
- | Runtime | A | Fine-grained: only dynamic parts get effects |
519
- | Innovation | A | Dynamic classes `div.('a', x && 'b')`, `<=>` binding |
498
+ ## Framework-Agnostic Design
520
499
 
521
- ## Components ⭐⭐⭐⭐⭐ (Fine-Grained, Production-Ready)
522
-
523
- **Clean syntax with Svelte-class performance.**
524
-
525
- | Aspect | Rating | Notes |
526
- |--------|--------|-------|
527
- | Syntax | A | Clean, obvious structure |
528
- | Props | A | Required, optional, defaults, rest props |
529
- | **Performance** | **A+** | **Fine-grained O(1) updates!** |
530
-
531
- ### Performance Comparison
532
-
533
- | Operation | Old Approach | New Approach |
534
- |-----------|--------------|--------------|
535
- | Update 1 text | O(n) rebuild | **O(1)** single node |
536
- | Update 1 attr | O(n) rebuild | **O(1)** single attr |
537
- | Add list item | O(n) rebuild | **O(1)** create 1 node |
538
- | Remove list item | O(n) rebuild | **O(1)** remove 1 node |
539
-
540
- **Result:** 30-40x faster for typical reactive updates!
500
+ Rip's reactivity system is **framework-agnostic** — use it with React, Vue, Svelte, or vanilla JavaScript. The reactive primitives (state, computed, effects) work independently of any UI layer.
541
501
 
542
502
  ## Competitive Analysis
543
503
 
544
- | Framework | Reactivity | Templates | Components | Performance | DX | Overall |
545
- |-----------|------------|-----------|------------|-------------|-----|---------|
546
- | **Rip** | A+ | A | A- | A | A+ | **A-** |
547
- | SolidJS | A+ | A | A | A+ | A | A |
548
- | Svelte 5 | A | A+ | A | A+ | A+ | A |
549
- | Vue 3 | A- | A | A | B+ | A | A- |
550
- | React | B | B+ | A | B | A- | B+ |
504
+ | Framework | Reactivity | DX | Performance | Overall |
505
+ |-----------|------------|-----|-------------|---------|
506
+ | **Rip** | A+ | A+ | A | **A** |
507
+ | SolidJS | A+ | A | A+ | A |
508
+ | Vue 3 | A- | A | B+ | A- |
509
+ | React | B | A- | B | B+ |
551
510
 
552
511
  **Rip's Position:**
553
512
 
554
513
  | Strength | Why |
555
514
  |----------|-----|
556
- | **Reactivity A+** | Signals, computed, effects, batching, Context API |
515
+ | **Reactivity A+** | State, computed, effects, batching |
557
516
  | **DX A+** | Cleanest syntax of all, no boilerplate |
558
- | **Performance A** | O(1) fine-grained updates, keyed reconciliation |
559
-
560
- | Gap | Path to A |
561
- |-----|-----------|
562
- | **Components A-** (not A) | Missing SSR/Hydration |
563
- | **Overall A-** (not A) | No ecosystem (router, state lib, devtools) |
564
-
565
- ## Completed Features ✅
566
-
567
- - [x] Reactivity primitives (signals, computed, effects)
568
- - [x] Template syntax and features
569
- - [x] Props system (`@prop`, `@prop?`, `@prop = default`, `@...rest`)
570
- - [x] Component composition
571
- - [x] Children/slots
572
- - [x] Lifecycle hooks
573
- - [x] Fine-grained DOM updates
574
- - [x] Fine-grained conditionals (if/else) with effect cleanup
575
- - [x] Fine-grained loops (for) with keyed reconciliation
576
- - [x] **Context API** (`setContext`, `getContext`, `hasContext`)
577
- - [x] **Error primitives** (`__catchErrors`, `__handleError`)
517
+ | **Framework-agnostic** | Use with any UI framework |
518
+
519
+ ## Completed Features
520
+
521
+ - [x] Reactivity primitives (state, computed, effects)
578
522
  - [x] **Batching** (`__batch()` for grouped updates)
523
+ - [x] **Error primitives** (`__catchErrors`, `__handleError`)
579
524
 
580
525
  ## Best Current Uses
581
526
 
@@ -0,0 +1,288 @@
1
+ # Rip Reactivity System
2
+
3
+ Rip implements a **fine-grained reactive system** that rivals and often exceeds the capabilities of major frameworks like Vue, Svelte, Solid, and React — in just ~200 lines of runtime code.
4
+
5
+ ## The Reactive Triad
6
+
7
+ Rip's entire reactivity model is built on three foundational concepts:
8
+
9
+ | Primitive | Description | Read As | Role | Purpose |
10
+ |-----------|-------------|---------|------|---------|
11
+ | `:=` | state | "has state" | **Source** | Where reactive data originates |
12
+ | `~=` | computed | "always equals" | **Derivation** | Computed values (lazy, cached) |
13
+ | `~>` | effect | "reacts to" | **Reaction** | Side effects when data changes |
14
+
15
+ These three primitives are the **minimal complete set** for reactive programming — everything React, Vue, Svelte, and Solid can do with state management, Rip can do too.
16
+
17
+ ---
18
+
19
+ ## Why These Three Are Complete
20
+
21
+ Every reactive system reduces to these three concepts:
22
+
23
+ | Framework | Source | Derivation | Reaction |
24
+ |-----------|--------|------------|----------|
25
+ | **Rip** | `:=` | `~=` | `~>` |
26
+ | Angular | `signal()` | `computed()` | `effect()` |
27
+ | Imba | `@property` | implicit | implicit |
28
+ | MobX | `observable` | `computed` | `autorun` |
29
+ | Next.js | `useState` | `useMemo` | `useEffect` |
30
+ | React | `useState` | `useMemo` | `useEffect` |
31
+ | Solid | `createSignal` | `createMemo` | `createEffect` |
32
+ | Svelte 4 | `let x` | `$: x` | `$: {}` |
33
+ | Svelte 5 | `$state` | `$derived` | `$effect` |
34
+ | Vue | `ref()` | `computed()` | `watch()` |
35
+
36
+ ### What You Can Build From These Three
37
+
38
+ - **Stores** → objects with state properties
39
+ - **Two-way binding** → state + effect that syncs
40
+ - **Async resources** → state + effect that fetches
41
+ - **Event handling** → update state → triggers reactions
42
+ - **Derived stores** → computed from other state
43
+
44
+ ### What's Missing Without Any One
45
+
46
+ - **Without `:=`** → No source of truth
47
+ - **Without `~=`** → Manual tracking or inefficient effects
48
+ - **Without `~>`** → Can't react to changes (no side effects)
49
+
50
+ ---
51
+
52
+ ## Quick Example
53
+
54
+ ```rip
55
+ count := 0 # count has state 0
56
+ doubled ~= count * 2 # doubled always equals count * 2
57
+ logger ~> console.log count # reacts to count changes
58
+ ~> console.log count # same thing, but the "fire and forget" version
59
+
60
+ increment: -> count += 1
61
+
62
+ increment() # Logs: 1
63
+ increment() # Logs: 2
64
+ ```
65
+
66
+ ---
67
+
68
+ ## How It Works
69
+
70
+ ### State (`:=`) — "has state"
71
+
72
+ State creates a **reactive container** that tracks its readers and notifies them on change.
73
+
74
+ ```rip
75
+ count := 0 # count has state 0
76
+ count += 1 # Update triggers dependents
77
+ ```
78
+
79
+ **Compiles to:**
80
+ ```javascript
81
+ const count = __state(0);
82
+ count.value += 1;
83
+ ```
84
+
85
+ **What happens internally:**
86
+ 1. Reading `count.value` inside an effect/computed **tracks** the dependency
87
+ 2. Writing to `count.value` **notifies** all subscribers
88
+ 3. Effects re-run, computeds mark dirty
89
+
90
+ ### Computed (`~=`) — "always equals"
91
+
92
+ Computed creates a **computed value** that automatically updates when dependencies change.
93
+
94
+ ```rip
95
+ count := 0
96
+ doubled ~= count * 2 # doubled always equals count * 2
97
+ ```
98
+
99
+ **Key features:**
100
+ - **Lazy** — only computes when read
101
+ - **Cached** — won't recompute unless dependencies change
102
+ - **Chainable** — computeds can depend on other computeds
103
+
104
+ ### Effect (`~>`) — "reacts to"
105
+
106
+ The effect operator runs **side effects** when dependencies change. Dependencies are auto-tracked from reactive values read in the body.
107
+
108
+ ```rip
109
+ ~> document.title = "Count: #{count}"
110
+ ```
111
+
112
+ **Key features:**
113
+ - **Auto-tracking** — dependencies detected automatically from body
114
+ - **Immediate** — runs once immediately to establish dependencies
115
+ - **Controllable** — optionally assign to a variable to control the effect
116
+
117
+ **Syntax:**
118
+ ```rip
119
+ # Fire and forget (no assignment)
120
+ ~> console.log count
121
+
122
+ # Controllable (assign to variable)
123
+ logger ~> console.log count
124
+ logger.stop! # Pause reactions
125
+ logger.run! # Resume reactions
126
+ logger.cancel! # Permanent disposal
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Comparison with Major Frameworks
132
+
133
+ ### State
134
+
135
+ | Feature | Rip | Vue | Solid | React |
136
+ |---------|:---:|:---:|:-----:|:-----:|
137
+ | Auto-tracking on read | ✅ | ✅ | ✅ | ❌ |
138
+ | Same-value skip | ✅ | ✅ | ✅ | ✅ |
139
+ | Re-entry protection | ✅ | ❌ | ❌ | ❌ |
140
+ | Lock for SSR | ✅ | ❌ | ❌ | ❌ |
141
+ | Cleanup/disposal | ✅ | ✅ | ✅ | ❌ |
142
+ | Raw read (untracked) | ✅ | ✅ | ✅ | ❌ |
143
+ | Primitive coercion | ✅ | ❌ | ❌ | N/A |
144
+
145
+ **Rip advantage:** `.lock()`, `.kill()`, `.read()` utilities that others lack.
146
+
147
+ ### Computed
148
+
149
+ | Feature | Rip | Vue | Solid | MobX |
150
+ |---------|:---:|:---:|:-----:|:----:|
151
+ | Lazy evaluation | ✅ | ✅ | ✅ | ✅ |
152
+ | Cached until deps change | ✅ | ✅ | ✅ | ✅ |
153
+ | Dirty propagation | ✅ | ✅ | ✅ | ✅ |
154
+ | Auto dependency cleanup | ✅ | ✅ | ✅ | ✅ |
155
+ | Chainable | ✅ | ✅ | ✅ | ✅ |
156
+ | Read without tracking | ✅ | ❌ | ❌ | ❌ |
157
+ | Lock (freeze value) | ✅ | ❌ | ❌ | ❌ |
158
+
159
+ **Rip advantage:** `.read()` for untracked access, `.lock()` to freeze.
160
+
161
+ ### Effect (`~>`)
162
+
163
+ | Feature | Rip | Vue | Svelte | React |
164
+ |---------|:---:|:---:|:------:|:-----:|
165
+ | Auto-tracking | ✅ | ✅ | ✅ | ❌ |
166
+ | No manual deps array | ✅ | ✅ | ✅ | ❌ |
167
+ | Runs immediately | ✅ | ✅ | ✅ | ✅ |
168
+ | Controllable (stop/run) | ✅ | ✅ | ✅ | ❌ |
169
+ | Re-runs on change | ✅ | ✅ | ✅ | ✅ |
170
+
171
+ **Rip advantage over React:** No manual dependency arrays!
172
+
173
+ ```javascript
174
+ // React - manual, error-prone
175
+ useEffect(() => {
176
+ document.title = `Count: ${count}`;
177
+ }, [count]); // 😩 Must list deps manually
178
+
179
+ // Rip - automatic
180
+ ~> document.title = "Count: #{count}" // 🎉 Deps tracked automatically
181
+ ```
182
+
183
+ ### Bundle Size
184
+
185
+ | Framework | Runtime Size |
186
+ |-----------|-------------|
187
+ | React | ~40 KB (minified) |
188
+ | Vue | ~34 KB |
189
+ | Svelte | ~2 KB (but grows with app size) |
190
+ | **Rip** | **~4 KB** (full runtime) |
191
+
192
+ ---
193
+
194
+ ## Advanced Features
195
+
196
+ ### Batching
197
+
198
+ Group multiple updates into a single flush:
199
+
200
+ ```javascript
201
+ __batch(() => {
202
+ count.value = 1;
203
+ name.value = "Alice";
204
+ // Effects run once at the end, not twice
205
+ });
206
+ ```
207
+
208
+ ### Untracked Reads
209
+
210
+ Read a value without creating a dependency:
211
+
212
+ ```javascript
213
+ const currentValue = count.read(); // No tracking
214
+ ```
215
+
216
+ ### Locking (SSR/Hydration)
217
+
218
+ Prevent writes during server-side rendering or freeze values:
219
+
220
+ ```javascript
221
+ count.lock(); // Now immutable
222
+ ```
223
+
224
+ ### Cleanup
225
+
226
+ Dispose of reactive values:
227
+
228
+ ```javascript
229
+ const finalValue = count.kill(); // Returns value, clears subscribers
230
+ ```
231
+
232
+ ---
233
+
234
+ ## The Architecture
235
+
236
+ ```
237
+ ┌─────────────┐ reads ┌─────────────┐
238
+ │ STATE │◄──────────────│ EFFECT │
239
+ │ count │ │ (side fx) │
240
+ └──────┬──────┘ └─────────────┘
241
+ │ ▲
242
+ │ notifies │ triggers
243
+ ▼ │
244
+ ┌─────────────┐ reads ┌──────┴──────┐
245
+ │ COMPUTED │◄──────────────│ EFFECT │
246
+ │ doubled │ │ (logger) │
247
+ └─────────────┘ └─────────────┘
248
+ ```
249
+
250
+ 1. **State** is the source of truth
251
+ 2. **Computed** derives from state (lazy, cached)
252
+ 3. **Effects** react to state/computed changes
253
+ 4. **Changes propagate** through the dependency graph
254
+
255
+ ---
256
+
257
+ ## Why Fine-Grained Reactivity?
258
+
259
+ Rip uses **fine-grained reactivity** (like Vue/Solid), not Virtual DOM diffing (like React).
260
+
261
+ | Approach | How it works | Pros | Cons |
262
+ |----------|--------------|------|------|
263
+ | **VDOM** (React) | Re-render tree, diff, patch | Simple mental model | Overhead, requires optimization |
264
+ | **Fine-grained** (Rip) | Track dependencies, update directly | Surgical updates, fast | More complex internally |
265
+
266
+ ### Result
267
+
268
+ - **No VDOM overhead** — changes propagate directly to subscribers
269
+ - **Surgical updates** — only the exact things that changed update
270
+ - **No `useMemo`/`useCallback` dance** — caching is automatic
271
+ - **Smaller bundles** — no diffing algorithm needed
272
+
273
+ ---
274
+
275
+ ## Summary
276
+
277
+ Rip's reactivity system:
278
+
279
+ ✅ **The Reactive Triad** — state (`:=`), computed (`~=`), effect (`~>`) <br>
280
+ ✅ **Natural reading** — "has state", "always equals", "reacts to" <br>
281
+ ✅ **Minimal complete set** — same model as Vue, Solid, MobX <br>
282
+ ✅ **Lazy computed** — only calculates when needed <br>
283
+ ✅ **Fine-grained** — surgical updates to subscribers <br>
284
+ ✅ **Controllable effects** — `.stop!`, `.run!`, `.cancel!` when needed <br>
285
+ ✅ **Tiny runtime** — ~200 lines, ~4 KB <br>
286
+ ✅ **Extra utilities** — `.lock()`, `.read()`, `.kill()` that others lack <br>
287
+
288
+ **On par with Vue/Solid. Better than React. A fraction of the size.**