rip-lang 2.9.1 → 3.0.0
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 +45 -2
- package/README.md +55 -42
- package/docs/RIP-INTERNALS.md +576 -0
- package/docs/RIP-LANG.md +1126 -0
- package/docs/{TYPES.md → RIP-TYPES.md} +3 -3
- package/docs/dist/rip.browser.js +3278 -5505
- package/docs/dist/rip.browser.min.js +248 -332
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/package.json +1 -1
- package/src/browser.js +3 -20
- package/src/compiler.js +1683 -4550
- package/src/grammar/grammar.rip +530 -489
- package/src/grammar/solar.rip +1 -1
- package/src/lexer.js +1318 -3094
- package/src/parser.js +212 -220
- package/src/repl.js +16 -9
- package/docs/BROWSER.md +0 -990
- package/docs/GUIDE.md +0 -636
- package/docs/INTERNALS.md +0 -857
- package/docs/RATIONALE.md +0 -180
- package/docs/examples/README.md +0 -33
- package/docs/examples/arrows.rip +0 -74
- package/docs/examples/async-await.rip +0 -59
- package/docs/examples/existential.rip +0 -86
- package/docs/examples/fibonacci.rip +0 -12
- package/docs/examples/module.rip +0 -48
- package/docs/examples/ranges.rip +0 -45
- package/docs/examples/reactivity.rip +0 -48
- package/docs/examples/switch.rip +0 -50
- package/docs/examples/ternary.rip +0 -36
- /package/docs/{REACTIVITY.md → RIP-REACTIVITY.md} +0 -0
package/docs/RATIONALE.md
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
<p><img src="rip.svg" alt="Rip Logo" width="100"></p>
|
|
2
|
-
|
|
3
|
-
# Why Rip Exists
|
|
4
|
-
|
|
5
|
-
> Philosophy, design decisions, and the case for a modern CoffeeScript successor.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## The Short Version
|
|
10
|
-
|
|
11
|
-
Rip exists because:
|
|
12
|
-
|
|
13
|
-
1. **Simplicity scales** — S-expressions make compilers 50% smaller and 10x easier to maintain
|
|
14
|
-
2. **Zero dependencies** — True autonomy from the npm ecosystem
|
|
15
|
-
3. **Modern output** — ES2022 everywhere, no legacy baggage
|
|
16
|
-
4. **Unique features** — 10+ innovations CoffeeScript never had
|
|
17
|
-
5. **Reactivity as operators** — `:=`, `~=`, `~>` are language syntax, not library imports
|
|
18
|
-
6. **Self-hosting** — Rip compiles itself, including its own parser generator
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## 1. Why S-Expressions
|
|
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
|
-
compile() { /* 50+ lines */ }
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Rip's S-Expression
|
|
34
|
-
["+", left, right] // That's it!
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
**Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~11,000 LOC — smaller, yet includes a complete reactive runtime.
|
|
38
|
-
|
|
39
|
-
### The Fundamental Rule
|
|
40
|
-
|
|
41
|
-
> **Transform the IR (s-expressions), not the output (strings)**
|
|
42
|
-
|
|
43
|
-
This single principle eliminates entire categories of bugs. When your IR is simple data (arrays), transformations are trivial and debuggable:
|
|
44
|
-
|
|
45
|
-
```javascript
|
|
46
|
-
// Debugging: inspect the data directly
|
|
47
|
-
console.log(sexpr);
|
|
48
|
-
// ["comprehension", ["*", "x", 2], [["for-in", ["x"], ["array", 1, 2, 3]]], []]
|
|
49
|
-
// Clear structure, 2-3 minutes to debug
|
|
50
|
-
|
|
51
|
-
// vs. string manipulation
|
|
52
|
-
console.log(code);
|
|
53
|
-
// "(() => {\n const result = [];\n for (const x of arr) {\n..."
|
|
54
|
-
// Blob of text, 20-30 minutes to debug
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
**Lisp got this right 60 years ago.** Code is data, data is code.
|
|
58
|
-
|
|
59
|
-
---
|
|
60
|
-
|
|
61
|
-
## 2. Rip vs CoffeeScript
|
|
62
|
-
|
|
63
|
-
| Metric | CoffeeScript | Rip |
|
|
64
|
-
|--------|--------------|-----|
|
|
65
|
-
| **Feature Parity** | Baseline | ~99% |
|
|
66
|
-
| **Unique Features** | 0 | 10+ innovations |
|
|
67
|
-
| **Dependencies** | Multiple | **ZERO** |
|
|
68
|
-
| **Self-Hosting** | No | **YES** |
|
|
69
|
-
| **Total LOC** | 17,760 | ~11,000 |
|
|
70
|
-
| **Output** | ES6 (2017) | ES2022 |
|
|
71
|
-
| **Reactivity** | None | Built-in |
|
|
72
|
-
|
|
73
|
-
### What Rip Adds
|
|
74
|
-
|
|
75
|
-
1. **Reactivity as operators** — `:=` state, `~=` computed, `~>` effects
|
|
76
|
-
2. **Ternary operator** — `x ? a : b` (freed by using `??` for nullish)
|
|
77
|
-
3. **Dammit operator** — `fetchData!` calls AND awaits
|
|
78
|
-
4. **Ruby-style regex** — `str =~ /pattern/` with captures in `_`
|
|
79
|
-
5. **Dual optional syntax** — Both CoffeeScript soak AND ES6 optional chaining
|
|
80
|
-
6. **Void functions** — `def process!` suppresses implicit returns
|
|
81
|
-
7. **Smart comprehensions** — Context-aware (loop vs. array building)
|
|
82
|
-
8. **`__DATA__` marker** — Ruby-style inline data sections
|
|
83
|
-
9. **Heregex** — Extended regex with comments and whitespace
|
|
84
|
-
10. **Zero dependencies** — Everything included, even the parser generator
|
|
85
|
-
|
|
86
|
-
### The Complete Feature Table
|
|
87
|
-
|
|
88
|
-
| Feature | CoffeeScript | Rip | Winner |
|
|
89
|
-
|---------|-------------|------|--------|
|
|
90
|
-
| Optional operators | 4 soak | 10 (5 soak + 5 ES6) | Rip |
|
|
91
|
-
| Ternary | No | Yes | Rip |
|
|
92
|
-
| Regex features | Basic | Ruby-style (`=~`, indexing) | Rip |
|
|
93
|
-
| Async shorthand | No | Dammit operator (`!`) | Rip |
|
|
94
|
-
| Void functions | No | `def fn!` | Rip |
|
|
95
|
-
| Reactivity | None | `:=`, `~=`, `~>` | Rip |
|
|
96
|
-
| Comprehension optimization | Always IIFE | Context-aware | Rip |
|
|
97
|
-
| Modules | CommonJS | ES6 | Rip |
|
|
98
|
-
| Classes | ES5 | ES6 | Rip |
|
|
99
|
-
| Dependencies | Multiple | **ZERO** | Rip |
|
|
100
|
-
| Parser generator | External (Jison) | **Built-in (solar.rip)** | Rip |
|
|
101
|
-
| Self-hosting | No | **Yes** | Rip |
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## 3. The Case Against CoffeeScript (And Why Rip Is Different)
|
|
106
|
-
|
|
107
|
-
The strongest argument against CoffeeScript in 2026:
|
|
108
|
-
|
|
109
|
-
- **Ecosystem abandoned** — Hiring, tooling, community all gone
|
|
110
|
-
- **TypeScript won** — Type safety became the standard
|
|
111
|
-
- **JavaScript absorbed it** — `?.`, `??`, `=>`, classes, destructuring all native now
|
|
112
|
-
- **Tooling rotted** — IDE support deprecated, build plugins unmaintained
|
|
113
|
-
|
|
114
|
-
**These are valid criticisms.** Rip addresses every one:
|
|
115
|
-
|
|
116
|
-
| Criticism | CoffeeScript's Problem | Rip's Answer |
|
|
117
|
-
|-----------|----------------------|--------------|
|
|
118
|
-
| No ecosystem | Dead community | Zero dependencies — doesn't need one |
|
|
119
|
-
| No types | Can't integrate with TS tooling | ES2022 output works with any JS toolchain |
|
|
120
|
-
| Tooling rot | Plugins unmaintained | Self-contained — nothing to maintain |
|
|
121
|
-
| No innovation | Frozen since 2017 | 10+ unique features, active development |
|
|
122
|
-
| No corporate backing | Abandoned | Self-hosting — depends on nothing |
|
|
123
|
-
|
|
124
|
-
**The key insight:** CoffeeScript failed because it depended on an ecosystem that moved on. Rip succeeds by depending on nothing.
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## 4. Reactivity as a Language Feature
|
|
129
|
-
|
|
130
|
-
This is Rip's signature innovation. While React, Vue, and Solid provide reactivity through library imports, Rip provides it as **language operators**:
|
|
131
|
-
|
|
132
|
-
```coffee
|
|
133
|
-
count := 0 # State — reactive container
|
|
134
|
-
doubled ~= count * 2 # Computed — auto-updates when count changes
|
|
135
|
-
~> console.log count # Effect — runs when dependencies change
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Compare to React:
|
|
139
|
-
```javascript
|
|
140
|
-
import { useState, useMemo, useEffect } from 'react';
|
|
141
|
-
const [count, setCount] = useState(0);
|
|
142
|
-
const doubled = useMemo(() => count * 2, [count]);
|
|
143
|
-
useEffect(() => { console.log(count); }, [count]);
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
**No imports. No hooks. No dependency arrays. Just operators.**
|
|
147
|
-
|
|
148
|
-
The reactive runtime is ~200 lines, embedded in the compiler, and only included when reactive operators are used.
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## 5. Design Principles
|
|
153
|
-
|
|
154
|
-
### Simplicity Scales
|
|
155
|
-
|
|
156
|
-
Simple IR (s-expressions), clear pipeline (lex → parse → generate), minimal code, comprehensive tests. Every design decision optimizes for **long-term maintainability**, not short-term cleverness.
|
|
157
|
-
|
|
158
|
-
### Zero Dependencies Is a Feature
|
|
159
|
-
|
|
160
|
-
```json
|
|
161
|
-
{ "dependencies": {} }
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
Everything included: compiler, parser generator, REPL, browser bundle, test framework. No supply chain attacks. No version conflicts. No `node_modules` bloat.
|
|
165
|
-
|
|
166
|
-
### Self-Hosting Proves Quality
|
|
167
|
-
|
|
168
|
-
Rip compiles its own parser generator (`solar.rip` → `parser.js`). If the compiler can compile itself, it works.
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
**See Also:**
|
|
173
|
-
- [GUIDE.md](GUIDE.md) — Complete language reference
|
|
174
|
-
- [REACTIVITY.md](REACTIVITY.md) — Reactivity deep dive
|
|
175
|
-
- [INTERNALS.md](INTERNALS.md) — Compiler architecture
|
|
176
|
-
- [BROWSER.md](BROWSER.md) — Browser usage and REPL
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
*Version 2.9.0 — 1115/1115 tests passing — Zero dependencies — Self-hosting*
|
package/docs/examples/README.md
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# Rip Examples
|
|
2
|
-
|
|
3
|
-
Focused examples demonstrating Rip's key features. Each file is self-contained
|
|
4
|
-
and can be compiled with `rip -c <file>` to see the generated JavaScript.
|
|
5
|
-
|
|
6
|
-
## Core Language
|
|
7
|
-
|
|
8
|
-
| Example | Demonstrates |
|
|
9
|
-
|---------|-------------|
|
|
10
|
-
| [fibonacci.rip](fibonacci.rip) | Functions, implicit returns, recursion |
|
|
11
|
-
| [arrows.rip](arrows.rip) | Arrow functions, callbacks, implicit object returns |
|
|
12
|
-
| [switch.rip](switch.rip) | Switch/when patterns (value and condition based) |
|
|
13
|
-
| [ternary.rip](ternary.rip) | JS-style ternary operator (`? :`) |
|
|
14
|
-
| [ranges.rip](ranges.rip) | Inclusive (`..`) and exclusive (`...`) ranges |
|
|
15
|
-
|
|
16
|
-
## Advanced Features
|
|
17
|
-
|
|
18
|
-
| Example | Demonstrates |
|
|
19
|
-
|---------|-------------|
|
|
20
|
-
| [reactivity.rip](reactivity.rip) | State (`:=`), computed (`~=`), effects (`~>`) |
|
|
21
|
-
| [async-await.rip](async-await.rip) | Dammit operator (`!`), auto-async detection |
|
|
22
|
-
| [existential.rip](existential.rip) | Null coalescing (`??`), optional chaining (`?.`) |
|
|
23
|
-
| [module.rip](module.rip) | Imports, exports, module patterns |
|
|
24
|
-
|
|
25
|
-
## Quick Start
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
# See what Rip generates
|
|
29
|
-
rip -c docs/examples/fibonacci.rip
|
|
30
|
-
|
|
31
|
-
# Run an example
|
|
32
|
-
rip docs/examples/reactivity.rip
|
|
33
|
-
```
|
package/docs/examples/arrows.rip
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# Arrow Function Examples
|
|
2
|
-
|
|
3
|
-
# =============================================================================
|
|
4
|
-
# Basic Arrow Functions (=> binds 'this', -> does not)
|
|
5
|
-
# =============================================================================
|
|
6
|
-
|
|
7
|
-
# Simple arrows
|
|
8
|
-
add = (a, b) => a + b
|
|
9
|
-
square = (x) => x * x
|
|
10
|
-
greet = (name) => "Hello, #{name}!" # CoffeeScript-style interpolation
|
|
11
|
-
farewell = (name) => "Goodbye, ${name}!" # JavaScript-style also works
|
|
12
|
-
|
|
13
|
-
# Arrow functions in objects (methods!)
|
|
14
|
-
math = {
|
|
15
|
-
add: (a, b) => a + b
|
|
16
|
-
subtract: (a, b) => a - b
|
|
17
|
-
multiply: (a, b) => a * b
|
|
18
|
-
divide: (a, b) => a / b
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
# Arrow functions as callbacks
|
|
22
|
-
numbers = [1, 2, 3, 4, 5]
|
|
23
|
-
doubled = numbers.map((x) => x * 2)
|
|
24
|
-
evens = numbers.filter((x) => x % 2 == 0)
|
|
25
|
-
sum = numbers.reduce((acc, x) => acc + x, 0)
|
|
26
|
-
|
|
27
|
-
# Arrow functions with object methods
|
|
28
|
-
users = [
|
|
29
|
-
{name: "Alice", age: 30}
|
|
30
|
-
{name: "Bob", age: 25}
|
|
31
|
-
]
|
|
32
|
-
|
|
33
|
-
names = users.map((u) => u.name)
|
|
34
|
-
adults = users.filter((u) => u.age >= 18)
|
|
35
|
-
|
|
36
|
-
# Composing arrows
|
|
37
|
-
pipeline = {
|
|
38
|
-
transform: (data) => data.map((x) => x * 2)
|
|
39
|
-
filter: (data) => data.filter((x) => x > 5)
|
|
40
|
-
process: (data) => data.reduce((sum, x) => sum + x, 0)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
# =============================================================================
|
|
44
|
-
# Implicit Object Returns (CoffeeScript-style elegance!)
|
|
45
|
-
# =============================================================================
|
|
46
|
-
|
|
47
|
-
# Simple implicit object (no braces needed!)
|
|
48
|
-
makePoint = (x, y) =>
|
|
49
|
-
x: x
|
|
50
|
-
y: y
|
|
51
|
-
|
|
52
|
-
# With shorthand (use explicit braces)
|
|
53
|
-
makePoint2 = (x, y) => {x, y}
|
|
54
|
-
|
|
55
|
-
# Data transformation
|
|
56
|
-
enrichUser = (user) =>
|
|
57
|
-
id: user.id
|
|
58
|
-
name: user.name
|
|
59
|
-
email: user.email
|
|
60
|
-
active: true
|
|
61
|
-
joined: Date.now()
|
|
62
|
-
|
|
63
|
-
# Simple API methods
|
|
64
|
-
api = {
|
|
65
|
-
getUser: (id) =>
|
|
66
|
-
id: id
|
|
67
|
-
name: "User #{id}"
|
|
68
|
-
timestamp: Date.now()
|
|
69
|
-
|
|
70
|
-
createResponse: (data, status) =>
|
|
71
|
-
data: data
|
|
72
|
-
status: status
|
|
73
|
-
success: status == 200
|
|
74
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# Async/Await with the "Dammit Operator" (!)
|
|
2
|
-
# Clean, visual async syntax
|
|
3
|
-
|
|
4
|
-
# Simple async function
|
|
5
|
-
def loadUser(id)
|
|
6
|
-
user = Db.findOne!(id)
|
|
7
|
-
user
|
|
8
|
-
|
|
9
|
-
# Multiple awaits (auto-detected as async!)
|
|
10
|
-
def getUserProfile(id)
|
|
11
|
-
user = Db.getUser!(id)
|
|
12
|
-
posts = Db.getPosts!(user.id)
|
|
13
|
-
friends = Db.getFriends!(user.id)
|
|
14
|
-
|
|
15
|
-
{user, posts, friends}
|
|
16
|
-
|
|
17
|
-
# No arguments - auto-calls!
|
|
18
|
-
def refreshCache()
|
|
19
|
-
data = fetchLatest!
|
|
20
|
-
cache = getCache!
|
|
21
|
-
cache.set!(data)
|
|
22
|
-
data
|
|
23
|
-
|
|
24
|
-
# Await in conditionals
|
|
25
|
-
def checkStatus(id)
|
|
26
|
-
user = Db.getUser!(id)
|
|
27
|
-
|
|
28
|
-
if user.active
|
|
29
|
-
status = Api.getStatus!(user.id)
|
|
30
|
-
{user, status, active: true}
|
|
31
|
-
else
|
|
32
|
-
{user, active: false}
|
|
33
|
-
|
|
34
|
-
# Arrow functions with await
|
|
35
|
-
fetchAndProcess = (id) => Api.fetch!(id).then!(processData)
|
|
36
|
-
|
|
37
|
-
# Await in object methods
|
|
38
|
-
api = {
|
|
39
|
-
loadData: (key) =>
|
|
40
|
-
data: Cache.get!(key)
|
|
41
|
-
timestamp: Date.now()
|
|
42
|
-
|
|
43
|
-
saveData: (key, value) =>
|
|
44
|
-
result = Cache.set!(key, value)
|
|
45
|
-
success: result.ok
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
# Complex async flow
|
|
49
|
-
def processUserData(userId)
|
|
50
|
-
user = Db.getUser!(userId)
|
|
51
|
-
settings = Db.getSettings!(userId)
|
|
52
|
-
activity = Api.getActivity!(userId)
|
|
53
|
-
|
|
54
|
-
{
|
|
55
|
-
user
|
|
56
|
-
settings
|
|
57
|
-
activity
|
|
58
|
-
fetchedAt: Date.now()
|
|
59
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
# Existential and Logical Assignment Operators
|
|
2
|
-
# Demonstrates the difference between nullish and falsy checks
|
|
3
|
-
|
|
4
|
-
# Binary Nullish Coalescing (??)
|
|
5
|
-
# Only replaces null/undefined
|
|
6
|
-
def getPort(config)
|
|
7
|
-
config.port ?? 8080
|
|
8
|
-
|
|
9
|
-
# Preserves 0, false, empty string
|
|
10
|
-
port1 = getPort({port: 0}) # → 0 (not replaced!)
|
|
11
|
-
port2 = getPort({port: null}) # → 8080 (replaced)
|
|
12
|
-
port3 = getPort({}) # → 8080 (undefined)
|
|
13
|
-
|
|
14
|
-
# Binary Logical OR (||)
|
|
15
|
-
# Replaces ALL falsy values
|
|
16
|
-
def getPortFalsy(config)
|
|
17
|
-
config.port || 8080
|
|
18
|
-
|
|
19
|
-
portA = getPortFalsy({port: 0}) # → 8080 (0 is falsy)
|
|
20
|
-
portB = getPortFalsy({port: null}) # → 8080 (null is falsy)
|
|
21
|
-
|
|
22
|
-
# Existential Assignment (?=)
|
|
23
|
-
# Assign only if null/undefined
|
|
24
|
-
def initializeConfig()
|
|
25
|
-
config = {}
|
|
26
|
-
config.port ?= 8080 # Sets to 8080 (undefined)
|
|
27
|
-
config.debug ?= false # Sets to false (undefined)
|
|
28
|
-
config.timeout ?= 0 # Sets to 0 (undefined)
|
|
29
|
-
|
|
30
|
-
# These won't change existing values
|
|
31
|
-
config.port ?= 9000 # Stays 8080
|
|
32
|
-
config.debug ?= true # Stays false (preserves false!)
|
|
33
|
-
config.timeout ?= 5000 # Stays 0 (preserves 0!)
|
|
34
|
-
|
|
35
|
-
config
|
|
36
|
-
|
|
37
|
-
# Logical OR Assignment (||=)
|
|
38
|
-
# Assign if falsy
|
|
39
|
-
def setDefaults(options)
|
|
40
|
-
options.name ||= "guest" # Replaces empty string
|
|
41
|
-
options.count ||= 10 # Replaces 0
|
|
42
|
-
options.enabled ||= true # Replaces false
|
|
43
|
-
options
|
|
44
|
-
|
|
45
|
-
# Logical AND Assignment (&&=)
|
|
46
|
-
# Update only if truthy
|
|
47
|
-
def processIfValid(data)
|
|
48
|
-
data.value &&= data.value * 2 # Only process if exists
|
|
49
|
-
data
|
|
50
|
-
|
|
51
|
-
# Real-world example: User preferences
|
|
52
|
-
def getUserPreferences(user)
|
|
53
|
-
prefs = user?.preferences ?? {}
|
|
54
|
-
|
|
55
|
-
# Set defaults for missing values (existential)
|
|
56
|
-
prefs.theme ?= "dark"
|
|
57
|
-
prefs.fontSize ?= 14
|
|
58
|
-
prefs.notifications ?= true
|
|
59
|
-
|
|
60
|
-
# But replace falsy user inputs (logical)
|
|
61
|
-
prefs.username ||= "anonymous"
|
|
62
|
-
|
|
63
|
-
prefs
|
|
64
|
-
|
|
65
|
-
# Chained nullish coalescing
|
|
66
|
-
def getConfigValue(primary, secondary, tertiary)
|
|
67
|
-
primary ?? secondary ?? tertiary ?? "default"
|
|
68
|
-
|
|
69
|
-
# Comparison table
|
|
70
|
-
def demonstrateDifference()
|
|
71
|
-
testValues = [null, undefined, false, 0, "", "text", 42]
|
|
72
|
-
|
|
73
|
-
# For each value, show behavior of ?? vs ||
|
|
74
|
-
results = []
|
|
75
|
-
|
|
76
|
-
testValues.forEach((val) => {
|
|
77
|
-
nullish = val ?? "default"
|
|
78
|
-
logical = val || "default"
|
|
79
|
-
results.push({
|
|
80
|
-
value: val
|
|
81
|
-
nullish: nullish
|
|
82
|
-
logical: logical
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
results
|
package/docs/examples/module.rip
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# Modules — Imports, Exports, and Module Patterns
|
|
2
|
-
|
|
3
|
-
# =============================================================================
|
|
4
|
-
# Importing
|
|
5
|
-
# =============================================================================
|
|
6
|
-
|
|
7
|
-
# Named imports
|
|
8
|
-
import { readFileSync, writeFileSync } from "fs"
|
|
9
|
-
import { join, resolve } from "path"
|
|
10
|
-
|
|
11
|
-
# Namespace import
|
|
12
|
-
import * as os from "os"
|
|
13
|
-
|
|
14
|
-
# Default import
|
|
15
|
-
# import express from "express"
|
|
16
|
-
|
|
17
|
-
# =============================================================================
|
|
18
|
-
# Exporting Functions
|
|
19
|
-
# =============================================================================
|
|
20
|
-
|
|
21
|
-
# Named function export
|
|
22
|
-
export def greet(name)
|
|
23
|
-
"Hello, #{name}!"
|
|
24
|
-
|
|
25
|
-
# Arrow function export
|
|
26
|
-
export double = (x) -> x * 2
|
|
27
|
-
export format = (obj) -> JSON.stringify(obj, null, 2)
|
|
28
|
-
|
|
29
|
-
# =============================================================================
|
|
30
|
-
# Exporting Values
|
|
31
|
-
# =============================================================================
|
|
32
|
-
|
|
33
|
-
export config =
|
|
34
|
-
timeout: 5000
|
|
35
|
-
retries: 3
|
|
36
|
-
verbose: false
|
|
37
|
-
|
|
38
|
-
export AUTHOR =! "Rip" # const export
|
|
39
|
-
|
|
40
|
-
# =============================================================================
|
|
41
|
-
# Default Export
|
|
42
|
-
# =============================================================================
|
|
43
|
-
|
|
44
|
-
export default
|
|
45
|
-
greet: greet
|
|
46
|
-
double: double
|
|
47
|
-
format: format
|
|
48
|
-
config: config
|
package/docs/examples/ranges.rip
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# Range Examples
|
|
2
|
-
# Smart compilation: small ranges = inline, large ranges = IIFE
|
|
3
|
-
|
|
4
|
-
# Inclusive ranges (..)
|
|
5
|
-
def demoInclusive()
|
|
6
|
-
nums = 1..5
|
|
7
|
-
console.log("1..5 =", nums) # [1, 2, 3, 4, 5]
|
|
8
|
-
|
|
9
|
-
# Exclusive ranges (...)
|
|
10
|
-
def demoExclusive()
|
|
11
|
-
nums = 1...5
|
|
12
|
-
console.log("1...5 =", nums) # [1, 2, 3, 4]
|
|
13
|
-
|
|
14
|
-
# Descending ranges
|
|
15
|
-
def demoDescending()
|
|
16
|
-
nums = 10..1
|
|
17
|
-
console.log("10..1 =", nums.slice(0, 5)) # [10, 9, 8, 7, 6, ...]
|
|
18
|
-
|
|
19
|
-
# Large ranges (generates IIFE, not inline)
|
|
20
|
-
def demoLarge()
|
|
21
|
-
nums = 1..100
|
|
22
|
-
console.log("1..100 length =", nums.length) # 100
|
|
23
|
-
console.log("First 5:", nums.slice(0, 5)) # [1, 2, 3, 4, 5]
|
|
24
|
-
|
|
25
|
-
# Dynamic ranges (runtime values)
|
|
26
|
-
def rangeBetween(start, end)
|
|
27
|
-
start..end
|
|
28
|
-
|
|
29
|
-
# Using ranges
|
|
30
|
-
def sumRange(n)
|
|
31
|
-
nums = 1..n
|
|
32
|
-
sum = 0
|
|
33
|
-
nums.forEach((x) => sum = sum + x)
|
|
34
|
-
sum
|
|
35
|
-
|
|
36
|
-
# Range in array
|
|
37
|
-
def mixedArray()
|
|
38
|
-
arr = [0, 1..3, 10, 20..22]
|
|
39
|
-
console.log("Mixed:", arr) # [0, [1,2,3], 10, [20,21,22]]
|
|
40
|
-
|
|
41
|
-
# Practical: Generate test data
|
|
42
|
-
def generateIds(count)
|
|
43
|
-
ids = 1..count
|
|
44
|
-
ids.map((id) => id)
|
|
45
|
-
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# Reactivity — Rip's signature feature
|
|
2
|
-
# Language-level operators for reactive state, computed values, and effects.
|
|
3
|
-
# No imports. No hooks. No dependency arrays. Just operators.
|
|
4
|
-
|
|
5
|
-
# =============================================================================
|
|
6
|
-
# State (:= creates reactive state)
|
|
7
|
-
# =============================================================================
|
|
8
|
-
|
|
9
|
-
count := 0 # Reactive state variable
|
|
10
|
-
name := "Alice" # Works with any type
|
|
11
|
-
items := [1, 2, 3] # Arrays too
|
|
12
|
-
|
|
13
|
-
# =============================================================================
|
|
14
|
-
# Computed (~= auto-updates when dependencies change)
|
|
15
|
-
# =============================================================================
|
|
16
|
-
|
|
17
|
-
doubled ~= count * 2 # Always equals count * 2
|
|
18
|
-
greeting ~= "Hello, #{name}!" # Recomputes when name changes
|
|
19
|
-
total ~= items.reduce((a, b) -> a + b, 0)
|
|
20
|
-
|
|
21
|
-
# =============================================================================
|
|
22
|
-
# Effects (~> runs automatically when dependencies change)
|
|
23
|
-
# =============================================================================
|
|
24
|
-
|
|
25
|
-
# Anonymous effect (runs whenever count changes)
|
|
26
|
-
~> console.log "Count is now: #{count}"
|
|
27
|
-
|
|
28
|
-
# Named effect (can be controlled/disposed)
|
|
29
|
-
logger ~> console.log "#{name} has #{count} items"
|
|
30
|
-
|
|
31
|
-
# =============================================================================
|
|
32
|
-
# Const (=! for values that never change)
|
|
33
|
-
# =============================================================================
|
|
34
|
-
|
|
35
|
-
MAX_RETRIES =! 10 # const — "equals, dammit!"
|
|
36
|
-
API_URL =! "https://api.example.com"
|
|
37
|
-
|
|
38
|
-
# =============================================================================
|
|
39
|
-
# How it works
|
|
40
|
-
# =============================================================================
|
|
41
|
-
|
|
42
|
-
# The operators compile to a tiny reactive runtime:
|
|
43
|
-
# count := 0 → const count = __state(0)
|
|
44
|
-
# doubled ~= count → const doubled = __computed(() => count * 2)
|
|
45
|
-
# ~> console.log → __effect(() => console.log(...))
|
|
46
|
-
#
|
|
47
|
-
# The runtime is embedded in the compiler (~100 lines) and only
|
|
48
|
-
# included in the output when reactive operators are used.
|
package/docs/examples/switch.rip
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# Switch/When/Else Examples
|
|
2
|
-
# Two modes: value-based and condition-based
|
|
3
|
-
|
|
4
|
-
# Value-based switch (matches against a value)
|
|
5
|
-
def getDayType(day)
|
|
6
|
-
switch day
|
|
7
|
-
when "Mon", "Tue", "Wed", "Thu", "Fri" then "weekday"
|
|
8
|
-
when "Sat", "Sun" then "weekend"
|
|
9
|
-
else "unknown"
|
|
10
|
-
|
|
11
|
-
# Condition-based switch (evaluates conditions)
|
|
12
|
-
def getGrade(score)
|
|
13
|
-
switch
|
|
14
|
-
when score < 60 then "F"
|
|
15
|
-
when score < 70 then "D"
|
|
16
|
-
when score < 80 then "C"
|
|
17
|
-
when score < 90 then "B"
|
|
18
|
-
else "A"
|
|
19
|
-
|
|
20
|
-
# With block syntax
|
|
21
|
-
def processDay(day)
|
|
22
|
-
switch day
|
|
23
|
-
when "Mon"
|
|
24
|
-
console.log("Start of week")
|
|
25
|
-
"work mode"
|
|
26
|
-
when "Fri"
|
|
27
|
-
console.log("Almost weekend!")
|
|
28
|
-
"party mode"
|
|
29
|
-
when "Sat", "Sun"
|
|
30
|
-
console.log("Weekend!")
|
|
31
|
-
"relax mode"
|
|
32
|
-
else
|
|
33
|
-
"default mode"
|
|
34
|
-
|
|
35
|
-
# Switch as expression (gets wrapped in IIFE)
|
|
36
|
-
def categorize(value)
|
|
37
|
-
category = switch value
|
|
38
|
-
when 1, 2, 3 then "low"
|
|
39
|
-
when 4, 5, 6 then "medium"
|
|
40
|
-
when 7, 8, 9 then "high"
|
|
41
|
-
else "out of range"
|
|
42
|
-
category
|
|
43
|
-
|
|
44
|
-
# Condition-based for complex logic
|
|
45
|
-
def classify(x, y)
|
|
46
|
-
switch
|
|
47
|
-
when x > 100 && y > 100 then "both large"
|
|
48
|
-
when x > 100 || y > 100 then "one large"
|
|
49
|
-
when x > 0 && y > 0 then "both positive"
|
|
50
|
-
else "other"
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# Ternary Operator Examples
|
|
2
|
-
# Both ? : and if/then/else syntaxes supported!
|
|
3
|
-
|
|
4
|
-
# Basic ternary
|
|
5
|
-
def abs(x)
|
|
6
|
-
result = x < 0 ? (0 - x) : x
|
|
7
|
-
result
|
|
8
|
-
|
|
9
|
-
# Nested ternary for classification
|
|
10
|
-
def classify(n)
|
|
11
|
-
n > 0 ? "positive" : n < 0 ? "negative" : "zero"
|
|
12
|
-
|
|
13
|
-
# Ternary in expressions
|
|
14
|
-
def max(a, b)
|
|
15
|
-
a > b ? a : b
|
|
16
|
-
|
|
17
|
-
# Ternary with objects
|
|
18
|
-
def makePoint(x, y)
|
|
19
|
-
x > 0 && y > 0 ? {x: x, y: y, quadrant: 1} : {x: x, y: y, quadrant: 0}
|
|
20
|
-
|
|
21
|
-
# CoffeeScript-style inline if/then/else
|
|
22
|
-
def greeting(isMorning)
|
|
23
|
-
if isMorning then "Good morning!" else "Good evening!"
|
|
24
|
-
|
|
25
|
-
def compare(a, b)
|
|
26
|
-
if a > b then "greater" else if a < b then "less" else "equal"
|
|
27
|
-
|
|
28
|
-
# Real-world example: validation
|
|
29
|
-
def validateAge(age)
|
|
30
|
-
message = age >= 18 ? "Adult" : age >= 13 ? "Teen" : "Child"
|
|
31
|
-
valid = age >= 0 && age <= 120
|
|
32
|
-
{
|
|
33
|
-
age: age
|
|
34
|
-
category: message
|
|
35
|
-
valid: valid ? "yes" : "no"
|
|
36
|
-
}
|
|
File without changes
|