version bump 0.1.1: null / undefined rendering
This commit is contained in:
parent
f57cdd3114
commit
08a0a31128
2
Makefile
2
Makefile
@ -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
|
||||
|
44
README.md
44
README.md
@ -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*$/,"");
|
||||
|
11
printj.js
11
printj.js
@ -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
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
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|"],
|
||||
|
Loading…
Reference in New Issue
Block a user