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/README.md CHANGED
@@ -5,1368 +5,461 @@
5
5
  <h1 align="center">Rip</h1>
6
6
 
7
7
  <p align="center">
8
- <strong>Elegant scripting language → Modern JavaScript (ES2022)</strong>
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.0-blue.svg" alt="Version"></a>
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
- ## Why Rip?
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
- ### ES2022 Target
22
+ A clean-room **CoffeeScript-inspired compiler** that produces modern JavaScript (ES2022). Built from scratch with an elegant S-expression architecture.
103
23
 
104
- Rip compiles to **modern JavaScript (ES2022)** for clean, efficient output:
105
-
106
- **Features Used:**
107
- - **ES2015 (ES6):** classes, let/const, arrow functions, template literals, destructuring
108
- - **ES2018:** async iteration (for await...of)
109
- - ✅ **ES2020:** optional chaining (`?.`), nullish coalescing (`??`)
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
- ## Zero Dependencies
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
- # Rip code - edit me!
208
- def fibonacci(n)
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
- console.log "Fib(10):", fibonacci(10)
227
- console.log "Domain:", domain
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
- **Step 2: S-Expression Intermediate Representation**
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
- **Key insights:**
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 _, domain, email, pattern;
50
+ let _;
260
51
 
261
- function fibonacci(n) {
262
- return ((n <= 1) ? n : (fibonacci((n - 1)) + fibonacci((n - 2))));
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
- pattern = /^\d+\s*[a-z]+$/i;
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
- ## Quick Start
285
-
286
- ### Installation & Setup
62
+ ## Installation
287
63
 
288
- **🚀 Fastest Way: Install from Bun Package Registry**
289
-
290
- First, install Bun if you haven't already:
64
+ ### Option 1: Install Globally (Recommended)
291
65
 
292
66
  ```bash
293
- # Install Bun (macOS, Linux, WSL)
67
+ # Install Bun if needed
294
68
  curl -fsSL https://bun.sh/install | bash
295
69
 
296
- # Or on Windows (PowerShell)
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 the REPL immediately!
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
- That's it! You can now:
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
- **Step 2: Set up global Bun loader**
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
- **That's it!** Now you can run `.rip` files from anywhere:
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
- ```coffee
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
- **Other commands:**
97
+ ## Quick Start
410
98
 
411
99
  ```bash
412
100
  # Interactive REPL
413
101
  ./bin/rip
414
102
 
415
- # Execute a Rip script (default behavior)
103
+ # Execute a file
416
104
  ./bin/rip examples/fibonacci.rip
417
105
 
418
- # Compile and show JavaScript output
106
+ # Compile to JavaScript
419
107
  ./bin/rip -c examples/fibonacci.rip
420
108
 
421
- # Compile and save to file
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
- ```bash
557
- bun run test # Run all 864 tests
558
- bun run parser # Rebuild parser from grammar (self-hosting!)
559
- bun run browser # Build browser bundles (43KB compressed)
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
- ## Language Features
120
+ ## Key Features
566
121
 
567
- ### Core Syntax
122
+ ### Elegant Syntax
568
123
 
569
124
  ```coffee
570
- # Variables (function-scoped, auto-hoisted)
571
- x = 42
572
- name = "Alice"
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
- # Functions (three styles - each has distinct behavior)
578
- def add(a, b) # Named, unbound this, hoisted
129
+ calculate = (a, b) -> # Thin arrow (unbound this)
579
130
  a + b
580
131
 
581
- multiply = (a, b) -> # Anonymous, unbound this, not hoisted
582
- a * b
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
- # Objects
602
- person =
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
- # Arrays
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, second] = numbers
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
- # Legacy existential operator (CoffeeScript compatibility)
652
- value = x ? y # SPACE? syntax (auto-converts to ??)
653
- value = x ?? y # Preferred modern syntax
148
+ # Optional chaining (dual syntax)
149
+ user?.profile?.name # ES6 native
150
+ arr?[0] # CoffeeScript soak
151
+ fn?(arg) # Soak call
654
152
 
655
- # Note: "x ? y" (with space before ?) automatically converts to "x ?? y"
656
- # This provides backwards compatibility with CoffeeScript's existential operator.
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
- def validate!(x)
680
- return if x < 0 # → Just "return" (no value)
681
- console.log "valid"
682
- # ← Executes, then returns undefined
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
- # Works with all function types:
685
- c! = (x) -> # Void thin arrow
686
- x * 2 # Executes but doesn't return value
163
+ ### Unique Features
687
164
 
688
- process! = (data) => # Void fat arrow
689
- data.toUpperCase() # Executes but returns undefined
165
+ ```coffee
166
+ # Dammit operator! - Call and await
167
+ result = fetchData! # → await fetchData()
168
+ user = getUser!(id) # → await getUser(id)
690
169
 
691
- # The ! at definition means:
692
- # - All statements execute (side effects preserved)
693
- # - Final "return;" added automatically
694
- # - Explicit "return expr" becomes just "return"
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
- # 3. PUNT OPERATOR (&) - At Call-Site (Prevents Await) [FUTURE]
698
- # &fetchData → fetchData() (no await)
699
- # Used in future implicit await mode
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 regular expressions
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
- $ # end of string
707
- ///i
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
- ## Optional Operators - Dual Syntax
775
-
776
- Rip provides **two distinct approaches** to safe property/method access:
198
+ ## Why S-Expressions?
777
199
 
778
- ### Single `?` - CoffeeScript Soak (Existence Checks)
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
- **Benefits:**
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
- ### Components
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() { /* complex logic */ }
211
+ compile() { /* 50+ lines */ }
923
212
  }
