rip-lang 2.5.1 → 2.7.2

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/CHANGELOG.md CHANGED
@@ -7,6 +7,53 @@ All notable changes to Rip will be documented in this file.
7
7
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
8
8
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
9
 
10
+ ## [2.7.2] - 2026-02-03
11
+
12
+ ### Clean ES Module REPL
13
+
14
+ **Proper `vm.SourceTextModule` Implementation**: The REPL now uses Node's standard ES module API instead of temp files:
15
+
16
+ ```coffee
17
+ rip> { Cash } = await import("./utils.rip")
18
+ rip> config = Cash((await import("./config.rip")).default)
19
+ rip> config.app.name
20
+ → 'Rip Labs API'
21
+ ```
22
+
23
+ **Key improvements:**
24
+ - Uses `vm.SourceTextModule` for in-memory module evaluation (no temp files)
25
+ - `.rip` files compiled on-the-fly via module linker
26
+ - Cross-runtime compatible (Node.js, Bun, potentially Deno)
27
+ - Dynamic `await import()` transformed to static imports automatically
28
+ - Clean variable persistence through VM context
29
+
30
+ This is the standard way to handle ES modules in sandboxed contexts - no hacks required.
31
+
32
+ ---
33
+
34
+ ## [2.7.1] - 2026-02-03
35
+
36
+ ### Bun-Native REPL with Dynamic Import Support
37
+
38
+ **Full `import()` Support in REPL**: The REPL now uses Bun's native evaluation instead of Node's `vm` module, enabling dynamic imports:
39
+
40
+ ```coffee
41
+ rip> { Cash } = await import("./utils.js")
42
+ rip> config = Cash((await import("./config.js")).default)
43
+ rip> config.app.name
44
+ → 'My App'
45
+ ```
46
+
47
+ **Key changes:**
48
+ - Replaced `vm.runInContext` with file-based async evaluation
49
+ - Variables persist across REPL lines via `globalThis.__ripRepl`
50
+ - Temp files cleaned up automatically on exit
51
+ - Reactive runtime injected into global scope for cross-line sharing
52
+
53
+ This enables using the REPL for real development workflows with module imports.
54
+
55
+ ---
56
+
10
57
  ## [2.5.1] - 2026-01-16
11
58
 
12
59
  ### Template Enhancement
@@ -176,7 +223,7 @@ Counter.new().mount("#app") # Selector string support
176
223
 
177
224
  | Category | Score | Notes |
178
225
  |----------|-------|-------|
179
- | **Reactivity** | A+ | Fine-grained, signals, effects |
226
+ | **Reactivity** | A+ | Fine-grained, state, effects |
180
227
  | **Templates** | A | S-expressions, Pug shorthand |
181
228
  | **Components** | A | Props, lifecycle, context API |
182
229
  | **Performance** | A | 250× faster parser gen, 51KB bundle |
@@ -187,7 +234,7 @@ Counter.new().mount("#app") # Selector string support
187
234
 
188
235
  **What's in 51KB?**
189
236
  - Complete compiler (lexer, parser, code generator)
190
- - Reactive runtime (signals, derived values, effects)
237
+ - Reactive runtime (state, computed values, effects)
191
238
  - Template engine (S-expression syntax, dynamic classes)
192
239
  - Component system (props, lifecycle, fine-grained updates)
193
240
  - Zero dependencies
@@ -292,10 +339,10 @@ Each item gets its own effect. Changing `selected` updates ONLY the affected cla
292
339
  ### Added - Reactive UI Framework
293
340
 
294
341
  **Phase 1: Reactivity** (previously released)
295
- - Signal-based reactivity: `count = 0` creates reactive state
296
- - Derived values: `doubled ~= count * 2` auto-tracks dependencies
297
- - Effects: `effect -> console.log count` runs on changes
298
- - Runtime: `__signal()`, `__computed()`, `__effect()`, `__batch()`, `__readonly()`
342
+ - State-based reactivity: `count := 0` creates reactive state
343
+ - Computed values: `doubled ~= count * 2` auto-tracks dependencies
344
+ - Effects: `~> console.log count` runs on changes
345
+ - Runtime: `__state()`, `__computed()`, `__effect()`, `__batch()`, `__readonly()`
299
346
 
