Skip to content
This repository was archived by the owner on Mar 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 114 additions & 7 deletions runtime/str.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,92 @@ func strIsDigit(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
return True.ToObject(), nil
}

func strIsLower(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
if raised := checkMethodArgs(f, "islower", args, StrType); raised != nil {
return nil, raised
}
s := toStrUnsafe(args[0]).Value()
if len(s) == 0 {
return False.ToObject(), nil
}
for i := range s {
if !isLower(s[i]) {
return False.ToObject(), nil
}
}
return True.ToObject(), nil
}

func strIsSpace(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
if raised := checkMethodArgs(f, "isspace", args, StrType); raised != nil {
return nil, raised
}
s := toStrUnsafe(args[0]).Value()
if len(s) == 0 {
return False.ToObject(), nil
}
for i := range s {
if !isSpace(s[i]) {
return False.ToObject(), nil
}
}
return True.ToObject(), nil
}

func strIsTitle(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
if raised := checkMethodArgs(f, "istitle", args, StrType); raised != nil {
return nil, raised
}

s := toStrUnsafe(args[0]).Value()
if len(s) == 0 {
return False.ToObject(), nil
}

if len(s) == 1 {
return GetBool(isUpper(s[0])).ToObject(), nil
}

cased := false
previousIsCased := false

for i := range s {
if isUpper(s[i]) {
if previousIsCased {
return False.ToObject(), nil
}
previousIsCased = true
cased = true
} else if isLower(s[i]) {
if !previousIsCased {
return False.ToObject(), nil
}
previousIsCased = true
cased = true
} else {
previousIsCased = false
}
}

return GetBool(cased).ToObject(), nil
}

func strIsUpper(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
if raised := checkMethodArgs(f, "isupper", args, StrType); raised != nil {
return nil, raised
}
s := toStrUnsafe(args[0]).Value()
if len(s) == 0 {
return False.ToObject(), nil
}
for i := range s {
if !isUpper(s[i]) {
return False.ToObject(), nil
}
}
return True.ToObject(), nil
}

