version bump 0.1.1: null / undefined rendering

This commit is contained in:
SheetJS 2016-10-08 13:24:13 -04:00
parent f57cdd3114
commit 08a0a31128
16 changed files with 395 additions and 43 deletions

@ -45,7 +45,7 @@ stress:
@make -C stress test
.PHONY: clean-stress
clean-stress: ## Remove stress tests
clean-stress: ## Remove stress tests
@make -C stress clean
## Code Checking

@ -1,7 +1,7 @@
# printj
Extended `sprintf` implementation (for the browser and nodejs). Emphasis on
compliance and performance.
compliance, performance and IE6+ support.
```JS
PRINTJ.sprintf("Hello %s!", "World");
@ -15,11 +15,15 @@ A self-contained specification of the printf format string is included below in
With [npm](https://www.npmjs.org/package/printj):
$ npm install printj
```bash
$ npm install printj
```
In the browser:
<script src="printj.js"></script>
```html
<script src="printj.js"></script>
```
The browser exposes a variable `PRINTJ`
@ -44,7 +48,7 @@ For example:
```js
> // var PRINTJ = require('printj'); // uncomment this line if in node
> var sprintf = PRINTJ.sprintf, vsprintf = PRINTJ.vsprintf;
> var sprintf = PRINTJ.sprintf, vsprintf = PRINTJ.vsprintf;
> sprintf("Hello %s", "SheetJS") // 'Hello SheetJS'
> sprintf("%d + %d = %d", 2,3,2+3) // '2 + 3 = 5'
> vsprintf("%d + %d = %d", [2,3,5]) // '2 + 3 = 5'
@ -87,7 +91,7 @@ requires access to a C compiler.
Please consult the attached LICENSE file for details. All rights not explicitly
granted by the Apache 2.0 license are reserved by the Original Author.
## Badges
## Badges
[![Build Status](https://travis-ci.org/SheetJS/printj.svg?branch=master)](https://travis-ci.org/SheetJS/printj)
@ -137,7 +141,7 @@ int vswprintf(wchar_t *ostr, const wchar_t *fmt, va_list arg_list);
C "strings" are really just arrays of numbers. An external code page (such as
ASCII) maps those numbers to characters. K&R defines two types of strings:
basic character set strings (`char *`) and extended character set strings
basic character set strings (`char *`) and extended character set strings
(`wchar_t *`). In contrast, JS has a true string value type.
Unlike in C, JS strings do not treat the null character as an end-of-string
@ -439,7 +443,7 @@ K&R "printable characters are always positive", the types are assumed unsigned.
K&R recognizes 3 floating point types. C99 later tied it to IEC 60559:
| C data type | precision | total bits | exponent | mantissa | fmt |
| C data type | precision | total bits | exponent | mantissa | fmt |
|:--------------|:----------|:----------:|:--------:|:--------:|------:|
| `float` | single | `32` | `8` | `23` | |
| `double` | double | `64` | `11` | `52` | `f` |
@ -543,36 +547,36 @@ is signed or unsigned according to the conversion specifier. If a length is
specified, it overrides the implied length of the conversion. The following
table describes the behavior of this implementation:
| implied C type | ctypes.json | length | conv default |
| implied C type | ctypes.json | length | conv default |
|:------------------------------------|:------------|:------:|:-------------|
| `int` or `unsigned int` | `int` | (none) | d i o u x X |
| `char` or `unsigned char` | `char` | hh |
| `short` or `unsigned short` | `short` | h |
| `long` or `unsigned long` | `long` | l | D U O |
| `long long` or `unsigned long long` | `longlong` | L ll q |
| `long long` or `unsigned long long` | `longlong` | L ll q |
| `intmax_t` or `uintmax_t` | `intmax_t` | j |
| `size_t` or `ssize_t` | `size_t` | z Z |
| `ptrdiff_t` or unsigned variant | `ptrdiff_t` | t |
## Rendering Unsigned Integers in Base 10 ("u" and "U" conversions)
`num.toString(10)` produces the correct result for exact integers.
`num.toString(10)` produces the correct result for exact integers.
`"u"` conversion restricts values to `int`; `"U"` restricts to `long`.
`"u"` conversion restricts values to `int`; `"U"` restricts to `long`.
## Rendering Unsigned Integers in Base 8 ("o" and "O" conversions)
Even though `num.toString(8)` is implementation-dependent, all browser
implementations use standard form for integers in the exact range.
implementations use standard form for integers in the exact range.
The alternate form (`#`) prints a `"0"` prefix.
`"o"` conversion restricts values to `int`; `"O"` restricts to `long`.
`"o"` conversion restricts values to `int`; `"O"` restricts to `long`.
## Rendering Unsigned Integers in Base 16 ("x" and "X" conversions)
Even though `num.toString(16)` is implementation-dependent, all browser
implementations use standard form for integers in the exact range.
implementations use standard form for integers in the exact range.
The alternate form (`#`) prints a `"0x"` or `"0X"` prefix.
@ -583,7 +587,7 @@ Unlike `"U" "O" "D"`, `"X"` conversion uses `A-F` instead of `a-f` in hex.
`num.toString(10)` produces the correct result for exact integers. The flags
`" +"` control prefixes for positive integers.
`"di"` conversions restrict values to `int`; `"D"` restricts to `long`.
`"di"` conversions restrict values to `int`; `"D"` restricts to `long`.
# Floating Point Conversions
@ -678,7 +682,7 @@ JS `num.toString(radix)` is implementation-dependent for valid non-10 radices
(`2-9, 11-36`). IE uses hex-mantissa decimal-hex-exponent form when the
absolute value of the base-2 exponent exceeds 60. Otherwise, IE uses an exact
standard hexadecimal form. Chrome, Safari and other browsers always use the
exact standard hexadecimal form. Both forms are easily converted to `"%a"` by
exact standard hexadecimal form. Both forms are easily converted to `"%a"` by
calculating and dividing by the appropriate power of 2.
For each non-zero normal floating point value, there are 4 acceptable strings
@ -695,7 +699,7 @@ of 2 and adjusting the exponent accordingly:
JS engines follow the glibc model: multiply by a suitable power of 16 so that
the mantissa is between 1 and 16, render left to right one digit at a time, then
fix the result at the end. FreeBSD and OSX always show the normalized form.
This implementation defaults to the normalized form. To switch to the glibc
This implementation defaults to the normalized form. To switch to the glibc
form, define `DO_NOT_NORMALIZE` in the `JSFLAGS` variable when building:
```bash
@ -958,7 +962,7 @@ JS. When converting C literal strings, there are a few differences in escaping:
| C escape sequence | Equivalent JS | Notes |
|:------------------|:--------------|:---------------------------------------|
| `"\a"` | `"\007"` | BEL character will not ring in browser |
| `"\a"` | `"\007"` | BEL character will not ring in browser |
| `"\?"` | `"?"` | JS does not handle trigraphs |
| `"\ooo"` (octal) | `"\ooo"` | JS uses Latin-1 for non-ASCII codes |
| `"\xhh"` (hex) | `"\xhh"` | JS uses Latin-1 for non-ASCII codes |
@ -970,7 +974,7 @@ JS. When converting C literal strings, there are a few differences in escaping:
- Full support for POSIX flags and positional parameters
- Emulation of BSD `quad_t` and `u_quad_t` conversion
- Parser accepts but does not emulate CRT wide and unicode character conversions
- glibc `Z` length conversion and extended `m` error support
- CRT `I/w` length but no `I32/I64`
- glibc `Z` length conversion and extended `m` error support
- Parser fails on CRT `I32`/`I64` fixed lengths
- Default `LP64` data model but can be configured to support `ILP32` or `LLP64`

@ -1 +1 @@
PRINTJ.version = '0.1.0';
PRINTJ.version = '0.1.1';

@ -11,7 +11,7 @@ var padstr = {
function pads(x/*:number*/, c/*:string*/)/*:string*/ { return PAD_(x,c); }
#define PADS(x,c) pads(x,c)
#else
#define PADS(x,c) PAD_(x,c)
#define PADS(x,c) PAD_(x,c)
#endif
#define PAD(x) pad = PADS(x, " ")
@ -41,11 +41,11 @@ function pads(x/*:number*/, c/*:string*/)/*:string*/ { return PAD_(x,c); }
#if SIZEOF_SIZE_T > 4 /* TODO: negative ptrs? */
#define CONV_SIZE_T(x) x = Math.abs(x);
#define SIZE_T_TO_HEX(n) n.toString(16)
#define SIZE_T_TO_HEX(n) n.toString(16)
#else
#define CONV_SIZE_T(x) x = (x>>>0);
#define SIZE_T_TO_HEX(n) n.toString(16)
#endif
#endif
#define IDX_POS 2

@ -1,6 +1,6 @@
/* JS has no concept of pointers so interpret the `l` key as an address */
case /*p*/ 112:
Vnum = typeof arg == "number" ? arg : Number(arg.l);
Vnum = typeof arg == "number" ? arg : arg ? Number(arg.l) : -1;
if(isnan(Vnum)) Vnum = -1;
if(alt) O = Vnum.toString(10);
else {
@ -24,7 +24,7 @@
/* JS-specific conversions (extension) */
case /*J*/ 74: O = (alt ? u_inspect : JSON.stringify)(arg); break;
case /*V*/ 86: O = String(arg.valueOf()); break;
case /*V*/ 86: O = arg == null ? "null" : String(arg.valueOf()); break;
case /*T*/ 84:
if(alt) { /* from '[object %s]' extract %s */
O = Object.prototype.toString.call(arg).substr(8);

@ -9,7 +9,7 @@
case 1: _DOIT(0xFF, 0x7F); break;
case 2: _DOIT(0xFFFF, 0x7FFF); break;
case 4: Vnum = sign ? (Vnum | 0) : (Vnum >>> 0); break;
default: Vnum = Math.round(Vnum); break;
default: Vnum = isnan(Vnum) ? 0 : Math.round(Vnum); break;
}
#undef _DOIT

@ -1,4 +1,5 @@
Vnum = Number(arg);
if(arg === null) Vnum = 0/0;
if(len == "L") bytes = 12;
var isf = isFinite(Vnum);
if(!isf) { /* Infinity or NaN */
@ -27,7 +28,7 @@
switch(isnum) {
/* f/F standard */
case 1: case 11:
if(Vnum < Math.pow(10,21)) {
if(Vnum < 1e21) {
O = Vnum.toFixed(prec);
if(isnum == 1) { if(prec===0 &&alt&& O.indexOf(".")==-1) O+="."; }
else if(!alt) O=O.replace(/(\.\d*[1-9])0*$/,"$1").replace(/\.0*$/,"");

@ -27,6 +27,8 @@ This implementation supports the full POSIX set of conversions. Consult the enc
<tr><th>example</th><th>result</th></tr>
</table>
<script type="text/javascript">/* jshint browser:true, evil:true */</script>
<script src="shim.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.min.js"></script>
<script src="printj.js"></script>
<script type="text/javascript">
var table = document.getElementById("data");

@ -1,6 +1,6 @@
{
"name": "printj",
"version": "0.1.0",
"version": "0.1.1",
"author": "sheetjs",
"description": "Pure-JS printf",
"keywords": [ "printf", "sprintf", "format", "string" ],

@ -26,7 +26,7 @@ var PRINTJ/*:any*/;
/*jshint ignore:end */
}(function(PRINTJ) {
PRINTJ.version = '0.1.0';
PRINTJ.version = '0.1.1';
function tokenize(fmt/*:string*/)/*:ParsedFmt*/ {
var out/*:ParsedFmt*/ = [];
@ -275,7 +275,7 @@ function doit(t/*:ParsedFmt*/, args/*:Array<any>*/)/*:string*/ {
/* JS has no concept of pointers so interpret the `l` key as an address */
case /*p*/ 112:
Vnum = typeof arg == "number" ? arg : Number(arg.l);
Vnum = typeof arg == "number" ? arg : arg ? Number(arg.l) : -1;
if(isNaN(Vnum)) Vnum = -1;
if(alt) O = Vnum.toString(10);
else {
@ -299,7 +299,7 @@ function doit(t/*:ParsedFmt*/, args/*:Array<any>*/)/*:string*/ {
/* JS-specific conversions (extension) */
case /*J*/ 74: O = (alt ? u_inspect : JSON.stringify)(arg); break;
case /*V*/ 86: O = String(arg.valueOf()); break;
case /*V*/ 86: O = arg == null ? "null" : String(arg.valueOf()); break;
case /*T*/ 84:
if(alt) { /* from '[object %s]' extract %s */
O = Object.prototype.toString.call(arg).substr(8);
@ -365,7 +365,7 @@ function doit(t/*:ParsedFmt*/, args/*:Array<any>*/)/*:string*/ {
case 1: Vnum = (Vnum & 0xFF); if(sign && (Vnum > 0x7F)) Vnum -= (0xFF + 1); break;
case 2: Vnum = (Vnum & 0xFFFF); if(sign && (Vnum > 0x7FFF)) Vnum -= (0xFFFF + 1); break;
case 4: Vnum = sign ? (Vnum | 0) : (Vnum >>> 0); break;
default: Vnum = Math.round(Vnum); break;
default: Vnum = isNaN(Vnum) ? 0 : Math.round(Vnum); break;
}
/* generate string */
@ -444,6 +444,7 @@ function doit(t/*:ParsedFmt*/, args/*:Array<any>*/)/*:string*/ {
} else if(isnum > 0) {
Vnum = Number(arg);
if(arg === null) Vnum = 0/0;
if(len == "L") bytes = 12;
var isf = isFinite(Vnum);
if(!isf) { /* Infinity or NaN */
@ -472,7 +473,7 @@ function doit(t/*:ParsedFmt*/, args/*:Array<any>*/)/*:string*/ {
switch(isnum) {
/* f/F standard */
case 1: case 11:
if(Vnum < Math.pow(10,21)) {
if(Vnum < 1e21) {
O = Vnum.toFixed(prec);
if(isnum == 1) { if(prec===0 &&alt&& O.indexOf(".")==-1) O+="."; }
else if(!alt) O=O.replace(/(\.\d*[1-9])0*$/,"$1").replace(/\.0*$/,"");

@ -24,7 +24,7 @@ var PRINTJ;
/*jshint ignore:end */
}(function(PRINTJ) {
PRINTJ.version = '0.1.0';
PRINTJ.version = '0.1.1';
function tokenize(fmt) {
var out = [];
@ -271,7 +271,7 @@ function doit(t, args) {
/* JS has no concept of pointers so interpret the `l` key as an address */
case /*p*/ 112:
Vnum = typeof arg == "number" ? arg : Number(arg.l);
Vnum = typeof arg == "number" ? arg : arg ? Number(arg.l) : -1;
if(isNaN(Vnum)) Vnum = -1;
if(alt) O = Vnum.toString(10);
else {
@ -295,7 +295,7 @@ function doit(t, args) {
/* JS-specific conversions (extension) */
case /*J*/ 74: O = (alt ? u_inspect : JSON.stringify)(arg); break;
case /*V*/ 86: O = String(arg.valueOf()); break;
case /*V*/ 86: O = arg == null ? "null" : String(arg.valueOf()); break;
case /*T*/ 84:
if(alt) { /* from '[object %s]' extract %s */
O = Object.prototype.toString.call(arg).substr(8);
@ -361,7 +361,7 @@ function doit(t, args) {
case 1: Vnum = (Vnum & 0xFF); if(sign && (Vnum > 0x7F)) Vnum -= (0xFF + 1); break;
case 2: Vnum = (Vnum & 0xFFFF); if(sign && (Vnum > 0x7FFF)) Vnum -= (0xFFFF + 1); break;
case 4: Vnum = sign ? (Vnum | 0) : (Vnum >>> 0); break;
default: Vnum = Math.round(Vnum); break;
default: Vnum = isNaN(Vnum) ? 0 : Math.round(Vnum); break;
}
/* generate string */
@ -440,6 +440,7 @@ function doit(t, args) {
} else if(isnum > 0) {
Vnum = Number(arg);
if(arg === null) Vnum = 0/0;
if(len == "L") bytes = 12;
var isf = isFinite(Vnum);
if(!isf) { /* Infinity or NaN */
@ -468,7 +469,7 @@ function doit(t, args) {
switch(isnum) {
/* f/F standard */
case 1: case 11:
if(Vnum < Math.pow(10,21)) {
if(Vnum < 1e21) {
O = Vnum.toFixed(prec);
if(isnum == 1) { if(prec===0 &&alt&& O.indexOf(".")==-1) O+="."; }
else if(!alt) O=O.replace(/(\.\d*[1-9])0*$/,"$1").replace(/\.0*$/,"");

237
shim.js Normal file

@ -0,0 +1,237 @@
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
if (!Object.keys) {
Object.keys = (function () {
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function (obj) {
if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');
var result = [];
for (var prop in obj) {
if (hasOwnProperty.call(obj, prop)) result.push(prop);
}
if (hasDontEnumBug) {
for (var i=0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
}
}
return result;
};
})();
}
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
if (!Array.prototype.filter)
{
Array.prototype.filter = function(fun /*, thisp */)
{
"use strict";
if (this == null)
throw new TypeError();
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun != "function")
throw new TypeError();
var res = [];
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in t)
{
var val = t[i]; // in case fun mutates this
if (fun.call(thisp, val, i, t))
res.push(val);
}
}
return res;
};
}
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g, '');
};
}
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
if (!Array.prototype.forEach)
{
Array.prototype.forEach = function(fun /*, thisArg */)
{
"use strict";
if (this === void 0 || this === null)
throw new TypeError();
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== "function")
throw new TypeError();
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++)
{
if (i in t)
fun.call(thisArg, t[i], i, t);
}
};
}
// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.com/#x15.4.4.19
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
// 1. Let O be the result of calling ToObject passing the |this| value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (thisArg) {
T = thisArg;
}
// 6. Let A be a new array created as if by the expression new Array(len) where Array is
// the standard built-in constructor with that name and len is the value of len.
A = new Array(len);
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while(k < len) {
var kValue, mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal method of O with argument Pk.
kValue = O[ k ];
// ii. Let mappedValue be the result of calling the Call internal method of callback
// with T as the this value and argument list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
// For best browser support, use the following:
A[ k ] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
}
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement, fromIndex) {
if ( this === undefined || this === null ) {
throw new TypeError( '"this" is null or not defined' );
}
var length = this.length >>> 0; // Hack to convert object.length to a UInt32
fromIndex = +fromIndex || 0;
if (Math.abs(fromIndex) === Infinity) {
fromIndex = 0;
}
if (fromIndex < 0) {
fromIndex += length;
if (fromIndex < 0) {
fromIndex = 0;
}
}
for (;fromIndex < length; fromIndex++) {
if (this[fromIndex] === searchElement) {
return fromIndex;
}
}
return -1;
};
}
// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
if (! Array.isArray) {
Array.isArray = function(obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
};
}
// https://github.com/ttaubert/node-arraybuffer-slice
// (c) 2013 Tim Taubert <tim@timtaubert.de>
// arraybuffer-slice may be freely distributed under the MIT license.
"use strict";
if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {
ArrayBuffer.prototype.slice = function (begin, end) {
begin = (begin|0) || 0;
var num = this.byteLength;
end = end === (void 0) ? num : (end|0);
// Handle negative values.
if (begin < 0) begin += num;
if (end < 0) end += num;
if (num === 0 || begin >= num || begin >= end) {
return new ArrayBuffer(0);
}
var length = Math.min(num - begin, end - begin);
var target = new ArrayBuffer(length);
var targetArray = new Uint8Array(target);
targetArray.set(new Uint8Array(this, begin, length));
return target;
};
}

@ -17,7 +17,7 @@ stress.h: generate_stress.njs
stress: stress.c stress.h tests.h
gcc $(CPPFLAGS) -o $@ $<
stress.njs: stress.js stress.h tests.h
stress.njs: stress.js stress.h tests.h
cpp -DJAVASCRIPT $(FLAGS) $< > $@
.PHONY: test

@ -51,7 +51,7 @@ function getarg(format/*:string*/, length/*:string*/)/*:string*/ {
switch(type) {
case 's': return "ws";
case 'd': if(length == "L") return "ld";
case 'i': case 'u': return length + type;
case 'i': case 'u': return length + type;
}
return type;
}

106
test.js

@ -37,3 +37,109 @@ describe('correctness', function() {
});
});
});
var sprintf = IMPLS["base"].sprintf;
var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ !\"#$%&'()*+,-./0123456789:;<=>?@[\\]^_~{|}`".split("");
var convs = "aAbBcCdDeEfFgGiJmnoOpsSTuUVxXyY%".split("");
var flags = " #'+-0".split("");
var digits = "0123456789".split("");
var lens = "hIjlLqtwzZ".split(""); lens.push("hh"); lens.push("ll");
var other = "$*.".split("");
var unused = []; chars.forEach(function(c) {
if(convs.indexOf(c) == -1 &&
flags.indexOf(c) == -1 &&
digits.indexOf(c) == -1 &&
lens.indexOf(c) == -1 &&
other.indexOf(c) == -1
) unused.push(c);
});
describe('special cases', function() {
it('fails on unrecognized format chars: ' + unused.join(""), function() {
unused.forEach(function(c) {
assert.throws(function() { sprintf("%" + c, 0); }, "Should fail on %" +c);
});
});
it('accepts all expected conversions: ' + convs.join(""), function() {
convs.forEach(function(c) {
assert.doesNotThrow(function() { sprintf("%" + c, 0); }, "Should pass on %" +c);
});
});
it('accepts all expected lengths: ' + lens.join(""), function() {
lens.forEach(function(l) {
var fmt = "%" + l + "X";
assert.doesNotThrow(function() { sprintf(fmt, 0); }, "Should pass on "+fmt);
});
});
it('accepts all expected flags: ' + flags.join(""), function() {
flags.forEach(function(l) {
var fmt = "%" + l + "X";
assert.doesNotThrow(function() { sprintf(fmt, 0); }, "Should pass on "+fmt);
});
});
it('correctly handles character conversions: cC', function() {
assert.equal(sprintf("|%c %c|", "69", 69), "|6 E|");
assert.equal(sprintf("|%c|", {toString:function() { return "69"; }, valueOf: function() { return 69; }} ), "|6|");
});
it('correctly handles error conversion: m', function() {
var x = new Error("sheetjs");
x.errno = 69; x.toString = function() { return "SHEETJS"; };
assert.equal(sprintf("|%#m|", x), "|sheetjs|");
delete x.message;
assert.equal(sprintf("|%#m|", x), "|Error number 69|");
delete x.errno;
assert.equal(sprintf("|%#m|", x), "|Error SHEETJS|");
});
it('correctly handles typeof and valueOf conversions: TV', function() {
assert.equal(sprintf("%1$T %1$#T", 1), 'number Number');
assert.equal(sprintf("%1$T %1$#T", 'foo'), 'string String');
assert.equal(sprintf("%1$T %1$#T", [1,2,3]), 'object Array');
assert.equal(sprintf("%1$T %1$#T", null), 'object Null');
assert.equal(sprintf("%1$T %1$#T", undefined), 'undefined Undefined');
var _f = function() { return "f"; };
var _3 = function() { return 3; };
assert.equal(sprintf("%1$d %1$s %1$V", {toString:_f}), '0 f f');
assert.equal(sprintf("%1$d %1$s %1$V", {valueOf:_3}), '3 [object Object] 3');
assert.equal(sprintf("%1$d %1$s %1$V", {valueOf:_3, toString:_f}), '3 f 3');
});
it('correctly handles standard integer conversions: diouxXDUO', function() {
assert.equal(sprintf("%02hhx %02hhX", 1, 1234321), "01 91");
assert.equal(sprintf("%02hhx %-02hhX", -1, -253), "ff 3 ");
assert.equal(sprintf("%#02llx", -3), "0xfffffffffffffffd");
assert.equal(sprintf("%#02llX", -3), "0XFFFFFFFFFFFFFFFD");
assert.equal(sprintf("%#02llo", -3), "01777777777777777777775");
assert.equal(sprintf("%#02llu", -3), "18446744073709551613");
assert.equal(sprintf("%#03lld", -3), "-03");
assert.equal(sprintf("%.9d %.9d", 123456, -123456), "000123456 -000123456");
});
it('correctly handles new binary conversions: bB', function() {
assert.equal(sprintf("%#b", -3), "0b11111111111111111111111111111101");
assert.equal(sprintf("%#5B", 3), " 0b11");
});
it('recognizes IEEE754 special values', function() {
assert.equal(sprintf("%a", Infinity), "inf");
assert.equal(sprintf("%e", -Infinity), "-inf");
assert.equal(sprintf("%f", 0/0), "nan");
assert.equal(sprintf("%g", 1/-Infinity), "-0");
});
it('correctly handles floating point conversions: aAeEfFgG', function() {
assert.equal(sprintf("%1$g %1$#g", 1e5), "100000 100000.");
assert.equal(sprintf("%.3g %.3g", 1.2345e-4, 1.2345e-5), "0.000123 1.23e-05");
assert.equal(sprintf("%f", 1.23e22), "12300000000000001048600.000000");
assert.equal(sprintf("%a %A %a %A", 1, .2, .69, 6e20), "0x1p+0 0X1.999999999999AP-3 0x1.6147ae147ae14p-1 0X1.043561A88293P+69");
assert.equal(sprintf("%La %LA %La %LA", 1, .2, .69, 6e20), "0x8p-3 0XC.CCCCCCCCCCCDP-6 0xb.0a3d70a3d70ap-4 0X8.21AB0D441498P+66");
assert.equal(sprintf("|%1$4.1f|%1$04.1f|%1$-4.1f", 1.2), "| 1.2|01.2|1.2 ");
assert.equal(sprintf("%1$.1a|%1$04.0f", -128), "-0x1.0p+7|-128");
assert.equal(sprintf("%1$.1a|%1$04.0f", -6.9e-11), "-0x1.3p-34|-000");
assert.equal(sprintf("%010.1a", 1.), "0x001.0p+0");
assert.equal(sprintf("%.7a %.7a", 129, -129), "0x1.0200000p+7 -0x1.0200000p+7");
assert.equal(sprintf("%.7a", -3.1), "-0x1.8cccccdp+1");
});
it('consistently handles null and undefined', function() {
assert.equal(sprintf("|%1$a|%1$A|%1$e|%1$E|%1$f|%1$F|%1$g|%1$G|", undefined), "|nan|NAN|nan|NAN|nan|NAN|nan|NAN|");
assert.equal(sprintf("|%1$a|%1$A|%1$e|%1$E|%1$f|%1$F|%1$g|%1$G|", null), "|nan|NAN|nan|NAN|nan|NAN|nan|NAN|");
assert.equal(sprintf("|%1$b|%1$B|%1$d|%1$D|%1$i|%1$o|%1$O|%1$u|%1$U|%1$x|%1$X|", undefined), "|0|0|0|0|0|0|0|0|0|0|0|");
assert.equal(sprintf("|%1$b|%1$B|%1$d|%1$D|%1$i|%1$o|%1$O|%1$u|%1$U|%1$x|%1$X|", null), "|0|0|0|0|0|0|0|0|0|0|0|");
});
});

@ -89865,7 +89865,7 @@ var tests = [
[["|%05.2Y|", 1], "|000TR|"],
[["|%-5.2y|", 0], "|fa |"],
// typeof
// typeof
[["|%1$T %1$#T|", 0], "|number Number|"],
[["|%1$T %1$#T|", 'foo'], "|string String|"],
[["|%1$T %1$#T|", [1,2,3]], "|object Array|"],