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 }