From 736d3dbbde5a9f2ede2c54fd40ef35d66219bd25 Mon Sep 17 00:00:00 2001 From: Ger Hobbelt Date: Mon, 25 Dec 2017 01:23:52 +0100 Subject: [PATCH] helpers-lib: augment the `function printFunctionSourceCodeContainer(f)` API to support ES6 arrow functions and extract the code content of any such function for inlining. This code will be used in jison and jison-lex to support arrow functions passed via the programmer API (a la https://github.com/zaach/jison-lex/issues/23 ) Also extended the unit tests to cover various function type input scenarios. --- packages/helpers-lib/code-stringification.js | 66 ++++++++++++++++++-- packages/helpers-lib/tests/tests.js | 47 ++++++++++++++ 2 files changed, 108 insertions(+), 5 deletions(-) diff --git a/packages/helpers-lib/code-stringification.js b/packages/helpers-lib/code-stringification.js index ca083f8b8..b95664998 100644 --- a/packages/helpers-lib/code-stringification.js +++ b/packages/helpers-lib/code-stringification.js @@ -11,19 +11,75 @@ function chkBugger(src) { /// HELPER FUNCTION: print the function in source code form, properly indented. /** @public */ function printFunctionSourceCode(f) { - chkBugger(f); - return String(f); + var src = String(f); + chkBugger(src); + return src; } -/// HELPER FUNCTION: print the function **content** in source code form, properly indented. + + +const funcRe = /^function[\s\r\n]*[^\(]*\(([^\)]*)\)[\s\r\n]*\{([^]*?)\}$/; +const arrowFuncRe = /^\(([^\)]*)\)[\s\r\n]*=>[\s\r\n]*(?:(?:\{([^]*?)\})|(?:(([^\s\r\n\{)])[^]*?)))$/; + +/// HELPER FUNCTION: print the function **content** in source code form, properly indented, +/// ergo: produce the code for inlining the function. +/// +/// Also supports ES6's Arrow Functions: +/// +/// ``` +/// function a(x) { return x; } ==> 'return x;' +/// function (x) { return x; } ==> 'return x;' +/// (x) => { return x; } ==> 'return x;' +/// (x) => x; ==> 'return x;' +/// (x) => do(1), do(2), x; ==> 'return (do(1), do(2), x);' +/// /** @public */ function printFunctionSourceCodeContainer(f) { - chkBugger(f); - return String(f).replace(/^[\s\r\n]*function\b[^\{]+\{/, '').replace(/\}[\s\r\n]*$/, ''); + var action = printFunctionSourceCode(f).trim(); + var args; + + // Also cope with Arrow Functions (and inline those as well?). + // See also https://github.com/zaach/jison-lex/issues/23 + var m = funcRe.exec(action); + if (m) { + args = m[1].trim(); + action = m[2].trim(); + } else { + m = arrowFuncRe.exec(action); + if (m) { + args = m[1].trim(); + if (m[4]) { + // non-bracketed version: implicit `return` statement! + // + // Q: Must we make sure we have extra braces around the return value + // to prevent JavaScript from inserting implit EOS (End Of Statement) + // markers when parsing this, when there are newlines in the code? + // A: No, we don't have to as arrow functions rvalues suffer from this + // same problem, hence the arrow function's programmer must already + // have formatted the code correctly. + action = m[3].trim(); + action = 'return ' + action + ';'; + } else { + action = m[2].trim(); + } + } else { + var e = new Error('Cannot extract code from function'); + e.subject = action; + throw e; + } + } + return { + args: args, + code: action, + }; } + + + + export default { printFunctionSourceCode, printFunctionSourceCodeContainer, diff --git a/packages/helpers-lib/tests/tests.js b/packages/helpers-lib/tests/tests.js index 36c55fc5c..ed12d527c 100644 --- a/packages/helpers-lib/tests/tests.js +++ b/packages/helpers-lib/tests/tests.js @@ -81,6 +81,53 @@ describe("helpers API", function () { 'PRETTY RANGE'); }); + /* istanbul ignore next: test functions' code is injected and then crashes the test due to extra code coverage statements having been injected */ + it("printFunctionSourceCode", function () { + function d(i) { /* mock for linters */ } + + assert.strictEqual(helpers.printFunctionSourceCode(function a(x) { return x; }), "function a(x) { return x; }"); + assert.strictEqual(helpers.printFunctionSourceCode(function (x) { return x; }), "function (x) { return x; }"); + assert.strictEqual(helpers.printFunctionSourceCode((x) => { return x; }), "(x) => { return x; }"); + assert.strictEqual(helpers.printFunctionSourceCode((x) => x), "(x) => x"); + assert.strictEqual(helpers.printFunctionSourceCode((x) => (d(1), d(2), x)), "(x) => (d(1), d(2), x)"); + + var f1 = function a(x) { return x; }; + var f2 = function (x) { return x; }; + var f3 = (x) => { return x; }; + var f4 = (x) => x; + var f5 = (x) => (d(1), d(2), x); + + assert.strictEqual(helpers.printFunctionSourceCode(f1), "function a(x) { return x; }"); + assert.strictEqual(helpers.printFunctionSourceCode(f2), "function (x) { return x; }"); + assert.strictEqual(helpers.printFunctionSourceCode(f3), "(x) => { return x; }"); + assert.strictEqual(helpers.printFunctionSourceCode(f4), "(x) => x"); + assert.strictEqual(helpers.printFunctionSourceCode(f5), "(x) => (d(1), d(2), x)"); + }); + + /* istanbul ignore next: test functions' code is injected and then crashes the test due to extra code coverage statements having been injected */ + it("printFunctionSourceCodeContainer", function () { + function d(i) { /* mock for linters */ } + var x; /* mock */ + + assert.deepEqual(helpers.printFunctionSourceCodeContainer(function a(x) { return x; }), { args: "x", code: "return x;" }); + assert.deepEqual(helpers.printFunctionSourceCodeContainer(function (x) { return x; }), { args: "x", code: "return x;" }); + assert.deepEqual(helpers.printFunctionSourceCodeContainer((x) => { return x; }), { args: "x", code: "return x;" }); + assert.deepEqual(helpers.printFunctionSourceCodeContainer((x) => x), { args: "x", code: "return x;" }); + assert.deepEqual(helpers.printFunctionSourceCodeContainer((x) => (d(1), d(2), x)), { args: "x", code: "return (d(1), d(2), x);" }); + + var f1 = function a(x) { return x; }; + var f2 = function (x) { return x; }; + var f3 = (x) => { return x; }; + var f4 = (x) => x; + var f5 = (x) => (d(1), d(2), x); + + assert.deepEqual(helpers.printFunctionSourceCodeContainer(f1), { args: "x", code: "return x;" }); + assert.deepEqual(helpers.printFunctionSourceCodeContainer(f2), { args: "x", code: "return x;" }); + assert.deepEqual(helpers.printFunctionSourceCodeContainer(f3), { args: "x", code: "return x;" }); + assert.deepEqual(helpers.printFunctionSourceCodeContainer(f4), { args: "x", code: "return x;" }); + assert.deepEqual(helpers.printFunctionSourceCodeContainer(f5), { args: "x", code: "return (d(1), d(2), x);" }); + }); + it("parseCodeChunkToAST + prettyPrintAST", function () { var rmCommonWS = helpers.rmCommonWS;