mirror of
https://github.com/robertkrimen/otto
synced 2025-10-19 19:55:30 +08:00
Make parseInt behavior correspond to the 15.1.2.2 algorithm
This commit is contained in:
parent
2ec9b0843c
commit
b5e40f8a85
75
builtin.go
75
builtin.go
|
|
@ -49,18 +49,77 @@ func builtinGlobal_isFinite(call FunctionCall) Value {
|
|||
return toValue(!math.IsNaN(value) && !math.IsInf(value, 0))
|
||||
}
|
||||
|
||||
func builtinGlobal_parseInt(call FunctionCall) Value {
|
||||
// Caveat emptor: This implementation does NOT match the specification
|
||||
string_ := strings.TrimSpace(toString(call.Argument(0)))
|
||||
radix := call.Argument(1)
|
||||
radixValue := 0
|
||||
if radix.IsDefined() {
|
||||
radixValue = int(toInt32(radix))
|
||||
// radix 3 => 2 (ASCII 50) +47
|
||||
// radix 11 => A/a (ASCII 65/97) +54/+86
|
||||
var parseInt_alphabetTable = func() []string {
|
||||
table := []string{"", "", "01"}
|
||||
for radix := 3; radix <= 36; radix += 1 {
|
||||
alphabet := table[radix-1]
|
||||
if radix <= 10 {
|
||||
alphabet += string(radix + 47)
|
||||
} else {
|
||||
alphabet += string(radix+54) + string(radix+86)
|
||||
}
|
||||
table = append(table, alphabet)
|
||||
}
|
||||
value, err := strconv.ParseInt(string_, radixValue, 64)
|
||||
return table
|
||||
}()
|
||||
|
||||
func builtinGlobal_parseInt(call FunctionCall) Value {
|
||||
input := strings.TrimSpace(toString(call.Argument(0)))
|
||||
if len(input) == 0 {
|
||||
return NaNValue()
|
||||
}
|
||||
|
||||
radix := int(toInt32(call.Argument(1)))
|
||||
|
||||
sign := int64(1)
|
||||
switch input[0] {
|
||||
case '+':
|
||||
sign = 1
|
||||
input = input[1:]
|
||||
case '-':
|
||||
sign = -1
|
||||
input = input[1:]
|
||||
}
|
||||
|
||||
strip := true
|
||||
if radix == 0 {
|
||||
radix = 10
|
||||
} else {
|
||||
if radix < 2 || radix > 36 {
|
||||
return NaNValue()
|
||||
} else if radix != 16 {
|
||||
strip = false
|
||||
}
|
||||
}
|
||||
|
||||
switch len(input) {
|
||||
case 0:
|
||||
return NaNValue()
|
||||
case 1:
|
||||
default:
|
||||
if strip {
|
||||
if input[0] == '0' && (input[1] == 'x' || input[1] == 'X') {
|
||||
input = input[2:]
|
||||
radix = 16
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alphabet := parseInt_alphabetTable[radix]
|
||||
if index := strings.IndexFunc(input, func(chr rune) bool {
|
||||
return !strings.ContainsRune(alphabet, chr)
|
||||
}); index != -1 {
|
||||
input = input[0:index]
|
||||
}
|
||||
|
||||
value, err := strconv.ParseInt(input, radix, 64)
|
||||
if err != nil {
|
||||
return NaNValue()
|
||||
}
|
||||
value *= sign
|
||||
|
||||
return toValue(value)
|
||||
}
|
||||
|
||||
|
|
|
|||
23
global.go
23
global.go
|
|
@ -701,8 +701,8 @@ func newContext() *_runtime {
|
|||
"undefined", UndefinedValue(),
|
||||
"NaN", NaNValue(),
|
||||
"Infinity", positiveInfinityValue(),
|
||||
"eval", 1, builtinGlobal_eval,
|
||||
"parseInt", builtinGlobal_parseInt,
|
||||
"eval", -1, builtinGlobal_eval,
|
||||
"parseInt", -2, builtinGlobal_parseInt,
|
||||
"parseFloat", builtinGlobal_parseFloat,
|
||||
"isNaN", builtinGlobal_isNaN,
|
||||
"isFinite", builtinGlobal_isFinite,
|
||||
|
|
@ -722,9 +722,6 @@ func newContext() *_runtime {
|
|||
self._newError["URIError"] = self.defineError("URIError")
|
||||
|
||||
self.eval = self.GlobalObject.get("eval")._object()
|
||||
// eval.prototype === undefined
|
||||
self.eval.stash.delete("prototype")
|
||||
self.eval._Function.Construct = nil
|
||||
|
||||
self.GlobalObject.prototype = self.Global.ObjectPrototype
|
||||
|
||||
|
|
@ -817,11 +814,21 @@ func (runtime *_runtime) newError(name string, message Value) *_object {
|
|||
}
|
||||
|
||||
func (runtime *_runtime) newNativeFunction(_nativeFunction _nativeFunction, length int, name string) *_object {
|
||||
prototype := true
|
||||
// TODO Do this a better way...
|
||||
if 0 > length {
|
||||
length *= -1
|
||||
prototype = false
|
||||
}
|
||||
self := runtime.newNativeFunctionObject(_nativeFunction, length, name)
|
||||
self.prototype = runtime.Global.FunctionPrototype
|
||||
prototype := runtime.newObject()
|
||||
self.stash.set("prototype", toValue(prototype), _propertyMode(0100))
|
||||
prototype.stash.set("constructor", toValue(self), _propertyMode(0101))
|
||||
if prototype {
|
||||
prototype := runtime.newObject()
|
||||
self.stash.set("prototype", toValue(prototype), _propertyMode(0100))
|
||||
prototype.stash.set("constructor", toValue(self), _propertyMode(0101))
|
||||
} else {
|
||||
self._Function.Construct = nil
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -119,13 +119,15 @@ func Test_parseInt(t *testing.T) {
|
|||
test(`parseInt(" 11\n")`, "11")
|
||||
test(`parseInt(" 11\n", 16)`, "17")
|
||||
test(`parseInt("Xyzzy")`, "NaN")
|
||||
test(`parseInt("0x0a")`, "10")
|
||||
test(`parseInt(" 0x11\n", 16)`, "17")
|
||||
test(`parseInt("0x0aXyzzy", 16)`, "10")
|
||||
test(`parseInt("0x1", 0)`, "1")
|
||||
if false {
|
||||
test(`parseInt(" 0x11\n", 16)`, "17")
|
||||
// TODO parseInt should return 10 in this scenario
|
||||
test(`parseInt("0x0aXyzzy")`, "10")
|
||||
// TODO
|
||||
test(`parseInt("0x10000000000000000000", 16)`, "75557863725914323419136")
|
||||
}
|
||||
test(`parseInt("0x0a", Infinity)`, "10")
|
||||
test(`parseInt.length === 2`, "true")
|
||||
test(`parseInt.prototype === undefined`, "true")
|
||||
}
|
||||
|
||||
func Test_parseFloat(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user