Commit b374c005 authored by YOU's avatar YOU Committed by Dylan Trotter

Implement str.find (#125)

parent 89f4c7cc
......@@ -487,12 +487,14 @@ func IndexInt(f *Frame, o *Object) (int, *BaseException) {
if raised != nil {
return 0, raised
}
if i != nil {
if i.isInstance(IntType) {
return toIntUnsafe(i).Value(), nil
}
if l := toLongUnsafe(i).Value(); numInIntRange(l) {
return int(l.Int64()), nil
}
}
return 0, f.RaiseType(IndexErrorType, fmt.Sprintf("cannot fit '%s' into an index-sized integer", o.typ))
}
......
......@@ -205,6 +205,49 @@ func strEq(f *Frame, v, w *Object) (*Object, *BaseException) {
return strCompare(v, w, False, True, False), nil
}
// strFind returns the lowest index in s where the substring sub is found such
// that sub is wholly contained in s[start:end]. Return -1 on failure.
func strFind(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
var raised *BaseException
// TODO: Support for unicode substring.
expectedTypes := []*Type{StrType, StrType, ObjectType, ObjectType}
argc := len(args)
if argc == 2 || argc == 3 {
expectedTypes = expectedTypes[:argc]
}
if raised := checkMethodArgs(f, "find/index", args, expectedTypes...); raised != nil {
return nil, raised
}
s := toStrUnsafe(args[0]).Value()
l := len(s)
start, end := 0, l
if argc >= 3 {
start, raised = IndexInt(f, args[2])
if raised != nil {
return nil, raised
}
}
if argc == 4 {
end, raised = IndexInt(f, args[3])
if raised != nil {
return nil, raised
}
}
if start > l {
return NewInt(-1).ToObject(), nil
}
start, end = adjustIndex(start, l), adjustIndex(end, l)
if start > end {
return NewInt(-1).ToObject(), nil
}
sub := toStrUnsafe(args[1]).Value()
index := strings.Index(s[start:end], sub)
if index != -1 {
index += start
}
return NewInt(index).ToObject(), nil
}
func strGE(f *Frame, v, w *Object) (*Object, *BaseException) {
return strCompare(v, w, False, True, True), nil
}
......@@ -552,6 +595,7 @@ func initStrType(dict map[string]*Object) {
dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", strGetNewArgs).ToObject()
dict["decode"] = newBuiltinFunction("decode", strDecode).ToObject()
dict["endswith"] = newBuiltinFunction("endswith", strEndsWith).ToObject()
dict["find"] = newBuiltinFunction("find", strFind).ToObject()
dict["join"] = newBuiltinFunction("join", strJoin).ToObject()
dict["lower"] = newBuiltinFunction("lower", strLower).ToObject()
dict["lstrip"] = newBuiltinFunction("lstrip", strLStrip).ToObject()
......
......@@ -264,6 +264,28 @@ func TestStrMethods(t *testing.T) {
{"endswith", wrapArgs("foo", newTestTuple("barfoo", "oo").ToObject()), True.ToObject(), nil},
{"endswith", wrapArgs("foo", 123), nil, mustCreateException(TypeErrorType, "endswith first arg must be str, unicode, or tuple, not int")},
{"endswith", wrapArgs("foo", newTestTuple(123).ToObject()), nil, mustCreateException(TypeErrorType, "expected a str")},
{"find", wrapArgs("", ""), NewInt(0).ToObject(), nil},
{"find", wrapArgs("", "", 1), NewInt(-1).ToObject(), nil},
{"find", wrapArgs("foobar", "bar"), NewInt(3).ToObject(), nil},
{"find", wrapArgs("foobar", "bar", NewInt(MaxInt)), NewInt(-1).ToObject(), nil},
// TODO: Support unicode substring.
{"find", wrapArgs("foobar", NewUnicode("bar")), nil, mustCreateException(TypeErrorType, "'find/index' requires a 'str' object but received a 'unicode'")},
{"find", wrapArgs("foobar", "bar", "baz"), nil, mustCreateException(IndexErrorType, "cannot fit '<type 'str'>' into an index-sized integer")},
{"find", wrapArgs("foobar", "bar", 0, "baz"), nil, mustCreateException(IndexErrorType, "cannot fit '<type 'str'>' into an index-sized integer")},
{"find", wrapArgs("foobar", "bar", 0, -2), NewInt(-1).ToObject(), nil},
{"find", wrapArgs("foobar", "foo", 0, 3), NewInt(0).ToObject(), nil},
{"find", wrapArgs("foobar", "foo", 10), NewInt(-1).ToObject(), nil},
{"find", wrapArgs("foobar", "foo", 3, 3), NewInt(-1).ToObject(), nil},
{"find", wrapArgs("foobar", "bar", 3, 5), NewInt(-1).ToObject(), nil},
{"find", wrapArgs("foobar", "bar", 5, 3), NewInt(-1).ToObject(), nil},
{"find", wrapArgs("bar", "foobar"), NewInt(-1).ToObject(), nil},
{"find", wrapArgs("bar", "a", 1, 10), NewInt(1).ToObject(), nil},
{"find", wrapArgs("bar", "a", NewLong(big.NewInt(1)), 10), NewInt(1).ToObject(), nil},
{"find", wrapArgs("bar", "a", 0, NewLong(big.NewInt(2))), NewInt(1).ToObject(), nil},
{"find", wrapArgs("bar", "a", 1, 3), NewInt(1).ToObject(), nil},
{"find", wrapArgs("bar", "a", 0, -1), NewInt(1).ToObject(), nil},
{"find", wrapArgs("foo", newTestTuple("barfoo", "oo").ToObject()), nil, mustCreateException(TypeErrorType, "'find/index' requires a 'str' object but received a 'tuple'")},
{"find", wrapArgs("foo", 123), nil, mustCreateException(TypeErrorType, "'find/index' requires a 'str' object but received a 'int'")},
{"join", wrapArgs(",", newTestList("foo", "bar")), NewStr("foo,bar").ToObject(), nil},
{"join", wrapArgs(":", newTestList("foo", "bar", NewUnicode("baz"))), NewUnicode("foo:bar:baz").ToObject(), nil},
{"join", wrapArgs("nope", NewTuple()), NewStr("").ToObject(), nil},
......
......@@ -14,11 +14,89 @@
# pylint: disable=redefined-outer-name
import sys
# Test Add
assert "foo" + "bar" == "foobar"
assert "foo" + u"bar" == u"foobar"
assert "baz" + "" == "baz"
# Test find
assert "".find("") == 0
assert "".find("", 1) == -1
assert "foobar".find("bar") == 3
assert "foobar".find("bar", 0, -2) == -1
assert "foobar".find("foo", 0, 3) == 0
assert "foobar".find("bar", 3, 5) == -1
assert "foobar".find("bar", 5, 3) == -1
assert "bar".find("foobar") == -1
assert "bar".find("a", 0, -1) == 1
assert 'abcdefghiabc'.find('abc') == 0
assert 'abcdefghiabc'.find('abc', 1) == 9
assert 'abcdefghiabc'.find('def', 4) == -1
assert 'abc'.find('', 0) == 0
assert 'abc'.find('', 3) == 3
assert 'abc'.find('c', long(1)) == 2
assert 'abc'.find('c', 0, long(3)) == 2
assert 'abc'.find('', 4) == -1
assert 'rrarrrrrrrrra'.find('a') == 2
assert 'rrarrrrrrrrra'.find('a', 4) == 12
assert 'rrarrrrrrrrra'.find('a', 4, 6) == -1
assert ''.find('') == 0
assert ''.find('', 1, 1) == -1
assert ''.find('', sys.maxint, 0) == -1
assert ''.find('xx') == -1
assert ''.find('xx', 1, 1) == -1
assert ''.find('xx', sys.maxint, 0) == -1
# TODO: Support unicode substring.
# assert "foobar".find(u"bar") == 3
# TODO: Support None.
# assert 'rrarrrrrrrrra'.find('a', 4, None) == 12
# assert 'rrarrrrrrrrra'.find('a', None, 6) == 2
class Foo(object):
def __index__(self):
return 3
assert 'abcd'.find('a', Foo()) == -1
try:
'ab'.find('xxx', sys.maxsize + 1, 0)
raise AssertionError
except IndexError:
pass
try:
"foo".find(123)
raise AssertionError
except TypeError:
pass
try:
'foo'.find() # pylint: disable=no-value-for-parameter
raise AssertionError
except TypeError:
pass
try:
'foo'.find(42)
raise AssertionError
except TypeError:
pass
try:
'foobar'.find("bar", "baz")
raise AssertionError
except IndexError:
pass
try:
'foobar'.find("bar", 0, "baz")
raise AssertionError
except IndexError:
pass
# Test Mod
assert "%s" % 42 == "42"
assert "%f" % 3.14 == "3.140000"
......
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