rip-lang 1.4.2 → 1.4.4
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 +40 -0
- package/README.md +258 -1171
- package/docs/dist/rip.browser.js +74 -68
- package/docs/dist/rip.browser.min.js +147 -142
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/package.json +1 -1
- package/src/codegen.js +20 -4
- package/src/compiler.js +90 -47
package/README.md
CHANGED
|
@@ -5,1368 +5,461 @@
|
|
|
5
5
|
<h1 align="center">Rip</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<strong>Elegant
|
|
8
|
+
<strong>Elegant CoffeeScript-inspired language → Modern JavaScript (ES2022)</strong>
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-1.4.
|
|
13
|
-
<a href="#es2022-target"><img src="https://img.shields.io/badge/target-ES2022-blue.svg" alt="Target"></a>
|
|
14
|
-
<a href="#current-status"><img src="https://img.shields.io/badge/tests-931%2F931-brightgreen.svg" alt="Tests"></a>
|
|
15
|
-
<a href="#current-status"><img src="https://img.shields.io/badge/coverage-100%25-brightgreen.svg" alt="Coverage"></a>
|
|
12
|
+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-1.4.4-blue.svg" alt="Version"></a>
|
|
16
13
|
<a href="#zero-dependencies"><img src="https://img.shields.io/badge/dependencies-ZERO-brightgreen.svg" alt="Dependencies"></a>
|
|
14
|
+
<a href="#status"><img src="https://img.shields.io/badge/tests-938%2F938-brightgreen.svg" alt="Tests"></a>
|
|
17
15
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
|
|
18
16
|
</p>
|
|
19
17
|
|
|
20
18
|
---
|
|
21
19
|
|
|
22
|
-
##
|
|
23
|
-
|
|
24
|
-
**Write less. Do more. Zero dependencies.**
|
|
25
|
-
|
|
26
|
-
Rip brings CoffeeScript's elegance to modern JavaScript—but **45% smaller**, completely standalone, and self-hosting. No build tools, no external dependencies, not even a parser generator. Just clone and go.
|
|
27
|
-
|
|
28
|
-
#### 💎 Elegant syntax with modern features
|
|
29
|
-
|
|
30
|
-
```coffee
|
|
31
|
-
# Async with dammit operator! (call and await)
|
|
32
|
-
fetchUser = (id) => fetch! "/api/user/${id}"
|
|
33
|
-
|
|
34
|
-
# Ruby-style regex with =~ operator and _ captures
|
|
35
|
-
def parseUsers(...inputs)
|
|
36
|
-
users = for input in inputs when input =~ /^(\w+):([^@]+@[\w.]+)$/
|
|
37
|
-
name = _[1] ?? "guest" # Nullish coalescing, _ captures
|
|
38
|
-
domain = input[/@([\w.]+)/, 1] # Regex extraction syntax
|
|
39
|
-
{ name, domain }
|
|
40
|
-
|
|
41
|
-
parseUsers "alice:alice@example.com", "bob:bob@test.org"
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
#### 🎯 Compiles to clean, modern JavaScript
|
|
45
|
-
|
|
46
|
-
```javascript
|
|
47
|
-
let _, fetchUser;
|
|
48
|
-
|
|
49
|
-
fetchUser = async (id) => await fetch(`/api/user/${id}`);
|
|
50
|
-
function parseUsers(...inputs) {
|
|
51
|
-
let domain, name, users;
|
|
52
|
-
|
|
53
|
-
users = [];
|
|
54
|
-
for (const input of inputs) {
|
|
55
|
-
if ((_ = toSearchable(input).match(/^(\w+):([^@]+@[\w.]+)$/))) {
|
|
56
|
-
name = (_[1] ?? "guest");
|
|
57
|
-
domain = (_ = toSearchable(input).match(/@([\w.]+)/)) && _[1];
|
|
58
|
-
users.push({name, domain});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return users;
|
|
62
|
-
};
|
|
63
|
-
parseUsers("alice:alice@example.com", "bob:bob@test.org");
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
#### 🚀 Run with: bun run example.rip
|
|
67
|
-
|
|
68
|
-
```json
|
|
69
|
-
[
|
|
70
|
-
{
|
|
71
|
-
"name": "alice",
|
|
72
|
-
"domain": "example.com",
|
|
73
|
-
}, {
|
|
74
|
-
"name": "bob",
|
|
75
|
-
"domain": "test.org",
|
|
76
|
-
}
|
|
77
|
-
]
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
**What makes Rip special?**
|
|
81
|
-
|
|
82
|
-
- 🎯 **Zero Dependencies** - Includes its own SLR(1) parser generator (solar.rip)
|
|
83
|
-
- 🚀 **Self-Hosting** - Rip compiles itself, including the parser generator
|
|
84
|
-
- ⚡ **Just Run It** - `bun your-script.rip` works instantly (automatic loader via bunfig.toml)
|
|
85
|
-
- 🎨 **Elegant** - Beautiful syntax, implicit returns, no semicolons
|
|
86
|
-
- 🧠 **Smart** - Context-aware comprehensions, range optimizations, auto-async
|
|
87
|
-
- 📦 **Complete** - Full compiler, triple REPL (terminal/browser/console), test framework
|
|
88
|
-
- 🔧 **Modern** - ES2022 output with classes, modules, optional chaining
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
## Runtime Compatibility
|
|
93
|
-
|
|
94
|
-
**Primary Targets:**
|
|
95
|
-
- 🎯 **Bun** - First-class support with automatic `.rip` loader (recommended)
|
|
96
|
-
- 🌐 **Browsers** - 43KB bundle, inline `<script type="text/rip">`, REPL
|
|
97
|
-
|
|
98
|
-
**Also Supported:**
|
|
99
|
-
- ✅ **Deno** - ES2022 output works natively
|
|
100
|
-
- ✅ **Node.js 12+** - Full compatibility with modern Node
|
|
20
|
+
## What is Rip?
|
|
101
21
|
|
|
102
|
-
|
|
22
|
+
A clean-room **CoffeeScript-inspired compiler** that produces modern JavaScript (ES2022). Built from scratch with an elegant S-expression architecture.
|
|
103
23
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
**
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
- ✅ **
|
|
110
|
-
- ✅ **ES2022:** static class fields, top-level await
|
|
111
|
-
|
|
112
|
-
**Not Used:**
|
|
113
|
-
- ❌ Private fields (`#var`) - not commonly needed
|
|
114
|
-
- ❌ WeakRefs, FinalizationRegistry - specialized use cases
|
|
115
|
-
|
|
116
|
-
**Why ES2022?** Modern output means smaller code, native features, and excellent performance across all runtimes.
|
|
24
|
+
**Key differentiators:**
|
|
25
|
+
- 🎯 **Zero dependencies** - Completely standalone (includes its own parser generator)
|
|
26
|
+
- 🚀 **Self-hosting** - Rip compiles itself (`bun run parser` works!)
|
|
27
|
+
- ⚡ **~50% smaller** than CoffeeScript (9,839 LOC vs 17,760 LOC)
|
|
28
|
+
- 🎨 **Modern output** - ES2022 with classes, modules, optional chaining
|
|
29
|
+
- ✅ **Production-ready** - 938/938 tests passing (100%)
|
|
117
30
|
|
|
118
31
|
---
|
|
119
32
|
|
|
120
|
-
##
|
|
121
|
-
|
|
122
|
-
**Rip is completely standalone with ZERO runtime or build dependencies!**
|
|
123
|
-
|
|
124
|
-
```json
|
|
125
|
-
{
|
|
126
|
-
"dependencies": {} // ← Completely empty!
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
**What's included:**
|
|
131
|
-
- ✅ **Full compiler** - Lexer, parser, code generator (all built-in)
|
|
132
|
-
- ✅ **Parser generator** - Complete SLR(1) parser generator (solar.rip)
|
|
133
|
-
- ✅ **Self-hosting** - Rip compiles itself, including the parser generator
|
|
134
|
-
- ✅ **Triple REPL** - Terminal, browser, and console REPLs built-in
|
|
135
|
-
- ✅ **Browser bundle** - 43KB self-contained compiler
|
|
136
|
-
- ✅ **Test runner** - Full test framework included
|
|
137
|
-
|
|
138
|
-
**What you need:**
|
|
139
|
-
- JavaScript runtime (Bun, Node.js, or browser)
|
|
140
|
-
- **That's it!**
|
|
141
|
-
|
|
142
|
-
**Self-hosting verification:**
|
|
143
|
-
```bash
|
|
144
|
-
# ONE COMMAND rebuilds the parser from scratch
|
|
145
|
-
bun run parser
|
|
146
|
-
|
|
147
|
-
# What this does:
|
|
148
|
-
# - Runs solar.rip (parser generator, written in Rip)
|
|
149
|
-
# - Reads grammar.rip (grammar spec, written in Rip)
|
|
150
|
-
# - Outputs parser.js (complete parser)
|
|
151
|
-
# Complete bootstrap loop with ZERO external tools ✅
|
|
152
|
-
```
|
|
33
|
+
## Quick Example
|
|
153
34
|
|
|
154
|
-
**No external compilers, no build tools, no transpilers** - just a JavaScript runtime and Rip itself!
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## How It Works
|
|
159
|
-
|
|
160
|
-
**The secret: S-expressions as intermediate representation**
|
|
161
|
-
|
|
162
|
-
Traditional compilers use complex AST node classes. Rip uses simple arrays:
|
|
163
|
-
|
|
164
|
-
```
|
|
165
|
-
Source → Tokens → S-Expressions → JavaScript
|
|
166
|
-
["=", "x", 42]
|
|
167
|
-
Simple arrays!
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
**Before (Traditional AST):**
|
|
171
|
-
```javascript
|
|
172
|
-
class BinaryOp {
|
|
173
|
-
constructor(op, left, right) { ... }
|
|
174
|
-
compile() { /* complex logic */ }
|
|
175
|
-
}
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
**After (S-Expressions):**
|
|
179
|
-
```javascript
|
|
180
|
-
case '+': {
|
|
181
|
-
const [left, right] = rest;
|
|
182
|
-
return `(${this.generate(left)} + ${this.generate(right)})`;
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
**Result: 45% smaller implementation**
|
|
187
|
-
|
|
188
|
-
| Component | CoffeeScript | Rip | Notes |
|
|
189
|
-
|-----------|--------------|-----|-------|
|
|
190
|
-
| Lexer+Rewriter | 3,558 LOC | **3,145 LOC** | Expanded syntax |
|
|
191
|
-
| Parser Generator | 2,285 LOC (Jison) | **928 LOC** (Solar) | Built-in, ~156× faster! |
|
|
192
|
-
| Compiler | 10,346 LOC (AST Nodes) | **5,221 LOC** (S-expressions) | Clean dispatch table |
|
|
193
|
-
| Tools | 1,571 LOC (Repl, Cake) | **520 LOC** (Repl, Browser) | 3 Repl's + Browser |
|
|
194
|
-
| **Total** | **17,760 LOC** | **9,814 LOC** | **~45% smaller** |
|
|
195
|
-
|
|
196
|
-
**Plus:**
|
|
197
|
-
- ✅ **ZERO dependencies** - Everything included
|
|
198
|
-
- ✅ **Self-hosting** - Rip compiles itself
|
|
199
|
-
- ✅ **No external tools** - Just a JavaScript runtime
|
|
200
|
-
|
|
201
|
-
### Real-World Example: The Complete Pipeline
|
|
202
|
-
|
|
203
|
-
Let's see how Rip code flows through the compilation pipeline:
|
|
204
|
-
|
|
205
|
-
**Step 1: Rip Source Code**
|
|
206
35
|
```coffee
|
|
207
|
-
#
|
|
208
|
-
|
|
209
|
-
if n <= 1
|
|
210
|
-
n
|
|
211
|
-
else
|
|
212
|
-
fibonacci(n - 1) + fibonacci(n - 2)
|
|
213
|
-
|
|
214
|
-
# Try heregex
|
|
215
|
-
pattern = ///
|
|
216
|
-
^ \d+ # digits
|
|
217
|
-
\s* # space
|
|
218
|
-
[a-z]+ # letters
|
|
219
|
-
$
|
|
220
|
-
///i
|
|
221
|
-
|
|
222
|
-
# Try regex features
|
|
223
|
-
email = "user@example.com"
|
|
224
|
-
domain = email[/@(.+)$/, 1]
|
|
36
|
+
# Async with dammit operator! (call and await)
|
|
37
|
+
fetchUser = (id) => fetch! "/api/user/${id}"
|
|
225
38
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
39
|
+
# Ruby-style regex with =~ operator
|
|
40
|
+
def parseEmail(input)
|
|
41
|
+
return unless input =~ /^(\w+)@([\w.]+)$/
|
|
42
|
+
{ user: _[1], domain: _[2] } # _ captures match groups
|
|
229
43
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
The parser converts this to simple arrays (s-expressions):
|
|
233
|
-
|
|
234
|
-
```lisp
|
|
235
|
-
(program
|
|
236
|
-
(def fibonacci (n)
|
|
237
|
-
(block
|
|
238
|
-
(if (<= n 1) (block n)
|
|
239
|
-
(block (+ (fibonacci (- n 1)) (fibonacci (- n 2)))))))
|
|
240
|
-
(= pattern "/^ \\d+\\s*[a-z]+$/i")
|
|
241
|
-
(= email "user@example.com")
|
|
242
|
-
(= domain (regex-index email /@(.+)$/ 1))
|
|
243
|
-
((. console log) "Fib(10):" (fibonacci 10))
|
|
244
|
-
((. console log) "Domain:" domain)
|
|
245
|
-
)
|
|
44
|
+
result = parseEmail "alice@example.com"
|
|
246
45
|
```
|
|
247
46
|
|
|
248
|
-
**
|
|
249
|
-
- Each operation is just an array: `["+", left, right]`
|
|
250
|
-
- No complex AST node classes - just plain data
|
|
251
|
-
- Easy to inspect, transform, and debug
|
|
252
|
-
- Pattern matching in codegen is straightforward
|
|
253
|
-
|
|
254
|
-
**Step 3: JavaScript Output**
|
|
255
|
-
|
|
256
|
-
The code generator pattern-matches on s-expressions to produce clean JavaScript:
|
|
47
|
+
**Compiles to clean ES2022:**
|
|
257
48
|
|
|
258
49
|
```javascript
|
|
259
|
-
let _
|
|
50
|
+
let _;
|
|
260
51
|
|
|
261
|
-
|
|
262
|
-
|
|
52
|
+
const fetchUser = async (id) => await fetch(`/api/user/${id}`);
|
|
53
|
+
function parseEmail(input) {
|
|
54
|
+
if (!(_ = toSearchable(input).match(/^(\w+)@([\w.]+)$/))) return;
|
|
55
|
+
return {user: _[1], domain: _[2]};
|
|
263
56
|
};
|
|
264
|
-
|
|
265
|
-
email = "user@example.com";
|
|
266
|
-
domain = (_ = toSearchable(email).match(/@(.+)$/)) && _[1];
|
|
267
|
-
console.log("Fib(10):", fibonacci(10));
|
|
268
|
-
console.log("Domain:", domain);
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
**The beauty:** Simple arrays in, clean JavaScript out. No complex AST traversal needed!
|
|
272
|
-
|
|
273
|
-
**Try it yourself:**
|
|
274
|
-
```bash
|
|
275
|
-
# See the s-expressions
|
|
276
|
-
echo 'x = 42' | ./bin/rip -s
|
|
277
|
-
|
|
278
|
-
# See the generated JavaScript
|
|
279
|
-
echo 'x = 42' | ./bin/rip -c
|
|
57
|
+
const result = parseEmail("alice@example.com");
|
|
280
58
|
```
|
|
281
59
|
|
|
282
60
|
---
|
|
283
61
|
|
|
284
|
-
##
|
|
285
|
-
|
|
286
|
-
### Installation & Setup
|
|
62
|
+
## Installation
|
|
287
63
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
First, install Bun if you haven't already:
|
|
64
|
+
### Option 1: Install Globally (Recommended)
|
|
291
65
|
|
|
292
66
|
```bash
|
|
293
|
-
# Install Bun
|
|
67
|
+
# Install Bun if needed
|
|
294
68
|
curl -fsSL https://bun.sh/install | bash
|
|
295
69
|
|
|
296
|
-
#
|
|
297
|
-
powershell -c "irm bun.sh/install.ps1|iex"
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
Then install Rip globally:
|
|
301
|
-
|
|
302
|
-
```bash
|
|
303
|
-
# Install Rip globally
|
|
70
|
+
# Install Rip
|
|
304
71
|
bun add -g rip-lang
|
|
305
72
|
|
|
306
|
-
# Start
|
|
307
|
-
rip
|
|
73
|
+
# Start using it!
|
|
74
|
+
rip # Interactive REPL
|
|
75
|
+
rip yourfile.rip # Compile a file
|
|
76
|
+
bun yourfile.rip # Execute directly
|
|
308
77
|
```
|
|
309
78
|
|
|
310
|
-
|
|
311
|
-
- Run `rip` anywhere for an interactive REPL
|
|
312
|
-
- Compile Rip files: `rip yourfile.rip`
|
|
313
|
-
- Execute `.rip` files: `bun yourfile.rip`
|
|
314
|
-
|
|
315
|
-
---
|
|
316
|
-
|
|
317
|
-
**Alternative: Install from Source**
|
|
79
|
+
### Option 2: Clone from Source
|
|
318
80
|
|
|
319
|
-
**Step 1: Clone the repository**
|
|
320
81
|
```bash
|
|
321
82
|
git clone https://github.com/shreeve/rip-lang.git
|
|
322
83
|
cd rip-lang
|
|
323
|
-
```
|
|
324
84
|
|
|
325
|
-
|
|
326
|
-
```bash
|
|
327
|
-
# Link Rip globally so it's available everywhere
|
|
85
|
+
# Link globally
|
|
328
86
|
bun link
|
|
329
87
|
|
|
330
88
|
# Add to global Bun config
|
|
331
89
|
echo 'preload = ["rip-lang/loader"]' >> ~/.bunfig.toml
|
|
332
|
-
```
|
|
333
90
|
|
|
334
|
-
|
|
335
|
-
```bash
|
|
336
|
-
cd ~/any-project
|
|
337
|
-
|
|
338
|
-
# Create a test file
|
|
339
|
-
echo 'def greet(name)
|
|
340
|
-
"Hello, ${name}!"
|
|
341
|
-
|
|
342
|
-
console.log greet("World")' > test.rip
|
|
343
|
-
|
|
344
|
-
# Run it!
|
|
345
|
-
bun test.rip # → Hello, World! ✨
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
**Verify your setup:**
|
|
349
|
-
```bash
|
|
350
|
-
# Check that rip-lang is linked
|
|
351
|
-
bun pm ls --global | grep rip-lang
|
|
352
|
-
|
|
353
|
-
# Check your global config
|
|
354
|
-
cat ~/.bunfig.toml
|
|
355
|
-
# Should include: preload = ["rip-lang/loader"]
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
---
|
|
359
|
-
|
|
360
|
-
**No npm install needed** - Rip has zero dependencies!
|
|
361
|
-
|
|
362
|
-
**Requirements:**
|
|
363
|
-
- **Bun** (recommended) - For automatic `.rip` loader and REPL
|
|
364
|
-
- **Or** any ES2022-compatible runtime: Deno, Node.js 12+, modern browsers
|
|
365
|
-
- Note: Deno/Node require compilation first (`./bin/rip -o output.js input.rip`)
|
|
366
|
-
|
|
367
|
-
### Usage
|
|
368
|
-
|
|
369
|
-
**The easiest way: Run .rip files directly with Bun**
|
|
370
|
-
|
|
371
|
-
```bash
|
|
372
|
-
# Just run it! The loader is automatic via bunfig.toml
|
|
91
|
+
# Run .rip files from anywhere
|
|
373
92
|
bun your-script.rip
|
|
374
|
-
|
|
375
|
-
# Example
|
|
376
|
-
echo 'def greet(name)
|
|
377
|
-
console.log "Hello, ${name}!"
|
|
378
|
-
|
|
379
|
-
greet "World"' > hello.rip
|
|
380
|
-
|
|
381
|
-
bun hello.rip
|
|
382
|
-
# → Hello, World!
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
**How it works:** The `bunfig.toml` preloads `rip-loader.ts`, which registers a Bun plugin that automatically compiles `.rip` files on-the-fly. No build step, no manual compilation—just run your code!
|
|
386
|
-
|
|
387
|
-
**You can also import .rip modules directly:**
|
|
388
|
-
|
|
389
|
-
```coffee
|
|
390
|
-
# utils.rip
|
|
391
|
-
export def add(a, b)
|
|
392
|
-
a + b
|
|
393
|
-
|
|
394
|
-
export multiply = (a, b) => a * b
|
|
395
93
|
```
|
|
396
94
|
|
|
397
|
-
|
|
398
|
-
# main.rip
|
|
399
|
-
import { add, multiply } from "./utils.rip"
|
|
400
|
-
|
|
401
|
-
console.log add(5, 3) # 8
|
|
402
|
-
console.log multiply(4, 7) # 28
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
```bash
|
|
406
|
-
bun main.rip # Works automatically!
|
|
407
|
-
```
|
|
95
|
+
---
|
|
408
96
|
|
|
409
|
-
|
|
97
|
+
## Quick Start
|
|
410
98
|
|
|
411
99
|
```bash
|
|
412
100
|
# Interactive REPL
|
|
413
101
|
./bin/rip
|
|
414
102
|
|
|
415
|
-
# Execute a
|
|
103
|
+
# Execute a file
|
|
416
104
|
./bin/rip examples/fibonacci.rip
|
|
417
105
|
|
|
418
|
-
# Compile
|
|
106
|
+
# Compile to JavaScript
|
|
419
107
|
./bin/rip -c examples/fibonacci.rip
|
|
420
108
|
|
|
421
|
-
#
|
|
109
|
+
# Save to file
|
|
422
110
|
./bin/rip -o output.js examples/fibonacci.rip
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
### Debug Flags (Mix and Match!)
|
|
426
|
-
|
|
427
|
-
Rip supports flexible debugging with flags that can be combined:
|
|
428
|
-
|
|
429
|
-
```bash
|
|
430
|
-
# Show ONLY s-expressions (no JavaScript)
|
|
431
|
-
./bin/rip -s examples/fibonacci.rip
|
|
432
|
-
|
|
433
|
-
# Show ONLY tokens (no JavaScript)
|
|
434
|
-
./bin/rip -t examples/fibonacci.rip
|
|
435
|
-
|
|
436
|
-
# Show s-expressions AND JavaScript
|
|
437
|
-
./bin/rip -s -c examples/fibonacci.rip
|
|
438
|
-
|
|
439
|
-
# Show tokens AND JavaScript
|
|
440
|
-
./bin/rip -t -c examples/fibonacci.rip
|
|
441
|
-
|
|
442
|
-
# Show EVERYTHING (full debug mode)
|
|
443
|
-
./bin/rip -s -t -c examples/fibonacci.rip
|
|
444
|
-
|
|
445
|
-
# Pipe mode (no headers, just output)
|
|
446
|
-
./bin/rip -q -c examples/fibonacci.rip
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
**How it works:**
|
|
450
|
-
- `rip script.rip` **executes** the script (default behavior)
|
|
451
|
-
- `echo 'code' | rip` **compiles** and shows JavaScript (stdin defaults to compile mode)
|
|
452
|
-
- `-c` flag **compiles** and shows JavaScript output
|
|
453
|
-
- `-o file.js` **compiles** and saves to file
|
|
454
|
-
- `-s` or `-t` alone show **only** that output (no JavaScript)
|
|
455
|
-
- Add `-c` to **also** show the compiled JavaScript
|
|
456
|
-
- Mix and match as needed for debugging
|
|
457
|
-
- Browser REPL always shows JavaScript (checkboxes toggle `-s` and `-t`)
|
|
458
|
-
|
|
459
|
-
### Interactive REPL
|
|
460
|
-
|
|
461
|
-
Rip includes a full-featured REPL for interactive development:
|
|
462
|
-
|
|
463
|
-
```bash
|
|
464
|
-
$ ./bin/rip
|
|
465
|
-
Rip 1.0.0 - Interactive REPL
|
|
466
|
-
Type .help for commands, Ctrl+C to exit
|
|
467
|
-
|
|
468
|
-
rip> x = 42
|
|
469
|
-
→ 42
|
|
470
|
-
|
|
471
|
-
rip> pattern = ///
|
|
472
|
-
....> \d+ # digits
|
|
473
|
-
....> [a-z]+ # letters
|
|
474
|
-
....> ///
|
|
475
|
-
→ /\d+[a-z]+/
|
|
476
|
-
|
|
477
|
-
rip> pattern.test('123abc')
|
|
478
|
-
→ true
|
|
479
|
-
|
|
480
|
-
rip> .vars
|
|
481
|
-
Defined variables:
|
|
482
|
-
x = 42
|
|
483
|
-
pattern = /\d+[a-z]+/
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
**REPL Features:**
|
|
487
|
-
- ✅ Variable persistence across evaluations
|
|
488
|
-
- ✅ Multi-line input (automatic detection)
|
|
489
|
-
- ✅ Command history (arrow keys)
|
|
490
|
-
- ✅ Special commands (.help, .vars, .clear, .history, .exit)
|
|
491
|
-
- ✅ Debug modes (.tokens, .sexp, .js)
|
|
492
|
-
- ✅ Pretty-printed output with colors
|
|
493
|
-
- ✅ Last result in `_` variable
|
|
494
|
-
|
|
495
|
-
### Browser REPL & Bundle
|
|
496
|
-
|
|
497
|
-
**Run Rip in your browser!** Browsers are a primary target with full support:
|
|
498
|
-
|
|
499
|
-
**Try it online (GitHub Pages):**
|
|
500
|
-
```
|
|
501
|
-
https://shreeve.github.io/rip-lang/
|
|
502
|
-
# Live REPL, examples, and live compiler
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
**Or run locally:**
|
|
506
|
-
```bash
|
|
507
|
-
# Build browser bundles (one-time)
|
|
508
|
-
bun run browser
|
|
509
|
-
|
|
510
|
-
# Start development server
|
|
511
|
-
bun run serve
|
|
512
|
-
|
|
513
|
-
# Open in browser
|
|
514
|
-
http://localhost:3000/
|
|
515
|
-
# (auto-redirects to REPL)
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
**What you get:**
|
|
519
|
-
- **REPL Console** - Terminal-like with commands, history, multi-line
|
|
520
|
-
- **Live Compiler** - Split pane showing Rip → JavaScript in real-time
|
|
521
|
-
- **43KB bundle** - Brotli-compressed (560KB → 43KB, 92% reduction!)
|
|
522
|
-
- **Inline scripts** - `<script type="text/rip">` auto-executes
|
|
523
|
-
- **Syntax highlighting** - Colored JavaScript output
|
|
524
|
-
- **All features work** - Heregex, regex+, classes, async, dammit operator, everything!
|
|
525
|
-
|
|
526
|
-
**Use in production:**
|
|
527
|
-
```html
|
|
528
|
-
<script src="https://cdn.example.com/rip.browser.min.js"></script>
|
|
529
|
-
<script type="text/rip">
|
|
530
|
-
def greet(name)
|
|
531
|
-
console.log "Hello, ${name}!"
|
|
532
|
-
greet "Browser"
|
|
533
|
-
</script>
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
See [docs/BROWSER.md](docs/BROWSER.md) for complete browser guide.
|
|
537
|
-
|
|
538
|
-
### Running Tests
|
|
539
|
-
|
|
540
|
-
```bash
|
|
541
|
-
# All tests (recommended)
|
|
542
|
-
bun run test
|
|
543
|
-
|
|
544
|
-
# Or run directly
|
|
545
|
-
bun test/runner.js test/rip
|
|
546
|
-
|
|
547
|
-
# Specific file
|
|
548
|
-
bun test/runner.js test/rip/functions.rip
|
|
549
|
-
|
|
550
|
-
# Use --no-cache during active development
|
|
551
|
-
bun --no-cache test/runner.js test/rip
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
### Quick Reference: NPM Scripts
|
|
555
111
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
bun run serve # Start dev server (REPL at localhost:3000)
|
|
112
|
+
# Debug flags (mix and match!)
|
|
113
|
+
./bin/rip -s examples/fibonacci.rip # Show s-expressions
|
|
114
|
+
./bin/rip -t examples/fibonacci.rip # Show tokens
|
|
115
|
+
./bin/rip -s -c examples/fibonacci.rip # Show both
|
|
561
116
|
```
|
|
562
117
|
|
|
563
118
|
---
|
|
564
119
|
|
|
565
|
-
##
|
|
120
|
+
## Key Features
|
|
566
121
|
|
|
567
|
-
###
|
|
122
|
+
### Elegant Syntax
|
|
568
123
|
|
|
569
124
|
```coffee
|
|
570
|
-
#
|
|
571
|
-
|
|
572
|
-
name
|
|
573
|
-
|
|
574
|
-
# Note: Variables hoist to top of their scope (program or function)
|
|
575
|
-
# Functions access outer variables via closure (CoffeeScript semantics)
|
|
125
|
+
# Functions (three styles)
|
|
126
|
+
def greet(name) # Named, hoisted
|
|
127
|
+
"Hello, ${name}!"
|
|
576
128
|
|
|
577
|
-
|
|
578
|
-
def add(a, b) # Named, unbound this, hoisted
|
|
129
|
+
calculate = (a, b) -> # Thin arrow (unbound this)
|
|
579
130
|
a + b
|
|
580
131
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
divide = (a, b) => # Anonymous, bound this, not hoisted
|
|
585
|
-
a / b
|
|
586
|
-
|
|
587
|
-
# Important: Arrow functions ALWAYS require parentheses
|
|
588
|
-
# () => expr (x) => expr (x, y) => expr
|
|
589
|
-
# Consistency over saving 2 characters!
|
|
590
|
-
|
|
591
|
-
# Conditionals
|
|
592
|
-
if x > 0
|
|
593
|
-
"positive"
|
|
594
|
-
else
|
|
595
|
-
"negative"
|
|
596
|
-
|
|
597
|
-
# Loops
|
|
598
|
-
for item in [1, 2, 3]
|
|
599
|
-
console.log item
|
|
132
|
+
handler = (event) => # Fat arrow (bound this)
|
|
133
|
+
@process event
|
|
600
134
|
|
|
601
|
-
#
|
|
602
|
-
|
|
603
|
-
name: "Bob"
|
|
604
|
-
age: 30
|
|
135
|
+
# Comprehensions (context-aware!)
|
|
136
|
+
squares = (x * x for x in [1..10]) # IIFE (result used)
|
|
605
137
|
|
|
606
|
-
#
|
|
607
|
-
numbers = [1, 2, 3, 4, 5]
|
|
608
|
-
|
|
609
|
-
# String interpolation
|
|
610
|
-
greeting = "Hello, ${name}!"
|
|
138
|
+
processItem x for x in items # Plain loop (result unused)
|
|
611
139
|
```
|
|
612
140
|
|
|
613
|
-
### Modern Features
|
|
141
|
+
### Modern JavaScript Features
|
|
614
142
|
|
|
615
143
|
```coffee
|
|
616
144
|
# Destructuring
|
|
617
145
|
{name, age} = person
|
|
618
|
-
[first,
|
|
619
|
-
|
|
620
|
-
# Spread/rest (dual syntax - prefix or postfix)
|
|
621
|
-
combined = [...arr1, ...arr2] # ES6 prefix (recommended)
|
|
622
|
-
combined = [arr1..., arr2...] # CoffeeScript postfix (compatibility)
|
|
623
|
-
def fn(first, ...rest) # ES6 prefix rest params
|
|
624
|
-
def fn(first, rest...) # CoffeeScript postfix rest params
|
|
625
|
-
{name, ...props} = person # ES6 prefix object rest
|
|
626
|
-
{name, props...} = person # CoffeeScript postfix object rest
|
|
627
|
-
|
|
628
|
-
# Note: Both syntaxes compile to the same ES6 JavaScript.
|
|
629
|
-
# Postfix syntax (x...) is for CoffeeScript compatibility.
|
|
630
|
-
# New code should use prefix syntax (...x) for clarity.
|
|
631
|
-
|
|
632
|
-
# Optional operators (dual syntax)
|
|
633
|
-
user?.profile?.name # ES6 optional chaining (native)
|
|
634
|
-
arr?[0] # CoffeeScript soak (existence check)
|
|
635
|
-
fn?(arg) # CoffeeScript soak call
|
|
636
|
-
|
|
637
|
-
# Nullish coalescing (?? - rejects null OR undefined)
|
|
638
|
-
port = config.port ?? 8080
|
|
639
|
-
|
|
640
|
-
# Otherwise operator (!? - rejects ONLY undefined)
|
|
641
|
-
timeout = config.timeout !? 5000 # null and 0 are valid!
|
|
642
|
-
enabled = options.enabled !? true # false means disabled, undefined means default
|
|
643
|
-
|
|
644
|
-
# Comparison:
|
|
645
|
-
null ?? 'default' # → 'default' (null fails)
|
|
646
|
-
null !? 'default' # → null (null is defined!)
|
|
647
|
-
|
|
648
|
-
# Use ?? when: Both null and undefined should use default
|
|
649
|
-
# Use !? when: Only undefined should use default (null/false/0 are meaningful)
|
|
146
|
+
[first, ...rest] = array
|
|
650
147
|
|
|
651
|
-
#
|
|
652
|
-
|
|
653
|
-
|
|
148
|
+
# Optional chaining (dual syntax)
|
|
149
|
+
user?.profile?.name # ES6 native
|
|
150
|
+
arr?[0] # CoffeeScript soak
|
|
151
|
+
fn?(arg) # Soak call
|
|
654
152
|
|
|
655
|
-
#
|
|
656
|
-
|
|
657
|
-
# Ternary operators (x ? y : z) are unaffected.
|
|
658
|
-
# New code should use ?? explicitly for clarity.
|
|
659
|
-
|
|
660
|
-
# ============================================================================
|
|
661
|
-
# Sigil Operators - ! has dual meaning based on context
|
|
662
|
-
# ============================================================================
|
|
663
|
-
|
|
664
|
-
# 1. DAMMIT OPERATOR (!) - At Call-Site (Forces Await)
|
|
665
|
-
result = fetchData! # → await fetchData() (calls AND awaits)
|
|
666
|
-
user = getUser!(id) # → await getUser(id)
|
|
667
|
-
data = api.get! # → await api.get()
|
|
668
|
-
|
|
669
|
-
# The ! at call-site does TWO things:
|
|
670
|
-
# 1. Calls the function (even without parens)
|
|
671
|
-
# 2. Prepends await to the call
|
|
672
|
-
|
|
673
|
-
# 2. VOID OPERATOR (!) - At Definition-Site (Suppresses Returns)
|
|
674
|
-
def processItems! # Side-effect only (always returns undefined)
|
|
675
|
-
for item in items
|
|
676
|
-
item.update()
|
|
677
|
-
# ← Executes all statements, then returns undefined
|
|
153
|
+
# Nullish coalescing
|
|
154
|
+
port = config.port ?? 8080
|
|
678
155
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
156
|
+
# Async/await auto-detection
|
|
157
|
+
def fetchData
|
|
158
|
+
data = await fetch "/api/data"
|
|
159
|
+
data.json()
|
|
160
|
+
# → async function fetchData() { ... }
|
|
161
|
+
```
|
|
683
162
|
|
|
684
|
-
|
|
685
|
-
c! = (x) -> # Void thin arrow
|
|
686
|
-
x * 2 # Executes but doesn't return value
|
|
163
|
+
### Unique Features
|
|
687
164
|
|
|
688
|
-
|
|
689
|
-
|
|
165
|
+
```coffee
|
|
166
|
+
# Dammit operator! - Call and await
|
|
167
|
+
result = fetchData! # → await fetchData()
|
|
168
|
+
user = getUser!(id) # → await getUser(id)
|
|
690
169
|
|
|
691
|
-
#
|
|
692
|
-
#
|
|
693
|
-
|
|
694
|
-
#
|
|
695
|
-
# - Always returns undefined
|
|
170
|
+
# Void functions - No implicit returns
|
|
171
|
+
def process! # Always returns undefined
|
|
172
|
+
doWork()
|
|
173
|
+
# No return value
|
|
696
174
|
|
|
697
|
-
#
|
|
698
|
-
|
|
699
|
-
|
|
175
|
+
# Ruby-style regex
|
|
176
|
+
email =~ /(.+)@(.+)/ # Match with _ capture
|
|
177
|
+
username = _[1] # Extract first group
|
|
178
|
+
domain = email[/@(.+)/, 1] # Inline extraction
|
|
700
179
|
|
|
701
|
-
# Heregex - Extended
|
|
180
|
+
# Heregex - Extended regex with comments
|
|
702
181
|
pattern = ///
|
|
703
182
|
^ \d+ # starts with digits
|
|
704
183
|
\s* # optional whitespace
|
|
705
184
|
[a-z]+ # followed by letters
|
|
706
|
-
$
|
|
707
|
-
|
|
708
|
-
# Compiles to: /^\d+\s*[a-z]+$/i
|
|
709
|
-
# Whitespace and comments automatically stripped!
|
|
710
|
-
|
|
711
|
-
# Ruby-style regex (Rip innovation!)
|
|
712
|
-
email =~ /(.+)@(.+)/ # Match with automatic _ capture
|
|
713
|
-
username = _[1] # Extract captures easily
|
|
714
|
-
domain = _[2]
|
|
715
|
-
|
|
716
|
-
zip = "12345-6789"[/^(\d{5})/, 1] # Inline extraction: "12345"
|
|
185
|
+
$
|
|
186
|
+
///i
|
|
717
187
|
|
|
718
188
|
# __DATA__ marker (Ruby-inspired)
|
|
719
|
-
# Embed data directly in source files
|
|
720
189
|
config = parseConfig(DATA)
|
|
721
190
|
|
|
722
191
|
__DATA__
|
|
723
192
|
host=localhost
|
|
724
193
|
port=8080
|
|
725
|
-
debug=true
|
|
726
|
-
|
|
727
|
-
# Classes
|
|
728
|
-
class Animal
|
|
729
|
-
constructor: (@name) ->
|
|
730
|
-
|
|
731
|
-
speak: ->
|
|
732
|
-
"${@name} says hello"
|
|
733
|
-
|
|
734
|
-
# Comprehensions (Context-Aware Optimization!)
|
|
735
|
-
# Rip intelligently chooses IIFE (array building) vs plain loop (side effects)
|
|
736
|
-
|
|
737
|
-
# IIFE when result is USED (value context):
|
|
738
|
-
result = (x * 2 for x in items) # Assignment
|
|
739
|
-
console.log(x * 2 for x in items) # Function argument
|
|
740
|
-
fn = -> (x * 2 for x in items) # Last statement (implicit return)
|
|
741
|
-
(x * 2 for x in [1,2,3]) # Single statement (REPL mode)
|
|
742
|
-
|
|
743
|
-
# Plain loop when result is DISCARDED (statement context):
|
|
744
|
-
fn = ->
|
|
745
|
-
processItem x for x in items # NOT last statement
|
|
746
|
-
doSomething() # Result unused, no IIFE!
|
|
747
|
-
# → for (const x of items) { processItem(x); }
|
|
748
|
-
|
|
749
|
-
# One-liner and multi-line are IDENTICAL when result unused:
|
|
750
|
-
processItem x for x in items # Same as ↓
|
|
751
|
-
for x in items # Same as ↑
|
|
752
|
-
processItem x
|
|
753
|
-
# Both → for (const x of items) { processItem(x); }
|
|
754
|
-
|
|
755
|
-
# Critical: Proper ordering with guards and value variables
|
|
756
|
-
for own k, v of obj when v > 5
|
|
757
|
-
process k, v
|
|
758
|
-
# → for (const k in obj) {
|
|
759
|
-
# if (obj.hasOwnProperty(k)) {
|
|
760
|
-
# const v = obj[k]; // Assign BEFORE guard check!
|
|
761
|
-
# if (v > 5) { // Guard can reference v
|
|
762
|
-
# process(k, v);
|
|
763
|
-
# }
|
|
764
|
-
# }
|
|
765
|
-
# }
|
|
766
194
|
```
|
|
767
195
|
|
|
768
|
-
**Rip is smarter than CoffeeScript:** Comprehensions automatically optimize to plain loops when the result isn't used, avoiding unnecessary array building and IIFE overhead. CoffeeScript always generates IIFE for comprehension syntax, even when wasteful!
|
|
769
|
-
|
|
770
|
-
**See [docs/COMPREHENSIONS.md](docs/COMPREHENSIONS.md) for complete specification of context rules and edge cases.**
|
|
771
|
-
|
|
772
196
|
---
|
|
773
197
|
|
|
774
|
-
##
|
|
775
|
-
|
|
776
|
-
Rip provides **two distinct approaches** to safe property/method access:
|
|
198
|
+
## Why S-Expressions?
|
|
777
199
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
**Compiles to explicit null/undefined checks:**
|
|
781
|
-
|
|
782
|
-
```coffee
|
|
783
|
-
# Existence check
|
|
784
|
-
arr?
|
|
785
|
-
# → (arr != null)
|
|
200
|
+
Traditional compilers use complex AST classes. Rip uses **simple arrays**:
|
|
786
201
|
|
|
787
|
-
# Soak indexing
|
|
788
|
-
arr?[0]
|
|
789
|
-
# → (arr != null ? arr[0] : undefined)
|
|
790
|
-
|
|
791
|
-
# Soak call
|
|
792
|
-
fn?(arg)
|
|
793
|
-
# → (typeof fn === 'function' ? fn(arg) : undefined)
|
|
794
|
-
|
|
795
|
-
# Soak prototype
|
|
796
|
-
obj?::toString
|
|
797
|
-
# → (obj != null ? obj.prototype.toString : undefined)
|
|
798
|
-
|
|
799
|
-
# Existential assignment
|
|
800
|
-
a ?= 10
|
|
801
|
-
# → a ??= 10
|
|
802
202
|
```
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
- Works in all browsers (transpiles to standard checks)
|
|
806
|
-
- Clear, explicit null/undefined handling
|
|
807
|
-
- CoffeeScript compatible
|
|
808
|
-
- Type-aware function checks
|
|
809
|
-
|
|
810
|
-
### Dot-based `?.` - ES6 Optional Chaining (Native)
|
|
811
|
-
|
|
812
|
-
**Passes through to native JavaScript:**
|
|
813
|
-
|
|
814
|
-
```coffee
|
|
815
|
-
# Optional property
|
|
816
|
-
user?.profile?.name
|
|
817
|
-
# → user?.profile?.name
|
|
818
|
-
|
|
819
|
-
# Optional index
|
|
820
|
-
arr?.[0]
|
|
821
|
-
# → arr?.[0]
|
|
822
|
-
|
|
823
|
-
# Optional call
|
|
824
|
-
fn?.(arg)
|
|
825
|
-
# → fn?.(arg)
|
|
826
|
-
|
|
827
|
-
# Nullish coalescing
|
|
828
|
-
x ?? defaultValue
|
|
829
|
-
# → x ?? defaultValue
|
|
830
|
-
|
|
831
|
-
# Nullish assignment
|
|
832
|
-
a ??= 10
|
|
833
|
-
# → a ??= 10
|
|
834
|
-
```
|
|
835
|
-
|
|
836
|
-
**Benefits:**
|
|
837
|
-
- Modern, concise syntax
|
|
838
|
-
- Native browser optimization
|
|
839
|
-
- Short-circuit evaluation
|
|
840
|
-
- Standard JavaScript (ES2020+)
|
|
841
|
-
|
|
842
|
-
### Mix and Match
|
|
843
|
-
|
|
844
|
-
**You can combine both styles in the same expression:**
|
|
845
|
-
|
|
846
|
-
```coffee
|
|
847
|
-
# ES6 optional property + CoffeeScript soak index
|
|
848
|
-
obj?.arr?[0]
|
|
849
|
-
# → (obj?.arr != null ? obj?.arr[0] : undefined)
|
|
850
|
-
|
|
851
|
-
# CoffeeScript soak + ES6 optional
|
|
852
|
-
users?[0]?.name
|
|
853
|
-
# → (users != null ? users[0] : undefined)?.name
|
|
854
|
-
```
|
|
855
|
-
|
|
856
|
-
### When to Use Which
|
|
857
|
-
|
|
858
|
-
**Use `?` (CoffeeScript soak) when:**
|
|
859
|
-
- Need older browser support (transpiles to checks)
|
|
860
|
-
- Want function type checking (`fn?()` validates it's callable)
|
|
861
|
-
- Following CoffeeScript patterns
|
|
862
|
-
- Debugging (explicit checks are clearer)
|
|
863
|
-
|
|
864
|
-
**Use `?.` (ES6 optional) when:**
|
|
865
|
-
- Targeting modern browsers (ES2020+)
|
|
866
|
-
- Want clean, concise native output
|
|
867
|
-
- Using standard JavaScript patterns
|
|
868
|
-
- Performance matters (native is faster)
|
|
869
|
-
|
|
870
|
-
**Use `?=` vs `??=`:**
|
|
871
|
-
- Both compile to ES6 `??=` (nullish coalescing assignment)
|
|
872
|
-
- `?=` is CoffeeScript-style syntax
|
|
873
|
-
- `??=` is ES6-style syntax
|
|
874
|
-
- Choose based on your team's preferences
|
|
875
|
-
|
|
876
|
-
Both syntaxes handle `null` and `undefined` - pick the style that fits your project!
|
|
877
|
-
|
|
878
|
-
---
|
|
879
|
-
|
|
880
|
-
## Architecture
|
|
881
|
-
|
|
882
|
-
### The Pipeline
|
|
883
|
-
|
|
884
|
-
```
|
|
885
|
-
┌────────┐ ┌────────────┐ ┌──────────┐ ┌─────────┐
|
|
886
|
-
│ Source │───>│ Lexer │───>│ Parser │───>│ Codegen │
|
|
887
|
-
│ Code │ │ (Coffee) │ │ (Solar) │ │ (Rip) │
|
|
888
|
-
└────────┘ └────────────┘ └──────────┘ └─────────┘
|
|
889
|
-
3,145 LOC 928 LOC 5,221 LOC
|
|
890
|
-
15 yrs tested Generated! S-expr w/Dispatch!
|
|
203
|
+
Source → Tokens → S-Expressions → JavaScript
|
|
204
|
+
["=", "x", 42]
|
|
891
205
|
```
|
|
892
206
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
**1. Lexer** (`src/lexer.js`)
|
|
896
|
-
- CoffeeScript 2.7 production lexer
|
|
897
|
-
- Handles all tokenization
|
|
898
|
-
- 15 years of edge cases handled
|
|
899
|
-
- Enhanced with compatibility features
|
|
900
|
-
|
|
901
|
-
**2. Parser** (`src/parser.js`)
|
|
902
|
-
- Solar-generated SLR(1) parser
|
|
903
|
-
- Built from grammar specification
|
|
904
|
-
- Generates s-expressions directly
|
|
905
|
-
- Regenerate with `bun run parser` (~80ms, instant feedback!)
|
|
906
|
-
- **Note:** Solar's 156× speed advantage over Jison (80ms vs 12.5s) made rapid grammar iteration possible
|
|
907
|
-
|
|
908
|
-
**3. Code Generator** (`src/codegen.js`)
|
|
909
|
-
- Pattern matches on s-expressions
|
|
910
|
-
- Generates JavaScript code
|
|
911
|
-
- 110+ node types implemented
|
|
912
|
-
- Clean-room implementation
|
|
913
|
-
|
|
914
|
-
### Why This Works
|
|
915
|
-
|
|
916
|
-
**S-expressions simplify everything:**
|
|
917
|
-
|
|
207
|
+
**Traditional AST approach:**
|
|
918
208
|
```javascript
|
|
919
|
-
// Traditional AST approach
|
|
920
209
|
class BinaryOp {
|
|
921
210
|
constructor(op, left, right) { ... }
|
|
922
|
-
compile() { /*
|
|
211
|
+
compile() { /* 50+ lines */ }
|
|
923
212
|
}
|
|
213
|
+
```
|
|
924
214
|
|
|
925
|
-
|
|
215
|
+
**Rip's S-expression approach:**
|
|
216
|
+
```javascript
|
|
926
217
|
case '+': {
|
|
927
218
|
const [left, right] = rest;
|
|
928
219
|
return `(${this.generate(left)} + ${this.generate(right)})`;
|
|
929
220
|
}
|
|
930
221
|
```
|
|
931
222
|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
---
|
|
935
|
-
|
|
936
|
-
## Bun Integration
|
|
223
|
+
**Result: ~50% smaller compiler**
|
|
937
224
|
|
|
938
|
-
|
|
225
|
+
| Component | CoffeeScript | Rip |
|
|
226
|
+
|-----------|--------------|-----|
|
|
227
|
+
| Lexer+Rewriter | 3,558 LOC | **3,145 LOC** |
|
|
228
|
+
| Parser Generator | 2,285 LOC (Jison) | **928 LOC** (Solar, built-in) |
|
|
229
|
+
| Compiler | 10,346 LOC (AST) | **5,246 LOC** (S-expr) |
|
|
230
|
+
| **Total** | **17,760 LOC** | **9,839 LOC** |
|
|
939
231
|
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
#### Option 1: Use Globally (Recommended)
|
|
943
|
-
|
|
944
|
-
Set up Rip loader to work in **all** your projects:
|
|
232
|
+
---
|
|
945
233
|
|
|
946
|
-
|
|
947
|
-
# Install Rip globally
|
|
948
|
-
cd /path/to/rip-lang
|
|
949
|
-
bun link
|
|
234
|
+
## Zero Dependencies
|
|
950
235
|
|
|
951
|
-
|
|
952
|
-
echo 'preload = ["rip-lang/loader"]' >> ~/.bunfig.toml
|
|
236
|
+
**Rip is completely standalone** - no runtime or build dependencies:
|
|
953
237
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"dependencies": {} // ← Empty!
|
|
241
|
+
}
|
|
957
242
|
```
|
|
958
243
|
|
|
959
|
-
|
|
244
|
+
**What's included:**
|
|
245
|
+
- ✅ Full compiler (lexer, parser, codegen)
|
|
246
|
+
- ✅ Parser generator (solar.rip - SLR(1))
|
|
247
|
+
- ✅ Triple REPL (terminal, browser, console)
|
|
248
|
+
- ✅ Browser bundle (43KB compressed)
|
|
249
|
+
- ✅ Test framework
|
|
960
250
|
|
|
251
|
+
**Self-hosting verification:**
|
|
961
252
|
```bash
|
|
962
|
-
#
|
|
963
|
-
|
|
253
|
+
bun run parser # Rebuilds parser from scratch (solar.rip + grammar.rip)
|
|
254
|
+
# Complete bootstrap loop - ZERO external tools! ✅
|
|
255
|
+
```
|
|
964
256
|
|
|
965
|
-
|
|
966
|
-
echo 'preload = ["rip-lang/loader"]' > bunfig.toml
|
|
257
|
+
---
|
|
967
258
|
|
|
968
|
-
|
|
969
|
-
bun your-script.rip
|
|
970
|
-
```
|
|
259
|
+
## Browser Support
|
|
971
260
|
|
|
972
|
-
|
|
261
|
+
**Run Rip in the browser!** Try it live: **[https://shreeve.github.io/rip-lang/](https://shreeve.github.io/rip-lang/)**
|
|
973
262
|
|
|
974
263
|
```bash
|
|
975
|
-
#
|
|
976
|
-
|
|
977
|
-
cp /path/to/rip-lang/rip-loader.ts .
|
|
978
|
-
cp -r /path/to/rip-lang/src .
|
|
264
|
+
# Build browser bundle
|
|
265
|
+
bun run browser
|
|
979
266
|
|
|
980
|
-
#
|
|
981
|
-
bun
|
|
267
|
+
# Start dev server
|
|
268
|
+
bun run serve # → http://localhost:3000
|
|
982
269
|
```
|
|
983
270
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
271
|
+
**Features:**
|
|
272
|
+
- 43KB bundle (brotli-compressed)
|
|
273
|
+
- Interactive REPL console
|
|
274
|
+
- Live compiler with syntax highlighting
|
|
275
|
+
- Inline `<script type="text/rip">` support
|
|
987
276
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
277
|
+
```html
|
|
278
|
+
<script src="https://cdn.example.com/rip.browser.min.js"></script>
|
|
279
|
+
<script type="text/rip">
|
|
280
|
+
def greet(name)
|
|
281
|
+
console.log "Hello, ${name}!"
|
|
282
|
+
greet "World"
|
|
283
|
+
</script>
|
|
993
284
|
```
|
|
994
285
|
|
|
995
|
-
|
|
996
|
-
```typescript
|
|
997
|
-
import { plugin } from "bun";
|
|
998
|
-
import { compileToJS } from "./src/compiler.js";
|
|
999
|
-
|
|
1000
|
-
await plugin({
|
|
1001
|
-
name: "rip-loader",
|
|
1002
|
-
async setup(build) {
|
|
1003
|
-
build.onLoad({ filter: /\.rip$/ }, async (args) => {
|
|
1004
|
-
const source = readFileSync(args.path, "utf-8");
|
|
1005
|
-
const js = compileToJS(source);
|
|
1006
|
-
return { contents: js, loader: "js" };
|
|
1007
|
-
});
|
|
1008
|
-
},
|
|
1009
|
-
});
|
|
1010
|
-
```
|
|
286
|
+
See [docs/BROWSER.md](docs/BROWSER.md) for details.
|
|
1011
287
|
|
|
1012
|
-
|
|
288
|
+
---
|
|
1013
289
|
|
|
1014
|
-
|
|
1015
|
-
- ✅ **Module imports** - `import { fn } from "./utils.rip"`
|
|
1016
|
-
- ✅ **Hot reloading** - Changes compile automatically
|
|
1017
|
-
- ✅ **Full ES6 modules** - Named exports, default exports, re-exports
|
|
1018
|
-
- ✅ **Error messages** - Compilation errors show source file and line
|
|
1019
|
-
- ✅ **Zero config** - Once set up globally, works everywhere
|
|
290
|
+
## Runtime Compatibility
|
|
1020
291
|
|
|
1021
|
-
|
|
292
|
+
**Primary targets:**
|
|
293
|
+
- 🎯 **Bun** - First-class support with automatic `.rip` loader
|
|
294
|
+
- 🌐 **Browsers** - 43KB bundle with REPL
|
|
1022
295
|
|
|
1023
|
-
|
|
296
|
+
**Also supported:**
|
|
297
|
+
- ✅ **Deno** - ES2022 output works natively
|
|
298
|
+
- ✅ **Node.js 12+** - Full compatibility
|
|
1024
299
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
300
|
+
**ES2022 features used:**
|
|
301
|
+
- ES2015: classes, let/const, arrow functions, template literals, destructuring
|
|
302
|
+
- ES2018: async iteration (for await...of)
|
|
303
|
+
- ES2020: optional chaining (`?.`), nullish coalescing (`??`)
|
|
304
|
+
- ES2022: static class fields, top-level await
|
|
1028
305
|
|
|
1029
|
-
|
|
1030
|
-
deno run script.js
|
|
306
|
+
---
|
|
1031
307
|
|
|
1032
|
-
|
|
1033
|
-
./bin/rip -o utils.js utils.rip
|
|
1034
|
-
deno run --allow-read main.js # imports from utils.js
|
|
1035
|
-
```
|
|
308
|
+
## Documentation
|
|
1036
309
|
|
|
1037
|
-
|
|
310
|
+
**For users:**
|
|
311
|
+
- [README.md](README.md) - This file (overview and quick start)
|
|
312
|
+
- [docs/examples/](docs/examples/) - Example programs
|
|
313
|
+
- [CHANGELOG.md](CHANGELOG.md) - Version history
|
|
1038
314
|
|
|
1039
|
-
|
|
315
|
+
**Technical references:**
|
|
316
|
+
- [AGENT.md](AGENT.md) - **Complete developer/AI agent guide** (start here!)
|
|
317
|
+
- [docs/CODEGEN.md](docs/CODEGEN.md) - All 110+ node types
|
|
318
|
+
- [docs/COMPREHENSIONS.md](docs/COMPREHENSIONS.md) - Context-aware comprehension rules
|
|
319
|
+
- [docs/SOLAR.md](docs/SOLAR.md) - Parser generator guide
|
|
320
|
+
- [docs/STRING.md](docs/STRING.md) - String metadata reference
|
|
321
|
+
- [docs/REGEX-PLUS.md](docs/REGEX-PLUS.md) - Ruby-style regex features
|
|
322
|
+
- [docs/BROWSER.md](docs/BROWSER.md) - Browser usage & REPLs
|
|
1040
323
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
324
|
+
**For contributors:**
|
|
325
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md) - Development workflow
|
|
326
|
+
- [docs/WORKFLOW.md](docs/WORKFLOW.md) - Command reference
|
|
1044
327
|
|
|
1045
|
-
|
|
1046
|
-
node script.js
|
|
328
|
+
---
|
|
1047
329
|
|
|
1048
|
-
|
|
1049
|
-
{
|
|
1050
|
-
"scripts": {
|
|
1051
|
-
"build": "./bin/rip -o dist/app.js src/app.rip",
|
|
1052
|
-
"start": "node dist/app.js"
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
```
|
|
330
|
+
## Status
|
|
1056
331
|
|
|
1057
|
-
**
|
|
332
|
+
**Version:** 1.4.3 - **PRODUCTION READY** 🎉
|
|
1058
333
|
|
|
1059
|
-
|
|
334
|
+
**Test results:**
|
|
335
|
+
- ✅ 938/938 tests passing (100%)
|
|
336
|
+
- ✅ Self-hosting operational
|
|
337
|
+
- ✅ All 110 node types implemented
|
|
338
|
+
- ✅ Browser bundle working
|
|
1060
339
|
|
|
1061
|
-
**
|
|
340
|
+
**Recent accomplishments (Nov 2025):**
|
|
341
|
+
- ✅ Dispatch table refactoring - All 110 operations use O(1) lookup
|
|
342
|
+
- ✅ Code cleanup - Removed 2,017 lines of dead code (28% reduction)
|
|
343
|
+
- ✅ Self-hosting restored - Fixed 'in' operator edge case
|
|
344
|
+
- ✅ S-expression approach - IR-level transforms, not string manipulation
|
|
1062
345
|
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
346
|
+
**Roadmap:**
|
|
347
|
+
- ✅ v1.0.0 - Initial release
|
|
348
|
+
- ✅ v1.4.3 - **CURRENT** - Production-ready, self-hosting
|
|
349
|
+
- 🔜 Continuous refinement based on community feedback
|
|
1067
350
|
|
|
1068
|
-
|
|
1069
|
-
cat ~/.bunfig.toml
|
|
1070
|
-
# Should include: preload = ["rip-lang/loader"]
|
|
351
|
+
See [CHANGELOG.md](CHANGELOG.md) for detailed history.
|
|
1071
352
|
|
|
1072
|
-
|
|
1073
|
-
cd /path/to/rip-lang
|
|
1074
|
-
bun link
|
|
353
|
+
---
|
|
1075
354
|
|
|
1076
|
-
|
|
1077
|
-
cd /path/to/rip-lang
|
|
1078
|
-
bun www/examples/fibonacci.rip
|
|
1079
|
-
# This should always work (uses local bunfig.toml)
|
|
1080
|
-
```
|
|
355
|
+
## Development
|
|
1081
356
|
|
|
1082
|
-
|
|
357
|
+
### Running Tests
|
|
1083
358
|
|
|
1084
359
|
```bash
|
|
1085
|
-
#
|
|
1086
|
-
|
|
1087
|
-
import { fn } from "./utils" # ❌ Won't work
|
|
1088
|
-
```
|
|
1089
|
-
|
|
1090
|
-
**Problem: "Cannot find package rip-lang"**
|
|
360
|
+
# All tests
|
|
361
|
+
bun run test
|
|
1091
362
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
cd /path/to/rip-lang
|
|
1095
|
-
bun link
|
|
363
|
+
# Specific file
|
|
364
|
+
bun test/runner.js test/rip/functions.rip
|
|
1096
365
|
|
|
1097
|
-
#
|
|
1098
|
-
bun
|
|
366
|
+
# Clear cache
|
|
367
|
+
bun --no-cache test/runner.js test/rip
|
|
1099
368
|
```
|
|
1100
369
|
|
|
1101
|
-
|
|
370
|
+
### Build Commands
|
|
1102
371
|
|
|
1103
|
-
|
|
372
|
+
```bash
|
|
373
|
+
bun run parser # Rebuild parser from grammar (self-hosting!)
|
|
374
|
+
bun run browser # Build 43KB browser bundle
|
|
375
|
+
bun run serve # Start dev server (REPL at localhost:3000)
|
|
376
|
+
```
|
|
1104
377
|
|
|
1105
378
|
### Project Structure
|
|
1106
379
|
|
|
1107
380
|
```
|
|
1108
381
|
rip/
|
|
1109
382
|
├── src/
|
|
1110
|
-
│ ├── lexer.js # CoffeeScript lexer (
|
|
383
|
+
│ ├── lexer.js # CoffeeScript lexer (adapted)
|
|
1111
384
|
│ ├── parser.js # Solar parser (generated)
|
|
1112
|
-
│ ├── codegen.js # Code generator (
|
|
1113
|
-
│ ├── compiler.js #
|
|
1114
|
-
│ ├── browser.js # Browser entry point
|
|
1115
|
-
│ ├── repl.js # Terminal REPL
|
|
385
|
+
│ ├── codegen.js # Code generator (S-expression dispatch)
|
|
386
|
+
│ ├── compiler.js # Pipeline orchestration
|
|
1116
387
|
│ └── grammar/
|
|
1117
388
|
│ ├── grammar.rip # Grammar specification
|
|
1118
389
|
│ └── solar.rip # Parser generator
|
|
1119
|
-
├── docs/ #
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
│ ├── SOLAR.md # Parser generator guide
|
|
1123
|
-
│ ├── STRING.md # String metadata reference
|
|
1124
|
-
│ ├── REGEX-PLUS.md # Ruby-style regex features
|
|
1125
|
-
│ ├── DAMMIT-OPERATOR.md # Async shorthand
|
|
1126
|
-
│ ├── BROWSER.md # Browser usage & REPLs
|
|
1127
|
-
│ └── COFFEESCRIPT-COMPARISON.md # Feature comparison
|
|
1128
|
-
├── test/
|
|
1129
|
-
│ ├── rip/ # Feature tests (20 files, 864 tests)
|
|
1130
|
-
│ └── runner.js # Test runner
|
|
1131
|
-
├── www/ # Browser bundles and demos
|
|
1132
|
-
├── examples/ # Example programs
|
|
1133
|
-
├── AGENT.md # AI agent handbook
|
|
390
|
+
├── docs/ # Complete documentation
|
|
391
|
+
├── test/rip/ # 23 test files, 938 tests
|
|
392
|
+
├── AGENT.md # Complete developer guide
|
|
1134
393
|
└── README.md # This file
|
|
1135
394
|
```
|
|
1136
395
|
|
|
1137
|
-
###
|
|
1138
|
-
|
|
1139
|
-
1. **Check parser output:**
|
|
1140
|
-
```bash
|
|
1141
|
-
echo 'your code' | ./bin/rip -s
|
|
1142
|
-
```
|
|
1143
|
-
|
|
1144
|
-
2. **Add to codegen:**
|
|
1145
|
-
```javascript
|
|
1146
|
-
case 'your-pattern': {
|
|
1147
|
-
// Generate code
|
|
1148
|
-
}
|
|
1149
|
-
```
|
|
1150
|
-
|
|
1151
|
-
3. **Write tests:**
|
|
1152
|
-
```coffee
|
|
1153
|
-
test "feature name", "code", expectedResult
|
|
1154
|
-
```
|
|
1155
|
-
|
|
1156
|
-
4. **Run tests:**
|
|
1157
|
-
```bash
|
|
1158
|
-
bun test/runner.js test/rip/your-test.rip
|
|
1159
|
-
```
|
|
396
|
+
### Contributing
|
|
1160
397
|
|
|
1161
|
-
|
|
398
|
+
1. Read [AGENT.md](AGENT.md) - Complete guide for developers and AI agents
|
|
399
|
+
2. Check [CONTRIBUTING.md](CONTRIBUTING.md) - Workflow with examples
|
|
400
|
+
3. Write tests first (test-driven development)
|
|
401
|
+
4. Run `bun run test` before committing
|
|
402
|
+
5. Follow existing patterns in [docs/CODEGEN.md](docs/CODEGEN.md)
|
|
1162
403
|
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
> Keep the pipeline clear (lex → parse → generate)
|
|
1168
|
-
> Keep the code minimal (pattern matching)
|
|
1169
|
-
|
|
1170
|
-
### Design Decisions
|
|
1171
|
-
|
|
1172
|
-
**Arrow Functions: Parentheses Always Required**
|
|
404
|
+
**Quick workflow:**
|
|
405
|
+
```bash
|
|
406
|
+
# 1. Check what parser emits
|
|
407
|
+
echo 'your code' | ./bin/rip -s
|
|
1173
408
|
|
|
1174
|
-
|
|
409
|
+
# 2. Implement in src/codegen.js (check dispatch table)
|
|
410
|
+
# 3. Write tests in test/rip/
|
|
1175
411
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
() => expr # Zero params
|
|
1179
|
-
(x) => expr # One param
|
|
1180
|
-
(x, y) => expr # Multiple params
|
|
412
|
+
# 4. Run tests
|
|
413
|
+
bun test/runner.js test/rip/your-test.rip
|
|
1181
414
|
|
|
1182
|
-
#
|
|
1183
|
-
|
|
415
|
+
# 5. Verify all tests pass
|
|
416
|
+
bun run test
|
|
1184
417
|
```
|
|
1185
418
|
|
|
1186
|
-
**Why?**
|
|
1187
|
-
- **Consistency:** One simple rule - no special cases
|
|
1188
|
-
- **Clarity:** Parameter lists are always obvious
|
|
1189
|
-
- **Simplicity:** Less cognitive overhead for developers
|
|
1190
|
-
- **Maintainability:** Simpler compiler implementation
|
|
1191
|
-
|
|
1192
|
-
We considered allowing `x => expr` (ES6 style) but decided consistency and simplicity were more valuable than saving 2 characters.
|
|
1193
|
-
|
|
1194
419
|
---
|
|
1195
420
|
|
|
1196
|
-
## Comparison
|
|
1197
|
-
|
|
1198
|
-
### vs CoffeeScript
|
|
421
|
+
## Comparison to CoffeeScript
|
|
1199
422
|
|
|
1200
423
|
| Feature | CoffeeScript | Rip |
|
|
1201
424
|
|---------|-------------|------|
|
|
1202
|
-
|
|
|
1203
|
-
|
|
|
1204
|
-
|
|
|
1205
|
-
|
|
|
1206
|
-
|
|
|
1207
|
-
| Modules | CommonJS |
|
|
1208
|
-
|
|
|
1209
|
-
| Maintenance | Complex AST | ✅ Simple sexps |
|
|
1210
|
-
| Extensibility | Hard | ✅ Easy (add a case) |
|
|
1211
|
-
|
|
1212
|
-
### Real-World Output Comparison
|
|
1213
|
-
|
|
1214
|
-
**The numbers don't lie.** When compiling a typical 400-line CoffeeScript file with classes, nested switches, comprehensions, and complex loops:
|
|
1215
|
-
|
|
1216
|
-
| Metric | CoffeeScript Output | Rip Output | Improvement |
|
|
1217
|
-
|--------|-------------------|-----------|-------------|
|
|
1218
|
-
| **Lines of code** | 608 lines | **304 lines** | **50% smaller!** |
|
|
1219
|
-
| **Syntax style** | ES5 (var, prototypes) | **ES2022 (let, classes)** | Modern |
|
|
1220
|
-
| **Readability** | Verbose, intermediate vars | **Clean, direct** | Better |
|
|
1221
|
-
| **Nested loops** | Complex iteration vars | **Plain, efficient loops** | Cleaner |
|
|
1222
|
-
|
|
1223
|
-
**Example: Loop with step** (the pattern that started this journey)
|
|
1224
|
-
|
|
1225
|
-
```coffeescript
|
|
1226
|
-
# Source: CoffeeScript/Rip
|
|
1227
|
-
@data(obj[i], obj[i+1]) for i in [0...obj.length] by 2
|
|
1228
|
-
```
|
|
425
|
+
| **Implementation** | 17,760 LOC | **9,839 LOC (~50% smaller)** |
|
|
426
|
+
| **Dependencies** | Multiple | **ZERO** |
|
|
427
|
+
| **Parser Generator** | External (Jison) | **Built-in (solar.rip)** |
|
|
428
|
+
| **Self-Hosting** | No | **Yes** |
|
|
429
|
+
| **Output** | ES5 (var, prototypes) | **ES2022 (let, classes)** |
|
|
430
|
+
| **Modules** | CommonJS | **ES6** |
|
|
431
|
+
| **Maintenance** | Complex AST | **Simple S-expressions** |
|
|
1229
432
|
|
|
1230
|
-
**
|
|
1231
|
-
```javascript
|
|
1232
|
-
for (i = k = 0, ref = obj.length; k < ref; i = k += 2) {
|
|
1233
|
-
this.data(obj[i], obj[i + 1]);
|
|
1234
|
-
}
|
|
1235
|
-
// Uses intermediate variable 'k', ref variable, complex initialization
|
|
1236
|
-
```
|
|
1237
|
-
|
|
1238
|
-
**Rip output** (clean):
|
|
1239
|
-
```javascript
|
|
1240
|
-
for (let i = 0; i < obj.length; i += 2) {
|
|
1241
|
-
this.data(obj[i], obj[(i + 1)]);
|
|
1242
|
-
}
|
|
1243
|
-
// Modern let, direct loop, readable
|
|
1244
|
-
```
|
|
1245
|
-
|
|
1246
|
-
**Both are functionally equivalent** - Rip just generates cleaner, more maintainable code! ✅
|
|
1247
|
-
|
|
1248
|
-
**More comparisons:**
|
|
1249
|
-
|
|
1250
|
-
| Pattern | CoffeeScript Style | Rip Style |
|
|
1251
|
-
|---------|-------------------|-----------|
|
|
1252
|
-
| **Variables** | `var x, y, z;` (function-scoped) | `let x, y, z;` (block-scoped) |
|
|
1253
|
-
| **Classes** | `Bar.prototype.method = function() {...}` | `class Bar { method() {...} }` |
|
|
1254
|
-
| **Null checks** | `ref = obj.prop; ref != null ? ref : void 0` | `obj?.prop` (native) |
|
|
1255
|
-
| **Loops** | Intermediate loop variables | Direct modern loops |
|
|
1256
|
-
| **Arrays** | `[].splice.call(array)` | Native `array.splice()` |
|
|
1257
|
-
|
|
1258
|
-
**Why Rip's output is better:**
|
|
1259
|
-
|
|
1260
|
-
- ✅ **Modern syntax** - ES2022 features work in all current runtimes
|
|
1261
|
-
- ✅ **Smaller bundles** - 50% less code to download/parse
|
|
1262
|
-
- ✅ **More readable** - Easier to debug generated code
|
|
1263
|
-
- ✅ **Better performance** - Engines optimize modern syntax better
|
|
1264
|
-
- ✅ **Future-proof** - Uses current JavaScript standards
|
|
433
|
+
**Real-world output comparison:**
|
|
1265
434
|
|
|
1266
|
-
|
|
435
|
+
Compiling a 400-line CoffeeScript file with classes, nested switches, and loops:
|
|
1267
436
|
|
|
1268
|
-
|
|
437
|
+
| Metric | CoffeeScript | Rip |
|
|
438
|
+
|--------|--------------|-----|
|
|
439
|
+
| Lines of code | 608 lines | **304 lines (50% smaller)** |
|
|
440
|
+
| Syntax | ES5 (var, prototypes) | **ES2022 (let, classes)** |
|
|
441
|
+
| Readability | Verbose with intermediate vars | **Clean and direct** |
|
|
1269
442
|
|
|
1270
|
-
**
|
|
1271
|
-
**Rip:** Simplicity, elegance, easy to extend
|
|
1272
|
-
|
|
1273
|
-
**Use TypeScript when:** You need types, big team, enterprise
|
|
1274
|
-
**Use Rip when:** You value elegance, simple tooling, small projects
|
|
1275
|
-
|
|
1276
|
-
---
|
|
1277
|
-
|
|
1278
|
-
## Current Status
|
|
1279
|
-
|
|
1280
|
-
**Version:** 1.1.0 - **ENHANCED RELEASE!** 🎉
|
|
1281
|
-
|
|
1282
|
-
**⚡ NEW: DUAL SYNTAX SUPPORT - CoffeeScript Compatibility!**
|
|
1283
|
-
|
|
1284
|
-
Now supports both ES6 and CoffeeScript syntax:
|
|
1285
|
-
- ✅ **Postfix spread/rest:** `args...` or `...args` (both work!)
|
|
1286
|
-
- ✅ **Legacy existential:** `x ? y` or `x ?? y` (both work!)
|
|
1287
|
-
- ✅ **Dual optional:** `x?.y` and `x?[y]` (both work!)
|
|
1288
|
-
|
|
1289
|
-
**Implemented:**
|
|
1290
|
-
- ✅ Core architecture (lexer + parser + codegen pipeline)
|
|
1291
|
-
- ✅ Test infrastructure (test/code/fail helpers, async support)
|
|
1292
|
-
- ✅ **Code generator - 100% COMPLETE!** (110+ node types, all features working)
|
|
1293
|
-
- ✅ **864/864 tests passing (100%)** - PERFECT SCORE!
|
|
1294
|
-
- ✅ **Dual syntax support** - ES6 + CoffeeScript compatibility
|
|
1295
|
-
- ✅ **Dammit operator (`!`)** - `fetchData!` → `await fetchData()`
|
|
1296
|
-
- ✅ Comprehensive documentation (production ready)
|
|
1297
|
-
- ✅ **Self-hosting** - Rip compiles itself!
|
|
1298
|
-
|
|
1299
|
-
**Test Results:**
|
|
1300
|
-
- **Rip tests: 864/864 (100%)** ✅ **PERFECT SCORE!**
|
|
1301
|
-
- **Compatibility: 45 tests** (postfix spread/rest + legacy existential)
|
|
1302
|
-
- **Guards: 27 tests** (when clauses + own keyword)
|
|
1303
|
-
- **Stabilization: 67 tests** (advanced patterns + 13 bootstrap bug tests)
|
|
1304
|
-
- **Functions: 71 tests** (including 10 void function tests)
|
|
1305
|
-
- **Total: 864 tests passing** (100% - every test passes!)
|
|
1306
|
-
- All test files organized (20 files, alphabetically sorted)
|
|
1307
|
-
- Zero redundant tests
|
|
1308
|
-
- **All node types implemented with test coverage** (100% of grammar!)
|
|
1309
|
-
- **New:** Dammit operator (call and await at call-site)
|
|
1310
|
-
- **New:** Void functions (side-effect only with ! at definition)
|
|
1311
|
-
- **New:** Smart comprehension optimization (context-aware)
|
|
1312
|
-
- **New:** Range loop optimization (73% smaller code)
|
|
1313
|
-
- **New:** Reverse iteration support (by -1)
|
|
1314
|
-
- **New:** Self-hosting complete (Rip compiles itself!)
|
|
1315
|
-
|
|
1316
|
-
**Key Features:**
|
|
1317
|
-
- ✅ **ZERO Dependencies** - Completely standalone, self-hosting compiler
|
|
1318
|
-
- ✅ **Parser Generator Included** - Full SLR(1) parser generator (solar.rip) built-in
|
|
1319
|
-
- ✅ **Triple REPL Support** - Terminal (`./bin/rip`), Browser (repl.html), Console (`rip()`)
|
|
1320
|
-
- ✅ **Browser Bundle** - 43KB brotli-compressed (560KB → 43KB, 92% reduction!)
|
|
1321
|
-
- ✅ **Dammit Operator (`!`)** - Call and await shorthand: `fetchData!` → `await fetchData()`
|
|
1322
|
-
- ✅ **Void Functions (`!`)** - Side-effect only: `def process!` → no implicit returns
|
|
1323
|
-
- ✅ **Heregex** - Extended regex with comments/whitespace (`///...///`)
|
|
1324
|
-
- ✅ **Ruby-style Regex** - `=~` operator and `x[/pattern/, n]` indexing
|
|
1325
|
-
- ✅ **__DATA__ Marker** - Ruby-inspired inline data sections
|
|
1326
|
-
- ✅ **toSearchable()** - Universal type coercion utility with security
|
|
1327
|
-
- ✅ Dual optional syntax (CoffeeScript soak + ES6 optional chaining) - 10 operators!
|
|
1328
|
-
- ✅ Complete ES6 modules with smart defaults
|
|
1329
|
-
- ✅ Async/await auto-detection (including for-await)
|
|
1330
|
-
- ✅ Generator auto-detection
|
|
1331
|
-
- ✅ Tagged template literals
|
|
1332
|
-
- ✅ Catch destructuring
|
|
1333
|
-
- ✅ Expansion markers
|
|
1334
|
-
- ✅ Heredoc strings (triple-quoted with dedenting)
|
|
1335
|
-
- ✅ Quote preservation
|
|
1336
|
-
- ✅ Per-function variable scoping
|
|
1337
|
-
- ✅ And 75+ more features!
|
|
1338
|
-
|
|
1339
|
-
**Roadmap:**
|
|
1340
|
-
- ✅ v0.1.0: **COMPLETE** - All 63 node types, 82% tests
|
|
1341
|
-
- ✅ v0.2.0: **COMPLETE** - 100% Rip coverage, 79% CS2
|
|
1342
|
-
- ✅ v0.3.0: **COMPLETE** - 90%+ CS2 compatibility, ES2022 target!
|
|
1343
|
-
- ✅ v0.3.3: **COMPLETE** - CS2 migration complete, 666 comprehensive tests, perfect organization!
|
|
1344
|
-
- ✅ v0.3.4: **COMPLETE** - Dammit operator (`!`), call-and-await shorthand!
|
|
1345
|
-
- ✅ v0.5.0: **COMPLETE** - Dual syntax support! Postfix spread/rest (`x...`) + legacy existential (`x ? y`)!
|
|
1346
|
-
- ✅ v0.5.1: **COMPLETE** - Smart comprehensions! Context-aware optimization, self-hosting, 843/843 tests!
|
|
1347
|
-
- ✅ v0.9.0: **COMPLETE** - Production release! Zero dependencies, complete documentation, 43KB browser bundle!
|
|
1348
|
-
- ✅ v1.0.0: **RELEASED** - Initial release! ~45% smaller than CoffeeScript! 🎉
|
|
1349
|
-
|
|
1350
|
-
See [CHANGELOG.md](CHANGELOG.md) for detailed progress.
|
|
443
|
+
**Both are functionally equivalent** - Rip just generates cleaner code!
|
|
1351
444
|
|
|
1352
445
|
---
|
|
1353
446
|
|
|
1354
|
-
##
|
|
447
|
+
## Why Rip?
|
|
1355
448
|
|
|
1356
|
-
|
|
1357
|
-
-
|
|
1358
|
-
-
|
|
1359
|
-
-
|
|
1360
|
-
-
|
|
449
|
+
**For users:**
|
|
450
|
+
- ✅ Elegant syntax without verbosity
|
|
451
|
+
- ✅ Modern JavaScript output
|
|
452
|
+
- ✅ Zero build tool complexity
|
|
453
|
+
- ✅ Browser support included
|
|
1361
454
|
|
|
1362
|
-
|
|
455
|
+
**For developers:**
|
|
456
|
+
- ✅ Simple architecture (S-expressions)
|
|
457
|
+
- ✅ Easy to extend (add a case!)
|
|
458
|
+
- ✅ Well-tested (938 tests)
|
|
459
|
+
- ✅ Well-documented (see AGENT.md)
|
|
1363
460
|
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
3. Implement in `src/codegen.js`
|
|
1367
|
-
4. Write tests in `test/rip/`
|
|
1368
|
-
5. Document in `docs/CODEGEN.md`
|
|
1369
|
-
6. Commit!
|
|
461
|
+
**Philosophy:**
|
|
462
|
+
> Simplicity scales. Keep the IR simple (s-expressions), keep the pipeline clear (lex → parse → generate), keep the code minimal (pattern matching).
|
|
1370
463
|
|
|
1371
464
|
---
|
|
1372
465
|
|
|
@@ -1376,19 +469,13 @@ MIT
|
|
|
1376
469
|
|
|
1377
470
|
---
|
|
1378
471
|
|
|
1379
|
-
## Note 🤖
|
|
1380
|
-
|
|
1381
|
-
*Some of the promotional language in this README was generated with AI assistance and may be... "enthusiastic". The code, tests, and technical claims are real and verifiable. If anything sounds too good to be true, please check the actual implementation—it's all there in the source. This is a practical tool, not a crusade. :)*
|
|
1382
|
-
|
|
1383
|
-
---
|
|
1384
|
-
|
|
1385
472
|
## Credits
|
|
1386
473
|
|
|
1387
474
|
**Inspired by:**
|
|
1388
|
-
- CoffeeScript
|
|
1389
|
-
- Lisp/Scheme
|
|
1390
|
-
- Solar
|
|
1391
|
-
- Ruby
|
|
475
|
+
- **CoffeeScript** - Syntax and lexer foundation
|
|
476
|
+
- **Lisp/Scheme** - S-expression approach
|
|
477
|
+
- **Solar** - Lightning-fast parser generator (80ms vs Jison's 12.5s!)
|
|
478
|
+
- **Ruby** - Regex operators, __DATA__ marker
|
|
1392
479
|
|
|
1393
480
|
**Built by:** Developers who believe simplicity scales
|
|
1394
481
|
|