From 63074861e1f19c10fcfeaed2d862dc8fb3ca7b99 Mon Sep 17 00:00:00 2001 From: FrigoEU Date: Mon, 1 Jan 2018 22:54:09 +0100 Subject: [PATCH 1/4] Don't shadow variables when making unsaturated uncurried function calls --- src/uncurry.js | 13 ++++++-- unittest/uncurry.js | 74 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 unittest/uncurry.js diff --git a/src/uncurry.js b/src/uncurry.js index 9f24c6a..27a4de6 100644 --- a/src/uncurry.js +++ b/src/uncurry.js @@ -207,13 +207,22 @@ module.exports = function (babel) { for (var i = uncurried.params.length - 1; i >= args.length; --i) { // TODO make a copy of the params ? - pushAll(path, statements, flattened, uncurried.params[i]); + var params = uncurried.params[i].map(function(node){ + if (node.type === "Identifier"){ + var uid = path.scope.generateUidIdentifier(node.name); + return Object.assign({}, node, {name: uid.name}); + } else { + return node; + } + }); + + pushAll(path, statements, flattened, params); body = { type: "FunctionExpression", id: null, // TODO make a copy of the params ? - params: uncurried.params[i], + params: params, body: { type: "BlockStatement", body: [{ diff --git a/unittest/uncurry.js b/unittest/uncurry.js new file mode 100644 index 0000000..ffa4436 --- /dev/null +++ b/unittest/uncurry.js @@ -0,0 +1,74 @@ +const assert = require("assert"); +const $babel = require("babel-core"); +const uncurry = require("../src/uncurry.js"); +const fs = require("fs"); +const child_process = require("child_process"); +const util = require("util"); + +const testCases = [{ + codeIn: [ + "const sum = function (a){return function(b){return a + b}};", + "sum(5)(7);" + ].join("\n"), + result: [ + "const _sum_uncurried = function (a,b){return a + b;};", + "const sum = function (a){return function(b){return _sum_uncurried(a,b); }};", + "_sum_uncurried(5,7);" + ].join("\n") +}, { + codeIn: [ + "const sum = function(a){return function(b){return a + b}};", + "var b = 5;", + "const sum5 = sum(b);", + "sum5(7)" + ].join("\n"), + result: [ + "const _sum_uncurried = function (a, b) {", + " return a + b;", + "};", + + "const sum = function (a) {", + " return function (b) {", + " return _sum_uncurried(a, b);", + " };", + "};", + "var b = 5;", + // Current bug: the argument is named "b" and shadows the variable declaration above (var b = 5) + "const sum5 = function (_b) {", + " return _sum_uncurried(b, _b);", + "};", + "sum5(7);", + ].join("\n") +}]; + +testCases.forEach(async function(testCase){ + var result = $babel.transform(testCase.codeIn, { + babelrc: false, + code: true, + ast: true, + sourceMaps: false, + plugins: [ + [uncurry, {debug: true}] + ] + }); + + assert(strip(result.code) === strip(testCase.result), [ + "", + "", + "------ The resulting code:", + "", + result.code, + "", + "------ is not equal to:", + "", + testCase.result, + "", + "------ Stripped: ", + strip(result.code), + strip(testCase.result) + ].join("\n")); +}); + +function strip(string){ + return string.replace(/\s/g, "").replace(/;/g, ""); +} From 548ca1bbbf19603503f07d35162b71920c4c014d Mon Sep 17 00:00:00 2001 From: FrigoEU Date: Mon, 1 Jan 2018 22:56:05 +0100 Subject: [PATCH 2/4] Remove unnecessary comment --- src/uncurry.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uncurry.js b/src/uncurry.js index 27a4de6..f8b66e8 100644 --- a/src/uncurry.js +++ b/src/uncurry.js @@ -206,7 +206,6 @@ module.exports = function (babel) { var created = false; for (var i = uncurried.params.length - 1; i >= args.length; --i) { - // TODO make a copy of the params ? var params = uncurried.params[i].map(function(node){ if (node.type === "Identifier"){ var uid = path.scope.generateUidIdentifier(node.name); From 1e4f3854fd7fc2346898a50efd2d7c489672dfdc Mon Sep 17 00:00:00 2001 From: FrigoEU Date: Mon, 1 Jan 2018 23:03:26 +0100 Subject: [PATCH 3/4] Remove unnecessary imports --- unittest/uncurry.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/unittest/uncurry.js b/unittest/uncurry.js index ffa4436..49a5da5 100644 --- a/unittest/uncurry.js +++ b/unittest/uncurry.js @@ -1,9 +1,6 @@ const assert = require("assert"); const $babel = require("babel-core"); const uncurry = require("../src/uncurry.js"); -const fs = require("fs"); -const child_process = require("child_process"); -const util = require("util"); const testCases = [{ codeIn: [ From 1ae60a5d4229d93792ce0ab564cafbd4f96102bc Mon Sep 17 00:00:00 2001 From: FrigoEU Date: Tue, 2 Jan 2018 02:02:32 +0100 Subject: [PATCH 4/4] Fix self-recursive functions uncurrying badly --- src/uncurry.js | 2 +- src/util.js | 24 +++++++++ unittest/uncurry.js | 124 +++++++++++++++++++++++++++++++------------- 3 files changed, 113 insertions(+), 37 deletions(-) diff --git a/src/uncurry.js b/src/uncurry.js index f8b66e8..912f514 100644 --- a/src/uncurry.js +++ b/src/uncurry.js @@ -48,7 +48,7 @@ function makeUncurried(binding, path, id, top) { type: "FunctionExpression", id: null, params: flattened, - body: x.body, + body: $util.cloneDeep(x.body), loc: top.loc }, unique: true diff --git a/src/util.js b/src/util.js index eea0185..1412665 100644 --- a/src/util.js +++ b/src/util.js @@ -130,6 +130,30 @@ exports.print = function (node) { }).code; }; +// Stolen from babel-types +exports.cloneDeep = function cloneDeep(node) { + if (!node) return node; + const newNode = {}; + + Object.keys(node).forEach(key => { + if (key[0] === "_") return; + + let val = node[key]; + + if (val) { + if (val.type) { + val = cloneDeep(val); + } else if (Array.isArray(val)) { + val = val.map(cloneDeep); + } + } + + newNode[key] = val; + }); + + return newNode; +}; + // https://github.com/babel/babylon/blob/master/ast/spec.md // TODO Import ? diff --git a/unittest/uncurry.js b/unittest/uncurry.js index 49a5da5..a75db0e 100644 --- a/unittest/uncurry.js +++ b/unittest/uncurry.js @@ -3,53 +3,105 @@ const $babel = require("babel-core"); const uncurry = require("../src/uncurry.js"); const testCases = [{ - codeIn: [ - "const sum = function (a){return function(b){return a + b}};", - "sum(5)(7);" - ].join("\n"), - result: [ - "const _sum_uncurried = function (a,b){return a + b;};", - "const sum = function (a){return function(b){return _sum_uncurried(a,b); }};", - "_sum_uncurried(5,7);" - ].join("\n") + codeIn: function test(){ + const sum = function (a){ + return function(b){ + return a + b; + }; + }; + sum(5)(7); + }, + result: function test(){ + const _sum_uncurried = function (a,b){ + return a + b; + }; + const sum = function (a){ + return function(b){ + return _sum_uncurried(a,b); + }; + }; + _sum_uncurried(5,7); + } }, { - codeIn: [ - "const sum = function(a){return function(b){return a + b}};", - "var b = 5;", - "const sum5 = sum(b);", - "sum5(7)" - ].join("\n"), - result: [ - "const _sum_uncurried = function (a, b) {", - " return a + b;", - "};", + codeIn: function test(){ + const sum = function(a){ + return function(b){ + return a + b; + }; + }; + var b = 5; + const sum5 = sum(b); + sum5(7); + }, + result: function test(){ + const _sum_uncurried = function (a, b) { + return a + b; + }; - "const sum = function (a) {", - " return function (b) {", - " return _sum_uncurried(a, b);", - " };", - "};", - "var b = 5;", - // Current bug: the argument is named "b" and shadows the variable declaration above (var b = 5) - "const sum5 = function (_b) {", - " return _sum_uncurried(b, _b);", - "};", - "sum5(7);", - ].join("\n") + const sum = function (a) { + return function (b) { + return _sum_uncurried(a, b); + }; + }; + var b = 5; + const sum5 = function (_b) { + return _sum_uncurried(b, _b); + }; + sum5(7); + } +}, { + codeIn: function test(){ + var sum = function(a){ + return function(b){ + var sumInner = function(a){ + return function(b){ + return sum(a, b); + }; + }; + return sumInner(a)(b); + }; + }; + sum(5)(7); + }, + result: function test() { + const _sum_uncurried = function (a, b) { + const _sumInner_uncurried2 = function (a, b) { + return sum(a, b); + }; + + var sumInner = function (a) { + return function (b) { + return _sumInner_uncurried2(a, b); + }; + }; + return _sumInner_uncurried2(a, b); + }; + + var sum = function (a) { + return function (b) { + const _sumInner_uncurried = function (a, b) { + return sum(a, b); + }; + + return _sum_uncurried(a, b); + }; + }; + _sum_uncurried(5, 7); + } }]; testCases.forEach(async function(testCase){ - var result = $babel.transform(testCase.codeIn, { + var result = $babel.transform(testCase.codeIn.toString(), { babelrc: false, code: true, - ast: true, + ast: false, sourceMaps: false, plugins: [ [uncurry, {debug: true}] ] }); - assert(strip(result.code) === strip(testCase.result), [ + assert(strip(result.code) === strip(testCase.result.toString()), [ "", "", "------ The resulting code:", @@ -58,11 +110,11 @@ testCases.forEach(async function(testCase){ "", "------ is not equal to:", "", - testCase.result, + testCase.result.toString(), "", "------ Stripped: ", strip(result.code), - strip(testCase.result) + strip(testCase.result.toString()) ].join("\n")); });