rip-lang 3.6.2 → 3.7.3
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 +37 -1
- package/README.md +42 -17
- package/docs/NOTES.md +93 -0
- package/docs/RIP-GUIDE.md +39 -1
- package/docs/RIP-INTERNALS.md +9 -9
- package/docs/RIP-LANG.md +220 -2
- package/docs/dist/rip.browser.js +449 -307
- package/docs/dist/rip.browser.min.js +206 -206
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/index.html +20 -7
- package/package.json +3 -3
- package/src/compiler.js +201 -126
- package/src/components.js +10 -10
- package/src/grammar/grammar.rip +8 -2
- package/src/lexer.js +91 -5
- package/src/parser.js +123 -120
- package/src/types.js +2 -2
package/docs/RIP-LANG.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Rip Language Reference
|
|
4
4
|
|
|
5
|
-
Rip is a modern reactive language that compiles to ES2022 JavaScript. It combines CoffeeScript's elegant syntax with built-in reactivity primitives. Zero dependencies, self-hosting, ~10,
|
|
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,800 LOC.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -121,6 +121,10 @@ else
|
|
|
121
121
|
status = active ? "on" : "off"
|
|
122
122
|
result = x > 5 ? "big" : "small"
|
|
123
123
|
|
|
124
|
+
# Ternary (Python-style postfix)
|
|
125
|
+
status = "active" if online else "offline"
|
|
126
|
+
label = "big" if x > 5 else "small"
|
|
127
|
+
|
|
124
128
|
# NOTE: Subscript in ternary true-branch needs parentheses
|
|
125
129
|
item = found ? (arr[0]) : default
|
|
126
130
|
|
|
@@ -227,6 +231,15 @@ until done
|
|
|
227
231
|
loop
|
|
228
232
|
data = fetch()
|
|
229
233
|
break if data.complete
|
|
234
|
+
|
|
235
|
+
# Loop N times
|
|
236
|
+
loop 5
|
|
237
|
+
console.log "hi"
|
|
238
|
+
# Compiles to: for (let _i = 0; _i < 5; _i++) { ... }
|
|
239
|
+
|
|
240
|
+
# Loop with variable or expression
|
|
241
|
+
loop retries
|
|
242
|
+
attempt!
|
|
230
243
|
```
|
|
231
244
|
|
|
232
245
|
## Comprehensions
|
|
@@ -275,6 +288,7 @@ Multiple lines
|
|
|
275
288
|
| `of` | `k of obj` | Object key existence |
|
|
276
289
|
| `?` (postfix) | `a?` | Existence check (`a != null`) |
|
|
277
290
|
| `?` (ternary) | `a ? b : c` | Ternary conditional |
|
|
291
|
+
| `if...else` (postfix) | `b if a else c` | Python-style ternary |
|
|
278
292
|
| `?.` `?.[]` `?.()` | `a?.b` `a?.[0]` `a?.()` | Optional chaining (ES6) |
|
|
279
293
|
| `?[]` `?()` | `a?[0]` `a?(x)` | Optional chaining shorthand |
|
|
280
294
|
| `??` | `a ?? b` | Nullish coalescing |
|
|
@@ -293,6 +307,15 @@ Multiple lines
|
|
|
293
307
|
| `!` | Void | `def process!` | Suppresses implicit return |
|
|
294
308
|
| `!?` | Otherwise | `val !? 5` | Default if undefined or throws |
|
|
295
309
|
| `=~` | Match | `str =~ /pat/` | Ruby-style regex match, captures in `_` |
|
|
310
|
+
| `::` | Prototype | `String::trim` | `String.prototype.trim` |
|
|
311
|
+
| `[-n]` | Negative index | `arr[-1]` | `arr.at(-1)` |
|
|
312
|
+
| `*` | String repeat | `"-" * 40` | `"-".repeat(40)` |
|
|
313
|
+
| `<` `<=` | Chained comparison | `1 < x < 10` | `(1 < x) && (x < 10)` |
|
|
314
|
+
| `\|>` | Pipe | `x \|> fn` or `x \|> fn(y)` | `fn(x)` or `fn(x, y)` |
|
|
315
|
+
| `.=` | Method assign | `x .= trim()` | `x = x.trim()` |
|
|
316
|
+
| `*` | Merge assign | `*obj = {a: 1}` | `Object.assign(obj, {a: 1})` |
|
|
317
|
+
| `not in` | Not in | `x not in arr` | Negated membership test |
|
|
318
|
+
| `not of` | Not of | `k not of obj` | Negated key existence |
|
|
296
319
|
|
|
297
320
|
## Assignment Operators
|
|
298
321
|
|
|
@@ -333,6 +356,10 @@ status = active ? 'on' : 'off'
|
|
|
333
356
|
result = valid ? obj.field : null
|
|
334
357
|
output = ready ? compute() : fallback
|
|
335
358
|
|
|
359
|
+
# Python-style postfix ternary
|
|
360
|
+
status = "active" if online else "offline"
|
|
361
|
+
label = "big" if x > 5 else "small"
|
|
362
|
+
|
|
336
363
|
# Nested
|
|
337
364
|
level = score > 90 ? 'A' : score > 80 ? 'B' : score > 70 ? 'C' : 'F'
|
|
338
365
|
|
|
@@ -349,6 +376,162 @@ result = riskyOperation() !? "default"
|
|
|
349
376
|
# If riskyOperation() throws or returns null/undefined, result = "default"
|
|
350
377
|
```
|
|
351
378
|
|
|
379
|
+
## Method Assignment (`.=`)
|
|
380
|
+
|
|
381
|
+
A Rip original. Compound assignment for method calls — apply a method to a
|
|
382
|
+
variable and assign the result back in one step:
|
|
383
|
+
|
|
384
|
+
```coffee
|
|
385
|
+
# Without .= — repeat the variable name every time
|
|
386
|
+
items = items.filter -> it.active
|
|
387
|
+
items = items.map -> it.name
|
|
388
|
+
items = items.sort (a, b) -> a.localeCompare b
|
|
389
|
+
str = str.trim()
|
|
390
|
+
str = str.toLowerCase()
|
|
391
|
+
|
|
392
|
+
# With .= — name it once, transform in place
|
|
393
|
+
items .= filter -> it.active
|
|
394
|
+
items .= map -> it.name
|
|
395
|
+
items .= sort (a, b) -> a.localeCompare b
|
|
396
|
+
str .= trim()
|
|
397
|
+
str .= toLowerCase()
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
`x .= method(args)` compiles to `x = x.method(args)`. It's the method-call
|
|
401
|
+
equivalent of `+=` — just as `x += 5` means `x = x + 5`, `x .= trim()`
|
|
402
|
+
means `x = x.trim()`.
|
|
403
|
+
|
|
404
|
+
This operator is unique to Rip. Other languages have `+=`, `-=`, `*=`, and
|
|
405
|
+
other arithmetic compound assignments, but none extend the concept to method
|
|
406
|
+
calls. Combined with implicit `it`, this enables remarkably concise data
|
|
407
|
+
transformation pipelines:
|
|
408
|
+
|
|
409
|
+
```coffee
|
|
410
|
+
users .= filter -> it.active
|
|
411
|
+
users .= map -> it.name
|
|
412
|
+
users .= sort()
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Works with any method — built-in or custom, with or without arguments. Spacing
|
|
416
|
+
is flexible — all of these are equivalent:
|
|
417
|
+
|
|
418
|
+
```coffee
|
|
419
|
+
str .= trim() # canonical (spaced)
|
|
420
|
+
str.=trim() # compact (no spaces)
|
|
421
|
+
str .=trim() # mixed
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Merge Assignment (`*`)
|
|
425
|
+
|
|
426
|
+
A Rip original. Merge properties into an existing object without repeating
|
|
427
|
+
its name:
|
|
428
|
+
|
|
429
|
+
```coffee
|
|
430
|
+
# Without * — repeat the object name or use verbose Object.assign
|
|
431
|
+
Object.assign config,
|
|
432
|
+
host: "localhost"
|
|
433
|
+
port: 3000
|
|
434
|
+
debug: true
|
|
435
|
+
|
|
436
|
+
# With * — clean and direct
|
|
437
|
+
*config =
|
|
438
|
+
host: "localhost"
|
|
439
|
+
port: 3000
|
|
440
|
+
debug: true
|
|
441
|
+
|
|
442
|
+
# Single line
|
|
443
|
+
*opts = {method: "POST", body: data}
|
|
444
|
+
|
|
445
|
+
# Dotted paths
|
|
446
|
+
*el.style =
|
|
447
|
+
color: "red"
|
|
448
|
+
fontSize: "14px"
|
|
449
|
+
|
|
450
|
+
# Merge user overrides into defaults
|
|
451
|
+
*defaults = userConfig
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
`*target = value` compiles to `Object.assign(target, value)`. The `*` reads
|
|
455
|
+
as "spread these into" — the same concept as `...` spread but as an
|
|
456
|
+
assignment. This is unique to Rip.
|
|
457
|
+
|
|
458
|
+
Common use cases: config objects, options bags, state initialization, DOM
|
|
459
|
+
styling, merging defaults with overrides — anywhere you're setting multiple
|
|
460
|
+
properties on an existing object.
|
|
461
|
+
|
|
462
|
+
## Prototype Operator (`::`)
|
|
463
|
+
|
|
464
|
+
Access `.prototype` with `::` (CoffeeScript-style). Disambiguated from type annotations by spacing:
|
|
465
|
+
|
|
466
|
+
```coffee
|
|
467
|
+
# Prototype access (no space after ::)
|
|
468
|
+
String::starts = String::startsWith
|
|
469
|
+
String::ends = String::endsWith
|
|
470
|
+
String::has = String::includes
|
|
471
|
+
|
|
472
|
+
# Now you can use them
|
|
473
|
+
"hello".starts "he" # true
|
|
474
|
+
"hello.rip".ends ".rip" # true
|
|
475
|
+
"error: bad".has "error" # true
|
|
476
|
+
|
|
477
|
+
# Define new prototype methods
|
|
478
|
+
String::shout = -> @toUpperCase() + "!"
|
|
479
|
+
"hello".shout() # "HELLO!"
|
|
480
|
+
|
|
481
|
+
# Type annotations (space after ::) — unaffected
|
|
482
|
+
name:: string = "Alice"
|
|
483
|
+
def greet(name:: string):: string
|
|
484
|
+
"Hello, #{name}!"
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
The rule is simple: `::` with **no space** before an identifier is prototype access. `::` with a **space** is a type annotation.
|
|
488
|
+
|
|
489
|
+
## Negative Indexing
|
|
490
|
+
|
|
491
|
+
Literal negative indexes compile to `.at()` for Python-style access from the end:
|
|
492
|
+
|
|
493
|
+
```coffee
|
|
494
|
+
arr = [10, 20, 30, 40, 50]
|
|
495
|
+
|
|
496
|
+
arr[-1] # → arr.at(-1) — 50 (last)
|
|
497
|
+
arr[-2] # → arr.at(-2) — 40 (second to last)
|
|
498
|
+
str[-1] # works on strings too
|
|
499
|
+
|
|
500
|
+
arr?[-1] # → arr?.at(-1) — optional variant
|
|
501
|
+
|
|
502
|
+
# Positive and variable indexes are unchanged
|
|
503
|
+
arr[0] # → arr[0] — normal access
|
|
504
|
+
arr[i] # → arr[i] — variable index
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
Only literal negative numbers trigger the `.at()` transform. Variable indexes pass through as-is.
|
|
508
|
+
|
|
509
|
+
## Pipe Operator (`|>`)
|
|
510
|
+
|
|
511
|
+
Pipes a value into a function as its first argument. Chains left-to-right:
|
|
512
|
+
|
|
513
|
+
```coffee
|
|
514
|
+
# Simple reference — value becomes the sole argument
|
|
515
|
+
5 |> double # → double(5)
|
|
516
|
+
10 |> Math.sqrt # → Math.sqrt(10)
|
|
517
|
+
"hello" |> console.log # → console.log("hello")
|
|
518
|
+
|
|
519
|
+
# Multi-arg — value is inserted as the FIRST argument
|
|
520
|
+
5 |> add(3) # → add(5, 3)
|
|
521
|
+
data |> filter(isActive) # → filter(data, isActive)
|
|
522
|
+
"World" |> greet("!") # → greet("World", "!")
|
|
523
|
+
|
|
524
|
+
# Chaining — reads left-to-right like a pipeline
|
|
525
|
+
5 |> double |> add(1) |> console.log
|
|
526
|
+
# → console.log(add(double(5), 1))
|
|
527
|
+
|
|
528
|
+
# Works with dotted methods
|
|
529
|
+
users |> Array.from # → Array.from(users)
|
|
530
|
+
data |> JSON.stringify(null, 2) # → JSON.stringify(data, null, 2)
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
This is the **Elixir-style** pipe — strictly better than F#'s (which only supports bare function references) and cleaner than Hack's (which requires a `%` placeholder). No special syntax to learn; if the right side is a call, the left value goes first.
|
|
534
|
+
|
|
352
535
|
---
|
|
353
536
|
|
|
354
537
|
# 4. Functions
|
|
@@ -420,6 +603,41 @@ def findUser(id)
|
|
|
420
603
|
null
|
|
421
604
|
```
|
|
422
605
|
|
|
606
|
+
## Implicit `it` Parameter
|
|
607
|
+
|
|
608
|
+
Arrow functions with no explicit parameters that reference `it` in the body automatically inject `it` as the parameter:
|
|
609
|
+
|
|
610
|
+
```coffee
|
|
611
|
+
# Without `it` — must name a throwaway variable
|
|
612
|
+
users.filter (u) -> u.active
|
|
613
|
+
names = users.map (u) -> u.name
|
|
614
|
+
|
|
615
|
+
# With `it` — cleaner
|
|
616
|
+
users.filter -> it.active
|
|
617
|
+
names = users.map -> it.name
|
|
618
|
+
orders.filter -> it.total > 100
|
|
619
|
+
|
|
620
|
+
# Works with fat arrows too
|
|
621
|
+
items.map => it.toUpperCase()
|
|
622
|
+
|
|
623
|
+
# Nested arrows — each level gets its own `it`
|
|
624
|
+
# Only the innermost param-less arrow with `it` is affected
|
|
625
|
+
groups.map -> it.items.filter -> it.active
|
|
626
|
+
|
|
627
|
+
# Explicit params still work normally
|
|
628
|
+
items.sort (a, b) -> a - b
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
Compiles to standard JavaScript — `it` becomes a regular function parameter:
|
|
632
|
+
|
|
633
|
+
```coffee
|
|
634
|
+
arr.filter -> it > 5
|
|
635
|
+
# → arr.filter(function(it) { return (it > 5); })
|
|
636
|
+
|
|
637
|
+
arr.map => it.name
|
|
638
|
+
# → arr.map(it => it.name)
|
|
639
|
+
```
|
|
640
|
+
|
|
423
641
|
## Calling Functions
|
|
424
642
|
|
|
425
643
|
```coffee
|
|
@@ -1165,4 +1383,4 @@ count = 10 # Logs: "Count: 10, Doubled: 20"
|
|
|
1165
1383
|
|
|
1166
1384
|
---
|
|
1167
1385
|
|
|
1168
|
-
*Rip 3.
|
|
1386
|
+
*Rip 3.7 — 1,219 tests passing — Zero dependencies — Self-hosting — ~10,800 LOC*
|