forked from golang/hotime
362 lines
6.7 KiB
Go
362 lines
6.7 KiB
Go
package otto
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var stringToNumberParseInteger = regexp.MustCompile(`^(?:0[xX])`)
|
|
|
|
func parseNumber(value string) float64 {
|
|
value = strings.Trim(value, builtinString_trim_whitespace)
|
|
|
|
if value == "" {
|
|
return 0
|
|
}
|
|
|
|
parseFloat := false
|
|
if strings.IndexRune(value, '.') != -1 {
|
|
parseFloat = true
|
|
} else if stringToNumberParseInteger.MatchString(value) {
|
|
parseFloat = false
|
|
} else {
|
|
parseFloat = true
|
|
}
|
|
|
|
if parseFloat {
|
|
number, err := strconv.ParseFloat(value, 64)
|
|
if err != nil && err.(*strconv.NumError).Err != strconv.ErrRange {
|
|
return math.NaN()
|
|
}
|
|
return number
|
|
}
|
|
|
|
number, err := strconv.ParseInt(value, 0, 64)
|
|
if err != nil {
|
|
return math.NaN()
|
|
}
|
|
return float64(number)
|
|
}
|
|
|
|
func (value Value) float64() float64 {
|
|
switch value.kind {
|
|
case valueUndefined:
|
|
return math.NaN()
|
|
case valueNull:
|
|
return 0
|
|
}
|
|
switch value := value.value.(type) {
|
|
case bool:
|
|
if value {
|
|
return 1
|
|
}
|
|
return 0
|
|
case int:
|
|
return float64(value)
|
|
case int8:
|
|
return float64(value)
|
|
case int16:
|
|
return float64(value)
|
|
case int32:
|
|
return float64(value)
|
|
case int64:
|
|
return float64(value)
|
|
case uint:
|
|
return float64(value)
|
|
case uint8:
|
|
return float64(value)
|
|
case uint16:
|
|
return float64(value)
|
|
case uint32:
|
|
return float64(value)
|
|
case uint64:
|
|
return float64(value)
|
|
case float64:
|
|
return value
|
|
case string:
|
|
return parseNumber(value)
|
|
case *_object:
|
|
return value.DefaultValue(defaultValueHintNumber).float64()
|
|
}
|
|
panic(fmt.Errorf("toFloat(%T)", value.value))
|
|
}
|
|
|
|
const (
|
|
float_2_32 float64 = 4294967296.0
|
|
float_2_31 float64 = 2147483648.0
|
|
float_2_16 float64 = 65536.0
|
|
sqrt1_2 float64 = math.Sqrt2 / 2
|
|
)
|
|
|
|
const (
|
|
maxUint32 = math.MaxUint32
|
|
maxInt = int(^uint(0) >> 1)
|
|
|
|
// int64
|
|
int64_maxInt8 int64 = math.MaxInt8
|
|
int64_minInt8 int64 = math.MinInt8
|
|
int64_maxInt16 int64 = math.MaxInt16
|
|
int64_minInt16 int64 = math.MinInt16
|
|
int64_maxInt32 int64 = math.MaxInt32
|
|
int64_minInt32 int64 = math.MinInt32
|
|
int64_maxUint8 int64 = math.MaxUint8
|
|
int64_maxUint16 int64 = math.MaxUint16
|
|
int64_maxUint32 int64 = math.MaxUint32
|
|
|
|
// float64
|
|
float_maxInt float64 = float64(int(^uint(0) >> 1))
|
|
float_minInt float64 = float64(int(-maxInt - 1))
|
|
float_maxUint float64 = float64(uint(^uint(0)))
|
|
float_maxUint64 float64 = math.MaxUint64
|
|
float_maxInt64 float64 = math.MaxInt64
|
|
float_minInt64 float64 = math.MinInt64
|
|
)
|
|
|
|
func toIntegerFloat(value Value) float64 {
|
|
float := value.float64()
|
|
if math.IsInf(float, 0) {
|
|
} else if math.IsNaN(float) {
|
|
float = 0
|
|
} else if float > 0 {
|
|
float = math.Floor(float)
|
|
} else {
|
|
float = math.Ceil(float)
|
|
}
|
|
return float
|
|
}
|
|
|
|
type _numberKind int
|
|
|
|
const (
|
|
numberInteger _numberKind = iota // 3.0 => 3.0
|
|
numberFloat // 3.14159 => 3.0, 1+2**63 > 2**63-1
|
|
numberInfinity // Infinity => 2**63-1
|
|
numberNaN // NaN => 0
|
|
)
|
|
|
|
type _number struct {
|
|
kind _numberKind
|
|
int64 int64
|
|
float64 float64
|
|
}
|
|
|
|
// FIXME
|
|
// http://www.goinggo.net/2013/08/gustavos-ieee-754-brain-teaser.html
|
|
// http://bazaar.launchpad.net/~niemeyer/strepr/trunk/view/6/strepr.go#L160
|
|
func (value Value) number() (number _number) {
|
|
switch value := value.value.(type) {
|
|
case int8:
|
|
number.int64 = int64(value)
|
|
return
|
|
case int16:
|
|
number.int64 = int64(value)
|
|
return
|
|
case uint8:
|
|
number.int64 = int64(value)
|
|
return
|
|
case uint16:
|
|
number.int64 = int64(value)
|
|
return
|
|
case uint32:
|
|
number.int64 = int64(value)
|
|
return
|
|
case int:
|
|
number.int64 = int64(value)
|
|
return
|
|
case int64:
|
|
number.int64 = value
|
|
return
|
|
}
|
|
|
|
float := value.float64()
|
|
if float == 0 {
|
|
return
|
|
}
|
|
|
|
number.kind = numberFloat
|
|
number.float64 = float
|
|
|
|
if math.IsNaN(float) {
|
|
number.kind = numberNaN
|
|
return
|
|
}
|
|
|
|
if math.IsInf(float, 0) {
|
|
number.kind = numberInfinity
|
|
}
|
|
|
|
if float >= float_maxInt64 {
|
|
number.int64 = math.MaxInt64
|
|
return
|
|
}
|
|
|
|
if float <= float_minInt64 {
|
|
number.int64 = math.MinInt64
|
|
return
|
|
}
|
|
|
|
var integer float64
|
|
if float > 0 {
|
|
integer = math.Floor(float)
|
|
} else {
|
|
integer = math.Ceil(float)
|
|
}
|
|
|
|
if float == integer {
|
|
number.kind = numberInteger
|
|
}
|
|
number.int64 = int64(float)
|
|
return
|
|
}
|
|
|
|
// ECMA 262: 9.5
|
|
func toInt32(value Value) int32 {
|
|
{
|
|
switch value := value.value.(type) {
|
|
case int8:
|
|
return int32(value)
|
|
case int16:
|
|
return int32(value)
|
|
case int32:
|
|
return value
|
|
}
|
|
}
|
|
floatValue := value.float64()
|
|
if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) {
|
|
return 0
|
|
}
|
|
if floatValue == 0 { // This will work for +0 & -0
|
|
return 0
|
|
}
|
|
remainder := math.Mod(floatValue, float_2_32)
|
|
if remainder > 0 {
|
|
remainder = math.Floor(remainder)
|
|
} else {
|
|
remainder = math.Ceil(remainder) + float_2_32
|
|
}
|
|
if remainder > float_2_31 {
|
|
return int32(remainder - float_2_32)
|
|
}
|
|
return int32(remainder)
|
|
}
|
|
|
|
func toUint32(value Value) uint32 {
|
|
{
|
|
switch value := value.value.(type) {
|
|
case int8:
|
|
return uint32(value)
|
|
case int16:
|
|
return uint32(value)
|
|
case uint8:
|
|
return uint32(value)
|
|
case uint16:
|
|
return uint32(value)
|
|
case uint32:
|
|
return value
|
|
}
|
|
}
|
|
floatValue := value.float64()
|
|
if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) {
|
|
return 0
|
|
}
|
|
if floatValue == 0 {
|
|
return 0
|
|
}
|
|
remainder := math.Mod(floatValue, float_2_32)
|
|
if remainder > 0 {
|
|
remainder = math.Floor(remainder)
|
|
} else {
|
|
remainder = math.Ceil(remainder) + float_2_32
|
|
}
|
|
return uint32(remainder)
|
|
}
|
|
|
|
func toUint16(value Value) uint16 {
|
|
{
|
|
switch value := value.value.(type) {
|
|
case int8:
|
|
return uint16(value)
|
|
case uint8:
|
|
return uint16(value)
|
|
case uint16:
|
|
return value
|
|
}
|
|
}
|
|
floatValue := value.float64()
|
|
if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) {
|
|
return 0
|
|
}
|
|
if floatValue == 0 {
|
|
return 0
|
|
}
|
|
remainder := math.Mod(floatValue, float_2_16)
|
|
if remainder > 0 {
|
|
remainder = math.Floor(remainder)
|
|
} else {
|
|
remainder = math.Ceil(remainder) + float_2_16
|
|
}
|
|
return uint16(remainder)
|
|
}
|
|
|
|
// toIntSign returns sign of a number converted to -1, 0 ,1
|
|
func toIntSign(value Value) int {
|
|
switch value := value.value.(type) {
|
|
case int8:
|
|
if int8(value) > 0 {
|
|
return 1
|
|
} else if int8(value) < 0 {
|
|
return -1
|
|
}
|
|
|
|
return 0
|
|
case int16:
|
|
if int16(value) > 0 {
|
|
return 1
|
|
} else if int16(value) < 0 {
|
|
return -1
|
|
}
|
|
|
|
return 0
|
|
case int32:
|
|
if int32(value) > 0 {
|
|
return 1
|
|
} else if int32(value) < 0 {
|
|
return -1
|
|
}
|
|
|
|
return 0
|
|
case uint8:
|
|
if uint8(value) > 0 {
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
case uint16:
|
|
if uint16(value) > 0 {
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
case uint32:
|
|
if uint32(value) > 0 {
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
floatValue := value.float64()
|
|
switch {
|
|
case math.IsNaN(floatValue), math.IsInf(floatValue, 0):
|
|
return 0
|
|
case floatValue == 0:
|
|
return 0
|
|
case floatValue > 0:
|
|
return 1
|
|
default:
|
|
return -1
|
|
}
|
|
}
|