rip-lang 3.7.3 → 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 +111 -0
- 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 +379 -461
- package/docs/dist/rip.browser.min.js +204 -220
- 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 +38 -16
- package/src/compiler.js +165 -226
- package/src/components.js +153 -140
- 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 +82 -30
- 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/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',
|
|
@@ -276,6 +274,7 @@ export class CodeGenerator {
|
|
|
276
274
|
this.programVars = new Set();
|
|
277
275
|
this.functionVars = new Map();
|
|
278
276
|
this.helpers = new Set();
|
|
277
|
+
this.scopeStack = []; // Track enclosing function scopes for proper variable hoisting
|
|
279
278
|
this.collectProgramVariables(sexpr);
|
|
280
279
|
let code = this.generate(sexpr);
|
|
281
280
|
|
|
@@ -310,14 +309,13 @@ export class CodeGenerator {
|
|
|
310
309
|
collect(sexpr);
|
|
311
310
|
|
|
312
311
|
// Match output lines to collected statement locations in parallel.
|
|
313
|
-
// Skip
|
|
312
|
+
// Skip lines with no source correspondence (preamble, braces, etc.).
|
|
314
313
|
let lines = code.split('\n');
|
|
315
314
|
let locIdx = 0;
|
|
316
315
|
for (let outLine = 0; outLine < lines.length; outLine++) {
|
|
317
316
|
let line = lines[outLine];
|
|
318
317
|
let trimmed = line.trim();
|
|
319
318
|
|
|
320
|
-
// Skip lines with no source correspondence
|
|
321
319
|
if (!trimmed || trimmed === '}' || trimmed === '});') continue;
|
|
322
320
|
if (trimmed.startsWith('let ') || trimmed.startsWith('var ')) continue;
|
|
323
321
|
if (trimmed.startsWith('const slice') || trimmed.startsWith('const modulo') || trimmed.startsWith('const toSearchable')) continue;
|
|
@@ -358,6 +356,7 @@ export class CodeGenerator {
|
|
|
358
356
|
}
|
|
359
357
|
|
|
360
358
|
if (head === 'readonly') return;
|
|
359
|
+
if (head === 'component') return; // Component body has its own scope
|
|
361
360
|
|
|
362
361
|
if (CodeGenerator.ASSIGNMENT_OPS.has(head)) {
|
|
363
362
|
let [target, value] = rest;
|
|
@@ -715,7 +714,7 @@ export class CodeGenerator {
|
|
|
715
714
|
|
|
716
715
|
if (this.usesTemplates && !skip) {
|
|
717
716
|
if (typeof globalThis !== 'undefined' && globalThis.__ripComponent) {
|
|
718
|
-
code += 'const {
|
|
717
|
+
code += 'const { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __Component } = globalThis.__ripComponent;\n';
|
|
719
718
|
} else {
|
|
720
719
|
code += this.getComponentRuntime();
|
|
721
720
|
}
|
|
@@ -928,11 +927,17 @@ export class CodeGenerator {
|
|
|
928
927
|
|
|
929
928
|
generatePropertyAccess(head, rest, context, sexpr) {
|
|
930
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
|
+
}
|
|
931
935
|
this.suppressReactiveUnwrap = true;
|
|
932
936
|
let objCode = this.generate(obj, 'value');
|
|
933
937
|
this.suppressReactiveUnwrap = false;
|
|
934
938
|
let needsParens = CodeGenerator.NUMBER_LITERAL_RE.test(objCode) ||
|
|
935
|
-
|
|
939
|
+
objCode.startsWith('await ') ||
|
|
940
|
+
((this.is(obj, 'object') || this.is(obj, 'yield')));
|
|
936
941
|
let base = needsParens ? `(${objCode})` : objCode;
|
|
937
942
|
if (meta(prop, 'await') === true) return `await ${base}.${str(prop)}()`;
|
|
938
943
|
if (meta(prop, 'predicate')) return `(${base}.${str(prop)} != null)`;
|
|
@@ -1133,9 +1138,7 @@ export class CodeGenerator {
|
|
|
1133
1138
|
// ---------------------------------------------------------------------------
|
|
1134
1139
|
|
|
1135
1140
|
generateBreak() { return 'break'; }
|
|
1136
|
-
generateBreakIf(head, rest) { return `if (${this.generate(rest[0], 'value')}) break`; }
|
|
1137
1141
|
generateContinue() { return 'continue'; }
|
|
1138
|
-
generateContinueIf(head, rest) { return `if (${this.generate(rest[0], 'value')}) continue`; }
|
|
1139
1142
|
|
|
1140
1143
|
generateExistential(head, rest) {
|
|
1141
1144
|
return `(${this.generate(rest[0], 'value')} != null)`;
|
|
@@ -1573,7 +1576,11 @@ export class CodeGenerator {
|
|
|
1573
1576
|
let keyCode;
|
|
1574
1577
|
if (this.is(key, 'dynamicKey')) keyCode = `[${this.generate(key[1], 'value')}]`;
|
|
1575
1578
|
else if (this.is(key, 'str')) keyCode = `[${this.generate(key, 'value')}]`;
|
|
1576
|
-
else
|
|
1579
|
+
else {
|
|
1580
|
+
this.suppressReactiveUnwrap = true;
|
|
1581
|
+
keyCode = this.generate(key, 'value');
|
|
1582
|
+
this.suppressReactiveUnwrap = false;
|
|
1583
|
+
}
|
|
1577
1584
|
let valCode = this.generate(value, 'value');
|
|
1578
1585
|
if (operator === '=') return `${keyCode} = ${valCode}`;
|
|
1579
1586
|
if (operator === ':') return `${keyCode}: ${valCode}`;
|
|
@@ -1728,6 +1735,65 @@ export class CodeGenerator {
|
|
|
1728
1735
|
// Comprehensions
|
|
1729
1736
|
// ---------------------------------------------------------------------------
|
|
1730
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
|
+
|
|
1731
1797
|
generateComprehension(head, rest, context) {
|
|
1732
1798
|
let [expr, iterators, guards] = rest;
|
|
1733
1799
|
if (context === 'statement') return this.generateComprehensionAsLoop(expr, iterators, guards);
|
|
@@ -1742,59 +1808,19 @@ export class CodeGenerator {
|
|
|
1742
1808
|
for (let iter of iterators) {
|
|
1743
1809
|
let [iterType, vars, iterable, stepOrOwn] = iter;
|
|
1744
1810
|
if (iterType === 'for-in') {
|
|
1745
|
-
let
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
let ivp = ((this.is(itemVar, 'array') || this.is(itemVar, 'object')))
|
|
1750
|
-
? this.generateDestructuringPattern(itemVar) : itemVar;
|
|
1751
|
-
|
|
1752
|
-
if (step && step !== null) {
|
|
1753
|
-
let ih = Array.isArray(iterable) && iterable[0];
|
|
1754
|
-
if (ih instanceof String) ih = str(ih);
|
|
1755
|
-
let isRange = ih === '..' || ih === '...';
|
|
1756
|
-
if (isRange) {
|
|
1757
|
-
let isExcl = ih === '...';
|
|
1758
|
-
let [s, e] = iterable.slice(1);
|
|
1759
|
-
let sc = this.generate(s, 'value'), ec = this.generate(e, 'value'), stc = this.generate(step, 'value');
|
|
1760
|
-
code += this.indent() + `for (let ${ivp} = ${sc}; ${ivp} ${isExcl ? '<' : '<='} ${ec}; ${ivp} += ${stc}) {\n`;
|
|
1761
|
-
this.indentLevel++;
|
|
1762
|
-
} else {
|
|
1763
|
-
let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
|
|
1764
|
-
let isNeg = this.is(step, '-', 1);
|
|
1765
|
-
code += isNeg
|
|
1766
|
-
? this.indent() + `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN} += ${stc}) {\n`
|
|
1767
|
-
: this.indent() + `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN} += ${stc}) {\n`;
|
|
1768
|
-
this.indentLevel++;
|
|
1769
|
-
if (!noVar) code += this.indent() + `const ${ivp} = ${ic}[${idxN}];\n`;
|
|
1770
|
-
}
|
|
1771
|
-
} else if (indexVar) {
|
|
1772
|
-
let ic = this.generate(iterable, 'value');
|
|
1773
|
-
code += this.indent() + `for (let ${indexVar} = 0; ${indexVar} < ${ic}.length; ${indexVar}++) {\n`;
|
|
1774
|
-
this.indentLevel++;
|
|
1775
|
-
code += this.indent() + `const ${ivp} = ${ic}[${indexVar}];\n`;
|
|
1776
|
-
} else {
|
|
1777
|
-
code += this.indent() + `for (const ${ivp} of ${this.generate(iterable, 'value')}) {\n`;
|
|
1778
|
-
this.indentLevel++;
|
|
1779
|
-
}
|
|
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';
|
|
1780
1815
|
} else if (iterType === 'for-of') {
|
|
1781
|
-
let own = stepOrOwn;
|
|
1782
|
-
|
|
1783
|
-
let [kv, vv] = va;
|
|
1784
|
-
let kvp = ((this.is(kv, 'array') || this.is(kv, 'object')))
|
|
1785
|
-
? this.generateDestructuringPattern(kv) : kv;
|
|
1786
|
-
let oc = this.generate(iterable, 'value');
|
|
1787
|
-
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';
|
|
1788
1818
|
this.indentLevel++;
|
|
1789
1819
|
if (own) code += this.indent() + `if (!Object.hasOwn(${oc}, ${kvp})) continue;\n`;
|
|
1790
1820
|
if (vv) code += this.indent() + `const ${vv} = ${oc}[${kvp}];\n`;
|
|
1791
1821
|
} else if (iterType === 'for-as') {
|
|
1792
|
-
let
|
|
1793
|
-
|
|
1794
|
-
let [fv] = va;
|
|
1795
|
-
let ivp = ((this.is(fv, 'array') || this.is(fv, 'object')))
|
|
1796
|
-
? this.generateDestructuringPattern(fv) : fv;
|
|
1797
|
-
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';
|
|
1798
1824
|
this.indentLevel++;
|
|
1799
1825
|
}
|
|
1800
1826
|
}
|
|
@@ -1807,7 +1833,7 @@ export class CodeGenerator {
|
|
|
1807
1833
|
let hasCtrl = (node) => {
|
|
1808
1834
|
if (typeof node === 'string' && (node === 'break' || node === 'continue')) return true;
|
|
1809
1835
|
if (!Array.isArray(node)) return false;
|
|
1810
|
-
if (['break', 'continue', '
|
|
1836
|
+
if (['break', 'continue', 'return', 'throw'].includes(node[0])) return true;
|
|
1811
1837
|
if (node[0] === 'if' || node[0] === 'unless') return node.slice(1).some(hasCtrl);
|
|
1812
1838
|
return node.some(hasCtrl);
|
|
1813
1839
|
};
|
|
@@ -1908,17 +1934,36 @@ export class CodeGenerator {
|
|
|
1908
1934
|
let hasAwait = this.containsAwait(body), hasYield = this.containsYield(body);
|
|
1909
1935
|
let cleanParams = params, autoAssign = [];
|
|
1910
1936
|
if (mName === 'constructor') {
|
|
1937
|
+
let isSubclass = !!parentClass;
|
|
1938
|
+
let atParamMap = isSubclass ? new Map() : null;
|
|
1911
1939
|
cleanParams = params.map(p => {
|
|
1912
|
-
|
|
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
|
+
}
|
|
1913
1956
|
return p;
|
|
1914
1957
|
});
|
|
1915
1958
|
for (let bm of boundMethods) autoAssign.unshift(`this.${bm} = this.${bm}.bind(this)`);
|
|
1959
|
+
if (atParamMap?.size > 0) this._atParamMap = atParamMap;
|
|
1916
1960
|
}
|
|
1917
1961
|
let pList = this.generateParamList(cleanParams);
|
|
1918
1962
|
let prefix = (isStatic ? 'static ' : '') + (hasAwait ? 'async ' : '') + (hasYield ? '*' : '');
|
|
1919
1963
|
code += this.indent() + `${prefix}${mName}(${pList}) `;
|
|
1920
1964
|
if (!isComputed) this.currentMethodName = mName;
|
|
1921
1965
|
code += this.generateMethodBody(body, autoAssign, mName === 'constructor', cleanParams);
|
|
1966
|
+
this._atParamMap = null;
|
|
1922
1967
|
this.currentMethodName = null;
|
|
1923
1968
|
code += '\n';
|
|
1924
1969
|
} else if (isStatic) {
|
|
@@ -2186,10 +2231,16 @@ export class CodeGenerator {
|
|
|
2186
2231
|
if (Array.isArray(params)) params.forEach(extractPN);
|
|
2187
2232
|
|
|
2188
2233
|
let bodyVars = this.collectFunctionVariables(body);
|
|
2189
|
-
let newVars = new Set([...bodyVars].filter(v =>
|
|
2234
|
+
let newVars = new Set([...bodyVars].filter(v =>
|
|
2235
|
+
!this.programVars.has(v) && !this.reactiveVars?.has(v) && !paramNames.has(v) &&
|
|
2236
|
+
!this.scopeStack.some(s => s.has(v)) // don't re-declare variables from enclosing scopes
|
|
2237
|
+
));
|
|
2190
2238
|
let noRetStmts = ['return', 'throw', 'break', 'continue'];
|
|
2191
2239
|
let loopStmts = ['for-in', 'for-of', 'for-as', 'while', 'until', 'loop'];
|
|
2192
2240
|
|
|
2241
|
+
// Track this function's scope so nested functions don't re-declare its variables
|
|
2242
|
+
this.scopeStack.push(new Set([...newVars, ...paramNames]));
|
|
2243
|
+
|
|
2193
2244
|
if (this.is(body, 'block')) {
|
|
2194
2245
|
let statements = this.unwrapBlock(body);
|
|
2195
2246
|
|
|
@@ -2284,16 +2335,26 @@ export class CodeGenerator {
|
|
|
2284
2335
|
|
|
2285
2336
|
this.indentLevel--;
|
|
2286
2337
|
code += this.indent() + '}';
|
|
2338
|
+
this.scopeStack.pop();
|
|
2287
2339
|
this.sideEffectOnly = prevSEO;
|
|
2288
2340
|
return code;
|
|
2289
2341
|
}
|
|
2290
2342
|
|
|
2291
2343
|
// Single expression
|
|
2292
2344
|
this.sideEffectOnly = prevSEO;
|
|
2293
|
-
|
|
2294
|
-
if (
|
|
2295
|
-
|
|
2296
|
-
|
|
2345
|
+
let result;
|
|
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')}; }`;
|
|
2353
|
+
else if (Array.isArray(body) && (noRetStmts.includes(body[0]) || loopStmts.includes(body[0]))) result = `{ ${this.generate(body, 'statement')}; }`;
|
|
2354
|
+
else if (sideEffectOnly) result = `{ ${this.generate(body, 'statement')}; return; }`;
|
|
2355
|
+
else result = `{ return ${this.generate(body, 'value')}; }`;
|
|
2356
|
+
this.scopeStack.pop();
|
|
2357
|
+
return result;
|
|
2297
2358
|
}
|
|
2298
2359
|
|
|
2299
2360
|
generateFunctionBody(body, params = [], sideEffectOnly = false) {
|
|
@@ -2368,34 +2429,10 @@ export class CodeGenerator {
|
|
|
2368
2429
|
if (iterators.length === 1) {
|
|
2369
2430
|
let [iterType, vars, iterable, stepOrOwn] = iterators[0];
|
|
2370
2431
|
if (iterType === 'for-in') {
|
|
2371
|
-
let
|
|
2372
|
-
|
|
2373
|
-
let noVar = va.length === 0;
|
|
2374
|
-
let [itemVar, indexVar] = noVar ? ['_i', null] : va;
|
|
2375
|
-
let ivp = ((this.is(itemVar, 'array') || this.is(itemVar, 'object')))
|
|
2376
|
-
? this.generateDestructuringPattern(itemVar) : itemVar;
|
|
2377
|
-
|
|
2378
|
-
if (step && step !== null) {
|
|
2379
|
-
let ih = Array.isArray(iterable) && iterable[0];
|
|
2380
|
-
if (ih instanceof String) ih = str(ih);
|
|
2381
|
-
let isRange = ih === '..' || ih === '...';
|
|
2382
|
-
if (isRange) {
|
|
2383
|
-
let isExcl = ih === '...';
|
|
2384
|
-
let [s, e] = iterable.slice(1);
|
|
2385
|
-
code += this.indent() + `for (let ${ivp} = ${this.generate(s, 'value')}; ${ivp} ${isExcl ? '<' : '<='} ${this.generate(e, 'value')}; ${ivp} += ${this.generate(step, 'value')}) {\n`;
|
|
2386
|
-
} else {
|
|
2387
|
-
let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
|
|
2388
|
-
let isNeg = this.is(step, '-', 1);
|
|
2389
|
-
code += isNeg
|
|
2390
|
-
? this.indent() + `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN} += ${stc}) {\n`
|
|
2391
|
-
: this.indent() + `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN} += ${stc}) {\n`;
|
|
2392
|
-
this.indentLevel++;
|
|
2393
|
-
if (!noVar) code += this.indent() + `const ${ivp} = ${ic}[${idxN}];\n`;
|
|
2394
|
-
}
|
|
2395
|
-
} else {
|
|
2396
|
-
code += this.indent() + `for (const ${ivp} of ${this.generate(iterable, 'value')}) {\n`;
|
|
2397
|
-
}
|
|
2432
|
+
let { header, setup } = this._forInHeader(vars, iterable, stepOrOwn);
|
|
2433
|
+
code += this.indent() + header + ' {\n';
|
|
2398
2434
|
this.indentLevel++;
|
|
2435
|
+
if (setup) code += this.indent() + setup + '\n';
|
|
2399
2436
|
if (guards && guards.length > 0) {
|
|
2400
2437
|
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2401
2438
|
this.indentLevel++;
|
|
@@ -2412,155 +2449,51 @@ export class CodeGenerator {
|
|
|
2412
2449
|
|
|
2413
2450
|
generateComprehensionAsLoop(expr, iterators, guards) {
|
|
2414
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
|
+
|
|
2415
2466
|
if (iterators.length === 1) {
|
|
2416
2467
|
let [iterType, vars, iterable, stepOrOwn] = iterators[0];
|
|
2417
2468
|
|
|
2418
2469
|
if (iterType === 'for-in') {
|
|
2419
|
-
let
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
? this.generateDestructuringPattern(itemVar) : itemVar;
|
|
2425
|
-
|
|
2426
|
-
if (step && step !== null) {
|
|
2427
|
-
let ih = Array.isArray(iterable) && iterable[0];
|
|
2428
|
-
if (ih instanceof String) ih = str(ih);
|
|
2429
|
-
let isRange = ih === '..' || ih === '...';
|
|
2430
|
-
if (isRange) {
|
|
2431
|
-
let isExcl = ih === '...';
|
|
2432
|
-
let [s, e] = iterable.slice(1);
|
|
2433
|
-
code += `for (let ${ivp} = ${this.generate(s, 'value')}; ${ivp} ${isExcl ? '<' : '<='} ${this.generate(e, 'value')}; ${ivp} += ${this.generate(step, 'value')}) `;
|
|
2434
|
-
} else {
|
|
2435
|
-
let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
|
|
2436
|
-
let isNeg = this.is(step, '-', 1);
|
|
2437
|
-
let isMinus1 = isNeg && (step[1] === '1' || step[1] === 1 || str(step[1]) === '1');
|
|
2438
|
-
let isPlus1 = !isNeg && (step === '1' || step === 1 || str(step) === '1');
|
|
2439
|
-
if (isMinus1) code += `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN}--) `;
|
|
2440
|
-
else if (isPlus1) code += `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN}++) `;
|
|
2441
|
-
else if (isNeg) code += `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN} += ${stc}) `;
|
|
2442
|
-
else code += `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN} += ${stc}) `;
|
|
2443
|
-
code += '{\n';
|
|
2444
|
-
this.indentLevel++;
|
|
2445
|
-
if (!noVar) code += this.indent() + `const ${ivp} = ${ic}[${idxN}];\n`;
|
|
2446
|
-
}
|
|
2447
|
-
if (guards?.length) {
|
|
2448
|
-
if (!isRange) code += this.indent();
|
|
2449
|
-
code += '{\n'; this.indentLevel++;
|
|
2450
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2451
|
-
this.indentLevel++;
|
|
2452
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2453
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2454
|
-
this.indentLevel--; code += this.indent() + '}';
|
|
2455
|
-
} else {
|
|
2456
|
-
if (!isRange) code += this.indent();
|
|
2457
|
-
code += '{\n'; this.indentLevel++;
|
|
2458
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2459
|
-
this.indentLevel--; code += this.indent() + '}';
|
|
2460
|
-
}
|
|
2461
|
-
if (!isRange) { this.indentLevel--; code += '\n' + this.indent() + '}'; }
|
|
2462
|
-
return code;
|
|
2463
|
-
}
|
|
2464
|
-
|
|
2465
|
-
if (indexVar) {
|
|
2466
|
-
let ic = this.generate(iterable, 'value');
|
|
2467
|
-
code += `for (let ${indexVar} = 0; ${indexVar} < ${ic}.length; ${indexVar}++) `;
|
|
2468
|
-
code += '{\n'; this.indentLevel++;
|
|
2469
|
-
code += this.indent() + `const ${ivp} = ${ic}[${indexVar}];\n`;
|
|
2470
|
-
} else {
|
|
2471
|
-
code += `for (const ${ivp} of ${this.generate(iterable, 'value')}) `;
|
|
2472
|
-
if (guards?.length) {
|
|
2473
|
-
code += '{\n'; this.indentLevel++;
|
|
2474
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2475
|
-
this.indentLevel++;
|
|
2476
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2477
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2478
|
-
this.indentLevel--; code += this.indent() + '}';
|
|
2479
|
-
} else {
|
|
2480
|
-
code += '{\n'; this.indentLevel++;
|
|
2481
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2482
|
-
this.indentLevel--; code += this.indent() + '}';
|
|
2483
|
-
}
|
|
2484
|
-
return code;
|
|
2485
|
-
}
|
|
2486
|
-
|
|
2487
|
-
// Fall through for indexVar case
|
|
2488
|
-
if (guards?.length) {
|
|
2489
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2490
|
-
this.indentLevel++;
|
|
2491
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2492
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2493
|
-
} else {
|
|
2494
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2495
|
-
}
|
|
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();
|
|
2496
2475
|
this.indentLevel--;
|
|
2497
2476
|
code += this.indent() + '}';
|
|
2498
2477
|
return code;
|
|
2499
2478
|
}
|
|
2500
2479
|
|
|
2501
2480
|
if (iterType === 'for-as') {
|
|
2502
|
-
let
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
code += '{\n'; this.indentLevel++;
|
|
2509
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2510
|
-
this.indentLevel++;
|
|
2511
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2512
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2513
|
-
this.indentLevel--; code += this.indent() + '}';
|
|
2514
|
-
} else {
|
|
2515
|
-
code += '{\n'; this.indentLevel++;
|
|
2516
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2517
|
-
this.indentLevel--; code += this.indent() + '}';
|
|
2518
|
-
}
|
|
2481
|
+
let { header } = this._forAsHeader(vars, iterable, stepOrOwn);
|
|
2482
|
+
code += header + ' {\n';
|
|
2483
|
+
this.indentLevel++;
|
|
2484
|
+
emitBody();
|
|
2485
|
+
this.indentLevel--;
|
|
2486
|
+
code += this.indent() + '}';
|
|
2519
2487
|
return code;
|
|
2520
2488
|
}
|
|
2521
2489
|
|
|
2522
2490
|
if (iterType === 'for-of') {
|
|
2523
|
-
let
|
|
2524
|
-
|
|
2525
|
-
let own = stepOrOwn;
|
|
2526
|
-
let oc = this.generate(iterable, 'value');
|
|
2527
|
-
code += `for (const ${kv} in ${oc}) {\n`;
|
|
2491
|
+
let { header, own, vv, oc, kvp } = this._forOfHeader(vars, iterable, stepOrOwn);
|
|
2492
|
+
code += header + ' {\n';
|
|
2528
2493
|
this.indentLevel++;
|
|
2529
|
-
if (own
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
} else if (own && vv && guards?.length) {
|
|
2533
|
-
code += this.indent() + `if (Object.hasOwn(${oc}, ${kv})) {\n`;
|
|
2534
|
-
this.indentLevel++;
|
|
2535
|
-
code += this.indent() + `const ${vv} = ${oc}[${kv}];\n`;
|
|
2536
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2537
|
-
this.indentLevel++;
|
|
2538
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2539
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2540
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2541
|
-
} else if (own && vv) {
|
|
2542
|
-
code += this.indent() + `if (Object.hasOwn(${oc}, ${kv})) {\n`;
|
|
2543
|
-
this.indentLevel++;
|
|
2544
|
-
code += this.indent() + `const ${vv} = ${oc}[${kv}];\n`;
|
|
2545
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2546
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2547
|
-
} else if (vv && guards?.length) {
|
|
2548
|
-
code += this.indent() + `const ${vv} = ${oc}[${kv}];\n`;
|
|
2549
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2550
|
-
this.indentLevel++;
|
|
2551
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2552
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2553
|
-
} else if (vv) {
|
|
2554
|
-
code += this.indent() + `const ${vv} = ${oc}[${kv}];\n`;
|
|
2555
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2556
|
-
} else if (guards?.length) {
|
|
2557
|
-
code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
|
|
2558
|
-
this.indentLevel++;
|
|
2559
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2560
|
-
this.indentLevel--; code += this.indent() + '}\n';
|
|
2561
|
-
} else {
|
|
2562
|
-
code += this.indent() + this.generate(expr, 'statement') + ';\n';
|
|
2563
|
-
}
|
|
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();
|
|
2564
2497
|
this.indentLevel--;
|
|
2565
2498
|
code += this.indent() + '}';
|
|
2566
2499
|
return code;
|
|
@@ -3043,6 +2976,7 @@ const __primitiveCoercion = {
|
|
|
3043
2976
|
};
|
|
3044
2977
|
|
|
3045
2978
|
function __state(initialValue) {
|
|
2979
|
+
if (initialValue != null && typeof initialValue === 'object' && typeof initialValue.read === 'function') return initialValue;
|
|
3046
2980
|
let value = initialValue;
|
|
3047
2981
|
const subscribers = new Set();
|
|
3048
2982
|
let notifying = false;
|
|
@@ -3142,14 +3076,19 @@ function __effect(fn) {
|
|
|
3142
3076
|
dependencies: new Set(),
|
|
3143
3077
|
|
|
3144
3078
|
run() {
|
|
3079
|
+
if (effect._cleanup) { effect._cleanup(); effect._cleanup = null; }
|
|
3145
3080
|
for (const dep of effect.dependencies) dep.delete(effect);
|
|
3146
3081
|
effect.dependencies.clear();
|
|
3147
3082
|
const prev = __currentEffect;
|
|
3148
3083
|
__currentEffect = effect;
|
|
3149
|
-
try {
|
|
3084
|
+
try {
|
|
3085
|
+
const result = fn();
|
|
3086
|
+
if (typeof result === 'function') effect._cleanup = result;
|
|
3087
|
+
} finally { __currentEffect = prev; }
|
|
3150
3088
|
},
|
|
3151
3089
|
|
|
3152
3090
|
dispose() {
|
|
3091
|
+
if (effect._cleanup) { effect._cleanup(); effect._cleanup = null; }
|
|
3153
3092
|
for (const dep of effect.dependencies) dep.delete(effect);
|
|
3154
3093
|
effect.dependencies.clear();
|
|
3155
3094
|
}
|