rip-lang 3.0.0 → 3.0.2

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.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
@@ -6,7 +6,7 @@
6
6
  # Each rule maps a pattern of tokens/types to an s-expression action.
7
7
  #
8
8
  # Action format:
9
- # - Numbers (1, 2, 3...) reference matched symbols by position
9
+ # - Numbers (1, 2, ...3) reference matched symbols by position
10
10
  # - ...N spreads the array at position N
11
11
  # - String literals become s-expression nodes: '["if", 2, 3]'
12
12
  # - Default action (no action given) returns position 1
@@ -15,10 +15,8 @@
15
15
  # - Removed :: and ?:: (prototype access) — reserved for future type annotations
16
16
  # - Removed INDEX_SOAK — use ES6 optional indexing (?.[i]) instead
17
17
  # - Removed FUNC_EXIST / OptFuncExist — use ES6 optional call (?.(args)) instead
18
- # - Removed Expression ? (existence check) — use ?? instead
19
18
  # - Renamed FORFROM → FORAS, for-from → for-as (for x as iterable)
20
19
  # - Simplified Invocation (no soak variants)
21
- #
22
20
  # ==============================================================================
23
21
 
24
22
  o = (pattern, action, options) ->
@@ -796,6 +794,9 @@ grammar =
796
794
  o '- Expression' , '["-", 2]', prec: 'UNARY_MATH'
797
795
  o '+ Expression' , '["+", 2]', prec: 'UNARY_MATH'
798
796
 
797
+ # Postfix existence check: expr? → (expr != null)
798
+ o 'Value ?' , '["?", 1]'
799
+
799
800
  # Await
800
801
  o 'AWAIT Expression' , '["await", 2]'
801
802
  o 'AWAIT INDENT Object OUTDENT' , '["await", 3]'
@@ -856,7 +857,7 @@ operators = """
856
857
  right DO_IIFE
857
858
  left . ?.
858
859
  left CALL_START CALL_END
859
- nonassoc ++ --
860
+ nonassoc ++ -- ?
860
861
  right UNARY DO
861
862
  right AWAIT
862
863
  right **
@@ -53,7 +53,7 @@ class Item
53
53
 
54
54
  # LR State: A set of items with transitions to other states
55
55
  class State
56
- constructor: (items...) ->
56
+ constructor: (...items) ->
57
57
  @id = null # state number (assigned later)
58
58
  @items = new Set(items) # kernel and closure items
59
59
  @transitions = new Map # symbol → next state
package/src/lexer.js CHANGED
@@ -643,6 +643,12 @@ export class Lexer {
643
643
 
644
644
  // If we're in an unfinished expression, suppress the newline
645
645
  if (this.isUnfinished()) {
646
+ // Exception: comma at a lower indent continues the outer call, not the block
647
+ if (size < this.indent && /^\s*,/.test(this.chunk) && !UNFINISHED.has(this.prevTag())) {
648
+ this.outdentTo(size, indent.length);
649
+ if (this.prevTag() === 'TERMINATOR') this.tokens.pop();
650
+ return indent.length;
651
+ }
646
652
  return indent.length;
647
653
  }
648
654
 
@@ -1070,6 +1076,8 @@ export class Lexer {
1070
1076
  else if (SHIFT.has(val)) tag = 'SHIFT';
1071
1077
  // Spaced ? → SPACE? (ternary)
1072
1078
  else if (val === '?' && prev?.spaced) tag = 'SPACE?';
1079
+ // ?[ and ?( without dot → treat as optional chaining (?.)
1080
+ else if (val === '?' && (this.chunk[1] === '[' || this.chunk[1] === '(')) tag = '?.';
1073
1081
  // Call/index context (ES6 optional chaining only)
1074
1082
  else if (prev) {
1075
1083
  if (val === '(' && !prev.spaced && CALLABLE.has(prev[0])) {
@@ -1184,12 +1192,11 @@ export class Lexer {
1184
1192
  let starter = null;
1185
1193
  let indent = null;
1186
1194
  let outdent = null;
1187
-
1188
1195
  let condition = (token, i) => {
1189
1196
  return token[1] !== ';' && SINGLE_CLOSERS.has(token[0]) &&
1190
1197
  !(token[0] === 'TERMINATOR' && EXPRESSION_CLOSE.has(this.tokens[i + 1]?.[0])) &&
1191
1198
  !(token[0] === 'ELSE' && starter !== 'THEN') ||
1192
- token[0] === ',' && (starter === '->' || starter === '=>') ||
1199
+ token[0] === ',' && (starter === '->' || starter === '=>') && !this.commaInImplicitCall(i) ||
1193
1200
  CALL_CLOSERS.has(token[0]) && (this.tokens[i - 1]?.newLine || this.tokens[i - 1]?.[0] === 'OUTDENT');
1194
1201
  };
1195
1202
 
@@ -1491,6 +1498,27 @@ export class Lexer {
1491
1498
  }
1492
1499
  }
1493
1500
 
1501
+ // Scan backward from comma to see if it's inside an implicit function call
1502
+ commaInImplicitCall(i) {
1503
+ let levels = 0;
1504
+ for (let j = i - 1; j >= 0; j--) {
1505
+ let tag = this.tokens[j][0];
1506
+ if (EXPRESSION_END.has(tag)) { levels++; continue; }
1507
+ if (EXPRESSION_START.has(tag)) {
1508
+ if (tag === 'INDENT') return false;
1509
+ levels--;
1510
+ if (levels < 0) return false;
1511
+ continue;
1512
+ }
1513
+ if (levels > 0) continue;
1514
+ if (IMPLICIT_FUNC.has(tag) && this.tokens[j].spaced) {
1515
+ let nt = this.tokens[j + 1]?.[0];
1516
+ return IMPLICIT_CALL.has(nt) || (nt === '...' && IMPLICIT_CALL.has(this.tokens[j + 2]?.[0]));
1517
+ }
1518
+ }
1519
+ return false;
1520
+ }
1521
+
1494
1522
  looksObjectish(j) {
1495
1523
  if (!this.tokens[j]) return false;
1496
1524
  if (this.tokens[j]?.[0] === '@' && this.tokens[j + 2]?.[0] === ':') return true;