diff --git a/README.md b/README.md index 1f160289..ecbf3984 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ The following features, which are not supported in standard JSON, have been adde - Object keys may be unquoted ECMAScript 5.1 _[IdentifierName]_ s. - Unquoted object keys may include character escapes. - Character escapes with two hex digits (`\xXX`) are supported for parsing, as well as the standard four digit `\uXXXX` form. -- Object keys may be single quoted or backtick quoted. +- Object keys may be single quoted or backtick quoted (using backticks does not, however, invoke string interpolation). - Object key/value pairs may have a single trailing comma. ### Arrays diff --git a/lib/bignumber-util.js b/lib/bignumber-util.js index a37d30f2..752bbdf3 100644 --- a/lib/bignumber-util.js +++ b/lib/bignumber-util.js @@ -86,15 +86,15 @@ module.exports = { } // Try to recover sign of negative zero. - if (Object.is(value, -0) || /-0\b/.test(value.valueOf().toString())) { + if (!str.startsWith('-') && (Object.is(value, -0) || /-0\b/.test(value.valueOf().toString()))) { str = '-' + str; } const special = /[-+]?Infinity|NaN/.test(str); if (goBig) { - if (special) { - return str; + if (special && suffix && !noSuffix) { + str += '_'; } return str + (noSuffix ? '' : suffix); diff --git a/lib/parse.js b/lib/parse.js index 490262df..8dd0eae6 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -151,6 +151,8 @@ module.exports = function parse(text, reviver, newOptions) { }, value() { + let trailingUnderscore = false; + switch (c) { case '{': case '[': @@ -218,11 +220,41 @@ module.exports = function parse(text, reviver, newOptions) { case 'I': read(); literal('nfinity'); + + if (peek() === '_') { + read(); + trailingUnderscore = true; + } + + if (peek() === 'm') { + read(); + return newToken(big.getBigDecimalType(), big.toBigDecimal(Infinity)); + } + + if (trailingUnderscore) { + throw invalidChar('_'); + } + return newToken('numeric', Infinity); case 'N': read(); literal('aN'); + + if (peek() === '_') { + read(); + trailingUnderscore = true; + } + + if (peek() === 'm') { + read(); + return newToken(big.getBigDecimalType(), big.toBigDecimal(NaN)); + } + + if (trailingUnderscore) { + throw invalidChar('_'); + } + return newToken('numeric', NaN); case "'": @@ -309,6 +341,8 @@ module.exports = function parse(text, reviver, newOptions) { }, sign() { + let trailingUnderscore = false; + switch (c) { case '.': buffer = read(); @@ -341,11 +375,41 @@ module.exports = function parse(text, reviver, newOptions) { case 'I': read(); literal('nfinity'); + + if (peek() === '_') { + read(); + trailingUnderscore = true; + } + + if (peek() === 'm') { + read(); + return newToken(big.getBigDecimalType(), big.toBigDecimal(sign * Infinity)); + } + + if (trailingUnderscore) { + throw invalidChar('_'); + } + return newToken('numeric', sign * Infinity); case 'N': read(); literal('aN'); + + if (peek() === '_') { + read(); + trailingUnderscore = true; + } + + if (peek() === 'm') { + read(); + return newToken(big.getBigDecimalType(), big.toBigDecimal(NaN)); + } + + if (trailingUnderscore) { + throw invalidChar('_'); + } + return newToken('numeric', NaN); } diff --git a/package.json b/package.json index 87976b41..a98849f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "json-z", - "version": "3.0.4", + "version": "3.0.5", "description": "JSON for everyone.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -23,7 +23,7 @@ "preversion": "npm run production", "production": "npm run lint && npm test && npm run build", "test": "tap -Rspec --100 test", - "quick-test": "tap -T -Rspec test/stringify.spec.js", + "quick-test": "tap -T -Rspec test/parse.spec.js", "version": "npm run build-package && git add package.json5" }, "repository": { diff --git a/test/errors.spec.js b/test/errors.spec.js index da7f61cb..96911a02 100644 --- a/test/errors.spec.js +++ b/test/errors.spec.js @@ -216,6 +216,7 @@ describe('JSONZ', () => { '0x_12', '0x1__2', '0x12_', '0o_12', '0o1__2', '0o12_', '0b_11', '0b1__1', '0b11_', + 'NaN_', '-NaN_', 'Infinity_', '-Infinity_', ]; for (const badNumber of badNumbers) { diff --git a/test/parse.spec.js b/test/parse.spec.js index 7d68f28c..336c38fa 100644 --- a/test/parse.spec.js +++ b/test/parse.spec.js @@ -19,6 +19,9 @@ function equalBigNumber(a, b) { else if (!!a !== !!b || !a) { return false; } + else if (typeof a.isNaN === 'function' && a.isNaN()) { + return typeof b.isNaN === 'function' && b.isNaN(); + } else if (typeof a.equals === 'function') { return a.equals(b); } @@ -327,6 +330,14 @@ t.test('parse(text)', t => { ); } + t.ok(equalBigNumber(JSONZ.parse('NaNm'), Decimal(NaN)), 'parses NaNm'); + t.ok(equalBigNumber(JSONZ.parse('NaN_m'), Decimal(NaN)), 'parses NaN_m'); + t.ok(equalBigNumber(JSONZ.parse('+NaN_m'), Decimal(NaN)), 'parses +NaN_m'); + t.ok(equalBigNumber(JSONZ.parse('Infinitym'), Decimal(Infinity)), 'parses Infinitym'); + t.ok(equalBigNumber(JSONZ.parse('Infinity_m'), Decimal(Infinity)), 'parses Infinity_m'); + t.ok(equalBigNumber(JSONZ.parse('-Infinitym'), Decimal(-Infinity)), 'parses -Infinitym'); + t.ok(equalBigNumber(JSONZ.parse('-Infinity_m'), Decimal(-Infinity)), 'parses -Infinity_m'); + JSONZ.setBigDecimal(null); t.ok(big.getBigDecimalType() === 'numeric', 'can disable big decimal support'); JSONZ.setBigDecimal(Decimal); diff --git a/test/stringify.spec.js b/test/stringify.spec.js index e80fdf2f..a5f96d51 100644 --- a/test/stringify.spec.js +++ b/test/stringify.spec.js @@ -162,6 +162,10 @@ describe('JSONZ', () => { assert.strictEqual(JSONZ.stringify(-0), '-0'); }); + it('doesn\'t create double negative sign', () => { + assert.strictEqual(JSONZ.stringify(-0.1), '-0.1'); + }); + it('stringifies non-finite numbers', () => { assert.strictEqual(JSONZ.stringify([Infinity, -Infinity, NaN]), '[Infinity,-Infinity,NaN]'); }); @@ -212,7 +216,7 @@ describe('JSONZ', () => { it('stringifies bigdecimal special values', () => { assert.strictEqual(JSONZ.stringify([big.toBigDecimal(1 / 0), big.toBigDecimal(-1 / 0), big.toBigDecimal(0 / 0)]), - '[Infinity,-Infinity,NaN]'); + '[Infinity_m,-Infinity_m,NaN_m]'); assert.strictEqual(JSONZ.stringify( big.toBigDecimal(1 / 0),