diff --git a/runtime/builtin_types.go b/runtime/builtin_types.go index 620316bf..4cef6bca 100644 --- a/runtime/builtin_types.go +++ b/runtime/builtin_types.go @@ -194,25 +194,36 @@ func builtinAll(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { if raised := checkFunctionArgs(f, "all", args, ObjectType); raised != nil { return nil, raised } - iter, raised := Iter(f, args[0]) + pred := func(o *Object) (bool, *BaseException) { + ret, raised := IsTrue(f, o) + if raised != nil { + return false, raised + } + return !ret, nil + } + foundFalseItem, raised := seqFindFirst(f, args[0], pred) if raised != nil { return nil, raised } - o, raised := Next(f, iter) - for ; raised == nil; o, raised = Next(f, iter) { + return GetBool(!foundFalseItem).ToObject(), raised +} + +func builtinAny(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { + if raised := checkFunctionArgs(f, "any", args, ObjectType); raised != nil { + return nil, raised + } + pred := func(o *Object) (bool, *BaseException) { ret, raised := IsTrue(f, o) if raised != nil { - return nil, raised - } - if !ret { - return False.ToObject(), nil + return false, raised } + return ret, nil } - if !raised.isInstance(StopIterationType) { + foundTrueItem, raised := seqFindFirst(f, args[0], pred) + if raised != nil { return nil, raised } - f.RestoreExc(nil, nil) - return True.ToObject(), nil + return GetBool(foundTrueItem).ToObject(), raised } func builtinBin(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { @@ -509,6 +520,7 @@ func init() { "__frame__": newBuiltinFunction("__frame__", builtinFrame).ToObject(), "abs": newBuiltinFunction("abs", builtinAbs).ToObject(), "all": newBuiltinFunction("all", builtinAll).ToObject(), + "any": newBuiltinFunction("any", builtinAny).ToObject(), "bin": newBuiltinFunction("bin", builtinBin).ToObject(), "callable": newBuiltinFunction("callable", builtinCallable).ToObject(), "chr": newBuiltinFunction("chr", builtinChr).ToObject(), diff --git a/runtime/builtin_types_test.go b/runtime/builtin_types_test.go index 0bcf36e2..91f543e5 100644 --- a/runtime/builtin_types_test.go +++ b/runtime/builtin_types_test.go @@ -92,6 +92,14 @@ func TestBuiltinFuncs(t *testing.T) { {f: "all", args: wrapArgs(13), wantExc: mustCreateException(TypeErrorType, "'int' object is not iterable")}, {f: "all", args: wrapArgs(newTestList(newObject(badNonZeroType))), wantExc: mustCreateException(RuntimeErrorType, "foo")}, {f: "all", args: wrapArgs(newObject(badIterType)), wantExc: mustCreateException(RuntimeErrorType, "foo")}, + {f: "any", args: wrapArgs(newTestList()), want: False.ToObject()}, + {f: "any", args: wrapArgs(newTestList(1, 2, 3)), want: True.ToObject()}, + {f: "any", args: wrapArgs(newTestList(1, 0, 1)), want: True.ToObject()}, + {f: "any", args: wrapArgs(newTestList(0, 0, 0)), want: False.ToObject()}, + {f: "any", args: wrapArgs(newTestList(False.ToObject(), False.ToObject())), want: False.ToObject()}, + {f: "any", args: wrapArgs(13), wantExc: mustCreateException(TypeErrorType, "'int' object is not iterable")}, + {f: "any", args: wrapArgs(newTestList(newObject(badNonZeroType))), wantExc: mustCreateException(RuntimeErrorType, "foo")}, + {f: "any", args: wrapArgs(newObject(badIterType)), wantExc: mustCreateException(RuntimeErrorType, "foo")}, {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()}, diff --git a/runtime/list.go b/runtime/list.go index 47bb1685..f357a2c9 100644 --- a/runtime/list.go +++ b/runtime/list.go @@ -162,7 +162,7 @@ func listAppend(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { } func listContains(f *Frame, l, v *Object) (*Object, *BaseException) { - return seqContains(f, toListUnsafe(l).elems, v) + return seqContains(f, l, v) } func listEq(f *Frame, v, w *Object) (*Object, *BaseException) { diff --git a/runtime/seq.go b/runtime/seq.go index d2da0b8c..01a25217 100644 --- a/runtime/seq.go +++ b/runtime/seq.go @@ -108,21 +108,45 @@ func seqClampIndex(i, seqLen int) int { return i } -func seqContains(f *Frame, elems []*Object, v *Object) (*Object, *BaseException) { - for _, i := range elems { - eq, raised := Eq(f, v, i) +func seqContains(f *Frame, iterable *Object, v *Object) (*Object, *BaseException) { + pred := func(o *Object) (bool, *BaseException) { + eq, raised := Eq(f, v, o) if raised != nil { - return nil, raised + return false, raised } ret, raised := IsTrue(f, eq) if raised != nil { - return nil, raised + return false, raised + } + return ret, nil + } + foundEqItem, raised := seqFindFirst(f, iterable, pred) + if raised != nil { + return nil, raised + } + return GetBool(foundEqItem).ToObject(), raised +} + +func seqFindFirst(f *Frame, iterable *Object, pred func(*Object) (bool, *BaseException)) (bool, *BaseException) { + iter, raised := Iter(f, iterable) + if raised != nil { + return false, raised + } + item, raised := Next(f, iter) + for ; raised == nil; item, raised = Next(f, iter) { + ret, raised := pred(item) + if raised != nil { + return false, raised } if ret { - return True.ToObject(), nil + return true, nil } } - return False.ToObject(), nil + if !raised.isInstance(StopIterationType) { + return false, raised + } + f.RestoreExc(nil, nil) + return false, nil } func seqForEach(f *Frame, iterable *Object, callback func(*Object) *BaseException) *BaseException { diff --git a/runtime/tuple.go b/runtime/tuple.go index f723e82c..d018c9b9 100644 --- a/runtime/tuple.go +++ b/runtime/tuple.go @@ -72,7 +72,7 @@ func tupleAdd(f *Frame, v, w *Object) (*Object, *BaseException) { } func tupleContains(f *Frame, t, v *Object) (*Object, *BaseException) { - return seqContains(f, toTupleUnsafe(t).elems, v) + return seqContains(f, t, v) } func tupleEq(f *Frame, v, w *Object) (*Object, *BaseException) { diff --git a/testing/builtin_test.py b/testing/builtin_test.py index cc6b6c29..62601d7b 100644 --- a/testing/builtin_test.py +++ b/testing/builtin_test.py @@ -55,6 +55,29 @@ else: raise AssertionError('this was supposed to raise an exception') + +# any(iterable) + +assert any([1, 2, 3]) +assert not any([]) +assert any([1, 1, 1, 0, 1]) +assert not any([0, 0, 0]) + +assert any([True, True]) +assert any([False, True, True]) +assert not any([False, False, False]) + +assert not any('') +assert any('abc') + +try: + any(13) +except TypeError as e: + assert str(e) == "'int' object is not iterable" +else: + raise AssertionError('this was supposed to raise an exception') + + # callable(x) assert not callable(1)