Skip to content
This repository has been archived by the owner on Mar 23, 2023. It is now read-only.

Add support for the abs builtin #28

Merged
merged 1 commit into from
Jan 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions runtime/builtin_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(),
Expand Down
9 changes: 9 additions & 0 deletions runtime/builtin_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()},
Expand Down
10 changes: 10 additions & 0 deletions runtime/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 6 additions & 0 deletions runtime/float.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
}
Expand Down Expand Up @@ -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}
Expand Down
13 changes: 13 additions & 0 deletions runtime/int.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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}
Expand Down
5 changes: 5 additions & 0 deletions runtime/long.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions runtime/slots.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 36 additions & 0 deletions testing/builtin_test.py
Original file line number Diff line number Diff line change
@@ -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')