otto/value_number.go
Steven Hartland fbcfda961e
ci: fix golangci-lint errors (#496)
Fix golangci-lint errors triggered by new version.

Disable deprecated linters.
2023-05-19 13:04:21 +01:00

337 lines
6.0 KiB
Go

package otto
import (
"errors"
"fmt"
"math"
"regexp"
"strconv"
"strings"
)
var stringToNumberParseInteger = regexp.MustCompile(`^(?:0[xX])`)
func parseNumber(value string) float64 {
value = strings.Trim(value, builtinStringTrimWhitespace)
if value == "" {
return 0
}
var parseFloat bool
switch {
case strings.ContainsRune(value, '.'):
parseFloat = true
case stringToNumberParseInteger.MatchString(value):
parseFloat = false
default:
parseFloat = true
}
if parseFloat {
number, err := strconv.ParseFloat(value, 64)
if err != nil && !errors.Is(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 (v Value) float64() float64 {
switch v.kind {
case valueUndefined:
return math.NaN()
case valueNull:
return 0
}
switch value := v.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)", v.value))
}
const (
sqrt1_2 float64 = math.Sqrt2 / 2
)
const (
maxUint32 = math.MaxUint32
maxInt = int(^uint(0) >> 1)
// 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
)
func toIntegerFloat(value Value) float64 {
float := value.float64()
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)
}
}
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 (v Value) number() _number {
var num _number
switch value := v.value.(type) {
case int8:
num.int64 = int64(value)
return num
case int16:
num.int64 = int64(value)
return num
case uint8:
num.int64 = int64(value)
return num
case uint16:
num.int64 = int64(value)
return num
case uint32:
num.int64 = int64(value)
return num
case int:
num.int64 = int64(value)
return num
case int64:
num.int64 = value
return num
}
float := v.float64()
if float == 0 {
return num
}
num.kind = numberFloat
num.float64 = float
if math.IsNaN(float) {
num.kind = numberNaN
return num
}
if math.IsInf(float, 0) {
num.kind = numberInfinity
}
if float >= floatMaxInt64 {
num.int64 = math.MaxInt64
return num
}
if float <= floatMinInt64 {
num.int64 = math.MinInt64
return num
}
var integer float64
if float > 0 {
integer = math.Floor(float)
} else {
integer = math.Ceil(float)
}
if float == integer {
num.kind = numberInteger
}
num.int64 = int64(float)
return num
}
// 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) || 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))
}
// toIntSign returns sign of a number converted to -1, 0 ,1.
func toIntSign(value Value) int {
switch value := value.value.(type) {
case int8:
if value > 0 {
return 1
} else if value < 0 {
return -1
}
return 0
case int16:
if value > 0 {
return 1
} else if value < 0 {
return -1
}
return 0
case int32:
if value > 0 {
return 1
} else if value < 0 {
return -1
}
return 0
case uint8:
if value > 0 {
return 1
}
return 0
case uint16:
if value > 0 {
return 1
}
return 0
case uint32:
if 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
}
}