300
347
  **Phase 2: Templates**
301
348
  - Indentation-based template syntax in `render` blocks
@@ -317,8 +364,8 @@ Each item gets its own effect. Changing `selected` updates ONLY the affected cla
317
364
  - Optional: `@label?`
318
365
  - Default: `@label = "default"`
319
366
  - Rest: `@...rest`
320
- - Reactive state within components (auto-signals)
321
- - Derived values within components (auto-computed)
367
+ - Reactive state within components (auto-state)
368
+ - Computed values within components (auto-computed)
322
369
  - Component composition: `Button label: "Click"` inside render
323
370
  - Children/slots: `@children` prop for nested content
324
371
  - Lifecycle hooks: `mounted:`, `unmounted:`
@@ -332,8 +379,8 @@ Each item gets its own effect. Changing `selected` updates ONLY the affected cla
332
379
  ### Technical Details
333
380
  - Components compile to ES6 classes with constructor, render, mount, unmount
334
381
  - Props validated at construction (required props throw if missing)
335
- - State variables become `__signal()` calls
336
- - Derived values become `__computed()` calls
382
+ - State variables become `__state()` calls
383
+ - Computed values become `__computed()` calls
337
384
  - `mount()` wraps render in `__effect()` for reactive updates
338
385
  - PascalCase names in templates trigger component instantiation
339
386
 
package/README.md CHANGED
@@ -5,39 +5,37 @@
5
5
  <h1 align="center">Rip</h1>
6
6
 
7
7
  <p align="center">
8
- <strong>Elegant reactive code that compiles to modern JavaScript</strong>
8
+ <strong>A modern language that compiles to JavaScript</strong>
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-2.5.1-blue.svg" alt="Version"></a>
12
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-2.7.2-blue.svg" alt="Version"></a>
13
13
  <a href="#zero-dependencies"><img src="https://img.shields.io/badge/dependencies-ZERO-brightgreen.svg" alt="Dependencies"></a>
14
- <a href="#"><img src="https://img.shields.io/badge/tests-1046%2F1046-brightgreen.svg" alt="Tests"></a>
14
+ <a href="#"><img src="https://img.shields.io/badge/tests-979%2F979-brightgreen.svg" alt="Tests"></a>
15
15
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
16
16
  </p>
17
17
 
18
18
  ---
19
19
 
20
- ## What is Rip?
20
+ Rip is a modern language inspired by CoffeeScript. It compiles to **ES2022** (classes, `?.`, `??`, modules), adds about a **dozen new operators**, includes **built-in reactivity**, and sports a self-hosting compiler with **zero dependencies** — all in about 10,000 lines of code.
21
21
 
22
- Rip is a modern reactive language that compiles to JavaScript. It takes the elegant, readable syntax that made CoffeeScript beloved and brings it into the modern era — with ES2022 output, built-in reactivity, and a clean component system for building UIs.
23
-
24
- **The language IS the framework.** Unlike React, Vue, or Svelte where reactivity comes from libraries or compiler magic, Rip's reactive features are **language-level operators**:
22
+ > **No imports. No hooks. No dependency arrays. Just write code.**
25
23
 
26
24
  ```coffee
27
- count := 0 # Signal (reactive state)
28
- doubled ~= count * 2 # Derived (auto-updates)
29
- effect -> log doubled # Effect (side effects)
30
- ```
25
+ data = fetchUsers! # Dammit operator (call + await)
26
+ user = User.new name: "Alice" # Ruby-style constructor
27
+ squares = (x * x for x in [1..10]) # List comprehension
31
28
 
