feat: top-level return

This commit is contained in:
cha0s 2024-06-18 22:29:40 -05:00
parent 8ba0c0681f
commit 9b2dab91ac
4 changed files with 49 additions and 2 deletions

View File

@ -10,6 +10,7 @@ import {
isIdentifier, isIdentifier,
isIfStatement, isIfStatement,
isObjectPattern, isObjectPattern,
isReturnStatement,
isVariableDeclarator, isVariableDeclarator,
isWhileStatement, isWhileStatement,
} from '@/swcx/types.js'; } from '@/swcx/types.js';
@ -206,7 +207,10 @@ export default class Sandbox {
if (!child) { if (!child) {
continue; continue;
} }
yield* this.execute(child, node); const result = yield* this.execute(child, node);
if (result) {
return result;
}
} }
// Loops... // Loops...
if (isForStatement(node)) { if (isForStatement(node)) {
@ -230,6 +234,10 @@ export default class Sandbox {
} }
} }
} while (loop); } while (loop);
// Top-level return.
if (isReturnStatement(node)) {
return !node.argument ? {value: undefined} : this.evaluate(node.argument);
}
if (isExpressionStatement(node)) { if (isExpressionStatement(node)) {
yield this.evaluate(node.expression); yield this.evaluate(node.expression);
} }

View File

@ -151,7 +151,7 @@ test('evaluates loops', async () => {
}); });
}); });
test('retuns undefined for nonexistent variables in scope', async () => { test('evaluates undefined for nonexistent variables in scope', async () => {
const sandbox = new Sandbox( const sandbox = new Sandbox(
await parse(` await parse(`
const x = y const x = y
@ -164,6 +164,36 @@ test('retuns undefined for nonexistent variables in scope', async () => {
}); });
}); });
test('returns values at the top level', async () => {
let sandbox;
sandbox = new Sandbox(
await parse(`
x = 16
y = 4
return [x * 3, y * 3]
x = 32
y = 8
`, {allowReturnOutsideFunction: true}),
);
const {value: {value}} = sandbox.run();
expect(value)
.to.deep.equal([48, 12]);
expect(sandbox.context)
.to.deep.equal({x: 16, y: 4});
sandbox = new Sandbox(
await parse(`
x = 16
y = 4
return
x = 32
y = 8
`, {allowReturnOutsideFunction: true}),
);
sandbox.run();
expect(sandbox.context)
.to.deep.equal({x: 16, y: 4});
});
test('sets variables in global scope', async () => { test('sets variables in global scope', async () => {
const sandbox = new Sandbox( const sandbox = new Sandbox(
await parse(` await parse(`

View File

@ -29,6 +29,7 @@ export const TRAVERSAL_PATH = {
OptionalChainingExpression: ['base'], OptionalChainingExpression: ['base'],
ParenthesisExpression: ['expression'], ParenthesisExpression: ['expression'],
RegExpLiteral: [], RegExpLiteral: [],
ReturnStatement: ['argument'],
StringLiteral: [], StringLiteral: [],
UnaryExpression: ['argument'], UnaryExpression: ['argument'],
UpdateExpression: ['argument'], UpdateExpression: ['argument'],

View File

@ -92,6 +92,14 @@ export function isObjectPattern(node) {
return true; return true;
} }
export function isReturnStatement(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'ReturnStatement') {
return false;
}
return true;
}
export function isSpreadElement(node) { export function isSpreadElement(node) {
/* v8 ignore next 3 */ /* v8 ignore next 3 */
if (!node || node.type !== 'SpreadElement') { if (!node || node.type !== 'SpreadElement') {