rip-lang 3.4.3 → 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 CHANGED
@@ -7,6 +7,45 @@ 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.4.3] - 2026-02-09
11
+
12
+ ### Source Maps & IDE Intelligence
13
+
14
+ - **Source Map V3 support** — New `src/sourcemaps.js` implements ECMA-426 source maps with zero dependencies. VLQ encoder + SourceMapGenerator class in ~120 lines.
15
+ - **Inline source maps** — `-m` flag embeds source maps as base64 data URLs in compiled output. One file for everything — debuggers read them natively.
16
+ - **Reverse source maps** — `toReverseMap()` provides O(1) source→generated position lookup for IDE type intelligence.
17
+ - **S-expression locations** — Parser now attaches `.loc = {r, c}` on every S-expression node. Locations flow from lexer through parser to code generator using consistent `{r, c}` naming.
18
+ - **Parser cleanup** — Removed legacy Jison location format (`first_line`/`first_column`), dead `ranges` variable, and `locFirst`/`locLast` extraction. Parser uses `{r, c}` natively.
19
+ - **VS Code Extension v0.3.1** — Level 2 type intelligence: autocomplete, hover, and go-to-definition from third-party `.d.ts` files inside `.rip` files. Shadow `.ts` compilation with 300ms debounce. Configurable via `rip.types.intellisense` setting.
20
+
21
+ ## [3.3.1] - 2026-02-09
22
+
23
+ ### Playground & Extension
24
+
25
+ - **Playground cleanup** — Eliminated dead CSS rules, extracted shared Monaco config, DRYed up toggle handlers, fixed flicker by restoring button states before page reveal, added smooth fade-in transition, defaulted light/dark mode to system `prefers-color-scheme`.
26
+ - **VS Code Extension v0.2.0** — Auto-generate `.d.ts` files on save, commands for single-file and workspace-wide type generation, auto-detect compiler binary, configurable settings.
27
+ - **Extension published** to VS Code Marketplace as `rip-lang.rip`.
28
+
29
+ ## [3.2.1] - 2026-02-08
30
+
31
+ ### Test Suite & Solar Cleanup
32
+
33
+ - **Test suite overhaul** — Redistributed tests from `stabilization.rip` and `compatibility.rip` into proper files, removed duplicates, added `reactivity.rip` and `types.rip` test files, added `for-as` guard tests. Now 1,140 tests.
34
+ - **Solar parser generator cleanup** — Removed ~79 lines of dead Jison compatibility code, optimized runtime parser with `.call` instead of `.apply`, modernized variable naming from `yy` prefixes.
35
+
36
+ ## [3.2.0] - 2026-02-08
37
+
38
+ ### Rip Types — Optional Type System
39
+
40
+ - **Type annotations** (`::`) on variables, parameters, and return types — compile-time only, stripped from JS output.
41
+ - **Type aliases** (`::=`) for named types, structural types, union types.
42
+ - **Interfaces** with `extends` support.
43
+ - **Enums** with runtime JS generation and `.d.ts` emission.
44
+ - **Generics** (`<T>`) for reusable type definitions.
45
+ - **`.d.ts` emission** — `emitTypes()` generates TypeScript declaration files directly from annotated token stream.
46
+ - **CLI flag** — `-d`/`--dts` generates `.d.ts` files alongside compiled JS.
47
+ - **Architecture** — All type logic consolidated in `src/types.js` (lexer sidecar), minimal changes to lexer and compiler. Added `::` and `::=` operators to lexer.
48
+
10
49
  ## [3.1.0] - 2026-02-08
11
50
 
12
51
  ### Rip UI — Zero-Build Reactive Web Framework
@@ -602,7 +641,7 @@ All 962 tests passing (100%) ✅
602
641
  - Zero performance impact, much more maintainable
603
642
 
604
643
  - **Added inline SVG favicon to HTML files**
605
- - repl.html, index.html, example.html now have embedded favicon
644
+ - index.html now has embedded favicon
606
645
  - No HTTP request needed for favicon
607
646
  - Works in all contexts (local, GitHub Pages, offline)
608
647
 
