rip-lang 3.4.4 → 3.4.5
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 +17 -15
- 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 +2 -2
- package/docs/dist/rip.browser.min.js +1 -1
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/index.html +10 -6
- package/package.json +3 -3
- 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
|
@@ -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
|
|
package/docs/RIP-TYPES.md
CHANGED
package/docs/dist/rip.browser.js
CHANGED
|
@@ -7536,8 +7536,8 @@ function getComponentRuntime() {
|
|
|
7536
7536
|
return new CodeGenerator({}).getComponentRuntime();
|
|
7537
7537
|
}
|
|
7538
7538
|
// src/browser.js
|
|
7539
|
-
var VERSION = "3.4.
|
|
7540
|
-
var BUILD_DATE = "2026-02-09@
|
|
7539
|
+
var VERSION = "3.4.5";
|
|
7540
|
+
var BUILD_DATE = "2026-02-09@14:23:53GMT";
|
|
7541
7541
|
var dedent = (s) => {
|
|
7542
7542
|
const m = s.match(/^[ \t]*(?=\S)/gm);
|
|
7543
7543
|
const i = Math.min(...(m || []).map((x) => x.length));
|
|
@@ -525,4 +525,4 @@ if (typeof globalThis !== 'undefined') {
|
|
|
525
525
|
`:"",Y=U.slice(0,W).join(`
|
|
526
526
|
`)}let $=new v().tokenize(Y);if(this.options.showTokens)$.forEach((R)=>console.log(`${R[0].padEnd(12)} ${JSON.stringify(R[1])}`)),console.log();let Q=null;if(this.options.types==="emit"||this.options.types==="check"||this.options.types===!0)Q=Q1($);$=$.filter((R)=>R[0]!=="TYPE_DECL");while($.length>0&&$[0][0]==="TERMINATOR")$.shift();if($.every((R)=>R[0]==="TERMINATOR"))return{tokens:$,sexpr:["program"],code:"",dts:Q,data:F,reactiveVars:{}};g.lexer={tokens:$,pos:0,setInput:function(){},lex:function(){if(this.pos>=this.tokens.length)return 1;let R=this.tokens[this.pos++],M=R[1];if(R.data)M=new String(M),Object.assign(M,R.data);return this.text=M,this.loc=R.loc,R[0]}};let D;try{D=g.parse(Y)}catch(R){if(/\?\s*\([^)]*\?[^)]*:[^)]*\)\s*:/.test(Y)||/\?\s+\w+\s+\?\s+/.test(Y))throw Error("Nested ternary operators are not supported. Use if/else statements instead.");throw R}if(this.options.showSExpr)console.log(y(D,0,!0)),console.log();let Z=null;if(this.options.sourceMap){let R=(this.options.filename||"output")+".js",M=this.options.filename||"input.rip";Z=new Y1(R,M,Y)}let X=new V({dataSection:F,skipReactiveRuntime:this.options.skipReactiveRuntime,skipComponentRuntime:this.options.skipComponentRuntime,reactiveVars:this.options.reactiveVars,sourceMap:Z}),J=X.compile(D),K=Z?Z.toJSON():null,z=Z?Z.toReverseMap():null;if(K&&this.options.sourceMap==="inline"){let R=typeof Buffer<"u"?Buffer.from(K).toString("base64"):btoa(K);J+=`
|
|
527
527
|
//# sourceMappingURL=data:application/json;base64,${R}`}else if(K&&this.options.filename)J+=`
|
|
528
|
-
//# sourceMappingURL=${this.options.filename}.js.map`;return{tokens:$,sexpr:D,code:J,dts:Q,map:K,reverseMap:z,data:F,reactiveVars:X.reactiveVars}}compileToJS(Y){return this.compile(Y).code}compileToSExpr(Y){return this.compile(Y).sexpr}}E1(V);V.prototype.generateEnum=Z1;function H2(Y,F={}){return new r(F).compile(Y)}function t(Y,F={}){return new r(F).compileToJS(Y)}function O2(){return new V({}).getReactiveRuntime()}function q2(){return new V({}).getComponentRuntime()}var v2="3.4.
|
|
528
|
+
//# sourceMappingURL=${this.options.filename}.js.map`;return{tokens:$,sexpr:D,code:J,dts:Q,map:K,reverseMap:z,data:F,reactiveVars:X.reactiveVars}}compileToJS(Y){return this.compile(Y).code}compileToSExpr(Y){return this.compile(Y).sexpr}}E1(V);V.prototype.generateEnum=Z1;function H2(Y,F={}){return new r(F).compile(Y)}function t(Y,F={}){return new r(F).compileToJS(Y)}function O2(){return new V({}).getReactiveRuntime()}function q2(){return new V({}).getComponentRuntime()}var v2="3.4.5",g2="2026-02-09@14:23:53GMT",P2=(Y)=>{let F=Y.match(/^[ \t]*(?=\S)/gm),U=Math.min(...(F||[]).map((W)=>W.length));return Y.replace(RegExp(`^[ ]{${U}}`,"gm"),"").trim()};async function T1(){let Y=document.querySelectorAll('script[type="text/rip"]');for(let F of Y){if(F.hasAttribute("data-rip-processed"))continue;try{let U=P2(F.textContent),W=t(U);(0,eval)(W),F.setAttribute("data-rip-processed","true")}catch(U){console.error("Error compiling Rip script:",U),console.error("Script content:",F.textContent)}}}if(typeof document<"u")if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",T1);else T1();function _2(Y){try{let U=t(Y).replace(/^let\s+[^;]+;\s*\n\s*/m,"");U=U.replace(/^const\s+/gm,"var ");let W=(0,eval)(U);if(W!==void 0)globalThis._=W;return W}catch(F){console.error("Rip compilation error:",F.message);return}}if(typeof globalThis<"u")globalThis.rip=_2;export{_2 as rip,T1 as processRipScripts,g as parser,O2 as getReactiveRuntime,q2 as getComponentRuntime,y as formatSExpr,t as compileToJS,H2 as compile,v2 as VERSION,v as Lexer,r as Compiler,V as CodeGenerator,g2 as BUILD_DATE};
|
|
Binary file
|
package/docs/index.html
CHANGED
|
@@ -907,9 +907,13 @@ console.log "Domain:", domain`;
|
|
|
907
907
|
modeToggle.addEventListener('click', () => {
|
|
908
908
|
isDark = !isDark;
|
|
909
909
|
applyMode();
|
|
910
|
-
|
|
911
|
-
themeSelect.value
|
|
912
|
-
|
|
910
|
+
// Only reset theme if currently using a built-in default
|
|
911
|
+
const current = themeSelect.value;
|
|
912
|
+
if (current === 'vs' || current === 'vs-dark') {
|
|
913
|
+
const defaultTheme = isDark ? 'vs-dark' : 'vs';
|
|
914
|
+
themeSelect.value = defaultTheme;
|
|
915
|
+
setTheme(defaultTheme);
|
|
916
|
+
}
|
|
913
917
|
});
|
|
914
918
|
|
|
915
919
|
// ========================================================================
|
|
@@ -1038,7 +1042,7 @@ console.log "Domain:", domain`;
|
|
|
1038
1042
|
// Tab Switching
|
|
1039
1043
|
// ========================================================================
|
|
1040
1044
|
|
|
1041
|
-
function switchToTab(tabName) {
|
|
1045
|
+
async function switchToTab(tabName) {
|
|
1042
1046
|
// Update URL hash
|
|
1043
1047
|
window.location.hash = tabName;
|
|
1044
1048
|
|
|
@@ -1057,7 +1061,7 @@ console.log "Domain:", domain`;
|
|
|
1057
1061
|
replEditor.focus();
|
|
1058
1062
|
} else if (tabName === 'compiler') {
|
|
1059
1063
|
// Compiler restores user's selected theme
|
|
1060
|
-
setTheme(themeSelect.value);
|
|
1064
|
+
await setTheme(themeSelect.value);
|
|
1061
1065
|
compileCode();
|
|
1062
1066
|
}
|
|
1063
1067
|
}
|
|
@@ -1083,7 +1087,7 @@ console.log "Domain:", domain`;
|
|
|
1083
1087
|
// Initialize from URL hash on page load (default to compiler)
|
|
1084
1088
|
const initialTab = window.location.hash.slice(1);
|
|
1085
1089
|
if (['compiler', 'repl'].includes(initialTab)) {
|
|
1086
|
-
switchToTab(initialTab);
|
|
1090
|
+
await switchToTab(initialTab);
|
|
1087
1091
|
} else {
|
|
1088
1092
|
// No hash - compiler is default
|
|
1089
1093
|
compileCode();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rip-lang",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.5",
|
|
4
4
|
"description": "A modern language that compiles to JavaScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/compiler.js",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"author": "Steve Shreeve <steve.shreeve@gmail.com>",
|
|
68
68
|
"license": "MIT",
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@rip-lang/api": "1.1.
|
|
71
|
-
"@rip-lang/ui": "0.1.
|
|
70
|
+
"@rip-lang/api": "1.1.5",
|
|
71
|
+
"@rip-lang/ui": "0.1.3"
|
|
72
72
|
}
|
|
73
73
|
}
|
package/docs/example.html
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Inline Rip Code Demo</title>
|
|
7
|
-
<style>
|
|
8
|
-
* {
|
|
9
|
-
margin: 0;
|
|
10
|
-
padding: 0;
|
|
11
|
-
box-sizing: border-box;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
body {
|
|
15
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
16
|
-
padding: 40px;
|
|
17
|
-
max-width: 800px;
|
|
18
|
-
margin: 0 auto;
|
|
19
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
20
|
-
min-height: 100vh;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
.container {
|
|
24
|
-
background: white;
|
|
25
|
-
border-radius: 12px;
|
|
26
|
-
padding: 40px;
|
|
27
|
-
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
h1 {
|
|
31
|
-
color: #333;
|
|
32
|
-
margin-bottom: 10px;
|
|
33
|
-
font-size: 32px;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.subtitle {
|
|
37
|
-
color: #666;
|
|
38
|
-
margin-bottom: 30px;
|
|
39
|
-
font-size: 16px;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.output {
|
|
43
|
-
background: #f8f9fa;
|
|
44
|
-
border-left: 4px solid #667eea;
|
|
45
|
-
padding: 20px;
|
|
46
|
-
margin: 20px 0;
|
|
47
|
-
border-radius: 4px;
|
|
48
|
-
font-family: 'Monaco', 'Menlo', monospace;
|
|
49
|
-
font-size: 14px;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.output h3 {
|
|
53
|
-
margin: 0 0 10px 0;
|
|
54
|
-
color: #667eea;
|
|
55
|
-
font-size: 14px;
|
|
56
|
-
text-transform: uppercase;
|
|
57
|
-
letter-spacing: 1px;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
button {
|
|
61
|
-
background: #667eea;
|
|
62
|
-
color: white;
|
|
63
|
-
border: none;
|
|
64
|
-
padding: 12px 24px;
|
|
65
|
-
border-radius: 6px;
|
|
66
|
-
cursor: pointer;
|
|
67
|
-
font-size: 16px;
|
|
68
|
-
font-weight: 600;
|
|
69
|
-
margin: 10px 10px 10px 0;
|
|
70
|
-
transition: all 0.3s;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
button:hover {
|
|
74
|
-
background: #5568d3;
|
|
75
|
-
transform: translateY(-2px);
|
|
76
|
-
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.code-block {
|
|
80
|
-
background: #282c34;
|
|
81
|
-
color: #abb2bf;
|
|
82
|
-
padding: 20px;
|
|
83
|
-
border-radius: 8px;
|
|
84
|
-
margin: 20px 0;
|
|
85
|
-
overflow-x: auto;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.code-block pre {
|
|
89
|
-
margin: 0;
|
|
90
|
-
font-family: 'Monaco', 'Menlo', monospace;
|
|
91
|
-
font-size: 13px;
|
|
92
|
-
line-height: 1.5;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.keyword { color: #c678dd; }
|
|
96
|
-
.function { color: #61afef; }
|
|
97
|
-
.string { color: #98c379; }
|
|
98
|
-
.comment { color: #5c6370; font-style: italic; }
|
|
99
|
-
</style>
|
|
100
|
-
</head>
|
|
101
|
-
<body>
|
|
102
|
-
<div class="container">
|
|
103
|
-
<h1>🔮 Inline Rip Demo</h1>
|
|
104
|
-
<p class="subtitle">Rip code embedded directly in HTML with <code><script type="text/rip"></code></p>
|
|
105
|
-
|
|
106
|
-
<div class="code-block">
|
|
107
|
-
<pre><span class="comment"># This Rip code runs automatically!</span>
|
|
108
|
-
<span class="keyword">def</span> <span class="function">greet</span>(name)
|
|
109
|
-
<span class="string">"Hello, ${name}! 👋"</span>
|
|
110
|
-
|
|
111
|
-
<span class="keyword">def</span> <span class="function">fibonacci</span>(n)
|
|
112
|
-
<span class="keyword">if</span> n <= 1
|
|
113
|
-
n
|
|
114
|
-
<span class="keyword">else</span>
|
|
115
|
-
fibonacci(n - 1) + fibonacci(n - 2)</pre>
|
|
116
|
-
</div>
|
|
117
|
-
|
|
118
|
-
<div id="output" class="output">
|
|
119
|
-
<h3>Output</h3>
|
|
120
|
-
<div id="results">Click the buttons to see Rip in action!</div>
|
|
121
|
-
</div>
|
|
122
|
-
|
|
123
|
-
<button onclick="testGreeting()">Test Greeting</button>
|
|
124
|
-
<button onclick="testFibonacci()">Calculate Fibonacci(10)</button>
|
|
125
|
-
<button onclick="testMath()">Test Math Operations</button>
|
|
126
|
-
</div>
|
|
127
|
-
|
|
128
|
-
<!-- Rip code embedded directly in the page! -->
|
|
129
|
-
<script type="text/rip">
|
|
130
|
-
# Define greeting function
|
|
131
|
-
def greet(name)
|
|
132
|
-
"Hello, ${name}! 👋"
|
|
133
|
-
|
|
134
|
-
# Define fibonacci function
|
|
135
|
-
def fibonacci(n)
|
|
136
|
-
if n <= 1
|
|
137
|
-
n
|
|
138
|
-
else
|
|
139
|
-
fibonacci(n - 1) + fibonacci(n - 2)
|
|
140
|
-
|
|
141
|
-
# Define math operations
|
|
142
|
-
def calculate(a, b)
|
|
143
|
-
sum = a + b
|
|
144
|
-
product = a * b
|
|
145
|
-
power = a ** b
|
|
146
|
-
{sum: sum, product: product, power: power}
|
|
147
|
-
</script>
|
|
148
|
-
|
|
149
|
-
<!-- Load the Rip compiler and runtime (loads after Rip scripts are in DOM) -->
|
|
150
|
-
<script type="module" src="./dist/rip.browser.min.js"></script>
|
|
151
|
-
|
|
152
|
-
<!-- Regular JavaScript to handle UI interactions -->
|
|
153
|
-
<script>
|
|
154
|
-
const output = document.getElementById('results');
|
|
155
|
-
|
|
156
|
-
function testGreeting() {
|
|
157
|
-
const result = greet('Rip User');
|
|
158
|
-
output.innerHTML = `<strong>greet('Rip User')</strong><br>→ ${result}`;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function testFibonacci() {
|
|
162
|
-
const result = fibonacci(10);
|
|
163
|
-
output.innerHTML = `<strong>fibonacci(10)</strong><br>→ ${result}`;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function testMath() {
|
|
167
|
-
const result = calculate(2, 3);
|
|
168
|
-
output.innerHTML = `
|
|
169
|
-
<strong>calculate(2, 3)</strong><br>
|
|
170
|
-
→ sum: ${result.sum}<br>
|
|
171
|
-
→ product: ${result.product}<br>
|
|
172
|
-
→ power: ${result.power}
|
|
173
|
-
`;
|
|
174
|
-
}
|
|
175
|
-
</script>
|
|
176
|
-
</body>
|
|
177
|
-
</html>
|