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

Implement str.find (#125)

parent 89f4c7cc
...@@ -487,11 +487,13 @@ func IndexInt(f *Frame, o *Object) (int, *BaseException) { ...@@ -487,11 +487,13 @@ func IndexInt(f *Frame, o *Object) (int, *BaseException) {
if raised != nil { if raised != nil {
return 0, raised return 0, raised
} }
if i.isInstance(IntType) { if i != nil {
return toIntUnsafe(i).Value(), nil if i.isInstance(IntType) {
} return toIntUnsafe(i).Value(), nil
if l := toLongUnsafe(i).Value(); numInIntRange(l) { }
return int(l.Int64()), 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)) 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) { ...@@ -205,6 +205,49 @@ func strEq(f *Frame, v, w *Object) (*Object, *BaseException) {
return strCompare(v, w, False, True, False), nil 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) { func strGE(f *Frame, v, w *Object) (*Object, *BaseException) {
return strCompare(v, w, False, True, True), nil return strCompare(v, w, False, True, True), nil
} }
...@@ -552,6 +595,7 @@ func initStrType(dict map[string]*Object) { ...@@ -552,6 +595,7 @@ func initStrType(dict map[string]*Object) {
dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", strGetNewArgs).ToObject() dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", strGetNewArgs).ToObject()
dict["decode"] = newBuiltinFunction("decode", strDecode).ToObject() dict["decode"] = newBuiltinFunction("decode", strDecode).ToObject()
dict["endswith"] = newBuiltinFunction("endswith", strEndsWith).ToObject() dict["endswith"] = newBuiltinFunction("endswith", strEndsWith).ToObject()
dict["find"] = newBuiltinFunction("find", strFind).ToObject()
dict["join"] = newBuiltinFunction("join", strJoin).ToObject() dict["join"] = newBuiltinFunction("join", strJoin).ToObject()
dict["lower"] = newBuiltinFunction("lower", strLower).ToObject() dict["lower"] = newBuiltinFunction("lower", strLower).ToObject()
dict["lstrip"] = newBuiltinFunction("lstrip", strLStrip).ToObject() dict["lstrip"] = newBuiltinFunction("lstrip", strLStrip).ToObject()
......
...@@ -264,6 +264,28 @@ func TestStrMethods(t *testing.T) { ...@@ -264,6 +264,28 @@ func TestStrMethods(t *testing.T) {
{"endswith", wrapArgs("foo", newTestTuple("barfoo", "oo").ToObject()), True.ToObject(), nil}, {"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", 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")}, {"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")), NewStr("foo,bar").ToObject(), nil},
{"join", wrapArgs(":", newTestList("foo", "bar", NewUnicode("baz"))), NewUnicode("foo:bar:baz").ToObject(), nil}, {"join", wrapArgs(":", newTestList("foo", "bar", NewUnicode("baz"))), NewUnicode("foo:bar:baz").ToObject(), nil},
{"join", wrapArgs("nope", NewTuple()), NewStr("").ToObject(), nil}, {"join", wrapArgs("nope", NewTuple()), NewStr("").ToObject(), nil},
......
...@@ -14,11 +14,89 @@ ...@@ -14,11 +14,89 @@
# pylint: disable=redefined-outer-name # pylint: disable=redefined-outer-name
import sys
# Test Add # Test Add
assert "foo" + "bar" == "foobar" assert "foo" + "bar" == "foobar"
assert "foo" + u"bar" == u"foobar" assert "foo" + u"bar" == u"foobar"
assert "baz" + "" == "baz" 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 # Test Mod
assert "%s" % 42 == "42" assert "%s" % 42 == "42"
assert "%f" % 3.14 == "3.140000" 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