2024-02-24 21:00:04 +00:00
|
|
|
package otto
|
|
|
|
|
|
|
|
import (
|
2024-04-04 14:46:14 +00:00
|
|
|
"errors"
|
2024-02-24 21:00:04 +00:00
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
var stringToNumberParseInteger = regexp.MustCompile(`^(?:0[xX])`)
|
|
|
|
|
|
|
|
func parseNumber(value string) float64 {
|
2024-04-04 14:46:14 +00:00
|
|
|
value = strings.Trim(value, builtinStringTrimWhitespace)
|
2024-02-24 21:00:04 +00:00
|
|
|
|
|
|
|
if value == "" {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
var parseFloat bool
|
|
|
|
switch {
|
|
|
|
case strings.ContainsRune(value, '.'):
|
2024-02-24 21:00:04 +00:00
|
|
|
parseFloat = true
|
2024-04-04 14:46:14 +00:00
|
|
|
case stringToNumberParseInteger.MatchString(value):
|
2024-02-24 21:00:04 +00:00
|
|
|
parseFloat = false
|
2024-04-04 14:46:14 +00:00
|
|
|
default:
|
2024-02-24 21:00:04 +00:00
|
|
|
parseFloat = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if parseFloat {
|
|
|
|
number, err := strconv.ParseFloat(value, 64)
|
2024-04-04 14:46:14 +00:00
|
|
|
if err != nil && !errors.Is(err, strconv.ErrRange) {
|
2024-02-24 21:00:04 +00:00
|
|
|
return math.NaN()
|
|
|
|
}
|
|
|
|
return number
|
|
|
|
}
|
|
|
|
|
|
|
|
number, err := strconv.ParseInt(value, 0, 64)
|
|
|
|
if err != nil {
|
|
|
|
return math.NaN()
|
|
|
|
}
|
|
|
|
return float64(number)
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
func (v Value) float64() float64 {
|
|
|
|
switch v.kind {
|
2024-02-24 21:00:04 +00:00
|
|
|
case valueUndefined:
|
|
|
|
return math.NaN()
|
|
|
|
case valueNull:
|
|
|
|
return 0
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
switch value := v.value.(type) {
|
2024-02-24 21:00:04 +00:00
|
|
|
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)
|
2024-04-04 14:46:14 +00:00
|
|
|
case *object:
|
2024-02-24 21:00:04 +00:00
|
|
|
return value.DefaultValue(defaultValueHintNumber).float64()
|
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
panic(fmt.Errorf("toFloat(%T)", v.value))
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2024-04-04 14:46:14 +00:00
|
|
|
sqrt1_2 float64 = math.Sqrt2 / 2
|
2024-02-24 21:00:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
maxUint32 = math.MaxUint32
|
|
|
|
maxInt = int(^uint(0) >> 1)
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
// int64.
|
|
|
|
int64MaxInt8 int64 = math.MaxInt8
|
|
|
|
int64MinInt8 int64 = math.MinInt8
|
|
|
|
int64MaxInt16 int64 = math.MaxInt16
|
|
|
|
int64MinInt16 int64 = math.MinInt16
|
|
|
|
int64MaxInt32 int64 = math.MaxInt32
|
|
|
|
int64MinInt32 int64 = math.MinInt32
|
|
|
|
int64MaxUint8 int64 = math.MaxUint8
|
|
|
|
int64MaxUint16 int64 = math.MaxUint16
|
|
|
|
int64MaxUint32 int64 = math.MaxUint32
|
|
|
|
|
|
|
|
// float64.
|
|
|
|
floatMaxInt float64 = float64(int(^uint(0) >> 1))
|
|
|
|
floatMinInt float64 = float64(-maxInt - 1)
|
|
|
|
floatMaxUint float64 = float64(^uint(0))
|
|
|
|
floatMaxUint64 float64 = math.MaxUint64
|
|
|
|
floatMaxInt64 float64 = math.MaxInt64
|
|
|
|
floatMinInt64 float64 = math.MinInt64
|
2024-02-24 21:00:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func toIntegerFloat(value Value) float64 {
|
|
|
|
float := value.float64()
|
2024-04-04 14:46:14 +00:00
|
|
|
switch {
|
|
|
|
case math.IsInf(float, 0):
|
|
|
|
return float
|
|
|
|
case math.IsNaN(float):
|
|
|
|
return 0
|
|
|
|
case float > 0:
|
|
|
|
return math.Floor(float)
|
|
|
|
default:
|
|
|
|
return math.Ceil(float)
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
type numberKind int
|
2024-02-24 21:00:04 +00:00
|
|
|
|
|
|
|
const (
|
2024-04-04 14:46:14 +00:00
|
|
|
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
|
2024-02-24 21:00:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type _number struct {
|
2024-04-04 14:46:14 +00:00
|
|
|
kind numberKind
|
2024-02-24 21:00:04 +00:00
|
|
|
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
|
2024-04-04 14:46:14 +00:00
|
|
|
func (v Value) number() _number {
|
|
|
|
var num _number
|
|
|
|
switch value := v.value.(type) {
|
2024-02-24 21:00:04 +00:00
|
|
|
case int8:
|
2024-04-04 14:46:14 +00:00
|
|
|
num.int64 = int64(value)
|
|
|
|
return num
|
2024-02-24 21:00:04 +00:00
|
|
|
case int16:
|
2024-04-04 14:46:14 +00:00
|
|
|
num.int64 = int64(value)
|
|
|
|
return num
|
2024-02-24 21:00:04 +00:00
|
|
|
case uint8:
|
2024-04-04 14:46:14 +00:00
|
|
|
num.int64 = int64(value)
|
|
|
|
return num
|
2024-02-24 21:00:04 +00:00
|
|
|
case uint16:
|
2024-04-04 14:46:14 +00:00
|
|
|
num.int64 = int64(value)
|
|
|
|
return num
|
2024-02-24 21:00:04 +00:00
|
|
|
case uint32:
|
2024-04-04 14:46:14 +00:00
|
|
|
num.int64 = int64(value)
|
|
|
|
return num
|
2024-02-24 21:00:04 +00:00
|
|
|
case int:
|
2024-04-04 14:46:14 +00:00
|
|
|
num.int64 = int64(value)
|
|
|
|
return num
|
2024-02-24 21:00:04 +00:00
|
|
|
case int64:
|
2024-04-04 14:46:14 +00:00
|
|
|
num.int64 = value
|
|
|
|
return num
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
float := v.float64()
|
2024-02-24 21:00:04 +00:00
|
|
|
if float == 0 {
|
2024-04-04 14:46:14 +00:00
|
|
|
return num
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
num.kind = numberFloat
|
|
|
|
num.float64 = float
|
2024-02-24 21:00:04 +00:00
|
|
|
|
|
|
|
if math.IsNaN(float) {
|
2024-04-04 14:46:14 +00:00
|
|
|
num.kind = numberNaN
|
|
|
|
return num
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if math.IsInf(float, 0) {
|
2024-04-04 14:46:14 +00:00
|
|
|
num.kind = numberInfinity
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
if float >= floatMaxInt64 {
|
|
|
|
num.int64 = math.MaxInt64
|
|
|
|
return num
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
if float <= floatMinInt64 {
|
|
|
|
num.int64 = math.MinInt64
|
|
|
|
return num
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var integer float64
|
|
|
|
if float > 0 {
|
|
|
|
integer = math.Floor(float)
|
|
|
|
} else {
|
|
|
|
integer = math.Ceil(float)
|
|
|
|
}
|
|
|
|
|
|
|
|
if float == integer {
|
2024-04-04 14:46:14 +00:00
|
|
|
num.kind = numberInteger
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
2024-04-04 14:46:14 +00:00
|
|
|
num.int64 = int64(float)
|
|
|
|
return num
|
2024-02-24 21:00:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
// ECMA 262: 9.5.
|
2024-02-24 21:00:04 +00:00
|
|
|
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) || floatValue == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert to int64 before int32 to force correct wrapping.
|
|
|
|
return int32(int64(floatValue))
|
|
|
|
}
|
|
|
|
|
|
|
|
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) || floatValue == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert to int64 before uint32 to force correct wrapping.
|
|
|
|
return uint32(int64(floatValue))
|
|
|
|
}
|
|
|
|
|
|
|
|
// ECMA 262 - 6.0 - 7.1.8.
|
|
|
|
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) || floatValue == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert to int64 before uint16 to force correct wrapping.
|
|
|
|
return uint16(int64(floatValue))
|
|
|
|
}
|
|
|
|
|
2024-04-04 14:46:14 +00:00
|
|
|
// toIntSign returns sign of a number converted to -1, 0 ,1.
|
2024-02-24 21:00:04 +00:00
|
|
|
func toIntSign(value Value) int {
|
|
|
|
switch value := value.value.(type) {
|
|
|
|
case int8:
|
2024-04-04 14:46:14 +00:00
|
|
|
if value > 0 {
|
2024-02-24 21:00:04 +00:00
|
|
|
return 1
|
2024-04-04 14:46:14 +00:00
|
|
|
} else if value < 0 {
|
2024-02-24 21:00:04 +00:00
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0
|
|
|
|
case int16:
|
2024-04-04 14:46:14 +00:00
|
|
|
if value > 0 {
|
2024-02-24 21:00:04 +00:00
|
|
|
return 1
|
2024-04-04 14:46:14 +00:00
|
|
|
} else if value < 0 {
|
2024-02-24 21:00:04 +00:00
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0
|
|
|
|
case int32:
|
2024-04-04 14:46:14 +00:00
|
|
|
if value > 0 {
|
2024-02-24 21:00:04 +00:00
|
|
|
return 1
|
2024-04-04 14:46:14 +00:00
|
|
|
} else if value < 0 {
|
2024-02-24 21:00:04 +00:00
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0
|
|
|
|
case uint8:
|
2024-04-04 14:46:14 +00:00
|
|
|
if value > 0 {
|
2024-02-24 21:00:04 +00:00
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0
|
|
|
|
case uint16:
|
2024-04-04 14:46:14 +00:00
|
|
|
if value > 0 {
|
2024-02-24 21:00:04 +00:00
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0
|
|
|
|
case uint32:
|
2024-04-04 14:46:14 +00:00
|
|
|
if value > 0 {
|
2024-02-24 21:00:04 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|