rip-lang 2.8.3 → 2.8.5
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/README.md +1 -1
- package/bin/rip +8 -4
- package/docs/dist/rip.browser.js +46 -21
- package/docs/dist/rip.browser.min.js +170 -167
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/package.json +1 -1
- package/src/compiler.js +40 -18
- package/src/grammar/grammar.rip +6 -6
- package/src/lexer.js +2 -2
- package/src/parser.js +5 -6
|
Binary file
|
package/package.json
CHANGED
package/src/compiler.js
CHANGED
|
@@ -2582,8 +2582,20 @@ export class CodeGenerator {
|
|
|
2582
2582
|
const stmts = this.withIndent(() => this.formatStatements(statements));
|
|
2583
2583
|
return `{\n${stmts.join('\n')}\n${this.indent()}}`;
|
|
2584
2584
|
}
|
|
2585
|
-
// In value context,
|
|
2586
|
-
|
|
2585
|
+
// In value context, handle sequences like (a; b; c)
|
|
2586
|
+
if (statements.length === 0) return 'undefined';
|
|
2587
|
+
if (statements.length === 1) return this.generate(statements[0], context);
|
|
2588
|
+
// Check if last statement is control flow (break/continue/return/throw)
|
|
2589
|
+
const last = statements[statements.length - 1];
|
|
2590
|
+
const lastIsControl = Array.isArray(last) && ['break', 'continue', 'return', 'throw'].includes(last[0]);
|
|
2591
|
+
if (lastIsControl) {
|
|
2592
|
+
// Control flow needs block form: { stmt1; stmt2; break; }
|
|
2593
|
+
const bodyParts = statements.map(s => this.addSemicolon(s, this.generate(s, 'statement')));
|
|
2594
|
+
return `{\n${this.withIndent(() => bodyParts.map(p => this.indent() + p).join('\n'))}\n${this.indent()}}`;
|
|
2595
|
+
}
|
|
2596
|
+
// Use comma operator for value sequences: (a, b, c) returns c
|
|
2597
|
+
const parts = statements.map(s => this.generate(s, 'value'));
|
|
2598
|
+
return `(${parts.join(', ')})`;
|
|
2587
2599
|
}
|
|
2588
2600
|
|
|
2589
2601
|
/**
|
|
@@ -2750,16 +2762,8 @@ export class CodeGenerator {
|
|
|
2750
2762
|
for (const whenClause of whens) {
|
|
2751
2763
|
const [, test, body] = whenClause;
|
|
2752
2764
|
|
|
2753
|
-
//
|
|
2754
|
-
|
|
2755
|
-
// ["-", "1"] → single s-expression (unary minus)
|
|
2756
|
-
// ["a", "b"] → multiple string/number values
|
|
2757
|
-
// Check if first element is an operator (s-expression) or a value (list)
|
|
2758
|
-
const firstTest = normalize(Array.isArray(test) && test.length > 0 ? test[0] : null);
|
|
2759
|
-
const isTestList = Array.isArray(test) && test.length > 0 &&
|
|
2760
|
-
typeof firstTest === 'string' &&
|
|
2761
|
-
!firstTest.match(/^[-+*\/%<>=!&|^~]$|^(typeof|delete|new|not|await|yield)$/);
|
|
2762
|
-
const tests = isTestList ? test : [test];
|
|
2765
|
+
// SimpleArgs always produces an array of test values
|
|
2766
|
+
const tests = test;
|
|
2763
2767
|
|
|
2764
2768
|
for (const t of tests) {
|
|
2765
2769
|
const tValue = normalize(t);
|
|
@@ -5099,20 +5103,35 @@ export class CodeGenerator {
|
|
|
5099
5103
|
// Don't unwrap if there are operators or multiple parts outside the parens
|
|
5100
5104
|
while (code.startsWith('(') && code.endsWith(')')) {
|
|
5101
5105
|
// Check if these parens wrap the entire expression
|
|
5102
|
-
let
|
|
5106
|
+
let parenDepth = 0;
|
|
5107
|
+
let bracketDepth = 0; // Track [] and {}
|
|
5103
5108
|
let canUnwrap = true;
|
|
5109
|
+
let hasCommaAtDepth1 = false;
|
|
5104
5110
|
|
|
5105
5111
|
for (let i = 0; i < code.length; i++) {
|
|
5106
|
-
|
|
5107
|
-
if (
|
|
5112
|
+
const ch = code[i];
|
|
5113
|
+
if (ch === '(') parenDepth++;
|
|
5114
|
+
if (ch === ')') parenDepth--;
|
|
5115
|
+
if (ch === '[' || ch === '{') bracketDepth++;
|
|
5116
|
+
if (ch === ']' || ch === '}') bracketDepth--;
|
|
5117
|
+
|
|
5118
|
+
// Check for comma operator at depth 1 (inside outer parens, not inside [] or {})
|
|
5119
|
+
if (ch === ',' && parenDepth === 1 && bracketDepth === 0) {
|
|
5120
|
+
hasCommaAtDepth1 = true;
|
|
5121
|
+
}
|
|
5108
5122
|
|
|
5109
5123
|
// If we hit depth 0 before the end, parens don't wrap everything
|
|
5110
|
-
if (
|
|
5124
|
+
if (parenDepth === 0 && i < code.length - 1) {
|
|
5111
5125
|
canUnwrap = false;
|
|
5112
5126
|
break;
|
|
5113
5127
|
}
|
|
5114
5128
|
}
|
|
5115
5129
|
|
|
5130
|
+
// Don't unwrap comma operator expressions - the parens are required
|
|
5131
|
+
if (hasCommaAtDepth1) {
|
|
5132
|
+
canUnwrap = false;
|
|
5133
|
+
}
|
|
5134
|
+
|
|
5116
5135
|
if (canUnwrap) {
|
|
5117
5136
|
code = code.slice(1, -1);
|
|
5118
5137
|
} else {
|
|
@@ -5459,10 +5478,13 @@ export class CodeGenerator {
|
|
|
5459
5478
|
const whenClause = whens[i];
|
|
5460
5479
|
const [, test, body] = whenClause;
|
|
5461
5480
|
|
|
5481
|
+
// SimpleArgs now always produces array; for if-chain we use first (typically only) condition
|
|
5482
|
+
const condition = Array.isArray(test) ? test[0] : test;
|
|
5483
|
+
|
|
5462
5484
|
if (i === 0) {
|
|
5463
|
-
code += `if (${this.generate(
|
|
5485
|
+
code += `if (${this.generate(condition, 'value')}) {\n`;
|
|
5464
5486
|
} else {
|
|
5465
|
-
code += ` else if (${this.generate(
|
|
5487
|
+
code += ` else if (${this.generate(condition, 'value')}) {\n`;
|
|
5466
5488
|
}
|
|
5467
5489
|
this.indentLevel++;
|
|
5468
5490
|
|
package/src/grammar/grammar.rip
CHANGED
|
@@ -585,10 +585,10 @@ grammar =
|
|
|
585
585
|
o 'Elision TERMINATOR'
|
|
586
586
|
]
|
|
587
587
|
|
|
588
|
-
# Simple argument lists for switch statements
|
|
588
|
+
# Simple argument lists for switch statements (always produces array)
|
|
589
589
|
SimpleArgs: [
|
|
590
|
-
o 'Expression'
|
|
591
|
-
o 'SimpleArgs , Expression', '
|
|
590
|
+
o 'Expression' , '[$1]' # Single: wrap in array
|
|
591
|
+
o 'SimpleArgs , Expression', '[...$1, $3]' # Multiple: spread + add
|
|
592
592
|
]
|
|
593
593
|
|
|
594
594
|
# Try/catch/finally exception handling
|
|
@@ -612,10 +612,10 @@ grammar =
|
|
|
612
612
|
o 'THROW INDENT Object OUTDENT', '["throw", 3]'
|
|
613
613
|
]
|
|
614
614
|
|
|
615
|
-
# Parenthesized expressions - unwrap single-element Bodies
|
|
615
|
+
# Parenthesized expressions - unwrap single-element Bodies, wrap multiple in block
|
|
616
616
|
Parenthetical: [
|
|
617
|
-
o '( Body )' , '$2.length === 1 ? $2[0] :
|
|
618
|
-
o '( INDENT Body OUTDENT )', '$3.length === 1 ? $3[0] :
|
|
617
|
+
o '( Body )' , '$2.length === 1 ? $2[0] : ["block", ...$2]'
|
|
618
|
+
o '( INDENT Body OUTDENT )', '$3.length === 1 ? $3[0] : ["block", ...$3]'
|
|
619
619
|
]
|
|
620
620
|
|
|
621
621
|
# While loop conditions (consolidated)
|
package/src/lexer.js
CHANGED
|
@@ -2579,7 +2579,7 @@ Rewriter = (function() {
|
|
|
2579
2579
|
// Close implicit objects such as:
|
|
2580
2580
|
// return a: 1, b: 2 unless true
|
|
2581
2581
|
// But NOT for || or && which should stay inside objects
|
|
2582
|
-
} else if (inImplicitObject() && !isLogicalOp && sameLine && tag !== 'TERMINATOR' && prevTag !== ':' && !((tag === 'POST_IF' || tag === 'FOR' || tag === 'WHILE' || tag === 'UNTIL') && startsLine && implicitObjectContinues(i + 1))) {
|
|
2582
|
+
} else if (inImplicitObject() && !isLogicalOp && sameLine && tag !== 'TERMINATOR' && prevTag !== ':' && !((tag === 'POST_IF' || tag === 'POST_UNLESS' || tag === 'FOR' || tag === 'WHILE' || tag === 'UNTIL') && startsLine && implicitObjectContinues(i + 1))) {
|
|
2583
2583
|
endImplicitObject();
|
|
2584
2584
|
// Close implicit objects when at end of line, line didn't end with a comma
|
|
2585
2585
|
// and the implicit object didn't start the line or the next line doesn't look like
|
|
@@ -3281,7 +3281,7 @@ IMPLICIT_UNSPACED_CALL = ['+', '-'];
|
|
|
3281
3281
|
|
|
3282
3282
|
// Tokens that always mark the end of an implicit call for single-liners.
|
|
3283
3283
|
// Includes || and && so that `read 'body' or ''` parses as `read('body') || ''`
|
|
3284
|
-
IMPLICIT_END = ['POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR', '||', '&&'];
|
|
3284
|
+
IMPLICIT_END = ['POST_IF', 'POST_UNLESS', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR', '||', '&&'];
|
|
3285
3285
|
|
|
3286
3286
|
// Literals that trigger comma insertion before arrows: get '/path' -> ... becomes get('/path', -> ...)
|
|
3287
3287
|
IMPLICIT_COMMA_BEFORE_ARROW = ['STRING', 'STRING_END', 'REGEX', 'REGEX_END', 'NUMBER', 'BOOL', 'NULL', 'UNDEFINED', 'INFINITY', 'NAN', ']', '}'];
|
package/src/parser.js
CHANGED
|
@@ -11,10 +11,10 @@ const parserInstance = {
|
|
|
11
11
|
switch (rule) {
|
|
12
12
|
case 1: return ["program"];
|
|
13
13
|
case 2: return ["program", ...$[$0]];
|
|
14
|
-
case 3: case 61: case 128: case 193: case 212: case 235: case 267: case 281: case 285: case 344: case 350: return [$[$0]];
|
|
15
|
-
case 4: case 129: case 194: case 213: case 236: case 268: return [...$[$0-2], $[$0]];
|
|
14
|
+
case 3: case 61: case 128: case 193: case 212: case 235: case 267: case 281: case 285: case 289: case 344: case 350: return [$[$0]];
|
|
15
|
+
case 4: case 129: case 194: case 213: case 236: case 268: case 290: return [...$[$0-2], $[$0]];
|
|
16
16
|
case 5: case 63: case 288: return $[$0-1];
|
|
17
|
-
case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 47: case 48: case 55: case 56: case 57: case 58: case 59: case 66: case 67: case 71: case 72: case 73: case 76: case 77: case 78: case 83: case 88: case 89: case 90: case 91: case 94: case 97: case 98: case 99: case 100: case 101: case 123: case 124: case 125: case 126: case 132: case 136: case 137: case 138: case 139: case 141: case 142: case 170: case 171: case 172: case 173: case 174: case 175: case 176: case 177: case 178: case 179: case 180: case 181: case 217: case 219: case 221: case 240: case 243: case 272: case 273: case 274: case 276: case
|
|
17
|
+
case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 47: case 48: case 55: case 56: case 57: case 58: case 59: case 66: case 67: case 71: case 72: case 73: case 76: case 77: case 78: case 83: case 88: case 89: case 90: case 91: case 94: case 97: case 98: case 99: case 100: case 101: case 123: case 124: case 125: case 126: case 132: case 136: case 137: case 138: case 139: case 141: case 142: case 170: case 171: case 172: case 173: case 174: case 175: case 176: case 177: case 178: case 179: case 180: case 181: case 217: case 219: case 221: case 240: case 243: case 272: case 273: case 274: case 276: case 309: case 342: case 358: case 360: return $[$0];
|
|
18
18
|
case 30: return ["def", $[$0-4], $[$0-2], $[$0]];
|
|
19
19
|
case 31: return ["def", $[$0-1], [], $[$0]];
|
|
20
20
|
case 32: return ["state", $[$0-2], $[$0]];
|
|
@@ -142,7 +142,6 @@ const parserInstance = {
|
|
|
142
142
|
case 279: return [...$[$0-2], ...$[$0-1]];
|
|
143
143
|
case 280: return [...$[$0-5], ...$[$0-4], ...$[$0-2], ...$[$0-1]];
|
|
144
144
|
case 284: return [...$[$0]];
|
|
145
|
-
case 290: return Array.isArray($[$0-2]) ? [...$[$0-2], $[$0]] : [$[$0-2], $[$0]];
|
|
146
145
|
case 291: return ["try", $[$0]];
|
|
147
146
|
case 292: return ["try", $[$0-1], $[$0]];
|
|
148
147
|
case 293: return ["try", $[$0-2], $[$0]];
|
|
@@ -151,8 +150,8 @@ const parserInstance = {
|
|
|
151
150
|
case 297: return [null, $[$0]];
|
|
152
151
|
case 298: return ["throw", $[$0]];
|
|
153
152
|
case 299: return ["throw", $[$0-1]];
|
|
154
|
-
case 300: return $[$0-1].length === 1 ? $[$0-1][0] :
|
|
155
|
-
case 301: return $[$0-2].length === 1 ? $[$0-2][0] :
|
|
153
|
+
case 300: return $[$0-1].length === 1 ? $[$0-1][0] : ["block", ...$[$0-1]];
|
|
154
|
+
case 301: return $[$0-2].length === 1 ? $[$0-2][0] : ["block", ...$[$0-2]];
|
|
156
155
|
case 302: return ["while", $[$0]];
|
|
157
156
|
case 303: return ["while", $[$0-2], $[$0]];
|
|
158
157
|
case 304: return ["until", $[$0]];
|