rip-lang 2.9.0 → 2.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,569 +0,0 @@
1
- <p><img src="rip.svg" alt="Rip Logo" width="100"></p>
2
-
3
- # Rip Philosophy & Design
4
-
5
- > Why Rip exists, how it compares to CoffeeScript, and the design decisions that shape it.
6
-
7
- ---
8
-
9
- ## Table of Contents
10
-
11
- 1. [Why S-Expressions](#1-why-s-expressions)
12
- 2. [Rip vs CoffeeScript](#2-rip-vs-coffeescript)
13
- 3. [Current Assessment](#3-current-assessment)
14
- 4. [The Full Debate](#4-the-full-debate)
15
-
16
- ---
17
-
18
- # 1. Why S-Expressions
19
-
20
- **TL;DR:** S-expressions make compilers 50% smaller, 10x easier to maintain, and infinitely more elegant.
21
-
22
- ## The Central Insight
23
-
24
- Most compilers use complex AST node classes. Rip uses **simple arrays**:
25
-
26
- ```javascript
27
- // Traditional AST (CoffeeScript, TypeScript, Babel)
28
- class BinaryOp {
29
- constructor(op, left, right) {
30
- this.op = op;
31
- this.left = left;
32
- this.right = right;
33
- }
34
- compile() { /* 50+ lines */ }
35
- optimize() { /* 30+ lines */ }
36
- validate() { /* 20+ lines */ }
37
- }
38
-
39
- // Rip's S-Expression
40
- ["+", left, right] // That's it!
41
- ```
42
-
43
- **Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~11,000 LOC—smaller, yet includes a complete reactive runtime with state, computed values, and effects.
44
-
45
- ## The Fundamental Rule
46
-
47
- > **Transform the IR (s-expressions), not the output (strings)**
48
-
49
- This single principle led to major refactorings that eliminated over 140 lines of fragile code.
50
-
51
- ## Real-World Example: Flattening Logical Chains
52
-
53
- **The Problem:**
54
- ```javascript
55
- // Parser creates deeply nested s-expressions:
56
- ["&&", ["&&", ["&&", ["!", "a"], ["!", "b"]], ["!", "c"]], "d"]
57
-
58
- // Which generates ugly code:
59
- if (((!a && !b) && !c) && d)
60
- ```
61
-
62
- **❌ String Manipulation Approach (What We Didn't Do)**
63
-
64
- ```javascript
65
- unwrapLogical(code) {
66
- // Try to fix the OUTPUT with regex
67
- while (code.includes('((')) {
68
- code = code.replace(/* complex regex */, /* replacement */);
69
- // 100+ lines of regex hell
70
- }
71
- }
72
- ```
73
-
74
- **Problems:**
75
- - 100+ lines of complex regex
76
- - Fragile (breaks if format changes)
77
- - Hard to debug
78
- - Slow (string parsing)
79
-
80
- **✅ S-Expression Approach (What We Actually Did)**
81
-
82
- ```javascript
83
- flattenBinaryChain(sexpr) {
84
- // Transform the IR directly
85
- if (!Array.isArray(sexpr) || sexpr[0] !== '&&') return sexpr;
86
-
87
- const operands = [];
88
- const collect = (expr) => {
89
- if (Array.isArray(expr) && expr[0] === '&&') {
90
- for (let i = 1; i < expr.length; i++) {
91
- collect(expr[i]);
92
- }
93
- } else {
94
- operands.push(expr);
95
- }
96
- };
97
-
98
- collect(sexpr);
99
- return ['&&', ...operands];
100
- }
101
-
102
- // Input: ["&&", ["&&", a, b], c]
103
- // Output: ["&&", a, b, c]
104
- // Generate: "a && b && c"
105
- ```
106
-
107
- **Benefits:**
108
- - 50 lines of clear logic
109
- - Type-safe (works with arrays)
110
- - Easy to debug (inspect data)
111
- - Fast (no parsing)
112
-
113
- ## The Developer Experience Gap
114
-
115
- ### Debugging Comparison
116
-
117
- **String Manipulation:**
118
- ```javascript
119
- console.log(iifeCode);
120
- // "(() => {\n const result = [];\n for (const x of arr) {\n..."
121
- // What you see: A blob of text
122
- // Time to debug: 20-30 minutes
123
- ```
124
-
125
- **S-Expression:**
126
- ```javascript
127
- console.log(sexpr);
128
- // ["comprehension", ["*", "x", 2], [["for-in", ["x"], ["array", 1, 2, 3], null]], []]
129
- // What you see: Clear structure
130
- // Time to debug: 2-3 minutes
131
- ```
132
-
133
- ### Time to Debug Issue
134
-
135
- **String Manipulation:** 58 minutes (and that's if you're lucky!)
136
- **S-Expression:** 8 minutes (and you're confident it's correct)
137
-
138
- **7x faster debugging!**
139
-
140
- ## The Architecture Lesson
141
-
142
- ### Why CoffeeScript is 17,760 LOC
143
-
144
- **CoffeeScript's approach:**
145
- ```javascript
146
- class Literal extends Base
147
- compile() { ... }
148
-
149
- class Block extends Base
150
- compile() { ... }
151
-
152
- class Op extends Base
153
- compile() { ... }
154
-
155
- // 50+ AST node classes
156
- // 10,000+ LOC of node definitions
157
- // Complex inheritance hierarchies
158
- ```
159
-
160
- ### Why Rip is ~14,000 LOC (with a complete framework)
161
-
162
- **Rip's approach:**
163
- ```javascript
164
- generate(sexpr, context) {
165
- switch (sexpr[0]) {
166
- case '+': return `(${left} + ${right})`;
167
- case 'if': return `if (${cond}) { ... }`;
168
- // Simple pattern matching
169
- // No classes, no inheritance
170
- // Just data transformation
171
- }
172
- }
173
- ```
174
-
175
- **The difference:**
176
- - CoffeeScript: Object-oriented with inheritance
177
- - Rip: Functional with pattern matching
178
-
179
- ## Practical Guidelines
180
-
181
- ### ✅ DO: Work at S-Expression Level
182
-
183
- ```javascript
184
- // Good: Transform before generating
185
- const flattened = this.flattenBinaryChain(sexpr);
186
- return this.generate(flattened, context);
187
-
188
- // Good: Detect pattern and change generation
189
- if (this.comprehensionTarget) {
190
- return this.generateComprehensionWithTarget(...);
191
- }
192
- ```
193
-
194
- ### ❌ DON'T: Manipulate Generated Strings
195
-
196
- ```javascript
197
- // Bad: Generate then parse
198
- const code = this.generate(expr);
199
- const unwrapped = code.match(/pattern/);
200
-
201
- // Bad: String replacement
202
- return code.replace(/old/g, 'new');
203
- ```
204
-
205
- ## The Philosophy
206
-
207
- ### Lisp Got It Right 60 Years Ago
208
-
209
- ```lisp
210
- ; Code is data, data is code
211
- (+ 1 2) ; This is both an expression AND a list
212
- ```
213
-
214
- **The insight:** When your IR is simple data (lists/arrays), transformations are trivial!
215
-
216
- ### Why Other Languages Forgot
217
-
218
- **Traditional compiler theory:**
219
- - "You need typed AST nodes"
220
- - "Object-oriented design is cleaner"
221
- - "Visitor pattern for traversal"
222
-
223
- **Reality:**
224
- - AST nodes → Complex inheritance
225
- - OOP → Boilerplate everywhere
226
- - Visitor pattern → Indirection hell
227
-
228
- **Rip remembered:**
229
- - Arrays → Simple access
230
- - Pattern matching → Direct logic
231
- - Recursion → Natural traversal
232
-
233
- ## The Bottom Line
234
-
235
- **Question:** Why s-expressions?
236
-
237
- **Answer:** Because transforming **data** is easier than parsing **strings**.
238
-
239
- **Evidence:**
240
- - 35% smaller compiler
241
- - 10x easier debugging
242
- - 7x faster refactoring
243
- - 100% elimination of regex fragility
244
- - Infinite improvement in developer joy
245
-
246
- **Philosophy:** Simplicity scales. ✨
247
-
248
- ---
249
-
250
- # 2. Rip vs CoffeeScript
251
-
252
- ## Quick Summary
253
-
254
- | Metric | CoffeeScript | Rip |
255
- |--------|--------------|-----|
256
- | **Feature Parity** | Baseline | ~99% |
257
- | **Unique Features** | 0 | 10+ innovations |
258
- | **Dependencies** | Multiple | **ZERO** |
259
- | **Self-Hosting** | No | **YES** |
260
- | **Total LOC** | 17,760 | ~14,000 |
261
-
262
- ## Rip's Killer Features (Not in CoffeeScript)
263
-
264
- ### 1. Dual Optional Syntax
265
-
266
- **CoffeeScript:** 4 soak operators (`?`, `?[]`, `?()`, `?::`)
267
-
268
- **Rip:** 10 total operators - **BOTH CoffeeScript soak AND ES6 optional!**
269
-
270
- ```coffee
271
- # CoffeeScript soak (transpiled)
272
- arr?[0] # → (arr != null ? arr[0] : undefined)
273
-
274
- # ES6 optional (native)
275
- obj?.prop # → obj?.prop
276
- arr?.[0] # → arr?.[0]
277
- x ?? y # → x ?? y
278
-
279
- # Mix and match!
280
- obj?.arr?[0] # ES6 + CoffeeScript together!
281
- ```
282
-
283
- ### 2. Zero Dependencies + Self-Hosting
284
-
285
- ```json
286
- // package.json
287
- {
288
- "dependencies": {} // ← ZERO dependencies!
289
- }
290
- ```
291
-
292
- **What's included:**
293
- - ✅ Full compiler (lexer, parser, codegen)
294
- - ✅ **SLR(1) parser generator** (solar.rip - ~1,000 lines)
295
- - ✅ Self-hosting capability (Rip compiles itself)
296
- - ✅ Triple REPL (terminal, browser, console)
297
- - ✅ Test framework
298
- - ✅ Browser bundler
299
-
300
- **Bootstrap loop:**
301
- ```bash
302
- # Rip compiles the parser generator
303
- ./bin/rip src/grammar/solar.rip → solar.js
304
-
305
- # solar.js compiles the grammar
306
- bun solar.js src/grammar/grammar.rip → parser.js
307
-
308
- # parser.js used by Rip → COMPLETE LOOP ✅
309
- ```
310
-
311
- ### 3. Ternary Operator (JavaScript Style)
312
-
313
- ```coffee
314
- # Both styles work in Rip!
315
- result = cond ? truthy : falsy # JavaScript style
316
- result = if cond then truthy else falsy # CoffeeScript style
317
- ```
318
-
319
- **Why possible:** By using `??` for nullish, `?` became available for ternary.
320
-
321
- ### 4. Heregex (Extended Regular Expressions)
322
-
323
- ```rip
324
- pattern = ///
325
- ^ \d+ # starts with digits
326
- \s* # optional whitespace
327
- [a-z]+ # followed by letters
328
- $ # end of string
329
- ///gi
330
-
331
- # Compiles to: /^\d+\s*[a-z]+$/gi
332
- # Comments and whitespace automatically stripped!
333
- ```
334
-
335
- ### 5. Ruby-Style Regex
336
-
337
- ```rip
338
- # =~ operator with automatic _ capture
339
- email =~ /(.+)@(.+)/
340
- username = _[1]
341
- domain = _[2]
342
-
343
- # Inline extraction
344
- zip = "12345-6789"[/^(\d{5})/, 1] # Returns "12345"
345
- ```
346
-
347
- ### 6. Dammit Operator (Call-and-Await)
348
-
349
- ```rip
350
- # The ! operator calls AND awaits
351
- result = fetchData! # → await fetchData()
352
- user = getUser!(id) # → await getUser(id)
353
- ```
354
-
355
- ### 7. Void Functions (Side-Effect Only)
356
-
357
- ```rip
358
- # ! at definition suppresses implicit returns
359
- def processItems!
360
- for item in items
361
- item.update()
362
- # ← Returns undefined, not last expression
363
- ```
364
-
365
- ### 8. Smart Comprehension Optimization
366
-
367
- ```rip
368
- fn = ->
369
- process x for x in arr # ← Rip: plain loop!
370
- doMore() # ← Last statement returns
371
- ```
372
-
373
- **CoffeeScript:** Generates IIFE (wasteful array building)
374
- **Rip:** Context-aware - plain loop when result unused!
375
-
376
- ### 9. __DATA__ Marker
377
-
378
- ```rip
379
- console.log "Config:", DATA
380
-
381
- __DATA__
382
- host=localhost
383
- port=8080
384
- debug=true
385
- ```
386
-
387
- Ruby-style __DATA__ marker for inline config/templates/test data.
388
-
389
- ### 10. Auto-Detection
390
-
391
- ```coffee
392
- # No manual async keyword needed!
393
- getData = ->
394
- await fetch(url)
395
- # Automatically becomes: async function getData()
396
-
397
- # No manual * needed!
398
- counter = ->
399
- yield 1
400
- # Automatically becomes: function* counter()
401
- ```
402
-
403
- ## Key Design Differences
404
-
405
- ### Module System
406
-
407
- | CoffeeScript | Rip | Rationale |
408
- |-------------|-----|-----------|
409
- | CommonJS | ES6 modules | Future-proof, tree-shaking |
410
- | `require()` | `import` | Native browser support |
411
-
412
- ### Class Compilation
413
-
414
- | CoffeeScript | Rip | Rationale |
415
- |-------------|-----|-----------|
416
- | ES5 functions | ES6 classes | Cleaner, native, optimized |
417
-
418
- ### Implementation Comparison
419
-
420
- | Component | CoffeeScript 2.7 | Rip | Difference |
421
- |-----------|------------------|-----|------------|
422
- | **Lexer+Rewriter** | 3,558 LOC | 3,537 LOC | Expanded syntax |
423
- | **Parser Generator** | 2,285 LOC (Jison) | ~1,000 LOC (Solar) | Built-in, ~250× faster! |
424
- | **Compiler** | 10,346 LOC (AST Nodes) | 5,500 LOC (S-expressions) | +Reactive runtime! |
425
- | **Total** | **17,760 LOC** | **~11,000 LOC** | **Smaller + reactive runtime** |
426
-
427
- ## Feature Comparison Table
428
-
429
- | Feature | CoffeeScript | Rip | Winner |
430
- |---------|-------------|------|--------|
431
- | **Optional operators** | 4 soak | 10 (5 soak + 5 ES6) | 🏆 Rip |
432
- | **Ternary** | ❌ No | ✅ Yes | 🏆 Rip |
433
- | **Heregex** | ⚠️ Deprecated | ✅ Full support | 🏆 Rip |
434
- | **Regex features** | Basic | ✅ Ruby-style (=~, indexing) | 🏆 Rip |
435
- | **REPL modes** | 1 (terminal) | 3 (terminal, browser, console) | 🏆 Rip |
436
- | **Async shorthand** | ❌ No | ✅ Dammit operator | 🏆 Rip |
437
- | **Void functions** | ❌ No | ✅ Side-effect only | 🏆 Rip |
438
- | **__DATA__ marker** | ❌ No | ✅ Ruby-style | 🏆 Rip |
439
- | **Comprehension optimization** | Always IIFE | Context-aware | 🏆 Rip |
440
- | **Modules** | CommonJS | ES6 | 🏆 Rip |
441
- | **Classes** | ES5 | ES6 | 🏆 Rip |
442
- | **Dependencies** | Multiple | ✅ **ZERO** | 🏆 Rip |
443
- | **Parser generator** | External (Jison) | ✅ **Built-in (solar.rip)** | 🏆 Rip |
444
- | **Self-hosting** | ❌ No | ✅ **Yes** | 🏆 Rip |
445
- | **Extensibility** | Hard | Easy | 🏆 Rip |
446
- | **Block comments** | ✅ Yes | ✅ Yes | 🤝 Tie |
447
- | **Chained compare** | ✅ Yes | ❌ No | 🏆 CS |
448
-
449
- **Score:** Rip 16, CoffeeScript 1, Tie 1
450
-
451
- ## When to Use Which
452
-
453
- ### Use CoffeeScript When:
454
- - ✅ Legacy codebase (already using CoffeeScript)
455
- - ✅ Need CommonJS modules
456
- - ✅ Team has CoffeeScript expertise
457
-
458
- ### Use Rip When:
459
- - ✅ Starting new project
460
- - ✅ Want modern ES6+ output
461
- - ✅ Value dual optional syntax
462
- - ✅ Need 100% test coverage
463
- - ✅ Want unique features (heregex, regex+, dammit)
464
- - ✅ Need easy extensibility
465
- - ✅ Want smaller, cleaner implementation
466
-
467
- ---
468
-
469
- # 3. Current Assessment
470
-
471
- > **v2.5.1 - Production-Ready with Fine-Grained Reactivity**
472
-
473
- ## Summary
474
-
475
- | Layer | Syntax | Runtime | Features | DX | Score |
476
- |-------|--------|---------|----------|-----|-------|
477
- | **Reactivity** | A+ | A+ | A+ | A+ | **A+** |
478
-
479
- ## Reactivity ⭐⭐⭐⭐⭐ (Production-Ready)
480
-
481
- **This is genuinely excellent.**
482
-
483
- ```coffee
484
- count := 0 # State
485
- doubled ~= count * 2 # Computed (auto-tracks)
486
- log ~> console.log count # Effect (auto-runs)
487
- ```
488
-
489
- | Aspect | Rating | Notes |
490
- |--------|--------|-------|
491
- | Syntax | A+ | `:=`, `~=`, `=!` are elegant and unique |
492
- | Semantics | A | Proper dependency tracking, lazy computed |
493
- | Performance | A- | Efficient - only recomputes when needed |
494
- | Scalability | A | Works the same at any scale |
495
-
496
- **Competitive with:** SolidJS signals, Vue 3 refs, Preact signals
497
-
498
- ## Framework-Agnostic Design
499
-
500
- Rip's reactivity system is **framework-agnostic** — use it with React, Vue, Svelte, or vanilla JavaScript. The reactive primitives (state, computed, effects) work independently of any UI layer.
501
-
502
- ## Competitive Analysis
503
-
504
- | Framework | Reactivity | DX | Performance | Overall |
505
- |-----------|------------|-----|-------------|---------|
506
- | **Rip** | A+ | A+ | A | **A** |
507
- | SolidJS | A+ | A | A+ | A |
508
- | Vue 3 | A- | A | B+ | A- |
509
- | React | B | A- | B | B+ |
510
-
511
- **Rip's Position:**
512
-
513
- | Strength | Why |
514
- |----------|-----|
515
- | **Reactivity A+** | State, computed, effects, batching |
516
- | **DX A+** | Cleanest syntax of all, no boilerplate |
517
- | **Framework-agnostic** | Use with any UI framework |
518
-
519
- ## Completed Features
520
-
521
- - [x] Reactivity primitives (state, computed, effects)
522
- - [x] **Batching** (`__batch()` for grouped updates)
523
- - [x] **Error primitives** (`__catchErrors`, `__handleError`)
524
-
525
- ## Best Current Uses
526
-
527
- - Building fast web applications
528
- - Projects requiring minimal bundle size
529
- - Learning reactive programming concepts
530
- - Projects valuing clean syntax over ecosystem size
531
-
532
- **The framework is production-ready** with performance competitive with the fastest frameworks available.
533
-
534
- ---
535
-
536
- # 4. The Full Debate
537
-
538
- For a thorough exploration of the arguments for and against languages like Rip in the modern JavaScript ecosystem, see:
539
-
540
- - [Why Not CoffeeScript](WHY-NOT-COFFEESCRIPT.md) - The strongest case against revival
541
- - [Why YES Rip](WHY-YES-RIP.md) - The counter-argument and Rip's answer
542
-
543
- These documents present a point/counterpoint debate:
544
- 1. **WHY-NOT-COFFEESCRIPT.md** - Makes the case that CoffeeScript should remain a historical artifact, citing ecosystem abandonment, TypeScript's dominance, and tooling degradation.
545
- 2. **WHY-YES-RIP.md** - Directly rebuts each argument, showing how Rip addresses these concerns with zero dependencies, modern output, and unique features.
546
-
547
- The debate structure demonstrates intellectual honesty—Rip acknowledges the valid criticisms and shows how it provides a different path forward.
548
-
549
- ---
550
-
551
- ## Conclusion
552
-
553
- **Rip exists because:**
554
-
555
- 1. **Simplicity scales** - S-expressions make compilers maintainable
556
- 2. **Zero dependencies** - True autonomy from the npm ecosystem
557
- 3. **Modern output** - ES6+ everywhere, no legacy baggage
558
- 4. **Unique features** - 10+ innovations CoffeeScript lacks
559
- 5. **Best-in-class DX** - Cleanest syntax in the industry
560
- 6. **Production-ready** - 100% test coverage, self-hosting
561
-
562
- **Philosophy:** Programming should be joyful, not painful. Rip remembers what we were fighting for.
563
-
564
- ---
565
-
566
- **See Also:**
567
- - [GUIDE.md](GUIDE.md) - Complete language reference
568
- - [INTERNALS.md](INTERNALS.md) - Compiler architecture
569
- - [BROWSER.md](BROWSER.md) - Browser usage and REPL guide