JavaScript 实现sprintf 功能代码

来源:互联网 发布:java做服务器看那些书 编辑:程序博客网 时间:2024/05/26 08:42
(function(window) {    var re = {        not_string: /[^s]/,        number: /[dief]/,        text: /^[^\x25]+/,        modulo: /^\x25{2}/,        placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fiosuxX])/,        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], 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, tree_length = parse_tree.length, node_type = "", arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = true, sign = ""        for (i = 0; i < tree_length; i++) {            node_type = get_type(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 (get_type(arg) == "function") {                    arg = arg()                }                if (re.not_string.test(match[8]) && (get_type(arg) != "number" && isNaN(arg))) {                    throw new TypeError(sprintf("[sprintf] expecting number but found %s", get_type(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 "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 "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.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 ? str_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 = {}    sprintf.parse = function(fmt) {        var _fmt = fmt, match = [], parse_tree = [], 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 = [], replacement_field = match[2], 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    }    var vsprintf = function(fmt, argv, _argv) {        _argv = (argv || []).slice(0)        _argv.splice(0, 0, fmt)        return sprintf.apply(null, _argv)    }    /**     * helpers     */    function get_type(variable) {        return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase()    }    function str_repeat(input, multiplier) {        return Array(multiplier + 1).join(input)    }    /**     * export to either browser or node.js     */    if (typeof exports !== "undefined") {        exports.sprintf = sprintf        exports.vsprintf = vsprintf    }    else {        window.sprintf = sprintf        window.vsprintf = vsprintf        if (typeof define === "function" && define.amd) {            define(function() {                return {                    sprintf: sprintf,                    vsprintf: vsprintf                }            })        }    }})(typeof window === "undefined" ? this : window);
var assert = require("assert"),    sprintfjs = require("../src/sprintf.js"),    sprintf = sprintfjs.sprintf,    vsprintf = sprintfjs.vsprintfdescribe("sprintfjs", function() {    it("should return formated strings for simple placeholders", function() {        assert.equal("%", sprintf("%%"))        assert.equal("10", sprintf("%b", 2))        assert.equal("A", sprintf("%c", 65))        assert.equal("2", sprintf("%d", 2))        assert.equal("2", sprintf("%i", 2))        assert.equal("2", sprintf("%d", "2"))        assert.equal("2", sprintf("%i", "2"))        assert.equal("2e+0", sprintf("%e", 2))        assert.equal("2", sprintf("%u", 2))        assert.equal("4294967294", sprintf("%u", -2))        assert.equal("2.2", sprintf("%f", 2.2))        assert.equal("10", sprintf("%o", 8))        assert.equal("%s", sprintf("%s", "%s"))        assert.equal("ff", sprintf("%x", 255))        assert.equal("FF", sprintf("%X", 255))        assert.equal("Polly wants a cracker", sprintf("%2$s %3$s a %1$s", "cracker", "Polly", "wants"))        assert.equal("Hello world!", sprintf("Hello %(who)s!", {"who": "world"}))    })    it("should return formated strings for complex placeholders", function() {        // sign        assert.equal("2", sprintf("%d", 2))        assert.equal("-2", sprintf("%d", -2))        assert.equal("+2", sprintf("%+d", 2))        assert.equal("-2", sprintf("%+d", -2))        assert.equal("2", sprintf("%i", 2))        assert.equal("-2", sprintf("%i", -2))        assert.equal("+2", sprintf("%+i", 2))        assert.equal("-2", sprintf("%+i", -2))        assert.equal("2.2", sprintf("%f", 2.2))        assert.equal("-2.2", sprintf("%f", -2.2))        assert.equal("+2.2", sprintf("%+f", 2.2))        assert.equal("-2.2", sprintf("%+f", -2.2))        assert.equal("-2.3", sprintf("%+.1f", -2.34))        assert.equal("-0.0", sprintf("%+.1f", -0.01))        assert.equal("-000000123", sprintf("%+010d", -123))        assert.equal("______-123", sprintf("%+'_10d", -123))        assert.equal("-234.34 123.2", sprintf("%f %f", -234.34, 123.2))        // padding        assert.equal("-0002", sprintf("%05d", -2))        assert.equal("-0002", sprintf("%05i", -2))        assert.equal("    <", sprintf("%5s", "<"))        assert.equal("0000<", sprintf("%05s", "<"))        assert.equal("____<", sprintf("%'_5s", "<"))        assert.equal(">    ", sprintf("%-5s", ">"))        assert.equal(">0000", sprintf("%0-5s", ">"))        assert.equal(">____", sprintf("%'_-5s", ">"))        assert.equal("xxxxxx", sprintf("%5s", "xxxxxx"))        assert.equal("1234", sprintf("%02u", 1234))        assert.equal(" -10.235", sprintf("%8.3f", -10.23456))        assert.equal("-12.34 xxx", sprintf("%f %s", -12.34, "xxx"))        // precision        assert.equal("2.3", sprintf("%.1f", 2.345))        assert.equal("xxxxx", sprintf("%5.5s", "xxxxxx"))        assert.equal("    x", sprintf("%5.1s", "xxxxxx"))    })    it("should return formated strings for callbacks", function() {        assert.equal("foobar", sprintf("%s", function() { return "foobar" }))        assert.equal(Date.now(), sprintf("%s", Date.now)) // should pass...    })})


0 0