213
+ ```
924
214
 
925
- // S-expression approach
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
- Simple pattern matching beats complex OOP hierarchies!
933
-
934
- ---
935
-
936
- ## Bun Integration
223
+ **Result: ~50% smaller compiler**
937
224
 
938
- Rip works seamlessly with Bun through automatic loader support!
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
- ### Quick Setup (3 Options)
941
-
942
- #### Option 1: Use Globally (Recommended)
943
-
944
- Set up Rip loader to work in **all** your projects:
232
+ ---
945
233
 
946
- ```bash
947
- # Install Rip globally
948
- cd /path/to/rip-lang
949
- bun link
234
+ ## Zero Dependencies
950
235
 
951
- # Create global Bun config (or add to existing ~/.bunfig.toml)
952
- echo 'preload = ["rip-lang/loader"]' >> ~/.bunfig.toml
236
+ **Rip is completely standalone** - no runtime or build dependencies:
953
237
 
954
- # Now run .rip files from anywhere!
955
- cd ~/my-project
956
- bun script.rip # Works!
238
+ ```json
239
+ {
240
+ "dependencies": {} // ← Empty!
241
+ }
957
242
  ```
958
243
 
959
- #### Option 2: Per-Project (From NPM - Coming Soon)
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
- # In your project directory
963
- bun add -d rip-lang
253
+ bun run parser # Rebuilds parser from scratch (solar.rip + grammar.rip)
254
+ # Complete bootstrap loop - ZERO external tools! ✅
255
+ ```
964
256
 
965
- # Create bunfig.toml
966
- echo 'preload = ["rip-lang/loader"]' > bunfig.toml
257
+ ---
967
258
 
968
- # Run
969
- bun your-script.rip
970
- ```
259
+ ## Browser Support
971
260
 
972
- #### Option 3: Per-Project (Local Development)
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
- # Copy files to your project
976
- cp /path/to/rip-lang/bunfig.toml .
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
- # Run
981
- bun your-script.rip
267
+ # Start dev server
268
+ bun run serve # → http://localhost:3000
982
269
  ```
983
270
 
984
- ### How It Works
985
-
986
- Two files enable automatic `.rip` file execution:
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
- **1. `bunfig.toml`** - Tells Bun to preload the loader:
989
- ```toml
990
- preload = ["rip-lang/loader"] # From package
991
- # or
992
- preload = ["./rip-loader.ts"] # Local file
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
- **2. `rip-loader.ts`** - Bun plugin that compiles `.rip` files on-the-fly:
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
- ### What You Get
288
+ ---
1013
289
 
1014
- - **Direct execution** - `bun script.rip` just works
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
- ### Using with Deno
292
+ **Primary targets:**
293
+ - 🎯 **Bun** - First-class support with automatic `.rip` loader
294
+ - 🌐 **Browsers** - 43KB bundle with REPL
1022
295
 
1023
- Deno works great with Rip's ES2022 output:
296
+ **Also supported:**
297
+ - ✅ **Deno** - ES2022 output works natively
298
+ - ✅ **Node.js 12+** - Full compatibility
1024
299
 
1025
- ```bash
1026
- # Compile first
1027
- ./bin/rip -o script.js your-script.rip
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
- # Run with Deno
1030
- deno run script.js
306
+ ---
1031
307
 
1032
- # Or use ES6 modules
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
- ### Using with Node.js
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
- Node.js 12+ supports all ES2022 features Rip uses:
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
- ```bash
1042
- # Compile first
1043
- ./bin/rip -o script.js your-script.rip
324
+ **For contributors:**
325
+ - [CONTRIBUTING.md](CONTRIBUTING.md) - Development workflow
326
+ - [docs/WORKFLOW.md](docs/WORKFLOW.md) - Command reference
1044
327
 
1045
- # Run with Node
1046
- node script.js
328
+ ---
1047
329
 
