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.
- package/docs/dist/rip.browser.js +66 -31
- package/docs/dist/rip.browser.min.js +180 -180
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/package.json +1 -1
- package/src/grammar/grammar.rip +5 -4
- package/src/grammar/solar.rip +1 -1
- package/src/lexer.js +30 -2
- package/src/parser.js +27 -26
|
Binary file
|
package/package.json
CHANGED
package/src/grammar/grammar.rip
CHANGED
|
@@ -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
|
|
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 **
|
package/src/grammar/solar.rip
CHANGED
|
@@ -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;
|