-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnumber.js
109 lines (96 loc) · 3.27 KB
/
number.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
'use strict'
const otherwise = require('otherwise')
const roundTo = require('round-to')
const sbo = require('sbo')
const thousandsSepOnly = [' ', '\u2009', "'"]
const numberDigits = '0123456789'
const infinities = [
['infinity', '+infinity'],
['-infinity', '\u2212infinity'],
]
const suffixes = ['kg', 'm', 'b']
const stringToNumber = require('parser-factory')('main', {
main ({char, consume, consumeRest, shift}, {userArgs: [{decimalComma, decimalPlacesInInt, ignoreBadChars} = {}]}) {
let thousandsSep = ''
let decimalSep = ''
let decimals = ''
let units = ''
let group = ''
let sign = 1
let leftParen
const rightParen = consume(')')
for (const [i, s] of suffixes.entries()) {
if (consume(...s)) {
units = '000'.repeat(i + 1)
break
}
}
while (char()) {
const sep = consume(',', '.', ...thousandsSepOnly)
if (sep) {
if (!decimals && !thousandsSep && !thousandsSepOnly.includes(sep)) {
decimalSep = sep
decimals = units
units = ''
group = ''
} else if ((group.length !== 3 && !(group.length === 2 && units.length >= 5 && (units - 3) % 2 === 0)) || (thousandsSep && thousandsSep !== sep && !(thousandsSep === '.' && sep === ' ')) || (decimalSep && decimalSep === sep)) {
if (!ignoreBadChars) return NaN
} else {
if (!thousandsSep) thousandsSep = sep
group = ''
}
} else if (consumeRest('-', '\u2212')) {
sign = -1
} else if (rightParen && consumeRest('(')) {
sign = -1
leftParen = true
} else {
const n = shift(1)
if (numberDigits.includes(n)) {
units = n + units
group = n + group
} else if (!ignoreBadChars) {
return NaN
}
}
}
if (!ignoreBadChars && rightParen && !leftParen) return NaN
if (decimals.length === 3 && decimalSep && !thousandsSep && !decimalComma === (decimalSep === ',')) {
units += decimals
decimals = ''
} else if (decimals) {
if (decimalPlacesInInt > 0) {
decimals = decimals.substr(0, decimalPlacesInInt).padEnd(2, '0')
} else {
decimals = '.' + decimals
}
}
const n = units + decimals
if (!n.length) return NaN
return sign * Number(n)
},
})
module.exports = sbo(function toNumber (x, {decimalComma, decimalPlacesInInt, elseCall, elseReturn = 0, elseThrow, finite = true, ignoreBadChars, round = false} = {}) {
if (x instanceof String) x = String(x)
if (typeof x === 'string') {
x = x.toLowerCase()
const testForInfinityString = positive => infinities[+!positive].some(
ignoreBadChars ? inf => x.includes(inf) : inf => x === inf,
)
x = testForInfinityString(true) ? Infinity
: testForInfinityString(false) ? -Infinity
: stringToNumber(
x.split('').reverse().join(''),
{decimalComma, decimalPlacesInInt, ignoreBadChars},
)
} else if (x !== null) {
x = Number(x)
}
if (x === null || Number.isNaN(x) || (finite && !Number.isFinite(x))) {
return otherwise({elseCall, elseReturn, elseThrow}, {defaultErrorClass: TypeError})
}
if (!decimalPlacesInInt && (round || round === 0)) {
return roundTo(x, round === true ? 0 : +round)
}
return x
})