Commit c6f8ba5e authored by Meador Inge's avatar Meador Inge Committed by Dylan Trotter

Add support for the `abs` builtin (#28)

Implement the `abs` as defined here:

* https://docs.python.org/2.7/library/functions.html#abs
parent 2c7a26ac
......@@ -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(),
......
......@@ -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()},
......
......@@ -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) {
......
......@@ -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}
......
......@@ -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}
......
......@@ -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)
......
......@@ -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
......
# 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')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment