diff --git a/runtime/builtin_types.go b/runtime/builtin_types.go index 1bb0e828..a64caa53 100644 --- a/runtime/builtin_types.go +++ b/runtime/builtin_types.go @@ -183,6 +183,13 @@ func initBuiltinType(typ *Type, info *builtinTypeInfo) { } } +func builtinAbs(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { + if raised := checkFunctionArgs(f, "abs", args, ObjectType); raised != nil { + return nil, raised + } + return Abs(f, args[0]) +} + func builtinBin(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { if raised := checkFunctionArgs(f, "bin", args, ObjectType); raised != nil { return nil, raised @@ -464,6 +471,7 @@ func builtinUniChr(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { func init() { builtinMap := map[string]*Object{ "__frame__": newBuiltinFunction("__frame__", builtinFrame).ToObject(), + "abs": newBuiltinFunction("abs", builtinAbs).ToObject(), "bin": newBuiltinFunction("bin", builtinBin).ToObject(), "chr": newBuiltinFunction("chr", builtinChr).ToObject(), "dir": newBuiltinFunction("dir", builtinDir).ToObject(), diff --git a/runtime/builtin_types_test.go b/runtime/builtin_types_test.go index 95c56144..1eaa0cc1 100644 --- a/runtime/builtin_types_test.go +++ b/runtime/builtin_types_test.go @@ -56,6 +56,15 @@ func TestBuiltinFuncs(t *testing.T) { want *Object wantExc *BaseException }{ + {f: "abs", args: wrapArgs(1, 2, 3), wantExc: mustCreateException(TypeErrorType, "'abs' requires 1 arguments")}, + {f: "abs", args: wrapArgs(1), want: NewInt(1).ToObject()}, + {f: "abs", args: wrapArgs(-1), want: NewInt(1).ToObject()}, + {f: "abs", args: wrapArgs(big.NewInt(2)), want: NewLong(big.NewInt(2)).ToObject()}, + {f: "abs", args: wrapArgs(big.NewInt(-2)), want: NewLong(big.NewInt(2)).ToObject()}, + {f: "abs", args: wrapArgs(NewFloat(3.4)), want: NewFloat(3.4).ToObject()}, + {f: "abs", args: wrapArgs(NewFloat(-3.4)), want: NewFloat(3.4).ToObject()}, + {f: "abs", args: wrapArgs(MinInt), want: NewLong(big.NewInt(MinInt).Neg(minIntBig)).ToObject()}, + {f: "abs", args: wrapArgs(NewStr("a")), wantExc: mustCreateException(TypeErrorType, "bad operand type for abs(): 'str'")}, {f: "bin", args: wrapArgs(64 + 8 + 1), want: NewStr("0b1001001").ToObject()}, {f: "bin", args: wrapArgs(MinInt), want: NewStr(fmt.Sprintf("-0b%b0", -(MinInt >> 1))).ToObject()}, {f: "bin", args: wrapArgs(0), want: NewStr("0b0").ToObject()}, diff --git a/runtime/core.go b/runtime/core.go index 368e502d..3b78865e 100644 --- a/runtime/core.go +++ b/runtime/core.go @@ -23,6 +23,16 @@ import ( var logFatal = func(msg string) { log.Fatal(msg) } +// Abs returns the result of o.__abs__ and is equivalent to the Python +// expression "abs(o)". +func Abs(f *Frame, o *Object) (*Object, *BaseException) { + abs := o.typ.slots.Abs + if abs == nil { + return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("bad operand type for abs(): '%s'", o.typ.Name())) + } + return abs.Fn(f, o) +} + // Add returns the result of adding v and w together according to the // __add/radd__ operator. func Add(f *Frame, v, w *Object) (*Object, *BaseException) { diff --git a/runtime/float.go b/runtime/float.go index 17c0b798..2cbc728a 100644 --- a/runtime/float.go +++ b/runtime/float.go @@ -50,6 +50,11 @@ func (f *Float) Value() float64 { return f.value } +func floatAbs(f *Frame, o *Object) (*Object, *BaseException) { + z := toFloatUnsafe(o).Value() + return NewFloat(math.Abs(z)).ToObject(), nil +} + func floatAdd(f *Frame, v, w *Object) (*Object, *BaseException) { return floatArithmeticOp(f, "__add__", v, w, func(v, w float64) float64 { return v + w }) } @@ -221,6 +226,7 @@ func floatSub(f *Frame, v, w *Object) (*Object, *BaseException) { func initFloatType(dict map[string]*Object) { dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", floatGetNewArgs).ToObject() + FloatType.slots.Abs = &unaryOpSlot{floatAbs} FloatType.slots.Add = &binaryOpSlot{floatAdd} FloatType.slots.Div = &binaryOpSlot{floatDiv} FloatType.slots.Eq = &binaryOpSlot{floatEq} diff --git a/runtime/int.go b/runtime/int.go index f56dd049..691df0c0 100644 --- a/runtime/int.go +++ b/runtime/int.go @@ -66,6 +66,18 @@ func (i *Int) IsTrue() bool { // IntType is the object representing the Python 'int' type. var IntType = newBasisType("int", reflect.TypeOf(Int{}), toIntUnsafe, ObjectType) +func intAbs(f *Frame, o *Object) (*Object, *BaseException) { + z := toIntUnsafe(o) + if z.Value() > 0 { + return z.ToObject(), nil + } + if z.Value() == MinInt { + nz := big.NewInt(int64(z.Value())) + return NewLong(nz.Neg(nz)).ToObject(), nil + } + return NewInt(-z.Value()).ToObject(), nil +} + func intAdd(f *Frame, v, w *Object) (*Object, *BaseException) { return intAddMulOp(f, "__add__", v, w, intCheckedAdd, longAdd) } @@ -287,6 +299,7 @@ func intXor(f *Frame, v, w *Object) (*Object, *BaseException) { func initIntType(dict map[string]*Object) { dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", intGetNewArgs).ToObject() + IntType.slots.Abs = &unaryOpSlot{intAbs} IntType.slots.Add = &binaryOpSlot{intAdd} IntType.slots.And = &binaryOpSlot{intAnd} IntType.slots.Div = &binaryOpSlot{intDiv} diff --git a/runtime/long.go b/runtime/long.go index 6bfd233f..4c2aeb8c 100644 --- a/runtime/long.go +++ b/runtime/long.go @@ -86,6 +86,10 @@ func (l *Long) Neg() *Long { // LongType is the object representing the Python 'long' type. var LongType = newBasisType("long", reflect.TypeOf(Long{}), toLongUnsafe, ObjectType) +func longAbs(z, x *big.Int) { + z.Abs(x) +} + func longAdd(z, x, y *big.Int) { z.Add(x, y) } @@ -299,6 +303,7 @@ func longXor(z, x, y *big.Int) { func initLongType(dict map[string]*Object) { dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", longGetNewArgs).ToObject() + LongType.slots.Abs = longUnaryOpSlot(longAbs) LongType.slots.Add = longBinaryOpSlot(longAdd) LongType.slots.And = longBinaryOpSlot(longAnd) LongType.slots.Div = longDivModOpSlot(longDiv) diff --git a/runtime/slots.go b/runtime/slots.go index 3aae31ec..9a0dfd34 100644 --- a/runtime/slots.go +++ b/runtime/slots.go @@ -369,6 +369,7 @@ func (s *unaryOpSlot) wrapCallable(callable *Object) bool { // The wrapper structs permit comparison of like slots which is occasionally // necessary to determine whether a function has been overridden by a subclass. type typeSlots struct { + Abs *unaryOpSlot Add *binaryOpSlot And *binaryOpSlot Basis *basisSlot diff --git a/testing/builtin_test.py b/testing/builtin_test.py new file mode 100644 index 00000000..81d22bce --- /dev/null +++ b/testing/builtin_test.py @@ -0,0 +1,36 @@ +# Copyright 2016 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pylint: disable=g-equals-none + +# abs(x) + +assert abs(1) == 1 +assert abs(-1) == 1 +assert isinstance(abs(-1), int) + +assert abs(long(2)) == 2 +assert abs(long(-2)) == 2 +assert isinstance(abs(long(-2)), long) + +assert abs(3.4) == 3.4 +assert abs(-3.4) == 3.4 +assert isinstance(abs(-3.4), float) + +try: + abs('a') +except TypeError as e: + assert str(e) == "bad operand type for abs(): 'str'" +else: + raise AssertionError('this was supposed to raise an exception')