32
- No imports. No hooks. No dependency arrays. Just write code.
29
+ str =~ /Hello, (\w+)/ # Regex match
30
+ log "Found: #{_[1]}" # Captures in _[1], _[2], etc.
31
+ ```
33
32
 
34
- The compiler is completely standalone with **zero dependencies**, and it's self-hosting: Rip compiles itself. At ~14,000 lines of code, it's smaller than CoffeeScript while including a complete reactive framework.
33
+ ---
35
34
 
36
35
  **What makes Rip different:**
37
- - **Reactive primitives** — `:=` signals, `~=` derived values, `effect` blocks as syntax
38
- - **Components as syntax** — `component Counter` with props, lifecycle, fine-grained DOM updates
39
- - **Templates** — Pug-style HTML in `render` blocks, two-way binding with `<=>`
40
36
  - **Modern output** — ES2022 with native classes, `?.`, `??`, modules
37
+ - **New operators** — `!`, `!?`, `//`, `%%`, `=~`, `.new()`, and more
38
+ - **Reactive operators** — `:=`, `~=`, `~>` as language syntax
41
39
  - **Zero dependencies** — everything included, even the parser generator
42
40
  - **Self-hosting** — `bun run parser` rebuilds the compiler from source
43
41
 
@@ -45,263 +43,150 @@ The compiler is completely standalone with **zero dependencies**, and it's self-
45
43
 
46
44
  ## Installation
47
45
 
48
- **Option 1: Install from npm**
49
46
  ```bash
50
- curl -fsSL https://bun.sh/install | bash # Install Bun (if needed)
51
- bun add -g rip-lang
47
+ bun add -g rip-lang # Install globally
52
48
  ```
53
49
 
54
- **Option 2: Clone from source**
55
50
  ```bash
56
- git clone https://github.com/shreeve/rip-lang.git
57
- cd rip-lang && bun link
58
- ```
59
-
60
- **Then use it:**
61
- ```bash
62
- rip # Interactive REPL
63
- rip file.rip # Run a file
64
- rip -c file.rip # Compile to JavaScript
65
- bun file.rip # Direct execution with Bun loader
51
+ rip # Interactive REPL
52
+ rip file.rip # Run a file
53
+ rip -c file.rip # Compile to JavaScript
66
54
  ```
67
55
 
68
56
  ---
69
57
 
70
- ## Language Features
58
+ ## Language
71
59
 
72
60
  ### Functions & Classes
61
+
73
62
  ```coffee
74
- # Three function styles
75
- def greet(name) # Named function (hoisted)
63
+ def greet(name) # Named function
76
64
  "Hello, #{name}!"
77
65
 
78
- add = (a, b) -> a + b # Arrow function
79
- handler = (e) => @process e # Fat arrow (preserves this)
80
-
81
- # Classes with clean syntax
82
- class Animal
83
- constructor: (@name) ->
84
- speak: -> console.log "#{@name} makes a sound"
66
+ add = (a, b) -> a + b # Arrow function
67
+ handler = (e) => @process e # Fat arrow (preserves this)
85
68
 
86
69
  class Dog extends Animal
87
- speak: -> console.log "#{@name} barks"
70
+ speak: -> log "#{@name} barks"
88
71
 
89
- # Ruby-style constructors (both styles work)
90
- dog = Dog.new("Buddy") # → new Dog("Buddy")
91
- dog = new Dog("Buddy") # Traditional JS style
72
+ dog = Dog.new("Buddy") # Ruby-style constructor
92
73
  ```
93
74
 
94
75
  ### Destructuring & Comprehensions
76
+
95
77
  ```coffee
96
- # Destructuring
97
78
  {name, age} = person
98
79
  [first, ...rest] = items
99
80
 
100
- # Comprehensions (context-aware!)
101
- squares = (x * x for x in [1..10]) # Returns array
102
- console.log x for x in items # Just loops (no array created)
81
+ squares = (x * x for x in [1..10]) # Array comprehension
82
+ console.log x for x in items # Loop (no array)
103
83
  ```
104
84
 
105
- ### Async & Optional Chaining
85
+ ### Async & Chaining
86
+
106
87
  ```coffee
107
- # Auto-async detection
108
88
  def loadUser(id)
109
- response = await fetch "/api/users/#{id}"
89
+ response = await fetch "/api/#{id}"
110
90
  await response.json()
111
91
 
112
- # Optional chaining (both styles work)
113
- user?.profile?.name # ES2020 native
114
- arr?[0] # CoffeeScript soak
115
- fn?(arg) # Safe call
92
+ user?.profile?.name # Optional chaining
93
+ data = fetchData! # Await shorthand
116
94
  ```
