1package otto
2
3import (
4	"fmt"
5	"math"
6	"regexp"
7	"strconv"
8	"strings"
9)
10
11var stringToNumberParseInteger = regexp.MustCompile(`^(?:0[xX])`)
12
13func parseNumber(value string) float64 {
14	value = strings.Trim(value, builtinString_trim_whitespace)
15
16	if value == "" {
17		return 0
18	}
19
20	parseFloat := false
21	if strings.IndexRune(value, '.') != -1 {
22		parseFloat = true
23	} else if stringToNumberParseInteger.MatchString(value) {
24		parseFloat = false
25	} else {
26		parseFloat = true
27	}
28
29	if parseFloat {
30		number, err := strconv.ParseFloat(value, 64)
31		if err != nil && err.(*strconv.NumError).Err != strconv.ErrRange {
32			return math.NaN()
33		}
34		return number
35	}
36
37	number, err := strconv.ParseInt(value, 0, 64)
38	if err != nil {
39		return math.NaN()
40	}
41	return float64(number)
42}
43
44func (value Value) float64() float64 {
45	switch value.kind {
46	case valueUndefined:
47		return math.NaN()
48	case valueNull:
49		return 0
50	}
51	switch value := value.value.(type) {
52	case bool:
53		if value {
54			return 1
55		}
56		return 0
57	case int:
58		return float64(value)
59	case int8:
60		return float64(value)
61	case int16:
62		return float64(value)
63	case int32:
64		return float64(value)
65	case int64:
66		return float64(value)
67	case uint:
68		return float64(value)
69	case uint8:
70		return float64(value)
71	case uint16:
72		return float64(value)
73	case uint32:
74		return float64(value)
75	case uint64:
76		return float64(value)
77	case float64:
78		return value
79	case string:
80		return parseNumber(value)
81	case *_object:
82		return value.DefaultValue(defaultValueHintNumber).float64()
83	}
84	panic(fmt.Errorf("toFloat(%T)", value.value))
85}
86
87const (
88	float_2_64   float64 = 18446744073709551616.0
89	float_2_63   float64 = 9223372036854775808.0
90	float_2_32   float64 = 4294967296.0
91	float_2_31   float64 = 2147483648.0
92	float_2_16   float64 = 65536.0
93	integer_2_32 int64   = 4294967296
94	integer_2_31 int64   = 2146483648
95	sqrt1_2      float64 = math.Sqrt2 / 2
96)
97
98const (
99	maxInt8   = math.MaxInt8
100	minInt8   = math.MinInt8
101	maxInt16  = math.MaxInt16
102	minInt16  = math.MinInt16
103	maxInt32  = math.MaxInt32
104	minInt32  = math.MinInt32
105	maxInt64  = math.MaxInt64
106	minInt64  = math.MinInt64
107	maxUint8  = math.MaxUint8
108	maxUint16 = math.MaxUint16
109	maxUint32 = math.MaxUint32
110	maxUint64 = math.MaxUint64
111	maxUint   = ^uint(0)
112	minUint   = 0
113	maxInt    = int(^uint(0) >> 1)
114	minInt    = -maxInt - 1
115
116	// int64
117	int64_maxInt    int64 = int64(maxInt)
118	int64_minInt    int64 = int64(minInt)
119	int64_maxInt8   int64 = math.MaxInt8
120	int64_minInt8   int64 = math.MinInt8
121	int64_maxInt16  int64 = math.MaxInt16
122	int64_minInt16  int64 = math.MinInt16
123	int64_maxInt32  int64 = math.MaxInt32
124	int64_minInt32  int64 = math.MinInt32
125	int64_maxUint8  int64 = math.MaxUint8
126	int64_maxUint16 int64 = math.MaxUint16
127	int64_maxUint32 int64 = math.MaxUint32
128
129	// float64
130	float_maxInt    float64 = float64(int(^uint(0) >> 1))
131	float_minInt    float64 = float64(int(-maxInt - 1))
132	float_minUint   float64 = float64(0)
133	float_maxUint   float64 = float64(uint(^uint(0)))
134	float_minUint64 float64 = float64(0)
135	float_maxUint64 float64 = math.MaxUint64
136	float_maxInt64  float64 = math.MaxInt64
137	float_minInt64  float64 = math.MinInt64
138)
139
140func toIntegerFloat(value Value) float64 {
141	float := value.float64()
142	if math.IsInf(float, 0) {
143	} else if math.IsNaN(float) {
144		float = 0
145	} else if float > 0 {
146		float = math.Floor(float)
147	} else {
148		float = math.Ceil(float)
149	}
150	return float
151}
152
153type _numberKind int
154
155const (
156	numberInteger  _numberKind = iota // 3.0 => 3.0
157	numberFloat                       // 3.14159 => 3.0, 1+2**63 > 2**63-1
158	numberInfinity                    // Infinity => 2**63-1
159	numberNaN                         // NaN => 0
160)
161
162type _number struct {
163	kind    _numberKind
164	int64   int64
165	float64 float64
166}
167
168// FIXME
169// http://www.goinggo.net/2013/08/gustavos-ieee-754-brain-teaser.html
170// http://bazaar.launchpad.net/~niemeyer/strepr/trunk/view/6/strepr.go#L160
171func (value Value) number() (number _number) {
172	switch value := value.value.(type) {
173	case int8:
174		number.int64 = int64(value)
175		return
176	case int16:
177		number.int64 = int64(value)
178		return
179	case uint8:
180		number.int64 = int64(value)
181		return
182	case uint16:
183		number.int64 = int64(value)
184		return
185	case uint32:
186		number.int64 = int64(value)
187		return
188	case int:
189		number.int64 = int64(value)
190		return
191	case int64:
192		number.int64 = value
193		return
194	}
195
196	float := value.float64()
197	if float == 0 {
198		return
199	}
200
201	number.kind = numberFloat
202	number.float64 = float
203
204	if math.IsNaN(float) {
205		number.kind = numberNaN
206		return
207	}
208
209	if math.IsInf(float, 0) {
210		number.kind = numberInfinity
211	}
212
213	if float >= float_maxInt64 {
214		number.int64 = math.MaxInt64
215		return
216	}
217
218	if float <= float_minInt64 {
219		number.int64 = math.MinInt64
220		return
221	}
222
223	integer := float64(0)
224	if float > 0 {
225		integer = math.Floor(float)
226	} else {
227		integer = math.Ceil(float)
228	}
229
230	if float == integer {
231		number.kind = numberInteger
232	}
233	number.int64 = int64(float)
234	return
235}
236
237// ECMA 262: 9.5
238func toInt32(value Value) int32 {
239	{
240		switch value := value.value.(type) {
241		case int8:
242			return int32(value)
243		case int16:
244			return int32(value)
245		case int32:
246			return value
247		}
248	}
249	floatValue := value.float64()
250	if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) {
251		return 0
252	}
253	if floatValue == 0 { // This will work for +0 & -0
254		return 0
255	}
256	remainder := math.Mod(floatValue, float_2_32)
257	if remainder > 0 {
258		remainder = math.Floor(remainder)
259	} else {
260		remainder = math.Ceil(remainder) + float_2_32
261	}
262	if remainder > float_2_31 {
263		return int32(remainder - float_2_32)
264	}
265	return int32(remainder)
266}
267
268func toUint32(value Value) uint32 {
269	{
270		switch value := value.value.(type) {
271		case int8:
272			return uint32(value)
273		case int16:
274			return uint32(value)
275		case uint8:
276			return uint32(value)
277		case uint16:
278			return uint32(value)
279		case uint32:
280			return value
281		}
282	}
283	floatValue := value.float64()
284	if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) {
285		return 0
286	}
287	if floatValue == 0 {
288		return 0
289	}
290	remainder := math.Mod(floatValue, float_2_32)
291	if remainder > 0 {
292		remainder = math.Floor(remainder)
293	} else {
294		remainder = math.Ceil(remainder) + float_2_32
295	}
296	return uint32(remainder)
297}
298
299func toUint16(value Value) uint16 {
300	{
301		switch value := value.value.(type) {
302		case int8:
303			return uint16(value)
304		case uint8:
305			return uint16(value)
306		case uint16:
307			return value
308		}
309	}
310	floatValue := value.float64()
311	if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) {
312		return 0
313	}
314	if floatValue == 0 {
315		return 0
316	}
317	remainder := math.Mod(floatValue, float_2_16)
318	if remainder > 0 {
319		remainder = math.Floor(remainder)
320	} else {
321		remainder = math.Ceil(remainder) + float_2_16
322	}
323	return uint16(remainder)
324}
325