Commit 7ddfe607 authored by Meador Inge's avatar Meador Inge Committed by Dylan Trotter

Implement the sequence `count` method (#198)

As specified here:

* https://docs.python.org/2/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer-xrange

This commit only uses the sequence count method for `list.count`,
but it can easily be applied to other sequence types in follow-on
commits.
parent c0a76bc2
...@@ -163,6 +163,14 @@ func listAppend(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { ...@@ -163,6 +163,14 @@ func listAppend(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
return None, nil return None, nil
} }
func listCount(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
argc := len(args)
if argc != 2 {
return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("count() takes exactly one argument (%d given)", argc))
}
return seqCount(f, args[0], args[1])
}
func listExtend(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { func listExtend(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
argc := len(args) argc := len(args)
if argc != 2 { if argc != 2 {
...@@ -391,6 +399,7 @@ func listSort(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { ...@@ -391,6 +399,7 @@ func listSort(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
func initListType(dict map[string]*Object) { func initListType(dict map[string]*Object) {
dict["append"] = newBuiltinFunction("append", listAppend).ToObject() dict["append"] = newBuiltinFunction("append", listAppend).ToObject()
dict["count"] = newBuiltinFunction("count", listCount).ToObject()
dict["extend"] = newBuiltinFunction("extend", listExtend).ToObject() dict["extend"] = newBuiltinFunction("extend", listExtend).ToObject()
dict["insert"] = newBuiltinFunction("insert", listInsert).ToObject() dict["insert"] = newBuiltinFunction("insert", listInsert).ToObject()
dict["pop"] = newBuiltinFunction("pop", listPop).ToObject() dict["pop"] = newBuiltinFunction("pop", listPop).ToObject()
......
...@@ -79,6 +79,19 @@ func TestListCompare(t *testing.T) { ...@@ -79,6 +79,19 @@ func TestListCompare(t *testing.T) {
} }
} }
func TestListCount(t *testing.T) {
cases := []invokeTestCase{
{args: wrapArgs(NewList(), NewInt(1)), want: NewInt(0).ToObject()},
{args: wrapArgs(NewList(None, None, None), None), want: NewInt(3).ToObject()},
{args: wrapArgs(newTestList()), wantExc: mustCreateException(TypeErrorType, "count() takes exactly one argument (1 given)")},
}
for _, cas := range cases {
if err := runInvokeMethodTestCase(ListType, "count", &cas); err != "" {
t.Error(err)
}
}
}
func BenchmarkListContains(b *testing.B) { func BenchmarkListContains(b *testing.B) {
b.Run("false-3", func(b *testing.B) { b.Run("false-3", func(b *testing.B) {
t := newTestList("foo", 42, "bar").ToObject() t := newTestList("foo", 42, "bar").ToObject()
......
...@@ -129,6 +129,28 @@ func seqContains(f *Frame, iterable *Object, v *Object) (*Object, *BaseException ...@@ -129,6 +129,28 @@ func seqContains(f *Frame, iterable *Object, v *Object) (*Object, *BaseException
return GetBool(foundEqItem).ToObject(), raised return GetBool(foundEqItem).ToObject(), raised
} }
func seqCount(f *Frame, iterable *Object, v *Object) (*Object, *BaseException) {
count := 0
raised := seqForEach(f, iterable, func(o *Object) *BaseException {
eq, raised := Eq(f, o, v)
if raised != nil {
return raised
}
t, raised := IsTrue(f, eq)
if raised != nil {
return raised
}
if t {
count++
}
return nil
})
if raised != nil {
return nil, raised
}
return NewInt(count).ToObject(), nil
}
func seqFindFirst(f *Frame, iterable *Object, pred func(*Object) (bool, *BaseException)) (bool, *BaseException) { func seqFindFirst(f *Frame, iterable *Object, pred func(*Object) (bool, *BaseException)) (bool, *BaseException) {
iter, raised := Iter(f, iterable) iter, raised := Iter(f, iterable)
if raised != nil { if raised != nil {
......
...@@ -43,6 +43,38 @@ func TestSeqApply(t *testing.T) { ...@@ -43,6 +43,38 @@ func TestSeqApply(t *testing.T) {
} }
} }
func TestSeqCount(t *testing.T) {
badEqType := newTestClass("Eq", []*Type{IntType}, newStringDict(map[string]*Object{
"__eq__": newBuiltinFunction("__eq__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
return nil, f.RaiseType(TypeErrorType, "uh oh")
}).ToObject(),
}))
badNonZeroType := newTestClass("BadNonZeroType", []*Type{ObjectType}, newStringDict(map[string]*Object{
"__nonzero__": newBuiltinFunction("__nonzero__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
return nil, f.RaiseType(TypeErrorType, "uh oh")
}).ToObject(),
}))
worseEqType := newTestClass("WorseEqCmp", []*Type{IntType}, newStringDict(map[string]*Object{
"__eq__": newBuiltinFunction("__eq__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
return newObject(badNonZeroType), nil
}).ToObject(),
}))
cases := []invokeTestCase{
{args: wrapArgs(newTestList(), NewInt(1)), want: NewInt(0).ToObject()},
{args: wrapArgs(newTestList(1, 2, 3, 4, 5, 6), NewInt(7)), want: NewInt(0).ToObject()},
{args: wrapArgs(newTestList(1, 2, 3, 2, 2, 4), NewInt(2)), want: NewInt(3).ToObject()},
{args: wrapArgs(newTestList(1, None, None, 3), None), want: NewInt(2).ToObject()},
{args: wrapArgs(newTestList("a", "b", "c", "d", "e"), NewStr("c")), want: NewInt(1).ToObject()},
{args: wrapArgs(newTestList(newObject(badEqType)), newObject(badEqType)), wantExc: mustCreateException(TypeErrorType, "uh oh")},
{args: wrapArgs(newTestList(newObject(worseEqType)), newObject(worseEqType)), wantExc: mustCreateException(TypeErrorType, "uh oh")},
}
for _, cas := range cases {
if err := runInvokeTestCase(wrapFuncForTest(seqCount), &cas); err != "" {
t.Error(err)
}
}
}
func TestSeqForEach(t *testing.T) { func TestSeqForEach(t *testing.T) {
fun := wrapFuncForTest(func(f *Frame, seq *Object) (*Object, *BaseException) { fun := wrapFuncForTest(func(f *Frame, seq *Object) (*Object, *BaseException) {
elems := []*Object{} elems := []*Object{}
......
...@@ -98,3 +98,15 @@ try: ...@@ -98,3 +98,15 @@ try:
assert AssertionError assert AssertionError
except TypeError: except TypeError:
pass pass
# Test count
assert [].count(0) == 0
assert [1, 2, 3].count(2) == 1
assert ["a", "b", "a", "a"].count("a") == 3
assert ([2] * 20).count(2) == 20
try:
[].count()
assert AssertionError
except TypeError:
pass
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