1*4947cdc7SCole Faust// Copyright 2017 The Bazel Authors. All rights reserved. 2*4947cdc7SCole Faust// Use of this source code is governed by a BSD-style 3*4947cdc7SCole Faust// license that can be found in the LICENSE file. 4*4947cdc7SCole Faust 5*4947cdc7SCole Faustpackage starlark 6*4947cdc7SCole Faust 7*4947cdc7SCole Faust// This file defines the library of built-ins. 8*4947cdc7SCole Faust// 9*4947cdc7SCole Faust// Built-ins must explicitly check the "frozen" flag before updating 10*4947cdc7SCole Faust// mutable types such as lists and dicts. 11*4947cdc7SCole Faust 12*4947cdc7SCole Faustimport ( 13*4947cdc7SCole Faust "errors" 14*4947cdc7SCole Faust "fmt" 15*4947cdc7SCole Faust "math" 16*4947cdc7SCole Faust "math/big" 17*4947cdc7SCole Faust "os" 18*4947cdc7SCole Faust "sort" 19*4947cdc7SCole Faust "strconv" 20*4947cdc7SCole Faust "strings" 21*4947cdc7SCole Faust "unicode" 22*4947cdc7SCole Faust "unicode/utf16" 23*4947cdc7SCole Faust "unicode/utf8" 24*4947cdc7SCole Faust 25*4947cdc7SCole Faust "go.starlark.net/syntax" 26*4947cdc7SCole Faust) 27*4947cdc7SCole Faust 28*4947cdc7SCole Faust// Universe defines the set of universal built-ins, such as None, True, and len. 29*4947cdc7SCole Faust// 30*4947cdc7SCole Faust// The Go application may add or remove items from the 31*4947cdc7SCole Faust// universe dictionary before Starlark evaluation begins. 32*4947cdc7SCole Faust// All values in the dictionary must be immutable. 33*4947cdc7SCole Faust// Starlark programs cannot modify the dictionary. 34*4947cdc7SCole Faustvar Universe StringDict 35*4947cdc7SCole Faust 36*4947cdc7SCole Faustfunc init() { 37*4947cdc7SCole Faust // https://github.com/google/starlark-go/blob/master/doc/spec.md#built-in-constants-and-functions 38*4947cdc7SCole Faust Universe = StringDict{ 39*4947cdc7SCole Faust "None": None, 40*4947cdc7SCole Faust "True": True, 41*4947cdc7SCole Faust "False": False, 42*4947cdc7SCole Faust "any": NewBuiltin("any", any), 43*4947cdc7SCole Faust "all": NewBuiltin("all", all), 44*4947cdc7SCole Faust "bool": NewBuiltin("bool", bool_), 45*4947cdc7SCole Faust "bytes": NewBuiltin("bytes", bytes_), 46*4947cdc7SCole Faust "chr": NewBuiltin("chr", chr), 47*4947cdc7SCole Faust "dict": NewBuiltin("dict", dict), 48*4947cdc7SCole Faust "dir": NewBuiltin("dir", dir), 49*4947cdc7SCole Faust "enumerate": NewBuiltin("enumerate", enumerate), 50*4947cdc7SCole Faust "fail": NewBuiltin("fail", fail), 51*4947cdc7SCole Faust "float": NewBuiltin("float", float), 52*4947cdc7SCole Faust "getattr": NewBuiltin("getattr", getattr), 53*4947cdc7SCole Faust "hasattr": NewBuiltin("hasattr", hasattr), 54*4947cdc7SCole Faust "hash": NewBuiltin("hash", hash), 55*4947cdc7SCole Faust "int": NewBuiltin("int", int_), 56*4947cdc7SCole Faust "len": NewBuiltin("len", len_), 57*4947cdc7SCole Faust "list": NewBuiltin("list", list), 58*4947cdc7SCole Faust "max": NewBuiltin("max", minmax), 59*4947cdc7SCole Faust "min": NewBuiltin("min", minmax), 60*4947cdc7SCole Faust "ord": NewBuiltin("ord", ord), 61*4947cdc7SCole Faust "print": NewBuiltin("print", print), 62*4947cdc7SCole Faust "range": NewBuiltin("range", range_), 63*4947cdc7SCole Faust "repr": NewBuiltin("repr", repr), 64*4947cdc7SCole Faust "reversed": NewBuiltin("reversed", reversed), 65*4947cdc7SCole Faust "set": NewBuiltin("set", set), // requires resolve.AllowSet 66*4947cdc7SCole Faust "sorted": NewBuiltin("sorted", sorted), 67*4947cdc7SCole Faust "str": NewBuiltin("str", str), 68*4947cdc7SCole Faust "tuple": NewBuiltin("tuple", tuple), 69*4947cdc7SCole Faust "type": NewBuiltin("type", type_), 70*4947cdc7SCole Faust "zip": NewBuiltin("zip", zip), 71*4947cdc7SCole Faust } 72*4947cdc7SCole Faust} 73*4947cdc7SCole Faust 74*4947cdc7SCole Faust// methods of built-in types 75*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#built-in-methods 76*4947cdc7SCole Faustvar ( 77*4947cdc7SCole Faust bytesMethods = map[string]*Builtin{ 78*4947cdc7SCole Faust "elems": NewBuiltin("elems", bytes_elems), 79*4947cdc7SCole Faust } 80*4947cdc7SCole Faust 81*4947cdc7SCole Faust dictMethods = map[string]*Builtin{ 82*4947cdc7SCole Faust "clear": NewBuiltin("clear", dict_clear), 83*4947cdc7SCole Faust "get": NewBuiltin("get", dict_get), 84*4947cdc7SCole Faust "items": NewBuiltin("items", dict_items), 85*4947cdc7SCole Faust "keys": NewBuiltin("keys", dict_keys), 86*4947cdc7SCole Faust "pop": NewBuiltin("pop", dict_pop), 87*4947cdc7SCole Faust "popitem": NewBuiltin("popitem", dict_popitem), 88*4947cdc7SCole Faust "setdefault": NewBuiltin("setdefault", dict_setdefault), 89*4947cdc7SCole Faust "update": NewBuiltin("update", dict_update), 90*4947cdc7SCole Faust "values": NewBuiltin("values", dict_values), 91*4947cdc7SCole Faust } 92*4947cdc7SCole Faust 93*4947cdc7SCole Faust listMethods = map[string]*Builtin{ 94*4947cdc7SCole Faust "append": NewBuiltin("append", list_append), 95*4947cdc7SCole Faust "clear": NewBuiltin("clear", list_clear), 96*4947cdc7SCole Faust "extend": NewBuiltin("extend", list_extend), 97*4947cdc7SCole Faust "index": NewBuiltin("index", list_index), 98*4947cdc7SCole Faust "insert": NewBuiltin("insert", list_insert), 99*4947cdc7SCole Faust "pop": NewBuiltin("pop", list_pop), 100*4947cdc7SCole Faust "remove": NewBuiltin("remove", list_remove), 101*4947cdc7SCole Faust } 102*4947cdc7SCole Faust 103*4947cdc7SCole Faust stringMethods = map[string]*Builtin{ 104*4947cdc7SCole Faust "capitalize": NewBuiltin("capitalize", string_capitalize), 105*4947cdc7SCole Faust "codepoint_ords": NewBuiltin("codepoint_ords", string_iterable), 106*4947cdc7SCole Faust "codepoints": NewBuiltin("codepoints", string_iterable), // sic 107*4947cdc7SCole Faust "count": NewBuiltin("count", string_count), 108*4947cdc7SCole Faust "elem_ords": NewBuiltin("elem_ords", string_iterable), 109*4947cdc7SCole Faust "elems": NewBuiltin("elems", string_iterable), // sic 110*4947cdc7SCole Faust "endswith": NewBuiltin("endswith", string_startswith), // sic 111*4947cdc7SCole Faust "find": NewBuiltin("find", string_find), 112*4947cdc7SCole Faust "format": NewBuiltin("format", string_format), 113*4947cdc7SCole Faust "index": NewBuiltin("index", string_index), 114*4947cdc7SCole Faust "isalnum": NewBuiltin("isalnum", string_isalnum), 115*4947cdc7SCole Faust "isalpha": NewBuiltin("isalpha", string_isalpha), 116*4947cdc7SCole Faust "isdigit": NewBuiltin("isdigit", string_isdigit), 117*4947cdc7SCole Faust "islower": NewBuiltin("islower", string_islower), 118*4947cdc7SCole Faust "isspace": NewBuiltin("isspace", string_isspace), 119*4947cdc7SCole Faust "istitle": NewBuiltin("istitle", string_istitle), 120*4947cdc7SCole Faust "isupper": NewBuiltin("isupper", string_isupper), 121*4947cdc7SCole Faust "join": NewBuiltin("join", string_join), 122*4947cdc7SCole Faust "lower": NewBuiltin("lower", string_lower), 123*4947cdc7SCole Faust "lstrip": NewBuiltin("lstrip", string_strip), // sic 124*4947cdc7SCole Faust "partition": NewBuiltin("partition", string_partition), 125*4947cdc7SCole Faust "replace": NewBuiltin("replace", string_replace), 126*4947cdc7SCole Faust "rfind": NewBuiltin("rfind", string_rfind), 127*4947cdc7SCole Faust "rindex": NewBuiltin("rindex", string_rindex), 128*4947cdc7SCole Faust "rpartition": NewBuiltin("rpartition", string_partition), // sic 129*4947cdc7SCole Faust "rsplit": NewBuiltin("rsplit", string_split), // sic 130*4947cdc7SCole Faust "rstrip": NewBuiltin("rstrip", string_strip), // sic 131*4947cdc7SCole Faust "split": NewBuiltin("split", string_split), 132*4947cdc7SCole Faust "splitlines": NewBuiltin("splitlines", string_splitlines), 133*4947cdc7SCole Faust "startswith": NewBuiltin("startswith", string_startswith), 134*4947cdc7SCole Faust "strip": NewBuiltin("strip", string_strip), 135*4947cdc7SCole Faust "title": NewBuiltin("title", string_title), 136*4947cdc7SCole Faust "upper": NewBuiltin("upper", string_upper), 137*4947cdc7SCole Faust } 138*4947cdc7SCole Faust 139*4947cdc7SCole Faust setMethods = map[string]*Builtin{ 140*4947cdc7SCole Faust "union": NewBuiltin("union", set_union), 141*4947cdc7SCole Faust } 142*4947cdc7SCole Faust) 143*4947cdc7SCole Faust 144*4947cdc7SCole Faustfunc builtinAttr(recv Value, name string, methods map[string]*Builtin) (Value, error) { 145*4947cdc7SCole Faust b := methods[name] 146*4947cdc7SCole Faust if b == nil { 147*4947cdc7SCole Faust return nil, nil // no such method 148*4947cdc7SCole Faust } 149*4947cdc7SCole Faust return b.BindReceiver(recv), nil 150*4947cdc7SCole Faust} 151*4947cdc7SCole Faust 152*4947cdc7SCole Faustfunc builtinAttrNames(methods map[string]*Builtin) []string { 153*4947cdc7SCole Faust names := make([]string, 0, len(methods)) 154*4947cdc7SCole Faust for name := range methods { 155*4947cdc7SCole Faust names = append(names, name) 156*4947cdc7SCole Faust } 157*4947cdc7SCole Faust sort.Strings(names) 158*4947cdc7SCole Faust return names 159*4947cdc7SCole Faust} 160*4947cdc7SCole Faust 161*4947cdc7SCole Faust// ---- built-in functions ---- 162*4947cdc7SCole Faust 163*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#all 164*4947cdc7SCole Faustfunc all(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 165*4947cdc7SCole Faust var iterable Iterable 166*4947cdc7SCole Faust if err := UnpackPositionalArgs("all", args, kwargs, 1, &iterable); err != nil { 167*4947cdc7SCole Faust return nil, err 168*4947cdc7SCole Faust } 169*4947cdc7SCole Faust iter := iterable.Iterate() 170*4947cdc7SCole Faust defer iter.Done() 171*4947cdc7SCole Faust var x Value 172*4947cdc7SCole Faust for iter.Next(&x) { 173*4947cdc7SCole Faust if !x.Truth() { 174*4947cdc7SCole Faust return False, nil 175*4947cdc7SCole Faust } 176*4947cdc7SCole Faust } 177*4947cdc7SCole Faust return True, nil 178*4947cdc7SCole Faust} 179*4947cdc7SCole Faust 180*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#any 181*4947cdc7SCole Faustfunc any(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 182*4947cdc7SCole Faust var iterable Iterable 183*4947cdc7SCole Faust if err := UnpackPositionalArgs("any", args, kwargs, 1, &iterable); err != nil { 184*4947cdc7SCole Faust return nil, err 185*4947cdc7SCole Faust } 186*4947cdc7SCole Faust iter := iterable.Iterate() 187*4947cdc7SCole Faust defer iter.Done() 188*4947cdc7SCole Faust var x Value 189*4947cdc7SCole Faust for iter.Next(&x) { 190*4947cdc7SCole Faust if x.Truth() { 191*4947cdc7SCole Faust return True, nil 192*4947cdc7SCole Faust } 193*4947cdc7SCole Faust } 194*4947cdc7SCole Faust return False, nil 195*4947cdc7SCole Faust} 196*4947cdc7SCole Faust 197*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#bool 198*4947cdc7SCole Faustfunc bool_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 199*4947cdc7SCole Faust var x Value = False 200*4947cdc7SCole Faust if err := UnpackPositionalArgs("bool", args, kwargs, 0, &x); err != nil { 201*4947cdc7SCole Faust return nil, err 202*4947cdc7SCole Faust } 203*4947cdc7SCole Faust return x.Truth(), nil 204*4947cdc7SCole Faust} 205*4947cdc7SCole Faust 206*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#bytes 207*4947cdc7SCole Faustfunc bytes_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 208*4947cdc7SCole Faust if len(kwargs) > 0 { 209*4947cdc7SCole Faust return nil, fmt.Errorf("bytes does not accept keyword arguments") 210*4947cdc7SCole Faust } 211*4947cdc7SCole Faust if len(args) != 1 { 212*4947cdc7SCole Faust return nil, fmt.Errorf("bytes: got %d arguments, want exactly 1", len(args)) 213*4947cdc7SCole Faust } 214*4947cdc7SCole Faust switch x := args[0].(type) { 215*4947cdc7SCole Faust case Bytes: 216*4947cdc7SCole Faust return x, nil 217*4947cdc7SCole Faust case String: 218*4947cdc7SCole Faust // Invalid encodings are replaced by that of U+FFFD. 219*4947cdc7SCole Faust return Bytes(utf8Transcode(string(x))), nil 220*4947cdc7SCole Faust case Iterable: 221*4947cdc7SCole Faust // iterable of numeric byte values 222*4947cdc7SCole Faust var buf strings.Builder 223*4947cdc7SCole Faust if n := Len(x); n >= 0 { 224*4947cdc7SCole Faust // common case: known length 225*4947cdc7SCole Faust buf.Grow(n) 226*4947cdc7SCole Faust } 227*4947cdc7SCole Faust iter := x.Iterate() 228*4947cdc7SCole Faust defer iter.Done() 229*4947cdc7SCole Faust var elem Value 230*4947cdc7SCole Faust var b byte 231*4947cdc7SCole Faust for i := 0; iter.Next(&elem); i++ { 232*4947cdc7SCole Faust if err := AsInt(elem, &b); err != nil { 233*4947cdc7SCole Faust return nil, fmt.Errorf("bytes: at index %d, %s", i, err) 234*4947cdc7SCole Faust } 235*4947cdc7SCole Faust buf.WriteByte(b) 236*4947cdc7SCole Faust } 237*4947cdc7SCole Faust return Bytes(buf.String()), nil 238*4947cdc7SCole Faust 239*4947cdc7SCole Faust default: 240*4947cdc7SCole Faust // Unlike string(foo), which stringifies it, bytes(foo) is an error. 241*4947cdc7SCole Faust return nil, fmt.Errorf("bytes: got %s, want string, bytes, or iterable of ints", x.Type()) 242*4947cdc7SCole Faust } 243*4947cdc7SCole Faust} 244*4947cdc7SCole Faust 245*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#chr 246*4947cdc7SCole Faustfunc chr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 247*4947cdc7SCole Faust if len(kwargs) > 0 { 248*4947cdc7SCole Faust return nil, fmt.Errorf("chr does not accept keyword arguments") 249*4947cdc7SCole Faust } 250*4947cdc7SCole Faust if len(args) != 1 { 251*4947cdc7SCole Faust return nil, fmt.Errorf("chr: got %d arguments, want 1", len(args)) 252*4947cdc7SCole Faust } 253*4947cdc7SCole Faust i, err := AsInt32(args[0]) 254*4947cdc7SCole Faust if err != nil { 255*4947cdc7SCole Faust return nil, fmt.Errorf("chr: %s", err) 256*4947cdc7SCole Faust } 257*4947cdc7SCole Faust if i < 0 { 258*4947cdc7SCole Faust return nil, fmt.Errorf("chr: Unicode code point %d out of range (<0)", i) 259*4947cdc7SCole Faust } 260*4947cdc7SCole Faust if i > unicode.MaxRune { 261*4947cdc7SCole Faust return nil, fmt.Errorf("chr: Unicode code point U+%X out of range (>0x10FFFF)", i) 262*4947cdc7SCole Faust } 263*4947cdc7SCole Faust return String(string(rune(i))), nil 264*4947cdc7SCole Faust} 265*4947cdc7SCole Faust 266*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict 267*4947cdc7SCole Faustfunc dict(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 268*4947cdc7SCole Faust if len(args) > 1 { 269*4947cdc7SCole Faust return nil, fmt.Errorf("dict: got %d arguments, want at most 1", len(args)) 270*4947cdc7SCole Faust } 271*4947cdc7SCole Faust dict := new(Dict) 272*4947cdc7SCole Faust if err := updateDict(dict, args, kwargs); err != nil { 273*4947cdc7SCole Faust return nil, fmt.Errorf("dict: %v", err) 274*4947cdc7SCole Faust } 275*4947cdc7SCole Faust return dict, nil 276*4947cdc7SCole Faust} 277*4947cdc7SCole Faust 278*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#dir 279*4947cdc7SCole Faustfunc dir(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 280*4947cdc7SCole Faust if len(kwargs) > 0 { 281*4947cdc7SCole Faust return nil, fmt.Errorf("dir does not accept keyword arguments") 282*4947cdc7SCole Faust } 283*4947cdc7SCole Faust if len(args) != 1 { 284*4947cdc7SCole Faust return nil, fmt.Errorf("dir: got %d arguments, want 1", len(args)) 285*4947cdc7SCole Faust } 286*4947cdc7SCole Faust 287*4947cdc7SCole Faust var names []string 288*4947cdc7SCole Faust if x, ok := args[0].(HasAttrs); ok { 289*4947cdc7SCole Faust names = x.AttrNames() 290*4947cdc7SCole Faust } 291*4947cdc7SCole Faust sort.Strings(names) 292*4947cdc7SCole Faust elems := make([]Value, len(names)) 293*4947cdc7SCole Faust for i, name := range names { 294*4947cdc7SCole Faust elems[i] = String(name) 295*4947cdc7SCole Faust } 296*4947cdc7SCole Faust return NewList(elems), nil 297*4947cdc7SCole Faust} 298*4947cdc7SCole Faust 299*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#enumerate 300*4947cdc7SCole Faustfunc enumerate(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 301*4947cdc7SCole Faust var iterable Iterable 302*4947cdc7SCole Faust var start int 303*4947cdc7SCole Faust if err := UnpackPositionalArgs("enumerate", args, kwargs, 1, &iterable, &start); err != nil { 304*4947cdc7SCole Faust return nil, err 305*4947cdc7SCole Faust } 306*4947cdc7SCole Faust 307*4947cdc7SCole Faust iter := iterable.Iterate() 308*4947cdc7SCole Faust defer iter.Done() 309*4947cdc7SCole Faust 310*4947cdc7SCole Faust var pairs []Value 311*4947cdc7SCole Faust var x Value 312*4947cdc7SCole Faust 313*4947cdc7SCole Faust if n := Len(iterable); n >= 0 { 314*4947cdc7SCole Faust // common case: known length 315*4947cdc7SCole Faust pairs = make([]Value, 0, n) 316*4947cdc7SCole Faust array := make(Tuple, 2*n) // allocate a single backing array 317*4947cdc7SCole Faust for i := 0; iter.Next(&x); i++ { 318*4947cdc7SCole Faust pair := array[:2:2] 319*4947cdc7SCole Faust array = array[2:] 320*4947cdc7SCole Faust pair[0] = MakeInt(start + i) 321*4947cdc7SCole Faust pair[1] = x 322*4947cdc7SCole Faust pairs = append(pairs, pair) 323*4947cdc7SCole Faust } 324*4947cdc7SCole Faust } else { 325*4947cdc7SCole Faust // non-sequence (unknown length) 326*4947cdc7SCole Faust for i := 0; iter.Next(&x); i++ { 327*4947cdc7SCole Faust pair := Tuple{MakeInt(start + i), x} 328*4947cdc7SCole Faust pairs = append(pairs, pair) 329*4947cdc7SCole Faust } 330*4947cdc7SCole Faust } 331*4947cdc7SCole Faust 332*4947cdc7SCole Faust return NewList(pairs), nil 333*4947cdc7SCole Faust} 334*4947cdc7SCole Faust 335*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#fail 336*4947cdc7SCole Faustfunc fail(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 337*4947cdc7SCole Faust sep := " " 338*4947cdc7SCole Faust if err := UnpackArgs("fail", nil, kwargs, "sep?", &sep); err != nil { 339*4947cdc7SCole Faust return nil, err 340*4947cdc7SCole Faust } 341*4947cdc7SCole Faust buf := new(strings.Builder) 342*4947cdc7SCole Faust buf.WriteString("fail: ") 343*4947cdc7SCole Faust for i, v := range args { 344*4947cdc7SCole Faust if i > 0 { 345*4947cdc7SCole Faust buf.WriteString(sep) 346*4947cdc7SCole Faust } 347*4947cdc7SCole Faust if s, ok := AsString(v); ok { 348*4947cdc7SCole Faust buf.WriteString(s) 349*4947cdc7SCole Faust } else { 350*4947cdc7SCole Faust writeValue(buf, v, nil) 351*4947cdc7SCole Faust } 352*4947cdc7SCole Faust } 353*4947cdc7SCole Faust 354*4947cdc7SCole Faust return nil, errors.New(buf.String()) 355*4947cdc7SCole Faust} 356*4947cdc7SCole Faust 357*4947cdc7SCole Faustfunc float(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 358*4947cdc7SCole Faust if len(kwargs) > 0 { 359*4947cdc7SCole Faust return nil, fmt.Errorf("float does not accept keyword arguments") 360*4947cdc7SCole Faust } 361*4947cdc7SCole Faust if len(args) == 0 { 362*4947cdc7SCole Faust return Float(0.0), nil 363*4947cdc7SCole Faust } 364*4947cdc7SCole Faust if len(args) != 1 { 365*4947cdc7SCole Faust return nil, fmt.Errorf("float got %d arguments, wants 1", len(args)) 366*4947cdc7SCole Faust } 367*4947cdc7SCole Faust switch x := args[0].(type) { 368*4947cdc7SCole Faust case Bool: 369*4947cdc7SCole Faust if x { 370*4947cdc7SCole Faust return Float(1.0), nil 371*4947cdc7SCole Faust } else { 372*4947cdc7SCole Faust return Float(0.0), nil 373*4947cdc7SCole Faust } 374*4947cdc7SCole Faust case Int: 375*4947cdc7SCole Faust return x.finiteFloat() 376*4947cdc7SCole Faust case Float: 377*4947cdc7SCole Faust return x, nil 378*4947cdc7SCole Faust case String: 379*4947cdc7SCole Faust if x == "" { 380*4947cdc7SCole Faust return nil, fmt.Errorf("float: empty string") 381*4947cdc7SCole Faust } 382*4947cdc7SCole Faust // +/- NaN or Inf or Infinity (case insensitive)? 383*4947cdc7SCole Faust s := string(x) 384*4947cdc7SCole Faust switch x[len(x)-1] { 385*4947cdc7SCole Faust case 'y', 'Y': 386*4947cdc7SCole Faust if strings.EqualFold(s, "infinity") || strings.EqualFold(s, "+infinity") { 387*4947cdc7SCole Faust return inf, nil 388*4947cdc7SCole Faust } else if strings.EqualFold(s, "-infinity") { 389*4947cdc7SCole Faust return neginf, nil 390*4947cdc7SCole Faust } 391*4947cdc7SCole Faust case 'f', 'F': 392*4947cdc7SCole Faust if strings.EqualFold(s, "inf") || strings.EqualFold(s, "+inf") { 393*4947cdc7SCole Faust return inf, nil 394*4947cdc7SCole Faust } else if strings.EqualFold(s, "-inf") { 395*4947cdc7SCole Faust return neginf, nil 396*4947cdc7SCole Faust } 397*4947cdc7SCole Faust case 'n', 'N': 398*4947cdc7SCole Faust if strings.EqualFold(s, "nan") || strings.EqualFold(s, "+nan") || strings.EqualFold(s, "-nan") { 399*4947cdc7SCole Faust return nan, nil 400*4947cdc7SCole Faust } 401*4947cdc7SCole Faust } 402*4947cdc7SCole Faust f, err := strconv.ParseFloat(s, 64) 403*4947cdc7SCole Faust if math.IsInf(f, 0) { 404*4947cdc7SCole Faust return nil, fmt.Errorf("floating-point number too large") 405*4947cdc7SCole Faust } 406*4947cdc7SCole Faust if err != nil { 407*4947cdc7SCole Faust return nil, fmt.Errorf("invalid float literal: %s", s) 408*4947cdc7SCole Faust } 409*4947cdc7SCole Faust return Float(f), nil 410*4947cdc7SCole Faust default: 411*4947cdc7SCole Faust return nil, fmt.Errorf("float got %s, want number or string", x.Type()) 412*4947cdc7SCole Faust } 413*4947cdc7SCole Faust} 414*4947cdc7SCole Faust 415*4947cdc7SCole Faustvar ( 416*4947cdc7SCole Faust inf = Float(math.Inf(+1)) 417*4947cdc7SCole Faust neginf = Float(math.Inf(-1)) 418*4947cdc7SCole Faust nan = Float(math.NaN()) 419*4947cdc7SCole Faust) 420*4947cdc7SCole Faust 421*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#getattr 422*4947cdc7SCole Faustfunc getattr(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 423*4947cdc7SCole Faust var object, dflt Value 424*4947cdc7SCole Faust var name string 425*4947cdc7SCole Faust if err := UnpackPositionalArgs("getattr", args, kwargs, 2, &object, &name, &dflt); err != nil { 426*4947cdc7SCole Faust return nil, err 427*4947cdc7SCole Faust } 428*4947cdc7SCole Faust if object, ok := object.(HasAttrs); ok { 429*4947cdc7SCole Faust v, err := object.Attr(name) 430*4947cdc7SCole Faust if err != nil { 431*4947cdc7SCole Faust // An error could mean the field doesn't exist, 432*4947cdc7SCole Faust // or it exists but could not be computed. 433*4947cdc7SCole Faust if dflt != nil { 434*4947cdc7SCole Faust return dflt, nil 435*4947cdc7SCole Faust } 436*4947cdc7SCole Faust return nil, nameErr(b, err) 437*4947cdc7SCole Faust } 438*4947cdc7SCole Faust if v != nil { 439*4947cdc7SCole Faust return v, nil 440*4947cdc7SCole Faust } 441*4947cdc7SCole Faust // (nil, nil) => no such field 442*4947cdc7SCole Faust } 443*4947cdc7SCole Faust if dflt != nil { 444*4947cdc7SCole Faust return dflt, nil 445*4947cdc7SCole Faust } 446*4947cdc7SCole Faust return nil, fmt.Errorf("getattr: %s has no .%s field or method", object.Type(), name) 447*4947cdc7SCole Faust} 448*4947cdc7SCole Faust 449*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#hasattr 450*4947cdc7SCole Faustfunc hasattr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 451*4947cdc7SCole Faust var object Value 452*4947cdc7SCole Faust var name string 453*4947cdc7SCole Faust if err := UnpackPositionalArgs("hasattr", args, kwargs, 2, &object, &name); err != nil { 454*4947cdc7SCole Faust return nil, err 455*4947cdc7SCole Faust } 456*4947cdc7SCole Faust if object, ok := object.(HasAttrs); ok { 457*4947cdc7SCole Faust v, err := object.Attr(name) 458*4947cdc7SCole Faust if err == nil { 459*4947cdc7SCole Faust return Bool(v != nil), nil 460*4947cdc7SCole Faust } 461*4947cdc7SCole Faust 462*4947cdc7SCole Faust // An error does not conclusively indicate presence or 463*4947cdc7SCole Faust // absence of a field: it could occur while computing 464*4947cdc7SCole Faust // the value of a present attribute, or it could be a 465*4947cdc7SCole Faust // "no such attribute" error with details. 466*4947cdc7SCole Faust for _, x := range object.AttrNames() { 467*4947cdc7SCole Faust if x == name { 468*4947cdc7SCole Faust return True, nil 469*4947cdc7SCole Faust } 470*4947cdc7SCole Faust } 471*4947cdc7SCole Faust } 472*4947cdc7SCole Faust return False, nil 473*4947cdc7SCole Faust} 474*4947cdc7SCole Faust 475*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#hash 476*4947cdc7SCole Faustfunc hash(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 477*4947cdc7SCole Faust var x Value 478*4947cdc7SCole Faust if err := UnpackPositionalArgs("hash", args, kwargs, 1, &x); err != nil { 479*4947cdc7SCole Faust return nil, err 480*4947cdc7SCole Faust } 481*4947cdc7SCole Faust 482*4947cdc7SCole Faust var h int 483*4947cdc7SCole Faust switch x := x.(type) { 484*4947cdc7SCole Faust case String: 485*4947cdc7SCole Faust // The Starlark spec requires that the hash function be 486*4947cdc7SCole Faust // deterministic across all runs, motivated by the need 487*4947cdc7SCole Faust // for reproducibility of builds. Thus we cannot call 488*4947cdc7SCole Faust // String.Hash, which uses the fastest implementation 489*4947cdc7SCole Faust // available, because as varies across process restarts, 490*4947cdc7SCole Faust // and may evolve with the implementation. 491*4947cdc7SCole Faust h = int(javaStringHash(string(x))) 492*4947cdc7SCole Faust case Bytes: 493*4947cdc7SCole Faust h = int(softHashString(string(x))) // FNV32 494*4947cdc7SCole Faust default: 495*4947cdc7SCole Faust return nil, fmt.Errorf("hash: got %s, want string or bytes", x.Type()) 496*4947cdc7SCole Faust } 497*4947cdc7SCole Faust return MakeInt(h), nil 498*4947cdc7SCole Faust} 499*4947cdc7SCole Faust 500*4947cdc7SCole Faust// javaStringHash returns the same hash as would be produced by 501*4947cdc7SCole Faust// java.lang.String.hashCode. This requires transcoding the string to 502*4947cdc7SCole Faust// UTF-16; transcoding may introduce Unicode replacement characters 503*4947cdc7SCole Faust// U+FFFD if s does not contain valid UTF-8. 504*4947cdc7SCole Faustfunc javaStringHash(s string) (h int32) { 505*4947cdc7SCole Faust for _, r := range s { 506*4947cdc7SCole Faust if utf16.IsSurrogate(r) { 507*4947cdc7SCole Faust c1, c2 := utf16.EncodeRune(r) 508*4947cdc7SCole Faust h = 31*h + c1 509*4947cdc7SCole Faust h = 31*h + c2 510*4947cdc7SCole Faust } else { 511*4947cdc7SCole Faust h = 31*h + r // r may be U+FFFD 512*4947cdc7SCole Faust } 513*4947cdc7SCole Faust } 514*4947cdc7SCole Faust return h 515*4947cdc7SCole Faust} 516*4947cdc7SCole Faust 517*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#int 518*4947cdc7SCole Faustfunc int_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 519*4947cdc7SCole Faust var x Value = zero 520*4947cdc7SCole Faust var base Value 521*4947cdc7SCole Faust if err := UnpackArgs("int", args, kwargs, "x", &x, "base?", &base); err != nil { 522*4947cdc7SCole Faust return nil, err 523*4947cdc7SCole Faust } 524*4947cdc7SCole Faust 525*4947cdc7SCole Faust if s, ok := AsString(x); ok { 526*4947cdc7SCole Faust b := 10 527*4947cdc7SCole Faust if base != nil { 528*4947cdc7SCole Faust var err error 529*4947cdc7SCole Faust b, err = AsInt32(base) 530*4947cdc7SCole Faust if err != nil { 531*4947cdc7SCole Faust return nil, fmt.Errorf("int: for base, got %s, want int", base.Type()) 532*4947cdc7SCole Faust } 533*4947cdc7SCole Faust if b != 0 && (b < 2 || b > 36) { 534*4947cdc7SCole Faust return nil, fmt.Errorf("int: base must be an integer >= 2 && <= 36") 535*4947cdc7SCole Faust } 536*4947cdc7SCole Faust } 537*4947cdc7SCole Faust res := parseInt(s, b) 538*4947cdc7SCole Faust if res == nil { 539*4947cdc7SCole Faust return nil, fmt.Errorf("int: invalid literal with base %d: %s", b, s) 540*4947cdc7SCole Faust } 541*4947cdc7SCole Faust return res, nil 542*4947cdc7SCole Faust } 543*4947cdc7SCole Faust 544*4947cdc7SCole Faust if base != nil { 545*4947cdc7SCole Faust return nil, fmt.Errorf("int: can't convert non-string with explicit base") 546*4947cdc7SCole Faust } 547*4947cdc7SCole Faust 548*4947cdc7SCole Faust if b, ok := x.(Bool); ok { 549*4947cdc7SCole Faust if b { 550*4947cdc7SCole Faust return one, nil 551*4947cdc7SCole Faust } else { 552*4947cdc7SCole Faust return zero, nil 553*4947cdc7SCole Faust } 554*4947cdc7SCole Faust } 555*4947cdc7SCole Faust 556*4947cdc7SCole Faust i, err := NumberToInt(x) 557*4947cdc7SCole Faust if err != nil { 558*4947cdc7SCole Faust return nil, fmt.Errorf("int: %s", err) 559*4947cdc7SCole Faust } 560*4947cdc7SCole Faust return i, nil 561*4947cdc7SCole Faust} 562*4947cdc7SCole Faust 563*4947cdc7SCole Faust// parseInt defines the behavior of int(string, base=int). It returns nil on error. 564*4947cdc7SCole Faustfunc parseInt(s string, base int) Value { 565*4947cdc7SCole Faust // remove sign 566*4947cdc7SCole Faust var neg bool 567*4947cdc7SCole Faust if s != "" { 568*4947cdc7SCole Faust if s[0] == '+' { 569*4947cdc7SCole Faust s = s[1:] 570*4947cdc7SCole Faust } else if s[0] == '-' { 571*4947cdc7SCole Faust neg = true 572*4947cdc7SCole Faust s = s[1:] 573*4947cdc7SCole Faust } 574*4947cdc7SCole Faust } 575*4947cdc7SCole Faust 576*4947cdc7SCole Faust // remove optional base prefix 577*4947cdc7SCole Faust baseprefix := 0 578*4947cdc7SCole Faust if len(s) > 1 && s[0] == '0' { 579*4947cdc7SCole Faust if len(s) > 2 { 580*4947cdc7SCole Faust switch s[1] { 581*4947cdc7SCole Faust case 'o', 'O': 582*4947cdc7SCole Faust baseprefix = 8 583*4947cdc7SCole Faust case 'x', 'X': 584*4947cdc7SCole Faust baseprefix = 16 585*4947cdc7SCole Faust case 'b', 'B': 586*4947cdc7SCole Faust baseprefix = 2 587*4947cdc7SCole Faust } 588*4947cdc7SCole Faust } 589*4947cdc7SCole Faust if baseprefix != 0 { 590*4947cdc7SCole Faust // Remove the base prefix if it matches 591*4947cdc7SCole Faust // the explicit base, or if base=0. 592*4947cdc7SCole Faust if base == 0 || baseprefix == base { 593*4947cdc7SCole Faust base = baseprefix 594*4947cdc7SCole Faust s = s[2:] 595*4947cdc7SCole Faust } 596*4947cdc7SCole Faust } else { 597*4947cdc7SCole Faust // For automatic base detection, 598*4947cdc7SCole Faust // a string starting with zero 599*4947cdc7SCole Faust // must be all zeros. 600*4947cdc7SCole Faust // Thus we reject int("0755", 0). 601*4947cdc7SCole Faust if base == 0 { 602*4947cdc7SCole Faust for i := 1; i < len(s); i++ { 603*4947cdc7SCole Faust if s[i] != '0' { 604*4947cdc7SCole Faust return nil 605*4947cdc7SCole Faust } 606*4947cdc7SCole Faust } 607*4947cdc7SCole Faust return zero 608*4947cdc7SCole Faust } 609*4947cdc7SCole Faust } 610*4947cdc7SCole Faust } 611*4947cdc7SCole Faust if base == 0 { 612*4947cdc7SCole Faust base = 10 613*4947cdc7SCole Faust } 614*4947cdc7SCole Faust 615*4947cdc7SCole Faust // we explicitly handled sign above. 616*4947cdc7SCole Faust // if a sign remains, it is invalid. 617*4947cdc7SCole Faust if s != "" && (s[0] == '-' || s[0] == '+') { 618*4947cdc7SCole Faust return nil 619*4947cdc7SCole Faust } 620*4947cdc7SCole Faust 621*4947cdc7SCole Faust // s has no sign or base prefix. 622*4947cdc7SCole Faust if i, ok := new(big.Int).SetString(s, base); ok { 623*4947cdc7SCole Faust res := MakeBigInt(i) 624*4947cdc7SCole Faust if neg { 625*4947cdc7SCole Faust res = zero.Sub(res) 626*4947cdc7SCole Faust } 627*4947cdc7SCole Faust return res 628*4947cdc7SCole Faust } 629*4947cdc7SCole Faust 630*4947cdc7SCole Faust return nil 631*4947cdc7SCole Faust} 632*4947cdc7SCole Faust 633*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#len 634*4947cdc7SCole Faustfunc len_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 635*4947cdc7SCole Faust var x Value 636*4947cdc7SCole Faust if err := UnpackPositionalArgs("len", args, kwargs, 1, &x); err != nil { 637*4947cdc7SCole Faust return nil, err 638*4947cdc7SCole Faust } 639*4947cdc7SCole Faust len := Len(x) 640*4947cdc7SCole Faust if len < 0 { 641*4947cdc7SCole Faust return nil, fmt.Errorf("len: value of type %s has no len", x.Type()) 642*4947cdc7SCole Faust } 643*4947cdc7SCole Faust return MakeInt(len), nil 644*4947cdc7SCole Faust} 645*4947cdc7SCole Faust 646*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#list 647*4947cdc7SCole Faustfunc list(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 648*4947cdc7SCole Faust var iterable Iterable 649*4947cdc7SCole Faust if err := UnpackPositionalArgs("list", args, kwargs, 0, &iterable); err != nil { 650*4947cdc7SCole Faust return nil, err 651*4947cdc7SCole Faust } 652*4947cdc7SCole Faust var elems []Value 653*4947cdc7SCole Faust if iterable != nil { 654*4947cdc7SCole Faust iter := iterable.Iterate() 655*4947cdc7SCole Faust defer iter.Done() 656*4947cdc7SCole Faust if n := Len(iterable); n > 0 { 657*4947cdc7SCole Faust elems = make([]Value, 0, n) // preallocate if length known 658*4947cdc7SCole Faust } 659*4947cdc7SCole Faust var x Value 660*4947cdc7SCole Faust for iter.Next(&x) { 661*4947cdc7SCole Faust elems = append(elems, x) 662*4947cdc7SCole Faust } 663*4947cdc7SCole Faust } 664*4947cdc7SCole Faust return NewList(elems), nil 665*4947cdc7SCole Faust} 666*4947cdc7SCole Faust 667*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#min 668*4947cdc7SCole Faustfunc minmax(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 669*4947cdc7SCole Faust if len(args) == 0 { 670*4947cdc7SCole Faust return nil, fmt.Errorf("%s requires at least one positional argument", b.Name()) 671*4947cdc7SCole Faust } 672*4947cdc7SCole Faust var keyFunc Callable 673*4947cdc7SCole Faust if err := UnpackArgs(b.Name(), nil, kwargs, "key?", &keyFunc); err != nil { 674*4947cdc7SCole Faust return nil, err 675*4947cdc7SCole Faust } 676*4947cdc7SCole Faust var op syntax.Token 677*4947cdc7SCole Faust if b.Name() == "max" { 678*4947cdc7SCole Faust op = syntax.GT 679*4947cdc7SCole Faust } else { 680*4947cdc7SCole Faust op = syntax.LT 681*4947cdc7SCole Faust } 682*4947cdc7SCole Faust var iterable Value 683*4947cdc7SCole Faust if len(args) == 1 { 684*4947cdc7SCole Faust iterable = args[0] 685*4947cdc7SCole Faust } else { 686*4947cdc7SCole Faust iterable = args 687*4947cdc7SCole Faust } 688*4947cdc7SCole Faust iter := Iterate(iterable) 689*4947cdc7SCole Faust if iter == nil { 690*4947cdc7SCole Faust return nil, fmt.Errorf("%s: %s value is not iterable", b.Name(), iterable.Type()) 691*4947cdc7SCole Faust } 692*4947cdc7SCole Faust defer iter.Done() 693*4947cdc7SCole Faust var extremum Value 694*4947cdc7SCole Faust if !iter.Next(&extremum) { 695*4947cdc7SCole Faust return nil, nameErr(b, "argument is an empty sequence") 696*4947cdc7SCole Faust } 697*4947cdc7SCole Faust 698*4947cdc7SCole Faust var extremeKey Value 699*4947cdc7SCole Faust var keyargs Tuple 700*4947cdc7SCole Faust if keyFunc == nil { 701*4947cdc7SCole Faust extremeKey = extremum 702*4947cdc7SCole Faust } else { 703*4947cdc7SCole Faust keyargs = Tuple{extremum} 704*4947cdc7SCole Faust res, err := Call(thread, keyFunc, keyargs, nil) 705*4947cdc7SCole Faust if err != nil { 706*4947cdc7SCole Faust return nil, err // to preserve backtrace, don't modify error 707*4947cdc7SCole Faust } 708*4947cdc7SCole Faust extremeKey = res 709*4947cdc7SCole Faust } 710*4947cdc7SCole Faust 711*4947cdc7SCole Faust var x Value 712*4947cdc7SCole Faust for iter.Next(&x) { 713*4947cdc7SCole Faust var key Value 714*4947cdc7SCole Faust if keyFunc == nil { 715*4947cdc7SCole Faust key = x 716*4947cdc7SCole Faust } else { 717*4947cdc7SCole Faust keyargs[0] = x 718*4947cdc7SCole Faust res, err := Call(thread, keyFunc, keyargs, nil) 719*4947cdc7SCole Faust if err != nil { 720*4947cdc7SCole Faust return nil, err // to preserve backtrace, don't modify error 721*4947cdc7SCole Faust } 722*4947cdc7SCole Faust key = res 723*4947cdc7SCole Faust } 724*4947cdc7SCole Faust 725*4947cdc7SCole Faust if ok, err := Compare(op, key, extremeKey); err != nil { 726*4947cdc7SCole Faust return nil, nameErr(b, err) 727*4947cdc7SCole Faust } else if ok { 728*4947cdc7SCole Faust extremum = x 729*4947cdc7SCole Faust extremeKey = key 730*4947cdc7SCole Faust } 731*4947cdc7SCole Faust } 732*4947cdc7SCole Faust return extremum, nil 733*4947cdc7SCole Faust} 734*4947cdc7SCole Faust 735*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#ord 736*4947cdc7SCole Faustfunc ord(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 737*4947cdc7SCole Faust if len(kwargs) > 0 { 738*4947cdc7SCole Faust return nil, fmt.Errorf("ord does not accept keyword arguments") 739*4947cdc7SCole Faust } 740*4947cdc7SCole Faust if len(args) != 1 { 741*4947cdc7SCole Faust return nil, fmt.Errorf("ord: got %d arguments, want 1", len(args)) 742*4947cdc7SCole Faust } 743*4947cdc7SCole Faust switch x := args[0].(type) { 744*4947cdc7SCole Faust case String: 745*4947cdc7SCole Faust // ord(string) returns int value of sole rune. 746*4947cdc7SCole Faust s := string(x) 747*4947cdc7SCole Faust r, sz := utf8.DecodeRuneInString(s) 748*4947cdc7SCole Faust if sz == 0 || sz != len(s) { 749*4947cdc7SCole Faust n := utf8.RuneCountInString(s) 750*4947cdc7SCole Faust return nil, fmt.Errorf("ord: string encodes %d Unicode code points, want 1", n) 751*4947cdc7SCole Faust } 752*4947cdc7SCole Faust return MakeInt(int(r)), nil 753*4947cdc7SCole Faust 754*4947cdc7SCole Faust case Bytes: 755*4947cdc7SCole Faust // ord(bytes) returns int value of sole byte. 756*4947cdc7SCole Faust if len(x) != 1 { 757*4947cdc7SCole Faust return nil, fmt.Errorf("ord: bytes has length %d, want 1", len(x)) 758*4947cdc7SCole Faust } 759*4947cdc7SCole Faust return MakeInt(int(x[0])), nil 760*4947cdc7SCole Faust default: 761*4947cdc7SCole Faust return nil, fmt.Errorf("ord: got %s, want string or bytes", x.Type()) 762*4947cdc7SCole Faust } 763*4947cdc7SCole Faust} 764*4947cdc7SCole Faust 765*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#print 766*4947cdc7SCole Faustfunc print(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 767*4947cdc7SCole Faust sep := " " 768*4947cdc7SCole Faust if err := UnpackArgs("print", nil, kwargs, "sep?", &sep); err != nil { 769*4947cdc7SCole Faust return nil, err 770*4947cdc7SCole Faust } 771*4947cdc7SCole Faust buf := new(strings.Builder) 772*4947cdc7SCole Faust for i, v := range args { 773*4947cdc7SCole Faust if i > 0 { 774*4947cdc7SCole Faust buf.WriteString(sep) 775*4947cdc7SCole Faust } 776*4947cdc7SCole Faust if s, ok := AsString(v); ok { 777*4947cdc7SCole Faust buf.WriteString(s) 778*4947cdc7SCole Faust } else if b, ok := v.(Bytes); ok { 779*4947cdc7SCole Faust buf.WriteString(string(b)) 780*4947cdc7SCole Faust } else { 781*4947cdc7SCole Faust writeValue(buf, v, nil) 782*4947cdc7SCole Faust } 783*4947cdc7SCole Faust } 784*4947cdc7SCole Faust 785*4947cdc7SCole Faust s := buf.String() 786*4947cdc7SCole Faust if thread.Print != nil { 787*4947cdc7SCole Faust thread.Print(thread, s) 788*4947cdc7SCole Faust } else { 789*4947cdc7SCole Faust fmt.Fprintln(os.Stderr, s) 790*4947cdc7SCole Faust } 791*4947cdc7SCole Faust return None, nil 792*4947cdc7SCole Faust} 793*4947cdc7SCole Faust 794*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#range 795*4947cdc7SCole Faustfunc range_(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 796*4947cdc7SCole Faust var start, stop, step int 797*4947cdc7SCole Faust step = 1 798*4947cdc7SCole Faust if err := UnpackPositionalArgs("range", args, kwargs, 1, &start, &stop, &step); err != nil { 799*4947cdc7SCole Faust return nil, err 800*4947cdc7SCole Faust } 801*4947cdc7SCole Faust 802*4947cdc7SCole Faust if len(args) == 1 { 803*4947cdc7SCole Faust // range(stop) 804*4947cdc7SCole Faust start, stop = 0, start 805*4947cdc7SCole Faust } 806*4947cdc7SCole Faust if step == 0 { 807*4947cdc7SCole Faust // we were given range(start, stop, 0) 808*4947cdc7SCole Faust return nil, nameErr(b, "step argument must not be zero") 809*4947cdc7SCole Faust } 810*4947cdc7SCole Faust 811*4947cdc7SCole Faust return rangeValue{start: start, stop: stop, step: step, len: rangeLen(start, stop, step)}, nil 812*4947cdc7SCole Faust} 813*4947cdc7SCole Faust 814*4947cdc7SCole Faust// A rangeValue is a comparable, immutable, indexable sequence of integers 815*4947cdc7SCole Faust// defined by the three parameters to a range(...) call. 816*4947cdc7SCole Faust// Invariant: step != 0. 817*4947cdc7SCole Fausttype rangeValue struct{ start, stop, step, len int } 818*4947cdc7SCole Faust 819*4947cdc7SCole Faustvar ( 820*4947cdc7SCole Faust _ Indexable = rangeValue{} 821*4947cdc7SCole Faust _ Sequence = rangeValue{} 822*4947cdc7SCole Faust _ Comparable = rangeValue{} 823*4947cdc7SCole Faust _ Sliceable = rangeValue{} 824*4947cdc7SCole Faust) 825*4947cdc7SCole Faust 826*4947cdc7SCole Faustfunc (r rangeValue) Len() int { return r.len } 827*4947cdc7SCole Faustfunc (r rangeValue) Index(i int) Value { return MakeInt(r.start + i*r.step) } 828*4947cdc7SCole Faustfunc (r rangeValue) Iterate() Iterator { return &rangeIterator{r, 0} } 829*4947cdc7SCole Faust 830*4947cdc7SCole Faust// rangeLen calculates the length of a range with the provided start, stop, and step. 831*4947cdc7SCole Faust// caller must ensure that step is non-zero. 832*4947cdc7SCole Faustfunc rangeLen(start, stop, step int) int { 833*4947cdc7SCole Faust switch { 834*4947cdc7SCole Faust case step > 0: 835*4947cdc7SCole Faust if stop > start { 836*4947cdc7SCole Faust return (stop-1-start)/step + 1 837*4947cdc7SCole Faust } 838*4947cdc7SCole Faust case step < 0: 839*4947cdc7SCole Faust if start > stop { 840*4947cdc7SCole Faust return (start-1-stop)/-step + 1 841*4947cdc7SCole Faust } 842*4947cdc7SCole Faust default: 843*4947cdc7SCole Faust panic("rangeLen: zero step") 844*4947cdc7SCole Faust } 845*4947cdc7SCole Faust return 0 846*4947cdc7SCole Faust} 847*4947cdc7SCole Faust 848*4947cdc7SCole Faustfunc (r rangeValue) Slice(start, end, step int) Value { 849*4947cdc7SCole Faust newStart := r.start + r.step*start 850*4947cdc7SCole Faust newStop := r.start + r.step*end 851*4947cdc7SCole Faust newStep := r.step * step 852*4947cdc7SCole Faust return rangeValue{ 853*4947cdc7SCole Faust start: newStart, 854*4947cdc7SCole Faust stop: newStop, 855*4947cdc7SCole Faust step: newStep, 856*4947cdc7SCole Faust len: rangeLen(newStart, newStop, newStep), 857*4947cdc7SCole Faust } 858*4947cdc7SCole Faust} 859*4947cdc7SCole Faust 860*4947cdc7SCole Faustfunc (r rangeValue) Freeze() {} // immutable 861*4947cdc7SCole Faustfunc (r rangeValue) String() string { 862*4947cdc7SCole Faust if r.step != 1 { 863*4947cdc7SCole Faust return fmt.Sprintf("range(%d, %d, %d)", r.start, r.stop, r.step) 864*4947cdc7SCole Faust } else if r.start != 0 { 865*4947cdc7SCole Faust return fmt.Sprintf("range(%d, %d)", r.start, r.stop) 866*4947cdc7SCole Faust } else { 867*4947cdc7SCole Faust return fmt.Sprintf("range(%d)", r.stop) 868*4947cdc7SCole Faust } 869*4947cdc7SCole Faust} 870*4947cdc7SCole Faustfunc (r rangeValue) Type() string { return "range" } 871*4947cdc7SCole Faustfunc (r rangeValue) Truth() Bool { return r.len > 0 } 872*4947cdc7SCole Faustfunc (r rangeValue) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: range") } 873*4947cdc7SCole Faust 874*4947cdc7SCole Faustfunc (x rangeValue) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) { 875*4947cdc7SCole Faust y := y_.(rangeValue) 876*4947cdc7SCole Faust switch op { 877*4947cdc7SCole Faust case syntax.EQL: 878*4947cdc7SCole Faust return rangeEqual(x, y), nil 879*4947cdc7SCole Faust case syntax.NEQ: 880*4947cdc7SCole Faust return !rangeEqual(x, y), nil 881*4947cdc7SCole Faust default: 882*4947cdc7SCole Faust return false, fmt.Errorf("%s %s %s not implemented", x.Type(), op, y.Type()) 883*4947cdc7SCole Faust } 884*4947cdc7SCole Faust} 885*4947cdc7SCole Faust 886*4947cdc7SCole Faustfunc rangeEqual(x, y rangeValue) bool { 887*4947cdc7SCole Faust // Two ranges compare equal if they denote the same sequence. 888*4947cdc7SCole Faust if x.len != y.len { 889*4947cdc7SCole Faust return false // sequences differ in length 890*4947cdc7SCole Faust } 891*4947cdc7SCole Faust if x.len == 0 { 892*4947cdc7SCole Faust return true // both sequences are empty 893*4947cdc7SCole Faust } 894*4947cdc7SCole Faust if x.start != y.start { 895*4947cdc7SCole Faust return false // first element differs 896*4947cdc7SCole Faust } 897*4947cdc7SCole Faust return x.len == 1 || x.step == y.step 898*4947cdc7SCole Faust} 899*4947cdc7SCole Faust 900*4947cdc7SCole Faustfunc (r rangeValue) contains(x Int) bool { 901*4947cdc7SCole Faust x32, err := AsInt32(x) 902*4947cdc7SCole Faust if err != nil { 903*4947cdc7SCole Faust return false // out of range 904*4947cdc7SCole Faust } 905*4947cdc7SCole Faust delta := x32 - r.start 906*4947cdc7SCole Faust quo, rem := delta/r.step, delta%r.step 907*4947cdc7SCole Faust return rem == 0 && 0 <= quo && quo < r.len 908*4947cdc7SCole Faust} 909*4947cdc7SCole Faust 910*4947cdc7SCole Fausttype rangeIterator struct { 911*4947cdc7SCole Faust r rangeValue 912*4947cdc7SCole Faust i int 913*4947cdc7SCole Faust} 914*4947cdc7SCole Faust 915*4947cdc7SCole Faustfunc (it *rangeIterator) Next(p *Value) bool { 916*4947cdc7SCole Faust if it.i < it.r.len { 917*4947cdc7SCole Faust *p = it.r.Index(it.i) 918*4947cdc7SCole Faust it.i++ 919*4947cdc7SCole Faust return true 920*4947cdc7SCole Faust } 921*4947cdc7SCole Faust return false 922*4947cdc7SCole Faust} 923*4947cdc7SCole Faustfunc (*rangeIterator) Done() {} 924*4947cdc7SCole Faust 925*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#repr 926*4947cdc7SCole Faustfunc repr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 927*4947cdc7SCole Faust var x Value 928*4947cdc7SCole Faust if err := UnpackPositionalArgs("repr", args, kwargs, 1, &x); err != nil { 929*4947cdc7SCole Faust return nil, err 930*4947cdc7SCole Faust } 931*4947cdc7SCole Faust return String(x.String()), nil 932*4947cdc7SCole Faust} 933*4947cdc7SCole Faust 934*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#reversed 935*4947cdc7SCole Faustfunc reversed(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 936*4947cdc7SCole Faust var iterable Iterable 937*4947cdc7SCole Faust if err := UnpackPositionalArgs("reversed", args, kwargs, 1, &iterable); err != nil { 938*4947cdc7SCole Faust return nil, err 939*4947cdc7SCole Faust } 940*4947cdc7SCole Faust iter := iterable.Iterate() 941*4947cdc7SCole Faust defer iter.Done() 942*4947cdc7SCole Faust var elems []Value 943*4947cdc7SCole Faust if n := Len(args[0]); n >= 0 { 944*4947cdc7SCole Faust elems = make([]Value, 0, n) // preallocate if length known 945*4947cdc7SCole Faust } 946*4947cdc7SCole Faust var x Value 947*4947cdc7SCole Faust for iter.Next(&x) { 948*4947cdc7SCole Faust elems = append(elems, x) 949*4947cdc7SCole Faust } 950*4947cdc7SCole Faust n := len(elems) 951*4947cdc7SCole Faust for i := 0; i < n>>1; i++ { 952*4947cdc7SCole Faust elems[i], elems[n-1-i] = elems[n-1-i], elems[i] 953*4947cdc7SCole Faust } 954*4947cdc7SCole Faust return NewList(elems), nil 955*4947cdc7SCole Faust} 956*4947cdc7SCole Faust 957*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#set 958*4947cdc7SCole Faustfunc set(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 959*4947cdc7SCole Faust var iterable Iterable 960*4947cdc7SCole Faust if err := UnpackPositionalArgs("set", args, kwargs, 0, &iterable); err != nil { 961*4947cdc7SCole Faust return nil, err 962*4947cdc7SCole Faust } 963*4947cdc7SCole Faust set := new(Set) 964*4947cdc7SCole Faust if iterable != nil { 965*4947cdc7SCole Faust iter := iterable.Iterate() 966*4947cdc7SCole Faust defer iter.Done() 967*4947cdc7SCole Faust var x Value 968*4947cdc7SCole Faust for iter.Next(&x) { 969*4947cdc7SCole Faust if err := set.Insert(x); err != nil { 970*4947cdc7SCole Faust return nil, nameErr(b, err) 971*4947cdc7SCole Faust } 972*4947cdc7SCole Faust } 973*4947cdc7SCole Faust } 974*4947cdc7SCole Faust return set, nil 975*4947cdc7SCole Faust} 976*4947cdc7SCole Faust 977*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#sorted 978*4947cdc7SCole Faustfunc sorted(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 979*4947cdc7SCole Faust // Oddly, Python's sorted permits all arguments to be positional, thus so do we. 980*4947cdc7SCole Faust var iterable Iterable 981*4947cdc7SCole Faust var key Callable 982*4947cdc7SCole Faust var reverse bool 983*4947cdc7SCole Faust if err := UnpackArgs("sorted", args, kwargs, 984*4947cdc7SCole Faust "iterable", &iterable, 985*4947cdc7SCole Faust "key?", &key, 986*4947cdc7SCole Faust "reverse?", &reverse, 987*4947cdc7SCole Faust ); err != nil { 988*4947cdc7SCole Faust return nil, err 989*4947cdc7SCole Faust } 990*4947cdc7SCole Faust 991*4947cdc7SCole Faust iter := iterable.Iterate() 992*4947cdc7SCole Faust defer iter.Done() 993*4947cdc7SCole Faust var values []Value 994*4947cdc7SCole Faust if n := Len(iterable); n > 0 { 995*4947cdc7SCole Faust values = make(Tuple, 0, n) // preallocate if length is known 996*4947cdc7SCole Faust } 997*4947cdc7SCole Faust var x Value 998*4947cdc7SCole Faust for iter.Next(&x) { 999*4947cdc7SCole Faust values = append(values, x) 1000*4947cdc7SCole Faust } 1001*4947cdc7SCole Faust 1002*4947cdc7SCole Faust // Derive keys from values by applying key function. 1003*4947cdc7SCole Faust var keys []Value 1004*4947cdc7SCole Faust if key != nil { 1005*4947cdc7SCole Faust keys = make([]Value, len(values)) 1006*4947cdc7SCole Faust for i, v := range values { 1007*4947cdc7SCole Faust k, err := Call(thread, key, Tuple{v}, nil) 1008*4947cdc7SCole Faust if err != nil { 1009*4947cdc7SCole Faust return nil, err // to preserve backtrace, don't modify error 1010*4947cdc7SCole Faust } 1011*4947cdc7SCole Faust keys[i] = k 1012*4947cdc7SCole Faust } 1013*4947cdc7SCole Faust } 1014*4947cdc7SCole Faust 1015*4947cdc7SCole Faust slice := &sortSlice{keys: keys, values: values} 1016*4947cdc7SCole Faust if reverse { 1017*4947cdc7SCole Faust sort.Stable(sort.Reverse(slice)) 1018*4947cdc7SCole Faust } else { 1019*4947cdc7SCole Faust sort.Stable(slice) 1020*4947cdc7SCole Faust } 1021*4947cdc7SCole Faust return NewList(slice.values), slice.err 1022*4947cdc7SCole Faust} 1023*4947cdc7SCole Faust 1024*4947cdc7SCole Fausttype sortSlice struct { 1025*4947cdc7SCole Faust keys []Value // nil => values[i] is key 1026*4947cdc7SCole Faust values []Value 1027*4947cdc7SCole Faust err error 1028*4947cdc7SCole Faust} 1029*4947cdc7SCole Faust 1030*4947cdc7SCole Faustfunc (s *sortSlice) Len() int { return len(s.values) } 1031*4947cdc7SCole Faustfunc (s *sortSlice) Less(i, j int) bool { 1032*4947cdc7SCole Faust keys := s.keys 1033*4947cdc7SCole Faust if s.keys == nil { 1034*4947cdc7SCole Faust keys = s.values 1035*4947cdc7SCole Faust } 1036*4947cdc7SCole Faust ok, err := Compare(syntax.LT, keys[i], keys[j]) 1037*4947cdc7SCole Faust if err != nil { 1038*4947cdc7SCole Faust s.err = err 1039*4947cdc7SCole Faust } 1040*4947cdc7SCole Faust return ok 1041*4947cdc7SCole Faust} 1042*4947cdc7SCole Faustfunc (s *sortSlice) Swap(i, j int) { 1043*4947cdc7SCole Faust if s.keys != nil { 1044*4947cdc7SCole Faust s.keys[i], s.keys[j] = s.keys[j], s.keys[i] 1045*4947cdc7SCole Faust } 1046*4947cdc7SCole Faust s.values[i], s.values[j] = s.values[j], s.values[i] 1047*4947cdc7SCole Faust} 1048*4947cdc7SCole Faust 1049*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#str 1050*4947cdc7SCole Faustfunc str(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1051*4947cdc7SCole Faust if len(kwargs) > 0 { 1052*4947cdc7SCole Faust return nil, fmt.Errorf("str does not accept keyword arguments") 1053*4947cdc7SCole Faust } 1054*4947cdc7SCole Faust if len(args) != 1 { 1055*4947cdc7SCole Faust return nil, fmt.Errorf("str: got %d arguments, want exactly 1", len(args)) 1056*4947cdc7SCole Faust } 1057*4947cdc7SCole Faust switch x := args[0].(type) { 1058*4947cdc7SCole Faust case String: 1059*4947cdc7SCole Faust return x, nil 1060*4947cdc7SCole Faust case Bytes: 1061*4947cdc7SCole Faust // Invalid encodings are replaced by that of U+FFFD. 1062*4947cdc7SCole Faust return String(utf8Transcode(string(x))), nil 1063*4947cdc7SCole Faust default: 1064*4947cdc7SCole Faust return String(x.String()), nil 1065*4947cdc7SCole Faust } 1066*4947cdc7SCole Faust} 1067*4947cdc7SCole Faust 1068*4947cdc7SCole Faust// utf8Transcode returns the UTF-8-to-UTF-8 transcoding of s. 1069*4947cdc7SCole Faust// The effect is that each code unit that is part of an 1070*4947cdc7SCole Faust// invalid sequence is replaced by U+FFFD. 1071*4947cdc7SCole Faustfunc utf8Transcode(s string) string { 1072*4947cdc7SCole Faust if utf8.ValidString(s) { 1073*4947cdc7SCole Faust return s 1074*4947cdc7SCole Faust } 1075*4947cdc7SCole Faust var out strings.Builder 1076*4947cdc7SCole Faust for _, r := range s { 1077*4947cdc7SCole Faust out.WriteRune(r) 1078*4947cdc7SCole Faust } 1079*4947cdc7SCole Faust return out.String() 1080*4947cdc7SCole Faust} 1081*4947cdc7SCole Faust 1082*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#tuple 1083*4947cdc7SCole Faustfunc tuple(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1084*4947cdc7SCole Faust var iterable Iterable 1085*4947cdc7SCole Faust if err := UnpackPositionalArgs("tuple", args, kwargs, 0, &iterable); err != nil { 1086*4947cdc7SCole Faust return nil, err 1087*4947cdc7SCole Faust } 1088*4947cdc7SCole Faust if len(args) == 0 { 1089*4947cdc7SCole Faust return Tuple(nil), nil 1090*4947cdc7SCole Faust } 1091*4947cdc7SCole Faust iter := iterable.Iterate() 1092*4947cdc7SCole Faust defer iter.Done() 1093*4947cdc7SCole Faust var elems Tuple 1094*4947cdc7SCole Faust if n := Len(iterable); n > 0 { 1095*4947cdc7SCole Faust elems = make(Tuple, 0, n) // preallocate if length is known 1096*4947cdc7SCole Faust } 1097*4947cdc7SCole Faust var x Value 1098*4947cdc7SCole Faust for iter.Next(&x) { 1099*4947cdc7SCole Faust elems = append(elems, x) 1100*4947cdc7SCole Faust } 1101*4947cdc7SCole Faust return elems, nil 1102*4947cdc7SCole Faust} 1103*4947cdc7SCole Faust 1104*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#type 1105*4947cdc7SCole Faustfunc type_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1106*4947cdc7SCole Faust if len(kwargs) > 0 { 1107*4947cdc7SCole Faust return nil, fmt.Errorf("type does not accept keyword arguments") 1108*4947cdc7SCole Faust } 1109*4947cdc7SCole Faust if len(args) != 1 { 1110*4947cdc7SCole Faust return nil, fmt.Errorf("type: got %d arguments, want exactly 1", len(args)) 1111*4947cdc7SCole Faust } 1112*4947cdc7SCole Faust return String(args[0].Type()), nil 1113*4947cdc7SCole Faust} 1114*4947cdc7SCole Faust 1115*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#zip 1116*4947cdc7SCole Faustfunc zip(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1117*4947cdc7SCole Faust if len(kwargs) > 0 { 1118*4947cdc7SCole Faust return nil, fmt.Errorf("zip does not accept keyword arguments") 1119*4947cdc7SCole Faust } 1120*4947cdc7SCole Faust rows, cols := 0, len(args) 1121*4947cdc7SCole Faust iters := make([]Iterator, cols) 1122*4947cdc7SCole Faust defer func() { 1123*4947cdc7SCole Faust for _, iter := range iters { 1124*4947cdc7SCole Faust if iter != nil { 1125*4947cdc7SCole Faust iter.Done() 1126*4947cdc7SCole Faust } 1127*4947cdc7SCole Faust } 1128*4947cdc7SCole Faust }() 1129*4947cdc7SCole Faust for i, seq := range args { 1130*4947cdc7SCole Faust it := Iterate(seq) 1131*4947cdc7SCole Faust if it == nil { 1132*4947cdc7SCole Faust return nil, fmt.Errorf("zip: argument #%d is not iterable: %s", i+1, seq.Type()) 1133*4947cdc7SCole Faust } 1134*4947cdc7SCole Faust iters[i] = it 1135*4947cdc7SCole Faust n := Len(seq) 1136*4947cdc7SCole Faust if i == 0 || n < rows { 1137*4947cdc7SCole Faust rows = n // possibly -1 1138*4947cdc7SCole Faust } 1139*4947cdc7SCole Faust } 1140*4947cdc7SCole Faust var result []Value 1141*4947cdc7SCole Faust if rows >= 0 { 1142*4947cdc7SCole Faust // length known 1143*4947cdc7SCole Faust result = make([]Value, rows) 1144*4947cdc7SCole Faust array := make(Tuple, cols*rows) // allocate a single backing array 1145*4947cdc7SCole Faust for i := 0; i < rows; i++ { 1146*4947cdc7SCole Faust tuple := array[:cols:cols] 1147*4947cdc7SCole Faust array = array[cols:] 1148*4947cdc7SCole Faust for j, iter := range iters { 1149*4947cdc7SCole Faust iter.Next(&tuple[j]) 1150*4947cdc7SCole Faust } 1151*4947cdc7SCole Faust result[i] = tuple 1152*4947cdc7SCole Faust } 1153*4947cdc7SCole Faust } else { 1154*4947cdc7SCole Faust // length not known 1155*4947cdc7SCole Faust outer: 1156*4947cdc7SCole Faust for { 1157*4947cdc7SCole Faust tuple := make(Tuple, cols) 1158*4947cdc7SCole Faust for i, iter := range iters { 1159*4947cdc7SCole Faust if !iter.Next(&tuple[i]) { 1160*4947cdc7SCole Faust break outer 1161*4947cdc7SCole Faust } 1162*4947cdc7SCole Faust } 1163*4947cdc7SCole Faust result = append(result, tuple) 1164*4947cdc7SCole Faust } 1165*4947cdc7SCole Faust } 1166*4947cdc7SCole Faust return NewList(result), nil 1167*4947cdc7SCole Faust} 1168*4947cdc7SCole Faust 1169*4947cdc7SCole Faust// ---- methods of built-in types --- 1170*4947cdc7SCole Faust 1171*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·get 1172*4947cdc7SCole Faustfunc dict_get(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1173*4947cdc7SCole Faust var key, dflt Value 1174*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &key, &dflt); err != nil { 1175*4947cdc7SCole Faust return nil, err 1176*4947cdc7SCole Faust } 1177*4947cdc7SCole Faust if v, ok, err := b.Receiver().(*Dict).Get(key); err != nil { 1178*4947cdc7SCole Faust return nil, nameErr(b, err) 1179*4947cdc7SCole Faust } else if ok { 1180*4947cdc7SCole Faust return v, nil 1181*4947cdc7SCole Faust } else if dflt != nil { 1182*4947cdc7SCole Faust return dflt, nil 1183*4947cdc7SCole Faust } 1184*4947cdc7SCole Faust return None, nil 1185*4947cdc7SCole Faust} 1186*4947cdc7SCole Faust 1187*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·clear 1188*4947cdc7SCole Faustfunc dict_clear(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1189*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1190*4947cdc7SCole Faust return nil, err 1191*4947cdc7SCole Faust } 1192*4947cdc7SCole Faust return None, b.Receiver().(*Dict).Clear() 1193*4947cdc7SCole Faust} 1194*4947cdc7SCole Faust 1195*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·items 1196*4947cdc7SCole Faustfunc dict_items(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1197*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1198*4947cdc7SCole Faust return nil, err 1199*4947cdc7SCole Faust } 1200*4947cdc7SCole Faust items := b.Receiver().(*Dict).Items() 1201*4947cdc7SCole Faust res := make([]Value, len(items)) 1202*4947cdc7SCole Faust for i, item := range items { 1203*4947cdc7SCole Faust res[i] = item // convert [2]Value to Value 1204*4947cdc7SCole Faust } 1205*4947cdc7SCole Faust return NewList(res), nil 1206*4947cdc7SCole Faust} 1207*4947cdc7SCole Faust 1208*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·keys 1209*4947cdc7SCole Faustfunc dict_keys(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1210*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1211*4947cdc7SCole Faust return nil, err 1212*4947cdc7SCole Faust } 1213*4947cdc7SCole Faust return NewList(b.Receiver().(*Dict).Keys()), nil 1214*4947cdc7SCole Faust} 1215*4947cdc7SCole Faust 1216*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·pop 1217*4947cdc7SCole Faustfunc dict_pop(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1218*4947cdc7SCole Faust var k, d Value 1219*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &k, &d); err != nil { 1220*4947cdc7SCole Faust return nil, err 1221*4947cdc7SCole Faust } 1222*4947cdc7SCole Faust if v, found, err := b.Receiver().(*Dict).Delete(k); err != nil { 1223*4947cdc7SCole Faust return nil, nameErr(b, err) // dict is frozen or key is unhashable 1224*4947cdc7SCole Faust } else if found { 1225*4947cdc7SCole Faust return v, nil 1226*4947cdc7SCole Faust } else if d != nil { 1227*4947cdc7SCole Faust return d, nil 1228*4947cdc7SCole Faust } 1229*4947cdc7SCole Faust return nil, nameErr(b, "missing key") 1230*4947cdc7SCole Faust} 1231*4947cdc7SCole Faust 1232*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·popitem 1233*4947cdc7SCole Faustfunc dict_popitem(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1234*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1235*4947cdc7SCole Faust return nil, err 1236*4947cdc7SCole Faust } 1237*4947cdc7SCole Faust recv := b.Receiver().(*Dict) 1238*4947cdc7SCole Faust k, ok := recv.ht.first() 1239*4947cdc7SCole Faust if !ok { 1240*4947cdc7SCole Faust return nil, nameErr(b, "empty dict") 1241*4947cdc7SCole Faust } 1242*4947cdc7SCole Faust v, _, err := recv.Delete(k) 1243*4947cdc7SCole Faust if err != nil { 1244*4947cdc7SCole Faust return nil, nameErr(b, err) // dict is frozen 1245*4947cdc7SCole Faust } 1246*4947cdc7SCole Faust return Tuple{k, v}, nil 1247*4947cdc7SCole Faust} 1248*4947cdc7SCole Faust 1249*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·setdefault 1250*4947cdc7SCole Faustfunc dict_setdefault(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1251*4947cdc7SCole Faust var key, dflt Value = nil, None 1252*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &key, &dflt); err != nil { 1253*4947cdc7SCole Faust return nil, err 1254*4947cdc7SCole Faust } 1255*4947cdc7SCole Faust dict := b.Receiver().(*Dict) 1256*4947cdc7SCole Faust if v, ok, err := dict.Get(key); err != nil { 1257*4947cdc7SCole Faust return nil, nameErr(b, err) 1258*4947cdc7SCole Faust } else if ok { 1259*4947cdc7SCole Faust return v, nil 1260*4947cdc7SCole Faust } else if err := dict.SetKey(key, dflt); err != nil { 1261*4947cdc7SCole Faust return nil, nameErr(b, err) 1262*4947cdc7SCole Faust } else { 1263*4947cdc7SCole Faust return dflt, nil 1264*4947cdc7SCole Faust } 1265*4947cdc7SCole Faust} 1266*4947cdc7SCole Faust 1267*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·update 1268*4947cdc7SCole Faustfunc dict_update(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1269*4947cdc7SCole Faust if len(args) > 1 { 1270*4947cdc7SCole Faust return nil, fmt.Errorf("update: got %d arguments, want at most 1", len(args)) 1271*4947cdc7SCole Faust } 1272*4947cdc7SCole Faust if err := updateDict(b.Receiver().(*Dict), args, kwargs); err != nil { 1273*4947cdc7SCole Faust return nil, fmt.Errorf("update: %v", err) 1274*4947cdc7SCole Faust } 1275*4947cdc7SCole Faust return None, nil 1276*4947cdc7SCole Faust} 1277*4947cdc7SCole Faust 1278*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·update 1279*4947cdc7SCole Faustfunc dict_values(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1280*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1281*4947cdc7SCole Faust return nil, err 1282*4947cdc7SCole Faust } 1283*4947cdc7SCole Faust items := b.Receiver().(*Dict).Items() 1284*4947cdc7SCole Faust res := make([]Value, len(items)) 1285*4947cdc7SCole Faust for i, item := range items { 1286*4947cdc7SCole Faust res[i] = item[1] 1287*4947cdc7SCole Faust } 1288*4947cdc7SCole Faust return NewList(res), nil 1289*4947cdc7SCole Faust} 1290*4947cdc7SCole Faust 1291*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·append 1292*4947cdc7SCole Faustfunc list_append(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1293*4947cdc7SCole Faust var object Value 1294*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &object); err != nil { 1295*4947cdc7SCole Faust return nil, err 1296*4947cdc7SCole Faust } 1297*4947cdc7SCole Faust recv := b.Receiver().(*List) 1298*4947cdc7SCole Faust if err := recv.checkMutable("append to"); err != nil { 1299*4947cdc7SCole Faust return nil, nameErr(b, err) 1300*4947cdc7SCole Faust } 1301*4947cdc7SCole Faust recv.elems = append(recv.elems, object) 1302*4947cdc7SCole Faust return None, nil 1303*4947cdc7SCole Faust} 1304*4947cdc7SCole Faust 1305*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·clear 1306*4947cdc7SCole Faustfunc list_clear(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1307*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1308*4947cdc7SCole Faust return nil, err 1309*4947cdc7SCole Faust } 1310*4947cdc7SCole Faust if err := b.Receiver().(*List).Clear(); err != nil { 1311*4947cdc7SCole Faust return nil, nameErr(b, err) 1312*4947cdc7SCole Faust } 1313*4947cdc7SCole Faust return None, nil 1314*4947cdc7SCole Faust} 1315*4947cdc7SCole Faust 1316*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·extend 1317*4947cdc7SCole Faustfunc list_extend(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1318*4947cdc7SCole Faust recv := b.Receiver().(*List) 1319*4947cdc7SCole Faust var iterable Iterable 1320*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &iterable); err != nil { 1321*4947cdc7SCole Faust return nil, err 1322*4947cdc7SCole Faust } 1323*4947cdc7SCole Faust if err := recv.checkMutable("extend"); err != nil { 1324*4947cdc7SCole Faust return nil, nameErr(b, err) 1325*4947cdc7SCole Faust } 1326*4947cdc7SCole Faust listExtend(recv, iterable) 1327*4947cdc7SCole Faust return None, nil 1328*4947cdc7SCole Faust} 1329*4947cdc7SCole Faust 1330*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·index 1331*4947cdc7SCole Faustfunc list_index(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1332*4947cdc7SCole Faust var value, start_, end_ Value 1333*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &value, &start_, &end_); err != nil { 1334*4947cdc7SCole Faust return nil, err 1335*4947cdc7SCole Faust } 1336*4947cdc7SCole Faust 1337*4947cdc7SCole Faust recv := b.Receiver().(*List) 1338*4947cdc7SCole Faust start, end, err := indices(start_, end_, recv.Len()) 1339*4947cdc7SCole Faust if err != nil { 1340*4947cdc7SCole Faust return nil, nameErr(b, err) 1341*4947cdc7SCole Faust } 1342*4947cdc7SCole Faust 1343*4947cdc7SCole Faust for i := start; i < end; i++ { 1344*4947cdc7SCole Faust if eq, err := Equal(recv.elems[i], value); err != nil { 1345*4947cdc7SCole Faust return nil, nameErr(b, err) 1346*4947cdc7SCole Faust } else if eq { 1347*4947cdc7SCole Faust return MakeInt(i), nil 1348*4947cdc7SCole Faust } 1349*4947cdc7SCole Faust } 1350*4947cdc7SCole Faust return nil, nameErr(b, "value not in list") 1351*4947cdc7SCole Faust} 1352*4947cdc7SCole Faust 1353*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·insert 1354*4947cdc7SCole Faustfunc list_insert(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1355*4947cdc7SCole Faust recv := b.Receiver().(*List) 1356*4947cdc7SCole Faust var index int 1357*4947cdc7SCole Faust var object Value 1358*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 2, &index, &object); err != nil { 1359*4947cdc7SCole Faust return nil, err 1360*4947cdc7SCole Faust } 1361*4947cdc7SCole Faust if err := recv.checkMutable("insert into"); err != nil { 1362*4947cdc7SCole Faust return nil, nameErr(b, err) 1363*4947cdc7SCole Faust } 1364*4947cdc7SCole Faust 1365*4947cdc7SCole Faust if index < 0 { 1366*4947cdc7SCole Faust index += recv.Len() 1367*4947cdc7SCole Faust } 1368*4947cdc7SCole Faust 1369*4947cdc7SCole Faust if index >= recv.Len() { 1370*4947cdc7SCole Faust // end 1371*4947cdc7SCole Faust recv.elems = append(recv.elems, object) 1372*4947cdc7SCole Faust } else { 1373*4947cdc7SCole Faust if index < 0 { 1374*4947cdc7SCole Faust index = 0 // start 1375*4947cdc7SCole Faust } 1376*4947cdc7SCole Faust recv.elems = append(recv.elems, nil) 1377*4947cdc7SCole Faust copy(recv.elems[index+1:], recv.elems[index:]) // slide up one 1378*4947cdc7SCole Faust recv.elems[index] = object 1379*4947cdc7SCole Faust } 1380*4947cdc7SCole Faust return None, nil 1381*4947cdc7SCole Faust} 1382*4947cdc7SCole Faust 1383*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·remove 1384*4947cdc7SCole Faustfunc list_remove(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1385*4947cdc7SCole Faust recv := b.Receiver().(*List) 1386*4947cdc7SCole Faust var value Value 1387*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &value); err != nil { 1388*4947cdc7SCole Faust return nil, err 1389*4947cdc7SCole Faust } 1390*4947cdc7SCole Faust if err := recv.checkMutable("remove from"); err != nil { 1391*4947cdc7SCole Faust return nil, nameErr(b, err) 1392*4947cdc7SCole Faust } 1393*4947cdc7SCole Faust for i, elem := range recv.elems { 1394*4947cdc7SCole Faust if eq, err := Equal(elem, value); err != nil { 1395*4947cdc7SCole Faust return nil, fmt.Errorf("remove: %v", err) 1396*4947cdc7SCole Faust } else if eq { 1397*4947cdc7SCole Faust recv.elems = append(recv.elems[:i], recv.elems[i+1:]...) 1398*4947cdc7SCole Faust return None, nil 1399*4947cdc7SCole Faust } 1400*4947cdc7SCole Faust } 1401*4947cdc7SCole Faust return nil, fmt.Errorf("remove: element not found") 1402*4947cdc7SCole Faust} 1403*4947cdc7SCole Faust 1404*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·pop 1405*4947cdc7SCole Faustfunc list_pop(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1406*4947cdc7SCole Faust recv := b.Receiver() 1407*4947cdc7SCole Faust list := recv.(*List) 1408*4947cdc7SCole Faust n := list.Len() 1409*4947cdc7SCole Faust i := n - 1 1410*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &i); err != nil { 1411*4947cdc7SCole Faust return nil, err 1412*4947cdc7SCole Faust } 1413*4947cdc7SCole Faust origI := i 1414*4947cdc7SCole Faust if i < 0 { 1415*4947cdc7SCole Faust i += n 1416*4947cdc7SCole Faust } 1417*4947cdc7SCole Faust if i < 0 || i >= n { 1418*4947cdc7SCole Faust return nil, nameErr(b, outOfRange(origI, n, list)) 1419*4947cdc7SCole Faust } 1420*4947cdc7SCole Faust if err := list.checkMutable("pop from"); err != nil { 1421*4947cdc7SCole Faust return nil, nameErr(b, err) 1422*4947cdc7SCole Faust } 1423*4947cdc7SCole Faust res := list.elems[i] 1424*4947cdc7SCole Faust list.elems = append(list.elems[:i], list.elems[i+1:]...) 1425*4947cdc7SCole Faust return res, nil 1426*4947cdc7SCole Faust} 1427*4947cdc7SCole Faust 1428*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·capitalize 1429*4947cdc7SCole Faustfunc string_capitalize(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1430*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1431*4947cdc7SCole Faust return nil, err 1432*4947cdc7SCole Faust } 1433*4947cdc7SCole Faust s := string(b.Receiver().(String)) 1434*4947cdc7SCole Faust res := new(strings.Builder) 1435*4947cdc7SCole Faust res.Grow(len(s)) 1436*4947cdc7SCole Faust for i, r := range s { 1437*4947cdc7SCole Faust if i == 0 { 1438*4947cdc7SCole Faust r = unicode.ToTitle(r) 1439*4947cdc7SCole Faust } else { 1440*4947cdc7SCole Faust r = unicode.ToLower(r) 1441*4947cdc7SCole Faust } 1442*4947cdc7SCole Faust res.WriteRune(r) 1443*4947cdc7SCole Faust } 1444*4947cdc7SCole Faust return String(res.String()), nil 1445*4947cdc7SCole Faust} 1446*4947cdc7SCole Faust 1447*4947cdc7SCole Faust// string_iterable returns an unspecified iterable value whose iterator yields: 1448*4947cdc7SCole Faust// - elems: successive 1-byte substrings 1449*4947cdc7SCole Faust// - codepoints: successive substrings that encode a single Unicode code point. 1450*4947cdc7SCole Faust// - elem_ords: numeric values of successive bytes 1451*4947cdc7SCole Faust// - codepoint_ords: numeric values of successive Unicode code points 1452*4947cdc7SCole Faustfunc string_iterable(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1453*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1454*4947cdc7SCole Faust return nil, err 1455*4947cdc7SCole Faust } 1456*4947cdc7SCole Faust s := b.Receiver().(String) 1457*4947cdc7SCole Faust ords := b.Name()[len(b.Name())-2] == 'd' 1458*4947cdc7SCole Faust codepoints := b.Name()[0] == 'c' 1459*4947cdc7SCole Faust if codepoints { 1460*4947cdc7SCole Faust return stringCodepoints{s, ords}, nil 1461*4947cdc7SCole Faust } else { 1462*4947cdc7SCole Faust return stringElems{s, ords}, nil 1463*4947cdc7SCole Faust } 1464*4947cdc7SCole Faust} 1465*4947cdc7SCole Faust 1466*4947cdc7SCole Faust// bytes_elems returns an unspecified iterable value whose 1467*4947cdc7SCole Faust// iterator yields the int values of successive elements. 1468*4947cdc7SCole Faustfunc bytes_elems(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1469*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1470*4947cdc7SCole Faust return nil, err 1471*4947cdc7SCole Faust } 1472*4947cdc7SCole Faust return bytesIterable{b.Receiver().(Bytes)}, nil 1473*4947cdc7SCole Faust} 1474*4947cdc7SCole Faust 1475*4947cdc7SCole Faust// A bytesIterable is an iterable returned by bytes.elems(), 1476*4947cdc7SCole Faust// whose iterator yields a sequence of numeric bytes values. 1477*4947cdc7SCole Fausttype bytesIterable struct{ bytes Bytes } 1478*4947cdc7SCole Faust 1479*4947cdc7SCole Faustvar _ Iterable = (*bytesIterable)(nil) 1480*4947cdc7SCole Faust 1481*4947cdc7SCole Faustfunc (bi bytesIterable) String() string { return bi.bytes.String() + ".elems()" } 1482*4947cdc7SCole Faustfunc (bi bytesIterable) Type() string { return "bytes.elems" } 1483*4947cdc7SCole Faustfunc (bi bytesIterable) Freeze() {} // immutable 1484*4947cdc7SCole Faustfunc (bi bytesIterable) Truth() Bool { return True } 1485*4947cdc7SCole Faustfunc (bi bytesIterable) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: %s", bi.Type()) } 1486*4947cdc7SCole Faustfunc (bi bytesIterable) Iterate() Iterator { return &bytesIterator{bi.bytes} } 1487*4947cdc7SCole Faust 1488*4947cdc7SCole Fausttype bytesIterator struct{ bytes Bytes } 1489*4947cdc7SCole Faust 1490*4947cdc7SCole Faustfunc (it *bytesIterator) Next(p *Value) bool { 1491*4947cdc7SCole Faust if it.bytes == "" { 1492*4947cdc7SCole Faust return false 1493*4947cdc7SCole Faust } 1494*4947cdc7SCole Faust *p = MakeInt(int(it.bytes[0])) 1495*4947cdc7SCole Faust it.bytes = it.bytes[1:] 1496*4947cdc7SCole Faust return true 1497*4947cdc7SCole Faust} 1498*4947cdc7SCole Faust 1499*4947cdc7SCole Faustfunc (*bytesIterator) Done() {} 1500*4947cdc7SCole Faust 1501*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·count 1502*4947cdc7SCole Faustfunc string_count(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1503*4947cdc7SCole Faust var sub string 1504*4947cdc7SCole Faust var start_, end_ Value 1505*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &sub, &start_, &end_); err != nil { 1506*4947cdc7SCole Faust return nil, err 1507*4947cdc7SCole Faust } 1508*4947cdc7SCole Faust 1509*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 1510*4947cdc7SCole Faust start, end, err := indices(start_, end_, len(recv)) 1511*4947cdc7SCole Faust if err != nil { 1512*4947cdc7SCole Faust return nil, nameErr(b, err) 1513*4947cdc7SCole Faust } 1514*4947cdc7SCole Faust 1515*4947cdc7SCole Faust var slice string 1516*4947cdc7SCole Faust if start < end { 1517*4947cdc7SCole Faust slice = recv[start:end] 1518*4947cdc7SCole Faust } 1519*4947cdc7SCole Faust return MakeInt(strings.Count(slice, sub)), nil 1520*4947cdc7SCole Faust} 1521*4947cdc7SCole Faust 1522*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isalnum 1523*4947cdc7SCole Faustfunc string_isalnum(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1524*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1525*4947cdc7SCole Faust return nil, err 1526*4947cdc7SCole Faust } 1527*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 1528*4947cdc7SCole Faust for _, r := range recv { 1529*4947cdc7SCole Faust if !unicode.IsLetter(r) && !unicode.IsDigit(r) { 1530*4947cdc7SCole Faust return False, nil 1531*4947cdc7SCole Faust } 1532*4947cdc7SCole Faust } 1533*4947cdc7SCole Faust return Bool(recv != ""), nil 1534*4947cdc7SCole Faust} 1535*4947cdc7SCole Faust 1536*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isalpha 1537*4947cdc7SCole Faustfunc string_isalpha(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1538*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1539*4947cdc7SCole Faust return nil, err 1540*4947cdc7SCole Faust } 1541*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 1542*4947cdc7SCole Faust for _, r := range recv { 1543*4947cdc7SCole Faust if !unicode.IsLetter(r) { 1544*4947cdc7SCole Faust return False, nil 1545*4947cdc7SCole Faust } 1546*4947cdc7SCole Faust } 1547*4947cdc7SCole Faust return Bool(recv != ""), nil 1548*4947cdc7SCole Faust} 1549*4947cdc7SCole Faust 1550*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isdigit 1551*4947cdc7SCole Faustfunc string_isdigit(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1552*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1553*4947cdc7SCole Faust return nil, err 1554*4947cdc7SCole Faust } 1555*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 1556*4947cdc7SCole Faust for _, r := range recv { 1557*4947cdc7SCole Faust if !unicode.IsDigit(r) { 1558*4947cdc7SCole Faust return False, nil 1559*4947cdc7SCole Faust } 1560*4947cdc7SCole Faust } 1561*4947cdc7SCole Faust return Bool(recv != ""), nil 1562*4947cdc7SCole Faust} 1563*4947cdc7SCole Faust 1564*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·islower 1565*4947cdc7SCole Faustfunc string_islower(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1566*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1567*4947cdc7SCole Faust return nil, err 1568*4947cdc7SCole Faust } 1569*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 1570*4947cdc7SCole Faust return Bool(isCasedString(recv) && recv == strings.ToLower(recv)), nil 1571*4947cdc7SCole Faust} 1572*4947cdc7SCole Faust 1573*4947cdc7SCole Faust// isCasedString reports whether its argument contains any cased code points. 1574*4947cdc7SCole Faustfunc isCasedString(s string) bool { 1575*4947cdc7SCole Faust for _, r := range s { 1576*4947cdc7SCole Faust if isCasedRune(r) { 1577*4947cdc7SCole Faust return true 1578*4947cdc7SCole Faust } 1579*4947cdc7SCole Faust } 1580*4947cdc7SCole Faust return false 1581*4947cdc7SCole Faust} 1582*4947cdc7SCole Faust 1583*4947cdc7SCole Faustfunc isCasedRune(r rune) bool { 1584*4947cdc7SCole Faust // It's unclear what the correct behavior is for a rune such as 'ffi', 1585*4947cdc7SCole Faust // a lowercase letter with no upper or title case and no SimpleFold. 1586*4947cdc7SCole Faust return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || unicode.SimpleFold(r) != r 1587*4947cdc7SCole Faust} 1588*4947cdc7SCole Faust 1589*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isspace 1590*4947cdc7SCole Faustfunc string_isspace(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1591*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1592*4947cdc7SCole Faust return nil, err 1593*4947cdc7SCole Faust } 1594*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 1595*4947cdc7SCole Faust for _, r := range recv { 1596*4947cdc7SCole Faust if !unicode.IsSpace(r) { 1597*4947cdc7SCole Faust return False, nil 1598*4947cdc7SCole Faust } 1599*4947cdc7SCole Faust } 1600*4947cdc7SCole Faust return Bool(recv != ""), nil 1601*4947cdc7SCole Faust} 1602*4947cdc7SCole Faust 1603*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·istitle 1604*4947cdc7SCole Faustfunc string_istitle(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1605*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1606*4947cdc7SCole Faust return nil, err 1607*4947cdc7SCole Faust } 1608*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 1609*4947cdc7SCole Faust 1610*4947cdc7SCole Faust // Python semantics differ from x==strings.{To,}Title(x) in Go: 1611*4947cdc7SCole Faust // "uppercase characters may only follow uncased characters and 1612*4947cdc7SCole Faust // lowercase characters only cased ones." 1613*4947cdc7SCole Faust var cased, prevCased bool 1614*4947cdc7SCole Faust for _, r := range recv { 1615*4947cdc7SCole Faust if 'A' <= r && r <= 'Z' || unicode.IsTitle(r) { // e.g. "Dž" 1616*4947cdc7SCole Faust if prevCased { 1617*4947cdc7SCole Faust return False, nil 1618*4947cdc7SCole Faust } 1619*4947cdc7SCole Faust prevCased = true 1620*4947cdc7SCole Faust cased = true 1621*4947cdc7SCole Faust } else if unicode.IsLower(r) { 1622*4947cdc7SCole Faust if !prevCased { 1623*4947cdc7SCole Faust return False, nil 1624*4947cdc7SCole Faust } 1625*4947cdc7SCole Faust prevCased = true 1626*4947cdc7SCole Faust cased = true 1627*4947cdc7SCole Faust } else if unicode.IsUpper(r) { 1628*4947cdc7SCole Faust return False, nil 1629*4947cdc7SCole Faust } else { 1630*4947cdc7SCole Faust prevCased = false 1631*4947cdc7SCole Faust } 1632*4947cdc7SCole Faust } 1633*4947cdc7SCole Faust return Bool(cased), nil 1634*4947cdc7SCole Faust} 1635*4947cdc7SCole Faust 1636*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isupper 1637*4947cdc7SCole Faustfunc string_isupper(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1638*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1639*4947cdc7SCole Faust return nil, err 1640*4947cdc7SCole Faust } 1641*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 1642*4947cdc7SCole Faust return Bool(isCasedString(recv) && recv == strings.ToUpper(recv)), nil 1643*4947cdc7SCole Faust} 1644*4947cdc7SCole Faust 1645*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·find 1646*4947cdc7SCole Faustfunc string_find(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1647*4947cdc7SCole Faust return string_find_impl(b, args, kwargs, true, false) 1648*4947cdc7SCole Faust} 1649*4947cdc7SCole Faust 1650*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·format 1651*4947cdc7SCole Faustfunc string_format(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1652*4947cdc7SCole Faust format := string(b.Receiver().(String)) 1653*4947cdc7SCole Faust var auto, manual bool // kinds of positional indexing used 1654*4947cdc7SCole Faust buf := new(strings.Builder) 1655*4947cdc7SCole Faust index := 0 1656*4947cdc7SCole Faust for { 1657*4947cdc7SCole Faust literal := format 1658*4947cdc7SCole Faust i := strings.IndexByte(format, '{') 1659*4947cdc7SCole Faust if i >= 0 { 1660*4947cdc7SCole Faust literal = format[:i] 1661*4947cdc7SCole Faust } 1662*4947cdc7SCole Faust 1663*4947cdc7SCole Faust // Replace "}}" with "}" in non-field portion, rejecting a lone '}'. 1664*4947cdc7SCole Faust for { 1665*4947cdc7SCole Faust j := strings.IndexByte(literal, '}') 1666*4947cdc7SCole Faust if j < 0 { 1667*4947cdc7SCole Faust buf.WriteString(literal) 1668*4947cdc7SCole Faust break 1669*4947cdc7SCole Faust } 1670*4947cdc7SCole Faust if len(literal) == j+1 || literal[j+1] != '}' { 1671*4947cdc7SCole Faust return nil, fmt.Errorf("format: single '}' in format") 1672*4947cdc7SCole Faust } 1673*4947cdc7SCole Faust buf.WriteString(literal[:j+1]) 1674*4947cdc7SCole Faust literal = literal[j+2:] 1675*4947cdc7SCole Faust } 1676*4947cdc7SCole Faust 1677*4947cdc7SCole Faust if i < 0 { 1678*4947cdc7SCole Faust break // end of format string 1679*4947cdc7SCole Faust } 1680*4947cdc7SCole Faust 1681*4947cdc7SCole Faust if i+1 < len(format) && format[i+1] == '{' { 1682*4947cdc7SCole Faust // "{{" means a literal '{' 1683*4947cdc7SCole Faust buf.WriteByte('{') 1684*4947cdc7SCole Faust format = format[i+2:] 1685*4947cdc7SCole Faust continue 1686*4947cdc7SCole Faust } 1687*4947cdc7SCole Faust 1688*4947cdc7SCole Faust format = format[i+1:] 1689*4947cdc7SCole Faust i = strings.IndexByte(format, '}') 1690*4947cdc7SCole Faust if i < 0 { 1691*4947cdc7SCole Faust return nil, fmt.Errorf("format: unmatched '{' in format") 1692*4947cdc7SCole Faust } 1693*4947cdc7SCole Faust 1694*4947cdc7SCole Faust var arg Value 1695*4947cdc7SCole Faust conv := "s" 1696*4947cdc7SCole Faust var spec string 1697*4947cdc7SCole Faust 1698*4947cdc7SCole Faust field := format[:i] 1699*4947cdc7SCole Faust format = format[i+1:] 1700*4947cdc7SCole Faust 1701*4947cdc7SCole Faust var name string 1702*4947cdc7SCole Faust if i := strings.IndexByte(field, '!'); i < 0 { 1703*4947cdc7SCole Faust // "name" or "name:spec" 1704*4947cdc7SCole Faust if i := strings.IndexByte(field, ':'); i < 0 { 1705*4947cdc7SCole Faust name = field 1706*4947cdc7SCole Faust } else { 1707*4947cdc7SCole Faust name = field[:i] 1708*4947cdc7SCole Faust spec = field[i+1:] 1709*4947cdc7SCole Faust } 1710*4947cdc7SCole Faust } else { 1711*4947cdc7SCole Faust // "name!conv" or "name!conv:spec" 1712*4947cdc7SCole Faust name = field[:i] 1713*4947cdc7SCole Faust field = field[i+1:] 1714*4947cdc7SCole Faust // "conv" or "conv:spec" 1715*4947cdc7SCole Faust if i := strings.IndexByte(field, ':'); i < 0 { 1716*4947cdc7SCole Faust conv = field 1717*4947cdc7SCole Faust } else { 1718*4947cdc7SCole Faust conv = field[:i] 1719*4947cdc7SCole Faust spec = field[i+1:] 1720*4947cdc7SCole Faust } 1721*4947cdc7SCole Faust } 1722*4947cdc7SCole Faust 1723*4947cdc7SCole Faust if name == "" { 1724*4947cdc7SCole Faust // "{}": automatic indexing 1725*4947cdc7SCole Faust if manual { 1726*4947cdc7SCole Faust return nil, fmt.Errorf("format: cannot switch from manual field specification to automatic field numbering") 1727*4947cdc7SCole Faust } 1728*4947cdc7SCole Faust auto = true 1729*4947cdc7SCole Faust if index >= len(args) { 1730*4947cdc7SCole Faust return nil, fmt.Errorf("format: tuple index out of range") 1731*4947cdc7SCole Faust } 1732*4947cdc7SCole Faust arg = args[index] 1733*4947cdc7SCole Faust index++ 1734*4947cdc7SCole Faust } else if num, ok := decimal(name); ok { 1735*4947cdc7SCole Faust // positional argument 1736*4947cdc7SCole Faust if auto { 1737*4947cdc7SCole Faust return nil, fmt.Errorf("format: cannot switch from automatic field numbering to manual field specification") 1738*4947cdc7SCole Faust } 1739*4947cdc7SCole Faust manual = true 1740*4947cdc7SCole Faust if num >= len(args) { 1741*4947cdc7SCole Faust return nil, fmt.Errorf("format: tuple index out of range") 1742*4947cdc7SCole Faust } else { 1743*4947cdc7SCole Faust arg = args[num] 1744*4947cdc7SCole Faust } 1745*4947cdc7SCole Faust } else { 1746*4947cdc7SCole Faust // keyword argument 1747*4947cdc7SCole Faust for _, kv := range kwargs { 1748*4947cdc7SCole Faust if string(kv[0].(String)) == name { 1749*4947cdc7SCole Faust arg = kv[1] 1750*4947cdc7SCole Faust break 1751*4947cdc7SCole Faust } 1752*4947cdc7SCole Faust } 1753*4947cdc7SCole Faust if arg == nil { 1754*4947cdc7SCole Faust // Starlark does not support Python's x.y or a[i] syntaxes, 1755*4947cdc7SCole Faust // or nested use of {...}. 1756*4947cdc7SCole Faust if strings.Contains(name, ".") { 1757*4947cdc7SCole Faust return nil, fmt.Errorf("format: attribute syntax x.y is not supported in replacement fields: %s", name) 1758*4947cdc7SCole Faust } 1759*4947cdc7SCole Faust if strings.Contains(name, "[") { 1760*4947cdc7SCole Faust return nil, fmt.Errorf("format: element syntax a[i] is not supported in replacement fields: %s", name) 1761*4947cdc7SCole Faust } 1762*4947cdc7SCole Faust if strings.Contains(name, "{") { 1763*4947cdc7SCole Faust return nil, fmt.Errorf("format: nested replacement fields not supported") 1764*4947cdc7SCole Faust } 1765*4947cdc7SCole Faust return nil, fmt.Errorf("format: keyword %s not found", name) 1766*4947cdc7SCole Faust } 1767*4947cdc7SCole Faust } 1768*4947cdc7SCole Faust 1769*4947cdc7SCole Faust if spec != "" { 1770*4947cdc7SCole Faust // Starlark does not support Python's format_spec features. 1771*4947cdc7SCole Faust return nil, fmt.Errorf("format spec features not supported in replacement fields: %s", spec) 1772*4947cdc7SCole Faust } 1773*4947cdc7SCole Faust 1774*4947cdc7SCole Faust switch conv { 1775*4947cdc7SCole Faust case "s": 1776*4947cdc7SCole Faust if str, ok := AsString(arg); ok { 1777*4947cdc7SCole Faust buf.WriteString(str) 1778*4947cdc7SCole Faust } else { 1779*4947cdc7SCole Faust writeValue(buf, arg, nil) 1780*4947cdc7SCole Faust } 1781*4947cdc7SCole Faust case "r": 1782*4947cdc7SCole Faust writeValue(buf, arg, nil) 1783*4947cdc7SCole Faust default: 1784*4947cdc7SCole Faust return nil, fmt.Errorf("format: unknown conversion %q", conv) 1785*4947cdc7SCole Faust } 1786*4947cdc7SCole Faust } 1787*4947cdc7SCole Faust return String(buf.String()), nil 1788*4947cdc7SCole Faust} 1789*4947cdc7SCole Faust 1790*4947cdc7SCole Faust// decimal interprets s as a sequence of decimal digits. 1791*4947cdc7SCole Faustfunc decimal(s string) (x int, ok bool) { 1792*4947cdc7SCole Faust n := len(s) 1793*4947cdc7SCole Faust for i := 0; i < n; i++ { 1794*4947cdc7SCole Faust digit := s[i] - '0' 1795*4947cdc7SCole Faust if digit > 9 { 1796*4947cdc7SCole Faust return 0, false 1797*4947cdc7SCole Faust } 1798*4947cdc7SCole Faust x = x*10 + int(digit) 1799*4947cdc7SCole Faust if x < 0 { 1800*4947cdc7SCole Faust return 0, false // underflow 1801*4947cdc7SCole Faust } 1802*4947cdc7SCole Faust } 1803*4947cdc7SCole Faust return x, true 1804*4947cdc7SCole Faust} 1805*4947cdc7SCole Faust 1806*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·index 1807*4947cdc7SCole Faustfunc string_index(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1808*4947cdc7SCole Faust return string_find_impl(b, args, kwargs, false, false) 1809*4947cdc7SCole Faust} 1810*4947cdc7SCole Faust 1811*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·join 1812*4947cdc7SCole Faustfunc string_join(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1813*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 1814*4947cdc7SCole Faust var iterable Iterable 1815*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &iterable); err != nil { 1816*4947cdc7SCole Faust return nil, err 1817*4947cdc7SCole Faust } 1818*4947cdc7SCole Faust iter := iterable.Iterate() 1819*4947cdc7SCole Faust defer iter.Done() 1820*4947cdc7SCole Faust buf := new(strings.Builder) 1821*4947cdc7SCole Faust var x Value 1822*4947cdc7SCole Faust for i := 0; iter.Next(&x); i++ { 1823*4947cdc7SCole Faust if i > 0 { 1824*4947cdc7SCole Faust buf.WriteString(recv) 1825*4947cdc7SCole Faust } 1826*4947cdc7SCole Faust s, ok := AsString(x) 1827*4947cdc7SCole Faust if !ok { 1828*4947cdc7SCole Faust return nil, fmt.Errorf("join: in list, want string, got %s", x.Type()) 1829*4947cdc7SCole Faust } 1830*4947cdc7SCole Faust buf.WriteString(s) 1831*4947cdc7SCole Faust } 1832*4947cdc7SCole Faust return String(buf.String()), nil 1833*4947cdc7SCole Faust} 1834*4947cdc7SCole Faust 1835*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·lower 1836*4947cdc7SCole Faustfunc string_lower(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1837*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1838*4947cdc7SCole Faust return nil, err 1839*4947cdc7SCole Faust } 1840*4947cdc7SCole Faust return String(strings.ToLower(string(b.Receiver().(String)))), nil 1841*4947cdc7SCole Faust} 1842*4947cdc7SCole Faust 1843*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·partition 1844*4947cdc7SCole Faustfunc string_partition(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1845*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 1846*4947cdc7SCole Faust var sep string 1847*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &sep); err != nil { 1848*4947cdc7SCole Faust return nil, err 1849*4947cdc7SCole Faust } 1850*4947cdc7SCole Faust if sep == "" { 1851*4947cdc7SCole Faust return nil, nameErr(b, "empty separator") 1852*4947cdc7SCole Faust } 1853*4947cdc7SCole Faust var i int 1854*4947cdc7SCole Faust if b.Name()[0] == 'p' { 1855*4947cdc7SCole Faust i = strings.Index(recv, sep) // partition 1856*4947cdc7SCole Faust } else { 1857*4947cdc7SCole Faust i = strings.LastIndex(recv, sep) // rpartition 1858*4947cdc7SCole Faust } 1859*4947cdc7SCole Faust tuple := make(Tuple, 0, 3) 1860*4947cdc7SCole Faust if i < 0 { 1861*4947cdc7SCole Faust if b.Name()[0] == 'p' { 1862*4947cdc7SCole Faust tuple = append(tuple, String(recv), String(""), String("")) 1863*4947cdc7SCole Faust } else { 1864*4947cdc7SCole Faust tuple = append(tuple, String(""), String(""), String(recv)) 1865*4947cdc7SCole Faust } 1866*4947cdc7SCole Faust } else { 1867*4947cdc7SCole Faust tuple = append(tuple, String(recv[:i]), String(sep), String(recv[i+len(sep):])) 1868*4947cdc7SCole Faust } 1869*4947cdc7SCole Faust return tuple, nil 1870*4947cdc7SCole Faust} 1871*4947cdc7SCole Faust 1872*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·replace 1873*4947cdc7SCole Faustfunc string_replace(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1874*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 1875*4947cdc7SCole Faust var old, new string 1876*4947cdc7SCole Faust count := -1 1877*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 2, &old, &new, &count); err != nil { 1878*4947cdc7SCole Faust return nil, err 1879*4947cdc7SCole Faust } 1880*4947cdc7SCole Faust return String(strings.Replace(recv, old, new, count)), nil 1881*4947cdc7SCole Faust} 1882*4947cdc7SCole Faust 1883*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rfind 1884*4947cdc7SCole Faustfunc string_rfind(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1885*4947cdc7SCole Faust return string_find_impl(b, args, kwargs, true, true) 1886*4947cdc7SCole Faust} 1887*4947cdc7SCole Faust 1888*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rindex 1889*4947cdc7SCole Faustfunc string_rindex(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1890*4947cdc7SCole Faust return string_find_impl(b, args, kwargs, false, true) 1891*4947cdc7SCole Faust} 1892*4947cdc7SCole Faust 1893*4947cdc7SCole Faust// https://github.com/google/starlark-go/starlark/blob/master/doc/spec.md#string·startswith 1894*4947cdc7SCole Faust// https://github.com/google/starlark-go/starlark/blob/master/doc/spec.md#string·endswith 1895*4947cdc7SCole Faustfunc string_startswith(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1896*4947cdc7SCole Faust var x Value 1897*4947cdc7SCole Faust var start, end Value = None, None 1898*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &x, &start, &end); err != nil { 1899*4947cdc7SCole Faust return nil, err 1900*4947cdc7SCole Faust } 1901*4947cdc7SCole Faust 1902*4947cdc7SCole Faust // compute effective substring. 1903*4947cdc7SCole Faust s := string(b.Receiver().(String)) 1904*4947cdc7SCole Faust if start, end, err := indices(start, end, len(s)); err != nil { 1905*4947cdc7SCole Faust return nil, nameErr(b, err) 1906*4947cdc7SCole Faust } else { 1907*4947cdc7SCole Faust if end < start { 1908*4947cdc7SCole Faust end = start // => empty result 1909*4947cdc7SCole Faust } 1910*4947cdc7SCole Faust s = s[start:end] 1911*4947cdc7SCole Faust } 1912*4947cdc7SCole Faust 1913*4947cdc7SCole Faust f := strings.HasPrefix 1914*4947cdc7SCole Faust if b.Name()[0] == 'e' { // endswith 1915*4947cdc7SCole Faust f = strings.HasSuffix 1916*4947cdc7SCole Faust } 1917*4947cdc7SCole Faust 1918*4947cdc7SCole Faust switch x := x.(type) { 1919*4947cdc7SCole Faust case Tuple: 1920*4947cdc7SCole Faust for i, x := range x { 1921*4947cdc7SCole Faust prefix, ok := AsString(x) 1922*4947cdc7SCole Faust if !ok { 1923*4947cdc7SCole Faust return nil, fmt.Errorf("%s: want string, got %s, for element %d", 1924*4947cdc7SCole Faust b.Name(), x.Type(), i) 1925*4947cdc7SCole Faust } 1926*4947cdc7SCole Faust if f(s, prefix) { 1927*4947cdc7SCole Faust return True, nil 1928*4947cdc7SCole Faust } 1929*4947cdc7SCole Faust } 1930*4947cdc7SCole Faust return False, nil 1931*4947cdc7SCole Faust case String: 1932*4947cdc7SCole Faust return Bool(f(s, string(x))), nil 1933*4947cdc7SCole Faust } 1934*4947cdc7SCole Faust return nil, fmt.Errorf("%s: got %s, want string or tuple of string", b.Name(), x.Type()) 1935*4947cdc7SCole Faust} 1936*4947cdc7SCole Faust 1937*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·strip 1938*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·lstrip 1939*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rstrip 1940*4947cdc7SCole Faustfunc string_strip(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1941*4947cdc7SCole Faust var chars string 1942*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &chars); err != nil { 1943*4947cdc7SCole Faust return nil, err 1944*4947cdc7SCole Faust } 1945*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 1946*4947cdc7SCole Faust var s string 1947*4947cdc7SCole Faust switch b.Name()[0] { 1948*4947cdc7SCole Faust case 's': // strip 1949*4947cdc7SCole Faust if chars != "" { 1950*4947cdc7SCole Faust s = strings.Trim(recv, chars) 1951*4947cdc7SCole Faust } else { 1952*4947cdc7SCole Faust s = strings.TrimSpace(recv) 1953*4947cdc7SCole Faust } 1954*4947cdc7SCole Faust case 'l': // lstrip 1955*4947cdc7SCole Faust if chars != "" { 1956*4947cdc7SCole Faust s = strings.TrimLeft(recv, chars) 1957*4947cdc7SCole Faust } else { 1958*4947cdc7SCole Faust s = strings.TrimLeftFunc(recv, unicode.IsSpace) 1959*4947cdc7SCole Faust } 1960*4947cdc7SCole Faust case 'r': // rstrip 1961*4947cdc7SCole Faust if chars != "" { 1962*4947cdc7SCole Faust s = strings.TrimRight(recv, chars) 1963*4947cdc7SCole Faust } else { 1964*4947cdc7SCole Faust s = strings.TrimRightFunc(recv, unicode.IsSpace) 1965*4947cdc7SCole Faust } 1966*4947cdc7SCole Faust } 1967*4947cdc7SCole Faust return String(s), nil 1968*4947cdc7SCole Faust} 1969*4947cdc7SCole Faust 1970*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·title 1971*4947cdc7SCole Faustfunc string_title(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1972*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1973*4947cdc7SCole Faust return nil, err 1974*4947cdc7SCole Faust } 1975*4947cdc7SCole Faust 1976*4947cdc7SCole Faust s := string(b.Receiver().(String)) 1977*4947cdc7SCole Faust 1978*4947cdc7SCole Faust // Python semantics differ from x==strings.{To,}Title(x) in Go: 1979*4947cdc7SCole Faust // "uppercase characters may only follow uncased characters and 1980*4947cdc7SCole Faust // lowercase characters only cased ones." 1981*4947cdc7SCole Faust buf := new(strings.Builder) 1982*4947cdc7SCole Faust buf.Grow(len(s)) 1983*4947cdc7SCole Faust var prevCased bool 1984*4947cdc7SCole Faust for _, r := range s { 1985*4947cdc7SCole Faust if prevCased { 1986*4947cdc7SCole Faust r = unicode.ToLower(r) 1987*4947cdc7SCole Faust } else { 1988*4947cdc7SCole Faust r = unicode.ToTitle(r) 1989*4947cdc7SCole Faust } 1990*4947cdc7SCole Faust prevCased = isCasedRune(r) 1991*4947cdc7SCole Faust buf.WriteRune(r) 1992*4947cdc7SCole Faust } 1993*4947cdc7SCole Faust return String(buf.String()), nil 1994*4947cdc7SCole Faust} 1995*4947cdc7SCole Faust 1996*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·upper 1997*4947cdc7SCole Faustfunc string_upper(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 1998*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { 1999*4947cdc7SCole Faust return nil, err 2000*4947cdc7SCole Faust } 2001*4947cdc7SCole Faust return String(strings.ToUpper(string(b.Receiver().(String)))), nil 2002*4947cdc7SCole Faust} 2003*4947cdc7SCole Faust 2004*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·split 2005*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rsplit 2006*4947cdc7SCole Faustfunc string_split(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 2007*4947cdc7SCole Faust recv := string(b.Receiver().(String)) 2008*4947cdc7SCole Faust var sep_ Value 2009*4947cdc7SCole Faust maxsplit := -1 2010*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &sep_, &maxsplit); err != nil { 2011*4947cdc7SCole Faust return nil, err 2012*4947cdc7SCole Faust } 2013*4947cdc7SCole Faust 2014*4947cdc7SCole Faust var res []string 2015*4947cdc7SCole Faust 2016*4947cdc7SCole Faust if sep_ == nil || sep_ == None { 2017*4947cdc7SCole Faust // special case: split on whitespace 2018*4947cdc7SCole Faust if maxsplit < 0 { 2019*4947cdc7SCole Faust res = strings.Fields(recv) 2020*4947cdc7SCole Faust } else if b.Name() == "split" { 2021*4947cdc7SCole Faust res = splitspace(recv, maxsplit) 2022*4947cdc7SCole Faust } else { // rsplit 2023*4947cdc7SCole Faust res = rsplitspace(recv, maxsplit) 2024*4947cdc7SCole Faust } 2025*4947cdc7SCole Faust 2026*4947cdc7SCole Faust } else if sep, ok := AsString(sep_); ok { 2027*4947cdc7SCole Faust if sep == "" { 2028*4947cdc7SCole Faust return nil, fmt.Errorf("split: empty separator") 2029*4947cdc7SCole Faust } 2030*4947cdc7SCole Faust // usual case: split on non-empty separator 2031*4947cdc7SCole Faust if maxsplit < 0 { 2032*4947cdc7SCole Faust res = strings.Split(recv, sep) 2033*4947cdc7SCole Faust } else if b.Name() == "split" { 2034*4947cdc7SCole Faust res = strings.SplitN(recv, sep, maxsplit+1) 2035*4947cdc7SCole Faust } else { // rsplit 2036*4947cdc7SCole Faust res = strings.Split(recv, sep) 2037*4947cdc7SCole Faust if excess := len(res) - maxsplit; excess > 0 { 2038*4947cdc7SCole Faust res[0] = strings.Join(res[:excess], sep) 2039*4947cdc7SCole Faust res = append(res[:1], res[excess:]...) 2040*4947cdc7SCole Faust } 2041*4947cdc7SCole Faust } 2042*4947cdc7SCole Faust 2043*4947cdc7SCole Faust } else { 2044*4947cdc7SCole Faust return nil, fmt.Errorf("split: got %s for separator, want string", sep_.Type()) 2045*4947cdc7SCole Faust } 2046*4947cdc7SCole Faust 2047*4947cdc7SCole Faust list := make([]Value, len(res)) 2048*4947cdc7SCole Faust for i, x := range res { 2049*4947cdc7SCole Faust list[i] = String(x) 2050*4947cdc7SCole Faust } 2051*4947cdc7SCole Faust return NewList(list), nil 2052*4947cdc7SCole Faust} 2053*4947cdc7SCole Faust 2054*4947cdc7SCole Faust// Precondition: max >= 0. 2055*4947cdc7SCole Faustfunc rsplitspace(s string, max int) []string { 2056*4947cdc7SCole Faust res := make([]string, 0, max+1) 2057*4947cdc7SCole Faust end := -1 // index of field end, or -1 in a region of spaces. 2058*4947cdc7SCole Faust for i := len(s); i > 0; { 2059*4947cdc7SCole Faust r, sz := utf8.DecodeLastRuneInString(s[:i]) 2060*4947cdc7SCole Faust if unicode.IsSpace(r) { 2061*4947cdc7SCole Faust if end >= 0 { 2062*4947cdc7SCole Faust if len(res) == max { 2063*4947cdc7SCole Faust break // let this field run to the start 2064*4947cdc7SCole Faust } 2065*4947cdc7SCole Faust res = append(res, s[i:end]) 2066*4947cdc7SCole Faust end = -1 2067*4947cdc7SCole Faust } 2068*4947cdc7SCole Faust } else if end < 0 { 2069*4947cdc7SCole Faust end = i 2070*4947cdc7SCole Faust } 2071*4947cdc7SCole Faust i -= sz 2072*4947cdc7SCole Faust } 2073*4947cdc7SCole Faust if end >= 0 { 2074*4947cdc7SCole Faust res = append(res, s[:end]) 2075*4947cdc7SCole Faust } 2076*4947cdc7SCole Faust 2077*4947cdc7SCole Faust resLen := len(res) 2078*4947cdc7SCole Faust for i := 0; i < resLen/2; i++ { 2079*4947cdc7SCole Faust res[i], res[resLen-1-i] = res[resLen-1-i], res[i] 2080*4947cdc7SCole Faust } 2081*4947cdc7SCole Faust 2082*4947cdc7SCole Faust return res 2083*4947cdc7SCole Faust} 2084*4947cdc7SCole Faust 2085*4947cdc7SCole Faust// Precondition: max >= 0. 2086*4947cdc7SCole Faustfunc splitspace(s string, max int) []string { 2087*4947cdc7SCole Faust var res []string 2088*4947cdc7SCole Faust start := -1 // index of field start, or -1 in a region of spaces 2089*4947cdc7SCole Faust for i, r := range s { 2090*4947cdc7SCole Faust if unicode.IsSpace(r) { 2091*4947cdc7SCole Faust if start >= 0 { 2092*4947cdc7SCole Faust if len(res) == max { 2093*4947cdc7SCole Faust break // let this field run to the end 2094*4947cdc7SCole Faust } 2095*4947cdc7SCole Faust res = append(res, s[start:i]) 2096*4947cdc7SCole Faust start = -1 2097*4947cdc7SCole Faust } 2098*4947cdc7SCole Faust } else if start == -1 { 2099*4947cdc7SCole Faust start = i 2100*4947cdc7SCole Faust } 2101*4947cdc7SCole Faust } 2102*4947cdc7SCole Faust if start >= 0 { 2103*4947cdc7SCole Faust res = append(res, s[start:]) 2104*4947cdc7SCole Faust } 2105*4947cdc7SCole Faust return res 2106*4947cdc7SCole Faust} 2107*4947cdc7SCole Faust 2108*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·splitlines 2109*4947cdc7SCole Faustfunc string_splitlines(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 2110*4947cdc7SCole Faust var keepends bool 2111*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &keepends); err != nil { 2112*4947cdc7SCole Faust return nil, err 2113*4947cdc7SCole Faust } 2114*4947cdc7SCole Faust var lines []string 2115*4947cdc7SCole Faust if s := string(b.Receiver().(String)); s != "" { 2116*4947cdc7SCole Faust // TODO(adonovan): handle CRLF correctly. 2117*4947cdc7SCole Faust if keepends { 2118*4947cdc7SCole Faust lines = strings.SplitAfter(s, "\n") 2119*4947cdc7SCole Faust } else { 2120*4947cdc7SCole Faust lines = strings.Split(s, "\n") 2121*4947cdc7SCole Faust } 2122*4947cdc7SCole Faust if strings.HasSuffix(s, "\n") { 2123*4947cdc7SCole Faust lines = lines[:len(lines)-1] 2124*4947cdc7SCole Faust } 2125*4947cdc7SCole Faust } 2126*4947cdc7SCole Faust list := make([]Value, len(lines)) 2127*4947cdc7SCole Faust for i, x := range lines { 2128*4947cdc7SCole Faust list[i] = String(x) 2129*4947cdc7SCole Faust } 2130*4947cdc7SCole Faust return NewList(list), nil 2131*4947cdc7SCole Faust} 2132*4947cdc7SCole Faust 2133*4947cdc7SCole Faust// https://github.com/google/starlark-go/blob/master/doc/spec.md#set·union. 2134*4947cdc7SCole Faustfunc set_union(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { 2135*4947cdc7SCole Faust var iterable Iterable 2136*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &iterable); err != nil { 2137*4947cdc7SCole Faust return nil, err 2138*4947cdc7SCole Faust } 2139*4947cdc7SCole Faust iter := iterable.Iterate() 2140*4947cdc7SCole Faust defer iter.Done() 2141*4947cdc7SCole Faust union, err := b.Receiver().(*Set).Union(iter) 2142*4947cdc7SCole Faust if err != nil { 2143*4947cdc7SCole Faust return nil, nameErr(b, err) 2144*4947cdc7SCole Faust } 2145*4947cdc7SCole Faust return union, nil 2146*4947cdc7SCole Faust} 2147*4947cdc7SCole Faust 2148*4947cdc7SCole Faust// Common implementation of string_{r}{find,index}. 2149*4947cdc7SCole Faustfunc string_find_impl(b *Builtin, args Tuple, kwargs []Tuple, allowError, last bool) (Value, error) { 2150*4947cdc7SCole Faust var sub string 2151*4947cdc7SCole Faust var start_, end_ Value 2152*4947cdc7SCole Faust if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &sub, &start_, &end_); err != nil { 2153*4947cdc7SCole Faust return nil, err 2154*4947cdc7SCole Faust } 2155*4947cdc7SCole Faust 2156*4947cdc7SCole Faust s := string(b.Receiver().(String)) 2157*4947cdc7SCole Faust start, end, err := indices(start_, end_, len(s)) 2158*4947cdc7SCole Faust if err != nil { 2159*4947cdc7SCole Faust return nil, nameErr(b, err) 2160*4947cdc7SCole Faust } 2161*4947cdc7SCole Faust var slice string 2162*4947cdc7SCole Faust if start < end { 2163*4947cdc7SCole Faust slice = s[start:end] 2164*4947cdc7SCole Faust } 2165*4947cdc7SCole Faust 2166*4947cdc7SCole Faust var i int 2167*4947cdc7SCole Faust if last { 2168*4947cdc7SCole Faust i = strings.LastIndex(slice, sub) 2169*4947cdc7SCole Faust } else { 2170*4947cdc7SCole Faust i = strings.Index(slice, sub) 2171*4947cdc7SCole Faust } 2172*4947cdc7SCole Faust if i < 0 { 2173*4947cdc7SCole Faust if !allowError { 2174*4947cdc7SCole Faust return nil, nameErr(b, "substring not found") 2175*4947cdc7SCole Faust } 2176*4947cdc7SCole Faust return MakeInt(-1), nil 2177*4947cdc7SCole Faust } 2178*4947cdc7SCole Faust return MakeInt(i + start), nil 2179*4947cdc7SCole Faust} 2180*4947cdc7SCole Faust 2181*4947cdc7SCole Faust// Common implementation of builtin dict function and dict.update method. 2182*4947cdc7SCole Faust// Precondition: len(updates) == 0 or 1. 2183*4947cdc7SCole Faustfunc updateDict(dict *Dict, updates Tuple, kwargs []Tuple) error { 2184*4947cdc7SCole Faust if len(updates) == 1 { 2185*4947cdc7SCole Faust switch updates := updates[0].(type) { 2186*4947cdc7SCole Faust case IterableMapping: 2187*4947cdc7SCole Faust // Iterate over dict's key/value pairs, not just keys. 2188*4947cdc7SCole Faust for _, item := range updates.Items() { 2189*4947cdc7SCole Faust if err := dict.SetKey(item[0], item[1]); err != nil { 2190*4947cdc7SCole Faust return err // dict is frozen 2191*4947cdc7SCole Faust } 2192*4947cdc7SCole Faust } 2193*4947cdc7SCole Faust default: 2194*4947cdc7SCole Faust // all other sequences 2195*4947cdc7SCole Faust iter := Iterate(updates) 2196*4947cdc7SCole Faust if iter == nil { 2197*4947cdc7SCole Faust return fmt.Errorf("got %s, want iterable", updates.Type()) 2198*4947cdc7SCole Faust } 2199*4947cdc7SCole Faust defer iter.Done() 2200*4947cdc7SCole Faust var pair Value 2201*4947cdc7SCole Faust for i := 0; iter.Next(&pair); i++ { 2202*4947cdc7SCole Faust iter2 := Iterate(pair) 2203*4947cdc7SCole Faust if iter2 == nil { 2204*4947cdc7SCole Faust return fmt.Errorf("dictionary update sequence element #%d is not iterable (%s)", i, pair.Type()) 2205*4947cdc7SCole Faust 2206*4947cdc7SCole Faust } 2207*4947cdc7SCole Faust defer iter2.Done() 2208*4947cdc7SCole Faust len := Len(pair) 2209*4947cdc7SCole Faust if len < 0 { 2210*4947cdc7SCole Faust return fmt.Errorf("dictionary update sequence element #%d has unknown length (%s)", i, pair.Type()) 2211*4947cdc7SCole Faust } else if len != 2 { 2212*4947cdc7SCole Faust return fmt.Errorf("dictionary update sequence element #%d has length %d, want 2", i, len) 2213*4947cdc7SCole Faust } 2214*4947cdc7SCole Faust var k, v Value 2215*4947cdc7SCole Faust iter2.Next(&k) 2216*4947cdc7SCole Faust iter2.Next(&v) 2217*4947cdc7SCole Faust if err := dict.SetKey(k, v); err != nil { 2218*4947cdc7SCole Faust return err 2219*4947cdc7SCole Faust } 2220*4947cdc7SCole Faust } 2221*4947cdc7SCole Faust } 2222*4947cdc7SCole Faust } 2223*4947cdc7SCole Faust 2224*4947cdc7SCole Faust // Then add the kwargs. 2225*4947cdc7SCole Faust before := dict.Len() 2226*4947cdc7SCole Faust for _, pair := range kwargs { 2227*4947cdc7SCole Faust if err := dict.SetKey(pair[0], pair[1]); err != nil { 2228*4947cdc7SCole Faust return err // dict is frozen 2229*4947cdc7SCole Faust } 2230*4947cdc7SCole Faust } 2231*4947cdc7SCole Faust // In the common case, each kwarg will add another dict entry. 2232*4947cdc7SCole Faust // If that's not so, check whether it is because there was a duplicate kwarg. 2233*4947cdc7SCole Faust if dict.Len() < before+len(kwargs) { 2234*4947cdc7SCole Faust keys := make(map[String]bool, len(kwargs)) 2235*4947cdc7SCole Faust for _, kv := range kwargs { 2236*4947cdc7SCole Faust k := kv[0].(String) 2237*4947cdc7SCole Faust if keys[k] { 2238*4947cdc7SCole Faust return fmt.Errorf("duplicate keyword arg: %v", k) 2239*4947cdc7SCole Faust } 2240*4947cdc7SCole Faust keys[k] = true 2241*4947cdc7SCole Faust } 2242*4947cdc7SCole Faust } 2243*4947cdc7SCole Faust 2244*4947cdc7SCole Faust return nil 2245*4947cdc7SCole Faust} 2246*4947cdc7SCole Faust 2247*4947cdc7SCole Faust// nameErr returns an error message of the form "name: msg" 2248*4947cdc7SCole Faust// where name is b.Name() and msg is a string or error. 2249*4947cdc7SCole Faustfunc nameErr(b *Builtin, msg interface{}) error { 2250*4947cdc7SCole Faust return fmt.Errorf("%s: %v", b.Name(), msg) 2251*4947cdc7SCole Faust} 2252