rip-lang 2.9.2 → 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 -5474
- 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 +529 -488
- package/src/lexer.js +1318 -3035
- 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/BROWSER.md
DELETED
|
@@ -1,990 +0,0 @@
|
|
|
1
|
-
<p><img src="rip.svg" alt="Rip Logo" width="100"></p>
|
|
2
|
-
|
|
3
|
-
# Rip in the Browser
|
|
4
|
-
|
|
5
|
-
**Complete Language + Reactive Runtime in 51KB**
|
|
6
|
-
|
|
7
|
-
Rip compiles to modern JavaScript and runs beautifully in the browser. With brotli compression, the entire package—compiler and reactive runtime—fits in just **51KB**. That's a complete language in less space than most utility libraries!
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 📦 Build Sizes
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
rip.browser.js 587 KB (readable, for debugging)
|
|
15
|
-
rip.browser.min.js 367 KB (minified, 37% smaller)
|
|
16
|
-
rip.browser.min.js.br 51 KB (brotli, 91% smaller!)
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
**What's in that 51KB?**
|
|
20
|
-
- Complete compiler (lexer, parser, code generator)
|
|
21
|
-
- Reactive runtime (state, computed values, effects)
|
|
22
|
-
- Zero dependencies
|
|
23
|
-
|
|
24
|
-
**For comparison:**
|
|
25
|
-
- React (min+gzip): ~42KB (just the library, no compiler)
|
|
26
|
-
- Vue (min+gzip): ~34KB (just the library, no compiler)
|
|
27
|
-
- Svelte: ~2KB runtime (but requires build step + compiler)
|
|
28
|
-
- **Rip: 51KB (complete language + reactive runtime, runs anywhere!)**
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## 🚀 Quick Start
|
|
33
|
-
|
|
34
|
-
### Build the Browser Bundle
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
bun run build:browser
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
This creates all three versions automatically.
|
|
41
|
-
|
|
42
|
-
### Serve Locally
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
bun run serve
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Then open: `http://localhost:3000/`
|
|
49
|
-
|
|
50
|
-
The server automatically serves `.br` files with brotli encoding to supporting browsers!
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## 🎮 Triple REPL Support
|
|
55
|
-
|
|
56
|
-
**Rip gives you THREE ways to code interactively!**
|
|
57
|
-
|
|
58
|
-
### 1. Terminal REPL
|
|
59
|
-
|
|
60
|
-
Full-featured command-line REPL with persistent history:
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
# Just run rip with no arguments
|
|
64
|
-
./bin/rip
|
|
65
|
-
|
|
66
|
-
# Or explicitly request REPL
|
|
67
|
-
./bin/rip -r
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
**Features:**
|
|
71
|
-
- ✅ Variable persistence across evaluations
|
|
72
|
-
- ✅ Multi-line input (automatic detection)
|
|
73
|
-
- ✅ Command history with arrow keys (saved to `~/.rip_history`)
|
|
74
|
-
- ✅ Special commands (.help, .vars, .clear, .history, .exit)
|
|
75
|
-
- ✅ Debug toggles (.tokens, .sexp, .js)
|
|
76
|
-
- ✅ Pretty printing with colors
|
|
77
|
-
- ✅ `_` variable stores last result
|
|
78
|
-
|
|
79
|
-
**Example Session:**
|
|
80
|
-
```
|
|
81
|
-
Rip 1.0.0 - Interactive REPL
|
|
82
|
-
Type .help for commands, Ctrl+C to exit
|
|
83
|
-
|
|
84
|
-
rip> x = 10
|
|
85
|
-
→ 10
|
|
86
|
-
|
|
87
|
-
rip> y = x + 5
|
|
88
|
-
→ 15
|
|
89
|
-
|
|
90
|
-
rip> console.log "x is #{x}, y is #{y}"
|
|
91
|
-
x is 10, y is 15
|
|
92
|
-
→ undefined
|
|
93
|
-
|
|
94
|
-
rip> # Test heregex
|
|
95
|
-
rip> pattern = ///
|
|
96
|
-
....> \d+ # digits
|
|
97
|
-
....> [a-z]+ # letters
|
|
98
|
-
....> ///
|
|
99
|
-
→ /\d+[a-z]+/
|
|
100
|
-
|
|
101
|
-
rip> pattern.test('123abc')
|
|
102
|
-
→ true
|
|
103
|
-
|
|
104
|
-
rip> .vars
|
|
105
|
-
Defined variables:
|
|
106
|
-
x = 10
|
|
107
|
-
y = 15
|
|
108
|
-
pattern = /\d+[a-z]+/
|
|
109
|
-
_ = true
|
|
110
|
-
|
|
111
|
-
rip> .exit
|
|
112
|
-
Goodbye!
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### 2. Browser REPL
|
|
116
|
-
|
|
117
|
-
**The Ultimate Rip Experience in Your Browser!**
|
|
118
|
-
|
|
119
|
-
```bash
|
|
120
|
-
# Build and serve
|
|
121
|
-
bun run build:browser
|
|
122
|
-
bun run serve
|
|
123
|
-
|
|
124
|
-
# Open in browser
|
|
125
|
-
http://localhost:3000/
|
|
126
|
-
# (auto-redirects to REPL)
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
Visit `docs/repl.html` for a full-featured browser REPL with **two powerful tabs:**
|
|
130
|
-
|
|
131
|
-
#### Tab 1: REPL Console
|
|
132
|
-
|
|
133
|
-
Terminal-like interactive environment:
|
|
134
|
-
|
|
135
|
-
```
|
|
136
|
-
rip> x = 42
|
|
137
|
-
→ 42
|
|
138
|
-
|
|
139
|
-
rip> pattern = ///
|
|
140
|
-
....> \d+ # digits
|
|
141
|
-
....> [a-z]+ # letters
|
|
142
|
-
....> ///
|
|
143
|
-
→ /\d+[a-z]+/
|
|
144
|
-
|
|
145
|
-
rip> "user@example.com"[/@(.+)$/, 1]
|
|
146
|
-
→ "example.com"
|
|
147
|
-
|
|
148
|
-
rip> .vars
|
|
149
|
-
Variables:
|
|
150
|
-
x = 42
|
|
151
|
-
pattern = /\d+[a-z]+/
|
|
152
|
-
_ = "example.com"
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
**Features:**
|
|
156
|
-
- ✅ Multi-line input (automatic detection)
|
|
157
|
-
- ✅ Command history (↑↓ arrow keys)
|
|
158
|
-
- ✅ Special commands (.help, .vars, .sexp, .tokens, .clear)
|
|
159
|
-
- ✅ Variable persistence across evaluations
|
|
160
|
-
- ✅ `_` variable for last result
|
|
161
|
-
- ✅ Colored output (prompts, results, errors)
|
|
162
|
-
- ✅ Auto-scrolling
|
|
163
|
-
|
|
164
|
-
#### Tab 2: Live Compiler
|
|
165
|
-
|
|
166
|
-
Split-pane editor showing real-time compilation:
|
|
167
|
-
|
|
168
|
-
**Left Pane (Rip):**
|
|
169
|
-
```coffee
|
|
170
|
-
def fibonacci(n)
|
|
171
|
-
if n <= 1
|
|
172
|
-
n
|
|
173
|
-
else
|
|
174
|
-
fibonacci(n - 1) + fibonacci(n - 2)
|
|
175
|
-
|
|
176
|
-
pattern = ///
|
|
177
|
-
^ \d+ # starts with digits
|
|
178
|
-
\s* # optional space
|
|
179
|
-
[a-z]+ # followed by letters
|
|
180
|
-
$
|
|
181
|
-
///i
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
**Right Pane (JavaScript - updates as you type!):**
|
|
185
|
-
```javascript
|
|
186
|
-
function fibonacci(n) {
|
|
187
|
-
if ((n <= 1)) {
|
|
188
|
-
return n;
|
|
189
|
-
} else {
|
|
190
|
-
return (fibonacci((n - 1)) + fibonacci((n - 2)));
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
let pattern;
|
|
195
|
-
|
|
196
|
-
pattern = /^\d+\s*[a-z]+$/i;
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
**Features:**
|
|
200
|
-
- ✅ Live compilation (instant feedback)
|
|
201
|
-
- ✅ Toggle s-expression display
|
|
202
|
-
- ✅ Toggle token display
|
|
203
|
-
- ✅ Syntax highlighting
|
|
204
|
-
- ✅ Beautiful dark theme
|
|
205
|
-
- ✅ All Rip features work
|
|
206
|
-
|
|
207
|
-
### 3. Console REPL
|
|
208
|
-
|
|
209
|
-
**Quick tests in any browser console!**
|
|
210
|
-
|
|
211
|
-
The browser bundle exports a global `rip()` function:
|
|
212
|
-
|
|
213
|
-
```javascript
|
|
214
|
-
// In browser console (F12) - after loading Rip
|
|
215
|
-
rip('x = 42') // → 42
|
|
216
|
-
rip('x + 10') // → 52
|
|
217
|
-
rip('"test"[/e(s)t/, 1]') // → "s"
|
|
218
|
-
rip('_') // → "s" (last result)
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
**Features:**
|
|
222
|
-
- ✅ Global `rip()` function
|
|
223
|
-
- ✅ Variable persistence (stored as globals)
|
|
224
|
-
- ✅ `_` variable for last result
|
|
225
|
-
- ✅ Perfect for quick experiments
|
|
226
|
-
|
|
227
|
-
---
|
|
228
|
-
|
|
229
|
-
## 💡 Usage Patterns
|
|
230
|
-
|
|
231
|
-
### Pattern 1: Inline Rip Execution
|
|
232
|
-
|
|
233
|
-
Write Rip directly in your HTML - it executes automatically:
|
|
234
|
-
|
|
235
|
-
```html
|
|
236
|
-
<!DOCTYPE html>
|
|
237
|
-
<html>
|
|
238
|
-
<head>
|
|
239
|
-
<title>My Rip App</title>
|
|
240
|
-
</head>
|
|
241
|
-
<body>
|
|
242
|
-
<h1>Hello from Rip!</h1>
|
|
243
|
-
<div id="output"></div>
|
|
244
|
-
|
|
245
|
-
<!-- Write Rip code directly! -->
|
|
246
|
-
<script type="text/rip">
|
|
247
|
-
# Define functions in Rip
|
|
248
|
-
def greet(name)
|
|
249
|
-
"Hello, #{name}! 👋"
|
|
250
|
-
|
|
251
|
-
# Use heregex for readable patterns
|
|
252
|
-
emailPattern = ///
|
|
253
|
-
^([^@]+) # username
|
|
254
|
-
@ # at sign
|
|
255
|
-
([^@]+) # domain
|
|
256
|
-
$
|
|
257
|
-
///
|
|
258
|
-
|
|
259
|
-
# Ruby-style regex matching
|
|
260
|
-
email = "user@example.com"
|
|
261
|
-
if email =~ emailPattern
|
|
262
|
-
username = _[1]
|
|
263
|
-
domain = _[2]
|
|
264
|
-
document.getElementById('output').textContent =
|
|
265
|
-
"User: #{username}, Domain: #{domain}"
|
|
266
|
-
</script>
|
|
267
|
-
|
|
268
|
-
<!-- Load Rip compiler (must come after Rip scripts) -->
|
|
269
|
-
<script type="module" src="https://your-cdn.com/rip.browser.min.js"></script>
|
|
270
|
-
</body>
|
|
271
|
-
</html>
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
**The Rip code runs automatically when the page loads!**
|
|
275
|
-
|
|
276
|
-
### Pattern 2: Interactive Compiler
|
|
277
|
-
|
|
278
|
-
Use Rip to compile code on-demand in the browser:
|
|
279
|
-
|
|
280
|
-
```html
|
|
281
|
-
<script type="module">
|
|
282
|
-
import { compile } from './dist/rip.browser.min.js';
|
|
283
|
-
|
|
284
|
-
const ripCode = `
|
|
285
|
-
def fibonacci(n)
|
|
286
|
-
if n <= 1
|
|
287
|
-
n
|
|
288
|
-
else
|
|
289
|
-
fibonacci(n - 1) + fibonacci(n - 2)
|
|
290
|
-
`;
|
|
291
|
-
|
|
292
|
-
const result = compile(ripCode);
|
|
293
|
-
console.log(result.code); // Generated JavaScript
|
|
294
|
-
|
|
295
|
-
// Execute it
|
|
296
|
-
eval(result.code);
|
|
297
|
-
console.log(fibonacci(10)); // 55
|
|
298
|
-
</script>
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
### Pattern 3: Dynamic Code Generation
|
|
302
|
-
|
|
303
|
-
Build tools, playgrounds, or educational apps:
|
|
304
|
-
|
|
305
|
-
```html
|
|
306
|
-
<script type="module">
|
|
307
|
-
import { compile } from './dist/rip.browser.min.js';
|
|
308
|
-
|
|
309
|
-
// Live code editor
|
|
310
|
-
document.getElementById('compile-btn').addEventListener('click', () => {
|
|
311
|
-
const source = document.getElementById('editor').value;
|
|
312
|
-
|
|
313
|
-
try {
|
|
314
|
-
const { code, sexpr, tokens } = compile(source);
|
|
315
|
-
|
|
316
|
-
document.getElementById('js-output').textContent = code;
|
|
317
|
-
document.getElementById('sexp-output').textContent =
|
|
318
|
-
JSON.stringify(sexpr, null, 2);
|
|
319
|
-
|
|
320
|
-
// Show tokens for learning
|
|
321
|
-
document.getElementById('tokens-output').textContent =
|
|
322
|
-
tokens.map(t => `${t[0]}: ${t[1]}`).join('\n');
|
|
323
|
-
|
|
324
|
-
} catch (error) {
|
|
325
|
-
document.getElementById('error').textContent = error.message;
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
</script>
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
---
|
|
332
|
-
|
|
333
|
-
## 🎨 What Works in the Browser
|
|
334
|
-
|
|
335
|
-
### All Rip Features Available
|
|
336
|
-
|
|
337
|
-
**✅ Modern Syntax:**
|
|
338
|
-
```coffee
|
|
339
|
-
# Destructuring
|
|
340
|
-
{name, age} = person
|
|
341
|
-
[first, ...rest] = array
|
|
342
|
-
|
|
343
|
-
# Optional chaining (dual syntax)
|
|
344
|
-
user?.profile?.name
|
|
345
|
-
arr?[0]
|
|
346
|
-
|
|
347
|
-
# Nullish coalescing
|
|
348
|
-
port = config.port ?? 8080
|
|
349
|
-
|
|
350
|
-
# Heregex
|
|
351
|
-
pattern = ///
|
|
352
|
-
^ \d+ # starts with digits
|
|
353
|
-
\s* # whitespace
|
|
354
|
-
[a-z]+ # letters
|
|
355
|
-
$
|
|
356
|
-
///gi
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
**✅ Ruby-Style Regex:**
|
|
360
|
-
```coffee
|
|
361
|
-
# Match operator
|
|
362
|
-
email =~ /(.+)@(.+)/
|
|
363
|
-
domain = _[2]
|
|
364
|
-
|
|
365
|
-
# Regex indexing
|
|
366
|
-
zip = "12345-6789"[/^(\d{5})/, 1] # "12345"
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
**✅ Functions:**
|
|
370
|
-
```coffee
|
|
371
|
-
# Three styles
|
|
372
|
-
def add(a, b) # Hoisted
|
|
373
|
-
a + b
|
|
374
|
-
|
|
375
|
-
multiply = (a, b) -> # Unbound this
|
|
376
|
-
a * b
|
|
377
|
-
|
|
378
|
-
divide = (a, b) => # Bound this
|
|
379
|
-
a / b
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
**✅ Classes:**
|
|
383
|
-
```coffee
|
|
384
|
-
class Person
|
|
385
|
-
constructor: (@name, @age) ->
|
|
386
|
-
|
|
387
|
-
greet: ->
|
|
388
|
-
"Hi, I'm #{@name}!"
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
**✅ Async/Await:**
|
|
392
|
-
```coffee
|
|
393
|
-
# Auto-detected!
|
|
394
|
-
fetchData = ->
|
|
395
|
-
response = await fetch(url)
|
|
396
|
-
await response.json()
|
|
397
|
-
|
|
398
|
-
# Or use dammit operator
|
|
399
|
-
getData = ->
|
|
400
|
-
result = fetchData!
|
|
401
|
-
result
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
**✅ String Interpolation:**
|
|
405
|
-
```coffee
|
|
406
|
-
name = "World"
|
|
407
|
-
message = "Hello, #{name}!"
|
|
408
|
-
|
|
409
|
-
# Heredocs
|
|
410
|
-
text = """
|
|
411
|
-
Multi-line
|
|
412
|
-
strings
|
|
413
|
-
"""
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
---
|
|
417
|
-
|
|
418
|
-
## 🌐 CDN Deployment
|
|
419
|
-
|
|
420
|
-
### Recommended Setup
|
|
421
|
-
|
|
422
|
-
**1. Build for production:**
|
|
423
|
-
```bash
|
|
424
|
-
bun run build:browser
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
**2. Upload to CDN:**
|
|
428
|
-
```bash
|
|
429
|
-
# Upload all three versions
|
|
430
|
-
aws s3 cp docs/dist/rip.browser.js s3://your-bucket/
|
|
431
|
-
aws s3 cp docs/dist/rip.browser.min.js s3://your-bucket/
|
|
432
|
-
aws s3 cp docs/dist/rip.browser.min.js.br s3://your-bucket/
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
**3. Configure CDN headers:**
|
|
436
|
-
```nginx
|
|
437
|
-
# For .br files
|
|
438
|
-
location ~ \.js\.br$ {
|
|
439
|
-
add_header Content-Encoding br;
|
|
440
|
-
add_header Content-Type application/javascript;
|
|
441
|
-
add_header Cache-Control "public, max-age=31536000";
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
# For regular .js files
|
|
445
|
-
location ~ \.js$ {
|
|
446
|
-
add_header Content-Type application/javascript;
|
|
447
|
-
add_header Cache-Control "public, max-age=31536000";
|
|
448
|
-
}
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
**4. Use in HTML:**
|
|
452
|
-
```html
|
|
453
|
-
<!-- Browsers with brotli support get 51KB version automatically -->
|
|
454
|
-
<script type="module" src="https://cdn.example.com/rip.browser.min.js"></script>
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
---
|
|
458
|
-
|
|
459
|
-
## 📊 Performance
|
|
460
|
-
|
|
461
|
-
### Bundle Size Breakdown
|
|
462
|
-
|
|
463
|
-
| Version | Size | Reduction | Use Case |
|
|
464
|
-
|---------|------|-----------|----------|
|
|
465
|
-
| **rip.browser.js** | 587 KB | - | Development, debugging |
|
|
466
|
-
| **rip.browser.min.js** | 367 KB | 37% | Production without brotli |
|
|
467
|
-
| **rip.browser.min.js.br** | 51 KB | **91%** | Production with brotli ✨ |
|
|
468
|
-
|
|
469
|
-
### Load Times
|
|
470
|
-
|
|
471
|
-
**On 3G connection (750 Kbps):**
|
|
472
|
-
- Unminified: ~6.5 seconds
|
|
473
|
-
- Minified: ~4.3 seconds
|
|
474
|
-
- **Brotli: ~0.47 seconds** ⚡
|
|
475
|
-
|
|
476
|
-
**On Cable (5 Mbps):**
|
|
477
|
-
- Unminified: ~1 second
|
|
478
|
-
- Minified: ~640ms
|
|
479
|
-
- **Brotli: ~70ms** 🚀
|
|
480
|
-
|
|
481
|
-
**Brotli support:** ~95% of browsers (all modern browsers)
|
|
482
|
-
|
|
483
|
-
---
|
|
484
|
-
|
|
485
|
-
## 🎯 Use Cases
|
|
486
|
-
|
|
487
|
-
### 1. Interactive Playgrounds
|
|
488
|
-
|
|
489
|
-
Build Rip learning environments:
|
|
490
|
-
```html
|
|
491
|
-
<textarea id="editor">def greet(name)
|
|
492
|
-
"Hello, #{name}!"</textarea>
|
|
493
|
-
<button id="run">Run</button>
|
|
494
|
-
<pre id="output"></pre>
|
|
495
|
-
|
|
496
|
-
<script type="module">
|
|
497
|
-
import { compile } from './rip.browser.min.js';
|
|
498
|
-
|
|
499
|
-
document.getElementById('run').onclick = () => {
|
|
500
|
-
const code = compile(editor.value).code;
|
|
501
|
-
output.textContent = eval(code);
|
|
502
|
-
};
|
|
503
|
-
</script>
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
### 2. Client-Side Templating
|
|
507
|
-
|
|
508
|
-
Use Rip for dynamic content generation:
|
|
509
|
-
```html
|
|
510
|
-
<script type="text/rip">
|
|
511
|
-
# Data from API
|
|
512
|
-
users = await fetch('/api/users').then(r -> r.json())
|
|
513
|
-
|
|
514
|
-
# Generate HTML with comprehensions
|
|
515
|
-
html = ("""
|
|
516
|
-
<div class="user">
|
|
517
|
-
<h3>#{user.name}</h3>
|
|
518
|
-
<p>#{user.email}</p>
|
|
519
|
-
</div>
|
|
520
|
-
""" for user in users).join('')
|
|
521
|
-
|
|
522
|
-
document.getElementById('users').innerHTML = html
|
|
523
|
-
</script>
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
### 3. Form Validation
|
|
527
|
-
|
|
528
|
-
Elegant validators in the browser:
|
|
529
|
-
```html
|
|
530
|
-
<script type="text/rip">
|
|
531
|
-
validators =
|
|
532
|
-
email: (v) -> v[/^[^@]+@[^@]+\.[a-z]{2,}$/i] and _[0]
|
|
533
|
-
phone: (v) -> v[/^(\d{10})$/] and _[1]
|
|
534
|
-
zip: (v) -> v[/^(\d{5})(-\d{4})?$/, 1] and _[1]
|
|
535
|
-
|
|
536
|
-
def validate(field, value)
|
|
537
|
-
validator = validators[field]
|
|
538
|
-
if validator
|
|
539
|
-
result = validator(value)
|
|
540
|
-
if result
|
|
541
|
-
{valid: true, normalized: result}
|
|
542
|
-
else
|
|
543
|
-
{valid: false, error: "Invalid #{field}"}
|
|
544
|
-
else
|
|
545
|
-
{valid: true, normalized: value}
|
|
546
|
-
</script>
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
### 4. Dynamic Dashboards
|
|
550
|
-
|
|
551
|
-
Real-time data processing with clean syntax:
|
|
552
|
-
```html
|
|
553
|
-
<script type="text/rip">
|
|
554
|
-
# WebSocket connection
|
|
555
|
-
ws = new WebSocket('ws://localhost:8080')
|
|
556
|
-
|
|
557
|
-
ws.onmessage = (event) ->
|
|
558
|
-
data = JSON.parse(event.data)
|
|
559
|
-
|
|
560
|
-
# Extract with regex
|
|
561
|
-
if data.message =~ /ERROR: (.+)/
|
|
562
|
-
showAlert(_[1])
|
|
563
|
-
else if data.message =~ /SUCCESS: (.+)/
|
|
564
|
-
showSuccess(_[1])
|
|
565
|
-
|
|
566
|
-
# Update UI
|
|
567
|
-
updateDashboard(data)
|
|
568
|
-
</script>
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
---
|
|
572
|
-
|
|
573
|
-
## 🔒 Security Considerations
|
|
574
|
-
|
|
575
|
-
### Safe Regex Matching
|
|
576
|
-
|
|
577
|
-
The `toSearchable()` helper includes injection protection:
|
|
578
|
-
|
|
579
|
-
```coffee
|
|
580
|
-
# Safe by default - rejects newlines
|
|
581
|
-
userInput =~ /^[a-z]+$/ # Returns null if input has \n
|
|
582
|
-
|
|
583
|
-
# Explicit multiline when needed
|
|
584
|
-
text =~ /pattern/m # Allows newlines with /m flag
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
### Sandboxed Execution
|
|
588
|
-
|
|
589
|
-
Browser environment is naturally sandboxed:
|
|
590
|
-
- No file system access
|
|
591
|
-
- No process execution
|
|
592
|
-
- Same-origin policy applies
|
|
593
|
-
|
|
594
|
-
---
|
|
595
|
-
|
|
596
|
-
## 📚 REPL Examples
|
|
597
|
-
|
|
598
|
-
### Basic Operations
|
|
599
|
-
|
|
600
|
-
```
|
|
601
|
-
rip> x = 42
|
|
602
|
-
→ 42
|
|
603
|
-
|
|
604
|
-
rip> doubled = x * 2
|
|
605
|
-
→ 84
|
|
606
|
-
|
|
607
|
-
rip> [1..5]
|
|
608
|
-
→ [1, 2, 3, 4, 5]
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
### Heregex
|
|
612
|
-
|
|
613
|
-
```
|
|
614
|
-
rip> pattern = ///
|
|
615
|
-
....> ^ \d+ # starts with digits
|
|
616
|
-
....> \s* # optional whitespace
|
|
617
|
-
....> [a-z]+ # followed by letters
|
|
618
|
-
....> $ # end of string
|
|
619
|
-
....> ///gi
|
|
620
|
-
→ /^\d+\s*[a-z]+$/gi
|
|
621
|
-
|
|
622
|
-
rip> pattern.test('123abc')
|
|
623
|
-
→ true
|
|
624
|
-
```
|
|
625
|
-
|
|
626
|
-
### Ruby-Style Regex
|
|
627
|
-
|
|
628
|
-
```
|
|
629
|
-
rip> email = "user@example.com"
|
|
630
|
-
→ "user@example.com"
|
|
631
|
-
|
|
632
|
-
rip> email =~ /(.+)@(.+)/
|
|
633
|
-
→ ["user@example.com", "user", "example.com"]
|
|
634
|
-
|
|
635
|
-
rip> username = _[1]
|
|
636
|
-
→ "user"
|
|
637
|
-
|
|
638
|
-
rip> domain = _[2]
|
|
639
|
-
→ "example.com"
|
|
640
|
-
|
|
641
|
-
rip> # Inline extraction
|
|
642
|
-
rip> "12345-6789"[/^(\d{5})/, 1]
|
|
643
|
-
→ "12345"
|
|
644
|
-
```
|
|
645
|
-
|
|
646
|
-
### Functions and Classes
|
|
647
|
-
|
|
648
|
-
```
|
|
649
|
-
rip> def factorial(n)
|
|
650
|
-
....> if n <= 1
|
|
651
|
-
....> 1
|
|
652
|
-
....> else
|
|
653
|
-
....> n * factorial(n - 1)
|
|
654
|
-
→ [Function: factorial]
|
|
655
|
-
|
|
656
|
-
rip> factorial(5)
|
|
657
|
-
→ 120
|
|
658
|
-
|
|
659
|
-
rip> class Point
|
|
660
|
-
....> constructor: (@x, @y) ->
|
|
661
|
-
....> distance: ->
|
|
662
|
-
....> Math.sqrt(@x**2 + @y**2)
|
|
663
|
-
→ [class Point]
|
|
664
|
-
|
|
665
|
-
rip> p = new Point(3, 4)
|
|
666
|
-
→ Point { x: 3, y: 4 }
|
|
667
|
-
|
|
668
|
-
rip> p.distance()
|
|
669
|
-
→ 5
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
### REPL Commands
|
|
673
|
-
|
|
674
|
-
```
|
|
675
|
-
rip> .help
|
|
676
|
-
Rip REPL Commands:
|
|
677
|
-
|
|
678
|
-
Special Commands:
|
|
679
|
-
.help Show this help message
|
|
680
|
-
.clear Clear the context (reset all variables)
|
|
681
|
-
.vars Show all defined variables
|
|
682
|
-
.history Show command history
|
|
683
|
-
.exit Exit the REPL (or Ctrl+C)
|
|
684
|
-
|
|
685
|
-
Debug Toggles:
|
|
686
|
-
.tokens Toggle token stream display
|
|
687
|
-
.sexp Toggle s-expression display
|
|
688
|
-
.js Toggle compiled JavaScript display
|
|
689
|
-
|
|
690
|
-
Tips:
|
|
691
|
-
- Multi-line input is supported (press Enter mid-expression)
|
|
692
|
-
- Use Tab for history navigation
|
|
693
|
-
- Previous results stored in _ variable
|
|
694
|
-
- Use Ctrl+C to cancel multi-line input or exit
|
|
695
|
-
```
|
|
696
|
-
|
|
697
|
-
---
|
|
698
|
-
|
|
699
|
-
## 🔧 API Reference
|
|
700
|
-
|
|
701
|
-
### compile(source, options)
|
|
702
|
-
|
|
703
|
-
```javascript
|
|
704
|
-
import { compile } from './rip.browser.min.js';
|
|
705
|
-
|
|
706
|
-
const result = compile('x = 42');
|
|
707
|
-
|
|
708
|
-
// Returns:
|
|
709
|
-
{
|
|
710
|
-
tokens: [[...], ...], // Lexer tokens
|
|
711
|
-
sexpr: [...], // S-expressions
|
|
712
|
-
code: "let x;\n\nx = 42;", // JavaScript
|
|
713
|
-
data: null // __DATA__ section if present
|
|
714
|
-
}
|
|
715
|
-
```
|
|
716
|
-
|
|
717
|
-
### compileToJS(source, options)
|
|
718
|
-
|
|
719
|
-
```javascript
|
|
720
|
-
import { compileToJS } from './rip.browser.min.js';
|
|
721
|
-
|
|
722
|
-
const js = compileToJS('def add(a, b)\n a + b');
|
|
723
|
-
// Returns: "function add(a, b) { return (a + b); }"
|
|
724
|
-
```
|
|
725
|
-
|
|
726
|
-
### Auto-Execution
|
|
727
|
-
|
|
728
|
-
Just load the script - it automatically finds and compiles all `<script type="text/rip">` tags:
|
|
729
|
-
|
|
730
|
-
```html
|
|
731
|
-
<script type="text/rip">
|
|
732
|
-
console.log "This runs automatically!"
|
|
733
|
-
</script>
|
|
734
|
-
|
|
735
|
-
<script type="module" src="./rip.browser.min.js"></script>
|
|
736
|
-
```
|
|
737
|
-
|
|
738
|
-
### rip() Function (Console REPL)
|
|
739
|
-
|
|
740
|
-
```javascript
|
|
741
|
-
// Globally available after loading browser bundle
|
|
742
|
-
rip('x = 42') // Returns 42, stores in global x
|
|
743
|
-
rip('x * 2') // Returns 84
|
|
744
|
-
rip('[1..10]') // Returns [1, 2, 3, ..., 10]
|
|
745
|
-
```
|
|
746
|
-
|
|
747
|
-
---
|
|
748
|
-
|
|
749
|
-
## 🎨 Advanced Examples
|
|
750
|
-
|
|
751
|
-
### Real-Time Data Processing
|
|
752
|
-
|
|
753
|
-
```html
|
|
754
|
-
<script type="text/rip">
|
|
755
|
-
# Parse server logs with regex
|
|
756
|
-
def parseLog(line)
|
|
757
|
-
if line =~ /\[(\w+)\] (.+?) - (.+)$/
|
|
758
|
-
level: _[1]
|
|
759
|
-
timestamp: _[2]
|
|
760
|
-
message: _[3]
|
|
761
|
-
else
|
|
762
|
-
null
|
|
763
|
-
|
|
764
|
-
# Process stream
|
|
765
|
-
ws = new WebSocket('ws://localhost:8080/logs')
|
|
766
|
-
ws.onmessage = (event) ->
|
|
767
|
-
parsed = parseLog(event.data)
|
|
768
|
-
if parsed?.level is 'ERROR'
|
|
769
|
-
showAlert(parsed.message)
|
|
770
|
-
</script>
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
### Form Validation
|
|
774
|
-
|
|
775
|
-
```html
|
|
776
|
-
<script type="text/rip">
|
|
777
|
-
validators =
|
|
778
|
-
email: (v) ->
|
|
779
|
-
v[/^([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/] and _[0]
|
|
780
|
-
|
|
781
|
-
phone: (v) ->
|
|
782
|
-
if v =~ /^(\d{3})(\d{3})(\d{4})$/
|
|
783
|
-
"(#{_[1]}) #{_[2]}-#{_[3]}"
|
|
784
|
-
else
|
|
785
|
-
null
|
|
786
|
-
|
|
787
|
-
zip: (v) ->
|
|
788
|
-
v[/^(\d{5})(-\d{4})?$/] and _[1] + (if _[2] then _[2] else "")
|
|
789
|
-
|
|
790
|
-
def validateForm(form)
|
|
791
|
-
errors = []
|
|
792
|
-
|
|
793
|
-
for field in ['email', 'phone', 'zip']
|
|
794
|
-
value = form[field]
|
|
795
|
-
validator = validators[field]
|
|
796
|
-
unless validator(value)
|
|
797
|
-
errors.push "Invalid #{field}"
|
|
798
|
-
|
|
799
|
-
if errors.length is 0
|
|
800
|
-
{valid: true}
|
|
801
|
-
else
|
|
802
|
-
{valid: false, errors: errors}
|
|
803
|
-
</script>
|
|
804
|
-
```
|
|
805
|
-
|
|
806
|
-
### Data Transformation
|
|
807
|
-
|
|
808
|
-
```html
|
|
809
|
-
<script type="text/rip">
|
|
810
|
-
# Transform API response
|
|
811
|
-
def normalizeUsers(users)
|
|
812
|
-
for user in users
|
|
813
|
-
# Extract name parts
|
|
814
|
-
fullName = user.name
|
|
815
|
-
if fullName =~ /^(\w+)\s+(\w+)$/
|
|
816
|
-
firstName: _[1]
|
|
817
|
-
lastName: _[2]
|
|
818
|
-
else
|
|
819
|
-
firstName: fullName
|
|
820
|
-
lastName: ""
|
|
821
|
-
|
|
822
|
-
# Normalize email
|
|
823
|
-
email: user.email.toLowerCase()
|
|
824
|
-
|
|
825
|
-
# Format phone
|
|
826
|
-
phone: if user.phone =~ /(\d{10})/
|
|
827
|
-
"(#{_[1][0..2]}) #{_[1][3..5]}-#{_[1][6..9]}"
|
|
828
|
-
else
|
|
829
|
-
user.phone
|
|
830
|
-
</script>
|
|
831
|
-
```
|
|
832
|
-
|
|
833
|
-
---
|
|
834
|
-
|
|
835
|
-
## 🚀 Performance Tips
|
|
836
|
-
|
|
837
|
-
### 1. Use Brotli
|
|
838
|
-
|
|
839
|
-
Always use `.min.js.br` with proper Content-Encoding:
|
|
840
|
-
```nginx
|
|
841
|
-
Content-Encoding: br
|
|
842
|
-
Content-Type: application/javascript
|
|
843
|
-
```
|
|
844
|
-
|
|
845
|
-
**Saves 89% bandwidth!**
|
|
846
|
-
|
|
847
|
-
### 2. Cache Aggressively
|
|
848
|
-
|
|
849
|
-
The compiler is deterministic - same input = same output:
|
|
850
|
-
```nginx
|
|
851
|
-
Cache-Control: public, max-age=31536000, immutable
|
|
852
|
-
```
|
|
853
|
-
|
|
854
|
-
### 3. Precompile for Production
|
|
855
|
-
|
|
856
|
-
For best performance, precompile to JavaScript during build:
|
|
857
|
-
```bash
|
|
858
|
-
# Development: inline Rip (51KB framework overhead)
|
|
859
|
-
<script type="text/rip">...</script>
|
|
860
|
-
|
|
861
|
-
# Production: precompiled JavaScript (no compiler needed)
|
|
862
|
-
<script>... compiled JS ...</script>
|
|
863
|
-
```
|
|
864
|
-
|
|
865
|
-
Use browser builds for:
|
|
866
|
-
- Rapid prototyping
|
|
867
|
-
- Interactive tools
|
|
868
|
-
- Educational demos
|
|
869
|
-
- Dynamic code generation
|
|
870
|
-
|
|
871
|
-
---
|
|
872
|
-
|
|
873
|
-
## 📚 Examples
|
|
874
|
-
|
|
875
|
-
See the `docs/examples/` directory for working demos:
|
|
876
|
-
|
|
877
|
-
**index.html** - Landing page with REPL redirect
|
|
878
|
-
|
|
879
|
-
**repl.html** - Interactive REPL and Live Compiler
|
|
880
|
-
- REPL Console tab (terminal-like with commands and history)
|
|
881
|
-
- Live Compiler tab (resizable split panes)
|
|
882
|
-
- Beautiful canonical s-expression display
|
|
883
|
-
- Tokens, s-expressions, and JavaScript views
|
|
884
|
-
- All features working
|
|
885
|
-
|
|
886
|
-
**example.html** - Auto-executing Rip scripts
|
|
887
|
-
- Multiple `<script type="text/rip">` blocks
|
|
888
|
-
- Functions available globally
|
|
889
|
-
- Interactive buttons calling Rip functions
|
|
890
|
-
- Demonstrates inline script execution
|
|
891
|
-
|
|
892
|
-
---
|
|
893
|
-
|
|
894
|
-
## 🌟 Why Rip in the Browser?
|
|
895
|
-
|
|
896
|
-
**1. Incredible Value**
|
|
897
|
-
- 51KB delivers compiler + reactive runtime + templates + components
|
|
898
|
-
- More features than most frameworks, smaller than most libraries
|
|
899
|
-
- Fast loading even on mobile
|
|
900
|
-
|
|
901
|
-
**2. Complete Language**
|
|
902
|
-
- Reactive primitives (state, computed, effects)
|
|
903
|
-
- Modern ES6+ output
|
|
904
|
-
- Framework-agnostic — use with any UI library
|
|
905
|
-
|
|
906
|
-
**3. Zero Build Step**
|
|
907
|
-
- Write Rip directly in HTML
|
|
908
|
-
- Auto-compiles on page load
|
|
909
|
-
- Perfect for prototyping
|
|
910
|
-
|
|
911
|
-
**4. Developer Experience**
|
|
912
|
-
- Clean, expressive syntax
|
|
913
|
-
- Readable code
|
|
914
|
-
- Interactive debugging
|
|
915
|
-
- Three REPL modes!
|
|
916
|
-
|
|
917
|
-
**5. Production Ready**
|
|
918
|
-
- Well-tested (1046/1046 tests passing)
|
|
919
|
-
- ES6+ output
|
|
920
|
-
- Reliable compilation
|
|
921
|
-
- Self-hosting compiler
|
|
922
|
-
|
|
923
|
-
---
|
|
924
|
-
|
|
925
|
-
## 📦 Installation
|
|
926
|
-
|
|
927
|
-
### From CDN (Coming Soon)
|
|
928
|
-
|
|
929
|
-
```html
|
|
930
|
-
<script type="module" src="https://unpkg.com/rip-lang/dist/rip.browser.min.js"></script>
|
|
931
|
-
```
|
|
932
|
-
|
|
933
|
-
### Self-Hosted
|
|
934
|
-
|
|
935
|
-
```html
|
|
936
|
-
<script type="module" src="/path/to/rip.browser.min.js"></script>
|
|
937
|
-
```
|
|
938
|
-
|
|
939
|
-
### NPM Package
|
|
940
|
-
|
|
941
|
-
```bash
|
|
942
|
-
npm install rip-lang
|
|
943
|
-
```
|
|
944
|
-
|
|
945
|
-
Then serve `node_modules/rip-lang/dist/rip.browser.min.js`
|
|
946
|
-
|
|
947
|
-
---
|
|
948
|
-
|
|
949
|
-
## 📖 Documentation
|
|
950
|
-
|
|
951
|
-
**Learn More:**
|
|
952
|
-
- [README.md](../README-ORIG.md) - Getting started
|
|
953
|
-
- [AGENT.md](../AGENT-ORIG.md) - AI developer guide
|
|
954
|
-
- [REGEX-PLUS.md](REGEX-PLUS.md) - Regex features guide
|
|
955
|
-
- [COMPREHENSIONS.md](COMPREHENSIONS.md) - Context-aware comprehensions
|
|
956
|
-
- [DAMMIT-OPERATOR.md](DAMMIT-OPERATOR.md) - Async shorthand syntax
|
|
957
|
-
|
|
958
|
-
---
|
|
959
|
-
|
|
960
|
-
## 🎉 Summary
|
|
961
|
-
|
|
962
|
-
**Rip in the browser gives you:**
|
|
963
|
-
|
|
964
|
-
✅ **51KB** - Complete language + reactive framework (brotli)
|
|
965
|
-
✅ **Reactivity** - State, computed values, effects
|
|
966
|
-
✅ **Templates** - S-expression syntax, fine-grained DOM updates
|
|
967
|
-
✅ **Components** - Props, lifecycle, zero virtual DOM
|
|
968
|
-
✅ **Triple REPL** - Terminal, Browser, Console
|
|
969
|
-
✅ **Modern ES6+** - Output works everywhere
|
|
970
|
-
✅ **Zero build** - Write Rip directly in HTML
|
|
971
|
-
✅ **Auto-execution** - `<script type="text/rip">` just works
|
|
972
|
-
|
|
973
|
-
**Perfect for:**
|
|
974
|
-
- 🎓 Learning and education
|
|
975
|
-
- 🔬 Experimentation and prototyping
|
|
976
|
-
- 🛠️ Building reactive applications
|
|
977
|
-
- 📊 Dynamic dashboards
|
|
978
|
-
- ✅ Client-side validation
|
|
979
|
-
- 🎨 Creative coding
|
|
980
|
-
|
|
981
|
-
---
|
|
982
|
-
|
|
983
|
-
**A complete language and reactive framework in 51KB.** ✨
|
|
984
|
-
|
|
985
|
-
---
|
|
986
|
-
|
|
987
|
-
**See Also:**
|
|
988
|
-
- Examples: `docs/examples/` directory
|
|
989
|
-
- Server: `bun run serve`
|
|
990
|
-
- Build: `bun run build:browser`
|