package/README.md CHANGED
@@ -9,15 +9,15 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.1.0-blue.svg" alt="Version"></a>
12
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.4.3-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
- <a href="#"><img src="https://img.shields.io/badge/tests-1130%2F1130-brightgreen.svg" alt="Tests"></a>
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>
16
16
  </p>
17
17
 
18
18
  ---
19
19
 
20
- Rip is a modern language inspired by CoffeeScript. It compiles to **ES2022** (classes, `?.`, `??`, modules), adds about a **dozen new operators**, includes **built-in reactivity**, and sports a self-hosting compiler with **zero dependencies** — all in about 7,700 lines of code.
20
+ Rip is a modern language inspired by CoffeeScript. It compiles to **ES2022** (classes, `?.`, `??`, modules), adds about a **dozen new operators**, includes **built-in reactivity**, and sports a self-hosting compiler with **zero dependencies** — all in about 10,300 lines of code.
21
21
 
22
22
  > **No imports. No hooks. No dependency arrays. Just write code.**
23
23
 
@@ -254,9 +254,9 @@ See [@rip-lang/ui](packages/ui/) for the full framework: Virtual File System, fi
254
254
  | **Reactivity** | None | Built-in |
255
255
  | **Dependencies** | Multiple | Zero |
256
256
  | **Self-hosting** | No | Yes |
257
- | **Lexer** | 3,558 LOC | 1,542 LOC |
258
- | **Compiler** | 10,346 LOC | 3,148 LOC |
259
- | **Total** | 17,760 LOC | ~7,700 LOC |
257
+ | **Lexer** | 3,558 LOC | 1,867 LOC |
258
+ | **Compiler** | 10,346 LOC | 3,292 LOC |
259
+ | **Total** | 17,760 LOC | ~10,300 LOC |
260
260
 
261
261
  Smaller codebase, modern output, built-in reactivity.
262
262
 
@@ -282,22 +282,26 @@ Run Rip directly in the browser:
282
282
  ## Architecture
283
283
 
284
284
  ```
285
- Source -> Lexer -> Parser -> S-Expressions -> Codegen -> JavaScript
286
- (1,542) (352) ["=", "x", 42] (3,148)
285
+ Source -> Lexer -> emitTypes -> Parser -> S-Expressions -> Codegen -> JavaScript
286
+ (1,867) (types.js) (357) ["=", "x", 42] (3,292) + source map
287
287
  ```
288
288
 
289
- Simple arrays instead of AST node classes. The compiler is self-hosting — `bun run parser` rebuilds from source.
289
+ Simple arrays (with `.loc`) instead of AST node classes. The compiler is self-hosting — `bun run parser` rebuilds from source.
290
290
 
291
291
  | Component | File | Lines |
292
292
  |-----------|------|-------|
293
- | Lexer + Rewriter | `src/lexer.js` | 1,542 |
294
- | Compiler + Codegen | `src/compiler.js` | 3,148 |
295
- | Parser (generated) | `src/parser.js` | 352 |
296
- | Grammar | `src/grammar/grammar.rip` | 887 |
297
- | Parser Generator | `src/grammar/solar.rip` | 1,001 |
298
- | REPL | `src/repl.js` | 654 |
299
- | Browser Entry | `src/browser.js` | 79 |
300
- | **Total** | | **7,663** |
293
+ | Lexer + Rewriter | `src/lexer.js` | 1,867 |
294
+ | Compiler + Codegen | `src/compiler.js` | 3,292 |
295
+ | Type System | `src/types.js` | 719 |
296
+ | Component System | `src/components.js` | 1,240 |
297
+ | Source Maps | `src/sourcemaps.js` | 122 |
298
+ | Parser (generated) | `src/parser.js` | 357 |
299
+ | Grammar | `src/grammar/grammar.rip` | 935 |
300
+ | Parser Generator | `src/grammar/solar.rip` | 916 |
301
+ | REPL | `src/repl.js` | 707 |
302
+ | Browser Entry | `src/browser.js` | 80 |
303
+ | Tags | `src/tags.js` | 63 |
304
+ | **Total** | | **10,298** |
301
305
 
302
306
  ---
303
307
 
