rip-lang 3.7.0 → 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 CHANGED
@@ -7,6 +7,16 @@ 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.7.3] - 2026-02-11
11
+
12
+ ### Fixes & Polish
13
+
14
+ - **Interface `::` fix** — `::` type annotations in interface bodies no longer produce double colons in `.d.ts` output.
15
+ - **`.=` dot chain support** — `obj.name .= toUpperCase()` now works (walks back full property chain).
16
+ - **`*` merge auto-init** — `*foo = {...}` auto-creates `foo` if undefined via `??= {}`.
17
+ - **`loop n` uses `it`** — Loop counter is accessible as `it` inside the body.
18
+ - **Syntax highlighting** — `|>` pipe operator added to VS Code, Vim, Playground, and rip-print.
19
+
10
20
  ## [3.7.0] - 2026-02-11
11
21
 
12
22
  ### Pipe Operator (`|>`)
package/README.md CHANGED
@@ -9,9 +9,9 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.7.0-blue.svg" alt="Version"></a>
12
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.7.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-1219%2F1219-brightgreen.svg" alt="Tests"></a>
14
+ <a href="#"><img src="https://img.shields.io/badge/tests-1225%2F1225-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
 
@@ -344,7 +344,7 @@ Rip includes optional packages for full-stack development:
344
344
 
345
345
  | Package | Version | Purpose |
346
346
  |---------|---------|---------|
347
- | [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.7.0 | Core language compiler |
347
+ | [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.7.3 | Core language compiler |
348
348
  | [@rip-lang/api](packages/api/) | 1.1.4 | HTTP framework (Sinatra-style routing, 37 validators) |
349
349
  | [@rip-lang/server](packages/server/) | 1.1.3 | Multi-worker app server (hot reload, HTTPS, mDNS) |
350
350
  | [@rip-lang/db](packages/db/) | 1.1.2 | DuckDB server with official UI (pure Bun FFI) |
@@ -390,7 +390,7 @@ rip file.rip # Run
390
390
  rip -c file.rip # Compile
391
391
  rip -t file.rip # Tokens
392
392
  rip -s file.rip # S-expressions
393
- bun run test # 1219 tests
393
+ bun run test # 1225 tests
394
394
  bun run parser # Rebuild parser
395
395
  bun run browser # Build browser bundle
396
396
  ```
package/docs/RIP-LANG.md CHANGED
@@ -412,7 +412,14 @@ users .= map -> it.name
412
412
  users .= sort()
413
413
  ```
414
414
 
415
- Works with any method — built-in or custom, with or without arguments.
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
+ ```
416
423
 
417
424
  ## Merge Assignment (`*`)
418
425
 
@@ -439,7 +439,7 @@ function collectStructuralType(tokens, indentIdx) {
439
439
  optional = true;
440
440
  j++;
441
441
  }
442
- if (tokens[j]?.[1] === ":")
442
+ if (tokens[j]?.[1] === ":" || tokens[j]?.[0] === "TYPE_ANNOTATION")
443
443
  j++;
444
444
  let propTypeTokens = [];
445
445
  let typeDepth = 0;
@@ -1993,13 +1993,18 @@ class Lexer {
1993
1993
  if (CODE_RE.test(val))
1994
1994
  this.tagParameters();
1995
1995
  if (val === "=" && prev && prev[1] === "." && !prev.spaced) {
1996
- let target = this.tokens[this.tokens.length - 2];
1997
- if (target && (target[0] === "IDENTIFIER" || target[0] === "PROPERTY" || target[0] === ")" || target[0] === "]")) {
1996
+ let tokens = this.tokens;
1997
+ let j = tokens.length - 2;
1998
+ while (j >= 1 && tokens[j][0] === "PROPERTY" && tokens[j - 1]?.[1] === ".")
1999
+ j -= 2;
2000
+ if (j >= 0 && (tokens[j][0] === "IDENTIFIER" || tokens[j][0] === ")" || tokens[j][0] === "]")) {
2001
+ let chainTokens = tokens.slice(j, tokens.length - 1);
1998
2002
  prev[0] = "=";
1999
2003
  prev[1] = "=";
2000
- let targetClone = tok(target[0], target[1], { pre: 0, row: target[2], col: target[3], len: target[4] });
2001
- let dot = tok(".", ".", { pre: 0, row: this.row, col: this.col, len: 1 });
2002
- this.tokens.push(targetClone, dot);
2004
+ for (let t of chainTokens) {
2005
+ this.tokens.push(tok(t[0], t[1], { pre: 0, row: t[2], col: t[3], len: t[4] }));
2006
+ }
2007
+ this.tokens.push(tok(".", ".", { pre: 0, row: this.row, col: this.col, len: 1 }));
2003
2008
  return val.length;
2004
2009
  }
2005
2010
  }
@@ -2049,16 +2054,24 @@ class Lexer {
2049
2054
  let m = /^((?:(?!\s)[$\w\x7f-\uffff])+(?:\.[a-zA-Z_$][\w]*)*)(\s*)=(?!=)/.exec(rest);
2050
2055
  if (m) {
2051
2056
  let target = m[1], space = m[2];
2057
+ let parts = target.split(".");
2058
+ let emitTarget = () => {
2059
+ this.emit("IDENTIFIER", parts[0]);
2060
+ for (let i = 1;i < parts.length; i++) {
2061
+ this.emit(".", ".");
2062
+ this.emit("PROPERTY", parts[i]);
2063
+ }
2064
+ };
2065
+ emitTarget();
2066
+ this.emit("=", "=");
2052
2067
  this.emit("IDENTIFIER", "Object");
2053
2068
  this.emit(".", ".");
2054
2069
  this.emit("PROPERTY", "assign");
2055
2070
  this.emit("CALL_START", "(");
2056
- let parts = target.split(".");
2057
- this.emit("IDENTIFIER", parts[0]);
2058
- for (let i = 1;i < parts.length; i++) {
2059
- this.emit(".", ".");
2060
- this.emit("PROPERTY", parts[i]);
2061
- }
2071
+ emitTarget();
2072
+ this.emit("COMPOUND_ASSIGN", "??=");
2073
+ this.emit("{", "{");
2074
+ this.emit("}", "}");
2062
2075
  this.emit(",", ",");
2063
2076
  let comma = this.prev();
2064
2077
  comma.mergeClose = true;
@@ -5638,7 +5651,7 @@ ${this.indent()}}`;
5638
5651
  generateLoopN(head, rest) {
5639
5652
  let [count, body] = rest;
5640
5653
  let n = this.generate(count, "value");
5641
- return `for (let _i = 0; _i < ${n}; _i++) ${this.generateLoopBody(body)}`;
5654
+ return `for (let it = 0; it < ${n}; it++) ${this.generateLoopBody(body)}`;
5642
5655
  }
5643
5656
  generateAwait(head, rest) {
5644
5657
  return `await ${this.generate(rest[0], "value")}`;
@@ -8028,8 +8041,8 @@ function getComponentRuntime() {
8028
8041
  return new CodeGenerator({}).getComponentRuntime();
8029
8042
  }
8030
8043
  // src/browser.js
8031
- var VERSION = "3.7.0";
8032
- var BUILD_DATE = "2026-02-11@09:35:09GMT";
8044
+ var VERSION = "3.7.3";
8045
+ var BUILD_DATE = "2026-02-11@10:29:15GMT";
8033
8046
  if (typeof globalThis !== "undefined" && !globalThis.__rip) {
8034
8047
  new Function(getReactiveRuntime())();
8035
8048
  }