rip-lang 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/rip-agent-onboarding.md +681 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +98 -0
- package/.github/ISSUE_TEMPLATE/coffeescript_compatibility.yml +86 -0
- package/.github/ISSUE_TEMPLATE/config.yml +9 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +82 -0
- package/.github/ISSUE_TEMPLATE/question.yml +55 -0
- package/.github/pull_request_template.md +84 -0
- package/AGENT.md +623 -0
- package/CONTRIBUTING.md +331 -0
- package/LICENSE +21 -0
- package/README.md +1245 -0
- package/SETUP.md +144 -0
- package/bar.coffee +394 -0
- package/bin/rip +162 -0
- package/bunfig.toml +11 -0
- package/docs/BROWSER.md +983 -0
- package/docs/CODEGEN.md +1023 -0
- package/docs/COFFEESCRIPT-COMPARISON.md +740 -0
- package/docs/COMPREHENSIONS.md +572 -0
- package/docs/REGEX-PLUS.md +441 -0
- package/docs/SOLAR.md +846 -0
- package/docs/SPECIAL-OPERATORS.md +769 -0
- package/docs/STRING.md +363 -0
- package/docs/WHY-NOT-COFFEESCRIPT.md +184 -0
- package/docs/WHY-YES-RIP.md +690 -0
- package/docs/WORKFLOW.md +306 -0
- package/docs/dist/rip.browser.js +6798 -0
- package/docs/dist/rip.browser.min.js +242 -0
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/example.html +177 -0
- package/docs/examples/README.md +154 -0
- package/docs/examples/arrows.rip +84 -0
- package/docs/examples/async-await.rip +59 -0
- package/docs/examples/existential.rip +86 -0
- package/docs/examples/fibonacci.rip +12 -0
- package/docs/examples/module.rip +38 -0
- package/docs/examples/object-syntax.rip +74 -0
- package/docs/examples/prototype.rip +30 -0
- package/docs/examples/ranges.rip +45 -0
- package/docs/examples/switch.rip +50 -0
- package/docs/examples/ternary.rip +36 -0
- package/docs/examples/use-loader.js +9 -0
- package/docs/examples/utils.rip +20 -0
- package/docs/index.html +65 -0
- package/docs/repl.html +914 -0
- package/docs/rip-1280w.png +0 -0
- package/docs/rip.svg +4 -0
- package/package.json +52 -0
- package/rip-loader.ts +27 -0
- package/scripts/build-browser.js +76 -0
- package/scripts/serve.js +71 -0
- package/src/browser.js +97 -0
- package/src/codegen.js +4808 -0
- package/src/compiler.js +270 -0
- package/src/grammar/grammar.rip +801 -0
- package/src/grammar/solar.rip +1056 -0
- package/src/lexer.js +3145 -0
- package/src/parser.js +352 -0
- package/src/repl.js +423 -0
- package/test/rip/assignment.rip +115 -0
- package/test/rip/async.rip +361 -0
- package/test/rip/basic.rip +171 -0
- package/test/rip/classes.rip +167 -0
- package/test/rip/compatibility.rip +338 -0
- package/test/rip/comprehensions.rip +104 -0
- package/test/rip/control.rip +177 -0
- package/test/rip/data.rip +215 -0
- package/test/rip/errors.rip +129 -0
- package/test/rip/functions.rip +443 -0
- package/test/rip/guards.rip +247 -0
- package/test/rip/literals.rip +131 -0
- package/test/rip/loops.rip +117 -0
- package/test/rip/modules.rip +87 -0
- package/test/rip/operators.rip +158 -0
- package/test/rip/optional.rip +184 -0
- package/test/rip/properties.rip +94 -0
- package/test/rip/regex.rip +301 -0
- package/test/rip/stabilization.rip +825 -0
- package/test/rip/strings.rip +483 -0
- package/test/runner.js +329 -0
package/AGENT.md
ADDED
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
# AI Agent Guide for Rip
|
|
2
|
+
|
|
3
|
+
**Purpose:** This document helps AI assistants understand and work with the Rip language compiler.
|
|
4
|
+
|
|
5
|
+
**What is Rip:** An elegant scripting language that compiles to modern JavaScript (ES2022), featuring zero dependencies, self-hosting capability, and an S-expression intermediate representation.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Quick Reference
|
|
10
|
+
|
|
11
|
+
### What Rip Does
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Rip Source Code → JavaScript (ES2022)
|
|
15
|
+
|
|
16
|
+
# Input
|
|
17
|
+
def greet(name)
|
|
18
|
+
"Hello, ${name}!"
|
|
19
|
+
|
|
20
|
+
# Output
|
|
21
|
+
function greet(name) {
|
|
22
|
+
return `Hello, ${name}!`;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### The Compilation Pipeline
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
Source → Lexer → Parser → S-Expressions → Codegen → JavaScript
|
|
30
|
+
3,145 340 LOC (arrays!) 4,738 ES2022
|
|
31
|
+
LOC generated LOC
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Components:**
|
|
35
|
+
1. **Lexer** (`src/lexer.js`) - CoffeeScript 2.7 lexer (battle-tested, 15 years)
|
|
36
|
+
2. **Parser** (`src/parser.js`) - SLR(1) parser generated by solar.rip
|
|
37
|
+
3. **Codegen** (`src/codegen.js`) - Pattern-matches s-expressions → JavaScript
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Essential Commands
|
|
42
|
+
|
|
43
|
+
### Running Code
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Run .rip files directly (Bun loader auto-compiles)
|
|
47
|
+
bun script.rip
|
|
48
|
+
|
|
49
|
+
# Interactive REPL
|
|
50
|
+
./bin/rip
|
|
51
|
+
|
|
52
|
+
# Compile to JavaScript
|
|
53
|
+
./bin/rip input.rip
|
|
54
|
+
./bin/rip -o output.js input.rip
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Testing
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# All tests (864 total)
|
|
61
|
+
bun run test
|
|
62
|
+
|
|
63
|
+
# Specific test file
|
|
64
|
+
bun test/runner.js test/rip/functions.rip
|
|
65
|
+
|
|
66
|
+
# During development (bypass Bun cache)
|
|
67
|
+
bun --no-cache test/runner.js test/rip
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Debugging
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# See tokens (lexer output)
|
|
74
|
+
echo 'x = 42' | ./bin/rip -t
|
|
75
|
+
|
|
76
|
+
# See s-expressions (parser output)
|
|
77
|
+
echo 'x = 42' | ./bin/rip -s
|
|
78
|
+
|
|
79
|
+
# See JavaScript (codegen output)
|
|
80
|
+
echo 'x = 42' | ./bin/rip -c
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Build Commands
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
bun run parser # Rebuild parser from grammar (self-hosting)
|
|
87
|
+
bun run browser # Build 43KB browser bundle
|
|
88
|
+
bun run serve # Start dev server (REPL at localhost:3000)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Key Architecture Concepts
|
|
94
|
+
|
|
95
|
+
### S-Expressions (The Core Innovation)
|
|
96
|
+
|
|
97
|
+
Rip uses **simple arrays** instead of complex AST node classes:
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
// Traditional AST approach (complex)
|
|
101
|
+
class BinaryOp {
|
|
102
|
+
constructor(op, left, right) { ... }
|
|
103
|
+
compile() { /* 50+ lines */ }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Rip's s-expression approach (simple)
|
|
107
|
+
['+', left, right]
|
|
108
|
+
|
|
109
|
+
// Code generation
|
|
110
|
+
case '+': {
|
|
111
|
+
const [left, right] = rest;
|
|
112
|
+
return `(${this.generate(left)} + ${this.generate(right)})`;
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**S-expression examples:**
|
|
117
|
+
- Assignment: `["=", "x", 42]`
|
|
118
|
+
- Function: `["def", "add", ["a", "b"], body]`
|
|
119
|
+
- Binary op: `["+", left, right]`
|
|
120
|
+
- Function call: `[callee, ...args]` (array without string head)
|
|
121
|
+
|
|
122
|
+
### Context-Aware Generation
|
|
123
|
+
|
|
124
|
+
The codegen passes a `context` parameter to determine how to compile patterns:
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
generate(sexpr, context = 'statement')
|
|
128
|
+
// context: 'statement' | 'value'
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Example - Comprehensions:**
|
|
132
|
+
- **Statement context** (result discarded) → Plain loop
|
|
133
|
+
- **Value context** (result used) → IIFE with array building
|
|
134
|
+
|
|
135
|
+
### Variable Scoping
|
|
136
|
+
|
|
137
|
+
CoffeeScript-style function scoping with closure access:
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
// Program level
|
|
141
|
+
let a, b, fn;
|
|
142
|
+
|
|
143
|
+
// Function level - only NEW variables
|
|
144
|
+
fn = function() {
|
|
145
|
+
let x, y; // New variables
|
|
146
|
+
a = 1; // Uses outer 'a' (closure)
|
|
147
|
+
};
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Implementation:**
|
|
151
|
+
- `collectProgramVariables()` - Top-level vars
|
|
152
|
+
- `collectFunctionVariables()` - Function-local vars (excludes outer vars)
|
|
153
|
+
|
|
154
|
+
### Auto-Detection
|
|
155
|
+
|
|
156
|
+
Functions automatically become `async` or `function*`:
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
containsAwait(sexpr) // → async function
|
|
160
|
+
containsYield(sexpr) // → function*
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Stops at function boundaries (nested functions checked separately).
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Critical Patterns
|
|
168
|
+
|
|
169
|
+
### Block Unwrapping
|
|
170
|
+
|
|
171
|
+
The parser wraps statements in `["block", ...statements]` everywhere. **Always unwrap:**
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
if (Array.isArray(body) && body[0] === 'block') {
|
|
175
|
+
const statements = body.slice(1); // Unwrap!
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### for-of Variable Ordering
|
|
180
|
+
|
|
181
|
+
**Critical:** When using `for own k, v of obj when guard`, the order matters:
|
|
182
|
+
|
|
183
|
+
```javascript
|
|
184
|
+
for (const k in obj) {
|
|
185
|
+
if (obj.hasOwnProperty(k)) { // 1. Own check FIRST
|
|
186
|
+
const v = obj[k]; // 2. Assign value SECOND
|
|
187
|
+
if (v > 5) { // 3. Guard check THIRD (can use v!)
|
|
188
|
+
// body
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Bug to avoid:** Never check guard before value variable is assigned!
|
|
195
|
+
|
|
196
|
+
### String Object Metadata
|
|
197
|
+
|
|
198
|
+
The lexer attaches metadata as String objects (not primitives):
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
// Check BEFORE converting to primitive
|
|
202
|
+
if (sexpr instanceof String) {
|
|
203
|
+
const metadata = sexpr.quote || sexpr.heregex || sexpr.await;
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Key metadata:**
|
|
208
|
+
- `.quote` - Original quote type (`'` or `"`)
|
|
209
|
+
- `.heregex` - Extended regex flag
|
|
210
|
+
- `.await` - Dammit operator flag
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Language Features Quick Reference
|
|
215
|
+
|
|
216
|
+
### Dual Optional Syntax (10 operators)
|
|
217
|
+
|
|
218
|
+
**CoffeeScript soak style:**
|
|
219
|
+
- `arr?[0]` → `(arr != null ? arr[0] : undefined)`
|
|
220
|
+
- `fn?(x)` → `(typeof fn === 'function' ? fn(x) : undefined)`
|
|
221
|
+
|
|
222
|
+
**ES6 optional chaining:**
|
|
223
|
+
- `obj?.prop` → `obj?.prop` (native)
|
|
224
|
+
- `arr?.[0]` → `arr?.[0]` (native)
|
|
225
|
+
- `fn?.(x)` → `fn?.(x)` (native)
|
|
226
|
+
|
|
227
|
+
Both can be mixed: `obj?.arr?[0]`
|
|
228
|
+
|
|
229
|
+
### Sigil Operators (!)
|
|
230
|
+
|
|
231
|
+
**At call-site (dammit):** Calls AND awaits
|
|
232
|
+
```rip
|
|
233
|
+
result = fetchData! # → await fetchData()
|
|
234
|
+
user = getUser!(id) # → await getUser(id)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**At definition (void):** Suppresses returns
|
|
238
|
+
```rip
|
|
239
|
+
def process! # → function process() { ...; return; }
|
|
240
|
+
doWork()
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Comprehensions (Context-Aware)
|
|
244
|
+
|
|
245
|
+
- **Value context** (result used) → IIFE builds array
|
|
246
|
+
- **Statement context** (result discarded) → Plain loop
|
|
247
|
+
|
|
248
|
+
See `docs/COMPREHENSIONS.md` for complete rules.
|
|
249
|
+
|
|
250
|
+
### CoffeeScript Compatibility
|
|
251
|
+
|
|
252
|
+
The lexer automatically converts:
|
|
253
|
+
- Postfix spread: `args...` → `...args`
|
|
254
|
+
- Legacy existential: `x ? y` → `x ?? y` (unless ternary)
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## File Organization
|
|
259
|
+
|
|
260
|
+
### Main Source Files
|
|
261
|
+
|
|
262
|
+
| File | Purpose | Can Modify? |
|
|
263
|
+
|------|---------|-------------|
|
|
264
|
+
| `src/lexer.js` | Tokenization + rewriter | ⚠️ Rewriter only |
|
|
265
|
+
| `src/parser.js` | S-expression parser | ❌ Generated (don't edit) |
|
|
266
|
+
| `src/codegen.js` | JavaScript generator | ✅ Main work happens here |
|
|
267
|
+
| `src/compiler.js` | Pipeline orchestration | ✅ Yes |
|
|
268
|
+
| `src/repl.js` | Terminal REPL | ✅ Yes |
|
|
269
|
+
| `src/browser.js` | Browser integration | ✅ Yes |
|
|
270
|
+
|
|
271
|
+
### Grammar and Generator
|
|
272
|
+
|
|
273
|
+
| File | Purpose | Can Modify? |
|
|
274
|
+
|------|---------|-------------|
|
|
275
|
+
| `src/grammar/grammar.rip` | Grammar specification | ⚠️ Expert only |
|
|
276
|
+
| `src/grammar/solar.rip` | Parser generator | ❌ No (given) |
|
|
277
|
+
|
|
278
|
+
**To regenerate parser:** `bun run parser`
|
|
279
|
+
|
|
280
|
+
### Test Files
|
|
281
|
+
|
|
282
|
+
| Directory | Contents |
|
|
283
|
+
|-----------|----------|
|
|
284
|
+
| `test/rip/` | 20 test files, 864 tests total |
|
|
285
|
+
| `test/runner.js` | Test framework |
|
|
286
|
+
|
|
287
|
+
**Test types:**
|
|
288
|
+
```rip
|
|
289
|
+
test "name", "code", expectedResult # Execute and compare
|
|
290
|
+
code "name", "input", "output" # Compare generated code
|
|
291
|
+
fail "name", "code" # Expect compilation failure
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Documentation
|
|
295
|
+
|
|
296
|
+
| File | Content |
|
|
297
|
+
|------|---------|
|
|
298
|
+
| `docs/CODEGEN.md` | 110+ node types reference |
|
|
299
|
+
| `docs/COMPREHENSIONS.md` | Context-aware generation |
|
|
300
|
+
| `docs/SOLAR.md` | Parser generator guide |
|
|
301
|
+
| `docs/STRING.md` | String metadata reference |
|
|
302
|
+
| `docs/REGEX-PLUS.md` | Ruby-style regex features |
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Development Workflow
|
|
307
|
+
|
|
308
|
+
### Modifying Existing Features
|
|
309
|
+
|
|
310
|
+
1. Check what parser emits: `echo 'code' | ./bin/rip -s`
|
|
311
|
+
2. Find the case in `src/codegen.js`
|
|
312
|
+
3. Modify the generation logic
|
|
313
|
+
4. Run tests: `bun run test`
|
|
314
|
+
5. Commit
|
|
315
|
+
|
|
316
|
+
### Adding New Features
|
|
317
|
+
|
|
318
|
+
1. Check if lexer/parser already support it
|
|
319
|
+
2. If grammar change needed: edit `src/grammar/grammar.rip` and run `bun run parser`
|
|
320
|
+
3. Add case to `src/codegen.js`
|
|
321
|
+
4. Write tests in `test/rip/`
|
|
322
|
+
5. Document in `docs/CODEGEN.md`
|
|
323
|
+
6. Run tests and commit
|
|
324
|
+
|
|
325
|
+
### Debugging Issues
|
|
326
|
+
|
|
327
|
+
1. Use `-s` to see parser output
|
|
328
|
+
2. Use `-c` to see codegen output
|
|
329
|
+
3. Use `-t` to see lexer tokens
|
|
330
|
+
4. Check for String object metadata
|
|
331
|
+
5. Verify context parameter usage
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Common Issues & Solutions
|
|
336
|
+
|
|
337
|
+
### "Unknown s-expression type: X"
|
|
338
|
+
|
|
339
|
+
**Problem:** Codegen missing a case for this pattern
|
|
340
|
+
|
|
341
|
+
**Solution:**
|
|
342
|
+
```bash
|
|
343
|
+
echo 'your code' | ./bin/rip -s # See what parser emits
|
|
344
|
+
grep "case 'X':" src/codegen.js # Check if implemented
|
|
345
|
+
# Add the missing case if needed
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### "Unexpected token" in Output
|
|
349
|
+
|
|
350
|
+
**Problem:** Generated JavaScript has syntax error
|
|
351
|
+
|
|
352
|
+
**Solution:**
|
|
353
|
+
```bash
|
|
354
|
+
echo 'your code' | ./bin/rip -c # See generated code
|
|
355
|
+
echo 'your code' | ./bin/rip -s # Check the s-expression
|
|
356
|
+
# Fix the codegen logic for that pattern
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Tests Not Reflecting Changes
|
|
360
|
+
|
|
361
|
+
**Problem:** Bun aggressively caches compiled modules
|
|
362
|
+
|
|
363
|
+
**Solution:**
|
|
364
|
+
```bash
|
|
365
|
+
bun --no-cache test/runner.js test/rip
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Block Not Unwrapping
|
|
369
|
+
|
|
370
|
+
**Problem:** Treating `["block", ...]` as a single node
|
|
371
|
+
|
|
372
|
+
**Solution:** Always check and unwrap:
|
|
373
|
+
```javascript
|
|
374
|
+
if (Array.isArray(body) && body[0] === 'block') {
|
|
375
|
+
const statements = body.slice(1);
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Key Implementation Details
|
|
382
|
+
|
|
383
|
+
### Helper Functions in Codegen
|
|
384
|
+
|
|
385
|
+
**Variable collection:**
|
|
386
|
+
- `collectProgramVariables(sexpr)` - Top-level
|
|
387
|
+
- `collectFunctionVariables(body)` - Function-local
|
|
388
|
+
|
|
389
|
+
**Body generation:**
|
|
390
|
+
- `generateFunctionBody(body, params, sideEffectOnly)` - Functions with implicit returns
|
|
391
|
+
- `generateLoopBody(body)` - Loops without returns
|
|
392
|
+
- `generateBlockWithReturns(block)` - IIFE blocks
|
|
393
|
+
|
|
394
|
+
**String processing:**
|
|
395
|
+
- `extractStringContent(strObj)` - Heredoc handling
|
|
396
|
+
- `processHeregex(content)` - Strip whitespace/comments
|
|
397
|
+
|
|
398
|
+
**Detection:**
|
|
399
|
+
- `containsAwait(sexpr)` - Auto-async
|
|
400
|
+
- `containsYield(sexpr)` - Auto-generator
|
|
401
|
+
|
|
402
|
+
### Critical Edge Cases
|
|
403
|
+
|
|
404
|
+
**1. for-of with guards and value variables:**
|
|
405
|
+
```javascript
|
|
406
|
+
// Correct order: own check → value assign → guard check
|
|
407
|
+
for (const k in obj) {
|
|
408
|
+
if (obj.hasOwnProperty(k)) {
|
|
409
|
+
const v = obj[k]; // BEFORE guard
|
|
410
|
+
if (guard) { } // After v is defined
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
**2. Postfix conditionals in assignments:**
|
|
416
|
+
```rip
|
|
417
|
+
x = 5 unless done
|
|
418
|
+
# Generate: if (!done) x = 5;
|
|
419
|
+
# NOT: x = (!done ? 5 : undefined) ← Would always assign!
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
**3. Switch in value context:**
|
|
423
|
+
```rip
|
|
424
|
+
result = switch x
|
|
425
|
+
when 1 then 'one'
|
|
426
|
+
when 2 then 'two'
|
|
427
|
+
# Needs IIFE wrapper, not statement form
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## Quick Pattern Reference
|
|
433
|
+
|
|
434
|
+
### Common S-Expression Patterns
|
|
435
|
+
|
|
436
|
+
```javascript
|
|
437
|
+
// Assignment
|
|
438
|
+
["=", target, value]
|
|
439
|
+
|
|
440
|
+
// Function definition
|
|
441
|
+
["def", "name", params, body]
|
|
442
|
+
|
|
443
|
+
// Arrow functions
|
|
444
|
+
["->", params, body] // Thin arrow (unbound this)
|
|
445
|
+
["=>", params, body] // Fat arrow (bound this)
|
|
446
|
+
|
|
447
|
+
// Conditionals
|
|
448
|
+
["if", condition, thenBlock, elseBlock?]
|
|
449
|
+
["unless", condition, body]
|
|
450
|
+
["?:", condition, trueExpr, falseExpr] // Ternary
|
|
451
|
+
|
|
452
|
+
// Loops
|
|
453
|
+
["for-in", vars, iterable, step?, guard?, body]
|
|
454
|
+
["for-of", vars, object, guard?, body]
|
|
455
|
+
["while", condition, body]
|
|
456
|
+
|
|
457
|
+
// Data structures
|
|
458
|
+
["array", ...elements]
|
|
459
|
+
["object", ...pairs] // pairs: [key, value]
|
|
460
|
+
|
|
461
|
+
// Operators
|
|
462
|
+
["+", left, right]
|
|
463
|
+
["==", left, right] // Maps to ===
|
|
464
|
+
["&&", left, right]
|
|
465
|
+
|
|
466
|
+
// Property access
|
|
467
|
+
[".", obj, "prop"]
|
|
468
|
+
["?..", obj, "prop"] // Optional
|
|
469
|
+
["[]", arr, index]
|
|
470
|
+
["?[]", arr, index] // Soak
|
|
471
|
+
|
|
472
|
+
// Special
|
|
473
|
+
["await", expr]
|
|
474
|
+
["yield", expr]
|
|
475
|
+
["return", expr?]
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
See `docs/CODEGEN.md` for complete catalog (110+ node types).
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## Test Framework
|
|
483
|
+
|
|
484
|
+
### Three Test Types
|
|
485
|
+
|
|
486
|
+
```rip
|
|
487
|
+
# Execute code and compare result
|
|
488
|
+
test "addition", "1 + 2", 3
|
|
489
|
+
|
|
490
|
+
# Compare generated JavaScript
|
|
491
|
+
code "assignment", "x = 42", "x = 42"
|
|
492
|
+
|
|
493
|
+
# Expect compilation failure
|
|
494
|
+
fail "invalid syntax", "let x ="
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Test Best Practices
|
|
498
|
+
|
|
499
|
+
- Use `\n` for simple tests (1-2 lines)
|
|
500
|
+
- Use `"""` for complex tests (3+ lines)
|
|
501
|
+
- Never use `;` to separate statements (parser treats as single statement)
|
|
502
|
+
- Test files are in `test/rip/*.rip` (20 files, organized by feature)
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## Self-Hosting
|
|
507
|
+
|
|
508
|
+
Rip compiles itself, including its parser generator:
|
|
509
|
+
|
|
510
|
+
```bash
|
|
511
|
+
# Rebuild the parser in one command
|
|
512
|
+
bun run parser
|
|
513
|
+
|
|
514
|
+
# What happens:
|
|
515
|
+
# 1. Bun runs solar.rip (parser generator, written in Rip)
|
|
516
|
+
# 2. Solar reads grammar.rip (grammar spec, written in Rip)
|
|
517
|
+
# 3. Outputs parser.js (complete parser)
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**Zero external tools required.** Everything needed to modify and rebuild Rip is included.
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## When Working with Rip
|
|
525
|
+
|
|
526
|
+
### Most Common Task: Fix or Extend Codegen
|
|
527
|
+
|
|
528
|
+
1. Determine the s-expression pattern: `echo 'code' | ./bin/rip -s`
|
|
529
|
+
2. Find or add the case in `src/codegen.js`
|
|
530
|
+
3. Implement the generation logic
|
|
531
|
+
4. Test: `bun run test`
|
|
532
|
+
5. Commit
|
|
533
|
+
|
|
534
|
+
### Less Common: Modify Grammar
|
|
535
|
+
|
|
536
|
+
1. Edit `src/grammar/grammar.rip`
|
|
537
|
+
2. Regenerate parser: `bun run parser`
|
|
538
|
+
3. Update codegen if new node types added
|
|
539
|
+
4. Test thoroughly
|
|
540
|
+
5. Document in `docs/CODEGEN.md`
|
|
541
|
+
|
|
542
|
+
### Understanding Existing Code
|
|
543
|
+
|
|
544
|
+
1. Use `-s` flag to see s-expressions
|
|
545
|
+
2. Search `src/codegen.js` for the pattern
|
|
546
|
+
3. Read tests in `test/rip/` for examples
|
|
547
|
+
4. Check `docs/CODEGEN.md` for documentation
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Important Notes
|
|
552
|
+
|
|
553
|
+
### Zero Dependencies
|
|
554
|
+
|
|
555
|
+
Rip has **zero runtime or build dependencies**. This is intentional and must be maintained. Everything needed is in the source:
|
|
556
|
+
|
|
557
|
+
- Full compiler
|
|
558
|
+
- Parser generator (solar.rip)
|
|
559
|
+
- Test framework
|
|
560
|
+
- Browser bundler
|
|
561
|
+
- REPL (terminal, browser, console)
|
|
562
|
+
|
|
563
|
+
### Bun Loader
|
|
564
|
+
|
|
565
|
+
The `bunfig.toml` + `rip-loader.ts` enable automatic `.rip` file execution:
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
bun script.rip # Compiles on-the-fly, no build step
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
This is the primary way to use Rip. Deno/Node require pre-compilation.
|
|
572
|
+
|
|
573
|
+
### ES2022 Target
|
|
574
|
+
|
|
575
|
+
Rip outputs modern JavaScript that runs in:
|
|
576
|
+
- Bun (primary target)
|
|
577
|
+
- Browsers (modern)
|
|
578
|
+
- Deno
|
|
579
|
+
- Node.js 12+
|
|
580
|
+
|
|
581
|
+
**Features used:** ES6 classes, let/const, arrow functions, template literals, destructuring, async iteration, optional chaining, nullish coalescing, static class fields, top-level await
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## Quick Tips
|
|
586
|
+
|
|
587
|
+
1. **Start with tests** - Look at passing tests to understand patterns
|
|
588
|
+
2. **Use `-s` liberally** - See exactly what parser emits
|
|
589
|
+
3. **Context matters** - Pass correct context ('statement' vs 'value') to children
|
|
590
|
+
4. **Check String objects** - Metadata flows through String objects
|
|
591
|
+
5. **Unwrap blocks** - Parser wraps statements in blocks everywhere
|
|
592
|
+
6. **Test immediately** - Don't modify without running tests
|
|
593
|
+
|
|
594
|
+
---
|
|
595
|
+
|
|
596
|
+
## Documentation Map
|
|
597
|
+
|
|
598
|
+
- **AGENT.md** (this file) - AI assistant guide
|
|
599
|
+
- **README.md** - User documentation
|
|
600
|
+
- **docs/CODEGEN.md** - Complete node type reference (110+ types)
|
|
601
|
+
- **docs/COMPREHENSIONS.md** - Context-aware generation rules
|
|
602
|
+
- **docs/SOLAR.md** - Parser generator + grammar guide
|
|
603
|
+
- **docs/STRING.md** - String metadata reference
|
|
604
|
+
- **docs/REGEX-PLUS.md** - Ruby-style regex (=~ and heregex)
|
|
605
|
+
- **docs/BROWSER.md** - Browser bundle + triple REPL
|
|
606
|
+
- **docs/COFFEESCRIPT-COMPARISON.md** - Feature comparison
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## Philosophy
|
|
611
|
+
|
|
612
|
+
**Simplicity scales.**
|
|
613
|
+
|
|
614
|
+
- Keep the IR simple (s-expressions)
|
|
615
|
+
- Keep the pipeline clear (lex → parse → generate)
|
|
616
|
+
- Keep the code minimal (pattern matching)
|
|
617
|
+
- Test everything (864/864 tests passing)
|
|
618
|
+
|
|
619
|
+
Rip achieves CoffeeScript's elegance with 50% less code by using s-expressions instead of complex AST nodes. The result is easier to understand, extend, and maintain.
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
**For AI Assistants:** You have everything needed to work with Rip. The code is well-tested, the architecture is clear, and the documentation is comprehensive. Trust the tests, use the debug tools, and follow the patterns already established.
|