rip-lang 3.0.1 → 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.1",
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
@@ -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
@@ -1076,6 +1076,8 @@ export class Lexer {
1076
1076
  else if (SHIFT.has(val)) tag = 'SHIFT';
1077
1077
  // Spaced ? → SPACE? (ternary)
1078
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 = '?.';
1079
1081
  // Call/index context (ES6 optional chaining only)
1080
1082
  else if (prev) {
1081
1083
  if (val === '(' && !prev.spaced && CALLABLE.has(prev[0])) {
@@ -1190,15 +1192,11 @@ export class Lexer {
1190
1192
  let starter = null;
1191
1193
  let indent = null;
1192
1194
  let outdent = null;
1193
- let bodyStart = null;
1194
-
1195
1195
  let condition = (token, i) => {
1196
1196
  return token[1] !== ';' && SINGLE_CLOSERS.has(token[0]) &&
1197
1197
  !(token[0] === 'TERMINATOR' && EXPRESSION_CLOSE.has(this.tokens[i + 1]?.[0])) &&
1198
1198
  !(token[0] === 'ELSE' && starter !== 'THEN') ||
1199
- token[0] === ',' && (starter === '->' || starter === '=>') &&
1200
- !(bodyStart != null && IMPLICIT_FUNC.has(this.tokens[bodyStart]?.[0]) && this.tokens[bodyStart]?.spaced &&
1201
- (IMPLICIT_CALL.has(this.tokens[bodyStart + 1]?.[0]) || (this.tokens[bodyStart + 1]?.[0] === '...' && IMPLICIT_CALL.has(this.tokens[bodyStart + 2]?.[0])))) ||
1199
+ token[0] === ',' && (starter === '->' || starter === '=>') && !this.commaInImplicitCall(i) ||
1202
1200
  CALL_CLOSERS.has(token[0]) && (this.tokens[i - 1]?.newLine || this.tokens[i - 1]?.[0] === 'OUTDENT');
1203
1201
  };
1204
1202
 
@@ -1240,7 +1238,6 @@ export class Lexer {
1240
1238
  if (SINGLE_LINERS.has(tag) && this.tokens[i + 1]?.[0] !== 'INDENT' &&
1241
1239
  !(tag === 'ELSE' && this.tokens[i + 1]?.[0] === 'IF')) {
1242
1240
  starter = tag;
1243
- bodyStart = i + 2;
1244
1241
  [indent, outdent] = this.makeIndentation();
1245
1242
  if (tag === 'THEN') indent.fromThen = true;
1246
1243
  tokens.splice(i + 1, 0, indent);
@@ -1501,6 +1498,27 @@ export class Lexer {
1501
1498
  }
1502
1499
  }
1503
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
+
1504
1522
  looksObjectish(j) {
1505
1523
  if (!this.tokens[j]) return false;
1506
1524
  if (this.tokens[j]?.[0] === '@' && this.tokens[j + 2]?.[0] === ':') return true;