github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/str.go (about)

     1  // Copyright 2016 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package grumpy
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"reflect"
    21  	"regexp"
    22  	"strconv"
    23  	"strings"
    24  	"sync/atomic"
    25  	"unicode"
    26  	"unicode/utf8"
    27  	"unsafe"
    28  )
    29  
    30  var (
    31  	// StrType is the object representing the Python 'str' type.
    32  	StrType                = newBasisType("str", reflect.TypeOf(Str{}), toStrUnsafe, BaseStringType)
    33  	whitespaceSplitRegexp  = regexp.MustCompile(`\s+`)
    34  	strASCIISpaces         = []byte(" \t\n\v\f\r")
    35  	strInterpolationRegexp = regexp.MustCompile(`^%([#0 +-]?)((\*|[0-9]+)?)((\.(\*|[0-9]+))?)[hlL]?([diouxXeEfFgGcrs%])`)
    36  	internedStrs           = map[string]*Str{}
    37  	caseOffset             = byte('a' - 'A')
    38  
    39  	internedName = NewStr("__name__")
    40  )
    41  
    42  type stripSide int
    43  
    44  const (
    45  	stripSideLeft stripSide = iota
    46  	stripSideRight
    47  	stripSideBoth
    48  )
    49  
    50  // InternStr adds s to the interned string map. Subsequent calls to NewStr()
    51  // will return the same underlying Str. InternStr is not thread safe and should
    52  // only be called during module initialization time.
    53  func InternStr(s string) *Str {
    54  	str, _ := internedStrs[s]
    55  	if str == nil {
    56  		str = &Str{Object: Object{typ: StrType}, value: s, hash: NewInt(hashString(s))}
    57  		internedStrs[s] = str
    58  	}
    59  	return str
    60  }
    61  
    62  // Str represents Python 'str' objects.
    63  type Str struct {
    64  	Object
    65  	value string
    66  	hash  *Int
    67  }
    68  
    69  // NewStr returns a new Str holding the given string value.
    70  func NewStr(value string) *Str {
    71  	if s := internedStrs[value]; s != nil {
    72  		return s
    73  	}
    74  	return &Str{Object: Object{typ: StrType}, value: value}
    75  }
    76  
    77  func toStrUnsafe(o *Object) *Str {
    78  	return (*Str)(o.toPointer())
    79  }
    80  
    81  // Decode produces a unicode object from the bytes of s assuming they have the
    82  // given encoding. Invalid code points are resolved using a strategy given by
    83  // errors: "ignore" will bypass them, "replace" will substitute the Unicode
    84  // replacement character (U+FFFD) and "strict" will raise UnicodeDecodeError.
    85  //
    86  // NOTE: Decoding UTF-8 data containing surrogates (e.g. U+D800 encoded as
    87  // '\xed\xa0\x80') will raise UnicodeDecodeError consistent with CPython 3.x
    88  // but different than 2.x.
    89  func (s *Str) Decode(f *Frame, encoding, errors string) (*Unicode, *BaseException) {
    90  	// TODO: Support custom encodings and error handlers.
    91  	normalized := normalizeEncoding(encoding)
    92  	if normalized != "utf8" {
    93  		return nil, f.RaiseType(LookupErrorType, fmt.Sprintf("unknown encoding: %s", encoding))
    94  	}
    95  	var runes []rune
    96  	for pos, r := range s.Value() {
    97  		switch {
    98  		case r != utf8.RuneError:
    99  			runes = append(runes, r)
   100  		case errors == EncodeIgnore:
   101  			// Do nothing
   102  		case errors == EncodeReplace:
   103  			runes = append(runes, unicode.ReplacementChar)
   104  		case errors == EncodeStrict:
   105  			format := "'%s' codec can't decode byte 0x%02x in position %d"
   106  			return nil, f.RaiseType(UnicodeDecodeErrorType, fmt.Sprintf(format, encoding, int(s.Value()[pos]), pos))
   107  		default:
   108  			format := "unknown error handler name '%s'"
   109  			return nil, f.RaiseType(LookupErrorType, fmt.Sprintf(format, errors))
   110  		}
   111  	}
   112  	return NewUnicodeFromRunes(runes), nil
   113  }
   114  
   115  // ToObject upcasts s to an Object.
   116  func (s *Str) ToObject() *Object {
   117  	return &s.Object
   118  }
   119  
   120  // Value returns the underlying string value held by s.
   121  func (s *Str) Value() string {
   122  	return s.value
   123  }
   124  
   125  func hashString(s string) int {
   126  	l := len(s)
   127  	if l == 0 {
   128  		return 0
   129  	}
   130  	h := int(s[0]) << 7
   131  	for i := 0; i < l; i++ {
   132  		h = (1000003 * h) ^ int(s[i])
   133  	}
   134  	h ^= l
   135  	if h == -1 {
   136  		h = -2
   137  	}
   138  	return h
   139  }
   140  
   141  func strAdd(f *Frame, v, w *Object) (*Object, *BaseException) {
   142  	if w.isInstance(UnicodeType) {
   143  		// CPython explicitly dispatches to unicode here so that's how
   144  		// we do it even though it would seem more natural to override
   145  		// unicode.__radd__.
   146  		ret, raised := toStrUnsafe(v).Decode(f, EncodeDefault, EncodeStrict)
   147  		if raised != nil {
   148  			return nil, raised
   149  		}
   150  		return unicodeAdd(f, ret.ToObject(), w)
   151  	}
   152  	if !w.isInstance(StrType) {
   153  		return NotImplemented, nil
   154  	}
   155  	stringV, stringW := toStrUnsafe(v).Value(), toStrUnsafe(w).Value()
   156  	if len(stringV)+len(stringW) < 0 {
   157  		// This indicates an int overflow.
   158  		return nil, f.RaiseType(OverflowErrorType, errResultTooLarge)
   159  	}
   160  	return NewStr(stringV + stringW).ToObject(), nil
   161  }
   162  
   163  func strCapitalize(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   164  	if raised := checkMethodArgs(f, "capitalize", args, StrType); raised != nil {
   165  		return nil, raised
   166  	}
   167  	s := toStrUnsafe(args[0]).Value()
   168  	numBytes := len(s)
   169  	if numBytes == 0 {
   170  		return args[0], nil
   171  	}
   172  	b := make([]byte, numBytes)
   173  	b[0] = toUpper(s[0])
   174  	for i := 1; i < numBytes; i++ {
   175  		b[i] = toLower(s[i])
   176  	}
   177  	return NewStr(string(b)).ToObject(), nil
   178  }
   179  
   180  func strCenter(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   181  	s, width, fill, raised := strJustDecodeArgs(f, args, "center")
   182  	if raised != nil {
   183  		return nil, raised
   184  	}
   185  	if len(s) >= width {
   186  		return NewStr(s).ToObject(), nil
   187  	}
   188  	marg := width - len(s)
   189  	left := marg/2 + (marg & width & 1)
   190  	return NewStr(pad(s, left, marg-left, fill)).ToObject(), nil
   191  }
   192  
   193  func strContains(f *Frame, o *Object, value *Object) (*Object, *BaseException) {
   194  	if value.isInstance(UnicodeType) {
   195  		decoded, raised := toStrUnsafe(o).Decode(f, EncodeDefault, EncodeStrict)
   196  		if raised != nil {
   197  			return nil, raised
   198  		}
   199  		return unicodeContains(f, decoded.ToObject(), value)
   200  	}
   201  	if !value.isInstance(StrType) {
   202  		format := "'in <string>' requires string as left operand, not %s"
   203  		return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, value.typ.Name()))
   204  	}
   205  	return GetBool(strings.Contains(toStrUnsafe(o).Value(), toStrUnsafe(value).Value())).ToObject(), nil
   206  }
   207  
   208  func strCount(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   209  	if raised := checkMethodArgs(f, "count", args, StrType, ObjectType); raised != nil {
   210  		return nil, raised
   211  	}
   212  	s := toStrUnsafe(args[0]).Value()
   213  	sep := toStrUnsafe(args[1]).Value()
   214  	cnt := strings.Count(s, sep)
   215  	return NewInt(cnt).ToObject(), nil
   216  }
   217  
   218  func strDecode(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   219  	// TODO: Accept unicode for encoding and errors args.
   220  	expectedTypes := []*Type{StrType, StrType, StrType}
   221  	argc := len(args)
   222  	if argc >= 1 && argc < 3 {
   223  		expectedTypes = expectedTypes[:argc]
   224  	}
   225  	if raised := checkMethodArgs(f, "decode", args, expectedTypes...); raised != nil {
   226  		return nil, raised
   227  	}
   228  	encoding := EncodeDefault
   229  	if argc > 1 {
   230  		encoding = toStrUnsafe(args[1]).Value()
   231  	}
   232  	errors := EncodeStrict
   233  	if argc > 2 {
   234  		errors = toStrUnsafe(args[2]).Value()
   235  	}
   236  	s, raised := toStrUnsafe(args[0]).Decode(f, encoding, errors)
   237  	if raised != nil {
   238  		return nil, raised
   239  	}
   240  	return s.ToObject(), nil
   241  }
   242  
   243  func strEndsWith(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   244  	return strStartsEndsWith(f, "endswith", args)
   245  }
   246  
   247  func strEq(f *Frame, v, w *Object) (*Object, *BaseException) {
   248  	return strCompare(v, w, False, True, False), nil
   249  }
   250  
   251  // strFind returns the lowest index in s where the substring sub is found such
   252  // that sub is wholly contained in s[start:end]. Return -1 on failure.
   253  func strFind(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   254  	return strFindOrIndex(f, args, func(s, sub string) (int, *BaseException) {
   255  		return strings.Index(s, sub), nil
   256  	})
   257  }
   258  
   259  func strGE(f *Frame, v, w *Object) (*Object, *BaseException) {
   260  	return strCompare(v, w, False, True, True), nil
   261  }
   262  
   263  // strGetItem returns a slice of string depending on whether index is an integer
   264  // or a slice. If index is neither of those types then a TypeError is returned.
   265  func strGetItem(f *Frame, o, key *Object) (*Object, *BaseException) {
   266  	s := toStrUnsafe(o).Value()
   267  	switch {
   268  	case key.typ.slots.Index != nil:
   269  		index, raised := IndexInt(f, key)
   270  		if raised != nil {
   271  			return nil, raised
   272  		}
   273  		index, raised = seqCheckedIndex(f, len(s), index)
   274  		if raised != nil {
   275  			return nil, raised
   276  		}
   277  		return NewStr(s[index : index+1]).ToObject(), nil
   278  	case key.isInstance(SliceType):
   279  		slice := toSliceUnsafe(key)
   280  		start, stop, step, sliceLen, raised := slice.calcSlice(f, len(s))
   281  		if raised != nil {
   282  			return nil, raised
   283  		}
   284  		if step == 1 {
   285  			return NewStr(s[start:stop]).ToObject(), nil
   286  		}
   287  		result := make([]byte, 0, sliceLen)
   288  		for j := start; j != stop; j += step {
   289  			result = append(result, s[j])
   290  		}
   291  		return NewStr(string(result)).ToObject(), nil
   292  	}
   293  	return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("string indices must be integers or slice, not %s", key.typ.Name()))
   294  }
   295  
   296  func strGetNewArgs(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   297  	if raised := checkMethodArgs(f, "__getnewargs__", args, StrType); raised != nil {
   298  		return nil, raised
   299  	}
   300  	return NewTuple1(args[0]).ToObject(), nil
   301  }
   302  
   303  func strGT(f *Frame, v, w *Object) (*Object, *BaseException) {
   304  	return strCompare(v, w, False, False, True), nil
   305  }
   306  
   307  func strHash(f *Frame, o *Object) (*Object, *BaseException) {
   308  	s := toStrUnsafe(o)
   309  	p := (*unsafe.Pointer)(unsafe.Pointer(&s.hash))
   310  	if v := atomic.LoadPointer(p); v != unsafe.Pointer(nil) {
   311  		return (*Int)(v).ToObject(), nil
   312  	}
   313  	h := NewInt(hashString(toStrUnsafe(o).Value()))
   314  	atomic.StorePointer(p, unsafe.Pointer(h))
   315  	return h.ToObject(), nil
   316  }
   317  
   318  func strIndex(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   319  	return strFindOrIndex(f, args, func(s, sub string) (i int, raised *BaseException) {
   320  		i = strings.Index(s, sub)
   321  		if i == -1 {
   322  			raised = f.RaiseType(ValueErrorType, "substring not found")
   323  		}
   324  		return i, raised
   325  	})
   326  }
   327  
   328  func strIsAlNum(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   329  	if raised := checkMethodArgs(f, "isalnum", args, StrType); raised != nil {
   330  		return nil, raised
   331  	}
   332  	s := toStrUnsafe(args[0]).Value()
   333  	if len(s) == 0 {
   334  		return False.ToObject(), nil
   335  	}
   336  	for i := range s {
   337  		if !isAlNum(s[i]) {
   338  			return False.ToObject(), nil
   339  		}
   340  	}
   341  	return True.ToObject(), nil
   342  }
   343  
   344  func strIsAlpha(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   345  	if raised := checkMethodArgs(f, "isalpha", args, StrType); raised != nil {
   346  		return nil, raised
   347  	}
   348  	s := toStrUnsafe(args[0]).Value()
   349  	if len(s) == 0 {
   350  		return False.ToObject(), nil
   351  	}
   352  	for i := range s {
   353  		if !isAlpha(s[i]) {
   354  			return False.ToObject(), nil
   355  		}
   356  	}
   357  	return True.ToObject(), nil
   358  }
   359  
   360  func strIsDigit(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   361  	if raised := checkMethodArgs(f, "isdigit", args, StrType); raised != nil {
   362  		return nil, raised
   363  	}
   364  	s := toStrUnsafe(args[0]).Value()
   365  	if len(s) == 0 {
   366  		return False.ToObject(), nil
   367  	}
   368  	for i := range s {
   369  		if !isDigit(s[i]) {
   370  			return False.ToObject(), nil
   371  		}
   372  	}
   373  	return True.ToObject(), nil
   374  }
   375  
   376  func strIsLower(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   377  	if raised := checkMethodArgs(f, "islower", args, StrType); raised != nil {
   378  		return nil, raised
   379  	}
   380  	s := toStrUnsafe(args[0]).Value()
   381  	if len(s) == 0 {
   382  		return False.ToObject(), nil
   383  	}
   384  	for i := range s {
   385  		if !isLower(s[i]) {
   386  			return False.ToObject(), nil
   387  		}
   388  	}
   389  	return True.ToObject(), nil
   390  }
   391  
   392  func strIsSpace(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   393  	if raised := checkMethodArgs(f, "isspace", args, StrType); raised != nil {
   394  		return nil, raised
   395  	}
   396  	s := toStrUnsafe(args[0]).Value()
   397  	if len(s) == 0 {
   398  		return False.ToObject(), nil
   399  	}
   400  	for i := range s {
   401  		if !isSpace(s[i]) {
   402  			return False.ToObject(), nil
   403  		}
   404  	}
   405  	return True.ToObject(), nil
   406  }
   407  
   408  func strIsTitle(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   409  	if raised := checkMethodArgs(f, "istitle", args, StrType); raised != nil {
   410  		return nil, raised
   411  	}
   412  
   413  	s := toStrUnsafe(args[0]).Value()
   414  	if len(s) == 0 {
   415  		return False.ToObject(), nil
   416  	}
   417  
   418  	if len(s) == 1 {
   419  		return GetBool(isUpper(s[0])).ToObject(), nil
   420  	}
   421  
   422  	cased := false
   423  	previousIsCased := false
   424  
   425  	for i := range s {
   426  		if isUpper(s[i]) {
   427  			if previousIsCased {
   428  				return False.ToObject(), nil
   429  			}
   430  			previousIsCased = true
   431  			cased = true
   432  		} else if isLower(s[i]) {
   433  			if !previousIsCased {
   434  				return False.ToObject(), nil
   435  			}
   436  			previousIsCased = true
   437  			cased = true
   438  		} else {
   439  			previousIsCased = false
   440  		}
   441  	}
   442  
   443  	return GetBool(cased).ToObject(), nil
   444  }
   445  
   446  func strIsUpper(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   447  	if raised := checkMethodArgs(f, "isupper", args, StrType); raised != nil {
   448  		return nil, raised
   449  	}
   450  	s := toStrUnsafe(args[0]).Value()
   451  	if len(s) == 0 {
   452  		return False.ToObject(), nil
   453  	}
   454  	for i := range s {
   455  		if !isUpper(s[i]) {
   456  			return False.ToObject(), nil
   457  		}
   458  	}
   459  	return True.ToObject(), nil
   460  }
   461  
   462  func strJoin(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   463  	if raised := checkMethodArgs(f, "join", args, StrType, ObjectType); raised != nil {
   464  		return nil, raised
   465  	}
   466  	sep := toStrUnsafe(args[0]).Value()
   467  	var result *Object
   468  	raised := seqApply(f, args[1], func(parts []*Object, _ bool) *BaseException {
   469  		numParts := len(parts)
   470  		if numParts == 0 {
   471  			result = NewStr("").ToObject()
   472  			return nil
   473  		}
   474  		// Calculate the size of the required buffer.
   475  		numChars := (numParts - 1) * len(sep)
   476  		for i, part := range parts {
   477  			if part.isInstance(StrType) {
   478  				numChars += len(toStrUnsafe(part).Value())
   479  			} else if part.isInstance(UnicodeType) {
   480  				// Some element was unicode so use the unicode
   481  				// implementation.
   482  				var raised *BaseException
   483  				s, raised := unicodeCoerce(f, args[0])
   484  				if raised != nil {
   485  					return raised
   486  				}
   487  				result, raised = unicodeJoinParts(f, s, parts)
   488  				return raised
   489  			} else {
   490  				format := "sequence item %d: expected string, %s found"
   491  				return f.RaiseType(TypeErrorType, fmt.Sprintf(format, i, part.typ.Name()))
   492  			}
   493  		}
   494  		// Piece together the result string into buf.
   495  		buf := bytes.Buffer{}
   496  		buf.Grow(numChars)
   497  		for i, part := range parts {
   498  			if i > 0 {
   499  				buf.WriteString(sep)
   500  			}
   501  			buf.WriteString(toStrUnsafe(part).Value())
   502  		}
   503  		result = NewStr(buf.String()).ToObject()
   504  		return nil
   505  	})
   506  	if raised != nil {
   507  		return nil, raised
   508  	}
   509  	return result, nil
   510  }
   511  
   512  func strLE(f *Frame, v, w *Object) (*Object, *BaseException) {
   513  	return strCompare(v, w, True, True, False), nil
   514  }
   515  
   516  func strLen(f *Frame, o *Object) (*Object, *BaseException) {
   517  	return NewInt(len(toStrUnsafe(o).Value())).ToObject(), nil
   518  }
   519  
   520  func strLJust(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   521  	s, width, fill, raised := strJustDecodeArgs(f, args, "ljust")
   522  	if raised != nil {
   523  		return nil, raised
   524  	}
   525  	if len(s) >= width {
   526  		return NewStr(s).ToObject(), nil
   527  	}
   528  	return NewStr(pad(s, 0, width-len(s), fill)).ToObject(), nil
   529  }
   530  
   531  func strLower(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   532  	expectedTypes := []*Type{StrType}
   533  	if raised := checkMethodArgs(f, "lower", args, expectedTypes...); raised != nil {
   534  		return nil, raised
   535  	}
   536  	s := toStrUnsafe(args[0]).Value()
   537  	numBytes := len(s)
   538  	if numBytes == 0 {
   539  		return args[0], nil
   540  	}
   541  	b := make([]byte, numBytes)
   542  	for i := 0; i < numBytes; i++ {
   543  		b[i] = toLower(s[i])
   544  	}
   545  	return NewStr(string(b)).ToObject(), nil
   546  }
   547  
   548  func strLStrip(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   549  	return strStripImpl(f, args, stripSideLeft)
   550  }
   551  
   552  func strLT(f *Frame, v, w *Object) (*Object, *BaseException) {
   553  	return strCompare(v, w, True, False, False), nil
   554  }
   555  
   556  func strMod(f *Frame, v, w *Object) (*Object, *BaseException) {
   557  	s := toStrUnsafe(v).Value()
   558  	switch {
   559  	case w.isInstance(DictType):
   560  		return nil, f.RaiseType(NotImplementedErrorType, "mappings not yet supported")
   561  	case w.isInstance(TupleType):
   562  		return strInterpolate(f, s, toTupleUnsafe(w))
   563  	default:
   564  		return strInterpolate(f, s, NewTuple1(w))
   565  	}
   566  }
   567  
   568  func strMul(f *Frame, v, w *Object) (*Object, *BaseException) {
   569  	s := toStrUnsafe(v).Value()
   570  	n, ok, raised := strRepeatCount(f, len(s), w)
   571  	if raised != nil {
   572  		return nil, raised
   573  	}
   574  	if !ok {
   575  		return NotImplemented, nil
   576  	}
   577  	return NewStr(strings.Repeat(s, n)).ToObject(), nil
   578  }
   579  
   580  func strNative(f *Frame, o *Object) (reflect.Value, *BaseException) {
   581  	return reflect.ValueOf(toStrUnsafe(o).Value()), nil
   582  }
   583  
   584  func strNE(f *Frame, v, w *Object) (*Object, *BaseException) {
   585  	return strCompare(v, w, True, False, True), nil
   586  }
   587  
   588  func strNew(f *Frame, t *Type, args Args, _ KWArgs) (*Object, *BaseException) {
   589  	if t != StrType {
   590  		// Allocate a plain str and then copy it's value into an object
   591  		// of the str subtype.
   592  		s, raised := strNew(f, StrType, args, nil)
   593  		if raised != nil {
   594  			return nil, raised
   595  		}
   596  		result := toStrUnsafe(newObject(t))
   597  		result.value = toStrUnsafe(s).Value()
   598  		return result.ToObject(), nil
   599  	}
   600  	argc := len(args)
   601  	if argc == 0 {
   602  		// Empty string.
   603  		return newObject(t), nil
   604  	}
   605  	if argc != 1 {
   606  		return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("str() takes at most 1 argument (%d given)", argc))
   607  	}
   608  	o := args[0]
   609  	if str := o.typ.slots.Str; str != nil {
   610  		result, raised := str.Fn(f, o)
   611  		if raised != nil {
   612  			return nil, raised
   613  		}
   614  		if !result.isInstance(StrType) {
   615  			format := "__str__ returned non-string (type %s)"
   616  			return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, result.typ.Name()))
   617  		}
   618  		return result, nil
   619  	}
   620  	s, raised := Repr(f, o)
   621  	if raised != nil {
   622  		return nil, raised
   623  	}
   624  	return s.ToObject(), nil
   625  }
   626  
   627  // strReplace returns a copy of the string s with the first n non-overlapping
   628  // instances of old replaced by sub. If old is empty, it matches at the
   629  // beginning of the string. If n < 0, there is no limit on the number of
   630  // replacements.
   631  func strReplace(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   632  	var raised *BaseException
   633  	// TODO: Support unicode replace.
   634  	expectedTypes := []*Type{StrType, StrType, StrType, ObjectType}
   635  	argc := len(args)
   636  	if argc == 3 {
   637  		expectedTypes = expectedTypes[:argc]
   638  	}
   639  	if raised := checkMethodArgs(f, "replace", args, expectedTypes...); raised != nil {
   640  		return nil, raised
   641  	}
   642  	n := -1
   643  	if argc == 4 {
   644  		n, raised = ToIntValue(f, args[3])
   645  		if raised != nil {
   646  			return nil, raised
   647  		}
   648  	}
   649  	s := toStrUnsafe(args[0]).Value()
   650  	// Returns early if no need to replace.
   651  	if n == 0 {
   652  		return NewStr(s).ToObject(), nil
   653  	}
   654  
   655  	old := toStrUnsafe(args[1]).Value()
   656  	sub := toStrUnsafe(args[2]).Value()
   657  	numBytes := len(s)
   658  	// Even if s and old is blank, replace should return sub, except n is negative.
   659  	// This is CPython specific behavior.
   660  	if numBytes == 0 && old == "" && n >= 0 {
   661  		return NewStr("").ToObject(), nil
   662  	}
   663  	// If old is non-blank, pass to strings.Replace.
   664  	if len(old) > 0 {
   665  		return NewStr(strings.Replace(s, old, sub, n)).ToObject(), nil
   666  	}
   667  
   668  	// If old is blank, insert sub after every bytes on s and beginning.
   669  	if n < 0 {
   670  		n = numBytes + 1
   671  	}
   672  	// Insert sub at beginning.
   673  	buf := bytes.Buffer{}
   674  	buf.WriteString(sub)
   675  	n--
   676  	// Insert after every byte.
   677  	i := 0
   678  	for n > 0 && i < numBytes {
   679  		buf.WriteByte(s[i])
   680  		buf.WriteString(sub)
   681  		i++
   682  		n--
   683  	}
   684  	// Write the remaining string.
   685  	if i < numBytes {
   686  		buf.WriteString(s[i:])
   687  	}
   688  	return NewStr(buf.String()).ToObject(), nil
   689  }
   690  
   691  func strRepr(_ *Frame, o *Object) (*Object, *BaseException) {
   692  	s := toStrUnsafe(o).Value()
   693  	buf := bytes.Buffer{}
   694  	buf.WriteRune('\'')
   695  	numBytes := len(s)
   696  	for i := 0; i < numBytes; i++ {
   697  		r := rune(s[i])
   698  		if escape, ok := escapeMap[r]; ok {
   699  			buf.WriteString(escape)
   700  		} else if r > unicode.MaxASCII || !unicode.IsPrint(r) {
   701  			buf.WriteString(fmt.Sprintf(`\x%02x`, r))
   702  		} else {
   703  			buf.WriteRune(r)
   704  		}
   705  	}
   706  	buf.WriteRune('\'')
   707  	return NewStr(buf.String()).ToObject(), nil
   708  }
   709  
   710  func strRFind(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   711  	return strFindOrIndex(f, args, func(s, sub string) (int, *BaseException) {
   712  		return strings.LastIndex(s, sub), nil
   713  	})
   714  }
   715  
   716  func strRIndex(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   717  	return strFindOrIndex(f, args, func(s, sub string) (i int, raised *BaseException) {
   718  		i = strings.LastIndex(s, sub)
   719  		if i == -1 {
   720  			raised = f.RaiseType(ValueErrorType, "substring not found")
   721  		}
   722  		return i, raised
   723  	})
   724  }
   725  
   726  func strRJust(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   727  	s, width, fill, raised := strJustDecodeArgs(f, args, "rjust")
   728  	if raised != nil {
   729  		return nil, raised
   730  	}
   731  	if len(s) >= width {
   732  		return NewStr(s).ToObject(), nil
   733  	}
   734  	return NewStr(pad(s, width-len(s), 0, fill)).ToObject(), nil
   735  }
   736  
   737  func strSplit(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   738  	expectedTypes := []*Type{StrType, ObjectType, IntType}
   739  	argc := len(args)
   740  	if argc == 1 || argc == 2 {
   741  		expectedTypes = expectedTypes[:argc]
   742  	}
   743  	if raised := checkMethodArgs(f, "split", args, expectedTypes...); raised != nil {
   744  		return nil, raised
   745  	}
   746  	sep := ""
   747  	if argc > 1 {
   748  		if arg1 := args[1]; arg1.isInstance(StrType) {
   749  			sep = toStrUnsafe(arg1).Value()
   750  			if sep == "" {
   751  				return nil, f.RaiseType(ValueErrorType, "empty separator")
   752  			}
   753  		} else if arg1 != None {
   754  			return nil, f.RaiseType(TypeErrorType, "expected a str separator")
   755  		}
   756  	}
   757  	maxSplit := -1
   758  	if argc > 2 {
   759  		if i := toIntUnsafe(args[2]).Value(); i >= 0 {
   760  			maxSplit = i + 1
   761  		}
   762  	}
   763  	s := toStrUnsafe(args[0]).Value()
   764  	var parts []string
   765  	if sep == "" {
   766  		s = strings.TrimLeft(s, string(strASCIISpaces))
   767  		parts = whitespaceSplitRegexp.Split(s, maxSplit)
   768  		l := len(parts)
   769  		if l > 0 && strings.Trim(parts[l-1], string(strASCIISpaces)) == "" {
   770  			parts = parts[:l-1]
   771  		}
   772  	} else {
   773  		parts = strings.SplitN(s, sep, maxSplit)
   774  	}
   775  	results := make([]*Object, len(parts))
   776  	for i, part := range parts {
   777  		results[i] = NewStr(part).ToObject()
   778  	}
   779  	return NewList(results...).ToObject(), nil
   780  }
   781  
   782  func strSplitLines(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   783  	expectedTypes := []*Type{StrType, ObjectType}
   784  	argc := len(args)
   785  	if argc == 1 {
   786  		expectedTypes = expectedTypes[:1]
   787  	}
   788  	if raised := checkMethodArgs(f, "splitlines", args, expectedTypes...); raised != nil {
   789  		return nil, raised
   790  	}
   791  	keepEnds := false
   792  	if argc == 2 {
   793  		i, raised := ToIntValue(f, args[1])
   794  		if raised != nil {
   795  			return nil, raised
   796  		}
   797  		keepEnds = i != 0
   798  	}
   799  	s := toStrUnsafe(args[0]).Value()
   800  	numChars := len(s)
   801  	start, end := 0, 0
   802  	lines := make([]*Object, 0, 2)
   803  	for start < numChars {
   804  		eol := 0
   805  		for end = start; end < numChars; end++ {
   806  			c := s[end]
   807  			if c == '\n' {
   808  				eol = end + 1
   809  				break
   810  			}
   811  			if c == '\r' {
   812  				eol = end + 1
   813  				if eol < numChars && s[eol] == '\n' {
   814  					eol++
   815  				}
   816  				break
   817  			}
   818  		}
   819  		if end >= numChars {
   820  			eol = end
   821  		}
   822  		line := ""
   823  		if keepEnds {
   824  			line = s[start:eol]
   825  		} else {
   826  			line = s[start:end]
   827  		}
   828  		lines = append(lines, NewStr(line).ToObject())
   829  		start = eol
   830  	}
   831  	return NewList(lines...).ToObject(), nil
   832  }
   833  
   834  func strStrip(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   835  	return strStripImpl(f, args, stripSideBoth)
   836  }
   837  
   838  func strRStrip(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   839  	return strStripImpl(f, args, stripSideRight)
   840  }
   841  
   842  func strStripImpl(f *Frame, args Args, side stripSide) (*Object, *BaseException) {
   843  	expectedTypes := []*Type{StrType, ObjectType}
   844  	argc := len(args)
   845  	if argc == 1 {
   846  		expectedTypes = expectedTypes[:argc]
   847  	}
   848  	if raised := checkMethodArgs(f, "strip", args, expectedTypes...); raised != nil {
   849  		return nil, raised
   850  	}
   851  	s := toStrUnsafe(args[0])
   852  	charsArg := None
   853  	if argc > 1 {
   854  		charsArg = args[1]
   855  	}
   856  	var chars []byte
   857  	switch {
   858  	case charsArg.isInstance(UnicodeType):
   859  		u, raised := s.Decode(f, EncodeDefault, EncodeStrict)
   860  		if raised != nil {
   861  			return nil, raised
   862  		}
   863  		return unicodeStrip(f, Args{u.ToObject(), charsArg}, nil)
   864  	case charsArg.isInstance(StrType):
   865  		chars = []byte(toStrUnsafe(charsArg).Value())
   866  	case charsArg == None:
   867  		chars = strASCIISpaces
   868  	default:
   869  		return nil, f.RaiseType(TypeErrorType, "strip arg must be None, str or unicode")
   870  	}
   871  	byteSlice := []byte(s.Value())
   872  	numBytes := len(byteSlice)
   873  	lindex := 0
   874  	if side == stripSideLeft || side == stripSideBoth {
   875  	LeftStrip:
   876  		for ; lindex < numBytes; lindex++ {
   877  			b := byteSlice[lindex]
   878  			for _, c := range chars {
   879  				if b == c {
   880  					continue LeftStrip
   881  				}
   882  			}
   883  			break
   884  		}
   885  	}
   886  	rindex := numBytes
   887  	if side == stripSideRight || side == stripSideBoth {
   888  	RightStrip:
   889  		for ; rindex > lindex; rindex-- {
   890  			b := byteSlice[rindex-1]
   891  			for _, c := range chars {
   892  				if b == c {
   893  					continue RightStrip
   894  				}
   895  			}
   896  			break
   897  		}
   898  	}
   899  	return NewStr(string(byteSlice[lindex:rindex])).ToObject(), nil
   900  }
   901  
   902  func strStartsWith(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   903  	return strStartsEndsWith(f, "startswith", args)
   904  }
   905  
   906  func strStr(_ *Frame, o *Object) (*Object, *BaseException) {
   907  	if o.typ == StrType {
   908  		return o, nil
   909  	}
   910  	return NewStr(toStrUnsafe(o).Value()).ToObject(), nil
   911  }
   912  
   913  func strSwapCase(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   914  	if raised := checkMethodArgs(f, "swapcase", args, StrType); raised != nil {
   915  		return nil, raised
   916  	}
   917  	s := toStrUnsafe(args[0]).Value()
   918  	numBytes := len(s)
   919  	if numBytes == 0 {
   920  		return args[0], nil
   921  	}
   922  	b := make([]byte, numBytes)
   923  	for i := 0; i < numBytes; i++ {
   924  		if isLower(s[i]) {
   925  			b[i] = toUpper(s[i])
   926  		} else if isUpper(s[i]) {
   927  			b[i] = toLower(s[i])
   928  		} else {
   929  			b[i] = s[i]
   930  		}
   931  	}
   932  	return NewStr(string(b)).ToObject(), nil
   933  }
   934  
   935  func initStrType(dict map[string]*Object) {
   936  	dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", strGetNewArgs).ToObject()
   937  	dict["capitalize"] = newBuiltinFunction("capitalize", strCapitalize).ToObject()
   938  	dict["count"] = newBuiltinFunction("count", strCount).ToObject()
   939  	dict["center"] = newBuiltinFunction("center", strCenter).ToObject()
   940  	dict["decode"] = newBuiltinFunction("decode", strDecode).ToObject()
   941  	dict["endswith"] = newBuiltinFunction("endswith", strEndsWith).ToObject()
   942  	dict["find"] = newBuiltinFunction("find", strFind).ToObject()
   943  	dict["index"] = newBuiltinFunction("index", strIndex).ToObject()
   944  	dict["isalnum"] = newBuiltinFunction("isalnum", strIsAlNum).ToObject()
   945  	dict["isalpha"] = newBuiltinFunction("isalpha", strIsAlpha).ToObject()
   946  	dict["isdigit"] = newBuiltinFunction("isdigit", strIsDigit).ToObject()
   947  	dict["islower"] = newBuiltinFunction("islower", strIsLower).ToObject()
   948  	dict["isspace"] = newBuiltinFunction("isspace", strIsSpace).ToObject()
   949  	dict["istitle"] = newBuiltinFunction("istitle", strIsTitle).ToObject()
   950  	dict["isupper"] = newBuiltinFunction("isupper", strIsUpper).ToObject()
   951  	dict["join"] = newBuiltinFunction("join", strJoin).ToObject()
   952  	dict["lower"] = newBuiltinFunction("lower", strLower).ToObject()
   953  	dict["ljust"] = newBuiltinFunction("ljust", strLJust).ToObject()
   954  	dict["lstrip"] = newBuiltinFunction("lstrip", strLStrip).ToObject()
   955  	dict["rfind"] = newBuiltinFunction("rfind", strRFind).ToObject()
   956  	dict["rindex"] = newBuiltinFunction("rindex", strRIndex).ToObject()
   957  	dict["rjust"] = newBuiltinFunction("rjust", strRJust).ToObject()
   958  	dict["split"] = newBuiltinFunction("split", strSplit).ToObject()
   959  	dict["splitlines"] = newBuiltinFunction("splitlines", strSplitLines).ToObject()
   960  	dict["startswith"] = newBuiltinFunction("startswith", strStartsWith).ToObject()
   961  	dict["strip"] = newBuiltinFunction("strip", strStrip).ToObject()
   962  	dict["swapcase"] = newBuiltinFunction("swapcase", strSwapCase).ToObject()
   963  	dict["replace"] = newBuiltinFunction("replace", strReplace).ToObject()
   964  	dict["rstrip"] = newBuiltinFunction("rstrip", strRStrip).ToObject()
   965  	dict["title"] = newBuiltinFunction("title", strTitle).ToObject()
   966  	dict["upper"] = newBuiltinFunction("upper", strUpper).ToObject()
   967  	dict["zfill"] = newBuiltinFunction("zfill", strZFill).ToObject()
   968  	StrType.slots.Add = &binaryOpSlot{strAdd}
   969  	StrType.slots.Contains = &binaryOpSlot{strContains}
   970  	StrType.slots.Eq = &binaryOpSlot{strEq}
   971  	StrType.slots.GE = &binaryOpSlot{strGE}
   972  	StrType.slots.GetItem = &binaryOpSlot{strGetItem}
   973  	StrType.slots.GT = &binaryOpSlot{strGT}
   974  	StrType.slots.Hash = &unaryOpSlot{strHash}
   975  	StrType.slots.LE = &binaryOpSlot{strLE}
   976  	StrType.slots.Len = &unaryOpSlot{strLen}
   977  	StrType.slots.LT = &binaryOpSlot{strLT}
   978  	StrType.slots.Mod = &binaryOpSlot{strMod}
   979  	StrType.slots.Mul = &binaryOpSlot{strMul}
   980  	StrType.slots.NE = &binaryOpSlot{strNE}
   981  	StrType.slots.New = &newSlot{strNew}
   982  	StrType.slots.Native = &nativeSlot{strNative}
   983  	StrType.slots.Repr = &unaryOpSlot{strRepr}
   984  	StrType.slots.RMul = &binaryOpSlot{strMul}
   985  	StrType.slots.Str = &unaryOpSlot{strStr}
   986  }
   987  
   988  func strCompare(v, w *Object, ltResult, eqResult, gtResult *Int) *Object {
   989  	if v == w {
   990  		return eqResult.ToObject()
   991  	}
   992  	if !w.isInstance(StrType) {
   993  		return NotImplemented
   994  	}
   995  	s1 := toStrUnsafe(v).Value()
   996  	s2 := toStrUnsafe(w).Value()
   997  	if s1 < s2 {
   998  		return ltResult.ToObject()
   999  	}
  1000  	if s1 == s2 {
  1001  		return eqResult.ToObject()
  1002  	}
  1003  	return gtResult.ToObject()
  1004  }
  1005  
  1006  func strInterpolate(f *Frame, format string, values *Tuple) (*Object, *BaseException) {
  1007  	var buf bytes.Buffer
  1008  	valueIndex := 0
  1009  	index := strings.Index(format, "%")
  1010  	for index != -1 {
  1011  		buf.WriteString(format[:index])
  1012  		format = format[index:]
  1013  		matches := strInterpolationRegexp.FindStringSubmatch(format)
  1014  		if matches == nil {
  1015  			return nil, f.RaiseType(ValueErrorType, "invalid format spec")
  1016  		}
  1017  		flags, fieldType := matches[1], matches[7]
  1018  		if fieldType != "%" && valueIndex >= len(values.elems) {
  1019  			return nil, f.RaiseType(TypeErrorType, "not enough arguments for format string")
  1020  		}
  1021  		fieldWidth := -1
  1022  		if matches[2] == "*" || matches[4] != "" {
  1023  			return nil, f.RaiseType(NotImplementedErrorType, "field width not yet supported")
  1024  		}
  1025  		if matches[2] != "" {
  1026  			var err error
  1027  			fieldWidth, err = strconv.Atoi(matches[2])
  1028  			if err != nil {
  1029  				return nil, f.RaiseType(TypeErrorType, fmt.Sprint(err))
  1030  			}
  1031  		}
  1032  		if flags != "" && flags != "0" {
  1033  			return nil, f.RaiseType(NotImplementedErrorType, "conversion flags not yet supported")
  1034  		}
  1035  		var val string
  1036  		switch fieldType {
  1037  		case "r", "s":
  1038  			o := values.elems[valueIndex]
  1039  			var s *Str
  1040  			var raised *BaseException
  1041  			if fieldType == "r" {
  1042  				s, raised = Repr(f, o)
  1043  			} else {
  1044  				s, raised = ToStr(f, o)
  1045  			}
  1046  			if raised != nil {
  1047  				return nil, raised
  1048  			}
  1049  			val = s.Value()
  1050  			if fieldWidth > 0 {
  1051  				val = strLeftPad(val, fieldWidth, " ")
  1052  			}
  1053  			buf.WriteString(val)
  1054  			valueIndex++
  1055  		case "f":
  1056  			o := values.elems[valueIndex]
  1057  			if v, ok := floatCoerce(o); ok {
  1058  				val := strconv.FormatFloat(v, 'f', 6, 64)
  1059  				if fieldWidth > 0 {
  1060  					fillchar := " "
  1061  					if flags != "" {
  1062  						fillchar = flags
  1063  					}
  1064  					val = strLeftPad(val, fieldWidth, fillchar)
  1065  				}
  1066  				buf.WriteString(val)
  1067  				valueIndex++
  1068  			} else {
  1069  				return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("float argument required, not %s", o.typ.Name()))
  1070  			}
  1071  		case "d", "x", "X", "o":
  1072  			o := values.elems[valueIndex]
  1073  			i, raised := ToInt(f, values.elems[valueIndex])
  1074  			if raised != nil {
  1075  				return nil, raised
  1076  			}
  1077  			if fieldType == "d" {
  1078  				s, raised := ToStr(f, i)
  1079  				if raised != nil {
  1080  					return nil, raised
  1081  				}
  1082  				val = s.Value()
  1083  			} else if matches[7] == "o" {
  1084  				if o.isInstance(LongType) {
  1085  					val = toLongUnsafe(o).Value().Text(8)
  1086  				} else {
  1087  					val = strconv.FormatInt(int64(toIntUnsafe(i).Value()), 8)
  1088  				}
  1089  			} else {
  1090  				if o.isInstance(LongType) {
  1091  					val = toLongUnsafe(o).Value().Text(16)
  1092  				} else {
  1093  					val = strconv.FormatInt(int64(toIntUnsafe(i).Value()), 16)
  1094  				}
  1095  				if fieldType == "X" {
  1096  					val = strings.ToUpper(val)
  1097  				}
  1098  			}
  1099  			if fieldWidth > 0 {
  1100  				fillchar := " "
  1101  				if flags != "" {
  1102  					fillchar = flags
  1103  				}
  1104  				val = strLeftPad(val, fieldWidth, fillchar)
  1105  			}
  1106  			buf.WriteString(val)
  1107  			valueIndex++
  1108  		case "%":
  1109  			val = "%"
  1110  			if fieldWidth > 0 {
  1111  				val = strLeftPad(val, fieldWidth, " ")
  1112  			}
  1113  			buf.WriteString(val)
  1114  		default:
  1115  			format := "conversion type not yet supported: %s"
  1116  			return nil, f.RaiseType(NotImplementedErrorType, fmt.Sprintf(format, fieldType))
  1117  		}
  1118  		format = format[len(matches[0]):]
  1119  		index = strings.Index(format, "%")
  1120  	}
  1121  	if valueIndex < len(values.elems) {
  1122  		return nil, f.RaiseType(TypeErrorType, "not all arguments converted during string formatting")
  1123  	}
  1124  	buf.WriteString(format)
  1125  	return NewStr(buf.String()).ToObject(), nil
  1126  }
  1127  
  1128  func strRepeatCount(f *Frame, numChars int, mult *Object) (int, bool, *BaseException) {
  1129  	var n int
  1130  	switch {
  1131  	case mult.isInstance(IntType):
  1132  		n = toIntUnsafe(mult).Value()
  1133  	case mult.isInstance(LongType):
  1134  		l := toLongUnsafe(mult).Value()
  1135  		if !numInIntRange(l) {
  1136  			return 0, false, f.RaiseType(OverflowErrorType, fmt.Sprintf("cannot fit '%s' into an index-sized integer", mult.typ.Name()))
  1137  		}
  1138  		n = int(l.Int64())
  1139  	default:
  1140  		return 0, false, nil
  1141  	}
  1142  	if n <= 0 {
  1143  		return 0, true, nil
  1144  	}
  1145  	if numChars > MaxInt/n {
  1146  		return 0, false, f.RaiseType(OverflowErrorType, errResultTooLarge)
  1147  	}
  1148  	return n, true, nil
  1149  }
  1150  
  1151  func adjustIndex(start, end, length int) (int, int) {
  1152  	if end > length {
  1153  		end = length
  1154  	} else if end < 0 {
  1155  		end += length
  1156  		if end < 0 {
  1157  			end = 0
  1158  		}
  1159  	}
  1160  	if start < 0 {
  1161  		start += length
  1162  		if start < 0 {
  1163  			start = 0
  1164  		}
  1165  	}
  1166  	return start, end
  1167  }
  1168  
  1169  func strStartsEndsWith(f *Frame, method string, args Args) (*Object, *BaseException) {
  1170  	expectedTypes := []*Type{StrType, ObjectType, IntType, IntType}
  1171  	argc := len(args)
  1172  	if argc == 2 || argc == 3 {
  1173  		expectedTypes = expectedTypes[:argc]
  1174  	}
  1175  	if raised := checkMethodArgs(f, method, args, expectedTypes...); raised != nil {
  1176  		return nil, raised
  1177  	}
  1178  	matchesArg := args[1]
  1179  	var matches []string
  1180  	switch {
  1181  	case matchesArg.isInstance(TupleType):
  1182  		elems := toTupleUnsafe(matchesArg).elems
  1183  		matches = make([]string, len(elems))
  1184  		for i, o := range elems {
  1185  			if !o.isInstance(BaseStringType) {
  1186  				return nil, f.RaiseType(TypeErrorType, "expected a str")
  1187  			}
  1188  			s, raised := ToStr(f, o)
  1189  			if raised != nil {
  1190  				return nil, raised
  1191  			}
  1192  			matches[i] = s.Value()
  1193  		}
  1194  	case matchesArg.isInstance(BaseStringType):
  1195  		s, raised := ToStr(f, matchesArg)
  1196  		if raised != nil {
  1197  			return nil, raised
  1198  		}
  1199  		matches = []string{s.Value()}
  1200  	default:
  1201  		msg := " first arg must be str, unicode, or tuple, not "
  1202  		return nil, f.RaiseType(TypeErrorType, method+msg+matchesArg.typ.Name())
  1203  	}
  1204  	s := toStrUnsafe(args[0]).Value()
  1205  	l := len(s)
  1206  	start, end := 0, l
  1207  	if argc >= 3 {
  1208  		start = toIntUnsafe(args[2]).Value()
  1209  	}
  1210  	if argc == 4 {
  1211  		end = toIntUnsafe(args[3]).Value()
  1212  	}
  1213  	start, end = adjustIndex(start, end, l)
  1214  	if start > end {
  1215  		// start == end may still return true when matching ''.
  1216  		return False.ToObject(), nil
  1217  	}
  1218  	s = s[start:end]
  1219  	matcher := strings.HasPrefix
  1220  	if method == "endswith" {
  1221  		matcher = strings.HasSuffix
  1222  	}
  1223  	for _, match := range matches {
  1224  		if matcher(s, match) {
  1225  			return True.ToObject(), nil
  1226  		}
  1227  	}
  1228  	return False.ToObject(), nil
  1229  }
  1230  
  1231  func strTitle(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
  1232  	expectedTypes := []*Type{StrType}
  1233  	if raised := checkMethodArgs(f, "title", args, expectedTypes...); raised != nil {
  1234  		return nil, raised
  1235  	}
  1236  	s := toStrUnsafe(args[0]).Value()
  1237  	numBytes := len(s)
  1238  	if numBytes == 0 {
  1239  		return args[0], nil
  1240  	}
  1241  	b := make([]byte, numBytes)
  1242  	previousIsCased := false
  1243  	for i := 0; i < numBytes; i++ {
  1244  		c := s[i]
  1245  		switch {
  1246  		case isLower(c):
  1247  			if !previousIsCased {
  1248  				c = toUpper(c)
  1249  			}
  1250  			previousIsCased = true
  1251  		case isUpper(c):
  1252  			if previousIsCased {
  1253  				c = toLower(c)
  1254  			}
  1255  			previousIsCased = true
  1256  		default:
  1257  			previousIsCased = false
  1258  		}
  1259  		b[i] = c
  1260  	}
  1261  	return NewStr(string(b)).ToObject(), nil
  1262  }
  1263  
  1264  func strUpper(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
  1265  	expectedTypes := []*Type{StrType}
  1266  	if raised := checkMethodArgs(f, "upper", args, expectedTypes...); raised != nil {
  1267  		return nil, raised
  1268  	}
  1269  	s := toStrUnsafe(args[0]).Value()
  1270  	numBytes := len(s)
  1271  	if numBytes == 0 {
  1272  		return args[0], nil
  1273  	}
  1274  	b := make([]byte, numBytes)
  1275  	for i := 0; i < numBytes; i++ {
  1276  		b[i] = toUpper(s[i])
  1277  	}
  1278  	return NewStr(string(b)).ToObject(), nil
  1279  }
  1280  
  1281  func strZFill(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
  1282  	if raised := checkMethodArgs(f, "zfill", args, StrType, ObjectType); raised != nil {
  1283  		return nil, raised
  1284  	}
  1285  	s := toStrUnsafe(args[0]).Value()
  1286  	width, raised := ToIntValue(f, args[1])
  1287  	if raised != nil {
  1288  		return nil, raised
  1289  	}
  1290  	return NewStr(strLeftPad(s, width, "0")).ToObject(), nil
  1291  }
  1292  
  1293  func init() {
  1294  	InternStr("")
  1295  	for i := 0; i < 256; i++ {
  1296  		InternStr(string([]byte{byte(i)}))
  1297  	}
  1298  }
  1299  
  1300  func toLower(b byte) byte {
  1301  	if isUpper(b) {
  1302  		return b + caseOffset
  1303  	}
  1304  	return b
  1305  }
  1306  
  1307  func toUpper(b byte) byte {
  1308  	if isLower(b) {
  1309  		return b - caseOffset
  1310  	}
  1311  	return b
  1312  }
  1313  
  1314  func isAlNum(c byte) bool {
  1315  	return isAlpha(c) || isDigit(c)
  1316  }
  1317  
  1318  func isAlpha(c byte) bool {
  1319  	return isUpper(c) || isLower(c)
  1320  }
  1321  
  1322  func isDigit(c byte) bool {
  1323  	return '0' <= c && c <= '9'
  1324  }
  1325  
  1326  func isLower(c byte) bool {
  1327  	return 'a' <= c && c <= 'z'
  1328  }
  1329  
  1330  func isSpace(c byte) bool {
  1331  	switch c {
  1332  	case ' ', '\n', '\t', '\v', '\f', '\r':
  1333  		return true
  1334  	default:
  1335  		return false
  1336  	}
  1337  }
  1338  
  1339  func isUpper(c byte) bool {
  1340  	return 'A' <= c && c <= 'Z'
  1341  }
  1342  
  1343  func pad(s string, left int, right int, fillchar string) string {
  1344  	buf := bytes.Buffer{}
  1345  
  1346  	if left < 0 {
  1347  		left = 0
  1348  	}
  1349  
  1350  	if right < 0 {
  1351  		right = 0
  1352  	}
  1353  
  1354  	if left == 0 && right == 0 {
  1355  		return s
  1356  	}
  1357  
  1358  	buf.Grow(left + len(s) + right)
  1359  	buf.WriteString(strings.Repeat(fillchar, left))
  1360  	buf.WriteString(s)
  1361  	buf.WriteString(strings.Repeat(fillchar, right))
  1362  
  1363  	return buf.String()
  1364  }
  1365  
  1366  // strLeftPad returns s padded with fillchar so that its length is at least width.
  1367  // Fillchar must be a single character. When fillchar is "0", s starting with a
  1368  // sign are handled correctly.
  1369  func strLeftPad(s string, width int, fillchar string) string {
  1370  	l := len(s)
  1371  	if width <= l {
  1372  		return s
  1373  	}
  1374  	buf := bytes.Buffer{}
  1375  	buf.Grow(width)
  1376  	if l > 0 && fillchar == "0" && (s[0] == '-' || s[0] == '+') {
  1377  		buf.WriteByte(s[0])
  1378  		s = s[1:]
  1379  		l = len(s)
  1380  		width--
  1381  	}
  1382  	// TODO: Support or throw fillchar len more than one.
  1383  	buf.WriteString(strings.Repeat(fillchar, width-l))
  1384  	buf.WriteString(s)
  1385  	return buf.String()
  1386  }
  1387  
  1388  type indexFunc func(string, string) (int, *BaseException)
  1389  
  1390  func strFindOrIndex(f *Frame, args Args, fn indexFunc) (*Object, *BaseException) {
  1391  	// TODO: Support for unicode substring.
  1392  	expectedTypes := []*Type{StrType, StrType, ObjectType, ObjectType}
  1393  	argc := len(args)
  1394  	if argc == 2 || argc == 3 {
  1395  		expectedTypes = expectedTypes[:argc]
  1396  	}
  1397  	if raised := checkMethodArgs(f, "find/index", args, expectedTypes...); raised != nil {
  1398  		return nil, raised
  1399  	}
  1400  	s := toStrUnsafe(args[0]).Value()
  1401  	l := len(s)
  1402  	start, end := 0, l
  1403  	var raised *BaseException
  1404  	if argc >= 3 && args[2] != None {
  1405  		start, raised = IndexInt(f, args[2])
  1406  		if raised != nil {
  1407  			return nil, raised
  1408  		}
  1409  	}
  1410  	if argc == 4 && args[3] != None {
  1411  		end, raised = IndexInt(f, args[3])
  1412  		if raised != nil {
  1413  			return nil, raised
  1414  		}
  1415  	}
  1416  	// Default to an impossible search.
  1417  	search, sub := "", "-"
  1418  	if start <= l {
  1419  		start, end = adjustIndex(start, end, l)
  1420  		if start <= end {
  1421  			sub = toStrUnsafe(args[1]).Value()
  1422  			search = s[start:end]
  1423  		}
  1424  	}
  1425  	index, raised := fn(search, sub)
  1426  	if raised != nil {
  1427  		return nil, raised
  1428  	}
  1429  	if index != -1 {
  1430  		index += start
  1431  	}
  1432  	return NewInt(index).ToObject(), nil
  1433  }
  1434  
  1435  func strJustDecodeArgs(f *Frame, args Args, name string) (string, int, string, *BaseException) {
  1436  	expectedTypes := []*Type{StrType, IntType, StrType}
  1437  	if raised := checkMethodArgs(f, name, args, expectedTypes...); raised != nil {
  1438  		return "", 0, "", raised
  1439  	}
  1440  	s := toStrUnsafe(args[0]).Value()
  1441  	width := toIntUnsafe(args[1]).Value()
  1442  	fill := toStrUnsafe(args[2]).Value()
  1443  
  1444  	if numChars := len(fill); numChars != 1 {
  1445  		return s, width, fill, f.RaiseType(TypeErrorType, fmt.Sprintf("%[1]s() argument 2 must be char, not str", name))
  1446  	}
  1447  
  1448  	return s, width, fill, nil
  1449  }