rip-lang 3.7.4 → 3.8.8
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 +97 -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 +516 -0
- package/docs/dist/rip-ui.min.js.br +0 -0
- package/docs/dist/rip.browser.js +285 -406
- package/docs/dist/rip.browser.min.js +166 -204
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/dist/ui.js +956 -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 +148 -221
- package/src/components.js +100 -95
- package/src/grammar/README.md +234 -0
- 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 +6 -5
- 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/docs/rip-fav.svg
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg width="420" height="420" viewBox="0 0 420 420" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<circle cx="210" cy="210" r="210" fill="white"/>
|
|
3
|
+
<path d="M114.5 263C79.772 263 45.7872 275.051 34.271 283.579C33.4994 284.151 34.012 285.229 34.9656 285.117C73.1916 280.629 115.309 292.74 146.5 304C178.5 315.552 221 336 269 336C314.651 336 372.074 310.499 386.401 293.944C387.038 293.208 386.301 292.353 385.435 292.799C376.614 297.341 364.243 306.018 316.073 306.948C265.273 307.928 159 263 114.5 263Z" fill="#0389FF"/>
|
|
4
|
+
<path d="M223.46 84C239.53 84 253.592 86.9253 265.645 92.7754C277.697 98.6254 287.071 107.048 293.767 118.043C300.462 129.038 303.811 142.219 303.811 157.584C303.811 173.09 300.357 186.165 293.449 196.808C287.068 206.742 278.259 214.402 267.026 219.792L309.603 297.958C297.885 297.851 283.094 295.413 266.52 291.62C257.58 289.574 248.214 287.157 238.645 284.547L209.127 229.054H188.782V270.333C176.996 266.968 165.515 263.788 154.823 261.15C146.038 258.984 137.665 257.152 130 255.885V84H223.46ZM188.782 183.381H209.505C216.412 183.381 222.297 182.535 227.16 180.844C232.094 179.082 235.865 176.297 238.473 172.491C241.151 168.685 242.49 163.716 242.49 157.584C242.49 151.382 241.151 146.342 238.473 142.466C235.865 138.519 232.094 135.628 227.16 133.796C222.297 131.893 216.412 130.941 209.505 130.941H188.782V183.381Z" fill="#BB0000"/>
|
|
5
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rip-lang",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.8",
|
|
4
4
|
"description": "A modern language that compiles to JavaScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/compiler.js",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"author": "Steve Shreeve <steve.shreeve@gmail.com>",
|
|
68
68
|
"license": "MIT",
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@rip-lang/api": "
|
|
71
|
-
"@rip-lang/ui": "
|
|
70
|
+
"@rip-lang/api": "workspace:*",
|
|
71
|
+
"@rip-lang/ui": "workspace:*"
|
|
72
72
|
}
|
|
73
73
|
}
|
package/scripts/serve.js
CHANGED
|
@@ -23,7 +23,8 @@ const MIME_TYPES = {
|
|
|
23
23
|
'.png': 'image/png',
|
|
24
24
|
'.jpg': 'image/jpeg',
|
|
25
25
|
'.svg': 'image/svg+xml',
|
|
26
|
-
'.ico': 'image/x-icon'
|
|
26
|
+
'.ico': 'image/x-icon',
|
|
27
|
+
'.rip': 'text/plain; charset=utf-8'
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
// Request handler for serving files
|
|
@@ -53,7 +54,7 @@ function handleRequest(req) {
|
|
|
53
54
|
headers: {
|
|
54
55
|
'Content-Type': MIME_TYPES[ext] || 'application/octet-stream',
|
|
55
56
|
'Content-Encoding': 'br',
|
|
56
|
-
'Cache-Control': '
|
|
57
|
+
'Cache-Control': 'no-cache'
|
|
57
58
|
}
|
|
58
59
|
});
|
|
59
60
|
} catch (e) {
|
package/src/browser.js
CHANGED
|
@@ -9,8 +9,8 @@ export { CodeGenerator, Compiler, compile, compileToJS, formatSExpr, getReactive
|
|
|
9
9
|
export const VERSION = "0.0.0";
|
|
10
10
|
export const BUILD_DATE = "0000-00-00@00:00:00GMT";
|
|
11
11
|
|
|
12
|
-
// Import
|
|
13
|
-
import { compileToJS, getReactiveRuntime } from './compiler.js';
|
|
12
|
+
// Import compiler functions for use in rip() function and globalThis registration
|
|
13
|
+
import { compile, compileToJS, formatSExpr, getReactiveRuntime, getComponentRuntime } from './compiler.js';
|
|
14
14
|
|
|
15
15
|
// Eagerly register Rip's reactive primitives on globalThis so that
|
|
16
16
|
// framework code (ui.rip) can use them directly without the compiler
|
|
@@ -26,6 +26,7 @@ const dedent = s => {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// Browser runtime for executing <script type="text/rip"> tags
|
|
29
|
+
// Supports both inline scripts and external files via src attribute
|
|
29
30
|
async function processRipScripts() {
|
|
30
31
|
const scripts = document.querySelectorAll('script[type="text/rip"]');
|
|
31
32
|
|
|
@@ -33,7 +34,18 @@ async function processRipScripts() {
|
|
|
33
34
|
if (script.hasAttribute('data-rip-processed')) continue;
|
|
34
35
|
|
|
35
36
|
try {
|
|
36
|
-
|
|
37
|
+
let ripCode;
|
|
38
|
+
if (script.src) {
|
|
39
|
+
const response = await fetch(script.src);
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
console.error(`Rip: failed to fetch ${script.src} (${response.status})`);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
ripCode = await response.text();
|
|
45
|
+
} else {
|
|
46
|
+
ripCode = dedent(script.textContent);
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
let jsCode;
|
|
38
50
|
try {
|
|
39
51
|
jsCode = compileToJS(ripCode);
|
|
@@ -113,13 +125,17 @@ if (typeof globalThis !== 'undefined') {
|
|
|
113
125
|
globalThis.rip = rip;
|
|
114
126
|
globalThis.importRip = importRip;
|
|
115
127
|
globalThis.compileToJS = compileToJS;
|
|
128
|
+
globalThis.__ripExports = { compile, compileToJS, formatSExpr, VERSION, BUILD_DATE, getReactiveRuntime, getComponentRuntime };
|
|
116
129
|
}
|
|
117
130
|
|
|
118
131
|
// Auto-process scripts when this module loads
|
|
132
|
+
// Expose __ripScriptsReady promise so other modules can await completion
|
|
119
133
|
if (typeof document !== 'undefined') {
|
|
120
134
|
if (document.readyState === 'loading') {
|
|
121
|
-
|
|
135
|
+
globalThis.__ripScriptsReady = new Promise(resolve => {
|
|
136
|
+
document.addEventListener('DOMContentLoaded', () => processRipScripts().then(resolve));
|
|
137
|
+
});
|
|
122
138
|
} else {
|
|
123
|
-
processRipScripts();
|
|
139
|
+
globalThis.__ripScriptsReady = processRipScripts();
|
|
124
140
|
}
|
|
125
141
|
}
|
package/src/compiler.js
CHANGED
|
@@ -201,9 +201,7 @@ export class CodeGenerator {
|
|
|
201
201
|
|
|
202
202
|
// Control flow — simple
|
|
203
203
|
'break': 'generateBreak',
|
|
204
|
-
'break-if': 'generateBreakIf',
|
|
205
204
|
'continue': 'generateContinue',
|
|
206
|
-
'continue-if': 'generateContinueIf',
|
|
207
205
|
'?': 'generateExistential',
|
|
208
206
|
'?:': 'generateTernary',
|
|
209
207
|
'|>': 'generatePipe',
|
|
@@ -311,14 +309,13 @@ export class CodeGenerator {
|
|
|
311
309
|
collect(sexpr);
|
|
312
310
|
|
|
313
311
|
// Match output lines to collected statement locations in parallel.
|
|
314
|
-
// Skip
|
|
312
|
+
// Skip lines with no source correspondence (preamble, braces, etc.).
|
|
315
313
|
let lines = code.split('\n');
|
|
316
314
|
let locIdx = 0;
|
|
317
315
|
for (let outLine = 0; outLine < lines.length; outLine++) {
|
|
318
316
|
let line = lines[outLine];
|
|
319
317
|
let trimmed = line.trim();
|
|
320
318
|
|
|
321
|
-
// Skip lines with no source correspondence
|
|
322
319
|
if (!trimmed || trimmed === '}' || trimmed === '});') continue;
|
|
323
320
|
if (trimmed.startsWith('let ') || trimmed.startsWith('var ')) continue;
|
|
324
321
|
if (trimmed.startsWith('const slice') || trimmed.startsWith('const modulo') || trimmed.startsWith('const toSearchable')) continue;
|
|
@@ -359,6 +356,7 @@ export class CodeGenerator {
|
|
|
359
356
|
}
|
|
360
357
|
|
|
361
358
|
if (head === 'readonly') return;
|
|
359
|
+
if (head === 'component') return; // Component body has its own scope
|
|
362
360
|
|
|
363
361
|
if (CodeGenerator.ASSIGNMENT_OPS.has(head)) {
|
|
364
362
|
let [target, value] = rest;
|
|
@@ -929,11 +927,17 @@ export class CodeGenerator {
|
|
|
929
927
|
|
|
930
928
|
generatePropertyAccess(head, rest, context, sexpr) {
|
|
931
929
|
let [obj, prop] = rest;
|
|
930
|
+
// In subclass constructors, rewrite @param refs (this.x) to _x for super() safety
|
|
931
|
+
if (this._atParamMap && obj === 'this') {
|
|
932
|
+
let mapped = this._atParamMap.get(str(prop));
|
|
933
|
+
if (mapped) return mapped;
|
|
934
|
+
}
|
|
932
935
|
this.suppressReactiveUnwrap = true;
|
|
933
936
|
let objCode = this.generate(obj, 'value');
|
|
934
937
|
this.suppressReactiveUnwrap = false;
|
|
935
938
|
let needsParens = CodeGenerator.NUMBER_LITERAL_RE.test(objCode) ||
|
|
936
|
-
|
|
939
|
+
objCode.startsWith('await ') ||
|
|
940
|
+
((this.is(obj, 'object') || this.is(obj, 'yield')));
|
|
937
941
|
let base = needsParens ? `(${objCode})` : objCode;
|
|
938
942
|
if (meta(prop, 'await') === true) return `await ${base}.${str(prop)}()`;
|
|
939
943
|
if (meta(prop, 'predicate')) return `(${base}.${str(prop)} != null)`;
|
|
@@ -1134,9 +1138,7 @@ export class CodeGenerator {
|
|
|
1134
1138
|
// ---------------------------------------------------------------------------
|
|
1135
1139
|
|
|
1136
1140
|
generateBreak() { return 'break'; }
|
|
1137
|
-
generateBreakIf(head, rest) { return `if (${this.generate(rest[0], 'value')}) break`; }
|
|
1138
1141
|
generateContinue() { return 'continue'; }
|
|
1139
|
-
generateContinueIf(head, rest) { return `if (${this.generate(rest[0], 'value')}) continue`; }
|
|
1140
1142
|
|
|
1141
1143
|
generateExistential(head, rest) {
|
|
1142
1144
|
return `(${this.generate(rest[0], 'value')} != null)`;
|
|
@@ -1574,7 +1576,11 @@ export class CodeGenerator {
|
|
|
1574
1576
|
let keyCode;
|
|
1575
1577
|
if (this.is(key, 'dynamicKey')) keyCode = `[${this.generate(key[1], 'value')}]`;
|
|
1576
1578
|
else if (this.is(key, 'str')) keyCode = `[${this.generate(key, 'value')}]`;
|
|
1577
|
-
else
|
|
1579
|
+
else {
|
|
1580
|
+
this.suppressReactiveUnwrap = true;
|
|
1581
|
+
keyCode = this.generate(key, 'value');
|
|
1582
|
+
this.suppressReactiveUnwrap = false;
|
|
1583
|
+
}
|
|
1578
1584
|
let valCode = this.generate(value, 'value');
|
|
1579
1585
|
if (operator === '=') return `${keyCode} = ${valCode}`;
|
|
1580
1586
|
if (operator === ':') return `${keyCode}: ${valCode}`;
|
|
@@ -1729,6 +1735,65 @@ export class CodeGenerator {
|
|
|
1729
1735
|
// Comprehensions
|
|
1730
1736
|
// ---------------------------------------------------------------------------
|
|
1731
1737
|
|
|
1738
|
+
// Shared: parse a for-in iterator and return { header, setup }.
|
|
1739
|
+
// header: the for(...) clause (no trailing brace)
|
|
1740
|
+
// setup: any `const x = arr[i]` preamble line, or null
|
|
1741
|
+
_forInHeader(vars, iterable, step) {
|
|
1742
|
+
let va = Array.isArray(vars) ? vars : [vars];
|
|
1743
|
+
let noVar = va.length === 0;
|
|
1744
|
+
let [itemVar, indexVar] = noVar ? ['_i', null] : va;
|
|
1745
|
+
let ivp = (this.is(itemVar, 'array') || this.is(itemVar, 'object'))
|
|
1746
|
+
? this.generateDestructuringPattern(itemVar) : itemVar;
|
|
1747
|
+
|
|
1748
|
+
if (step && step !== null) {
|
|
1749
|
+
let ih = Array.isArray(iterable) && iterable[0];
|
|
1750
|
+
if (ih instanceof String) ih = str(ih);
|
|
1751
|
+
let isRange = ih === '..' || ih === '...';
|
|
1752
|
+
if (isRange) {
|
|
1753
|
+
let isExcl = ih === '...';
|
|
1754
|
+
let [s, e] = iterable.slice(1);
|
|
1755
|
+
let sc = this.generate(s, 'value'), ec = this.generate(e, 'value'), stc = this.generate(step, 'value');
|
|
1756
|
+
return { header: `for (let ${ivp} = ${sc}; ${ivp} ${isExcl ? '<' : '<='} ${ec}; ${ivp} += ${stc})`, setup: null };
|
|
1757
|
+
}
|
|
1758
|
+
let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
|
|
1759
|
+
let isNeg = this.is(step, '-', 1);
|
|
1760
|
+
let isMinus1 = isNeg && (step[1] === '1' || step[1] === 1 || str(step[1]) === '1');
|
|
1761
|
+
let isPlus1 = !isNeg && (step === '1' || step === 1 || str(step) === '1');
|
|
1762
|
+
let update = isMinus1 ? `${idxN}--` : isPlus1 ? `${idxN}++` : `${idxN} += ${stc}`;
|
|
1763
|
+
let header = isNeg
|
|
1764
|
+
? `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${update})`
|
|
1765
|
+
: `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${update})`;
|
|
1766
|
+
return { header, setup: noVar ? null : `const ${ivp} = ${ic}[${idxN}];` };
|
|
1767
|
+
}
|
|
1768
|
+
if (indexVar) {
|
|
1769
|
+
let ic = this.generate(iterable, 'value');
|
|
1770
|
+
return {
|
|
1771
|
+
header: `for (let ${indexVar} = 0; ${indexVar} < ${ic}.length; ${indexVar}++)`,
|
|
1772
|
+
setup: `const ${ivp} = ${ic}[${indexVar}];`,
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
return { header: `for (const ${ivp} of ${this.generate(iterable, 'value')})`, setup: null };
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
// Shared: parse a for-of (object) iterator and return { header, own, vv, oc, kvp }.
|
|
1779
|
+
_forOfHeader(vars, iterable, own) {
|
|
1780
|
+
let va = Array.isArray(vars) ? vars : [vars];
|
|
1781
|
+
let [kv, vv] = va;
|
|
1782
|
+
let kvp = (this.is(kv, 'array') || this.is(kv, 'object'))
|
|
1783
|
+
? this.generateDestructuringPattern(kv) : kv;
|
|
1784
|
+
let oc = this.generate(iterable, 'value');
|
|
1785
|
+
return { header: `for (const ${kvp} in ${oc})`, own, vv, oc, kvp };
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
// Shared: parse a for-as (iterator) spec and return { header }.
|
|
1789
|
+
_forAsHeader(vars, iterable, isAwait) {
|
|
1790
|
+
let va = Array.isArray(vars) ? vars : [vars];
|
|
1791
|
+
let [fv] = va;
|
|
1792
|
+
let ivp = (this.is(fv, 'array') || this.is(fv, 'object'))
|
|
1793
|
+
? this.generateDestructuringPattern(fv) : fv;
|
|
1794
|
+
return { header: `for ${isAwait ? 'await ' : ''}(const ${ivp} of ${this.generate(iterable, 'value')})` };
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1732
1797
|
generateComprehension(head, rest, context) {
|
|
1733
1798
|
let [expr, iterators, guards] = rest;
|
|
1734
1799
|
if (context === 'statement') return this.generateComprehensionAsLoop(expr, iterators, guards);
|
|
@@ -1743,59 +1808,19 @@ export class CodeGenerator {
|
|
|
1743
1808
|
for (let iter of iterators) {
|
|
1744
1809
|
let [iterType, vars, iterable, stepOrOwn] = iter;
|
|
1745
1810
|
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
|
-
}
|
|
1811
|
+
let { header, setup } = this._forInHeader(vars, iterable, stepOrOwn);
|
|
1812
|
+
code += this.indent() + header + ' {\n';
|
|
1813
|
+
this.indentLevel++;
|
|
1814
|
+
if (setup) code += this.indent() + setup + '\n';
|
|
1781
1815
|
} 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`;
|
|
1816
|
+
let { header, own, vv, oc, kvp } = this._forOfHeader(vars, iterable, stepOrOwn);
|
|
1817
|
+
code += this.indent() + header + ' {\n';
|
|
1789
1818
|
this.indentLevel++;
|
|
1790
1819
|
if (own) code += this.indent() + `if (!Object.hasOwn(${oc}, ${kvp})) continue;\n`;
|
|
1791
1820
|
if (vv) code += this.indent() + `const ${vv} = ${oc}[${kvp}];\n`;
|
|
1792
1821
|
} 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`;
|
|
1822
|
+
let { header } = this._forAsHeader(vars, iterable, iter[3]);
|
|
1823
|
+
code += this.indent() + header + ' {\n';
|
|
1799
1824
|
this.indentLevel++;
|
|
1800
1825
|
}
|
|
1801
1826
|
}
|
|
@@ -1808,7 +1833,7 @@ export class CodeGenerator {
|
|
|
1808
1833
|
let hasCtrl = (node) => {
|
|
1809
1834
|
if (typeof node === 'string' && (node === 'break' || node === 'continue')) return true;
|
|
1810
1835
|
if (!Array.isArray(node)) return false;
|
|
1811
|
-
if (['break', 'continue', '
|
|
1836
|
+
if (['break', 'continue', 'return', 'throw'].includes(node[0])) return true;
|
|
1812
1837
|
if (node[0] === 'if' || node[0] === 'unless') return node.slice(1).some(hasCtrl);
|
|
1813
1838
|
return node.some(hasCtrl);
|
|
1814
1839
|
};
|
|
@@ -1909,17 +1934,36 @@ export class CodeGenerator {
|
|
|
1909
1934
|
let hasAwait = this.containsAwait(body), hasYield = this.containsYield(body);
|
|
1910
1935
|
let cleanParams = params, autoAssign = [];
|
|
1911
1936
|
if (mName === 'constructor') {
|
|
1937
|
+
let isSubclass = !!parentClass;
|
|
1938
|
+
let atParamMap = isSubclass ? new Map() : null;
|
|
1912
1939
|
cleanParams = params.map(p => {
|
|
1913
|
-
|
|
1940
|
+
// Handle @param: ['.', 'this', 'name']
|
|
1941
|
+
if (this.is(p, '.') && p[1] === 'this') {
|
|
1942
|
+
let name = p[2];
|
|
1943
|
+
let param = isSubclass ? `_${name}` : name;
|
|
1944
|
+
autoAssign.push(`this.${name} = ${param}`);
|
|
1945
|
+
if (isSubclass) atParamMap.set(name, param);
|
|
1946
|
+
return param;
|
|
1947
|
+
}
|
|
1948
|
+
// Handle @param with default: ['default', ['.', 'this', 'name'], value]
|
|
1949
|
+
if (this.is(p, 'default') && this.is(p[1], '.') && p[1][1] === 'this') {
|
|
1950
|
+
let name = p[1][2];
|
|
1951
|
+
let param = isSubclass ? `_${name}` : name;
|
|
1952
|
+
autoAssign.push(`this.${name} = ${param}`);
|
|
1953
|
+
if (isSubclass) atParamMap.set(name, param);
|
|
1954
|
+
return ['default', param, p[2]];
|
|
1955
|
+
}
|
|
1914
1956
|
return p;
|
|
1915
1957
|
});
|
|
1916
1958
|
for (let bm of boundMethods) autoAssign.unshift(`this.${bm} = this.${bm}.bind(this)`);
|
|
1959
|
+
if (atParamMap?.size > 0) this._atParamMap = atParamMap;
|
|
1917
1960
|
}
|
|
1918
1961
|
let pList = this.generateParamList(cleanParams);
|
|
1919
1962
|
let prefix = (isStatic ? 'static ' : '') + (hasAwait ? 'async ' : '') + (hasYield ? '*' : '');
|
|
1920
1963
|
code += this.indent() + `${prefix}${mName}(${pList}) `;
|
|
1921
1964
|
if (!isComputed) this.currentMethodName = mName;
|
|
1922
1965
|
code += this.generateMethodBody(body, autoAssign, mName === 'constructor', cleanParams);
|
|
1966
|
+
this._atParamMap = null;
|
|
1923
1967
|
this.currentMethodName = null;
|
|
1924
1968
|
code += '\n';
|
|
1925
1969
|
} else if (isStatic) {
|
|
@@ -2299,7 +2343,13 @@ export class CodeGenerator {
|
|
|
2299
2343
|
// Single expression
|
|
2300
2344
|
this.sideEffectOnly = prevSEO;
|
|
2301
2345
|
let result;
|
|
2302
|
-
if (isConstructor
|
|
2346
|
+
if (isConstructor && autoAssignments.length > 0) {
|
|
2347
|
+
// Constructor with @params as a single expression — need to emit autoAssignments
|
|
2348
|
+
let isSuper = Array.isArray(body) && body[0] === 'super';
|
|
2349
|
+
let bodyCode = this.generate(body, 'statement');
|
|
2350
|
+
let assigns = autoAssignments.map(a => `${a};`).join(' ');
|
|
2351
|
+
result = isSuper ? `{ ${bodyCode}; ${assigns} }` : `{ ${assigns} ${bodyCode}; }`;
|
|
2352
|
+
} else if (isConstructor || this.hasExplicitControlFlow(body)) result = `{ ${this.generate(body, 'statement')}; }`;
|
|
2303
2353
|
else if (Array.isArray(body) && (noRetStmts.includes(body[0]) || loopStmts.includes(body[0]))) result = `{ ${this.generate(body, 'statement')}; }`;
|
|
2304
2354
|
else if (sideEffectOnly) result = `{ ${this.generate(body, 'statement')}; return; }`;
|
|
2305
2355
|
else result = `{ return ${this.generate(body, 'value')}; }`;
|
|
@@ -2379,34 +2429,10 @@ export class CodeGenerator {
|
|
|
2379
2429
|
if (iterators.length === 1) {
|
|
2380
2430
|
let [iterType, vars, iterable, stepOrOwn] = iterators[0];
|
|
2381
2431
|
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
|
-
}
|
|
2432
|
+
let { header, setup } = this._forInHeader(vars, iterable, stepOrOwn);
|
|
2433
|
+
code += this.indent() + header + ' {\n';
|
|
2409
2434
|
this.indentLevel++;
|
|
2435
|
+
if (setup) code += this.indent() + setup + '\n';
|
|
2410
2436
|
if (guards && guards.length > 0) {
|
|
2411
2437
|
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2412
2438
|
this.indentLevel++;
|
|
@@ -2423,155 +2449,51 @@ export class CodeGenerator {
|
|
|
2423
2449
|
|
|
2424
2450
|
generateComprehensionAsLoop(expr, iterators, guards) {
|
|
2425
2451
|
let code = '';
|
|
2452
|
+
let guardCond = guards?.length ? guards.map(g => this.generate(g, 'value')).join(' && ') : null;
|
|
2453
|
+
|
|
2454
|
+
// Helper: emit the loop body with optional guard wrapping
|
|
2455
|
+
let emitBody = () => {
|
|
2456
|
+
if (guardCond) {
|
|
2457
|
+
code += this.indent() + `if (${guardCond}) {\n`;
|
|
2458
|
+
this.indentLevel++;
|
|
2459
|
+
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2460
|
+
this.indentLevel--; code += this.indent() + '}\n';
|
|
2461
|
+
} else {
|
|
2462
|
+
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2463
|
+
}
|
|
2464
|
+
};
|
|
2465
|
+
|
|
2426
2466
|
if (iterators.length === 1) {
|
|
2427
2467
|
let [iterType, vars, iterable, stepOrOwn] = iterators[0];
|
|
2428
2468
|
|
|
2429
2469
|
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
|
-
}
|
|
2470
|
+
let { header, setup } = this._forInHeader(vars, iterable, stepOrOwn);
|
|
2471
|
+
code += header + ' {\n';
|
|
2472
|
+
this.indentLevel++;
|
|
2473
|
+
if (setup) code += this.indent() + setup + '\n';
|
|
2474
|
+
emitBody();
|
|
2507
2475
|
this.indentLevel--;
|
|
2508
2476
|
code += this.indent() + '}';
|
|
2509
2477
|
return code;
|
|
2510
2478
|
}
|
|
2511
2479
|
|
|
2512
2480
|
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
|
-
}
|
|
2481
|
+
let { header } = this._forAsHeader(vars, iterable, stepOrOwn);
|
|
2482
|
+
code += header + ' {\n';
|
|
2483
|
+
this.indentLevel++;
|
|
2484
|
+
emitBody();
|
|
2485
|
+
this.indentLevel--;
|
|
2486
|
+
code += this.indent() + '}';
|
|
2530
2487
|
return code;
|
|
2531
2488
|
}
|
|
2532
2489
|
|
|
2533
2490
|
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`;
|
|
2491
|
+
let { header, own, vv, oc, kvp } = this._forOfHeader(vars, iterable, stepOrOwn);
|
|
2492
|
+
code += header + ' {\n';
|
|
2539
2493
|
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
|
-
}
|
|
2494
|
+
if (own) code += this.indent() + `if (!Object.hasOwn(${oc}, ${kvp})) continue;\n`;
|
|
2495
|
+
if (vv) code += this.indent() + `const ${vv} = ${oc}[${kvp}];\n`;
|
|
2496
|
+
emitBody();
|
|
2575
2497
|
this.indentLevel--;
|
|
2576
2498
|
code += this.indent() + '}';
|
|
2577
2499
|
return code;
|
|
@@ -3154,14 +3076,19 @@ function __effect(fn) {
|
|
|
3154
3076
|
dependencies: new Set(),
|
|
3155
3077
|
|
|
3156
3078
|
run() {
|
|
3079
|
+
if (effect._cleanup) { effect._cleanup(); effect._cleanup = null; }
|
|
3157
3080
|
for (const dep of effect.dependencies) dep.delete(effect);
|
|
3158
3081
|
effect.dependencies.clear();
|
|
3159
3082
|
const prev = __currentEffect;
|
|
3160
3083
|
__currentEffect = effect;
|
|
3161
|
-
try {
|
|
3084
|
+
try {
|
|
3085
|
+
const result = fn();
|
|
3086
|
+
if (typeof result === 'function') effect._cleanup = result;
|
|
3087
|
+
} finally { __currentEffect = prev; }
|
|
3162
3088
|
},
|
|
3163
3089
|
|
|
3164
3090
|
dispose() {
|
|
3091
|
+
if (effect._cleanup) { effect._cleanup(); effect._cleanup = null; }
|
|
3165
3092
|
for (const dep of effect.dependencies) dep.delete(effect);
|
|
3166
3093
|
effect.dependencies.clear();
|
|
3167
3094
|
}
|