100 lines
3.8 KiB
JavaScript
100 lines
3.8 KiB
JavaScript
|
"use strict";
|
||
|
module.exports = codegen;
|
||
|
|
||
|
/**
|
||
|
* Begins generating a function.
|
||
|
* @memberof util
|
||
|
* @param {string[]} functionParams Function parameter names
|
||
|
* @param {string} [functionName] Function name if not anonymous
|
||
|
* @returns {Codegen} Appender that appends code to the function's body
|
||
|
*/
|
||
|
function codegen(functionParams, functionName) {
|
||
|
|
||
|
/* istanbul ignore if */
|
||
|
if (typeof functionParams === "string") {
|
||
|
functionName = functionParams;
|
||
|
functionParams = undefined;
|
||
|
}
|
||
|
|
||
|
var body = [];
|
||
|
|
||
|
/**
|
||
|
* Appends code to the function's body or finishes generation.
|
||
|
* @typedef Codegen
|
||
|
* @type {function}
|
||
|
* @param {string|Object.<string,*>} [formatStringOrScope] Format string or, to finish the function, an object of additional scope variables, if any
|
||
|
* @param {...*} [formatParams] Format parameters
|
||
|
* @returns {Codegen|Function} Itself or the generated function if finished
|
||
|
* @throws {Error} If format parameter counts do not match
|
||
|
*/
|
||
|
|
||
|
function Codegen(formatStringOrScope) {
|
||
|
// note that explicit array handling below makes this ~50% faster
|
||
|
|
||
|
// finish the function
|
||
|
if (typeof formatStringOrScope !== "string") {
|
||
|
var source = toString();
|
||
|
if (codegen.verbose)
|
||
|
console.log("codegen: " + source); // eslint-disable-line no-console
|
||
|
source = "return " + source;
|
||
|
if (formatStringOrScope) {
|
||
|
var scopeKeys = Object.keys(formatStringOrScope),
|
||
|
scopeParams = new Array(scopeKeys.length + 1),
|
||
|
scopeValues = new Array(scopeKeys.length),
|
||
|
scopeOffset = 0;
|
||
|
while (scopeOffset < scopeKeys.length) {
|
||
|
scopeParams[scopeOffset] = scopeKeys[scopeOffset];
|
||
|
scopeValues[scopeOffset] = formatStringOrScope[scopeKeys[scopeOffset++]];
|
||
|
}
|
||
|
scopeParams[scopeOffset] = source;
|
||
|
return Function.apply(null, scopeParams).apply(null, scopeValues); // eslint-disable-line no-new-func
|
||
|
}
|
||
|
return Function(source)(); // eslint-disable-line no-new-func
|
||
|
}
|
||
|
|
||
|
// otherwise append to body
|
||
|
var formatParams = new Array(arguments.length - 1),
|
||
|
formatOffset = 0;
|
||
|
while (formatOffset < formatParams.length)
|
||
|
formatParams[formatOffset] = arguments[++formatOffset];
|
||
|
formatOffset = 0;
|
||
|
formatStringOrScope = formatStringOrScope.replace(/%([%dfijs])/g, function replace($0, $1) {
|
||
|
var value = formatParams[formatOffset++];
|
||
|
switch ($1) {
|
||
|
case "d": case "f": return String(Number(value));
|
||
|
case "i": return String(Math.floor(value));
|
||
|
case "j": return JSON.stringify(value);
|
||
|
case "s": return String(value);
|
||
|
}
|
||
|
return "%";
|
||
|
});
|
||
|
if (formatOffset !== formatParams.length)
|
||
|
throw Error("parameter count mismatch");
|
||
|
body.push(formatStringOrScope);
|
||
|
return Codegen;
|
||
|
}
|
||
|
|
||
|
function toString(functionNameOverride) {
|
||
|
return "function " + (functionNameOverride || functionName || "") + "(" + (functionParams && functionParams.join(",") || "") + "){\n " + body.join("\n ") + "\n}";
|
||
|
}
|
||
|
|
||
|
Codegen.toString = toString;
|
||
|
return Codegen;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Begins generating a function.
|
||
|
* @memberof util
|
||
|
* @function codegen
|
||
|
* @param {string} [functionName] Function name if not anonymous
|
||
|
* @returns {Codegen} Appender that appends code to the function's body
|
||
|
* @variation 2
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* When set to `true`, codegen will log generated code to console. Useful for debugging.
|
||
|
* @name util.codegen.verbose
|
||
|
* @type {boolean}
|
||
|
*/
|
||
|
codegen.verbose = false;
|