117
95
 
118
- ---
119
-
120
- ## Unique Features
121
-
122
- ### New Operators & Syntax
123
-
124
- | Feature | Example | What it does |
125
- |---------|---------|--------------|
126
- | **Ruby `.new()`** | `Counter.new()` | Ruby-style constructor → `new Counter()` |
127
- | **Dammit `!`** | `fetchData!` | Calls the function AND awaits it |
128
- | **Void `!`** | `def process!` | Suppresses implicit return (always returns undefined) |
129
- | **Otherwise `!?`** | `val !? 5` | Defaults only if `undefined` (null/0/false are kept!) |
130
- | **Floor div `//`** | `7 // 2` | Floor division → `Math.floor(7 / 2)` = 3 |
131
- | **True mod `%%`** | `-1 %% 3` | True modulo (not remainder) → 2, not -1 |
132
- | **Equal, dammit! `=!`** | `MAX =! 100` | Forces `const` declaration (can't reassign) |
133
- | **Ternary `?:`** | `x > 0 ? 'yes' : 'no'` | JS-style ternary (plus CoffeeScript's if/then/else) |
134
- | **Dual optional** | `a?.b` and `a?[0]` | Both ES6 native and CoffeeScript soak styles |
135
-
136
- ### Regex Enhancements
137
-
138
- | Feature | Example | What it does |
139
- |---------|---------|--------------|
140
- | **Match `=~`** | `str =~ /(\w+)/` | Ruby-style regex, captures in `_[1]` |
141
- | **Regex index** | `str[/pat/, 1]` | Extract capture group directly |
142
- | **Heregex** | `///pat # comment///` | Extended regex with comments and whitespace |
143
-
144
- ### Strings & Data
145
-
146
- | Feature | Example | What it does |
147
- |---------|---------|--------------|
148
- | **Heredoc** | `'''` closing column | Smart indentation — closing position sets left margin |
149
- | **`__DATA__`** | `__DATA__\nconfig...` | Ruby-style inline data section, accessible as `DATA` |
150
-
151
- ### Reactivity (Built-in)
152
-
153
- | Feature | Example | What it does |
154
- |---------|---------|--------------|
155
- | **Signal `:=`** | `count := 0` | Creates reactive state container |
156
- | **Derived `~=`** | `doubled ~= count * 2` | Auto-updates when dependencies change |
157
- | **Effect** | `effect -> log x` | Runs whenever referenced signals change |
158
-
159
- ### Components & Templates
160
-
161
- | Feature | Example | What it does |
162
- |---------|---------|--------------|
163
- | **Component** | `component Counter` | Define reactive UI component |
164
- | **Render** | `render` block | Indentation-based HTML templates |
165
- | **Props** | `@prop`, `@prop?`, `@prop = default` | Component input from parent |
166
- | **Rest props** | `@...rest` | Capture remaining props |
167
- | **Two-way bind** | `input value <=> name` | Bidirectional data binding |
168
- | **Event handlers** | `@click: handler` | DOM event binding |
169
- | **Lifecycle** | `mounted:`, `unmounted:` | Component lifecycle hooks |
170
- | **Context API** | `setContext`, `getContext` | Pass data down component tree |
171
- | **Fine-grained** | No virtual DOM | Surgical DOM updates via signals |
172
-
173
- **→ 26 major enhancements over CoffeeScript!**
174
-
175
- ---
96
+ ### Reactivity
176
97
 
177
- ## Reactivity
98
+ State, computed values, and effects as language operators:
178
99
 
179
- **The language IS the framework.** Reactivity is built into Rip's syntax—not a library you import, not hooks you call. Just operators.
100
+ | Operator | Mnemonic | Example | What it does |
101
+ |----------|----------|---------|--------------|
102
+ | `=` | "gets value" | `x = 5` | Regular assignment |
103
+ | `:=` | "has state" | `count := 0` | Reactive state container |
104
+ | `~=` | "always equals" | `twice ~= count * 2` | Auto-updates on changes |
105
+ | `~>` | "reacts to" | `~> log count` | Runs on dependency changes |
106
+ | `=!` | "equals, dammit!" | `MAX =! 100` | Readonly constant |
180
107
 
181
- ```coffee
182
- count := 0 # Signal — reactive state
183
- doubled ~= count * 2 # Derived — auto-updates when count changes
184
- effect -> console.log doubled # Effect — runs when dependencies change
185
-
186
- count = 5 # doubled becomes 10, effect logs "10"
187
- count = 10 # doubled becomes 20, effect logs "20"
188
- ```
108
+ ---
189
109
 
190
- **Compare to React:**
191
- ```javascript
192
- // React: imports, hooks, dependency arrays, rules...
193
- import { useState, useMemo, useEffect } from 'react';
194
- const [count, setCount] = useState(0);
195
- const doubled = useMemo(() => count * 2, [count]);
196
- useEffect(() => console.log(doubled), [doubled]);
197
- ```
110
+ ## New Operators
198
111
 
199
- **Rip: 3 lines. React: 5 lines + imports + dependency arrays + hook rules.**
112
+ | Operator | Example | What it does |
113
+ |----------|---------|--------------|
114
+ | `!` (dammit) | `fetchData!` | Calls AND awaits |
115
+ | `!` (void) | `def process!` | Suppresses implicit return |
116
+ | `!?` (otherwise) | `val !? 5` | Default only if `undefined` |
117
+ | `?:` (ternary) | `x > 0 ? 'yes' : 'no'` | JS-style ternary expression |
118
+ | `?.` `?[]` | `a?.b` `a?[0]` | Optional chaining (both styles) |
119
+ | `...` (spread) | `[...rest, last]` | Spread at start or end (JS: end only) |
120
+ | `//` | `7 // 2` | Floor division → 3 |
121
+ | `%%` | `-1 %% 3` | True modulo → 2 |
122
+ | `=~` | `str =~ /Hello, (\w+)/` | Match (captures in `_`) |
123
+ | `[//, n]` | `str[/Hello, (\w+)/, 1]` | Extract capture n |
124
+ | `.new()` | `Dog.new()` | Ruby-style constructor |
200
125
 
201
- | Concept | React | Vue | Solid | Rip |
202
- |---------|-------|-----|-------|-----|
203
- | State | `useState()` | `ref()` | `createSignal()` | `x := 0` |
204
- | Derived | `useMemo()` | `computed()` | `createMemo()` | `x ~= y * 2` |
205
- | Effect | `useEffect()` | `watch()` | `createEffect()` | `effect ->` |
126
+ **Optional chaining** Both CoffeeScript and ES6 styles are supported:
206
127
 
207
- No imports. No hooks. No dependency arrays. Just operators that do what they say.
128
+ | Syntax | Style | Compiles to |
129
+ |--------|-------|-------------|
130
+ | `obj?[0]` | CoffeeScript | `(obj != null ? obj[0] : undefined)` |
131
+ | `fn?(arg)` | CoffeeScript | `(typeof fn === 'function' ? fn(arg) : undefined)` |
132
+ | `obj?.[0]` | ES6/JS | `obj?.[0]` |
133
+ | `fn?.(arg)` | ES6/JS | `fn?.(arg)` |
208
134
 
209
- [Full reactivity guide →](docs/GUIDE.md#reactivity)
135
+ ### Heredoc & Heregex
210
136
 
211
- ---
212
-
213
- ## Components
214
-
215
- Components are a **language construct**, not a pattern. Define with the `component` keyword, get props, state, lifecycle, and fine-grained DOM updates—all without a virtual DOM.
137
+ **Heredoc** — The closing `'''` position sets the left margin (smart dedent):
216
138
 
217
139
  ```coffee
218
- component Counter
219
- @label = "Count" # Prop with default
220
- @initial = 0 # Another prop
221
-
222
- count := @initial # Reactive state (signal)
223
- doubled ~= count * 2 # Derived value (auto-updates)
224
-
225
- inc: -> count += 1 # Methods
226
- dec: -> count -= 1
227
-
228
- render
229
- div.counter
230
- h2 @label
231
- span.value count
232
- span.derived " (×2 = #{doubled})"
233
- button @click: @dec, "−"
234
- button @click: @inc, "+"
235
-
236
- # Mount with Ruby-style constructor
237
- Counter.new(label: "Score", initial: 10).mount "#app"
140
+ html = '''
141
+ <div>
142
+ <p>Hello</p>
143
+ </div>
144
+ '''
145
+ # Result: " <div>\n <p>Hello</p>\n </div>" (note the leading 2 spaces)
238
146
  ```
239
147
 
240
- **What you get:**
241
- - **Props:** `@prop` (required), `@prop?` (optional), `@prop = default`
242
- - **State:** Signals (`:=`) and derived values (`~=`) just work
243
- - **Lifecycle:** `mounted:`, `unmounted:`, `updated:`
244
- - **Context:** `setContext`/`getContext` for deep prop passing
245
- - **Fine-grained updates:** Only changed DOM nodes update—no virtual DOM diffing
148
+ **Heregex** Extended regex with comments and whitespace:
246
149
 
247
- [Component guide →](docs/GUIDE.md#components)
150
+ ```coffee
151
+ pattern = ///
152
+ ^(\d{3}) # area code
153
+ -(\d{4}) # number
154
+ ///
155
+ ```
248
156
 
249
157
  ---
250
158
 
251
- ## Templates
159
+ ## vs React / Vue / Solid
160
+
161
+ | Concept | React | Vue | Solid | Rip |
162
+ |---------|-------|-----|-------|-----|
163
+ | State | `useState()` | `ref()` | `createSignal()` | `x := 0` |
164
+ | Computed | `useMemo()` | `computed()` | `createMemo()` | `x ~= y * 2` |
165
+ | Effect | `useEffect()` | `watch()` | `createEffect()` | `~> body` |
252
166
 
253
- Indentation-based HTML with Pug-style selectors. Templates compile to **fine-grained DOM operations**—when a signal changes, only the affected text node or attribute updates. No virtual DOM, no diffing, no wasted work.
167
+ Rip's reactivity is framework-agnostic use it with React, Vue, Svelte, or vanilla JS.
254
168
 
255
- ```coffee
256
- render
257
- div#app.container
258
- h1.title "Hello, #{name}!"
259
-
260
- # Two-way binding with <=> operator
261
- input type: "text", value <=> username
262
- input type: "number", value <=> count # Auto-uses valueAsNumber!
263
-
264
- # Dynamic classes (Tailwind-friendly)
265
- button.btn.("primary" if active) @click: submit
266
- "Submit"
267
-
268
- # Loops with keys for efficient updates
269
- ul.items
270
- for item in items, key: item.id
271
- li.item item.name
272
- ```
169
+ ---
273
170
 
274
- **Template features:**
275
- | Syntax | What it does |
276
- |--------|--------------|
277
- | `div#id.class1.class2` | IDs and classes (CSS selector style) |
278
- | `@click: handler` | Event binding |
279
- | `@click.prevent.stop:` | Event modifiers |
280
- | `@keydown.enter:` | Key modifiers |
281
- | `value <=> var` | Two-way binding (auto-syncs input ↔ variable) |
282
- | `.("class", cond && "other")` | Dynamic classes |
283
- | `for x in arr, key: x.id` | Keyed iteration |
284
- | `span if condition` | Conditional rendering |
285
-
286
- **The `<=>` operator** handles two-way binding automatically:
287
- ```coffee
288
- # This one line...
289
- input type: "number", value <=> count
171
+ ## vs CoffeeScript
290
172
 
291
- # ...replaces all this React ceremony:
292
- # <input type="number" value={count}
293
- # onChange={e => setCount(parseInt(e.target.value) || 0)} />
294
- ```
173
+ | Feature | CoffeeScript | Rip |
174
+ |---------|--------------|-----|
175
+ | **Output** | ES5 (var, prototypes) | ES2022 (classes, `?.`, `??`) |
176
+ | **Reactivity** | None | Built-in |
177
+ | **Dependencies** | Multiple | Zero |
178
+ | **Self-hosting** | No | Yes |
179
+ | **Lexer** | 3,558 LOC | 3,250 LOC |
180
+ | **Compiler** | 10,346 LOC | 5,878 LOC |
181
+ | **Total** | 17,760 LOC | ~10,100 LOC |
295
182
 
296
- [Template guide →](docs/GUIDE.md#templates)
183
+ Smaller codebase, modern output, built-in reactivity.
297
184
 
298
185
  ---
299
186
 
300
- ## Browser Support
187
+ ## Browser
301
188
 
302
- Run Rip directly in the browser (51KB compressed—complete language + reactive framework).
303
-
304
- **Try it live:** [https://shreeve.github.io/rip-lang/](https://shreeve.github.io/rip-lang/)
189
+ Run Rip directly in the browser (51KB compressed):
305
190
 
306
191
  ```html
307
192
  <script src="https://shreeve.github.io/rip-lang/docs/dist/rip.browser.min.js"></script>
@@ -312,78 +197,50 @@ Run Rip directly in the browser (51KB compressed—complete language + reactive
312
197
  </script>
313
198
  ```
314
199
 
315
- ```bash
316
- bun run browser # Build the bundle
317
- bun run serve # Start dev server at localhost:3000
318
- ```
319
-
320
- ---
321
-
322
- ## Modernizing What CoffeeScript Started
323
-
324
- CoffeeScript showed us beautiful syntax. Rip takes that vision further:
325
-
326
- | | Rip | CoffeeScript |
327
- |---|---|---|
328
- | **Output** | ES2022 (classes, `?.`, `??`) | ES5 (var, prototypes) |
329
- | **Reactivity** | Built-in (signals, effects, templates) | None |
330
- | **Dependencies** | Zero | Multiple |
331
- | **Self-hosting** | Yes (compiles itself) | No |
332
- | **Codebase** | ~14,000 LOC | 17,760 LOC |
200
+ **Try it live:** [shreeve.github.io/rip-lang](https://shreeve.github.io/rip-lang/)
333
201
 
334
202
  ---
335
203
 
336
- ## Why S-Expressions?
337
-
338
- Traditional compilers use complex AST node classes. Rip uses simple arrays:
204
+ ## Architecture
339
205
 
340
206
  ```
341
207
  Source → Lexer → Parser → S-Expressions → Codegen → JavaScript
342
208
  ["=", "x", 42]
343
209
  ```
344
210
 
345
- **Traditional AST:**
346
- ```javascript
347
- class BinaryOp {
348
- constructor(op, left, right) { ... }
349
- compile() { /* 50 lines */ }
350
- }
351
- ```
211
+ Simple arrays instead of AST node classes. The compiler is self-hosting — `bun run parser` rebuilds from source.
352
212
 
353
- **Rip's approach:**
354
- ```javascript
355
- case '+': return `(${gen(left)} + ${gen(right)})`;
356
- ```
357
-
358
- | Component | CoffeeScript | Rip |
359
- |-----------|--------------|-----|
360
- | Lexer | 3,558 LOC | 3,537 LOC |
361
- | Parser Generator | 2,285 LOC (Jison) | ~1,000 LOC (Solar) |
362
- | Compiler | 10,346 LOC | 7,965 LOC |
363
- | **Total** | **17,760 LOC** | **~14,000 LOC** |
213
+ ---
364
214
 
365
- Result: Smaller than CoffeeScript, yet includes a complete **reactive framework** with signals, derived values, effects, templates, and components.
215
+ ## The Rip Stack
366
216
 
367
- ---
217
+ Rip includes optional packages for full-stack development:
368
218
 
369
- ## Runtime Compatibility
219
+ | Package | Purpose | Lines |
220
+ |---------|---------|-------|
221
+ | [@rip-lang/api](packages/api/) | Web framework (Sinatra-style) | ~595 |
222
+ | [@rip-lang/server](packages/server/) | Multi-worker process manager | ~1,110 |
223
+ | [@rip-lang/db](packages/db/) | DuckDB HTTP server | ~225 |
224
+ | [@rip-lang/schema](packages/schema/) | ORM with reactive models | ~420 |
370
225
 
371
- **Runs everywhere:** Bun (first-class), Node.js 12+, Deno, modern browsers.
226
+ ```bash
227
+ bun add @rip-lang/api @rip-lang/server
228
+ ```
372
229
 
373
- Output uses ES2022: classes, `let`/`const`, `?.`, `??`, `for await`, top-level await.
230
+ [Full stack documentation →](packages/README.md)
374
231
 
375
232
  ---
376
233
 
377
234
  ## Quick Reference
378
235
 
379
236
  ```bash
380
- rip # Interactive REPL
381
- rip file.rip # Run a file
382
- rip -c file.rip # Compile to JavaScript
383
- rip -s file.rip # Show S-expressions (debug parser)
384
- rip -t file.rip # Show tokens (debug lexer)
385
- bun run test # Run all 1046 tests
386
- bun run parser # Rebuild parser (self-hosting!)
237
+ rip # REPL
238
+ rip file.rip # Run
239
+ rip -c file.rip # Compile
240
+ rip -t file.rip # Tokens
241
+ rip -s file.rip # S-expressions
242
+ bun run test # 979 tests
243
+ bun run parser # Rebuild parser
387
244
  bun run browser # Build browser bundle
388
245
  ```
389
246
 
@@ -393,12 +250,11 @@ bun run browser # Build browser bundle
393
250
 
394
251
  | Guide | Description |
395
252
  |-------|-------------|
396
- | [AGENT.md](AGENT.md) | Complete developer/AI guide |
397
- | [docs/GUIDE.md](docs/GUIDE.md) | Language guide (reactivity, templates, components) |
398
- | [docs/INTERNALS.md](docs/INTERNALS.md) | Compiler architecture, S-expressions |
399
- | [docs/BROWSER.md](docs/BROWSER.md) | Browser usage, REPL |
253
+ | [docs/GUIDE.md](docs/GUIDE.md) | Language guide |
254
+ | [docs/REACTIVITY.md](docs/REACTIVITY.md) | Reactivity deep dive |
255
+ | [docs/INTERNALS.md](docs/INTERNALS.md) | Compiler architecture |
256
+ | [packages/](packages/README.md) | Full-stack packages |
400
257
  | [CONTRIBUTING.md](CONTRIBUTING.md) | How to contribute |
401
- | [CHANGELOG.md](CHANGELOG.md) | Version history |
402
258
 
403
259
  ---
404
260
 
@@ -408,7 +264,7 @@ bun run browser # Build browser bundle
408
264
  { "dependencies": {} }
409
265
  ```
410
266
 
411
- Everything included: compiler, parser generator (solar.rip), REPL, browser bundle, test framework. Rip compiles itself — `bun run parser` rebuilds from source.
267
+ Everything included: compiler, parser generator, REPL, browser bundle, test framework.
412
268
 
413
269
  ---
414
270
 
@@ -416,24 +272,12 @@ Everything included: compiler, parser generator (solar.rip), REPL, browser bundl
416
272
 
417
273
  > *Simplicity scales.*
418
274
 
419
- Keep the IR simple (S-expressions), keep the pipeline clear (lex → parse → generate), keep the code minimal. Test everything.
275
+ Simple IR (S-expressions), clear pipeline (lex → parse → generate), minimal code, comprehensive tests.
420
276
 
421
277
  ---
422
278
 
423
- ## Credits
279
+ **Inspired by:** CoffeeScript, Lisp, Ruby | **Powered by:** [Bun](https://bun.sh)
424
280
 
425
- **Inspired by:** CoffeeScript (syntax), Lisp/Scheme (S-expressions), Ruby (regex operators, `__DATA__`), Solar (parser generator).
281
+ MIT License
426
282
 
427
- **Powered by:** [Bun](https://bun.sh) the fast all-in-one JavaScript runtime.
428
-
429
- ---
430
-
431
- ## License
432
-
433
- MIT
434
-
435
- ---
436
-
437
- <p align="center">
438
- <strong>Start simple. Build incrementally. Ship elegantly.</strong> ✨
439
- </p>
283
+ <p align="center"><strong>Start simple. Build incrementally. Ship elegantly.</strong></p>