1048
- # Or add to package.json scripts
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
- **Note:** Bun's automatic loader is the recommended approach. For Deno/Node, compile `.rip` → `.js` first, then run the compiled output.
332
+ **Version:** 1.4.3 - **PRODUCTION READY** 🎉
1058
333
 
1059
- ### Troubleshooting
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
- **Problem: `bun script.rip` doesn't work**
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
- ```bash
1064
- # 1. Check if rip-lang is linked globally
1065
- bun pm ls --global
1066
- # Should show "rip-lang"
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
- # 2. Check if global config exists
1069
- cat ~/.bunfig.toml
1070
- # Should include: preload = ["rip-lang/loader"]
351
+ See [CHANGELOG.md](CHANGELOG.md) for detailed history.
1071
352
 
1072
- # 3. Re-link if needed
1073
- cd /path/to/rip-lang
1074
- bun link
353
+ ---
1075
354
 
1076
- # 4. Test from the rip-lang directory first
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
- **Problem: Imports not working**
357
+ ### Running Tests
1083
358
 
1084
359
  ```bash
1085
- # Make sure you're using .rip extension in imports
1086
- import { fn } from "./utils.rip" # ✅ Good
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
- ```bash
1093
- # The package needs to be linked first
1094
- cd /path/to/rip-lang
1095
- bun link
363
+ # Specific file
364
+ bun test/runner.js test/rip/functions.rip
1096
365
 
1097
- # Verify it worked
1098
- bun pm ls --global | grep rip-lang
366
+ # Clear cache
367
+ bun --no-cache test/runner.js test/rip
1099
368
  ```
1100
369
 
1101
- ---
370
+ ### Build Commands
1102
371
 
1103
- ## Development
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 (given)
383
+ │ ├── lexer.js # CoffeeScript lexer (adapted)
1111
384
  │ ├── parser.js # Solar parser (generated)
1112
- │ ├── codegen.js # Code generator (our work!)
1113
- │ ├── compiler.js # Main pipeline
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/ # Documentation
1120
- ├── CODEGEN.md # Pattern reference
1121
- ├── COMPREHENSIONS.md # Comprehension spec
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
- ### Adding Features
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
- 5. **Document in docs/CODEGEN.md**
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
- ### Philosophy
1164
-
1165
- > **Simplicity scales.**
1166
- > Keep the IR simple (s-expressions)
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
- Rip requires parentheses for ALL arrow function parameters:
409
+ # 2. Implement in src/codegen.js (check dispatch table)
410
+ # 3. Write tests in test/rip/
1175
411
 
1176
- ```coffee
1177
- # Always use parentheses
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
- # Never omit (even though ES6 allows it)
1183
- x => expr # Not supported
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
- | Syntax | Elegant | Elegant (inspired by CS) |
1203
- | Implementation | 17,760 LOC | **9,814 LOC (~45% smaller)** |
1204
- | Dependencies | Multiple | **ZERO** |
1205
- | Parser Generator | External (Jison) | **Built-in (solar.rip)** |
1206
- | Self-Hosting | No | **Yes** |
1207
- | Modules | CommonJS | ES6 native |
1208
- | Classes | ES5 functions | ES6 classes |
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
- **CoffeeScript output** (verbose):
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
- **Bottom line:** Rip compiles elegant source code to elegant JavaScript. CoffeeScript compiles elegant source to verbose ES5.
435
+ Compiling a 400-line CoffeeScript file with classes, nested switches, and loops:
1267
436
 
1268
- ### vs TypeScript
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
- **TypeScript:** Type safety, large ecosystem
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
- ## Contributing
447
+ ## Why Rip?
1355
448
 
1356
- We're building this clean-room style! Check out:
1357
- - [AGENT.md](AGENT.md) - For AI agents/developers
1358
- - [docs/CODEGEN.md](docs/CODEGEN.md) - Pattern reference
1359
- - [docs/COMPREHENSIONS.md](docs/COMPREHENSIONS.md) - Comprehension context specification
1360
- - [docs/](docs/) - Complete documentation library
449
+ **For users:**
450
+ - Elegant syntax without verbosity
451
+ - Modern JavaScript output
452
+ - Zero build tool complexity
453
+ - Browser support included
1361
454
 
1362
- ### Development Workflow
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
- 1. Pick a pattern to implement
1365
- 2. Check parser output with `-s` flag
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 (syntax and lexer)
1389
- - Lisp/Scheme (s-expressions)
1390
- - Solar (parser generator) - **This project wouldn't have been possible without Solar's incredible speed (80ms vs Jison's 12.5 seconds for parser generation). Solar's performance enabled rapid iteration on grammar changes with instant feedback, making development a joy.**
1391
- - Ruby (regex operators, __DATA__ marker)
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