func strJoin(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
if raised := checkMethodArgs(f, "join", args, StrType, ObjectType); raised != nil {
return nil, raised
Expand Down Expand Up @@ -807,9 +893,9 @@ func strSwapCase(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
}
b := make([]byte, numBytes)
for i := 0; i < numBytes; i++ {
if s[i] >= 'a' && s[i] <= 'z' {
if isLower(s[i]) {
b[i] = toUpper(s[i])
} else if s[i] >= 'A' && s[i] <= 'Z' {
} else if isUpper(s[i]) {
b[i] = toLower(s[i])
} else {
b[i] = s[i]
Expand All @@ -828,6 +914,10 @@ func initStrType(dict map[string]*Object) {
dict["isalnum"] = newBuiltinFunction("isalnum", strIsAlNum).ToObject()
dict["isalpha"] = newBuiltinFunction("isalpha", strIsAlpha).ToObject()
dict["isdigit"] = newBuiltinFunction("isdigit", strIsDigit).ToObject()
dict["islower"] = newBuiltinFunction("islower", strIsLower).ToObject()
dict["isspace"] = newBuiltinFunction("isspace", strIsSpace).ToObject()
dict["istitle"] = newBuiltinFunction("istitle", strIsTitle).ToObject()
dict["isupper"] = newBuiltinFunction("isupper", strIsUpper).ToObject()
dict["join"] = newBuiltinFunction("join", strJoin).ToObject()
dict["lower"] = newBuiltinFunction("lower", strLower).ToObject()
dict["lstrip"] = newBuiltinFunction("lstrip", strLStrip).ToObject()
Expand Down Expand Up @@ -1119,12 +1209,12 @@ func strTitle(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
for i := 0; i < numBytes; i++ {
c := s[i]
switch {
case s[i] >= 'a' && s[i] <= 'z':
case isLower(c):
if !previousIsCased {
c = toUpper(c)
}
previousIsCased = true
case s[i] >= 'A' && s[i] <= 'Z':
case isUpper(c):
if previousIsCased {
c = toLower(c)
}
Expand Down Expand Up @@ -1174,14 +1264,14 @@ func init() {
}

func toLower(b byte) byte {
if b >= 'A' && b <= 'Z' {
if isUpper(b) {
return b + caseOffset
}
return b
}

func toUpper(b byte) byte {
if b >= 'a' && b <= 'z' {
if isLower(b) {
return b - caseOffset
}
return b
Expand All @@ -1192,13 +1282,30 @@ func isAlNum(c byte) bool {
}

func isAlpha(c byte) bool {
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z'
return isUpper(c) || isLower(c)
}

func isDigit(c byte) bool {
return '0' <= c && c <= '9'
}

func isLower(c byte) bool {
return 'a' <= c && c <= 'z'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you replace the other identical comparisons in this file (e.g. in isAlpha and strTitle, toUpper) with a call to this function? Ditto for isUpper().

}

func isSpace(c byte) bool {
switch c {
case ' ', '\n', '\t', '\v', '\f', '\r':
return true
default:
return false
}
}

func isUpper(c byte) bool {
return 'A' <= c && c <= 'Z'
}

// strLeftPad returns s padded with fillchar so that its length is at least width.
// Fillchar must be a single character. When fillchar is "0", s starting with a
// sign are handled correctly.
Expand Down
23 changes: 23 additions & 0 deletions runtime/str_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,29 @@ func TestStrMethods(t *testing.T) {
{"isdigit", wrapArgs(""), False.ToObject(), nil},
{"isdigit", wrapArgs("abc#123"), False.ToObject(), nil},
{"isdigit", wrapArgs("123", "456"), nil, mustCreateException(TypeErrorType, "'isdigit' of 'str' requires 1 arguments")},
{"islower", wrapArgs("abc"), True.ToObject(), nil},
{"islower", wrapArgs("ABC"), False.ToObject(), nil},
{"islower", wrapArgs(""), False.ToObject(), nil},
{"islower", wrapArgs("abc#123"), False.ToObject(), nil},
{"islower", wrapArgs("123", "456"), nil, mustCreateException(TypeErrorType, "'islower' of 'str' requires 1 arguments")},
{"isupper", wrapArgs("abc"), False.ToObject(), nil},
{"isupper", wrapArgs("ABC"), True.ToObject(), nil},
{"isupper", wrapArgs(""), False.ToObject(), nil},
{"isupper", wrapArgs("abc#123"), False.ToObject(), nil},
{"isupper", wrapArgs("123", "456"), nil, mustCreateException(TypeErrorType, "'isupper' of 'str' requires 1 arguments")},
{"isspace", wrapArgs(""), False.ToObject(), nil},
{"isspace", wrapArgs(" "), True.ToObject(), nil},
{"isspace", wrapArgs("\n\t\v\f\r "), True.ToObject(), nil},
{"isspace", wrapArgs(""), False.ToObject(), nil},
{"isspace", wrapArgs("asdad"), False.ToObject(), nil},
{"isspace", wrapArgs(" "), True.ToObject(), nil},
{"isspace", wrapArgs(" ", "456"), nil, mustCreateException(TypeErrorType, "'isspace' of 'str' requires 1 arguments")},
{"istitle", wrapArgs("abc"), False.ToObject(), nil},
{"istitle", wrapArgs("Abc&D"), True.ToObject(), nil},
{"istitle", wrapArgs("ABc&D"), False.ToObject(), nil},
{"istitle", wrapArgs(""), False.ToObject(), nil},
{"istitle", wrapArgs("abc#123"), False.ToObject(), nil},
{"istitle", wrapArgs("ABc&D", "456"), nil, mustCreateException(TypeErrorType, "'istitle' of 'str' requires 1 arguments")},
{"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},
Expand Down