@@ -305,12 +309,17 @@ Simple arrays instead of AST node classes. The compiler is self-hosting — `bun
305
309
 
306
310
  Rip includes optional packages for full-stack development:
307
311
 
308
- | Package | Purpose | Lines |
309
- |---------|---------|-------|
310
- | [@rip-lang/ui](packages/ui/) | Zero-build reactive web framework (VFS, router, components) | ~1,300 |
311
- | [@rip-lang/api](packages/api/) | HTTP framework (Sinatra-style routing, 37 validators) | ~1,050 |
312
- | [@rip-lang/server](packages/server/) | Multi-worker app server (hot reload, HTTPS, mDNS) | ~1,210 |
313
- | [@rip-lang/db](packages/db/) | DuckDB server with official UI (pure Bun FFI) | ~1,740 |
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 |
314
323
 
315
324
  ```bash
316
325
  bun add -g @rip-lang/db # Installs everything (rip-lang + api + db)
@@ -348,7 +357,7 @@ rip file.rip # Run
348
357
  rip -c file.rip # Compile
349
358
  rip -t file.rip # Tokens
350
359
  rip -s file.rip # S-expressions
351
- bun run test # 1073 tests
360
+ bun run test # 1140 tests
352
361
  bun run parser # Rebuild parser
353
362
  bun run browser # Build browser bundle
354
363
  ```
@@ -359,11 +368,12 @@ bun run browser # Build browser bundle
359
368
 
360
369
  | Guide | Description |
361
370
  |-------|-------------|
362
- | [docs/RIP-LANG.md](docs/RIP-LANG.md) | Language reference |
363
- | [docs/RIP-REACTIVITY.md](docs/RIP-REACTIVITY.md) | Reactivity deep dive |
364
- | [docs/RIP-INTERNALS.md](docs/RIP-INTERNALS.md) | Compiler internals |
365
- | [packages/](packages/README.md) | Full-stack packages |
366
- | [CONTRIBUTING.md](CONTRIBUTING.md) | How to contribute |
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 |
367
377
 
368
378
  ---
369
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.*
@@ -1,4 +1,6 @@
1
- # Rip Compiler Internals
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
 
@@ -43,7 +45,7 @@ class BinaryOp {
43
45
  ["+", left, right] // That's it!
44
46
  ```
45
47
 
46
- **Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~7,700 LOC — smaller, yet includes a complete reactive runtime.
48
+ **Result:** CoffeeScript's compiler is 17,760 LOC. Rip's is ~10,300 LOC — smaller, yet includes a complete reactive runtime, type system, component system, and source maps.
47
49
 
48
50
  > **Transform the IR (s-expressions), not the output (strings).**
49
51
 
@@ -75,7 +77,7 @@ console.log(code);
75
77
  | Dependencies | Multiple | **Zero** |
76
78
  | Parser generator | External (Jison) | **Built-in (Solar)** |
77
79
  | Self-hosting | No | **Yes** |
78
- | Total LOC | 17,760 | ~7,700 |
80
+ | Total LOC | 17,760 | ~10,300 |
79
81
 
80
82
  ## Design Principles
81
83
 
@@ -91,7 +93,7 @@ console.log(code);
91
93
 
92
94
  ```
93
95
  Source Code → Lexer → emitTypes → Parser → S-Expressions → Codegen → JavaScript
94
- (1,866) (types.js) (356) (simple arrays) (3,219) (ES2022)
96
+ (1,867) (types.js) (357) (arrays + .loc) (3,292) + source map
95
97
 
96
98
  file.d.ts (when types: "emit")
97
99
  ```
@@ -100,13 +102,15 @@ Source Code → Lexer → emitTypes → Parser → S-Expressions → C
100
102
 
101
103
  | File | Purpose | Lines | Modify? |
102
104
  |------|---------|-------|---------|
