rip-lang 3.10.5 → 3.10.6
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 +11 -0
- package/README.md +18 -18
- package/bin/rip +9 -20
- package/docs/RIP-INTERNALS.md +14 -14
- package/docs/RIP-LANG.md +218 -70
- package/docs/dist/rip-ui.min.js +97 -97
- package/docs/dist/rip-ui.min.js.br +0 -0
- package/docs/dist/rip.browser.min.js +153 -153
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ All notable changes to Rip will be documented in this file.
|
|
|
7
7
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
8
8
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
9
9
|
|
|
10
|
+
## [3.10.6] - 2026-02-20
|
|
11
|
+
|
|
12
|
+
### CLI — Fix Multiline Argument Corruption
|
|
13
|
+
|
|
14
|
+
- **`execSync` → `spawnSync` in `bin/rip`** — The CLI runner was passing script arguments through a shell via `execSync` with string interpolation. Any argument containing a newline (JSON payloads, heredoc content, multiline strings) was silently split into separate shell commands. Replaced all three `execSync` call sites (`.rip` file execution, compiled temp file execution, and `bin/` script fallback) with `spawnSync` using argument arrays, bypassing the shell entirely. Exit codes use `?? 1` (nullish coalescing) instead of `|| 0` for correct signal-kill handling.
|
|
15
|
+
|
|
16
|
+
### Utilities — `curl.rip` Improvements
|
|
17
|
+
|
|
18
|
+
- **Stdin TTY guard** — `curl.rip` no longer hangs when run interactively without a payload argument. Added `process.stdin.isTTY` check to only read from stdin when piped or heredoc'd.
|
|
19
|
+
- **Documented inline and heredoc modes** — Updated header comments to clearly show both usage patterns.
|
|
20
|
+
|
|
10
21
|
## [3.9.1] - 2026-02-18
|
|
11
22
|
|
|
12
23
|
### Runtime — Reactivity & Reconciliation
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.
|
|
12
|
+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.10.6-blue.svg" alt="Version"></a>
|
|
13
13
|
<a href="#zero-dependencies"><img src="https://img.shields.io/badge/dependencies-ZERO-brightgreen.svg" alt="Dependencies"></a>
|
|
14
14
|
<a href="#"><img src="https://img.shields.io/badge/tests-1%2C243%2F1%2C243-brightgreen.svg" alt="Tests"></a>
|
|
15
15
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
|
|
@@ -290,8 +290,8 @@ See [@rip-lang/ui](packages/ui/) for the full framework: file-based router, reac
|
|
|
290
290
|
| **Dependencies** | Multiple | Zero |
|
|
291
291
|
| **Self-hosting** | No | Yes |
|
|
292
292
|
| **Lexer** | 3,558 LOC | 2,024 LOC |
|
|
293
|
-
| **Compiler** | 10,346 LOC | 3,
|
|
294
|
-
| **Total** | 17,760 LOC | ~
|
|
293
|
+
| **Compiler** | 10,346 LOC | 3,293 LOC |
|
|
294
|
+
| **Total** | 17,760 LOC | ~11,100 LOC |
|
|
295
295
|
|
|
296
296
|
Smaller codebase, modern output, built-in reactivity.
|
|
297
297
|
|
|
@@ -326,7 +326,7 @@ await rip("res = fetch! 'https://api.example.com/todos/1'; res.json!") // → {
|
|
|
326
326
|
|
|
327
327
|
```
|
|
328
328
|
Source -> Lexer -> emitTypes -> Parser -> S-Expressions -> Codegen -> JavaScript
|
|
329
|
-
(1,761) (types.js) (359) ["=", "x", 42] (3,
|
|
329
|
+
(1,761) (types.js) (359) ["=", "x", 42] (3,293) + source map
|
|
330
330
|
```
|
|
331
331
|
|
|
332
332
|
Simple arrays (with `.loc`) instead of AST node classes. The compiler is self-hosting — `bun run parser` rebuilds from source.
|
|
@@ -334,17 +334,17 @@ Simple arrays (with `.loc`) instead of AST node classes. The compiler is self-ho
|
|
|
334
334
|
| Component | File | Lines |
|
|
335
335
|
|-----------|------|-------|
|
|
336
336
|
| Lexer + Rewriter | `src/lexer.js` | 1,761 |
|
|
337
|
-
| Compiler + Codegen | `src/compiler.js` | 3,
|
|
337
|
+
| Compiler + Codegen | `src/compiler.js` | 3,293 |
|
|
338
338
|
| Type System | `src/types.js` | 1,099 |
|
|
339
|
-
| Component System | `src/components.js` | 1,
|
|
340
|
-
| Source Maps | `src/sourcemaps.js` |
|
|
339
|
+
| Component System | `src/components.js` | 1,750 |
|
|
340
|
+
| Source Maps | `src/sourcemaps.js` | 189 |
|
|
341
341
|
| Parser (generated) | `src/parser.js` | 359 |
|
|
342
342
|
| Grammar | `src/grammar/grammar.rip` | 944 |
|
|
343
343
|
| Parser Generator | `src/grammar/solar.rip` | 929 |
|
|
344
344
|
| REPL | `src/repl.js` | 601 |
|
|
345
|
-
| Browser Entry | `src/browser.js` |
|
|
345
|
+
| Browser Entry | `src/browser.js` | 151 |
|
|
346
346
|
| Tags | `src/tags.js` | 62 |
|
|
347
|
-
| **Total** | | **~
|
|
347
|
+
| **Total** | | **~11,138** |
|
|
348
348
|
|
|
349
349
|
---
|
|
350
350
|
|
|
@@ -354,15 +354,15 @@ Rip includes optional packages for full-stack development:
|
|
|
354
354
|
|
|
355
355
|
| Package | Version | Purpose |
|
|
356
356
|
|---------|---------|---------|
|
|
357
|
-
| [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.
|
|
358
|
-
| [@rip-lang/api](packages/api/) | 1.1.
|
|
359
|
-
| [@rip-lang/server](packages/server/) | 1.1.
|
|
360
|
-
| [@rip-lang/db](packages/db/) | 1.
|
|
361
|
-
| [@rip-lang/ui](packages/ui/) | 0.3.
|
|
362
|
-
| [@rip-lang/swarm](packages/swarm/) | 1.1.
|
|
363
|
-
| [@rip-lang/csv](packages/csv/) | 1.1.
|
|
364
|
-
| [@rip-lang/schema](packages/schema/) | 0.2.
|
|
365
|
-
| [VS Code Extension](packages/vscode/) | 0.
|
|
357
|
+
| [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.10.6 | Core language compiler |
|
|
358
|
+
| [@rip-lang/api](packages/api/) | 1.1.10 | HTTP framework (Sinatra-style routing, 37 validators) |
|
|
359
|
+
| [@rip-lang/server](packages/server/) | 1.1.19 | Multi-worker app server (hot reload, HTTPS, mDNS) |
|
|
360
|
+
| [@rip-lang/db](packages/db/) | 1.2.0 | DuckDB server with official UI + ActiveRecord-style client |
|
|
361
|
+
| [@rip-lang/ui](packages/ui/) | 0.3.14 | Zero-build reactive web framework (stash, router, hash routing) |
|
|
362
|
+
| [@rip-lang/swarm](packages/swarm/) | 1.1.4 | Parallel job runner with worker pool |
|
|
363
|
+
| [@rip-lang/csv](packages/csv/) | 1.1.4 | CSV parser + writer |
|
|
364
|
+
| [@rip-lang/schema](packages/schema/) | 0.2.1 | Unified schema → TypeScript types, SQL DDL, validation, ORM |
|
|
365
|
+
| [VS Code Extension](packages/vscode/) | 0.5.0 | Syntax highlighting, type intelligence, source maps |
|
|
366
366
|
|
|
367
367
|
```bash
|
|
368
368
|
bun add -g @rip-lang/db # Installs everything (rip-lang + api + db)
|
package/bin/rip
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import { readFileSync, writeFileSync, existsSync, statSync, unlinkSync } from 'fs';
|
|
4
|
-
import { execSync, spawn } from 'child_process';
|
|
4
|
+
import { execSync, spawnSync, spawn } from 'child_process';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { dirname, basename, join } from 'path';
|
|
7
7
|
import { Compiler } from '../src/compiler.js';
|
|
@@ -189,14 +189,10 @@ async function main() {
|
|
|
189
189
|
const loaderPath = join(__dirname, '../rip-loader.js');
|
|
190
190
|
|
|
191
191
|
if (inputFile.endsWith('.rip')) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
process.exit(0);
|
|
197
|
-
} catch (error) {
|
|
198
|
-
process.exit(error.status || 1);
|
|
199
|
-
}
|
|
192
|
+
const result = spawnSync('bun', ['--preload', loaderPath, inputFile, ...scriptArgs], {
|
|
193
|
+
stdio: 'inherit'
|
|
194
|
+
});
|
|
195
|
+
process.exit(result.status ?? 1);
|
|
200
196
|
} else {
|
|
201
197
|
// Non-.rip files (e.g. shebang scripts): compile, write temp file, execute
|
|
202
198
|
const source = readFileSync(inputFile, 'utf-8');
|
|
@@ -206,7 +202,8 @@ async function main() {
|
|
|
206
202
|
let exitCode = 0;
|
|
207
203
|
try {
|
|
208
204
|
writeFileSync(tmp, result.code);
|
|
209
|
-
|
|
205
|
+
const r = spawnSync('bun', ['--preload', loaderPath, tmp, ...scriptArgs], { stdio: 'inherit' });
|
|
206
|
+
exitCode = r.status ?? 1;
|
|
210
207
|
} catch (error) {
|
|
211
208
|
exitCode = error.status || 1;
|
|
212
209
|
}
|
|
@@ -230,16 +227,8 @@ async function main() {
|
|
|
230
227
|
const binScript = join(repoRoot, 'bin', inputFile);
|
|
231
228
|
|
|
232
229
|
if (existsSync(binScript)) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
execSync(`"${binScript}" ${scriptArgs.join(' ')}`, {
|
|
236
|
-
stdio: 'inherit',
|
|
237
|
-
shell: true
|
|
238
|
-
});
|
|
239
|
-
process.exit(0);
|
|
240
|
-
} catch (error) {
|
|
241
|
-
process.exit(error.status || 1);
|
|
242
|
-
}
|
|
230
|
+
const r = spawnSync(binScript, scriptArgs, { stdio: 'inherit' });
|
|
231
|
+
process.exit(r.status ?? 1);
|
|
243
232
|
}
|
|
244
233
|
} catch {
|
|
245
234
|
// Not in a git repo, or git not available - fall through to normal error
|
package/docs/RIP-INTERNALS.md
CHANGED
|
@@ -45,7 +45,7 @@ class BinaryOp {
|
|
|
45
45
|
["+", left, right] // That's it!
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
**Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~10,
|
|
48
|
+
**Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~10,400 LOC — smaller, yet includes a complete reactive runtime, type system, component system, and source maps.
|
|
49
49
|
|
|
50
50
|
> **Transform the IR (s-expressions), not the output (strings).**
|
|
51
51
|
|
|
@@ -77,7 +77,7 @@ console.log(code);
|
|
|
77
77
|
| Dependencies | Multiple | **Zero** |
|
|
78
78
|
| Parser generator | External (Jison) | **Built-in (Solar)** |
|
|
79
79
|
| Self-hosting | No | **Yes** |
|
|
80
|
-
| Total LOC | 17,760 | ~10,
|
|
80
|
+
| Total LOC | 17,760 | ~10,400 |
|
|
81
81
|
|
|
82
82
|
## Design Principles
|
|
83
83
|
|
|
@@ -93,7 +93,7 @@ console.log(code);
|
|
|
93
93
|
|
|
94
94
|
```
|
|
95
95
|
Source Code → Lexer → emitTypes → Parser → S-Expressions → Codegen → JavaScript
|
|
96
|
-
(1,
|
|
96
|
+
(1,761) (types.js) (359) (arrays + .loc) (3,293) + source map
|
|
97
97
|
↓
|
|
98
98
|
file.d.ts (when types: "emit")
|
|
99
99
|
```
|
|
@@ -102,15 +102,15 @@ Source Code → Lexer → emitTypes → Parser → S-Expressions → C
|
|
|
102
102
|
|
|
103
103
|
| File | Purpose | Lines | Modify? |
|
|
104
104
|
|------|---------|-------|---------|
|
|
105
|
-
| `src/lexer.js` | Lexer + Rewriter | 1,
|
|
106
|
-
| `src/compiler.js` | Compiler + Code Generator | 3,
|
|
105
|
+
| `src/lexer.js` | Lexer + Rewriter | 1,761 | Yes |
|
|
106
|
+
| `src/compiler.js` | Compiler + Code Generator | 3,293 | Yes |
|
|
107
107
|
| `src/types.js` | Type System (lexer sidecar) | 1,099 | Yes |
|
|
108
|
-
| `src/components.js` | Component System (compiler sidecar) | 1,
|
|
109
|
-
| `src/sourcemaps.js` | Source Map V3 Generator |
|
|
110
|
-
| `src/tags.js` | HTML Tag Classification |
|
|
111
|
-
| `src/parser.js` | Generated parser |
|
|
112
|
-
| `src/grammar/grammar.rip` | Grammar specification |
|
|
113
|
-
| `src/grammar/solar.rip` | Parser generator |
|
|
108
|
+
| `src/components.js` | Component System (compiler sidecar) | 1,750 | Yes |
|
|
109
|
+
| `src/sourcemaps.js` | Source Map V3 Generator | 189 | Yes |
|
|
110
|
+
| `src/tags.js` | HTML Tag Classification | 62 | Yes |
|
|
111
|
+
| `src/parser.js` | Generated parser | 359 | No (auto-gen) |
|
|
112
|
+
| `src/grammar/grammar.rip` | Grammar specification | 944 | Yes (carefully) |
|
|
113
|
+
| `src/grammar/solar.rip` | Parser generator | 929 | No |
|
|
114
114
|
|
|
115
115
|
## Example Flow
|
|
116
116
|
|
|
@@ -275,7 +275,7 @@ S-expressions are simple arrays that serve as Rip's intermediate representation
|
|
|
275
275
|
|
|
276
276
|
# 4. Lexer & Rewriter
|
|
277
277
|
|
|
278
|
-
The lexer (`src/lexer.js`) is a clean reimplementation that replaces the old lexer (3,260 lines) with ~1,
|
|
278
|
+
The lexer (`src/lexer.js`) is a clean reimplementation that replaces the old lexer (3,260 lines) with ~1,760 lines producing the same token vocabulary the parser expects.
|
|
279
279
|
|
|
280
280
|
## Architecture
|
|
281
281
|
|
|
@@ -439,7 +439,7 @@ REGEX tokens store `delimiter` and optional `heregex` flags in `token.data`.
|
|
|
439
439
|
|
|
440
440
|
# 6. Compiler
|
|
441
441
|
|
|
442
|
-
The compiler (`src/compiler.js`) is a clean reimplementation replacing the old compiler (6,016 lines) with ~3,
|
|
442
|
+
The compiler (`src/compiler.js`) is a clean reimplementation replacing the old compiler (6,016 lines) with ~3,293 lines producing identical JavaScript output.
|
|
443
443
|
|
|
444
444
|
## Structure
|
|
445
445
|
|
|
@@ -577,4 +577,4 @@ rip> .js # Toggle JS display
|
|
|
577
577
|
|
|
578
578
|
---
|
|
579
579
|
|
|
580
|
-
*Rip 3.
|
|
580
|
+
*Rip 3.10 — 1,243 tests — Zero dependencies — Self-hosting — ~13,500 LOC*
|
package/docs/RIP-LANG.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Rip Language Reference
|
|
4
4
|
|
|
5
|
-
Rip is a modern reactive language that compiles to ES2022 JavaScript. It combines CoffeeScript's elegant syntax with built-in reactivity primitives. Zero dependencies, self-hosting, ~
|
|
5
|
+
Rip is a modern reactive language that compiles to ES2022 JavaScript. It combines CoffeeScript's elegant syntax with built-in reactivity primitives. Zero dependencies, self-hosting, ~13,500 LOC.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -17,7 +17,7 @@ Rip is a modern reactive language that compiles to ES2022 JavaScript. It combine
|
|
|
17
17
|
7. [Async Patterns](#7-async-patterns)
|
|
18
18
|
8. [Modules & Imports](#8-modules--imports)
|
|
19
19
|
9. [Regex Features](#9-regex-features)
|
|
20
|
-
10. [
|
|
20
|
+
10. [Packages](#10-packages)
|
|
21
21
|
11. [CLI Tools & Scripts](#11-cli-tools--scripts)
|
|
22
22
|
12. [Types](#12-types)
|
|
23
23
|
13. [JavaScript Interop](#13-javascript-interop)
|
|
@@ -663,6 +663,19 @@ counter = Counter.new(initial: 5)
|
|
|
663
663
|
# Same as: new Counter({initial: 5})
|
|
664
664
|
```
|
|
665
665
|
|
|
666
|
+
## Implicit Commas
|
|
667
|
+
|
|
668
|
+
When a literal value is followed by an arrow function, Rip inserts a comma automatically:
|
|
669
|
+
|
|
670
|
+
```coffee
|
|
671
|
+
# Clean route handlers
|
|
672
|
+
get '/users' -> User.all!
|
|
673
|
+
get '/users/:id' -> User.find params.id
|
|
674
|
+
post '/users' -> User.create body
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
This enables Sinatra-style routing and other DSLs where functions take a value and a callback.
|
|
678
|
+
|
|
666
679
|
---
|
|
667
680
|
|
|
668
681
|
# 5. Classes
|
|
@@ -1087,98 +1100,218 @@ text =~ /line2/m # Works with /m flag
|
|
|
1087
1100
|
|
|
1088
1101
|
---
|
|
1089
1102
|
|
|
1090
|
-
# 10.
|
|
1103
|
+
# 10. Packages
|
|
1104
|
+
|
|
1105
|
+
Rip includes optional packages for full-stack development. All are written in Rip, have zero dependencies, and run on Bun.
|
|
1106
|
+
|
|
1107
|
+
```bash
|
|
1108
|
+
bun add @rip-lang/api # Web framework
|
|
1109
|
+
bun add @rip-lang/server # Production server
|
|
1110
|
+
bun add @rip-lang/ui # Reactive web UI
|
|
1111
|
+
bun add @rip-lang/db # DuckDB server + client
|
|
1112
|
+
bun add @rip-lang/schema # ORM + validation
|
|
1113
|
+
bun add @rip-lang/swarm # Parallel job runner
|
|
1114
|
+
bun add @rip-lang/csv # CSV parser + writer
|
|
1115
|
+
```
|
|
1091
1116
|
|
|
1092
|
-
##
|
|
1117
|
+
## @rip-lang/api — Web Framework
|
|
1118
|
+
|
|
1119
|
+
Sinatra-style routing with `@` context magic and built-in validators.
|
|
1093
1120
|
|
|
1094
1121
|
```coffee
|
|
1095
|
-
import {
|
|
1122
|
+
import { get, post, use, read, start, notFound } from '@rip-lang/api'
|
|
1096
1123
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1124
|
+
# Routes — return data directly
|
|
1125
|
+
get '/' -> { message: 'Hello!' }
|
|
1126
|
+
get '/users/:id' -> User.find!(read 'id', 'id!')
|
|
1127
|
+
|
|
1128
|
+
# Form validation with read()
|
|
1129
|
+
post '/signup' ->
|
|
1130
|
+
email = read 'email', 'email!' # required email
|
|
1131
|
+
age = read 'age', 'int', [18, 120] # integer between 18-120
|
|
1132
|
+
role = read 'role', ['admin', 'user'] # enum
|
|
1133
|
+
{ success: true, email, age, role }
|
|
1134
|
+
|
|
1135
|
+
# File serving
|
|
1136
|
+
get '/css/*' -> @send "public/#{@req.path.slice(5)}"
|
|
1137
|
+
notFound -> @send 'index.html', 'text/html; charset=UTF-8'
|
|
1138
|
+
|
|
1139
|
+
# Middleware
|
|
1140
|
+
import { cors, logger, sessions } from '@rip-lang/api/middleware'
|
|
1141
|
+
|
|
1142
|
+
use logger()
|
|
1143
|
+
use cors origin: '*'
|
|
1144
|
+
use sessions secret: process.env.SECRET
|
|
1108
1145
|
|
|
1109
|
-
|
|
1146
|
+
# Lifecycle hooks
|
|
1147
|
+
before -> @start = Date.now()
|
|
1148
|
+
after -> console.log "#{@req.method} #{@req.path} - #{Date.now() - @start}ms"
|
|
1149
|
+
|
|
1150
|
+
start port: 3000
|
|
1110
1151
|
```
|
|
1111
1152
|
|
|
1112
|
-
|
|
1153
|
+
### read() Validators
|
|
1113
1154
|
|
|
1114
1155
|
```coffee
|
|
1115
|
-
|
|
1156
|
+
id = read 'id', 'id!' # positive integer (required)
|
|
1157
|
+
count = read 'count', 'whole' # non-negative integer
|
|
1158
|
+
price = read 'price', 'money' # cents (multiplies by 100)
|
|
1159
|
+
name = read 'name', 'string' # collapses whitespace
|
|
1160
|
+
email = read 'email', 'email' # valid email format
|
|
1161
|
+
phone = read 'phone', 'phone' # US phone → (555) 123-4567
|
|
1162
|
+
state = read 'state', 'state' # two-letter → uppercase
|
|
1163
|
+
zip = read 'zip', 'zip' # 5-digit zip
|
|
1164
|
+
url = read 'url', 'url' # valid URL
|
|
1165
|
+
uuid = read 'id', 'uuid' # UUID format
|
|
1166
|
+
date = read 'date', 'date' # YYYY-MM-DD
|
|
1167
|
+
time = read 'time', 'time' # HH:MM or HH:MM:SS
|
|
1168
|
+
flag = read 'flag', 'bool' # boolean
|
|
1169
|
+
tags = read 'tags', 'array' # must be array
|
|
1170
|
+
ids = read 'ids', 'ids' # "1,2,3" → [1, 2, 3]
|
|
1171
|
+
slug = read 'slug', 'slug' # URL-safe slug
|
|
1172
|
+
```
|
|
1173
|
+
|
|
1174
|
+
## @rip-lang/server — Production Server
|
|
1175
|
+
|
|
1176
|
+
Multi-worker process manager with hot reload, automatic HTTPS, and mDNS.
|
|
1116
1177
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
}
|
|
1178
|
+
```bash
|
|
1179
|
+
rip-server # Start (uses ./index.rip)
|
|
1180
|
+
rip-server -w # With file watching + hot-reload
|
|
1181
|
+
rip-server myapp # Named (accessible at myapp.local)
|
|
1182
|
+
rip-server http:3000 # HTTP on specific port
|
|
1183
|
+
```
|
|
1124
1184
|
|
|
1125
|
-
|
|
1126
|
-
try
|
|
1127
|
-
req.json!
|
|
1128
|
-
catch
|
|
1129
|
-
null
|
|
1185
|
+
## @rip-lang/ui — Reactive Web Framework
|
|
1130
1186
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1187
|
+
Zero-build reactive framework. Ships the compiler to the browser and compiles `.rip` components on demand. File-based routing, unified reactive stash, and SSE hot reload.
|
|
1188
|
+
|
|
1189
|
+
```coffee
|
|
1190
|
+
# Server setup (index.rip)
|
|
1191
|
+
import { get, use, start, notFound } from '@rip-lang/api'
|
|
1192
|
+
import { ripUI } from '@rip-lang/ui/serve'
|
|
1193
|
+
|
|
1194
|
+
dir = import.meta.dir
|
|
1195
|
+
use ripUI dir: dir, components: 'routes', includes: ['ui'], watch: true
|
|
1196
|
+
get '/css/*' -> @send "#{dir}/css/#{@req.path.slice(5)}"
|
|
1197
|
+
notFound -> @send "#{dir}/index.html", 'text/html; charset=UTF-8'
|
|
1198
|
+
start port: 3000
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
```coffee
|
|
1202
|
+
# Component (routes/counter.rip)
|
|
1203
|
+
Counter = component
|
|
1204
|
+
@count := 0
|
|
1205
|
+
doubled ~= @count * 2
|
|
1206
|
+
|
|
1207
|
+
increment: -> @count += 1
|
|
1208
|
+
|
|
1209
|
+
render
|
|
1210
|
+
div.counter
|
|
1211
|
+
h1 "Count: #{@count}"
|
|
1212
|
+
p "Doubled: #{doubled}"
|
|
1213
|
+
button @click: @increment, "+"
|
|
1214
|
+
```
|
|
1215
|
+
|
|
1216
|
+
## @rip-lang/db — DuckDB Server + Client
|
|
1217
|
+
|
|
1218
|
+
HTTP server for DuckDB with the official DuckDB UI built in, plus an ActiveRecord-style client library.
|
|
1219
|
+
|
|
1220
|
+
```bash
|
|
1221
|
+
rip-db # In-memory database
|
|
1222
|
+
rip-db mydata.duckdb # File-based database
|
|
1223
|
+
```
|
|
1136
1224
|
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1225
|
+
```coffee
|
|
1226
|
+
# Client library
|
|
1227
|
+
import { connect, query, findAll, Model } from '@rip-lang/db/client'
|
|
1140
1228
|
|
|
1141
|
-
|
|
1142
|
-
id = parseInt(_[1])
|
|
1143
|
-
user = db.users.find (u) -> u.id is id
|
|
1144
|
-
if user then Response.json(user)
|
|
1145
|
-
else Response.json({error: "Not found"}, status: 404)
|
|
1229
|
+
connect 'http://localhost:4213'
|
|
1146
1230
|
|
|
1147
|
-
|
|
1148
|
-
body = parseBody!(req)
|
|
1149
|
-
user = {id: db.nextId++, ...body}
|
|
1150
|
-
db.users.push(user)
|
|
1151
|
-
Response.json(user, status: 201)
|
|
1231
|
+
users = findAll! "SELECT * FROM users WHERE role = $1", ['admin']
|
|
1152
1232
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1233
|
+
User = Model 'users'
|
|
1234
|
+
user = User.find! 42
|
|
1235
|
+
User.where(active: true).order('name').limit(10).all!
|
|
1155
1236
|
```
|
|
1156
1237
|
|
|
1157
|
-
##
|
|
1238
|
+
## @rip-lang/swarm — Parallel Job Runner
|
|
1158
1239
|
|
|
1159
1240
|
```coffee
|
|
1160
|
-
import {
|
|
1241
|
+
import { swarm, init, retry, todo } from '@rip-lang/swarm'
|
|
1161
1242
|
|
|
1162
|
-
|
|
1243
|
+
setup = ->
|
|
1244
|
+
unless retry()
|
|
1245
|
+
init()
|
|
1246
|
+
for i in [1..100] then todo(i)
|
|
1163
1247
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
fetch: (req, server) ->
|
|
1167
|
-
if server.upgrade(req)
|
|
1168
|
-
return
|
|
1169
|
-
new Response("WebSocket server")
|
|
1248
|
+
perform = (task, ctx) ->
|
|
1249
|
+
await Bun.sleep(Math.random() * 1000)
|
|
1170
1250
|
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1251
|
+
swarm { setup, perform }
|
|
1252
|
+
```
|
|
1253
|
+
|
|
1254
|
+
## @rip-lang/csv — CSV Parser + Writer
|
|
1175
1255
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
client.send(message) unless client is ws
|
|
1256
|
+
```coffee
|
|
1257
|
+
import { CSV } from '@rip-lang/csv'
|
|
1179
1258
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1259
|
+
# Parse
|
|
1260
|
+
rows = CSV.read "name,age\nAlice,30\nBob,25\n", headers: true
|
|
1261
|
+
# [{name: 'Alice', age: '30'}, {name: 'Bob', age: '25'}]
|
|
1262
|
+
|
|
1263
|
+
# Write
|
|
1264
|
+
CSV.save! 'output.csv', rows
|
|
1265
|
+
```
|
|
1266
|
+
|
|
1267
|
+
## @rip-lang/schema — ORM + Validation
|
|
1268
|
+
|
|
1269
|
+
```coffee
|
|
1270
|
+
import { Model } from '@rip-lang/schema'
|
|
1271
|
+
|
|
1272
|
+
class User extends Model
|
|
1273
|
+
@table = 'users'
|
|
1274
|
+
@schema
|
|
1275
|
+
name: { type: 'string', required: true }
|
|
1276
|
+
email: { type: 'email', unique: true }
|
|
1277
|
+
|
|
1278
|
+
user = User.find!(25)
|
|
1279
|
+
user.name = 'Alice'
|
|
1280
|
+
user.save!()
|
|
1281
|
+
```
|
|
1282
|
+
|
|
1283
|
+
## Full-Stack Example
|
|
1284
|
+
|
|
1285
|
+
A complete API server in Rip:
|
|
1286
|
+
|
|
1287
|
+
```coffee
|
|
1288
|
+
import { get, post, use, read, start, notFound } from '@rip-lang/api'
|
|
1289
|
+
import { cors, logger } from '@rip-lang/api/middleware'
|
|
1290
|
+
|
|
1291
|
+
use logger()
|
|
1292
|
+
use cors origin: '*'
|
|
1293
|
+
|
|
1294
|
+
# In-memory store
|
|
1295
|
+
users = []
|
|
1296
|
+
nextId = 1
|
|
1297
|
+
|
|
1298
|
+
get '/api/users' -> users
|
|
1299
|
+
|
|
1300
|
+
get '/api/users/:id' ->
|
|
1301
|
+
id = read 'id', 'id!'
|
|
1302
|
+
user = users.find (u) -> u.id is id
|
|
1303
|
+
user or throw { status: 404, message: 'Not found' }
|
|
1304
|
+
|
|
1305
|
+
post '/api/users' ->
|
|
1306
|
+
name = read 'name', 'string!'
|
|
1307
|
+
email = read 'email', 'email!'
|
|
1308
|
+
user = { id: nextId++, name, email }
|
|
1309
|
+
users.push user
|
|
1310
|
+
user
|
|
1311
|
+
|
|
1312
|
+
notFound -> { error: 'Not found' }
|
|
1313
|
+
|
|
1314
|
+
start port: 3000
|
|
1182
1315
|
```
|
|
1183
1316
|
|
|
1184
1317
|
---
|
|
@@ -1539,4 +1672,19 @@ Each would need design discussion before building.
|
|
|
1539
1672
|
|
|
1540
1673
|
---
|
|
1541
1674
|
|
|
1542
|
-
|
|
1675
|
+
## Resources
|
|
1676
|
+
|
|
1677
|
+
- [Rip Playground](https://shreeve.github.io/rip-lang/) — Try Rip in the browser
|
|
1678
|
+
- [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=rip-lang.rip) — IDE support
|
|
1679
|
+
- [GitHub](https://github.com/shreeve/rip-lang) — Source code
|
|
1680
|
+
|
|
1681
|
+
| Document | Purpose |
|
|
1682
|
+
|----------|---------|
|
|
1683
|
+
| **[RIP-LANG.md](RIP-LANG.md)** | Full language reference (this file) |
|
|
1684
|
+
| **[RIP-TYPES.md](RIP-TYPES.md)** | Type system specification |
|
|
1685
|
+
| **[RIP-INTERNALS.md](RIP-INTERNALS.md)** | Compiler architecture & design decisions |
|
|
1686
|
+
| **[AGENT.md](../AGENT.md)** | AI agent guide for working on the compiler |
|
|
1687
|
+
|
|
1688
|
+
---
|
|
1689
|
+
|
|
1690
|
+
*Rip 3.10 — 1,243 tests — Zero dependencies — Self-hosting — ~13,500 LOC*
|