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

Commit

Permalink
Add support for the abs builtin (#28)
Browse files Browse the repository at this point in the history
Implement the `abs` as defined here:

* https://docs.python.org/2.7/library/functions.html#abs
  • Loading branch information
meadori authored and trotterdylan committed Jan 6, 2017
1 parent 2c7a26a commit c6f8ba5
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 0 deletions.
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')

0 comments on commit c6f8ba5

Please sign in to comment.