Support for await and yield in pipelines (#9401)

This commit is contained in:
Thiago Arrais 2019-02-04 17:58:46 -03:00 committed by Nicolò Ribaudo
parent 65cbbc1ef8
commit fe71154626
45 changed files with 300 additions and 110 deletions

View File

@ -1,54 +1,52 @@
import { types as t } from "@babel/core"; import { types as t } from "@babel/core";
const minimalVisitor = { const minimalVisitor = {
BinaryExpression: { BinaryExpression(path) {
exit(path) { const { scope } = path;
const { scope } = path; const { node } = path;
const { node } = path; const { operator, left } = node;
const { operator, left } = node; let { right } = node;
let { right } = node; if (operator !== "|>") return;
if (operator !== "|>") return;
let optimizeArrow = let optimizeArrow =
t.isArrowFunctionExpression(right) && t.isArrowFunctionExpression(right) &&
t.isExpression(right.body) && t.isExpression(right.body) &&
!right.async && !right.async &&
!right.generator; !right.generator;
let param; let param;
if (optimizeArrow) { if (optimizeArrow) {
const { params } = right; const { params } = right;
if (params.length === 1 && t.isIdentifier(params[0])) { if (params.length === 1 && t.isIdentifier(params[0])) {
param = params[0]; param = params[0];
} else if (params.length > 0) { } else if (params.length > 0) {
optimizeArrow = false; optimizeArrow = false;
}
} else if (t.isIdentifier(right, { name: "eval" })) {
right = t.sequenceExpression([t.numericLiteral(0), right]);
} }
} else if (t.isIdentifier(right, { name: "eval" })) {
right = t.sequenceExpression([t.numericLiteral(0), right]);
}
if (optimizeArrow && !param) { if (optimizeArrow && !param) {
// Arrow function with 0 arguments // Arrow function with 0 arguments
path.replaceWith(t.sequenceExpression([left, right.body])); path.replaceWith(t.sequenceExpression([left, right.body]));
return; return;
} }
const placeholder = scope.generateUidIdentifierBasedOnNode(param || left); const placeholder = scope.generateUidIdentifierBasedOnNode(param || left);
scope.push({ id: placeholder }); scope.push({ id: placeholder });
if (param) { if (param) {
path.get("right").scope.rename(param.name, placeholder.name); path.get("right").scope.rename(param.name, placeholder.name);
} }
const call = optimizeArrow const call = optimizeArrow
? right.body ? right.body
: t.callExpression(right, [t.cloneNode(placeholder)]); : t.callExpression(right, [t.cloneNode(placeholder)]);
path.replaceWith( path.replaceWith(
t.sequenceExpression([ t.sequenceExpression([
t.assignmentExpression("=", t.cloneNode(placeholder), left), t.assignmentExpression("=", t.cloneNode(placeholder), left),
call, call,
]), ]),
); );
},
}, },
}; };

View File

@ -1,41 +1,47 @@
import { types as t } from "@babel/core"; import { types as t } from "@babel/core";
import minimalVisitor from "./minimalVisitor";
const updateTopicReferenceVisitor = { const updateTopicReferenceVisitor = {
PipelinePrimaryTopicReference(path) { PipelinePrimaryTopicReference(path) {
path.replaceWith(this.topicId); path.replaceWith(this.topicId);
}, },
AwaitExpression(path) {
throw path.buildCodeFrameError(
"await is not supported inside pipeline expressions yet",
);
},
YieldExpression(path) {
throw path.buildCodeFrameError(
"yield is not supported inside pipeline expressions yet",
);
},
PipelineTopicExpression(path) { PipelineTopicExpression(path) {
path.skip(); path.skip();
}, },
}; };
const smartVisitor = { const smartVisitor = {
...minimalVisitor, BinaryExpression(path) {
PipelineTopicExpression(path) { const { scope } = path;
const topicId = path.scope.generateUidIdentifier("topic"); const { node } = path;
const { operator, left, right } = node;
if (operator !== "|>") return;
path.traverse(updateTopicReferenceVisitor, { topicId }); const placeholder = scope.generateUidIdentifierBasedOnNode(left);
scope.push({ id: placeholder });
const arrowFunctionExpression = t.arrowFunctionExpression( let call;
[topicId], if (t.isPipelineTopicExpression(right)) {
path.node.expression, path
.get("right")
.traverse(updateTopicReferenceVisitor, { topicId: placeholder });
call = right.expression;
} else {
// PipelineBareFunction
let callee = right.callee;
if (t.isIdentifier(callee, { name: "eval" })) {
callee = t.sequenceExpression([t.numericLiteral(0), callee]);
}
call = t.callExpression(callee, [t.cloneNode(placeholder)]);
}
path.replaceWith(
t.sequenceExpression([
t.assignmentExpression("=", t.cloneNode(placeholder), left),
call,
]),
); );
path.replaceWith(arrowFunctionExpression);
},
PipelineBareFunction(path) {
path.replaceWith(path.node.callee);
}, },
}; };

View File

@ -1,6 +1,6 @@
var _ref, _ref2, _sum; var _sum, _ref, _ref2;
var result = (_sum = (_ref2 = (_ref = [5, 10], _ref.map(x => x * 2)), _ref2.reduce((a, b) => a + b)), _sum + 1); var result = (_sum = (_ref = (_ref2 = [5, 10], _ref2.map(x => x * 2)), _ref.reduce((a, b) => a + b)), _sum + 1);
expect(result).toBe(31); expect(result).toBe(31);
var inc = x => x + 1; var inc = x => x + 1;
@ -8,7 +8,7 @@ var inc = x => x + 1;
var double = x => x * 2; var double = x => x * 2;
var result2 = [4, 9].map(x => { var result2 = [4, 9].map(x => {
var _x, _ref3; var _ref3, _x;
return _ref3 = (_x = x, inc(_x)), double(_ref3); return _ref3 = (_x = x, inc(_x)), double(_ref3);
}); });

View File

@ -8,6 +8,6 @@ var result = 1
|> (async (x) => await x + 1) |> (async (x) => await x + 1)
|> then((x) => x + 1); |> then((x) => x + 1);
result.then(val => { return result.then(val => {
expect(val).toBe(3); expect(val).toBe(3);
}); });

View File

@ -1,4 +1,4 @@
var _, _ref; var _ref, _;
function then(fn) { function then(fn) {
return async value => { return async value => {

View File

@ -1,4 +1,4 @@
var _, _ref; var _ref, _;
var inc = x => x + 1; var inc = x => x + 1;

View File

@ -1,4 +1,4 @@
var _a, _ref; var _ref, _a;
var a = 1, var a = 1,
b = 2, b = 2,

View File

@ -1,3 +1,6 @@
{ {
"plugins": [["proposal-pipeline-operator", { "proposal": "minimal" }]] "plugins": [["proposal-pipeline-operator", { "proposal": "minimal" }]],
"parserOpts": {
"allowReturnOutsideFunction": true
}
} }

View File

@ -1,4 +1,4 @@
var _ref, _, _ref2; var _ref, _ref2, _;
var inc = x => x + 1; var inc = x => x + 1;

View File

@ -0,0 +1,13 @@
const triple = (x) => x * 3;
async function myFunction(n) {
return n
|> Math.abs
|> Promise.resolve(#)
|> await #
|> triple;
}
return myFunction(-7).then(result => {
expect(result).toBe(21);
});

View File

@ -0,0 +1,6 @@
{
"parserOpts": {
"allowReturnOutsideFunction": true
},
"minNodeVersion": "8.0.0"
}

View File

@ -0,0 +1,9 @@
{
"plugins": [
["proposal-pipeline-operator", { "proposal": "smart" }],
"transform-arrow-functions"
],
"parserOpts": {
"allowReturnOutsideFunction": true
}
}

View File

@ -0,0 +1,13 @@
const triple = (x) => x * 3;
async function myFunction(n) {
return n
|> Math.abs
|> Promise.resolve(#)
|> await #
|> triple;
}
return myFunction(-7).then(result => {
expect(result).toBe(21);
});

View File

@ -1,5 +1,6 @@
async function myFunction() { async function myFunction(n) {
const value = -5.9 return n
|> abs |> Math.abs
|> await Math.floor(#); |> Promise.resolve(#)
|> await #;
} }

View File

@ -1,4 +1,9 @@
{ {
"throws": "await is not supported inside pipeline expressions yet", "plugins": [
"plugins": [["proposal-pipeline-operator", { "proposal": "smart" }]] ["proposal-pipeline-operator", { "proposal": "smart" }]
],
"parserOpts": {
"allowReturnOutsideFunction": true
},
"minNodeVersion": "8.0.0"
} }

View File

@ -0,0 +1,5 @@
async function myFunction(n) {
var _ref, _ref2, _n;
return _ref = (_ref2 = (_n = n, Math.abs(_n)), Promise.resolve(_ref2)), await _ref;
}

View File

@ -1,4 +1,4 @@
var _ref, _ref2; var _ref, _ref2;
const abs = Math.abs; const abs = Math.abs;
const value = (_ref2 = (_ref = -5.9, abs(_ref)), Math.floor(_ref2)); const value = (_ref = (_ref2 = -5.9, abs(_ref2)), Math.floor(_ref));

View File

@ -1,3 +1,3 @@
var _topic2, _topic4, _topic6; var _ref, _ref2, _;
var result = (_topic6 = (_topic4 = (_topic2 = 5, _topic2 + 1), _topic4 + _topic4), Math.pow((x => x * 7)(_topic6), 2)); var result = (_ref = (_ref2 = (_ = 5, _ + 1), _ref2 + _ref2), Math.pow((x => x * 7)(_ref), 2));

View File

@ -0,0 +1,5 @@
let sum = 0;
for (var i = 0 |> #; i <= 10; i++)
sum = sum + i;
expect(sum).toBe(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1)

View File

@ -0,0 +1,5 @@
let sum = 0;
for (var i = 0 |> #; i <= 10; i++)
sum = sum + i;
expect(sum).toBe(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1)

View File

@ -0,0 +1,9 @@
let sum = 0;
for (var i = (_ = 0, _); i <= 10; i++) {
var _;
sum = sum + i;
}
expect(sum).toBe(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1);

View File

@ -0,0 +1,6 @@
let sum = 0;
for (var i = 0; i <= 10; i = i |> # + 1) {
sum = sum + i;
}
expect(sum).toBe(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1)

View File

@ -0,0 +1,6 @@
let sum = 0;
for (var i = 0; i <= 10; i = i |> # + 1) {
sum = sum + i;
}
expect(sum).toBe(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1)

View File

@ -0,0 +1,9 @@
let sum = 0;
for (var i = 0; i <= 10; i = (_i = i, _i + 1)) {
var _i;
sum = sum + i;
}
expect(sum).toBe(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1);

View File

@ -0,0 +1,5 @@
let sum = 0;
for (var i = 0; i |> # <= 10; i++)
sum = sum + i;
expect(sum).toBe(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1)

View File

@ -0,0 +1,5 @@
let sum = 0;
for (var i = 0; (i |> # <= 10); ++i)
sum = sum + i;
expect(sum).toBe(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1)

View File

@ -0,0 +1,9 @@
let sum = 0;
for (var i = 0; _i = i, _i <= 10; ++i) {
var _i;
sum = sum + i;
}
expect(sum).toBe(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1);

View File

@ -0,0 +1,4 @@
if (v |> e |> f)
g() |> h(#, #+1) |> i;
else
j();

View File

@ -0,0 +1,3 @@
var _ref, _v, _ref2, _g;
if (_ref = (_v = v, e(_v)), f(_ref)) _ref2 = (_g = g(), h(_g, _g + 1)), i(_ref2);else j();

View File

@ -0,0 +1,7 @@
(function() {
'use strict';
var result = '(function() { return this; })()'
|> eval;
expect(result).not.toBeUndefined();
})();

View File

@ -0,0 +1,7 @@
(function() {
'use strict';
var result = '(function() { return this; })()'
|> eval;
expect(result).not.toBeUndefined();
})();

View File

@ -0,0 +1,8 @@
(function () {
'use strict';
var _functionReturn;
var result = (_functionReturn = '(function() { return this; })()', (0, eval)(_functionReturn));
expect(result).not.toBeUndefined();
})();

View File

@ -1,7 +1,7 @@
var _topic2, _topic8; var _ref, _;
var result = (_topic8 = (_topic2 = 5, Math.pow(_topic2, 2)), [10, 20, 30, 40, 50].filter(n => { var result = (_ref = (_ = 5, Math.pow(_, 2)), [10, 20, 30, 40, 50].filter(n => {
var _topic5, _topic7; var _ref2, _ref3;
return _topic7 = (_topic5 = _topic8, n > _topic5), !_topic7; return _ref2 = (_ref3 = _ref, n > _ref3), !_ref2;
})); }));

View File

@ -1,10 +1,10 @@
var _ref, _topic2, _ref2; var _ref, _ref2, _ref3;
function area(rect) { function area(rect) {
return rect.width * rect.height; return rect.width * rect.height;
} }
const result = (_ref2 = (_topic2 = (_ref = -5, Math.abs(_ref)), { const result = (_ref = (_ref2 = (_ref3 = -5, Math.abs(_ref3)), {
width: _topic2, width: _ref2,
height: _topic2 + 3 height: _ref2 + 3
}), area(_ref2)); }), area(_ref));

View File

@ -1,3 +1,8 @@
{ {
"plugins": [["proposal-pipeline-operator", { "proposal": "smart" }]] "plugins": [
["proposal-pipeline-operator", { "proposal": "smart" }]
],
"parserOpts": {
"allowReturnOutsideFunction": true
}
} }

View File

@ -1 +1 @@
v |> #.method |> f; v |> #.method() |> f;

View File

@ -1,3 +1,3 @@
var _topic2, _ref; var _ref, _v;
_ref = (_topic2 = v, _topic2.method), f(_ref); _ref = (_v = v, _v.method()), f(_ref);

View File

@ -1,7 +1,7 @@
var _ref, _topic2, _topic4, _ref2; var _ref, _ref2, _ref3, _ref4;
const result = (_ref2 = (_topic4 = (_topic2 = (_ref = -2.2 // -2.2 const result = (_ref = (_ref2 = (_ref3 = (_ref4 = -2.2 // -2.2
, Math.floor(_ref) // -3 , Math.floor(_ref4) // -3
), () => Math.pow(_topic2, 5) // () => -243 ), () => Math.pow(_ref3, 5) // () => -243
), _topic4() // -243 ), _ref2() // -243
), Math.sign(_ref2)); // -1 ), Math.sign(_ref)); // -1

View File

@ -0,0 +1,7 @@
let i = 0
let sum = 0
while (i |> (i = # + 1) |> # <= 10)
sum = sum + i;
expect(sum).toBe(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1)

View File

@ -0,0 +1,7 @@
let i = 0
let sum = 0
while (i |> (i = # + 1) |> # <= 10)
sum = sum + i;
expect(sum).toBe(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1)

View File

@ -0,0 +1,10 @@
let i = 0;
let sum = 0;
while (_ref = (_i = i, i = _i + 1), _ref <= 10) {
var _ref, _i;
sum = sum + i;
}
expect(sum).toBe(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1);

View File

@ -0,0 +1,13 @@
function *myGenerator(n) {
return n
|> (yield #)
|> Math.abs;
}
const myIterator = myGenerator(15);
const yieldedValue = myIterator.next().value;
const returnedValue = myIterator.next(-30).value;
expect(yieldedValue).toBe(15);
expect(returnedValue).toBe(30);

View File

@ -1,5 +1,5 @@
function *myGenerator() { function *myGenerator(n) {
return v return n
|> (yield #) |> (yield #)
|> g; |> Math.abs;
} }

View File

@ -1,4 +0,0 @@
{
"throws": "yield is not supported inside pipeline expressions yet",
"plugins": [["proposal-pipeline-operator", { "proposal": "smart" }]]
}

View File

@ -0,0 +1,5 @@
function* myGenerator(n) {
var _ref, _n;
return _ref = (_n = n, yield _n), Math.abs(_ref);
}