feat: new
This commit is contained in:
parent
ce802a8499
commit
b9985a573d
|
@ -35,6 +35,8 @@ export default function evaluate(node, {scope} = {}) {
|
||||||
return evaluators.binary(node, {evaluate, scope});
|
return evaluators.binary(node, {evaluate, scope});
|
||||||
case 'MemberExpression':
|
case 'MemberExpression':
|
||||||
return evaluators.member(node, {evaluate, scope});
|
return evaluators.member(node, {evaluate, scope});
|
||||||
|
case 'NewExpression':
|
||||||
|
return evaluators.new(node, {evaluate, scope});
|
||||||
case 'ObjectExpression':
|
case 'ObjectExpression':
|
||||||
return evaluators.object(node, {evaluate, scope});
|
return evaluators.object(node, {evaluate, scope});
|
||||||
case 'UnaryExpression':
|
case 'UnaryExpression':
|
||||||
|
|
21
app/astride/evaluators/new.js
Normal file
21
app/astride/evaluators/new.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
export default function(node, {evaluate, scope}) {
|
||||||
|
let asyncArgs = false;
|
||||||
|
const args = [];
|
||||||
|
for (let i = 0; i < node.arguments.length; i++) {
|
||||||
|
const arg = node.arguments[i];
|
||||||
|
const {async, value} = evaluate(arg, {scope});
|
||||||
|
asyncArgs ||= async;
|
||||||
|
args.push(value);
|
||||||
|
}
|
||||||
|
const {callee} = node;
|
||||||
|
const {async, value} = evaluate(callee, {scope});
|
||||||
|
if (asyncArgs || async) {
|
||||||
|
return {
|
||||||
|
async: true,
|
||||||
|
value: Promise
|
||||||
|
.all([value, Promise.all(args)])
|
||||||
|
.then(([callee, args]) => new callee(...args)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {value: new value(...args)};
|
||||||
|
}
|
48
app/astride/evaluators/new.test.js
Normal file
48
app/astride/evaluators/new.test.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import {expect, test} from 'vitest';
|
||||||
|
|
||||||
|
import evaluate from '@/astride/evaluate.js';
|
||||||
|
import expression from '@/astride/test/expression.js';
|
||||||
|
|
||||||
|
const scopeTest = test.extend({
|
||||||
|
scope: async ({}, use) => {
|
||||||
|
await use({
|
||||||
|
S: {O: {}},
|
||||||
|
get(k) { return this.S[k]; },
|
||||||
|
set(k, v) { return this.S[k] = v; }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
class C {
|
||||||
|
foo = 'bar';
|
||||||
|
constructor(a, b) {
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scopeTest('creates instances', async ({scope}) => {
|
||||||
|
scope.set('C', C);
|
||||||
|
const evaluated = evaluate(await expression('new C(1, 2)'), {scope});
|
||||||
|
expect(evaluated.value)
|
||||||
|
.to.deep.include({
|
||||||
|
a: 1,
|
||||||
|
b: 2,
|
||||||
|
foo: 'bar',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
scopeTest('creates instances with async dependencies', async ({scope}) => {
|
||||||
|
scope.set('C', C);
|
||||||
|
scope.set('a', Promise.resolve(1));
|
||||||
|
scope.set('b', Promise.resolve(2));
|
||||||
|
const evaluated = evaluate(await expression('new C(await a, await b)'), {scope});
|
||||||
|
expect(evaluated.async)
|
||||||
|
.to.equal(true);
|
||||||
|
expect(await evaluated.value)
|
||||||
|
.to.deep.include({
|
||||||
|
a: 1,
|
||||||
|
b: 2,
|
||||||
|
foo: 'bar',
|
||||||
|
});
|
||||||
|
});
|
|
@ -150,6 +150,7 @@ export default class Sandbox {
|
||||||
case 'ObjectExpression':
|
case 'ObjectExpression':
|
||||||
case 'Identifier':
|
case 'Identifier':
|
||||||
case 'MemberExpression':
|
case 'MemberExpression':
|
||||||
|
case 'NewExpression':
|
||||||
case 'UpdateExpression': {
|
case 'UpdateExpression': {
|
||||||
result = this.evaluateToResult(node);
|
result = this.evaluateToResult(node);
|
||||||
if (result.yield) {
|
if (result.yield) {
|
||||||
|
|
|
@ -121,6 +121,39 @@ test('runs arbitrary number of ops', async () => {
|
||||||
.to.equal(150);
|
.to.equal(150);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('instantiates', async () => {
|
||||||
|
const sandbox = new Sandbox(
|
||||||
|
await parse(`
|
||||||
|
const x = new C(1, 2);
|
||||||
|
const y = new C(await a, await b);
|
||||||
|
`),
|
||||||
|
{
|
||||||
|
a: Promise.resolve(1),
|
||||||
|
b: Promise.resolve(2),
|
||||||
|
C: class {
|
||||||
|
foo = 'bar';
|
||||||
|
constructor(a, b) {
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
await finish(sandbox);
|
||||||
|
expect(sandbox.context.x)
|
||||||
|
.to.deep.include({
|
||||||
|
a: 1,
|
||||||
|
b: 2,
|
||||||
|
foo: 'bar',
|
||||||
|
});
|
||||||
|
expect(sandbox.context.y)
|
||||||
|
.to.deep.include({
|
||||||
|
a: 1,
|
||||||
|
b: 2,
|
||||||
|
foo: 'bar',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('deletes', async () => {
|
test('deletes', async () => {
|
||||||
const sandbox = new Sandbox(
|
const sandbox = new Sandbox(
|
||||||
await parse(`
|
await parse(`
|
||||||
|
|
|
@ -16,6 +16,7 @@ export const TRAVERSAL_PATH = {
|
||||||
Identifier: [],
|
Identifier: [],
|
||||||
IfStatement: ['alternate', 'consequent', 'test'],
|
IfStatement: ['alternate', 'consequent', 'test'],
|
||||||
MemberExpression: ['object', 'property'],
|
MemberExpression: ['object', 'property'],
|
||||||
|
NewExpression: ['arguments', 'callee'],
|
||||||
Literal: [],
|
Literal: [],
|
||||||
LogicalExpression: ['left', 'right'],
|
LogicalExpression: ['left', 'right'],
|
||||||
ObjectExpression: ['properties'],
|
ObjectExpression: ['properties'],
|
||||||
|
|
Loading…
Reference in New Issue
Block a user