UNPKG

nyks

Version:

nodejs exupery style

196 lines (177 loc) 6.16 kB
"use strict"; /** * Note that this module is exclude from istanbul code coverage * TODO : complete tests (see tests/sprintf.js) for full coverage */ /* eslint no-prototype-builtins: "off" */ /* istanbul ignore file */ const kindOf = require('mout/lang/kindOf'); const repeat = require('../string/repeat'); var re = { not_string : /[^s]/, number : /[diefg]/, json : /[j]/, not_json : /[^j]/, text : /^[^\x25]+/, modulo : /^\x25{2}/, placeholder : /^\x25(?:([1-9]\d*)\$|\(([^)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosuxX])/, key : /^([a-z_][a-z_\d]*)/i, key_access : /^\.([a-z_][a-z_\d]*)/i, index_access : /^\[(\d+)\]/, sign : /^[+-]/ }; function sprintf() { var key = arguments[0]; var cache = sprintf.cache; if(!(cache[key] && cache.hasOwnProperty(key))) cache[key] = sprintf.parse(key); return sprintf.format.call(null, cache[key], arguments); } sprintf.format = function(parse_tree, argv) { var cursor = 1; var tree_length = parse_tree.length; var node_type = ""; var output = []; var is_positive = true; var sign = ""; var arg; var i; var k; var match; var pad; var pad_character; var pad_length; for(i = 0; i < tree_length; i++) { node_type = kindOf(parse_tree[i]); if(node_type === "String") { output[output.length] = parse_tree[i]; } else if(node_type === "Array") { match = parse_tree[i]; // convenience purposes only if(match[2]) { // keyword argument arg = argv[cursor]; for(k = 0; k < match[2].length; k++) { if(!arg.hasOwnProperty(match[2][k])) throw new Error(sprintf("[sprintf] property '%s' does not exist", match[2][k])); arg = arg[match[2][k]]; } } else if(match[1]) { // positional argument (explicit) arg = argv[match[1]]; } else { // positional argument (implicit) arg = argv[cursor++]; } if(kindOf(arg) == "Function") arg = arg(); if(re.not_string.test(match[8]) && re.not_json.test(match[8]) && (kindOf(arg) != "Number" && isNaN(arg))) throw new TypeError(sprintf("[sprintf] expecting number but found %s", kindOf(arg))); if(re.number.test(match[8])) is_positive = arg >= 0; switch(match[8]) { case "b": arg = arg.toString(2); break; case "c": arg = String.fromCharCode(arg); break; case "d": case "i": arg = parseInt(arg, 10); break; case "j": arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0); break; case "e": arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break; case "f": arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break; case "g": arg = match[7] ? parseFloat(arg).toPrecision(match[7]) : parseFloat(arg); break; case "o": arg = arg.toString(8); break; case "s": arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break; case "u": arg = arg >>> 0; break; case "x": arg = arg.toString(16); break; case "X": arg = arg.toString(16).toUpperCase(); break; } if(re.json.test(match[8])) { output[output.length] = arg; } else { if(re.number.test(match[8]) && (!is_positive || match[3])) { sign = is_positive ? "+" : "-"; arg = arg.toString().replace(re.sign, ""); } else { sign = ""; } pad_character = match[4] ? match[4] === "0" ? "0" : match[4].charAt(1) : " "; pad_length = match[6] - (sign + arg).length; pad = match[6] ? (pad_length > 0 ? repeat(pad_character, pad_length) : "") : ""; output[output.length] = match[5] ? sign + arg + pad : (pad_character === "0" ? sign + pad + arg : pad + sign + arg); } } } return output.join(""); }; sprintf.cache = {}; /* istanbul ignore next */ sprintf.parse = function(fmt) { var _fmt = fmt; var match = []; var parse_tree = []; var arg_names = 0; while(_fmt) { if((match = re.text.exec(_fmt)) !== null) { parse_tree[parse_tree.length] = match[0]; } else if((match = re.modulo.exec(_fmt)) !== null) { parse_tree[parse_tree.length] = "%"; } else if((match = re.placeholder.exec(_fmt)) !== null) { if(match[2]) { arg_names |= 1; var field_list = []; var replacement_field = match[2]; var field_match = []; if((field_match = re.key.exec(replacement_field)) !== null) { field_list[field_list.length] = field_match[1]; while((replacement_field = replacement_field.substring(field_match[0].length)) !== "") { if((field_match = re.key_access.exec(replacement_field)) !== null) field_list[field_list.length] = field_match[1]; else if((field_match = re.index_access.exec(replacement_field)) !== null) field_list[field_list.length] = field_match[1]; else throw new SyntaxError("[sprintf] failed to parse named argument key"); } } else { throw new SyntaxError("[sprintf] failed to parse named argument key"); } match[2] = field_list; } else { arg_names |= 2; } if(arg_names === 3) throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported"); parse_tree[parse_tree.length] = match; } else { throw new SyntaxError("[sprintf] unexpected placeholder"); } _fmt = _fmt.substring(match[0].length); } return parse_tree; }; /* istanbul ignore next */ var vsprintf = function(fmt, argv, _argv) { _argv = (argv || []).slice(0); _argv.splice(0, 0, fmt); return sprintf.apply(null, _argv); }; module.exports = sprintf; module.exports.vsprintf = vsprintf;