rip-lang 3.4.4 → 3.4.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 +1 -1
- package/README.md +18 -16
- package/docs/RIP-GUIDE.md +598 -0
- package/docs/RIP-INTERNALS.md +4 -1
- package/docs/RIP-LANG.md +2 -0
- package/docs/RIP-REACTIVITY.md +3 -1
- package/docs/RIP-TYPES.md +2 -0
- package/docs/dist/rip.browser.js +27 -3
- package/docs/dist/rip.browser.min.js +131 -131
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/index.html +19 -15
- package/package.json +3 -3
- package/scripts/serve.js +1 -2
- package/src/lexer.js +21 -1
- package/docs/example.html +0 -177
package/CHANGELOG.md
CHANGED
|
@@ -641,7 +641,7 @@ All 962 tests passing (100%) ✅
|
|
|
641
641
|
- Zero performance impact, much more maintainable
|
|
642
642
|
|
|
643
643
|
- **Added inline SVG favicon to HTML files**
|
|
644
|
-
-
|
|
644
|
+
- index.html now has embedded favicon
|
|
645
645
|
- No HTTP request needed for favicon
|
|
646
646
|
- Works in all contexts (local, GitHub Pages, offline)
|
|
647
647
|
|
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.4.
|
|
12
|
+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.4.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-1140%2F1140-brightgreen.svg" alt="Tests"></a>
|
|
15
15
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
|
|
@@ -309,16 +309,17 @@ Simple arrays (with `.loc`) instead of AST node classes. The compiler is self-ho
|
|
|
309
309
|
|
|
310
310
|
Rip includes optional packages for full-stack development:
|
|
311
311
|
|
|
312
|
-
| Package | Purpose |
|
|
313
|
-
|
|
314
|
-
| [
|
|
315
|
-
| [@rip-lang/api](packages/api/) | HTTP framework (Sinatra-style routing, 37 validators) |
|
|
316
|
-
| [@rip-lang/server](packages/server/) | Multi-worker app server (hot reload, HTTPS, mDNS) |
|
|
317
|
-
| [@rip-lang/db](packages/db/) | DuckDB server with official UI (pure Bun FFI) |
|
|
318
|
-
| [@rip-lang/
|
|
319
|
-
| [@rip-lang/
|
|
320
|
-
| [@rip-lang/
|
|
321
|
-
| [
|
|
312
|
+
| Package | Version | Purpose |
|
|
313
|
+
|---------|---------|---------|
|
|
314
|
+
| [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.4.4 | Core language compiler |
|
|
315
|
+
| [@rip-lang/api](packages/api/) | 1.1.4 | HTTP framework (Sinatra-style routing, 37 validators) |
|
|
316
|
+
| [@rip-lang/server](packages/server/) | 1.1.3 | Multi-worker app server (hot reload, HTTPS, mDNS) |
|
|
317
|
+
| [@rip-lang/db](packages/db/) | 1.1.2 | DuckDB server with official UI (pure Bun FFI) |
|
|
318
|
+
| [@rip-lang/ui](packages/ui/) | 0.1.2 | Zero-build reactive web framework (VFS, router, components) |
|
|
319
|
+
| [@rip-lang/swarm](packages/swarm/) | 1.1.1 | Parallel job runner with worker pool |
|
|
320
|
+
| [@rip-lang/csv](packages/csv/) | 1.1.1 | CSV parser + writer |
|
|
321
|
+
| [@rip-lang/schema](packages/schema/) | 0.1.0 | ORM + validation |
|
|
322
|
+
| [VS Code Extension](packages/vscode/) | 0.3.1 | Syntax highlighting, type intelligence, source maps |
|
|
322
323
|
|
|
323
324
|
```bash
|
|
324
325
|
bun add -g @rip-lang/db # Installs everything (rip-lang + api + db)
|
|
@@ -367,11 +368,12 @@ bun run browser # Build browser bundle
|
|
|
367
368
|
|
|
368
369
|
| Guide | Description |
|
|
369
370
|
|-------|-------------|
|
|
370
|
-
| [docs/RIP-
|
|
371
|
-
| [
|
|
372
|
-
| [docs/RIP-
|
|
373
|
-
| [
|
|
374
|
-
| [
|
|
371
|
+
| [docs/RIP-GUIDE.md](docs/RIP-GUIDE.md) | Users / AI — practical guide for using Rip in projects |
|
|
372
|
+
| [AGENT.md](AGENT.md) | AI agents — get up to speed for working on the compiler |
|
|
373
|
+
| [docs/RIP-LANG.md](docs/RIP-LANG.md) | Users — full language reference |
|
|
374
|
+
| [docs/RIP-TYPES.md](docs/RIP-TYPES.md) | Contributors — type system specification |
|
|
375
|
+
| [docs/RIP-REACTIVITY.md](docs/RIP-REACTIVITY.md) | Users — reactivity deep dive |
|
|
376
|
+
| [docs/RIP-INTERNALS.md](docs/RIP-INTERNALS.md) | Contributors — compiler architecture |
|
|
375
377
|
|
|
376
378
|
---
|
|
377
379
|
|
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
<img src="https://raw.githubusercontent.com/shreeve/rip-lang/main/docs/rip.png" style="width:50px" /> <br>
|
|
2
|
+
|
|
3
|
+
# Rip Guide
|
|
4
|
+
|
|
5
|
+
A practical guide for using Rip in your projects. Rip is a modern language that compiles to ES2022 JavaScript. It runs on [Bun](https://bun.sh) and has zero dependencies.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Getting Started
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install Bun (if needed)
|
|
13
|
+
curl -fsSL https://bun.sh/install | bash
|
|
14
|
+
|
|
15
|
+
# Install Rip
|
|
16
|
+
bun add -g rip-lang
|
|
17
|
+
|
|
18
|
+
# Run a file
|
|
19
|
+
rip app.rip
|
|
20
|
+
|
|
21
|
+
# Interactive REPL
|
|
22
|
+
rip
|
|
23
|
+
|
|
24
|
+
# Compile to JavaScript
|
|
25
|
+
rip -c app.rip
|
|
26
|
+
|
|
27
|
+
# Compile with source map
|
|
28
|
+
rip -cm app.rip
|
|
29
|
+
|
|
30
|
+
# Generate .d.ts type declarations
|
|
31
|
+
rip -d app.rip
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Rip files use the `.rip` extension. Bun runs them directly via `bun app.rip` or `rip app.rip`.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Language Basics
|
|
39
|
+
|
|
40
|
+
Rip uses **significant whitespace** (indentation, not braces) and **implicit returns** (the last expression is the return value). Semicolons and parentheses are optional in most contexts.
|
|
41
|
+
|
|
42
|
+
### Variables
|
|
43
|
+
|
|
44
|
+
```coffee
|
|
45
|
+
# Assignment (compiles to let)
|
|
46
|
+
name = "Alice"
|
|
47
|
+
count = 0
|
|
48
|
+
|
|
49
|
+
# Constant ("equals, dammit!")
|
|
50
|
+
MAX =! 100
|
|
51
|
+
|
|
52
|
+
# Destructuring
|
|
53
|
+
{name, age} = person
|
|
54
|
+
[first, ...rest] = items
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Strings
|
|
58
|
+
|
|
59
|
+
```coffee
|
|
60
|
+
# Interpolation (both styles work)
|
|
61
|
+
greeting = "Hello, #{name}!"
|
|
62
|
+
greeting = "Hello, ${name}!"
|
|
63
|
+
|
|
64
|
+
# Heredocs (closing delimiter sets left margin)
|
|
65
|
+
html = """
|
|
66
|
+
<div>
|
|
67
|
+
<p>Hello</p>
|
|
68
|
+
</div>
|
|
69
|
+
"""
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Functions
|
|
73
|
+
|
|
74
|
+
```coffee
|
|
75
|
+
# Named function
|
|
76
|
+
def greet(name)
|
|
77
|
+
"Hello, #{name}!"
|
|
78
|
+
|
|
79
|
+
# Arrow function
|
|
80
|
+
add = (a, b) -> a + b
|
|
81
|
+
|
|
82
|
+
# Fat arrow (preserves this)
|
|
83
|
+
handler = (e) => @process(e)
|
|
84
|
+
|
|
85
|
+
# Default parameters
|
|
86
|
+
def greet(name = "World")
|
|
87
|
+
"Hello, #{name}!"
|
|
88
|
+
|
|
89
|
+
# Void function (suppresses return)
|
|
90
|
+
def log!(message)
|
|
91
|
+
console.log message
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Classes
|
|
95
|
+
|
|
96
|
+
```coffee
|
|
97
|
+
class Animal
|
|
98
|
+
constructor: (@name) ->
|
|
99
|
+
|
|
100
|
+
speak: -> "#{@name} makes a sound"
|
|
101
|
+
|
|
102
|
+
class Dog extends Animal
|
|
103
|
+
speak: -> "#{@name} barks!"
|
|
104
|
+
|
|
105
|
+
# Ruby-style instantiation
|
|
106
|
+
dog = Dog.new "Buddy"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Control Flow
|
|
110
|
+
|
|
111
|
+
```coffee
|
|
112
|
+
# If/else
|
|
113
|
+
if user.admin
|
|
114
|
+
showAdmin()
|
|
115
|
+
else
|
|
116
|
+
showUser()
|
|
117
|
+
|
|
118
|
+
# Ternary
|
|
119
|
+
status = active ? "on" : "off"
|
|
120
|
+
|
|
121
|
+
# Postfix
|
|
122
|
+
console.log "hi" if ready
|
|
123
|
+
return unless valid
|
|
124
|
+
|
|
125
|
+
# Switch
|
|
126
|
+
result = switch status
|
|
127
|
+
when "active" then "Running"
|
|
128
|
+
when "done" then "Complete"
|
|
129
|
+
else "Unknown"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Loops
|
|
133
|
+
|
|
134
|
+
```coffee
|
|
135
|
+
# Arrays
|
|
136
|
+
for item in items
|
|
137
|
+
console.log item
|
|
138
|
+
|
|
139
|
+
for item, i in items # with index
|
|
140
|
+
console.log "#{i}: #{item}"
|
|
141
|
+
|
|
142
|
+
# Objects
|
|
143
|
+
for key, value of object
|
|
144
|
+
console.log "#{key} = #{value}"
|
|
145
|
+
|
|
146
|
+
# Ranges
|
|
147
|
+
for i in [1..10] # inclusive (1 to 10)
|
|
148
|
+
for i in [1...10] # exclusive (1 to 9)
|
|
149
|
+
|
|
150
|
+
# Comprehensions
|
|
151
|
+
squares = (x * x for x in [1..10])
|
|
152
|
+
evens = (x for x in items when x % 2 is 0)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Operators
|
|
158
|
+
|
|
159
|
+
Rip extends JavaScript with powerful operators:
|
|
160
|
+
|
|
161
|
+
| Operator | Name | Example | Result |
|
|
162
|
+
|----------|------|---------|--------|
|
|
163
|
+
| `!` | Dammit | `fetchData!` | `await fetchData()` |
|
|
164
|
+
| `!` | Void | `def log!` | Function returns `undefined` |
|
|
165
|
+
| `=!` | Readonly | `MAX =! 100` | `const MAX = 100` |
|
|
166
|
+
| `!?` | Otherwise | `val !? 5` | Default if undefined/throws |
|
|
167
|
+
| `?` | Existence | `x?` | `x != null` |
|
|
168
|
+
| `?.` | Optional chain | `a?.b?.c` | ES6 optional chaining |
|
|
169
|
+
| `?[]` | Optional index | `arr?[0]` | `arr?.[0]` |
|
|
170
|
+
| `??` | Nullish | `a ?? b` | ES6 nullish coalescing |
|
|
171
|
+
| `//` | Floor div | `7 // 2` | `3` |
|
|
172
|
+
| `%%` | True modulo | `-1 %% 3` | `2` (always positive) |
|
|
173
|
+
| `=~` | Regex match | `str =~ /pat/` | Match, captures in `_` |
|
|
174
|
+
| `:=` | State | `count := 0` | Reactive signal |
|
|
175
|
+
| `~=` | Computed | `doubled ~= x * 2` | Reactive computed |
|
|
176
|
+
| `~>` | Effect | `~> console.log x` | Reactive side effect |
|
|
177
|
+
| `**` | Power | `2 ** 10` | `1024` |
|
|
178
|
+
| `..` | Range | `[1..5]` | Inclusive range |
|
|
179
|
+
| `...` | Spread/rest | `[...a, ...b]` | ES6 spread |
|
|
180
|
+
|
|
181
|
+
### Dammit Operator (`!`)
|
|
182
|
+
|
|
183
|
+
The most distinctive Rip operator. Appended to a function call, it both calls AND awaits:
|
|
184
|
+
|
|
185
|
+
```coffee
|
|
186
|
+
# These are equivalent:
|
|
187
|
+
data = fetchUsers!
|
|
188
|
+
data = await fetchUsers()
|
|
189
|
+
|
|
190
|
+
# With arguments:
|
|
191
|
+
user = getUser!(id)
|
|
192
|
+
user = await getUser(id)
|
|
193
|
+
|
|
194
|
+
# Functions are auto-async when they contain !
|
|
195
|
+
def loadData(id)
|
|
196
|
+
user = getUser!(id)
|
|
197
|
+
posts = getPosts!(user.id)
|
|
198
|
+
{user, posts}
|
|
199
|
+
# Compiles to: async function loadData(id) { ... }
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Regex Match (`=~`)
|
|
203
|
+
|
|
204
|
+
Ruby-style pattern matching with captures stored in `_`:
|
|
205
|
+
|
|
206
|
+
```coffee
|
|
207
|
+
if text =~ /Hello, (\w+)/
|
|
208
|
+
console.log "Found: #{_[1]}"
|
|
209
|
+
|
|
210
|
+
# Direct extraction via regex indexing
|
|
211
|
+
domain = "user@example.com"[/@(.+)$/, 1] # "example.com"
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Guard Clauses
|
|
215
|
+
|
|
216
|
+
```coffee
|
|
217
|
+
def loadUser(id)
|
|
218
|
+
data = fetchUser!(id) or return {error: "Not found"}
|
|
219
|
+
token = headers.auth or throw new Error "No auth"
|
|
220
|
+
port = config.port ?? return 3000
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Implicit Commas
|
|
224
|
+
|
|
225
|
+
When a literal value is followed by an arrow function, Rip inserts a comma automatically:
|
|
226
|
+
|
|
227
|
+
```coffee
|
|
228
|
+
# Clean route handlers
|
|
229
|
+
get '/users' -> User.all!
|
|
230
|
+
get '/users/:id' -> User.find params.id
|
|
231
|
+
post '/users' -> User.create body
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Reactivity
|
|
237
|
+
|
|
238
|
+
Rip provides fine-grained reactivity as language-level operators, not library imports:
|
|
239
|
+
|
|
240
|
+
```coffee
|
|
241
|
+
# State — reactive container
|
|
242
|
+
count := 0
|
|
243
|
+
|
|
244
|
+
# Computed — derived value (lazy, cached)
|
|
245
|
+
doubled ~= count * 2
|
|
246
|
+
message ~= "Count is #{count}"
|
|
247
|
+
|
|
248
|
+
# Effect — side effect, re-runs when dependencies change
|
|
249
|
+
~> console.log "Count changed to #{count}"
|
|
250
|
+
|
|
251
|
+
# Update state (triggers dependents)
|
|
252
|
+
count = 5
|
|
253
|
+
# doubled is now 10
|
|
254
|
+
# effect logs: "Count changed to 5"
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Reactive Methods
|
|
258
|
+
|
|
259
|
+
```coffee
|
|
260
|
+
count := 0
|
|
261
|
+
count.read() # Get value without tracking
|
|
262
|
+
count.lock() # Make readonly
|
|
263
|
+
count.free() # Unsubscribe from dependencies
|
|
264
|
+
count.kill() # Clean up, return final value
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Type Annotations
|
|
270
|
+
|
|
271
|
+
Rip's optional type system adds annotations that are **erased** from JavaScript output and **emitted** as `.d.ts` files for TypeScript interoperability.
|
|
272
|
+
|
|
273
|
+
```coffee
|
|
274
|
+
# Annotate parameters and return types
|
|
275
|
+
def greet(name:: string):: string
|
|
276
|
+
"Hello, #{name}!"
|
|
277
|
+
|
|
278
|
+
# Type aliases
|
|
279
|
+
User ::= type
|
|
280
|
+
id: number
|
|
281
|
+
name: string
|
|
282
|
+
email?: string # optional property
|
|
283
|
+
|
|
284
|
+
# Interfaces
|
|
285
|
+
interface Animal
|
|
286
|
+
name: string
|
|
287
|
+
speak: => void
|
|
288
|
+
|
|
289
|
+
# Enums (emit runtime JS + .d.ts)
|
|
290
|
+
enum Status
|
|
291
|
+
Active
|
|
292
|
+
Inactive
|
|
293
|
+
Pending
|
|
294
|
+
|
|
295
|
+
# Generate .d.ts
|
|
296
|
+
# rip -d myfile.rip
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Modules
|
|
302
|
+
|
|
303
|
+
Standard ES6 module syntax:
|
|
304
|
+
|
|
305
|
+
```coffee
|
|
306
|
+
# Import
|
|
307
|
+
import express from 'express'
|
|
308
|
+
import { readFile } from 'fs'
|
|
309
|
+
import * as path from 'path'
|
|
310
|
+
|
|
311
|
+
# Export
|
|
312
|
+
export def processData(data)
|
|
313
|
+
data.map (x) -> x * 2
|
|
314
|
+
|
|
315
|
+
export default { process: processData }
|
|
316
|
+
|
|
317
|
+
# Re-export
|
|
318
|
+
export { foo, bar } from './utils'
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Async Patterns
|
|
324
|
+
|
|
325
|
+
```coffee
|
|
326
|
+
# Traditional await
|
|
327
|
+
user = await getUser(id)
|
|
328
|
+
|
|
329
|
+
# Dammit operator (call + await)
|
|
330
|
+
user = getUser!(id)
|
|
331
|
+
posts = getPosts!(user.id)
|
|
332
|
+
|
|
333
|
+
# Error handling
|
|
334
|
+
try
|
|
335
|
+
data = fetchData!
|
|
336
|
+
catch error
|
|
337
|
+
console.error error
|
|
338
|
+
|
|
339
|
+
# Async iteration
|
|
340
|
+
for item as! asyncIterable
|
|
341
|
+
console.log item
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## Packages
|
|
347
|
+
|
|
348
|
+
Rip includes optional packages for full-stack development. All are written in Rip, have zero dependencies, and run on Bun.
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
bun add @rip-lang/api # Web framework
|
|
352
|
+
bun add @rip-lang/server # Production server
|
|
353
|
+
bun add @rip-lang/db # DuckDB server
|
|
354
|
+
bun add @rip-lang/ui # Reactive web UI
|
|
355
|
+
bun add @rip-lang/schema # ORM + validation
|
|
356
|
+
bun add @rip-lang/swarm # Parallel job runner
|
|
357
|
+
bun add @rip-lang/csv # CSV parser + writer
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### @rip-lang/api — Web Framework
|
|
361
|
+
|
|
362
|
+
Sinatra-style routing with `@` context magic and 37 built-in validators.
|
|
363
|
+
|
|
364
|
+
```coffee
|
|
365
|
+
import { get, post, use, read, start, notFound } from '@rip-lang/api'
|
|
366
|
+
|
|
367
|
+
# Routes — return data directly
|
|
368
|
+
get '/' -> { message: 'Hello!' }
|
|
369
|
+
get '/users/:id' -> User.find!(read 'id', 'id!')
|
|
370
|
+
|
|
371
|
+
# Form validation with read()
|
|
372
|
+
post '/signup' ->
|
|
373
|
+
email = read 'email', 'email!' # required email
|
|
374
|
+
age = read 'age', 'int', [18, 120] # integer between 18-120
|
|
375
|
+
role = read 'role', ['admin', 'user'] # enum
|
|
376
|
+
{ success: true, email, age, role }
|
|
377
|
+
|
|
378
|
+
# File serving
|
|
379
|
+
get '/css/*' -> @send "public/#{@req.path.slice(5)}"
|
|
380
|
+
notFound -> @send 'index.html', 'text/html; charset=UTF-8'
|
|
381
|
+
|
|
382
|
+
# Middleware
|
|
383
|
+
import { cors, logger, sessions } from '@rip-lang/api/middleware'
|
|
384
|
+
|
|
385
|
+
use logger()
|
|
386
|
+
use cors origin: '*'
|
|
387
|
+
use sessions secret: process.env.SECRET
|
|
388
|
+
|
|
389
|
+
# Lifecycle hooks
|
|
390
|
+
before -> @start = Date.now()
|
|
391
|
+
after -> console.log "#{@req.method} #{@req.path} - #{Date.now() - @start}ms"
|
|
392
|
+
|
|
393
|
+
start port: 3000
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
#### read() Validators
|
|
397
|
+
|
|
398
|
+
```coffee
|
|
399
|
+
id = read 'id', 'id!' # positive integer (required)
|
|
400
|
+
count = read 'count', 'whole' # non-negative integer
|
|
401
|
+
price = read 'price', 'money' # cents (multiplies by 100)
|
|
402
|
+
name = read 'name', 'string' # collapses whitespace
|
|
403
|
+
email = read 'email', 'email' # valid email format
|
|
404
|
+
phone = read 'phone', 'phone' # US phone → (555) 123-4567
|
|
405
|
+
state = read 'state', 'state' # two-letter → uppercase
|
|
406
|
+
zip = read 'zip', 'zip' # 5-digit zip
|
|
407
|
+
url = read 'url', 'url' # valid URL
|
|
408
|
+
uuid = read 'id', 'uuid' # UUID format
|
|
409
|
+
date = read 'date', 'date' # YYYY-MM-DD
|
|
410
|
+
time = read 'time', 'time' # HH:MM or HH:MM:SS
|
|
411
|
+
flag = read 'flag', 'bool' # boolean
|
|
412
|
+
tags = read 'tags', 'array' # must be array
|
|
413
|
+
ids = read 'ids', 'ids' # "1,2,3" → [1, 2, 3]
|
|
414
|
+
slug = read 'slug', 'slug' # URL-safe slug
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### @rip-lang/server — Production Server
|
|
418
|
+
|
|
419
|
+
Multi-worker process manager with hot reload, HTTPS, and mDNS.
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
rip-server # Start (uses ./index.rip)
|
|
423
|
+
rip-server -w # With file watching + hot-reload
|
|
424
|
+
rip-server myapp # Named (accessible at myapp.local)
|
|
425
|
+
rip-server http:3000 # HTTP on specific port
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### @rip-lang/db — DuckDB Server
|
|
429
|
+
|
|
430
|
+
HTTP server for DuckDB with JSONCompact responses.
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
rip-db mydata.duckdb --port=4000
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
```coffee
|
|
437
|
+
# Query from Rip
|
|
438
|
+
import { get, start } from '@rip-lang/api'
|
|
439
|
+
import { DB } from '@rip-lang/db'
|
|
440
|
+
|
|
441
|
+
db = DB.new 'data.duckdb'
|
|
442
|
+
|
|
443
|
+
get '/users' -> db.query! "SELECT * FROM users"
|
|
444
|
+
get '/users/:id' -> db.query! "SELECT * FROM users WHERE id = ?", [read 'id', 'id!']
|
|
445
|
+
|
|
446
|
+
start port: 3000
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### @rip-lang/ui — Reactive Web Framework
|
|
450
|
+
|
|
451
|
+
Zero-build framework. Ships the 40KB compiler to the browser and compiles `.rip` components on demand.
|
|
452
|
+
|
|
453
|
+
```coffee
|
|
454
|
+
# Server setup (index.rip)
|
|
455
|
+
import { get, use, start, notFound } from '@rip-lang/api'
|
|
456
|
+
import { ripUI } from '@rip-lang/ui/serve'
|
|
457
|
+
|
|
458
|
+
dir = import.meta.dir
|
|
459
|
+
use ripUI pages: "#{dir}/pages", watch: true
|
|
460
|
+
notFound -> @send "#{dir}/index.html", 'text/html; charset=UTF-8'
|
|
461
|
+
start port: 3000
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
```coffee
|
|
465
|
+
# Component (pages/counter.rip)
|
|
466
|
+
Counter = component
|
|
467
|
+
@count := 0
|
|
468
|
+
doubled ~= @count * 2
|
|
469
|
+
|
|
470
|
+
increment: -> @count += 1
|
|
471
|
+
|
|
472
|
+
render
|
|
473
|
+
div.counter
|
|
474
|
+
h1 "Count: #{@count}"
|
|
475
|
+
p "Doubled: #{doubled}"
|
|
476
|
+
button @click: @increment, "+"
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### @rip-lang/swarm — Parallel Job Runner
|
|
480
|
+
|
|
481
|
+
```coffee
|
|
482
|
+
import { swarm, init, retry, todo } from '@rip-lang/swarm'
|
|
483
|
+
|
|
484
|
+
setup = ->
|
|
485
|
+
unless retry()
|
|
486
|
+
init()
|
|
487
|
+
for i in [1..100] then todo(i)
|
|
488
|
+
|
|
489
|
+
perform = (task, ctx) ->
|
|
490
|
+
await Bun.sleep(Math.random() * 1000)
|
|
491
|
+
|
|
492
|
+
swarm { setup, perform }
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### @rip-lang/csv — CSV Parser + Writer
|
|
496
|
+
|
|
497
|
+
```coffee
|
|
498
|
+
import { CSV } from '@rip-lang/csv'
|
|
499
|
+
|
|
500
|
+
# Parse
|
|
501
|
+
rows = CSV.read "name,age\nAlice,30\nBob,25\n", headers: true
|
|
502
|
+
# [{name: 'Alice', age: '30'}, {name: 'Bob', age: '25'}]
|
|
503
|
+
|
|
504
|
+
# Write
|
|
505
|
+
CSV.save! 'output.csv', rows
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### @rip-lang/schema — ORM + Validation
|
|
509
|
+
|
|
510
|
+
```coffee
|
|
511
|
+
import { Model } from '@rip-lang/schema'
|
|
512
|
+
|
|
513
|
+
class User extends Model
|
|
514
|
+
@table = 'users'
|
|
515
|
+
@schema
|
|
516
|
+
name: { type: 'string', required: true }
|
|
517
|
+
email: { type: 'email', unique: true }
|
|
518
|
+
|
|
519
|
+
user = User.find!(25)
|
|
520
|
+
user.name = 'Alice'
|
|
521
|
+
user.save!()
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## Full-Stack Example
|
|
527
|
+
|
|
528
|
+
A complete API server in Rip:
|
|
529
|
+
|
|
530
|
+
```coffee
|
|
531
|
+
import { get, post, use, read, start, notFound } from '@rip-lang/api'
|
|
532
|
+
import { cors, logger } from '@rip-lang/api/middleware'
|
|
533
|
+
|
|
534
|
+
use logger()
|
|
535
|
+
use cors origin: '*'
|
|
536
|
+
|
|
537
|
+
# In-memory store
|
|
538
|
+
users = []
|
|
539
|
+
nextId = 1
|
|
540
|
+
|
|
541
|
+
get '/api/users' -> users
|
|
542
|
+
|
|
543
|
+
get '/api/users/:id' ->
|
|
544
|
+
id = read 'id', 'id!'
|
|
545
|
+
user = users.find (u) -> u.id is id
|
|
546
|
+
user or throw { status: 404, message: 'Not found' }
|
|
547
|
+
|
|
548
|
+
post '/api/users' ->
|
|
549
|
+
name = read 'name', 'string!'
|
|
550
|
+
email = read 'email', 'email!'
|
|
551
|
+
user = { id: nextId++, name, email }
|
|
552
|
+
users.push user
|
|
553
|
+
user
|
|
554
|
+
|
|
555
|
+
notFound -> { error: 'Not found' }
|
|
556
|
+
|
|
557
|
+
start port: 3000
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## CLI Reference
|
|
563
|
+
|
|
564
|
+
```bash
|
|
565
|
+
rip # REPL
|
|
566
|
+
rip file.rip # Run
|
|
567
|
+
rip -c file.rip # Compile to JS (stdout)
|
|
568
|
+
rip -o out.js file.rip # Compile to file
|
|
569
|
+
rip -m file.rip # Compile with inline source map
|
|
570
|
+
rip -d file.rip # Generate .d.ts
|
|
571
|
+
rip -t file.rip # Show tokens
|
|
572
|
+
rip -s file.rip # Show S-expressions
|
|
573
|
+
rip -q -c file.rip # Quiet (no headers)
|
|
574
|
+
bun run test # Run test suite (1,140 tests)
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## Documentation
|
|
580
|
+
|
|
581
|
+
| Document | Audience | Purpose |
|
|
582
|
+
|----------|----------|---------|
|
|
583
|
+
| **[RIP-GUIDE.md](RIP-GUIDE.md)** | Users / AI | Practical guide for using Rip in projects |
|
|
584
|
+
| **[AGENT.md](../AGENT.md)** | AI agents | Get up to speed for working on the compiler |
|
|
585
|
+
| **[RIP-LANG.md](RIP-LANG.md)** | Users | Full language reference |
|
|
586
|
+
| **[RIP-TYPES.md](RIP-TYPES.md)** | Contributors | Type system specification |
|
|
587
|
+
| **[RIP-REACTIVITY.md](RIP-REACTIVITY.md)** | Users | Reactivity deep dive |
|
|
588
|
+
| **[RIP-INTERNALS.md](RIP-INTERNALS.md)** | Contributors | Compiler architecture |
|
|
589
|
+
|
|
590
|
+
## Resources
|
|
591
|
+
|
|
592
|
+
- [Rip Playground](https://shreeve.github.io/rip-lang/) — Try Rip in the browser
|
|
593
|
+
- [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=rip-lang.rip) — IDE support
|
|
594
|
+
- [GitHub](https://github.com/shreeve/rip-lang) — Source code
|
|
595
|
+
|
|
596
|
+
---
|
|
597
|
+
|
|
598
|
+
*Rip — Start simple. Build incrementally. Ship elegantly.*
|
package/docs/RIP-INTERNALS.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
<img src="https://raw.githubusercontent.com/shreeve/rip-lang/main/docs/rip.png" style="width:50px" /> <br>
|
|
2
|
+
|
|
3
|
+
# Rip Internals
|
|
2
4
|
|
|
3
5
|
> Architecture, design decisions, and technical reference for the Rip compiler.
|
|
4
6
|
|
|
@@ -570,6 +572,7 @@ rip> .js # Toggle JS display
|
|
|
570
572
|
---
|
|
571
573
|
|
|
572
574
|
**See Also:**
|
|
575
|
+
- [RIP-GUIDE.md](RIP-GUIDE.md) — Practical guide for using Rip
|
|
573
576
|
- [RIP-LANG.md](RIP-LANG.md) — Language reference
|
|
574
577
|
- [RIP-TYPES.md](RIP-TYPES.md) — Type system specification
|
|
575
578
|
- [RIP-REACTIVITY.md](RIP-REACTIVITY.md) — Reactivity deep dive
|
package/docs/RIP-LANG.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<img src="https://raw.githubusercontent.com/shreeve/rip-lang/main/docs/rip.png" style="width:50px" /> <br>
|
|
2
|
+
|
|
1
3
|
# Rip 3.0 Language Reference
|
|
2
4
|
|
|
3
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, ~10,300 LOC.
|
package/docs/RIP-REACTIVITY.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
<img src="https://raw.githubusercontent.com/shreeve/rip-lang/main/docs/rip.png" style="width:50px" /> <br>
|
|
2
|
+
|
|
3
|
+
# Rip Reactivity
|
|
2
4
|
|
|
3
5
|
Rip implements a **fine-grained reactive system** that rivals and often exceeds the capabilities of major frameworks like Vue, Svelte, Solid, and React — in just ~200 lines of runtime code.
|
|
4
6
|
|