103
- | `src/lexer.js` | Lexer + Rewriter | 1,866 | Yes |
104
- | `src/compiler.js` | Compiler + Code Generator | 3,219 | Yes |
105
- | `src/types.js` | Type System (lexer sidecar) | 718 | Yes |
106
- | `src/components.js` | Component System (compiler sidecar) | ~1,240 | Yes |
107
- | `src/parser.js` | Generated parser | 356 | No (auto-gen) |
108
- | `src/grammar/grammar.rip` | Grammar specification | 934 | Yes (carefully) |
109
- | `src/grammar/solar.rip` | Parser generator | 1,001 | No |
105
+ | `src/lexer.js` | Lexer + Rewriter | 1,867 | Yes |
106
+ | `src/compiler.js` | Compiler + Code Generator | 3,292 | Yes |
107
+ | `src/types.js` | Type System (lexer sidecar) | 719 | Yes |
108
+ | `src/components.js` | Component System (compiler sidecar) | 1,240 | Yes |
109
+ | `src/sourcemaps.js` | Source Map V3 Generator | 122 | Yes |
110
+ | `src/tags.js` | HTML Tag Classification | 63 | Yes |
111
+ | `src/parser.js` | Generated parser | 357 | No (auto-gen) |
112
+ | `src/grammar/grammar.rip` | Grammar specification | 935 | Yes (carefully) |
113
+ | `src/grammar/solar.rip` | Parser generator | 916 | No |
110
114
 
111
115
  ## Example Flow
112
116
 
@@ -271,7 +275,7 @@ S-expressions are simple arrays that serve as Rip's intermediate representation
271
275
 
272
276
  # 4. Lexer & Rewriter
273
277
 
274
- The lexer (`src/lexer.js`) is a clean reimplementation that replaces the old lexer (3,260 lines) with ~1,550 lines producing the same token vocabulary the parser expects.
278
+ The lexer (`src/lexer.js`) is a clean reimplementation that replaces the old lexer (3,260 lines) with ~1,870 lines producing the same token vocabulary the parser expects.
275
279
 
276
280
  ## Architecture
277
281
 
@@ -435,7 +439,7 @@ REGEX tokens store `delimiter` and optional `heregex` flags in `token.data`.
435
439
 
436
440
  # 6. Compiler
437
441
 
438
- The compiler (`src/compiler.js`) is a clean reimplementation replacing the old compiler (6,016 lines) with ~3,150 lines producing identical JavaScript output.
442
+ The compiler (`src/compiler.js`) is a clean reimplementation replacing the old compiler (6,016 lines) with ~3,290 lines producing identical JavaScript output.
439
443
 
440
444
  ## Structure
441
445
 
@@ -479,7 +483,7 @@ The `Compiler` class's lexer adapter reconstructs `new String()` wrapping from t
479
483
 
480
484
  | Area | Old lines | New lines | Reduction |
481
485
  |------|-----------|-----------|-----------|
482
- | Total file | 6,016 | ~3,150 | **48%** |
486
+ | Total file | 6,016 | ~3,290 | **45%** |
483
487
  | Body generation | ~500 | ~200 | 60% |
484
488
  | Variable collection | ~230 | ~100 | 57% |
485
489
  | Helper methods | ~600 | ~250 | 58% |
@@ -490,7 +494,7 @@ The `Compiler` class's lexer adapter reconstructs `new String()` wrapping from t
490
494
 
491
495
  **Solar** is a complete SLR(1) parser generator included with Rip — written in Rip, compiled by Rip, zero external dependencies.
492
496
 
493
- **Location:** `src/grammar/solar.rip` (1,001 lines)
497
+ **Location:** `src/grammar/solar.rip` (916 lines)
494
498
 
495
499
  ## Grammar Syntax
496
500
 
