xref: /aosp_15_r20/external/starlark-go/starlark/library.go (revision 4947cdc739c985f6d86941e22894f5cefe7c9e9a)
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