github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/core.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 "fmt" 19 "log" 20 "reflect" 21 "sync/atomic" 22 ) 23 24 var ( 25 logFatal = func(msg string) { log.Fatal(msg) } 26 // ThreadCount is the number of goroutines started with StartThread that 27 // have not yet joined. 28 ThreadCount int64 29 ) 30 31 // Abs returns the result of o.__abs__ and is equivalent to the Python 32 // expression "abs(o)". 33 func Abs(f *Frame, o *Object) (*Object, *BaseException) { 34 abs := o.typ.slots.Abs 35 if abs == nil { 36 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("bad operand type for abs(): '%s'", o.typ.Name())) 37 } 38 return abs.Fn(f, o) 39 } 40 41 // Add returns the result of adding v and w together according to the 42 // __add/radd__ operator. 43 func Add(f *Frame, v, w *Object) (*Object, *BaseException) { 44 return binaryOp(f, v, w, v.typ.slots.Add, v.typ.slots.RAdd, w.typ.slots.RAdd, "+") 45 } 46 47 // And returns the result of the bitwise and operator v & w according to 48 // __and/rand__. 49 func And(f *Frame, v, w *Object) (*Object, *BaseException) { 50 return binaryOp(f, v, w, v.typ.slots.And, v.typ.slots.RAnd, w.typ.slots.RAnd, "&") 51 } 52 53 // Assert raises an AssertionError if the given cond does not evaluate to true. 54 // If msg is not nil, it is converted to a string via ToStr() and passed as args 55 // to the raised exception. 56 func Assert(f *Frame, cond *Object, msg *Object) *BaseException { 57 result, raised := IsTrue(f, cond) 58 if raised == nil && !result { 59 if msg == nil { 60 raised = f.Raise(AssertionErrorType.ToObject(), nil, nil) 61 } else { 62 var s *Str 63 s, raised = ToStr(f, msg) 64 if raised == nil { 65 raised = f.RaiseType(AssertionErrorType, s.Value()) 66 } 67 } 68 } 69 return raised 70 } 71 72 // Compare implements a 3-way comparison which returns: 73 // 74 // -1 if v < w 75 // 0 if v == w 76 // 1 if v > w 77 // 78 // It closely resembles the behavior of CPython's do_cmp in object.c. 79 func Compare(f *Frame, v, w *Object) (*Object, *BaseException) { 80 cmp := v.typ.slots.Cmp 81 if v.typ == w.typ && cmp != nil { 82 return cmp.Fn(f, v, w) 83 } 84 r, raised := tryRichTo3wayCompare(f, v, w) 85 if r != NotImplemented { 86 return r, raised 87 } 88 r, raised = try3wayCompare(f, v, w) 89 if r != NotImplemented { 90 return r, raised 91 } 92 return NewInt(compareDefault(f, v, w)).ToObject(), nil 93 } 94 95 // Contains checks whether value is present in seq. It first checks the 96 // __contains__ method of seq and, if that is not available, attempts to find 97 // value by iteration over seq. It is equivalent to the Python expression 98 // "value in seq". 99 func Contains(f *Frame, seq, value *Object) (bool, *BaseException) { 100 if contains := seq.typ.slots.Contains; contains != nil { 101 ret, raised := contains.Fn(f, seq, value) 102 if raised != nil { 103 return false, raised 104 } 105 return IsTrue(f, ret) 106 } 107 iter, raised := Iter(f, seq) 108 if raised != nil { 109 return false, raised 110 } 111 o, raised := Next(f, iter) 112 for ; raised == nil; o, raised = Next(f, iter) { 113 eq, raised := Eq(f, o, value) 114 if raised != nil { 115 return false, raised 116 } 117 if ret, raised := IsTrue(f, eq); raised != nil { 118 return false, raised 119 } else if ret { 120 return true, nil 121 } 122 } 123 if !raised.isInstance(StopIterationType) { 124 return false, raised 125 } 126 f.RestoreExc(nil, nil) 127 return false, nil 128 } 129 130 // DelAttr removes the attribute of o given by name. Equivalent to the Python 131 // expression delattr(o, name). 132 func DelAttr(f *Frame, o *Object, name *Str) *BaseException { 133 delAttr := o.typ.slots.DelAttr 134 if delAttr == nil { 135 return f.RaiseType(SystemErrorType, fmt.Sprintf("'%s' object has no __delattr__ method", o.typ.Name())) 136 } 137 return delAttr.Fn(f, o, name) 138 } 139 140 // DelVar removes the named variable from the given namespace dictionary such 141 // as a module globals dict. 142 func DelVar(f *Frame, namespace *Dict, name *Str) *BaseException { 143 deleted, raised := namespace.DelItem(f, name.ToObject()) 144 if raised != nil { 145 return raised 146 } 147 if !deleted { 148 return f.RaiseType(NameErrorType, fmt.Sprintf("name '%s' is not defined", name.Value())) 149 } 150 return nil 151 } 152 153 // DelItem performs the operation del o[key]. 154 func DelItem(f *Frame, o, key *Object) *BaseException { 155 delItem := o.typ.slots.DelItem 156 if delItem == nil { 157 return f.RaiseType(TypeErrorType, fmt.Sprintf("'%s' object does not support item deletion", o.typ.Name())) 158 } 159 return delItem.Fn(f, o, key) 160 } 161 162 // Div returns the result of dividing v by w according to the __div/rdiv__ 163 // operator. 164 func Div(f *Frame, v, w *Object) (*Object, *BaseException) { 165 return binaryOp(f, v, w, v.typ.slots.Div, v.typ.slots.RDiv, w.typ.slots.RDiv, "/") 166 } 167 168 // DivMod returns the result (quotient and remainder tuple) of dividing v by w 169 // according to the __divmod/rdivmod__ operator. 170 func DivMod(f *Frame, v, w *Object) (*Object, *BaseException) { 171 return binaryOp(f, v, w, v.typ.slots.DivMod, v.typ.slots.RDivMod, w.typ.slots.RDivMod, "divmod()") 172 } 173 174 // Eq returns the equality of v and w according to the __eq__ operator. 175 func Eq(f *Frame, v, w *Object) (*Object, *BaseException) { 176 r, raised := compareRich(f, compareOpEq, v, w) 177 if raised != nil { 178 return nil, raised 179 } 180 if r != NotImplemented { 181 return r, nil 182 } 183 return GetBool(compareDefault(f, v, w) == 0).ToObject(), nil 184 } 185 186 // FloorDiv returns the equality of v and w according to the __floordiv/rfloordiv__ operator. 187 func FloorDiv(f *Frame, v, w *Object) (*Object, *BaseException) { 188 return binaryOp(f, v, w, v.typ.slots.FloorDiv, v.typ.slots.RFloorDiv, w.typ.slots.RFloorDiv, "//") 189 } 190 191 // FormatExc calls traceback.format_exc, falling back to the single line 192 // exception message if that fails, e.g. "NameError: name 'x' is not defined\n". 193 func FormatExc(f *Frame) (s string) { 194 exc, tb := f.ExcInfo() 195 defer func() { 196 if s == "" { 197 strResult, raised := ToStr(f, exc.ToObject()) 198 if raised == nil && strResult.Value() != "" { 199 s = fmt.Sprintf("%s: %s\n", exc.typ.Name(), strResult.Value()) 200 } else { 201 s = exc.typ.Name() + "\n" 202 } 203 } 204 f.RestoreExc(exc, tb) 205 }() 206 tbMod, raised := SysModules.GetItemString(f, "traceback") 207 if raised != nil || tbMod == nil { 208 return 209 } 210 formatExc, raised := GetAttr(f, tbMod, NewStr("format_exc"), nil) 211 if raised != nil { 212 return 213 } 214 result, raised := formatExc.Call(f, nil, nil) 215 if raised != nil || !result.isInstance(StrType) { 216 return 217 } 218 return toStrUnsafe(result).Value() 219 } 220 221 // GE returns the result of operation v >= w. 222 func GE(f *Frame, v, w *Object) (*Object, *BaseException) { 223 r, raised := compareRich(f, compareOpGE, v, w) 224 if raised != nil { 225 return nil, raised 226 } 227 if r != NotImplemented { 228 return r, nil 229 } 230 return GetBool(compareDefault(f, v, w) >= 0).ToObject(), nil 231 } 232 233 // GetItem returns the result of operation o[key]. 234 func GetItem(f *Frame, o, key *Object) (*Object, *BaseException) { 235 getItem := o.typ.slots.GetItem 236 if getItem == nil { 237 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("'%s' object has no attribute '__getitem__'", o.typ.Name())) 238 } 239 return getItem.Fn(f, o, key) 240 } 241 242 // GetAttr returns the named attribute of o. Equivalent to the Python expression 243 // getattr(o, name, def). 244 func GetAttr(f *Frame, o *Object, name *Str, def *Object) (*Object, *BaseException) { 245 // TODO: Fall back to __getattr__. 246 getAttribute := o.typ.slots.GetAttribute 247 if getAttribute == nil { 248 msg := fmt.Sprintf("'%s' has no attribute '%s'", o.typ.Name(), name.Value()) 249 return nil, f.RaiseType(AttributeErrorType, msg) 250 } 251 result, raised := getAttribute.Fn(f, o, name) 252 if raised != nil && raised.isInstance(AttributeErrorType) && def != nil { 253 f.RestoreExc(nil, nil) 254 result, raised = def, nil 255 } 256 return result, raised 257 } 258 259 // GT returns the result of operation v > w. 260 func GT(f *Frame, v, w *Object) (*Object, *BaseException) { 261 r, raised := compareRich(f, compareOpGT, v, w) 262 if raised != nil { 263 return nil, raised 264 } 265 if r != NotImplemented { 266 return r, nil 267 } 268 return GetBool(compareDefault(f, v, w) > 0).ToObject(), nil 269 } 270 271 // Hash returns the hash of o according to its __hash__ operator. 272 func Hash(f *Frame, o *Object) (*Int, *BaseException) { 273 hash := o.typ.slots.Hash 274 if hash == nil { 275 _, raised := hashNotImplemented(f, o) 276 return nil, raised 277 } 278 h, raised := hash.Fn(f, o) 279 if raised != nil { 280 return nil, raised 281 } 282 if !h.isInstance(IntType) { 283 return nil, f.RaiseType(TypeErrorType, "an integer is required") 284 } 285 return toIntUnsafe(h), nil 286 } 287 288 // Hex returns the result of o.__hex__ if defined. 289 func Hex(f *Frame, o *Object) (*Object, *BaseException) { 290 hex := o.typ.slots.Hex 291 if hex == nil { 292 raised := f.RaiseType(TypeErrorType, "hex() argument can't be converted to hex") 293 return nil, raised 294 } 295 h, raised := hex.Fn(f, o) 296 if raised != nil { 297 return nil, raised 298 } 299 if !h.isInstance(StrType) { 300 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("__hex__ returned non-string (type %s)", h.typ.name)) 301 } 302 return h, nil 303 } 304 305 // IAdd returns the result of v.__iadd__ if defined, otherwise falls back to 306 // Add. 307 func IAdd(f *Frame, v, w *Object) (*Object, *BaseException) { 308 return inplaceOp(f, v, w, v.typ.slots.IAdd, Add) 309 } 310 311 // IAnd returns the result of v.__iand__ if defined, otherwise falls back to 312 // And. 313 func IAnd(f *Frame, v, w *Object) (*Object, *BaseException) { 314 return inplaceOp(f, v, w, v.typ.slots.IAnd, And) 315 } 316 317 // IDiv returns the result of v.__idiv__ if defined, otherwise falls back to 318 // div. 319 func IDiv(f *Frame, v, w *Object) (*Object, *BaseException) { 320 return inplaceOp(f, v, w, v.typ.slots.IDiv, Div) 321 } 322 323 // IFloorDiv returns the result of v.__ifloordiv__ if defined, otherwise falls back to 324 // floordiv. 325 func IFloorDiv(f *Frame, v, w *Object) (*Object, *BaseException) { 326 return inplaceOp(f, v, w, v.typ.slots.IFloorDiv, FloorDiv) 327 } 328 329 // ILShift returns the result of v.__ilshift__ if defined, otherwise falls back 330 // to lshift. 331 func ILShift(f *Frame, v, w *Object) (*Object, *BaseException) { 332 return inplaceOp(f, v, w, v.typ.slots.ILShift, LShift) 333 } 334 335 // IMod returns the result of v.__imod__ if defined, otherwise falls back to 336 // mod. 337 func IMod(f *Frame, v, w *Object) (*Object, *BaseException) { 338 return inplaceOp(f, v, w, v.typ.slots.IMod, Mod) 339 } 340 341 // IMul returns the result of v.__imul__ if defined, otherwise falls back to 342 // mul. 343 func IMul(f *Frame, v, w *Object) (*Object, *BaseException) { 344 return inplaceOp(f, v, w, v.typ.slots.IMul, Mul) 345 } 346 347 // Invert returns the result of o.__invert__ and is equivalent to the Python 348 // expression "~o". 349 func Invert(f *Frame, o *Object) (*Object, *BaseException) { 350 invert := o.typ.slots.Invert 351 if invert == nil { 352 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("bad operand type for unary ~: '%s'", o.typ.Name())) 353 } 354 return invert.Fn(f, o) 355 } 356 357 // IOr returns the result of v.__ior__ if defined, otherwise falls back to Or. 358 func IOr(f *Frame, v, w *Object) (*Object, *BaseException) { 359 return inplaceOp(f, v, w, v.typ.slots.IOr, Or) 360 } 361 362 // IPow returns the result of v.__pow__ if defined, otherwise falls back to IPow. 363 func IPow(f *Frame, v, w *Object) (*Object, *BaseException) { 364 return inplaceOp(f, v, w, v.typ.slots.IPow, Pow) 365 } 366 367 // IRShift returns the result of v.__irshift__ if defined, otherwise falls back 368 // to rshift. 369 func IRShift(f *Frame, v, w *Object) (*Object, *BaseException) { 370 return inplaceOp(f, v, w, v.typ.slots.IRShift, RShift) 371 } 372 373 // IsInstance returns true if the type o is an instance of classinfo, or an 374 // instance of an element in classinfo (if classinfo is a tuple). It returns 375 // false otherwise. The argument classinfo must be a type or a tuple whose 376 // elements are types like the isinstance() Python builtin. 377 func IsInstance(f *Frame, o *Object, classinfo *Object) (bool, *BaseException) { 378 return IsSubclass(f, o.typ.ToObject(), classinfo) 379 } 380 381 // IsSubclass returns true if the type o is a subtype of classinfo or a subtype 382 // of an element in classinfo (if classinfo is a tuple). It returns false 383 // otherwise. The argument o must be a type and classinfo must be a type or a 384 // tuple whose elements are types like the issubclass() Python builtin. 385 func IsSubclass(f *Frame, o *Object, classinfo *Object) (bool, *BaseException) { 386 if !o.isInstance(TypeType) { 387 return false, f.RaiseType(TypeErrorType, "issubclass() arg 1 must be a class") 388 } 389 t := toTypeUnsafe(o) 390 errorMsg := "classinfo must be a type or tuple of types" 391 if classinfo.isInstance(TypeType) { 392 return t.isSubclass(toTypeUnsafe(classinfo)), nil 393 } 394 if !classinfo.isInstance(TupleType) { 395 return false, f.RaiseType(TypeErrorType, errorMsg) 396 } 397 for _, elem := range toTupleUnsafe(classinfo).elems { 398 if !elem.isInstance(TypeType) { 399 return false, f.RaiseType(TypeErrorType, errorMsg) 400 } 401 if t.isSubclass(toTypeUnsafe(elem)) { 402 return true, nil 403 } 404 } 405 return false, nil 406 } 407 408 // IsTrue returns the truthiness of o according to the __nonzero__ operator. 409 func IsTrue(f *Frame, o *Object) (bool, *BaseException) { 410 switch o { 411 case True.ToObject(): 412 return true, nil 413 case False.ToObject(), None: 414 return false, nil 415 } 416 nonzero := o.typ.slots.NonZero 417 if nonzero != nil { 418 r, raised := nonzero.Fn(f, o) 419 if raised != nil { 420 return false, raised 421 } 422 if r.isInstance(IntType) { 423 return toIntUnsafe(r).IsTrue(), nil 424 } 425 msg := fmt.Sprintf("__nonzero__ should return bool, returned %s", r.typ.Name()) 426 return false, f.RaiseType(TypeErrorType, msg) 427 } 428 if o.typ.slots.Len != nil { 429 l, raised := Len(f, o) 430 if raised != nil { 431 return false, raised 432 } 433 return l.IsTrue(), nil 434 } 435 return true, nil 436 } 437 438 // ISub returns the result of v.__isub__ if defined, otherwise falls back to 439 // sub. 440 func ISub(f *Frame, v, w *Object) (*Object, *BaseException) { 441 if isub := v.typ.slots.ISub; isub != nil { 442 return isub.Fn(f, v, w) 443 } 444 return Sub(f, v, w) 445 } 446 447 // Iter implements the Python iter() builtin. It returns an iterator for o if 448 // o is iterable. Otherwise it raises TypeError. 449 // Note that the iter(f, sentinel) form is not yet supported. 450 func Iter(f *Frame, o *Object) (*Object, *BaseException) { 451 // TODO: Support iter(f, sentinel) usage. 452 iter := o.typ.slots.Iter 453 if iter != nil { 454 return iter.Fn(f, o) 455 } 456 if o.typ.slots.GetItem != nil { 457 return newSeqIterator(o), nil 458 } 459 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("'%s' object is not iterable", o.typ.Name())) 460 } 461 462 // IXor returns the result of v.__ixor__ if defined, otherwise falls back to 463 // Xor. 464 func IXor(f *Frame, v, w *Object) (*Object, *BaseException) { 465 return inplaceOp(f, v, w, v.typ.slots.IXor, Xor) 466 } 467 468 // LE returns the result of operation v <= w. 469 func LE(f *Frame, v, w *Object) (*Object, *BaseException) { 470 r, raised := compareRich(f, compareOpLE, v, w) 471 if raised != nil { 472 return nil, raised 473 } 474 if r != NotImplemented { 475 return r, nil 476 } 477 return GetBool(compareDefault(f, v, w) <= 0).ToObject(), nil 478 } 479 480 // Len returns the length of the given sequence object. 481 func Len(f *Frame, o *Object) (*Int, *BaseException) { 482 lenSlot := o.typ.slots.Len 483 if lenSlot == nil { 484 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("object of type '%s' has no len()", o.typ.Name())) 485 } 486 r, raised := lenSlot.Fn(f, o) 487 if raised != nil { 488 return nil, raised 489 } 490 if !r.isInstance(IntType) { 491 return nil, f.RaiseType(TypeErrorType, "an integer is required") 492 } 493 return toIntUnsafe(r), nil 494 } 495 496 // LShift returns the result of v << w according to the __lshift/rlshift__ 497 // operator. 498 func LShift(f *Frame, v, w *Object) (*Object, *BaseException) { 499 return binaryOp(f, v, w, v.typ.slots.LShift, v.typ.slots.RLShift, w.typ.slots.RLShift, "<<") 500 } 501 502 // LT returns the result of operation v < w. 503 func LT(f *Frame, v, w *Object) (*Object, *BaseException) { 504 r, raised := compareRich(f, compareOpLT, v, w) 505 if raised != nil { 506 return nil, raised 507 } 508 if r != NotImplemented { 509 return r, nil 510 } 511 return GetBool(compareDefault(f, v, w) < 0).ToObject(), nil 512 } 513 514 // Mod returns the remainder from the division of v by w according to the 515 // __mod/rmod__ operator. 516 func Mod(f *Frame, v, w *Object) (*Object, *BaseException) { 517 return binaryOp(f, v, w, v.typ.slots.Mod, v.typ.slots.RMod, w.typ.slots.RMod, "%") 518 } 519 520 // Mul returns the result of multiplying v and w together according to the 521 // __mul/rmul__ operator. 522 func Mul(f *Frame, v, w *Object) (*Object, *BaseException) { 523 return binaryOp(f, v, w, v.typ.slots.Mul, v.typ.slots.RMul, w.typ.slots.RMul, "*") 524 } 525 526 // Pow returns the result of x**y, the base-x exponential of y according to the 527 // __pow/rpow__ operator. 528 func Pow(f *Frame, v, w *Object) (*Object, *BaseException) { 529 return binaryOp(f, v, w, v.typ.slots.Pow, v.typ.slots.RPow, w.typ.slots.RPow, "**") 530 } 531 532 // Or returns the result of the bitwise or operator v | w according to 533 // __or/ror__. 534 func Or(f *Frame, v, w *Object) (*Object, *BaseException) { 535 return binaryOp(f, v, w, v.typ.slots.Or, v.typ.slots.ROr, w.typ.slots.ROr, "|") 536 } 537 538 // Index returns the o converted to a Python int or long according to o's 539 // __index__ slot. 540 func Index(f *Frame, o *Object) (*Object, *BaseException) { 541 if o.isInstance(IntType) || o.isInstance(LongType) { 542 return o, nil 543 } 544 index := o.typ.slots.Index 545 if index == nil { 546 return nil, nil 547 } 548 i, raised := index.Fn(f, o) 549 if raised != nil { 550 return nil, raised 551 } 552 if !i.isInstance(IntType) && !i.isInstance(LongType) { 553 format := "__index__ returned non-(int,long) (type %s)" 554 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, i.typ.Name())) 555 } 556 return i, nil 557 } 558 559 // IndexInt returns the value of o converted to a Go int according to o's 560 // __index__ slot. 561 // It raises a TypeError if o doesn't have an __index__ method. 562 func IndexInt(f *Frame, o *Object) (i int, raised *BaseException) { 563 if index := o.typ.slots.Index; index != nil { 564 // Unwrap __index__ slot and fall through. 565 o, raised = index.Fn(f, o) 566 if raised != nil { 567 return 0, raised 568 } 569 } 570 if o.isInstance(IntType) { 571 return toIntUnsafe(o).Value(), nil 572 } 573 if o.isInstance(LongType) { 574 l := toLongUnsafe(o).Value() 575 // Anything bigger than maxIntBig will treat as maxIntBig. 576 if !numInIntRange(l) { 577 l = maxIntBig 578 } 579 return int(l.Int64()), nil 580 } 581 return 0, f.RaiseType(TypeErrorType, errBadSliceIndex) 582 } 583 584 // Invoke calls the given callable with the positional arguments given by args 585 // and *varargs, and the keyword arguments by keywords and **kwargs. It first 586 // packs the arguments into slices for the positional and keyword arguments, 587 // then it passes those to *Object.Call. 588 func Invoke(f *Frame, callable *Object, args Args, varargs *Object, keywords KWArgs, kwargs *Object) (*Object, *BaseException) { 589 if varargs != nil { 590 raised := seqApply(f, varargs, func(elems []*Object, _ bool) *BaseException { 591 numArgs := len(args) 592 packed := make([]*Object, numArgs+len(elems)) 593 copy(packed, args) 594 copy(packed[numArgs:], elems) 595 args = packed 596 return nil 597 }) 598 if raised != nil { 599 return nil, raised 600 } 601 } 602 if kwargs != nil { 603 if !kwargs.isInstance(DictType) { 604 format := "argument after ** must be a dict, not %s" 605 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, kwargs.typ.Name())) 606 } 607 kwargsDict := toDictUnsafe(kwargs) 608 numKeywords := len(keywords) 609 numKwargs, raised := Len(f, kwargs) 610 if raised != nil { 611 return nil, raised 612 } 613 // Don't bother synchronizing access to len(kwargs) since it's just a 614 // hint and it doesn't matter if it's a little off. 615 packed := make(KWArgs, numKeywords, numKeywords+numKwargs.Value()) 616 copy(packed, keywords) 617 raised = seqForEach(f, kwargs, func(o *Object) *BaseException { 618 if !o.isInstance(StrType) { 619 return f.RaiseType(TypeErrorType, "keywords must be strings") 620 } 621 s := toStrUnsafe(o).Value() 622 // Search for dupes linearly assuming small number of keywords. 623 for _, kw := range keywords { 624 if kw.Name == s { 625 format := "got multiple values for keyword argument '%s'" 626 return f.RaiseType(TypeErrorType, fmt.Sprintf(format, s)) 627 } 628 } 629 item, raised := kwargsDict.GetItem(f, o) 630 if raised != nil { 631 return raised 632 } 633 if item == nil { 634 return raiseKeyError(f, o) 635 } 636 packed = append(packed, KWArg{Name: s, Value: item}) 637 return nil 638 }) 639 if raised != nil { 640 return nil, raised 641 } 642 keywords = packed 643 } 644 return callable.Call(f, args, keywords) 645 } 646 647 // NE returns the non-equality of v and w according to the __ne__ operator. 648 func NE(f *Frame, v, w *Object) (*Object, *BaseException) { 649 r, raised := compareRich(f, compareOpNE, v, w) 650 if raised != nil { 651 return nil, raised 652 } 653 if r != NotImplemented { 654 return r, nil 655 } 656 return GetBool(compareDefault(f, v, w) != 0).ToObject(), nil 657 } 658 659 // Next implements the Python next() builtin. It calls next on the provided 660 // iterator. It raises TypeError if iter is not an iterator object. 661 // Note that the next(it, default) form is not yet supported. 662 func Next(f *Frame, iter *Object) (*Object, *BaseException) { 663 // TODO: Support next(it, default) usage. 664 next := iter.typ.slots.Next 665 if next == nil { 666 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("%s object is not an iterator", iter.typ.Name())) 667 } 668 return next.Fn(f, iter) 669 } 670 671 // Oct returns the result of o.__oct__ if defined. 672 func Oct(f *Frame, o *Object) (*Object, *BaseException) { 673 oct := o.typ.slots.Oct 674 if oct == nil { 675 raised := f.RaiseType(TypeErrorType, "oct() argument can't be converted to oct") 676 return nil, raised 677 } 678 o, raised := oct.Fn(f, o) 679 if raised != nil { 680 return nil, raised 681 } 682 if !o.isInstance(StrType) { 683 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("__oct__ returned non-string (type %s)", o.typ.name)) 684 } 685 return o, nil 686 } 687 688 // Pos returns the result of o.__pos__ and is equivalent to the Python 689 // expression "+o". 690 func Pos(f *Frame, o *Object) (*Object, *BaseException) { 691 pos := o.typ.slots.Pos 692 if pos == nil { 693 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("bad operand type for unary +: '%s'", o.typ.Name())) 694 } 695 return pos.Fn(f, o) 696 } 697 698 // Print implements the Python print statement. It calls str() on the given args 699 // and outputs the results to stdout separated by spaces. Similar to the Python 700 // print statement. 701 func Print(f *Frame, args Args, nl bool) *BaseException { 702 // TODO: Support outputting to files other than stdout and softspace. 703 var end string 704 if nl { 705 end = "\n" 706 } else if len(args) > 0 { 707 end = " " 708 } 709 return pyPrint(f, args, " ", end, Stdout) 710 } 711 712 // Repr returns a string containing a printable representation of o. This is 713 // equivalent to the Python expression "repr(o)". 714 func Repr(f *Frame, o *Object) (*Str, *BaseException) { 715 repr := o.typ.slots.Repr 716 if repr == nil { 717 s, raised := o.typ.FullName(f) 718 if raised != nil { 719 return nil, raised 720 } 721 return NewStr(fmt.Sprintf("<%s object at %p>", s, o)), nil 722 } 723 r, raised := repr.Fn(f, o) 724 if raised != nil { 725 return nil, raised 726 } 727 if !r.isInstance(StrType) { 728 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("__repr__ returned non-string (type %s)", r.typ.Name())) 729 } 730 return toStrUnsafe(r), nil 731 } 732 733 // ResolveClass resolves name in the class dict given by class, falling back to 734 // the provided local if it is non-nil, otherwise falling back to globals. 735 // This is used by the code generator to resolve names in the context of a class 736 // definition. If the class definition occurs in a closure in which a local of 737 // the given name is present then local will be non-nil, otherwise it will be 738 // nil. 739 func ResolveClass(f *Frame, class *Dict, local *Object, name *Str) (*Object, *BaseException) { 740 if value, raised := class.GetItem(f, name.ToObject()); raised != nil || value != nil { 741 return value, raised 742 } 743 if local != nil { 744 if raised := CheckLocal(f, local, name.Value()); raised != nil { 745 return nil, raised 746 } 747 return local, nil 748 } 749 return ResolveGlobal(f, name) 750 } 751 752 // ResolveGlobal looks up name in the frame's dict of global variables or in 753 // the Builtins dict if absent. It raises NameError when absent from both. 754 func ResolveGlobal(f *Frame, name *Str) (*Object, *BaseException) { 755 if value, raised := f.Globals().GetItem(f, name.ToObject()); raised != nil || value != nil { 756 return value, raised 757 } 758 value, raised := Builtins.GetItem(f, name.ToObject()) 759 if raised != nil { 760 return nil, raised 761 } 762 if value == nil { 763 return nil, f.RaiseType(NameErrorType, fmt.Sprintf("name '%s' is not defined", name.Value())) 764 } 765 return value, nil 766 } 767 768 // RShift returns the result of v >> w according to the __rshift/rrshift__ 769 // operator. 770 func RShift(f *Frame, v, w *Object) (*Object, *BaseException) { 771 return binaryOp(f, v, w, v.typ.slots.RShift, v.typ.slots.RRShift, w.typ.slots.RRShift, ">>") 772 } 773 774 // CheckLocal validates that the local variable with the given name and value 775 // has been bound and raises UnboundLocalError if not. 776 func CheckLocal(f *Frame, value *Object, name string) *BaseException { 777 if value == UnboundLocal { 778 format := "local variable '%s' referenced before assignment" 779 return f.RaiseType(UnboundLocalErrorType, fmt.Sprintf(format, name)) 780 } 781 return nil 782 } 783 784 // SetAttr sets the attribute of o given by name to value. Equivalent to the 785 // Python expression setattr(o, name, value). 786 func SetAttr(f *Frame, o *Object, name *Str, value *Object) *BaseException { 787 setAttr := o.typ.slots.SetAttr 788 if setAttr == nil { 789 return f.RaiseType(SystemErrorType, fmt.Sprintf("'%s' object has no __setattr__ method", o.typ.Name())) 790 } 791 return setAttr.Fn(f, o, name, value) 792 } 793 794 // SetItem performs the operation o[key] = value. 795 func SetItem(f *Frame, o, key, value *Object) *BaseException { 796 setItem := o.typ.slots.SetItem 797 if setItem == nil { 798 return f.RaiseType(TypeErrorType, fmt.Sprintf("'%s' object has no attribute '__setitem__'", o.typ.Name())) 799 } 800 return setItem.Fn(f, o, key, value) 801 } 802 803 // StartThread runs callable in a new goroutine. 804 func StartThread(callable *Object) { 805 go func() { 806 atomic.AddInt64(&ThreadCount, 1) 807 defer atomic.AddInt64(&ThreadCount, -1) 808 f := NewRootFrame() 809 _, raised := callable.Call(f, nil, nil) 810 if raised != nil { 811 Stderr.writeString(FormatExc(f)) 812 } 813 }() 814 } 815 816 // Sub returns the result of subtracting v from w according to the 817 // __sub/rsub__ operator. 818 func Sub(f *Frame, v, w *Object) (*Object, *BaseException) { 819 return binaryOp(f, v, w, v.typ.slots.Sub, v.typ.slots.RSub, w.typ.slots.RSub, "-") 820 } 821 822 // TieTarget is a data structure used to facilitate iterator unpacking in 823 // assignment statements. A TieTarget should have one of Target or Children 824 // populated but not both. 825 // 826 // As an example, the targets in the Python assignment 'foo, bar = ...' 827 // could be represented as: 828 // 829 // TieTarget{ 830 // Children: []TieTarget{{Target: &foo}, {Target: &bar}}, 831 // } 832 type TieTarget struct { 833 // Target is a destination pointer where an unpacked value will be 834 // stored. 835 Target **Object 836 // Children contains a sequence of TieTargets that should be unpacked 837 // into. 838 Children []TieTarget 839 } 840 841 // Tie takes a (possibly nested) TieTarget and recursively unpacks the 842 // elements of o by iteration, assigning the results to the Target fields of t. 843 // If the structure of o is not suitable to be unpacked into t, then an 844 // exception is raised. 845 func Tie(f *Frame, t TieTarget, o *Object) *BaseException { 846 if t.Target != nil { 847 *t.Target = o 848 return nil 849 } 850 iter, raised := Iter(f, o) 851 if raised != nil { 852 return raised 853 } 854 for i, child := range t.Children { 855 if value, raised := Next(f, iter); raised == nil { 856 if raised := Tie(f, child, value); raised != nil { 857 return raised 858 } 859 } else if raised.isInstance(StopIterationType) { 860 return f.RaiseType(ValueErrorType, fmt.Sprintf("need more than %d values to unpack", i)) 861 } else { 862 return raised 863 } 864 } 865 _, raised = Next(f, iter) 866 if raised == nil { 867 return f.RaiseType(ValueErrorType, "too many values to unpack") 868 } 869 if !raised.isInstance(StopIterationType) { 870 return raised 871 } 872 f.RestoreExc(nil, nil) 873 return nil 874 } 875 876 // ToInt converts o to an integer type according to the __int__ slot. If the 877 // result is not an int or long, then an exception is raised. 878 func ToInt(f *Frame, o *Object) (*Object, *BaseException) { 879 if o.typ == IntType || o.typ == LongType { 880 return o, nil 881 } 882 intSlot := o.typ.slots.Int 883 if intSlot == nil { 884 return nil, f.RaiseType(TypeErrorType, "an integer is required") 885 } 886 i, raised := intSlot.Fn(f, o) 887 if raised != nil { 888 return nil, raised 889 } 890 if i.isInstance(IntType) || i.isInstance(LongType) { 891 return i, nil 892 } 893 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("__int__ returned non-int (type %s)", i.typ.Name())) 894 } 895 896 // ToIntValue converts o to an integer according to the __int__ slot. If the 897 // result is not an int or long, or if the long value is too large to fit into 898 // an int, then an exception is raised. 899 func ToIntValue(f *Frame, o *Object) (int, *BaseException) { 900 i, raised := ToInt(f, o) 901 if raised != nil { 902 return 0, raised 903 } 904 if i.isInstance(IntType) { 905 return toIntUnsafe(i).Value(), nil 906 } 907 return toLongUnsafe(i).IntValue(f) 908 } 909 910 // ToNative converts o to a native Go object according to the __native__ 911 // operator. 912 func ToNative(f *Frame, o *Object) (reflect.Value, *BaseException) { 913 if native := o.typ.slots.Native; native != nil { 914 return native.Fn(f, o) 915 } 916 return reflect.ValueOf(o), nil 917 } 918 919 // ToStr is a convenience function for calling "str(o)". 920 func ToStr(f *Frame, o *Object) (*Str, *BaseException) { 921 result, raised := StrType.Call(f, []*Object{o}, nil) 922 if raised != nil { 923 return nil, raised 924 } 925 if !result.isInstance(StrType) { 926 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("__str__ returned non-string (type %s)", result.typ.Name())) 927 } 928 return toStrUnsafe(result), nil 929 } 930 931 // Neg returns the result of o.__neg__ and is equivalent to the Python 932 // expression "-o". 933 func Neg(f *Frame, o *Object) (*Object, *BaseException) { 934 neg := o.typ.slots.Neg 935 if neg == nil { 936 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("bad operand type for unary -: '%s'", o.typ.Name())) 937 } 938 return neg.Fn(f, o) 939 } 940 941 // Xor returns the result of the bitwise xor operator v ^ w according to 942 // __xor/rxor__. 943 func Xor(f *Frame, v, w *Object) (*Object, *BaseException) { 944 return binaryOp(f, v, w, v.typ.slots.Xor, v.typ.slots.RXor, w.typ.slots.RXor, "^") 945 } 946 947 const ( 948 errResultTooLarge = "result too large" 949 errUnsupportedOperand = "unsupported operand type(s) for %s: '%s' and '%s'" 950 ) 951 952 // binaryOp picks an appropriate operator method (op or rop) from v or w and 953 // returns its result. It raises TypeError if no appropriate method is found. 954 // It is similar to CPython's binary_op1 function from abstract.c. 955 func binaryOp(f *Frame, v, w *Object, op, vrop, wrop *binaryOpSlot, opName string) (*Object, *BaseException) { 956 if v.typ != w.typ && w.typ.isSubclass(v.typ) { 957 // w is an instance of a subclass of type(v), so prefer w's more 958 // specific rop, but only if it is overridden (wrop != vrop). 959 if wrop != nil && wrop != vrop { 960 r, raised := wrop.Fn(f, w, v) 961 if raised != nil { 962 return nil, raised 963 } 964 if r != NotImplemented { 965 return r, nil 966 } 967 } 968 } 969 if op != nil { 970 r, raised := op.Fn(f, v, w) 971 if raised != nil { 972 return nil, raised 973 } 974 if r != NotImplemented { 975 return r, nil 976 } 977 } 978 if wrop != nil { 979 r, raised := wrop.Fn(f, w, v) 980 if raised != nil { 981 return nil, raised 982 } 983 if r != NotImplemented { 984 return r, nil 985 } 986 } 987 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(errUnsupportedOperand, opName, v.typ.Name(), w.typ.Name())) 988 } 989 990 func inplaceOp(f *Frame, v, w *Object, slot *binaryOpSlot, fallback binaryOpFunc) (*Object, *BaseException) { 991 if slot != nil { 992 return slot.Fn(f, v, w) 993 } 994 return fallback(f, v, w) 995 } 996 997 type compareOp int 998 999 const ( 1000 compareOpLT compareOp = iota 1001 compareOpLE 1002 compareOpEq 1003 compareOpNE 1004 compareOpGE 1005 compareOpGT 1006 ) 1007 1008 var compareOpSwapped = []compareOp{ 1009 compareOpGT, 1010 compareOpGE, 1011 compareOpEq, 1012 compareOpNE, 1013 compareOpLE, 1014 compareOpLT, 1015 } 1016 1017 func (op compareOp) swapped() compareOp { 1018 return compareOpSwapped[op] 1019 } 1020 1021 func (op compareOp) slot(t *Type) *binaryOpSlot { 1022 switch op { 1023 case compareOpLT: 1024 return t.slots.LT 1025 case compareOpLE: 1026 return t.slots.LE 1027 case compareOpEq: 1028 return t.slots.Eq 1029 case compareOpNE: 1030 return t.slots.NE 1031 case compareOpGE: 1032 return t.slots.GE 1033 case compareOpGT: 1034 return t.slots.GT 1035 } 1036 panic(fmt.Sprintf("invalid compareOp value: %d", op)) 1037 } 1038 1039 func compareRich(f *Frame, op compareOp, v, w *Object) (*Object, *BaseException) { 1040 r, raised := tryRichCompare(f, op, v, w) 1041 if raised != nil { 1042 return nil, raised 1043 } 1044 if r != NotImplemented { 1045 return r, nil 1046 } 1047 return try3wayToRichCompare(f, op, v, w) 1048 } 1049 1050 // convert3wayToObject converts the integer results from a 3-way 1051 // comparison to a suitable boolean value for the given rich 1052 // comparison op. 1053 func convert3wayToObject(op compareOp, c int) *Object { 1054 b := false 1055 switch op { 1056 case compareOpLT: 1057 b = c < 0 1058 case compareOpLE: 1059 b = c <= 0 1060 case compareOpEq: 1061 b = c == 0 1062 case compareOpNE: 1063 b = c != 0 1064 case compareOpGE: 1065 b = c >= 0 1066 case compareOpGT: 1067 b = c > 0 1068 } 1069 return GetBool(b).ToObject() 1070 } 1071 1072 // try3wayToRichCompare tries to perform a rich comparison operation on the given objects 1073 // with the given comparison op using 3-way comparison. It closely resembles the behavior 1074 // of CPython's try_3way_to_rich_compare in object.c. 1075 func try3wayToRichCompare(f *Frame, op compareOp, v, w *Object) (*Object, *BaseException) { 1076 r, raised := try3wayCompare(f, v, w) 1077 if raised != nil { 1078 return nil, raised 1079 } 1080 c := 0 1081 if r == NotImplemented { 1082 c = compareDefault(f, v, w) 1083 } else { 1084 c = toIntUnsafe(r).Value() 1085 } 1086 return convert3wayToObject(op, c), nil 1087 } 1088 1089 // tryRichCompare tries to perform a rich comparison operation on the given 1090 // objects with the given comparison op using the rich comparison methods. 1091 // It closely resembles the behavior of CPython's try_rich_compare in object.c. 1092 func tryRichCompare(f *Frame, op compareOp, v, w *Object) (*Object, *BaseException) { 1093 if v.typ != w.typ && w.typ.isSubclass(v.typ) { 1094 // type(w) is a subclass of type(v) so try to use w's 1095 // comparison operators since they're more specific. 1096 slot := op.swapped().slot(w.typ) 1097 if slot != nil { 1098 r, raised := slot.Fn(f, w, v) 1099 if raised != nil { 1100 return nil, raised 1101 } 1102 if r != NotImplemented { 1103 return r, nil 1104 } 1105 } 1106 } 1107 slot := op.slot(v.typ) 1108 if slot != nil { 1109 r, raised := slot.Fn(f, v, w) 1110 if raised != nil { 1111 return nil, raised 1112 } 1113 if r != NotImplemented { 1114 return r, nil 1115 } 1116 } 1117 slot = op.swapped().slot(w.typ) 1118 if slot != nil { 1119 return slot.Fn(f, w, v) 1120 } 1121 return NotImplemented, nil 1122 } 1123 1124 // compareDefault returns is the fallback logic for object comparison. It 1125 // closely resembles the behavior of CPython's default_3way_compare in object.c. 1126 func compareDefault(f *Frame, v, w *Object) int { 1127 if v.typ == w.typ { 1128 pv, pw := uintptr(v.toPointer()), uintptr(w.toPointer()) 1129 if pv < pw { 1130 return -1 1131 } 1132 if pv == pw { 1133 return 0 1134 } 1135 return 1 1136 } 1137 if v == None { 1138 return -1 1139 } 1140 if w == None { 1141 return 1 1142 } 1143 // TODO: In default_3way_compare, the number type name is the empty 1144 // string so it evaluates less than non-number types. Once Grumpy 1145 // supports the concept of number types, add this behavior. 1146 if v.typ.Name() < w.typ.Name() { 1147 return -1 1148 } 1149 if v.typ.Name() != w.typ.Name() { 1150 return 1 1151 } 1152 if uintptr(v.typ.toPointer()) < uintptr(w.typ.toPointer()) { 1153 return -1 1154 } 1155 return 1 1156 } 1157 1158 // tryRichCompareBool tries a rich comparison with the given comparison op and 1159 // returns a bool indicating if the relation is true. It closely resembles the 1160 // behavior of CPython's try_rich_compare_bool in object.c. 1161 func tryRichCompareBool(f *Frame, op compareOp, v, w *Object) (bool, *BaseException) { 1162 r, raised := tryRichCompare(f, op, v, w) 1163 if raised != nil { 1164 return false, raised 1165 } 1166 if r == NotImplemented { 1167 return false, nil 1168 } 1169 br, raised := IsTrue(f, r) 1170 if raised != nil { 1171 return false, raised 1172 } 1173 return br, raised 1174 } 1175 1176 // halfCompare tries a comparison with the __cmp__ slot, ensures the result 1177 // is an integer, and returns it. It closely resembles the behavior of CPython's 1178 // half_compare in typeobject.c. 1179 func halfCompare(f *Frame, v, w *Object) (*Object, *BaseException) { 1180 cmp := v.typ.slots.Cmp 1181 r, raised := cmp.Fn(f, v, w) 1182 if raised != nil { 1183 return nil, raised 1184 } 1185 if !r.isInstance(IntType) { 1186 return nil, f.RaiseType(TypeErrorType, "an integer is required") 1187 } 1188 return r, nil 1189 } 1190 1191 // try3wayCompare tries a comparison with the __cmp__ slot with the given 1192 // arguments. It first tries to use the __cmp__ slot on v and if that fails 1193 // on w. It closely resembles the behavior of CPython's try_3way_compare in 1194 // object.c. 1195 func try3wayCompare(f *Frame, v, w *Object) (*Object, *BaseException) { 1196 cmp := v.typ.slots.Cmp 1197 if cmp != nil { 1198 return halfCompare(f, v, w) 1199 } 1200 cmp = w.typ.slots.Cmp 1201 if cmp != nil { 1202 r, raised := halfCompare(f, w, v) 1203 if raised != nil { 1204 return nil, raised 1205 } 1206 return intNeg(f, r) 1207 } 1208 return NotImplemented, nil 1209 } 1210 1211 // tryRichTo3wayCompare tries to compute a 3-way comparison in terms of 1212 // the rich comparison operators (if they exist). It closely resembles 1213 // the behavior of CPython's try_rich_to_3way_compare in object.c. 1214 func tryRichTo3wayCompare(f *Frame, v, w *Object) (*Object, *BaseException) { 1215 var tries = []struct { 1216 op compareOp 1217 outcome int 1218 }{ 1219 {compareOpEq, 0}, 1220 {compareOpLT, -1}, 1221 {compareOpGT, 1}, 1222 } 1223 for _, try := range tries { 1224 r, raised := tryRichCompareBool(f, try.op, v, w) 1225 if raised != nil { 1226 return nil, raised 1227 } 1228 if r { 1229 return NewInt(try.outcome).ToObject(), nil 1230 } 1231 } 1232 return NotImplemented, nil 1233 } 1234 1235 func checkFunctionArgs(f *Frame, function string, args Args, types ...*Type) *BaseException { 1236 if len(args) != len(types) { 1237 msg := fmt.Sprintf("'%s' requires %d arguments", function, len(types)) 1238 return f.RaiseType(TypeErrorType, msg) 1239 } 1240 for i, t := range types { 1241 if !args[i].isInstance(t) { 1242 format := "'%s' requires a '%s' object but received a %q" 1243 return f.RaiseType(TypeErrorType, fmt.Sprintf(format, function, t.Name(), args[i].typ.Name())) 1244 } 1245 } 1246 return nil 1247 } 1248 1249 func checkFunctionVarArgs(f *Frame, function string, args Args, types ...*Type) *BaseException { 1250 if len(args) <= len(types) { 1251 return checkFunctionArgs(f, function, args, types...) 1252 } 1253 return checkFunctionArgs(f, function, args[:len(types)], types...) 1254 } 1255 1256 func checkMethodArgs(f *Frame, method string, args Args, types ...*Type) *BaseException { 1257 if len(args) != len(types) { 1258 msg := fmt.Sprintf("'%s' of '%s' requires %d arguments", method, types[0].Name(), len(types)) 1259 return f.RaiseType(TypeErrorType, msg) 1260 } 1261 for i, t := range types { 1262 if !args[i].isInstance(t) { 1263 format := "'%s' requires a '%s' object but received a '%s'" 1264 return f.RaiseType(TypeErrorType, fmt.Sprintf(format, method, t.Name(), args[i].typ.Name())) 1265 } 1266 } 1267 return nil 1268 } 1269 1270 func checkMethodVarArgs(f *Frame, method string, args Args, types ...*Type) *BaseException { 1271 if len(args) <= len(types) { 1272 return checkMethodArgs(f, method, args, types...) 1273 } 1274 return checkMethodArgs(f, method, args[:len(types)], types...) 1275 } 1276 1277 func hashNotImplemented(f *Frame, o *Object) (*Object, *BaseException) { 1278 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("unhashable type: '%s'", o.typ.Name())) 1279 } 1280 1281 // pyPrint encapsulates the logic of the Python print function. 1282 func pyPrint(f *Frame, args Args, sep, end string, file *File) *BaseException { 1283 for i, arg := range args { 1284 if i > 0 { 1285 err := file.writeString(sep) 1286 if err != nil { 1287 return f.RaiseType(IOErrorType, err.Error()) 1288 } 1289 } 1290 1291 s, raised := ToStr(f, arg) 1292 if raised != nil { 1293 return raised 1294 } 1295 1296 err := file.writeString(s.Value()) 1297 if err != nil { 1298 return f.RaiseType(IOErrorType, err.Error()) 1299 } 1300 } 1301 1302 err := file.writeString(end) 1303 if err != nil { 1304 return f.RaiseType(IOErrorType, err.Error()) 1305 } 1306 1307 return nil 1308 }