@@ -529,7 +533,7 @@ Parenthetical: [
529
533
  | Parse time | 12,500ms | ~50ms |
530
534
  | Dependencies | Many | Zero |
531
535
  | Self-hosting | No | Yes |
532
- | Code size | 2,285 LOC | 1,001 LOC |
536
+ | Code size | 2,285 LOC | 916 LOC |
533
537
 
534
538
  After modifying `src/grammar/grammar.rip`:
535
539
 
@@ -558,27 +562,21 @@ rip> .sexp # Toggle s-expression display
558
562
  rip> .js # Toggle JS display
559
563
  ```
560
564
 
561
- Compare old and new compilers across all test suites:
562
-
563
- ```bash
564
- bun src/compare-compilers.js
565
- ```
566
-
567
565
  ---
568
566
 
569
567
  # 9. Future Work
570
568
 
571
- - Comment preservation for source maps
572
569
  - Parser update to read `.data` directly instead of `new String()` properties
573
570
  - Once parser supports `.data`, the `meta()`/`str()` helpers become trivial to update
574
571
 
575
572
  ---
576
573
 
577
574
  **See Also:**
575
+ - [RIP-GUIDE.md](RIP-GUIDE.md) — Practical guide for using Rip
578
576
  - [RIP-LANG.md](RIP-LANG.md) — Language reference
579
577
  - [RIP-TYPES.md](RIP-TYPES.md) — Type system specification
580
578
  - [RIP-REACTIVITY.md](RIP-REACTIVITY.md) — Reactivity deep dive
581
579
 
582
580
  ---
583
581
 
584
- *Rip 3.0 — 1,130 tests passing — Zero dependencies — Self-hosting — ~8,800 LOC*
582
+ *Rip 3.4 — 1,140 tests passing — Zero dependencies — Self-hosting — ~10,300 LOC*
package/docs/RIP-LANG.md CHANGED
@@ -1,6 +1,8 @@
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
- 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, ~7,700 LOC.
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.
4
6
 
5
7
  ---
6
8
 
@@ -1163,4 +1165,4 @@ count = 10 # Logs: "Count: 10, Doubled: 20"
1163
1165
 
1164
1166
  ---
1165
1167
 
1166
- *Rip 3.0 — 1,073 tests passing — Zero dependencies — Self-hosting — ~7,700 LOC*
1168
+ *Rip 3.4 — 1,140 tests passing — Zero dependencies — Self-hosting — ~10,300 LOC*
@@ -1,4 +1,6 @@
1
- # Rip Reactivity System
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
@@ -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 Types
2
4
 
3
5
  > **Types describe what you mean. Code does what you say.**
@@ -3983,7 +3983,7 @@ if (typeof globalThis !== 'undefined') {
3983
3983
  };
3984
3984
  }
3985
3985
 
3986
- // src/sourcemap.js
3986
+ // src/sourcemaps.js
3987
3987
  var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3988
3988
  function vlqEncode(value) {
3989
3989
  let result = "";
@@ -7536,8 +7536,8 @@ function getComponentRuntime() {
7536
7536
  return new CodeGenerator({}).getComponentRuntime();
7537
7537
  }
7538
7538
  // src/browser.js
7539
- var VERSION = "3.4.3";
7540
- var BUILD_DATE = "2026-02-09@07:17:43GMT";
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.3",g2="2026-02-09@07:17:43GMT",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};
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
@@ -191,7 +191,7 @@
191
191
  display: flex;
192
192
  align-items: flex-start;
193
193
  gap: 4px;
194
- background: #1e1e1e;
194
+ background: #2d2d30;
195
195
  padding: 8px 10px;
196
196
  border: 1px solid #3e3e42;
197
197
  border-radius: 4px;
@@ -907,9 +907,13 @@ console.log "Domain:", domain`;
907
907
  modeToggle.addEventListener('click', () => {
908
908
  isDark = !isDark;
909
909
  applyMode();
910
- const defaultTheme = isDark ? 'vs-dark' : 'vs';
911
- themeSelect.value = defaultTheme;
912
- setTheme(defaultTheme);
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",
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.4",
71
- "@rip-lang/ui": "0.1.2"
70
+ "@rip-lang/api": "1.1.5",
71
+ "@rip-lang/ui": "0.1.3"
72
72
  }
73
73
  }
package/src/compiler.js CHANGED
@@ -12,7 +12,7 @@ import { Lexer } from './lexer.js';
12
12
  import { parser } from './parser.js';
13
13
  import { installComponentSupport } from './components.js';
14
14
  import { emitTypes, generateEnum } from './types.js';
15
- import { SourceMapGenerator } from './sourcemap.js';
15
+ import { SourceMapGenerator } from './sourcemaps.js';
16
16
 
17
17
  // =============================================================================
18
18
  // Metadata helpers — isolate all new String() awareness here
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>&lt;script type="text/rip"&gt;</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>
File without changes