rip-lang 3.7.4 → 3.8.9
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 +106 -1
- package/README.md +42 -34
- package/docs/RIP-INTERNALS.md +2 -4
- package/docs/RIP-LANG.md +150 -3
- package/docs/RIP-TYPES.md +1 -2
- package/docs/demo.html +342 -0
- package/docs/dist/rip-ui.min.js +514 -0
- package/docs/dist/rip-ui.min.js.br +0 -0
- package/docs/dist/rip.browser.js +327 -478
- package/docs/dist/rip.browser.min.js +168 -208
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/dist/ui.js +962 -0
- package/docs/dist/ui.min.js +2 -0
- package/docs/dist/ui.min.js.br +0 -0
- package/docs/dist/ui.rip +957 -0
- package/docs/dist/ui.rip.br +0 -0
- package/docs/examples.rip +180 -0
- package/docs/index.html +3 -1599
- package/docs/playground-app.html +1022 -0
- package/docs/playground-js.html +1645 -0
- package/docs/playground-rip-ui.html +1419 -0
- package/docs/playground-rip.html +1450 -0
- package/docs/rip-fav.svg +5 -0
- package/package.json +3 -3
- package/scripts/serve.js +3 -2
- package/src/browser.js +21 -5
- package/src/compiler.js +191 -292
- package/src/components.js +100 -95
- package/src/grammar/README.md +234 -0
- package/src/grammar/grammar.rip +5 -5
- package/src/grammar/lunar.rip +2412 -0
- package/src/grammar/solar.rip +18 -4
- package/src/lexer.js +53 -24
- package/src/parser-rd.js +3242 -0
- package/src/parser.js +10 -9
- package/src/repl.js +24 -5
- package/docs/NOTES.md +0 -93
- package/docs/RIP-GUIDE.md +0 -698
- package/docs/RIP-REACTIVITY.md +0 -311
package/src/compiler.js
CHANGED
|
@@ -148,6 +148,7 @@ export class CodeGenerator {
|
|
|
148
148
|
|
|
149
149
|
// Special operators
|
|
150
150
|
'%%': 'generateModulo',
|
|
151
|
+
'%%=': 'generateModuloAssign',
|
|
151
152
|
'//': 'generateFloorDiv',
|
|
152
153
|
'//=': 'generateFloorDivAssign',
|
|
153
154
|
'..': 'generateRange',
|
|
@@ -201,9 +202,7 @@ export class CodeGenerator {
|
|
|
201
202
|
|
|
202
203
|
// Control flow — simple
|
|
203
204
|
'break': 'generateBreak',
|
|
204
|
-
'break-if': 'generateBreakIf',
|
|
205
205
|
'continue': 'generateContinue',
|
|
206
|
-
'continue-if': 'generateContinueIf',
|
|
207
206
|
'?': 'generateExistential',
|
|
208
207
|
'?:': 'generateTernary',
|
|
209
208
|
'|>': 'generatePipe',
|
|
@@ -215,12 +214,10 @@ export class CodeGenerator {
|
|
|
215
214
|
|
|
216
215
|
// Control flow — complex
|
|
217
216
|
'if': 'generateIf',
|
|
218
|
-
'unless': 'generateIf',
|
|
219
217
|
'for-in': 'generateForIn',
|
|
220
218
|
'for-of': 'generateForOf',
|
|
221
219
|
'for-as': 'generateForAs',
|
|
222
220
|
'while': 'generateWhile',
|
|
223
|
-
'until': 'generateUntil',
|
|
224
221
|
'try': 'generateTry',
|
|
225
222
|
'throw': 'generateThrow',
|
|
226
223
|
'control': 'generateControl',
|
|
@@ -311,17 +308,16 @@ export class CodeGenerator {
|
|
|
311
308
|
collect(sexpr);
|
|
312
309
|
|
|
313
310
|
// Match output lines to collected statement locations in parallel.
|
|
314
|
-
// Skip
|
|
311
|
+
// Skip lines with no source correspondence (preamble, braces, etc.).
|
|
315
312
|
let lines = code.split('\n');
|
|
316
313
|
let locIdx = 0;
|
|
317
314
|
for (let outLine = 0; outLine < lines.length; outLine++) {
|
|
318
315
|
let line = lines[outLine];
|
|
319
316
|
let trimmed = line.trim();
|
|
320
317
|
|
|
321
|
-
// Skip lines with no source correspondence
|
|
322
318
|
if (!trimmed || trimmed === '}' || trimmed === '});') continue;
|
|
323
319
|
if (trimmed.startsWith('let ') || trimmed.startsWith('var ')) continue;
|
|
324
|
-
if (trimmed.startsWith('const slice') || trimmed.startsWith('const modulo') || trimmed.startsWith('const
|
|
320
|
+
if (trimmed.startsWith('const slice') || trimmed.startsWith('const modulo') || trimmed.startsWith('const toMatchable')) continue;
|
|
325
321
|
if (trimmed.startsWith('const {') && trimmed.includes('__')) continue;
|
|
326
322
|
if (trimmed.startsWith('} else')) continue;
|
|
327
323
|
if (trimmed.startsWith('//# source')) continue;
|
|
@@ -359,6 +355,7 @@ export class CodeGenerator {
|
|
|
359
355
|
}
|
|
360
356
|
|
|
361
357
|
if (head === 'readonly') return;
|
|
358
|
+
if (head === 'component') return; // Component body has its own scope
|
|
362
359
|
|
|
363
360
|
if (CodeGenerator.ASSIGNMENT_OPS.has(head)) {
|
|
364
361
|
let [target, value] = rest;
|
|
@@ -384,13 +381,6 @@ export class CodeGenerator {
|
|
|
384
381
|
return;
|
|
385
382
|
}
|
|
386
383
|
|
|
387
|
-
if (head === 'unless') {
|
|
388
|
-
let [condition, body] = rest;
|
|
389
|
-
this.collectProgramVariables(condition);
|
|
390
|
-
this.collectProgramVariables(body);
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
384
|
if (head === 'try') {
|
|
395
385
|
this.collectProgramVariables(rest[0]);
|
|
396
386
|
if (rest.length >= 2 && Array.isArray(rest[1]) && rest[1].length === 2 && rest[1][0] !== 'block') {
|
|
@@ -545,7 +535,7 @@ export class CodeGenerator {
|
|
|
545
535
|
return `super.${this.currentMethodName}(${args})`;
|
|
546
536
|
}
|
|
547
537
|
|
|
548
|
-
// Postfix if
|
|
538
|
+
// Postfix if on single-arg call
|
|
549
539
|
if (context === 'statement' && rest.length === 1) {
|
|
550
540
|
let cond = this.findPostfixConditional(rest[0]);
|
|
551
541
|
if (cond) {
|
|
@@ -554,7 +544,7 @@ export class CodeGenerator {
|
|
|
554
544
|
let condCode = this.generate(cond.condition, 'value');
|
|
555
545
|
let valCode = this.generate(argWithout, 'value');
|
|
556
546
|
let callStr = `${callee}(${valCode})`;
|
|
557
|
-
return
|
|
547
|
+
return `if (${condCode}) ${callStr}`;
|
|
558
548
|
}
|
|
559
549
|
}
|
|
560
550
|
|
|
@@ -567,7 +557,7 @@ export class CodeGenerator {
|
|
|
567
557
|
|
|
568
558
|
// Statement sequence (comma operator)
|
|
569
559
|
if (Array.isArray(head) && typeof head[0] === 'string') {
|
|
570
|
-
let stmtOps = ['=', '+=', '-=', '*=', '/=', '%=', '**=', '&&=', '||=', '??=', 'if', '
|
|
560
|
+
let stmtOps = ['=', '+=', '-=', '*=', '/=', '%=', '**=', '&&=', '||=', '??=', 'if', 'return', 'throw'];
|
|
571
561
|
if (stmtOps.includes(head[0])) {
|
|
572
562
|
let exprs = sexpr.map(stmt => this.generate(stmt, 'value'));
|
|
573
563
|
return `(${exprs.join(', ')})`;
|
|
@@ -585,7 +575,7 @@ export class CodeGenerator {
|
|
|
585
575
|
return `new ${needsParens ? `(${ctorCode})` : ctorCode}(${args})`;
|
|
586
576
|
}
|
|
587
577
|
|
|
588
|
-
// Postfix if
|
|
578
|
+
// Postfix if on single-arg method call
|
|
589
579
|
if (context === 'statement' && rest.length === 1) {
|
|
590
580
|
let cond = this.findPostfixConditional(rest[0]);
|
|
591
581
|
if (cond) {
|
|
@@ -594,7 +584,7 @@ export class CodeGenerator {
|
|
|
594
584
|
let condCode = this.generate(cond.condition, 'value');
|
|
595
585
|
let valCode = this.generate(argWithout, 'value');
|
|
596
586
|
let callStr = `${calleeCode}(${valCode})`;
|
|
597
|
-
return
|
|
587
|
+
return `if (${condCode}) ${callStr}`;
|
|
598
588
|
}
|
|
599
589
|
}
|
|
600
590
|
|
|
@@ -638,7 +628,7 @@ export class CodeGenerator {
|
|
|
638
628
|
}
|
|
639
629
|
|
|
640
630
|
// Generate body first to detect needed helpers
|
|
641
|
-
let blockStmts = ['def', 'class', 'if', '
|
|
631
|
+
let blockStmts = ['def', 'class', 'if', 'for-in', 'for-of', 'for-as', 'while', 'loop', 'switch', 'try'];
|
|
642
632
|
let statementsCode = other.map((stmt, index) => {
|
|
643
633
|
let isSingle = other.length === 1 && imports.length === 0 && exports.length === 0;
|
|
644
634
|
let isObj = this.is(stmt, 'object');
|
|
@@ -680,8 +670,8 @@ export class CodeGenerator {
|
|
|
680
670
|
if (!skip) {
|
|
681
671
|
if (this.helpers.has('slice')) { code += 'const slice = [].slice;\n'; needsBlank = true; }
|
|
682
672
|
if (this.helpers.has('modulo')) { code += 'const modulo = (n, d) => { n = +n; d = +d; return (n % d + d) % d; };\n'; needsBlank = true; }
|
|
683
|
-
if (this.helpers.has('
|
|
684
|
-
code += 'const
|
|
673
|
+
if (this.helpers.has('toMatchable')) {
|
|
674
|
+
code += 'const toMatchable = (v, allowNewlines) => {\n';
|
|
685
675
|
code += ' if (typeof v === "string") return !allowNewlines && /[\\n\\r]/.test(v) ? null : v;\n';
|
|
686
676
|
code += ' if (v == null) return "";\n';
|
|
687
677
|
code += ' if (typeof v === "number" || typeof v === "bigint" || typeof v === "boolean") return String(v);\n';
|
|
@@ -781,6 +771,13 @@ export class CodeGenerator {
|
|
|
781
771
|
return `modulo(${this.generate(left, 'value')}, ${this.generate(right, 'value')})`;
|
|
782
772
|
}
|
|
783
773
|
|
|
774
|
+
generateModuloAssign(head, rest) {
|
|
775
|
+
let [target, value] = rest;
|
|
776
|
+
this.helpers.add('modulo');
|
|
777
|
+
let t = this.generate(target, 'value'), v = this.generate(value, 'value');
|
|
778
|
+
return `${t} = modulo(${t}, ${v})`;
|
|
779
|
+
}
|
|
780
|
+
|
|
784
781
|
generateFloorDiv(head, rest) {
|
|
785
782
|
let [left, right] = rest;
|
|
786
783
|
return `Math.floor(${this.generate(left, 'value')} / ${this.generate(right, 'value')})`;
|
|
@@ -870,33 +867,29 @@ export class CodeGenerator {
|
|
|
870
867
|
}
|
|
871
868
|
}
|
|
872
869
|
|
|
873
|
-
// Postfix if
|
|
870
|
+
// Postfix if on assignment with || operator
|
|
874
871
|
if (context === 'statement' && head === '=' && Array.isArray(value) &&
|
|
875
872
|
(value[0] === '||' || value[0] === '&&') && value.length === 3) {
|
|
876
873
|
let [binOp, left, right] = value;
|
|
877
|
-
if (
|
|
878
|
-
let [
|
|
874
|
+
if (this.is(right, 'if') && right.length === 3) {
|
|
875
|
+
let [, condition, wrappedValue] = right;
|
|
879
876
|
let unwrapped = Array.isArray(wrappedValue) && wrappedValue.length === 1 ? wrappedValue[0] : wrappedValue;
|
|
880
877
|
let fullValue = [binOp, left, unwrapped];
|
|
881
878
|
let t = this.generate(target, 'value'), c = this.generate(condition, 'value'), v = this.generate(fullValue, 'value');
|
|
882
|
-
return
|
|
879
|
+
return `if (${c}) ${t} = ${v}`;
|
|
883
880
|
}
|
|
884
881
|
}
|
|
885
882
|
|
|
886
|
-
// Postfix if
|
|
883
|
+
// Postfix if on simple assignment
|
|
887
884
|
if (context === 'statement' && head === '=' && Array.isArray(value) && value.length === 3) {
|
|
888
885
|
let [valHead, condition, actualValue] = value;
|
|
889
886
|
let isPostfix = Array.isArray(actualValue) && actualValue.length === 1 &&
|
|
890
887
|
(!Array.isArray(actualValue[0]) || actualValue[0][0] !== 'block');
|
|
891
|
-
if (
|
|
888
|
+
if (valHead === 'if' && isPostfix) {
|
|
892
889
|
let unwrapped = Array.isArray(actualValue) && actualValue.length === 1 ? actualValue[0] : actualValue;
|
|
893
890
|
let t = this.generate(target, 'value');
|
|
894
891
|
let condCode = this.unwrapLogical(this.generate(condition, 'value'));
|
|
895
892
|
let v = this.generate(unwrapped, 'value');
|
|
896
|
-
if (valHead === 'unless') {
|
|
897
|
-
if (condCode.includes(' ') || /[<>=&|]/.test(condCode)) condCode = `(${condCode})`;
|
|
898
|
-
return `if (!${condCode}) ${t} = ${v}`;
|
|
899
|
-
}
|
|
900
893
|
return `if (${condCode}) ${t} = ${v}`;
|
|
901
894
|
}
|
|
902
895
|
}
|
|
@@ -929,11 +922,17 @@ export class CodeGenerator {
|
|
|
929
922
|
|
|
930
923
|
generatePropertyAccess(head, rest, context, sexpr) {
|
|
931
924
|
let [obj, prop] = rest;
|
|
925
|
+
// In subclass constructors, rewrite @param refs (this.x) to _x for super() safety
|
|
926
|
+
if (this._atParamMap && obj === 'this') {
|
|
927
|
+
let mapped = this._atParamMap.get(str(prop));
|
|
928
|
+
if (mapped) return mapped;
|
|
929
|
+
}
|
|
932
930
|
this.suppressReactiveUnwrap = true;
|
|
933
931
|
let objCode = this.generate(obj, 'value');
|
|
934
932
|
this.suppressReactiveUnwrap = false;
|
|
935
933
|
let needsParens = CodeGenerator.NUMBER_LITERAL_RE.test(objCode) ||
|
|
936
|
-
|
|
934
|
+
objCode.startsWith('await ') ||
|
|
935
|
+
((this.is(obj, 'object') || this.is(obj, 'yield')));
|
|
937
936
|
let base = needsParens ? `(${objCode})` : objCode;
|
|
938
937
|
if (meta(prop, 'await') === true) return `await ${base}.${str(prop)}()`;
|
|
939
938
|
if (meta(prop, 'predicate')) return `(${base}.${str(prop)} != null)`;
|
|
@@ -947,12 +946,12 @@ export class CodeGenerator {
|
|
|
947
946
|
|
|
948
947
|
generateRegexIndex(head, rest) {
|
|
949
948
|
let [value, regex, captureIndex] = rest;
|
|
950
|
-
this.helpers.add('
|
|
949
|
+
this.helpers.add('toMatchable');
|
|
951
950
|
this.programVars.add('_');
|
|
952
951
|
let v = this.generate(value, 'value'), r = this.generate(regex, 'value');
|
|
953
952
|
let idx = captureIndex !== null ? this.generate(captureIndex, 'value') : '0';
|
|
954
953
|
let allowNL = r.includes('/m') ? ', true' : '';
|
|
955
|
-
return `(_ =
|
|
954
|
+
return `(_ = toMatchable(${v}${allowNL}).match(${r})) && _[${idx}]`;
|
|
956
955
|
}
|
|
957
956
|
|
|
958
957
|
generateIndexAccess(head, rest) {
|
|
@@ -1062,11 +1061,6 @@ export class CodeGenerator {
|
|
|
1062
1061
|
let [expr] = rest;
|
|
1063
1062
|
if (this.sideEffectOnly) return 'return';
|
|
1064
1063
|
|
|
1065
|
-
if (this.is(expr, 'unless')) {
|
|
1066
|
-
let [, condition, body] = expr;
|
|
1067
|
-
let val = Array.isArray(body) && body.length === 1 ? body[0] : body;
|
|
1068
|
-
return `if (!${this.generate(condition, 'value')}) return ${this.generate(val, 'value')}`;
|
|
1069
|
-
}
|
|
1070
1064
|
if (this.is(expr, 'if')) {
|
|
1071
1065
|
let [, condition, body, ...elseParts] = expr;
|
|
1072
1066
|
if (elseParts.length === 0) {
|
|
@@ -1074,11 +1068,10 @@ export class CodeGenerator {
|
|
|
1074
1068
|
return `if (${this.generate(condition, 'value')}) return ${this.generate(val, 'value')}`;
|
|
1075
1069
|
}
|
|
1076
1070
|
}
|
|
1077
|
-
if (this.is(expr, 'new') && Array.isArray(expr[1]) && expr[1][0] === '
|
|
1078
|
-
let [,
|
|
1079
|
-
let [, condition, body] = unlessNode;
|
|
1071
|
+
if (this.is(expr, 'new') && Array.isArray(expr[1]) && expr[1][0] === 'if') {
|
|
1072
|
+
let [, condition, body] = expr[1];
|
|
1080
1073
|
let val = Array.isArray(body) && body.length === 1 ? body[0] : body;
|
|
1081
|
-
return `if (
|
|
1074
|
+
return `if (${this.generate(condition, 'value')}) return ${this.generate(['new', val], 'value')}`;
|
|
1082
1075
|
}
|
|
1083
1076
|
return `return ${this.generate(expr, 'value')}`;
|
|
1084
1077
|
}
|
|
@@ -1134,9 +1127,7 @@ export class CodeGenerator {
|
|
|
1134
1127
|
// ---------------------------------------------------------------------------
|
|
1135
1128
|
|
|
1136
1129
|
generateBreak() { return 'break'; }
|
|
1137
|
-
generateBreakIf(head, rest) { return `if (${this.generate(rest[0], 'value')}) break`; }
|
|
1138
1130
|
generateContinue() { return 'continue'; }
|
|
1139
|
-
generateContinueIf(head, rest) { return `if (${this.generate(rest[0], 'value')}) continue`; }
|
|
1140
1131
|
|
|
1141
1132
|
generateExistential(head, rest) {
|
|
1142
1133
|
return `(${this.generate(rest[0], 'value')} != null)`;
|
|
@@ -1198,16 +1189,6 @@ export class CodeGenerator {
|
|
|
1198
1189
|
// ---------------------------------------------------------------------------
|
|
1199
1190
|
|
|
1200
1191
|
generateIf(head, rest, context, sexpr) {
|
|
1201
|
-
if (head === 'unless') {
|
|
1202
|
-
let [condition, body] = rest;
|
|
1203
|
-
if (Array.isArray(body) && body.length === 1 && (!Array.isArray(body[0]) || body[0][0] !== 'block')) body = body[0];
|
|
1204
|
-
if (context === 'value') {
|
|
1205
|
-
return `(!${this.generate(condition, 'value')} ? ${this.extractExpression(body)} : undefined)`;
|
|
1206
|
-
}
|
|
1207
|
-
let condCode = this.unwrap(this.generate(condition, 'value'));
|
|
1208
|
-
if (/[ <>=&|]/.test(condCode)) condCode = `(${condCode})`;
|
|
1209
|
-
return `if (!${condCode}) ` + this.generate(body, 'statement');
|
|
1210
|
-
}
|
|
1211
1192
|
let [condition, thenBranch, ...elseBranches] = rest;
|
|
1212
1193
|
return context === 'value'
|
|
1213
1194
|
? this.generateIfAsExpression(condition, thenBranch, elseBranches)
|
|
@@ -1431,11 +1412,6 @@ export class CodeGenerator {
|
|
|
1431
1412
|
return code + (guard ? this.generateLoopBodyWithGuard(body, guard) : this.generateLoopBody(body));
|
|
1432
1413
|
}
|
|
1433
1414
|
|
|
1434
|
-
generateUntil(head, rest) {
|
|
1435
|
-
let [cond, body] = rest;
|
|
1436
|
-
return `while (!(${this.unwrap(this.generate(cond, 'value'))})) ` + this.generateLoopBody(body);
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
1415
|
generateRange(head, rest) {
|
|
1440
1416
|
if (head === '...') {
|
|
1441
1417
|
if (rest.length === 1) return `...${this.generate(rest[0], 'value')}`;
|
|
@@ -1504,11 +1480,11 @@ export class CodeGenerator {
|
|
|
1504
1480
|
|
|
1505
1481
|
generateRegexMatch(head, rest) {
|
|
1506
1482
|
let [left, right] = rest;
|
|
1507
|
-
this.helpers.add('
|
|
1483
|
+
this.helpers.add('toMatchable');
|
|
1508
1484
|
this.programVars.add('_');
|
|
1509
1485
|
let r = this.generate(right, 'value');
|
|
1510
1486
|
let allowNL = r.includes('/m') ? ', true' : '';
|
|
1511
|
-
return `(_ =
|
|
1487
|
+
return `(_ = toMatchable(${this.generate(left, 'value')}${allowNL}).match(${r}))`;
|
|
1512
1488
|
}
|
|
1513
1489
|
|
|
1514
1490
|
generateNew(head, rest) {
|
|
@@ -1574,7 +1550,11 @@ export class CodeGenerator {
|
|
|
1574
1550
|
let keyCode;
|
|
1575
1551
|
if (this.is(key, 'dynamicKey')) keyCode = `[${this.generate(key[1], 'value')}]`;
|
|
1576
1552
|
else if (this.is(key, 'str')) keyCode = `[${this.generate(key, 'value')}]`;
|
|
1577
|
-
else
|
|
1553
|
+
else {
|
|
1554
|
+
this.suppressReactiveUnwrap = true;
|
|
1555
|
+
keyCode = this.generate(key, 'value');
|
|
1556
|
+
this.suppressReactiveUnwrap = false;
|
|
1557
|
+
}
|
|
1578
1558
|
let valCode = this.generate(value, 'value');
|
|
1579
1559
|
if (operator === '=') return `${keyCode} = ${valCode}`;
|
|
1580
1560
|
if (operator === ':') return `${keyCode}: ${valCode}`;
|
|
@@ -1642,20 +1622,18 @@ export class CodeGenerator {
|
|
|
1642
1622
|
let [expr] = rest;
|
|
1643
1623
|
if (Array.isArray(expr)) {
|
|
1644
1624
|
let checkExpr = expr, wrapperType = null;
|
|
1645
|
-
if (expr[0] === 'new' && Array.isArray(expr[1]) &&
|
|
1625
|
+
if (expr[0] === 'new' && Array.isArray(expr[1]) && expr[1][0] === 'if') {
|
|
1646
1626
|
wrapperType = 'new'; checkExpr = expr[1];
|
|
1647
|
-
} else if (expr[0] === 'if'
|
|
1627
|
+
} else if (expr[0] === 'if') {
|
|
1648
1628
|
checkExpr = expr;
|
|
1649
1629
|
}
|
|
1650
|
-
if (checkExpr[0] === 'if'
|
|
1651
|
-
let [
|
|
1630
|
+
if (checkExpr[0] === 'if') {
|
|
1631
|
+
let [, condition, body] = checkExpr;
|
|
1652
1632
|
let unwrapped = Array.isArray(body) && body.length === 1 ? body[0] : body;
|
|
1653
1633
|
expr = wrapperType === 'new' ? ['new', unwrapped] : unwrapped;
|
|
1654
1634
|
let condCode = this.generate(condition, 'value');
|
|
1655
1635
|
let throwCode = `throw ${this.generate(expr, 'value')}`;
|
|
1656
|
-
return
|
|
1657
|
-
? `if (!(${condCode})) {\n${this.indent()} ${throwCode};\n${this.indent()}}`
|
|
1658
|
-
: `if (${condCode}) {\n${this.indent()} ${throwCode};\n${this.indent()}}`;
|
|
1636
|
+
return `if (${condCode}) {\n${this.indent()} ${throwCode};\n${this.indent()}}`;
|
|
1659
1637
|
}
|
|
1660
1638
|
}
|
|
1661
1639
|
let throwStmt = `throw ${this.generate(expr, 'value')}`;
|
|
@@ -1729,6 +1707,65 @@ export class CodeGenerator {
|
|
|
1729
1707
|
// Comprehensions
|
|
1730
1708
|
// ---------------------------------------------------------------------------
|
|
1731
1709
|
|
|
1710
|
+
// Shared: parse a for-in iterator and return { header, setup }.
|
|
1711
|
+
// header: the for(...) clause (no trailing brace)
|
|
1712
|
+
// setup: any `const x = arr[i]` preamble line, or null
|
|
1713
|
+
_forInHeader(vars, iterable, step) {
|
|
1714
|
+
let va = Array.isArray(vars) ? vars : [vars];
|
|
1715
|
+
let noVar = va.length === 0;
|
|
1716
|
+
let [itemVar, indexVar] = noVar ? ['_i', null] : va;
|
|
1717
|
+
let ivp = (this.is(itemVar, 'array') || this.is(itemVar, 'object'))
|
|
1718
|
+
? this.generateDestructuringPattern(itemVar) : itemVar;
|
|
1719
|
+
|
|
1720
|
+
if (step && step !== null) {
|
|
1721
|
+
let ih = Array.isArray(iterable) && iterable[0];
|
|
1722
|
+
if (ih instanceof String) ih = str(ih);
|
|
1723
|
+
let isRange = ih === '..' || ih === '...';
|
|
1724
|
+
if (isRange) {
|
|
1725
|
+
let isExcl = ih === '...';
|
|
1726
|
+
let [s, e] = iterable.slice(1);
|
|
1727
|
+
let sc = this.generate(s, 'value'), ec = this.generate(e, 'value'), stc = this.generate(step, 'value');
|
|
1728
|
+
return { header: `for (let ${ivp} = ${sc}; ${ivp} ${isExcl ? '<' : '<='} ${ec}; ${ivp} += ${stc})`, setup: null };
|
|
1729
|
+
}
|
|
1730
|
+
let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
|
|
1731
|
+
let isNeg = this.is(step, '-', 1);
|
|
1732
|
+
let isMinus1 = isNeg && (step[1] === '1' || step[1] === 1 || str(step[1]) === '1');
|
|
1733
|
+
let isPlus1 = !isNeg && (step === '1' || step === 1 || str(step) === '1');
|
|
1734
|
+
let update = isMinus1 ? `${idxN}--` : isPlus1 ? `${idxN}++` : `${idxN} += ${stc}`;
|
|
1735
|
+
let header = isNeg
|
|
1736
|
+
? `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${update})`
|
|
1737
|
+
: `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${update})`;
|
|
1738
|
+
return { header, setup: noVar ? null : `const ${ivp} = ${ic}[${idxN}];` };
|
|
1739
|
+
}
|
|
1740
|
+
if (indexVar) {
|
|
1741
|
+
let ic = this.generate(iterable, 'value');
|
|
1742
|
+
return {
|
|
1743
|
+
header: `for (let ${indexVar} = 0; ${indexVar} < ${ic}.length; ${indexVar}++)`,
|
|
1744
|
+
setup: `const ${ivp} = ${ic}[${indexVar}];`,
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
return { header: `for (const ${ivp} of ${this.generate(iterable, 'value')})`, setup: null };
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
// Shared: parse a for-of (object) iterator and return { header, own, vv, oc, kvp }.
|
|
1751
|
+
_forOfHeader(vars, iterable, own) {
|
|
1752
|
+
let va = Array.isArray(vars) ? vars : [vars];
|
|
1753
|
+
let [kv, vv] = va;
|
|
1754
|
+
let kvp = (this.is(kv, 'array') || this.is(kv, 'object'))
|
|
1755
|
+
? this.generateDestructuringPattern(kv) : kv;
|
|
1756
|
+
let oc = this.generate(iterable, 'value');
|
|
1757
|
+
return { header: `for (const ${kvp} in ${oc})`, own, vv, oc, kvp };
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
// Shared: parse a for-as (iterator) spec and return { header }.
|
|
1761
|
+
_forAsHeader(vars, iterable, isAwait) {
|
|
1762
|
+
let va = Array.isArray(vars) ? vars : [vars];
|
|
1763
|
+
let [fv] = va;
|
|
1764
|
+
let ivp = (this.is(fv, 'array') || this.is(fv, 'object'))
|
|
1765
|
+
? this.generateDestructuringPattern(fv) : fv;
|
|
1766
|
+
return { header: `for ${isAwait ? 'await ' : ''}(const ${ivp} of ${this.generate(iterable, 'value')})` };
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1732
1769
|
generateComprehension(head, rest, context) {
|
|
1733
1770
|
let [expr, iterators, guards] = rest;
|
|
1734
1771
|
if (context === 'statement') return this.generateComprehensionAsLoop(expr, iterators, guards);
|
|
@@ -1743,59 +1780,19 @@ export class CodeGenerator {
|
|
|
1743
1780
|
for (let iter of iterators) {
|
|
1744
1781
|
let [iterType, vars, iterable, stepOrOwn] = iter;
|
|
1745
1782
|
if (iterType === 'for-in') {
|
|
1746
|
-
let
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
let ivp = ((this.is(itemVar, 'array') || this.is(itemVar, 'object')))
|
|
1751
|
-
? this.generateDestructuringPattern(itemVar) : itemVar;
|
|
1752
|
-
|
|
1753
|
-
if (step && step !== null) {
|
|
1754
|
-
let ih = Array.isArray(iterable) && iterable[0];
|
|
1755
|
-
if (ih instanceof String) ih = str(ih);
|
|
1756
|
-
let isRange = ih === '..' || ih === '...';
|
|
1757
|
-
if (isRange) {
|
|
1758
|
-
let isExcl = ih === '...';
|
|
1759
|
-
let [s, e] = iterable.slice(1);
|
|
1760
|
-
let sc = this.generate(s, 'value'), ec = this.generate(e, 'value'), stc = this.generate(step, 'value');
|
|
1761
|
-
code += this.indent() + `for (let ${ivp} = ${sc}; ${ivp} ${isExcl ? '<' : '<='} ${ec}; ${ivp} += ${stc}) {\n`;
|
|
1762
|
-
this.indentLevel++;
|
|
1763
|
-
} else {
|
|
1764
|
-
let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
|
|
1765
|
-
let isNeg = this.is(step, '-', 1);
|
|
1766
|
-
code += isNeg
|
|
1767
|
-
? this.indent() + `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN} += ${stc}) {\n`
|
|
1768
|
-
: this.indent() + `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN} += ${stc}) {\n`;
|
|
1769
|
-
this.indentLevel++;
|
|
1770
|
-
if (!noVar) code += this.indent() + `const ${ivp} = ${ic}[${idxN}];\n`;
|
|
1771
|
-
}
|
|
1772
|
-
} else if (indexVar) {
|
|
1773
|
-
let ic = this.generate(iterable, 'value');
|
|
1774
|
-
code += this.indent() + `for (let ${indexVar} = 0; ${indexVar} < ${ic}.length; ${indexVar}++) {\n`;
|
|
1775
|
-
this.indentLevel++;
|
|
1776
|
-
code += this.indent() + `const ${ivp} = ${ic}[${indexVar}];\n`;
|
|
1777
|
-
} else {
|
|
1778
|
-
code += this.indent() + `for (const ${ivp} of ${this.generate(iterable, 'value')}) {\n`;
|
|
1779
|
-
this.indentLevel++;
|
|
1780
|
-
}
|
|
1783
|
+
let { header, setup } = this._forInHeader(vars, iterable, stepOrOwn);
|
|
1784
|
+
code += this.indent() + header + ' {\n';
|
|
1785
|
+
this.indentLevel++;
|
|
1786
|
+
if (setup) code += this.indent() + setup + '\n';
|
|
1781
1787
|
} else if (iterType === 'for-of') {
|
|
1782
|
-
let own = stepOrOwn;
|
|
1783
|
-
|
|
1784
|
-
let [kv, vv] = va;
|
|
1785
|
-
let kvp = ((this.is(kv, 'array') || this.is(kv, 'object')))
|
|
1786
|
-
? this.generateDestructuringPattern(kv) : kv;
|
|
1787
|
-
let oc = this.generate(iterable, 'value');
|
|
1788
|
-
code += this.indent() + `for (const ${kvp} in ${oc}) {\n`;
|
|
1788
|
+
let { header, own, vv, oc, kvp } = this._forOfHeader(vars, iterable, stepOrOwn);
|
|
1789
|
+
code += this.indent() + header + ' {\n';
|
|
1789
1790
|
this.indentLevel++;
|
|
1790
1791
|
if (own) code += this.indent() + `if (!Object.hasOwn(${oc}, ${kvp})) continue;\n`;
|
|
1791
1792
|
if (vv) code += this.indent() + `const ${vv} = ${oc}[${kvp}];\n`;
|
|
1792
1793
|
} else if (iterType === 'for-as') {
|
|
1793
|
-
let
|
|
1794
|
-
|
|
1795
|
-
let [fv] = va;
|
|
1796
|
-
let ivp = ((this.is(fv, 'array') || this.is(fv, 'object')))
|
|
1797
|
-
? this.generateDestructuringPattern(fv) : fv;
|
|
1798
|
-
code += this.indent() + `for ${isAwait ? 'await ' : ''}(const ${ivp} of ${this.generate(iterable, 'value')}) {\n`;
|
|
1794
|
+
let { header } = this._forAsHeader(vars, iterable, iter[3]);
|
|
1795
|
+
code += this.indent() + header + ' {\n';
|
|
1799
1796
|
this.indentLevel++;
|
|
1800
1797
|
}
|
|
1801
1798
|
}
|
|
@@ -1808,12 +1805,12 @@ export class CodeGenerator {
|
|
|
1808
1805
|
let hasCtrl = (node) => {
|
|
1809
1806
|
if (typeof node === 'string' && (node === 'break' || node === 'continue')) return true;
|
|
1810
1807
|
if (!Array.isArray(node)) return false;
|
|
1811
|
-
if (['break', 'continue', '
|
|
1812
|
-
if (node[0] === 'if'
|
|
1808
|
+
if (['break', 'continue', 'return', 'throw'].includes(node[0])) return true;
|
|
1809
|
+
if (node[0] === 'if') return node.slice(1).some(hasCtrl);
|
|
1813
1810
|
return node.some(hasCtrl);
|
|
1814
1811
|
};
|
|
1815
1812
|
|
|
1816
|
-
let loopStmts = ['for-in', 'for-of', 'for-as', 'while', '
|
|
1813
|
+
let loopStmts = ['for-in', 'for-of', 'for-as', 'while', 'loop'];
|
|
1817
1814
|
if (this.is(expr, 'block')) {
|
|
1818
1815
|
for (let i = 0; i < expr.length - 1; i++) {
|
|
1819
1816
|
let s = expr[i + 1], isLast = i === expr.length - 2;
|
|
@@ -1909,17 +1906,36 @@ export class CodeGenerator {
|
|
|
1909
1906
|
let hasAwait = this.containsAwait(body), hasYield = this.containsYield(body);
|
|
1910
1907
|
let cleanParams = params, autoAssign = [];
|
|
1911
1908
|
if (mName === 'constructor') {
|
|
1909
|
+
let isSubclass = !!parentClass;
|
|
1910
|
+
let atParamMap = isSubclass ? new Map() : null;
|
|
1912
1911
|
cleanParams = params.map(p => {
|
|
1913
|
-
|
|
1912
|
+
// Handle @param: ['.', 'this', 'name']
|
|
1913
|
+
if (this.is(p, '.') && p[1] === 'this') {
|
|
1914
|
+
let name = p[2];
|
|
1915
|
+
let param = isSubclass ? `_${name}` : name;
|
|
1916
|
+
autoAssign.push(`this.${name} = ${param}`);
|
|
1917
|
+
if (isSubclass) atParamMap.set(name, param);
|
|
1918
|
+
return param;
|
|
1919
|
+
}
|
|
1920
|
+
// Handle @param with default: ['default', ['.', 'this', 'name'], value]
|
|
1921
|
+
if (this.is(p, 'default') && this.is(p[1], '.') && p[1][1] === 'this') {
|
|
1922
|
+
let name = p[1][2];
|
|
1923
|
+
let param = isSubclass ? `_${name}` : name;
|
|
1924
|
+
autoAssign.push(`this.${name} = ${param}`);
|
|
1925
|
+
if (isSubclass) atParamMap.set(name, param);
|
|
1926
|
+
return ['default', param, p[2]];
|
|
1927
|
+
}
|
|
1914
1928
|
return p;
|
|
1915
1929
|
});
|
|
1916
1930
|
for (let bm of boundMethods) autoAssign.unshift(`this.${bm} = this.${bm}.bind(this)`);
|
|
1931
|
+
if (atParamMap?.size > 0) this._atParamMap = atParamMap;
|
|
1917
1932
|
}
|
|
1918
1933
|
let pList = this.generateParamList(cleanParams);
|
|
1919
1934
|
let prefix = (isStatic ? 'static ' : '') + (hasAwait ? 'async ' : '') + (hasYield ? '*' : '');
|
|
1920
1935
|
code += this.indent() + `${prefix}${mName}(${pList}) `;
|
|
1921
1936
|
if (!isComputed) this.currentMethodName = mName;
|
|
1922
1937
|
code += this.generateMethodBody(body, autoAssign, mName === 'constructor', cleanParams);
|
|
1938
|
+
this._atParamMap = null;
|
|
1923
1939
|
this.currentMethodName = null;
|
|
1924
1940
|
code += '\n';
|
|
1925
1941
|
} else if (isStatic) {
|
|
@@ -2098,7 +2114,7 @@ export class CodeGenerator {
|
|
|
2098
2114
|
findPostfixConditional(expr) {
|
|
2099
2115
|
if (!Array.isArray(expr)) return null;
|
|
2100
2116
|
let h = expr[0];
|
|
2101
|
-
if (
|
|
2117
|
+
if (h === 'if' && expr.length === 3) return {type: h, condition: expr[1], value: expr[2]};
|
|
2102
2118
|
if (h === '+' || h === '-' || h === '*' || h === '/') {
|
|
2103
2119
|
for (let i = 1; i < expr.length; i++) {
|
|
2104
2120
|
let found = this.findPostfixConditional(expr[i]);
|
|
@@ -2192,7 +2208,7 @@ export class CodeGenerator {
|
|
|
2192
2208
|
!this.scopeStack.some(s => s.has(v)) // don't re-declare variables from enclosing scopes
|
|
2193
2209
|
));
|
|
2194
2210
|
let noRetStmts = ['return', 'throw', 'break', 'continue'];
|
|
2195
|
-
let loopStmts = ['for-in', 'for-of', 'for-as', 'while', '
|
|
2211
|
+
let loopStmts = ['for-in', 'for-of', 'for-as', 'while', 'loop'];
|
|
2196
2212
|
|
|
2197
2213
|
// Track this function's scope so nested functions don't re-declare its variables
|
|
2198
2214
|
this.scopeStack.push(new Set([...newVars, ...paramNames]));
|
|
@@ -2240,7 +2256,7 @@ export class CodeGenerator {
|
|
|
2240
2256
|
return;
|
|
2241
2257
|
}
|
|
2242
2258
|
|
|
2243
|
-
if (!isConstructor && !sideEffectOnly && isLast &&
|
|
2259
|
+
if (!isConstructor && !sideEffectOnly && isLast && h === 'if') {
|
|
2244
2260
|
let [cond, thenB, ...elseB] = stmt.slice(1);
|
|
2245
2261
|
let hasMulti = (b) => this.is(b, 'block') && b.length > 2;
|
|
2246
2262
|
if (hasMulti(thenB) || elseB.some(hasMulti)) {
|
|
@@ -2299,7 +2315,13 @@ export class CodeGenerator {
|
|
|
2299
2315
|
// Single expression
|
|
2300
2316
|
this.sideEffectOnly = prevSEO;
|
|
2301
2317
|
let result;
|
|
2302
|
-
if (isConstructor
|
|
2318
|
+
if (isConstructor && autoAssignments.length > 0) {
|
|
2319
|
+
// Constructor with @params as a single expression — need to emit autoAssignments
|
|
2320
|
+
let isSuper = Array.isArray(body) && body[0] === 'super';
|
|
2321
|
+
let bodyCode = this.generate(body, 'statement');
|
|
2322
|
+
let assigns = autoAssignments.map(a => `${a};`).join(' ');
|
|
2323
|
+
result = isSuper ? `{ ${bodyCode}; ${assigns} }` : `{ ${assigns} ${bodyCode}; }`;
|
|
2324
|
+
} else if (isConstructor || this.hasExplicitControlFlow(body)) result = `{ ${this.generate(body, 'statement')}; }`;
|
|
2303
2325
|
else if (Array.isArray(body) && (noRetStmts.includes(body[0]) || loopStmts.includes(body[0]))) result = `{ ${this.generate(body, 'statement')}; }`;
|
|
2304
2326
|
else if (sideEffectOnly) result = `{ ${this.generate(body, 'statement')}; return; }`;
|
|
2305
2327
|
else result = `{ return ${this.generate(body, 'value')}; }`;
|
|
@@ -2379,34 +2401,10 @@ export class CodeGenerator {
|
|
|
2379
2401
|
if (iterators.length === 1) {
|
|
2380
2402
|
let [iterType, vars, iterable, stepOrOwn] = iterators[0];
|
|
2381
2403
|
if (iterType === 'for-in') {
|
|
2382
|
-
let
|
|
2383
|
-
|
|
2384
|
-
let noVar = va.length === 0;
|
|
2385
|
-
let [itemVar, indexVar] = noVar ? ['_i', null] : va;
|
|
2386
|
-
let ivp = ((this.is(itemVar, 'array') || this.is(itemVar, 'object')))
|
|
2387
|
-
? this.generateDestructuringPattern(itemVar) : itemVar;
|
|
2388
|
-
|
|
2389
|
-
if (step && step !== null) {
|
|
2390
|
-
let ih = Array.isArray(iterable) && iterable[0];
|
|
2391
|
-
if (ih instanceof String) ih = str(ih);
|
|
2392
|
-
let isRange = ih === '..' || ih === '...';
|
|
2393
|
-
if (isRange) {
|
|
2394
|
-
let isExcl = ih === '...';
|
|
2395
|
-
let [s, e] = iterable.slice(1);
|
|
2396
|
-
code += this.indent() + `for (let ${ivp} = ${this.generate(s, 'value')}; ${ivp} ${isExcl ? '<' : '<='} ${this.generate(e, 'value')}; ${ivp} += ${this.generate(step, 'value')}) {\n`;
|
|
2397
|
-
} else {
|
|
2398
|
-
let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
|
|
2399
|
-
let isNeg = this.is(step, '-', 1);
|
|
2400
|
-
code += isNeg
|
|
2401
|
-
? this.indent() + `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN} += ${stc}) {\n`
|
|
2402
|
-
: this.indent() + `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN} += ${stc}) {\n`;
|
|
2403
|
-
this.indentLevel++;
|
|
2404
|
-
if (!noVar) code += this.indent() + `const ${ivp} = ${ic}[${idxN}];\n`;
|
|
2405
|
-
}
|
|
2406
|
-
} else {
|
|
2407
|
-
code += this.indent() + `for (const ${ivp} of ${this.generate(iterable, 'value')}) {\n`;
|
|
2408
|
-
}
|
|
2404
|
+
let { header, setup } = this._forInHeader(vars, iterable, stepOrOwn);
|
|
2405
|
+
code += this.indent() + header + ' {\n';
|
|
2409
2406
|
this.indentLevel++;
|
|
2407
|
+
if (setup) code += this.indent() + setup + '\n';
|
|
2410
2408
|
if (guards && guards.length > 0) {
|
|
2411
2409
|
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2412
2410
|
this.indentLevel++;
|
|
@@ -2423,155 +2421,51 @@ export class CodeGenerator {
|
|
|
2423
2421
|
|
|
2424
2422
|
generateComprehensionAsLoop(expr, iterators, guards) {
|
|
2425
2423
|
let code = '';
|
|
2424
|
+
let guardCond = guards?.length ? guards.map(g => this.generate(g, 'value')).join(' && ') : null;
|
|
2425
|
+
|
|
2426
|
+
// Helper: emit the loop body with optional guard wrapping
|
|
2427
|
+
let emitBody = () => {
|
|
2428
|
+
if (guardCond) {
|
|
2429
|
+
code += this.indent() + `if (${guardCond}) {\n`;
|
|
2430
|
+
this.indentLevel++;
|
|
2431
|
+
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2432
|
+
this.indentLevel--; code += this.indent() + '}\n';
|
|
2433
|
+
} else {
|
|
2434
|
+
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2435
|
+
}
|
|
2436
|
+
};
|
|
2437
|
+
|
|
2426
2438
|
if (iterators.length === 1) {
|
|
2427
2439
|
let [iterType, vars, iterable, stepOrOwn] = iterators[0];
|
|
2428
2440
|
|
|
2429
2441
|
if (iterType === 'for-in') {
|
|
2430
|
-
let
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
? this.generateDestructuringPattern(itemVar) : itemVar;
|
|
2436
|
-
|
|
2437
|
-
if (step && step !== null) {
|
|
2438
|
-
let ih = Array.isArray(iterable) && iterable[0];
|
|
2439
|
-
if (ih instanceof String) ih = str(ih);
|
|
2440
|
-
let isRange = ih === '..' || ih === '...';
|
|
2441
|
-
if (isRange) {
|
|
2442
|
-
let isExcl = ih === '...';
|
|
2443
|
-
let [s, e] = iterable.slice(1);
|
|
2444
|
-
code += `for (let ${ivp} = ${this.generate(s, 'value')}; ${ivp} ${isExcl ? '<' : '<='} ${this.generate(e, 'value')}; ${ivp} += ${this.generate(step, 'value')}) `;
|
|
2445
|
-
} else {
|
|
2446
|
-
let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
|
|
2447
|
-
let isNeg = this.is(step, '-', 1);
|
|
2448
|
-
let isMinus1 = isNeg && (step[1] === '1' || step[1] === 1 || str(step[1]) === '1');
|
|
2449
|
-
let isPlus1 = !isNeg && (step === '1' || step === 1 || str(step) === '1');
|
|
2450
|
-
if (isMinus1) code += `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN}--) `;
|
|
2451
|
-
else if (isPlus1) code += `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN}++) `;
|
|
2452
|
-
else if (isNeg) code += `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN} += ${stc}) `;
|
|
2453
|
-
else code += `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN} += ${stc}) `;
|
|
2454
|
-
code += '{\n';
|
|
2455
|
-
this.indentLevel++;
|
|
2456
|
-
if (!noVar) code += this.indent() + `const ${ivp} = ${ic}[${idxN}];\n`;
|
|
2457
|
-
}
|
|
2458
|
-
if (guards?.length) {
|
|
2459
|
-
if (!isRange) code += this.indent();
|
|
2460
|
-
code += '{\n'; this.indentLevel++;
|
|
2461
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2462
|
-
this.indentLevel++;
|
|
2463
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2464
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2465
|
-
this.indentLevel--; code += this.indent() + '}';
|
|
2466
|
-
} else {
|
|
2467
|
-
if (!isRange) code += this.indent();
|
|
2468
|
-
code += '{\n'; this.indentLevel++;
|
|
2469
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2470
|
-
this.indentLevel--; code += this.indent() + '}';
|
|
2471
|
-
}
|
|
2472
|
-
if (!isRange) { this.indentLevel--; code += '\n' + this.indent() + '}'; }
|
|
2473
|
-
return code;
|
|
2474
|
-
}
|
|
2475
|
-
|
|
2476
|
-
if (indexVar) {
|
|
2477
|
-
let ic = this.generate(iterable, 'value');
|
|
2478
|
-
code += `for (let ${indexVar} = 0; ${indexVar} < ${ic}.length; ${indexVar}++) `;
|
|
2479
|
-
code += '{\n'; this.indentLevel++;
|
|
2480
|
-
code += this.indent() + `const ${ivp} = ${ic}[${indexVar}];\n`;
|
|
2481
|
-
} else {
|
|
2482
|
-
code += `for (const ${ivp} of ${this.generate(iterable, 'value')}) `;
|
|
2483
|
-
if (guards?.length) {
|
|
2484
|
-
code += '{\n'; this.indentLevel++;
|
|
2485
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2486
|
-
this.indentLevel++;
|
|
2487
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2488
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2489
|
-
this.indentLevel--; code += this.indent() + '}';
|
|
2490
|
-
} else {
|
|
2491
|
-
code += '{\n'; this.indentLevel++;
|
|
2492
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2493
|
-
this.indentLevel--; code += this.indent() + '}';
|
|
2494
|
-
}
|
|
2495
|
-
return code;
|
|
2496
|
-
}
|
|
2497
|
-
|
|
2498
|
-
// Fall through for indexVar case
|
|
2499
|
-
if (guards?.length) {
|
|
2500
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2501
|
-
this.indentLevel++;
|
|
2502
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2503
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2504
|
-
} else {
|
|
2505
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2506
|
-
}
|
|
2442
|
+
let { header, setup } = this._forInHeader(vars, iterable, stepOrOwn);
|
|
2443
|
+
code += header + ' {\n';
|
|
2444
|
+
this.indentLevel++;
|
|
2445
|
+
if (setup) code += this.indent() + setup + '\n';
|
|
2446
|
+
emitBody();
|
|
2507
2447
|
this.indentLevel--;
|
|
2508
2448
|
code += this.indent() + '}';
|
|
2509
2449
|
return code;
|
|
2510
2450
|
}
|
|
2511
2451
|
|
|
2512
2452
|
if (iterType === 'for-as') {
|
|
2513
|
-
let
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
code += '{\n'; this.indentLevel++;
|
|
2520
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2521
|
-
this.indentLevel++;
|
|
2522
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2523
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2524
|
-
this.indentLevel--; code += this.indent() + '}';
|
|
2525
|
-
} else {
|
|
2526
|
-
code += '{\n'; this.indentLevel++;
|
|
2527
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2528
|
-
this.indentLevel--; code += this.indent() + '}';
|
|
2529
|
-
}
|
|
2453
|
+
let { header } = this._forAsHeader(vars, iterable, stepOrOwn);
|
|
2454
|
+
code += header + ' {\n';
|
|
2455
|
+
this.indentLevel++;
|
|
2456
|
+
emitBody();
|
|
2457
|
+
this.indentLevel--;
|
|
2458
|
+
code += this.indent() + '}';
|
|
2530
2459
|
return code;
|
|
2531
2460
|
}
|
|
2532
2461
|
|
|
2533
2462
|
if (iterType === 'for-of') {
|
|
2534
|
-
let
|
|
2535
|
-
|
|
2536
|
-
let own = stepOrOwn;
|
|
2537
|
-
let oc = this.generate(iterable, 'value');
|
|
2538
|
-
code += `for (const ${kv} in ${oc}) {\n`;
|
|
2463
|
+
let { header, own, vv, oc, kvp } = this._forOfHeader(vars, iterable, stepOrOwn);
|
|
2464
|
+
code += header + ' {\n';
|
|
2539
2465
|
this.indentLevel++;
|
|
2540
|
-
if (own
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
} else if (own && vv && guards?.length) {
|
|
2544
|
-
code += this.indent() + `if (Object.hasOwn(${oc}, ${kv})) {\n`;
|
|
2545
|
-
this.indentLevel++;
|
|
2546
|
-
code += this.indent() + `const ${vv} = ${oc}[${kv}];\n`;
|
|
2547
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2548
|
-
this.indentLevel++;
|
|
2549
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2550
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2551
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2552
|
-
} else if (own && vv) {
|
|
2553
|
-
code += this.indent() + `if (Object.hasOwn(${oc}, ${kv})) {\n`;
|
|
2554
|
-
this.indentLevel++;
|
|
2555
|
-
code += this.indent() + `const ${vv} = ${oc}[${kv}];\n`;
|
|
2556
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2557
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2558
|
-
} else if (vv && guards?.length) {
|
|
2559
|
-
code += this.indent() + `const ${vv} = ${oc}[${kv}];\n`;
|
|
2560
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2561
|
-
this.indentLevel++;
|
|
2562
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2563
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2564
|
-
} else if (vv) {
|
|
2565
|
-
code += this.indent() + `const ${vv} = ${oc}[${kv}];\n`;
|
|
2566
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2567
|
-
} else if (guards?.length) {
|
|
2568
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2569
|
-
this.indentLevel++;
|
|
2570
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2571
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2572
|
-
} else {
|
|
2573
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2574
|
-
}
|
|
2466
|
+
if (own) code += this.indent() + `if (!Object.hasOwn(${oc}, ${kvp})) continue;\n`;
|
|
2467
|
+
if (vv) code += this.indent() + `const ${vv} = ${oc}[${kvp}];\n`;
|
|
2468
|
+
emitBody();
|
|
2575
2469
|
this.indentLevel--;
|
|
2576
2470
|
code += this.indent() + '}';
|
|
2577
2471
|
return code;
|
|
@@ -2588,7 +2482,7 @@ export class CodeGenerator {
|
|
|
2588
2482
|
generateIfElseWithEarlyReturns(ifStmt) {
|
|
2589
2483
|
let [head, condition, thenBranch, ...elseBranches] = ifStmt;
|
|
2590
2484
|
let code = '';
|
|
2591
|
-
let condCode =
|
|
2485
|
+
let condCode = this.generate(condition, 'value');
|
|
2592
2486
|
code += this.indent() + `if (${condCode}) {\n`;
|
|
2593
2487
|
code += this.withIndent(() => this.generateBranchWithReturn(thenBranch));
|
|
2594
2488
|
code += this.indent() + '}';
|
|
@@ -2738,7 +2632,7 @@ export class CodeGenerator {
|
|
|
2738
2632
|
if (!generated || generated.endsWith(';')) return false;
|
|
2739
2633
|
if (!generated.endsWith('}')) return true;
|
|
2740
2634
|
let h = Array.isArray(stmt) ? stmt[0] : null;
|
|
2741
|
-
return !['def', 'class', 'if', '
|
|
2635
|
+
return !['def', 'class', 'if', 'for-in', 'for-of', 'for-as', 'while', 'loop', 'switch', 'try'].includes(h);
|
|
2742
2636
|
}
|
|
2743
2637
|
|
|
2744
2638
|
addSemicolon(stmt, generated) { return generated + (this.needsSemicolon(stmt, generated) ? ';' : ''); }
|
|
@@ -2765,7 +2659,7 @@ export class CodeGenerator {
|
|
|
2765
2659
|
return stmts.some(s => Array.isArray(s) && ['return', 'throw', 'break', 'continue'].includes(s[0]));
|
|
2766
2660
|
});
|
|
2767
2661
|
}
|
|
2768
|
-
if (t === 'if'
|
|
2662
|
+
if (t === 'if') {
|
|
2769
2663
|
let [, , thenB, elseB] = body;
|
|
2770
2664
|
return this.branchHasControlFlow(thenB) && elseB && this.branchHasControlFlow(elseB);
|
|
2771
2665
|
}
|
|
@@ -3154,14 +3048,19 @@ function __effect(fn) {
|
|
|
3154
3048
|
dependencies: new Set(),
|
|
3155
3049
|
|
|
3156
3050
|
run() {
|
|
3051
|
+
if (effect._cleanup) { effect._cleanup(); effect._cleanup = null; }
|
|
3157
3052
|
for (const dep of effect.dependencies) dep.delete(effect);
|
|
3158
3053
|
effect.dependencies.clear();
|
|
3159
3054
|
const prev = __currentEffect;
|
|
3160
3055
|
__currentEffect = effect;
|
|
3161
|
-
try {
|
|
3056
|
+
try {
|
|
3057
|
+
const result = fn();
|
|
3058
|
+
if (typeof result === 'function') effect._cleanup = result;
|
|
3059
|
+
} finally { __currentEffect = prev; }
|
|
3162
3060
|
},
|
|
3163
3061
|
|
|
3164
3062
|
dispose() {
|
|
3063
|
+
if (effect._cleanup) { effect._cleanup(); effect._cleanup = null; }
|
|
3165
3064
|
for (const dep of effect.dependencies) dep.delete(effect);
|
|
3166
3065
|
effect.dependencies.clear();
|
|
3167
3066
|
}
|