mirror of
https://github.com/protobufjs/protobuf.js.git
synced 2025-12-08 20:58:55 +00:00
144 lines
5.0 KiB
JavaScript
144 lines
5.0 KiB
JavaScript
"use strict";
|
|
module.exports = codegen;
|
|
|
|
var blockOpenRe = /[{[]$/,
|
|
blockCloseRe = /^[}\]]/,
|
|
casingRe = /:$/,
|
|
branchRe = /^\s*(?:if|}?else if|while|for)\b|\b(?:else)\s*$/,
|
|
breakRe = /\b(?:break|continue)(?: \w+)?;?$|^\s*return\b/;
|
|
|
|
/**
|
|
* A closure for generating functions programmatically.
|
|
* @memberof util
|
|
* @namespace
|
|
* @function
|
|
* @param {...string} params Function parameter names
|
|
* @returns {Codegen} Codegen instance
|
|
* @property {boolean} supported Whether code generation is supported by the environment.
|
|
* @property {boolean} verbose=false When set to true, codegen will log generated code to console. Useful for debugging.
|
|
* @property {function(string, ...*):string} sprintf Underlying sprintf implementation
|
|
*/
|
|
function codegen() {
|
|
var params = [],
|
|
src = [],
|
|
indent = 1,
|
|
inCase = false;
|
|
for (var i = 0; i < arguments.length;)
|
|
params.push(arguments[i++]);
|
|
|
|
/**
|
|
* A codegen instance as returned by {@link codegen}, that also is a sprintf-like appender function.
|
|
* @typedef Codegen
|
|
* @type {function}
|
|
* @param {string} format Format string
|
|
* @param {...*} args Replacements
|
|
* @returns {Codegen} Itself
|
|
* @property {function(string=):string} str Stringifies the so far generated function source.
|
|
* @property {function(string=, Object=):function} eof Ends generation and builds the function whilst applying a scope.
|
|
*/
|
|
/**/
|
|
function gen() {
|
|
var args = [],
|
|
i = 0;
|
|
for (; i < arguments.length;)
|
|
args.push(arguments[i++]);
|
|
var line = sprintf.apply(null, args);
|
|
var level = indent;
|
|
if (src.length) {
|
|
var prev = src[src.length - 1];
|
|
|
|
// block open or one time branch
|
|
if (blockOpenRe.test(prev))
|
|
level = ++indent; // keep
|
|
else if (branchRe.test(prev))
|
|
++level; // once
|
|
|
|
// casing
|
|
if (casingRe.test(prev) && !casingRe.test(line)) {
|
|
level = ++indent;
|
|
inCase = true;
|
|
} else if (inCase && breakRe.test(prev)) {
|
|
level = --indent;
|
|
inCase = false;
|
|
}
|
|
|
|
// block close
|
|
if (blockCloseRe.test(line))
|
|
level = --indent;
|
|
}
|
|
for (i = 0; i < level; ++i)
|
|
line = "\t" + line;
|
|
src.push(line);
|
|
return gen;
|
|
}
|
|
|
|
/**
|
|
* Stringifies the so far generated function source.
|
|
* @param {string} [name] Function name, defaults to generate an anonymous function
|
|
* @returns {string} Function source using tabs for indentation
|
|
* @inner
|
|
*/
|
|
function str(name) {
|
|
return "function" + (name ? " " + name.replace(/[^\w_$]/g, "_") : "") + "(" + params.join(",") + ") {\n" + src.join("\n") + "\n}";
|
|
}
|
|
|
|
gen.str = str;
|
|
|
|
/**
|
|
* Ends generation and builds the function whilst applying a scope.
|
|
* @param {string} [name] Function name, defaults to generate an anonymous function
|
|
* @param {Object.<string,*>} [scope] Function scope
|
|
* @returns {function} The generated function, with scope applied if specified
|
|
* @inner
|
|
*/
|
|
function eof(name, scope) {
|
|
if (typeof name === "object") {
|
|
scope = name;
|
|
name = undefined;
|
|
}
|
|
var source = gen.str(name);
|
|
if (codegen.verbose)
|
|
console.log("--- codegen ---\n" + source.replace(/^/mg, "> ").replace(/\t/g, " ")); // eslint-disable-line no-console
|
|
var keys = Object.keys(scope || (scope = {}));
|
|
return Function.apply(null, keys.concat("return " + source)).apply(null, keys.map(function(key) { return scope[key]; })); // eslint-disable-line no-new-func
|
|
// ^ Creates a wrapper function with the scoped variable names as its parameters,
|
|
// calls it with the respective scoped variable values ^
|
|
// and returns our brand-new properly scoped function.
|
|
//
|
|
// This works because "Invoking the Function constructor as a function (without using the
|
|
// new operator) has the same effect as invoking it as a constructor."
|
|
// https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Function
|
|
}
|
|
|
|
gen.eof = eof;
|
|
|
|
return gen;
|
|
}
|
|
|
|
function sprintf(format) {
|
|
var args = [],
|
|
i = 1;
|
|
for (; i < arguments.length;)
|
|
args.push(arguments[i++]);
|
|
i = 0;
|
|
format = format.replace(/%([dfjs])/g, function($0, $1) {
|
|
switch ($1) {
|
|
case "d":
|
|
return Math.floor(args[i++]);
|
|
case "f":
|
|
return Number(args[i++]);
|
|
case "j":
|
|
return JSON.stringify(args[i++]);
|
|
default:
|
|
return args[i++];
|
|
}
|
|
});
|
|
if (i !== args.length)
|
|
throw Error("argument count mismatch");
|
|
return format;
|
|
}
|
|
|
|
codegen.sprintf = sprintf;
|
|
codegen.supported = false; try { codegen.supported = codegen("a","b")("return a-b").eof()(2,1) === 1; } catch (e) {} // eslint-disable-line no-empty
|
|
codegen.verbose = false;
|