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) { ...@@ -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) { func builtinBin(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
if raised := checkFunctionArgs(f, "bin", args, ObjectType); raised != nil { if raised := checkFunctionArgs(f, "bin", args, ObjectType); raised != nil {
return nil, raised return nil, raised
...@@ -464,6 +471,7 @@ func builtinUniChr(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { ...@@ -464,6 +471,7 @@ func builtinUniChr(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
func init() { func init() {
builtinMap := map[string]*Object{ builtinMap := map[string]*Object{
"__frame__": newBuiltinFunction("__frame__", builtinFrame).ToObject(), "__frame__": newBuiltinFunction("__frame__", builtinFrame).ToObject(),
"abs": newBuiltinFunction("abs", builtinAbs).ToObject(),
"bin": newBuiltinFunction("bin", builtinBin).ToObject(), "bin": newBuiltinFunction("bin", builtinBin).ToObject(),
"chr": newBuiltinFunction("chr", builtinChr).ToObject(), "chr": newBuiltinFunction("chr", builtinChr).ToObject(),
"dir": newBuiltinFunction("dir", builtinDir).ToObject(), "dir": newBuiltinFunction("dir", builtinDir).ToObject(),
......
...@@ -56,6 +56,15 @@ func TestBuiltinFuncs(t *testing.T) { ...@@ -56,6 +56,15 @@ func TestBuiltinFuncs(t *testing.T) {
want *Object want *Object
wantExc *BaseException 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(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(MinInt), want: NewStr(fmt.Sprintf("-0b%b0", -(MinInt >> 1))).ToObject()},
{f: "bin", args: wrapArgs(0), want: NewStr("0b0").ToObject()}, {f: "bin", args: wrapArgs(0), want: NewStr("0b0").ToObject()},
......
...@@ -23,6 +23,16 @@ import ( ...@@ -23,6 +23,16 @@ import (
var logFatal = func(msg string) { log.Fatal(msg) } 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 returns the result of adding v and w together according to the
// __add/radd__ operator. // __add/radd__ operator.
func Add(f *Frame, v, w *Object) (*Object, *BaseException) { func Add(f *Frame, v, w *Object) (*Object, *BaseException) {
......
...@@ -50,6 +50,11 @@ func (f *Float) Value() float64 { ...@@ -50,6 +50,11 @@ func (f *Float) Value() float64 {
return f.value 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) { func floatAdd(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatArithmeticOp(f, "__add__", v, w, func(v, w float64) float64 { return v + w }) 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) { ...@@ -221,6 +226,7 @@ func floatSub(f *Frame, v, w *Object) (*Object, *BaseException) {
func initFloatType(dict map[string]*Object) { func initFloatType(dict map[string]*Object) {
dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", floatGetNewArgs).ToObject() dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", floatGetNewArgs).ToObject()
FloatType.slots.Abs = &unaryOpSlot{floatAbs}
FloatType.slots.Add = &binaryOpSlot{floatAdd} FloatType.slots.Add = &binaryOpSlot{floatAdd}
FloatType.slots.Div = &binaryOpSlot{floatDiv} FloatType.slots.Div = &binaryOpSlot{floatDiv}
FloatType.slots.Eq = &binaryOpSlot{floatEq} FloatType.slots.Eq = &binaryOpSlot{floatEq}
......
...@@ -66,6 +66,18 @@ func (i *Int) IsTrue() bool { ...@@ -66,6 +66,18 @@ func (i *Int) IsTrue() bool {
// IntType is the object representing the Python 'int' type. // IntType is the object representing the Python 'int' type.
var IntType = newBasisType("int", reflect.TypeOf(Int{}), toIntUnsafe, ObjectType) 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) { func intAdd(f *Frame, v, w *Object) (*Object, *BaseException) {
return intAddMulOp(f, "__add__", v, w, intCheckedAdd, longAdd) return intAddMulOp(f, "__add__", v, w, intCheckedAdd, longAdd)
} }
...@@ -287,6 +299,7 @@ func intXor(f *Frame, v, w *Object) (*Object, *BaseException) { ...@@ -287,6 +299,7 @@ func intXor(f *Frame, v, w *Object) (*Object, *BaseException) {
func initIntType(dict map[string]*Object) { func initIntType(dict map[string]*Object) {
dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", intGetNewArgs).ToObject() dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", intGetNewArgs).ToObject()
IntType.slots.Abs = &unaryOpSlot{intAbs}
IntType.slots.Add = &binaryOpSlot{intAdd} IntType.slots.Add = &binaryOpSlot{intAdd}
IntType.slots.And = &binaryOpSlot{intAnd} IntType.slots.And = &binaryOpSlot{intAnd}
IntType.slots.Div = &binaryOpSlot{intDiv} IntType.slots.Div = &binaryOpSlot{intDiv}
......
...@@ -86,6 +86,10 @@ func (l *Long) Neg() *Long { ...@@ -86,6 +86,10 @@ func (l *Long) Neg() *Long {
// LongType is the object representing the Python 'long' type. // LongType is the object representing the Python 'long' type.
var LongType = newBasisType("long", reflect.TypeOf(Long{}), toLongUnsafe, ObjectType) 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) { func longAdd(z, x, y *big.Int) {
z.Add(x, y) z.Add(x, y)
} }
...@@ -299,6 +303,7 @@ func longXor(z, x, y *big.Int) { ...@@ -299,6 +303,7 @@ func longXor(z, x, y *big.Int) {
func initLongType(dict map[string]*Object) { func initLongType(dict map[string]*Object) {
dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", longGetNewArgs).ToObject() dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", longGetNewArgs).ToObject()
LongType.slots.Abs = longUnaryOpSlot(longAbs)
LongType.slots.Add = longBinaryOpSlot(longAdd) LongType.slots.Add = longBinaryOpSlot(longAdd)
LongType.slots.And = longBinaryOpSlot(longAnd) LongType.slots.And = longBinaryOpSlot(longAnd)
LongType.slots.Div = longDivModOpSlot(longDiv) LongType.slots.Div = longDivModOpSlot(longDiv)
......
...@@ -369,6 +369,7 @@ func (s *unaryOpSlot) wrapCallable(callable *Object) bool { ...@@ -369,6 +369,7 @@ func (s *unaryOpSlot) wrapCallable(callable *Object) bool {
// The wrapper structs permit comparison of like slots which is occasionally // The wrapper structs permit comparison of like slots which is occasionally
// necessary to determine whether a function has been overridden by a subclass. // necessary to determine whether a function has been overridden by a subclass.
type typeSlots struct { type typeSlots struct {
Abs *unaryOpSlot
Add *binaryOpSlot Add *binaryOpSlot
And *binaryOpSlot And *binaryOpSlot
Basis *basisSlot 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